信息安全系统设计基础第五周学习总结

实验四 第三章 程序的机器级表示

本章学习内容是汇编语言,一定要能读懂

3.1-3.7中练习重点:3.1,3.3,3.5,3.6,3.9,3.14,3.15,3.16,3.22,3.23,3.27,3.29,3.30,3.33,3.34

X86 寻址方式经历三代:

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

对于机器级编程来说,其中两种抽象尤为重要

     1  机器级程序的格式和行为,定义为指令集体系结构(ISA),它定义了处理器状态,指令的格式,以及每条指令对状态的影响
     2  机器级程序使用的存储器地址是虚拟地址,提供的存储器模型看上去是一个非常大的字节数组

 

数据格式:由于是从16位体系结构扩展成32位,intel用术语字(word)表示16位数据类型,因此32位为双字(double words),64位数为4字(quad words)

 

对于ISA,要有以下基本观念:IA32的ISA和x86-64的ISA,以及其他大多数ISA,在抽象时都将指令按顺序执行抽象。但是处理器的硬件可以并发地执行许多指令,并且采用了一些safeguards来确保并行执行之后的结果和一条一条顺序执行的结果一样

一个IA32 CPU包含一组8个存储32位值的寄存器,用以存整数数据和指针:eax,ecx,edx,ebx,esi,edi,esp,ebp.大多数情况下前六个都用作通用寄存器,eax,ecx,edx的存储和恢复惯例不同于ebx,edi,esi(前三者为被调用者保存,后三者为调用者保存,详见3.7.3);最后两个用于存储指针,由于在过处理中非常重要,分别指向栈帧的顶部和底部,必须保持

 

关于ISA的指令:

一是,ISA中每条指令占用字节数不等,常用的指令所需的字节数少,不太常用的指令的字节数多,这样的话,相对于每条指令占用等长字节数的ISA,这种占用字节数不等的ISA为程序产生的总空间要更少。

 二是,ISA中的指令在设计时,达到了一个效果,对于一个二进制指令串,从某个字节开始,译码的结果是唯一的,这是达到了编码理论中“唯一可译性”的要求。

 (我觉得大概正是反汇编器正是利用了唯一可译性,从而将一串指令字节序列分割开来的)

 

 IA机器代码和原始的C代码差别很大

     1  程序计数器(PC)指令将要执行的下一条指令在存储器中的地址
     2  整数寄存器文件包含8个命名的位置,分别存储32位的值
     3  一些浮点寄存器存放浮点数据

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

注意:  64位机器上想要得到32代码:gcc -m32 -S xxx.c
       MAC OS中没有objdump, 有个基本等价的命令otool 
       Ubuntu中 gcc -S code.c (不带-O1) 产生的代码更接近教材中代码(删除"."开头的语句)

二进制文件可以用od 命令查看,也可以用gdb的x命令查看。 有些输出内容过多,我们可以使用 more或less命令结合管道查看,也可以使用输出重定向来查看

            od code.o | more
            od code.o > code.txt

当一个源文件生成了'.o'的目标二进制文件后,无法直接查看。

但是还是有个查看目标代码文件内容的方法,就是对'.o'目标文件使用反汇编器。它的输出还是二进制文件,但是,反汇编器将这些二进制按照指令进行了分段。让我们知道哪一段是一个指令(格式上与汇编器产生的汇编文件一样,分行的)

 

表中不同数据的汇编代码后缀

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

数据传送指令有三个变种:movb(传送字节)movw(传送字)movl(传送双字)

 

寄存器:

     esi edi可以用来操纵数组

     esp ebp用来操纵栈帧

     对于寄存器,特别是通用寄存器中的eax,ebx,ecx,edx,要理解32位的eax,16位的ax,8位的ah,al都是独立的,通过下面例子说明:

    假定当前是32位x86机器,eax寄存器的值为0x8226,执行完addw $0x8266, %ax指令后eax的值是多少? 
解析:0x8226+0x826=0x1044c, ax是16位寄存器,出现溢出,最高位的1会丢掉,剩下0x44c,不要以为eax是32位的不会发生溢出

 

操作数的三种类型:立即数、寄存器、存储器

     立即数:即常数值

     寄存器:表示某个寄存器内容

     存储器:根据计算出来的地址(通常称有效地址)访问某个存储器位置

     因此寻址方式也有多种,如:立即数寻址、寄存器寻址、绝对寻址、间接寻址、变址寻址、伸缩化  的变址寻址……

 

有效地址的计算方式 Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s

 

区分MOV,MOVS,MOVZ三个命令

     MOV相当于C语言的赋值”=“

     MOVS将作了符号扩展的字节传送到字

     MOVZ将作了零扩展的字节传送到字

(不能从内存地址直接MOV到另一个内存地址,要用寄存器中转一下)

mov族(mov指令还有很多兄弟指令如movb、movw、movsb、movzb)、pop、push

movsb、movzb分别为符号扩展、零扩展,它们只拷贝一个字节,源操作数均为单字节,并设置目的操作数中其余的位,效果如下:

      初始假设:%dh=8D  %eax=98765432

      1   movb   %dh,%al       ;%eax=9876548D

      2   movsbl %dh,%eax    ;%eax=FFFFFF8D(目的操作数高24位设为源字节最高位,在这里为很显然为1,所以前24位为全F)

      3   movzbl %dh,%eax    ;%eax=0000008D(目的操作数高24位被设为0)

pushl指令等价于:

          subl $4,%esp

          movl %ebp,(%esp)  //注意这里的括号引起的差别

popl指令等价于:

           movl (%esp),%eax

           addl $4,%esp

 

栈顶元素的地址是所有栈中元素地址中最低的

 

1.指针其实是地址,间接引用指针就是将该指针放在一个寄存器中 ,然后在间接存储器引用中引用这个寄存器

2.局部变量通常保存在寄存器中,而不是存储器(个人猜测应该是局部变量属于动态分配,局部变量因此被动态置入寄存器,而非存储器)

例如调用exchange:

     int a = 4;

     int b = exchange(&a,3);

     printf("a=%d,b=%d\n",a,b);

     打印出a=3,b=4

(&(取址)创建一个指针,在本例中,该指针指向保存局部变量a的位置.然后函数exchange用3覆盖存储在a中的值,但是返回4作为函数的值)

 

算术和逻辑操作

     移位操作 :sall==shll(填0)  sarl(算术右移,填符号位)  shrl(逻辑右移,填0)

     特殊算术操作:imull(有符号64位乘法)  mull(无符号64位乘法)  cltd(转换为四字) idivl(有符号除法)   divl(无符号除法)

 

 

使用gcc –S –o main.s main.c -m32命令编译成汇编代码

posted @ 2015-10-11 14:00  20135305  阅读(393)  评论(1编辑  收藏  举报