从反汇编代码分析c语言在x86上的工作原理

来源:互联网 发布:python 线程同步 编辑:程序博客网 时间:2024/04/27 18:01

一.首先给出c语言程序代码:

//文件名:main.cint g(int x){    return x+700;}int f(int x){    return g(x);}int main(){    return f(5)+1;}

二.用GCC 将C语言程序代码编译为汇编代码:

在命令行中输入命令:gcc –S –o  main.s  main.c -m32


编译输出的汇编代码如图2-1所示:


图2-1(实验截图)


为了更加方便地去分析程序,首先对汇编代码进行进一步的处理,去掉了以'.'号开头的代码,处理后的汇编代码如下:

g:pushl%ebpmovl%esp, %ebpmovl8(%ebp), %eaxaddl$700, %eaxpopl%ebpretf:pushl%ebpmovl%esp, %ebpsubl    $4, %espmovl8(%ebp), %eaxmovl%eax, (%esp)callgleaveretmain:pushl%ebpmovl%esp, %ebpsubl$4, %espmovl$5, (%esp)callfaddl    $1, %eaxleaveret


三.汇编程序分析

程序看上去很简单,但是当操作系统将我们的程序加载到内存中时执行时,操作系统会对程序内的偏移地址进行重定位操作。此时程序在内存中地址将变得十分的复杂。显然这对我们的程序分析是十分不利的.因此在开始分析程序之前我们有必要做一些事先的约定假设。当然这些预定假设并不会对程序的准确性和严密性产生丝毫的影响。

假设如下:

3个函数g()、f()、main()的开始地址分别为200、300、400,
在进入main函数之前寄存器ebp和esp的值如下:ebp=100;esp=90;


好了下面正式开始我们的分析

;首先重main函数开始

;进入main函数

pushl        %ebp                   ;将栈基地址压栈 。寄存器状态:ebp=100;esp=86; 

;内存单元87-100存放的是main函数外部的栈基地址

movl         %esp, %ebp        ;将栈相对的清空了 。寄存器状态:ebp=86;esp=86;
subl          $4, %esp              ;栈顶指针减4 。寄存器状态:ebp=86;esp=82;
movl  $5, (%esp)           ;立即数5存放在内存单元82-85 中。

;调用函数f(5)
call            f                          ;这里一共完成了两个动作:将EIP指针自增后圧栈然后将EIP指针指向f()函数的地址

;寄存器状态:eip=300     ;ebp=86;esp=78;
;进入函数f
        pushl%ebp                  ;内存单元74-77存放86
movl %esp, %ebp        ; esp=74;ebp=74;
subl    $4, %esp              ;esp=70
movl 8(%ebp), %eax    取出参数
movl %eax, (%esp)       ;将eax的值5存放到地址70-73

;进入函数g()
call g                                   ;调用函数g,eip圧栈(地址为66-69),esp=66,ebp=74;eip=200

pushl %ebp                    ;内存单元62-65存放74   ;ebp=74;esp=62; 
movl %esp, %ebp          ;ebp=esp=62;
movl 8(%ebp), %eax     ;从地址62+8的位置取出32位整数5放入寄存器eax中。
addl     $700, %eax                寄存器eax自增700,后eax=705
popl     %ebp                        ;清理堆栈,ebp=74;esp=66
ret                                         ;函数返回到g,等价于 popl eip ;esp=70 ebp=74

;已经离开函数g,返回值存放在寄存器eax中。
;准备离开函数f

leave                                     ;离开函数,popl-> ebp ,esp=ebp=86;   
ret                                         ;返回到main函数中

;已经离开函数f,返回值存放在寄存器eax中。
addl    $1, %eax                    ;eax =706寄存器
leave                                      ;清理main函数的堆栈
ret                    
;main函数已经返回


总结:
1.不关在高级语言中函数是如何相互调用的,最终都会被编译器编译为指令序列。
2.c语言中函数的参数通过栈传递
3.在子程序中使用了的寄存器必须在调用子程序前圧栈,这一步骤称为保存现场。子程序使用完毕返回时必须将上一次圧栈的寄存器值恢复到寄存器,这一步骤称为恢复现场。这里的保存现场和恢复现场由编译器处理。

2.宏指令enter和leave 实现了高级语言中函数与函数间的逻辑隔离。
宏指令:enter 被展开为:
pushl%ebp
movl %esp, %ebp        

宏指令:leave 被展开为:
movl        %ebp,%esp
popl        %ebp  




作者:徐崇龙
2015年3月8日
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000



0 0