拆除csapp二进制炸弹超详细的解析之phase_1

概述

以前学习了汇编语言,为了加深巩固,写一个拆除csapp二进制炸弹超详细的解析答案,希望可以帮助其他人吧!

实验所需工具

首先从 http://csapp.cs.cmu.edu/3e/bomb.tar 下载所需的代码。

objdump 这个反汇编器很强大,但是对于这个实验,我们可以不用使用它,因为有一个调试代码神器gdb,可以使用gdb,但有一个更好用的
gdb加强版,cgdb,对我们使用cgdb,这个可以实时的指出我们在调试哪一行指令。

cgdb的安装很简单,参考 https://cgdb.github.io/ 使用cgdb需要掌握一些常用命令。

操作系统为linux。

工具介绍完毕,接下来开始crack the code!

phase_1解析

cgdb1截图

首先在bomb二进制目录中 touch my-input 创建一个文件, my-input里面随便输入一些东西即可,然后r < my-input 开始运行起来

首先执行si命令

80│ bomb.c:
81│ 73          input = read_line();             /* Get input                   */
82│    0x0000000000400e32 <+146>:   callq  0x40149e <read_line>
83│
84│ 74          phase_1(input);                  /* Run the phase               */
85├──> 0x0000000000400e37 <+151>:   mov    %rax,%rdi
86│    0x0000000000400e3a <+154>:   callq  0x400ee0 <phase_1>
87│
88│ 75          phase_defused();                 /* Drat!  They figured it out!
89│    0x0000000000400e3f <+159>:   callq  0x4015c4 <phase_defused>



                            Figure 1.1

Figure 1.1第82行首先调用 read_line() 函数,函数的返回值存储在%rax中,即我们的输入存储在%rax中。

用下列命令可以查看我们的输入内容:

(gdb) x /s $rax
0x603780 <input_strings>:       "you are phase_1 !!!"

在第85行中,把%rax的值复制到%rdi中。然后第86行调用,我们跟进去看看

 1│ Dump of assembler code for function phase_1:
 2├──> 0x0000000000400ee0 <+0>:     sub    $0x8,%rsp
 3│    0x0000000000400ee4 <+4>:     mov    $0x402400,%esi
 4│    0x0000000000400ee9 <+9>:     callq  0x401338 <strings_not_equal>
 5│    0x0000000000400eee <+14>:    test   %eax,%eax
 6│    0x0000000000400ef0 <+16>:    je     0x400ef7 <phase_1+23>
 7│    0x0000000000400ef2 <+18>:    callq  0x40143a <explode_bomb>
 8│    0x0000000000400ef7 <+23>:    add    $0x8,%rsp
 9│    0x0000000000400efb <+27>:    retq
10│ End of assembler dump.



                            Figure 1.2

可以看到 Figure 1.2中 第2行栈地址减少8,刚好是两个4字节的大小,第3行把一个常量地址的值复制到%esi,特别要注意0x402400这个常
量地址,如果了解游戏外挂制作的话,分析游戏首先要找基地址,找不到基地址下面的就没法谈了,所谓的基地址就是在游戏设计开发中
写死的值,一般为全局常量,是不会发生改变的,即使在游戏重新启动的情况下。所以可以大胆地猜测这个$0x402400里面存放的值就是
本关卡的答案,可以看一下里面存放的值:

(gdb) x /s 0x402400
0x402400:       "Border relations with Canada have never been better."

可以看到里面是字符 Border relations with Canada have never been better. 可能就是我们想要的答案,但这仅仅是猜测,还需要我们去验证。

第4行调用 strings_not_equal 去判断我们输入的字符串是否和程序中写死的全局变量中存放的字符串相等,如果相等则eax的值等于0,接
下来的第5行判断eax是否等于0, je表示相等则执行0x400ef7处的代码,第8行 add $0x8,%rsp 的作用是恢复栈平衡,恢复栈平衡一般有
两种方法,一种是内平衡,一种是外平衡。第8行用的是内平衡,即在函数调用中恢复栈平衡叫做内平衡,在函数调用之外恢复栈平衡叫做
外平衡。本例中的内平衡相当与c语言中的stdcall。

基于上述的推理,我们可以大胆的猜测: 判断函数 strings_not_equal 中用到了%rdi和%esi,其中%rdi是存储我们输入的内容,%esi是本关卡
的答案,如果我们是输入和本关卡中预先设置的答案一样则炸弹不会爆炸,即 Figure 1.2 第4行的返回值eax为0.

当你进入strings_not_equal中时会发现最前面的几行如下:

1│ Dump of assembler code for function strings_not_equal:
2├──> 0x0000000000401338 <+0>:     push   %r12
3│    0x000000000040133a <+2>:     push   %rbp
4│    0x000000000040133b <+3>:     push   %rbx
5│    0x000000000040133c <+4>:     mov    %rdi,%rbx
6│    0x000000000040133f <+7>:     mov    %rsi,%rbp
7│    0x0000000000401342 <+10>:    callq  0x40131b <string_length>
.......
.......
.......



                     Figure 1.3

在Figure 1.3中第5行的 mov %rdi,%rbx 和第6行 mov %rsi,%rbp,再加上第7行,现在完全可以肯定我们上面的推论都是正确的。

至此得到 phase_1 的答案为Border relations with Canada have never been better.