Bomb Lab 在这里插入代码片
phase_1
0000000000001254 <phase_1>:
1254: 48 83 ec 08 sub $0x8,%rsp
1258: 48 8d 35 31 18 00 00 lea 0x1831(%rip),%rsi # 2a90 <_IO_stdin_used+0x150>
125f: e8 ed 04 00 00 callq 1751 <strings_not_equal>
1264: 85 c0 test %eax,%eax
1266: 75 05 jne 126d <phase_1+0x19>
1268: 48 83 c4 08 add $0x8,%rsp
126c: c3 retq
126d: e8 e3 07 00 00 callq 1a55 <explode_bomb>
1272: eb f4 jmp 1268 <phase_1+0x14>
结合1258和125f 我们可以猜测0x1831(%rip)处存放了本phase的答案
利用gdb工具查看%rsi处存的地址,再使用gdb打印
\000是字符串数组的终止符,故该字符串为 There are rumors on the internets.
因此本phase答案为该字符串
phase_2
0000000000001274 <phase_2>:
1274: 55 push %rbp
1275: 53 push %rbx
1276: 48 83 ec 28 sub $0x28,%rsp
127a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
1281: 00 00
1283: 48 89 44 24 18 mov %rax,0x18(%rsp)
1288: 31 c0 xor %eax,%eax
128a: 48 89 e6 mov %rsp,%rsi
128d: e8 ff 07 00 00 callq 1a91 <read_six_numbers>
1292: 83 3c 24 01 cmpl $0x1,(%rsp) #第一个数字和1相比,%rsp处存的是第一个数字的地址
1296: 75 09 jne 12a1 <phase_2+0x2d>
1298: 48 89 e3 mov %rsp,%rbx #用%rbx存第一个数字的地址
129b: 48 8d 6b 14 lea 0x14(%rbx),%rbp #%rbp存最后一个数字的地址
129f: eb 10 jmp 12b1 <phase_2+0x3d>
12a1: e8 af 07 00 00 callq 1a55 <explode_bomb>
12a6: eb f0 jmp 1298 <phase_2+0x24>
12a8: 48 83 c3 04 add $0x4,%rbx #更新%rbx为第二个数字的地址
12ac: 48 39 eb cmp %rbp,%rbx #
12af: 74 10 je 12c1 <phase_2+0x4d>
12b1: 8b 03 mov (%rbx),%eax #%eax存第一个数字 #%eax存第二个数字
12b3: 01 c0 add %eax,%eax #%eax等于所存数字的2倍 #%eax等于所存数字的2倍
12b5: 39 43 04 cmp %eax,0x4(%rbx) #第二个数字与第一个的2倍相比 #第三个与第二个相比
12b8: 74 ee je 12a8 <phase_2+0x34>
12ba: e8 96 07 00 00 callq 1a55 <explode_bomb>
12bf: eb e7 jmp 12a8 <phase_2+0x34>
12c1: 48 8b 44 24 18 mov 0x18(%rsp),%rax
12c6: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
12cd: 00 00
12cf: 75 07 jne 12d8 <phase_2+0x64>
12d1: 48 83 c4 28 add $0x28,%rsp
12d5: 5b pop %rbx
12d6: 5d pop %rbp
12d7: c3 retq
12d8: e8 b3 fb ff ff callq e90 <__stack_chk_fail@plt>
通过128d行 <read_six_numbers>可以推断出,本关的密码为六个数字
- 由于参数入栈的顺序是从右向左,所以(%rsp)到0x14(%rsp)所存的是从第一个参数到第六个参数
- 1292和1296行结合来看,如果第一个数字不是1就爆炸,故第一个数字为1
- 1298行用%rbx来存第一个数字的地址
- 129b行用%rbp来存最后一个数字的地址
进入循环
- 判断%eax中存的数字的两倍是否与输入中的下一个数字相等
- 不等于就爆炸
- 相等进入下一次循环
- 更新%rbx为下一个数字的地址
即以上循环在判断输入的数字是否为一个公比为2,首项为1的等比数列
故密码为 1 2 4 8 16 32
phase_3
00000000000012dd <phase_3>:
12dd: 48 83 ec 18 sub $0x18,%rsp
12e1: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
12e8: 00 00
12ea: 48 89 44 24 08 mov %rax,0x8(%rsp)
12ef: 31 c0 xor %eax,%eax
12f1: 48 8d 4c 24 04 lea 0x4(%rsp),%rcx
12f6: 48 89 e2 mov %rsp,%rdx
12f9: 48 8d 35 8d 1a 00 00 lea 0x1a8d(%rip),%rsi # 2d8d <array.3415+0x28d>
1300: e8 2b fc ff ff callq f30 <__isoc99_sscanf@plt>
1305: 83 f8 01 cmp $0x1,%eax
1308: 7e 1d jle 1327 <phase_3+0x4a> # <=1 直接爆炸
130a: 83 3c 24 07 cmpl $0x7,(%rsp)
130e: 0f 87 99 00 00 00 ja 13ad <phase_3+0xd0> # > 7 直接爆炸
1314: 8b 04 24 mov (%rsp),%eax # 将第一个数字存到%eax
1317: 48 8d 15 c2 17 00 00 lea 0x17c2(%rip),%rdx # 2ae0 <_IO_stdin_used+0x1a0>
131e: 48 63 04 82 movslq (%rdx,%rax,4),%rax
1322: 48 01 d0 add %rdx,%rax
1325: ff e0 jmpq *%rax
1327: e8 29 07 00 00 callq 1a55 <explode_bomb>
132c: eb dc jmp 130a <phase_3+0x2d>
132e: b8 0d 03 00 00 mov $0x30d,%eax
1333: eb 05 jmp 133a <phase_3+0x5d>
1335: b8 00 00 00 00 mov $0x0,%eax
133a: 2d 62 03 00 00 sub $0x362,%eax
133f: 05 c5 02 00 00 add $0x2c5,%eax
1344: 2d fa 00 00 00 sub $0xfa,%eax
1349: 05 fa 00 00 00 add $0xfa,%eax
134e: 2d fa 00 00 00 sub $0xfa,%eax
1353: 05 fa 00 00 00 add $0xfa,%eax
1358: 2d fa 00 00 00 sub $0xfa,%eax
135d: 83 3c 24 05 cmpl $0x5,(%rsp)
1361: 7f 06 jg 1369 <phase_3+0x8c> # >5 bomb!
1363: 39 44 24 04 cmp %eax,0x4(%rsp)
1367: 74 05 je 136e <phase_3+0x91>
1369: e8 e7 06 00 00 callq 1a55 <explode_bomb>
136e: 48 8b 44 24 08 mov 0x8(%rsp),%rax
1373: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
137a: 00 00
137c: 75 3b jne 13b9 <phase_3+0xdc>
137e: 48 83 c4 18 add $0x18,%rsp
1382: c3 retq
1383: b8 00 00 00 00 mov $0x0,%eax
1388: eb b5 jmp 133f <phase_3+0x62> #case 2
138a: b8 00 00 00 00 mov $0x0,%eax
138f: eb b3 jmp 1344 <phase_3+0x67>
1391: b8 00 00 00 00 mov $0x0,%eax
1396: eb b1 jmp 1349 <phase_3+0x6c>
1398: b8 00 00 00 00 mov $0x0,%eax
139d: eb af jmp 134e <phase_3+0x71>
139f: b8 00 00 00 00 mov $0x0,%eax
13a4: eb ad jmp 1353 <phase_3+0x76>
13a6: b8 00 00 00 00 mov $0x0,%eax
13ab: eb ab jmp 1358 <phase_3+0x7b>
13ad: e8 a3 06 00 00 callq 1a55 <explode_bomb>
13b2: b8 00 00 00 00 mov $0x0,%eax
13b7: eb a4 jmp 135d <phase_3+0x80>
13b9: e8 d2 fa ff ff callq e90 <__stack_chk_fail@plt>
判断输入类型
- 结合12f9和1300可以推测%rsi中存了scanf的参数
- 通过gdb打印(%rsi)得到%d %d,故scanf的参数为两个int
- 1305 1308中scanf返回结果如果<=1直接爆炸,验证了我们的参数为2个
函数主体
- 130a和130e能够确定第一个参数值不能大于7
- 从1317到1325可以推测此后进入了switch语句,第一个参数是switch的参数
- 通过试探,发现输入第一个参数为2时进入1383行,即1383行对应case2
- 1388 jmp到133f
- 从133f到1358的计算 %eax = 0x362 - 0xfa = 459
- 1363和1367比较了第二个参数和%eax,相同则返回
故 2 459为此switch的一组解答
phase_4
00000000000013be <func4>:
13be: b8 00 00 00 00 mov $0x0,%eax
13c3: 85 ff test %edi,%edi
13c5: 7e 07 jle 13ce <func4+0x10> # %edi <= 0 return
13c7: 89 f0 mov %esi,%eax # %eax = 第2个数字
13c9: 83 ff 01 cmp $0x1,%edi # %edi初始值 = 7
13cc: 75 02 jne 13d0 <func4+0x12>
13ce: f3 c3 repz retq
13d0: 41 54 push %r12
13d2: 55 push %rbp
13d3: 53 push %rbx
13d4: 41 89 f4 mov %esi,%r12d # %r12d = 第2个参数
13d7: 89 fb mov %edi,%ebx # %ebx = %edi
13d9: 8d 7f ff lea -0x1(%rdi),%edi # %edi --
13dc: e8 dd ff ff ff callq 13be <func4>
13e1: 42 8d 2c 20 lea (%rax,%r12,1),%ebp
13e5: 8d 7b fe lea -0x2(%rbx),%edi
13e8: 44 89 e6 mov %r12d,%esi
13eb: e8 ce ff ff ff callq 13be <func4>
13f0: 01 e8 add %ebp,%eax
13f2: 5b pop %rbx
13f3: 5d pop %rbp
13f4: 41 5c pop %r12
13f6: c3 retq
00000000000013f7 <phase_4>:
13f7: 48 83 ec 18 sub $0x18,%rsp
13fb: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
1402: 00 00
1404: 48 89 44 24 08 mov %rax,0x8(%rsp)
1409: 31 c0 xor %eax,%eax
140b: 48 89 e1 mov %rsp,%rcx
140e: 48 8d 54 24 04 lea 0x4(%rsp),%rdx
1413: 48 8d 35 73 19 00 00 lea 0x1973(%rip),%rsi # 2d8d <array.3415+0x28d>
141a: e8 11 fb ff ff callq f30 <__isoc99_sscanf@plt>
141f: 83 f8 02 cmp $0x2,%eax
1422: 75 0b jne 142f <phase_4+0x38>
1424: 8b 04 24 mov (%rsp),%eax
1427: 83 e8 02 sub $0x2,%eax
142a: 83 f8 02 cmp $0x2,%eax
142d: 76 05 jbe 1434 <phase_4+0x3d> # <=
142f: e8 21 06 00 00 callq 1a55 <explode_bomb> # (第2个数字)>4 bomb!
1434: 8b 34 24 mov (%rsp),%esi # %esi=第2个数字
1437: bf 07 00 00 00 mov $0x7,%edi
143c: e8 7d ff ff ff callq 13be <func4>
1441: 39 44 24 04 cmp %eax,0x4(%rsp) # 返回值和第1个参数相等
1445: 74 05 je 144c <phase_4+0x55>
1447: e8 09 06 00 00 callq 1a55 <explode_bomb>
144c: 48 8b 44 24 08 mov 0x8(%rsp),%rax
1451: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
1458: 00 00
145a: 75 05 jne 1461 <phase_4+0x6a>
145c: 48 83 c4 18 add $0x18,%rsp
1460: c3 retq
1461: e8 2a fa ff ff callq e90 <__stack_chk_fail@plt>
输入数据类型
- 通过1413和141a发现和上题一样用了scanf,且注释中 2d8d 都一样,故可知此题也需要2个int作为输入
- 141f和1422判断scanf 的返回值是否为2 验证了需要两个数字输入
函数主体
- 1424到142f判断第二个参数是否大于4,如果大于4就explode
- 调用func4函数,将7作为第一个参数,将输入的第二个数值作为第一个参数
- 接着比较输入的第一个数字和func4的返回结果是否相等,不等就bomb
- 故输入的第一个数字应该为func4(7,第二个参数)的返回结果
func4
-
通过13dc、13eb两行反复调用func4本身可以看出func4是个递归函数
-
从13be到13ce可以看出该函数的递归边界为
- 若%edi <= 0 ,返回值%eax = 0
- 若%edi = 1,返回值%eax = %esi
int func4(int edi,int esi){ if(edi == 0)return 0; if(edi == 1)return esi; return func4(edi-1,esi)+func4(edi-2,esi); }
经过测试在第二个参数为3的情况下,用gdb打印出执行func4后的结果为99
故一组解答为 99 3
phase_5
0000000000001466 <phase_5>:
1466: 48 83 ec 18 sub $0x18,%rsp
146a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
1471: 00 00
1473: 48 89 44 24 08 mov %rax,0x8(%rsp)
1478: 31 c0 xor %eax,%eax
147a: 48 8d 4c 24 04 lea 0x4(%rsp),%rcx
147f: 48 89 e2 mov %rsp,%rdx
1482: 48 8d 35 04 19 00 00 lea 0x1904(%rip),%rsi # 2d8d <array.3415+0x28d>
1489: e8 a2 fa ff ff callq f30 <__isoc99_sscanf@plt>
148e: 83 f8 01 cmp $0x1,%eax # 参数数量<=1 explode
1491: 7e 5a jle 14ed <phase_5+0x87>
1493: 8b 04 24 mov (%rsp),%eax
1496: 83 e0 0f and $0xf,%eax #取低四位的a1
1499: 89 04 24 mov %eax,(%rsp)
149c: 83 f8 0f cmp $0xf,%eax #低四位为0xf就bomb
149f: 74 32 je 14d3 <phase_5+0x6d>
14a1: b9 00 00 00 00 mov $0x0,%ecx
14a6: ba 00 00 00 00 mov $0x0,%edx
14ab: 48 8d 35 4e 16 00 00 lea 0x164e(%rip),%rsi # 2b00 <array.3415>
14b2: 83 c2 01 add $0x1,%edx #olution.
14b5: 48 98 cltq
14b7: 8b 04 86 mov (%rsi,%rax,4),%eax #4个字节所以要乘4
14ba: 01 c1 add %eax,%ecx
14bc: 83 f8 0f cmp $0xf,%eax #15次以内不能取到0xf
14bf: 75 f1 jne 14b2 <phase_5+0x4c>
14c1: c7 04 24 0f 00 00 00 movl $0xf,(%rsp)
14c8: 83 fa 0f cmp $0xf,%edx
14cb: 75 06 jne 14d3 <phase_5+0x6d> #edx == 0xf 循环15次
14cd: 39 4c 24 04 cmp %ecx,0x4(%rsp) #ecx == a2
14d1: 74 05 je 14d8 <phase_5+0x72>
14d3: e8 7d 05 00 00 callq 1a55 <explode_bomb>
14d8: 48 8b 44 24 08 mov 0x8(%rsp),%rax
14dd: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
14e4: 00 00
14e6: 75 0c jne 14f4 <phase_5+0x8e>
14e8: 48 83 c4 18 add $0x18,%rsp
14ec: c3 retq
14ed: e8 63 05 00 00 callq 1a55 <explode_bomb>
14f2: eb 9f jmp 1493 <phase_5+0x2d>
14f4: e8 97 f9 ff ff callq e90 <__stack_chk_fail@plt>
首先通过1489行推断需要的输入类型
- 看到1482行中注释是2d8d,意识到输入和前两个phase一样,都是“%d %d”
- 148e行sscanf返回的参数如果小于等于1则explode验证了需要输入两个参数
接下来进入循环
-
1593行开始 进入前先进行判断:取第一个参数的最后4位如果是0xf则bomb
-
进入循环,14b2表面%edx是循环变量i
-
循环过程中(14b7)跟新%eax的值(进入时为第一个参数)
-
根据14ab行提示,发现2b00处存了一个大小为16的int数组
- 用gdb查看,发现数组为{a 2 e 7 8 c f b 0 4 1 d 3 9 6 5}
-
14b7中每次将%eax的值跟新为数组中对应位置所存储的数值
-
14ba处用%ecx计算每一次%eax的累加值
-
14bc处验证在%eax是否为0xf
- 如果等于,再去验证%edx是否为0xf,即判断是否是在循环第十五次取得oxf的数值
-
满足第十五次循环取得oxf的情况下再去看%ecx的值与第二个参数是否相等
接下来推理输入数据
由于第十五次需要取得f,根据数组逆向寻找第一次的数据,发现结果为5
接下来寻找十五次结果的累加值,先随便输入第二个参数,通过gdb工具查看十五次循环以后%ecx的值,发现是115
即答案为 5 115
phase_6
00000000000014f9 <phase_6>:
14f9: 41 55 push %r13
14fb: 41 54 push %r12
14fd: 55 push %rbp
14fe: 53 push %rbx
14ff: 48 83 ec 68 sub $0x68,%rsp
1503: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
150a: 00 00
150c: 48 89 44 24 58 mov %rax,0x58(%rsp)
1511: 31 c0 xor %eax,%eax
1513: 49 89 e4 mov %rsp,%r12
1516: 4c 89 e6 mov %r12,%rsi
1519: e8 73 05 00 00 callq 1a91 <read_six_numbers>
151e: 41 bd 00 00 00 00 mov $0x0,%r13d
1524: eb 25 jmp 154b <phase_6+0x52>
1526: e8 2a 05 00 00 callq 1a55 <explode_bomb>
152b: eb 2d jmp 155a <phase_6+0x61>
152d: 83 c3 01 add $0x1,%ebx
1530: 83 fb 05 cmp $0x5,%ebx
1533: 7f 12 jg 1547 <phase_6+0x4e>
1535: 48 63 c3 movslq %ebx,%rax
1538: 8b 04 84 mov (%rsp,%rax,4),%eax
153b: 39 45 00 cmp %eax,0x0(%rbp)
153e: 75 ed jne 152d <phase_6+0x34>
1540: e8 10 05 00 00 callq 1a55 <explode_bomb>
1545: eb e6 jmp 152d <phase_6+0x34>
1547: 49 83 c4 04 add $0x4,%r12
154b: 4c 89 e5 mov %r12,%rbp
154e: 41 8b 04 24 mov (%r12),%eax
1552: 83 e8 01 sub $0x1,%eax
1555: 83 f8 05 cmp $0x5,%eax
1558: 77 cc ja 1526 <phase_6+0x2d>
155a: 41 83 c5 01 add $0x1,%r13d
155e: 41 83 fd 06 cmp $0x6,%r13d
1562: 74 35 je 1599 <phase_6+0xa0>
1564: 44 89 eb mov %r13d,%ebx
1567: eb cc jmp 1535 <phase_6+0x3c>
1569: 48 8b 52 08 mov 0x8(%rdx),%rdx
156d: 83 c0 01 add $0x1,%eax
1570: 39 c8 cmp %ecx,%eax
1572: 75 f5 jne 1569 <phase_6+0x70>
1574: 48 89 54 f4 20 mov %rdx,0x20(%rsp,%rsi,8)
1579: 48 83 c6 01 add $0x1,%rsi
157d: 48 83 fe 06 cmp $0x6,%rsi
1581: 74 1d je 15a0 <phase_6+0xa7>
1583: 8b 0c b4 mov (%rsp,%rsi,4),%ecx
1586: b8 01 00 00 00 mov $0x1,%eax
158b: 48 8d 15 9e 2c 20 00 lea 0x202c9e(%rip),%rdx # 204230 <node1>
1592: 83 f9 01 cmp $0x1,%ecx
1595: 7f d2 jg 1569 <phase_6+0x70>
1597: eb db jmp 1574 <phase_6+0x7b>
1599: be 00 00 00 00 mov $0x0,%esi
159e: eb e3 jmp 1583 <phase_6+0x8a>
15a0: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx
15a5: 48 8b 44 24 28 mov 0x28(%rsp),%rax
15aa: 48 89 43 08 mov %rax,0x8(%rbx)
15ae: 48 8b 54 24 30 mov 0x30(%rsp),%rdx
15b3: 48 89 50 08 mov %rdx,0x8(%rax)
15b7: 48 8b 44 24 38 mov 0x38(%rsp),%rax
15bc: 48 89 42 08 mov %rax,0x8(%rdx)
15c0: 48 8b 54 24 40 mov 0x40(%rsp),%rdx
15c5: 48 89 50 08 mov %rdx,0x8(%rax)
15c9: 48 8b 44 24 48 mov 0x48(%rsp),%rax
15ce: 48 89 42 08 mov %rax,0x8(%rdx)
15d2: 48 c7 40 08 00 00 00 movq $0x0,0x8(%rax)
15d9: 00
15da: bd 05 00 00 00 mov $0x5,%ebp
15df: eb 09 jmp 15ea <phase_6+0xf1>
15e1: 48 8b 5b 08 mov 0x8(%rbx),%rbx
15e5: 83 ed 01 sub $0x1,%ebp
15e8: 74 11 je 15fb <phase_6+0x102>
15ea: 48 8b 43 08 mov 0x8(%rbx),%rax
15ee: 8b 00 mov (%rax),%eax
15f0: 39 03 cmp %eax,(%rbx)
15f2: 7d ed jge 15e1 <phase_6+0xe8>
15f4: e8 5c 04 00 00 callq 1a55 <explode_bomb>
15f9: eb e6 jmp 15e1 <phase_6+0xe8>
15fb: 48 8b 44 24 58 mov 0x58(%rsp),%rax
1600: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
1607: 00 00
1609: 75 0b jne 1616 <phase_6+0x11d>
160b: 48 83 c4 68 add $0x68,%rsp
160f: 5b pop %rbx
1610: 5d pop %rbp
1611: 41 5c pop %r12
1613: 41 5d pop %r13
1615: c3 retq
1616: e8 75 f8 ff ff callq e90 <__stack_chk_fail@plt>
读入数据
- 从1519可知要读入6个数字
验证数据
-
154b到1555保证了每个数字都小于等于6
-
1535到153e保证了六个数字互不相同
整个过程写成c语言为
for(i= 0; i <= 5; i++){ if(a[i] >6) explode_bomb(); for(j=i+1; j<=5; j++){ if(a[i] == a[j]) explode_bomb(); }
-
15a0到15da根据输入到六个数字将链表重新排序
- 重新建立链表
- 存储原本链表中输入数字位置的元素
for(i=0; i<=5; i++){ p = list; for(j=1; j<a[i]; j++) p = p->next; list_array[i]=p; }
-
15e1到15f2的循环用来判读新的链表是否按照元素的降序排列
p = list_array; for(i=0; i<=4; i++){ if(*p <*p->next) explode_bomb(); p = p->next; }
得到正确序列
- 利用gdb工具 当程序执行到这一行一行 查看%rdx存储的地址
158b: 48 8d 15 9e 2c 20 00 lea 0x202c9e(%rip),%rdx # 204230 <node1>
-
从而得到 node1 的地址为0x555555758230
-
用gdb将它打印出来
-
该数据结构分为 int value int index node*next
-
故得到node1以后可以得到所有node
-
根据value值对index排序得到1 5 6 4 3 2
所以最后一题答案为1 5 6 4 3 2