code to get address of last 2 called functions

1

I was trying to get address of the function being called last time without the use of gdb so that i can directly trace the functions being called. I have got the return address but when i am tracing the address of function i am not getting the address of function in below the address of the instruction to be returned. I saw the Intel SDM(Software Development Manual) to check the the instruction opcode(opcode for call given is ec and ff). But on relative address difference i didn't get the address of the functions being called. I matched the content of memory using my function as well as gdb, and they are same.

Please help me figure out the address of last 2 functions being called. Thanks in advance.

My code and my gdb debugging results are as follows:

My code is:

#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();
}

My gdb debugging results are:

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)
linux
assembly
stack
asked on Stack Overflow Jan 15, 2015 by Ankit Sharma

2 Answers

1

In general, you can't do that because you don't know the stack layout of your callers. Assuming they use standard stack frames you can use the backtrace function which is more portable, or backtrace_symbols if you want symbols. That said if you insist on manually walking the stack, here is an example:

#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);
}

Sample run:

 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
answered on Stack Overflow Jan 15, 2015 by Jester • edited Jan 16, 2015 by Jester
1
        //next statement fetching the return addresss-4 address value
        //this instruction is giving some false result(till now, don't know the reason)

The principal error here is to assume that the length of the call instruction were 4 - in fact, in the Dump of assembler code for function f1 we see that the call 0x8048424 <f2> is 23 - 18 = 5 bytes long. Then from the output line

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

which shows the four bytes at retaddr-8 we see that the first byte of the call instruction (at retaddr-5) is e8.

In the general case, you would have to account for different opcodes and instruction lengths.

answered on Stack Overflow Jul 27, 2015 by Armali

User contributions licensed under CC BY-SA 3.0