是否有可能伪造返回地址,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
使用间接调用可以让您将同一蹦床用于多个函数,但代价是如果没有简单的短模式,分支会错误预测。
当然,这些蹦床都不能工作,除非你能把它们放在图书馆将接受电话的地址的内存中。