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)
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
//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.
User contributions licensed under CC BY-SA 3.0