程序的机器级表示内容补充及扩展
第五周练习补充
-
代码
-
删除gcc产生代码中以"."开头的编译器指令
-
每条指令相应栈帧的情况
EIP & EBP & ESP
eax, ebx, ecx, edx, esi, edi, ebp, esp等都是X86 汇编语言中CPU上的通用寄存器的名称,是32位的寄存器。如果用C语言来解释,可以把这些寄存器当作变量看待。
比方说:add eax,-2
//可以认为是给变量eax加上-2这样的一个值。
这些32位寄存器有多种用途,但每一个都有“专长”,有各自的特别之处。
-
EAX 是"累加器", 它是很多加法乘法指令的缺省寄存器。
-
EBX 是"基地址"寄存器, 在内存寻址时存放基地址。
-
ECX 是计数器, 是重复前缀指令和LOOP指令的内定计数器。(在汇编指令中,loop通常实现循环功能)
-
EDX 是被用来放整数除法产生的余数。
-
ESI/EDI分别叫做"源/目标索引寄存器",因为在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串.
-
EBP是"基址指针", 它最经常被用作高级语言函数调用的"框架指针". 在破解的时候,经常可以看见一个标准的函数起始代码:
push ebp ; 保存当前ebp mov ebp,esp ; EBP设为当前堆栈指针 sub esp, xxx ; 预留xxx字节给函数临时变量. ...
这样一来,EBP 构成了该函数的一个框架, 在EBP上方分别是原来的EBP, 返回地址和参数;EBP下方则是临时变量。函数返回时作mov %ebp , %esp
、pop %ebp
(相当于leave
)、ret
即可。
- ESP 专门用作堆栈指针,被形象地称为栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,ESP也就越来越小。(在32位平台上,ESP每次减少4字节)
- esp:寄存器存放当前线程的栈顶指针
- ebp:寄存器存放当前线程的栈底指针
- eip:寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从EIP寄存器中读取下一条指令的内存地址,然后继续执行。
EIP,EBP,ESP都是系统的寄存器,里面存的都是些地址。
栈的数据结构,主要有以下特点:后进先出。
其实它还有以下两个作用:
- 栈是用来存储临时变量,函数传递的中间结果。
- 操作系统维护的,对于程序员是透明的。
栈的原理
- 写个小程序:
当程序进行函数调用的时候,我们经常说的是先将函数压栈,当函数调用结束后,再出栈。这一切的工作都是系统帮我们自动完成的。但在完成的过程中,系统会用到下面三种寄存器:
1.EIP
2.EBP
3.ESP
- 当调用test函数开始时,三者的作用:
1.EIP寄存器里存储的是CPU下次要执行的指令的地址。
也就是调用完test函数后,让CPU知道应该执行main函数中的printf("End of function call")
语句了。
2.EBP寄存器里存储的是栈的栈底指针,通常叫栈基址,这个是一开始进行test()函数调用之前,由ESP传递给EBP的。(在函数调用前可以这么理解:ESP存储的是栈顶地址,也是栈底地址。)
3.ESP寄存器里存储的是在调用函数test()之后,栈的栈顶。并且始终指向栈顶。
- 当调用test函数结束后,三者的作用:
1.系统根据EIP寄存器里存储的地址,CPU就能够知道函数调用完,下一步应该做什么,也就是应该执行main函数中的printf("End of function call")
。
2.EBP寄存器存储的是栈底地址,而这个地址是由ESP在函数调用前传递给EBP的。等到调用结束,EBP会把其地址再次传回给ESP。所以ESP又一次指向了函数调用结束后,栈顶的地址。
代码调试中的问题
在把test.c编译成汇编代码时,我本来想用实验楼练习中的方法gcc –S –o test.s test.c -m32
编译成32位的,可是遇到了比上次还不理解的问题。在上次练习时,代码开头有#include <stdio.h>
时编译就会出错,而没有时就可以编译。
但是这次代码开头有#include <stdio.h>
时编译也会出错,而没有时还是不行,这个地方不知道怎么解决。
所以我只好直接用gcc -S test.c
编译成汇编代码。
当我习惯性的删除所有以"."开头的语句时,发现代码中出现了".LC0"和".LC1",我想起来这两个东西是我刚才删掉的以"."开头的语句中的东西,应该是调用了这两部分的函数,但是这两部分函数具体的结构表示不知道怎么看。
本周代码托管截图
其他(感悟、思考等,可选)
这篇博客没有写本周的内容,只是对上周的内容的补充和扩展,以及更深入的理解。本周的内容会写在周天的博客中。
其他内容
因为在创建test.c 及test.s文件时放错了文件夹目录,所以复习了文件移动的指令。
mv命令
-
格式
mv [options] 源文件或目录 目标文件或目录 -
[options]主要参数
-i:交互方式操作。如果mv操作将导致对已存在的目标文件的覆盖,此时系统询问是否重写,要求用户回答”y”或”n”,这样可以避免误覆盖文件。
-f:禁止交互操作。mv操作要覆盖某个已有的目标文件时不给任何指示,指定此参数后i参数将不再起作用。
- 第二个参数
当第二个参数类型是文件时,mv命令完成文件重命名,它将所给的源文件或目录重命名为给定的目标文件名。
当第二个参数是已存在的目录名称时,源文件或目录参数可以有多个,mv命令将各参数指定的源文件均移至目标目录中。在跨文件系统移动文件时,mv先拷贝,再将原有文件删除,而链至该文件的链接也将丢失。
- 应用实例
(1)将/home/20145333中的所有文件移到当前目录中:
$ mv /home/20145333/* .
(2)将文件test.c重命名为test01.c:
$ mv test.c test01.c
(3)把当前目录的一个子目录里的文件移动到另一个子目录里
$ mv 子目录名/* 另一个目录
(4)移动当前文件夹下的所有文件到上一级目录
$ mv * ../
("*"可以换成任意文件)
安装了"tree"