总共7个phase,非常好玩…
进入每一个phase时,我们应该弄清楚的是:
1、输入格式:需要我们输入什么,是字符串,还是整数,还是字符,输入个数是多少?
2、爆炸点:哪些语句可能引起爆炸,通常为一个比较语句cmp,这是解题的关键,告诉应该输入的值是多少。因此,找到这些爆炸点即可能跳到<explode_bomb>爆炸函数的语句,在这些地方打上断点哦。
3、循环、递归:当发现jump会往之前的语句跳时,可能是循环或者递归,建议在这段代码做个标记,弄清楚每一段在做什么操作。
4、每个寄存器是用来干什么的:
最常使用的gdb语句:
1、b+空格+地址/函数名打断点
2、x /s+地址/寄存器
打印该地址处的字符串,s可以换成其他字母,d打印十进制整数,c打印字符,x打印十六进制整数。
3、i r
查看寄存器的值(同info r
)。
4、stepi
单步进入,不跳过子函数;nexti
跳过子函数,逐步执行主函数。
5、continue
跳到下一个断点。
6、run
开始运行。
另外!!可以画图帮助理解!!
命令行中进入实验,然后打开gdb开始调试:
gdb ./bomb
然后在每个炸弹处打断点:phase_1 到 phase_6
(gdb) b phase_1
phase_1
000000000400ead <phase_1>:
400ead: 48 83 ec 08 sub $0x8,%rsp
400eb1: be 8c 24 40 00 mov $0x40248c,%esi
400eb6: e8 4b 05 00 00 callq 401406 <strings_not_equal>
400ebb: 85 c0 test %eax,%eax
400ebd: 74 05 je 400ec4 <phase_1+0x17>
400ebf: e8 41 06 00 00 callq 401505 <explode_bomb>
400ec4: 48 83 c4 08 add $0x8,%rsp
400ec8: c3 retq
进入phase_1后,第二行看到比较输入参数和存储在地址 0x40248c 处的值,gdb调试中输入并得到:
(gdb) x /s 0x40248c
0x40248c: "Public speaking is very easy."
答案 :Public speaking is very easy.
phase_2
小 tips:先把炸弹找出来,然后在可能跳到炸弹的地方打断点。
000000000400ec9 <phase_2>:
400ec9: 55 push %rbp
400eca: 53 push %rbx
400ecb: 48 83 ec 28 sub $0x28,%rsp
400ecf: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
400ed6: 00 00
400ed8: 48 89 44 24 18 mov %rax,0x18(%rsp)
400edd: 31 c0 xor %eax,%eax
400edf: 48 89 e6 mov %rsp,%rsi
400ee2: e8 40 06 00 00 callq 401527 <read_six_numbers>
400ee7: 83 3c 24 00 cmpl $0x0,(%rsp) //x1=0
400eeb: 75 07 jne 400ef4 <phase_2+0x2b>
400eed: 83 7c 24 04 01 cmpl $0x1,0x4(%rsp) //x2=1
400ef2: 74 05 je 400ef9 <phase_2+0x30>
400ef4: e8 0c 06 00 00 callq 401505 <explode_bomb>
400ef9: 48 89 e3 mov %rsp,%rbx
400efc: 48 8d 6c 24 10 lea 0x10(%rsp),%rbp//x5
//循环
400f01: 8b 43 04 mov 0x4(%rbx),%eax//x2 x3..x5
400f04: 03 03 add (%rbx),%eax //x1+x2 x2+x3.. x4+x5
400f06: 39 43 08 cmp %eax,0x8(%rbx)
//x3=x1+x2=1 x4=x2+x3=2 x5=x3+x4 x6=x4+x5=5
400f09: 74 05 je 400f10 <phase_2+0x47>
400f0b: e8 f5 05 00 00 callq 401505 <explode_bomb>
400f10: 48 83 c3 04 add $0x4,%rbx//x2->x5
400f14: 48 39 eb cmp %rbp,%rbx//比较x2和x5 x3和x5..
400f17: 75 e8 jne 400f01 <phase_2+0x38>
//循环结束
400f19: 48 8b 44 24 18 mov 0x18(%rsp),%rax
400f1e: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
400f25: 00 00
400f27: 74 05 je 400f2e <phase_2+0x65>
400f29: e8 f2 fb ff ff callq 400b20 <__stack_chk_fail@plt>
400f2e: 48 83 c4 28 add $0x28,%rsp
400f32: 5b pop %rbx
400f33: 5d pop %rbp
400f34: c3 retq
前5行和后7行可以跳过,callq 401527 <read_six_numbers>
告诉我们读取6个数,猜测输入为6个整数。
往下看,有给在栈中,栈顶保存的值应为x1=0,向上x2=1,否则会jump到
401505 <explode_bomb>
炸弹爆炸。
然后在%rbp中保留了x5,即lea 0x10(%rsp),%rbp
0x10是16,栈顶为x1,每4位一个整数,那么0x10(%rsp)
就是x5,然后进入循环,循环过程打在代码数字里面了,最后发现是斐波那契数列的前六个数。
答案 : 0 1 1 2 3 5
phase_3
粗略地总体看一下,中间有很多400f8e: 00
类似的语句,然后它们之间的代码好像做的都是差不多是操作。
000000000400f35 <phase_3>:
400f35: 48 83 ec 28 sub $0x28,%rsp
400f39: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
400f40: 00 00
400f42: 48 89 44 24 18 mov %rax,0x18(%rsp)
400f47: 31 c0 xor %eax,%eax
400f49: 4c 8d 44 24 14 lea 0x14(%rsp),%r8
400f4e: 48 8d 4c 24 0f lea 0xf(%rsp),%rcx
400f53: 48 8d 54 24 10 lea 0x10(%rsp),%rdx
400f58: be aa 24 40 00 mov $0x4024aa,%esi // "%d %c %d"
400f5d: e8 7e fc ff ff callq 400be0 <__isoc99_sscanf@plt>
400f62: 83 f8 02 cmp $0x2,%eax //输入的东西个数为3
400f65: 7f 05 jg 400f6c <phase_3+0x37>
400f67: e8 99 05 00 00 callq 401505 <explode_bomb>
400f6c: 83 7c 24 10 07 cmpl $0x7,0x10(%rsp)
400f71: 0f 87 fc 00 00 00 ja 401073 <phase_3+0x13e> //不可以跳<=7
400f77: 8b 44 24 10 mov 0x10(%rsp),%eax
400f7b: ff 24 c5 c0 24 40 00 jmpq *0x4024c0(,%rax,8)
400f82: b8 77 00 00 00 mov $0x77,%eax
400f87: 81 7c 24 14 91 03 00 cmpl $0x391,0x14(%rsp) //相等
400f8e: 00
400f8f: 0f 84 e8 00 00 00 je 40107d <phase_3+0x148>
400f95: e8 6b 05 00 00 callq 401505 <explode_bomb>
400f9a: b8 77 00 00 00 mov $0x77,%eax
400f9f: e9 d9 00 00 00 jmpq 40107d <phase_3+0x148>
400fa4: b8 6a 00 00 00 mov $0x6a,%eax
400fa9: 81 7c 24 14 f6 02 00 cmpl $0x2f6,0x14(%rsp)
400fb0: 00
400fb1: 0f 84 c6 00 00 00 je 40107d <phase_3+0x148>
400fb7: e8 49 05 00 00 callq 401505 <explode_bomb>
400fbc: b8 6a 00 00 00 mov $0x6a,%eax
400fc1: e9 b7 00 00 00 jmpq 40107d <phase_3+0x148>
400fc6: b8 61 00 00 00 mov $0x61,%eax
400fcb: 81 7c 24 14 7d 03 00 cmpl $0x37d,0x14(%rsp)
400fd2: 00
400fd3: 0f 84 a4 00 00 00 je 40107d <phase_3+0x148>
400fd9: e8 27 05 00 00 callq 401505 <explode_bomb>
400fde: b8 61 00 00 00 mov $0x61,%eax
400fe3: e9 95 00 00 00 jmpq 40107d <phase_3+0x148>
400fe8: b8 65 00 00 00 mov $0x65,%eax
400fed: 81 7c 24 14 85 00 00 cmpl $0x85,0x14(%rsp)
400ff4: 00
400ff5: 0f 84 82 00 00 00 je 40107d <phase_3+0x148>
400ffb: e8 05 05 00 00 callq 401505 <explode_bomb>
401000: b8 65 00 00 00 mov $0x65,%eax
401005: eb 76 jmp 40107d <phase_3+0x148>
401007: b8 64 00 00 00 mov $0x64,%eax
40100c: 81 7c 24 14 f3 00 00 cmpl $0xf3,0x14(%rsp)
401013: 00
401014: 74 67 je 40107d <phase_3+0x148>
401016: e8 ea 04 00 00 callq 401505 <explode_bomb>
40101b: b8 64 00 00 00 mov $0x64,%eax
401020: eb 5b jmp 40107d <phase_3+0x148>
401022: b8 6f 00 00 00 mov $0x6f,%eax
401027: 81 7c 24 14 9d 03 00 cmpl $0x39d,0x14(%rsp)
40102e: 00
40102f: 74 4c je 40107d <phase_3+0x148>
401031: e8 cf 04 00 00 callq 401505 <explode_bomb>
401036: b8 6f 00 00 00 mov $0x6f,%eax
40103b: eb 40 jmp 40107d <phase_3+0x148>
40103d: b8 6e 00 00 00 mov $0x6e,%eax
401042: 81 7c 24 14 bf 01 00 cmpl $0x1bf,0x14(%rsp)
401049: 00
40104a: 74 31 je 40107d <phase_3+0x148>
40104c: e8 b4 04 00 00 callq 401505 <explode_bomb>
401051: b8 6e 00 00 00 mov $0x6e,%eax
401056: eb 25 jmp 40107d <phase_3+0x148>
401058: b8 6b 00 00 00 mov $0x6b,%eax
40105d: 81 7c 24 14 a0 01 00 cmpl $0x1a0,0x14(%rsp)
401064: 00 0
401065: 74 16 je 40107d <phase_3+0x148>
401067: e8 99 04 00 00 callq 401505 <explode_bomb>
40106c: b8 6b 00 00 00 mov $0x6b,%eax
401071: eb 0a jmp 40107d <phase_3+0x148>
401073: e8 8d 04 00 00 callq 401505 <explode_bomb>
401078: b8 62 00 00 00 mov $0x62,%eax
40107d: 3a 44 24 0f cmp 0xf(%rsp),%al //相等
401081: 74 05 je 401088 <phase_3+0x153>
401083: e8 7d 04 00 00 callq 401505 <explode_bomb>
401088: 48 8b 44 24 18 mov 0x18(%rsp),%rax
40108d: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
401094: 00 00
401096: 74 05 je 40109d <phase_3+0x168>
401098: e8 83 fa ff ff callq 400b20 <__stack_chk_fail@plt>
40109d: 48 83 c4 28 add $0x28,%rsp
4010a1: c3 retq
一开始 0x400f49->0x400f53 很令人迷惑,不知道在干嘛,继续往下看。
看了后四行恍然大悟,看到 mov $0x4024aa,%esi
,gdb查看0x4024aa处的值
因为%rsi为参数寄存器,会传给下一个函数
callq 400be0 <__isoc99_sscanf@plt>
,我们可以查一下这个标准函数是计算输入个数的,那么0x4024aa处应该是一个串,我们s查看
(gdb) x /s 0x4024aa
0x4024aa: "%d %c %d"
!! 输入格式为 "%d %c %d"
然后是比较输入个数,输入个数必须要超过2,明显可以看出我们的输入个数为3个,会跳过炸弹。
到这里,回头看这三句就可以理解啦!
400f49: 4c 8d 44 24 14 lea 0x14(%rsp),%r8
400f4e: 48 8d 4c 24 0f lea 0xf(%rsp),%rcx
400f53: 48 8d 54 24 10 lea 0x10(%rsp),%rdx
从下往上 0xf(%rsp) 存输入 %c,0x10(%rsp) 和 0x14(%rsp) 分别存了输入的两个整数 %d。
猜一下,第一个数x1保存在0x10(%rsp) ,第二个数保存在0x14(%rsp)。
400f6c: 83 7c 24 10 07 cmpl $0x7,0x10(%rsp)
400f71: 0f 87 fc 00 00 00 ja 401073 <phase_3+0x13e>
这两句,提示x1>=7,不然就会爆炸。
紧接着:出现一个地址,还有一个随着x1变化的偏移量,gdb中查看该地址处的值
400f7b: ff 24 c5 c0 24 40 00 jmpq *0x4024c0(,%rax,8)
(gdb) x /x 0x4024c0
0x4024c0: 0x00400f82
这一句是就是下一个语句,当x1=0便会跳到这一句,前面有x1<=7的条件,那猜测如果x1=1、2、3、4、5、6、7呢,在gdb中获取另外7个地址,但x1=7时是无效地址,所以舍去。
然后依次查看这些地址,有很多重复操作的地方:如下
mov $0x77,%eax
cmpl $0x391,0x14(%rsp)
je 40107d <phase_3+0x148>
这段时关键,%rax此时存一个值,这个值会在0x40107d与输入的进行比较:
40107d: 3a 44 24 0f cmp 0xf(%rsp),%al
所以当x1=0时,输入字符的ASCII值为0x77,x2为0x391,转换成字符与十进制整数就是0 w 913。另还有6个答案。至此,可以知道这是一个switch语句,当值为0-6时跳转到不同的地方。
答案 :0 w 913 / 1 i 758 / 2 a 893 / 3 e 133 / 4 d 243 / 5 o 925 / 6 n 447
phase_4
00000000004010d5 <phase_4>:
4010d5: 48 83 ec 18 sub $0x18,%rsp
4010d9: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
4010e0: 00 00
4010e2: 48 89 44 24 08 mov %rax,0x8(%rsp)
4010e7: 31 c0 xor %eax,%eax
4010e9: 48 8d 4c 24 04 lea 0x4(%rsp),%rcx
4010ee: 48 89 e2 mov %rsp,%rdx
4010f1: be 77 26 40 00 mov $0x402677,%esi
4010f6: e8 e5 fa ff ff callq 400be0 <__isoc99_sscanf@plt>
4010fb: 83 f8 02 cmp $0x2,%eax //=2
4010fe: 75 06 jne 401106 <phase_4+0x31>
401100: 83 3c 24 0e cmpl $0xe,(%rsp) //<=e
401104: 76 05 jbe 40110b <phase_4+0x36>
401106: e8 fa 03 00 00 callq 401505 <explode_bomb>
40110b: ba 0e 00 00 00 mov $0xe,%edx
401110: be 00 00 00 00 mov $0x0,%esi
401115: 8b 3c 24 mov (%rsp),%edi
401118: e8 85 ff ff ff callq 4010a2 <func4>
40111d: 83 f8 23 cmp $0x23,%eax
401120: 75 07 jne 401129 <phase_4+0x54>
401122: 83 7c 24 04 23 cmpl $0x23,0x4(%rsp) //返回0x23
401127: 74 05 je 40112e <phase_4+0x59>
401129: e8 d7 03 00 00 callq 401505 <explode_bomb>
40112e: 48 8b 44 24 08 mov 0x8(%rsp),%rax
401133: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
40113a: 00 00
40113c: 74 05 je 401143 <phase_4+0x6e>
40113e: e8 dd f9 ff ff callq 400b20 <__stack_chk_fail@plt>
401143: 48 83 c4 18 add $0x18,%rsp
401147: c3 retq
同样,出现这一段:
4010f1: be 77 26 40 00 mov $0x402677,%esi
4010f6: e8 e5 fa ff ff callq 400be0 <__isoc99_sscanf@plt>
4010fb: 83 f8 02 cmp $0x2,%eax //=2
4010fe: 75 06 jne 401106 <phase_4+0x31>
我们查看0x402677处的东西:
(gdb) x /s 0x402677
0x402677: "%d %d"
了解到输入是两个整数,设为x1和x2。下面是对输入个数的判断,不为两个则会引爆炸弹。
然后下面的一段,为函数func4作参数准备,%rdi、%rsi、%rdx应该为3个参数,
401100: 83 3c 24 0e cmpl $0xe,(%rsp) //<=e
401104: 76 05 jbe 40110b <phase_4+0x36>
401106: e8 fa 03 00 00 callq 401505 <explode_bomb>
40110b: ba 0e 00 00 00 mov $0xe,%edx
401110: be 00 00 00 00 mov $0x0,%esi
401115: 8b 3c 24 mov (%rsp),%edi
401118: e8 85 ff ff ff callq 4010a2 <func4>
栈顶处的值必须<=e,然后%rdi存栈顶值,%rsi置0,%rdx为e。然后不慌着看func4,我们继续往后看phase_4:
40111d: 83 f8 23 cmp $0x23,%eax
401120: 75 07 jne 401129 <phase_4+0x54>
401122: 83 7c 24 04 23 cmpl $0x23,0x4(%rsp) //返回0x23
401127: 74 05 je 40112e <phase_4+0x59>
401129: e8 d7 03 00 00 callq 401505 <explode_bomb>
40112e: 48 8b 44 24 08 mov 0x8(%rsp),%rax
说明func4的返回值必须为0x23,否则会引爆炸弹,并且栈顶+4处的值也应该为0x23,猜想(%rsp)栈顶处存x1的值,0x4(%rsp)处存x2=0x23(十进制为35),然后我们带着要使func4的返回值为0x23的目的进入func4,x1为第一个参数。
00000000004010a2 <func4>:
4010a2: 53 push %rbx
4010a3: 89 d0 mov %edx,%eax
4010a5: 29 f0 sub %esi,%eax
4010a7: 89 c3 mov %eax,%ebx
4010a9: c1 eb 1f shr $0x1f,%ebx //向0舍入除法
4010ac: 01 d8 add %ebx,%eax
4010ae: d1 f8 sar %eax
4010b0: 8d 1c 30 lea (%rax,%rsi,1),%ebx
4010b3: 39 fb cmp %edi,%ebx //x1与某值比较
4010b5: 7e 0c jle 4010c3 <func4+0x21> //x1>=这个值
4010b7: 8d 53 ff lea -0x1(%rbx),%edx //小于时
4010ba: e8 e3 ff ff ff callq 4010a2 <func4>
4010bf: 01 d8 add %ebx,%eax
4010c1: eb 10 jmp 4010d3 <func4+0x31>
4010c3: 89 d8 mov %ebx,%eax
4010c5: 39 fb cmp %edi,%ebx
4010c7: 7d 0a jge 4010d3 <func4+0x31>
4010c9: 8d 73 01 lea 0x1(%rbx),%esi
4010cc: e8 d1 ff ff ff callq 4010a2 <func4>
4010d1: 01 d8 add %ebx,%eax
4010d3: 5b pop %rbx
4010d4: c3 retq
前面的代码是给%rbx一个值,并与x1进行比较,可以直接info r查看值,也可以自己算一下,差不多就是(%rdx-%rsi)/2
向0 舍入。第一次:%rbx=7
4010b3: 39 fb cmp %edi,%ebx //x1与某值比较
4010b5: 7e 0c jle 4010c3 <func4+0x21> //x1>=这个值
4010b7: 8d 53 ff lea -0x1(%rbx),%edx //小于时
4010ba: e8 e3 ff ff ff callq 4010a2 <func4>
当x1>=7时,跳到0x4010c3,否则将%rbx的值减1后给第三个参数寄存器,又进入func4,这里提示,func4又调用func4,说明应该是一个递归,那我们的关键是找到出口。先不管上一次递归,我们看当x1>=7时,跳转到0x4010c3:
4010c3: 89 d8 mov %ebx,%eax
4010c5: 39 fb cmp %edi,%ebx
4010c7: 7d 0a jge 4010d3 <func4+0x31>
4010c9: 8d 73 01 lea 0x1(%rbx),%esi
4010cc: e8 d1 ff ff ff callq 4010a2 <func4>
又比较x1和7的值,如果x1<=7,就不做递归,否则还是要将%rbx的值加1后继续call func4。跳转到0x4010c3的条件是x1>=7,然后递归结束的条件是x1<=7,因此结束递归的条件应该为x1=7(第一次操作为7,通用是x1=%rbx为递归结束条件)。
假设在这里我们就跳出循环,那么返回值为7,不满足0x23。所以还是需要做递归,每次call cunc4后,会有add %ebx,%eax
。
我们写一下,并且画一张图,就一目了然了。
答案 :8 35
phase_5
0000000000401148 <phase_5>:
401148: 48 83 ec 18 sub $0x18,%rsp
40114c: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
401153: 00 00
401155: 48 89 44 24 08 mov %rax,0x8(%rsp)
40115a: 31 c0 xor %eax,%eax
40115c: 48 8d 4c 24 04 lea 0x4(%rsp),%rcx
401161: 48 89 e2 mov %rsp,%rdx
401164: be 77 26 40 00 mov $0x402677,%esi
401169: e8 72 fa ff ff callq 400be0 <__isoc99_sscanf@plt>
40116e: 83 f8 01 cmp $0x1,%eax //输入的个数大于1
401171: 7f 05 jg 401178 <phase_5+0x30>
401173: e8 8d 03 00 00 callq 401505 <explode_bomb>
401178: 8b 04 24 mov (%rsp),%eax
40117b: 83 e0 0f and $0xf,%eax //取后四位
40117e: 89 04 24 mov %eax,(%rsp)
401181: 83 f8 0f cmp $0xf,%eax
401184: 74 2f je 4011b5 <phase_5+0x6d>0 //后四位不为f
401186: b9 00 00 00 00 mov $0x0,%ecx
40118b: ba 00 00 00 00 mov $0x0,%edx
401190: 83 c2 01 add $0x1,%edx
401193: 48 98 cltq
401195: 8b 04 85 00 25 40 00 mov 0x402500(,%rax,4),%eax
40119c: 01 c1 add %eax,%ecx
40119e: 83 f8 0f cmp $0xf,%eax
4011a1: 75 ed jne 401190 <phase_5+0x48>
4011a3: c7 04 24 0f 00 00 00 movl $0xf,(%rsp)
4011aa: 83 fa 0f cmp $0xf,%edx //等于15
4011ad: 75 06 jne 4011b5 <phase_5+0x6d>
4011af: 3b 4c 24 04 cmp 0x4(%rsp),%ecx
4011b3: 74 05 je 4011ba <phase_5+0x72>
4011b5: e8 4b 03 00 00 callq 401505 <explode_bomb>
4011ba: 48 8b 44 24 08 mov 0x8(%rsp),%rax
4011bf: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
4011c6: 00 00
4011c8: 74 05 je 4011cf <phase_5+0x87>
4011ca: e8 51 f9 ff ff callq 400b20 <__stack_chk_fail@plt>
4011cf: 48 83 c4 18 add $0x18,%rsp
4011d3: c3 retq
查看0x402677处的东西:(和phase_4相同)输入“%d %d”。但是后面的比较语句看来,输入个数只需大于1就可,不一定是两个。
同样0x4(%rsp)存x2,(%rsp)存x1。
401178: 8b 04 24 mov (%rsp),%eax
40117b: 83 e0 0f and $0xf,%eax //取后四位
40117e: 89 04 24 mov %eax,(%rsp)
401181: 83 f8 0f cmp $0xf,%eax
401184: 74 2f je 4011b5 <phase_5+0x6d>0 //后四位不为f
这段说明,x1的后四位不是0xf,否则会引爆。
401190: 83 c2 01 add $0x1,%edx
401193: 48 98 cltq
401195: 8b 04 85 00 25 40 00 mov 0x402500(,%rax,4),%eax
40119c: 01 c1 add %eax,%ecx
40119e: 83 f8 0f cmp $0xf,%eax
4011a1: 75 ed jne 401190 <phase_5+0x48>
上面这段,开始循环,一开始%eax保存的是x1的后四位,然后取0x402500(,%rax,4)
处的值给%rax,直到%rax的值为0xf,并且%rdx每次会加1,%rcx每次会累加%rax的值。%rax一开始可能是0x0-0xe中任意一个数,查看这15个数。
(gdb) x /16d 0x402500
0x402500 <array.3600>: 10 2 14 7
0x402510 <array.3600+16>: 8 12 15 11
0x402520 <array.3600+32>: 0 4 1 13
0x402530 <array.3600+48>: 3 9 6 5
是一个数组:10 2 14 7 8 12 15 11 0 4 1 13 3 9 6 (5)。
4011a3: c7 04 24 0f 00 00 00 movl $0xf,(%rsp)
4011aa: 83 fa 0f cmp $0xf,%edx //等于15
4011ad: 75 06 jne 4011b5 <phase_5+0x6d>
4011af: 3b 4c 24 04 cmp 0x4(%rsp),%ecx
4011b3: 74 05 je 4011ba <phase_5+0x72>
%rdx=15说明上面的循环做了15次,也就是说%rcx是这个数组15个整数的和,它等于我们要输入的x2也就是0x4(%rsp)。而关键在于这一句
401195: 8b 04 85 00 25 40 00 mov 0x402500(,%rax,4),%eax
怎样让最后一次%rax=15,并且寻址了15次,用数组下标来看,就是说每次i=a[i],15的下标为6,找到6的下标为14,这样推下去:15->6->14->2->10->0->8->4->9->13->11->7->3->12->5 ->15会指回15。那我们一开始输入的是5。
答案 :5 115
phase_6
其实做到这里已经掌握一些套路了,也越做越顺,phase_6给我的感觉就是非常绕,非常多的跳转,然后我后面把代码分成一段一段看,做好标记,就好很多。就像下面这样:
00000000004011d4 <phase_6>:
4011d4: 41 55 push %r13
4011d6: 41 54 push %r12
4011d8: 55 push %rbp
4011d9: 53 push %rbx
4011da: 48 83 ec 68 sub $0x68,%rsp
4011de: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
4011e5: 00 00
4011e7: 48 89 44 24 58 mov %rax,0x58(%rsp)
4011ec: 31 c0 xor %eax,%eax
4011ee: 48 89 e6 mov %rsp,%rsi
4011f1: e8 31 03 00 00 callq 401527 <read_six_numbers>
4011f6: 49 89 e4 mov %rsp,%r12
4011f9: 41 bd 00 00 00 00 mov $0x0,%r13d
①六个数小于等于6
4011ff: 4c 89 e5 mov %r12,%rbp
401202: 41 8b 04 24 mov (%r12),%eax
401206: 83 e8 01 sub $0x1,%eax
401209: 83 f8 05 cmp $0x5,%eax //x1<=6
40120c: 76 05 jbe 401213 <phase_6+0x3f>
40120e: e8 f2 02 00 00 callq 401505 <explode_bomb>
401213: 41 83 c5 01 add $0x1,%r13d
401217: 41 83 fd 06 cmp $0x6,%r13d //第一步
40121b: 74 3d je 40125a <phase_6+0x86>
②且每个数不相等6
40121d: 44 89 eb mov %r13d,%ebx
401220: 48 63 c3 movslq %ebx,%rax
401223: 8b 04 84 mov (%rsp,%rax,4),%eax
401226: 39 45 00 cmp %eax,0x0(%rbp)
401229: 75 05 jne 401230 <phase_6+0x5c>
40122b: e8 d5 02 00 00 callq 401505 <explode_bomb>
401230: 83 c3 01 add $0x1,%ebx
401233: 83 fb 05 cmp $0x5,%ebx
401236: 7e e8 jle 401220 <phase_6+0x4c>//每个数不相等
401238: 49 83 c4 04 add $0x4,%r12
40123c: eb c1 jmp 4011ff <phase_6+0x2b>
④当x的值>1时,
40123e: 48 8b 52 08 mov 0x8(%rdx),%rdx
401242: 83 c0 01 add $0x1,%eax //count+1
401245: 39 c8 cmp %ecx,%eax //count加到和x相等为止
401247: 75 f5 jne 40123e <phase_6+0x6a>
x<=1 || 从上面进入 将每个node中的数依次填入栈中,顺序按照输入的值给出。
401249: 48 89 54 74 20 mov %rdx,0x20(%rsp,%rsi,2)
40124e: 48 83 c6 04 add $0x4,%rsi
401252: 48 83 fe 18 cmp $0x18,%rsi //加到%rsi=24
401256: 75 07 jne 40125f <phase_6+0x8b>
401258: eb 19 jmp 401273 <phase_6+0x9f> //跳出
③
40125a: be 00 00 00 00 mov $0x0,%esi
40125f: 8b 0c 34 mov (%rsp,%rsi,1),%ecx
401262: b8 01 00 00 00 mov $0x1,%eax //count=1
401267: ba f0 42 60 00 mov $0x6042f0,%edx //指针node1
40126c: 83 f9 01 cmp $0x1,%ecx
40126f: 7f cd jg 40123e <phase_6+0x6a>
401271: eb d6 jmp 401249 <phase_6+0x75>
⑤新创建链表
401273: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx
401278: 48 8d 44 24 20 lea 0x20(%rsp),%rax
40127d: 48 8d 74 24 48 lea 0x48(%rsp),%rsi
401282: 48 89 d9 mov %rbx,%rcx
401285: 48 8b 50 08 mov 0x8(%rax),%rdx
401289: 48 89 51 08 mov %rdx,0x8(%rcx)
40128d: 48 83 c0 08 add $0x8,%rax
401291: 48 89 d1 mov %rdx,%rcx
401294: 48 39 f0 cmp %rsi,%rax
401297: 75 ec jne 401285 <phase_6+0xb1>
401299: 48 c7 42 08 00 00 00 movq $0x0,0x8(%rdx)
4012a0: 00
⑥从大到小
4012a1: bd 05 00 00 00 mov $0x5,%ebp
4012a6: 48 8b 43 08 mov 0x8(%rbx),%rax
4012aa: 8b 00 mov (%rax),%eax
*4012ac: 39 03 cmp %eax,(%rbx)
4012ae: 7d 05 jge 4012b5 <phase_6+0xe1>
4012b0: e8 50 02 00 00 callq 401505 <explode_bomb>
4012b5: 48 8b 5b 08 mov 0x8(%rbx),%rbx
4012b9: 83 ed 01 sub $0x1,%ebp
4012bc: 75 e8 jne 4012a6 <phase_6+0xd2>
4012be: 48 8b 44 24 58 mov 0x58(%rsp),%rax
4012c3: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
4012ca: 00 00
4012cc: 74 05 je 4012d3 <phase_6+0xff>
4012ce: e8 4d f8 ff ff callq 400b20 <__stack_chk_fail@plt>
4012d3: 48 83 c4 68 add $0x68,%rsp
4012d7: 5b pop %rbx
4012d8: 5d pop %rbp
4012d9: 41 5c pop %r12
4012db: 41 5d pop %r13
4012dd: c3 retq
解题过程标在代码中了,具体来说就是考察链表与指针,一开始题目给了一个链表,然后会根据我们给的0-6的顺序,将这些结点的值存入栈中,顺序是从大到小,最初链表如下:
(gdb) x/24wx 0x6042f0
0x6042f0 <node1>: 0x000000f1 0x00000001 0x00604300 0x00000000
0x604300 <node2>: 0x0000038f 0x00000002 0x00604310 0x00000000
0x604310 <node3>: 0x00000075 0x00000003 0x00604320 0x00000000
0x604320 <node4>: 0x00000123 0x00000004 0x00604330 0x00000000
0x604330 <node5>: 0x00000297 0x00000005 0x00604340 0x00000000
0x604340 <node6>: 0x00000399 0x00000006 0x00000000 0x00000000
从大到小,答案 :6 2 5 4 1 3
secret_phase
听同学说才知道还有一个,做完好几天了,也忘记之前干了啥。
然后又重新看,这个炸弹在phase_defused里面
4016a0: 83 3d e5 30 20 00 06 cmpl $0x6,0x2030e5(%rip) # 60478c <num_input_strings>
4016a7: 75 5e jne 401707 <phase_defused+0x7b>
在phase_defused处打断点,每通过一个炸弹,0x2030e5(%rip)
的值会加1,直到通过6个炸弹。
4016b8: be c1 26 40 00 mov $0x4026c1,%esi
4016bd: bf 90 48 60 00 mov $0x604890,%edi
这两句,查看一下这两个地址
(gdb) x /s 0x604890
0x604890 <input_strings+240>: "8 35"
(gdb) x /s 0x4026c1
0x4026c1: "%d %d %s"
8 35是我们在第4关输入的,要在后面加一个字符串才能触发secret_phase。
4016cc: be ca 26 40 00 mov $0x4026ca,%esi
(gdb) x /s 0x4026ca
0x4026ca: "DrEvil"
要在后面输入的是DrEvil。进入secret_phase:
000000000040131c <secret_phase>:
40131c: 53 push %rbx
40131d: e8 44 02 00 00 callq 401566 <read_line>
401322: ba 0a 00 00 00 mov $0xa,%edx
401327: be 00 00 00 00 mov $0x0,%esi
40132c: 48 89 c7 mov %rax,%rdi
40132f: e8 8c f8 ff ff callq 400bc0 <strtol@plt>
401334: 48 89 c3 mov %rax,%rbx
401337: 8d 40 ff lea -0x1(%rax),%eax
40133a: 3d e8 03 00 00 cmp $0x3e8,%eax //<=0x3e9
40133f: 76 05 jbe 401346 <secret_phase+0x2a>
401341: e8 bf 01 00 00 callq 401505 <explode_bomb>
401346: 89 de mov %ebx,%esi
401348: bf 10 41 60 00 mov $0x604110,%edi
40134d: e8 8c ff ff ff callq 4012de <fun7>
401352: 83 f8 03 cmp $0x3,%eax //返回值为3
401355: 74 05 je 40135c <secret_phase+0x40>
401357: e8 a9 01 00 00 callq 401505 <explode_bomb>
40135c: bf 40 25 40 00 mov $0x402540,%edi
401361: e8 9a f7 ff ff callq 400b00 <puts@plt>
401366: e8 21 03 00 00 callq 40168c <phase_defused>
40136b: 5b pop %rbx
40136c: c3 retq
把输入变成一个十进制整数(查一下strtol函数,第三个参数为0xa,所以转换为10进制),这个数不大于0x3e9(1001),然后设置了%rsi=x(输入的数),%rdi=0x604110,应该是一个结构体?(反正就是包含有一个整数值,两个指针n1、n2),进入fun7。(注意还是先看返回值,返回值是3,带着答案去看。)
fun7也是一个递归,不同条件,下一次递归的参数不同。
00000000004012de <fun7>:
4012de: 48 83 ec 08 sub $0x8,%rsp
4012e2: 48 85 ff test %rdi,%rdi //不为0
4012e5: 74 2b je 401312 <fun7+0x34>
4012e7: 8b 17 mov (%rdi),%edx //24
4012e9: 39 f2 cmp %esi,%edx
4012eb: 7e 0d jle 4012fa <fun7+0x1c>
4012ed: 48 8b 7f 08 mov 0x8(%rdi),%rdi
4012f1: e8 e8 ff ff ff callq 4012de <fun7> //递归
4012f6: 01 c0 add %eax,%eax
4012f8: eb 1d jmp 401317 <fun7+0x39>
4012fa: b8 00 00 00 00 mov $0x0,%eax
4012ff: 39 f2 cmp %esi,%edx
401301: 74 14 je 401317 <fun7+0x39>
401303: 48 8b 7f 10 mov 0x10(%rdi),%rdi
401307: e8 d2 ff ff ff callq 4012de <fun7>
40130c: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
401310: eb 05 jmp 401317 <fun7+0x39>
401312: b8 ff ff ff ff mov $0xffffffff,%eax
401317: 48 83 c4 08 add $0x8,%rsp
40131b: c3 retq
条件是当这个结构的值d等于x的时候,返回0,如果d<x,a置0,调用fun7(n2)并且a=2a+1;如果d>x,调用fun7(n1),并且a=2a。因为我们要a=3,所以只能是a=20+1=1,a=21+1=3,都进入d<x这一边,调了两次,如下:
(gdb) x /60wx 0x0604110
0x604110 <n1>: 0x00000024 0x00000000 0x00604130 0x00000000
0x604120 <n1+16>: 0x00604150 0x00000000 0x00000000 0x00000000
0x604130 <n21>: 0x00000008 0x00000000 0x006041b0 0x00000000
0x604140 <n21+16>: 0x00604170 0x00000000 0x00000000 0x00000000
0x604150 <n22>: 0x00000032 0x00000000 0x00604190 0x00000000
0x604160 <n22+16>: 0x006041d0 0x00000000 0x00000000 0x00000000
0x604170 <n32>: 0x00000016 0x00000000 0x00604290 0x00000000
0x604180 <n32+16>: 0x00604250 0x00000000 0x00000000 0x00000000
0x604190 <n33>: 0x0000002d 0x00000000 0x006041f0 0x00000000
0x6041a0 <n33+16>: 0x006042b0 0x00000000 0x00000000 0x00000000
0x6041b0 <n31>: 0x00000006 0x00000000 0x00604210 0x00000000
0x6041c0 <n31+16>: 0x00604270 0x00000000 0x00000000 0x00000000
0x6041d0 <n34>: 0x0000006b 0x00000000 0x00604230 0x00000000
0x6041e0 <n34+16>: 0x006042d0 0x00000000 0x00000000 0x00000000
0x6041f0 <n45>: 0x00000028 0x00000000 0x00000000 0x00000000
0x604200 <n45+16>: 0x00000000 0x00000000 0x00000000 0x00000000
开始时0x604110第一次0x604150第二次0x6041d0,*0x6041d0值为0x6b,转换成十进制为107。
答案 :107
!!!!后我们做一次总的输入输出,终于解完啦!!BOOM!!
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Public speaking is very easy.
Phase 1 defused. How about the next one?
0 1 1 2 3 5
That's number 2. Keep going!
0 w 913
Halfway there!
8 35 DrEvil
So you got that one. Try this one.
5 115
Good work! On to the next...
6 2 5 4 1 3
Curses, you've found the secret phase!
But finding it and solving it are quite different...
Continuing.
107
Wow! You've defused the secret stage!
Congratulations! You've defused the bomb!
呼~~下个实验见。
本科小白!若有错误请指正!