我试图在一个dll中修补远程调用我自己的地址而不是已编译的地址。
以下是我要修补的字节:
{ 0xFF, 0x15, 0x30, 0x20, 0x00, 0x10 }
哪个应转换为:call DWORD PTR ds:0x10002030
我认为这意味着与call [0x10002030]
是的,我正在尝试修补对IAT功能的实际调用。因为iat实际上存储的是一个稍后被赋值给api函数的实际地址的整数,所以为了调用该函数,我们必须取消引用从IAT给我们的地址。
现在因为我想要修补它以转到我自己的功能,我不需要取消引用任何东西。现在的问题是原始的dereference调用是6个字节长,所以我需要保持6个字节或更低。
我正在尝试基本上找到一种方法来调用一个地址,这不是相对的(重要的!)并且不在我所在的当前模块中,最后在小于或等于6的情况下进行字节。
我想要这样的事情:
call 0xDEADBEEF
但我不希望0xDEADBEEF成为相对地址。我想直接调用0xDEADBEEF
重申一下,我需要专门修补此调用,没有其他办法解决它。在这种情况下,正常的IAT挂钩不起作用。
编辑:
我一直在研究它,似乎
push 0xdeadbeef
ret
可能有用,但我发现了一个问题。因为我必须自己处理返回,所以我不能用6个字节来处理。但我想我知道一种方法。
原始来电来自:call [0x10002030]
致:
push jFunction
ret
jFunction本质上是一种存根:
mov eax, 0xDEADBEEF
call eax
但现在问题是如何恢复原来的执行?
使用简单的
pop ebx
ret
使jmp返回原始main函数,而不是ret之后的指令。
正如我之前所说的因为我正在使用的打包器,我不能使用相对偏移,所以在这种情况下我再也无法确定主函数的开始和我想要的指令之间的相对偏移。我无法将所需地址推送到“jFunction”,因为我只有6个字节需要处理。
我开始认为没有相对地址就不可能。
这是我目前的测试代码:x86(调试)MSVC 2015 我正在使用内联汇编和__declspec(裸)来轻松调试解决方案,而不是修补我需要的东西并进行测试。
答案 0 :(得分:0)
您知道要修补的call
指令的地址,因此请计算0xdeadbeef - start_of_next_insn
以获得正常近似直接相对rel32
的正确call
位移(5个字节)。
其他非疯狂的选项是替换内存中的函数指针,或更改寻址模式以从其他地方加载指针,但使其成为简单的直接调用显然是最好的。
这总是在32位模式下工作,但在64位模式下,两个地址可能超过+ -2GiB(rel32的范围超出范围)。但我们知道您处于32位模式,因为FF 15 4bytes
解码为绝对寻址模式。在64位模式下,这将是RIP相对函数指针。 RIP相对接管了[disp32]
寻址模式的无SIB编码。
这几乎与Call an absolute pointer in x86 machine code重复,主要是回答有效地解决填充而不是在call
之前或之后放置NOP。
使用0x67地址大小的前缀(无效)将其填充为6个字节。 GNU ld
already does this when relaxing an indirect call
to a near direct call
,objdump -d /bin/ls
我可以看到一些addr32 call ...
指令,因此这是安全的(CPU供应商无法在不中断的情况下将67 E8
的含义更改为新指令很多现有的二进制文件)。
一般来说,假设前缀在将来不起作用是不安全的(例如rep bsr
在支持它的CPU上运行lzcnt
),但 this 案例因现有用法而安全,例如rep ret
。