调用堆栈恢复实操

19 篇文章 0 订阅
1 篇文章 0 订阅

这篇文章用于实践关于x86下关于调用堆栈恢复。

具体原理请参看这篇文章 http://blog.csdn.net/djvc/article/details/10202201

函数调用栈的结构布局如下图所示

一个典型的栈帧
 
      ESP==>|           :             |
            |           .             |
            +-------------------------+
            | 被调用者保存的寄存器现场   |
            | EBX,ESI和EDI(根据需要) |
            +-------------------------+
            |  临时空间                |
            +-------------------------+
            |  局部变量#2              | [EBP - 8]
            +-------------------------+
            |  局部变量#1              | [EBP - 4]
            +-------------------------+
      EBP==>|  调用者的EBP             |
            +-------------------------+
            |  返回地址                |
            +-------------------------+
            |  实际参数#1              | [EBP + 8]
            +-------------------------+
            |  实际参数#2              | [EBP + 12]
            +-------------------------+
            |  实际参数#3              | [EBP + 16]
            +-------------------------+
            |  调用者保存的寄存器现场    |
            |  EAX,ECX和EDX(根据需要)|
            +-------------------------+
            |            :            |
            |            .            |
      
                     图 1     
图1是一个典型的栈帧,图中,栈顶在上,地址空间往下增长。

测试代码片段:

#include<stdlib.h>
#include<stdio.h>

void func1()
{
        printf("func1");
}


void func2(int a)
{
        int b = a+2;
        printf("func2 %d",b);
        func1();
}





int main(int argc,const char* argv[])
{
        func2(argc);
        return 0;
}

用gdb调试该程序

[pirate@ljf callstack]$ gdb callstack

(gdb) b callstack.c:6
Breakpoint 1 at 0x4005a8: file callstack.c, line 6.
(gdb) r
Starting program: /home/ejoy/cplus_test/callstack/callstack

Breakpoint 1, func1 () at callstack.c:6
6               printf("func1");

(gdb) info reg
rax            0x7      7
rbx            0x0      0
rcx            0x0      0
rdx            0x3e2937ae10     266979487248
rsi            0x400716 4196118
rdi            0x3e29379780     266979481472
rbp            0x7fffffffe4f0   0x7fffffffe4f0
rsp            0x7fffffffe4f0   0x7fffffffe4f0
r8             0x1      1
r9             0x1      1
r10            0xffffffff       4294967295
r11            0x246    582
r12            0x4004c0 4195520
r13            0x7fffffffe620   140737488348704
r14            0x0      0
r15            0x0      0
rip            0x4005a8 0x4005a8 <func1()+4>
eflags         0x202    [ IF ]
cs             0xe033   57395
ss             0xe02b   57387
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0

在这里,我得到了rbp里的值,就是我们需要关注的ebp,我们看看rbp指向的内存信息,

(gdb) x/100xg 0x7fffffffe4f0   ##由于虚拟机是64位的,所以显示按8字节处理

0x7fffffffe4f0: 0x00007fffffffe520      0x00000000004005e6
0x7fffffffe500: 0x00007fffffffe638      0x0000000100400665
0x7fffffffe510: 0x0000003e2900fb68      0x0000000300400620
0x7fffffffe520: 0x00007fffffffe540      0x0000000000400601
0x7fffffffe530: 0x00007fffffffe628      0x0000000100000000
0x7fffffffe540: 0x0000000000000000      0x0000003e2901ec5d
0x7fffffffe550: 0x0000000000000000      0x00007fffffffe628

由于是8字节,所以一行就显示2个字段。从上面我们就可以看出调用的层次关系了,譬如

0x7fffffffe4f0: 0x00007fffffffe520      0x00000000004005e6

                                   |                                         |                                        

                     上一层的栈帧地址          下一条指令的地址(函数返回后的执行命令地址)

顺藤摸瓜,

(gdb) b *0x4005a8
Note: breakpoint 1 also set at pc 0x4005a8.
Breakpoint 2 at 0x4005a8: file callstack.c, line 6.
(gdb) b *0x4005e6
Breakpoint 3 at 0x4005e6: file callstack.c, line 15.
(gdb) b *0x400601
Breakpoint 4 at 0x400601: file callstack.c, line 24.

对于指定文件和指定行数(上面代码行数)就可以指定调用层次了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值