2017-2018-1 20145237 《信息安全系统设计基础》第十三周学习总结

2017-2018-1 20145237 《信息安全系统设计基础》第十三周学习总结

教材学习内容总结

第三章 程序的机器级表示

3.1历史观点

X86 寻址方式经历三代:

(1)DOS时代的平坦模式,不区分用户空间和内核空间,很不安全
(2) 8086的分段模式
(3) IA32的带保护模式的平坦模式

3.2程序编码

gcc -S xxx.c -o xxx.s 获得汇编代码,也可以用objdump -d xxx 反汇编

机器编程的两种抽象
(1)指令集体系结构(Instruction set architecture,ISA)——定义指令格式以及每条指令执行之后对状态的影响。大多数ISA将程序行为描述成按顺序执行的;
(2)虚拟地址
状态可见的几种处理器

(1)程序计数器 (PC,用%eip表示)
(2)整数寄存器 (包含8个命名的位置,存储32位的值)
(3)条件码寄存器 (实现if和while语句)
(4)浮点寄存器 (存放浮点数)

代码示例
(1)gcc -S xxx.c -o xxx.s 获得汇编代码
eg:unix> gcc -01 -S code.c
(2)objdump -d xxx 反汇编;
eg:unix> objdump -d code.o
(3)64位机器上想要得到32代码:gcc -m32 -S xxx.c
关于格式的注解:以“.”开头的行都是指导汇编器和链接器的命令,我们通常可以忽略这些行。gcc -S 产生的汇编中可以把 以”.“开始的语句都删除了再阅读。

3.3数据格式

8 位:字节16位:字32位:双字64位:四字

数据传送指令的三个变种:

•movb 传送字节
•movw 传送字
•movl 传送双字

3.4访问信息

操作数的三种类型

(1)立即数
(2)寄存器
(3)存储器

寻址方式

•立即数寻址方式
格式:$后加用标准c表示法表示的整数,如$0xAFF
•寄存器寻址方式
•存储器寻址方式
•直接寻址方式
•寄存器间接寻址方式
•寄存器相对寻址方式
•基址变址寻址方式
•相对基址变址寻址方式

数据传送指令

mov指令
把一个字节(字)操作数从源SRC传送至目的地DST

IA32的限制:两个操作数都不能指向存储器。

此外:
•movb 传送字节
•movw 传送字
•movl 传送双字
•movs 符号位扩展
•movz 零扩展

压栈push

指令格式——PUSH r16/m16/seg

如果压入的是双字,栈顶指针-4

出栈pop

指令格式——POP r16/m16/seg

3.5算术和逻辑操作

加载有效地址
加载有效地址指令——leal
指令形式:从存储器读取数据到寄存器。即将有效地址写入到目的操作数,而目的操作数必须是寄存器;并不真实引用存储器。

一元操作和二元操作
一元操作
只有一个操作数,既是源又是目的,可以是一个寄存器,或者存储器位置。
二元操作
源操作数 目的操作数
第一个操作数可以是立即数、寄存器或者存储器位置第二个操作数可以是寄存器或者存储器位置但是不能同时是存储器位置。
移位操作
SAL 算术左移SAR 算术右移
SHL 逻辑左移SHR 逻辑右移
有符号数进行算数移位,无符号数逻辑移位
特殊操作
imull,有符号数乘法
双操作数,从两个32位操作数产生一个32位的乘积。
mull,无符号数乘法
idivl有符号除法
divl无符号除法

3.6控制

条件码
(1)条件码寄存器:他们描述了最近的算术或逻辑操作的属性。
(2)常用的条件码:CF:进位标志;ZF:零标志;SF:符号标志;OF:溢出标志。
(3)LEAL指令:加载有效地址,将数据从存储器读到寄存器。

NEG,取负
SUB S,D,将D-S的结果送至D
移位操作 SAL,SHL,SAR,SHR的移位量可以是立即数或%cl中的数
(4)其他常见指令:

XOR:进位标志和溢出标志会设置成0
INC:设置溢出和零标志
DEC:设置溢出和零标志
CMP:根据操作数之差设置条件码
SUB:设置条件码,更新寄存器
TEST:改变目的寄存器的值

访问条件码

常用使用方法:

(1)根据条件码的某个组合,将一个字节设置成0或1
(2)可以条件跳转到某个其他部分
(3)可以有条件的传送数据
跳转指令及其编码
(1)JUMP指令,导致执行切换到程序中一个全新的位置。
(2)无条件跳转——jmp.<标号> 跳转到标号所指示的语句处;jmp *<操作数指示符> 【注意:如果形如%eax,即以%eax中的值作为跳转目标;而形如(%eax)则是以其中的值作为地址,读出跳转目标】
(3)有条件跳转——类似于SET类指令,是根据条件码或者其组合来跳转。(SET类指令根据t=a-b的结果所设置的条件码来将一个字节(目的操作数)设置为0或者1)
循环
(1)do-while循环
do-while语句的通用形式:

do
body-statement
while(test-expr);

等价的goto语句:

loop:
  body-statement
  t = test-sxpr;
  if(t)
  goto loop;

(2)while循环
while语句的通用形式:

while (test-expr)
body-statement

等价的goto语句:

t = test-sxpr;
if(!t)
goto done;  
loop:
  body-statement
  t = test-sxpr;
  if(t)
  goto loop;
done:

(3)for循环
for循环的通用形式:

for(init-expr;test-expr;update-expr)
body-statement

等价的goto语句:

init-expr
t=test-expr;
if(!t)
goto done;
loop:
body-statement
update-expr;
t=test-expr;
if(t)
    goto loop;
done:

(4)switch语句:根据一个整数索引值进行多重分支;通过使用跳转表使其更加高效。跳转表是一个数组,表项i是一个代码段地址(C语言用&表示一个指向数据值的指针;而&&表示一个指向代码位置的指针)

3.7过程

栈帧结构
栈用来传递参数、存储返回信息、保存寄存器,以及本地存储。
本质上栈帧还是栈
栈帧的两个指针:

寄存器%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,表示向下移动一层。

实验楼中练习的实现

使用 gcc –S –o main.s main.c -m32编译

c语言代码如下:

编译后

删除gcc产生代码中以"."开头的编译器指令

实验楼代码的反汇编分析

g:
     pushl   %ebp            //保存现场,将父函数的栈底寄存器存入当前程序栈中
     movl    %esp, %ebp      //构建当前函数堆栈
     movl    8(%ebp), %eax   //从父函数堆栈中取得参数,存入ax寄存器
     addl    $3, %eax        //完成+3操作
     popl    %ebp            //恢复原父函数堆栈
     ret                     //g函数所占用的栈帧“消失”
f:
     pushl   %ebp            //保存现场,将父函数的栈底寄存器存入当前程序栈中
     movl    %esp, %ebp      //构建当前函数堆栈
     pushl   8(%ebp)         //压栈内存空间
     call    g               //调用g
     addl    $4,%esp         //数据压入堆栈,栈顶指针esp减少4字节
     leave                   //清理局部变量空间
     ret                     //返回,f函数所占用的栈帧“消失”
main:
     pushl   %ebp            //保存现场,将父函数的栈底寄存器存入当前程序栈中
     movl    %esp, %ebp      //构建当前函数堆栈
     pushl   $8              //压栈
     call    f               //调用f
     addl    $4,%esp         //数据压入堆栈,栈顶指针esp减少4字节
     addl    $1, %eax        //完成+1操作`
     leave                   //清理局部变量空间
     ret                     //返回,main函数所占用的栈帧“消失”

对每条指令画出相应栈帧的情况

教材中的习题
3.22

3.23

3.29

3.34

教材学习中的问题和解决过程

为什么汇编中有两个分支呢?

代码调试中的问题和解决过程

代码托管

(statistics.sh脚本的运行结果截图)

本周结对学习情况

- [20155208](http://www.cnblogs.com/xuzihan/p/8052601.html)

解决同伴在学习中遇到的问题:

问题:怎么样让负数等于正数?

解决过程: 在负数后加上U

其他(感悟、思考等,可选)

原文地址:https://www.cnblogs.com/20145237fhn/p/8052491.html