直接调用x86汇编



我试图基本上在dll中修补一个远调用到我自己的地址,而不是一个已经编译的。

这是我试图修补的字节:

{ 0xFF, 0x15, 0x30, 0x20, 0x00, 0x10 }

应该翻译成:call DWORD PTR ds:0x10002030

我认为这意味着:call [0x10002030]

是的,我正试图将对IAT函数的实际调用修补为其他东西。因为iat本质上是存储一个整数,这个整数稍后会被赋值给api函数的实际地址,为了调用函数,我们必须从iat中解引用给我们的地址。

现在,因为我想把这个修改成我自己的函数,我不需要去引用任何东西。现在的问题是原始的解引用调用是6字节长,所以我需要保持在6字节或更低。

我试图从本质上找到一种方法来调用一个地址,这不是相对的(重要!),不在我所在的当前模块内,最后在小于或等于6字节的时间内完成。

我想要这样的东西:

call 0xDEADBEEF

,但我不希望0xDEADBEEF是一个相对地址。我想直接调用0xDEADBEEF

重申一下,我需要专门修补这个调用,没有其他方法。在这种情况下,一个正常的IAT钩子是不能胜任的。

Edit:

我一直在进一步调查,似乎

push 0xdeadbeef
ret

可能工作,但我发现了一个问题。因为我必须自己处理返回,我不能在6字节内完成。但我想我有办法。

原呼叫来自:call [0x10002030]To:

push jFunction 
ret

和jFunction本质上是一个存根的排序:

mov eax, 0xDEADBEEF
call eax

但现在的问题是我如何回到原来的执行?

使用简单的

pop ebx
ret

使JMP返回到原来的main函数,而不是返回到ret后面的指令。

正如我之前所说的,因为我正在使用的包装器,我不能使用相对偏移量,所以在这种情况下,我不能确定主函数开始和我想要去的指令之间的相对偏移量。我不能把想要的地址推入"jFunction",因为我只有6个字节要处理。

我开始认为没有相对地址这是不可能的。

这是我目前的测试代码:x86(调试)MSVC 2015我使用内联汇编和__declspec(裸)来轻松调试解决方案,而不是修补我需要的和测试。

当前测试代码

您知道要修补的call指令的地址,因此计算0xdeadbeef - start_of_next_insn以获得正常的近直接相对call(5字节)的正确rel32位移。

另一个不太疯狂的选择是替换内存中的函数指针,或者改变寻址模式以从其他地方加载指针,但使其成为一个简单的直接调用显然是最好的。

这总是在32位模式下工作,但在64位模式下,两个地址可能相距超过+-2GiB(超出rel32的范围)。但是我们知道你处于32位模式,因为FF 15 4bytes解码为绝对寻址模式。在64位模式下,这是一个与rip相关的函数指针。RIP-relative取代了[disp32]寻址模式下的no-SIB编码。

这几乎是x86机器码中调用绝对指针的副本,主要是为了有效地回答地址填充,而不是在call之前或之后放置NOP。

用0x67地址大小的前缀填充它(这将没有效果),使其成为6字节。GNU ld已经在将间接call放松为接近直接的call时做到了这一点,对于objdump -d /bin/ls,我可以看到一些addr32 call ...指令,所以这是安全的(CPU供应商不能在不破坏大量现有二进制文件的情况下将67 E8的含义更改为一些新指令)。

一般来说,假设前缀在将来没有影响是不安全的(例如rep bsr在支持它的cpu上作为lzcnt运行),但是这种情况是安全的,因为现有的使用,如rep ret

最新更新