一个简单程序的 gdb 反汇编 分析

本文通过一个简单的C语言程序实例,详细解析了使用GDB进行调试的过程及反汇编代码的具体含义,包括GCC编译选项、watchpoint设置、内存内容查看等关键点。

《深入理解计算机》的第三章已经看了两遍了,一直都是在看书里的程序,一些内容都是第一次接触,也一直没有在真正的Linux上用过,今天写了下发现和书里的还是有些出入,下面是我自己的理解。

gdb除了反汇编外,还可以用于代码的调试。下面就简单记录下容易忘记的部分。

1.gcc -g -o code code.c   这里不要忘记-g

2.watchpoint的概念

   VC用惯了就是不好,在vc里ms没有这个概念,至少我没遇到过。在gdb中有一个watch命令,格式是watch + addr, 当内存中这个地址被写入新内容时程序就会中断。

3.查看内存内容的方法

  X/4b  &temp

  这个意思就是输出7个byte从temp所在地址开始的内存内容。

  比如我的temp = 0xfeff,那么会输出0xff 0xfe 0x00 0x00(小端地址法)

 

下面是自己花了点时间在Linux下写了一个简单的程序,并分析了下反汇编的代码,以及内存情况。

第一部分是c语言代码,编译时用gcc -o code code.c,没用-O2优化

 

第二部分是用gdb code的反汇编代码,';'后面是我自己的注释,关于前4句的原理可以看最下面的文章,对理解比较重要。

有疑问的部分:

0x080483d8 <main+49>:   add    $0x10,%esp:从这里往前看,似乎只有3次push操作,应该add 0xc,只是在这个程序中多add0x4也没关系。

0x080483ec <main+69>:   sub    $0x8,%esp: 这个好像没用到,好像也不是用于字节对齐,因为在它前面一次调用也是从这个值开始的。

 

在这里leave等价于:

mov %esp,%ebp;

pop %ebp

 

 

第三部分是内存变化的简单示意图:

 

-----------------------------------------------------------------------------------------------------------------------

下面来自于网上

C 代码:

int main(){
}

 

push %ebp
mov %esp,%ebp
sub $0x8,%esp//这句可以勉强理解,可能是为以后定义变量欲留位置,不知对否?
and $0xfffffff0,%esp//这里就不好理解了,为什么要把后面的4位全都置0???
mov $0x0, %eax
sub %eax,%esp
leave
ret

 

首先我觉得有必要把LEAVE替换一下,他等价于mov esp,ebp;pop ebp这两条指令.因此有如下的解释:

 

因此在执行了1之后的堆栈图是: ----> 这个ARG其实就是int main(int argc, char argv[]),其实这里还可以跟一个env来着
ARG[N]
..........
ARG[0]
EIP
EBP //ARG表示涵数的参数,对于MAIN涵数,POSIX定义了两个,历史上有三个的.
因此3语句的作用正好把ESP指向了为ARG[0]保留的地址,也就是跳过为EBP和EIP保留的位置
那么4语句就是为ARG[0]ARG[1]保留位置了
由于MAIN被POSIX规定了两个参数,因此不论有没有都要保留两个位置,又因为没有ARG[2]和MAIN的局部变量,所以把EAX置0,这样一举两得,EAX又可以作为返回值,(通常涵数返回值是INT的,都用EAX表示).
其实大家可能还有不理解,那就是既然涵数的参数都已经压栈了,直接用不就行了?为什么还要拷贝过来呢?
这是C语言的要求,为了局部化,从逻辑上,EBP(含)以上都是属于主调涵数的,所以拷过来当然是必要的.否则C语言如何实现传值而不改变主调涵数的值呢?
整个注释如下:
1:push %ebp
2:mov %esp,%ebp
3:sub $0x8,%esp //为EBP,EIP保留位置
4:and $0xfffffff0,%esp //为ARG[0],ARG[1]保留位置
5:mov $0x0, %eax //一举两用,既作返回值,有表示不需要保留位置
6:sub %eax,%esp //保留0个位置
mov %epb ,%esp //恢复ESP到PUSH %EBP指令时的位置
pop %ebp //恢复EBP的值
ret

<think> 嗯,用户这次询问的是如何使用gdb通过反汇编分析coredump文件。用户在前面的引用中已经提到了gdb、objdump等工具,但明确指出需要的是gdb反汇编分析方法。 从引用材料来看,用户可能已经尝试过基本的bt命令但效果不佳(如引用1中提到"只能看到堆栈,看不到具体参数的数据信息")。这说明用户遇到了更深层次的问题,需要进入汇编层面分析。用户应该是Linux环境下的C/C++开发者,正在调试一个棘手的段错误问题。 用户可能真正需要的是: 1)如何定位到汇编指令级别的崩溃点 2)如何解读关键寄存器和内存状态 3)如何将汇编映射回源代码 这些在引用2和引用3中其实有零散线索,但需要系统整理。 特别注意引用3提到的"-g3"编译选项很关键,这能保留宏定义等调试信息。另外引用2中提到的info registers命令特别重要,但用户可能没注意到。 考虑到用户已经尝试过bt失败,应该重点教他frame结合disas的方法,而不是单纯堆栈跟踪。对于"print 0x0"这种空指针问题,通过反汇编看寄存器状态比看源代码更直接。 用户似乎对valgrind报错有困惑(引用1中的工具启动失败),但其实对于即时崩溃的场景,gdb反汇编比valgrind更合适,这点需要说明。另外要警告用户注意编译优化问题(-O0),否则反汇编可能误导。 最后生成的问题考虑了用户可能的后续需求:如何解读汇编(问题1)、没有符号表怎么办(问题2)、以及混合源码分析技巧(问题3)。特别是问题2很实用,现实中经常遇到没有调试符号的生产环境问题。 </think> 以下是使用 GDB 通过反汇编分析 Core Dump 的详细方法,结合引用内容整理关键步骤: --- ### **核心步骤** 1. **编译时添加调试信息** 在编译程序时**必须添加 `-g` 调试选项**(建议配合 `-O0` 禁用优化): ```bash gcc -g -O0 your_program.c -o your_program ``` 否则无法查看源代码关联信息(引用[3])。 2. **启动 GDB 加载 Core 文件** ```bash gdb ./your_program core_file ``` 3. **查看崩溃位置** ```gdb (gdb) bt # 显示崩溃时的函数调用栈 (gdb) frame N # 切换到栈帧N(N为bt输出的编号) ``` 4. **反汇编关键代码** - **反汇编当前函数**: ```gdb (gdb) disas /m # 混合显示汇编与源码行号 ``` - **反汇编指定函数**: ```gdb (gdb) disas function_name ``` 5. **定位具体指令** - **查看程序计数器(PC)**: ```gdb (gdb) info registers rip # x86_64 架构 (gdb) x/i $pc # 显示当前指令 ``` - **反汇编指定地址范围**(示例): ```gdb (gdb) disas 0x4005a0, 0x4005c0 ``` 6. **分析寄存器和内存** - 检查寄存器值: ```gdb (gdb) info registers # 所有寄存器 (gdb) print $rax # 特定寄存器 ``` - 检查内存内容: ```gdb (gdb) x/4xw 0x7fffffffd100 # 以4字节16进制格式查看4个字 ``` --- ### **关键技巧** 1. **结合源码与汇编** 使用 `disas /m` 可同时显示汇编指令和对应的源码行号(需编译时加 `-g`)。 2. **检查无效内存访问** 若崩溃指令是 `mov` 或访存操作: ```gdb (gdb) print *(char**)0x7fffffffd100 # 检查指针指向内容 (gdb) info symbol 0x4005a0 # 查看地址所属符号 ``` 3. **验证函数参数** 在函数入口断点处检查参数是否异常: ```gdb (gdb) frame 1 (gdb) info locals # 查看局部变量(引用[2]) (gdb) print *this # 若 this=0x0 则为空指针调用(引用[1]) ``` 4. **无调试符号时的补救** 若缺少调试符号: - 用 `objdump -D your_program > disasm.txt` 生成完整反汇编 - 在 GDB 中用 `disas $pc-20, $pc+20` 查看崩溃点上下文 - 通过 `addr2line -e your_program 0x4005a0` 转换地址到代码位置(引用[1]) --- ### **典型场景示例** **问题:空指针解引用** ```gdb (gdb) bt #0 0x0000000000400556 in GetTestItem (this=0x0) at test.cpp:10 (gdb) disas /m Dump of assembler code for function GetTestItem: 10 return this->item; # 源码行 0x000000000040054d <+0>: mov 0x8(%rdi),%rax # 崩溃指令:访问rdi+8 0x0000000000400551 <+4>: retq End of assembler dump (gdb) print $rdi # 输出 $1 = 0 → this 指针为空! ``` --- ### **常见错误解决** - **`valgrind` 不可用**:安装完整工具链 `sudo apt install valgrind`(引用[1]) - **无调试信息**:重新编译程序并确保带 `-g` 选项(引用[3]) - **地址无符号**:用 `nm your_program | grep 地址前缀` 查找最近符号 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值