直接调用 x86 程序集
Direct Call x86 Assembly
我试图在一个 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 挂钩将不起作用。
编辑:
我一直在研究它,似乎
push 0xdeadbeef
ret
可能可行,但我发现了一个问题。因为我必须自己处理 return,所以我不能用 6 个字节来处理。但我想我知道我能做到的方法。
原始呼叫来自:call [0x10002030]
至:
push jFunction
ret
jFunction 本质上是一种存根:
mov eax, 0xDEADBEEF
call eax
但现在的问题是如何恢复到原来的执行状态?
使用简单的
pop ebx
ret
使其跳转到原来的main函数,而不是ret之后的指令。
正如我之前所说,由于我使用的加壳器,我不能使用相对偏移量,所以在这种情况下,我再次无法确定主函数开始和我想要执行的指令之间的相对偏移量到。而且我无法将想要的地址推送到 "jFunction",因为我只有 6 个字节要处理。
我开始认为没有相对地址这是不可能的。
这是我当前的测试代码:x86(调试)MSVC 2015
我正在使用内联汇编和 __declspec(naked) 轻松调试解决方案,而不是修补我需要的内容和测试。
您知道要修补的 call
指令的地址,因此计算 0xdeadbeef - start_of_next_insn
以获得正常近直接相对 call
的正确 rel32
位移(5 个字节)。
其他不疯狂的选择是替换内存中的函数指针,或者改变寻址模式以从其他地方加载指针,但使其成为简单的直接调用显然是最好的。
这始终适用于 32 位模式,但在 64 位模式下,两个地址之间的距离可能超过 +-2GiB(超出 rel32 的范围)。但我们知道您处于 32 位模式,因为 FF 15 4bytes
解码为绝对寻址模式。在 64 位模式下,这将是一个 RIP 相关的函数指针。 RIP-relative 接管了 [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
运行),但是 这个案例是安全的,因为现有的用法,比如rep ret
.
我试图在一个 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 挂钩将不起作用。
编辑:
我一直在研究它,似乎
push 0xdeadbeef
ret
可能可行,但我发现了一个问题。因为我必须自己处理 return,所以我不能用 6 个字节来处理。但我想我知道我能做到的方法。
原始呼叫来自:call [0x10002030]
至:
push jFunction
ret
jFunction 本质上是一种存根:
mov eax, 0xDEADBEEF
call eax
但现在的问题是如何恢复到原来的执行状态?
使用简单的
pop ebx
ret
使其跳转到原来的main函数,而不是ret之后的指令。
正如我之前所说,由于我使用的加壳器,我不能使用相对偏移量,所以在这种情况下,我再次无法确定主函数开始和我想要执行的指令之间的相对偏移量到。而且我无法将想要的地址推送到 "jFunction",因为我只有 6 个字节要处理。
我开始认为没有相对地址这是不可能的。
这是我当前的测试代码:x86(调试)MSVC 2015 我正在使用内联汇编和 __declspec(naked) 轻松调试解决方案,而不是修补我需要的内容和测试。
您知道要修补的 call
指令的地址,因此计算 0xdeadbeef - start_of_next_insn
以获得正常近直接相对 call
的正确 rel32
位移(5 个字节)。
其他不疯狂的选择是替换内存中的函数指针,或者改变寻址模式以从其他地方加载指针,但使其成为简单的直接调用显然是最好的。
这始终适用于 32 位模式,但在 64 位模式下,两个地址之间的距离可能超过 +-2GiB(超出 rel32 的范围)。但我们知道您处于 32 位模式,因为 FF 15 4bytes
解码为绝对寻址模式。在 64 位模式下,这将是一个 RIP 相关的函数指针。 RIP-relative 接管了 [disp32]
寻址模式的无 SIB 编码。
这几乎是 Call an absolute pointer in x86 machine code 的重复,主要是为了有效地解决地址填充问题,而不是在 call
.
用 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
运行),但是 这个案例是安全的,因为现有的用法,比如rep ret
.