所需要使用的调试工具
1. gdb工具
- 使用 break *地址来设置断点,使用c指令从断点继续执行
- 使用 info registers 指令观察寄存器状态
- 使用 x/s 指令用字符串类型显示内存中值*
2.objdump工具
使用 objdump -d execfile > exfiles.s 对可执行文件进行反汇编并输入到一个文件中
使用objdump -t execfile 对可执行文件中符号表进行提取
Strings工具
使用strings bomb 可以将可执行文件中的所有可见字符打印出来
开始进入拆弹过程
phase_1
input = read_line(); /* Get input */
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");
可以看到phase_1的C代码要求我们首先输入一个字符串,然后再进行比较
所以我们可以使用gdb在phase_1开始处设置断点并进行一次操作、查看寄存器的值
(gdb) break *0x400ee0
Breakpoint 1 at 0x400ee0
(gdb) run
Starting program: /home/legend/demo/bomb/bomb
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
My first try!
My first try!就是随便打的,主要看它存入了哪个寄存器(不出意外是%rdi)
Breakpoint 1, 0x0000000000400ee0 in phase_1 ()
(gdb) info registers
rsi 0x603780 6305664
rdi 0x603780 6305664
(gdb) x/s 0x603780
0x603780 <input_strings>: "My first try!"
因为后面程序中改变了%rsi。所以可以推断,输入的字符串确实存入了%rdi 寄存器
可以看到在后续代码里%rsi的值被改变,此刻我们再使用x/s查看其值发现是一个字符串
(gdb) x/s 0x402400
0x402400: "Border relations with Canada have never been better."
接下来我们要进行对phase_1的分析
对应的汇编程序
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
看到它调用了<string_not_equal>
截取一个重要部分
0000000000401338 <strings_not_equal>:
401338: 41 54 push %r12
40133a: 55 push %rbp
40133b: 53 push %rbx
40133c: 48 89 fb mov %rdi,%rbx
40133f: 48 89 f5 mov %rsi,%rbp
401342: e8 d4 ff ff ff callq 40131b <string_length>
401347: 41 89 c4 mov %eax,%r12d
40134a: 48 89 ef mov %rbp,%rdi
40134d: e8 c9 ff ff ff callq 40131b <string_length>
401352: ba 01 00 00 00 mov $0x1,%edx
401357: 41 39 c4 cmp %eax,%r12d
40135a: 75 3f jne 40139b <strings_not_equal+0x63>
40139b: 89 d0 mov %edx,%eax
40139d: 5b pop %rbx
40139e: 5d pop %rbp
40139f: 41 5c pop %r12
4013a1: c3 retq
对stringlength分析会得出该函数是用来返回字符串长度的
可以看到在not_equal函数里当我们键入的字符串长度与%rsi不同时,可以看到函数返回1;
可以推测出当两字符串相同时返回0
test设置条件码后je根据ZF条件码可以跳过炸弹启动程序
即只有我们键入的string和程序存入%rsi的string相同时才会执行je
所以可以得到我们看到的%rsi中的字符串就是keys
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Border relations with Canada have never been better.
Phase 1 defused. How about the next one?
解决!Yep!
phase_2
该阶段的问题主要是理解代码中的循环
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>
先看函数中调用的<read_six_numbers>
000000000040145c <read_six_numbers>:
40145c: 48 83 ec 18 sub $0x18,%rsp
401460: 48 89 f2 mov %rsi,%rdx
401463: 48 8d 4e 04 lea 0x4(%rsi),%rcx
401467: 48 8d 46 14 lea 0x14(%rsi),%rax
40146b: 48 89 44 24 08 mov %rax,0x8(%rsp)
401470: 48 8d 46 10 lea 0x10(%rsi),%rax
401474: 48 89 04 24 mov %rax,(%rsp)
401478: 4c 8d 4e 0c lea 0xc(%rsi),%r9
40147c: 4c 8d 46 08 lea 0x8(%rsi),%r8
401480: be c3 25 40 00 mov $0x4025c3,%esi
401485: b8 00 00 00 00 mov $0x0,%eax
40148a: e8 61 f7 ff ff callq 400bf0 <__isoc99_sscanf@plt>
40148f: 83 f8 05 cmp $0x5,%eax
401492: 7f 05 jg 401499 <read_six_numbers+0x3d>
401494: e8 a1 ff ff ff callq 40143a <explode_bomb>
401499: 48 83 c4 18 add $0x18,%rsp
40149d: c3 retq
可直接从<read_six_numbers>的后面开始看。
不难得到当输入字符数量少于6个时<read_six_numbers>会直接引爆炸弹。
所以keys的长度应该要大于等于6个字符。
再回头看<phase_2>中的主体代码
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
因为我们的目的是要得到keys
所以这里的一个处理技巧是:
我们不去管引爆炸弹的情况,只按照可以jump的条件分析。
只要我们遵循着它的跳跃进行一轮就会发现
这个循环的条件为输入的keys中后一位是前一位的2倍,且第一位为1,数据为int型
可从如下代码得出
400f0a: 83 3c 24 01 cmpl $0x1,(%rsp)
//可以看出rsp即栈顶中存的为数字1,
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>
并且要求长度至少为6
可从我们对<read_six_numbers>得出
或者从如下截取的两段代码得出
400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx
400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp
//rbp为rsp+24,rbx为rsp+4
400f25: 48 83 c3 04 add $0x4,%rbx
400f29: 48 39 eb cmp %rbp,%rbx
//再根据比较条件,可以看出至少要24/4位的keys
结果
可以看到长度大于6后就不再进行判断了(255也过了)
Phase 1 defused. How about the next one?
1 2 4 8 16 32 64 128 255
That's number 2. Keep going!
phase_2解决!Yep!
pahse_3
该部分考查对switch的理解
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>
从上面这一部分中可以看到我们输入的keys的长度不能少于两位,否则引爆炸弹
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
再对上述代码主体进行分析,发现代码将%rax的值变成了我们第一个输入进的数字,且这个数字不能大于7
400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8)
%rax会影响我们跳转的地址。
接下来使用gdb工具对%rax不同值对应内存地址中的值取出,对应下面的各个情况
在此只举%rax=2的例子,其余6个值也可行
发现对应地址
(gdb) x/u *0x402480
0x400f83 <phase_3+64>: 184
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
在我们的举例中,%rax=0x2c3,十进制707
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
再看上述代码,可以发现,当%rax的值与我们输入的第二个数字相同时,就得到了keys
所以该phase共有7个keys可供选择
结果
That's number 2. Keep going!
2 707
Halfway there!
phase_3完成!Yep!
phase_4
这个阶段考查的是递归函数
先看phase_4在调用fun_4前的代码
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>
这段代码比较简单,从phase_4中看到我们输入的keys是两个数字
且第一个输入的数字应该小于等于14
现在主要是去分析fun_4的功能
此时传入的%rdx=14,%rsi=0,%rdi=我们输入的第一个数字
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
此时我们一直跟随着可跳转的代码,发现当rdi==1时,就可以直接跳出递归,且此时%rax=0
继续看phase_4的后一段代码
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
察看jne语句可知%rax=0不会引爆炸弹,所以第一个输入的数字可以为1
继续向下看,看到另一个cmpl语句,此时我们可以确定输入的第二个数字一定为0
所以我们的keys就是1 0
Halfway there!
1 0
So you got that one. Try this one.
phase_4完成!Yep!
phase_5
感觉这段主要是考察寄存器的细节和栈的存储,特点并不是很鲜明,但很有趣,有种解密的感觉
0000000000401062 <phase_5>:
401062: 53 push %rbx
401063: 48 83 ec 20 sub $0x20,%rsp
401067: 48 89 fb mov %rdi,%rbx
40106a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
401071: 00 00
401073: 48 89 44 24 18 mov %rax,0x18(%rsp)
401078: 31 c0 xor %eax,%eax
40107a: e8 9c 02 00 00 callq 40131b <string_length>
40107f: 83 f8 06 cmp $0x6,%eax
401082: 74 4e je 4010d2 <phase_5+0x70>
401084: e8 b1 03 00 00 callq 40143a <explode_bomb>
可以看到我们输入的keys是六位的
401089: eb 47 jmp 4010d2 <phase_5+0x70>
40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx
40108f: 88 0c 24 mov %cl,(%rsp)
401092: 48 8b 14 24 mov (%rsp),%rdx
401096: 83 e2 0f and $0xf,%edx
401099: 0f b6 92 b0 24 40 00 movzbl 0x4024b0(%rdx),%edx
4010a0: 88 54 04 10 mov %dl,0x10(%rsp,%rax,1)
4010a4: 48 83 c0 01 add $0x1,%rax
4010a8: 48 83 f8 06 cmp $0x6,%rax
4010ac: 75 dd jne 40108b <phase_5+0x29>
可以看到在这段程序不断取我们输入keys的,并且将每一位的低4位写入%rdx
然后通过movzbl 0x4024b0(%rdx),%edx得到真正的keys
所以我们使用gdb工具查看0x4024b0中究竟存储的是什么
x/s 0x4024b0
0x4024b0 <array.3449>:"maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"
我们就是在这个字段中寻找六个字符,凑成keys
4010ae: c6 44 24 16 00 movb $0x0,0x16(%rsp)
4010b3: be 5e 24 40 00 mov $0x40245e,%esi
4010b8: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
4010bd: e8 76 02 00 00 callq 401338 <strings_not_equal>
4010c2: 85 c0 test %eax,%eax
4010c4: 74 13 je 4010d9 <phase_5+0x77>
4010c6: e8 6f 03 00 00 callq 40143a <explode_bomb>
4010cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
4010d0: eb 07 jmp 4010d9 <phase_5+0x77>
4010d2: b8 00 00 00 00 mov $0x0,%eax
4010d7: eb b2 jmp 40108b <phase_5+0x29>
4010d9: 48 8b 44 24 18 mov 0x18(%rsp),%rax
4010de: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
4010e5: 00 00
4010e7: 74 05 je 4010ee <phase_5+0x8c>
4010e9: e8 42 fa ff ff callq 400b30 <__stack_chk_fail@plt>
4010ee: 48 83 c4 20 add $0x20,%rsp
4010f2: 5b pop %rbx
4010f3: c3 retq
哈哈,可以看到出现了<string_not_equal>函数,那么0x40245e中存储的就是我们需要从上述字段中寻找的各个字符
使用gdb查看0x40245e中存储了什么
(gdb) x/s 0x40245e
0x40245e: "flyers"
嗡嗡嗡,全是苍蝇啊
maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?
再看这里面的字符,我们分别发现在9,15,14,5,6,7这六个位置
所以我们输入字符的二进制低四位应该是:
1001
1111
1110
0101
0110
0111
再去找ASCII表
附一个 ASCII表的链接
可以得到我们输入的字符是:就字母区段来说应该是ionefg或者IONEFG,或者是这几个字母按顺序的大小写混写
当然还有可能是许多其他字符和数字的组合,这里不一一列举
哇哈哈哈,成功了!!!
phase_5完成!Yep!
phase_6
淦,好长啊!
这一部分是考察对链表的重新链接
00000000004010f4 <phase_6>:
4010f4: 41 56 push %r14
4010f6: 41 55 push %r13
4010f8: 41 54 push %r12
4010fa: 55 push %rbp
4010fb: 53 push %rbx
4010fc: 48 83 ec 50 sub $0x50,%rsp
401100: 49 89 e5 mov %rsp,%r13
401103: 48 89 e6 mov %rsp,%rsi
401106: e8 51 03 00 00 callq 40145c <read_six_numbers>
由<read_six_numbers>可以得知我们的keys长度至少为6
40110b: 49 89 e6 mov %rsp,%r14
40110e: 41 bc 00 00 00 00 mov $0x0,%r12d
401114: 4c 89 ed mov %r13,%rbp
401117: 41 8b 45 00 mov 0x0(%r13),%eax
40111b: 83 e8 01 sub $0x1,%eax
40111e: 83 f8 05 cmp $0x5,%eax
401121: 76 05 jbe 401128 <phase_6+0x34>
401123: e8 12 03 00 00 callq 40143a <explode_bomb>
401128: 41 83 c4 01 add $0x1,%r12d
40112c: 41 83 fc 06 cmp $0x6,%r12d
401130: 74 21 je 401153 <phase_6+0x5f>
401132: 44 89 e3 mov %r12d,%ebx
401135: 48 63 c3 movslq %ebx,%rax
401138: 8b 04 84 mov (%rsp,%rax,4),%eax
40113b: 39 45 00 cmp %eax,0x0(%rbp)
40113e: 75 05 jne 401145 <phase_6+0x51>
401140: e8 f5 02 00 00 callq 40143a <explode_bomb>
401145: 83 c3 01 add $0x1,%ebx
401148: 83 fb 05 cmp $0x5,%ebx
40114b: 7e e8 jle 401135 <phase_6+0x41>
40114d: 49 83 c5 04 add $0x4,%r13
401151: eb c1 jmp 401114 <phase_6+0x20>
阅读上述代码发现这是实现了确保每个数字都小于6且大于0
401153: 48 8d 74 24 18 lea 0x18(%rsp),%rsi
401158: 4c 89 f0 mov %r14,%rax
40115b: b9 07 00 00 00 mov $0x7,%ecx
401160: 89 ca mov %ecx,%edx
401162: 2b 10 sub (%rax),%edx
401164: 89 10 mov %edx,(%rax)
401166: 48 83 c0 04 add $0x4,%rax
40116a: 48 39 f0 cmp %rsi,%rax
40116d: 75 f1 jne 401160 <phase_6+0x6c>
这段代码实现了7-每个数字的功能
40116f: be 00 00 00 00 mov $0x0,%esi
401174: eb 21 jmp 401197 <phase_6+0xa3>
401176: 48 8b 52 08 mov 0x8(%rdx),%rdx
40117a: 83 c0 01 add $0x1,%eax
40117d: 39 c8 cmp %ecx,%eax
40117f: 75 f5 jne 401176 <phase_6+0x82>
401181: eb 05 jmp 401188 <phase_6+0x94>
401183: ba d0 32 60 00 mov $0x6032d0,%edx
401188: 48 89 54 74 20 mov %rdx,0x20(%rsp,%rsi,2)
40118d: 48 83 c6 04 add $0x4,%rsi
401191: 48 83 fe 18 cmp $0x18,%rsi
401195: 74 14 je 4011ab <phase_6+0xb7>
401197: 8b 0c 34 mov (%rsp,%rsi,1),%ecx
40119a: 83 f9 01 cmp $0x1,%ecx
40119d: 7e e4 jle 401183 <phase_6+0x8f>
40119f: b8 01 00 00 00 mov $0x1,%eax
4011a4: ba d0 32 60 00 mov $0x6032d0,%edx
4011a9: eb cb jmp 401176 <phase_6+0x82>
相当于把一个链表中的不同值传入栈中
设 order为数字的次序, value为数字的值
这段代码实现了当value等于1时将0x6032d0传入rsp+32+(order-1) * 8
value大于1时,将0x6032d0+8 * (value-1)值传入rsp+32+(order-1) * 8
我们再看一看链表中的结点元素
(gdb) p/s *0x6032d0@24
$2 = {332, 1, 6304480, 0, 168, 2, 6304496, 0,
924, 3, 6304512, 0, 691, 4, 6304528, 0,
477, 5, 6304544, 0, 443, 6, 0, 0}
现在可以看到这六个结点的值分别为332,168,924,691,477,443
4011ab: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx
4011b0: 48 8d 44 24 28 lea 0x28(%rsp),%rax
4011b5: 48 8d 74 24 50 lea 0x50(%rsp),%rsi
4011ba: 48 89 d9 mov %rbx,%rcx
4011bd: 48 8b 10 mov (%rax),%rdx
4011c0: 48 89 51 08 mov %rdx,0x8(%rcx)
4011c4: 48 83 c0 08 add $0x8,%rax
4011c8: 48 39 f0 cmp %rsi,%rax
4011cb: 74 05 je 4011d2 <phase_6+0xde>
4011cd: 48 89 d1 mov %rdx,%rcx
4011d0: eb eb jmp 4011bd <phase_6+0xc9>
4011d2: 48 c7 42 08 00 00 00 movq $0x0,0x8(%rdx)
这段代码实现了将链表直接相连接,并且将最后的指针为0(NULL)
4011d9: 00
4011da: bd 05 00 00 00 mov $0x5,%ebp
4011df: 48 8b 43 08 mov 0x8(%rbx),%rax
4011e3: 8b 00 mov (%rax),%eax
4011e5: 39 03 cmp %eax,(%rbx)
4011e7: 7d 05 jge 4011ee <phase_6+0xfa>
4011e9: e8 4c 02 00 00 callq 40143a <explode_bomb>
4011ee: 48 8b 5b 08 mov 0x8(%rbx),%rbx
4011f2: 83 ed 01 sub $0x1,%ebp
4011f5: 75 e8 jne 4011df <phase_6+0xeb>
4011f7: 48 83 c4 50 add $0x50,%rsp
4011fb: 5b pop %rbx
4011fc: 5d pop %rbp
4011fd: 41 5c pop %r12
4011ff: 41 5d pop %r13
401201: 41 5e pop %r14
401203: c3 retq
这段代码实现了判断rsp+32链表必须是递减的,否则直接引爆炸弹
现在我们可以知道排序应该为3 4 5 6 1 2
所以我们输入的值应该为4 3 2 1 6 5(因为前面的排序是用7-输入数字得到的顺序)
phase_6完成 ! Yep!
secret_phase
PDF中写道还有一个secret_phase,在汇编文件里先找了找,果真发现了secret_phase
看到这里我首先想到的是怎么进入这个secret,可以看到这里面并没有给我们提供接口
然后,嘿嘿,我就用了 ctrl+f 神器搜索了一下secret_phase这个词
没想到这个接口竟然藏在每个阶段都在使用的phase_defused里面
最危险的地方就是最安全的地方???
由于过长这里只截取了一段用于推出接口的代码
00000000004015c4 <phase_defused>:
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
(gdb) x/s 0x603870
0x603870 <input_strings+240>: "1 0"
(gdb) x/s 0x402619
0x402619: "%d %d %s"
充满好奇地看了看这两个地址的内容,发现,淦,这不就是phase_4的keys吗!
(gdb) x/s 0x402622
0x402622: "DrEvil"
这时向后看,它将0x402622的内容和0x10(%rsp)进行了比较
看一下里面的内容。啊,这就有那味了,应该是要我们输入的开关,那么在哪输入呢…
啊哈!phase_4的keys再加上这个DrEvil就是三个输入,那应该就可以打开这个隐藏过程了
嘿嘿,我们来试一试!
Halfway there!
1 0 DrEvil
So you got that one. Try this one.
ionefg
Good work! On to the next...
4 3 2 1 6 5
Curses, you've found the secret phase!
But finding it and solving it are quite different...
_看来我们已经成功进入了,接下来是解密的时候了!!!
先看secret_phase
0000000000401242 <secret_phase>:
401242: 53 push %rbx
401243: e8 56 02 00 00 callq 40149e <read_line>
Curses, you've found the secret phase!
But finding it and solving it are quite different...
6
(gdb) info registers
rax 0x603960 6306144
(gdb) x/s 0x603960
0x603960 <input_strings+480>: "6"
利用gdb工具得出read_line将我们的输入存入%rax返回
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>、
再次通过gdb工具调查strtol@plt的功能
Curses, you've found the secret phase!
But finding it and solving it are quite different...
66
Breakpoint 1, 0x000000000040125a in secret_phase ()
(gdb) info registers
rax 0x42 66
可以看到该函数是将输入的字符串变成常数返回
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>
从这段代码可以得知输入的数字应该小于1001
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
可以看到只有fun7的返回值为2才不会引爆炸弹,所以我们重点研究它
这是secret_phase中调用的fun7
0000000000401204 <fun7>:
401204: 48 83 ec 08 sub $0x8,%rsp
401208: 48 85 ff test %rdi,%rdi
40120b: 74 2b je 401238 <fun7+0x34>
40120d: 8b 17 mov (%rdi),%edx
40120f: 39 f2 cmp %esi,%edx
401211: 7e 0d jle 401220 <fun7+0x1c>
401213: 48 8b 7f 08 mov 0x8(%rdi),%rdi
401217: e8 e8 ff ff ff callq 401204 <fun7>
40121c: 01 c0 add %eax,%eax
40121e: eb 1d jmp 40123d <fun7+0x39>
401220: b8 00 00 00 00 mov $0x0,%eax
401225: 39 f2 cmp %esi,%edx
401227: 74 14 je 40123d <fun7+0x39>
401229: 48 8b 7f 10 mov 0x10(%rdi),%rdi
40122d: e8 d2 ff ff ff callq 401204 <fun7>
401232: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
401236: eb 05 jmp 40123d <fun7+0x39>
401238: b8 ff ff ff ff mov $0xffffffff,%eax
40123d: 48 83 c4 08 add $0x8,%rsp
401241: c3 retq
可以看到对于不同情况,rdi只有两个选项,即+16或者+8,这时候还没有头绪,就先用gdb康康这里面存储的内容叭
(gdb) x/ 0x6030f0
0x6030f0 <n1>: 0x24
(gdb) x/ 0x6030f8
0x6030f8 <n1+8>: 0x603110 <n21>
(gdb) x/ 0x603100
0x603100 <n1+16>: 0x603130 <n22>
(gdb) x/ 0x603110
0x603110 <n21>: 0x8
(gdb) x/ 0x603130
0x603130 <n22>: 0x32
(gdb) x/ 0x603118
0x603118 <n21+8>: 0x603190 <n31>
(gdb) x/ 0x603120
0x603120 <n21+16>: 0x603150 <n32>
(gdb) x/ 0x603130
0x603130 <n22>: 0x32
(gdb) x/ 0x603138
0x603138 <n22+8>: 0x603170 <n33>
(gdb) x/ 0x603140
0x603140 <n22+16>: 0x6031b0 <n34>
(gdb) x/20a 0x6030f0
这… 貌似是一颗二叉树???<21>代表第二层第一个结点???
我们来画一画叭(均为10进制)
汇编可以改写为
int fun7(rdi,int input)
{
int value=(%rdi);
if(value==0)
return -1;
if(%rdi>input)
{
%rdi=%rdi+8;
fun7(%rdi,input);
return_value=2*return_value;
return return_value;
}
else
{
if(%rdi==input)
{
return_value=0;
return return_value;
}
%rdi=%rdi+16;
fun7(%rdi,input);
return_value=1+2*return_value;
return return_value;
}
}
要让返回值等于2,只有在最后出现2(1+2 * 0)才可以。即先返回0,再返回 1+2 * return,再返回2 * return。
所以最外层的0x24一定大于输入的值,而最内层一定是与输入相等
中间一层必须小于输入,我们可以在树中看到36,8,22这个序列正好符合
所以keys应该就是22,我们来试一试!
Curses, you've found the secret phase!
But finding it and solving it are quite different...
22
Wow! You've defused the secret stage!
Congratulations! You've defused the bomb!
NICE!!!
花费了数个夜晚,终于拆完了!
可惜我的头发啊啊啊啊!!!