我反转了ELF x86,我想了解为什么将返回地址再次压入堆栈?

main:
lea    ecx, [esp+0x4 {argc}]
and    esp, 0xfffffff0
push   dword [ecx-0x4 {__return_addr}] {var_4}
push   ebp, {var_8}
mov    ebp, esp
push   edi {var_c}
push   ecx {argc} {var_10}
sub    esp, 0xb0
mov    eax, dword [ecx+0x4 {argv}]
mov    dword [ebp-0x9c {var_a4}], eax
mov    eax, dword [gs:0x14]
mov    dword [ebp-0xc {var_14}], eax
xor    eax, eax {0x0}
cmp    dword [ecx {argc}], 0x2
je     0x80485ae
...


评论

这发生在哪几行?

我想这是在推[ecx-0x4] var_4。正如我们在x86-32中一样,不能直接调用eip。

哪些编译器提供此代码?看起来好像是在从函数内部更改函数的返回地址。据我所知,这是所有ABI中的违规行为,因此它必须是非常特定的功能(如__libc_start_main())或混淆技术,以解决递归遍历反汇编技术。

#1 楼

它在将堆栈对齐到16个字节的and esp, 0xfffffff0指令之前出现在堆栈上。该指令不会删除先前在esp处的数据(因此ecx-4仍指向返回地址),但是堆栈指针现在指向的值可能与函数开始时的值不同。因此,需要将返回值([ecx-4])压入堆栈,以使esp指向返回地址,而不是一些垃圾数据。然后,函数返回地址位于esp(即等于0x11111118)。但是在esp操作之后,[esp]现在等于and,因此函数返回地址位于esp地址,该地址不在堆栈上(实际上是在堆栈下面),并且0x11111110现在指向其他数据,这绝对不是函数返回地址。但是我们知道esp+8指向该地址(因为esp = ecx-4 = ecx),所以我们将0x11111118+4压入堆栈,因此0x1111111C现在指向它。