调试教程第六部分:导航内核调试器






4.98/5 (35投票s)
2004年8月7日
22分钟阅读

174343
学习内核调试器的基础知识。
引言
在本教程中,我们将介绍内核调试器的一些基本功能,并习惯使用它。我显然不能涵盖所有内容,因此只选择了一些主题来帮助您熟悉调试器。希望本文对您的调试冒险有所帮助。
安装
要设置内核调试器,您需要修改您的BOOT.INI文件,如"调试教程第1部分"中所述。应输入/DEBUG /DEBUGPORT=COM1 /BAUDRATE=115200作为选项。如果您不指定速度,我相信默认值是19200。我会在boot.ini中创建一个单独的条目,以便您可以在调试和非调试之间进行选择。您只需复制已有的内容并进行修改即可。
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional"
/fastdetect
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional"
/fastdetect /debug /debugport=com1 /baudrate=115200
(Shown on seperate line to prevent long lines in this article.
Should be on the same line in boot.ini).
如果您已经设置了Firewire,也可以使用它。要连接,请将一条空调制解调器电缆从您要调试的机器的指定COM端口连接到您要用于调试的机器的COM端口。
还有其他运行在同一台机器上的内核调试器,如Softice。甚至还有一个可以使用的"LiveKD"。本教程只涉及可以从微软下载的用于Windows NT的免费工具。还有其他版本的工具适用于Windows 9x。
要连接到机器,只需打开WINDBG并从文件菜单中选择"内核调试"。选择"COM"选项卡(如果您使用了Firewire,则选择其他选项卡),然后输入选项。您指定的COM端口是您将电缆连接到本地机器的位置,而不是您在远程机器的BOOT.INI中指定的端口。如果您正在使用VMWare,在设置COM端口时,您可以选择将其输出到管道。此URL显示了VMWare的设置。
如果机器已经启动,您可能需要按"Control + C"才能进入调试器。
其他一些提示;如果您在连接时遇到问题,您可能需要尝试以下一些方法
- 在调试菜单中按Control + Break或"Break"。
- 重新同步(调试 -> 内核连接)。
- 循环波特率(调试 -> 内核连接)。
- 尝试不同的COM端口,确保电缆连接到正确的端口。
- 关闭调试器并尝试通过打开另一个调试器重新连接。
- 确保boot.ini正确。启动时,应在名称旁边看到"DEBUGGER ENABLED"。
准备就绪!
现在我们已连接,可以开始了。首先,确保您使用!symfix或"调试教程第1部分"中指定的任何方法指向微软符号服务器。使用环境变量设置您的符号路径,例如,并添加您可能需要的任何其他符号路径,例如您自己的软件的符号路径。
首先,让我们从一些简单的事情开始。让我们查找系统上的所有进程。这是通过进入内核调试器并使用"!process 0 0"来完成的。请记住,如果您收到错误,请确保您指向微软的符号服务器。调试器需要找到内存中的某些变量才能执行不同的操作。
您可能会看到每个进程类似以下内容
PROCESS fcdbe2c0 SessionId: 0 Cid: 00a4 Peb: 7ffdf000 ParentCid: 008c
DirBase: 0401d000 ObjectTable: fcdc39c8 TableSize: 354.
Image: winlogon.exe
那么,这一切意味着什么?
进程 (PROCESS)
第一个是EPROCESS
结构的地址。这是一个描述该进程的内核数据结构。此进程的地址是"fcdbe2c0"。如果您有符号,您可以使用早期教程中提到的"dt"命令来查看此结构。
dt _EPROCESS fcdbe2c0
或
dt nt!_EPROCESS fcdbe2c0
如果这不起作用并且您正在调试Windows 2000机器,您可能需要使用"!processfields"代替。它将显示fcdbe2c0的偏移量,然后您可以手动转储地址以查看您想要的数据。
会话ID (SESSION ID)
接下来是"会话ID"。在多用户环境(终端服务)中,登录到机器的不同用户将获得自己的"会话"。通常,会话0是控制台,尽管这在具有快速用户切换功能的Windows XP中略有不同。为了好玩,用几个用户登录Windows XP(除非您有2000或2003服务器版本,您可以尝试同样的事情)并键入"qwinsta"。它应该为您提供用户及其会话ID的输出。
CID
"Cid"是进程标识符。如果您在任务管理器中查看并选择"PID"显示在列中,就是它。它是此进程的PID。
PEB
PEB是进程环境块。这是一个用户模式数据结构,这意味着什么?通常,您会看到每个进程的此位置地址都相同。我希望任何想使用内核调试器的人都能理解虚拟地址。在内核中,系统地址空间映射到所有进程(不深入探讨"会话空间"的细节),但用户模式地址映射到该进程。因此,显然,一个进程中的相同地址与另一个进程中的物理地址不同。那么,如果所有地址都相同,我们如何查看特定进程的PEB?我们需要将我们的"上下文"更改为该进程。这样,正确的页面目录表将被加载,我们可以正确地翻译该进程中的数据。我们将在稍后学习如何设置上下文。当您这样做时,就像在本地调试器中一样,您可以在内核调试器中查看PEB。
dt _PEB 7ffdf000
或
!peb.
父CID (PARENT CID)
"父Cid"正如您所想。它是创建此进程的进程,即父进程。
DIRBASE
"DirBase"是目录基址。这是此进程运行时CR3的值。它是虚拟地址到物理地址转换的起始地址。实际上,有一个命令可以为您完成此操作。!ptov。因此,目录基址是0401d000,您必须去掉最后3位(应始终为0)。
!ptov 0401d
kd> !ptov 0401d
4195000 10000
40d6000 20000
3a5d000 6d000
40f9000 6e000
4117000 6f000
405a000 70000
409b000 71000
409c000 72000
412f000 73000
413a000 74000
42d1000 76000
426f000 77000
4416000 78000
4458000 7a000
439d000 7b000
43b6000 7c000
4880000 7d000
4831000 7e000
4ac3000 7f000
4b66000 80000
4a67000 81000
4f42000 84000
50a7000 89000
4f81000 8d000
523f000 94000
5240000 a8000
524f000 ac000
5226000 b3000
52c4000 bc000
4fda000 d6000
52af000 dc000
5403000 e2000
51cc000 e5000
509f000 ec000
5325000 ee000
52aa000 f0000
512c000 f2000
522e000 f3000
52f1000 f4000
5174000 f6000
5249000 f7000
575e000 f8000
56df000 f9000
5721000 fb000
58e3000 fd000
576c000 fe000
57ad000 ff000
5833000 105000
583c000 10e000
...
左侧的地址是物理地址。右侧的地址是应用程序使用的虚拟地址。要查看物理地址,您无需切换上下文。您使用"!dc"、"!db"等命令,而不是作用于虚拟地址的"dc"、"db"命令。您还可以使用"!eb"等命令写入数据来更改物理地址处存储的数据。
kd> !dc 5833000
# 5833000 72656d6d 6c616963 666f5320 72617774 mmercial Softwar
# 5833010 75502065 73696c62 73726568 30414320 e Publishers CA0
# 5833020 0d309f81 862a0906 0df78648 05010101 ..0...*.H.......
# 5833030 8d810300 89813000 00818102 6569d3c3 .....0........ie
# 5833040 54940152 62c628ab 5554b318 458744c5 R..T.(.b..TU.D.E
# 5833050 7ec23b4a c8d7d3d8 d88d8680 9c16f10c J;.~............
# 5833060 29a96bcc 73768fb2 62c5c892 1eed3ca6 .k.)..vs...b.<..
# 5833070 13f07505 4d146c00 079098d4 817369be .u...l.M.....is.
对象表 (OBJECT TABLE)
这是该进程句柄映射的表。这是一个包含大小和指向对象以处理列表的指针的数据结构。内核就是这样将用户模式句柄转换为其匹配的内核对象的。我们稍后会再次查看句柄。
表大小 (TABLESIZE)
这是表的十进制大小。是的,这现在有点非标准,因为我们有一些十六进制数字,如PID,但表的大小是十进制的。这显示了此进程在其句柄表中有多少条目。如果您打开任务管理器并查看"句柄计数",它应该与此数字匹配。这是此进程中正在使用的句柄条目数。
镜像 (IMAGE)
这是一个简单的。此进程的映像,在本例中是WINLOGON.EXE。
切换上下文
有不同的方法来切换上下文。您可以使用.context <DirBase>,您可以使用.trap <trap address>,但在这里我将向您展示如何切换到线程。
首先,让我们转储WinLogon的线程。
!process fcdbe2c0 ff
必须将EPROCESS
结构连同一些标志提供给内核调试器。现在,ff用于显示所有内容。
kd> !process fcdbe2c0 ff
PROCESS fcdbe2c0 SessionId: 0 Cid: 00a4 Peb: 7ffdf000 ParentCid: 008c
DirBase: 0401d000 ObjectTable: fcdc39c8 TableSize: 354.
Image: winlogon.exe
VadRoot fcda2ce8 Clone 0 Private 798. Modified 1085. Locked 0.
DeviceMap fcebfa48
Token e1b762d0
ElapsedTime 21:07:55.0882
UserTime 0:00:01.0241
KernelTime 0:00:03.0805
QuotaPoolUsage[PagedPool] 36408
QuotaPoolUsage[NonPagedPool] 62184
Working Set Sizes (now,min,max) (652, 50, 345) (2608KB, 200KB, 1380KB)
PeakWorkingSetSize 2034
VirtualSize 34 Mb
PeakVirtualSize 36 Mb
PageFaultCount 4919
MemoryPriority BACKGROUND
BasePriority 13
CommitCharge 1357
THREAD fcdbd020 Cid a4.84 Teb: 7ffde000 Win32Thread: e1b7b8e8
WAIT: (WrUserRequest) UserMode Non-Alertable
fcdb9820 SynchronizationEvent
Not impersonating
Owning Process fcdbe2c0
Wait Start TickCount 49551906 Elapsed Ticks: 13726
Context Switch Count 3360 LargeStack
UserTime 0:00:00.0290
KernelTime 0:00:01.0041
Start Address 0x01001674
Stack Init f7660000 Current f765fcc8 Base f7660000 Limit f765d000 Call 0
Priority 15 BasePriority 15 PriorityDecrement 0 DecrementCount 0
Kernel stack not resident.
ChildEBP RetAddr Args to Child
f765fce0 8042d61c 00000000 e1b7b8e8 00000001 nt!KiSwapThread+0xc5
f765fd08 a00159cb fcdb9820 0000000d 00000001 nt!KeWaitForSingleObject+0x1a1
f765fd44 a0014f6c 000020ff 00000000 00000001 win32k!xxxSleepThread+0x183
f765fd54 a0014f53 0006fde8 80461691 00000000 win32k!xxxWaitMessage+0xe
f765fd5c 80461691 00000000 00000000 00000018 win32k!NtUserWaitMessage+0xb
f765fd5c 77e14b53 00000000 00000000 00000018 nt!KiSystemService+0xc4
0006fe14 77e23570 00070022 00000000 00000010 +0x77e14b53
0006fe38 77e2381e 01000000 01024c10 00000000 +0x77e23570
0006fe58 77e3dcf8 01000000 01024c10 00000000 +0x77e2381e
0006fe7c 01006052 01000000 00000578 00000000 +0x77e3dcf8
0006feb8 01005fa8 000766b8 01000000 00000578 +0x1006052
0006fef0 010099c7 000766b8 01000000 00000578 +0x1005fa8
0006ff28 010019e4 000766b8 00000005 0007366c +0x10099c7
0006ff58 0100179e 01000000 00000000 0007366c +0x10019e4
0006fff4 00000000 7ffdf000 000000c8 00000100 +0x100179e
THREAD fcdb8b80 Cid a4.c0 Teb: 7ffdd000 Win32Thread: 00000000
WAIT: (DelayExecution) UserMode Alertable
fcdb8c68 NotificationTimer
Not impersonating
Owning Process fcdbe2c0
Wait Start TickCount 49564648 Elapsed Ticks: 984
Context Switch Count 33085
UserTime 0:00:00.0000
KernelTime 0:00:00.0000
Start Address 0x77e92c50
Win32 Start Address 0x77f88164
Stack Init f78f4000 Current f78f3cc4 Base f78f4000 Limit f78f1000 Call 0
Priority 13 BasePriority 13 PriorityDecrement 0 DecrementCount 0
Kernel stack not resident.
ChildEBP RetAddr Args to Child
f78f3cdc 8042d00e f78f3d64 003dffac 003dffac nt!KiSwapThread+0xc5
f78f3d04 804c04e6 003dfc01 00000001 f78f3d34 nt!KeDelayExecutionThread+0x180
f78f3d54 80461691 00000001 003dffac 00000000 nt!NtDelayExecution+0x7f
f78f3d54 77f90333 00000001 003dffac 00000000 nt!KiSystemService+0xc4
003dffb4 77e92ca8 0006feb8 00000000 00000000 +0x77f90333
003dffec 00000000 77f88164 0006feb8 00000000 +0x77e92ca8
... (Continues on with more information)
不要被“内核堆栈不在内存中”吓到。我们知道内存会被交换到磁盘,对吗?所以,在使用内核调试器时,并非所有内存都始终可用。我们必须忍受这一点。在上述情况下,堆栈在内存中,至少足以获取堆栈跟踪,这并不总是正确的。上面只是意味着堆栈的一部分不在内存中,在这种情况下。
那么,让我们将上下文设置为第一个线程。
kd> .process fcdbe2c0
Implicit process is now fcdbe2c0
WARNING: .cache forcedecodeuser is not enabled
kd> .cache forcedecodeuser
Max cache size is : 1024000 bytes (0x3e8 KB)
Total memory in cache : 0 bytes (0 KB)
Number of regions cached: 0
0 full reads broken into 0 partial reads
counts: 0 cached/0 uncached, 0.00% cached
bytes : 0 cached/0 uncached, 0.00% cached
** Transition PTEs are implicitly decoded
** User virtual addresses are translated to physical addresses before access
kd> .thread fcdbd020
Implicit thread is now fcdbd020
kd> kb
*** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr Args to Child
f765fce0 8042d61c 00000000 e1b7b8e8 00000001 nt!KiSwapThread+0xc5
f765fd08 a00159cb fcdb9820 0000000d 00000001 nt!KeWaitForSingleObject+0x1a1
f765fd44 a0014f6c 000020ff 00000000 00000001 win32k!xxxSleepThread+0x183
f765fd54 a0014f53 0006fde8 80461691 00000000 win32k!xxxWaitMessage+0xe
f765fd5c 80461691 00000000 00000000 00000018 win32k!NtUserWaitMessage+0xb
f765fd5c 77e14b53 00000000 00000000 00000018 nt!KiSystemService+0xc4
WARNING: Frame IP not in any known module. Following frames may be wrong.
0006fe14 77e23570 00070022 00000000 00000010 0x77e14b53
0006fe38 77e2381e 01000000 01024c10 00000000 0x77e23570
0006fe58 77e3dcf8 01000000 01024c10 00000000 0x77e2381e
0006fe7c 01006052 01000000 00000578 00000000 0x77e3dcf8
0006feb8 01005fa8 000766b8 01000000 00000578 0x1006052
0006fef0 010099c7 000766b8 01000000 00000578 0x1005fa8
0006ff28 010019e4 000766b8 00000005 0007366c 0x10099c7
0006ff58 0100179e 01000000 00000000 0007366c 0x10019e4
0006fff4 00000000 7ffdf000 000000c8 00000100 0x100179e
kd> !reload
Connected to Windows 2000 2195 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
...................................................................................
Loading unloaded module list
No unloaded module list present
Loading User Symbols
..................................................
kd> kb
*** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr Args to Child
f765fce0 8042d61c 00000000 e1b7b8e8 00000001 nt!KiSwapThread+0xc5
f765fd08 a00159cb fcdb9820 0000000d 00000001 nt!KeWaitForSingleObject+0x1a1
f765fd44 a0014f6c 000020ff 00000000 00000001 win32k!xxxSleepThread+0x183
f765fd54 a0014f53 0006fde8 80461691 00000000 win32k!xxxWaitMessage+0xe
f765fd5c 80461691 00000000 00000000 00000018 win32k!NtUserWaitMessage+0xb
f765fd5c 77e14b53 00000000 00000000 00000018 nt!KiSystemService+0xc4
0006fde0 77e23680 00000000 00000000 0000ffff USER32!NtUserWaitMessage+0xb
0006fe14 77e23570 00070022 00000000 00000010 USER32!DialogBox2+0x216
0006fe38 77e2381e 01000000 01024c10 00000000 USER32!InternalDialogBox+0xd1
0006fe58 77e3dcf8 01000000 01024c10 00000000 USER32!DialogBoxIndirectParamAorW+0x34
0006fe7c 01006052 01000000 00000578 00000000 USER32!DialogBoxParamW+0x3d
0006feb8 01005fa8 000766b8 01000000 00000578 winlogon!TimeoutDialogBoxParam+0x27
0006fef0 010099c7 000766b8 01000000 00000578 winlogon!WlxDialogBoxParam+0x7b
0006ff10 01004bdc 000766b8 00071fc8 000766b8 winlogon!BlockWaitForUserAction+0x3c
0006ff28 010019e4 000766b8 00000005 0007366c winlogon!MainLoop+0x1bf
0006ff58 0100179e 01000000 00000000 0007366c winlogon!WinMain+0x2a5
0006fff4 00000000 7ffdf000 000000c8 00000100 winlogon!WinMainCRTStartup+0x156
现在,您是否注意到一旦我设置了上下文,我必须重新加载符号才能查看用户模式符号?那是因为我们需要强制内核调试器读取新上下文并为该进程加载新符号。如果您多次切换进程,您可能已经注意到内核调试器会告诉您有关前一个进程的符号。这就是为什么您应该在切换上下文后尝试重新加载符号。
您还应该注意,内核堆栈与用户模式堆栈不同。当线程调用到内核时,它会切换堆栈。内核有自己的堆栈,用户模式有自己的堆栈。这只是一个参考信息。
那么,现在您可能要问,PROCESS和THREAD列表中的所有参数是什么意思?我们已经介绍了顶部标题,所以让我们从进程标题开始。不过,其中大部分信息对您通常来说都是无用的。
VadRoot fcda2ce8 Clone 0 Private 798. Modified 1085. Locked 0.
"VadRoot"基本上是一个指向二叉树的指针,该树包含进程中所有虚拟地址的地址,包括它们的开始和结束范围。这被称为"虚拟地址描述符"。如果您遍历这棵树,您将最终看到与在用户模式调试器中执行x *!时相同的输出,但实际上您可能会看到更多。这是因为,这不仅包括模块,还包括所有映射的虚拟地址及其范围。例如,如果您使用Virtual Alloc分配了一些大块内存,这些内存将显示在此树列表中。
尝试 !vad fcda2ce8
(请注意,您显然应该将内核调试器中列出的地址放在那里。)
DeviceMap fcebfa48
这是系统的对象设备映射。虽然每个进程都有自己的指向此对象的指针,但您可以在下面看到,它全局存储在内核中。
kd> x nt!ObSystemDeviceMap
8047f79c nt!ObSystemDeviceMap = <NO type information>
kd> dd nt!ObSystemDeviceMap L 1
8047f79c fcebfa48
本文不会深入介绍设备堆栈或其他类似内容。本文只是对基础知识的介绍。
Token e1b762d0
此进程的用户令牌。尝试 !token e1b762d0
ElapsedTime 21:07:55.0882
UserTime 0:00:01.0241
KernelTime 0:00:03.0805
在内核和用户模式下花费的时间。
QuotaPoolUsage[PagedPool] 36408
QuotaPoolUsage[NonPagedPool] 62184
Working Set Sizes (now,min,max) (652, 50, 345) (2608KB, 200KB, 1380KB)
PeakWorkingSetSize 2034
VirtualSize 34 Mb
PeakVirtualSize 36 Mb
PageFaultCount 4919
MemoryPriority BACKGROUND
BasePriority 13
CommitCharge 1357
这些也很好理解。它们只是显示内存使用情况和统计数据。
现在,让我们看看线程信息。我们可以通过使用!thread <thread>来做到这一点。
kd> !thread fcdbd020
THREAD fcdbd020 Cid a4.84 Teb: 7ffde000 Win32Thread: e1b7b8e8 WAIT:
(WrUserRequest) UserMode Non-Alertable
fcdb9820 SynchronizationEvent
Not impersonating
Owning Process fcdbe2c0
Wait Start TickCount 49551906 Elapsed Ticks: 14128
Context Switch Count 3360 LargeStack
UserTime 0:00:00.0290
KernelTime 0:00:01.0041
Start Address winlogon!WinMainCRTStartup (0x01001674)
Stack Init f7660000 Current f765fcc8 Base f7660000 Limit f765d000 Call 0
Priority 15 BasePriority 15 PriorityDecrement 0 DecrementCount 0
Kernel stack not resident.
ChildEBP RetAddr Args to Child
f765fce0 8042d61c 00000000 e1b7b8e8 00000001 nt!KiSwapThread+0xc5
f765fd08 a00159cb fcdb9820 0000000d 00000001 nt!KeWaitForSingleObject+0x1a1
f765fd44 a0014f6c 000020ff 00000000 00000001 win32k!xxxSleepThread+0x183
f765fd54 a0014f53 0006fde8 80461691 00000000 win32k!xxxWaitMessage+0xe
f765fd5c 80461691 00000000 00000000 00000018 win32k!NtUserWaitMessage+0xb
f765fd5c 77e14b53 00000000 00000000 00000018 nt!KiSystemService+0xc4
0006fde0 77e23680 00000000 00000000 0000ffff USER32!NtUserWaitMessage+0xb
0006fe14 77e23570 00070022 00000000 00000010 USER32!DialogBox2+0x216
0006fe38 77e2381e 01000000 01024c10 00000000 USER32!InternalDialogBox+0xd1
0006fe58 77e3dcf8 01000000 01024c10 00000000 USER32!DialogBoxIndirectParamAorW+0x34
0006fe7c 01006052 01000000 00000578 00000000 USER32!DialogBoxParamW+0x3d
0006feb8 01005fa8 000766b8 01000000 00000578 winlogon!TimeoutDialogBoxParam+0x27
0006fef0 010099c7 000766b8 01000000 00000578 winlogon!WlxDialogBoxParam+0x7b
0006ff10 01004bdc 000766b8 00071fc8 000766b8 winlogon!BlockWaitForUserAction+0x3c
0006ff28 010019e4 000766b8 00000005 0007366c winlogon!MainLoop+0x1bf
0006ff58 0100179e 01000000 00000000 0007366c winlogon!WinMain+0x2a5
0006fff4 00000000 7ffdf000 000000c8 00000100 winlogon!WinMainCRTStartup+0x156
现在,让我们看看每个标题信息。我们之前记得进程地址实际上是一个EPROCESS
结构。那么,这是一个ETHREAD
结构。这可以通过使用dt nt!_ETHREAD <address>或!threadfields来显示。
那么,让我们分解一下。
THREAD fcdbd020 Cid a4.84 Teb: 7ffde000
Win32Thread: e1b7b8e8 WAIT: (WrUserRequest)
UserMode Non-Alertable
fcdb9820 SynchronizationEvent
第一个地址是ETHREAD结构的地址,正如我们提到的。第二个是进程.线程ID。进程ID是A4h,线程ID是84h。线程ID在查看死锁时很有用,因为同步对象由线程拥有。在查找窗口信息时也很有用,因为每个窗口都由特定的线程拥有。这些线程ID在用户模式调试器中可用。
TEB。线程环境块,正如之前的教程中提到的。它具有线程特定信息,并指向PEB,因此线程可以轻松访问某些信息。堆栈限制和结束也在此处。!teb或dt _TEB <address>。您显然需要在此进程和线程的上下文中才能使用!teb。如果TEB为NULL
,则表示它是一个系统线程。线程只在内核中执行,它不是用户模式线程。
接下来是“Win32Thread”。这是什么?这是WIN32k.SYS维护的每个线程数据结构。
最后一部分是线程的状态。此线程处于等待状态。它在用户模式下处于不可警报的等待状态。有关可警报、不可警报、内核与用户模式等待的更多信息,请参阅MSDN关于KeWaitForMultipleObjects或APC(异步过程调用)的文章。
Not impersonating
此线程是否正在模拟。如果您熟悉RPC,服务器可以模拟调用者以代表调用者执行操作或获取有关调用者的信息。如果此线程正在模拟,您将在此处看到一个模拟令牌,您可以对其执行!token <address>以获取信息。某些对内核的调用会查看此模拟令牌(如果存在)并使用它,而不是进程的用户令牌。
Owning Process fcdbe2c0
进程本身的EPROCESS
地址。在本例中,它是Winlogon。您有时可能会看到奇怪的事情,即一个线程似乎拥有与其显示的不同所有者。内核可能会调用KeAttachProcess()
以在不同的上下文中执行操作。
Wait Start TickCount 49551906 Elapsed Ticks: 14128
Context Switch Count 3360 LargeStack
UserTime 0:00:00.0290
KernelTime 0:00:01.0041
此线程在用户模式和内核模式下花费的时间。如果您正在寻找一个进程占用资源,这些是您查找进程然后线程的值。“已用滴答”是自此线程执行以来经过的时间。如果您在发生后立即寻找占用资源,这是另一个要查看的字段,哪些线程的已用滴答最少。
Start Address winlogon!WinMainCRTStartup (0x01001674)
这是线程的起始地址。
Stack Init f7660000 Current f765fcc8 Base f7660000 Limit f765d000 Call 0
这指的是内核堆栈,而不是可以在TEB中找到的用户模式堆栈。内核模式堆栈的当前位置及其限制,以及它开始的位置。
Priority 15 BasePriority 15 PriorityDecrement 0 DecrementCount 0
此线程的优先级。
您可能会看到其他内容,但这是一个基本列表。如果您注意到LPC,您可以执行"!lpc message <message id>"之类的操作,例如,如果它们仍然存在,则获取调用的发起者。
现在我们处于上下文之中,我们可以做诸如设置断点、更改内存等事情。请记住,更改驱动程序中的内存将更改所有人的内存。设置断点也是一样,您为系统上的每个人设置断点。这是一个内核调试器,内核模式驱动程序对于系统(或会话,对于我们在此处不讨论的某些其他驱动程序)是全局的。
我还能做什么?
您可以做很多事情,但正如我之前提到的,我们只涉及一些基础知识。它还取决于您工作的领域。例如,开发硬件驱动程序的人可能从不使用甚至查看用户模式中的任何内容,甚至不查看驱动程序堆栈的更高层。他们可能只知道一些硬件命令。只处理用户模式应用程序或交互的人可能只使用内核调试器来获取一些额外信息,或者找出谁在进行RPC调用等等。
页表项 (Page Table Entries)
使用内核调试器或内核转储的唯一问题是某些信息可能不存在,它可能已被分页出去。那么,我们如何判断内存是否有效或已分页出去呢?
在x86处理器中,将虚拟地址转换为物理地址时存在多级间接寻址。有一个页目录项指向一个页表项。如果您想找到页表项,这就是您的方法。请记住,我们已经知道如何使用进程的页目录查找虚拟地址的物理地址。
!pte是查找页表项的答案。
kd> !pte 80461691
80461691 - PDE at C0300804 PTE at C0201184
contains 00034163 contains 00461121
pfn 34 G-DA--KWV pfn 461 G--A--KRV
kd> dc C0300804 L1
c0300804 00034163
kd> dc C0201184 L1
c0201184 00461121
只是为了演示,如果您转储这些地址,这就是您得到的结果。虚拟地址首先添加到GDT/LDT中,然后进行转换。虚拟地址的一部分作为偏移量以获取页目录项。然后地址的下一部分用于获取页表项。虚拟地址的最后3个半字节实际上是PTE指向的最后3个半字节。因此,PTE表示461xxx是物理地址。让我们找出并转储虚拟地址和物理地址
kd> !dc 461691
# 461690 8be58bd3 dff1240d 3c558bff 01289189 .....$....U<..(.
# 4616a0 f7fa0000 00007045 06750002 016c45f6 ....Ep....u..El.
# 4616b0 1d8b5874 ffdff124 002e43c6 004a7b80 tX..$....C...{J.
# 4616c0 dd8b4874 c7444389 003b5043 43c70000 tH...CD.CP;....C
# 4616d0 00002338 3443c700 00000023 003043c7 8#....C4#....C0.
# 4616e0 b9000000 00000001 059815ff fb508040 ............@.P.
# 4616f0 6a006a53 f132e801 ff59fffc 40059c15 Sj.j..2...Y....@
# 461700 44438b80 90abebfa 548bff8b 8b644c24 ..CD.......T$Ld.
kd> dc 80461691
80461691 0d8be58b ffdff124 893c558b 00012891 ....$....U<..(..
804616a1 45f7fa00 02000070 f6067500 74016c45 ...Ep....u..El.t
804616b1 241d8b58 c6ffdff1 80002e43 74004a7b X..$....C...{J.t
804616c1 89dd8b48 43c74443 00003b50 3843c700 H...CD.CP;....C8
804616d1 00000023 233443c7 c7000000 00003043 #....C4#....C0..
804616e1 01b90000 ff000000 40059815 53fb5080 ...........@.P.S
804616f1 016a006a fcf132e8 15ff59ff 8040059c j.j..2...Y....@.
80461701 fa44438b 8b90abeb 24548bff 1d8b644c .CD.......T$Ld..
如您所见,确实如此。物理地址转储略有不同,因为它在内存的开头而不是1处转储,因此它已对齐,但您可以看到它是相同的信息。如果我们转储它们"dc 80461690",您会看到它们是相同的。
您还可以反向操作,获取PTE地址,然后找到它所属的虚拟地址。
kd> !pte C0201184
80461000 - PDE at C0300804 PTE at C0201184
contains 00034163 contains 00461121
pfn 34 G-DA--KWV pfn 461 G--A--KRV
无效地址会发生什么?
kd> !pte 40
00000040 - PDE at C0300000 PTE at C0000000
contains 01800067 contains 00000000
pfn 1800 --DA--UWV not valid
kd> dc C0300000 L1
c0300000 01800067
kd> dc C0000000 L1
c0000000 00000000
kd> !pte 66740020
66740020 - PDE at C0300664 PTE at C0199D00
contains 00000000 unavailable
kd> dc C0300664 L1
c0300664 00000000
kd> dc C0199D00 L1
c0199d00 00a8d4c0
"无效"表示它不是一个有效的虚拟地址。(一些地址可能"无效"只是因为它们从未被使用过,所以它们没有被映射,我发现了,至少这是我的理论!例如,堆栈内存被映射到您的进程中,但它从未被弄脏,那么为什么要把它放在页面文件中呢?)"不可用"表示它不可用,但它是有效的,或者它认为它是有效的。否则,该地址是有效的。有时,如果一个地址"不可用",它会列出页面文件偏移量,有时则不会。可执行文件和二进制文件是使用内存映射文件完成的。您可以通过执行!memusage来看到这一点。这意味着这些文件,除非被修改,否则不会在页面文件中,因此没有页面文件偏移量。
还有一个!sysptes可以转储所有系统pte。
句柄信息
在内核中显示句柄信息很简单。一旦您将上下文设置为正确的进程,就像在用户模式中一样,只需!handle,但有一个例外。您需要指定要转储句柄的进程。
kd> !handle 0 0 fcdbe2c0
processor number 0
PROCESS fcdbe2c0 SessionId: 0 Cid: 00a4 Peb: 7ffdf000 ParentCid: 008c
DirBase: 0401d000 ObjectTable: fcdc39c8 TableSize: 354.
Image: winlogon.exe
Handle Table at e1b78000 with 354 Entries in use
0004: Object: e1267030 GrantedAccess: 000f001f
0008: Object: fcdee160 GrantedAccess: 00100003
000c: Object: fcdee120 GrantedAccess: 00100003
0010: Object: fcdbe280 GrantedAccess: 00100003
第一个是用户模式中使用的句柄号。第二个是“对象”或对象的内核地址,最后一个是授予对象的访问权限。您还可以指定标志以转储更多信息,如下所示
kd> !handle 0 ff fcdbe2c0
processor number 0
PROCESS fcdbe2c0 SessionId: 0 Cid: 00a4 Peb: 7ffdf000 ParentCid: 008c
DirBase: 0401d000 ObjectTable: fcdc39c8 TableSize: 354.
Image: winlogon.exe
Handle Table at e1b78000 with 354 Entries in use
0000: free handle
0004: Object: e1267030 GrantedAccess: 000f001f
Object: e1267030 Type: (fcebc580) Section
ObjectHeader: e1267018
HandleCount: 1 PointerCount: 1
0008: Object: fcdee160 GrantedAccess: 00100003
Object: fcdee160 Type: (fcebf420) Event
ObjectHeader: fcdee148
HandleCount: 1 PointerCount: 1
000c: Object: fcdee120 GrantedAccess: 00100003
Object: fcdee120 Type: (fcebf420) Event
ObjectHeader: fcdee108
HandleCount: 1 PointerCount: 1
0010: Object: fcdbe280 GrantedAccess: 00100003
Object: fcdbe280 Type: (fcebf420) Event
ObjectHeader: fcdbe268
HandleCount: 1 PointerCount: 1
0014: Object: fcec6c50 GrantedAccess: 00000003
Object: fcec6c50 Type: (fcee4b40) Directory
ObjectHeader: fcec6c38
HandleCount: 15 PointerCount: 44
Directory Object: fcebfab0 Name: KnownDlls
HashBucket[ 00 ]: e137ca00 Section 'gdi32.dll'
e134b900 Section 'imagehlp.dll'
e13154a0 Section 'url.dll'
HashBucket[ 02 ]: e13dee00 Section 'MPR.dll'
HashBucket[ 03 ]: e135b040 Section 'ole32.dll'
e135d2a0 Section 'urlmon.dll'
HashBucket[ 04 ]: e1373600 Section 'lz32.dll'
e138bde0 Section 'olesvr32.dll'
HashBucket[ 06 ]: e137ef40 Section 'shell32.dll'
e12c7880 Section 'wldap32.dll'
HashBucket[ 09 ]: e13126e0 Section 'user32.dll'
e13118a0 Section 'version.dll'
HashBucket[ 10 ]: e135af80 Section 'olecli32.dll'
HashBucket[ 14 ]: e13713e0 Section 'MSASN1.DLL'
HashBucket[ 16 ]: e1314c60 Section 'COMCTL32.DLL'
fcde6c50 SymbolicLink 'KnownDllPath'
HashBucket[ 17 ]: e13171c0 Section 'CRYPT32.dll'
HashBucket[ 18 ]: e1316e60 Section 'advapi32.dll'
e13583a0 Section 'oleaut32.dll'
HashBucket[ 19 ]: e135c980 Section 'SHLWAPI.DLL'
e12c2fc0 Section 'wow32.dll'
e1316460 Section 'olecnv32.dll'
HashBucket[ 23 ]: e131a520 Section 'comdlg32.dll'
HashBucket[ 26 ]: e1317b80 Section 'wininet.dll'
HashBucket[ 27 ]: e12d5fc0 Section 'olethk32.dll'
HashBucket[ 28 ]: e124c800 Section 'MSVCRT.DLL'
HashBucket[ 31 ]: e134dbe0 Section 'rpcrt4.dll'
HashBucket[ 32 ]: e130b460 Section 'kernel32.dll'
0018: Object: fccc3c88 GrantedAccess: 00100020 (Inherit)
Object: fccc3c88 Type: (fced7c40) File
ObjectHeader: fccc3c70
HandleCount: 1 PointerCount: 1
Directory Object: 00000000 Name: \WINNT\system32 {HarddiskVolume1}
001c: Object: fcdfb730 GrantedAccess: 000f000f
Object: fcdfb730 Type: (fcee4b40) Directory
ObjectHeader: fcdfb718
HandleCount: 14 PointerCount: 18
Directory Object: fcebfab0 Name: Windows
HashBucket[ 04 ]: e1b76d40 Port 'SbApiPort'
HashBucket[ 09 ]: e1b65a00 Port 'ApiPort'
HashBucket[ 32 ]: fcdc1750 Directory 'WindowStations'
0020: Object: fcdb9de0 GrantedAccess: 00100003
Object: fcdb9de0 Type: (fcebf420) Event
ObjectHeader: fcdb9dc8
HandleCount: 1 PointerCount: 1
0024: Object: e1b7be30 GrantedAccess: 001f0001 (Protected)
Object: e1b7be30 Type: (fceb7640) Port
ObjectHeader: e1b7be18
HandleCount: 1 PointerCount: 18
0028: Object: fceca3c0 GrantedAccess: 00000001
Object: fceca3c0 Type: (fcebda40) Mutant
ObjectHeader: fceca3a8
HandleCount: 15 PointerCount: 16
Directory Object: fcebfab0 Name: NlsCacheMutant
002c: Object: fcdb99a0 GrantedAccess: 00100003
Object: fcdb99a0 Type: (fcebf420) Event
ObjectHeader: fcdb9988
HandleCount: 1 PointerCount: 1
您也可以不使用0,而是指定一个实际的句柄值,只列出一个句柄。
kd> !handle 2c ff fcdbe2c0
processor number 0
PROCESS fcdbe2c0 SessionId: 0 Cid: 00a4 Peb: 7ffdf000 ParentCid: 008c
DirBase: 0401d000 ObjectTable: fcdc39c8 TableSize: 354.
Image: winlogon.exe
Handle Table at e1b78000 with 354 Entries in use
002c: Object: fcdb99a0 GrantedAccess: 00100003
Object: fcdb99a0 Type: (fcebf420) Event
ObjectHeader: fcdb9988
HandleCount: 1 PointerCount: 1
还有其他方法可以转储对象。您会注意到有一个“对象:”和一个地址,!object 也可以用于该地址。内核调试器在查找句柄信息时所做的,实际上只是查找 ObjectTable 地址,然后找到句柄表,然后由于它知道数据结构,因此只转储信息。
我们来尝试手动解释这个表。首先,ObjectTable位于fcdc39c8。
kd> dc fcdc39c8
fcdc39c8 00000000 00000162 e1b78000 fcdbe2c0 ....b...........
fcdc39d8 000000a4 0000016f 00000300 fcdbe0a8 ....o...........
fcdc39e8 fcdbe5a8 00000000 00000000 00000000 ................
fcdc39f8 00000000 00000000 00000000 00000000 ................
fcdc3a08 00000000 00000000 00000000 00000000 ................
fcdc3a18 00000000 fcdb079c fcdc42bc 00040000 .........B......
fcdc3a28 00000000 fcdc3a2c fcdc3a2c 0030005c ....,:..,:..\.0.
fcdc3a38 00300030 00000030 07018004 69436d4d 0.0.0.......MmCi
kd> ? 162
Evaluate expression: 354 = 00000162
您会注意到我转储了一个变量,它基本上指定了正在使用的句柄数量。您还会注意到,下一个值是!handle所说的句柄表的地址。现在,让我们转储它。
kd> dc e1b78000
e1b78000 e1b78400 00000000 00000000 00000000 ................
e1b78010 00000000 00000000 00000000 00000000 ................
e1b78020 00000000 00000000 00000000 00000000 ................
e1b78030 00000000 00000000 00000000 00000000 ................
e1b78040 00000000 00000000 00000000 00000000 ................
e1b78050 00000000 00000000 00000000 00000000 ................
e1b78060 00000000 00000000 00000000 00000000 ................
e1b78070 00000000 00000000 00000000 00000000 ................
kd> dc e1b78400
e1b78400 e1b78800 e1d47000 e1d47800 00000000 .....p...x......
e1b78410 00000000 00000000 00000000 00000000 ................
e1b78420 00000000 00000000 00000000 00000000 ................
e1b78430 00000000 00000000 00000000 00000000 ................
e1b78440 00000000 00000000 00000000 00000000 ................
e1b78450 00000000 00000000 00000000 00000000 ................
e1b78460 00000000 00000000 00000000 00000000 ................
e1b78470 00000000 00000000 00000000 00000000 ................
kd> dc e1b78800
e1b78800 00000000 00000001 61267018 000f001f .........p&a....
e1b78810 7cdee148 00100003 7cdee108 00100003 H..|.......|....
e1b78820 7cdbe268 00100003 7cec6c38 00000003 h..|....8l.|....
e1b78830 7ccc3c72 00100020 7cdfb718 000f000f r<.| ......|....
e1b78840 7cdb9dc8 00100003 61b7be19 001f0001 ...|.......a....
e1b78850 7ceca3a8 00000001 7cdb9988 00100003 ...|.......|....
e1b78860 613f5ec8 000f003f 6125dcd8 000f001f .^?a?.....%a....
e1b78870 7cdb9809 001f0003 7cdc3778 0002000f ...|....x7.|....
现在,我转储地址,它看起来有另一个地址。所以我转储它,看起来现在有3个地址,所以我转储第一个。我发现了什么?嗯,我发现了一个似乎是“表”的东西。如果您查看上面,每对的第二个DWORD
似乎与转储顺序中对象的访问掩码匹配。000f001f,后面是00100003,完全匹配。现在,第一个数字如何与对象相关联?如果我转储它们,它们不是有效地址!我们有几种方法可以解决这个难题。第一种是调试内核调试器并找出它在哪里进行翻译。第二种是使用这些对象之一调用内核并找出它如何进行翻译。最后一种显然是尝试我们能想到的一切来将其翻译回来。好吧,我们来尝试一些东西。让我们在这些位置之一设置一个“ba r1”,并希望Winlogon尝试使用它。我们也可以编写自己的应用程序,但我有点懒。我们需要找到一个我们认为Winlogon会经常尝试使用的对象。
我实际上认为用像记事本这样的应用程序来做这件事可能更容易。找到它打开的句柄,然后在这些句柄上设置断点,然后关闭它。它将需要关闭这些句柄!
我们已经发现,如果此位置为0,则它是一个“空闲”句柄。
现在,我在记事本对象表中设置了4个“BA R1 <地址>”。我们最多只能设置4个,所以我随机选择了一些句柄。然后我关闭记事本。我最终进入一个名为“ExMapHandleToPointer
”的函数。
eax=e1dfb8f0 ebx=00000000 ecx=e1dfb800 edx=00000000 esi=61caf508 edi=fb6e6bd0
eip=804b3971 esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei pl zr na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!ExLockHandleTableEntry+0xe:
804b3971 85f6 test esi,esi
kd>; p;r
eax=e1dfb8f0 ebx=00000000 ecx=e1dfb800 edx=00000000 esi=61caf508 edi=fb6e6bd0
eip=804b3973 esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei pl nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202
nt!ExLockHandleTableEntry+0x10:
804b3973 8975f8 mov [ebp-0x8],esi ss:0010:fb6e6b00=e1dfb800
kd>;
eax=e1dfb8f0 ebx=00000000 ecx=e1dfb800 edx=00000000 esi=61caf508 edi=fb6e6bd0
eip=804b3976 esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei pl nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202
nt!ExLockHandleTableEntry+0x13:
804b3976 0f8449f0ffff je nt!ExLockHandleTableEntry+0x5c (804b29c5) [br=0]
kd>;
eax=e1dfb8f0 ebx=00000000 ecx=e1dfb800 edx=00000000 esi=61caf508 edi=fb6e6bd0
eip=804b397c esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei pl nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202
nt!ExLockHandleTableEntry+0x15:
804b397c 0f8eabdb0000 jle nt!ExLockHandleTableEntry+0x31 (804c152d) [br=0]
kd>;
eax=e1dfb8f0 ebx=00000000 ecx=e1dfb800 edx=00000000 esi=61caf508 edi=fb6e6bd0
eip=804b3982 esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei pl nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202
nt!ExLockHandleTableEntry+0x17:
804b3982 8bc6 mov eax,esi
kd>;
eax=61caf508 ebx=00000000 ecx=e1dfb800 edx=00000000 esi=61caf508 edi=fb6e6bd0
eip=804b3984 esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei pl nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202
nt!ExLockHandleTableEntry+0x19:
804b3984 0d00000080 or eax,0x80000000
kd>;
eax=e1caf508 ebx=00000000 ecx=e1dfb800 edx=00000000 esi=61caf508 edi=fb6e6bd0
eip=804b3989 esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei ng nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000282
nt!ExLockHandleTableEntry+0x1e:
804b3989 8945fc mov [ebp-0x4],eax ss:0010:fb6e6b04=e1dfb800
kd>;
eax=e1caf508 ebx=00000000 ecx=e1dfb800 edx=00000000 esi=61caf508 edi=fb6e6bd0
eip=804b398c esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei ng nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000282
nt!ExLockHandleTableEntry+0x21:
804b398c 8b45f8 mov eax,[ebp-0x8] ss:0010:fb6e6b00=61caf508
kd>;
eax=61caf508 ebx=00000000 ecx=e1dfb800 edx=00000000 esi=61caf508 edi=fb6e6bd0
eip=804b398f esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei ng nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000282
nt!ExLockHandleTableEntry+0x24:
804b398f 8b4d0c mov ecx,[ebp+0xc] ss:0010:fb6e6b14=e1dfb8f0
kd>;
eax=61caf508 ebx=00000000 ecx=e1dfb8f0 edx=00000000 esi=61caf508 edi=fb6e6bd0
eip=804b3992 esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei ng nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000282
nt!ExLockHandleTableEntry+0x27:
804b3992 8b55fc mov edx,[ebp-0x4] ss:0010:fb6e6b04=e1caf508
kd>;
eax=61caf508 ebx=00000000 ecx=e1dfb8f0 edx=e1caf508 esi=61caf508 edi=fb6e6bd0
eip=804b3995 esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei ng nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000282
nt!ExLockHandleTableEntry+0x2a:
804b3995 0fb111 cmpxchg [ecx],edx ds:0023:e1dfb8f0=61caf508
kd>;
Breakpoint 1 hit
eax=61caf508 ebx=00000000 ecx=e1dfb8f0 edx=e1caf508 esi=61caf508 edi=fb6e6bd0
eip=804b3998 esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei pl zr na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!ExLockHandleTableEntry+0x2d:
804b3998 3bc6 cmp eax,esi
kd>;
eax=61caf508 ebx=00000000 ecx=e1dfb8f0 edx=e1caf508 esi=61caf508 edi=fb6e6bd0
eip=804b399a esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei pl zr na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!ExLockHandleTableEntry+0x2f:
804b399a 0f858ddb0000 jne nt!ExLockHandleTableEntry+0x31 (804c152d) [br=0]
kd>;
eax=61caf508 ebx=00000000 ecx=e1dfb8f0 edx=e1caf508 esi=61caf508 edi=fb6e6bd0
eip=804b39a0 esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei pl zr na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!ExLockHandleTableEntry+0x60:
804b39a0 b001 mov al,0x1
kd>;
eax=61caf501 ebx=00000000 ecx=e1dfb8f0 edx=e1caf508 esi=61caf508 edi=fb6e6bd0
eip=804b39a2 esp=fb6e6af8 ebp=fb6e6b08 iopl=0 nv up ei pl zr na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!ExLockHandleTableEntry+0x62:
804b39a2 5e pop esi
kd>;
eax=61caf501 ebx=00000000 ecx=e1dfb8f0 edx=e1caf508 esi=e1dfb8f0 edi=fb6e6bd0
eip=804b39a3 esp=fb6e6afc ebp=fb6e6b08 iopl=0 nv up ei pl zr na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!ExLockHandleTableEntry+0x63:
804b39a3 5b pop ebx
kd>;
eax=61caf501 ebx=00000078 ecx=e1dfb8f0 edx=e1caf508 esi=e1dfb8f0 edi=fb6e6bd0
eip=804b39a4 esp=fb6e6b00 ebp=fb6e6b08 iopl=0 nv up ei pl zr na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!ExLockHandleTableEntry+0x64:
804b39a4 c9 leave
kd>;
eax=61caf501 ebx=00000078 ecx=e1dfb8f0 edx=e1caf508 esi=e1dfb8f0 edi=fb6e6bd0
eip=804b39a5 esp=fb6e6b0c ebp=fb6e6bc4 iopl=0 nv up ei pl zr na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!ExLockHandleTableEntry+0x65:
804b39a5 c20800 ret 0x8
kd>;
eax=61caf501 ebx=00000078 ecx=e1dfb8f0 edx=e1caf508 esi=e1dfb8f0 edi=fb6e6bd0
eip=804b3a17 esp=fb6e6b18 ebp=fb6e6bc4 iopl=0 nv up ei pl zr na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!ExMapHandleToPointer+0x1e:
804b3a17 f6d8 neg al
kd>;
eax=61caf5ff ebx=00000078 ecx=e1dfb8f0 edx=e1caf508 esi=e1dfb8f0 edi=fb6e6bd0
eip=804b3a19 esp=fb6e6b18 ebp=fb6e6bc4 iopl=0 nv up ei ng nz ac po cy
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000297
nt!ExMapHandleToPointer+0x20:
804b3a19 1bc0 sbb eax,eax
kd>;
eax=ffffffff ebx=00000078 ecx=e1dfb8f0 edx=e1caf508 esi=e1dfb8f0 edi=fb6e6bd0
eip=804b3a1b esp=fb6e6b18 ebp=fb6e6bc4 iopl=0 nv up ei ng nz ac po cy
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000297
nt!ExMapHandleToPointer+0x22:
804b3a1b 23c6 and eax,esi
kd>;
eax=e1dfb8f0 ebx=00000078 ecx=e1dfb8f0 edx=e1caf508 esi=e1dfb8f0 edi=fb6e6bd0
eip=804b3a1d esp=fb6e6b18 ebp=fb6e6bc4 iopl=0 nv up ei ng nz na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000286
nt!ExMapHandleToPointer+0x24:
804b3a1d 5e pop esi
kd>;
eax=e1dfb8f0 ebx=00000078 ecx=e1dfb8f0 edx=e1caf508 esi=00000000 edi=fb6e6bd0
eip=804b3a1e esp=fb6e6b1c ebp=fb6e6bc4 iopl=0 nv up ei ng nz na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000286
nt!ExMapHandleToPointer+0x25:
804b3a1e c20800 ret 0x8
kd>; !handle 78 ff fcc654e0
processor number 0
PROCESS fcc654e0 SessionId: 0 Cid: 03ac Peb: 7ffdf000 ParentCid: 02e0
DirBase: 03cc6000 ObjectTable: fcc59988 TableSize: 16.
Image: NOTEPAD.EXE
Handle Table at e1dfb000 with 16 Entries in use
0078: Object: e1caf520 GrantedAccess: 000f003f (Locked)
Object: e1caf520 Type: (fcebc160) Key
ObjectHeader: e1caf508
HandleCount: 1 PointerCount: 1
Directory Object: 00000000 Name: \REGISTRY\USER
那么,我们取得了什么成就?我们注意到我们索引到句柄表并找到了那个奇怪的数字。那个奇怪的数字随后被对象头“e1caf508”替换。实际对象位于ObjectHeader + 18h,所以e1caf508 + 18 = e1caf520。
kd> !object e1caf520
Object: e1caf520 Type: (fcebc160) Key
ObjectHeader: e1caf508
HandleCount: 1 PointerCount: 1
Directory Object: 00000000 Name: \REGISTRY\USER
那么,这是如何计算出来的呢?很简单,“or eax,0x80000000”。现在让我们在其他一些对象上试试。我们有7cdee148,然后我们得到fcdee148。加上18h,我们应该能够!object。
kd> !object fcdee160
Object: fcdee160 Type: (fcebf420) Event
ObjectHeader: fcdee148
HandleCount: 1 PointerCount: 1
所以,就这么简单。
堆信息
特定进程的堆可以从内核模式显示。将上下文设置为该进程并转储PEB。PEB将指向堆的数量和可用的堆列表。然后每个堆都有一个可作为链表使用的VirtualAlloc列表。所有这些都可以在用户模式下完成,这里没有什么特别的。唯一的区别是,如果它被分页出去,您可能无法显示所有信息。无论如何,幸运的是您甚至不必经历所有这些。您只需在内核中使用!heap!只需设置上下文并像往常一样使用!heap。您可以回顾调试教程第3部分以了解更多关于堆的信息。
池
内核驱动程序通常使用来自分页池和非分页池的内存。您可以获取一个地址并执行!pool来确定其大小、标签信息等。如果您开发驱动程序,您知道当您在内核中分配内存时,它来自全局池,而不是按进程分配。因此,如果您损坏了内存,您最终可能会损坏系统中的其他驱动程序。当驱动程序分配内存时,它们通常会指定一个“池标签”,然后将其与内存关联。这就是我们如何判断哪个驱动程序分配了哪个内存。还有!poolfind *可以根据池标签查找池。您可以在线或其他地方查找有关池标签及其相关信息。
例如,让我们对之前处理的对象执行!pool。
kd> !pool e1caf520
e1caf000 size: 60 previous size: 0 (Allocated) NtFd
e1caf060 size: 20 previous size: 60 (Free) ....
e1caf080 size: 40 previous size: 20 (Allocated) NtFs
e1caf0c0 size: 20 previous size: 40 (Free) Key
e1caf0e0 size: 20 previous size: 20 (Allocated) CMVI
e1caf100 size: 40 previous size: 20 (Allocated) CMVa
e1caf140 size: 20 previous size: 40 (Free) CMVa
e1caf160 size: 40 previous size: 20 (Lookaside) Key (Protected)
e1caf1a0 size: 20 previous size: 40 (Allocated) Ntf0
e1caf1c0 size: 40 previous size: 20 (Allocated) IoNm
e1caf200 size: 20 previous size: 40 (Free) Gla8
e1caf220 size: 40 previous size: 20 (Allocated) CMVa
e1caf260 size: 40 previous size: 40 (Allocated) CMVa
e1caf2a0 size: 20 previous size: 40 (Free) Key
e1caf2c0 size: 60 previous size: 20 (Allocated) Ntfo
e1caf320 size: 40 previous size: 60 (Allocated) CMNb (Protected)
e1caf360 size: 40 previous size: 40 (Allocated) CMIn (Protected)
e1caf3a0 size: a0 previous size: 40 (Allocated) SeSd
e1caf440 size: 20 previous size: a0 (Free) Usqm
e1caf460 size: 40 previous size: 20 (Allocated) CMVa
e1caf4a0 size: 40 previous size: 40 (Allocated) CMVa
e1caf4e0 size: 20 previous size: 40 (Allocated) Ntf0
*e1caf500 size: 40 previous size: 20 (Allocated) *Key (Protected)
e1caf540 size: 20 previous size: 40 (Free) Gla8
e1caf560 size: 40 previous size: 20 (Allocated) CMVa
e1caf5a0 size: 20 previous size: 40 (Free) SYSA
e1caf5c0 size: 20 previous size: 20 (Allocated) CMNb (Protected)
e1caf5e0 size: 40 previous size: 20 (Allocated) CMNb (Protected)
e1caf620 size: 40 previous size: 40 (Allocated) CMNb (Protected)
e1caf660 size: a0 previous size: 40 (Allocated) CMDa
e1caf700 size: 40 previous size: a0 (Allocated) NtFL
e1caf740 size: 40 previous size: 40 (Allocated) CMNb (Protected)
e1caf780 size: a0 previous size: 40 (Allocated) MmSt
e1caf820 size: 40 previous size: a0 (Allocated) CMNb (Protected)
e1caf860 size: 20 previous size: 40 (Allocated) ObDi
e1caf880 size: 20 previous size: 20 (Free) CMDa
e1caf8a0 size: 20 previous size: 20 (Allocated) CMVI
e1caf8c0 size: 40 previous size: 20 (Allocated) IoNm
e1caf900 size: 80 previous size: 40 (Allocated) FSim
e1caf980 size: 20 previous size: 80 (Free) MmSt
e1caf9a0 size: 60 previous size: 20 (Allocated) CMDa
e1cafa00 size: 3e0 previous size: 60 (Allocated) NtfF
e1cafde0 size: 60 previous size: 3e0 (Allocated) CMDa
e1cafe40 size: 40 previous size: 60 (Allocated) CMVa
e1cafe80 size: 40 previous size: 40 (Allocated) CMVa
e1cafec0 size: 80 previous size: 40 (Allocated) FSim
e1caff40 size: 40 previous size: 80 (Allocated) Key (Protected)
e1caff80 size: 80 previous size: 40 (Allocated) Gla@
内核调试器将用"*"显示分配。所有内核调试器所做的就是获取地址,去掉最后3个数字并用0替换。在这种情况下,e1caf520变成了e1caf000。然后它只是从那里遍历池。它是一个链表,就像用户模式堆一样,只是它不是通过指针链接而是通过大小链接。大小指向前方和后方。在上述情况下,大小移动了5位,大小以字节表示。以上面的最后一个分配为例。
kd> db e1caff80
e1caff80 02 81 02 04 47 6c 61 40-59 01 10 08 00 00 00 00 ....Gla@Y.......
e1caff90 00 00 00 80 00 00 00 00-07 00 00 00 00 00 00 00 ................
e1caffa0 00 00 00 00 14 00 00 80-8b 00 00 00 f0 00 75 00 ..............u.
e1caffb0 00 00 00 00 d4 d0 c8 00-00 00 00 00 00 00 00 00 ................
e1caffc0 01 00 00 00 00 00 00 00-ff ff ff 00 04 00 00 00 ................
e1caffd0 08 00 00 00 c8 d0 d4 00-00 00 00 00 d4 d0 c8 00 ................
e1caffe0 01 05 00 00 00 00 00 05-15 00 00 00 85 e7 7e 2f ..............~/
e1cafff0 23 f3 f6 63 f8 9f b4 74-01 02 00 00 00 00 00 00 #..c...t........
kd> ? 02 << 5
Evaluate expression: 64 = 00000040
kd> ? 04 << 5
Evaluate expression: 128 = 00000080
中间的标志指定一个SHORT
,例如,它说明分配是空闲还是已使用。标签是接下来的4个字节,然后是已分配的内存。8字节的头部,就像在用户模式下一样。(当然,用户模式下的大分配在堆的“VirtualAlloc”列表中有一个大头部信息。)当然,内核也可以在池中分配大块内存,这如何工作?您只是获得了一些未从池中分配的页面内存,您获得自己的地址,看起来像“e1caf000”,与在用户模式下这样做有32字节头部大小信息不同,这里没有头部信息。
就绪线程
就绪线程是指那些已经准备好执行的线程,处于“就绪”状态,而不是“等待”状态。
kd> !ready 0
Ready Threads at priority 0
THREAD fcebf020 Cid 8.4 Teb: 00000000 Win32Thread: 00000000 READY
只需!ready就会显示整个线程信息。在Windows XP/2003中,还有一个!running,它将显示多处理器系统上当前正在运行的线程。另外,用户模式调试器中的~<数字><数字>s在线程之间切换,而在内核调试器中则在处理器之间切换。
CPU ID
您还可以做其他事情,例如!cpuid。
kd> !cpuid
CP F/M/S Manufacturer MHz
0 5,8,12 AuthenticAMD 350
虚拟内存使用情况
要显示内存使用情况,您可以使用!vm。
kd> !vm
*** Virtual Memory Usage ***
Physical Memory: 31613 ( 126452 Kb)
Page File: \??\C:\pagefile.sys
Current: 184320Kb Free Space: 180464Kb
Minimum: 184320Kb Maximum: 368640Kb
Available Pages: 17617 ( 70468 Kb)
ResAvail Pages: 26203 ( 104812 Kb)
Modified Pages: 543 ( 2172 Kb)
NonPagedPool Usage: 677 ( 2708 Kb)
NonPagedPool Max: 12528 ( 50112 Kb)
PagedPool 0 Usage: 2993 ( 11972 Kb)
PagedPool 1 Usage: 439 ( 1756 Kb)
PagedPool 2 Usage: 451 ( 1804 Kb)
PagedPool Usage: 3883 ( 15532 Kb)
PagedPool Maximum: 25600 ( 102400 Kb)
Shared Commit: 324 ( 1296 Kb)
Special Pool: 0 ( 0 Kb)
Free System PTEs: 14442 ( 57768 Kb)
Shared Process: 1125 ( 4500 Kb)
PagedPool Commit: 3883 ( 15532 Kb)
Driver Commit: 912 ( 3648 Kb)
Committed pages: 11945 ( 47780 Kb)
Commit limit: 73862 ( 295448 Kb)
Total Private: 5662 ( 22648 Kb)
00a4 winlogon.exe 1358 ( 5432 Kb)
00d8 services.exe 587 ( 2348 Kb)
02e4 explorer.exe 557 ( 2228 Kb)
01b4 SPOOLSV.EXE 503 ( 2012 Kb)
00e4 lsass.exe 446 ( 1784 Kb)
01e8 svchost.exe 412 ( 1648 Kb)
00a8 csrss.exe 329 ( 1316 Kb)
0374 mspaint.exe 303 ( 1212 Kb)
008c smss.exe 273 ( 1092 Kb)
018c svchost.exe 227 ( 908 Kb)
0290 winvnc.exe 164 ( 656 Kb)
0264 winmgmt.exe 154 ( 616 Kb)
02e0 cmd.exe 143 ( 572 Kb)
0228 mstask.exe 141 ( 564 Kb)
0210 regsvc.exe 59 ( 236 Kb)
0008 System 6 ( 24 Kb)
如果您正在寻找“系统内存是否不足”,您基本上需要查看“已提交页面”与“提交限制”。已提交页面是当前正在使用的内存量,而提交限制是可使用的总内存量。
下一个可以使用的工具是!memusage,它会构建系统上所有内存映射文件的映射。
kd> !memusage
loading PFN database
loading (99% complete)
Zeroed: 3163 ( 12652 kb)
Free: 20 ( 80 kb)
Standby: 14434 ( 57736 kb)
Modified: 543 ( 2172 kb)
ModifiedNoWrite: 0 ( 0 kb)
Active/Valid: 13567 ( 54268 kb)
Transition: 0 ( 0 kb)
Unknown: 0 ( 0 kb)
TOTAL: 31727 (126908 kb)
Building kernel map
Finished building kernel map
Usage Summary (in Kb):
Control Valid Standby Dirty Shared Locked PageTables name
fce7a3c8 468 16776 0 0 0 0 No Name for File
fccf6148 0 2876 0 0 0 0 mapped_file( CIM.REP )
fcd43388 0 96 0 0 0 0 mapped_file( chord.wav )
fcc40568 0 48 0 0 0 0 mapped_file( index.dat )
fcc64308 0 2420 0 0 0 0 mapped_file( mshtml.dll )
fcce8b28 0 188 0 0 0 0 mapped_file( rasppp.dll )
fcc76728 0 208 0 0 0 0 mapped_file( h323.tsp )
fccec3c8 220 92 0 0 0 0 mapped_file( netcfgx.dll )
fce48e88 1152 180 0 0 0 0 mapped_file( win32k.sys )
fcde4f28 16 460 0 0 0 0 mapped_file( wininet.dll )
fcc7f808 0 32 0 0 0 0 mapped_file( wbemcomn.dll )
fcc4d7a8 0 388 0 0 0 0 mapped_file( ntvdm.exe )
fcceb128 80 200 0 0 0 0 mapped_file( rasdlg.dll )
fcde5a08 200 896 0 120 0 0 mapped_file( shell32.dll )
fceca188 3196 1280 0 0 0 0 No Name for File
fccecd88 108 36 0 0 0 0 mapped_file( tapisrv.dll )
fcda5a28 8 204 0 0 0 0 mapped_file( msgina.dll )
fcc83348 0 300 0 0 0 0 mapped_file( wbemess.dll )
fcce9248 40 756 0 0 0 0 mapped_file( shdocvw.dll )
fcd10a68 0 32 0 0 0 0 mapped_file( notepad.exe )
fcd7dd08 0 504 0 0 0 0 mapped_file( jscript.dll )
fcc7cc48 0 32 0 0 0 0 mapped_file( mswsock.dll )
fcc4a4a8 0 116 0 0 0 0 mapped_file( dxtmsft.dll )
fcce95e8 32 40 0 0 0 0 mapped_file( ntmarta.dll )
fcce89a8 0 12 0 0 0 0 mapped_file( ntlsapi.dll )
fcc821a8 0 68 0 0 0 0 mapped_file( modemui.dll )
fcc7a4e8 16 196 0 0 0 0 mapped_file( msi.dll )
fccec688 112 32 0 0 0 0 mapped_file( rasmans.dll )
fcc4ea68 240 64 0 0 0 0 mapped_file( mspaint.exe )
fcd64ba8 0 32 0 0 0 0 mapped_file( tapisrv.dll )
fcde4aa8 0 36 0 0 0 0 mapped_file( mpr.dll )
fcdc33a8 164 64 0 0 0 0 mapped_file( winsrv.dll )
fcceeb08 0 32 0 0 0 0 mapped_file( wkssvc.dll )
fccf3ee8 0 32 0 0 0 0 mapped_file( winlogon.exe )
fcc54a28 112 36 0 0 0 0 mapped_file( cmd.exe )
fcc60408 0 148 0 0 0 0 mapped_file( dxtrans.dll )
fccee4a8 0 32 0 0 0 0 mapped_file( cryptsvc.dll )
fcc7fe08 0 32 0 0 0 0 mapped_file( SPOOLSV.EXE )
fcd7e788 80 512 0 0 0 0 mapped_file( browseui.dll )
fcc7ed28 0 0 4 0 0 0 mapped_file( software.LOG )
fcc7f128 40 264 0 8 0 0 mapped_file( netshell.dll )
fcc51ac8 0 32 0 0 0 0 mapped_file( jscript.dll )
...
-------- 12 0 0 ----- ----- 8 pagefile section (78a7)
-------- 456 0 0 ----- ----- 72 process ( mstask.exe )
-------- 452 536 116 ----- ----- 92 process ( lsass.exe )
-------- 140 0 0 ----- ----- 28 process ( smss.exe )
-------- 88 452 0 ----- ----- 52 process ( winmgmt.exe )
-------- 236 0 0 ----- ----- 36 process ( regsvc.exe )
-------- 520 0 0 ----- ----- 72 process ( winvnc.exe )
-------- 12 0 0 ----- ----- 8 pagefile section (6933)
-------- 1320 100 0 ----- 0 ----- driver ( ntoskrnl.exe )
-------- 60 16 0 ----- 0 ----- driver ( hal.dll )
...
如您所见,所有加载到系统中的文件都作为内存映射文件加载。这意味着操作系统使用磁盘上的副本持续将其加载到内存中。它不会将其复制到内存中,然后将其放入页面文件。要获取有关这些内存映射文件的信息,例如当前映射了多少引用等等,请使用!ca。
kd> !ca fcdee008
ControlArea @fcdee008
Segment: e1b764c8 Flink 0 Blink: 0
Section Ref 1 Pfn Ref 1e Mapped Views: 1
User Ref 2 Subsections 4 Flush Count: 0
File Object fcdbe728 ModWriteCount 0 System Views: 0
WaitForDel 0 Paged Usage 100 NonPaged Usage c0
Flags (90000a0) Image File HadUserReference Accessed
File: \WINNT\system32\winlogon.exe
Segment @ e1b764c8:
ControlArea fcdee008 Total Ptes 0 NonExtendPtes: 2d
WriteUserRef 2d Extend Info 0 SizeOfSegment: 2d000
Image Base 0 Committed e1b765b8 Based Addr 2d36f438
PTE Template: 3
Image commit 1000000 Image Info: 0
ProtoPtes e1b76500
Subsection 1. @ fcdee040
ControlArea: fcdee008 Starting Sector 0 Number Of Sectors 2
Base Pte e1b76500 Ptes In subsect 1 Unused Ptes 0
Flags 15 Sector Offset 0 Protection 1
ReadOnly SubsectionStatic
Subsection 2. @ fcdee060
ControlArea: fcdee008 Starting Sector 2 Number Of Sectors ff
Base Pte e1b76504 Ptes In subsect 20 Unused Ptes 0
Flags 35 Sector Offset 0 Protection 3
ReadOnly SubsectionStatic
Subsection 3. @ fcdee080
ControlArea: fcdee008 Starting Sector 101 Number Of Sectors 11
Base Pte e1b76584 Ptes In subsect 3 Unused Ptes 0
Flags 55 Sector Offset 0 Protection 5
ReadOnly SubsectionStatic
Subsection 4. @ fcdee0a0
ControlArea: fcdee008 Starting Sector 112 Number Of Sectors 48
Base Pte e1b76590 Ptes In subsect 9 Unused Ptes 0
Flags 15 Sector Offset 0 Protection 1
ReadOnly SubsectionStatic
您会注意到这台机器上Winlogon只有一个映射视图。如果它显示0个视图并且进程正在运行,请不要担心;查看列表,该文件可能有两个列表,另一个是正在使用的mapview。
结论
这是内核调试器及其一些功能的简单介绍。虽然我没有涵盖所有内容,但我希望您现在至少了解了基础知识并能够进行导航。总有帮助和http://www.google.com/!您每天都会学到新东西;一个人不可能知道所有事情,但他们可以尝试!我努力确保所提供的信息尽可能准确,但仍可能有遗漏,所以如果我错过了什么或有任何误报,请告诉我。将来我可能会添加更多内核调试教程,涵盖其他主题。我们甚至还没有涵盖IRP!
您通常不会处理列表中每一个细节。您通常会使用大量不同的命令来获取不同的信息,并且每个命令您可能只会使用数据的子集,这没关系。您甚至可能不关心其他数据是什么,甚至不知道它是什么。也许即使您知道它是什么,它对您也永远不会有用。所以,如果您不知道显示的信息是什么,或者如果您无法在线找到任何参考资料,请不要气馁。但是,如果您确实需要知道,您总是可以在地址上设置ba并跟踪谁在使用它做什么。