【问题标题】:How to change returned value of function如何更改函数的返回值
【发布时间】:2017-07-25 23:25:30
【问题描述】:

这个程序中有一个函数,它当前返回一个 1。我希望它返回一个 0。

由此我推断:我们可以将偏移量添加到程序计数器uregs[R_PC]+arg0,以找到返回值的地址。

我分配了一个 32 位的“0”,我尝试将其中的 2 个字节写入返回值所在的地址(我们的函数期望返回一个 BOOL16,所以我们只需要 2 个字节的 0):

sudo dtrace -p "$(getpid)" -w -n '
int *zero;
BEGIN { zero=alloca(4); *zero=0; }
pid$target::TextOutA:return {
    copyout(zero, uregs[R_PC]+arg0, 2);
}'

我当然明白:

dtrace:启用探测 ID 2 时出错(ID 320426:pid60498:gdi32.dll.so:TextOutA:return):在 DIF 偏移量 60 处的操作 #1 中的地址无效 (0x41f21c)

uregs[R_PC] 大概是一个用户空间地址。可能copyout() 想要一个内核地址。

如何将用户空间地址uregs[R_PC] 转换为内核空间? 我知道使用copyin() 我们可以将存储在用户空间地址的数据读取到内核空间。但这并没有给我们该内存的内核地址。

或者:还有其他方法可以使用 DTrace 更改返回值吗?

【问题讨论】:

  • 我没有看到arg0 + u_regs[R_PC]; 如何为您获取返回值的地址。那是两个地址。添加它们似乎没有任何意义。怎么知道返回值的地址?它很可能通过寄存器传递。实际返回的arg1。见docs.oracle.com/cd/E19253-01/817-6223/chp-pid/index.html
  • 我从文档中的理解是u_regs[R_PC] 是程序计数器,而arg0程序计数器的偏移量。因此,u_regs[R_PC] 将是一个 绝对 地址,arg0 是一个相对偏移量,您可以将其添加到该绝对地址,以获得不同的绝对地址。
  • 对于arg1:当然这包含,但我的意图是在函数返回之前修改值。我们在 DTrace (copyout()) 中修改数据的唯一工具要求我们知道数据的地址。

标签: dtrace


【解决方案1】:

DTrace 不是解决此问题的正确工具。您应该改用 dbx、mdb 或 gdb 之类的调试器。

与此同时,我将尝试澄清您提到的一些概念。

首先,您可能会在源代码中看到一个简单函数只有一个返回值。很可能编译结果,即函数的机器特定实现,也只包含一个退出点。然而,通常情况下,实现可能包含多个退出点,开发人员知道函数从哪个特定退出点返回可能很有用。返回探针的arg0 给出的正是这些信息,描述为从函数开始的偏移量。那么,您的 D 脚本正在尝试更新程序或库本身的一部分;尽管添加arg0 使目标地址有些随机,但结果很可能仍在文本部分内,它是只读的。

其次,在常见情况下,函数的实现通过将值存储在特定寄存器中来返回值;例如%rax 在 amd64 上。因此,覆盖返回值将需要覆盖寄存器值。这是不可能的,因为 DTrace 对用户级寄存器的访问是只读的。

一个函数的实现方式可能是,当它返回时,它会在将返回值写入适当的寄存器之前从特定的内存位置恢复返回值。如果是这种情况,那么确实可以在访问内存之前修改内存中的值 (given its location)。但是,这仅适用于部分情况:返回值可能同样包含在另一个寄存器中,或者在程序文本本身中简单地表示为常量。无论如何,考虑到更合适的调试工具的存在,这将比它的价值要麻烦得多。

【讨论】:

  • 太棒了!感谢您的详细解释。链接的答案也很有用。好的,所以arg0 指向一个指令(即“这个函数中的返回指令之一”),而不是数据——所以即使我有它的内核空间地址,这不是我想要的。是的,这可能是不同工具的工作。
猜你喜欢
  • 2013-03-11
  • 2011-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多