伪造 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

这具有不编辑游戏二进制文件的优点。我见过不止一款游戏会自动校验和,所以如果你编辑它,即使是在闲置的空间里,你也会遇到麻烦。如果他们在验证调用来自游戏二进制文件时遇到了这么多麻烦,那么他们可能会防御游戏二进制文件被编辑。很高兴他们没有跟踪调用图。

【讨论】:

这将抛出返回地址预测器。这可能会导致对所有returns 的分支错误预测到更高的级别;我不确定返回地址堆栈在从不匹配的调用/调用对中恢复时有多好。库函数中的retaddress_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 返回地址?的主要内容,如果未能解决你的问题,请参考以下文章

汇编语言实验五

pikachu靶场——SSRF(服务器端请求伪造)

试图让 asm.js 返回一个类型化的数组

CORS - 从 Postman 伪造 CORS 预检无法返回标头

36.伪造目标不可达的ICMP数据包

36.伪造目标不可达的ICMP数据包