2017-2018-1 20155324 《信息安全系统设计基础》第5周学习总结
信息安全的核心思维方式“逆向”在这有很好很直接的体现,反汇编就是直接的逆向工程。
反汇编(Disassembly):把目标代码转为汇编代码的过程,也可以说是把机器语言转换为汇编语言代码、低级转高级的意思,常用于软件破解(例如找到它是如何注册的,从而解出它的注册码或者编写注册机)、外挂技术、病毒分析、逆向工程、软件汉化等领域。学习和理解反汇编语言对软件调试、漏洞分析、OS的内核原理及理解高级语言代码都有相当大的帮助,在此过程中我们可以领悟到软件作者的编程思想。总之一句话:软件一切神秘的运行机制全在反汇编代码里面。
通常,编写程序是利用高级语言如C,C++,Delphi等高级语言进行编程的,然后再经过编译程序生成可以被计算机系统直接执行的文件(机器语言)。反汇编即是指将这些执行文件反编译还原成汇编语言或其他语言。但通常反编译出来的程序与原程序会存在些许不同,虽然执行效果相同,但程序代码会发生很大的变化,要读懂反汇编需要有扎实的高级语言编写功底和汇编功底。
X86 寻址方式:
1 DOS时代的平坦模式,不区分用户空间和内核空间,很不安全
2 8086的分段模式
3 IA32的带保护模式的平坦模式
ISA的定义
指令集体系结构(ISA)定义了处理器状态、指令的格式、以及每条指令对状态的影响。大多数ISA包括IA32和x84-64,将程序的行为描述成好像每条指令是按顺序执行的。
- 汇编命令与反汇编命令
用gcc -S xxx.c -o xxx.s
获得汇编代码,用gcc -c code.c
产生目标文件code.o
(二进制文件,无法直接查看),用objdump -d xxx.o
反汇编可以查看目标代码文件内容。
汇编代码(函数前两条和后两条汇编代码,所有函数都有,建立函数调用栈帧):
前两条:
pushl %ebp 将寄存器%ebp的内容压入程序栈
movl %esp,%ebp 得到新栈低,将当前栈顶赋予栈低
后两条:
popl %ebp 过程调用结束,恢复旧栈低
ret 子程序的返回指令
64位机器上想要得到32代码:gcc -m32 -S xxx.c
gcc -S 产生的汇编中可以把 以”.“开始的语句都删除了再阅读。
查看二进制文件
二进制文件可以用od 命令查看,也可以用gdb的x命令查看。 有些输出内容过多,我们可以使用 more或less命令结合管道查看,也可以使用输出重定向来查看:
od code.o | more
od code.o > code.txt
不同数据的汇编代码后缀
Linux和Windows的汇编格式区别
1 Intel代码省略了指示大小的后缀。
2 Intel代码省略了寄存器名字前面的'%'符号。
3 Intel代码用不同的方式来描述寄存器中位置。
4 在带有多个操作数的指令情况下,列出操作数的顺序相反。
ATT是GCC、OBJDUMP和其它一些我们使用的工具的默认格式。Microsoft的工具、以及来自Intel的文档,其汇编代码都是Intel格式的。使用gcc -S -masm=intel code.c
可以使GCC产生Intel格式的代码。
寄存器
esi edi可以用来操纵数组,esp ebp用来操纵栈帧。32位的eax,16位的ax,8位的ah,al都是独立的。
- 寻址方式
操作数三种类型
立即数,即常数值
寄存器,表示某个寄存器的内容
存储器,根据计算出来的地址(有效地址)访问某个存储器位置。
有效地址
计算方式Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s
MOV
- MOV
相当于C语言的赋值“=”,不能从内存地址直接MOV到另一个内存地址,要用寄存器中转一下。
MOV类中的指令将源操作数的值复制到目的操作数中,源操作数指定的值是一个立即数,存储在寄存器或存储器中,目的操作数指定一个位置,要么是一个寄存器,要么是一个存储器地址。
- push与pop
先进后出:push将数据压入栈中,pop弹出,弹出的永远是最近被压入的。用数组实现栈,进行操作的一端为栈顶。栈向下增长,栈顶元素的地址是所有栈中元素地址中最低的。栈指针%esp保存栈顶元素的地址。
源操作数、目的操作数应为字操作数。
- 指针
C语言中“指针”其实就是地址。
- 局部变量
局部变量通常是保存在寄存器中。因为,寄存器访问比存储器访问要快得多。
- 算术和逻辑运算
1、一元操作:只有一个操作数,既是源又是目的,可以是一个寄存器,或者存储器位置。
2、二元操作:第一个操作数可以是立即数、寄存器或者存储器位置;第二个操作数既是源也是又是目的,可以是寄存器或者存储器位置,但是不能同时是存储器位置。
操作的顺序:第二个操作数 操作符 第一个操作数
3、移位操作:先给出移位量,第二项给出要移位的数值。
源操作数(移位量):立即数或者放在单字节寄存器元素%cl中。目的操作数:一个寄存器或是一个存储器位置。
- 使用disassemble指令获取汇编代码(因为之前执行的命令中有-m32,所以此处显示的是32位汇编代码)
- 用
i r
指令查看各寄存器的值
可见此时主函数的栈基址为0xffffd058
,用x 0xffffd058指令查看内存地址中的值:
因此,目前%esp所指堆栈内容为0,%ebp所指内容也为0
- 使用display /i $pc(结合display命令和寄存器/pc内部变量)指令进行设置
依次如下指令调试汇编代码,并查看%esp、%ebp和堆栈内容:
1、使用si指令单步跟踪一条机器指令
2、使用i r指令查看各寄存器的值(在这里要看%eip、%eax、%esp和%ebp)
3、使用x/na %esp对应的值指令查看堆栈变化
之后一直重复执行上述三步,直至结束
有条件跳转的条件看状态寄存器(教材上叫条件码寄存器) 注意leal不改变条件码寄存器。
1、分支(if/switch)
C语言if-else 语句:
if(test-expr)
then-statement
else
else-statement
(注:test-expr 整数表达式[假/真])
汇编实现形式:
t = test-expr;
if (!t)
goto false;
then-statement
goto done;
false:
else-statement
done:
2、循环(while, for)
C语言do-while循环:
do
body-statement
while(test-expr);
汇编实现形式:
loop:
body-statement
t = test-expr;
if(t)
goto loop;
while循环、for循环均需转换成do-while形式在转换
gdb调试分析汇总表
指令 | %eip | %ebp | %esp | %eax | 堆栈 |
---|---|---|---|---|---|
push $0x13 | 0x80483f9 | 0xffffd058 | 0xffffd058 | 0xf7fbadbc | 0x00000000 |
call 0x80483e6 | 0x80483fb | 0xffffd058 | 0xffffd054 | 0xf7fbadbc | 0x13 0x0 |
push %ebp | 0x80483e6 | 0xffffd058 | 0xffffd050 | 0xf7fbadbc | 0x8048400 0x13 0x0 |
mov %esp,%ebp | 0x80483e7 | 0xffffd058 | 0xffffd04c | 0xf7fbadbc | 0xffffd058 0x8048400 0x13 0x0 |
pushl 0x8(%ebp) | 0x80483e9 | 0xffffd04c | 0xffffd04c | 0xf7fbadbc | 0xffffd058 0x8048400 0x13 0x0 |
call 0x80483db | 0x80483ec | 0xffffd04c | 0xffffd048 | 0xf7fbadbc | 0x13 0xffffd058 0x8048400 0x13 0x0 |
push %ebp | 0x80483db | 0xffffd04c | 0xffffd044 | 0xf7fbadbc | 0x80483f1 0x13 0xffffd058 0x8048400 0x13 0x0 |
mov %esp,%ebp | 0x80483dc | 0xffffd04c | 0xffffd040 | 0xf7fbadbc | 0xffffd04c 0x80483f1 0x13 0xffffd058 0x8048400 0x13 0x0 |
mov 0x8(%ebp),%eax | 0x80483de | 0xffffd040 | 0xffffd040 | 0xf7fbadbc | 0xffffd04c 0x80483f1 0x13 0xffffd058 0x8048400 0x13 0x0 |
add $0x13,%eax | 0x80483e1 | 0xffffd040 | 0xffffd040 | 0x13 | 0xffffd04c 0x80483f1 0x13 0xffffd058 0x8048400 0x13 0x0 |
pop %ebp | 0x80483e4 | 0xffffd040 | 0xffffd040 | 0x26 | 0xffffd04c 0x80483f1 0x13 0xffffd058 0x8048400 0x13 0x0 |
ret | 0x80483e5 | 0xffffd04c | 0xffffd044 | 0x26 | 0x80483f1 0x13 0xffffd058 0x8048400 0x13 0x0 |
add | $0x4,%esp | 0x80483f1 | 0xffffd04c | 0xffffd048 | 0x26 |
leave | 0x80483f4 | 0xffffd04c | 0xffffd04c | 0x26 | 0xffffd058 0x8048400 0x13 0x0 |
ret | 0x80483f5 | 0xffffd058 | 0xffffd050 | 0x26 | 0x8048400 0x13 0x0 |
add | $0x4,%esp | 0x8048400 | 0xffffd058 | 0xffffd054 | 0x26 |
add | $0x13,%eax | 0x8048403 | 0xffffd058 | 0xffffd058 | 0x26 |
leave | 0x8048406 | 0xffffd058 | 0xffffd058 | 0x39 | |
ret | 0x8048407 | 0x0 | 0xffffd05c | 0x39 |
教材学习中的问题和解决过程
问题:区分MOV,MOVS,MOVZ
解决方案:
1.MOV相当于C语言的赋值”=“
2.MOVS将作了符号扩展的字节传送到字
3.MOVZ将作了零扩展的字节传送到字
代码托管
其他(感悟、思考等,可选)
通过本周的学习,C语言代码的汇编和反汇编、C语言代码和汇编代码的转换以及加强阅读汇编代码的能力提高上。
尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。
计划学习时间:XX小时
实际学习时间:XX小时
改进情况:
(有空多看看现代软件工程 课件
软件工程师能力自我评价表)