伪造 ASM 返回地址?
Posted
技术标签:
【中文标题】伪造 ASM 返回地址?【英文标题】:Faking ASM Return Address? 【发布时间】:2016-06-19 14:58:51 【问题描述】:是否可以在 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);
【问题讨论】:
看起来检查做了一个 abs(ret-middleofdll) > 阈值。由于您已经在打补丁,您首先需要将您的 dll 加载到未使用的Padding 地址周围的 4GB 中,并首先将地址检查补丁到 &byte_A6132A 的 0xffffffff。或者您将 dll 加载到游戏的未使用内存区域中。 【参考方案1】:更好的方法:
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
这具有不编辑游戏二进制文件的优点。我见过不止一款游戏会自动校验和,所以如果你编辑它,即使是在闲置的空间里,你也会遇到麻烦。如果他们在验证调用来自游戏二进制文件时遇到了这么多麻烦,那么他们可能会防御游戏二进制文件被编辑。很高兴他们没有跟踪调用图。
【讨论】:
这将抛出返回地址预测器。这可能会导致对所有ret
urns 的分支错误预测到更高的级别;我不确定返回地址堆栈在从不匹配的调用/调用对中恢复时有多好。库函数中的ret
和address_of_fragment_in_exe
中的ret
都使用刚刚压入堆栈而没有call
的地址。您可能希望使用pop ecx
/ jmp ecx
模拟片段中的ret
(因为在函数返回期间 ecx 可以安全地破坏)。不过,这并不能解决库函数中 ret
的问题。
@Peter Cordes:除非你能在预先存在的二进制文件中找到极其罕见的片段,否则你不会得到这样的选择。
哦,我明白你现在在说什么了。你找到一个现有的 add esp, 8
/ ret
片段,并使用它。我的回答是想象有空间将一些蹦床函数放在允许的内存范围内,但不能将所有代码映射到那里。【参考方案2】:
你的意思是调用函数的入口,[esp]
的返回地址不是实际的调用站点?
你可以模仿call
,按下你想要的任何东西然后跳跃。当然,你这样调用的函数会返回你给它的返回地址。不匹配的call
/ret
也会有显着的性能损失,因为你会破坏 CPU 的返回地址预测器堆栈。
你能把一些蹦床函数放在一个可接受的地址范围内,并通过它们调用吗?尽管在堆栈上传递参数的 32 位 ABI 中这确实很不方便。我想您必须将一个额外的 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
这不是很好,并且需要为每个库函数复制的东西需要大量代码。 (而且它不会生成堆栈帧,因此可能会损害调试/异常处理?)对于没有很多参数的函数,执行类似的操作可能会更短
push [esp+8] ; 2nd arg
push [esp+8] ; 1st arg
call lib_function
add esp, 8
ret
使用间接调用可以让您对多个函数使用相同的蹦床,但如果没有简单的短模式,则会以分支错误预测为代价。
当然,这些蹦床都不能工作,除非你能把它们放在内存中库可以接受调用的地址。
【讨论】:
以上是关于伪造 ASM 返回地址?的主要内容,如果未能解决你的问题,请参考以下文章