以一个简单求阶乘的代码为例:
1 #include <stdio.h> 2 3 unsigned int fact(unsigned int n) 4 { 5 if (n == 0) 6 return 1; 7 return n * fact(n - 1); 8 } 9 10 int main(void) 11 { 12 int c = 0; 13 14 c = fact(5); 15 16 return c; 17 }
其反汇编:
(gdb) disassemble main Dump of assembler code for function main: 0x080483bb <+0>: push %ebp 0x080483bc <+1>: mov %esp,%ebp 0x080483be <+3>: and $0xfffffff0,%esp 0x080483c1 <+6>: sub $0x20,%esp 0x080483c4 <+9>: movl $0x0,0x1c(%esp) 0x080483cc <+17>: movl $0x5,(%esp) 0x080483d3 <+24>: call 0x8048394 <fact> 0x080483d8 <+29>: mov %eax,0x1c(%esp) 0x080483dc <+33>: mov 0x1c(%esp),%eax 0x080483e0 <+37>: leave 0x080483e1 <+38>: ret End of assembler dump. (gdb) disassemble fact Dump of assembler code for function fact: 0x08048394 <+0>: push %ebp => 0x08048395 <+1>: mov %esp,%ebp 0x08048397 <+3>: sub $0x18,%esp 0x0804839a <+6>: cmpl $0x0,0x8(%ebp) 0x0804839e <+10>: jne 0x80483a7 <fact+19> 0x080483a0 <+12>: mov $0x1,%eax 0x080483a5 <+17>: jmp 0x80483b9 <fact+37> 0x080483a7 <+19>: mov 0x8(%ebp),%eax 0x080483aa <+22>: sub $0x1,%eax 0x080483ad <+25>: mov %eax,(%esp) 0x080483b0 <+28>: call 0x8048394 <fact> 0x080483b5 <+33>: imul 0x8(%ebp),%eax 0x080483b9 <+37>: leave 0x080483ba <+38>: ret
fact函数中栈如下:
| args | 参数列表
| return addr | 返回地址,即fact函数的下一条指令地址
ebp->| original ebp | main函数中的栈帧指针
...
esp->| |
因此,只要得到当前ebp, 那么(ebp)就是上一次函数调用的ebp, (ebp + 4)就是返回地址, (ebp + (i + 2) * 4)就是第i(i = 0, 1, 2...)个参数.
1 void backtrace(int level) 2 { 3 unsigned int *ebp = 0, *eip = 0, args[4] = {0}; 4 int i = 0, times = 0; 5 if (level < 0) 6 level = 0x7fffffff; 7 8 // get ebp 9 __asm__ __volatile__( 10 "mov %%ebp, %0 " 11 : "=m"(ebp) 12 ); 13 eip = (unsigned int *)ebp[1]; // get eip and args 14 for (i = 0; i < 4; i++) 15 args[i] = ebp[i + 2]; 16 17 while (*ebp != NULL && times < level) 18 { 19 printf(" ebp:0x%08x eip:0x%08x args:0x%08x 0x%08x 0x%08x 0x%08x ", 20 ebp, eip, args[0], args[1], args[2], args[3]); 21 ebp = (unsigned int *)ebp[0]; // get ebp 22 eip = (unsigned int *)ebp[1]; 23 for (i = 0; i < 4; i++) 24 args[i] = ebp[i + 2]; 25 times++; 26 } 27 }