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

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

教材学习内容总结

目录编程

  • 程序编码

  • 栈帧结构

  • 过程的实现

  • 过程调用和返回指令

  • 寄存器使用惯例

  • 过程实例

  • 递归过程

我我的认为虽然实践很重要,可是书本上的一些重点也是须要记录的,这样在之后的复习中就能够知道在当时学习的时候重点是什么,因此我仍是要对第三章的重点作记录。
个人实践内容在总结的后面。数组

历史观点

  • Intel处理器第一代是单芯片,16位微处理器
  • 第一代是x8086,也就是上学期学的汇编语言
  • 每一个后继处理器的设计都是后向兼容的,能够保证较早版本上编译的代码在较新的处理器上运行。安全

    程序编码

    GCC将源代码转化为可执行代码的步骤:函数

  • C预处理器——扩展源代码-生成.i文件
  • 编译器——产生两个源代码的汇编代码-——生成.s文件
  • 汇编器——将汇编代码转化成二进制目标代码——生成.o文件
  • 连接器——产生可执行代码文件布局

命令gcc---GCC C、C++编译器。是Linux上默认的编译器。
gcc命令调用一系列程序,将源代码转化成可执行代码:学习

  • C预处理器扩展源代码*.c,插入全部用#include命令指定的文件,并扩展全部用#define声明指定的宏。
  • 编译器产生两个源代码的汇编代码,名字为*.s
  • 汇编器将汇编代码转化成二进制目标代码(机器代码的一种形式)文件名为*.o
  • 衔接器将两个目标代码文件与实现库函数的代码合并,并产生最终的可执行代码文件

2.一、机器级代码测试

1.机器级编程的两种抽象
(1)指令集结构ISA
是机器级程序的格式和行为,定义了处理器状态、指令的格式,以及每条指令对状态的影响。
(2)机器级程序使用的存储器地址是虚拟地址
看上去是一个很是大的字节数组,其实是将多个硬件存储器和操做系统软件组合起来。编码

2.汇编代码的特色:
用可读性更好的文本格式来表示。操作系统

3.几个处理器:命令行

  • 程序计数器(CS:IP)
  • 整数寄存器(AX,BX,CX,DX)
  • 条件码寄存器(OF,SF,ZF,AF,PF,CF)
  • 浮点寄存器

一条机器指令只执行一个很是基本的操做。

程序编码

书上107页的代码,须要用到反汇编器。在Linux系统中,带‘d’命令行标志的程序OBJDUMP能够充当这个角色。

objdump -d xxx.xx

数据格式

1.Intel中:

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

2.c语言基本数据类型对应的IA32表示

char    字节  1字节
short   字   2字节
int     双字  4字节
long int 双字 4字节
long long int (不支持) 4字节
char *  双字 4字节
float   单精度 4字节
double  双精度 8字节
long double 扩展精度 10/12字节

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

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

    访问信息

    一个IA32中央处理单元(CPU)包含8个存储32位置的寄存器

    操做数指示符

  • 当即数
  • 寄存器
  • 存储器

    寻址方式

(1)当即数寻址方式

格式:$后加用标准c表示法表示的整数,如$0xAFF

(2)寄存器寻址方式

如%eax,与汇编中学过的AX寄存器类比。

(3)存储器寻址方式

  • 直接寻址方式
  • 寄存器间接寻址方式
  • 寄存器相对寻址方式
  • 基址变址寻址方式
  • 相对基址变址寻址方式

    数据传送指令

    MOV

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

MOVS

  • movsbw 将作了符号扩展的字节传送到字
  • movsbl 将作了符号扩展的字节传送到双字
  • movswl 将作了符号扩展的字传送到双字

MOVZ

  • movzbw 将作了零扩展的字节传送到字
  • movzbl 将作了零扩展的字节传送到双字
  • movzwl 将作了零扩展的字传送到双字

算术和逻辑操做

1、加载有效地址

加载有效地址指令——leal,是movl指令的变形。

指令形式:从存储器读取数据到寄存器。

实际:将有效地址写入到目的操做数,而目的操做数必须是寄存器;并不真实引用存储器。

机器代码:二进制格式

汇编代码:用可读性更好的文本格式来表示。

一条机器指令值执行一个很是基本的操做。

2.二、代码示例

  • gcc -o1 –S code.c

    gcc将.c文件编译器产生可读汇编代码文件

  • gcc –o1 –C code.c

    gcc将.c文件编译并汇编该代码,二进制格式。

  • objdump –d code.o

    用于查看目标代码文件的内容,将目标文件反汇编为汇编代码文件.s

  • gcc –o1 –o main.c prog

    生成可执行文件

1.栈帧结构

若是要学明白栈帧结构,首先要知道栈帧是什么?从书中,我看到对栈帧的概念是:

为单个过程分配的那部分栈就叫作栈帧。栈帧的最顶端以两个指针界定——帧指针(寄存器ebp)和栈指针(esp)。

不过我认为对刚接触的咱们来讲,就是将其看作一个函数便可。既栈帧就是帧指针ebp和栈指针esp之间的内容,即函数主体,ebp指向函数头,位置肯定,esp指向函数尾,随函数内部变量的增长或减小而移动。

每个被调用的函数都有一个本身的栈帧结构,而且栈帧结构是由函数本身造成的。须要注意的是:CPU中的寄存器ebp和esp都只有一个。

个人疑问是:每个被调用的函数都有一个本身的栈帧结构,可是一段代码都有一个函数,没有界限,可是栈帧的寄存器ebp和esp都只有一个,因此如何来协调使用完成函数调用的呢?

首先学习了地址空间分配的问题,以下图:

我上网查了一些资料并进行了学习和采纳,如IA32程序用程序栈来支持过程调用。机器用栈来传递过程参数、存储返回信息、保存寄存器用于之后恢复,以及本地存储。而为单个过程分配的那部分栈称为帧栈(stack frame)。

  帧栈能够认为是程序栈的一段,它有两个端点,一个标识着起始地址,一个标识着结束地址,而这两个地址,则分别存储在固定的寄存器当中,即起始地址存在%ebp寄存器当中,结束地址存在%esp寄存器当中。也就是说寄存器 %ebp 为帧指针,寄存器 %esp 为栈指针。

  当程序执行时,栈指针能够移动,所以大多数信息的访问都是相对于帧指针的。
  
如图:

  
上图包含了程序栈的构成,它由一系列栈帧构成,这些栈帧每个都对应一个过程,并且每个帧指针+4的位置都存储着函数的返回地址,每个帧指针指向的存储器位置当中都备份着调用者的帧指针。各位须要知道的是,每个栈帧都创建在调用者的下方(也就是地址递减的方向),当被调用者执行完毕时,这一段栈帧会被释放。还有一点很重要的是,%ebp和%esp的值指示着栈帧的两端,而栈指针会在运行时移动,因此大部分时候,在访问存储器的时候会基于帧指针访问,由于在一直移动的栈指针没法根据偏移量准确的定位一个存储器位置。

对这一块内容学习的收获:

从书中的学习和查资料对这一小块的内容有了更多的了解,好比栈的空间分配和释放。栈帧是向地址递减的方向延伸,因此要想给栈分配空间,能够将栈的指针减去一个空间的值,形象的来讲,就是栈的指针上移和下移就是对栈空间的分配和释放,也就是栈变长或者变短。 

过程的实现

首先要明白什么是过程的实现?个人理解就是数据在调用者和被调用者之间传递,以及在被调用者当中局部变量内存的分配以及释放。

那么这一过程是如何实现的?

我上网学习了一些资料和博客,参数传递以及局部变量内存的分配和释放有下面的几个操做,这些操做都是咱们常常产生汇编代码所看到的东西。

1.如下汇编代码的实现就是备份原来的帧指针,调整当前的帧指针到栈指针的位置。

pushl   %ebp
movl    %esp, %ebp

2.如下代码的实现就是建立的栈帧给被调用者准备,当被调用者使用栈帧时,须要给临时变量分配预留内存

subl    $16,%esp

3.如下代码处理备份被调用者保存的寄存器当中的值果有值的话,备份的方式则压入栈顶

pushl    %ebx

4.使用创建好的栈帧,好比读取和写入,通常使用mov,push以及pop指令等等。

5.恢复被调用者寄存器当中的值,这一过程实际上是从栈帧中将备份的值再恢复到寄存器,不过此时这些值可能已经不在栈顶了。所以在恢复时,大多数会使用pop指令,但也并不是必定如此。

6.释放被调用者的栈帧,释放就意味着将栈指针加大,而具体的作法通常是直接将栈指针指向帧指针,所以会采用相似下面的汇编代码处理(也多是addl)。

movl    %ebp,%esp

7.恢复调用者的栈帧,恢复其实就是调整栈帧两端,使得当前栈帧的区域又回到了原始的位置。由于栈指针已经在第六步调整好了,所以此时只须要将备份的原帧指针弹出到%ebp便可。相似的汇编代码以下。  

popl    %ebp

8.弹出返回地址,跳出当前过程,继续执行调用者的代码。此时会将栈顶的返回地址弹出到PC,而后程序将按照弹出的返回地址继续执行。这个过程通常使用ret指令完成。

过程调用和返回指令

学习了一些过程调用和返回的指令:

指令:

  • call指令
  • leavel指令
  • ret指令

首先来学习什么是call指令:

hello.c -> 预处理 hello.i -> 编译阶段 hello.s -> 汇编阶段 hello.o -> 连接阶段 ->hello

从上面过程来看,通过编译阶段生成汇编是必须的。

首先先写一个hello.c程序

#include <stdio.h>  
int main()  
{  
    printf("hello, world\n");  
    return 0;  
}

汇编产生的代码以下;

在hello.c中只调用了一个库函数就是printf因此代码里有callq和retq看看下面几行

400520:    e8 bb fe ff ff                 callq  4003e0 <puts@plt>
400525:    b8 00 00 00 00           mov    $0x0,%eax
40052a:    c9                                  leaveq 
40052b:    c3                                   retq

最前面是内存地址(虚拟地址),接着是地址内存的机器指令,接下来是汇编代码call指令调用printf函数时,先把下一条指令 mov %0x0, %eax 压栈,而后跳转到printf函数执行printf函数。执行完了执行ret指令,ret指令是从栈中弹出刚才压栈的指令,继续执行此指令和后面的指令。栈段也就是起了一个临时保存的做用。因此ret和call是配合着使用的。

call 和 ret

call: 将当前的IP 或者 CS:IP 压入栈中跳转到指定位置

ret : 用栈中所保存的数据赋值给IP的, 跳转回来。

寄存器使用惯例

程序寄存器组是惟一可以被全部过程共享的资源。虽然在给定时刻只能有一个过程是活动的,可是咱们必须保证当一个过程(调用者)调用另外一个过程(被调用者)时,被调用者不会覆盖某个调用者稍后会使用的寄存器的值。为此必须采用一组统一的寄存器使用惯例,全部的过程都必须遵照,包括程序库的过程。

  假如没有这些规矩,好比在调用一个过程时,不管是调用者仍是被调用者,均可能更新寄存器的值。假设调用者在%edx中存了一个整数值100,而被调用者也使用这个寄存器,并更新成了1000,因而悲剧就发生了。当过程调用完毕返回后,调用者再使用%edx的时候,值已经从100变成了1000,这几乎必将致使程序会错误的执行下去。因此便有以下规矩:

  在 IA32 中,寄存器%eax,%edx和%ecx被划分为调用者保存寄存器。当过程 P 调用 Q 时,Q能够覆盖这些寄存器,而不会破坏 P 所需的数据。

  寄存器%ebx,%esi和%edi被划分为被调用者保存寄存器。这里 Q 必须在覆盖这些寄存器的值以前,先把他们保存到栈中,并在返回前恢复它们,由于 P(或某个更高层次的过程)可能会在从此的计算中须要这些值。上面所说的过程实现的8个步骤中第三步即是如此。

考虑以下代码:

int P(int x)
{
int y = x*x;
int z = Q(y);     
return y+z;
}

过程 P 在调用 Q以前会先计算 y 的值,并且它必须保证 y 的值在 Q返回后是可用的。这里有两种方法实现:

  • 能够在调用 Q 以前,将 y 的值保存在本身的帧栈中;当 Q 返回时,过程 P 就能够从栈中取出y 的值。换句话说就是调用者 P 本身保存这个值。

  • 能够将 y 保存在被调用者保存寄存器中。若是 Q ,或者其它 Q 调用的程序想使用这个寄存器,它必须将这个寄存器的值保存在帧栈中,并在返回前恢复该值。换句话说就是被调用者保存这个值。当 Q 返回到 P 时,y 的值会在被调用者保存寄存器中,或者是由于寄存器根本就没有改变,或者是由于它被保存并恢复了。

过程实例

编写一个代码:function.c

#include <stdio.h>
int add(int a,int b){
register int c = a + b;
return c;
}
int main(){
int a = 100;
int b = 101;
int c = add(a,b);
return c;
}

在主函数过程当中调用add过程。

gcc -O0 -S function.c

为了完整的展示那8个步骤,所以给变量c加了register关键字修饰,这将会将c送入寄存器,从而更改被调用者保存寄存器,就会致使步骤3的发生。如下是main函数以及add函数各自的栈帧状况:

对以上学习内容的总结:

1.add函数会将返回结果存入%eax(前提是返回值可使用整数来表示),在main函数中,call指令以后,默认将%eax做为返回结果来使用。

2.全部函数(包括main函数)都必须有第1步和第六、七、8步,这是必须的4步。咱们的栈指针和帧指针有固定的大小关系,即栈指针永远小于等于帧指针,当两者相等时,当前栈帧被认为没有分配内存空间。

递归过程

一个过程也能调用本身自己的,就是递归调用。由于每一个调用在栈中都有它本身的私人空间,多个未完成调用的局部变量不会互相影响栈的原则也提供了适当的策略,当过程被调用时分布局部存储空间,当过程执行完毕返回时释放存储空间。

  下面是一段求 n 的阶乘的递归调用代码:

int rfact(int n){
int result;
if(n<=1){
    result = 1;
}else{
    result = n * rfact(n-1);
}
return result;
}

产生汇编代码:

上面的汇编代码,当用参数 n 来调用时,首先代码 2~5 行会建立一个帧栈,其中包含 %ebp 的旧值、保存的被调用者保存的寄存器 %ebx 的值,以及当递归调用自身的时候保存参数的四个字节。

  以下图所示,它用寄存器 %ebx 来保存过程参数 n 的值(第 6 行代码)。它将寄存器 %ebx 中的返回值设置为 1,预期 n<=1 的状况,它就会跳转到完成代码。
  
对于递归的状况,计算 n-1,将这个值存储在栈上,而后调用函数自身(第10~12行),在代码的完成部分,咱们能够假设:

  ①、寄存器%eax保存这(n-1)!的值

  ②、被调用保存寄存器%ebx保存着参数n

  所以将这两个值相乘(第 13 行)获得该函数的返回值。对于终止条件和递归调用,代码都会继续到完成部分(第15~17行),恢复栈和被调用者保存寄存器,而后在返回。

  因此咱们看到递归调用一个函数自己与调用其它函数是同样的。栈规则提供了一种机制,每次函数调用都有它本身的私有状态信息(保存的返回值、栈指针和被调用者保存寄存器的值)存储。若是须要,它还能够提供局部变量的存储。分配和释放的栈规则很天然的就与函数调用——返回的顺序匹配

2、一元操做和二元操做

1.一元操做

只有一个操做数,既是源又是目的,能够是一个寄存器,或者存储器位置。

2.二元操做

第一个操做数能够是当即数、寄存器或者存储器位置
第二个操做数能够是寄存器或者存储器位置
可是不能同时是存储器位置。
3、移位操做

SAL 算术左移
SHL 逻辑左移
SAR 算术右移(补符号位)
SHR 逻辑右移(补0)

控制

1、条件码

CF:进位标志
ZF:零标志
SF:符号标志
OF:溢出标志

条件码的改变:

数据传送指令
MOV 不影响标志位
PUSH POP 不影响标志位
XCHG 交换指令 不影响标志位
XLAT 换码指令 不影响标志位
LEA 有效地址送寄存器指令 不影响标志位
PUSHF 标志进栈指令 不影响标志位
POPF 标志出栈指令 标志位由装入值决定
2、访问条件码

这个指的是SET指令,经过set与不一样的条件码的组合,达到不一样的跳转条件。
某些底层的机器指令可能有多个名字,咱们称之为“同义名”。
3、跳转指令及其编码
jump分为直接跳转和间接跳转:

直接跳转:后面跟标号做为跳转目标
间接跳转:*后面跟一个操做数指示符

4、循环

C语言提供了多种循环结构。即do-while,while,for。汇编中没有相应的指令存在,能够用条件测试和跳转组合起来实现循环的效果。
循环的实践我在下面写出。

过程

一个过程调用包括将数据和控制从代码的一部分传递到另外一部分。另外,它还必须在进入时为过程的局部变量分配空间,并在退出时释放这些空间。
1、栈帧结构

栈用来传递参数、存储返回信息、保存寄存器,以及本地存储。

1.栈帧

为单个过程分配的那部分栈称为栈帧,通用结构见149页

因此本质上栈帧仍是栈。

2.两个指针

最顶端的栈帧以两个指针界定:

寄存器%ebp-帧指针

寄存器%esp-栈指针

栈指针可移动,因此信息访问多相对于帧指针。

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

在开始实践的时候,我在编译时候按照书上的指令来执行可是缺没办法生成正确的.o文件

都赖我想起来,老师上课说过,由于版本不同,因此在敲指令的时候,要把-o去掉,这样就能够生成能够检验的汇编语言代码。

最后,我还掌握了另一种看汇编代码的指令:

cat xxx.s

还有一种就是书上的代码方式:

objdump -d xxx.o

实验楼中的练习:

个人代码中汇编是movq %rbq,由于是我没使用-m32。
在64位下因此是movq %rbq。

书上还有几个实践,其中看代码的二进制代码:

相关文章
相关标签/搜索