0

LD_PRELOAD 技术允许我们向现有二进制文件提供我们自己的自定义标准库函数,覆盖标准函数或操纵它们的行为,提供一种有趣的方式来试验二进制文件并了解其行为。

我读过 LD_PRELOAD 可用于“检查点”程序——也就是说,在任何给定时间产生完整内存状态、调用堆栈和指令指针的记录——允许我们“重置”随意编程回到之前的状态。

我很清楚我们如何记录堆的状态。由于我们可以提供自己版本的 malloc 和相关函数,我们的预加载库显然可以完美地了解内存状态。

我想不通的是我们的预加载函数如何确定调用堆栈和指令指针;然后稍后将它们重置为先前记录的值。显然,这对于检查点是必要的。是否有可以做到这一点的标准库函数?还是需要不同的技术?

4

1 回答 1

1

我读过 LD_PRELOAD 可用于“检查点”程序......允许我们随意将程序“重置”回之前的状态。

这是一个粗略的简化。这种“检查点”机制不可能恢复任何打开的文件描述符或任何互斥体,因为它们的状态部分位于内核内部。

我很清楚我们如何记录内存状态。...
我想不通的是我们的预加载函数如何确定调用堆栈和指令指针;

指令指针预加载的函数内部,并且在例如register void *rip __asm__("rip")on中很容易获得x86_64。但是你(很可能)不关心那个地址——你可能关心你的函数的调用者。这也很容易获得__builtin_return_address()(至少在使用 GCC 时)。

并且调用堆栈的其余部分保存在内存中(更准确地说是在堆栈区域中),因此如果您知道内存的内容,就知道调用堆栈。

实际上,当您使用例如where带有core转储的 GDB 命令时,这正是 GDB 所做的——它从 中读取内存内容并从中core恢复调用堆栈。

更新:

我在原来的帖子里写过,我知道如何检查内存,但实际上我只知道如何检查堆。如何查看所有堆栈帧的全部内容?

无论内存“属于”堆、堆栈还是代码,检查内存的工作方式都是一样的。您只需取消引用一个指针,然后瞧——您将获得该位置的内存内容。

你的意思可能是:

  1. 如何找到堆栈的位置
  2. 如何解码

第一个问题的答案是特定于操作系统的,您没有用任何操作系统标记您的问题。

假设您在 Linux 上,定位堆栈的一种方法是解析条目以/proc/self/maps查找“覆盖”当前堆栈(即“覆盖”任何局部变量的地址)的条目(连续地址范围)。

对于第二个问题,答案是:

  • 这很复杂1
  • 实际上不需要对其进行解码以保存/恢复其状态。

1要弄清楚如何解码堆栈,您可以查看调试器的源代码(例如 GDB 和 LLDB)。

这也是非常特定于操作系统和处理器的。

您需要了解调用约定。x86_64您需要了解展开描述符。要查找局部变量,您需要了解DWARF调试格式。

我提到它很复杂吗?

于 2022-01-24T05:09:07.173 回答