信息安全系统设计基础第4周学习总结
第三章 程序的机器级表示
第一节 历史观点
X86 寻址方式经历三代:
- 1 DOS时代的平坦模式(不区分用户空间和内核空间,很不安全)
- 2 8086的分段模式
- 3 IA32的带保护模式的平坦模式
第二节 程序编码
gcc -S xxx.c -o xxx.s 获得汇编代码,也可以用objdump -d xxx 反汇编
64位机器上想要得到32代码:gcc -m32 -S xxx.c
机器级编程的两种抽象
(1)指令集结构ISA
是机器级程序的格式和行为,定义了处理器状态、指令的格式,以及每条指令对状态的影响。
(2)机器级程序使用的存储器地址是虚拟地址
处理器:
- 程序计数器(CS:IP)
- 整数寄存器(AX,BX,CX,DX)
- 条件码寄存器(OF,SF,ZF,AF,PF,CF)
- 浮点寄存器
二、代码示例
二进制文件可以用od 命令查看,也可以用gdb的x命令查看。 有些输出内容过多,我们可以使用 more或less命令结合管道查看,也可以使用输出重定向来查看。
在读取地址时要注意是不是小端法,小端法的正确读法是与自然方向相反。
第三节 数据格式
8 位:字节16位:字32位:双字64位:四字
数据传送指令的三个变种:
- movb 传送字节
- movw 传送字
- movl 传送双字
第四节 访问信息
操作数的三种类型
立即数
寄存器
存储器
寻址方式
(1)立即数寻址方式
格式:$后加用标准c表示法表示的整数,如$0xAFF
(2)寄存器寻址方式
(3)存储器寻址方式
- 直接寻址方式
- 寄存器间接寻址方式
- 寄存器相对寻址方式
- 基址变址寻址方式
- 相对基址变址寻址方式
数据传送指令
mov指令
把一个字节(字)操作数从源SRC传送至目的地DST
IA32的限制:两个操作数都不能指向存储器。
此外:
- movb 传送字节
- movw 传送字
- movl 传送双字
- movs 符号位扩展
- movz 零扩展
push与pop(后进先出)
压栈push
指令格式——PUSH r16/m16/seg
如果压入的是双字,栈顶指针-4
出栈pop
指令格式——POP r16/m16/seg
第五节 算术和逻辑操作
加载有效地址
加载有效地址指令——leal
指令形式:从存储器读取数据到寄存器。即将有效地址写入到目的操作数,而目的操作数必须是寄存器;并不真实引用存储器。
一元操作和二元操作
一元操作
只有一个操作数,既是源又是目的,可以是一个寄存器,或者存储器位置。
二元操作
源操作数 目的操作数
第一个操作数可以是立即数、寄存器或者存储器位置第二个操作数可以是寄存器或者存储器位置但是不能同时是存储器位置。
移位操作
SAL 算术左移SAR 算术右移
SHL 逻辑左移SHR 逻辑右移
有符号数进行算数移位,无符号数逻辑移位
特殊操作
imull,有符号数乘法
双操作数,从两个32位操作数产生一个32位的乘积。
mull,无符号数乘法
idivl有符号除法
divl无符号除法
第六节 控制
条件码
CF:进位标志ZF:零标志SF:符号标志OF:溢出标志
书上图3-7中的指令除改变寄存器的值外,也会设置条件码;而TEXT指令与CMP指令都只设置条件码
访问条件码
SET指令,通过set与不同的条件码的组合,达到不同的跳转条件。SET指令根据t=a-b的结果设置条件码
跳转指令JUMP 及其编码
jump分为直接跳转和间接跳转:
直接跳转:后面跟标号作为跳转目标间接跳转:*后面跟一个操作数指示符
循环
do-while循环
先执行循环体语句,再执行判断。
while循环
GCC的方法是,使用条件分支,表示省略循环体的第一次执行,归根究底,还是要把循环改成do-while的样子,然后用goto翻译。
for循环
for循环可以轻易的改成while循环,所以再依照上面的方法改成do-while再翻译。
第七节 过程
栈帧结构
栈用来传递参数、存储返回信息、保存寄存器,以及本地存储。
本质上栈帧还是栈
栈帧的两个指针:
寄存器%ebp-帧指针
寄存器%esp-栈指针
关于被调用者Q用栈的几个用处:
1.保存不能存放在寄存器中的局部变量。
2.存放它调用的其他过程的参数。
转移控制
call
call指令和转移指令相似,同样分直接和间接,直接调用的目标是标号,间接调用的目标是*后面跟一个操作数指示符,和JMP一样。
CALL指令的效果是将返回地址入栈,并跳转到被调用过程的起始处。返回地址是还在程序中紧跟在call后面的那条指令的地址。
ret
ret指从栈中弹出地址,并跳转到这个位置。
leave
这个指令可以使栈做好返回的准备,等价于:
movl %ebp,%esppopl %ebp
寄存器使用惯例
当要保存一个值以待以后运算可用的时候,有两种选择:
1.由调用者保存。在调用之前就压进栈。
2.由被调用者保存,在刚被调用的时候就压进栈,并在返回之前恢复。
查看函数调用栈信息的GDB命令
Ÿ backtrace/bt n
n是一个正整数,表示只打印栈顶上n层的栈信息。
-n表一个负整数,表示只打印栈底下n层的栈信息。
Ÿ frame n
n是一个从0开始的整数,是栈中的层编号。这个指令的意思是移动到n指定的栈帧中去,并打印选中的栈的信息。如果没有n,则打印当前帧的信息。
Ÿ up n
表示向栈的上面移动n层,可以不打n,表示向上移动一层。
Ÿ down n
表示向栈的下面移动n层,可以不打n,表示向下移动一层。
参考资料:20135202闫佳欣的《第三章 程序的机器级表示》总结
遇到的问题:
- P113页题3.1
在最开始时,我完全看不懂题目,后来我查看了参考答案,对这道题有了深刻的理解,在图3-3中完全标出了寻址方式。对于寻址方式可以方便记忆,对比图3-3与题3.1其中E表示寄存器、R表示地址栏以及寄存器的值、M[]对应地址栏对应的值。
- P116页题3.3
在此题中,我还是不能很好的区分8位、16位、32位寄存器的表示符号,以及movb\movw\movl的对应字节,这在后来阅读GCC代码有很大的帮助,后来我重复记忆,多看汇编代码,就可以很好的理解了。
- P149页图3-21与P115页图3-5
在最开始看图3-5时,我完全不能理解popl的操作以及%esp 在此操作中的行为。后来,在看图3-21时,我已充分了解了指针%ebp的作用,然后再回来看这个图就很好理解了。
学完此章节,我觉得3.1-3.6对我理解3.7有很大的帮助,也是重新复习了一下汇编,其中的一些指令有些细微差距,最后的栈帧结构更加深了我对栈操作的理解。