伪造 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 中,你也会遇到麻烦。如果他们经历了如此多的麻烦以验证调用来自游戏二进制文件,那么他们可能会防御游戏二进制文件被编辑。很高兴他们没有跟踪调用图。
是否可以伪造 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 中,你也会遇到麻烦。如果他们经历了如此多的麻烦以验证调用来自游戏二进制文件,那么他们可能会防御游戏二进制文件被编辑。很高兴他们没有跟踪调用图。