本次学习内容为计算机的工作原理,主要从存储计算机的工作模型,基本的汇编语言以及汇编代码的执行过程三个方面来进行整理,重点分析汇编代码的执行过程,以一个简单的C语言程序反汇编实验为例进行分析。
存储程序的计算机工作模型
冯诺依曼体系结构
冯诺依曼体系结构的核心思想是存储程序计算机,即将程序和数据都存放在计算机中,按存储器的存储程序首地址执行程序的第一条指令,是各种计算机体系结构都要遵从的一个“客观规律”。
冯诺依曼体系主要特点有三个:
1.冯诺依曼体系将计算机分为:内存(程序内存,数据内存),CPU(运算器,控制器,寄存器),输入设备和输出设备。其中运算器,存储器,控制器与输入输出设备五部分构成了计算机的硬件。
2.计算机内部采用二进制来表示指令和程序
3.将编写好的程序和数据先存入存储器中然后启动计算机工作。
CPU的工作过程可以大体分为三步:取指令,分析指令与执行指令。其中,指令数据与地址的暂存都发生在寄存器中。汇编语言的主要作用对象就是寄存器。CPU的寄存器主要分为通用寄存器,控制寄存器和段寄存器三种。Intel处理器系列(X86)经历了16位(8086,1978),32位(i386,1985)和64位(Pentium 4E,2004)三个关键阶段。不同位数CPU的寄存器功能相差不大,下面详细介绍X86-32的相关情况。
X86-32有8个通用寄存器:4个数据寄存器(EAX、EBX、ECX和EDX),2个变址寄存器(ESI和EDI)以及2个栈指针寄存器(ESP和EBP);6个段寄存器(ES、CS、SS、DS、FS和GS);2个控制寄存器:1个指令指针寄存器(EIP),1个标志寄存器(EFlags)。
基本的汇编语言
本文简单介绍几个汇编指令,详细的汇编指令解释可从以下链接查看https://blog.csdn.net/liuy88141/article/details/46391211?utm_source=blogxgwz0
MOVL
字符串传送指令 MOVL
格式: MOVL OPRD1,OPRD2
功能: OPRD1<--OPRD2.
说明:
- 其中OPRD2为源串符号地址,OPRD1为目的串符号地址.
- 字节串操作: 若DF=0,则作加, 若DF=1,则作减.
- 对字串操作时: 若DF=0,则作加,若DF=1,则作减,.
- 在指令中不出现操作数时,字节串传送格式为MOVSB、字串传送格式为MOVSW.
- 本指令不影响标志位.
POPL
堆栈操作指令POPL
格式: POPL OPRD
功能:实现弹出操作的指令
说明:
- OPRD为32位(字)操作数,可以是寄存器或存储器操作数.
- POPL指令的操作过程是: POLP OPRD:OPRD<--((SP)),(SP)<--(SP)+2
它与压入操作相反,是先弹出栈顶的数顶,然后再修改指针SP的内容.
3.对状态标志位没有影响
PUSHL
堆栈操作指令 PUSHL
格式: PUSHL OPRD
功能: 实现压入操作的指令
说明:
- OPRD为32位(字)操作数,可以是寄存器或存储器操作数.
- PUSHL的操作过程是: (SP)<--(SP)-2,((sp))<--OPRD 即先修改堆栈指针SP(压入时为自动减2),然后,将指定的操作数送入新的栈顶位置.
此处的((SP))<--OPRD,也可以理解为: [(SS)*16+(SP)]<--OPRD 或 [SS:SP]<--OPRD
3.对状态标志位没有影响
CALL
过程调用指令 CALL
格式: CALL OPRD
功能: 过程调用指令
说明:
- 其中OPRD为过程的目的地址.
- 过程调用可分为段内调用和段间调用两种.寻址方式也可以分为直接寻址和间接寻址两种.
- 本指令不影响标志位.
RET
返回指令 RET
格式: RET
功能: 当调用的过程结束后实现从过程返回至原调用程序的下一条指令,本指令不影响标志位.
说明:
由于在过程定义时,已指明其近(NEAR)或远(FAR)的属性,所以RET指令根据段内调用与段间调用,执行不同的操作
对段内调用: 返回时,由堆栈弹出一个字的返回地址的段内偏移量至IP.
对段外调用: 返回时,由堆栈弹出的第一个字为返回地址的段内偏移量,将其送入IP中,由堆栈弹出第二个字为返回地址的段基址,将其送入CS中.
汇编代码的执行过程
在本部分我们以一个反汇编的小实验为例进行分析。实验环境为实验楼提供的基于Web访问方式的64位虚拟机环境。具体过程如下;
对main.c文件进行编译
产生“.s”为扩展名的汇编代码文件main.s
汇编指令执行过程分析
图中最后得到的代码主要分为三个部分:g函数,f函数以及main函数。
程序从main函数开始执行
pushl%ebp
esp的值首先减4,然后将ebp的值存储在esp指示的位置,则栈中序号为1的位置存储了ebp的当前值(0),ebp仍指向位置0,esp指向位置1;
movl %esp, %ebp
ebp与esp都指向位置1;
subl $4, %esp
esp指向位置2;
movl $12, (%esp)
栈的位置2存储立即数12;
call f
首先将eip压入栈中,eip此时的值是下一条将要执行的指令的位置,我们不妨以行号代表,即将行号23存入栈中位置3,此时esp指向位置3,ebp指向位置1;然后将函数f的起始位置赋给寄存器eip,即下一条指令为f的第一条指令;
pushl %ebp
栈中位置4存储ebp的值(位置1),esp指向位置4;
movl %esp, %ebp
esp和ebp指向位置4;
subl $4, %esp
esp指向位置5;
movl 8(%ebp), %eax
将ebp之前两个位置处的值给寄存器eax,即将位置2处存储的值12给eax;
movl %eax, (%esp)
将eax中的值存储到esp指向的位置,即栈中位置5存储12;
call g
首先将eip压入栈中,即将行号15存入栈中位置6,此时esp指向位置6,ebp指向位置4;然后将函数g的起始位置赋给寄存器eip,即下一条指令为g的第一条指令;
pushl %ebp
栈中位置7存储ebp的值(位置4),esp指向位置7;
movl %esp, %ebp
esp和ebp都指向位置7;
movl 8(%ebp), %eax
将ebp之前两个位置处的值给寄存器eax,即将位置5处存储的值12给eax;
addl $6, %eax
eax中的值加上6,变为18;
popl %ebp
将栈顶数据弹出到ebp中,即将栈中位置7的值“位置4”赋给ebp,同时esp指向位置6;
ret
将栈顶数据弹出到eip中,即将栈中位置6的值“行15”赋给eip,同时esp指向位置5,下一条指令变为第15行处的指令;
leave
这条指令首先将ebp的值赋给esp,即esp和ebp都指向位置4,然后将栈顶元素弹出到ebp中,即将栈中位置4的值“位置1”赋给ebp,同时esp指向位置3;
ret
将栈顶数据弹出到eip中,即将栈中位置3的值“行23”赋给eip,同时esp指向位置2,下一条指令变为第23行处的指令;
addl $1, %eax
将寄存器eax中的值加1,变为19;
leave
先将ebp的值赋给esp,esp和ebp都指向位置1,然后将栈顶元素弹出到ebp中,即栈中位置4的值“位置0”赋给ebp,同时esp指向位置0;
ret
将栈顶数据弹出到eip中,即将栈中位置0的值赋给eip,同时esp指向位置-1,下一条指令变为程序开始前的指令。
遇到问题:在小实验删除“.”打头字符串过程误使用“g^.s*/d”指令
解决方法:使用了“g/./d’”后成功解决
心得体会:最近还没有找到合适的学习节奏,每次作业都完成的匆匆忙忙,但是相比上一周的毫无计划性已经有所进步,争取在两周内能完全适应