计算机是如何工作的
反汇编一个简单的C程序
1、在实验环境下,创建main.c,并进入该文件进行代码的编写
在VIM文本编辑器中编写完代码后按“Shift”+“:”,发现没有进入预想的命令模式,查阅后发现需要先按“Esc”进入命令模式,再按“Shift”+“:”,然后输入“wq”即可进行保存。
2、对main.c文件进行直接编译,并查看程序的返回值
从下图可以看到开始时输入查看程序返回值的命令后一直没有正确返回,起初认为main.c没有编译成功,但是查看后a.out文件是存在的,最后发现在输入echo $?命令时少了“?”。所以一个小小的问题都会引起结果的错误,在输入命令时要格外认真。
3、将main.c编译成汇编代码
对main.s进行简化,以便分析汇编代码,结果如下:
4、分析汇编代码
整个程序包含main、f、g三个函数,在运行过程堆栈的变化如下:
程序从main 函数开始执行,具体过程如下:
- 第18行:pushl %ebp,ESP的地址减4即ESP指向标号1,将EBP寄存器的值放到栈顶;
- 第19行:movl %esp,%ebp,将EBP指向ESP所指的位置即标号1;
- 第20行:subl $4,%esp,ESP寄存器减4 即ESP指向标号2;
- 第21行:movl $1,(%esp) ,将立即数1放入到ESP所指向的位置,为即将调用的f函数做准备;
- 第22行:call f, 把EIP的值23压入栈顶,再将函数f的第一条指令的位置9放到EIP中,ESP指向标号3;
- 第9行:pushl %ebp, ESP指向标号4,将EBP寄存器的值放到栈顶;
- 第10行:movl %esp,%ebp, 将EBP指向ESP所指的位置即标号4;
- 第11行:subl $4,%esp,ESP寄存器指向标号5;
- 第12行:movl $8(%ebp),%eax ,EBP寄存器变址寻址,EBP寄存器的值加8,指向立即数为1的位置,将立即数1放到EAX寄存器中;
- 第13行:movl %eax,(%esp), 将EAX中存储的立即数1放到ESP所指的位置;
- 第14行:call g,把EIP的值即15压入栈顶,再将函数g的第一条指令的位置2放到EIP中,ESP指向标号6;
- 第2行:pushl %ebp, ESP指向标号7,将EBP寄存器的值放到栈顶;
- 第3行:movl %esp,%ebp,将EBP指向ESP所指的位置,并且EIP的值加一,指向8(%ebp),%eax。
- 第4行:movl 8(%ebp),%eax,EBP寄存器变址寻址,EBP寄存器的值加8,指向立即数为1的位置,将立即数1放到了EAX寄存器中;
- 第5行:addl $5,%eax,将立即数5加到EAX中,EAX的值即1+5为6;
- 第6行:popl %ebp,恢复函数f的函数调用堆栈基址EBP寄存器,即EBP指向标号4,ESP指向标号6;
- 第7行:ret ,将ESP寄存器所指向的栈空间存储单元放到EIP寄存器中,即EIP指向第15行指令,ESP指向标号5;
- 第15行:leave ,撤销函数堆栈,EBP指向标号1,ESP指向标号3;
- 第16行:ret ,将ESP寄存器所指向的内容放到EIP寄存器中,即EIP指向第23行指令,ESP指向标号2;
- 第23行:add $9,%eax, 把EAX寄存器立即数加9,即6+9为15。EAX存储器是默认存储函数返回值的寄存器;
- 第24行:leave,撤销函数main的堆栈;