编写自己的调试器处理断点

在本文中,将探讨Linux下的调试器原理,并尝试编写一个简单的调试器(tracer)来调试一个示例程序(tracee)。调试器是程序员用来检查和调试程序的工具,它们通过设置断点、单步执行和查看调用栈等方式帮助开发者理解程序的运行状态。本文的目的是让读者了解调试器的内部机制,并希望读者能够编写一个易于使用的命令行调试器。

读者需要具备Linux操作系统的基础知识,特别是信号及其处理机制。调试器依赖于信号来从被调试程序(debuggee)接收通知(例如:SIGTRAP)。调试器总是通过wait函数等待来自tracee的某个信号。

断点

断点允许用户在被调试程序的执行流程中设置一个中断点。用户可以这样做是为了在执行到这一点时评估某些条件。调试器会在被调试的可执行文件的进程空间中的特定地址(用户希望设置断点的地方)添加一条指令: int 3 (opcode: 0xcc)。当遇到这条指令时,EIP(指令指针)会被移动到中断服务例程(在这种情况下是int 3)。服务例程会保存CPU寄存器(所有中断服务例程都必须这样做),并向附加的调试器发送信号:调用了ptrace(PTRACE_ATTACH, pid, ...)的进程。

使用代码

在阅读本文时,必须始终参考附带的代码。断点(opcode: 0xcc)是通过代码引入到debuggee中的:

C++ static unsigned char c[] = {0xcc, 0xc3, 0x12, 0x34, 0x45}; static void (*pfunc)() = (void(*)())c; static int i = mprotect((unsigned long int)c & 0xfffffffffffff000, sizeof(c), PROT_EXEC | PROT_READ | PROT_WRITE); pfunc();

使用mprotect(Windows中的virtualprotect的等价物)来为此内存提供执行访问权限。所有商业调试器都会使用ptrace(PTRACE_POKEDATA,...)注入断点(不带代码),这相当于Windows中的WriteProcessMemory,它们显然会在更改之前保存指令并在正确执行时恢复它。

获取调用栈

Linux没有与Windows的StackWalk64等价的函数,尽管有第三方堆栈展开库(将使用)。当需要堆栈遍历时,调试器必须逐字节遍历堆栈(属于wait返回的threadID)。由于堆栈仅维护函数的返回地址,它必须检查前面的字节以确保调用已发出。为了确保被调用的函数确实是函数而不是标签,会采取额外的步骤,这是通过查看将帧指针推送到堆栈上的明显迹象来完成的。

C++ void Getbacktrace(int thetid) { unw_cursor_t cursor; unw_word_t ip; unw_addr_space_t as; struct UPT_info *ui = NULL; as = unw_create_addr_space(&_UPT_accessors, 0); ui = _UPT_create(thetid); int rc = unw_init_remote(&cursor, as, ui); while (unw_step(&cursor) > 0) { //walk the stack one frame at a time unw_word_t offset, pc; unw_get_reg(&cursor, UNW_REG_IP, &pc); char buffer[STR_MAX] = {}; if (0 == unw_get_proc_name(&cursor, buffer, sizeof(buffer), &offset)) //get mangled function names printf("%s\n", buffer); PrintFileAndLine(DEBUGGEE, pc); //use addr2line } _UPT_destroy(ui); unw_destroy_addr_space(as); }

如前所述,调试器将使用wait,在while循环中旋转(代码是自解释的),并在debuggee退出时退出(由于缺少退出部分,这是读者的练习):

C++ ptrace(PTRACE_ATTACH, pid, NULL); printf("error %u\n", errno); //lets attach the process while (1) { pid_t tid = wait(&status); if (WIFSTOPPED(status)) { ..... ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo); ptrace(PTRACE_CONT, pid, NULL, siginfo.si_signo); } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485