第三个炸弹

2025-05-18
1170字

phase_3的汇编代码比较长,我们一段一段看:

1sub  $0x18,%rsp                   ;400f43
2lea  0xc(%rsp),%rcx               ;400f47
3lea  0x8(%rsp),%rdx               ;400f4c
4mov  $0x4025cf,%esi               ;400f51
5mov  $0x0,%eax                    ;400f56
6call 400bf0 <__isoc99_sscanf@plt> ;400f5b
7cmp  $0x1,%eax                    ;400f60
8jg   400f6a <phase_3+0x27>        ;400f63
9call 40143a <explode_bomb>        ;400f65

这段和phase_2的逻辑一致。用x /s 0x4025cf看一下格式化字符串:%d %d,说明这段代码就是读取两个int分别放到0x8(%rsp)0xc(%rsp)的位置,sscanf之后也校验了一下是否成功读取到两个数字。

接下来的两行代码:

10cmpl $0x7,0x8(%rsp)               ;400f6a
11ja   400fad <phase_3+0x6a>        ;400f6f

意思是第一个数字和数字7进行比较,如果第一个数字大于7,则跳转到400fad引爆炸弹(ja的意思是Jump if Above)。

接下来的两行代码:

12mov  0x8(%rsp),%eax               ;400f71
13jmp  *0x402470(,%rax,8)           ;400f75

将第一个数字放到%eax中,因为第一个数字是4字节的int,所以用了%eax而不是%rax,然后进行跳转。跳转指令中*的含义的C语言中的取内容符含义一致,也就是跳转到指针0x402470(,%rax,8)指向的地方。

0x402470(,%rax,8)的含义是disp(base,index,scale),表示base + disp + index * scalebase缺省为0。在mov 0x8(%rsp),%eax中,%rax的高32位会被补0,所以在第一个数字非负的情况下,这里的%rax保存的就是第一个数字。在64位系统中一个指针占8个字节,所以scale是8。

也就是说,这条指令会根据第一个数字,跳转到不同的地方执行指令。这是一个很明显的switch case语句。

我们已经知道第一个数字的取值范围是0-7,所以只需要查看从0x402470开始的8个指针分别指向哪里,就知道这个8个case分别对应什么了(8gx:以x(十六进制)形式查看8个元素,每个元素是一个giant word(8字节)):

(gdb) x/8gx 0x402470
0x402470: 0x0000000000400f7c 0x0000000000400fb9
0x402480: 0x0000000000400f83 0x0000000000400f8a
0x402490: 0x0000000000400f91 0x0000000000400f98
0x4024a0: 0x0000000000400f9f 0x0000000000400fa6

好巧(不巧才怪了),对应的就是phase_3接下来的一些代码:

14mov  $0xcf,%eax                   ;400f7c 对应0
15jmp  400fbe <phase_3+0x7b>        ;400f81
16mov  $0x2c3,%eax                  ;400f83 对应2
17jmp  400fbe <phase_3+0x7b>        ;400f88
18mov  $0x100,%eax                  ;400f8a 对应3
19jmp  400fbe <phase_3+0x7b>        ;400f8f
20mov  $0x185,%eax                  ;400f91 对应4
21jmp  400fbe <phase_3+0x7b>        ;400f96
22mov  $0xce,%eax                   ;400f98 对应5
23jmp  400fbe <phase_3+0x7b>        ;400f9d
24mov  $0x2aa,%eax                  ;400f9f 对应6
25jmp  400fbe <phase_3+0x7b>        ;400fa4
26mov  $0x147,%eax                  ;400fa6 对应7
27jmp  400fbe <phase_3+0x7b>        ;400fab
28call 40143a <explode_bomb>        ;400fad
29mov  $0x0,%eax                    ;400fb2
30jmp  400fbe <phase_3+0x7b>        ;400fb7
31mov  $0x137,%eax                  ;400fb9 对应1
32cmp  0xc(%rsp),%eax               ;400fbe
33je   400fc9 <phase_3+0x86>        ;400fc2
34call 40143a <explode_bomb>        ;400fc4
35add  $0x18,%rsp                   ;400fc9
36ret                               ;400fcd

可以看到所有case都是把一个数字放到%eax中,然后跳转到400fbe这条指令(1对应的这个case下面一条指令就是400fbe,所以它不用跳转,顺序执行就行了),而这条指令以及之后几条指令的意思是:如果%eax的值和第二个数字相等,就跳过炸弹引爆函数。

结合每个case中放入%eax的数字,我们很容易就可以得到第一个数字和第二个数字正确的对应关系:

第一个数字 第二个数字
0 0xcf = 12 * 16 + 15 = 207
1 0x137 = 1 * 256 + 3 * 16 + 7 = 311
2 0x2c3 = 707
3 0x100 = 256
4 0x185 = 389
5 0xce = 206
6 0x2aa = 682
7 0x147 = 327

随便挑选一组(例如3 256)放到answer.txt的第三行,然后./bomb answer.txt验证:

Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Phase 1 defused. How about the next one?
That's number 2.  Keep going!
Halfway there!

BOOM!!!
The bomb has blown up.