|
論壇說明 |
歡迎您來到『史萊姆論壇』 ^___^ 您目前正以訪客的身份瀏覽本論壇,訪客所擁有的權限將受到限制,您可以瀏覽本論壇大部份的版區與文章,但您將無法參與任何討論或是使用私人訊息與其他會員交流。若您希望擁有完整的使用權限,請註冊成為我們的一份子,註冊的程序十分簡單、快速,而且最重要的是--註冊是完全免費的! 請點擊這裡:『註冊成為我們的一份子!』 |
|
主題工具 | 顯示模式 |
2004-02-23, 03:24 AM | #1 |
榮譽會員
|
小議Windows NT/2000分頁機制
記憶體管理是操作系統最重要的一部分,其決定了操作系統的效能。
Intel X86採用分 段、分頁的記憶體機制,Windows NT/2000充分利用了此項先進的機制。段內IA-32體系使 用頁目錄(Page Directory)與頁表(Page Table,SoftICE中Page指令可以顯示出頁目錄與 頁表等)形成對4G位址的尋址能力。文中未特別說明,均討論執行平台為Intel 32位處理 器的Windows NT/2000,所提及的Windows也僅指Windows NT/2000。Windows中不管用戶 態與核心態的程式碼段、資料段與堆倉段基址均為0,文中提到的邏輯位址(由段基址與偏 移量兩部分組成)與線性位址值是相等的。由於對於用戶來說線性位址是其可見的,若我 未特別指出物理位址,所說的位址也僅指線性位址。 頁目錄(PDE)由1024項組成,每項均指向一頁表(PTE),每一頁表也由1024個頁組成 ,IA-32體系每頁大小為4K,所以可尋址範圍為4G(1024*1024*4K)。Windows中每個工作 都擁有其各自的工作位址空間,即擁有其各自的頁目錄與頁表。每個工作均使用線性地 址C0300000H指向其特定的頁目錄所在的位址,而頁目錄中每項(即頁表)均依次排列在線 性位址C0000000H處,每個頁表均佔用4K(1024*4)字元,如第一個頁表位於C0000000H處 ,而第二個頁表位於C0000000+1000H(4K),即C0001000中,依次類推,計算公式即為C0 000000H+頁目錄偏移值(線性位址的高10位)*1000H,在下面我將利用此公式。當然以上 描述的前提是每個頁表均位於實體記憶體中(由頁目錄中每項中的P位指定),這也是為什麼 IA-32使用兩級頁表的原因,否則每個工作除其程式碼與資料等外還額外需要4M(4*1024*1 024)的存儲器。 上面的機制即實現了物理位址尋址,也就實現了在Windows NT/2000中物理位址與線 性位址的相互轉換(雖然CPU在對記憶體操作時只需要線性位址轉換成物理位址,但我們在 分析程序程式碼等仍需要物理位址轉換成線性位址)。照例先看看SoftICE的分析吧: // addr explorer指令後以下操作將只在工作explorer的私有工作空間進行 :addr explorer // 顯示explorer工作頁目錄所在物理位址,即工作切換至explorer後PDBR(CR3)中 的值 :addr CR3 LDT Base:Limit KPEB Addr PID Name . . . 00C10000 FF9FC920 036C explorer . . . /* 線性位址的格式:0-11位對應1頁(4096字元)的偏移量 12-21位對當前頁表中1024項中尋址 22-31位對頁目錄進行尋址 高20位(12-31位)又稱為楨 根據上面提及的公式,即可以得到物理位址高20位的值,再加上線性位址頁表 偏移(作為物理 位址的低12位),即實現了線性位址轉化為物理位址,用公式表示為: @(C0000000h+PDE*1000h+PTE*4)&0fffff000h+PO =@(C0000000h+4*(PDE*400h+PTE))&0fffff000h+PO =@(C0000000h+(PDE>>10d+PTE)<<2d)&0fffff000h+PO =@(C0000000h+(LA>>12d)<<2d)&0fffff000h+PO =@(C0000000h+(LA&0xFFFFF000)>>10d)&0fffff000h+PO 上式中用PDE與PTE分別代表Page Directory與Page Table的偏移值,用LA 代表給定的線性 位址,用PO代表LA的低12位,用h與d分別代表16/10進制,@後表示取後位址指 針中的內容。 如此分析後,線性位址C0300000所對應的頁目錄為300h,頁表為300h,偏移量 為0 則 C0000000h+PD*1000h+PT*4+PO=C0000000+300h*1000h+300h*4+0 */ :dd c0000000+300*1000+300*4 l 4 0010:C0300C00 00C10063 01A31063 00000000 0141F163 c...c.......c.A. | |_低12位(0-11)063為內容位、Intel保留位與系統(OS)使用位 // 顯示C0300000的物理位址(00C10000) :? dword(@(c0000000+300*1000+300*4))&fffff000+C0300000&00000fff 00C10000 //用SoftICE驗證 hys dword(@(c0000000+300*1000+300*4))&fffff000+C0300000&00000fff C0300000 age C0300000 Linear Physical Attributes C0300000 00C10000 P D A S RW 其實上面最後一條指令就可以實現所有其它指令的功能,下面我列出實現的程式碼段: // 線性位址->物理位址 // SoftICE中Page指令可以實現此功能 // 一個線性位址對應唯一的物理位址 // 此函數若返回0說明此線性位址未對應物理位址 ULONG LinearAddressToPhysicalAddress(ULONG lAddress) { unsigned int *pAddr; unsigned int *PageDirectoryEntry=(unsigned int *)0xC0300000; unsigned int *PageTableEntry=(unsigned int *)0xC0000000; //判斷頁目錄是否有效,第0位(P)為存在位,請參閱相關書籍 if((!(PageDirectoryEntry[lAddress>>22]&0xFFFFF000)) &&(!(PageDirectoryEntry[lAddress>>22]&0x00000001))) return 0; //@(C0000000h+(LA&0xFFFFF000)>>10d)&0fffff000h+PO 見上敘述 pAddr=(int *)((int)PageTableEntry+((lAddress&0xFFFFF000)>>10)); if((*pAddr)&1) return ((*pAddr) &0xFFFFF000) |(lAddress&0x00000FFF); return 0; } 那麼又如何逆向實現物理位址轉換成線性位址呢?雖然其間沒有任何關係,但因為知 道頁目錄與頁表的直接位置,可使用直接在這個範圍中搜尋的方法。理論上這個範圍最 大為1024*1024*4(4M),但由於很多頁目錄項當前均不存在於實體記憶體中,所以實際上搜 索範圍小得多。這也導致一個問題,有可能導致顯示藍色(內核態訪問不存在的位址)。所以 以下給出的程式碼我檢查了頁目錄中每一項的P位。 // 物理位址->線性位址 // 相當於SoftICE中Phys指令 // 搜尋所有有效的頁表尋找指定物理位址 // 有可能多個線性位址同時指向同一個物理位址 // 此函數若未輸出任何結果表明當前還沒有線性位址映射至此物理位址中 void PhysicalAddressToLinearAddress(ULONG pAddress) { unsigned int *pAddr; unsigned int *PageDirectoryEntry=(unsigned int *)0xC0300000; unsigned int *PageTableEntry=(unsigned int *)0xC0000000; int i,j; DbgPrint("\n"); for(i=0;i<1024;i++) if((PageDirectoryEntry[i]&0xFFFFF000)&&(PageDirectoryEntry[i]&0x 00000001)) for(j=0;j<1024;j++){ pAddr=(int *)((int)PageTableEntry+i*4096+j*4); if((*pAddr)&0x00000001) if(((*pAddr)&0xFFFFF000)==(pAddress&0xFFFFF000)) DbgPrint("%08X\n", ((i*4*1024*1024+j*4*1024)&0xFFFFF000)|(pAddr ess&0x00000FFF)); } } 上面兩個程序段涉及到2G-4G範圍(線性位址C0000000以上)的記憶體訪問,普通用戶態 的程序無法實現。在Windows中請使用設備驅動程式以使其在核心態正確執行。 那麼Windows又是如何利用分頁機制以高效、合理的利用好有限的實體記憶體呢?Jeff rey Richter的傳統著作< Edition>>全面闡述了Windows的記憶體管理機制,相關原理請直接參閱此書!下面我列出 同一程序(mspaint.exe)的兩個執行實例的線性位址與物理位址的對應關係,以說明Win dows的記憶體分頁機制。 // 下面是mspaint.exe裝入記憶體後各段的位址(兩個同時執行的mspaint.exe工作的 映射位址一致) :map32 mspaint Owner Obj Name Obj# Address Size Type mspaint .text 0001 001B:01001000 0003A500 CODE RO mspaint .data 0002 0023:0103C000 00002670 IDATA RW mspaint .rsrc 0003 0023:0103F000 000116C8 IDATA RO ------------- | |_邏輯位址 // mspaint.exe第一個執行實例的線性位址與物理位址的對應關係 Linear Address Range Physical Address Range Attributes -------------------- ---------------------- ---------- 00010000 - 00010FFF 03A8B000 - 03A8BFFF 047 00020000 - 00020FFF 03BCC000 - 03BCCFFF 047 0006D000 - 0006DFFF 018BC000 - 018BCFFF 047 . . . . . . . . . //mspaint.exe第一個實例的.text段 01001000 - 01001FFF 00596000 - 00596FFF 005 01002000 - 01002FFF 03F97000 - 03F97FFF 005 01003000 - 01003FFF 03D58000 - 03D58FFF 005 . . . . . . . . . //mspaint.exe第一個實例的.data段 0103C000 - 0103CFFF 0225F000 - 0225FFFF 047 0103D000 - 0103DFFF 03620000 - 03620FFF 047 0103E000 - 0103EFFF 03C1E000 - 03C1EFFF 047 . . . . . . . . . //mspaint.exe第一個實例的.rsrc段 0103F000 - 0103FFFF 01652000 - 01652FFF 025 01040000 - 01040FFF 02653000 - 02653FFF 005 01041000 - 01041FFF 003D4000 - 003D4FFF 005 . . . . . . . . . //mspaint.exe第一個實例的頁目錄的表 C0300000 - C0300FFF 030FD000 - 030FDFFF 063 C0301000 - C0301FFF 017FE000 - 017FEFFF 063 C0303000 - C0303FFF 0141F000 - 0141FFFF 163 . . . . . . . . . FFD0F000 - FFD0FFFF 000FF000 - 000FFFFF 023 FFDF0000 - FFDF0FFF 0026A000 - 0026AFFF 163 FFDFF000 - FFDFFFFF 00269000 - 00269FFF 163 // mspaint.exe第二個執行實例的線性位址與物理位址的對應關係 Linear Address Range Physical Address Range Attributes -------------------- ---------------------- ---------- 00010000 - 00010FFF 03A6A000 - 03A6AFFF 047 00020000 - 00020FFF 0352B000 - 0352BFFF 067 0006D000 - 0006DFFF 03413000 - 03413FFF 047 . . . . . . . . . //mspaint.exe第二個實例的.text段 01001000 - 01001FFF 00596000 - 00596FFF 005 01002000 - 01002FFF 03F97000 - 03F97FFF 005 01003000 - 01003FFF 03D58000 - 03D58FFF 005 . . . . . . . . . //mspaint.exe第二個實例的.data段 0103C000 - 0103CFFF 030DF000 - 030DFFFF 047 0103D000 - 0103DFFF 009A0000 - 009A0FFF 047 0103E000 - 0103EFFF 02089000 - 02089FFF 047 . . . . . . . . . //mspaint.exe第二個實例的.rsrc段 0103F000 - 0103FFFF 01652000 - 01652FFF 005 01040000 - 01040FFF 02653000 - 02653FFF 005 01041000 - 01041FFF 003D4000 - 003D4FFF 005 . . . . . . . . . //mspaint.exe第二個實例的頁目錄的表 C0300000 - C0300FFF 037C9000 - 037C9FFF 063 C0301000 - C0301FFF 02F8A000 - 02F8AFFF 063 C0303000 - C0303FFF 0141F000 - 0141FFFF 163 . . . . . . . . . FFD0F000 - FFD0FFFF 000FF000 - 000FFFFF 023 FFDF0000 - FFDF0FFF 0026A000 - 0026AFFF 163 FFDFF000 - FFDFFFFF 00269000 - 00269FFF 163 上面列表直接從mspaint.exe的兩個同時執行的實例的頁目錄與頁表中取得。實際上 你只要理解物理位址與線性位址的相互轉換,稍微修改一下上面所給的兩個程式碼段便能 得到上面的信息(32位x86平台Windows 2000 Server Build 2195的某一時刻所取,我的機 器實體記憶體為64M,即040000000H字元)。 Windows中每個工作均有其私有的線性位址,在線性位址的前2G(用戶空間,Windows 2000 Server的ntoskrnl.exe的MmHighestUserAddress指出用戶空間最大值7FFEFFFFH) ,上例中列出的兩個實例同時執行的mspaint.exe的.text段與.rsrc段均指向同一物理地 址空間,而.data段指向不同的物理空間。這是由於不同段的作用與性質決定,不難理解 。在後2G(內核空間,Windows 2000 Server的ntoskrnl.exe的MmSystemRangeStart指出其 線性位址的開始位置),大部分物理空間均由兩個實例共享,實際上不同程序所有執行的 例程均共享這2G,當然頁目錄(C0300000h)與頁表(C0000000h)等其它比較特殊的操作除 外,上例中基本可以看出這個規則。當然頁目錄與頁表也有專案是指向同一物理區域,這 樣才能實現工作間共享實體記憶體,如上面mspaint.exe的兩個實例的頁目錄中C0303000-C 0303FFF所映射的線性位址00800000-00BFFFFF(4M)指向同一物理區域。 這只是討論通常情況下Windows如何在程序間高效的使用記憶體,實際上Windows賦予 頁很多機制,如Copy On Write等,使.text段等需要時也可以指向不同的物理位址,典 型情況是使用用戶態的偵錯器(Microsoft Visual C++所附的IDE偵錯環境等)對應用程式 進行偵錯。當然Windows也提供讓資料共享同一實體記憶體區域的方法,即使用Microsoft 連接器(link)的section開關,賦予特定的段共享(S)內容。 在<<淺析Windows NT/2000環境切換>>(Nsfocus Magazine 12)中我曾詳細的介紹了 Windows NT/2000環境切換後頁目錄基址(CR3)切換程式碼,那麼Windows NT/2000如何為應 用程序從頭分配頁目錄與頁表呢?因為只有在建立新工作時才牽涉到頁目錄與頁表的分配 ,所以還是讓我們看看Kernel32.dll中的CreateProcessW程式碼吧(CreateProcessA間接調 用CreateProcessW). 可以簡單的將流程用程式碼如下顯示: KERNEL32!CreateProcessW . .(一些錯誤例程如工作文件是否存在,內核對像安全性檢查等程序) . ;開啟文件 001B:77E7DDD2 CALL [ntdll!NtOpenFile] . .(主要是一些參數的壓棧程式碼) . ;為可執行文件分配虛擬位址 001B:77E7DE0A CALL [ntdll!NtCreateSection] . . . ;關閉文件 001B:77E7DE1E CALL [ntdll!NtClose] . . . ;使用NtCreateProcess新增工作 001B:77E7DF83 CALL [ntdll!NtCreateProcess] . . . 其實上面ntdll.dll的四個程序在Windows 2000 Server Build 2195中分別是Servi ce ID為64h、2bh、18h與29h的System Service。關於System Service可參閱<<再談Win dows NT/2000內部資料結構>>(Nsfocus Magazine 11)。 我們繼續看看NtCreateProcess處理流程: :u ntdll!NtCreateProcess //用戶態,就是常說的Native API ntdll!NtCreateProcess 001B:77F92D2C MOV EAX,00000029 001B:77F92D31 LEA EDX,[ESP+04] 001B:77F92D35 INT 2E //使用中斷門進入核心態 001B:77F92D37 RET 0020 :ntcall Service table address: 804704D8 Number of services:000000F8 . . . 0029 0008:804AD948 params=08 ntoskrnl!SeUnlockSubjectContext+0514 | |_ID為29h的System Service(NtCreateProcess)的入口位址 . . . :u 8:804ad948 0008:804AD948 55 PUSH EBP 0008:804AD949 8BEC MOV EBP,ESP 0008:804AD94B 6AFF PUSH FF 0008:804AD94D 6890354080 PUSH 80403590 0008:804AD952 682CCC4580 PUSH ntoskrnl!_except_handler3 0008:804AD957 64A100000000 MOV EAX,FS:[00000000] 0008:804AD95D 50 PUSH EAX 0008:804AD95E 64892500000000 MOV FS:[00000000],ESP . . . ;EBP-30中此時存放新增工作的KPEB,以下幾句實現對KPEB後的0A2H*4(648)字元清零 0008:804ADAF5 B9A2000000 MOV ECX,000000A2 0008:804ADAFA 33C0 XOR EAX,EAX 0008:804ADAFC 8B7DD0 MOV EDI,[EBP-30] 0008:804ADAFF F3AB REPZ STOSD . . . 0008:804AD5E7 55 PUSH EBP 0008:804AD5E8 8BEC MOV EBP,ESP ;KPEB與工作Context分別作為這個程序的第一與第四個參數傳入(ebp+8與ebp+14h) 0008:804AD5EA 8B4508 MOV EAX,[EBP+08] 0008:804AD5ED 8D4808 LEA ECX,[EAX+08] 0008:804AD5F0 C60003 MOV BYTE PTR [EAX],03 0008:804AD5F3 89480C MOV [EAX+0C],ECX 0008:804AD5F6 C640021B MOV BYTE PTR [EAX+02],1B 0008:804AD5FA 8909 MOV [ECX],ECX 0008:804AD5FC 8A4D0C MOV CL,[EBP+0C] 0008:804AD5FF 884862 MOV [EAX+62],CL 0008:804AD602 8B4D10 MOV ECX,[EBP+10] 0008:804AD605 89485C MOV [EAX+5C],ECX 0008:804AD608 8A4D18 MOV CL,[EBP+18] 0008:804AD60B 884864 MOV [EAX+64],CL 0008:804AD60E 8B4D14 MOV ECX,[EBP+14] 0008:804AD611 8B11 MOV EDX,[ECX] ;EDX存放工作Context(即頁目錄的物理位址) ;至於工作Context的算法,由於不僅與ntoskrnl.exe中的幾個變數有關,還與執行環 境息息相關,感興趣的自己用SoftICE跟跟 ;將工作Context放入新增的KPEB中 0008:804AD613 895018 MOV [EAX+18],EDX ;18H是工作Context相對KPEB的 偏移 . . . ;以下實現將新增的工作的KPEB插入系統KPEB的雙向鏈表 0008:804ADD22 A184A14680 MOV EAX,[8046A184] /* 8046A180用以下兩條SoftICE指令輸出結果就可以很容易理解 :? (@8046a180)-a0 FE4E1D60 4266532192 (-28435104) "﨨`" :? @PsInitialSystemProcess //顯示System工作的KPEB FE4E1D60 4266532192 (-28435104) "﨨`" 即實現將新KPEB鏈插入已有鏈表尾 在插入KPEB後,系統就可以根據上面提供的頁目錄對工作進行調度(即新工作擁 有新的私有的工作空間) */ 0008:804ADD27 8B4DD0 MOV ECX,[EBP-30] 0008:804ADD2A C781A000000080A14680 MOV DWORD PTR [ECX+000000A0],8046A1 80 0008:804ADD34 8B4DD0 MOV ECX,[EBP-30] 0008:804ADD37 8981A4000000 MOV [ECX+000000A4],EAX 0008:804ADD3D 8B4DD0 MOV ECX,[EBP-30] 0008:804ADD40 81C1A0000000 ADD ECX,000000A0 ;是不是可以找出鏈狀 結構相對KPEB的偏移呢? 0008:804ADD46 8908 MOV [EAX],ECX 0008:804ADD48 8B45D0 MOV EAX,[EBP-30] 0008:804ADD4B 05A0000000 ADD EAX,000000A0 0008:804ADD50 A384A14680 MOV [8046A184],EAX 為了更直觀,上面的程式碼我只是按照系統執行流程列出(相對於實際的磁牒存放順序 ).其實系統在新增工作初,首先用ObCreateObject建立section內核對像(section對象並 不分配實體記憶體,Windows 2000 DDK Documentation中有詳細介紹),然後才有牽涉到頁目 錄與頁表,自己在分析程式碼時要特別注意,還有可以使用IDA對ntoskrnl.exe工作分析,畢 竟IDA對程式碼流程提供的比較清晰。至於以上所提的工作Context,系統KPEB雙向鏈表等請 參閱<<淺析Windows NT/2000環境切換>>,在那我已經進行了比較詳細的說明。 在深入分析Windows的有關記憶體操作的API(如VirtualAllocEx,CreateFileMapping, HeapAlloc等等)後,還可以發現很多其它方面重要的信息。如跟蹤ntoskrnl!NtCreateSe ction(Windows在裝入可執行文件與CreateFileMapping等都最終使用此函數)可發現Cop y On Write等機制是如何實現的等等.這些都留著你自己去找找了。Windows 2000支持多 個頁面文件(命名為Pagefile.sys),這牽涉到原型PTE(Sofice中用ProtoPTE表示),分頁文 件使用的PTE等等很多的機制,要分析之,還要對Windows 2000的分層驅動程式中的FSD有 很好的理解.簡單的說即文中只討論到PTE中的P位為1的情況。好了,還是那句話,以上分 析,如有錯誤,還望指點(tsu00@263.net)! 參考資料: 1.Jeffrey Richter << Programming Applications for Microsoft Windows,Fourth Edition >> 2.Intel Corp<< Intel Architecture Software Developer's Manual,Volume 3 >> 3.<< Undocumented Windows NT >>附帶源碼 4.Windows 2000 DDK Documentation |
送花文章: 3,
|