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

code to get address of last 2 called functions

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

请帮我找出最后调用的两个函数的地址。 提前致谢。

我的代码和我的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    [=11=]x18,%esp
   0x08048610 <+6>: movl   [=11=]x804878b,(%esp)
   0x08048617 <+13>:    call   0x8048354 <puts@plt>
   0x0804861c <+18>:    call   0x8048424 <f2>
   0x08048621 <+23>:    movl   [=11=]x8048792,(%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    [=11=]xfffffff0,%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)

一般来说,您不能这样做,因为您不知道调用者的堆栈布局。假设他们使用标准堆栈帧,您可以使用更便携的 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
        //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.

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