使用VS调试时,被调试进程如何被断下来的。
不是什么新鲜的东西,很多书上写的非常详细了,不过有人问到,简要的扯下这个VS如何断下进程的。
++++++++++++++++++++++++++
当用VS的时候,按下F10后,为什么能够停在程序的入口处呢?
这个问题首先从windows API CreateProcess的Process Creation Flags说起。如果在创建进程的时候加了这个DEBUG_PROCESS这个flag. 那么进程环境块(Process environment block, 简称PEB)中的BeingDebugged设为1. 如果用Windbg的命令!peb来看的话
0:000> !peb
PEB at 7ffd8000
InheritedAddressSpace: No
ReadImageFileExecOptions: No
BeingDebugged: Yes <== 这个是yes.
PEB at 7ffd8000
InheritedAddressSpace: No
ReadImageFileExecOptions: No
BeingDebugged: Yes <== 这个是yes.
下一步是LdrpInitializeProcess函数被调用来初始化进程的时候,这个函数会去检查BeingDebugged这个位,如果为1的话,就会调用函数ntdll!DbgBreakPoint这个函数。
比如说用windbg来开始调试notepad.exe的话,首先断下来的地方在ntdll!DbgBreakPoint函数里
0:000> kL
ChildEBP RetAddr
0007fb1c 7c940442 ntdll!DbgBreakPoint
0007fc94 7c9210af ntdll!LdrpInitializeProcess+0xffa
0007fd1c 7c90e457 ntdll!_LdrpInitialize+0x183
00000000 00000000 ntdll!KiUserApcDispatcher+0x7
ChildEBP RetAddr
0007fb1c 7c940442 ntdll!DbgBreakPoint
0007fc94 7c9210af ntdll!LdrpInitializeProcess+0xffa
0007fd1c 7c90e457 ntdll!_LdrpInitialize+0x183
00000000 00000000 ntdll!KiUserApcDispatcher+0x7
这个函数非常简单,只有一个int 3指令。
ntdll!DbgBreakPoint:
7c90120e cc int 3
7c90120f c3 ret
7c90120e cc int 3
7c90120f c3 ret
这里是任何debuger第一次断下来进程的地方。无论是windbg还是VS.
在这个时候notepad.exe的binary已经被load到内存里面了,但是还没有开始执行。这个时候VS就可以加载符号文件,并且知道用户写的代码的入口在什么地方。于是在入口处设下一个断点,然后直接恢复进程的执行,而用户并不会察觉到其实VS debuger已经中断过进程一次了,而是直接发现程序停在了main处。
哪么调试器的attach功能,和break当前被调试的进程又是如何实现的呢?程序已经跑起来了,这个时候可能没有被设置任何一个断点。
这个是利用Windows API CreateRemoteThread来实现的。调试器会调用这个函数在目标进程中创建一个线程,这个线程执行的代码如下:
00adffc8 7c951e40 ntdll!DbgBreakPoint
00adfff4 00000000 ntdll!DbgUiRemoteBreakin+0x2d
00adfff4 00000000 ntdll!DbgUiRemoteBreakin+0x2d
还是ntdll!DbgBreakPoint这个函数,由于这个函数里面有个int 3指令。CPU执行到了int 3指令的时候就会触发异常处理,最终debuger会处理这个异常,并且把进程中的所有线程挂起。
在windbg中可以看到notepad.exe被断下来以后有2个线程:
0:001> ~*
0 Id: 4b8c.3dd4 Suspend: 1 Teb: 7ffde000 Unfrozen
Start: notepad!WinMainCRTStartup (0100739d)
Priority: 0 Priority class: 32 Affinity: 3
. 1 Id: 4b8c.4c14 Suspend: 1 Teb: 7ffdd000 Unfrozen
Start: ntdll!DbgUiRemoteBreakin (7c951e13)
Priority: 0 Priority class: 32 Affinity: 3
0 Id: 4b8c.3dd4 Suspend: 1 Teb: 7ffde000 Unfrozen
Start: notepad!WinMainCRTStartup (0100739d)
Priority: 0 Priority class: 32 Affinity: 3
. 1 Id: 4b8c.4c14 Suspend: 1 Teb: 7ffdd000 Unfrozen
Start: ntdll!DbgUiRemoteBreakin (7c951e13)
Priority: 0 Priority class: 32 Affinity: 3
线程0就是notepad本身的main thread.
线程1就是远程创建的线程。
当然在VS debuger里面不会看到ntdll!DbgBreakPoint这个函数,因为VS比较友好,会自动把当前处理了的线程切换到main thread上面。