0%

【CSAPP#0x00】CSAPP:Lab2 BombLab

寒假学的时候没做,现在补票来做一下这个(看似)六阶段的炸弹程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
00000000004015c4 <phase_defused>:
4015c4: 48 83 ec 78 sub $0x78,%rsp
4015c8: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
4015cf: 00 00
4015d1: 48 89 44 24 68 mov %rax,0x68(%rsp)
4015d6: 31 c0 xor %eax,%eax
4015d8: 83 3d 81 21 20 00 06 cmpl $0x6,0x202181(%rip) # 603760 <num_input_strings>
4015df: 75 5e jne 40163f <phase_defused+0x7b>
4015e1: 4c 8d 44 24 10 lea 0x10(%rsp),%r8
4015e6: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx
4015eb: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
4015f0: be 19 26 40 00 mov $0x402619,%esi
4015f5: bf 70 38 60 00 mov $0x603870,%edi
4015fa: e8 f1 f5 ff ff callq 400bf0 <__isoc99_sscanf@plt>
4015ff: 83 f8 03 cmp $0x3,%eax
401602: 75 31 jne 401635 <phase_defused+0x71>
401604: be 22 26 40 00 mov $0x402622,%esi
401609: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
40160e: e8 25 fd ff ff callq 401338 <strings_not_equal>
401613: 85 c0 test %eax,%eax
401615: 75 1e jne 401635 <phase_defused+0x71>
401617: bf f8 24 40 00 mov $0x4024f8,%edi
40161c: e8 ef f4 ff ff callq 400b10 <puts@plt>
401621: bf 20 25 40 00 mov $0x402520,%edi
401626: e8 e5 f4 ff ff callq 400b10 <puts@plt>
40162b: b8 00 00 00 00 mov $0x0,%eax
401630: e8 0d fc ff ff callq 401242 <secret_phase>
401635: bf 58 25 40 00 mov $0x402558,%edi
40163a: e8 d1 f4 ff ff callq 400b10 <puts@plt>
40163f: 48 8b 44 24 68 mov 0x68(%rsp),%rax
401644: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
40164b: 00 00
40164d: 74 05 je 401654 <phase_defused+0x90>
40164f: e8 dc f4 ff ff callq 400b30 <__stack_chk_fail@plt>
401654: 48 83 c4 78 add $0x78,%rsp
401658: c3 retq
401659: 90 nop
40165a: 90 nop
40165b: 90 nop
40165c: 90 nop
40165d: 90 nop
40165e: 90 nop
40165f: 90 nop

Phase 1

1
2
3
4
5
6
7
8
9
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

似乎是和位于0x402400的一个字符串进行比较。

strings -t x ./bomb 打印所有字符串,找到如下一条:

1
2400 Border relations with Canada have never been better.

于是通过本阶段。

Phase 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
0000000000400efc <phase_2>:
400efc: 55 push %rbp
400efd: 53 push %rbx
400efe: 48 83 ec 28 sub $0x28,%rsp
400f02: 48 89 e6 mov %rsp,%rsi
400f05: e8 52 05 00 00 callq 40145c <read_six_numbers>
400f0a: 83 3c 24 01 cmpl $0x1,(%rsp)
400f0e: 74 20 je 400f30 <phase_2+0x34>
400f10: e8 25 05 00 00 callq 40143a <explode_bomb>
400f15: eb 19 jmp 400f30 <phase_2+0x34>
400f17: 8b 43 fc mov -0x4(%rbx),%eax
400f1a: 01 c0 add %eax,%eax
400f1c: 39 03 cmp %eax,(%rbx)
400f1e: 74 05 je 400f25 <phase_2+0x29>
400f20: e8 15 05 00 00 callq 40143a <explode_bomb>
400f25: 48 83 c3 04 add $0x4,%rbx
400f29: 48 39 eb cmp %rbp,%rbx
400f2c: 75 e9 jne 400f17 <phase_2+0x1b>
400f2e: eb 0c jmp 400f3c <phase_2+0x40>
400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx
400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp
400f3a: eb db jmp 400f17 <phase_2+0x1b>
400f3c: 48 83 c4 28 add $0x28,%rsp
400f40: 5b pop %rbx
400f41: 5d pop %rbp
400f42: c3 retq

似乎是先在栈上读入六个数字,然后首先检查第一个数字是不是1,然后进行一个循环。
循环初始条件在0x400f30处设置,%rbx为第二个数字地址,%rbp为第七个数字地址(实际上没有)。
循环体是0x400f17到0x400f20,检查%rbx的前一个数字乘以2后是否与%rbx当前所指数字相等。如果不相等就引爆,相等就进入循环条件判断部分。
循环条件判断部分是0x400f25到0x400f2e,让%rbx指向下一个数字以后和%rbp比较,如果一样说明循环结束,跳到函数结尾(0x400f3c);不一样说明循环继续,再次跳到循环体的开头0x400f17。

所以输入数字:1 2 4 8 16 32,通过本阶段。

Phase 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
0000000000400f43 <phase_3>:
400f43: 48 83 ec 18 sub $0x18,%rsp
400f47: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx
400f4c: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
400f51: be cf 25 40 00 mov $0x4025cf,%esi
400f56: b8 00 00 00 00 mov $0x0,%eax
400f5b: e8 90 fc ff ff callq 400bf0 <__isoc99_sscanf@plt>
400f60: 83 f8 01 cmp $0x1,%eax
400f63: 7f 05 jg 400f6a <phase_3+0x27>
400f65: e8 d0 04 00 00 callq 40143a <explode_bomb>
400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp)
400f6f: 77 3c ja 400fad <phase_3+0x6a>
400f71: 8b 44 24 08 mov 0x8(%rsp),%eax
400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8)
400f7c: b8 cf 00 00 00 mov $0xcf,%eax
400f81: eb 3b jmp 400fbe <phase_3+0x7b>
400f83: b8 c3 02 00 00 mov $0x2c3,%eax
400f88: eb 34 jmp 400fbe <phase_3+0x7b>
400f8a: b8 00 01 00 00 mov $0x100,%eax
400f8f: eb 2d jmp 400fbe <phase_3+0x7b>
400f91: b8 85 01 00 00 mov $0x185,%eax
400f96: eb 26 jmp 400fbe <phase_3+0x7b>
400f98: b8 ce 00 00 00 mov $0xce,%eax
400f9d: eb 1f jmp 400fbe <phase_3+0x7b>
400f9f: b8 aa 02 00 00 mov $0x2aa,%eax
400fa4: eb 18 jmp 400fbe <phase_3+0x7b>
400fa6: b8 47 01 00 00 mov $0x147,%eax
400fab: eb 11 jmp 400fbe <phase_3+0x7b>
400fad: e8 88 04 00 00 callq 40143a <explode_bomb>
400fb2: b8 00 00 00 00 mov $0x0,%eax
400fb7: eb 05 jmp 400fbe <phase_3+0x7b>
400fb9: b8 37 01 00 00 mov $0x137,%eax
400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax
400fc2: 74 05 je 400fc9 <phase_3+0x86>
400fc4: e8 71 04 00 00 callq 40143a <explode_bomb>
400fc9: 48 83 c4 18 add $0x18,%rsp
400fcd: c3 retq

开头先scanf(“%d %d”, %rsp+8, %rsp+c),读入两个数字到栈上。如果读取成功数小于2个则立刻引爆。(其实是调用了sscanf,但是差不多)
然后检查第一个数,如果作为无符号数大于7的话立刻引爆。

然后尝试跳转到 qword ptr \[rax\*8 + 0x402470] ,用gdb查看此处是啥:

1
2
3
4
5
pwndbg> x/7gx 0x402470
0x402470: 0x0000000000400f7c 0x0000000000400fb9
0x402480: 0x0000000000400f83 0x0000000000400f8a
0x402490: 0x0000000000400f91 0x0000000000400f98
0x4024a0: 0x0000000000400f9f

可以发现是一些phase_3函数的地址。这些地址对应的指令都是一个格式,给%rax赋个值然后跳转到0x400fbe,把这个值和读入的第二个数进行比较,相同就正常ret(过关)。
所以这个阶段有8种解法(应该要换成十进制数):
0 0xcf
1 0x137
2 0x2c3
3 0x100
4 0x185
5 0xce
6 0x2aa
7 0x147

Phase 4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
000000000040100c <phase_4>:
40100c: 48 83 ec 18 sub $0x18,%rsp
401010: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx
401015: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
40101a: be cf 25 40 00 mov $0x4025cf,%esi
40101f: b8 00 00 00 00 mov $0x0,%eax
401024: e8 c7 fb ff ff callq 400bf0 <__isoc99_sscanf@plt>
401029: 83 f8 02 cmp $0x2,%eax
40102c: 75 07 jne 401035 <phase_4+0x29>
40102e: 83 7c 24 08 0e cmpl $0xe,0x8(%rsp)
401033: 76 05 jbe 40103a <phase_4+0x2e>
401035: e8 00 04 00 00 callq 40143a <explode_bomb>
40103a: ba 0e 00 00 00 mov $0xe,%edx
40103f: be 00 00 00 00 mov $0x0,%esi
401044: 8b 7c 24 08 mov 0x8(%rsp),%edi
401048: e8 81 ff ff ff callq 400fce <func4>
40104d: 85 c0 test %eax,%eax
40104f: 75 07 jne 401058 <phase_4+0x4c>
401051: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp)
401056: 74 05 je 40105d <phase_4+0x51>
401058: e8 dd 03 00 00 callq 40143a <explode_bomb>
40105d: 48 83 c4 18 add $0x18,%rsp
401061: c3 retq

0000000000400fce <func4>:
400fce: 48 83 ec 08 sub $0x8,%rsp
400fd2: 89 d0 mov %edx,%eax
400fd4: 29 f0 sub %esi,%eax
400fd6: 89 c1 mov %eax,%ecx
400fd8: c1 e9 1f shr $0x1f,%ecx
400fdb: 01 c8 add %ecx,%eax
400fdd: d1 f8 sar %eax
400fdf: 8d 0c 30 lea (%rax,%rsi,1),%ecx
400fe2: 39 f9 cmp %edi,%ecx
400fe4: 7e 0c jle 400ff2 <func4+0x24>
400fe6: 8d 51 ff lea -0x1(%rcx),%edx
400fe9: e8 e0 ff ff ff callq 400fce <func4>
400fee: 01 c0 add %eax,%eax
400ff0: eb 15 jmp 401007 <func4+0x39>
400ff2: b8 00 00 00 00 mov $0x0,%eax
400ff7: 39 f9 cmp %edi,%ecx
400ff9: 7d 0c jge 401007 <func4+0x39>
400ffb: 8d 71 01 lea 0x1(%rcx),%esi
400ffe: e8 cb ff ff ff callq 400fce <func4>
401003: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
401007: 48 83 c4 08 add $0x8,%rsp
40100b: c3 retq

首先还是先scanf(“%d %d”, %rsp+8, %rsp+c),读入两个数字到栈上。如果读取成功数不是2个则立刻引爆。
然后比较第一个数和0xe,要小于等于0xe,否则立即引爆。

然后调用了func4((%rsp+8), 0, 0xe)并检查返回值是否为0,不是就引爆。

想要让func4返回值为0,则必须在0x400fe4处成功跳转到0x400ff2。这就要求:
(edx-esi最高位 + edx-esi) / 2 + esi == edi

其中只有edi是由我们自己控制的,所以计算上式得到edi应该为7。

返回后,比较第二个参数是否为0,不是就引爆。
然后本阶段就结束了,答案是7 0。

Phase 5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
pwndbg> disassemble phase_5
Dump of assembler code for function phase_5:
0x0000000000401062 <+0>: push rbx
0x0000000000401063 <+1>: sub rsp,0x20
0x0000000000401067 <+5>: mov rbx,rdi
0x000000000040106a <+8>: mov rax,QWORD PTR fs:0x28
0x0000000000401073 <+17>: mov QWORD PTR [rsp+0x18],rax
0x0000000000401078 <+22>: xor eax,eax
0x000000000040107a <+24>: call 0x40131b <string_length>
0x000000000040107f <+29>: cmp eax,0x6
0x0000000000401082 <+32>: je 0x4010d2 <phase_5+112>
0x0000000000401084 <+34>: call 0x40143a <explode_bomb>
0x0000000000401089 <+39>: jmp 0x4010d2 <phase_5+112>
0x000000000040108b <+41>: movzx ecx,BYTE PTR [rbx+rax*1]
0x000000000040108f <+45>: mov BYTE PTR [rsp],cl
0x0000000000401092 <+48>: mov rdx,QWORD PTR [rsp]
0x0000000000401096 <+52>: and edx,0xf
0x0000000000401099 <+55>: movzx edx,BYTE PTR [rdx+0x4024b0]
0x00000000004010a0 <+62>: mov BYTE PTR [rsp+rax*1+0x10],dl
0x00000000004010a4 <+66>: add rax,0x1
0x00000000004010a8 <+70>: cmp rax,0x6
0x00000000004010ac <+74>: jne 0x40108b <phase_5+41>
0x00000000004010ae <+76>: mov BYTE PTR [rsp+0x16],0x0
0x00000000004010b3 <+81>: mov esi,0x40245e
0x00000000004010b8 <+86>: lea rdi,[rsp+0x10]
0x00000000004010bd <+91>: call 0x401338 <strings_not_equal>
0x00000000004010c2 <+96>: test eax,eax
0x00000000004010c4 <+98>: je 0x4010d9 <phase_5+119>
0x00000000004010c6 <+100>: call 0x40143a <explode_bomb>
0x00000000004010cb <+105>: nop DWORD PTR [rax+rax*1+0x0]
0x00000000004010d0 <+110>: jmp 0x4010d9 <phase_5+119>
0x00000000004010d2 <+112>: mov eax,0x0
0x00000000004010d7 <+117>: jmp 0x40108b <phase_5+41>
0x00000000004010d9 <+119>: mov rax,QWORD PTR [rsp+0x18]
0x00000000004010de <+124>: xor rax,QWORD PTR fs:0x28
0x00000000004010e7 <+133>: je 0x4010ee <phase_5+140>
0x00000000004010e9 <+135>: call 0x400b30 <__stack_chk_fail@plt>
0x00000000004010ee <+140>: add rsp,0x20
0x00000000004010f2 <+144>: pop rbx
0x00000000004010f3 <+145>: ret

(pwndgb使用Intel语法汇编,对本人人性化一些)

首先检查输入长度是否为6,不是就爆炸,是的话跳转到0x4010d2。
然后开始一个循环,执行6次,每次把输入的第i个字符&0xf后,将[rdx+0x4024b0]读到[rsp+i+0x10]的位置。

1
2
3
4
5
6
7
8
9
pwndbg> x/15gx 0x4024b0
0x4024b0: 0x737265697564616d 0x6c796276746f666e
0x4024c0: 0x7420756f79206f53 0x756f79206b6e6968
0x4024d0: 0x6f7473206e616320 0x6f62206568742070
0x4024e0: 0x206874697720626d 0x202c632d6c727463
0x4024f0: 0x003f756f79206f64 0x202c736573727543
0x402500: 0x6620657627756f79 0x65687420646e756f
0x402510: 0x2074657263657320 0x0000216573616870
0x402520: 0x646e696620747542

循环结束后,把[rsp+0x16]置为0,然后比较rsp+0x10处的字符串和0x40245e处的字符串,相同则通过。

1
2
$ strings -t x ./bomb | grep 245e
245e flyers

对应16进制为:66 6c 79 65 72 73
对应偏移为(手动对照计算,注意小端法):9 f e 5 6 7
再前面补上一个0x6,让这些字符变成可读字符:69 6f 6e 65 66 67
对应ascii编码:ionefg

Phase 6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
pwndbg> disassemble phase_6
Dump of assembler code for function phase_6:
0x00000000004010f4 <+0>: push r14
0x00000000004010f6 <+2>: push r13
0x00000000004010f8 <+4>: push r12
0x00000000004010fa <+6>: push rbp
0x00000000004010fb <+7>: push rbx
0x00000000004010fc <+8>: sub rsp,0x50
0x0000000000401100 <+12>: mov r13,rsp
0x0000000000401103 <+15>: mov rsi,rsp
0x0000000000401106 <+18>: call 0x40145c <read_six_numbers>
0x000000000040110b <+23>: mov r14,rsp
0x000000000040110e <+26>: mov r12d,0x0
0x0000000000401114 <+32>: mov rbp,r13
0x0000000000401117 <+35>: mov eax,DWORD PTR [r13+0x0]
0x000000000040111b <+39>: sub eax,0x1
0x000000000040111e <+42>: cmp eax,0x5
0x0000000000401121 <+45>: jbe 0x401128 <phase_6+52>
0x0000000000401123 <+47>: call 0x40143a <explode_bomb>
0x0000000000401128 <+52>: add r12d,0x1
0x000000000040112c <+56>: cmp r12d,0x6
0x0000000000401130 <+60>: je 0x401153 <phase_6+95>
0x0000000000401132 <+62>: mov ebx,r12d
0x0000000000401135 <+65>: movsxd rax,ebx
0x0000000000401138 <+68>: mov eax,DWORD PTR [rsp+rax*4]
0x000000000040113b <+71>: cmp DWORD PTR [rbp+0x0],eax
0x000000000040113e <+74>: jne 0x401145 <phase_6+81>
0x0000000000401140 <+76>: call 0x40143a <explode_bomb>
0x0000000000401145 <+81>: add ebx,0x1
0x0000000000401148 <+84>: cmp ebx,0x5
0x000000000040114b <+87>: jle 0x401135 <phase_6+65>
0x000000000040114d <+89>: add r13,0x4
0x0000000000401151 <+93>: jmp 0x401114 <phase_6+32>
0x0000000000401153 <+95>: lea rsi,[rsp+0x18]
0x0000000000401158 <+100>: mov rax,r14
0x000000000040115b <+103>: mov ecx,0x7
0x0000000000401160 <+108>: mov edx,ecx
0x0000000000401162 <+110>: sub edx,DWORD PTR [rax]
0x0000000000401164 <+112>: mov DWORD PTR [rax],edx
0x0000000000401166 <+114>: add rax,0x4
0x000000000040116a <+118>: cmp rax,rsi
0x000000000040116d <+121>: jne 0x401160 <phase_6+108>
0x000000000040116f <+123>: mov esi,0x0
0x0000000000401174 <+128>: jmp 0x401197 <phase_6+163>
0x0000000000401176 <+130>: mov rdx,QWORD PTR [rdx+0x8]
0x000000000040117a <+134>: add eax,0x1
0x000000000040117d <+137>: cmp eax,ecx
0x000000000040117f <+139>: jne 0x401176 <phase_6+130>
0x0000000000401181 <+141>: jmp 0x401188 <phase_6+148>
0x0000000000401183 <+143>: mov edx,0x6032d0
0x0000000000401188 <+148>: mov QWORD PTR [rsp+rsi*2+0x20],rdx
0x000000000040118d <+153>: add rsi,0x4
0x0000000000401191 <+157>: cmp rsi,0x18
0x0000000000401195 <+161>: je 0x4011ab <phase_6+183>
0x0000000000401197 <+163>: mov ecx,DWORD PTR [rsp+rsi*1]
0x000000000040119a <+166>: cmp ecx,0x1
0x000000000040119d <+169>: jle 0x401183 <phase_6+143>
0x000000000040119f <+171>: mov eax,0x1
0x00000000004011a4 <+176>: mov edx,0x6032d0
0x00000000004011a9 <+181>: jmp 0x401176 <phase_6+130>
0x00000000004011ab <+183>: mov rbx,QWORD PTR [rsp+0x20]
0x00000000004011b0 <+188>: lea rax,[rsp+0x28]
0x00000000004011b5 <+193>: lea rsi,[rsp+0x50]
0x00000000004011ba <+198>: mov rcx,rbx
0x00000000004011bd <+201>: mov rdx,QWORD PTR [rax]
0x00000000004011c0 <+204>: mov QWORD PTR [rcx+0x8],rdx
0x00000000004011c4 <+208>: add rax,0x8
0x00000000004011c8 <+212>: cmp rax,rsi
0x00000000004011cb <+215>: je 0x4011d2 <phase_6+222>
0x00000000004011cd <+217>: mov rcx,rdx
0x00000000004011d0 <+220>: jmp 0x4011bd <phase_6+201>
0x00000000004011d2 <+222>: mov QWORD PTR [rdx+0x8],0x0
0x00000000004011da <+230>: mov ebp,0x5
0x00000000004011df <+235>: mov rax,QWORD PTR [rbx+0x8]
0x00000000004011e3 <+239>: mov eax,DWORD PTR [rax]
0x00000000004011e5 <+241>: cmp DWORD PTR [rbx],eax
0x00000000004011e7 <+243>: jge 0x4011ee <phase_6+250>
0x00000000004011e9 <+245>: call 0x40143a <explode_bomb>
0x00000000004011ee <+250>: mov rbx,QWORD PTR [rbx+0x8]
0x00000000004011f2 <+254>: sub ebp,0x1
0x00000000004011f5 <+257>: jne 0x4011df <phase_6+235>
0x00000000004011f7 <+259>: add rsp,0x50
0x00000000004011fb <+263>: pop rbx
0x00000000004011fc <+264>: pop rbp
0x00000000004011fd <+265>: pop r12
0x00000000004011ff <+267>: pop r13
0x0000000000401201 <+269>: pop r14
0x0000000000401203 <+271>: ret

首先向%rsp读入6个数字。
然后检查第一个数字是否小于等于6,不是则爆炸。

然后是一个嵌套循环:
首先依次检查剩下的5个数字是否与第一个相等(相等则爆炸),然后检查后4个和第二个是否相等……以此类推。

循环结束后来到0x401153。
首先一个循环(0x401153 - 0x40116d)让六个数字都有 n = 7 - n

然后进行一个循环,依据第n个数字的值,把0x6032d0的一个奇怪的八字节数组的值移到栈上rsp+0x20的地方。

1
2
3
4
5
6
7
pwndbg> x/20gx 0x6032d0
0x6032d0 <node1>: 0x000000010000014c 0x00000000006032e0
0x6032e0 <node2>: 0x00000002000000a8 0x00000000006032f0
0x6032f0 <node3>: 0x000000030000039c 0x0000000000603300
0x603300 <node4>: 0x00000004000002b3 0x0000000000603310
0x603310 <node5>: 0x00000005000001dd 0x0000000000603320
0x603320 <node6>: 0x00000006000001bb

具体对应关系是:
<=1 0x6032d0
2 0x6032e0
3 0x6032f0
4 0x603300
5 0x603310
6 0x603320
7 0
可以发现这些大部分都是地址。

然后对于读到栈上的地址进行一个循环,将上一个地址+8对应的值修改成下一个地址,如此执行5次。(如此拗口,不是我说不清楚,是题目逻辑就难懂……)
循环结束后,把最后一个地址+8的值设成0。

最后,进行一次循环比较:
要求对于前五个栈上的地址,它指向的值都要大于等于该地址+8所指向的值(int32)。
由于该地址+8在刚刚那轮里面其实已经全部修改成下一个地址了,所以实际上就是比较该地址和下个地址哪个指向的值更大而已。

可以看出,按照大到小排序的话,应该是3,4,5,6,1,2(注意由于取值是int32类型,所以是这样,否则就是654321),然后进行一下逆推(之前有个n=7-n),变成4,3,2,1,6,5。
答案就是这个。

Secret Phase

在phase_defused里可以发现有一个secret_phase,做法是在第四阶段后面加上一个DrEvil字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
0000000000401242 <secret_phase>:
401242: 53 push %rbx
401243: e8 56 02 00 00 callq 40149e <read_line>
401248: ba 0a 00 00 00 mov $0xa,%edx
40124d: be 00 00 00 00 mov $0x0,%esi
401252: 48 89 c7 mov %rax,%rdi
401255: e8 76 f9 ff ff callq 400bd0 <strtol@plt>
40125a: 48 89 c3 mov %rax,%rbx
40125d: 8d 40 ff lea -0x1(%rax),%eax
401260: 3d e8 03 00 00 cmp $0x3e8,%eax
401265: 76 05 jbe 40126c <secret_phase+0x2a>
401267: e8 ce 01 00 00 callq 40143a <explode_bomb>
40126c: 89 de mov %ebx,%esi
40126e: bf f0 30 60 00 mov $0x6030f0,%edi
401273: e8 8c ff ff ff callq 401204 <fun7>
401278: 83 f8 02 cmp $0x2,%eax
40127b: 74 05 je 401282 <secret_phase+0x40>
40127d: e8 b8 01 00 00 callq 40143a <explode_bomb>
401282: bf 38 24 40 00 mov $0x402438,%edi
401287: e8 84 f8 ff ff callq 400b10 <puts@plt>
40128c: e8 33 03 00 00 callq 4015c4 <phase_defused>
401291: 5b pop %rbx
401292: c3 retq

在secret_phase中,首先会读入一个长整数(strtol可以从字符串里读取一个long)(10进制),如果大于1001的话就会爆炸(并且是无符号判断)。

然后执行了fun7(0x6030f0, 读入的数字),并要求其返回值为2。0x6030f0位置有一些程序自带的值,并且程序除了这里以外,没有任何地方使用过(在IDA PRO中用xref可以查找)。

fun7是一个非常搞人的递归函数,我概括一下它的逻辑:

  1. arg1指向的int为0:返回0xFFFFFFFF
  2. arg1指向的int大于arg2:返回 2 * fun7(*(arg1+8), arg2)
  3. arg1指向的int小于arg2:返回 2 * fun7(*(arg1+16), arg2) + 1
  4. arg1指向的int等于arg2:返回0

接下来就是想办法构造一个arg2使得最终结果返回为2。
为了方便,我把数据以人类友好的方式抄在了纸上,这里我放一个用IDA看的完整列表(人类不友好):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
00000000006030E0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00000000006030F0 24 00 00 00 00 00 00 00 10 31 60 00 00 00 00 00
0000000000603100 30 31 60 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603110 08 00 00 00 00 00 00 00 90 31 60 00 00 00 00 00
0000000000603120 50 31 60 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603130 32 00 00 00 00 00 00 00 70 31 60 00 00 00 00 00
0000000000603140 B0 31 60 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603150 16 00 00 00 00 00 00 00 70 32 60 00 00 00 00 00
0000000000603160 30 32 60 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603170 2D 00 00 00 00 00 00 00 D0 31 60 00 00 00 00 00
0000000000603180 90 32 60 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603190 06 00 00 00 00 00 00 00 F0 31 60 00 00 00 00 00
00000000006031A0 50 32 60 00 00 00 00 00 00 00 00 00 00 00 00 00
00000000006031B0 6B 00 00 00 00 00 00 00 10 32 60 00 00 00 00 00
00000000006031C0 B0 32 60 00 00 00 00 00 00 00 00 00 00 00 00 00
00000000006031D0 28 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000000006031E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000000006031F0 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603200 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603210 63 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603230 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603250 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603260 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603270 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000000603290 2F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000000006032A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000000006032B0 E9 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000000006032C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000000006032D0 4C 01 00 00 01 00 00 00 E0 32 60 00 00 00 00 00
00000000006032E0 A8 00 00 00 02 00 00 00 F0 32 60 00 00 00 00 00
00000000006032F0 9C 03 00 00 03 00 00 00 00 33 60 00 00 00 00 00
0000000000603300 B3 02 00 00 04 00 00 00 10 33 60 00 00 00 00 00
0000000000603310 DD 01 00 00 05 00 00 00 20 33 60 00 00 00 00 00
0000000000603320 BB 01 00 00 06 00 00 00 00 00 00 00 00 00 00 00

我首先假设arg2<0x24,返回2 * fun7(0x603110, arg2)。

在fun7(0x603110, arg2)中,我想要引发第三种情况,又*0x603110 = 0x8,所以我假设arg2>0x8,于是返回2 * fun7(0x603150, arg2) + 1。

在fun7(0x603150, arg2)中,我想要引发第四种情况,又*0x60311=50 = 0x16 = 22,所以我令arg2=22。

然后就解决了,上述所有假设全都非常巧合地成立!(看来老师没想为难我们哈哈)

最终,题目答案如下:

1
2
3
4
5
6
7
Border relations with Canada have never been better.
1 2 4 8 16 32
0 207
7 0 DrEvil
ionefg
4 3 2 1 6 5
22

结语

我一开始都是挑战自己,一行一行看汇编做的。但是后来(具体来说是Phase6)我有点窒息了,于是就非常不争气地打开了我的IDA PRO……

然后发现IDA PRO虽好,但是有的地方反而比原始的汇编难懂,比如说 %rsp+0x20,在IDA PRO的汇编里显示为 %rsp + 0x58 + var_38,属于是美中不足了。

最后放一张拆弹成功图片:

OHHHHHHH