1、通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的

姓名:周毅

原创作品转载请注明出处 

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

一、基础知识

1、计算机一条一条的执行指令;

2、寄存器%EIP总是存着下一条指令所在内存的地址;

3、本课程用到的3个寄存器(32位):

          %eax:默认“累加器”,也是一个通用寄存器,往往也是存储函数返回值的寄存器。

          %esp:“基址指针”,一般作为一个函数的框架指针;

          %ebp:堆栈指针,典型的用法是配合push和pop汇编指令间接改变自身,%ebp就是栈顶指针;

4、本课程用到的汇编指令:

      movl A,B          将A赋值给B,其中A可为寄存器、立即数、内存地址、对应内存地址中的内存等,B可为寄存器、对应内存地址指向的空间;

     

      addl A,B           B=A+B

       subl A,B           B=B-A

      push和pop指令

        push A   将A入栈,栈顶指针esp减4然后存入栈顶

        pop A     esp地址内容出栈赋值给A,esp加4


        在函数开头处经常见到如下汇编指令:

      

      其含义表明将上一个函数的框架指针%ebp入栈,然后将当前栈顶指针%esp作为当前函数的框架指针。

      leave指令

       leave等价于:

       movl %ebp,%esp

       popl  %ebp

       在ret指令之前,作为恢复返回函数的基址和栈顶指针状态。

       call和ret指令

    call A       A为函数名或地址,表示执行A函数或执行A地址的命令;

    ret            返回执行调用当前函数指令的下一条指令;

      call和ret成套使用,如同C语言的调用和返回,图中右边可以看成左边call和ret的伪汇编执行(实际%eip是无法直接访问的)。


5、本课程需要用到的linux命令

      得到C语言code.c的汇编代码code.s:

gcc -S -o code.s code.c 

      得到C语言code.c的可执行程序code,-g将代码信息编译到可执行文件中方便gdb调试:

gcc -o code code.c -g

      gdb调试:

gdb code                   调试可执行程序code

(gdb) r                    运行到下一个断点处

(gdb) b 8                  行号8设置断点

(gdb) b main               main函数名设置断点

(gdb) b *0x8048333         在内存0x8048333处设置断点
(gdb) s                    执行下一条语句(若调用函数则进入函数)

(gdb) n                    执行下一条语句,调用函数当做一条语句

(gdb) p i                  输出变量i的值

(gdb) i r                  输出所有寄存器的值

(gdb) q                    退出


二、实验

1、创建文件code.c,然后写入如下C代码:

 

2、得到对应汇编指令文件code.s:

 

3、打开code.s:

 

4、去掉.开头的行:

 

5、汇编代码分析:

 




 












6、结果验证(查看寄存器里的值):

 

      odjdump -d code得到code的汇编代码(因为实验楼环境为64位,所以寄存器为%r开头)

 

      使用gdb调试,我们在main函数的返回指令0x400525处设置断点,这时查看%rax的值发现为13,与分析结果一致。

 

6、结果验证(直接C语言调试):

      修改源代码如下:

 

      gdb调试,b 12在12行设置断点,r运行发现12行a=f(9)+1正要执行,n执行这条指令,p a输出a的值为13,符合分析结果。

 

 

三、总结

      这次实验用到了gcc和gdb的常用命令,涉及到汇编及其指令执行时内存和寄存器的变化。

       通过这次实验,了解了计算机执行程序时的内部原理:

       1、每条指令执行时,先从eip取指令地址,然后从内存地址处取指令执行;

      2、eip总是存下一条指令(取指后eip自动指向下条指令地址);

      3、eip不能直接修改,只能通过转跳指令call,ret,jmp等间接修改eip,即下条指令的执行地址;

      4、call指令执行时,计算机先将下条指令地址eip存入栈中,然后改变eip地址为需要执行的地址;

      5、ret指令执行时,计算机出栈恢复执行call时的下条指令地址至eip,执行下条指令相当于返回执行,所以call,ret总是成对的;

      6、ebp往往作为一个函数的基地址,esp作为栈顶地址,所以函数开始往往有push %ebp,mov %esp,%ebp两条指令;

      7、leave与第6条相对,往往在ret前出现,相当于pop %ebp,mov %ebp,%esp两条指令,恢复调用前的基址和栈顶指针。

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值