如果你想截获对一系列地址的写操作,可以使用 mprotect() 函数将相关内存标记为不可写,并使用 sigaction() 安装信号处理程序来捕获由此引起的 SIGSEGV 信号,记录日志或执行其他操作,然后再将页面标记为可写。
你需要访问X86调试寄存器:http://en.wikipedia.org/wiki/Debug_register 你需要在DR0到DR3中设置断点地址,然后在DR7中设置条件(数据写入)。中断会发生,你可以运行调试代码以读取DR6并找到引起断点的原因。如果GDB无法工作,你可以尝试使用更简单/更小的调试器,例如http://sourceforge.net/projects/minibug/。如果这也没有起作用,你至少可以自己了解如何使用处理器上的调试硬件。此外,IBM开发者资源有一个很好的关于掌握Linux调试技术的文章,应该提供一些额外的选项:http://www.ibm.com/developerworks/linux/library/l-debug/ 在Windows上进行此操作的一个相当不错的文章在这里(我知道你正在运行Linux,但其他人可能会看到这个问题想要在Windows上进行):http://www.codeproject.com/KB/debug/hardwarebreakpoint.aspx -Adam
mprotect有一个缺点:你的内存必须是页面边界对齐的。我的有问题的内存在堆栈上,无法使用mprotect()。正如Adam所说,你想要操作调试寄存器。在Windows上,我使用了这个:http://www.morearty.com/code/breakpoint/,它非常好用。我还将它移植到了Mach-O(Mac OS X),也很好用。这也很容易,因为Mach-O有thread_set_state(),它相当于SetThreadContext()。Linux的问题在于它没有这样的等价物。我找到了ptrace,但我想,这不可能是全部,一定有更简单的方法。但实际上没有。但他们正在研究一个hw_breakpoint API,适用于内核和用户空间。(见http://lwn.net/Articles/317153/)但是当我发现这个链接:http://blogs.oracle.com/nike/entry/memory_debugger_for_linux,我尝试了一下,结果并不那么糟糕。ptrace方法的原理是通过某些“外部进程”作为“调试器”,附加到您的程序上,注入新值到调试寄存器中,并以设置了新硬件断点的程序终止。问题是,您可以使用fork()自己创建这个“外部进程”(我尝试过pthread没有成功),并在您的代码中内联执行这些简单的步骤。addwatchpoint代码必须适应64位Linux,但只需要将USER_DR7等更改为offsetof(struct user, u_debugreg [7])即可。另一个问题是,在PTRACE_ATTACH之后,您必须等待调试程序实际停止。但是,正确的做法不是在忙碌的循环中重试POKEUSER,而是在您的pid上进行waitpid()。使用ptrace方法的唯一问题是您的程序一次只能连接一个“调试器”。因此,如果您的程序已经在gdb控制下运行,则ptrace附加将失败。但是,就像示例代码所做的那样,您可以为SIGTRAP注册信号处理程序,不使用gdb运行,并在捕获信号时进入忙碌循环等待gdb连接。从那里,您可以看到谁尝试写入您的内存。
GDB有这个功能:它被称为硬件观察点,在Linux/x86上得到了很好的支持: (gdb) watch *(int *)0x12345678 如果您的应用程序崩溃了GDB,请从CVS Head构建当前的GDB。 如果该GDB仍然失败,请提交GDB错误报告。 很可能我们可以比您绕过SIGSEGV处理程序更快地修复GDB(提供了一个良好的测试案例),并且对GDB的修复也有助于您解决未来的问题。