信息安全系统设计基础第五周学习总结
第三章 程序的机器级表示
3.1 历史观点
X86 寻址方式经历三代:
1 DOS时代的平坦模式,不区分用户空间和内核空间,很不安全
2 8086的分段模式
3 IA32的带保护模式的平坦模式
3.2 程序代码
3.2.1 机器级代码
第一种抽象:程序的格式和行为
ISA,指令集体系结构,定义了处理器状态,指令的格式,以及每条指令对状态的影响。
第二种抽象:虚拟地址
程序计数器:(IA32中称为PC,用%eip表示)
指示将要执行的下一条指令在存储器中的地址
整数寄存器文件包含8个命名的位置,分别存储32位的值。
条件码寄存器保存着最近执行的算术或逻辑指令的状态信息
一组浮点寄存器存放浮点数据
3.2.2 代码示例
unix> gcc -01 -S code.c 获得C语言编译器产生的汇编代码,产生一个汇编文件code.s。
gcc -S xxx.c -o xxx.s 可以获得汇编代码。
unix> objdump -d code.o 将二进制格式的目标代码文件反汇编成汇编语言的形式。
汇编函数固有格式:
function:
push %ebp
mov %esp,%ebp
.
.
.
pop %ebp
ret
64位机器得到32位代码:gcc -m32 -S xxx.c
ubuntu 中 gcc -S code.c 也可产生代码,更接近教材(删除“.”开头的语句)
在文件code.o上运行gdb
(gdb) x/17xb xxx
告诉gdb检查17个十六进制格式的字节
用od命令查看二进制文件:
od code.o | more 使用more或less结合管道查看
od code.o > code.txt 使用输出重定向查看
3.3 数据格式
C语言声明 Intel数据类型 汇编后缀 大小(字节) char 字节 b 1 short 字 w 2 int 双字 l 4 long int 双字 l 4 long long int —— — 4 char* 双字 l 4 float 单精度 s 4 double 双精度 l 8 long double 扩展精度 t 10/12
字节:8位 movb
字(word):16位 movw
双字(double words):32位 movl
四字(quad words)
3.4 访问信息
esi edi可以用来操纵数组,esp ebp用来操纵栈帧。
3.4.1 操作数指示符
1.立即数(常数)
$-577 $0x1F
2.寄存器
Ea:任意寄存器a;
R[Ea]:寄存器a的值
3.存储器
Mb[Addr]:对存储在存储器中从有效地址Addr开始的b个字节值的引用。
有效地址的计算方法:
Imm(Eb,Ei,s)= Imm+R[Eb]+R[Ei]*s
Imm立即数偏移;
Eb基址寄存器;
Ei变址寄存器;
s比例因子(s=1,2,4or8)
3.4.2 数据传送指令
指令 效果
MOV S,D D<-S
MOVS S,D D<-符号扩展(S)
MOVZ S,D D<-零扩展(S)
pushl S 将双字压栈
popl D 将双字出栈
注意:不能从内存地址直接MOV到另一个内存地址,要用寄存器中转一下。
栈“底”地址较大,栈“顶”地址较小
指针就是地址;
局部变量保存在寄存器中。
3.5 算术和逻辑操作
每个指令类都有对字节,字,双字的操作。这些操作被分为四组:
加载有效地址、一元操作、二元操作和移位
3.5.1 加载有效地址
leal S,D D<-&S
例:若寄存器%edx的值为x,则
leal 7(%edx,%edx,4),%eax 的结果为:
将寄存器%eax的值设置为5x+7
3.5.2 一元操作和二元操作
二元操作减法:
SUB S,D D <- D-S
3.5.3 移位
移位量用单字节编码,只允许0-31位的移位。
移位量可以是一个立即数,或者放在单字节寄存器元素%cl中。
3.6 控制
3.6.1 条件码
当我们用add执行一条令t=a+b的指令,其中t,a,b都是整型。
CF 进位:(unsigned) t < (unsigned) a 无符号溢出
ZF 零 :(t==0) 零
SF 负数:(t<0) 负数
OF 溢出:(a<0==b<0)&&(t<0!=a<0) 有符号溢出
注意:leal不改变条件码寄存器
3.6.2 访问条件码
条件码常见使用方法:
1.根据条件码的某个组合,将一个字节设置为0或者1;
2.条件跳转到程序的某个其他部分
3.有条件地传送数据
3.6.5 循环
1.do-while循环
movl 8(%ebp), %edx ;Get n movl $1, %eax ;Set result = 1 .L2: ;loop: imull %edx, %eax ;Compute result *= n subl $1, %edx ;Decrement n cmpl $1, %edx ;Compare n:1 jg .L2 ;If >, goto loop
3.7 过程
一个过程调用包括将数据(以过程参数和返回值的形式)和控制从代码的一部分传递到另一部分。
同时,他必须在进入时为过程的局部变量分配空间,并在退出时释放。
3.7.1 栈帧结构
机器用栈来传递过程参数、存储返回信息、保存寄存器用于以后恢复,以及本地存储。
为单个过程分配的那部分栈称为栈帧,大多数信息的访问都是相对于帧指针的。
3.7.2 转移控制
call:将返回地址入栈,并跳转到被调用过程的起始处。
返回地址:紧跟在call后面的那条指令的地址。
ret:从栈中弹出返回地址,并跳转之。
3.7.3 寄存器使用惯例
寄存器%eax、%edx和%ecx被划分为调用者保存寄存器。
寄存器%ebx、%esi、%edi被划分为被调用者保存寄存器。
3.7.4 过程示例
旁注:GCC坚持一个函数使用的所有栈空间必须是16字节的整数倍,包括保存%ebp值的4个字节和返回值的4个字节。
3.7.5 递归过程
递归调用自身和调用其它函数是一样的。栈规则提供了一种机制,每次函数调用都有它自己私有的状态信息存储(保存的返回位置,栈指针和被调用者保存寄存器的值)