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

概述

接着 拆除csapp二进制炸弹超详细的解析之phase_3 继续解析phase_4 。

cgdb4截图

phase_4解析

同样地首先执行si命令:

128│ bomb.c:
129│ 94          input = read_line();
130├──> 0x0000000000400e86 <+230>:   callq  0x40149e <read_line>
131│
132│ 95          phase_4(input);
133│    0x0000000000400e8b <+235>:   mov    %rax,%rdi
134│    0x0000000000400e8e <+238>:   callq  0x40100c <phase_4>
135│
136│ 96          phase_defused();
137│    0x0000000000400e93 <+243>:   callq  0x4015c4 <phase_defused>



                    Figure 4.1

Figure 4.1中第130行读取我们的输入,返回值rax在第133行被复制给rdi.可以查看我们输入的内容:

(gdb) x /s $rax
0x603870 <input_strings+240>:   "I'm in phase_4 for test"

我们输入的内容是 I’m in phase_4 for test ,确实是这样的。

接下来si命令进入第134行:

 1│ Dump of assembler code for function phase_4:
 2├──> 0x000000000040100c <+0>:     sub    $0x18,%rsp
 3│    0x0000000000401010 <+4>:     lea    0xc(%rsp),%rcx
 4│    0x0000000000401015 <+9>:     lea    0x8(%rsp),%rdx
 5│    0x000000000040101a <+14>:    mov    $0x4025cf,%esi
 6│    0x000000000040101f <+19>:    mov    $0x0,%eax
 7│    0x0000000000401024 <+24>:    callq  0x400bf0 <__isoc99_sscanf@plt>
 8│    0x0000000000401029 <+29>:    cmp    $0x2,%eax
 9│    0x000000000040102c <+32>:    jne    0x401035 <phase_4+41>
10│    0x000000000040102e <+34>:    cmpl   $0xe,0x8(%rsp)
11│    0x0000000000401033 <+39>:    jbe    0x40103a <phase_4+46>
12│    0x0000000000401035 <+41>:    callq  0x40143a <explode_bomb>
13│    0x000000000040103a <+46>:    mov    $0xe,%edx
14│    0x000000000040103f <+51>:    mov    $0x0,%esi
15│    0x0000000000401044 <+56>:    mov    0x8(%rsp),%edi
16│    0x0000000000401048 <+60>:    callq  0x400fce <func4>
17│    0x000000000040104d <+65>:    test   %eax,%eax
18│    0x000000000040104f <+67>:    jne    0x401058 <phase_4+76>
19│    0x0000000000401051 <+69>:    cmpl   $0x0,0xc(%rsp)
20│    0x0000000000401056 <+74>:    je     0x40105d <phase_4+81>
21│    0x0000000000401058 <+76>:    callq  0x40143a <explode_bomb>
22│    0x000000000040105d <+81>:    add    $0x18,%rsp
23│    0x0000000000401061 <+85>:    retq
24│ End of assembler dump.



                   Figure 4.2

在Figure 4.2中,第2行的十六进制0x18的十进制值是24,所以第2行的结果是开辟24字节的内存空间。第3行把地址rsp本身的地址编号的
值加上十六进制c(十进制的12)的结果存放到rcx,第4行把地址rsp本身的地址编号的值加上十六进制8(十进制的8)的结果存放到rdx中。

第5行指令中又一次出现了常量地址,之前说过常量地址里面的内容很重要,查看下0x4025cf里面的内容:

(gdb) x /s 0x4025cf
0x4025cf:       "%d %d"

第6行把eax的值设置为0,第7行调用sscanf函数读取输入,第8行判断sscanf的返回值eax是否等于2,如果不等于2,则跳转到第12行,触发
炸弹爆炸机制。这道题和phase_3相似,但比它简单一些。由于我们输入的内容是 I’m in phase_4 for test ,不是两个整数,所以我们
把我们的输入改为2 3,再次执行程序。

当输入的内容刚好是两个整数时,执行第10行比较第1个参数和十六进制e(十进制14)的大小,当小于或者等于14时,炸弹不爆炸,执行
第13行,把十进制14复制给edx寄存器,第14行把esi的值置为0,第15行把第1个参数复制给edi,第16行调用func4函数,我们进入func4:

 1│ Dump of assembler code for function func4:
 2├──> 0x0000000000400fce <+0>:     sub    $0x8,%rsp
 3│    0x0000000000400fd2 <+4>:     mov    %edx,%eax
 4│    0x0000000000400fd4 <+6>:     sub    %esi,%eax
 5│    0x0000000000400fd6 <+8>:     mov    %eax,%ecx
 6│    0x0000000000400fd8 <+10>:    shr    $0x1f,%ecx
 7│    0x0000000000400fdb <+13>:    add    %ecx,%eax
 8│    0x0000000000400fdd <+15>:    sar    %eax
 9│    0x0000000000400fdf <+17>:    lea    (%rax,%rsi,1),%ecx
10│    0x0000000000400fe2 <+20>:    cmp    %edi,%ecx
11│    0x0000000000400fe4 <+22>:    jle    0x400ff2 <func4+36>
12│    0x0000000000400fe6 <+24>:    lea    -0x1(%rcx),%edx
13│    0x0000000000400fe9 <+27>:    callq  0x400fce <func4>
14│    0x0000000000400fee <+32>:    add    %eax,%eax
15│    0x0000000000400ff0 <+34>:    jmp    0x401007 <func4+57>
16│    0x0000000000400ff2 <+36>:    mov    $0x0,%eax
17│    0x0000000000400ff7 <+41>:    cmp    %edi,%ecx
18│    0x0000000000400ff9 <+43>:    jge    0x401007 <func4+57>
19│    0x0000000000400ffb <+45>:    lea    0x1(%rcx),%esi
20│    0x0000000000400ffe <+48>:    callq  0x400fce <func4>
21│    0x0000000000401003 <+53>:    lea    0x1(%rax,%rax,1),%eax
22│    0x0000000000401007 <+57>:    add    $0x8,%rsp
23│    0x000000000040100b <+61>:    retq
24│ End of assembler dump.



            Figure 4.3

在 Figure 4.3 中,第1行先开辟8字节空间,然后把edx里面的十进制14复制到eax,第4行从eax减去esi(十进制0)得到的结果放入eax中,
第5行把eax的值(现在为14),复制到ecx中,第6行的shr是逻辑右移(sar是算术右移),十六进制0x1f等于十进制31,ecx现在的值为十进制
14,二进制表示为1110,1110右移31位后变为0,即ecx里面现在是32个0,第7行的结果是eax现在为14,第8行的sar执行算术右移1位,即
1110右移1位,变为二进制的111(十进制为7),现在eax的值变为7。第9行rax的值为7,rsi的值为0,所以ecx的值为7。

上面提到edi里面存储的是我们的第一个参数,第10行比较ecx里面的值7和我们输入的第一个参数的大小,第11行jle表示如果7小于或等于
我们输入的第一个参数,则跳转到第16行,把eax设置为0,然后比较ecx和edi的大小,第18行的jge表示ecx大于或等于edi时跳转到第22
行,从func4这个函数返回,否则继续执行递归.由此我们可以得到第1个参数可以为7,如果第1个参数为7,则func4函数返回0。这时我们
又回到了phase_4函数的第17行,如下面的 Figure 4.4 所示:

15│    0x0000000000401044 <+56>:    mov    0x8(%rsp),%edi
16│    0x0000000000401048 <+60>:    callq  0x400fce <func4>
17│    0x000000000040104d <+65>:    test   %eax,%eax
18│    0x000000000040104f <+67>:    jne    0x401058 <phase_4+76>
19│    0x0000000000401051 <+69>:    cmpl   $0x0,0xc(%rsp)
20│    0x0000000000401056 <+74>:    je     0x40105d <phase_4+81>
21│    0x0000000000401058 <+76>:    callq  0x40143a <explode_bomb>
22│    0x000000000040105d <+81>:    add    $0x18,%rsp
23│    0x0000000000401061 <+85>:    retq
24│ End of assembler dump.



               Figure 4.4

当我们的第一个参数是7时,func4的返回值eax为0,第18行的jne表示eax不等于0时,则跳转到 Figure 4.4 中的第21行,触发炸弹爆炸机
制,不过当第一个参数是7时,eax为0,不会触发炸弹爆炸机制,此时跳转到 Figure 4.4 中的第19行,判断第2个参数是否为0,如果第2
个参数等于0,则跳转到 Figure 4.4 中的第22行,接下来从phase_4中安全返回。

所以我们得到本关卡的答案为 7 0