伪造 ASM 退货地址



是否有可能伪造返回地址,ebp + 4。

我目前正在编写一个 DLL,您将注入到游戏中,其中它将调用游戏函数来执行操作,但我调用的函数会根据程序本身检查返回地址,如果它在其基础之外,它会检测到它。

那么基本上有没有办法以任何方式伪造退货地址?

它的工作原理是这样的:

if ( (_BYTE *)retaddr - (_BYTE *)unusedPadding >= (unsigned int)&byte_A6132A )
  {
    dword_11E59F8 |= 0x200000u;
    dword_162D06C = 0;
    result = (void (*)())sub_51FEE0(dword_11E59FC, v5, (_BYTE *)retaddr - (_BYTE *)unusedPadding, ebx0, edi0, a1);
  }

更好的方法:

    push returnshere
    push your_second_argument
    push your_first_argument
    push address_of_fragment_in_exe
    jmp  function_you_want_to_call
returnshere:
    ; more code

其中address_of_ret_in_exe是此片段的地址:

    add esp, 8 ;4 * number of arguments pushed
    ret

这样做的好处是不编辑游戏二进制文件。我见过不止一款游戏自己校验和,所以如果你编辑它,即使在松弛的空间里,你也有麻烦了。如果他们经历了如此多的麻烦,以至于验证调用来自游戏二进制文件,那么他们可能会对游戏二进制文件进行编辑。只是很高兴他们没有跟踪调用图。

你的意思是这样进入被调用的函数,[esp]的返回地址不是实际的调用站点?

你可以模仿call,推动任何你想要的东西,然后跳跃。 当然,您以这种方式调用的函数将返回到您为其提供的返回地址。 不匹配的 call/ret 也会有明显的性能损失,因为你会破坏 CPU 的返回地址预测器堆栈。


你能把一些蹦床功能放在一个可接受的地址范围内,然后通过它们调用吗? 尽管这在堆栈上传递参数的 32 位 ABI 中真的很不方便。 我想你必须将一个额外的 arg 传递给蹦床函数,它可以用来存储返回地址,而不是复制所有 arg。

所以蹦床可能是这样的:

mov   [esp+20], esi      ; save esi in a dummy arg slot
mov   esi, [esp]         ; save the orig return address in a call-preserved reg
add   esp, 4             ; call with the original args
call  lib_function
push  esi                ; restore the orig return address
mov   esi, [esp+20]      ; and restore esi
ret                      ; return to orig return address

这并不好,并且需要大量代码来复制每个库函数。 (而且它不会生成堆栈帧,因此可能会损害调试/异常处理? 对于没有很多参数的函数,执行类似操作可能会更短

push   [esp+8]         ; 2nd arg
push   [esp+8]         ; 1st arg
call  lib_function
add    esp, 8
ret

使用间接调用可以让您将同一蹦床用于多个函数,但代价是如果没有简单的短模式,分支会错误预测。

当然,这些蹦床都不能工作,除非你能把它们放在图书馆将接受电话的地址的内存中。

最新更新