伪造 ASM Return 地址?

Faking ASM Return Address?

是否可以伪造 return 地址,ebp + 4。

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

所以基本上有什么方法可以伪造 return 地址吗?

它是这样工作的:

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);
  }

你的意思是在调用函数的入口处,[esp]处的return地址不是实际的调用点?

你可以模仿 call 推你想要的东西然后跳。当然,你这样调用的函数会return到你给它的return地址。对于不匹配的 call/ret,也会有显着的性能损失,因为您会破坏 CPU 的 return-地址预测器堆栈。


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

所以蹦床可能是这样的:

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

使用间接调用可以让您对多个函数使用相同的 trampoline,但如果没有简单的短模式,则会以分支预测错误为代价。

当然 none 这些 trampoline 可以工作,除非你可以将它们保存在内存中图书馆将接受来自的调用的地址。

更好的方法:

    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

这样做的好处是无需编辑游戏二进制文件。我见过不止一款会自行校验和的游戏,所以如果你编辑了它,即使是在 slack space 中,你也会遇到麻烦。如果他们经历了如此多的麻烦以验证调用来自游戏二进制文件,那么他们可能会防御游戏二进制文件被编辑。很高兴他们没有跟踪调用图。