Windows(x86)頁表與虛擬空間之我見
執行環境:
主機系統:Windows 10 x64
目標系統:Windows XP sp3 x86
工具軟體:
虛擬機器:Virtual Box
編譯器:Visual Studio 2017
偵錯程式:Windbg、OllyDBG
參考書籍:
《Windows 核心設計思想》
《Windows 核心程式設計》
《軟體除錯》
參考博文:
《詳解Windows記憶體分頁機制》本文引用了以上書籍、文章的部分觀點,算是一篇學習筆記。在此感謝各位作者!
頁表機制準確的說是CPU實現的特性,由OS加以利用。網上有很多陳述頁表機制的文章,但以Linux居多,Windows偏少,且很多都是理論層面借圖表的闡述,或許對於科班出身的人士來說是小菜一碟,可對我等自學愛好者而言卻經常是一臉懵逼的感嘆!因此,萌發了動手實踐的想法,經過一翻折騰,總算小有心得,就此記錄於網際,希望本文能給和我一樣的愛好者以幫助。本文可能存在很多歪解,請各位看官多多斧正!
本文試圖解決的疑問:
頁有多少個?
頁每個程式都有嗎?
頁多大?
頁放在哪裡?
頁如何檢視?
頁如何修改?
頁如何隔離程式?
頁和虛擬地址的關係?
頁在何時被使用?
從test.exe開始:
// test.exe 原始碼 #include <iostream> int main() { std::cin.get (); // 等待做手腳 int* p = NULL; *p = 0x89abcdef ; // 向0指標寫入資料 std::cout << "Hello nullptr!\n"; // 改編自經典:) return 0; }
程式碼很簡單不出大問題的話,編譯執行敲回車直接崩潰QAQ。在預設設定的Win10(17134)cmd下可正常執行和退出,但沒有列印“Hello nullptr”,在XP下報錯終止,錯誤程式碼0xC0000005(訪問違規)。可見Win10在異常處理上有所調整(記得《Windows核心程式設計》有提過在Vista之後即如此)。
有C/C++基礎的碼友一定記得一條原則,0x00000000為空指標,設計思想很簡單,例如呼叫malloc返回0表示失敗,如果返回0指標是可用的,那錯誤用什麼表示呢?
我的執行結果是正常的,我承認我搞事情了,嘿嘿!
在動手完成空指標寫入資料之前先大致瞭解一些基本情況,在Windbg列舉程式。
kd> !process 0 0 **** NT ACTIVE PROCESS DUMP **** PROCESS 8a0e59c8 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 DirBase: 00039000 ObjectTable: e1000d10 HandleCount: 240. Image: System PROCESS 89be5020 SessionId: 0 Cid: 05d8 Peb: 7ffd6000 ParentCid: 05e4 DirBase: 19f3c000 ObjectTable: e1e27cc8 HandleCount: 35. Image: cmd.exe PROCESS 89ba8318 SessionId: 0 Cid: 0578 Peb: 7ffde000 ParentCid: 0268 DirBase: 19f97000 ObjectTable: e1b8ef08 HandleCount: 82. Image: taskmgr.exe PROCESS 89f52c10 SessionId: 0 Cid: 07c0 Peb: 7ffde000 ParentCid: 05e4 DirBase: 1d42f000 ObjectTable: e1ba5700 HandleCount: 77. Image: OllyICE.exe PROCESS 89bfc470 SessionId: 0 Cid: 01b8 Peb: 7ffde000 ParentCid: 05d8 DirBase: 24231000 ObjectTable: e1d764b8 HandleCount: 7. Image: test.exe ...
資訊經過刪減,留下了幾個比較熟悉的程式,文中類似資訊也會將無關清除,清除部分會用“...”表示,不再單獨註明。
這些程式分別是:taskmgr.exe工作管理員、cmd.exe控制檯、System系統程式、OllyICE.exe偵錯程式、test.exe測試程式。
每個程式的DirBase項的16進位制數可以發現以下特徵:
1、各程式不同;
2、可被4096整除;
3、以000結尾。
如果你的顯示結果與特徵不符,那是另外一種模式PAE,本文不做解釋。從特徵上分析,可被4096整除說明是4K對齊;各程式不相同說明每個程式的起始頁不同,因此得以隔離程式;000結尾說明PDE沒有屬性,PDE為何物見下文。
瞭解了程式資訊之後,再來看看程式的地址、佔用空間大小等資訊,著重觀察test.exe。
在工作管理員視窗紅色框部分,是各程式使用實體記憶體佔用大小,單位Kb,這些數都可以被4整除,所得商即該程式使用的記憶體頁數。
當test.exe在前臺時(最大化)使用了193(772K/4)頁記憶體,在後臺時(最小化)使用9(36/4)頁,由此可見當程式非活動狀態或需求非常少時,實體記憶體會歸還給OS。從test.exe程式碼可知,程式阻塞在cin.get ()呼叫,基本上沒什麼執行需求。(切換的視窗是cmd,因test執行於此)
OD視窗紅色框部分,是test.exe的邏輯地址和空間大小資訊,此空間大小是程式自身可能用到的大小,粗略合計約1000(4M/4K)頁,通過OD的簡要資訊,可獲知地址處都存放了什麼,如主棧、PE頭、.text程式碼段等。這裡的空間大小基本是固定的。
經過以上分析得到一個結果,程式虛擬空間遠大於物理佔用,實際上程式在執行時,不會將所有內容都放入實體記憶體,僅將當前執行所需要的程式碼、資料放入物理頁。
有了這些線索,我們通過實驗來進一步印證。利用PROCESS項的16進位制數將test.exe切換至當前環境。預設當前環境是System程式,所以查詢顯示資訊將是System程式的。
kd> !process 0 0 ... PROCESS 89bfc470 SessionId: 0 Cid: 01b8 Peb: 7ffde000 ParentCid: 05d8 DirBase: 24231000 ObjectTable: e1d764b8 HandleCount: 7. Image: test.exe ... kd> .process /i /p 89bfc470 kd> g kd> r cr3 cr3=24231000
使用r指令檢視了cr3暫存器,顯示的資訊與DirBase項一致,實際上OS在後臺不斷的自動切換cr3,讓每個程式都得到一點執行時間。
實驗所需的知識點簡單介紹一下(以下僅是小頁面,大頁面請翻閱書籍):每個程式都有一頁(4K)存放一個頁目錄(Page Directory Entry,PDE),PDE佔4K空間,分成1024項,每項4B,每項描述一個頁表(Page Table Entr,PTE),PTE的高20位是基址域,低12位是屬性域;PTE也佔4K,同樣可分成1024項,每項4B,每項描述一頁(Page),Page的高20位是基址域,低12位是屬性域。
根據上述可得
1024(PTE 個數)*1024(Page 個數)*4096(Page 大小)=4G(總空間);
1024(PTE 個數)*1024(Page個數)*4(描述項大小)=4M(總佔用);
1024(PTE個數)*1024(Page個數)=1M(總頁數);
實際總佔用僅是理論值,一般程式不可能達到4M,下面實驗會得到證實。
檢視test.exe的PDE,我將PDE分成兩部分,前512項和後512項分別進行分析,因PDE管理4G虛擬空間,但其高2G(後512項)是核心態使用,剩餘低2G(前512項)由使用者態使用(PAE模式使用者態擁有更多的空間)。
kd> !process 0 0 ... PROCESS 89bfc470 SessionId: 0 Cid: 01b8 Peb: 7ffde000 ParentCid: 05d8 DirBase: 24231000 ObjectTable: e1d764b8 HandleCount: 7. Image: test.exe ... kd> !dd 24231000 l200 #24231000 24766067 245e0067 00000000 00000000 ... #242317c0 00000000 00000000 247cf067 00000000 ... #242317f0 00000000 2459a067 00000000 2451e067 ...
PDE前512項中只有5個非空PTE,前面說過1個PTE佔用4K實體記憶體,含1024個Page描述,也就是說1個PTE維護4M(1024*4096)空間,因此可認為OS給test.exe分配了20M(4*5)虛擬空間。
我把以上資料和OD的邏輯地址整理成表進行對比。
我相信你已經找到了相似特徵,邏輯地址與PTE是對映關係。更準確的說邏輯地址值是一個三段式資料結構,關於該結構我用一段程式碼來說明。
// demo.exe 原始碼 #include <iostream> int main() { struct { ULONG offset : 12; ULONG pteIndex : 10; ULONG pdeIndex : 10; } va { 0 }; using namespace std; while (true) { cin >> hex >> *(ULONG*)&va; cout << hex << "邏輯地址:0x" << *(ULONG*)&va << endl << "PDE索引:0x" << va.pdeIndex << " PDE物理偏移:0x" << va.pdeIndex * 4 << endl << "PTE索引:0x" << va.pteIndex << " PTE物理偏移:0x" << va.pteIndex * 4 << endl << "頁內偏移:0x" << va.offset << endl; } return 0; }
PDE可通過cr3得到,PDE+偏移即可找到PTE描述,PTE+偏移又可找到Page描述,Page+頁內偏移可指向具體資料,這就是用邏輯地址搜尋頁表的過程。
進一步檢視PTE索引1(第二個PTE)的內容:
kd> !process 0 0 ... PROCESS 89bfc470 SessionId: 0 Cid: 01b8 Peb: 7ffde000 ParentCid: 05d8 DirBase: 24231000 ObjectTable: e1d764b8 HandleCount: 7. Image: test.exe ... kd> !dd 24231000 #24231000 24766067 245e0067 00000000 00000000 ... kd> !dd 245e0000 l400 #245e0000 24430025 2456c025 2436d025 243ee025 #245e0010 242af025 24470025 24271025 244f2025 #245e0020 242b3025 242f4025 24375025 242b6025 #245e0030 23fb7025 242b8025 240f9025 2423a025 #245e0040 243fb025 241bc025 241bd025 242be025 #245e0050 242bf025 24540025 243c1025 24502025 #245e0060 24543025 248c4025 24705025 27da8025 #245e0070 2459e025 24647025 00000000 24708025 #245e0080 24349025 00000000 2444a025 2454b025 #245e0090 2450c025 00000000 00000000 00000000 #245e00a0 24362025 24523025 24664025 2465f067 #245e00b0 24660067 24625025 00000000 00000000 ...
擷取部分中共41項非空Page描述(省略的都是空Page),第二個PTE管理的4M空間範圍是0x00400000~0x007FFFFF,回顧OD截圖在這4M範圍內的大小合計是46頁,這裡相差5個頁,原因是什麼我不清楚,猜測可能是軟體之間的誤差,有知道的請告知!
PTE和Page的低12位是屬性域,資料上對應的如025、067等,屬性域其中一位描述了資料是在實體記憶體還是在硬碟的虛擬記憶體,OS通過屬性域控制記憶體,PTE可控制4M,Page可控制4K。更多屬性的內容請翻閱書籍。
在OD截圖可知0x00401000是.text段的起始,從demo程式的顯示結果獲知該邏輯地址PTE的索引1偏移4,現在來看該Page內容:
kd> !process 0 0 ... PROCESS 89bfc470 SessionId: 0 Cid: 01b8 Peb: 7ffde000 ParentCid: 05d8 DirBase: 24231000 ObjectTable: e1d764b8 HandleCount: 7. Image: test.exe ... kd> !dd 24231000 #24231000 24766067 245e0067 00000000 00000000 ... kd> !dd 245e0000 #245e0000 24430025 2456c025 2436d025 243ee025 ... kd> !db 2456c000 #2456c000 b9 a0 c1 42 00 e8 24 2b-00 00 68 29 b5 41 00 e8 #2456c010 d2 25 00 00 59 c3 68 33-b5 41 00 e8 c6 25 00 00 #2456c020 59 c3 68 3d b5 41 00 e8-ba 25 00 00 59 c3 6a 01 #2456c030 6a 00 68 a8 c2 42 00 b9-00 c3 42 00 e8 82 33 00 #2456c040 00 68 47 b5 41 00 e8 9b-25 00 00 59 c3 56 57 6a #2456c050 00 e8 ee b9 00 00 59 bf-a8 c2 42 00 8b f0 8b cf #2456c060 e8 ca 33 00 00 6a 00 56-8b cf c7 05 a8 c2 42 00 #2456c070 18 ce 41 00 e8 98 37 00-00 68 51 b5 41 00 e8 63
或許有人會問這些資料是什麼?我們來看一張截圖,看完之後自然豁然開朗……!
所謂的頁內偏移就是以上資料頁位元組的位置,Page[0]=b9,Page[1]=a0……Page[15]=e8,偏移的範圍是0~4095(4K頁內)。
上文書說到:“令貴妃魏瓔珞為救五阿哥永琪……”。咦!好像是走錯片場了……,好吧,書接上文……。
前面分析了PDE前512項,基本清楚了含蓋的內容,現在來分析PDE的後512項,我將test.exe的後512項PTE與taskmgr、cmd、System、OllyICE等4個程式的後512項PTE進行了對比,發現這些PTE只有2項不同,其它完全一致,共有414項非空PTE(含2項不同),按每項管理4M來計算(414-2)*4=1648M,可認為這些空間是所有程式共享的。
不同項和偏移如下:
kd> !process 0 0 **** NT ACTIVE PROCESS DUMP **** PROCESS 8a0e59c8 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 DirBase: 00039000 ObjectTable: e1000d10 HandleCount: 240. Image: System PROCESS 89be5020 SessionId: 0 Cid: 05d8 Peb: 7ffd6000 ParentCid: 05e4 DirBase: 19f3c000 ObjectTable: e1e27cc8 HandleCount: 35. Image: cmd.exe PROCESS 89ba8318 SessionId: 0 Cid: 0578 Peb: 7ffde000 ParentCid: 0268 DirBase: 19f97000 ObjectTable: e1b8ef08 HandleCount: 82. Image: taskmgr.exe PROCESS 89f52c10 SessionId: 0 Cid: 07c0 Peb: 7ffde000 ParentCid: 05e4 DirBase: 1d42f000 ObjectTable: e1ba5700 HandleCount: 77. Image: OllyICE.exe PROCESS 89bfc470 SessionId: 0 Cid: 01b8 Peb: 7ffde000 ParentCid: 05d8 DirBase: 24231000 ObjectTable: e1d764b8 HandleCount: 7. Image: test.exe ... kd> !dd 00039000+c00 l2 # 39c00 00039067 0a640063 kd> !dd 19f3c000+c00 l2 #19f3cc00 19f3c063 19ffd063 kd> !dd 19f97000+c00 l2 #19f97c00 19f97063 19f58063 kd> !dd 1d42f000+c00 l2 #1d42fc00 1d42f063 1d5f0063 kd> !dd 24231000+c00 l2 #24231c00 24231063 244b2063
從資料上看不同的第一個PTE基址域都指向了自己,而屬性域指明瞭該PTE歸核心態空間所有,第二個不同PTE沒有深挖。如有知者請告知。將這個位置轉換成邏輯地址是0xC0000000,可以想見PTE是由核心態空間儲存的。
現在來說下test.exe向0地址寫入資料的方法,首先將0x00000000邏輯地址分解成三段式結構,通過所得結果找到對應頁一探究竟。
kd> !process 0 0 ... PROCESS 89bfc470 SessionId: 0 Cid: 01b8 Peb: 7ffde000 ParentCid: 05d8 DirBase: 24231000 ObjectTable: e1d764b8 HandleCount: 7. Image: test.exe ... // 修改之前 kd> !dd 24231000 #24231000 24766067 245e0067 00000000 00000000 ... // 原因是沒有分配頁 kd> !dd 24766000 #24766000 00000000 00000000 00000000 00000000 ... // 修改 kd> !ed 24766000 29e3c067 // 修改之後 kd> !dd 24766000 #24766000 29e3c067 00000000 00000000 00000000
或許有人會問“29e3c067”是哪裡來的,《OS 學習筆記》的作者是將主棧的描述掛到此處,但我認為主棧涉及區域性變數、呼叫引數和返回地址等,修改可能造成程式報錯,因此我的方法是用OD的外掛分配一頁堆空間,並將堆邏輯地址解析成三段式找到對應的Page描述將其掛到此處,“29e3c067”就是該堆頁的描述。
總結:
test.exe分析結果統計,1個PDE,5個PTE,佔6個4K物理頁,後512項那些PTE或複製或對映,總之這些都不屬於當前程式,5個PTE所描述的是20M虛擬空間,程式僅用4M,20M是5120頁,4M是1024頁,急需執行的193頁放入實體記憶體。
原文作者: khristian (看雪ID)
原文連結:https://bbs.pediy.com/thread-246611.htm
轉載請註明:轉自看雪論壇
看雪推薦:
2、[原創]覺醒之戰Ⅰ:洞察HW程式設計師的腦洞
3、[原創]幾種常見的注入姿勢
4、[翻譯]StaDynA:解決Android APP安全分析中的動態程式碼更新問題
5、[原創]新手——win32程式的半生(CreateProcess)
相關文章
- Linux虛擬主機與Windows虛擬主機之間有什麼區別2020-09-14LinuxWindows
- 【虛擬機器】Windows(x86)上部署ARM虛擬機器(Ubuntu)2024-07-26虛擬機WindowsUbuntu
- 程式虛擬空間分佈2022-03-13
- 虛擬主機資料庫與空間關係2020-12-28資料庫
- vm exsi 擴充套件windows虛擬機器磁碟空間2024-05-30套件Windows虛擬機
- 表空間常見的操作2015-01-08
- 解決Ubuntu虛擬機器佔用空間與實際空間不符問題2022-03-23Ubuntu虛擬機
- 表空間管理之bigfile表空間設定2011-07-26
- 給虛擬機器的增加空間2018-12-04虛擬機
- Virtualbox 《虛擬機器空間整理》2024-10-12虛擬機
- 為Linux虛擬機器增加磁碟空間2016-05-23Linux虛擬機
- 虛擬空間的陷阱--對微軟的檄文 (轉)2007-12-03微軟
- 測試表的空間壓縮與表空間的關係2010-11-04
- mysql之 表空間傳輸2018-09-28MySql
- Oracle 本地表空間管理與字典表空間管理2016-11-15Oracle
- 讀懂作業系統之虛擬記憶體頁表(五)2020-06-29作業系統記憶體
- MySQL 中的共享表空間與獨立表空間如何選擇2021-07-25MySql
- mysql之 共享表空間與獨立表空間、frm,MYD,MYI.idb,par檔案說明2017-05-29MySql
- 用Jcmd命令分析虛擬機器Metaspace元空間2020-10-26虛擬機
- 程式的虛擬地址空間——NULL指標分割槽2015-01-13Null指標
- LVM擴容-----給虛擬機器新增空間2015-02-27LVM虛擬機
- 虛擬磁碟空間滿後的擴充套件方法2012-12-16套件
- 調整vmware虛擬機器硬碟空間的方法2011-04-14虛擬機硬碟
- 虛擬塊硬碟,新增點swap分割槽空間(轉)2007-08-08硬碟
- Linux 虛擬機器擴充套件磁碟空間2012-05-19Linux虛擬機套件
- windows xp和linux虛擬機器之間使用ftp共享檔案2009-09-15WindowsLinux虛擬機FTP
- 解謎遊戲:在虛擬與現實之間反覆橫跳2019-11-29遊戲
- [DB2]表空間之DMS、自動儲存的DMS表空間2010-07-26DB2
- SYSAUX表空間清理之SM/OPTSTAT2021-09-21UX
- Oracle SQL 基本操作之 表空間2014-01-30OracleSQL
- oracle之 SYSAUX表空間維護2018-01-12OracleUX
- C++中的虛擬函式與虛擬函式表 (轉)2007-12-13C++函式
- 傳輸表空間(從Linux到Windows)2015-01-29LinuxWindows
- 遊戲設計&建築設計:未來,誰來規劃我們的虛擬空間?2021-01-13遊戲設計
- Undo表空間與redo日誌2011-06-20
- Windows逆向之配置虛擬機器環境2024-03-13Windows虛擬機
- DB2頁大小、表大小和表空間大小限制2016-01-11DB2
- 虛擬函式,虛擬函式表2018-08-07函式