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

程序的机器级

由于格式不够美观,我重新将博客换了一种编译器编写,但因为编译器的修改必须重新开一篇博客。留此截图以示提交日期,此博客为格式修改过的新随笔,以此为准。

一、x86寻址方式的历程

DOS时代的平坦模式,不区分用户空间和内核空间,很不安全;8086的分段模式;IA32的带保护模式的平坦模式
特别注意:i386增加了平坦寻址模式,Linux和最近版本的windows系列操作系统都是使用这种模式,这是Intel系列中第一台Unix操作系统的机器。

二、程序编程

1、机器级代码

①ISA:机器级程序的格式和行为,定义为指令集体系机构,它定义了处理器状态指令的格式,以及每条指令对状态的影响。
机器级程序使用的存储器地址是虚拟地址,提供的存储器模型看上去是一个非常大的字符数组。
②PC:程序计数器。在IA32中,用%eip表示,指示将要执行的下一条指令在存储器中的地址。
③程序存储器:包含程序的可执行机器代码,操作系统需要的一些信息,用来管理过程调用和返回的运行时栈,以及用户分配的存储器块。

④ARM(举一反三部分)参考链接:

http://blog.chinaunix.net/uid-25067956-id-398205.html

2、代码示例

①64位机上想得到32位代码:

gcc -m32 -S xxx.c

②编译并产生汇编目标文件xxx.o:

gcc -O1 -c xxx.c

③获得汇编代码:

gcc -S xxx.c -o xxx.s

④Ubuntu中获得汇编代码:(更接近教材)

gcc -S xxx.c

⑤教材中获得汇编代码:(编译器使用的是第一级优化)

gcc -O1 -S xxx.c

⑥汇编代码中需要牢记以下几句:

pushl %ebp
movl %esp,%ebp
……
popl %ebp
ret

⑦二进制文件可用od命令查看,也可以用gdb的x查看:

(gdb)x/17xb sum

(表示检查17个十六进制的字节)
⑧显示代码过多或过少可用more、less结合管道查看,也可以用输出重定向:

od xxx.o | more
od xxx.o > xxx.txt

⑨反汇编:

objdump -d xxx.o

⑩MAC OS中没有objdump,可用otool代替;

***注意:汇编代码中另类结尾有'l'指的是大小指示符。

3、关于格式的注释

当带选项-S和-O1运行gcc时,会产生xxx.s文件,其中带有'.'开头的行是指导汇编器和链接器的命令。
gcc -S产生的汇编代码中可以把以'.'开头的语句删除再使用也没关系。
了解Linux和windows的汇编格式的区别:Intel代码省略了指示大小的后缀,即'l';Intel代码省略了寄存器名字前面的'%'符号,用的是esp,而不是%esp。

三、数据格式

C语言数据类型在IA32中的大小:

四、访问信息

1、IA32的整数寄存器:

其中,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位的不会发生溢出。

2、操作数的整数寄存器

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

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

寻址方式和操作数格式:

3、数据传送指令

不能从内存地址直接MOV到另一个内存地址,要用寄存器中转一下,因此需要两个指令。

MOV:将源操作数的值复制到目的操作数中;
MOVS:将一个较小的源数据复制到一个较大的数据位置,高位用位扩展;
MOVZ:将一个较小的源数据复制到一个较大的数据位置,高位用零扩展。
push:把数据压入栈中;
pop:删除数据。

栈顶元素的地址是所有栈中元素地址中最低的,栈指针%esp保存着栈顶元素的地址。

4、数据传送示例

指针是地址。
局部变量通常是保存在寄存器中,而不是存储器中。寄存器访问比存储器访问要快得多。

五、算数和逻辑操作

第一类:目的操作数必须是一个寄存器

第二类:一元操作。操作数既是源又是目的。可以是寄存器也可以是存储器。

第三类:二元操作。第二个操作数既是源又是目的。但两个操作数不能同时是存储器。

第四类:移位操作。位移量是一个立即数或放在单字节寄存器%cl中。移位操作的目的操作数可以是一个寄存器或是一个存储器位置。

 

六、控制

机器代码提供两种基本的低级机制来实现有条件的行为:测试数据值,然后根据测试的结果来改变控制流或者数据流。
控制部分运用分支、循环语句实现。

最核心的是跳转语句、有条件跳转、无条件跳转。

1、条件码(状态寄存器):描述最近的算术或逻辑操作的属性。

重点关注:CF、ZF、SF、OF

leal指令不改变任何条件码,因为是用来进行地址计算的。

>>有两类指令只设置条件码而不改变任何其他寄存器。

>>CMP指令根据他们的两个操作数之差来设置条件码。除了至设置条件码而不更新目标寄存器之外。CMP与SUB行为是一样的。

2、访问条件码

SET指令:

SET命令的描述都适用的情况是:执行比较指令,根据计算t=a-b设置条件码。
当t=a-b中,当a-b<0时,CMP指令会设置仅为标志,因此无符号比较使用的是进位标志和零标志的组合。

3、跳转指令及其编码

跳转指令会导致执行切换到程序中一个全新的位置。这些跳转的目的地通常用一个标号指明。
跳转语句主要有有条件跳转(if,switch,while,for)和无条件跳转jmp(实现goto)。

主要查看书本包括例子:

p130/p131: if-else 
p132/p133: do-while
p134/p135: while
p137/p138: for
p144/p145: switch

七、过程

IA32通过栈帧来实现过程调用。数据传递、局部变量的分配和释放通过操纵程序栈来实现。

1、栈帧结构

机器用栈来传递过程参数、存储返回信息、保存寄存器用于以后恢复以及本地存储。

最顶端的栈帧以两个指针界定,寄存器%ebp为帧指针,而寄存器%esp为栈指针。

2、转移控制


call指令有一个目标,即指明被调用过程起始的指令地址。同跳转一样,调用可以是直接的,也可以是间接地。直接调用的目标是一个标号,而间接调用的目标是*后面跟一个操作数指示符。
call指令的效果是将返回地址入栈,并跳转到被调用过程的起始处。返回地址是在程序中紧跟在call后面的那条指令的地址。
ret指令从栈中弹出地址,并跳转到这个位置。栈指针要指向前面call指令存储返回地址的位置。
返回值存在%eax中。

八、使用GDB调试器

1、启动GDB:

gdb xxx

2、补充知识点:

<<<<查看函数调用栈信息的GDB命令
①backtrace/bt n
n是一个正整数,表示只打印栈顶上n层的栈信息。
-n表一个负整数,表示只打印栈底下n层的栈信息。
②frame n
n是一个从0开始的整数,是栈中的层编号。比如:frame 0,表示栈顶,frame 1,表示栈的第二层。
这个指令的意思是移动到n指定的栈帧中去,并打印选中的栈的信息。若不输入数字则默认打印当前帧的信息。 
③up n 表示向栈的上面移动n层,若不输入数字则默认向上移动一层。
④down n 表示向栈的下面移动n层,若不输入数字则默认向下移动一层。

九、遇到的问题和解决方法

起先做实验练习的时候,在编译week4.c时输入的编译代码是:

gcc -O1 -S week4.c

 

导致编译所得的汇编代码很奇怪,与应该有的代码差之甚远,如图:

将其删除为纯汇编代码后非常简短,无法直观看出栈帧使用情况。原因是老师给出的编译代码、书上的编译代码没有将功能区分清楚,才导致了这样的情况。

原本以为是系统环境的差别导致的,但是在MAC OS中编译也是十分简短,该有的代码没有出现。后来仔细尝试和再次查阅实验指导材料才发现是没有在64位环境下将代码转换为32位的汇编代码。

 

十、实验练习

①使用vim编辑代码:

②编译:

③使用vim查看汇编文件:


或者使用cat xxx.s查看汇编文件效果一致。
⑤修改汇编代码并另存为week4_final_.s:

代码操作完成。

虚拟机Ubuntu上操作截图:

纯汇编代码:

堆栈使用情况:

十一、参考文献

1、《深入理解计算机系统》pdf

2、ARM知识拓展博客:http://blog.chinaunix.net/uid-25067956-id-398205.html

3、实验楼实验指导书:https://www.shiyanlou.com/courses/413 实验四

posted @ 2015-10-12 21:40  20135313吴子怡  阅读(447)  评论(2编辑  收藏  举报