windbg - VAD (!vad)、PTE (!pte) 和加载的模块和部分(lm 和 !dh)之间有什么关系?

逆向工程 视窗 风袋 核心
2021-06-26 15:20:43

[从https://stackoverflow.com/q/55438806/254959交叉发布(但关闭了原文)]

我试图更好地了解 Windows 进程内存,并且在 VAD 树、PTE 和加载模块之间的关系方面存在一些差距。

下面的输出是从内核调试会话中捕获的,但在运行简单编译的“hello world”C 程序的简单进程的上下文中。

  1. 当我做一个时lm,列出了许多模块,但是当我做一个 !vad 时,我只看到 5 个映射(我自己的进程二进制文件和 4 个其他 Windows DLL)。
kd> lm
start             end                 module name
00007ffd`ea8f0000 00007ffd`ea91b000   vertdll    (deferred)             
00007ffd`ea920000 00007ffd`eab00000   ntdll      (pdb symbols)          C:\ProgramData\Dbg\sym\ntdll.pdb\13B64B553003FA22AB7CCD36A3A5431F1\ntdll.pdb
fffff416`a3800000 fffff416`a3b94000   win32kfull   (deferred)             
fffff416`a3ba0000 fffff416`a3db2000   win32kbase   (deferred)             
fffff416`a3dc0000 fffff416`a3dca000   TSDDD      (deferred)             
fffff416`a3dd0000 fffff416`a3e11000   cdd        (deferred)             
fffff416`a4290000 fffff416`a4307000   win32k     (deferred)             
fffff800`a5c01000 fffff800`a64d3000   nt         (pdb symbols)          C:\ProgramData\Dbg\sym\ntkrnlmp.pdb\31C51B7D1C2545A88F69E13FC73E68941\ntkrnlmp.pdb
fffff800`a64d3000 fffff800`a6552000   hal        (deferred)             
fffff800`a6600000 fffff800`a6647000   kd_02_8086   (deferred)             
...
fffff80b`85960000 fffff80b`8597c000   disk       (deferred)             
...
kd> !vad
VAD           Level     Start       End Commit
ffff8908f102b0c0  4     7ffe0     7ffe0      1 Private      READONLY           
ffff8908ef465290  3     7ffe1     7ffef     -1 Private      READONLY           
ffff8908f169f100  4   fb63c20   fb63d1f      6 Private      READWRITE          
ffff8908ef4c86e0  2   fb63e00   fb63fff      3 Private      READWRITE          
ffff8908ef17e3b0  3  2e38e030  2e38e03f      0 Mapped       READWRITE          Pagefile section, shared commit 0x10
ffff8908ef592280  4  2e38e040  2e38e046      1 Private      READWRITE          
ffff8908f1873410  1  2e38e050  2e38e068      0 Mapped       READONLY           Pagefile section, shared commit 0x19
ffff8908ef106a00  3  2e38e070  2e38e073      0 Mapped       READONLY           Pagefile section, shared commit 0x4
ffff8908f19eea10  2  2e38e080  2e38e080      0 Mapped       READONLY           Pagefile section, shared commit 0x1
ffff8908f0fba340  3  2e38e090  2e38e090      1 Private      READWRITE          
ffff8908f0fdc980  0  2e38e0a0  2e3900a0      1 Private      READWRITE          
ffff8908ef215060  3  2e390130  2e39022f     17 Private      READWRITE          
ffff8908ef084860  2  2e390230  2e3902f4      0 Mapped       READONLY           \Windows\System32\locale.nls
ffff8908f14e3e90  3 7ff67f9a0 7ff67fa9f      0 Mapped       READONLY           Pagefile section, shared commit 0x5
ffff8908ef3025c0  1 7ff67faa0 7ff67fac2      0 Mapped       READONLY           Pagefile section, shared commit 0x23
ffff8908f0ef7c70  4 7ff67fe30 7ff67fe51      3 Mapped  Exe  EXECUTE_WRITECOPY  \Users\user\Desktop\test\x64\Release\test.exe
ffff8908ef6ad770  3 7ffde5240 7ffde52c7      5 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\apphelp.dll
ffff8908ef1bcf70  4 7ffde7470 7ffde76d5      8 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\KernelBase.dll
ffff8908f16717a0  2 7ffde9270 7ffde931d      5 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\kernel32.dll
ffff8908f0e50c30  3 7ffdea920 7ffdeaaff     12 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\ntdll.dll

当我!pte甚至db在一个不在!vad输出中的disk模块(特别是模块)上时,它是一个有效的映射地址,我什至可以读取它的内容。

kd> !pte fffff80b`85960000
                                           VA fffff80b85960000
PXE at FFFFFB7DBEDF6F80    PPE at FFFFFB7DBEDF0170    PDE at FFFFFB7DBE02E160    PTE at FFFFFB7C05C2CB00
contains 0000000001109063  contains 0A0000007E55D863  contains 0A00000002A81863  contains 890000007D044963
pfn 1109      ---DA--KWEV  pfn 7e55d     ---DA--KWEV  pfn 2a81      ---DA--KWEV  pfn 7d044     -G-DA--KW-V
kd> db fffff80b`85960000
fffff80b`85960000  4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00  MZ..............
fffff80b`85960010  b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00  ........@.......
fffff80b`85960020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fffff80b`85960030  00 00 00 00 00 00 00 00-00 00 00 00 d8 00 00 00  ................
fffff80b`85960040  0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68  ........!..L.!Th
fffff80b`85960050  69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f  is program canno
fffff80b`85960060  74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20  t be run in DOS 
fffff80b`85960070  6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00  mode....$.......

那么,为什么!vad不包含这个条目呢?

  1. 当我!dh在加载的 PE 之一上时,例如,我自己进程的 EXE,我可以看到它由具有不同内存访问保护的不同部分组成。
kd> !dh 7ff67fe30000

File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
    8664 machine (X64)
       6 number of sections
5CA04653 time date stamp Sat Mar 30 21:47:15 2019

       0 file pointer to symbol table
       0 number of symbols
      F0 size of optional header
      22 characteristics
            Executable
            App can handle >2gb addresses

...

SECTION HEADER #1
   .text name
   10FA0 virtual size
    1000 virtual address
   11000 size of raw data
...
60000020 flags
         Code
         (no align specified)
         Execute Read

SECTION HEADER #2
  .rdata name
    96F6 virtual size
   12000 virtual address
    9800 size of raw data
   11400 file pointer to raw data
...
40000040 flags
         Initialized Data
         (no align specified)
         Read Only

...

SECTION HEADER #6
  .reloc name
     614 virtual size
   21000 virtual address
     800 size of raw data
...
42000040 flags
         Initialized Data
         Discardable
         (no align specified)
         Read Only

而且,根据 Windows Internals 一书,

每个虚拟连续范围的非空闲虚拟地址都有一个 VAD,这些虚拟地址都具有相同的特征(保留与提交与映射、内存访问保护等)。

所以我希望每个加载的模块都由几个 VAD 条目表示,每个部分都有不同的内存访问保护。

但是将!vad每个加载的模块显示为具有权限的单个条目EXECUTE_WRITECOPY

kd> !vad
VAD           Level     Start       End Commit
...
ffff8908f0ef7c70  4 7ff67fe30 7ff67fe51      3 Mapped  Exe  EXECUTE_WRITECOPY  \Users\user\Desktop\test\x64\Release\test.exe
ffff8908ef6ad770  3 7ffde5240 7ffde52c7      5 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\apphelp.dll
ffff8908ef1bcf70  4 7ffde7470 7ffde76d5      8 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\KernelBase.dll
ffff8908f16717a0  2 7ffde9270 7ffde931d      5 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\kernel32.dll
ffff8908f0e50c30  3 7ffdea920 7ffdeaaff     12 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\ntdll.dll

为什么呢?

  1. 在这种情况下,我如何才能全面了解进程可访问的内存页面及其相关的内存访问保护?最初,我想到了依靠!vad输出,但似乎它没有给出完整的画面?这是否意味着我应该遍历并运行!pte从 0x0 到 0xFFFFFFFFFFFFFFFF 的每个 0x1000 的倍数?
1个回答

当你在 kd 会话中时 lm 显示内核模式模块和用户模式模块

使用 lm u 只显示用户模式模块

这将与 !vad 显示一致

内核模式模块不与单个进程相关联,因此它们不是进程虚拟地址描述符表的一部分

显然你可以得到任何虚拟地址的 pte 显然你可以从任何虚拟地址读取

iirc vs 是引用图像中的限定符 VADS 都将 MEM_IMAGE 作为它们的类型,并且它们将是连续的,您不能将它们从 !vad 拆分为单独的部分

您需要在用户模式下对特定进程中的特定图像执行 !vadump

单个 CONTIGOUS MEM_IMAGE vad

0: kd> !vad ffffde04de1b7d90
VAD           Level     Start       End Commit
ffffde04de1b7d90  0 7ffdb7c80 7ffdb7e6c     21 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\ntdll.dll

Total VADs: 1, average level: 1, maximum depth: 0
Total private commit: 0x15 pages (84 KB)
Total shared commit:  0 pages (0 KB)

我将此过程附加到目标中的本地windbg

这个 vad 的分裂

BaseAddress:       00007ffdb7c80000  << + 
RegionSize:        0000000000001000  <<  ====
State:             00001000  MEM_COMMIT
Protect:           00000002  PAGE_READONLY
Type:              01000000  MEM_IMAGE

BaseAddress:       00007ffdb7c81000 <<<<<<<<<<<< + 
RegionSize:        0000000000117000 << ====
State:             00001000  MEM_COMMIT
Protect:           00000020  PAGE_EXECUTE_READ
Type:              01000000  MEM_IMAGE

BaseAddress:       00007ffdb7d98000 <<<<<<<<<<<<<<
RegionSize:        0000000000047000
State:             00001000  MEM_COMMIT
Protect:           00000002  PAGE_READONLY
Type:              01000000  MEM_IMAGE

BaseAddress:       00007ffdb7ddf000
RegionSize:        0000000000001000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE
Type:              01000000  MEM_IMAGE

BaseAddress:       00007ffdb7de0000
RegionSize:        0000000000002000
State:             00001000  MEM_COMMIT
Protect:           00000008  PAGE_WRITECOPY
Type:              01000000  MEM_IMAGE

BaseAddress:       00007ffdb7de2000
RegionSize:        0000000000008000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE
Type:              01000000  MEM_IMAGE

BaseAddress:       00007ffdb7dea000  <<<< + 
RegionSize:        0000000000083000  <<<  =====      ((00007ffdb7e6d000 - page size) / pagesize)  = vad end  7ffdb7e6c
State:             00001000  MEM_COMMIT
Protect:           00000002  PAGE_READONLY
Type:              01000000  MEM_IMAGE

用户模式下的 lm

0:001> lm m ntdll
Browse full module list
start             end                 module name
00007ffd`b7c80000 00007ffd`b7e6d000   ntdll     

您可以在此处执行 !dh 并添加查看每个虚拟地址将匹配的虚拟地址 !vadumps 条目并将显示应用了哪些不同的保护

阅读 VirtualAlloc 和 Virtual Protect

您可以分配非常大的内存并更改它们之间页面大小的保护,大分配相当于 vad 而 !vadump 在用户模式下将显示 virtualprotected 状态

一个进程只能访问用户模式虚拟地址它不能访问内核模式地址它需要一种机制来做到这一点(系统调用完成这项工作)