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

《深入理解计算机》的第三章已经看了两遍了,一直都是在看书里的程序,一些内容都是第一次接触,也一直没有在真正的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优化

#include <stdio.h> int add(int a, int b) { return a + b; } int main() { int sum, a, b; scanf("%d%d",&a, &b); sum = add(a, b); printf("sum = %d/n", sum); return 0; }

 

第二部分是用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

 

 

Dump of assembler code for function main: 0x080483a7 <main+0>: push %ebp 0x080483a8 <main+1>: mov %esp,%ebp 0x080483aa <main+3>: sub $0x18,%esp 0x080483ad <main+6>: and $0xfffffff0,%esp 0x080483b0 <main+9>: mov $0x0,%eax 0x080483b5 <main+14>: add $0xf,%eax 0x080483b8 <main+17>: add $0xf,%eax 0x080483bb <main+20>: shr $0x4,%eax 0x080483be <main+23>: shl $0x4,%eax ;%eax = 0x10 0x080483c1 <main+26>: sub %eax,%esp ;%esp -= 0x10 0x080483c3 <main+28>: sub $0x4,%esp ;%esp -= 0x4 0x080483c6 <main+31>: lea 0xfffffff4(%ebp),%eax 0xfffffff4 = -12 = -0xc 0x080483c9 <main+34>: push %eax ;push &b 0x080483ca <main+35>: lea 0xfffffff8(%ebp),%eax 0xfffffff8 = -8 = -0x8 0x080483cd <main+38>: push %eax ;push &a 0x080483ce <main+39>: push $0x80484e8 ;point to "%d%d" 0x080483d3 <main+44>: call 0x80482c4 ;call scanf() 0x080483d8 <main+49>: add $0x10,%esp ;%esp back, but I should it just need ot back 0xc 0x080483db <main+52>: pushl 0xfffffff4(%ebp) ;b 0x080483de <main+55>: pushl 0xfffffff8(%ebp) ;a 0x080483e1 <main+58>: call 0x804839c <add> ;call add() 0x080483e6 <main+63>: add $0x8,%esp ;%esp back 0x8 0x080483e9 <main+66>: mov %eax,0xfffffffc(%ebp) ;0xfffffffc = -4 = -0x4 0x080483ec <main+69>: sub $0x8,%esp ;reserve 0x8, seems not used 0x080483ef <main+72>: pushl 0xfffffffc(%ebp) ;push sum 0x080483f2 <main+75>: push $0x80484ed ;point to "sum = %d/n" 0x080483f7 <main+80>: call 0x80482e4 ;call printf() 0x080483fc <main+85>: add $0x10,%esp %esp back 0x10 0x080483ff <main+88>: mov $0x0,%eax ;set return_val 0 0x08048404 <main+93>: leave 0x08048405 <main+94>: ret 0x08048406 <main+95>: nop 0x08048407 <main+96>: nop End of assembler dump. (gdb) Dump of assembler code for function add: 0x0804839c <add+0>: push %ebp 0x0804839d <add+1>: mov %esp,%ebp 0x0804839f <add+3>: mov 0xc(%ebp),%eax 0x080483a2 <add+6>: add 0x8(%ebp),%eax 0x080483a5 <add+9>: leave 0x080483a6 <add+10>: ret End of assembler dump.

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

一个简单程序的 gdb 反汇编 分析_第1张图片

 

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

下面来自于网上

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

你可能感兴趣的:(c,linux,function,汇编,语言,byte)