【问题标题】:why this piece of code can get the function address from return address?为什么这段代码可以从返回地址得到函数地址?
【发布时间】:2014-01-22 16:02:41
【问题描述】:

return_address 是通过编写一小段获取 ebp 的汇编代码获得的,因此我们可以通过将 ebp 增加 4 来获得返回地址。 这里return_addressint 类型,但我们可以将其转换为int*

 int extract_function_address(int return_address) {
        int *offset_address_ptr = (int*)(return_address - 5 + 1);
        int offset = *offset_address_ptr;   
        int func_address = return_address + offset;

        return func_address;
    }

我使用gdb 单步执行它

(gdb) disas bar
Dump of assembler code for function bar:
   0x08048304 <+0>: push   %ebp
   0x08048305 <+1>: mov    %esp,%ebp
   0x08048307 <+3>: sub    $0x8,%esp
   0x0804830a <+6>: mov    0xc(%ebp),%eax
   0x0804830d <+9>: mov    0x8(%ebp),%edx
   0x08048310 <+12>:    add    %edx,%eax
   0x08048312 <+14>:    mov    %eax,-0x4(%ebp)
   0x08048315 <+17>:    mov    -0x4(%ebp),%eax
   0x08048318 <+20>:    mov    %eax,0x8(%ebp)
   0x0804831b <+23>:    mov    0x81e2460,%eax
   0x08048320 <+28>:    mov    %eax,(%esp)
   0x08048323 <+31>:    call   0x8048358 <traceback>
   0x08048328 <+36>:    leave  
   0x08048329 <+37>:    ret    
End of assembler dump.


(gdb) disas foo
Dump of assembler code for function foo:
   0x0804832a <+0>: push   %ebp
   0x0804832b <+1>: mov    %esp,%ebp
   0x0804832d <+3>: sub    $0x8,%esp
   0x08048330 <+6>: movl   $0x11,0x4(%esp)
   0x08048338 <+14>:    movl   $0x5,(%esp)
   0x0804833f <+21>:    call   0x8048304 <bar>
   0x08048344 <+26>:    leave  
   0x08048345 <+27>:    ret    
End of assembler dump.

我将返回地址作为0x08048344 传递给函数。偏移量为-64,返回值为0x8048304,即bar的起始地址。

为什么会这样?

这是barfoo所在的C文件

#include "traceback.h"
#include <stdio.h>

void bar(int x, int y)
{
  int z;
  z = x + y;
  traceback(stdout);
}

void foo() {
  bar (5,17);
}

int main (int argc, char **argv)
{
  foo();
  return 0;
}

我把那段代码放在traceback(FILE *fp)

【问题讨论】:

  • 纯粹病态的好奇心,如果你开启全面优化,它仍然有效吗?
  • 我在Makefile中使用的标志是CFLAGS= -Wall -Werror -gstabs -O0 -m32 -Itraceback -mpreferred-stack-boundary=2

标签: c assembly operating-system stack reverse-engineering


【解决方案1】:

我不确定,但看起来代码从返回位置之前的指令中获取了调用地址。

【讨论】:

    【解决方案2】:

    调用指令汇编为E8 AA BB CC DD,其中AA BB CC DD 是目标函数相对于调用之后的指令的偏移量,即相对于返回地址的偏移量。在 gdb 中尝试 x/5bx 0x0804833f 以查看编码指令。请注意,偏移量将采用 little endian 字节顺序。

    因此,(return_address - 5 + 1) 指向调用指令的偏移量。 offset = *offset_address_ptr 从调用指令中读取此偏移量,return_address + offset 指向目标函数。

    【讨论】:

    • 哇.. 谢谢。我对此完全没有想法。您知道有什么参考资料,以便我可以阅读更多相关信息吗?
    • 您可以阅读有关缓冲区溢出的文章中的一些相关内容,包括 Aleph One 的传奇文章:Smashing The Stack For Fun And Profit
    • 是的。你说的对。我查看了第 3-53 页的英特尔架构手册 cs.cmu.edu/~410/doc/intel-isr.pdf。但它总是使用相对位移吗?如果它使用直接地址,上面的代码会中断吗?
    • 取eip值更好吗?
    • 除非您使用指向函数的指针(或类似的东西),否则目标地址在编译时是已知的。因此,编译器将使用具有相对偏移量的“near”调用生成代码,如手册第 3-53 页所述。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-08
    相关资源
    最近更新 更多