代码获取最后2个被调用函数的地址

时间:2015-01-15 12:59:40

标签: linux assembly stack

我试图在不使用gdb的情况下获取上次调用的函数的地址,以便我可以直接跟踪被调用的函数。我有返回地址,但是当我跟踪函数的地址时,我没有在要返回的指令的地址下面获得函数的地址。我看到了英特尔SDM(软件开发手册)检查指令操作码(给出的调用操作码是ec和ff)。但是在相对地址差异方面,我没有得到被调用函数的地址。我使用我的函数和gdb匹配内存的内容,它们是相同的。

请帮我弄清楚被调用的最后2个函数的地址。 提前谢谢。

我的代码和我的gdb调试结果如下:

我的代码是:

#include"stdio.h"

void f2()
{
    printf(" in f2: entry point\n");
    unsigned int retaddr=0, ebpr=0, calladdr=0;
    int i=0;
    asm("movl %%ebp, %0": "=r"(ebpr));
    while(i++<2){
        //next statement is fetching the return address from stack
        asm("movl 4(%1), %0": "=r"(retaddr) : "r"(ebpr)  ); // %0 = "=r"(retaddr), '=' means op ; %1 = "r"(ebspr)
        //next statement fetching the return addresss-4 address value
        //this instruction is giving some false result(till now, don't know the reason)
        retaddr-=4;
        asm("movl (%1), %0": "=r"(calladdr) : "r"(retaddr)  );
            printf(" in f2: ebp: 0x%x  \tf1 address is: 0x%x calladdr: 0x%x \n",ebpr,retaddr,calladdr);
        retaddr+=4;
        printf("\n");
        asm("movl -16(%1), %0": "=r"(calladdr) : "r"(retaddr)  );
            printf(" in f2: ebp: 0x%x  \tretaddr: 0x%x calladdr: 0x%x \n",ebpr,retaddr,calladdr);
        asm("movl -12(%1), %0": "=r"(calladdr) : "r"(retaddr)  );
            printf(" in f2: ebp: 0x%x  \tretaddr: 0x%x calladdr: 0x%x \n",ebpr,retaddr,calladdr);
        asm("movl -8(%1), %0": "=r"(calladdr) : "r"(retaddr)  );
            printf(" in f2: ebp: 0x%x  \tretaddr: 0x%x calladdr: 0x%x \n",ebpr,retaddr,calladdr);
        asm("movl -4(%1), %0": "=r"(calladdr) : "r"(retaddr)  );
            printf(" in f2: ebp: 0x%x  \tf1 address is: 0x%x calladdr: 0x%x \n",ebpr,retaddr,calladdr);
        asm("movl (%1), %0": "=r"(calladdr) : "r"(retaddr)  );
            printf(" in f2: ebp: 0x%x  \tretaddr: 0x%x calladdr: 0x%x \n",ebpr,retaddr,calladdr);
        asm("movl 4(%1), %0": "=r"(calladdr) : "r"(retaddr)  );
            printf(" in f2: ebp: 0x%x  \tretaddr: 0x%x calladdr: 0x%x \n",ebpr,retaddr,calladdr);
        asm("movl 8(%1), %0": "=r"(calladdr) : "r"(retaddr)  );
            printf(" in f2: ebp: 0x%x  \tretaddr: 0x%x calladdr: 0x%x \n",ebpr,retaddr,calladdr);
        //this instruction is extracting the called address which is our function's address which was called

        printf("\n");
        asm("movl (%1), %0": "=r"(retaddr) : "r"(ebpr)  );
        ebpr=retaddr;
    }
    printf(" out f2\n");
}

void f1()
{
    printf(" in f1\n");
    f2();
    printf(" out f1\n");
}

void main()
{
    f1();
}

我的gdb调试结果是:

Breakpoint 1, f2 () at toUploadsaveStack02.c:37
37          ebpr=retaddr;
(gdb) disas f1
Dump of assembler code for function f1:
   0x0804860a <+0>: push   %ebp
   0x0804860b <+1>: mov    %esp,%ebp
   0x0804860d <+3>: sub    $0x18,%esp
   0x08048610 <+6>: movl   $0x804878b,(%esp)
   0x08048617 <+13>:    call   0x8048354 <puts@plt>
   0x0804861c <+18>:    call   0x8048424 <f2>
   0x08048621 <+23>:    movl   $0x8048792,(%esp)
   0x08048628 <+30>:    call   0x8048354 <puts@plt>
   0x0804862d <+35>:    leave  
   0x0804862e <+36>:    ret    
End of assembler dump.
(gdb) disas main
Dump of assembler code for function main:
   0x0804862f <+0>: push   %ebp
   0x08048630 <+1>: mov    %esp,%ebp
   0x08048632 <+3>: and    $0xfffffff0,%esp
   0x08048635 <+6>: call   0x804860a <f1>
   0x0804863a <+11>:    mov    %ebp,%esp
   0x0804863c <+13>:    pop    %ebp
   0x0804863d <+14>:    ret    
End of assembler dump.
(gdb) c
Continuing.
 in f2: ebp: 0xbffff3b8     f1 address is: 0x8048636 calladdr: 0xffffffd0 

 in f2: ebp: 0xbffff3b8     retaddr: 0x804863a calladdr: 0xc9fffffd 
 in f2: ebp: 0xbffff3b8     retaddr: 0x804863a calladdr: 0xe58955c3 
 in f2: ebp: 0xbffff3b8     retaddr: 0x804863a calladdr: 0xe8f0e483 
 in f2: ebp: 0xbffff3b8     f1 address is: 0x804863a calladdr: 0xffffffd0 
 in f2: ebp: 0xbffff3b8     retaddr: 0x804863a calladdr: 0xc35dec89 
 in f2: ebp: 0xbffff3b8     retaddr: 0x804863a calladdr: 0x89559090 
 in f2: ebp: 0xbffff3b8     retaddr: 0x804863a calladdr: 0x8dc35de5 


Breakpoint 1, f2 () at toUploadsaveStack02.c:37
37          ebpr=retaddr;
(gdb) bt
#0  f2 () at toUploadsaveStack02.c:37
#1  0x08048621 in f1 () at toUploadsaveStack02.c:45
#2  0x0804863a in main () at toUploadsaveStack02.c:51
(gdb) x /9x f1
0x804860a <f1>: 0x83e58955  0x04c718ec  0x04878b24  0xfd38e808
0x804861a <f1+16>:  0x03e8ffff  0xc7fffffe  0x87922404  0x27e80804
0x804862a <f1+32>:  0xc9fffffd
(gdb) x /9x main
0x804862f <main>:   0x83e58955  0xd0e8f0e4  0x89ffffff  0x90c35dec
0x804863f:  0xe5895590  0x748dc35d  0xbc8d0026  0x00000027
0x804864f:  0xe5895500
(gdb)

2 个答案:

答案 0 :(得分:1)

一般情况下,您无法做到这一点,因为您不了解来电者的堆叠布局。假设他们使用标准堆栈帧,您可以使用更便携的backtrace函数,如果需要符号,可以使用backtrace_symbols。也就是说,如果你坚持手动移动堆栈,这是一个例子:

#include"stdio.h"

void f2()
{
    puts(" in f2: entry point");
    unsigned long* ebp;
    int i=0;
    asm("movl %%ebp, %0": "=rm"(ebp));
    while(i++<2){
        printf("ret addr: %p instruction: %02x target: %p\n", ebp[1],
            *((unsigned char*)ebp[1]-5),
            *((int*)ebp[1]-1) + ebp[1]);
        ebp=(unsigned long*)*ebp;
    }
    printf(" out f2\n");
}

void f1()
{
    printf(" in f1\n");
    f2();
ret:
    printf(" out f1 at %p\n", &&ret);
}

void main()
{
    printf(" in main. f1=%p f2=%p\n", f1, f2);
    f1();
ret:
    printf(" out main at %p\n", &&ret);
}

示例运行:

 in main. f1=0x80484df f2=0x804844c
 in f1
 in f2: entry point
ret addr: 0x80484f6 instruction: e8 target: 0x804844c
ret addr: 0x8048536 instruction: e8 target: 0x80484df
 out f2
 out f1 at 0x80484f6
 out main at 0x8048536

答案 1 :(得分:1)

        //next statement fetching the return addresss-4 address value
        //this instruction is giving some false result(till now, don't know the reason)

这里的主要错误是假设call指令的长度为4 - 事实上,在Dump of assembler code for function f1我们看到call 0x8048424 <f2>是23 - 18 = 5字节长。然后从输出线

 in f2: ebp: 0xbffff3b8     retaddr: 0x804863a calladdr: 0xe8f0e483 

显示retaddr - 8处的四个字节,我们看到call指令的第一个字节(retaddr - 5)为e8

在一般情况下,您必须考虑不同的操作码和指令长度。