可能在x86架构的Linux系统中捕获写入地址吗?

8

我希望能够检测内存地址的写入,例如通过设置附加到中断的回调函数来实现。有人知道如何做吗?

我想在运行时实现这个功能(可能gdb有此功能,但我的特定应用程序会导致gdb崩溃)。

4个回答

15
如果你想截获对一系列地址的写操作,可以使用 mprotect() 函数将相关内存标记为不可写,并使用 sigaction() 安装信号处理程序来捕获由此引起的 SIGSEGV 信号,记录日志或执行其他操作,然后再将页面标记为可写。

8
你需要访问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

1
调试寄存器只能在特权级0即内核中访问。请参阅http://pdos.csail.mit.edu/6.828/2008/readings/i386/s12_02.htm。 - Adam Rosenfield

4
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连接。从那里,您可以看到谁尝试写入您的内存。

4

GDB有这个功能:它被称为硬件观察点,在Linux/x86上得到了很好的支持:

(gdb) watch *(int *)0x12345678

如果您的应用程序崩溃了GDB,请从CVS Head构建当前的GDB。

如果该GDB仍然失败,请提交GDB错误报告

很可能我们可以比您绕过SIGSEGV处理程序更快地修复GDB(提供了一个良好的测试案例),并且对GDB的修复也有助于您解决未来的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接