深入瞭解Intel保護模式
學習逆向工程也快一年的時間了,從開始的16位真實模式下的記憶體定址模型到32位下保護模式記憶體的模型,真實模式下的較為簡單,段地址*16+偏移地址就是定址的記憶體,但是保護模式下就遠遠沒有這麼簡單了。很簡單的一個例子,windows下支援多程序,並且程序之間是相互隔離的,不允許隨意的將一個程序的資料寫入到另外一個程序的空間中,並且每個時刻CPU只允許執行一個程式,何以為見呢?
開啟OD,分別用OD載入兩個程式,而且是兩個不同的程序,就看程式碼段的資料,假設我們檢視0x0041F69A處的指令,我們發現兩個程序的資料是不一樣的,這裡也會讓我們感到奇怪,為什麼兩個程序處在相同地址處的資料不同呢?當然我們知道是保護機制中隔離在作怪,但是他是如何做到隔離的呢?為什麼不能隨意的向另外一個程序的空間中寫入值呢?所以就這幾天的學習情況對一下幾個問題進行討論:
1.OD或者其他偵錯程式中的地址到底是個什麼樣的地址?虛擬地址,線性地址和實體地址間的聯絡又是什麼?
2.Windows系統是如何做到每個程序地址空間的隔離的?
3.傳說中的GDT,LDT表的作用?
4.虛擬地址是如何對映到實體地址的?
5.如何通過虛擬地址找到程序實際所在的實體地址?
6.如何通過實驗來驗證windows中某個程序中的一個虛擬地址就是實體記憶體中的地址呢?
7.如何通過修改對映關係來達到在保護模式下,兩個不同的程序可以寫入到對方程序的物理空間中呢?
首先還是得簡單介紹一下保護模式下的兩種保護機制:分段和分頁。這個應該都比較好理解,將記憶體中的資料進行分段,不同型別的資料處於不同的段中,比如程式碼段就處於程式碼段的記憶體中,資料就處於資料段的程式碼中等等。對於分頁呢,我的理解是,將記憶體按照一定的大小劃分區域,比如按照4KB大小,將記憶體分為N塊,每塊就稱為是一頁。不知道這麼理解是不是正確的。
然後一個比較不同的方面就是在保護模式下,段暫存器(CS,DS,ES,SS,FS,GS)不再作為定址用,因為只有16位,而且每個暫存器都是32位的,足以進行定址的操作了。當然,這裡段暫存器我們也稱為段選擇器。這裡,段選擇器就不再像真實模式下進行定址了,而是為一個索引,索引一個二進位制的結構,這個結構中包含了每一段的細節,比如說程式碼段具有哪些許可權,什麼許可權的程序可以訪問它(Ring3 Or Ring0)。當然這個結構我們稱為段描述表,其中的項稱為段描述符。
當然啦這樣的描述符表就是全域性描述符表(GDT)和區域性描述符表(LDT)了。也就是傳說中的GDT和LDT了,當然每個系統中只存在一個GDT,並且是必須存在的,而LDT是可選擇的,並且每個任務都有一個不同的LDT,這次我們主要集中在GDT上。下面看一下段選擇器的結構及GDT的結構:
上面圖還是非常清晰的,我們可以看到段選擇器包含了當前程序請求的特權,位0表示當前程序請求許可權,即是ring0還是ring3,而且一般也只用到這兩個許可權,位2表示的是使用GDT還是LDT來存放段描述符的,位3到位15就是存放索引的位置。
GDT存放在哪裡呢?Intel中有個48位的暫存器GDTR,存放的就是GDTR的地址,應該是從系統初始化的時候就開始存在了,並且這個結構也只有特定的許可權程序進行才能進行讀寫操作,GDTR中,低16位表示的是GDT的大小(以位元組為單位),其餘32位儲存了GDT的起始線性地址,即第一個位元組的線性地址。然後就是段描述符,是如下的結構:
是一個64位的結構,最上面的結構是高32位,下面的結構是低32位的結構,其中主要的幾個位置就DPL,表示引用段的許可權,其他位的作用上圖描述的非常清楚,不再多說。下面我們就來看看GDT的結構,我在xp sp3下作的實驗,偵錯程式核心模式的,否則看不到GDTR及其結構,首先在虛擬機器中用OD載入一個程式,我們來看看每個段暫存器的值是多少:
換了幾個不同的程式,發現在ring3層的程式,CS為0x1B,SS為0x23,ring0下就沒有繼續看了,我們拿CS做例子吧,將CS做分解:0000000000011 0 11
第1,2位為3(0B11),第2位為0,最高位為3,可以看出表示的是引用的段許可權為最低,並且存放在GDT中,那我們就看看GDT長啥樣子的:
1 |
kd> r gdtr |
2 |
gdtr=8003f000 |
3 |
kd> r gdtl |
4 |
gdtl=000003ff |
這裡使用gdtr來表示GDT手位元組地址,gdtl表示的是GDT中索引的個數,下面我們就來看看GDT表中的資料,如果單單使用dd檢視的話太不直觀,windbg中我們可以使用dg命令來檢視GDT中的結構和值,第一個引數為索引開始值,第二個引數為要檢視索引結束值:
1 |
kd> dg 0 3f8 |
2 |
P Si Gr Pr Lo |
3 |
Sel Base Limit Type l ze an es ng Flags |
4 |
---- -------- -------- ---------- - -- -- -- -- -------- |
5 |
0000 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000 |
6 |
0008 00000000 ffffffff Code RE 0 Bg Pg P Nl 00000c9a |
7 |
0010 00000000 ffffffff Data RW 0 Bg Pg P Nl 00000c92 |
8 |
0018 00000000 ffffffff Code RE 3 Bg Pg P Nl 00000cfa |
9 |
0020 00000000 ffffffff Data RW 3 Bg Pg P Nl 00000cf2 |
10 |
0028 80042000 000020ab TSS32 Busy 0 Nb By P Nl 0000008b |
11 |
0030 ffdff000 00001fff Data RW 0 Bg Pg P Nl 00000c92 |
12 |
0038 00000000 00000fff Data RW Ac 3 Bg By P Nl 000004f3 |
13 |
0040 00000400 0000ffff Data RW 3 Nb By P Nl 000000f2 |
14 |
0048 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000 |
15 |
0050 8054af00 00000068 TSS32 Avl 0 Nb By P Nl 00000089 |
16 |
0058 8054af68 00000068 TSS32 Avl 0 Nb By P Nl 00000089 |
17 |
0060 00022f40 0000ffff Data RW 0 Nb By P Nl 00000092 |
18 |
0068 000b8000 00003fff Data RW 0 Nb By P Nl 00000092 |
19 |
0070 ffff7000 000003ff Data RW 0 Nb By P Nl 00000092 |
20 |
0078 80400000 0000ffff Code RE 0 Nb By P Nl 0000009a |
21 |
0080 80400000 0000ffff Data RW 0 Nb By P Nl 00000092 |
22 |
0088 00000000 00000000 Data RW 0 Nb By P Nl 00000092 |
23 |
0090 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000 |
24 |
0098 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000 |
25 |
00A0 821b2350 00000068 TSS32 Avl 0 Nb By P Nl 00000089 |
26 |
00A8 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000 |
27 |
00B0 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000 |
28 |
00B8 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000 |
29 |
00C0 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000 |
30 |
00C8 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000 |
31 |
00D0 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000 |
32 |
00D8 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000 |
33 |
00E0 f871a000 0000ffff Code RE Ac 0 Nb By P Nl 0000009f |
34 |
00E8 00000000 0000ffff Data RW 0 Nb By P Nl 00000092 |
35 |
00F0 804fb688 000003b7 Code EO 0 Nb By P Nl 00000098 |
36 |
00F8 00000000 0000ffff Data RW 0 Nb By P Nl 00000092 |
37 |
0100 f8397400 0000ffff Data RW Ac 0 Bg By P Nl 00000493 |
38 |
0108 f8397400 0000ffff Data RW Ac 0 Bg By P Nl 00000493 |
39 |
0110 f8397400 0000ffff Data RW Ac 0 Bg By P Nl 00000493 |
40 |
0118 00008003 0000f120 <Reserved> 0 Nb By Np Nl 00000000 |
我們檢視CS和SS所在段的資訊:
1 |
kd> dg 1b |
2 |
P Si Gr Pr Lo |
3 |
Sel Base Limit Type l ze an es ng Flags |
4 |
---- -------- -------- ---------- - -- -- -- -- -------- |
5 |
001B 00000000 ffffffff Code RE 3 Bg Pg P Nl 00000cfa |
6 |
kd> dg 23 |
7 |
P Si Gr Pr Lo |
8 |
Sel Base Limit Type l ze an es ng Flags |
9 |
---- -------- -------- ---------- - -- -- -- -- -------- |
10 |
0023 00000000 ffffffff Data RW 3 Bg Pg P Nl 00000cf2 |
我們可以看出這裡第五列為段的訪問許可權,和我們手工的出來的結果相同,當然這裡我們可以看出來,這裡我們根本沒有分段,環0和環3的起始結束地址都是從(00000000-ffffffff),所有的段描述符都指向同一個段,所以windows並沒有有使用Intel硬體平臺為其提供的所有附屬專案。所以我猜測windows下虛擬地址和線性地址的值是相同的。而且我也發現其實GDT在程序間隔離也沒起到太大的作用,因為這裡GDT也只是起到檢視段的許可權的作用,當然也有門的一些資訊,我們這裡不討論門,以上也都是我個人的理解,如果有誤也希望各位進行指出。
下面就是分頁保護,這個就和程序地址間的隔離關係就比較大了,我們都知道,32位下每個程序分配的空間為4GB,當然這個4GB只是程序看到的,感覺到的,是虛擬地址,當然這個地址也是線性地址,並不是都能使用,比如說我們看到指令的地址都處於00400000開始位置,如果到00200000的話還能訪問嗎?基本上是不行的,你可以用OD嘗試一下,這裡的位置是不可訪問的,執行到此位置就會發生異常,當然如果都能訪問的話那豈不是相當恐怖的一件事情!你一個程序就全部佔了4GB空間,你讓其他程序怎麼辦?而且你的實體記憶體很大情況下也不會有4GB的,像我的虛擬機器也就只有512MB呢。猜想00200000這個地址應該是被對映到別的程序的空間裡面了,是很有可能的。當然對映絕非那麼簡單的對映。
啟動分頁後,線性地址空間被分為幾個固定長度的儲存塊,這些塊被稱為頁(大小可以是4KB,2MB或者4MB),當然我們實際中使用的頁大小都為4KB,當然如果不啟動分頁的話線性地址就是實體地址,但是啟動後就不一樣了,這個線性地址不再是一個簡單的地址了,這32位被分成了不同的部分,每個部分有不同的含義,如下:
一個線性地址被分為3個部分,第一個部分(0-11位)才是偏移,第三個部分(22-31位)指定了頁目錄的陣列結構的項,這些項被稱為頁目錄項(PDE),並且頁目錄的第一個位元組地址,即其開始位置的實體地址(不是線性地址)存放在CR3暫存器中,當然如果都是線性地址的話那還怎麼到真實的記憶體中查詢值呢?由於索引一共就10位,所以一個頁目錄最多儲存1024個PDE,每個PDE儲存了頁表的二級陣列結構的起始實體地址(不是線性地址),即PDE儲存了頁表第一個位元組的實體地址。第二個部分(12-21位)指定了頁表中特定的項,頁表中的項被順序排列成陣列,稱為頁表項(PTE),當然,一個頁表最多能儲存1024個PTE。當然,每個PTE儲存的就是記憶體頁的第一個實體地址,如果將第一個部分(0-11位)的值和PTE提供的物理基址相加,就可以得到實體記憶體中第一個位元組的地址了。具體關係見下面的圖:
要注意的是CR3,PDE和PTE中的值都是實體地址!通過CR3來查詢程序頁目錄的實體地址,所以存在如下關係:CR3相當於我的電腦,PDE相當於根目錄,PTE相當於磁碟(C,D,E),就是一個索引的關係,通過索引我們就可以找到線性地址所對應的實體地址了,下面看看PDE和PTE的結構:
如果要分析記憶體保護的話,重要的幾項就是U/S標誌和W標誌。U/S定義了兩個基於頁的不同特權級:使用者和超級使用者。如果該位被清0,則PTE指向的頁(或在給定PDE之下的頁被分配為超級使用者許可權)。W標誌位用於指明一個頁或者一組頁是隻讀還是可寫,如果W位標誌被置位,表明該頁(或該頁組)可寫可讀。
但是一般情況下,我們記憶體是開啟PAE分頁的,CR4暫存器中有個PAE標誌位,如果開啟電話那麼線性地址就被分為4個部分而不是3個部分,具體見下面的圖,PAE的效果就是可以將處理器訪問的實體記憶體擴充到4GB以上,使可用的地址線達到52條:
PDPT索引就是頁目錄指標表(PDPT),共兩位,一共可以有4個數組,其中的4個數組被稱為PDPTE。當然此時分頁的暫存器和不開啟PAE情況下作用也是相同的,CR3指向PDPT的實體地址,當然此時CR3並不是儲存實體地址所有52為,因為很多位都為0,所以沒必要所有都儲存進去,尋找實體地址的過程如下:
這裡我們發現將線性地址轉換為實體地址的過程和不開啟PAE時候的情況是基本上相同的,並且如果開啟PAE分頁,那麼PDPTE,PDE和PTE都是64位的,並且標誌位幾乎完全相同,最重要的在於其實實體地址在大小上是可變的,而且根據當前處理器可用的地址線數量進行變化:
當然重要的還是CR3暫存器,CR3暫存器儲存頁目錄表首位元組的實體地址。如果每個程序都有自己CR3副本,並且把該副本作為核心維護排程上下文的一部分,則兩個程序完全會出現擁有相同的線性地址,就像我們OD除錯的那樣,但是最終對映到的實體地址一定是不相同的!不知道在哪看到的,程序切換的時候CR3的值也發生相應的改變,這樣CPU就可以切換到不同的程序空間中了,因為每個程序都是具有自己的空間,而且相互隔離的。還有一個CR0暫存器,其中的WP(防寫)如果置為的話那麼超級程式碼也無法寫入只讀的保護區域,需要將保護位置為0才行,比如ssdt表,如果我們直接在核心中修改其地址的話肯定會藍屏的,需要將CR0的WP位變為0,下面為這些控制暫存器的結構圖:
結構還是相當清晰的,還有要注意的是,如果開啟PAE分頁的話,CR3暫存器的位會發生相應的改變:
這裡我們發現,PDPT的地址只使用了27位,那如何表示一個52位的地址呢?其實很簡單,我們只需要將後面沒使用的位全部填充為0就可以了。在不開啟PAE分頁的情況下,PTE的最高那20位表示的為頁基址,比如0x12345,那麼後面的位我們可以看做包含著0,即0x12345[0][0][0],如果不含隱式0,這個地址有時稱作頁幀號(PNF),頁幀表示無力記憶體中的一個區域,用於存需要佔用實體記憶體的頁。
windows下每個程序都分配了一個自己專用的CR3控制暫存器的值,並且CR3控制暫存器有頁目錄20位的PFN,所以每個程序都有自己的頁目錄,相關的CR3值儲存在程序KPROESS的DirectoryTableBase這個欄位中,KPROCESS是EPROCESS的子結構,什麼是子結構呢?這裡表示KPROCESS和EPROCESS部分有重疊:
1 |
kd> !process 0 0 |
2 |
**** NT ACTIVE PROCESS DUMP **** |
3 |
PROCESS 821b9830 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 |
4 |
DirBase: 00b18000 ObjectTable: e1000c98 HandleCount: 279. |
5 |
Image: System |
6 |
7 |
PROCESS 81c57760 SessionId: none Cid: 0224 Peb: 7ffd4000 ParentCid: 0004 |
8 |
DirBase: 08c40020 ObjectTable: e13f8fb8 HandleCount: 19. |
9 |
Image: smss.exe |
10 |
11 |
PROCESS 81fe6648 SessionId: 0 Cid: 0264 Peb: 7ffd5000 ParentCid: 0224 |
12 |
DirBase: 08c40040 ObjectTable: e1524858 HandleCount: 344. |
13 |
Image: csrss.exe |
14 |
15 |
PROCESS 8211d020 SessionId: 0 Cid: 027c Peb: 7ffd6000 ParentCid: 0224 |
16 |
DirBase: 08c40060 ObjectTable: e1516968 HandleCount: 509. |
17 |
Image: winlogon.exe |
18 |
19 |
PROCESS 820fdae0 SessionId: 0 Cid: 02ac Peb: 7ffdb000 ParentCid: 027c |
20 |
DirBase: 08c40080 ObjectTable: e17ea610 HandleCount: 266. |
21 |
Image: services.exe |
22 |
23 |
PROCESS 820bed78 SessionId: 0 Cid: 02c8 Peb: 7ffdf000 ParentCid: 027c |
24 |
DirBase: 08c400c0 ObjectTable: e17eae98 HandleCount: 331. |
25 |
Image: lsass.exe |
26 |
27 |
PROCESS 81f4d770 SessionId: 0 Cid: 0368 Peb: 7ffd8000 ParentCid: 02ac |
28 |
DirBase: 08c400e0 ObjectTable: e1a81188 HandleCount: 25. |
29 |
Image: vmacthlp.exe |
30 |
31 |
PROCESS 81cd0ca8 SessionId: 0 Cid: 0374 Peb: 7ffd9000 ParentCid: 02ac |
32 |
DirBase: 08c40100 ObjectTable: e17509d8 HandleCount: 199. |
33 |
Image: svchost.exe |
34 |
35 |
PROCESS 81cb5a98 SessionId: 0 Cid: 03c4 Peb: 7ffdd000 ParentCid: 02ac |
36 |
DirBase: 08c40120 ObjectTable: e1aceaa0 HandleCount: 252. |
37 |
Image: svchost.exe |
38 |
39 |
PROCESS 81c57da0 SessionId: 0 Cid: 0420 Peb: 7ffd3000 ParentCid: 02ac |
40 |
DirBase: 08c40140 ObjectTable: e1ac9c08 HandleCount: 1137. |
41 |
Image: svchost.exe |
42 |
43 |
PROCESS 81c7e4d8 SessionId: 0 Cid: 0460 Peb: 7ffd8000 ParentCid: 02ac |
44 |
DirBase: 08c40160 ObjectTable: e1ae2328 HandleCount: 71. |
45 |
Image: svchost.exe |
46 |
47 |
PROCESS 81cf98e0 SessionId: 0 Cid: 047c Peb: 7ffdc000 ParentCid: 02ac |
48 |
DirBase: 08c40180 ObjectTable: e16ed958 HandleCount: 203. |
49 |
Image: svchost.exe |
50 |
51 |
PROCESS 821001c0 SessionId: 0 Cid: 05e8 Peb: 7ffd4000 ParentCid: 05c0 |
52 |
DirBase: 08c401e0 ObjectTable: e1481e00 HandleCount: 418. |
53 |
Image: explorer.exe |
54 |
55 |
PROCESS 81fcb5e8 SessionId: 0 Cid: 0638 Peb: 7ffd9000 ParentCid: 02ac |
56 |
DirBase: 08c40200 ObjectTable: e1524728 HandleCount: 138. |
57 |
Image: spoolsv.exe |
58 |
59 |
PROCESS 820f4da0 SessionId: 0 Cid: 0718 Peb: 7ffd6000 ParentCid: 05e8 |
60 |
DirBase: 08c401a0 ObjectTable: e17cd9a0 HandleCount: 135. |
61 |
Image: vmtoolsd.exe |
62 |
63 |
PROCESS 81f60c08 SessionId: 0 Cid: 0740 Peb: 7ffd9000 ParentCid: 05e8 |
64 |
DirBase: 08c40260 ObjectTable: e2046a38 HandleCount: 77. |
65 |
Image: ctfmon.exe |
66 |
67 |
PROCESS 81c4b650 SessionId: 0 Cid: 00b4 Peb: 7ffda000 ParentCid: 02ac |
68 |
DirBase: 08c40220 ObjectTable: e1ff58c0 HandleCount: 275. |
69 |
Image: vmtoolsd.exe |
70 |
71 |
PROCESS 81e23a20 SessionId: 0 Cid: 040c Peb: 7ffd3000 ParentCid: 02ac |
72 |
DirBase: 08c40300 ObjectTable: e14b3080 HandleCount: 99. |
73 |
Image: TPAutoConnSvc.exe |
74 |
75 |
PROCESS 8202ba80 SessionId: 0 Cid: 0694 Peb: 7ffdc000 ParentCid: 02ac |
76 |
DirBase: 08c40320 ObjectTable: e1e1a2a8 HandleCount: 107. |
77 |
Image: alg.exe |
78 |
79 |
PROCESS 81ac2da0 SessionId: 0 Cid: 04ec Peb: 7ffdc000 ParentCid: 0420 |
80 |
DirBase: 08c40340 ObjectTable: e1d4f8f0 HandleCount: 39. |
81 |
Image: wscntfy.exe |
82 |
83 |
PROCESS 8204f410 SessionId: 0 Cid: 06ec Peb: 7ffde000 ParentCid: 040c |
84 |
DirBase: 08c40360 ObjectTable: e1e93e40 HandleCount: 71. |
85 |
Image: TPAutoConnect.exe |
86 |
87 |
PROCESS 81ce5650 SessionId: 0 Cid: 05b4 Peb: 7ffde000 ParentCid: 0420 |
88 |
DirBase: 08c402e0 ObjectTable: e14f6820 HandleCount: 142. |
89 |
Image: wuauclt.exe |
90 |
91 |
PROCESS 81aed818 SessionId: 0 Cid: 01b0 Peb: 7ffda000 ParentCid: 05e8 |
92 |
DirBase: 08c40240 ObjectTable: e1a680e8 HandleCount: 60. |
93 |
Image: ?á°????a[LCG].exe |
94 |
95 |
PROCESS 820f1340 SessionId: 0 Cid: 0254 Peb: 7ffd6000 ParentCid: 05e8 |
96 |
DirBase: 08c400a0 ObjectTable: e12ac518 HandleCount: 15. |
97 |
Image: monitor.exe |
這個命令顯示了系統中所有活動程序的列表,!process命令用於顯示一個或者多個程序的資訊,第一個引數一般為程序ID或者是分配給程序的EPROCESS塊的16進位制資訊。我們看看打印出的結果,DirBase這個變量表示的就是儲存在CR3暫存器中的實體地址,PROCESS後面的16進位制數表示的是程序EPROCESS的線性地址,我們可以檢視一下monitor.exe這個程序的EPROCESS和KPROCESS值:
1 |
kd> dt nt!_EPROCESS 820f1340 |
2 |
+0x000 Pcb : _KPROCESS |
3 |
+0x06c ProcessLock : _EX_PUSH_LOCK |
4 |
+0x070 CreateTime : _LARGE_INTEGER 0x1d0c299`b24158da |
5 |
+0x078 ExitTime : _LARGE_INTEGER 0x0 |
6 |
+0x080 RundownProtect : _EX_RUNDOWN_REF |
7 |
+0x084 UniqueProcessId : 0x00000254 Void |
8 |
+0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x8055b158 - 0x81aed8a0 ] |
9 |
+0x090 QuotaUsage : [3] 0x848 |
10 |
+0x09c QuotaPeak : [3] 0x870 |
11 |
+0x0a8 CommitCharge : 0xb5 |
12 |
+0x0ac PeakVirtualSize : 0x2168000 |
13 |
+0x0b0 VirtualSize : 0x1a7e000 |
14 |
+0x0b4 SessionProcessLinks : _LIST_ENTRY [ 0xf8bb6014 - 0x81aed8cc ] |
15 |
+0x0bc DebugPort : 0x81aee880 Void |
16 |
+0x0c0 ExceptionPort : 0xe13e20f8 Void |
17 |
+0x0c4 ObjectTable : 0xe12ac518 _HANDLE_TABLE |
18 |
+0x0c8 Token : _EX_FAST_REF |
19 |
kd> dt nt!_KPROCESS 820f1340 |
20 |
+0x000 Header : _DISPATCHER_HEADER |
21 |
+0x010 ProfileListHead : _LIST_ENTRY [ 0x820f1350 - 0x820f1350 ] |
22 |
+0x018 DirectoryTableBase : [2] 0x8c400a0 |
23 |
+0x020 LdtDescriptor : _KGDTENTRY |
24 |
+0x028 Int21Descriptor : _KIDTENTRY |
25 |
+0x030 IopmOffset : 0x20ac |
26 |
+0x032 Iopl : 0
'' |
27 |
+0x033 Unused : 0
'' |
28 |
+0x034 ActiveProcessors : 0 |
29 |
+0x038 KernelTime : 0x16 |
30 |
+0x03c UserTime : 1 |
31 |
+0x040 ReadyListHead : _LIST_ENTRY [ 0x820f1380 - 0x820f1380 ] |
32 |
+0x048 SwapListEntry : _SINGLE_LIST_ENTRY |
33 |
+0x04c VdmTrapcHandler : (null)
|
34 |
+0x050 ThreadListHead : _LIST_ENTRY [ 0x82033b78 - 0x82033b78 ] |
35 |
+0x058 ProcessLock : 0 |
36 |
+0x05c Affinity : 1 |
37 |
+0x060 StackCount : 1 |
38 |
+0x062 BasePriority : 8
'' |
39 |
+0x063 ThreadQuantum : 6
'' |
40 |
+0x064 AutoAlignment : 0
'' |
41 |
+0x065 State : 0
'' |
42 |
+0x066 ThreadSeed : 0
'' |
43 |
+0x067 DisableBoost : 0
'' |
44 |
+0x068 PowerState : 0
'' |
45 |
+0x069 DisableQuantum : 0
'' |
46 |
+0x06a IdealNode : 0
'' |
47 |
+0x06b Flags : _KEXECUTE_OPTIONS |
48 |
+0x06b ExecuteOptions : 0x32
'2' |
dt表示的就是檢視某個結構,最後的引數為要檢視EPROCESS或者KPROCESS的地址,之所以稱為子結構就是因為兩個結構是重合的!EPROCESS的第一個欄位就是KPROCESS,我們可以看見其中DirectoryTableBase的值就是DirBase的值。
好的,下面我們就來做幾個實驗:
實驗一
分別使用手工和windbg的方式將某個虛擬地址轉換為實體地址,使用windbg直接檢視實體記憶體中的內容,隨後直接修改實體地址中的內容,看看虛擬地址中的資料是否發生變化。同時使用除錯工具直接定位實體記憶體的方法。
首先我們檢視一下CR4暫存器的值,使用.formats命令可以檢視到其每位的內容:
1 |
kd> .formats cr4 |
2 |
Evaluate expression: |
3 |
Hex: 000006f9 |
4 |
Decimal: 1785 |
5 |
Octal: 00000003371 |
6 |
Binary: 00000000 00000000 00000110 11111001 |
7 |
Chars: .... |
8 |
Time: Thu Jan 01 08:29:45 1970 |
9 |
Float: low 2.50132e-042 high 0 |
10 |
Double: 8.81907e-321 |
我們可以看到,這裡第5位為1,表示開啟PAE分頁,我們用OD附加我們經常使用的monitor.exe這個程式,載入sys時候經常用到,這裡入口如下:
我們可以看到虛擬地址為0041F69A,這個也就是線性地址了,在windows下,那麼我們如何得到其實體地址呢?按照上面的PDPT轉換過程,首先我們找到程序頁面的實體地址:
1 |
kd> !process 820f1340 |
2 |
PROCESS 820f1340 SessionId: 0 Cid: 0254 Peb: 7ffd6000 ParentCid: 05e8 |
3 |
DirBase: 08c400a0 ObjectTable: e12ac518 HandleCount: 15. |
4 |
Image: monitor.exe |
5 |
VadRoot 81b99200 Vads 53 Clone 0 Private 114. Modified 0. Locked 0. |
6 |
DeviceMap e152fe18 |
7 |
Token e10c7568 |
8 |
........................................................................................ |
1.轉換線性地址並得到其資訊
這裡我們可以看到,頁目錄指標表實體地址為08c400a0,我們來將0041F69A這個虛擬地址進行分解:(00 000000010 000011111 011010011010)
得到了線性地址的四個部分,PDPT頁目錄指標索引號為0,9位頁目錄索引為2(000000010),9位頁表索引為31(000011111),12位物理頁偏移量為1690(011010011010)。
2.定位頁目錄指標表並獲取頁目錄表物理頁地址
我們在dd命令前加上一個!表示後面的引數是實體地址,即檢視實體地址中的內容:
1 |
kd> ! dd
08c400a0 |
2 |
# 8c400a0 0d21e001 00000000 1ae9f001 00000000 |
3 |
# 8c400b0 12be0001 00000000 00c5d001 00000000 |
4 |
# 8c400c0 0a266001 00000000 0a267001 00000000 |
5 |
# 8c400d0 0a268001 00000000 0a265001 00000000 |
6 |
# 8c400e0 0a8b2001 00000000 0a8b3001 00000000 |
7 |
# 8c400f0 0a8b4001 00000000 0a8b1001 00000000 |
8 |
# 8c40100 0aa82001 00000000 0aa83001 00000000 |
9 |
# 8c40110 0aa44001 00000000 0aa81001 00000000 |
由於我們的頁目錄指標索引號為0,所以頁目錄的地址為0d21e001,但是我們只用到了其中的20位,所以頁目錄表實體地址的首地址為0d21e000