如何改变函数的返回值

How to change returned value of function

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

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

我已经分配了一个 32 位的“0”,我尝试将它的 2 个字节写入 return 值所在的地址(我们的函数期望 return 一个 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: error on enabled probe ID 2 (ID 320426: pid60498:gdi32.dll.so:TextOutA:return): invalid address (0x41f21c) in action #1 at DIF offset 60

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

如何将用户space地址uregs[R_PC]翻译成内核-space?我知道copyin()我们可以将存储在 user-space 地址的数据读入 kernel-space。但这并没有给我们那个内存的内核地址。

或者:是否有其他方法可以使用 DTrace 更改 return 值?

DTrace 不是执行此操作的正确工具。您应该改用像 dbx、mdb 或 gdb 这样的调试器。

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

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

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

一个函数的实现方式可能是,在 return 中,它会先从特定内存位置恢复 return 值,然后再将其写入适当的寄存器。如果是这种情况,那么确实可以在访问之前修改内存中的值 ()。然而,这仅适用于一部分情况:return 值可能同样包含在另一个寄存器中,或者只是在程序文本本身中表示为常量。无论如何,考虑到更合适的调试工具的存在,这将是麻烦得不值得的。