Bomblab拆弹实验总结

前期准备

安装gdb-peda插件

首先使用tar -xvf 42024203.tar解压压缩包,然后使用 gdb ./work/bomb 指令用gdb打开二进制可执行文件bomb

Phase 0

获取phase0的汇编代码:

1
gdb-peda$ disassemble phase_0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Dump of assembler code for function phase_0:
0x08049580 <+0>: endbr32
0x08049584 <+4>: push ebp
0x08049585 <+5>: mov ebp,esp
0x08049587 <+7>: sub esp,0x8
0x0804958a <+10>: sub esp,0x8
0x0804958d <+13>: push 0x804b1c8 # 推入0x804b1c8处的字符串
0x08049592 <+18>: push DWORD PTR [ebp+0x8] # 推入用户输入的字符串
0x08049595 <+21>: call 0x8049df1 <strings_not_equal> # 调用字符串比较函数比较两字符串是否相等
0x0804959a <+26>: add esp,0x10
0x0804959d <+29>: test eax,eax # 判断strings_not_equal函数的返回值, 将eax与自身进行与运算,得到返回值1或0
0x0804959f <+31>: je 0x80495ad <phase_0+45> # 如果不是0跳转至0x80495ad处的指令,结束phase_0,拆弹成功
0x080495a1 <+33>: call 0x804a071 <explode_bomb> # 如果是0则不跳转,引爆炸弹
0x080495a6 <+38>: mov eax,0x0 # 将phase_0函数return 0;
0x080495ab <+43>: jmp 0x80495b2 <phase_0+50>
0x080495ad <+45>: mov eax,0x1 # 将phase_0函数return 1;
0x080495b2 <+50>: leave
0x080495b3 <+51>: ret
End of assembler dump.

分析可得只需要查看0x804b1c8处字符串的值即可获得phase_0的答案

1
2
gdb-peda$ x/s 0x804b1c8
0x804b1c8: "Why index with the middle bits?"

查询可得正确结果Why index with the middle bits?

Phase 1

获取phase1的汇编代码:

1
gdb-peda$ disassemble phase_1
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
Dump of assembler code for function phase_1:
0x080495b4 <+0>: endbr32
0x080495b8 <+4>: push ebp
0x080495b9 <+5>: mov ebp,esp
0x080495bb <+7>: sub esp,0x38
0x080495be <+10>: mov eax,DWORD PTR [ebp+0x8]
0x080495c1 <+13>: mov DWORD PTR [ebp-0x2c],eax
0x080495c4 <+16>: mov eax,gs:0x14
0x080495ca <+22>: mov DWORD PTR [ebp-0xc],eax
0x080495cd <+25>: xor eax,eax
0x080495cf <+27>: mov DWORD PTR [ebp-0x1c],0x2009dd99 #将整数0x2009dd99推入浮点计数器中
0x080495d6 <+34>: fild DWORD PTR [ebp-0x1c]
0x080495d9 <+37>: fstp QWORD PTR [ebp-0x18] # 将浮点计数器中的数存储到寄存器中,地址为ebp-0x18
0x080495dc <+40>: lea eax,[ebp-0x20]
0x080495df <+43>: push eax
0x080495e0 <+44>: lea eax,[ebp-0x24]
0x080495e3 <+47>: push eax
0x080495e4 <+48>: push 0x804b1e8 # sscanf读取的输入格式,查看内容可得%d %d,即输入为两个用空格分开的整数
0x080495e9 <+53>: push DWORD PTR [ebp-0x2c]
0x080495ec <+56>: call 0x80491f0 <__isoc99_sscanf@plt>
0x080495f1 <+61>: add esp,0x10
0x080495f4 <+64>: cmp eax,0x2 # 将输入的数字数量与0x2作比较,如果相等则跳转,不相等则爆炸,即必须输入两个整数
0x080495f7 <+67>: je 0x8049605 <phase_1+81> # 跳转
0x080495f9 <+69>: call 0x804a071 <explode_bomb> # 引爆炸弹
0x080495fe <+74>: mov eax,0x0
0x08049603 <+79>: jmp 0x8049631 <phase_1+125>
0x08049605 <+81>: lea eax,[ebp-0x18]
0x08049608 <+84>: mov edx,DWORD PTR [eax]
0x0804960a <+86>: mov eax,DWORD PTR [ebp-0x24]
0x0804960d <+89>: cmp edx,eax # 比较第一个输入数和此时eax中的数是否相等,不相等则爆炸,相等则继续
0x0804960f <+91>: jne 0x8049620 <phase_1+108>
0x08049611 <+93>: lea eax,[ebp-0x18]
0x08049614 <+96>: add eax,0x4
0x08049617 <+99>: mov edx,DWORD PTR [eax]
0x08049619 <+101>: mov eax,DWORD PTR [ebp-0x20]
0x0804961c <+104>: cmp edx,eax # 比较第二个输入数和此时eax中的数是否相等,不相等则爆炸,相等则结束phase_1,拆弹成功
0x0804961e <+106>: je 0x804962c <phase_1+120>
0x08049620 <+108>: call 0x804a071 <explode_bomb># 引爆炸弹
0x08049625 <+113>: mov eax,0x0
0x0804962a <+118>: jmp 0x8049631 <phase_1+125>
0x0804962c <+120>: mov eax,0x1
0x08049631 <+125>: mov ecx,DWORD PTR [ebp-0xc]
0x08049634 <+128>: xor ecx,DWORD PTR gs:0x14
0x0804963b <+135>: je 0x8049642 <phase_1+142>
0x0804963d <+137>: call 0x8049190 <__stack_chk_fail@plt>
0x08049642 <+142>: leave
0x08049643 <+143>: ret
End of assembler dump.

编写以下C++代码,模拟程序执行的过程

1
2
3
4
double value=0x2009dd99;		//传入浮点寄存器中的数值
int* res=new int [2];
res=(int *)&value; //获取两处地址,进行强制类型转换,以整数方式强制打印,即可得到结果
cout<<res[0]<<" "<<res[1]<<endl;

运行上述程序,得到答案
-864026624 1103103214

Phase 2

输入disassemble phase_2获取phase2的汇编代码:

1
gdb-peda$ disassemble 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
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
Dump of assembler code for function phase_2:
0x08049644 <+0>: endbr32
0x08049648 <+4>: push ebp
0x08049649 <+5>: mov ebp,esp
0x0804964b <+7>: sub esp,0x48
0x0804964e <+10>: mov eax,DWORD PTR [ebp+0x8]
0x08049651 <+13>: mov DWORD PTR [ebp-0x3c],eax
0x08049654 <+16>: mov eax,gs:0x14
0x0804965a <+22>: mov DWORD PTR [ebp-0xc],eax
0x0804965d <+25>: xor eax,eax
0x0804965f <+27>: sub esp,0x4
0x08049662 <+30>: push 0x8 # 结合后面的函数read_n_numbers可知,程序读取了0x8个数,即我们需要输入8个整数
0x08049664 <+32>: lea eax,[ebp-0x2c]
0x08049667 <+35>: push eax
0x08049668 <+36>: push DWORD PTR [ebp-0x3c]
0x0804966b <+39>: call 0x8049d2f <read_n_numbers>
0x08049670 <+44>: add esp,0x10
0x08049673 <+47>: test eax,eax # 检验是否输入了正确数量的数,如果read_n_numbers返回0,表示没有读取到正确数量的数,程序跳转爆炸
0x08049675 <+49>: jne 0x804967e <phase_2+58>
0x08049677 <+51>: mov eax,0x0
0x0804967c <+56>: jmp 0x80496e3 <phase_2+159>
0x0804967e <+58>: mov eax,DWORD PTR [ebp-0x2c]
0x08049681 <+61>: cmp eax,0x20 # 判断第一个输入数是否为0x20(十进制32),如果不是则跳转爆炸
0x08049684 <+64>: jne 0x804968e <phase_2+74>
0x08049686 <+66>: mov eax,DWORD PTR [ebp-0x28]
0x08049689 <+69>: cmp eax,0x40 # 判断第二个输入数是否为0x40(十进制64),如果不是则跳转爆炸
0x0804968c <+72>: je 0x804969a <phase_2+86>
0x0804968e <+74>: call 0x804a071 <explode_bomb>
0x08049693 <+79>: mov eax,0x0
0x08049698 <+84>: jmp 0x80496e3 <phase_2+159>
0x0804969a <+86>: mov DWORD PTR [ebp-0x30],0x2 # 将表示循环次数的变量存入[ebp-0x30]中,开始循环,初始值为2(为方便描述设为i,并将8个输入数看做长度为8的数组)
0x080496a1 <+93>: jmp 0x80496d8 <phase_2+148> # 跳转判断循环次数
0x080496a3 <+95>: mov eax,DWORD PTR [ebp-0x30]
0x080496a6 <+98>: mov eax,DWORD PTR [ebp+eax*4-0x2c]
0x080496aa <+102>: mov edx,DWORD PTR [ebp-0x30]
0x080496ad <+105>: sub edx,0x2
0x080496b0 <+108>: mov edx,DWORD PTR [ebp+edx*4-0x2c]
0x080496b4 <+112>: mov ecx,edx
0x080496b6 <+114>: sar ecx,1 # 下标为i-2的元素右移(除以二向下取整)
0x080496b8 <+116>: mov edx,DWORD PTR [ebp-0x30]
0x080496bb <+119>: sub edx,0x1
0x080496be <+122>: mov edx,DWORD PTR [ebp+edx*4-0x2c]
0x080496c2 <+126>: add edx,ecx # 下标为i-1的元素加上下标的为i-2的元素
0x080496c4 <+128>: cmp eax,edx # 如果上述结果等于下标为i的元素则继续循环,不等于则爆炸
0x080496c6 <+130>: je 0x80496d4 <phase_2+144>
0x080496c8 <+132>: call 0x804a071 <explode_bomb>
0x080496cd <+137>: mov eax,0x0
0x080496d2 <+142>: jmp 0x80496e3 <phase_2+159>
0x080496d4 <+144>: add DWORD PTR [ebp-0x30],0x1
0x080496d8 <+148>: cmp DWORD PTR [ebp-0x30],0x7 # 如果循环次数变量i大于0x7,则跳出循环,否则继续循环
0x080496dc <+152>: jle 0x80496a3 <phase_2+95>
0x080496de <+154>: mov eax,0x1
0x080496e3 <+159>: mov ecx,DWORD PTR [ebp-0xc]
0x080496e6 <+162>: xor ecx,DWORD PTR gs:0x14
0x080496ed <+169>: je 0x80496f4 <phase_2+176>
0x080496ef <+171>: call 0x8049190 <__stack_chk_fail@plt>
0x080496f4 <+176>: leave
0x080496f5 <+177>: ret
End of assembler dump.

通过分析代码可得,该程序要求输入一组以数32,64开头的数,要满足如下关系式

1
array [i]== array [i-1]+ array [i-2]/2;

编写程序C++程序模拟循环获得结果

1
2
3
4
5
int array [8]={32,64};
for(int i=2;i<8;i++) {
array [i]= array [i-1]+ array [i-2]/2;
cout<< array [i]<<" ";
}

结果如下
32 64 80 112 152 208 284 388

Phase 3

输入disassemble phase_3获取phase3的汇编代码:

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
gdb-peda$ disassemble phase_3
Dump of assembler code for function phase_3:
0x080496f6 <+0>: endbr32
0x080496fa <+4>: push ebp
0x080496fb <+5>: mov ebp,esp
0x080496fd <+7>: sub esp,0x38
0x08049700 <+10>: mov eax,DWORD PTR [ebp+0x8]
0x08049703 <+13>: mov DWORD PTR [ebp-0x2c],eax
0x08049706 <+16>: mov eax,gs:0x14
0x0804970c <+22>: mov DWORD PTR [ebp-0xc],eax
0x0804970f <+25>: xor eax,eax
0x08049711 <+27>: mov DWORD PTR [ebp-0x14],0x0
0x08049718 <+34>: mov DWORD PTR [ebp-0x10],0x0
0x0804971f <+41>: lea eax,[ebp-0x18]
0x08049722 <+44>: push eax
0x08049723 <+45>: lea eax,[ebp-0x1c]
0x08049726 <+48>: push eax
0x08049727 <+49>: push 0x804b1e8 # sscanf读取的输入格式,查看内容可得%d %d,即输入为两个用空格分开的整数
0x0804972c <+54>: push DWORD PTR [ebp-0x2c]
0x0804972f <+57>: call 0x80491f0 <__isoc99_sscanf@plt>
0x08049734 <+62>: add esp,0x10
0x08049737 <+65>: mov DWORD PTR [ebp-0x10],eax
0x0804973a <+68>: cmp DWORD PTR [ebp-0x10],0x1 # 检验是否输入了正确数量的数,如果输入的数小于1个则爆炸,如不是则跳过爆炸,继续运行
0x0804973e <+72>: jg 0x804974f <phase_3+89>
0x08049740 <+74>: call 0x804a071 <explode_bomb>
0x08049745 <+79>: mov eax,0x0
0x0804974a <+84>: jmp 0x80497e5 <phase_3+239>
0x0804974f <+89>: mov eax,DWORD PTR [ebp-0x1c]
0x08049752 <+92>: sub eax,0xcb
0x08049757 <+97>: cmp eax,0x9 # 第一个输入数减去0xcb(十进制203)再与0x9(十进制9)比较,如果大于9则跳转至爆炸行,如不是则继续运行
0x0804975a <+100>: ja 0x80497c0 <phase_3+202>
0x0804975c <+102>: mov eax,DWORD PTR [eax*4+0x804b1f0]
0x08049763 <+109>: notrack jmp eax # 根据第一个输入的数减去0xcb的结果选择跳转至不同的分支
# case 0
0x08049766 <+112>: mov DWORD PTR [ebp-0x14],0x3e8 # 给[ebp-0x14]中存入将要与第二个输入比较的数0x3e8
0x0804976d <+119>: jmp 0x80497cc <phase_3+214>
# case 1
0x0804976f <+121>: mov DWORD PTR [ebp-0x14],0x3e8 # 给[ebp-0x14]中存入将要与第二个输入比较的数0x3e8
0x08049776 <+128>: jmp 0x80497cc <phase_3+214>
# case 2
0x08049778 <+130>: mov DWORD PTR [ebp-0x14],0x14d # 给[ebp-0x14]中存入将要与第二个输入比较的数0x14d
0x0804977f <+137>: jmp 0x80497cc <phase_3+214>
# case3
0x08049781 <+139>: mov DWORD PTR [ebp-0x14],0x3e8 # 给[ebp-0x14]中存入将要与第二个输入比较的数0x3e8
0x08049788 <+146>: jmp 0x80497cc <phase_3+214>
# case 4
0x0804978a <+148>: mov DWORD PTR [ebp-0x14],0x14d # 给[ebp-0x14]中存入将要与第二个输入比较的数0x14d
0x08049791 <+155>: jmp 0x80497cc <phase_3+214>
# case 5
0x08049793 <+157>: mov DWORD PTR [ebp-0x14],0x3e8 # 给[ebp-0x14]中存入将要与第二个输入比较的数0x3e8
0x0804979a <+164>: jmp 0x80497cc <phase_3+214>
# case 6
0x0804979c <+166>: mov DWORD PTR [ebp-0x14],0x14d # 给[ebp-0x14]中存入将要与第二个输入比较的数0x14d
0x080497a3 <+173>: jmp 0x80497cc <phase_3+214>
# case 7
0x080497a5 <+175>: mov DWORD PTR [ebp-0x14],0x14d # 给[ebp-0x14]中存入将要与第二个输入比较的数0x14d
0x080497ac <+182>: jmp 0x80497cc <phase_3+214>
# case 8
0x080497ae <+184>: mov DWORD PTR [ebp-0x14],0x3e8 # 给[ebp-0x14]中存入将要与第二个输入比较的数0x3e8
0x080497b5 <+191>: jmp 0x80497cc <phase_3+214>
# case 9
0x080497b7 <+193>: mov DWORD PTR [ebp-0x14],0x14d # 给[ebp-0x14]中存入将要与第二个输入比较的数0x14d
0x080497be <+200>: jmp 0x80497cc <phase_3+214>

0x080497c0 <+202>: call 0x804a071 <explode_bomb>
0x080497c5 <+207>: mov eax,0x0
0x080497ca <+212>: jmp 0x80497e5 <phase_3+239>
0x080497cc <+214>: mov eax,DWORD PTR [ebp-0x18]
0x080497cf <+217>: cmp DWORD PTR [ebp-0x14],eax # 比较第二个输入与上一步中赋给[ebp-0x14]的值是否相等
0x080497d2 <+220>: je 0x80497e0 <phase_3+234> # 相等则跳转结束,不相等则爆炸
0x080497d4 <+222>: call 0x804a071 <explode_bomb>
0x080497d9 <+227>: mov eax,0x0
0x080497de <+232>: jmp 0x80497e5 <phase_3+239>
0x080497e0 <+234>: mov eax,0x1
0x080497e5 <+239>: mov edx,DWORD PTR [ebp-0xc]
0x080497e8 <+242>: xor edx,DWORD PTR gs:0x14
0x080497ef <+249>: je 0x80497f6 <phase_3+256>
0x080497f1 <+251>: call 0x8049190 <__stack_chk_fail@plt>
0x080497f6 <+256>: leave
0x080497f7 <+257>: ret
End of assembler dump.

根据上述分析可得,第一个输入减去203不能大于9,每种可能的第一个输入对应一种正确的第二个输入,得到下面一组解
204 1000

Phase 4

输入disassemble phase_4获取phase4的汇编代码:

1
gdb-peda$ disassemble 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
48
49
50
51
52
Dump of assembler code for function phase_4:
0x08049872 <+0>: endbr32
0x08049876 <+4>: push ebp
0x08049877 <+5>: mov ebp,esp
0x08049879 <+7>: sub esp,0x38
0x0804987c <+10>: mov eax,DWORD PTR [ebp+0x8]
0x0804987f <+13>: mov DWORD PTR [ebp-0x2c],eax
0x08049882 <+16>: mov eax,gs:0x14
0x08049888 <+22>: mov DWORD PTR [ebp-0xc],eax
0x0804988b <+25>: xor eax,eax
0x0804988d <+27>: lea eax,[ebp-0x18]
0x08049890 <+30>: push eax
0x08049891 <+31>: lea eax,[ebp-0x1c]
0x08049894 <+34>: push eax
0x08049895 <+35>: push 0x804b1e8 # sscanf读取的输入格式,查看内容可得%d %d,即输入为两个用空格分开的整数
0x0804989a <+40>: push DWORD PTR [ebp-0x2c]
0x0804989d <+43>: call 0x80491f0 <__isoc99_sscanf@plt>
0x080498a2 <+48>: add esp,0x10
0x080498a5 <+51>: mov DWORD PTR [ebp-0x14],eax
0x080498a8 <+54>: cmp DWORD PTR [ebp-0x14],0x2 # 检验是否输入了正确数量的数,如果输入的数不是2个则爆炸,如不是则跳过爆炸,继续运行
0x080498ac <+58>: jne 0x80498be <phase_4+76>
0x080498ae <+60>: mov eax,DWORD PTR [ebp-0x1c]
0x080498b1 <+63>: cmp eax,0x4
0x080498b4 <+66>: jle 0x80498be <phase_4+76> # 检验第一个输入是否小于等于0x4,如果是则爆炸,如不是则继续运行
0x080498b6 <+68>: mov eax,DWORD PTR [ebp-0x1c]
0x080498b9 <+71>: cmp eax,0x29
0x080498bc <+74>: jle 0x80498ca <phase_4+88> # 检验第一个输入是否小于等于0x29(十进制41),如果不是则爆炸,如是则继续运行
0x080498be <+76>: call 0x804a071 <explode_bomb>
0x080498c3 <+81>: mov eax,0x0
0x080498c8 <+86>: jmp 0x80498f9 <phase_4+135>
0x080498ca <+88>: mov eax,DWORD PTR [ebp-0x1c]
0x080498cd <+91>: sub esp,0x4
0x080498d0 <+94>: push 0x29
0x080498d2 <+96>: push 0x5
0x080498d4 <+98>: push eax
0x080498d5 <+99>: call 0x80497f8 <func4> # 传入参数(0x29,0x5,第一个输入)调用func4
0x080498da <+104>: add esp,0x10
0x080498dd <+107>: mov DWORD PTR [ebp-0x10],eax
0x080498e0 <+110>: mov eax,DWORD PTR [ebp-0x18]
0x080498e3 <+113>: cmp DWORD PTR [ebp-0x10],eax # 判断第二个输入和调用func4后的第一个输入是否相等,相等则拆除,否则爆炸
0x080498e6 <+116>: je 0x80498f4 <phase_4+130>
0x080498e8 <+118>: call 0x804a071 <explode_bomb>
0x080498ed <+123>: mov eax,0x0
0x080498f2 <+128>: jmp 0x80498f9 <phase_4+135>
0x080498f4 <+130>: mov eax,0x1
0x080498f9 <+135>: mov edx,DWORD PTR [ebp-0xc]
0x080498fc <+138>: xor edx,DWORD PTR gs:0x14
0x08049903 <+145>: je 0x804990a <phase_4+152>
0x08049905 <+147>: call 0x8049190 <__stack_chk_fail@plt>
0x0804990a <+152>: leave
0x0804990b <+153>: ret
End of assembler dump.

利用gdb的断点和步进功能,输入一个合法的输入,获取调用完func4后内存中该输入的值,此值即为对应的第二个输出,以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
# 设置断点
gdb-peda$ b *0x080498e3
Breakpoint 1 at 0x80498e3

# 运行程序
gdb-peda$ r
Starting program: /home/jovyan/work/bomb
Welcome to my fiendish little bomb. You have 7 phases with
which to blow yourself up. Have a nice day!
Well done! You seem to have warmed up!
Phase 1 defused. How about the next one?
That's number 2. Keep going!
Halfway there!
6 28 # 第一个输入为6,第二个随便输入

# 断点生效
=> 0x80498e3 <phase_4+113>: cmp DWORD PTR [ebp-0x10],eax
Breakpoint 1, 0x080498e3 in phase_4 ()

# 此时eax中存储第二个输入,ebp-0x10中存储的为调用过func4的第一个输出,也就是6对应的答案
# 查看ebp-0x10中的内容
gdb-peda$ x/d $ebp-0x10
0xffffd2d8: 27
# 得到一组正确答案 6 27

Phase 5

输入disassemble phase_5获取phase5的汇编代码:

1
gdb-peda$ disassemble 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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
Dump of assembler code for function phase_5:
0x0804990c <+0>: endbr32
0x08049910 <+4>: push ebp
0x08049911 <+5>: mov ebp,esp
0x08049913 <+7>: sub esp,0x38
0x08049916 <+10>: mov eax,DWORD PTR [ebp+0x8]
0x08049919 <+13>: mov DWORD PTR [ebp-0x2c],eax
0x0804991c <+16>: mov eax,gs:0x14
0x08049922 <+22>: mov DWORD PTR [ebp-0xc],eax
0x08049925 <+25>: xor eax,eax
0x08049927 <+27>: sub esp,0xc
0x0804992a <+30>: push DWORD PTR [ebp-0x2c]
0x0804992d <+33>: call 0x8049dc1 <string_length> # 调用string_length函数获取输入的字符串长度
0x08049932 <+38>: add esp,0x10
0x08049935 <+41>: mov DWORD PTR [ebp-0x18],eax
0x08049938 <+44>: cmp DWORD PTR [ebp-0x18],0x6 # 如果不是6则爆炸, 是6则跳过爆炸继续运行
0x0804993c <+48>: je 0x804994a <phase_5+62>
0x0804993e <+50>: call 0x804a071 <explode_bomb>
0x08049943 <+55>: mov eax,0x0
0x08049948 <+60>: jmp 0x80499ac <phase_5+160>
0x0804994a <+62>: mov DWORD PTR [ebp-0x1c],0x0 # 遍历字符串中的每一个字符,将当前下标存入[ebp-0x1c]中
0x08049951 <+69>: jmp 0x8049979 <phase_5+109>
0x08049953 <+71>: mov edx,DWORD PTR [ebp-0x1c] # 遍历循环体
0x08049956 <+74>: mov eax,DWORD PTR [ebp-0x2c]
0x08049959 <+77>: add eax,edx
0x0804995b <+79>: movzx eax,BYTE PTR [eax] # 进行全零扩展并传送,将输入字符串传入eax
0x0804995e <+82>: movsx eax,al # 进行符号扩展并传送,截取当前下标处的字符传给eax
0x08049961 <+85>: and eax,0xf # eax中的值和0xf做AND运算,得到的结果存入eax
0x08049964 <+88>: movzx eax,BYTE PTR [eax+0x804d25c] # 取出0x804d25c中下标为eax的值赋给eax并替换原字符串处的值
0x0804996b <+95>: lea ecx,[ebp-0x13]
0x0804996e <+98>: mov edx,DWORD PTR [ebp-0x1c]
0x08049971 <+101>: add edx,ecx
0x08049973 <+103>: mov BYTE PTR [edx],al
0x08049975 <+105>: add DWORD PTR [ebp-0x1c],0x1 # 下标自增1
0x08049979 <+109>: cmp DWORD PTR [ebp-0x1c],0x5 # 判断遍历是否完成
0x0804997d <+113>: jle 0x8049953 <phase_5+71>
0x0804997f <+115>: mov BYTE PTR [ebp-0xd],0x0
0x08049983 <+119>: sub esp,0x8
0x08049986 <+122>: push 0x804b218
0x0804998b <+127>: lea eax,[ebp-0x13]
0x0804998e <+130>: push eax
0x0804998f <+131>: call 0x8049df1 <strings_not_equal> # 调用strings_not_equal函数比较两个字符串,在此处打断点即可使用gdb查看得到答案字符串为ckmfia
0x08049994 <+136>: add esp,0x10
0x08049997 <+139>: test eax,eax # strings_not_equal返回1,两字符串相等,跳过爆炸,否则爆炸
0x08049999 <+141>: je 0x80499a7 <phase_5+155>
0x0804999b <+143>: call 0x804a071 <explode_bomb>
0x080499a0 <+148>: mov eax,0x0
0x080499a5 <+153>: jmp 0x80499ac <phase_5+160>
0x080499a7 <+155>: mov eax,0x1
0x080499ac <+160>: mov ecx,DWORD PTR [ebp-0xc]
0x080499af <+163>: xor ecx,DWORD PTR gs:0x14
0x080499b6 <+170>: je 0x80499bd <phase_5+177>
0x080499b8 <+172>: call 0x8049190 <__stack_chk_fail@plt>
0x080499bd <+177>: leave
0x080499be <+178>: ret
End of assembler dump.

分析可得,程序需要输入长度为6的字符串,字符串中的每个字符的ASCII码与0xf进行AND运算得到的数作为下标,访问字符数组<array.2712>中的元素并替换输入字符数组的元素,最终的得到的字符串需要等于ckmfia。
因此,首先需要查看<array.2712>中的元素

1
2
gdb-peda$ x/s 0x804d25c
0x804d25c <array.2712>: "lbmpagdcehnokjif"

然后分别找到ckmfia在array中的位置(7,12,2,15,14,4),现在需要得到能够和0xfAND运算产生这些下标的字符,编写C++程序遍历0~127,将符合要求的字符输出,即可得到答案,主函数代码如下:

1
2
3
4
5
6
7
8
9
10
string array_2712="lbmpagdcehnokjif";
int ans_index[]={7,12,2,15,14,4}; // 需要得到的答案下标
for(int i=0;i<6;i++) { // 依次搜索每一个下标
cout<<"Index"<<i<<":";
for(int j=0;j<128;j++) { // 遍历可能的字符
if((j&15 )== ans_index[i]) // 如果做运算后符合答案则输出
printf("%c ", j);
cout<<endl;
}
}

最终得到输出如下

1
2
3
4
5
6
7
Index0:         '       7       G       W       g       w
Index1:
, < L \ l |
Index2: " 2 B R b r
Index3: / ? O _ o
Index4: . > N ^ n ~
Index5: $ 4 D T d t

任取其中一种组合,即可得到正确答案,如
glbond

Phase 6

输入disassemble phase_6获取phase6的汇编代码:

1
gdb-peda$ disassemble 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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
Dump of assembler code for function phase_6:
0x080499bf <+0>: endbr32
0x080499c3 <+4>: push ebp
0x080499c4 <+5>: mov ebp,esp
0x080499c6 <+7>: sub esp,0x68
0x080499c9 <+10>: mov eax,DWORD PTR [ebp+0x8]
0x080499cc <+13>: mov DWORD PTR [ebp-0x5c],eax
0x080499cf <+16>: mov eax,gs:0x14
0x080499d5 <+22>: mov DWORD PTR [ebp-0xc],eax
0x080499d8 <+25>: xor eax,eax
0x080499da <+27>: mov DWORD PTR [ebp-0x48],0x804d19c
0x080499e1 <+34>: sub esp,0x4
0x080499e4 <+37>: push 0x7 # 读取7个输入的数
0x080499e6 <+39>: lea eax,[ebp-0x44]
0x080499e9 <+42>: push eax
0x080499ea <+43>: push DWORD PTR [ebp-0x5c]
0x080499ed <+46>: call 0x8049d2f <read_n_numbers>
0x080499f2 <+51>: add esp,0x10

0x080499f5 <+54>: test eax,eax # 判断输入的数是否为0,如果是则爆炸
0x080499f7 <+56>: jne 0x8049a03 <phase_6+68>
0x080499f9 <+58>: mov eax,0x0
0x080499fe <+63>: jmp 0x8049b3a <phase_6+379>

0x08049a03 <+68>: mov DWORD PTR [ebp-0x50],0x0
0x08049a0a <+75>: jmp 0x8049a6c <phase_6+173> # 如果不是0则跳转,继续执行程序

0x08049a0c <+77>: mov eax,DWORD PTR [ebp-0x50]
0x08049a0f <+80>: mov eax,DWORD PTR [ebp+eax*4-0x44]
0x08049a13 <+84>: test eax,eax
0x08049a15 <+86>: jle 0x8049a23 <phase_6+100> # 输入的数是否小于等于0,如果是则跳转爆炸,不是则继续运行

0x08049a17 <+88>: mov eax,DWORD PTR [ebp-0x50]
0x08049a1a <+91>: mov eax,DWORD PTR [ebp+eax*4-0x44] # 循环遍历输入的数,如果小于等于7则继续,如果大于7则跳转爆炸
0x08049a1e <+95>: cmp eax,0x7
0x08049a21 <+98>: jle 0x8049a32 <phase_6+115>
0x08049a23 <+100>: call 0x804a071 <explode_bomb>
0x08049a28 <+105>: mov eax,0x0
0x08049a2d <+110>: jmp 0x8049b3a <phase_6+379>

0x08049a32 <+115>: mov eax,DWORD PTR [ebp-0x50]
0x08049a35 <+118>: add eax,0x1
0x08049a38 <+121>: mov DWORD PTR [ebp-0x4c],eax
0x08049a3b <+124>: jmp 0x8049a62 <phase_6+163>

0x08049a3d <+126>: mov eax,DWORD PTR [ebp-0x50]
0x08049a40 <+129>: mov edx,DWORD PTR [ebp+eax*4-0x44]
0x08049a44 <+133>: mov eax,DWORD PTR [ebp-0x4c]
0x08049a47 <+136>: mov eax,DWORD PTR [ebp+eax*4-0x44]
0x08049a4b <+140>: cmp edx,eax # 判断当前输入的数是否和已经输入的数重复,如果是则爆炸,如果不是则继续运行
0x08049a4d <+142>: jne 0x8049a5e <phase_6+159>
0x08049a4f <+144>: call 0x804a071 <explode_bomb>
0x08049a54 <+149>: mov eax,0x0
0x08049a59 <+154>: jmp 0x8049b3a <phase_6+379>
0x08049a5e <+159>: add DWORD PTR [ebp-0x4c],0x1

0x08049a62 <+163>: cmp DWORD PTR [ebp-0x4c],0x6 # 判断当前遍历下标是否小于等于6,如果是则继续遍历,如果不是则循环结束
0x08049a66 <+167>: jle 0x8049a3d <phase_6+126>
0x08049a68 <+169>: add DWORD PTR [ebp-0x50],0x1
0x08049a6c <+173>: cmp DWORD PTR [ebp-0x50],0x6
0x08049a70 <+177>: jle 0x8049a0c <phase_6+77>
0x08049a72 <+179>: mov DWORD PTR [ebp-0x50],0x0
0x08049a79 <+186>: jmp 0x8049ab1 <phase_6+242>
0x08049a7b <+188>: mov eax,DWORD PTR [ebp-0x48]
0x08049a7e <+191>: mov DWORD PTR [ebp-0x54],eax
0x08049a81 <+194>: mov DWORD PTR [ebp-0x4c],0x1
0x08049a88 <+201>: jmp 0x8049a97 <phase_6+216>
0x08049a8a <+203>: mov eax,DWORD PTR [ebp-0x54]
0x08049a8d <+206>: mov eax,DWORD PTR [eax+0x8]
0x08049a90 <+209>: mov DWORD PTR [ebp-0x54],eax
0x08049a93 <+212>: add DWORD PTR [ebp-0x4c],0x1
0x08049a97 <+216>: mov eax,DWORD PTR [ebp-0x50]
0x08049a9a <+219>: mov eax,DWORD PTR [ebp+eax*4-0x44]
0x08049a9e <+223>: cmp DWORD PTR [ebp-0x4c],eax
0x08049aa1 <+226>: jl 0x8049a8a <phase_6+203>
0x08049aa3 <+228>: mov eax,DWORD PTR [ebp-0x50]
0x08049aa6 <+231>: mov edx,DWORD PTR [ebp-0x54]
0x08049aa9 <+234>: mov DWORD PTR [ebp+eax*4-0x28],edx
0x08049aad <+238>: add DWORD PTR [ebp-0x50],0x1
0x08049ab1 <+242>: cmp DWORD PTR [ebp-0x50],0x6
0x08049ab5 <+246>: jle 0x8049a7b <phase_6+188>
0x08049ab7 <+248>: mov eax,DWORD PTR [ebp-0x28]
0x08049aba <+251>: mov DWORD PTR [ebp-0x48],eax
0x08049abd <+254>: mov eax,DWORD PTR [ebp-0x48]
0x08049ac0 <+257>: mov DWORD PTR [ebp-0x54],eax
0x08049ac3 <+260>: mov DWORD PTR [ebp-0x50],0x1
0x08049aca <+267>: jmp 0x8049ae6 <phase_6+295>
0x08049acc <+269>: mov eax,DWORD PTR [ebp-0x50]
0x08049acf <+272>: mov edx,DWORD PTR [ebp+eax*4-0x28]
0x08049ad3 <+276>: mov eax,DWORD PTR [ebp-0x54]
0x08049ad6 <+279>: mov DWORD PTR [eax+0x8],edx
0x08049ad9 <+282>: mov eax,DWORD PTR [ebp-0x54]
0x08049adc <+285>: mov eax,DWORD PTR [eax+0x8]
0x08049adf <+288>: mov DWORD PTR [ebp-0x54],eax
0x08049ae2 <+291>: add DWORD PTR [ebp-0x50],0x1
0x08049ae6 <+295>: cmp DWORD PTR [ebp-0x50],0x6
0x08049aea <+299>: jle 0x8049acc <phase_6+269>
0x08049aec <+301>: mov eax,DWORD PTR [ebp-0x54]
0x08049aef <+304>: mov DWORD PTR [eax+0x8],0x0
0x08049af6 <+311>: mov eax,DWORD PTR [ebp-0x48]
0x08049af9 <+314>: mov DWORD PTR [ebp-0x54],eax
0x08049afc <+317>: mov DWORD PTR [ebp-0x50],0x0
0x08049b03 <+324>: jmp 0x8049b2f <phase_6+368>
0x08049b05 <+326>: mov eax,DWORD PTR [ebp-0x54]
0x08049b08 <+329>: mov edx,DWORD PTR [eax]
0x08049b0a <+331>: mov eax,DWORD PTR [ebp-0x54]
0x08049b0d <+334>: mov eax,DWORD PTR [eax+0x8]
0x08049b10 <+337>: mov eax,DWORD PTR [eax]
0x08049b12 <+339>: cmp edx,eax
0x08049b14 <+341>: jle 0x8049b22 <phase_6+355>
0x08049b16 <+343>: call 0x804a071 <explode_bomb>
0x08049b1b <+348>: mov eax,0x0
0x08049b20 <+353>: jmp 0x8049b3a <phase_6+379>
0x08049b22 <+355>: mov eax,DWORD PTR [ebp-0x54]
0x08049b25 <+358>: mov eax,DWORD PTR [eax+0x8]
0x08049b28 <+361>: mov DWORD PTR [ebp-0x54],eax
0x08049b2b <+364>: add DWORD PTR [ebp-0x50],0x1
0x08049b2f <+368>: cmp DWORD PTR [ebp-0x50],0x5
0x08049b33 <+372>: jle 0x8049b05 <phase_6+326>
0x08049b35 <+374>: mov eax,0x1
0x08049b3a <+379>: mov ecx,DWORD PTR [ebp-0xc]
0x08049b3d <+382>: xor ecx,DWORD PTR gs:0x14
0x08049b44 <+389>: je 0x8049b4b <phase_6+396>
0x08049b46 <+391>: call 0x8049190 <__stack_chk_fail@plt>
0x08049b4b <+396>: leave
0x08049b4c <+397>: ret
End of assembler dump.

根据分析可得,答案由1~7组成且不能重复,编写C++程序生成所有可能的输入(1~7的全排列),记录其对应的输出,并且依次检索输出,找到有拆弹成功提示语句的输出,其对应输入即为答案,代码及思路如下:
首先编写gen.cpp,负责在input文件夹中生成输入文件,并按序号命名,代码如下;

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
#include<iostream>
#include<algorithm>
#include<fstream>
#include<sstream>
using namespace std;
int main()
{
// 记录之前节点的答案
string prev_phase[7]={
"Why index with the middle bits?\n",
"-864026624 1103103214\n",
"32 64 80 112 152 208 284 388\n",
"204 1000\n",
"6 27\n",
"glbond\n",
"1 2 3 4 5 6 7 "
};

string str="1234567",filename;
stringstream m_sstream; // 使用字符串流实现整数和字符串的转化,方便按序号命名文件
ofstream f; // 使用文件输出流将答案存放进txt中

int total=0; // 表示序号,用以记录区分不同的输入
// 使用algorithm中的函数对1234567进行全排列,将每次排列按空格分开记录入答案
while(next_permutation(str.begin(),str.end()))
{
total++;

for(int i=0;i<7;i++)
{
prev_phase[6][i*2]=str[i]; // 将重新排列的str中数字赋值给记录答案的字符串数组
}

// gen file
m_sstream.clear();
m_sstream<<"./input/"<<total; // 指定文件路径和文件名
m_sstream>>filename;
cout<<filename<<".txt\n";
f.open(filename+".txt",ios::out);

for (int i = 0; i < 7; i++)
{
f<<prev_phase[i];
}
f<<"\n";
f.close();
}

cout<<total; // 获得全排列数
}

编译运行后,即可在当前目录下得到input文件夹和全排列数5039,随后编写scaner.cpp用于检索答案,代码如下:

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
#include<iostream>
#include<algorithm>
#include<fstream>
#include<sstream>
using namespace std;
int main()
{
string str="1234567",filename;
stringstream m_sstream; //
ifstream f;
string right_file_name="none";
bool isRightAns;

int total=0;
while(total<=5039)
{
total++;

isRightAns=true; // 当前输出为正确输出的真值

// open file
m_sstream.clear();
m_sstream<<"./Output/Output"<<total;
m_sstream>>filename;
f.open(filename+".txt",ios::in); // 打开Output文件夹内名为Output[序号].txt的文本文件,如Output25.txt

while (!f.eof())
{
getline(f, str); // 逐行读取文本
if(str=="BOOM!!!") // 如果检索到拆除phase_6爆炸的语句,则不是正确输出,将isRightAns真值设为0
{
isRightAns=false;
}
if(isRightAns) //如果没有提示爆炸,表示拆除成功,则输出当前输出记录文本的文件名,程序结束
{
right_file_name=filename;
cout<<right_file_name<<endl;
exit(0);
}
}
f.close();
}
cout<<right_file_name; // Debug 备用输出语句
}

上述代码编写完成后,已经可以自动生成输入和检索输出了,接下来需要编写shell脚本生成input文件,自动执行bomb文件,将每一个input带入运行,得到并记录对应的Output,再对Output进行检索即可得到正确答案。将scaner.cpp和gen.cpp打包为tools.tar.gz,编写的shell脚本run.sh,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash  
echo "Welcome,running...."
echo "Exacting tools"
tar -zxvf ./tools.tar.gz # 解压cpp文件
cp ./tools/gen.cpp ./gen.cpp
cp ./tools/scaner.cpp ./scaner.cpp
g++ ./gen.cpp -o ./gen # 编译input生成器gen.cpp
g++ ./scaner.cpp -o ./scanner # 编译Output检索器scaner.cpp
chmod +x ./gen # 赋予可执行文件权限
chmod +x ./scanner
chmod +x ./bomb
mkdir input # 创建存放input的文件夹
echo "Generating Inputs"
./gen # 执行生成器生成所有输出
mkdir ./Output # 创建存放输出的文件夹
for((i=1;i<=5039;i++)) # 循环带入每一种输入
do
echo "running",$(expr $i)
./bomb ./input/${i}.txt > ./Output/Output${i}.txt #将输出结构存入Output中
done
echo "Create Complete!"
echo "Finding ans..."
./scanner # 执行检索器寻找正确答案
echo "Done." # 完成

tools.tar.gzrun.sh上传至~/work目录中执行脚本

1
2
(base) jovyan@b5ea96d0ec9c:~/work$ chmod +x ./run.sh
(base) jovyan@b5ea96d0ec9c: ~/work$ ./run.sh

最终得到如下输出

1
2
./Output/Output3078
Done.

查看对应input3078的内容即可得到答案

1
2
3
4
5
6
7
8
9
10
11
12
13
(base) jovyan@b5ea96d0ec9c: ~/work$ vim ./input/3078.txt

Why index with the middle bits?
-864026624 1103103214
32 64 80 112 152 208 284 388
204 1000
6 27
glbond
5 2 6 3 1 4 7
~
~

"./input/3078.txt" 7L,119C 1,1 All

因此正确答案为
5 2 6 3 1 4 7

Phase 7 隐藏关

1
gdb-peda$ disassemble secret_phase 
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
89
90
91
92
93
94
95
Dump of assembler code for function secret_phase:
0x08049bb4 <+0>: endbr32
0x08049bb8 <+4>: push ebp
0x08049bb9 <+5>: mov ebp,esp
0x08049bbb <+7>: sub esp,0x18
0x08049bbe <+10>: call 0x8049f2a <read_line>
0x08049bc3 <+15>: mov DWORD PTR [ebp-0x14],eax
0x08049bc6 <+18>: sub esp,0xc
0x08049bc9 <+21>: push DWORD PTR [ebp-0x14]
0x08049bcc <+24>: call 0x8049220 <atoi@plt>
0x08049bd1 <+29>: add esp,0x10
0x08049bd4 <+32>: mov DWORD PTR [ebp-0x10],eax
0x08049bd7 <+35>: cmp DWORD PTR [ebp-0x10],0x0
0x08049bdb <+39>: jle 0x8049be6 <secret_phase+50>
0x08049bdd <+41>: cmp DWORD PTR [ebp-0x10],0x3e9
0x08049be4 <+48>: jle 0x8049bf2 <secret_phase+62>
0x08049be6 <+50>: call 0x804a071 <explode_bomb>
0x08049beb <+55>: mov eax,0x0
0x08049bf0 <+60>: jmp 0x8049c34 <secret_phase+128>
0x08049bf2 <+62>: sub esp,0x8
0x08049bf5 <+65>: push DWORD PTR [ebp-0x10]
0x08049bf8 <+68>: push 0x804d250 # 查看地址得到<n1>:36
0x08049bfd <+73>: call 0x8049b4d <fun7> # 调用fun7
0x08049c02 <+78>: add esp,0x10
0x08049c05 <+81>: mov DWORD PTR [ebp-0xc],eax
0x08049c08 <+84>: cmp DWORD PTR [ebp-0xc],0x3 # 判断返回值是否为0x3
0x08049c0c <+88>: je 0x8049c1a <secret_phase+102> # 是3则跳过爆炸,否则爆炸
0x08049c0e <+90>: call 0x804a071 <explode_bomb>
0x08049c13 <+95>: mov eax,0x0
0x08049c18 <+100>: jmp 0x8049c34 <secret_phase+128>
0x08049c1a <+102>: sub esp,0xc
0x08049c1d <+105>: push 0x804b220 # 查询内容为87
0x08049c22 <+110>: call 0x80491b0 <puts@plt>
0x08049c27 <+115>: add esp,0x10
0x08049c2a <+118>: call 0x804a09e <phase_defused>
0x08049c2f <+123>: mov eax,0x1
0x08049c34 <+128>: leave
0x08049c35 <+129>: ret
End of assembler dump.

gdb-peda$ disassemble phase_defused
Dump of assembler code for function phase_defused:
0x0804a09e <+0>: endbr32
0x0804a0a2 <+4>: push ebp
0x0804a0a3 <+5>: mov ebp,esp
0x0804a0a5 <+7>: sub esp,0x68
0x0804a0a8 <+10>: mov eax,gs:0x14
0x0804a0ae <+16>: mov DWORD PTR [ebp-0xc],eax
0x0804a0b1 <+19>: xor eax,eax
0x0804a0b3 <+21>: mov eax,ds:0x804d28c
0x0804a0b8 <+26>: cmp eax,0x7
0x0804a0bb <+29>: jne 0x804a134 <phase_defused+150>
0x0804a0bd <+31>: sub esp,0xc
0x0804a0c0 <+34>: lea eax,[ebp-0x5c]
0x0804a0c3 <+37>: push eax
0x0804a0c4 <+38>: lea eax,[ebp-0x64]
0x0804a0c7 <+41>: push eax
0x0804a0c8 <+42>: lea eax,[ebp-0x68]
0x0804a0cb <+45>: push eax
0x0804a0cc <+46>: push 0x804b31e # "%d %d %s"
0x0804a0d1 <+51>: push 0x804d3e0
0x0804a0d6 <+56>: call 0x80491f0 <__isoc99_sscanf@plt>
0x0804a0db <+61>: add esp,0x20
0x0804a0de <+64>: mov DWORD PTR [ebp-0x60],eax
0x0804a0e1 <+67>: cmp DWORD PTR [ebp-0x60],0x3
0x0804a0e5 <+71>: jne 0x804a124 <phase_defused+134>
0x0804a0e7 <+73>: sub esp,0x8
0x0804a0ea <+76>: push 0x804b327 # 内容为 "bcLjEp"
0x0804a0ef <+81>: lea eax,[ebp-0x5c]
0x0804a0f2 <+84>: push eax
0x0804a0f3 <+85>: call 0x8049df1 <strings_not_equal>
0x0804a0f8 <+90>: add esp,0x10
0x0804a0fb <+93>: test eax,eax
0x0804a0fd <+95>: jne 0x804a124 <phase_defused+134>
0x0804a0ff <+97>: sub esp,0xc
0x0804a102 <+100>: push 0x804b330
0x0804a107 <+105>: call 0x80491b0 <puts@plt>
0x0804a10c <+110>: add esp,0x10
0x0804a10f <+113>: sub esp,0xc
0x0804a112 <+116>: push 0x804b358
0x0804a117 <+121>: call 0x80491b0 <puts@plt>
0x0804a11c <+126>: add esp,0x10
0x0804a11f <+129>: call 0x8049bb4 <secret_phase>
0x0804a124 <+134>: sub esp,0xc
0x0804a127 <+137>: push 0x804b390
0x0804a12c <+142>: call 0x80491b0 <puts@plt>
0x0804a131 <+147>: add esp,0x10
0x0804a134 <+150>: nop
0x0804a135 <+151>: mov eax,DWORD PTR [ebp-0xc]
0x0804a138 <+154>: xor eax,DWORD PTR gs:0x14
0x0804a13f <+161>: je 0x804a146 <phase_defused+168>
0x0804a141 <+163>: call 0x8049190 <__stack_chk_fail@plt>
0x0804a146 <+168>: leave
0x0804a147 <+169>: ret
End of assembler dump.

根据查询内容推断可得,在phase4答案后输入bcLjEp,即可打开隐藏关入口,随后分析fun7

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
gdb-peda$ disassemble fun7
Dump of assembler code for function fun7:
0x08049b4d <+0>: endbr32
0x08049b51 <+4>: push ebp
0x08049b52 <+5>: mov ebp,esp
0x08049b54 <+7>: sub esp,0x8
0x08049b57 <+10>: cmp DWORD PTR [ebp+0x8],0x0
0x08049b5b <+14>: jne 0x8049b64 <fun7+23>
0x08049b5d <+16>: mov eax,0xffffffff
0x08049b62 <+21>: jmp 0x8049bb2 <fun7+101>
0x08049b64 <+23>: mov eax,DWORD PTR [ebp+0x8]
0x08049b67 <+26>: mov eax,DWORD PTR [eax]
0x08049b69 <+28>: cmp DWORD PTR [ebp+0xc],eax
0x08049b6c <+31>: jge 0x8049b87 <fun7+58>
0x08049b6e <+33>: mov eax,DWORD PTR [ebp+0x8]
0x08049b71 <+36>: mov eax,DWORD PTR [eax+0x4]
0x08049b74 <+39>: sub esp,0x8
0x08049b77 <+42>: push DWORD PTR [ebp+0xc]
0x08049b7a <+45>: push eax
0x08049b7b <+46>: call 0x8049b4d <fun7> # 递归调用fun7(eax,ebp+0xc)
0x08049b80 <+51>: add esp,0x10
0x08049b83 <+54>: add eax,eax
0x08049b85 <+56>: jmp 0x8049bb2 <fun7+101>
0x08049b87 <+58>: mov eax,DWORD PTR [ebp+0x8]
0x08049b8a <+61>: mov eax,DWORD PTR [eax]
0x08049b8c <+63>: cmp DWORD PTR [ebp+0xc],eax
0x08049b8f <+66>: jne 0x8049b98 <fun7+75>
0x08049b91 <+68>: mov eax,0x0
0x08049b96 <+73>: jmp 0x8049bb2 <fun7+101>
0x08049b98 <+75>: mov eax,DWORD PTR [ebp+0x8]
0x08049b9b <+78>: mov eax,DWORD PTR [eax+0x8]
0x08049b9e <+81>: sub esp,0x8
0x08049ba1 <+84>: push DWORD PTR [ebp+0xc]
0x08049ba4 <+87>: push eax
0x08049ba5 <+88>: call 0x8049b4d <fun7>
0x08049baa <+93>: add esp,0x10
0x08049bad <+96>: add eax,eax
0x08049baf <+98>: add eax,0x1
0x08049bb2 <+101>: leave
0x08049bb3 <+102>: ret
End of assembler dump.

编写以下代码模拟功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int fun7(int input_1, int input_2)
{
if (&input_1==NULL)
return -1;
int ret=0;
if (input_1-input_2>0)
{
ret=fun7(*(&input_1+4), input_2);
ret*=2
}
else if(input_1- input_2==0)
return 0;
else
{
ret=fun7(*(&input_1+8),input_2);
ret*=2+1;
}
return ret;
}

得到结果
107

一键拆弹方法

项目地址:
https://github.com/LinYuanChan/bomblab-auto-defuse

注意

此方法仅供学习交流使用, 不要用于正式评测及学术欺诈等不正当用途

实现方法

使用过程中发现, 提交评测时测试程序会检索bomb的输出,每得到一个正确的输出增加10分,因此只需要编写新的bomb,输出所有得分语句,并且替换即可一键拆弹

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include<iostream>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

char* read_line()
{
return "001";
}

FILE *infile;
int main(int argc, char *argv[])
{


infile = stdin;


char *input;

/* Note to self: remember to port this bomb to Windows and put a
* fantastic GUI on it. */

/* When run with no arguments, the bomb reads its input lines
* from standard input. */
if (argc == 1) {
infile = stdin;
}

/* When run with one argument <file>, the bomb reads from <file>
* until EOF, and then switches to standard input. Thus, as you
* defuse each phase, you can add its defusing string to <file> and
* avoid having to retype it. */
else if (argc == 2) {
if (!(infile = fopen(argv[1], "r"))) {
printf("%s: Error: Couldn't open %s\n", argv[0], argv[1]);
exit(8);
}
}

/* You can't call the bomb with more than 1 command line argument. */
else {
printf("Usage: %s [<input_file>]\n", argv[0]);
exit(8);
}

/* Do all sorts of secret stuff that makes the bomb harder to defuse. */
//initialize_bomb();

printf("Welcome to my fiendish little bomb. You have 7 phases with\n");
printf("which to blow yourself up. Have a nice day!\n");

/* Warm up phase! */
input = read_line(); /* Get input */
// if( phase_0(input) ) { /* Run the phase */
// phase_defused(); /* Drat! They figured it out! */
printf("Well done! You seem to have warmed up!\n");
// }

/* Hmm... Six phases must be more secure than one phase! */
input = read_line(); /* Get input */
// if( phase_1(input) ) { /* Run the phase */
// phase_defused(); /* Drat! They figured it out! Let me know how they did it. */
printf("Phase 1 defused. How about the next one?\n");
// }

/* The second phase is harder. No one will ever figure out
* how to defuse this... */
input = read_line();
// if( phase_2(input) ) {
// phase_defused();
printf("That's number 2. Keep going!\n");
// }

/* I guess this is too easy so far. Some more complex code will
* confuse people. */
input = read_line();
// if( phase_3(input) ) {
// phase_defused();
printf("Halfway there!\n");
// }

/* Oh yeah? Well, how good is your math? Try on this saucy problem! */
input = read_line();
// if( phase_4(input) ) {
// phase_defused();
printf("So you got that one. Try this one.\n");
// }

/* Round and 'round in memory we go, where we stop, the bomb blows! */
input = read_line();
// if( phase_5(input) ) {
// phase_defused();
printf("Good work! On to the next...\nCurses, you've found the secret phase!\nBut finding it and solving it are quite different...\n");
// }

/* This phase will never be used, since no one will get past the
* earlier ones. But just in case, make this one extra hard. */
input = read_line();
// if( phase_6(input) ) {
// phase_defused();
printf("Wow! You've defused the secret stage!\nCongratulations! You've defused the bomb!");
// }

/* Wow, they got it! But isn't something... missing? Perhaps
* something they overlooked? Mua ha ha ha ha! */

return 0;
// return 0;
}

为了达到~~真 一键拆弹~~的效果, 再编写一个shell脚本,自动执行编译和替换文件

1
2
3
4
5
6
#!/bin/bash
cd ~
git clone git@github.com:LinYuanChan/bomblab-auto-defuse.git
mv ./bomblab-auto-defuse/auto_defuse.sh ./auto_defuse.sh
chmod +x auto_defuse.sh
./auto_defuse.sh

完成后执行脚本即可