是否有可能在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);
}
答案 0 :(得分:5)
更好的方式:
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
这具有不编辑游戏二进制文件的优点。我已经看过不止一个游戏校验自己,所以如果你编辑它,即使在松弛的空间,你也有麻烦。如果他们经历了如此多的麻烦来验证来自游戏二进制文件的调用,那么他们可能会对编辑的游戏二进制文件进行防御。很高兴他们不跟踪调用图。
答案 1 :(得分:1)
你的意思是在进入被叫函数时,[esp]
的返回地址不是实际的呼叫站点?
你可以通过推动你想要的任何东西然后跳跃来模仿call
。当然,您以这种方式调用的函数将返回到您提供的返回地址。对于不匹配的call
/ ret
,也会有明显的性能损失,因为您将打破CPU的返回地址预测器堆栈。
您可以将一些蹦床功能放在可接受的地址范围内,并通过它们进行呼叫吗?虽然这在32位ABI中非常不方便,它可以在堆栈中传递args。我想你必须将一个额外的arg传递给它可以用来存储返回地址的trampoline函数,而不是复制所有的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
这并不精彩,并且需要为每个库函数复制需要大量代码的代码。 (并且它不会构成堆栈帧,因此可能会影响调试/异常处理?)对于没有很多args的函数,执行类似
的操作可能会更短push [esp+8] ; 2nd arg
push [esp+8] ; 1st arg
call lib_function
add esp, 8
ret
使用间接调用可以让你使用相同的蹦床来实现多种功能,但如果没有简单的短模式,则会以分支误预测为代价。
当然,这些蹦床都不会起作用,除非您可以将它们粘贴在库中接受来电的地址的内存中。