【转】根据EBP得到函数堆栈
【原文地址】http://blog.chinaunix.net/uid-8118579-id-2034131.html
如果stack没有corruption的话,我们可以利用GDB的bt命令得到function的backtrace.
但如果stack corruption了,gdb是不能帮我们得到backtrace的。本文介绍了根据EBP得到backtrace的方法,这种方法只适用于X86架构。
栈的基本模型
参数N |
↓高地址 |
参数… |
函数参数入栈的顺序与具体的调用方式有关 |
参数 3 |
|
参数 2 |
|
参数 1 |
|
eip |
返回本次调用后,下一条指令的地址 |
ebp |
这里保存调用者的ebp,然后ebp寄存器会指向此时的栈顶。 |
临时变量1 |
|
临时变量2 |
|
临时变量3 |
|
临时变量… |
|
临时变量n |
↓低地址 |
函数调用发生的时候,先把函数参数(从右往左顺序压)压入stack,再压入函数调用的下条指令的address. 接着进入调用函数体中先执行"pushl %ebp"和"movl %esp, %ebp"(一般已经由编译器加入到函数头中了),接着就是把函数体中的局部变量压入栈中。再遇到函数的调用的嵌套则依此类推。
这个方法的基本原理是先得到EBP中的地址,根据这个地址可以得到调用者的ebp的地址,然后我们可以根据调用者的EBP得到调用函数时的指令地址(ebp+4, 32-bit OS),然后利用gdb的disassem命令得到函数调用的地址。以此类推,我们可以得到整个函数调用的堆栈。
一个例子:
#include
void funcc(int c)
{
printf("%d\n", c);
}
void funcb(int b, int c)
{
printf("%d\n", b);
funcc(c);
}
void funca(int a, int b, int c)
{
printf("%d\n", a);
funcb(b, c);
}
main()
{
int a = 4;
int b = 3;
int c = 2;
int d = 1;
funca(a, b, c);
printf("%d\n", d);
}
Generating a binary with debug info using GCC.
gcc –g –o 12 12.c
Then using gdb to get the backtrace according to EBP.
gdb 12
(gdb) b funcc
Breakpoint 1 at 0x4004a3: file 12.c, line 5.
(gdb) r
Starting program: /home/jx/example/12
4
3
Breakpoint 1, funcc (c=2) at 12.c:5
5 printf("%d\n", c);
(gdb) info reg
rax 0x2 2
rbx 0x3a11c19bc0 249406004160
rcx 0x3a1214a760 249411446624
rdx 0x3a1214b920 249411451168
rsi 0x2aaaaaaac000 46912496123904
rdi 0x2 2
rbp 0x7fff60038730 0x7fff60038730 =è64bit OS, ebp has changed to rbp.
rsp 0x7fff60038720 0x7fff60038720
r8 0xffffffff 4294967295
r9 0x3a11f12a40 249409120832
r10 0x22 34
r11 0x246 582
r12 0x0 0
r13 0x7fff60038870 140734804232304
r14 0x0 0
r15 0x0 0
rip 0x4004a3 0x4004a3<funcc+11>
eflags 0x202 [ IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) x /8x 0x7fff60038738(0x7fff60038730 + 8, 64bit OS, rbp + 8, eip)
(gdb) x /2x 0x7fff60038738
0x7fff60038738: 0x004004df 0x00000000
(gdb) disassem 0x004004df
Dump of assembler code for function funcb:
0x00000000004004b7 <funcb+0>: push %rbp
0x00000000004004b8 <funcb+1>: mov %rsp,%rbp
0x00000000004004bb <funcb+4>: sub $0x10,%rsp
0x00000000004004bf <funcb+8>: mov %edi,0xfffffffffffffffc(%rbp)
0x00000000004004c2 <funcb+11>: mov %esi,0xfffffffffffffff8(%rbp)
0x00000000004004c5 <funcb+14>: mov 0xfffffffffffffffc(%rbp),%esi
0x00000000004004c8 <funcb+17>: mov $0x400658,%edi
0x00000000004004cd <funcb+22>: mov $0x0,%eax
0x00000000004004d2 <funcb+27>: callq 0x400398<printf@plt>
0x00000000004004d7 <funcb+32>: mov 0xfffffffffffffff8(%rbp),%edi
0x00000000004004da <funcb+35>: callq 0x400498
0x00000000004004df <funcb+40>: leaveq
0x00000000004004e0 <funcb+41>: retq
(gdb) x /2x 0x7fff60038730 ==========èget upper stack’s rbp
0x7fff60038730: 0x60038750 0x00007fff
(gdb) x /2x 0x7fff60038758 ========è upper stack’s rip
0x7fff60038758: 0x0040050f 0x00000000
(gdb) disassem 0x0040050f
Dump of assembler code for function funca:
0x00000000004004e1 <funca+0>: push %rbp
0x00000000004004e2 <funca+1>: mov %rsp,%rbp
0x00000000004004e5 <funca+4>: sub $0x10,%rsp
0x00000000004004e9 <funca+8>: mov %edi,0xfffffffffffffffc(%rbp)
0x00000000004004ec <funca+11>: mov %esi,0xfffffffffffffff8(%rbp)
0x00000000004004ef <funca+14>: mov %edx,0xfffffffffffffff4(%rbp)
0x00000000004004f2 <funca+17>: mov 0xfffffffffffffffc(%rbp),%esi
0x00000000004004f5 <funca+20>: mov $0x400658,%edi
0x00000000004004fa <funca+25>: mov $0x0,%eax
0x00000000004004ff <funca+30>: callq 0x400398<printf@plt>
0x0000000000400504 <funca+35>: mov 0xfffffffffffffff4(%rbp),%esi
0x0000000000400507 <funca+38>: mov 0xfffffffffffffff8(%rbp),%edi
0x000000000040050a <funca+41>: callq 0x4004b7
0x000000000040050f <funca+46>: leaveq
0x0000000000400510 <funca+47>: retq
End of assembler dump.
(gdb) x /2x 0x7fff60038750 =èget upper upper ebp from upper ebp
0x7fff60038750: 0x60038770 0x00007fff
(gdb) x /2x 0x7fff60038778 =èeip from ebp
0x7fff60038778: 0x00400543 0x00000000
(gdb) disassem 0x00400543
Dump of assembler code for function main:
0x0000000000400511 <main+0>: push %rbp
0x0000000000400512 <main+1>: mov %rsp,%rbp
0x0000000000400515 <main+4>: sub $0x10,%rsp
0x0000000000400519 <main+8>: movl $0x4,0xfffffffffffffff0(%rbp)
0x0000000000400520 <main+15>: movl $0x3,0xfffffffffffffff4(%rbp)
0x0000000000400527 <main+22>: movl $0x2,0xfffffffffffffff8(%rbp)
0x000000000040052e <main+29>: movl $0x1,0xfffffffffffffffc(%rbp)
0x0000000000400535 <main+36>: mov 0xfffffffffffffff8(%rbp),%edx
0x0000000000400538 <main+39>: mov 0xfffffffffffffff4(%rbp),%esi
0x000000000040053b <main+42>: mov 0xfffffffffffffff0(%rbp),%edi
0x000000000040053e <main+45>: callq 0x4004e1
0x0000000000400543 <main+50>: mov 0xfffffffffffffffc(%rbp),%esi
0x0000000000400546 <main+53>: mov $0x400658,%edi
0x000000000040054b <main+58>: mov $0x0,%eax
0x0000000000400550 <main+63>: callq 0x400398<printf@plt>
0x0000000000400555 <main+68>: leaveq
0x0000000000400556 <main+69>: retq
End of assembler dump.
So we got the backtrace as main->funca->funcb->funcc