转:
http://advdbg.org/blogs/advdbg_system/articles/1492.aspx
使用内核调试会话也可以执行一些用户态调试任务,比如向位于用户态的模块设置断点。但这样做与使用用户态调试器有什么不同呢?我们就以向NTDLL.dll模块的ZwTerminateProcess函数(Stub)为例谈谈二者的区别。
区别一、在内核调试会话中设置这个断点的“难度”略大些。这是因为NTDLL不属于内核态的模块,所以内核会话通常不会加载这个模块(的符号),因此当执行bp命令时很可能被自动蜕化为bu命令。
0: kd> bp ntdll!ZwTerminateProcess
Bp expression 'ntdll!ZwTerminateProcess' could not be resolved, adding deferred bp
恢复执行后,一般的操作也不会触发调试器来加载NTDLL模块和解决这个未决的断点。因此再中断下来,重新加载符号也可能没有用:
2: kd> .reload
Connected to Windows Vista 6000 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
................................................................................ .............................................
Loading User Symbols
Loading unloaded module list
........
2: kd> bl
0 eu 0001 (0001) (ntdll!ZwTerminateProcess)
使用.reload命令强制加载这个模块也不那么容易:
0: kd> .reload /s /f ntdll.dll
"ntdll.dll" was not found in the image list.
Debugger will attempt to load "ntdll.dll" at given base 00000000.
Please provide the full image name, including the extension (i.e. kernel32.dll)
for more reliable results.Base address and size overrides can be given as
.reload <image.ext>=<base>,<size>.
Unable to add module at 00000000
那么该如何设置呢?方法一需要以下几步:
1.A 使用!process命令显示当前进程:
kd> !process
PROCESS 80af22a0 SessionId: none Cid: 0000 Peb: 00000000 ParentCid: 0000
DirBase: 00039000 ObjectTable: e1001e38 HandleCount: 240.
Image: Idle
如果像上面这样是IDLE进程或者是System这些没有用户态的进程,那么就需要执行下面一步,否则跳到1.C。
1.B 使用!process 0 0命令列出所有进程,然后选一个普通的Windows进程,并切换到这个进程:
kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
...
PROCESS 82748330 SessionId: 0 Cid: 0110 Peb: 7ffde000 ParentCid: 059c
DirBase: 13076000 ObjectTable: e1a55640 HandleCount: 72.
Image: notepad.exe
kd> .PROCESS 82748330
Implicit process is now 82748330
WARNING: .cache forcedecodeuser is not enabled
1.C 执行.reload或.reload /user重新加载符号:
kd> .reload
Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
................................................................................................
Loading User Symbols
...............................
Loading unloaded module list
..............................
kd> lm m ntdll
start end module name
7c800000 7c8c3000 ntdll (pdb symbols) d:/symbols/ntdll.pdb/9A2A73EBE8194059A14361915257B0B01/ntdll.pdb
第二种看起来可能更费事的方法就是在系统服务的内核函数设置断点,断点命中后,执行栈回溯这样的命令,再执行.reload(加/user会省些时间,不是必须)。例如:
0: kd> bp nt!NtTerminateProcess
0: kd> g
Breakpoint 2 hit
nt!NtTerminateProcess:
81a1b043 8bff mov edi,edi
0: kd> kv
ChildEBP RetAddr Args to Child
9f272d54 8188c96a 00000000 00000000 0021f998 nt!NtTerminateProcess
9f272d54 77c20f34 00000000 00000000 0021f998 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ 9f272d64)
WARNING: Frame IP not in any known module. Following frames may be wrong.
0021f998 7682d873 00000000 77e8f3b0 ffffffff 0x77c20f34
...
0: kd> .reload
Connected to Windows Vista 6000 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
..............................................................................................................................
Loading User Symbols
..................
再执行kv:
0: kd> kv
ChildEBP RetAddr Args to Child
9f272d54 8188c96a 00000000 00000000 0021f998 nt!NtTerminateProcess
9f272d54 77c20f34 00000000 00000000 0021f998 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ 9f272d64)
0021f978 77c20580 77bfa35f 00000000 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0021f97c 77bfa35f 00000000 00000000 00af0e70 ntdll!NtTerminateProcess+0xc (FPO: [2,0,0])
0021f998 7682d872 00000000 77e8f3b0 ffffffff ntdll!RtlExitUserProcess+0x39 (FPO: [Non-Fpo])
此时可以确信内核调试会话已经加载NTDLL的符号了,再显示断点:
0: kd> bl
0 e 77c20574 0001 (0001) ntdll!NtTerminateProcess
2 e 81a1b043 0001 (0001) nt!NtTerminateProcess
这个显示表明内核调试会话已经落实了这个用户态的断点。
如果是在加载NTDLL模块后再执行bp命令,恢复执行后,KD会有一个提示告诉我们它成功的向断点位置写入了INT 3。
0: kd> bp ntdll!ZwTerminateProcess
0: kd> bl
0 e 77c20574 0001 (0001) ntdll!NtTerminateProcess
0: kd> g
KD: write to 77c20574 ok
相对而言,如果是在用户态调试会话中,因为NTDLL会被映射到所有用户态进程中,而且ZwTerminateProcess是导出的函数,所以bp ntdll!ZwTerminateProcess会非常顺利的执行。
区别二、断点的作用范围不同,在内核调试会话中设置的ntdll!NtTerminateProcess断点会影响所有进程(可能有特例),而在用户态调试中对这个位置设置的断点只对当前进程有效。举例来说,刚才在内核调试会话中设置bp断点时的当前进程是notepad,但是当我们关闭计算器进程时这个断点也会命中。甚至当我们新启动一个 WinMine程序,然后关闭它时,断点也会命中。
相对而言,如果是在调试notepad进程的用户态调试会话中对ntdll!NtTerminateProcess设置一个断点,那么这绝不会影响其它进程。
那么为什么有这个差异呢?
首先解释一下,为什么在内核调试会话中设置的断点会影响所有进程。还是通过试验来说明,我们先想办法观察到我们设置的断点所对应的INT 3指令。当KD落实我们的断点后,将目标再中断到调试器,这时无论是直接观察线性地址还是物理地址,都看不到INT 3:
1: kd> dd 77c20574
77c20574 000152cc 0300ba00 12ff7ffe 900008c2
77c20584 000153b8 0300ba00 12ff7ffe 900008c2
77c20594 000154b8 0300ba00 12ff7ffe 00498dc3
77c205a4 000155b8 0300ba00 12ff7ffe 00498dc3
77c205b4 000156b8 0300ba00 12ff7ffe 00498dc3
77c205c4 000157b8 0300ba00 12ff7ffe 900010c2
77c205d4 000158b8 0300ba00 12ff7ffe 900018c2
77c205e4 000159b8 0300ba00 12ff7ffe 900010c2
0: kd> !pte 77c20574
VA 77c20574
PDE at 00000000C0601DF0 PTE at 00000000C03BE100
contains 000000001C9AC867 contains 000000001DDDD025
pfn 1c9ac ---DA--UWEV pfn 1dddd ----A--UREV
0: kd> !dd 1dddd574
#1dddd574 000152b8 0300ba00 12ff7ffe 900008c2
#1dddd584 000153b8 0300ba00 12ff7ffe 900008c2
#1dddd594 000154b8 0300ba00 12ff7ffe 00498dc3
#1dddd5a4 000155b8 0300ba00 12ff7ffe 00498dc3
#1dddd5b4 000156b8 0300ba00 12ff7ffe 00498dc3
#1dddd5c4 000157b8 0300ba00 12ff7ffe 900010c2
#1dddd5d4 000158b8 0300ba00 12ff7ffe 900018c2
#1dddd5e4 000159b8 0300ba00 12ff7ffe 900010c2
这是因为调试器在将目标中断到调试器之前会恢复已经设置的断点,按Ctrl+Alt+D启用WinDBG与KD的通信过程后就可以看到这样的信息:
DbgKdRestoreBreakPoint(1) returns 00000000
当恢复执行时,WinDBG会重新把断点写入:
DbgKdWriteBreakPoint(77c20574) returns 00000000, 1
那么如何观察到写入的INT 3呢?一种很惬意的方法就是使用ITP这样的硬件调试器,用了ITP,对付这样的任务真是手到擒来(图1)。
图1 使用硬件调试器观察断点指令(0xCC)
因为NTDLL是映射到所有进程中的,所以每个进程执行NtTerminateProcess函数时都会撞见这个0xCC,于是乎这个断点对所有进程都起作用也就在情理之中了。
下面再说说另一种情况,也就是在用户态调试器中对ntdll!NtTerminateProcess设置断点,难道这时就没有把0xCC写在大家都会“撞见”的地方么?的确如此。
我们在内核调试会话中使用bc *命令清除所有断点,并恢复执行一次,而且通过ITP观察确保刚才的0xcc已经不在。然后在目标系统中启动系统中自带的NTSD来调试计算器程序,并使用bp ntdll!NtTerminateProcess设置一个断点。恢复执行一次,以便让调试器写入这个断点。然后退出计算器程序,这时计算器程序会中断到 NTSD,NTSD中不做分析,直接用g命令恢复执行,这下,我们前面设置的nt!NtTerminateProcess断点会命中,也就是中断到内核调试器中。
在内核调试器中,观察nt!NtTerminateProcess所对应的线性地址:
1: kd> dd 77c20574
77c20574 000152cc 0300ba00 12ff7ffe 900008c2
77c20584 000153b8 0300ba00 12ff7ffe 900008c2
77c20594 000154b8 0300ba00 12ff7ffe 00498dc3
77c205a4 000155b8 0300ba00 12ff7ffe 00498dc3
77c205b4 000156b8 0300ba00 12ff7ffe 00498dc3
77c205c4 000157b8 0300ba00 12ff7ffe 900010c2
77c205d4 000158b8 0300ba00 12ff7ffe 900018c2
77c205e4 000159b8 0300ba00 12ff7ffe 900010c2
睁大眼睛看那个0xCC,对的,这里的确有0xCC。因为内核断点已经取消了,这一定是用户态调试器写入的。
接下来的问题是,既然这里有0xCC,那么为什么不影响其它进程呢?注意这个线性地址与前面的一模一样。
其中的奥妙在于这个线性地址已经不再是前面那个物理地址了:
1: kd> !pte 77c20574
VA 77c20574
PDE at 00000000C0601DF0 PTE at 00000000C03BE100
contains 00000000012E0867 contains 00000000049BD025
pfn 12e0 ---DA--UWEV pfn 49bd ----A--UREV
虽然还同是一个线性地址,但是它现在对应的物理地址变成了49bd574。观察这个物理地址,其内容与刚才使用线性地址的得到的结果是一样的:
1: kd> !dd 49bd574
# 49bd574 000152cc 0300ba00 12ff7ffe 900008c2
# 49bd584 000153b8 0300ba00 12ff7ffe 900008c2
# 49bd594 000154b8 0300ba00 12ff7ffe 00498dc3
# 49bd5a4 000155b8 0300ba00 12ff7ffe 00498dc3
# 49bd5b4 000156b8 0300ba00 12ff7ffe 00498dc3
# 49bd5c4 000157b8 0300ba00 12ff7ffe 900010c2
# 49bd5d4 000158b8 0300ba00 12ff7ffe 900018c2
# 49bd5e4 000159b8 0300ba00 12ff7ffe 900010c2
而此时,物理地址1dddd574那里根本没有0xCC。
说到这里,谜团基本揭开了。事实上, 对于一个普通的进程,系统会把NTDLL的代码映射给它,如果这个进程始终很普通,那么它便会永远使用这份映射过来的代码。但是当它要修改代码时,系统会执行所谓的 Copy on Write动作,为其复制一份,让它来写。结合我们的情况,当在用户态调试会话中向NTDLL中设置断点时,系统为其复制了一份代码,让它去写,因此它写入的断点只有它自己“撞的到”,不会影响其它进程。但是当在内核会话中写入断点时,因为是内核调试引擎执行的写动作,所以没有触发Copy on Write,因此KD写入的断点写在了公共的代码上,会影响到使用这个公共代码的所有进程。
怎么在内核调试模式调应用程序(驱动程序,应用程序一锅汇了)
转: http://bbs.driverdevelop.com/htm_data/125/0901/115354.html
先看一下上面的张银奎大侠的文章,然后我们再分析一下怎么做。
看了上面的文章,也许你已经成功了,那就没必要看下面的文字 ,如果你还没有明白,我们就实操一下:
环境:
双机内核模式调试状态,即通过windbg连接另一台机器调试驱动,不管对方是真机还是虚拟机。此时,我们可以调试驱动程序(我假设你已经会调驱动,并且环境准备好了,想调的应用程序的pdb文件也准备好了).
通常,我们可能会在目标机(被调试的机器上)再运行一个windbg或者用vc的远程调式工具进行调试。但是我现在的所有工程,包括测试程序都是用winddk编译的,改用vc调试比较麻烦,现在就是想在调驱动的同时继续调试我的测试程序,这样方便又直接。
现在处于kd> 状态 ,可以直接设置驱动程序的断点,但是你要是设置应用程序的断点,会收到错误消息,说找不到这个模块。好,我们就来记它能找到:
!process 0 0
会出列进程相关信息,如
PROCESS 820a36d0 SessionId: 0 Cid: 07cc Peb: 7ffd9000 ParentCid: 01d4
DirBase: 09b00220 ObjectTable: e18ecc48 HandleCount: 104.
Image: alg.exe
PROCESS 820d2020 SessionId: 0 Cid: 0118 Peb: 7ffdf000 ParentCid: 00c4
DirBase: 09b00260 ObjectTable: e19252b0 HandleCount: 470.
Image: explorer.exe
PROCESS 82232b58 SessionId: 0 Cid: 016c Peb: 7ffdf000 ParentCid: 0118
DirBase: 09b002c0 ObjectTable: e188c9d8 HandleCount: 76.
Image: epsng_certd.exe
PROCESS 82170020 SessionId: 0 Cid: 00d8 Peb: 7ffde000 ParentCid: 0118
DirBase: 09b002a0 ObjectTable: e1916e98 HandleCount: 36.
Image: VMwareTray.exe
PROCESS 821353d8 SessionId: 0 Cid: 0170 Peb: 7ffdc000 ParentCid: 0118
DirBase: 09b002e0 ObjectTable: e1c8e138 HandleCount: 128.
Image: VMwareUser.exe
找到你关心的进程 ,如 explorer.exe ,它的进程 PROCESS 值,其实就是EPROCESS, 为 820d2020
然后,你要切换进程上下文
.Process 820d2020 注意:这里的Process 前面有一个点,不要看成你的显示器上的灰尘
这样,就处于这个进程上下文了。
.reload /user 加载用户模式符号文件 (保证你的符号文件处于正确的目录中)
然后就可以直接 下断点了,比如
bp explorer!xxxxfunc
其它的没啥区别了
使用内核调试会话也可以执行一些用户态调试任务,比如向位于用户态的模块设置断点。但这样做与使用用户态调试器有什么不同呢?我们就以向NTDLL.dll模块的ZwTerminateProcess函数(Stub)为例谈谈二者的区别。
区别一、在内核调试会话中设置这个断点的“难度”略大些。这是因为NTDLL不属于内核态的模块,所以内核会话通常不会加载这个模块(的符号),因此当执行bp命令时很可能被自动蜕化为bu命令。
0: kd> bp ntdll!ZwTerminateProcess
Bp expression 'ntdll!ZwTerminateProcess' could not be resolved, adding deferred bp
恢复执行后,一般的操作也不会触发调试器来加载NTDLL模块和解决这个未决的断点。因此再中断下来,重新加载符号也可能没有用:
2: kd> .reload
Connected to Windows Vista 6000 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
................................................................................ .............................................
Loading User Symbols
Loading unloaded module list
........
2: kd> bl
0 eu 0001 (0001) (ntdll!ZwTerminateProcess)
使用.reload命令强制加载这个模块也不那么容易:
0: kd> .reload /s /f ntdll.dll
"ntdll.dll" was not found in the image list.
Debugger will attempt to load "ntdll.dll" at given base 00000000.
Please provide the full image name, including the extension (i.e. kernel32.dll)
for more reliable results.Base address and size overrides can be given as
.reload <image.ext>=<base>,<size>.
Unable to add module at 00000000
那么该如何设置呢?方法一需要以下几步:
1.A 使用!process命令显示当前进程:
kd> !process
PROCESS 80af22a0 SessionId: none Cid: 0000 Peb: 00000000 ParentCid: 0000
DirBase: 00039000 ObjectTable: e1001e38 HandleCount: 240.
Image: Idle
如果像上面这样是IDLE进程或者是System这些没有用户态的进程,那么就需要执行下面一步,否则跳到1.C。
1.B 使用!process 0 0命令列出所有进程,然后选一个普通的Windows进程,并切换到这个进程:
kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
...
PROCESS 82748330 SessionId: 0 Cid: 0110 Peb: 7ffde000 ParentCid: 059c
DirBase: 13076000 ObjectTable: e1a55640 HandleCount: 72.
Image: notepad.exe
kd> .PROCESS 82748330
Implicit process is now 82748330
WARNING: .cache forcedecodeuser is not enabled
1.C 执行.reload或.reload /user重新加载符号:
kd> .reload
Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
................................................................................................
Loading User Symbols
...............................
Loading unloaded module list
..............................
kd> lm m ntdll
start end module name
7c800000 7c8c3000 ntdll (pdb symbols) d:/symbols/ntdll.pdb/9A2A73EBE8194059A14361915257B0B01/ntdll.pdb
第二种看起来可能更费事的方法就是在系统服务的内核函数设置断点,断点命中后,执行栈回溯这样的命令,再执行.reload(加/user会省些时间,不是必须)。例如:
0: kd> bp nt!NtTerminateProcess
0: kd> g
Breakpoint 2 hit
nt!NtTerminateProcess:
81a1b043 8bff mov edi,edi
0: kd> kv
ChildEBP RetAddr Args to Child
9f272d54 8188c96a 00000000 00000000 0021f998 nt!NtTerminateProcess
9f272d54 77c20f34 00000000 00000000 0021f998 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ 9f272d64)
WARNING: Frame IP not in any known module. Following frames may be wrong.
0021f998 7682d873 00000000 77e8f3b0 ffffffff 0x77c20f34
...
0: kd> .reload
Connected to Windows Vista 6000 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
..............................................................................................................................
Loading User Symbols
..................
再执行kv:
0: kd> kv
ChildEBP RetAddr Args to Child
9f272d54 8188c96a 00000000 00000000 0021f998 nt!NtTerminateProcess
9f272d54 77c20f34 00000000 00000000 0021f998 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ 9f272d64)
0021f978 77c20580 77bfa35f 00000000 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0021f97c 77bfa35f 00000000 00000000 00af0e70 ntdll!NtTerminateProcess+0xc (FPO: [2,0,0])
0021f998 7682d872 00000000 77e8f3b0 ffffffff ntdll!RtlExitUserProcess+0x39 (FPO: [Non-Fpo])
此时可以确信内核调试会话已经加载NTDLL的符号了,再显示断点:
0: kd> bl
0 e 77c20574 0001 (0001) ntdll!NtTerminateProcess
2 e 81a1b043 0001 (0001) nt!NtTerminateProcess
这个显示表明内核调试会话已经落实了这个用户态的断点。
如果是在加载NTDLL模块后再执行bp命令,恢复执行后,KD会有一个提示告诉我们它成功的向断点位置写入了INT 3。
0: kd> bp ntdll!ZwTerminateProcess
0: kd> bl
0 e 77c20574 0001 (0001) ntdll!NtTerminateProcess
0: kd> g
KD: write to 77c20574 ok
相对而言,如果是在用户态调试会话中,因为NTDLL会被映射到所有用户态进程中,而且ZwTerminateProcess是导出的函数,所以bp ntdll!ZwTerminateProcess会非常顺利的执行。
区别二、断点的作用范围不同,在内核调试会话中设置的ntdll!NtTerminateProcess断点会影响所有进程(可能有特例),而在用户态调试中对这个位置设置的断点只对当前进程有效。举例来说,刚才在内核调试会话中设置bp断点时的当前进程是notepad,但是当我们关闭计算器进程时这个断点也会命中。甚至当我们新启动一个 WinMine程序,然后关闭它时,断点也会命中。
相对而言,如果是在调试notepad进程的用户态调试会话中对ntdll!NtTerminateProcess设置一个断点,那么这绝不会影响其它进程。
那么为什么有这个差异呢?
首先解释一下,为什么在内核调试会话中设置的断点会影响所有进程。还是通过试验来说明,我们先想办法观察到我们设置的断点所对应的INT 3指令。当KD落实我们的断点后,将目标再中断到调试器,这时无论是直接观察线性地址还是物理地址,都看不到INT 3:
1: kd> dd 77c20574
77c20574 000152cc 0300ba00 12ff7ffe 900008c2
77c20584 000153b8 0300ba00 12ff7ffe 900008c2
77c20594 000154b8 0300ba00 12ff7ffe 00498dc3
77c205a4 000155b8 0300ba00 12ff7ffe 00498dc3
77c205b4 000156b8 0300ba00 12ff7ffe 00498dc3
77c205c4 000157b8 0300ba00 12ff7ffe 900010c2
77c205d4 000158b8 0300ba00 12ff7ffe 900018c2
77c205e4 000159b8 0300ba00 12ff7ffe 900010c2
0: kd> !pte 77c20574
VA 77c20574
PDE at 00000000C0601DF0 PTE at 00000000C03BE100
contains 000000001C9AC867 contains 000000001DDDD025
pfn 1c9ac ---DA--UWEV pfn 1dddd ----A--UREV
0: kd> !dd 1dddd574
#1dddd574 000152b8 0300ba00 12ff7ffe 900008c2
#1dddd584 000153b8 0300ba00 12ff7ffe 900008c2
#1dddd594 000154b8 0300ba00 12ff7ffe 00498dc3
#1dddd5a4 000155b8 0300ba00 12ff7ffe 00498dc3
#1dddd5b4 000156b8 0300ba00 12ff7ffe 00498dc3
#1dddd5c4 000157b8 0300ba00 12ff7ffe 900010c2
#1dddd5d4 000158b8 0300ba00 12ff7ffe 900018c2
#1dddd5e4 000159b8 0300ba00 12ff7ffe 900010c2
这是因为调试器在将目标中断到调试器之前会恢复已经设置的断点,按Ctrl+Alt+D启用WinDBG与KD的通信过程后就可以看到这样的信息:
DbgKdRestoreBreakPoint(1) returns 00000000
当恢复执行时,WinDBG会重新把断点写入:
DbgKdWriteBreakPoint(77c20574) returns 00000000, 1
那么如何观察到写入的INT 3呢?一种很惬意的方法就是使用ITP这样的硬件调试器,用了ITP,对付这样的任务真是手到擒来(图1)。

图1 使用硬件调试器观察断点指令(0xCC)
因为NTDLL是映射到所有进程中的,所以每个进程执行NtTerminateProcess函数时都会撞见这个0xCC,于是乎这个断点对所有进程都起作用也就在情理之中了。
下面再说说另一种情况,也就是在用户态调试器中对ntdll!NtTerminateProcess设置断点,难道这时就没有把0xCC写在大家都会“撞见”的地方么?的确如此。
我们在内核调试会话中使用bc *命令清除所有断点,并恢复执行一次,而且通过ITP观察确保刚才的0xcc已经不在。然后在目标系统中启动系统中自带的NTSD来调试计算器程序,并使用bp ntdll!NtTerminateProcess设置一个断点。恢复执行一次,以便让调试器写入这个断点。然后退出计算器程序,这时计算器程序会中断到 NTSD,NTSD中不做分析,直接用g命令恢复执行,这下,我们前面设置的nt!NtTerminateProcess断点会命中,也就是中断到内核调试器中。
在内核调试器中,观察nt!NtTerminateProcess所对应的线性地址:
1: kd> dd 77c20574
77c20574 000152cc 0300ba00 12ff7ffe 900008c2
77c20584 000153b8 0300ba00 12ff7ffe 900008c2
77c20594 000154b8 0300ba00 12ff7ffe 00498dc3
77c205a4 000155b8 0300ba00 12ff7ffe 00498dc3
77c205b4 000156b8 0300ba00 12ff7ffe 00498dc3
77c205c4 000157b8 0300ba00 12ff7ffe 900010c2
77c205d4 000158b8 0300ba00 12ff7ffe 900018c2
77c205e4 000159b8 0300ba00 12ff7ffe 900010c2
睁大眼睛看那个0xCC,对的,这里的确有0xCC。因为内核断点已经取消了,这一定是用户态调试器写入的。
接下来的问题是,既然这里有0xCC,那么为什么不影响其它进程呢?注意这个线性地址与前面的一模一样。
其中的奥妙在于这个线性地址已经不再是前面那个物理地址了:
1: kd> !pte 77c20574
VA 77c20574
PDE at 00000000C0601DF0 PTE at 00000000C03BE100
contains 00000000012E0867 contains 00000000049BD025
pfn 12e0 ---DA--UWEV pfn 49bd ----A--UREV
虽然还同是一个线性地址,但是它现在对应的物理地址变成了49bd574。观察这个物理地址,其内容与刚才使用线性地址的得到的结果是一样的:
1: kd> !dd 49bd574
# 49bd574 000152cc 0300ba00 12ff7ffe 900008c2
# 49bd584 000153b8 0300ba00 12ff7ffe 900008c2
# 49bd594 000154b8 0300ba00 12ff7ffe 00498dc3
# 49bd5a4 000155b8 0300ba00 12ff7ffe 00498dc3
# 49bd5b4 000156b8 0300ba00 12ff7ffe 00498dc3
# 49bd5c4 000157b8 0300ba00 12ff7ffe 900010c2
# 49bd5d4 000158b8 0300ba00 12ff7ffe 900018c2
# 49bd5e4 000159b8 0300ba00 12ff7ffe 900010c2
而此时,物理地址1dddd574那里根本没有0xCC。
说到这里,谜团基本揭开了。事实上, 对于一个普通的进程,系统会把NTDLL的代码映射给它,如果这个进程始终很普通,那么它便会永远使用这份映射过来的代码。但是当它要修改代码时,系统会执行所谓的 Copy on Write动作,为其复制一份,让它来写。结合我们的情况,当在用户态调试会话中向NTDLL中设置断点时,系统为其复制了一份代码,让它去写,因此它写入的断点只有它自己“撞的到”,不会影响其它进程。但是当在内核会话中写入断点时,因为是内核调试引擎执行的写动作,所以没有触发Copy on Write,因此KD写入的断点写在了公共的代码上,会影响到使用这个公共代码的所有进程。
怎么在内核调试模式调应用程序(驱动程序,应用程序一锅汇了)
转: http://bbs.driverdevelop.com/htm_data/125/0901/115354.html
先看一下上面的张银奎大侠的文章,然后我们再分析一下怎么做。
看了上面的文章,也许你已经成功了,那就没必要看下面的文字 ,如果你还没有明白,我们就实操一下:
环境:
双机内核模式调试状态,即通过windbg连接另一台机器调试驱动,不管对方是真机还是虚拟机。此时,我们可以调试驱动程序(我假设你已经会调驱动,并且环境准备好了,想调的应用程序的pdb文件也准备好了).
通常,我们可能会在目标机(被调试的机器上)再运行一个windbg或者用vc的远程调式工具进行调试。但是我现在的所有工程,包括测试程序都是用winddk编译的,改用vc调试比较麻烦,现在就是想在调驱动的同时继续调试我的测试程序,这样方便又直接。
现在处于kd> 状态 ,可以直接设置驱动程序的断点,但是你要是设置应用程序的断点,会收到错误消息,说找不到这个模块。好,我们就来记它能找到:
!process 0 0
会出列进程相关信息,如
PROCESS 820a36d0 SessionId: 0 Cid: 07cc Peb: 7ffd9000 ParentCid: 01d4
DirBase: 09b00220 ObjectTable: e18ecc48 HandleCount: 104.
Image: alg.exe
PROCESS 820d2020 SessionId: 0 Cid: 0118 Peb: 7ffdf000 ParentCid: 00c4
DirBase: 09b00260 ObjectTable: e19252b0 HandleCount: 470.
Image: explorer.exe
PROCESS 82232b58 SessionId: 0 Cid: 016c Peb: 7ffdf000 ParentCid: 0118
DirBase: 09b002c0 ObjectTable: e188c9d8 HandleCount: 76.
Image: epsng_certd.exe
PROCESS 82170020 SessionId: 0 Cid: 00d8 Peb: 7ffde000 ParentCid: 0118
DirBase: 09b002a0 ObjectTable: e1916e98 HandleCount: 36.
Image: VMwareTray.exe
PROCESS 821353d8 SessionId: 0 Cid: 0170 Peb: 7ffdc000 ParentCid: 0118
DirBase: 09b002e0 ObjectTable: e1c8e138 HandleCount: 128.
Image: VMwareUser.exe
找到你关心的进程 ,如 explorer.exe ,它的进程 PROCESS 值,其实就是EPROCESS, 为 820d2020
然后,你要切换进程上下文
.Process 820d2020 注意:这里的Process 前面有一个点,不要看成你的显示器上的灰尘
这样,就处于这个进程上下文了。
.reload /user 加载用户模式符号文件 (保证你的符号文件处于正确的目录中)
然后就可以直接 下断点了,比如
bp explorer!xxxxfunc
其它的没啥区别了