此程序中有一个函数,当前返回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更改返回值?
答案 0 :(得分:2)
DTrace不是正确的工具。您应该使用像dbx,mdb或gdb这样的调试器。
与此同时,我将尝试澄清您提到的一些概念。
首先,您可以在源代码中看到一个简单的函数,即只有一个返回。很可能编译结果,即函数的机器特定实现,也只包含单个退出点。但是,通常情况下,实现可能包含多个出口点,并且开发人员可能知道函数返回哪个特定的出口点。正是这个信息被描述为函数起点的偏移量,由返回探测器arg0
给出。然后,您的D脚本正在尝试更新程序或库本身的一部分;虽然添加arg0
会使目标地址有些随机,但结果很可能仍在文本部分中,这是只读的。
其次,在通常情况下,函数的实现通过将其存储在特定寄存器中来返回值;例如amd64上的%rax
。因此,覆盖返回值将需要覆盖寄存器值。这是不可能的,因为DTrace对用户域寄存器的访问是只读的。
有可能以这样的方式实现函数:当它返回时,它在将特定存储器位置写入适当的寄存器之前从特定存储器位置恢复返回值。如果是这种情况,那么实际上可以在访问之前修改内存中的值(given its location)。但是,这仅适用于一部分情况:返回值可能同样包含在另一个寄存器中,或者只是在程序文本本身中表示为常量。无论如何,考虑到存在更合适的调试工具,它将比它的价值更加麻烦。