深入理解计算机系统 BombLab 实验报告
又快有一个月没写博客了,最近在看《深入理解计算机系统》这本书,目前看完了第三章,看完这章,对程序的机器级表示算是有了一个入门,也对 C 语言里函数栈帧有了一个初步的理解。
为了加深对书本内容的认识,以后每学习完一部分章节,就完成相应书本附带的实验题目。
第三章对应的实验是 BombLab,下面是我做这个实验的过程。
BombLab 分为 6 个普通关卡和一个隐形关卡,为了开始闯关,得先弄清楚从哪里开始行动。
首先使用 objdump 命令 objdump -t bomb > bomb_symboltable 来生成 bomb 文件的符号表(部分),如下:
bomb: file format elf64-x86-64 SYMBOL TABLE: 0000000000400238 l d .interp 0000000000000000 .interp 0000000000400254 l d .note.ABI-tag 0000000000000000 .note.ABI-tag 0000000000400274 l d .note.gnu.build-id 0000000000000000 .note.gnu.build-id 0000000000400298 l d .gnu.hash 0000000000000000 .gnu.hash 00000000004002c8 l d .dynsym 0000000000000000 .dynsym 00000000004005c8 l d .dynstr 0000000000000000 .dynstr 0000000000400736 l d .gnu.version 0000000000000000 .gnu.version ...
...
...
...
... 0000000000000000 F *UND* 0000000000000000 __ctype_b_loc@@GLIBC_2.3 0000000000603750 g O .bss 0000000000000008 stderr@@GLIBC_2.2.5 0000000000000000 F *UND* 0000000000000000 __sprintf_chk@@GLIBC_2.3.4 0000000000000000 F *UND* 0000000000000000 socket@@GLIBC_2.2.5
这个文件内容太多,我们只提取出含有关键字 bomb 的行,如下:
0000000000000000 l df *ABS* 0000000000000000 bomb.c 00000000004013ba g F .text 0000000000000002 initialize_bomb_solve 000000000040143a g F .text 0000000000000022 explode_bomb 000000000060375c g O .bss 0000000000000004 bomb_id 00000000004013a2 g F .text 0000000000000018 initialize_bomb
其中 000000000040143a g F .text 0000000000000022 explode_bomb 这一行就是用来引爆炸弹用的,我们可以先记住这个地址,以备用。
下面我们再对 bomb 文件进行反汇编,使用命令 objdump -d bomb > bomb_disassamble 可以得到 bomb 文件的反汇编文件,由于文件内容太多,这里就不全部贴出来了,在接下来的闯关中,会陆陆续续的讲这个文件中的一些汇编贴出来使用。
有了这些准备条件,下面我们开始闯关!
注意:接下来所有贴出来的函数的反汇编代码,都可以通过对 bomb 文件进行反汇编得到。
第一关:
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
首先看第一条指令 sub $0x8,%rsp,这条指令用来分配 8 字节的函数栈帧,指令 mov $0x402400,%esi ,则将立即数 0x402400 传入寄存器 %esi 中,然后调用 strings_not_equal 这个函数, test %eax,%eax 这条指令判断寄存器 %eax 里是否为 0,如果为 0,则直接跳到 add $0x8,%rsp ,将函数指针加 8,释放栈帧。如果不为 0,则执行 callq 40143a <explode_bomb> ,引爆炸弹。
下面对 strings_not_equal 函数的反汇编代码进行分析:
1 0000000000401338 <strings_not_equal>: 2 401338: 41 54 push %r12 3 40133a: 55 push %rbp 4 40133b: 53 push %rbx 5 40133c: 48 89 fb mov %rdi,%rbx 6 40133f: 48 89 f5 mov %rsi,%rbp 7 401342: e8 d4 ff ff ff callq 40131b <string_length> 8 401347: 41 89 c4 mov %eax,%r12d 9 40134a: 48 89 ef mov %rbp,%rdi 10 40134d: e8 c9 ff ff ff callq 40131b <string_length> 11 401352: ba 01 00 00 00 mov $0x1,%edx 12 401357: 41 39 c4 cmp %eax,%r12d 13 40135a: 75 3f jne 40139b <strings_not_equal+0x63> 14 40135c: 0f b6 03 movzbl (%rbx),%eax 15 40135f: 84 c0 test %al,%al 16 401361: 74 25 je 401388 <strings_not_equal+0x50> 17 401363: 3a 45 00 cmp 0x0(%rbp),%al 18 401366: 74 0a je 401372 <strings_not_equal+0x3a> 19 401368: eb 25 jmp 40138f <strings_not_equal+0x57> 20 40136a: 3a 45 00 cmp 0x0(%rbp),%al 21 40136d: 0f 1f 00 nopl (%rax) 22 401370: 75 24 jne 401396 <strings_not_equal+0x5e> 23 401372: 48 83 c3 01 add $0x1,%rbx 24 401376: 48 83 c5 01 add $0x1,%rbp 25 40137a: 0f b6 03 movzbl (%rbx),%eax 26 40137d: 84 c0 test %al,%al 27 40137f: 75 e9 jne 40136a <strings_not_equal+0x32> 28 401381: ba 00 00 00 00 mov $0x0,%edx 29 401386: eb 13 jmp 40139b <strings_not_equal+0x63> 30 401388: ba 00 00 00 00 mov $0x0,%edx 31 40138d: eb 0c jmp 40139b <strings_not_equal+0x63> 32 40138f: ba 01 00 00 00 mov $0x1,%edx 33 401394: eb 05 jmp 40139b <strings_not_equal+0x63> 34 401396: ba 01 00 00 00 mov $0x1,%edx 35 40139b: 89 d0 mov %edx,%eax 36 40139d: 5b pop %rbx 37 40139e: 5d pop %rbp 38 40139f: 41 5c pop %r12 39 4013a1: c3 retq
由于 strings_not_equal 函数会用到 string_length 函数,所以将 string_length 函数的反汇编代码一并贴出来:
40 000000000040131b <string_length>: 41 40131b: 80 3f 00 cmpb $0x0,(%rdi) 42 40131e: 74 12 je 401332 <string_length+0x17> 43 401320: 48 89 fa mov %rdi,%rdx 44 401323: 48 83 c2 01 add $0x1,%rdx 45 401327: 89 d0 mov %edx,%eax 46 401329: 29 f8 sub %edi,%eax 47 40132b: 80 3a 00 cmpb $0x0,(%rdx) 48 40132e: 75 f3 jne 401323 <string_length+0x8> 49 401330: f3 c3 repz retq 50 401332: b8 00 00 00 00 mov $0x0,%eax 51 401337: c3 retq
代码 2 ~ 4 行先保存相关的寄存器值。
代码 5 ~ 6 行将传给函数的参数保存进寄存器中。
看到这里,也许能得到两个合理的猜想:
- strings_not_equal 函数用来比较两个字符串是否相等,这个函数的一个参数就是在函数调用前,通过 mov $0x402400,%esi 这条指令来指定,也许 0x402400 这个值就是已经存放在内存中的某个字符串的首地址(只是猜想)。
- strings_not_equal 函数的第二个参数是通过 %rdi 来指定,可能就是我们输入的字符串的首地址。
如果是这样的话,那 0x402400 这个地址处存放的字符串就是 phase_1 的答案。
下面我们通过 GDB 来验证我们的猜想。
首先使用 gdb bomb 来启动我们需要调试的程序 bomb(前提是这个程序由 gcc bomb.c -g -o bomb 生成)。
命令行进入下面的模式:
这是我们再输入:
break explode_bomb
break phase_1
来为程序设置相应的断点。
然后执行 run 来运行,程序会在第一个断点处停下,这时需要我们输入一个字符串,由于只是来验证猜想,先随便输入一个字符串,接着会到达第二个断点处,如下:
接下来我们使用 stepi 命令来单步执行,使用 disas 命令可以查看我们当前执行到什么地方,最后使用 print 命令来查看寄存器相关的信息,如下:
所以字符串Border relations with Canada have never been better.就是 phase_1 最终的答案。
第二关
这是 phase_2 的反汇编代码:
1 0000000000400efc <phase_2>: 2 400efc: 55 push %rbp 3 400efd: 53 push %rbx 4 400efe: 48 83 ec 28 sub $0x28,%rsp 5 400f02: 48 89 e6 mov %rsp,%rsi 6 400f05: e8 52 05 00 00 callq 40145c <read_six_numbers> 7 400f0a: 83 3c 24 01 cmpl $0x1,(%rsp) 8 400f0e: 74 20 je 400f30 <phase_2+0x34> # 满足条件,跳转到 20 行 9 400f10: e8 25 05 00 00 callq 40143a <explode_bomb> # 不满足,直接引爆炸弹 10 400f15: eb 19 jmp 400f30 <phase_2+0x34> 11 400f17: 8b 43 fc mov -0x4(%rbx),%eax 12 400f1a: 01 c0 add %eax,%eax 13 400f1c: 39 03 cmp %eax,(%rbx) 14 400f1e: 74 05 je 400f25 <phase_2+0x29> # 满足条件,跳转到 16 行 15 400f20: e8 15 05 00 00 callq 40143a <explode_bomb> # 不满足,则引爆炸弹 16 400f25: 48 83 c3 04 add $0x4,%rbx 17 400f29: 48 39 eb cmp %rbp,%rbx 18 400f2c: 75 e9 jne 400f17 <phase_2+0x1b> # 满足条件,跳转到 11 行 19 400f2e: eb 0c jmp 400f3c <phase_2+0x40> # 不满足,跳转到 23 行 20 400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx 21 400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp 22 400f3a: eb db jmp 400f17 <phase_2+0x1b> 23 400f3c: 48 83 c4 28 add $0x28,%rsp 24 400f40: 5b pop %rbx 25 400f41: 5d pop %rbp 26 400f42: c3 retq
函数功能分析:
这个函数先保存相应的寄存器(第 2 ~ 3 行),接着为函数分配 0x28 字节的栈帧(第 4 行),
这是 read_six_numbers 的反汇编代码:
1 000000000040145c <read_six_numbers>: 2 40145c: 48 83 ec 18 sub $0x18,%rsp 3 401460: 48 89 f2 mov %rsi,%rdx 4 401463: 48 8d 4e 04 lea 0x4(%rsi),%rcx 5 401467: 48 8d 46 14 lea 0x14(%rsi),%rax 6 40146b: 48 89 44 24 08 mov %rax,0x8(%rsp) 7 401470: 48 8d 46 10 lea 0x10(%rsi),%rax 8 401474: 48 89 04 24 mov %rax,(%rsp) 9 401478: 4c 8d 4e 0c lea 0xc(%rsi),%r9 10 40147c: 4c 8d 46 08 lea 0x8(%rsi),%r8 11 401480: be c3 25 40 00 mov $0x4025c3,%esi 12 401485: b8 00 00 00 00 mov $0x0,%eax 13 40148a: e8 61 f7 ff ff callq 400bf0 <__isoc99_sscanf@plt> 14 40148f: 83 f8 05 cmp $0x5,%eax 15 401492: 7f 05 jg 401499 <read_six_numbers+0x3d> # 满足条件则跳转到 17 行 16 401494: e8 a1 ff ff ff callq 40143a <explode_bomb> 17 401499: 48 83 c4 18 add $0x18,%rsp 18 40149d: c3 retq
函数功能分析:
范德萨发