调试器是如何工作的?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (29)

我一直在想,调试器是如何工作的?特别是可以“附加”到已经运行的可执行文件。我知道编译器会将代码翻译成机器语言,但那么调试器如何“知道”它所连接的是什么?

提问于
用户回答回答于

调试器的工作细节取决于你正在调试什么,以及操作系统是什么。对于Windows上的本机调试,您可以在MSDN上找到一些详细信息:Win32 Debugging API

用户通过名称或进程ID告诉调试器要附加哪个进程。如果它是一个名称,那么调试器将查找进程ID,并通过系统调用启动调试会话; 在Windows下这将是DebugActiveProcess

一旦连接,调试器将进入一个事件循环,就像任何UI一样,但不是来自窗口系统的事件,操作系统将根据正在调试的进程中发生的事件生成事件 - 例如发生异常。请参阅WaitForDebugEvent

调试器能够读写目标进程的虚拟内存,甚至可以通过操作系统提供的API调整其寄存器值。请参阅Windows 的调试功能列表。

调试器能够使用符号文件中的信息将地址转换为源代码中的变量名称和位置。符号文件信息是一组独立的API,并不是操作系统的核心部分。在Windows上,这是通过调试接口访问SDK

如果你正在调试托管环境(.NET,Java等),该过程通常看起来很相似,但细节不同,因为虚拟机环境提供了调试API而不是底层操作系统。

用户回答回答于

据我了解:

对于x86上的软件断点,调试器用CCint3)替换指令的第一个字节。这WriteProcessMemory在Windows上完成。当CPU获得该指令并执行时int3,这会导致CPU产生调试异常。操作系统收到这个中断,意识到这个进程正在被调试,并通知调试器进程该中断点被命中。

在命中断点并且进程停止后,调试器查找其断点列表,并用CC原来存在的字节替换。调试器套TF,所述陷阱标志EFLAGS(通过修改CONTEXT),并且继续处理。陷阱标记使CPU INT 1在下一条指令中自动生成单步异常()。

当正在调试的进程下一次停止时,调试器再次用断点指令的第一个字节替换CC,并且过程继续。

我不确定这是否是所有调试器实现的方式,但是我已经编写了一个Win32程序,该程序使用此机制来调试自己。

扫码关注云+社区