您的位置:首页 > 其它

20145232韩文浩《信息安全系统设计基础》第5周学习总结

2016-10-16 18:17 351 查看

第三章 程序的机器级表示

寻址方式历史

Intel处理器系列俗称x86,开始时它是第一代单芯片。16位为处理器之一。

8086是第一代,也是汇编课程中学习的处理器型号。

程序编码

GCC为32位执行的默认调用。(gcc -m32)

在GCC中获得汇编代码与反汇编

获得汇编代码:gcc –S xxx.c –o xxx.s。

反汇编(将二进制目标文件还原为汇编代码)objdump –d xxx.o。

注:64位机器上想要得到32位代码:gcc –m32 –S xxx.c。

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

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


要查看目标代码文件的内容,最有价值的是反汇编器

objdump -d code.o


机器代码和它的反汇编表示的一些特性:

IA32指令长度从1到15个字节不等

设计指令格式的方式是,从某个给定位置开始,可以将字节唯一的解码成机器指令

反汇编器只是基于机器代码文件中的字节序列来确定汇编代码,不需要访问程序的源代码或汇编代码

反汇编器使用的指令命名规则与GCC生成的汇编代码使用的有些差别

数据格式

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字节

访问信息

数据传送指令

MOV类由三条指令组成:

movb 传送字节

movw 传送字

movl 传送双字

操作数:

MOV reg/mem, imm      ;立即数寄存器或存储器 MOV reg/mem/seg, reg ;寄存器的值寄存器/内存/段寄存器 MOV reg/seg, mem       ;内存单元的值寄存器/段寄存器 MOV reg/mem, seg      ;段寄存器的值寄存器/内存单元 IA32的限制:两个操作数都不能指向存储器。


push&pop

堆栈:

1.后进先出

2.栈指针指向栈顶元素

3.栈朝低地址方向增长

压栈push:

格式PUSH r16/m16/seg

功能:

第一步:SP←SP-2 ;堆栈指针SP上移

第二步:(SS):(SP)←r16/m16/seg ;字操作数存入堆栈顶部

出栈pop:

格式POP r16/m16/seg

功能:

第一步:r16/m16/seg← (SS):(SP) ;栈顶的一个字传送到指定的目的操作数

第二步:SP←SP+2 ;堆栈指针SP下移,指向新的栈顶

算术和逻辑操作

加载有效地址指令
leal,是movl指令的变形,对比汇编中的LEA指令学习。

实际上是将有效地址写入目的操作数。

一元操作和二元操作

一元操作

INC       加1
DEC       减1
NEG       取负
NOT       取补

只有一个操作数,既是源又是目的,可以是一个寄存器,或者存储器位置。

二元操作

ADD       加
SUB       减
IMUL      乘
XOR       异或
OR        或
AND       与

第一个操作数可以是立即数、寄存器或者存储器位置

第二个操作数既是源也是又是目的。可以是寄存器或者存储器位置,但是不能同时是存储器位置。

注意操作的顺序:第二个操作数 操作符 第一个操作数

移位操作

先给出移位量,第二项给出要移位的数值。

SAL       左移
SHL       左移(等同于SAL)
SAR       算术右移
SHR       逻辑右移


特殊操作

乘法

乘积截断

imull   双操作数
从两个32位操作数产生一个32位的乘积。

乘积不截断

mull    无符号数乘法
imull   有符号数乘法
要求一个参数必须在寄存器%eax中,另一个作为指令的源操作数给出。
乘积的高32位在%edx中,低32位在%eax中。

除法

有符号除法

idivl 操作数
将DX:AX中的64位数作为被除数,操作数中为除数
结果:商在AX中,余数在DX中。

无符号除法

divl指令
通常会事先设定寄存器%edx为0。

控制

条件码寄存器
CF:进位标志。最近操作使最高位产生进位。用于检查无符号操作数的溢出。

ZF:零标志。最近操作结果为0。

SF:符号标志。最近操作得到的结果为负数。

OF:溢出标志。最近操作导致一个补码溢出。

访问条件码
使用方法:根据条件码的某个组合,将一个字节设置为0或1。

SET指令:执行比较指令,根据计算
t=a-b
的结果设置条件码

可以条件跳转到程序的某个其他部分,可以有条件的传送数据。

跳转指令及其编码
无条件跳转

直接跳转:跳转目标是作为指令的一部分编码的。

间接跳转:跳转目标是从寄存器或存储器位置中读出的。

当执行与PC相关的寻址时,程序计数器的值是跳转指令后面的那条指令的地址,而不是跳转指令本身的地址。

翻译条件分支
将条件表达式和语句从c语言翻译成机器语言,最常用的方式就是结合有条件和无条件跳转

if(test-expr)
then-statement
else
else-statement

(注:test-expr    整数表达式[假/真])

循环
汇编中可以用条件测试和跳转组合起来实现循环的效果,但是大多数汇编器中都要先将其他形式的循环转换成do-while格式。

do-while循环

do
body-statement
while(test-expr);

翻译成如下条件和goto语句:

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


while循环

while (test-expr)
body-statement

转换成 do-while 形式:

if(!test-expr)
goto done;
do
body-statement
while(test-expr);
done:

翻译成 goto 形式:

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


for循环

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

同while形式

init-expr;
while(test-expr){
body-statement
update-expr;
}

转换成 do-while 形式:

init-expr;
if(!test-expr)
goto done;
do{
body-statement
update-expr;
}while(test-expr);
done;

转换成 goto 代码:

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


Switch语句
Switch语句是多重分支的典型,而且使用的是跳转表这种数据类型,是的搜索的更快更高效。

所以这里的关键就是要领会使用跳转表是一种非常有效的实现多重分支的方法。

过程

进入,为过程的局部变量分配空间
将数据(以过程参数和返回值的形式)和控制从代码的一部分传递到另一部分。
退出,释放这些空间。

栈帧: 为单个过程分配的那部分栈

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

寄存器%ebp为帧指针
寄存器%esp为栈指针


栈向低地址方向增长,栈指针%esp指向栈顶元素:

栈指针值适当减小可以分配没有指定初始值的数据的空间
类似的,可以通过增加栈指针来释放空间

转移控制

call指令

目标:指明被调用过程起始的指令地址。

效果:将返回地址入栈,并跳转到被调用过程的起始处。

ret指令

从栈中弹出地址,并跳转到这个位置.

使用这个指令时,栈指针要指向前面call指令存储返回地址的位置。

leave

这个指令可以使栈做好返回的准备

寄存器使用惯例

程序寄存器组是唯一能被所有过程共享的资源。

必须保证一个过程(调用者)在调用另一个过程(被调用者)时,被调用者不会覆盖某个调用者寄存器中的值。

%eax,%edx,%ecx 划分为调用者保存寄存器

%ebx,%esi,%edi 划分为被调用者保存寄存器

%ebp,%esp 保持寄存器

%eax 保存函数返回值

GDB命令

运行
run(简写r): 运行程序,当遇到断点后,程序会在断点处停止运行,等待用户输入下一步的命令。

continue(简写c):继续执行,到下一个断点处(或运行结束)

next(简写n): 单步跟踪程序,当遇到函数调用时,直接调用,不进入此函数体;

step(简写s):单步调试如果有函数调用,则进入函数;与命令n不同,n是不进入调用的函数的

until:运行程序直到退出循环体; / until+行号: 运行至某行

finish: 运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息。

call 函数(参数):调用“函数”,并传递“参数”,如:call gdb_test(55)

quit:简记为 q ,退出gdb 设置断点

设置断点
break n(简写b n):在第n行处设置断点 ;可以带上代码路径和代码名称: bOAGUPDATE.cpp:578)

break func:在函数func()的入口处设置断点,如:break cb_button

delete 断点号n:删除第n个断点

disable 断点号n:暂停第n个断点

enable 断点号n:开启第n个断点

clear 行号n:清除第n行的断点

info breakpoints(简写info b) :显示当前程序的断点设置情况

查看源代码
list(简写l):列出程序的源代码,默认每次显示10行。

list 行号:将显示当前文件以“行号”为中心的前后10行代码,如:list 12

list 函数名:将显示“函数名”所在函数的源代码,如:list main

list :不带参数,将接着上一次 list 命令的,输出下边的内容。

打印表达式
print 表达式:简记为 p ,其中“表达式”可以是任何当前正在被测试程序的有效表达式,比如当前正在调试C语言的程序,那么“表达式”可以是任何C语言的有效表达式,包括数字,变量甚至是函数调用。

print a:将显示整数 a 的值

print ++a:将把 a 中的值加1,并显示出来

print name:将显示字符串 name 的值

print gdb_test(22):将以整数22作为参数调用 gdb_test() 函数

print gdb_test(a):将以变量 a 作为参数调用 gdb_test() 函数

display 表达式:在单步运行时将非常有用,使用display命令设置一个表达式后,它将在每次单步进行指令后,紧接着输出被设置的表达式及值。

watch 表达式:设置一个监视点,一旦被监视的“表达式”的值改变,gdb将强行终止正在被调试的程序。

查询运行信息
where/bt :当前运行的堆栈列表

set args 参数:指定运行时的参数

show args:查看设置好的参数

info program: 来查看程序的是否在运行,进程号,被暂停的原因

分割窗口
layout:用于分割窗口,可以一边查看代码,一边测试

layout src:显示源代码窗口

layout asm:显示反汇编窗口

layout regs:显示源代码/反汇编和CPU寄存器窗口

layout split:显示源代码和反汇编窗口

Ctrl + L:刷新窗口

实践过程

C语言编译器产生的汇编代码



查看代码的二进制代码



将C语言文件编译成可执行文件并查看可执行文件的二进制内容



将可执行文件反编译成汇编代码



将调用函数的栈帧栈底地址入栈,即将bp寄存器的值压入调用栈中

建立新的栈帧,将被调函数的栈帧栈底地址放入bp寄存器中

本周代码托管截图

https://git.oschina.net/albieh

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

有不少内容都是在上学期学的汇编基础上加以巩固,理解起来比较轻松,也遇到了以前许多没有遇到的命令。

学习进度条

代码行数(新增/累积)博客量(新增/累积)学习时间(新增/累积)重要成长
目标5000行16篇400小时
第一周80/801/120/20
第二周130/2101/218/38
第三周300/5101/322/60
第五周300/8101/430/90

参考资料

《深入理解计算机系统V2》学习指导

...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: