ASM - “函数调用”期间修改的寄存器

时间:2014-09-01 12:36:14

标签: assembly call cpu-registers

我尝试挂钩一些函数,如果x86上的__stdcall或__cdecl不重要。

我想做以下事情:
1.保留堆栈
2.保留登记册
做我的事 4.恢复寄存器
5.恢复堆栈

我没有堆栈问题,但我的寄存器备份确实存在一些问题:我无法在不修改其中一些寄存器的情况下备份寄存器(我无法使用堆栈,因为我无法以这种方式备份堆栈)!

我可以在堆上备份它们(我使用带有.EAX,.EBX等成员的结构),我从ASM访问该结构,这是问题...我必须修改一些寄存器能够做到这一点!

然而,这是我的故事。我真正想要理解的是以下问题的答案:

是否有任何"规则"关于可以修改的寄存器和函数调用期间未修改的寄存器?

我用调试器检查了一些函数调用。我在"添加一个断点,调用SomeFunction",按F8到"跳过函数调用"并检查修改后的寄存器。我可以看到这样的事情: 1. ESP / EBP可能会根据调用约定进行修改(cdecl vs stdcall) 2. EAX,EBX,EDX - 几乎总是被修改! 3. EBX,EDI,ESI似乎总是得到保留!

所以这里来了我的"伪解决方案":如果我只保留那些寄存器(EBX,EDI,ESI),那还可以吗?我不会搞砸堆栈,所以EBP和ESP不是问题。但我必须修改一些寄存器(EAX,ECX,EDX)。

我对某些编译器优化有任何问题吗?是否有可能通过修改那些无辜的"来搞乱代码?寄存器:EAX,ECX,EDX?

谢谢

3 个答案:

答案 0 :(得分:0)

目前还不清楚为什么除了保存寄存器的标准技术之外还需要其他任何东西。通常的计划是:

    ; call site: push args into stack, move to registers as required for callee
          push  arg1
          push  arg2
          mov   reg, arg3
          call  subroutine
          lea   esp, sizeof(arg1)+sizeof(arg2)[esp]  ; pop arg1 and arg2
          ...

    subroutine:
          push  ebp

          push  reg1     ; save the registers that subroutine is documented to preserve
          ...
          push  regn     ; if you insist, save *all* the registers
          mov   ebp, esp ; now ebp points to stacked args/saved registers no matter what you do next
          ; note: we have not saved the ESP, but we do know how much we pushed on ESP
          ...
          body of subroutine, move ESP up and down
          ...
    subroutine_exit:
          mov    esp, ebp ; now points to saved registers
          pop    regn
          ...
          pop    reg1
          pop    ebp
          ret

答案 1 :(得分:0)

是的,调用约定允许您破坏EAXECXEDX。我相信microsoft c ++ ECX用于this指针。请注意,这仅适用于遵循约定的函数。如果您尝试挂钩可能已经过优化的内部函数而不使用标准约定,那么它将无法工作。

您可以在数据部分保存内容,如果适用,请特别注意重入和并发。

答案 2 :(得分:0)

是的,我做了以下事情:
1.跳转而不是前5个功能字节(经典挂钩)
2.跳转到我的裸体asm功能
3.在我的函数中,我需要调用一些函数(memcpy - 恢复原始字节,FlushInstruction缓存)并使用结构,所以在这里我需要修改一些寄存器
4.我在堆栈上添加了东西,但最后堆栈与" call"中的相同。此刻
5.我用正确的堆栈JUMP到回调函数(jmp EAX)!我无法回到这里,因为在回调函数中我需要调用原始函数并返回"调用函数+ 5" (在通话时将EIP返回堆栈)。而且我必须使用正确的堆栈跳转,因为参数在堆栈上,挂钩是通用的,我不知道有多少参数!

所以,在回调函数中我做我的东西,调用原始函数并恢复钩子。我需要的是这样的:我需要恢复寄存器,或者确保我不修改它们,但正如我所说,我必须这样做。

此刻我试图避免使用EBX,EDI和ESI。我希望它会起作用,我不会有优化问题。如果它不起作用,我将尝试在我的结构中备份它们并在回调函数中恢复它们。

谢谢大家!