寫在前面
此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。
看此教程之前,問幾個問題,基礎知識儲備好了嗎?保護模式篇學會了嗎?練習做完了嗎?沒有的話就不要繼續了。
? 華麗的分割線 ?
核心線性地址管理概述
在開始線性地址管理之前,我們先從巨集觀上了解核心線性地址的劃分割槽域:
如下是系統的全域性變數:
可以看出微軟把作業系統的記憶體區域的用途劃分的十分清晰,當然你處於0環時,可以不用遵守這種約定,但最好不要與其衝突。如果自己寫個處於保護模式下的作業系統玩玩自己隨便定。
線性地址管理
我們在3環的程式,如果你對虛擬地址瞭解比較多的話一定會知道下面的表格:
分割槽 | x86 32位Windows |
---|---|
空指標賦值區 | 0x00000000 - 0x0000FFFF |
使用者模式區 | 0x00010000 - 0x7FFEFFFF |
64KB禁入區 | 0x7FFF0000 - 0x7FFFFFFF |
核心 | 0x80000000 - 0xFFFFFFFF |
一個程式不可能每一個線性地址都對應一個物理頁,如果要清楚地瞭解線性地址的使用,就必須有一個記錄,在EPROCESS
結構體中,有一個成員VadRoot
,如下所示:
kd> dt _EPROCESS
ntdll!_EPROCESS
……
+0x118 HardwareTrigger : Uint4B
+0x11c VadRoot : Ptr32 Void
+0x120 VadHint : Ptr32 Void
+0x124 CloneRoot : Ptr32 Void
+0x128 NumberOfPrivatePages : Uint4B
+0x12c NumberOfLockedPages : Uint4B
……
而這個VadRoot
的記錄是由二叉樹結構組織的,我們來看看它的結構:
kd> dt _MMVAD
nt!_MMVAD
+0x000 StartingVpn : Uint4B
+0x004 EndingVpn : Uint4B
+0x008 Parent : Ptr32 _MMVAD
+0x00c LeftChild : Ptr32 _MMVAD
+0x010 RightChild : Ptr32 _MMVAD
+0x014 u : __unnamed
+0x018 ControlArea : Ptr32 _CONTROL_AREA
+0x01c FirstPrototypePte : Ptr32 _MMPTE
+0x020 LastContiguousPte : Ptr32 _MMPTE
+0x024 u2 : __unnamed
裡面有兩個共用體型別,我翻了翻Win2000
洩露程式碼如下所示:
typedef struct _MMVAD {
ULONG_PTR StartingVpn;
ULONG_PTR EndingVpn;
struct _MMVAD *Parent;
struct _MMVAD *LeftChild;
struct _MMVAD *RightChild;
union {
ULONG_PTR LongFlags;
MMVAD_FLAGS VadFlags;
} u;
PCONTROL_AREA ControlArea;
PMMPTE FirstPrototypePte;
PMMPTE LastContiguousPte;
union {
ULONG LongFlags2;
MMVAD_FLAGS2 VadFlags2;
} u2;
union {
LIST_ENTRY List;
MMADDRESS_LIST Secured;
} u3;
union {
PMMBANKED_SECTION Banked;
PMMEXTEND_INFO ExtendedInfo;
} u4;
} MMVAD, *PMMVAD;
可以看出,上面的結構體比XP
多了兩個共用體,共用體的成員僅供參考。其中第一個共用體成員十分重要,它描述了大量主要的屬性,其結構體如下:
kd> dt _MMVAD_FLAGS
nt!_MMVAD_FLAGS
+0x000 CommitCharge : Pos 0, 19 Bits
+0x000 PhysicalMapping : Pos 19, 1 Bit
+0x000 ImageMap : Pos 20, 1 Bit
+0x000 UserPhysicalPages : Pos 21, 1 Bit
+0x000 NoChange : Pos 22, 1 Bit
+0x000 WriteWatch : Pos 23, 1 Bit
+0x000 Protection : Pos 24, 5 Bits
+0x000 LargePages : Pos 29, 1 Bit
+0x000 MemCommit : Pos 30, 1 Bit
+0x000 PrivateMemory : Pos 31, 1 Bit
下面我將會用實驗進行介紹。
實驗講解
我在虛擬機器開了一個記事本,輸入如下指令:
kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
……
Failed to get VadRoot
PROCESS 89d37da0 SessionId: 0 Cid: 018c Peb: 7ffd3000 ParentCid: 05e8
DirBase: 13a40260 ObjectTable: e1f4db50 HandleCount: 44.
Image: notepad.exe
kd> dt _EPROCESS 89d37da0
ntdll!_EPROCESS
……
+0x118 HardwareTrigger : 0
+0x11c VadRoot : 0x89d080a8 Void
+0x120 VadHint : 0x89e22630 Void
+0x124 CloneRoot : (null)
……
對於Windbg
來說,它提供了一個十分簡單檢視線性地址的記錄情況的指令,如下所示:
kd> !vad 0x89d080a8
VAD Level Start End Commit
89eb60e8 3 10 10 1 Private READWRITE
89cf10e8 2 20 20 1 Private READWRITE
89d310c8 5 30 3f 6 Private READWRITE
89f833e0 4 40 7f 18 Private READWRITE
89d372d0 3 80 82 0 Mapped READONLY Pagefile section, shared commit 0x3
89d350a8 4 90 91 0 Mapped READONLY Pagefile section, shared commit 0x2
89e22630 1 a0 19f 21 Private READWRITE
89cff0b8 4 1a0 1af 6 Private READWRITE
89e31380 3 1b0 1bf 0 Mapped READWRITE Pagefile section, shared commit 0x3
89d350d8 4 1c0 1d5 0 Mapped READONLY \WINDOWS\system32\unicode.nls
89cf50d8 2 1e0 220 0 Mapped READONLY \WINDOWS\system32\locale.nls
89cf70a8 4 230 270 0 Mapped READONLY \WINDOWS\system32\sortkey.nls
89d200a8 3 280 285 0 Mapped READONLY \WINDOWS\system32\sorttbls.nls
89d080a8 0 290 2d0 0 Mapped READONLY Pagefile section, shared commit 0x41
89d170d8 5 2e0 3a7 0 Mapped EXECUTE_READ Pagefile section, shared commit 0x3
89f68f20 6 3b0 3bf 8 Private READWRITE
89aeaf98 7 3c0 3c0 1 Private READWRITE
89abfd58 8 3d0 3d0 1 Private READWRITE
89d790d8 10 3e0 3e1 0 Mapped READONLY Pagefile section, shared commit 0x2
89d140d8 9 3f0 3f1 0 Mapped READONLY Pagefile section, shared commit 0x2
89d96330 10 400 40f 3 Private READWRITE
89d250e8 4 410 41f 8 Private READWRITE
89d100b8 3 420 42f 4 Private READWRITE
89d250b8 4 430 432 0 Mapped READONLY \WINDOWS\system32\ctype.nls
89dd71f8 2 440 47f 3 Private READWRITE
89d56260 5 480 582 0 Mapped READONLY Pagefile section, shared commit 0x103
89d280a8 4 590 88f 0 Mapped EXECUTE_READ Pagefile section, shared commit 0x1a
89de0f60 3 890 90f 1 Private READWRITE
89d060a8 5 910 95f 0 Mapped READONLY Pagefile section, shared commit 0x50
89d8ae80 4 960 960 0 Mapped READWRITE Pagefile section, shared commit 0x1
89d42ec0 6 970 9af 0 Mapped READWRITE Pagefile section, shared commit 0x10
89d42e90 5 9b0 9bd 0 Mapped READWRITE Pagefile section, shared commit 0xe
89d310e8 7 9c0 abf 123 Private READWRITE
89dd9e80 6 ad0 b4f 0 Mapped READWRITE Pagefile section, shared commit 0x7
89abfcd8 1 1000 1012 3 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\notepad.exe
89d280d8 7 58fb0 59179 9 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\AppPatch\AcGenral.dll
89d0e0d8 8 5adc0 5adf6 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\uxtheme.dll
89d100d8 6 5cc30 5cc55 20 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\shimeng.dll
89d2e248 7 62c20 62c28 1 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\lpk.dll
89d230d8 5 72f70 72f95 3 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\winspool.drv
89cf70d8 8 73640 7366d 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\MSCTFIME.IME
89d790a8 7 73fa0 7400a 16 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\usp10.dll
89dd93e0 8 74680 746cb 3 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\MSCTF.dll
89d0e0a8 6 759d0 75a7e 3 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\userenv.dll
89d200d8 7 76300 7631c 1 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\imm32.dll
89d080d8 4 76320 76366 4 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\comdlg32.dll
89d342d0 8 76990 76acc 8 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\ole32.dll
89cc50a8 7 76b10 76b39 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\winmm.dll
89d140a8 8 770f0 7717a 4 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\oleaut32.dll
89d170a8 6 77180 77282 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\WinSxS\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.2600.5512_x-ww_35d4ce83\comctl32.dll
89a865b8 8 77bb0 77bc4 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\msacm32.dll
89d0f0d8 9 77bd0 77bd7 1 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\version.dll
89d230a8 7 77be0 77c37 7 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\msvcrt.dll
89abfd18 8 77d10 77d9f 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\user32.dll
89cf50a8 5 77da0 77e48 5 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\advapi32.dll
89d590a8 6 77e50 77ee1 1 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\rpcrt4.dll
89d590d8 8 77ef0 77f38 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\gdi32.dll
89d010a8 9 77f40 77fb5 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\shlwapi.dll
89d270d8 7 77fc0 77fd0 1 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\secur32.dll
89d372a0 3 7c800 7c91d 5 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\kernel32.dll
89eb60b8 2 7c920 7c9b2 5 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\ntdll.dll
89cf60d8 5 7d590 7dd83 30 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\shell32.dll
89d9e908 4 7f6f0 7f7ef 0 Mapped EXECUTE_READ Pagefile section, shared commit 0x7
89d010d8 3 7ffa0 7ffd2 0 Mapped READONLY Pagefile section, shared commit 0x33
89abfc58 4 7ffd3 7ffd3 1 Private READWRITE
89ac1ac8 5 7ffdf 7ffdf 1 Private READWRITE
Total VADs: 66, average level: 6, maximum depth: 10
Total private commit: 0x161 pages (1412 KB)
Total shared commit: 0x21d pages (2164 KB)
其中VAD
就是MMVAD
結構體的地址,Level
就是二叉樹的層級,根節點是0,如果不懂請具體請自行學習二叉樹的相關知識。
我們們的物理頁大小都是4KB
,用十六進位制來表示的話就是0x1000
,所以上面的Start
和End
就是除掉0x1000
的結果。如果這兩個值相等,就只是使用一個物理頁。
上面還有一個Commit
,這個就是你使用VirtualAlloc
函式傳參時MEM_COMMIT
的物理頁個數。下面我們開始手動遍歷VAD
看看情況:
kd> dt _MMVAD 0x89d080a8
nt!_MMVAD
+0x000 StartingVpn : 0x290
+0x004 EndingVpn : 0x2d0
+0x008 Parent : (null)
+0x00c LeftChild : 0x89e22630 _MMVAD
+0x010 RightChild : 0x89abfcd8 _MMVAD
+0x014 u : __unnamed
+0x018 ControlArea : 0x89e7e540 _CONTROL_AREA
+0x01c FirstPrototypePte : 0xe15a69f0 _MMPTE
+0x020 LastContiguousPte : 0xe15a6bf0 _MMPTE
+0x024 u2 : __unnamed
上面的內容對應用!vad
的記錄:
VAD Level Start End Commit
89d080a8 0 290 2d0 0 Mapped READONLY Pagefile section, shared commit 0x41
StartingVpn
和EndingVpn
分別對應的是記錄中Start
和End
,Parent
就是二叉樹的父節點,LeftChild
和RightChild
分別對應二叉樹的左子節點和右子節點,我們來看看它的VadFlags
屬性:
kd> dx -id 0,0,805539a0 -r1 (*((ntkrnlpa!__unnamed *)0x89d080bc))
(*((ntkrnlpa!__unnamed *)0x89d080bc)) [Type: __unnamed]
[+0x000] LongFlags : 0x1000000 [Type: unsigned long]
[+0x000] VadFlags [Type: _MMVAD_FLAGS]
kd> dx -id 0,0,805539a0 -r1 (*((ntkrnlpa!_MMVAD_FLAGS *)0x89d080bc))
(*((ntkrnlpa!_MMVAD_FLAGS *)0x89d080bc)) [Type: _MMVAD_FLAGS]
[+0x000 (18: 0)] CommitCharge : 0x0 [Type: unsigned long]
[+0x000 (19:19)] PhysicalMapping : 0x0 [Type: unsigned long]
[+0x000 (20:20)] ImageMap : 0x0 [Type: unsigned long]
[+0x000 (21:21)] UserPhysicalPages : 0x0 [Type: unsigned long]
[+0x000 (22:22)] NoChange : 0x0 [Type: unsigned long]
[+0x000 (23:23)] WriteWatch : 0x0 [Type: unsigned long]
[+0x000 (28:24)] Protection : 0x1 [Type: unsigned long]
[+0x000 (29:29)] LargePages : 0x0 [Type: unsigned long]
[+0x000 (30:30)] MemCommit : 0x0 [Type: unsigned long]
[+0x000 (31:31)] PrivateMemory : 0x0 [Type: unsigned long]
CommitCharge
是幾,那個Commit
記錄的就是幾。有關線性地址的屬性有兩個,一個是Private
(私有),另一個就是Mapped
(對映)。由於我們示例的頁是對映的只讀記憶體,所以PrivateMemory
值為0,有關線性記憶體的屬性,也就是Protection
的值,我們先看看如下列舉:
#define MM_READONLY 1
#define MM_EXECUTE 2
#define MM_EXECUTE_READ 3
#define MM_READWRITE 4 // bit 2 is set if this is writable.
#define MM_WRITECOPY 5
#define MM_EXECUTE_READWRITE 6
#define MM_EXECUTE_WRITECOPY 7
這個屬性就介紹完了,我們再看看ImageMap
屬性,這個就是使用!vad
的資訊中有的記錄有Exe
這個字樣,如果ImageMap
為1,那麼就是映象檔案。
重要的成員介紹完後,我們繼續接著一個支線繼續遍歷:
kd> dt _MMVAD 0x89abfcd8
nt!_MMVAD
+0x000 StartingVpn : 0x1000
+0x004 EndingVpn : 0x1012
+0x008 Parent : 0x89d080a8 _MMVAD
+0x00c LeftChild : 0x89dd71f8 _MMVAD
+0x010 RightChild : 0x89eb60b8 _MMVAD
+0x014 u : __unnamed
+0x018 ControlArea : 0x89b1af50 _CONTROL_AREA
+0x01c FirstPrototypePte : 0xe1faebd8 _MMPTE
+0x020 LastContiguousPte : 0xfffffffc _MMPTE
+0x024 u2 : __unnamed
kd> dt _MMVAD 0x89eb60b8
nt!_MMVAD
+0x000 StartingVpn : 0x7c920
+0x004 EndingVpn : 0x7c9b2
+0x008 Parent : 0x89abfcd8 _MMVAD
+0x00c LeftChild : 0x89d372a0 _MMVAD
+0x010 RightChild : 0x89d010d8 _MMVAD
+0x014 u : __unnamed
+0x018 ControlArea : 0x89fad2d8 _CONTROL_AREA
+0x01c FirstPrototypePte : 0xe13e1b38 _MMPTE
+0x020 LastContiguousPte : 0xfffffffc _MMPTE
+0x024 u2 : __unnamed
kd> dt _MMVAD 0x89d010d8
nt!_MMVAD
+0x000 StartingVpn : 0x7ffa0
+0x004 EndingVpn : 0x7ffd2
+0x008 Parent : 0x89eb60b8 _MMVAD
+0x00c LeftChild : 0x89d9e908 _MMVAD
+0x010 RightChild : 0x89abfc58 _MMVAD
+0x014 u : __unnamed
+0x018 ControlArea : 0x89faa1f8 _CONTROL_AREA
+0x01c FirstPrototypePte : 0xe100f658 _MMPTE
+0x020 LastContiguousPte : 0xe100f7e8 _MMPTE
+0x024 u2 : __unnamed
kd> dt _MMVAD 0x89abfc58
nt!_MMVAD
+0x000 StartingVpn : 0x7ffd3
+0x004 EndingVpn : 0x7ffd3
+0x008 Parent : 0x89d010d8 _MMVAD
+0x00c LeftChild : (null)
+0x010 RightChild : 0x89ac1ac8 _MMVAD
+0x014 u : __unnamed
+0x018 ControlArea : (null)
+0x01c FirstPrototypePte : (null)
+0x020 LastContiguousPte : (null)
+0x024 u2 : __unnamed
可以看到最後一個成員沒有了左子樹,說明到頭了。為了更好的講解上面的成員,我用這個記錄:
VAD Level Start End Commit
89eb60b8 2 7c920 7c9b2 5 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\ntdll.dll
然後在Windbg
如下所示:
kd> dt _MMVAD 89eb60b8
nt!_MMVAD
+0x000 StartingVpn : 0x7c920
+0x004 EndingVpn : 0x7c9b2
+0x008 Parent : 0x89abfcd8 _MMVAD
+0x00c LeftChild : 0x89d372a0 _MMVAD
+0x010 RightChild : 0x89d010d8 _MMVAD
+0x014 u : __unnamed
+0x018 ControlArea : 0x89fad2d8 _CONTROL_AREA
+0x01c FirstPrototypePte : 0xe13e1b38 _MMPTE
+0x020 LastContiguousPte : 0xfffffffc _MMPTE
+0x024 u2 : __unnamed
kd> dx -id 0,0,805539a0 -r1 (*((ntkrnlpa!__unnamed *)0x89eb60cc))
(*((ntkrnlpa!__unnamed *)0x89eb60cc)) [Type: __unnamed]
[+0x000] LongFlags : 0x7100005 [Type: unsigned long]
[+0x000] VadFlags [Type: _MMVAD_FLAGS]
kd> dx -id 0,0,805539a0 -r1 (*((ntkrnlpa!_MMVAD_FLAGS *)0x89eb60cc))
(*((ntkrnlpa!_MMVAD_FLAGS *)0x89eb60cc)) [Type: _MMVAD_FLAGS]
[+0x000 (18: 0)] CommitCharge : 0x5 [Type: unsigned long]
[+0x000 (19:19)] PhysicalMapping : 0x0 [Type: unsigned long]
[+0x000 (20:20)] ImageMap : 0x1 [Type: unsigned long]
[+0x000 (21:21)] UserPhysicalPages : 0x0 [Type: unsigned long]
[+0x000 (22:22)] NoChange : 0x0 [Type: unsigned long]
[+0x000 (23:23)] WriteWatch : 0x0 [Type: unsigned long]
[+0x000 (28:24)] Protection : 0x7 [Type: unsigned long]
[+0x000 (29:29)] LargePages : 0x0 [Type: unsigned long]
[+0x000 (30:30)] MemCommit : 0x0 [Type: unsigned long]
[+0x000 (31:31)] PrivateMemory : 0x0 [Type: unsigned long]
如果仔細的話有的後面會跟著檔案路徑,而這個名稱有是在哪裡記錄著呢?它在ControlArea
這個屬性中,我們就拿著上面剛舉例的繼續:
kd> dx -id 0,0,805539a0 -r1 ((ntkrnlpa!_CONTROL_AREA *)0x89fad2d8)
((ntkrnlpa!_CONTROL_AREA *)0x89fad2d8) : 0x89fad2d8 [Type: _CONTROL_AREA *]
[+0x000] Segment : 0xe13e1af8 [Type: _SEGMENT *]
[+0x004] DereferenceList [Type: _LIST_ENTRY]
[+0x00c] NumberOfSectionReferences : 0x1 [Type: unsigned long]
[+0x010] NumberOfPfnReferences : 0x53 [Type: unsigned long]
[+0x014] NumberOfMappedViews : 0x15 [Type: unsigned long]
[+0x018] NumberOfSubsections : 0x5 [Type: unsigned short]
[+0x01a] FlushInProgressCount : 0x0 [Type: unsigned short]
[+0x01c] NumberOfUserReferences : 0x16 [Type: unsigned long]
[+0x020] u [Type: __unnamed]
[+0x024] FilePointer : 0x89fbe9a0 : "\WINDOWS\system32\ntdll.dll" - Device for "\FileSystem\Ntfs" [Type: _FILE_OBJECT *]
[+0x028] WaitingForDeletion : 0x0 [Type: _EVENT_COUNTER *]
[+0x02c] ModifiedWriteCount : 0x0 [Type: unsigned short]
[+0x02e] NumberOfSystemCacheViews : 0x0 [Type: unsigned short]
看到FilePointer
這個成員了嗎?而這個又是_FILE_OBJECT
結構體指標,具體的可以在Windbg
檢視檔名到底儲存在哪裡。
本節練習
本節的答案將會在下一節進行講解,務必把本節練習做完後看下一個講解內容。不要偷懶,實驗是學習本教程的捷徑。
俗話說得好,光說不練假把式,如下是本節相關的練習。如果練習沒做好,就不要看下一節教程了,越到後面,不做練習的話容易夾生了,開始還明白,後來就真的一點都不明白了。本節練習不多,請保質保量的完成。
1️⃣ 寫程式碼實現需輸入程式的PID
來獲取列印其VAD
樹。
下一篇
記憶體管理篇——私有記憶體和對映記憶體