??此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我。
你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。
??看此教程之前,问几个问题,基础知识储备好了吗?保护模式篇学会了吗?练习做完了吗?没有的话就不要继续了。
?
?? 华丽的分割线 ??
?
??在开始线性地址管理之前,我们先从宏观上了解内核线性地址的划分区域:

??如下是系统的全局变量:

??可以看出微软把操作系统的内存区域的用途划分的十分清晰,当然你处于0环时,可以不用遵守这种约定,但最好不要与其冲突。如果自己写个处于保护模式下的操作系统玩玩自己随便定。
??我们在3环的程序,如果你对虚拟地址了解比较多的话一定会知道下面的表格:
| 分区 | x86 32位Windows |
|---|---|
| 空指针赋值区 | 0x00000000 - 0x0000FFFF |
| 用户模式区 | 0x00010000 - 0x7FFEFFFF |
| 64KB禁入区 | 0x7FFF0000 - 0x7FFFFFFF |
| 内核 | 0x80000000 - 0xFFFFFFFF |
??一个进程不可能每一个线性地址都对应一个物理页,如果要清楚地了解线性地址的使用,就必须有一个记录,在EPROCESS结构体中,有一个成员VadRoot,如下所示:
kd> dt _EPROCESSntdll!_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 _MMVADnt!_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_FLAGSnt!_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 VadRootPROCESS 89d37da0 SessionId: 0 Cid: 018c Peb: 7ffd3000 ParentCid: 05e8 DirBase: 13a40260 ObjectTable: e1f4db50 HandleCount: 44. Image: notepad.exekd> dt _EPROCESS 89d37da0ntdll!_EPROCESS …… +0x118 HardwareTrigger : 0 +0x11c VadRoot : 0x89d080a8 Void +0x120 VadHint : 0x89e22630 Void +0x124 CloneRoot : (null) ……??对于Windbg来说,它提供了一个十分简单查看线性地址的记录情况的指令,如下所示:
kd> !vad 0x89d080a8VAD Level Start End Commit89eb60e8 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 0x389d350a8 4 90 91 0 Mapped READONLY Pagefile section, shared commit 0x289e22630 1 a0 19f 21 Private READWRITE 89cff0b8 4 1a0 1af 6 Private READWRITE 89e31380 3 1b0 1bf 0 Mapped READWRITE Pagefile section, shared commit 0x389d350d8 4 1c0 1d5 0 Mapped READONLY \WINDOWS\system32\unicode.nls89cf50d8 2 1e0 220 0 Mapped READONLY \WINDOWS\system32\locale.nls89cf70a8 4 230 270 0 Mapped READONLY \WINDOWS\system32\sortkey.nls89d200a8 3 280 285 0 Mapped READONLY \WINDOWS\system32\sorttbls.nls89d080a8 0 290 2d0 0 Mapped READONLY Pagefile section, shared commit 0x4189d170d8 5 2e0 3a7 0 Mapped EXECUTE_READ Pagefile section, shared commit 0x389f68f20 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 0x289d140d8 9 3f0 3f1 0 Mapped READONLY Pagefile section, shared commit 0x289d96330 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.nls89dd71f8 2 440 47f 3 Private READWRITE 89d56260 5 480 582 0 Mapped READONLY Pagefile section, shared commit 0x10389d280a8 4 590 88f 0 Mapped EXECUTE_READ Pagefile section, shared commit 0x1a89de0f60 3 890 90f 1 Private READWRITE 89d060a8 5 910 95f 0 Mapped READONLY Pagefile section, shared commit 0x5089d8ae80 4 960 960 0 Mapped READWRITE Pagefile section, shared commit 0x189d42ec0 6 970 9af 0 Mapped READWRITE Pagefile section, shared commit 0x1089d42e90 5 9b0 9bd 0 Mapped READWRITE Pagefile section, shared commit 0xe89d310e8 7 9c0 abf 123 Private READWRITE 89dd9e80 6 ad0 b4f 0 Mapped READWRITE Pagefile section, shared commit 0x789abfcd8 1 1000 1012 3 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\notepad.exe89d280d8 7 58fb0 59179 9 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\AppPatch\AcGenral.dll89d0e0d8 8 5adc0 5adf6 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\uxtheme.dll89d100d8 6 5cc30 5cc55 20 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\shimeng.dll89d2e248 7 62c20 62c28 1 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\lpk.dll89d230d8 5 72f70 72f95 3 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\winspool.drv89cf70d8 8 73640 7366d 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\MSCTFIME.IME89d790a8 7 73fa0 7400a 16 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\usp10.dll89dd93e0 8 74680 746cb 3 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\MSCTF.dll89d0e0a8 6 759d0 75a7e 3 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\userenv.dll89d200d8 7 76300 7631c 1 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\imm32.dll89d080d8 4 76320 76366 4 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\comdlg32.dll89d342d0 8 76990 76acc 8 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\ole32.dll89cc50a8 7 76b10 76b39 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\winmm.dll89d140a8 8 770f0 7717a 4 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\oleaut32.dll89d170a8 6 77180 77282 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\WinSxS\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.2600.5512_x-ww_35d4ce83\comctl32.dll89a865b8 8 77bb0 77bc4 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\msacm32.dll89d0f0d8 9 77bd0 77bd7 1 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\version.dll89d230a8 7 77be0 77c37 7 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\msvcrt.dll89abfd18 8 77d10 77d9f 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\user32.dll89cf50a8 5 77da0 77e48 5 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\advapi32.dll89d590a8 6 77e50 77ee1 1 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\rpcrt4.dll89d590d8 8 77ef0 77f38 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\gdi32.dll89d010a8 9 77f40 77fb5 2 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\shlwapi.dll89d270d8 7 77fc0 77fd0 1 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\secur32.dll89d372a0 3 7c800 7c91d 5 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\kernel32.dll89eb60b8 2 7c920 7c9b2 5 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\ntdll.dll89cf60d8 5 7d590 7dd83 30 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\shell32.dll89d9e908 4 7f6f0 7f7ef 0 Mapped EXECUTE_READ Pagefile section, shared commit 0x789d010d8 3 7ffa0 7ffd2 0 Mapped READONLY Pagefile section, shared commit 0x3389abfc58 4 7ffd3 7ffd3 1 Private READWRITE 89ac1ac8 5 7ffdf 7ffdf 1 Private READWRITE Total VADs: 66, average level: 6, maximum depth: 10Total 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 0x89d080a8nt!_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 Commit89d080a8 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 0x89abfcd8nt!_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 : __unnamedkd> dt _MMVAD 0x89eb60b8nt!_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 : __unnamedkd> dt _MMVAD 0x89d010d8nt!_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 : __unnamedkd> dt _MMVAD 0x89abfc58nt!_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 Commit89eb60b8 2 7c920 7c9b2 5 Mapped Exe EXECUTE_WRITECOPY \WINDOWS\system32\ntdll.dll??然后在Windbg如下所示:
kd> dt _MMVAD 89eb60b8nt!_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 : __unnamedkd> 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树。
??内存管理篇——私有内存和映射内存
