文章目录
1. 笔记
本章的主要讲了编程语法,包括:汇编和C语言。
1.1 basics
1.介绍了intel x86 processors的发展历史(AMD和ARM);
2.指令集大的分类:cisc和risc;
3.c代码到可执行代码的过程:
- c代码;
- 汇编代码(gcc -og -S);
- 二进制文件(gcc);
- 二进制文件(链接了其他的静态库)(gcc 或者 ld);
4.Objdump(将二进制代码转换成汇编代码);
5.gdb调试时使用disassemble命令,也可以获取汇编代码;
6.x86-64寄存器:rax、rbx、rcx、rdx、rsi、rdi、rsp、rb和 r8-r15;
- rax表示是64bit;
- eax表示32bit;
- ax表示16bit;
- ah表示高8bit;
- al表示低8bit;
7.Operation:
8.寻址方式(三种模式):
- movq (%rcx),%rax:将rcx寄存器指向的地址数据移动到rax
- movq 8(%rbp),%rdx:将[rbp]+8地址的数据移动到rdx
- Mem[Reg[Rb]+S*Reg[Ri]+ D](D只能是1、2、4和8)
假设rdx=0xf000 rcx=0x0100,则计算地址的结果如下:
9. 地址计算指令leaq,它主要有两个用途:
- 计算地址(而不会将该地址的数据存放到dest);
- 计算算术表达式,形如x+k*y(k是1、2、4或者8)(和取址指令一样)(要比普通的加法和乘法快一些)
1.2 control
一般采用linux类型的汇编模式,而不是Intel模式的;它们之间差别还是很大的
1.X86-64单bit的寄存器:CF、SF、ZF、OF
- cf:下图是cf的置1的情况,分别是:进位(unsigned溢出)和借位;
- sf:下图是sf置1的情况,根据符号位是否为1来判断;
- zf:当数据全为0时,zf置1;
- of:当两个数(符号位一致)相加,得到结果的符号位同原数据的符号位不一致;
2.Comq src2,src1指令:类似于src1-src2,影响PSW;
3.Testq src2,src1指令:类似于计算src1&src2;(经常用于判断一个寄存器里面得值是否为0)
- Zf set:src1&src2 == 0;
- Sf set:src1&src2 < 0;
4.Set dest指令:用于判断PSW的值,当条件成立时将1写入dest;
- 下图时setl成立的示意图:
5.Movzbl:高位填充为0;
6.jx指令:
7.c语言goto;
8.c语言条件语句:if、a?b:c(注意b和c如果是表达式都会被计算,所以会增加复杂度);
9.循环语句:do-while、while、for;
10.Switch:编译成汇编后,会结合jump table和rdi(index)找到对应的跳转入口;
1.3 Procedures
1.ABI:机器程序级别的接口(windows linux ios都有自己的abi,大致相同,细节不同)。它包括:passing control、passing data、memory management等机制;
- 比如一个编译好的二进制程序,如果放在ABI修改后的机器上运行会导致程序出现无法预计的错误;
2.Stack:向低地址增长
- Pushq src:修改(减少8bytes)%rsp;将src写入stack
- Popq dest:将栈顶的数据放入dest;修改(增加8bytes)%rsp
3.passing control
- 使用stack;
- 执行call label语句时:将return address压入栈;跳转到label执行;
- Return address:当前call label语句的下一条语句的地址;
- 执行ret语句时:pop出栈的return address;跳转到该处运行;
4.Passing data
- 参数传递:前6个参数通过寄存器:%rdi、%rsi、%rdx、%rcx、%r8、%r9;剩余的参数通过stack传递;(浮点数参数有单独的寄存器);
- 返回值:%rax
5.Stack frames:
- Contents:返回地址;局部变量空间;临时空间
- Management:stack分配空间(call)和释放空间的时机(return);
call的时候是否压入%rbp是可选的
6.Linux stack frame
7.Register saving conventions
- “Caller Saved”:Caller saves temporary values in its frame before the call;
- “Callee Saved”:Callee saves temporary values in its frame before using;Callee restores them before returning to caller;
Caller-saved registers (AKA volatile registers, or call-clobbered) are used to hold temporary quantities that need not be preserved across calls. caller如果在callee返回之后需要使用这些寄存器,则应该在callee之前保存它们。
Callee-saved registers (AKA non-volatile registers, or call-preserved) are used to hold long-lived values that should be preserved across calls. 说明这些寄存器在callee返回时会被恢复,所以caller可以不用保存这些寄存器,即使后面需要使用也不需要保存(因为Callee返回时会恢复)。
2. bomb lab
本实验主要考察了汇编、程序ABI的了解、gdb调试等基础知识。实验提供了一个二进制文件:bomb和一个c语言文件:bomb.c。
2.1 phase 1
Phase 1的函数汇编代码如下:
0000000000400ee0 <phase_1>:
400ee0: 48 83 ec 08 sub $0x8,%rsp
400ee4: be 00 24 40 00 mov $0x402400,%esi
400ee9: e8 4a 04 00 00 callq 401338 <strings_not_equal>
400eee: 85 c0 test %eax,%eax
400ef0: 74 05 je 400ef7 <phase_1+0x17>
400ef2: e8 43 05 00 00 callq 40143a <explode_bomb>
400ef7: 48 83 c4 08 add $0x8,%rsp
400efb: c3 retq
很显然phase 1函数将我们的输入同bomb程序预先存储的字符串进行匹配,如果不一致的话则会引爆炸弹。
我们知道%rdi和%rsi可以用来传递参数,%rdi当前指向我们输入的字符串的地址,那么%esi就指向密钥。从汇编mov $0x402400,%esi可知,该密钥存储在0x402400内存地址上。
用gdb查看该地址存放的字符串的命令是:x/s 0x402400。得到输出结果:Border relations with Canada have never been better.
2.2 phase 2
Phase 2的汇编代码如下:
0000000000400efc <phase_2>:
400efc: 55 push %rbp
400efd: 53 push %rbx
400efe: 48 83 ec 28 sub $0x28,%rsp
400f02: 48 89 e6 mov %rsp,%rsi
400f05: e8 52 05 00 00 callq 40145c <read_six_numbers>
400f0a: 83 3c 24 01 cmpl $0x1,(%rsp)
400f0e: 74 20 je 400f30 <phase_2+0x34>
400f10: e8 25 05 00 00 callq 40143a <explode_bomb>
400f15: eb 19 jmp 400f30 <phase_2+0x34>
400f17: 8b 43 fc mov -0x4(%rbx),%eax
400f1a: 01 c0 add %eax,%eax
400f1c: 39 03 cmp %eax,(%rbx)
400f1e: 74 05 je 400f25 <phase_2+0x29>
400f20: e8 15 05 00 00 callq 40143a <explode_bomb>
400f25: 48 83 c3 04 add $0x4,%rbx
400f29: