x86汇编语言中,'call'指令的替代方法是什么?

20

有哪些替代x86 call指令的方法?也许可以像将返回地址压入栈中然后跳转这样的方式?

另外,是否有命令可以获取内存中的当前位置?


2
请查看这个答案,以及相关的问题。 - Kerrek SB
3个回答

26

call指令实际上为您执行此操作。例如,call my_func将执行类似以下的操作:

push ret_address
jmp my_func

接下来的ret调用只是在某种意义上使用了你刚刚推送到jmp中的地址进行返回。你不想使用call是否有特定原因,或者它对你不可用?要获取当前内存位置,可以尝试读取eip寄存器(无法写入)。


我正在使用C#注入一个asm代码存根,该存根调用另一个进程内部的函数。基本上想知道是否有更优雅的方法来调用该函数,而不会破坏任何寄存器或堆栈(以便在调用之前保留它们)。我使用的另一种解决方案是只需编写跳转指令,然后静态地跟随函数地址(类似于Windows处理dll函数的方式)。但是这意味着要跟踪注入代码的缓冲区大小,我认为这很不优雅,但看起来目前只有这个选项。 - rick
10
您可以写入EIP寄存器,指令只是不叫MOV而已,而是叫JMP。 - Bo Persson
@Bo,你能做到吗?我认为在某些操作系统中有机制可以避免这种情况发生,但我可能错了,因为我从未尝试过向 EIP 写入任何内容,因为处理器本身会处理它。 - Jesus Ramos
1
@JesusRamos:跳转/调用或返回指令实际上是设置EIP,使CPU执行的不是下一条指令。如果这是真的,那么你从来没有编写过EIP,这意味着你编写的所有代码都是直线型的,没有分支,包括不返回任何地方。(如果你只编写了内联汇编并让编译器制作循环/条件语句,则可能是可能的)。另请参见为什么不能直接设置指令指针?/直接读取程序计数器 - Peter Cordes
ret 只是 x86 中 "pop eip" 的写法。 - Peter Cordes
显示剩余6条评论

7
您可以直接推送一个 dword 值并跳转到过程。推送的值将成为返回地址:
push return_address (push eax if address in eax)
jmp call_address

请记得如果该特定调用存在参数,也要将参数推送。

什么是内存中的当前位置?我想你是指当前指令指针。你不能直接获取它,但可以使用异常处理程序(SEH handler)在引起已处理异常时获取该值。


3
如果我没有弄混的话,我会说这取决于具体情况。正如您可以在此处看到的,存在近距离调用和远距离调用,但是我们只考虑近距离调用。这里又有两种可能性。
E8 <offset> # relative
FF <address> # absolute

其中FF表示绝对“跳转”,而E8确实是相对于当前的eip

因此,如果您有例如:

E8 32 45 ab 6f

这意味着
call 0x3245ab6f

这将被翻译为:
push %eip
add $0x3245ab6f, %eip

而不是

push %eip
jmp $0x3245ab6f

请注意,call 指令会将自身的下一条指令地址(即超过自身长度的地址)压入栈中。由于 x86 架构中没有可见的 %eip 寄存器,因此您应该在伪代码中注释说明您的 %eip 是已经增加了 call 指令长度后的 EIP 寄存器。 (但是,是的,相对位移是相对于指令结尾即返回地址的,就像 64 位 RIP 相对寻址一样。) - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接