获取最后 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
.
在一般情况下,您必须考虑不同的操作码和指令长度。
我试图在不使用 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
.
在一般情况下,您必须考虑不同的操作码和指令长度。