Bomb Lab

操作系统:linux

调试工具:gdb

Preparation before the lab

实验由phase_1 ~ phase_6组成,共6个阶段,
我们需要分别输入6个字符串密码,以便解除Dr. Evil的炸弹,
但只要有1个字符串输错,炸弹就会爆炸

那么如何知道6个正确的字符串呢?
我们需要分别查看phase_1 ~ phase_6的汇编代码,
而炸弹就存放在其中,我们要根据汇编代码小心地避开炸弹,分析出正确的字符串即可。
而分析需要用到学习的汇编知识常用gdb指令以及linux终端指令

需要用到的linux终端指令
别忘了先进入文件地址中

  • objdump -d bomb > assembly.s
    反编译出bomb.c的汇编代码,并存放在assembly.s中(assembly.s是自己命名的)

  • gdb 文件名
    用gdb调试工具打开某个文件
    eg. gdb bomb 用gdb调试工具打开bomb.c文件
    我们就是要用gdb调试工具打开bomb.c文件后做实验

常用gdb指令:
gdb指令必须用gdb调试工具打开文件后才能调用

  • run (可简写为:r)
    运行文件

  • quit (可简写为:q)
    停止运行,跳出gdb调试工具

  • break 函数名 (可简写为:b 函数名)
    在某个函数处打断点,以便之后用disas展示这个函数的汇编代码。
    eg. b phase_1 在phase_1函数打断点

  • disas 函数名
    展示这个函数的汇编代码(但必须先打断点)
    eg. disas phase_1 展示phase_1的汇编代码

  • x/s 明码或者寄存器
    用以访问明码或者寄存器对应内容
    eg1. x/s 0x40139b 访问0x40139b对应内容
    eg2. x/s $rsi 访问%rsi对应内容


正式分析之前需要注意到一点,我们输入的字符串都是放入到%rdi当中的。
(其实一般情况下,输入也是放到%rdi的)
也就是说,在每个phase调用时,默认%rdi这个寄存器存放着输入的字符串。
如何得到这点的呢?是通过分析main函数的汇编代码得出的。

  # main part
   0x0000000000400e32 <+146>:	callq  0x40149e <read_line> 
   0x0000000000400e37 <+151>:	mov    %rax,%rdi # input store %rdi
   0x0000000000400e3a <+154>:	callq  0x400ee0 <phase_1>
   0x0000000000400e3f <+159>:	callq  0x4015c4 <phase_defused>
   0x0000000000400e44 <+164>:	mov    $0x4023a8,%edi
   0x0000000000400e49 <+169>:	callq  0x400b10 <puts@plt>
   0x0000000000400e4e <+174>:	callq  0x40149e <read_line>
   0x0000000000400e53 <+179>:	mov    %rax,%rdi # input store %rdi
   0x0000000000400e56 <+182>:	callq  0x400efc <phase_2>
   0x0000000000400e5b <+187>:	callq  0x4015c4 <phase_defused>
   0x0000000000400e60 <+192>:	mov    $0x4022ed,%edi
   0x0000000000400e65 <+197>:	callq  0x400b10 <puts@plt>
   0x0000000000400e6a <+202>:	callq  0x40149e <read_line>
   0x0000000000400e6f <+207>:	mov    %rax,%rdi # input store %rdi
   0x0000000000400e72 <+210>:	callq  0x400f43 <phase_3>
   0x0000000000400e77 <+215>:	callq  0x4015c4 <phase_defused>
   0x0000000000400e7c <+220>:	mov    $0x40230b,%edi
   0x0000000000400e81 <+225>:	callq  0x400b10 <puts@plt>
   0x0000000000400e86 <+230>:	callq  0x40149e <read_line>
   0x0000000000400e8b <+235>:	mov    %rax,%rdi # input store %rdi
   0x0000000000400e8e <+238>:	callq  0x40100c <phase_4>
   0x0000000000400e93 <+243>:	callq  0x4015c4 <phase_defused>
   0x0000000000400e98 <+248>:	mov    $0x4023d8,%edi
   0x0000000000400e9d <+253>:	callq  0x400b10 <puts@plt>
   0x0000000000400ea2 <+258>:	callq  0x40149e <read_line>
   0x0000000000400ea7 <+263>:	mov    %rax,%rdi # input store %rdi
   0x0000000000400eaa <+266>:	callq  0x401062 <phase_5>
   0x0000000000400eaf <+271>:	callq  0x4015c4 <phase_defused>
   0x0000000000400eb4 <+276>:	mov    $0x40231a,%edi
   0x0000000000400eb9 <+281>:	callq  0x400b10 <puts@plt>
   0x0000000000400ebe <+286>:	callq  0x40149e <read_line>
   0x0000000000400ec3 <+291>:	mov    %rax,%rdi # input store %rdi
   0x0000000000400ec6 <+294>:	callq  0x4010f4 <phase_6>
   0x0000000000400ecb <+299>:	callq  0x4015c4 <phase_defused>
   0x0000000000400ed0 <+304>:	mov    $0x0,%eax
   0x0000000000400ed5 <+309>:	pop    %rbx
   0x0000000000400ed6 <+310>:	retq   

分析:我们可以看到,在每个phase调用前,都callq 0x40149e <read_line>,然后mov %rax,%rdi,这样子会将我们输入的字符串最终存到%rdi中。(我们是把字符串输入到read_line的,而read_line会返回到%rax中,mov操作将%rax存到%rdi注意%rax往往用来放返回值)

至于read_line的汇编代码我就不分析了,通过名字也可以看出:它是用来返回我们的输入值的

下面开始分析phase_1 ~ phase_6的汇编代码,
得出认为正确的字符串,并逐个解除炸弹

phase_1

查看phase_1的汇编:

   0x0000000000400ee0 <+0>:	sub    $0x8,%rsp
   0x0000000000400ee4 <+4>:	mov    $0x402400,%esi
   0x0000000000400ee9 <+9>:	callq  0x401338 <strings_not_equal>
   0x0000000000400eee <+14>:	test   %eax,%eax
   0x0000000000400ef0 <+16>:	je     0x400ef7 <phase_1+23>
   0x0000000000400ef2 <+18>:	callq  0x40143a <explode_bomb>
   0x0000000000400ef7 <+23>:	add    $0x8,%rsp
   0x0000000000400efb <+27>:	retq   

查看string_not_equal的汇编:

  #string_not_equal
   0x0000000000401338 <+0>:     push   %r12 # 栈帧相关
   0x000000000040133a <+2>:     push   %rbp # 栈帧相关
   0x000000000040133b <+3>:     push   %rbx # 栈帧相关
   0x000000000040133c <+4>:     mov    %rdi,%rbx # 第一个参数保存到%rbx
   0x000000000040133f <+7>:     mov    %rsi,%rbp # 第二个参数保存到%rbp
   0x0000000000401342 <+10>:	callq  0x40131b <string_length> # 以第一个参数作为入参,调用函数
   0x0000000000401347 <+15>:	mov    %eax,%r12d # 返回值保存到%r12d
   0x000000000040134a <+18>:	mov    %rbp,%rdi # 把第二个参数作为入参
   0x000000000040134d <+21>:	callq  0x40131b <string_length> # 调用函数
   0x0000000000401352 <+26>:	mov    $0x1,%edx # 将1赋值给%edx,作为strings_not_equal的返回结果预备值
   0x0000000000401357 <+31>:	cmp    %eax,%r12d # 比较两个参数分别作为入参的调用结果
   0x000000000040135a <+34>:	jne    0x40139b <strings_not_equal+99> # 不相等时跳转
   0x000000000040135c <+36>:	movzbl (%rbx),%eax # 长度相等时,对(%rbx)内存的值付给%eax
   0x000000000040135f <+39>:	test   %al,%al
   0x0000000000401361 <+41>:	je     0x401388 <strings_not_equal+80> # 如果这个值为0,即字符串的结束字符,则跳转
   0x0000000000401363 <+43>:	cmp    0x0(%rbp),%al # 对(%rbp)取值,和%al做比较;此时(%rbp)是内存的值,%al是我们输入的字符串的第一个字符
   0x0000000000401366 <+46>:	je     0x401372 <strings_not_equal+58> # 相等跳转
   0x0000000000401368 <+48>:	jmp    0x40138f <strings_not_equal+87> # 不相等跳转
   0x000000000040136a <+50>:	cmp    0x0(%rbp),%al # 比较%rbp和%rbx
   0x000000000040136d <+53>:	nopl   (%rax)
   0x0000000000401370 <+56>:	jne    0x401396 <strings_not_equal+94>
   0x0000000000401372 <+58>:	add    $0x1,%rbx # 第一个参数指针+1
   0x0000000000401376 <+62>:	add    $0x1,%rbp # 第二个参数指针+1
   0x000000000040137a <+66>:	movzbl (%rbx),%eax
   0x000000000040137d <+69>:	test   %al,%al # 若%al为空即0,结束循环
   0x000000000040137f <+71>:	jne    0x40136a <strings_not_equal+50> # 重新进行字符比对
   0x0000000000401381 <+73>:	mov    $0x0,%edx
   0x0000000000401386 <+78>:	jmp    0x40139b <strings_not_equal+99>
   0x0000000000401388 <+80>:	mov    $0x0,%edx
   0x000000000040138d <+85>:	jmp    0x40139b <strings_not_equal+99>
   0x000000000040138f <+87>:	mov    $0x1,%edx
   0x0000000000401394 <+92>:	jmp    0x40139b <strings_not_equal+99> 
   0x0000000000401396 <+94>:	mov    $0x1,%edx
   0x000000000040139b <+99>:	mov    %edx,%eax # 将返回结果预备值赋值(0或1)给%eax,准备返回
   0x000000000040139d <+101>:	pop    %rbx
   0x000000000040139e <+102>:	pop    %rbp
   0x000000000040139f <+103>:	pop    %r12
   0x00000000004013a1 <+105>:	retq    

未完续待~