【问题标题】:Possible to trap write to address (x86 - linux)可以捕获写入地址(x86 - linux)
【发布时间】:2009-03-03 22:54:12
【问题描述】:

我希望能够检测写入内存地址的时间——例如通过设置附加到中断的回调。有人知道怎么做吗?

我希望能够在运行时执行此操作(可能 gdb 具有此功能,但我的特殊 应用程序导致 gdb 崩溃)。

【问题讨论】:

    标签: c++ c linux x86


    【解决方案1】:

    如果您想拦截对一系列地址的写入,您可以使用mprotect() 将有问题的内存标记为不可写,并使用sigaction() 安装信号处理程序以捕获生成的 SIGSEGV,进行日志记录或其他任何东西并将页面再次标记为可写。

    【讨论】:

      【解决方案2】:

      您需要访问 X86 调试寄存器:http://en.wikipedia.org/wiki/Debug_register

      您需要在 DR0 到 DR3 之一中设置断点地址,然后在 DR7 中设置条件(数据写入)。将发生中断,您可以运行调试代码来读取 DR6 并找出导致断点的原因。

      如果 GDB 不起作用,您可以尝试更简单/更小的调试器,例如 http://sourceforge.net/projects/minibug/ - 如果不起作用,您至少可以通过代码并了解如何在处理器上使用调试硬件自己。

      此外,还有一个关于掌握 linux 调试技术的优秀 IBM 开发人员资源,它应该提供一些额外的选项:

      http://www.ibm.com/developerworks/linux/library/l-debug/

      关于这样做的一篇相当不错的文章是 windows 在这里(我知道你在 linux 上运行,但其他人可能会想在 windows 中做这个问题):

      http://www.codeproject.com/KB/debug/hardwarebreakpoint.aspx

      -亚当

      【讨论】:

      【解决方案3】:

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

      (gdb) watch *(int *)0x12345678
      

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

      如果该 GDB 仍然失败,请提交 GDB bug

      我们修复 GDB 的速度可能比您破解 SIGSEGV 处理程序的速度更快(提供了一个很好的测试用例),而且对 GDB 的修复也可以帮助您解决未来的问题。

      【讨论】:

        【解决方案4】:

        mprotect 确实有一个缺点:您的内存必须是页边界对齐的。我在堆栈上有问题的内存,无法使用 mprotect()。

        正如亚当所说,你想要的是操纵调试寄存器。在 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 附加。从那里你可以看到谁试图写下你的记忆。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-12-21
          • 1970-01-01
          • 1970-01-01
          • 2014-01-30
          • 2011-01-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多