20135205信息安全系统设计基础第五周学习总结
第三章:程序的机器级表示
第一节:历史观点
Intel处理器系列俗称x86,经历了一个长时期的、不断进化的发展过程。
8086是第一代单芯片、十六位微处理器之一。增加了一个8位外部总线最初的机器型号有32768字节的存储器和两个软驱。
到目前的Core i7:既支持超线程,又是多核,最初的版本支持每个核上执行两个程序,每个芯片上最多四个核。
每个后继处理器的设计都是后向兼容的——较早版本上的代码可以在较新的处理器上运行。
平坦寻址方式:使程序员将整个存储空间看做一个大的字节数组。
第二节:程序编码
1、指令集体系结构(ISA):机器级程序的指令的格式和行为,它定义了处理器状态、指令的格式,以及每条指令对状态的影响。
2、机器级程序使用的存储器地址是虚拟地址:提供的存储器模型看上去是一个非常大的字节数组。
3、IA32机器代码和原始的C代码差别非常大。一些通常对C语言程序员隐藏的处理器状态是可见的:
程序计数器(CS:IP)(在IA32中通常称为PC,用%eip表示)指示将要执行的下一条指令在存储器中的地址。
理解:当执行一条指令时,首先需要根据PC中存放的指令地址,将指令由内存取到指令寄存器中,此过程称为“取指令”。与此同时,PC中的地址或自动加1或由转移指针给出下一条指令的地址。
此后经过分析指令,执行指令。完成第一条指令的执行,而后根据PC取出第二条指令的地址,如此循环,执行每一条指令。(出自维基百科)
整数寄存器(AX,BX,CX,DX):文件包含8个命名的位置,分别存储32个值。存储地址(对于C语言的指针)或整数数据。
条件寄存器(OF,SF,ZF,AF,PF,CF):保存着最近执行的算法或逻辑指令的状态信息。用来实现控制或数据流中的变化,比如用来实现if或while。
一组浮点寄存器存放浮点数据。
4、反汇编
1)代码示例:code.c
Int accum = 0;
Int sum (int x, int y)
{
Int t = x + y ;
accum += t ;
return ;
}
命令行:
gcc –S code.c 就能得到汇编代码
这会使GCC产生一个汇编文件code.s
汇编代码文件包含各种声明:
sum :
pushl %ebp
movl %esp, %ebp
movl 12(%ebp), %eax
addl 8(%ebp), %eax
addl %eax, accum
popl %ebp
ret
命令行: gcc –c code.c
GCC编译并汇编该代码
产生code.o,二进制格式无法直接查看。
2)反汇编器
命令行:
Objdump –d code.o
可以查看目标代码文件的内容
有些输出内容过多,我们可以使用 more或less命令结合管道查看,也可以使用输出重定向来查看
od code.o | more
od code.o > code.txt
3)机器代码和它的反汇编表示的特性值注意:
IA32指令长度从1到15个字节不等。
设计指令格式的方式是,从某个给定位置开始,可以将字节唯一地解码成机器指令。
反汇编只是基于机器代码文件中的字节序列来确定汇编代码。它不需要访问程序的源代码或汇编代码。反汇编器使用的指令命名规则与GCC生产的汇编代码使用的有些细微的差别。
4)注释格式
以“.”开头的行都是指导汇编器和链接器的命令。可以忽略。
了解Linux和Windows的汇编格式有点区别:ATT格式和Intel格式:
后者代码省略了指示大小的后缀。指令mov不是movl
后者代码省略了寄存器名字前面的%。如esp而不是%esp
后者代码用不同的方式描述存储器中的位置。
在带有多个操作数的指令情况下列出指令数的顺序相反。
第三节:数据格式
C声明 |
Intel数据类型 |
汇编代码后缀 |
大小(字节) |
Char |
字节 |
B |
1 |
Short |
字 |
W |
2 |
Int |
双字 |
1 |
4 |
Long int |
双字 |
l |
4 |
Long long int |
- |
- |
4 |
Char * |
双字 |
l |
4 |
Float |
单精度 |
S |
4 |
Double |
双精度 |
l |
8 |
Long double |
扩展精度 |
t |
10/12 |
大多数GCC生产的汇编代码指令都有一个后缀,表明操作数的大小。
例如:
Movb(传送字节)、movw(传送字)和movl(传送双字节)。
第四节:访问信息
1、esi edi可以用来操纵数组,esp ebp用来操纵栈帧。
通用寄存器中的eax,ebx,ecx,edx,大家要理解32位的eax,16位的ax,8位的ah,al都是独立的。有可能发生溢出,最高位丢失。
2、有效地址的计算方式 Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s
大多数指令有一个或多个操作数指令有一个或多个操作数,指示出执行一个操作中药引用的源数据值,以及放置结果的目标位置。各种不同的操作数的可能性被分为三种类型
立即数:也是常数值,书写方式是“$”后面跟一个用标准C表示法表示的整数。任何能放进一个32位的字里的数值都可以用作立即数。
寄存器:表示某个寄存器的内容,对双字操作来说,可以是8个32位寄存器中的一个(例如%eax),对字操作来说,可以是8个16位寄存器中的一个(例如:%ax),或者对字节操作来说可以使8个单字节寄存器元素中的一个(例如:%al)。
存储器引用:根据计算的地址(有效地址)访问某个存储器位置。M[Addr]表示对存储器中从地址Addr开始的b个字节值得引用。注释:M有下角标b,为了简便可省去。
3、数据传送指令
指令 |
效果 |
描述 |
MOV S、D |
D<-S |
传送 |
MOVS S、D |
D<-符号扩展(S) |
传送符号扩展的字节 |
MOVZ S、D |
D<-零扩展(S) |
传送零扩展的字节 |
MOVS和MOVZ指令类都是将一个较小的源数据复制到一个较大的数据位置,高位用符号位扩展(用原值的最高位数值进行填充)或零扩展(高位用0填充)进行填充。
Push:把数据压入栈中
Pop:删除数据,弹出数据。
栈顶元素的地址是所有栈中元素地址中最低的。
第五节:算法和逻辑操作
目的操作数必须是一个寄存器。
指令 效果 描述
Lead S,D D<-&S 加载有效地址
SUB S,D D<-D-S 减
第六节:控制
1) 条件码
条件码:CPU维护着一组单个位的条件码寄存器
2)访问条件码
根据条件码的某个组合将一个字节设置为0或者1;
可以条件跳转到程序的某个其他的部分;
可以有条件地传送数据;
3)跳转指令机器及其编码
Jmp.L1
.L1:
会导致程序跳过两个命令间的指令
4)有条件跳转的条件看状态寄存器(条件码寄存器)。
if else汇编结构:汇编器为then-statement和else-statement产生各自的代码块。它会插入条件和无条件分支,以保证能执行正确的代码块。
do-while:
loop
body-statement
while(test-expr);
这个循环的效果就是重复执行body-statement,对est-expr求值,如果求值结果非0则继续循环。
Loop
body-statement
if(t)
goto loop
汇编结构:
.L2:
jg .L2:
while、for:汇编结构都是将循环转化成goto结构,在转化成汇编结构,与do-while相似。
5)switch语句
通过跳转访问代码位置。
第七节:过程
1)IA32通过栈来实现过程调用,一个1调用包括将数据(以过程参数和返回值的形式)和控制从代码的一部分传递到另一部分。
为单个过程分配的那部分栈成为栈帧。寄存器%ebp为帧指针,寄存器%esp为栈指针。
程序运行时栈指针可以移动,因此大多数信息的访问都是相对于帧指针的。
2)call指令:有一个目标,即指明被调用过程起始的指令地址。
效果为将返回地址入栈。
Call/ret函数说明:call指令将控制转移到一个函数的起始,而ret指令返回到call指令后的那个指令。函数返回值存在%eax中