来做大名鼎鼎的Bomb Lab
了。
实验材料在http://csapp.cs.cmu.edu/3e/labs.html
准备
一开始我们手上的材料,
README
文件里面没有内容,.c
文件中是炸弹的源代码和一些补充说明,缺少两个头文件导致无法编译,还有一个可执行文件,作为逆向工程的对象。
使用命令
objdump -d bomb > bomb.asm
得到bomb
可执行代码段的汇编代码。
粗略浏览一遍整个汇编代码,找到main
中和我们实验有关的一段:
400e32: e8 67 06 00 00 callq 40149e
400e37: 48 89 c7 mov %rax,%rdi
400e3a: e8 a1 00 00 00 callq 400ee0
400e3f: e8 80 07 00 00 callq 4015c4
400e44: bf a8 23 40 00 mov $0x4023a8,%edi
400e49: e8 c2 fc ff ff callq 400b10
400e4e: e8 4b 06 00 00 callq 40149e
400e53: 48 89 c7 mov %rax,%rdi
400e56: e8 a1 00 00 00 callq 400efc
400e5b: e8 64 07 00 00 callq 4015c4
400e60: bf ed 22 40 00 mov $0x4022ed,%edi
400e65: e8 a6 fc ff ff callq 400b10
400e6a: e8 2f 06 00 00 callq 40149e
400e6f: 48 89 c7 mov %rax,%rdi
400e72: e8 cc 00 00 00 callq 400f43
400e77: e8 48 07 00 00 callq 4015c4
400e7c: bf 0b 23 40 00 mov $0x40230b,%edi
400e81: e8 8a fc ff ff callq 400b10
400e86: e8 13 06 00 00 callq 40149e
400e8b: 48 89 c7 mov %rax,%rdi
400e8e: e8 79 01 00 00 callq 40100c
400e93: e8 2c 07 00 00 callq 4015c4
400e98: bf d8 23 40 00 mov $0x4023d8,%edi
400e9d: e8 6e fc ff ff callq 400b10
400ea2: e8 f7 05 00 00 callq 40149e
400ea7: 48 89 c7 mov %rax,%rdi
400eaa: e8 b3 01 00 00 callq 401062
400eaf: e8 10 07 00 00 callq 4015c4
400eb4: bf 1a 23 40 00 mov $0x40231a,%edi
400eb9: e8 52 fc ff ff callq 400b10
400ebe: e8 db 05 00 00 callq 40149e
400ec3: 48 89 c7 mov %rax,%rdi
400ec6: e8 29 02 00 00 callq 4010f4
400ecb: e8 f4 06 00 00 callq 4015c4
400ed0: b8 00 00 00 00 mov $0x0,%eax
这里的意思是每次读一行,然后执行对应阶段的函数,判断是否成功解除。
Phase 1
找到phase1
的代码:
0000000000400ee0 :
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
400eee: 85 c0 test %eax,%eax
400ef0: 74 05 je 400ef7
400ef2: e8 43 05 00 00 callq 40143a
400ef7: 48 83 c4 08 add $0x8,%rsp
400efb: c3 retq
可以看到这里调用了
函数,根据结果来选择是否调用
函数,而传入的两个参数为rsi
和rdi
,rsi
是通过mov
设置的立即数0x402400
,而rdi
从主函数中可以看到是
返回的结果。
这里我们可以合理猜测,phase 1
读取一行字符串,传入字符串的地址和地址0x402400
,两者进行比较,如果不相等则引爆炸弹。查看
和
的内部逻辑后佐证了想法。
判断字符串长度是否相等,
判断两个字符串是否相等。
000000000040131b :
40131b: 80 3f 00 cmpb $0x0,(%rdi)
40131e: 74 12 je 401332
401320: 48 89 fa mov %rdi,%rdx
401323: 48 83 c2 01 add $0x1,%rdx
401327: 89 d0 mov %edx,%eax
401329: 29 f8 sub %edi,%eax
40132b: 80 3a 00 cmpb $0x0,(%rdx)
40132e: 75 f3 jne 401323
401330: f3 c3 repz retq
401332: b8 00 00 00 00 mov $0x0,%eax
401337: c3 retq
0000000000401338 :
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
401347: 41 89 c4 mov %eax,%r12d
40134a: 48 89 ef mov %rbp,%rdi
40134d: e8 c9 ff ff ff callq 40131b
401352: ba 01 00 00 00 mov $0x1,%edx
401357: 41 39 c4 cmp %eax,%r12d
40135a: 75 3f jne 40139b
40135c: 0f b6 03 movzbl (%rbx),%eax
40135f: 84 c0 test %al,%al
401361: 74 25 je 401388
401363: 3a 45 00 cmp 0x0(%rbp),%al
401366: 74 0a je 401372
401368: eb 25 jmp 40138f
40136a: 3a 45 00 cmp 0x0(%rbp),%al
40136d: 0f 1f 00 nopl (%rax)
401370: 75 24 jne 401396
401372: 48 83 c3 01 add $0x1,%rbx
401376: 48 83 c5 01 add $0x1,%rbp
40137a: 0f b6 03 movzbl (%rbx),%eax
40137d: 84 c0 test %al,%al
40137f: 75 e9 jne 40136a
401381: ba 00 00 00 00 mov $0x0,%edx
401386: eb 13 jmp 40139b
401388: ba 00 00 00 00 mov $0x0,%edx
40138d: eb 0c jmp 40139b
40138f: ba 01 00 00 00 mov $0x1,%edx
401394: eb 05 jmp 40139b
401396: ba 01 00 00 00 mov $0x1,%edx
40139b: 89 d0 mov %edx,%eax
40139d: 5b pop %rbx
40139e: 5d pop %rbp
40139f: 41 5c pop %r12
4013a1: c3 retq
那么我们要做的就是找到地址为0x402400
处的字符串就行了。
使用命令:
objdump -h bomb
查看各个段的信息如下:
bomb: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 0000001c 0000000000400238 0000000000400238 00000238 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 0000000000400254 0000000000400254 00000254 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 0000000000400274 0000000000400274 00000274 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .gnu.hash 00000030 0000000000400298 0000000000400298 00000298 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynsym 00000300 00000000004002c8 00000000004002c8 000002c8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynstr 0000016d 00000000004005c8 00000000004005c8 000005c8 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version 00000040 0000000000400736 0000000000400736 00000736 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version_r 00000060 0000000000400778 0000000000400778 00000778 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rela.dyn 00000060 00000000004007d8 00000000004007d8 000007d8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rela.plt 00000288 0000000000400838 0000000000400838 00000838 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .init 0000000e 0000000000400ac0 0000000000400ac0 00000ac0 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 000001c0 0000000000400ad0 0000000000400ad0 00000ad0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .text 00001614 0000000000400c90 0000000000400c90 00000c90 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .fini 00000009 00000000004022a4 00000000004022a4 000022a4 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .rodata 000004e5 00000000004022b0 00000000004022b0 000022b0 2**4
CONTENTS, ALLOC, LOAD, READONLY, DATA
15 .eh_frame_hdr 00000104 0000000000402798 0000000000402798 00002798 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame 00000454 00000000004028a0 00000000004028a0 000028a0 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .init_array 00000008 0000000000602df8 0000000000602df8 00002df8 2**3
CONTENTS, ALLOC, LOAD, DATA
18 .fini_array 00000008 0000000000602e00 0000000000602e00 00002e00 2**3
CONTENTS, ALLOC, LOAD, DATA
19 .jcr 00000008 0000000000602e08 0000000000602e08 00002e08 2**3
CONTENTS, ALLOC, LOAD, DATA
20 .dynamic 000001d0 0000000000602e10 0000000000602e10 00002e10 2**3
CONTENTS, ALLOC, LOAD, DATA
21 .got 00000008 0000000000602fe0 0000000000602fe0 00002fe0 2**3
CONTENTS, ALLOC, LOAD, DATA
22 .got.plt 000000f0 0000000000602fe8 0000000000602fe8 00002fe8 2**3
CONTENTS, ALLOC, LOAD, DATA
23 .data 00000660 00000000006030e0 00000000006030e0 000030e0 2**5
CONTENTS, ALLOC, LOAD, DATA
24 .bss 000006d0 0000000000603740 0000000000603740 00003740 2**5
ALLOC
25 .comment 00000053 0000000000000000 0000000000000000 00003740 2**0
CONTENTS, READONLY
26 .debug_aranges 00000030 0000000000000000 0000000000000000 00003793 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
27 .debug_info 000007a3 0000000000000000 0000000000000000 000037c3 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
28 .debug_abbrev 0000021f 0000000000000000 0000000000000000 00003f66 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
29 .debug_line 00000161 0000000000000000 0000000000000000 00004185 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
30 .debug_str 000002f3 0000000000000000 0000000000000000 000042e6 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
31 .debug_loc 00000188 0000000000000000 0000000000000000 000045d9 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
发现我们要找的0x402400
在.rodata
段,再使用命令:
objdump -j .rodata -S bomb
单独查看.rodata
的源代码。
bomb: file format elf64-x86-64
Disassembly of section .rodata:
00000000004022b0 <_IO_stdin_used>:
4022b0: 01 00 02 00 72 00 25 73 3a 20 45 72 72 6f 72 3a ....r.%s: Error:
4022c0: 20 43 6f 75 6c 64 6e 27 74 20 6f 70 65 6e 20 25 Couldn't open %
4022d0: 73 0a 00 55 73 61 67 65 3a 20 25 73 20 5b 3c 69 s..Usage: %s []..Tha
4022f0: 74 27 73 20 6e 75 6d 62 65 72 20 32 2e 20 20 4b t's number 2. K
402300: 65 65 70 20 67 6f 69 6e 67 21 00 48 61 6c 66 77 eep going!.Halfw
402310: 61 79 20 74 68 65 72 65 21 00 47 6f 6f 64 20 77 ay there!.Good w
402320: 6f 72 6b 21 20 20 4f 6e 20 74 6f 20 74 68 65 20 ork! On to the
402330: 6e 65 78 74 2e 2e 2e 00 57 65 6c 63 6f 6d 65 20 next....Welcome
402340: 74 6f 20 6d 79 20 66 69 65 6e 64 69 73 68 20 6c to my fiendish l
402350: 69 74 74 6c 65 20 62 6f 6d 62 2e 20 59 6f 75 20 ittle bomb. You
402360: 68 61 76 65 20 36 20 70 68 61 73 65 73 20 77 69 have 6 phases wi
402370: 74 68 00 00 00 00 00 00 77 68 69 63 68 20 74 6f th......which to
402380: 20 62 6c 6f 77 20 79 6f 75 72 73 65 6c 66 20 75 blow yourself u
402390: 70 2e 20 48 61 76 65 20 61 20 6e 69 63 65 20 64 p. Have a nice d
4023a0: 61 79 21 00 00 00 00 00 50 68 61 73 65 20 31 20 ay!.....Phase 1
4023b0: 64 65 66 75 73 65 64 2e 20 48 6f 77 20 61 62 6f defused. How abo
4023c0: 75 74 20 74 68 65 20 6e 65 78 74 20 6f 6e 65 3f ut the next one?
...
4023d8: 53 6f 20 79 6f 75 20 67 6f 74 20 74 68 61 74 20 So you got that
4023e8: 6f 6e 65 2e 20 20 54 72 79 20 74 68 69 73 20 6f one. Try this o
4023f8: 6e 65 2e 00 00 00 00 00 42 6f 72 64 65 72 20 72 ne......Border r
402408: 65 6c 61 74 69 6f 6e 73 20 77 69 74 68 20 43 61 elations with Ca
402418: 6e 61 64 61 20 68 61 76 65 20 6e 65 76 65 72 20 nada have never
402428: 62 65 65 6e 20 62 65 74 74 65 72 2e 00 00 00 00 been better.....
402438: 57 6f 77 21 20 59 6f 75 27 76 65 20 64 65 66 75 Wow! You've defu
402448: 73 65 64 20 74 68 65 20 73 65 63 72 65 74 20 73 sed the secret s
402458: 74 61 67 65 21 00 66 6c 79 65 72 73 00 00 00 00 tage!.flyers....
...
402470: 7c 0f 40 00 00 00 00 00 b9 0f 40 00 00 00 00 00 |.@.......@.....
402480: 83 0f 40 00 00 00 00 00 8a 0f 40 00 00 00 00 00 ..@.......@.....
402490: 91 0f 40 00 00 00 00 00 98 0f 40 00 00 00 00 00 ..@.......@.....
4024a0: 9f 0f 40 00 00 00 00 00 a6 0f 40 00 00 00 00 00 ..@.......@.....
00000000004024b0 :
4024b0: 6d 61 64 75 69 65 72 73 6e 66 6f 74 76 62 79 6c maduiersnfotvbyl
4024c0: 53 6f 20 79 6f 75 20 74 68 69 6e 6b 20 79 6f 75 So you think you
4024d0: 20 63 61 6e 20 73 74 6f 70 20 74 68 65 20 62 6f can stop the bo
4024e0: 6d 62 20 77 69 74 68 20 63 74 72 6c 2d 63 2c 20 mb with ctrl-c,
4024f0: 64 6f 20 79 6f 75 3f 00 43 75 72 73 65 73 2c 20 do you?.Curses,
402500: 79 6f 75 27 76 65 20 66 6f 75 6e 64 20 74 68 65 you've found the
402510: 20 73 65 63 72 65 74 20 70 68 61 73 65 21 00 00 secret phase!..
402520: 42 75 74 20 66 69 6e 64 69 6e 67 20 69 74 20 61 But finding it a
402530: 6e 64 20 73 6f 6c 76 69 6e 67 20 69 74 20 61 72 nd solving it ar
402540: 65 20 71 75 69 74 65 20 64 69 66 66 65 72 65 6e e quite differen
402550: 74 2e 2e 2e 00 00 00 00 43 6f 6e 67 72 61 74 75 t.......Congratu
402560: 6c 61 74 69 6f 6e 73 21 20 59 6f 75 27 76 65 20 lations! You've
402570: 64 65 66 75 73 65 64 20 74 68 65 20 62 6f 6d 62 defused the bomb
402580: 21 00 57 65 6c 6c 2e 2e 2e 00 4f 4b 2e 20 3a 2d !.Well....OK. :-
402590: 29 00 49 6e 76 61 6c 69 64 20 70 68 61 73 65 25 ).Invalid phase%
4025a0: 73 0a 00 0a 42 4f 4f 4d 21 21 21 00 54 68 65 20 s...BOOM!!!.The
4025b0: 62 6f 6d 62 20 68 61 73 20 62 6c 6f 77 6e 20 75 bomb has blown u
4025c0: 70 2e 00 25 64 20 25 64 20 25 64 20 25 64 20 25 p..%d %d %d %d %
4025d0: 64 20 25 64 00 45 72 72 6f 72 3a 20 50 72 65 6d d %d.Error: Prem
4025e0: 61 74 75 72 65 20 45 4f 46 20 6f 6e 20 73 74 64 ature EOF on std
4025f0: 69 6e 00 47 52 41 44 45 5f 42 4f 4d 42 00 45 72 in.GRADE_BOMB.Er
402600: 72 6f 72 3a 20 49 6e 70 75 74 20 6c 69 6e 65 20 ror: Input line
402610: 74 6f 6f 20 6c 6f 6e 67 00 25 64 20 25 64 20 25 too long.%d %d %
402620: 73 00 44 72 45 76 69 6c 00 67 72 65 61 74 77 68 s.DrEvil.greatwh
402630: 69 74 65 2e 69 63 73 2e 63 73 2e 63 6d 75 2e 65 ite.ics.cs.cmu.e
402640: 64 75 00 61 6e 67 65 6c 73 68 61 72 6b 2e 69 63 du.angelshark.ic
402650: 73 2e 63 73 2e 63 6d 75 2e 65 64 75 00 6d 61 6b s.cs.cmu.edu.mak
402660: 6f 73 68 61 72 6b 2e 69 63 73 2e 63 73 2e 63 6d oshark.ics.cs.cm
402670: 75 2e 65 64 75 00 00 00 50 72 6f 67 72 61 6d 20 u.edu...Program
402680: 74 69 6d 65 64 20 6f 75 74 20 61 66 74 65 72 20 timed out after
402690: 25 64 20 73 65 63 6f 6e 64 73 0a 00 00 00 00 00 %d seconds......
4026a0: 45 72 72 6f 72 3a 20 48 54 54 50 20 72 65 71 75 Error: HTTP requ
4026b0: 65 73 74 20 66 61 69 6c 65 64 20 77 69 74 68 20 est failed with
4026c0: 65 72 72 6f 72 20 25 64 3a 20 25 73 00 00 00 00 error %d: %s....
4026d0: 47 45 54 20 2f 25 73 2f 73 75 62 6d 69 74 72 2e GET /%s/submitr.
4026e0: 70 6c 2f 3f 75 73 65 72 69 64 3d 25 73 26 6c 61 pl/?userid=%s&la
4026f0: 62 3d 25 73 26 72 65 73 75 6c 74 3d 25 73 26 73 b=%s&result=%s&s
402700: 75 62 6d 69 74 3d 73 75 62 6d 69 74 20 48 54 54 ubmit=submit HTT
402710: 50 2f 31 2e 30 0d 0a 0d 0a 00 00 00 00 00 00 00 P/1.0...........
402720: 45 72 72 6f 72 3a 20 55 6e 61 62 6c 65 20 74 6f Error: Unable to
402730: 20 63 6f 6e 6e 65 63 74 20 74 6f 20 73 65 72 76 connect to serv
402740: 65 72 20 25 73 00 00 00 25 25 25 30 32 58 00 25 er %s...%%%02X.%
402750: 73 20 25 64 20 25 5b 61 2d 7a 41 2d 7a 20 5d 00 s %d %[a-zA-z ].
402760: 63 68 61 6e 67 65 6d 65 2e 69 63 73 2e 63 73 2e changeme.ics.cs.
402770: 63 6d 75 2e 65 64 75 00 0a 41 55 54 4f 52 45 53 cmu.edu..AUTORES
402780: 55 4c 54 5f 53 54 52 49 4e 47 3d 25 73 0a 00 63 ULT_STRING=%s..c
402790: 73 61 70 70 00 sapp.
找到地址0x402400
开始的字符串就得到了答案:Border relations with Canada have never been better.
当然有更简单的方法:在gdb
中使用x/s 0x402400
直接就能得到答案,上面的做法能够对整个程序各段有个粗略的了解,后面的题目会使用gdb
直接查看所需要的信息。
phase 2
0000000000400efc :
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
400f0a: 83 3c 24 01 cmpl $0x1,(%rsp)
400f0e: 74 20 je 400f30
400f10: e8 25 05 00 00 callq 40143a
400f15: eb 19 jmp 400f30
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
400f20: e8 15 05 00 00 callq 40143a
400f25: 48 83 c3 04 add $0x4,%rbx
400f29: 48 39 eb cmp %rbp,%rbx
400f2c: 75 e9 jne 400f17
400f2e: eb 0c jmp 400f3c
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
400f3c: 48 83 c4 28 add $0x28,%rsp
400f40: 5b pop %rbx
400f41: 5d pop %rbp
400f42: c3 retq
主要逻辑就是:首先读入六个数字存入栈中,然后第一个和0x1
进行比较,如果不等直接结束。
如果相等,那么栈指针向高地址移动八个字节,和前一个的两倍进行比较,如果不等就直接结束,以此类推。
那么答案就是以1
为首,公比为2
的等比数列:1 2 4 8 16 32
。
phase 3
首先贴出汇编代码:
0000000000400f43 :
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
400f65: e8 d0 04 00 00 callq 40143a
400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp)
400f6f: 77 3c ja 400fad
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
400f83: b8 c3 02 00 00 mov $0x2c3,%eax
400f88: eb 34 jmp 400fbe
400f8a: b8 00 01 00 00 mov $0x100,%eax
400f8f: eb 2d jmp 400fbe
400f91: b8 85 01 00 00 mov $0x185,%eax
400f96: eb 26 jmp 400fbe
400f98: b8 ce 00 00 00 mov $0xce,%eax
400f9d: eb 1f jmp 400fbe
400f9f: b8 aa 02 00 00 mov $0x2aa,%eax
400fa4: eb 18 jmp 400fbe
400fa6: b8 47 01 00 00 mov $0x147,%eax
400fab: eb 11 jmp 400fbe
400fad: e8 88 04 00 00 callq 40143a
400fb2: b8 00 00 00 00 mov $0x0,%eax
400fb7: eb 05 jmp 400fbe
400fb9: b8 37 01 00 00 mov $0x137,%eax
400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax
400fc2: 74 05 je 400fc9
400fc4: e8 71 04 00 00 callq 40143a
400fc9: 48 83 c4 18 add $0x18,%rsp
400fcd: c3
看到满屏幕的jmp
,我的直觉就是这可能是个多分支结构,如switch
或者是if...elseif...
。
运行后随意输入字符串,在400f5b
也就是sscanf
处打断点,观察返回值,汇编代码中的逻辑是如果返回值小于等于一,则结束。使用命令
man sscanf
查看sscanf
函数的声明和注释,节选得到:
SYNOPSIS
#include
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
DESCRIPTION
The scanf() function reads input from the standard input stream stdin, fscanf() reads input from the stream
pointer stream, and sscanf() reads its input from the character string pointed to by str.
RETURN VALUE
On success, these functions return the number of input items successfully matched and assigned; this can be
fewer than provided for, or even zero, in the event of an early matching failure.
意思是sscanf
接受两个字符串,第一个是输入的字符串,第二个是读取的格式,就像我们平常使用的scanf
的第一个格式字符串那样。
在gdb
中用x/s $rdi
和x/s $rsi
查看sscanf
接受的两个字符串参数,第一个是我们的输入,第二个则是:
(gdb) x/s $rsi
0x4025cf: "%d %d"
显然意思就是读两个整数。
继续往下看,发现两个整数分别存在了$rsp+0x8
和$rsp+0xc
的位置。
400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp)
400f6f: 77 3c ja 400fad
400f71: 8b 44 24 08 mov 0x8(%rsp),%eax
400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8)
这四行的逻辑是:首先用第一个输入数和0x7
进行比较,如果大于的话,跳到0x400fad
也就是引爆炸弹的位置,否则跳到0x402470+0x8*%rax
的位置中的那个值,注意这里是一个间接寻址,因为前面有个*
。
假设我们输入的第一个数是1
,那么跳转的会是地址为0x402478
处的值,用x/8 0x402478
查看该位置的八个字节,结果为:
(gdb) x/8 0x402478
0x402478: 0xb9 0x0f 0x40 0x00 0x00 0x00 0x00 0x00
由于是小端,容易发现前三个字节形成一个跳转的地址:0x4000b9
,查看附近的汇编代码:
400fb9: b8 37 01 00 00 mov $0x137,%eax
400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax
400fc2: 74 05 je 400fc9
400fc4: e8 71 04 00 00 callq 40143a
400fc9: 48 83 c4 18 add $0x18,%rsp
400fcd: c3 retq
这几行的逻辑是:用第二个数和0x137
进行比较,如果不等就爆炸,等于就成功返回。那么答案已经很显然了,就是:1 311
。
当然这道题有多个解,因为是switch
结构,大概第一个数从1
到7
都行吧,只要第二个数按照他的要求给出就成功了。
phase 4
汇编源码:
0000000000400fce :
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
400fe6: 8d 51 ff lea -0x1(%rcx),%edx
400fe9: e8 e0 ff ff ff callq 400fce
400fee: 01 c0 add %eax,%eax
400ff0: eb 15 jmp 401007
400ff2: b8 00 00 00 00 mov $0x0,%eax
400ff7: 39 f9 cmp %edi,%ecx
400ff9: 7d 0c jge 401007
400ffb: 8d 71 01 lea 0x1(%rcx),%esi
400ffe: e8 cb ff ff ff callq 400fce
401003: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
401007: 48 83 c4 08 add $0x8,%rsp
40100b: c3 retq
000000000040100c :
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
40102e: 83 7c 24 08 0e cmpl $0xe,0x8(%rsp)
401033: 76 05 jbe 40103a
401035: e8 00 04 00 00 callq 40143a
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
40104d: 85 c0 test %eax,%eax
40104f: 75 07 jne 401058
401051: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp)
401056: 74 05 je 40105d
401058: e8 dd 03 00 00 callq 40143a
40105d: 48 83 c4 18 add $0x18,%rsp
401061: c3 retq
首先浏览了一下,发现phase_4
调用了func4
,然后fun4
里面又调用了自己,这必然是个递归函数。
然后与上一题类似,在sscanf
处读取他的第二个参数,发现同样也是"%d %d"
读取两个整数,这两个整数的位置也是和上一题相同,分别在%rsp+0x8
和%rsp+0xc
,然后看这几句:
40102e: 83 7c 24 08 0e cmpl $0xe,0x8(%rsp)
401033: 76 05 jbe 40103a
401035: e8 00 04 00 00 callq 40143a
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
将第一个数和0xe
进行无符号的比较,只有小于或等于才继续进行,注意这里是无符号的比较,那么就将第一个数的范围限定为0x0
到0xe
,排除了较大的数和负数。然后设置了%edx
,%esi
和%edi
,分别是fun4
的三个参数,发现这里%rsp+0xc
也就是第二个输入数并没有参与运算,继续往后看:
40104d: 85 c0 test %eax,%eax
40104f: 75 07 jne 401058
401051: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp)
401056: 74 05 je 40105d
401058: e8 dd 03 00 00 callq 40143a
40105d: 48 83 c4 18 add $0x18,%rsp
401061: c3 retq
容易看出将fun4
的返回值和0x0
进行了比较,将%rsp+0xc
也和0x0
进行了比较,两者都相等时才正常返回,那么这里第二个输入数已经确定为0
了,第一个输入数需要使得fun4
返回0
。
回过头来看fun4
,输入的三个参数已经确定分别为输入数1
,0
和0xe
,这里一段杂乱无章的递归程序我没看出什么特征,只能仔细翻译为c
语言如下:
int fun4(int a, int b, int c)
{
int d = (int((unsigned(c - b) >> 15) + (c - b)) >> 1) + b;
if (a < d)
return 2 * fun4(a, b, d - 1);
else if (d < a)
return 2 * fun4(a, d + 1, c) + 1;
return 0;
}
注意这里的逻辑右移和算数右移,由于数据范围过小,直接暴力跑出答案为0
或1
或3
或7
。逐个试验后发现都成功了。
ps:
把这段c
代码编译后和原汇编代码长得还挺像的。
phase 5
汇编代码:
0000000000401062 :
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
40107f: 83 f8 06 cmp $0x6,%eax
401082: 74 4e je 4010d2
401084: e8 b1 03 00 00 callq 40143a
401089: eb 47 jmp 4010d2
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
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
4010c2: 85 c0 test %eax,%eax
4010c4: 74 13 je 4010d9
4010c6: e8 6f 03 00 00 callq 40143a
4010cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
4010d0: eb 07 jmp 4010d9
4010d2: b8 00 00 00 00 mov $0x0,%eax
4010d7: eb b2 jmp 40108b
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
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
看到
后面跟着
大概就猜到又是要比较两个字符串了。
40107a: e8 9c 02 00 00 callq 40131b
40107f: 83 f8 06 cmp $0x6,%eax
401082: 74 4e je 4010d2
401084: e8 b1 03 00 00 callq 40143a
首先判断读入的字符串长度是否为6
,不是的话引爆。
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
这里是一个循环,可以看出循环变量%rax
从1
到6
,每次将输入字符串中的一位取最低4
位,找到栈中地址为0x4024b0+%rax
的地方,将其中的值edx
放入栈中%rsp+%rax+0x10
的位置,也就是通过一个映射,将输入的字符最低4
位作为偏移量,变换成另外一个字符串。
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
4010c2: 85 c0 test %eax,%eax
4010c4: 74 13 je 4010d9
4010c6: e8 6f 03 00 00 callq 40143a
最后再和0x40245e
处的字符串进行比较,这一部分在前面的阶段中也出现过。我们可以先用x/s 0x40245e
获取进行比较的常量字符串为:
(gdb) x/s 0x40245e
0x40245e: "flyers"
然后同样用x/s 0x4024b0
找到映射的那个数组:
(gdb) x/s 0x4024b0
0x4024b0 : "maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"
逐个寻找每个字母的偏移量,并且使得我们输入的字符串最低四位与这个偏移量相等即可,一个可行的答案是:ionefg
。
phase 6
代码:
00000000004010f4 :
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
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
401123: e8 12 03 00 00 callq 40143a
401128: 41 83 c4 01 add $0x1,%r12d
40112c: 41 83 fc 06 cmp $0x6,%r12d
401130: 74 21 je 401153
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
401140: e8 f5 02 00 00 callq 40143a
401145: 83 c3 01 add $0x1,%ebx
401148: 83 fb 05 cmp $0x5,%ebx
40114b: 7e e8 jle 401135
40114d: 49 83 c5 04 add $0x4,%r13
401151: eb c1 jmp 401114
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
40116f: be 00 00 00 00 mov $0x0,%esi
401174: eb 21 jmp 401197
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
401181: eb 05 jmp 401188
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
401197: 8b 0c 34 mov (%rsp,%rsi,1),%ecx
40119a: 83 f9 01 cmp $0x1,%ecx
40119d: 7e e4 jle 401183
40119f: b8 01 00 00 00 mov $0x1,%eax
4011a4: ba d0 32 60 00 mov $0x6032d0,%edx
4011a9: eb cb jmp 401176
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
4011cd: 48 89 d1 mov %rdx,%rcx
4011d0: eb eb jmp 4011bd
4011d2: 48 c7 42 08 00 00 00 movq $0x0,0x8(%rdx)
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
4011e9: e8 4c 02 00 00 callq 40143a
4011ee: 48 8b 5b 08 mov 0x8(%rbx),%rbx
4011f2: 83 ed 01 sub $0x1,%ebp
4011f5: 75 e8 jne 4011df
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
最后一个阶段太长了,于是偷了个懒,很多地方靠猜也猜出来了,整个流程分为许多过程:
一开始是读入6
个数字,和之前一样,存在栈中,地址分别从%rsp
到%rsp+0x14
。
首先是一段二重循环,先看内循环:
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
401140: e8 f5 02 00 00 callq 40143a
401145: 83 c3 01 add $0x1,%ebx
401148: 83 fb 05 cmp $0x5,%ebx
这里是一段内部循环,每次将%rsp+4*%rax
与%rbp
进行比较,这里的%rsp+4*%rax
就是我们读入的数组,%rbp
是当前遍历的数字。
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
401123: e8 12 03 00 00 callq 40143a
401128: 41 83 c4 01 add $0x1,%r12d
40112c: 41 83 fc 06 cmp $0x6,%r12d
401130: 74 21 je 401153
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
401140: e8 f5 02 00 00 callq 40143a
401145: 83 c3 01 add $0x1,%ebx
401148: 83 fb 05 cmp $0x5,%ebx
40114b: 7e e8 jle 401135
40114d: 49 83 c5 04 add $0x4,%r13
401151: eb c1 jmp 401114
外层循环不断改变%rbp
,结合在一起就是用一个二重循环判断输入的数字中必须两两不同,而且还要范围在1
到6
之间。
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
这里一段的作用是用常数7
减去输入的各个数字。
再继续往下看:
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
401181: eb 05 jmp 401188
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
401197: 8b 0c 34 mov (%rsp,%rsi,1),%ecx
40119a: 83 f9 01 cmp $0x1,%ecx
40119d: 7e e4 jle 401183
40119f: b8 01 00 00 00 mov $0x1,%eax
4011a4: ba d0 32 60 00 mov $0x6032d0,%edx
第一行到第四行是一个小循环,每次将%eax
加一,直到等于%ecx
为止,同时还执行了mov 0x8(%rdx),%rdx
的操作,这里非常重要,因为这句的意思是用自己位置偏移8
后所指向的值代替自己,也就是这是一个指向下一个的指针,使用命令查看数据:
(gdb) x/16 0x6032d0
0x6032d0 : 0x4c 0x01 0x00 0x00 0x01 0x00 0x00 0x00
0x6032d8 : 0xe0 0x32 0x60 0x00 0x00 0x00 0x00 0x00
变量名
和指向下一个地址的0x6032e0
验证了猜想:这是一个链表。
再观察整个结构,发现%rcx
是从%rsp
所指向的位置开始,也就是我们输入的第一个数,遍历整个输入的六个数的。
在找到链表的第%rcx
个数后,会放到栈上的0x20(%rsp,%rsi,2)
处。
那么这段代码的意思就很明显了,根据我们的输入数字,依次暴力找到链表的那个位置,依次放到栈上连续的一些位置。
其实做到这里,已经猜到了一些答案了,既然我们的输入得是一到六,又两两不能相同,再结合上面的操作,不难猜到是要用这些数字对链表进行一个排序。
然后我直接跳到最后看:
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
4011e9: e8 4c 02 00 00 callq 40143a
4011ee: 48 8b 5b 08 mov 0x8(%rbx),%rbx
4011f2: 83 ed 01 sub $0x1,%ebp
4011f5: 75 e8 jne 4011df
这里也比较简单,依次对链表的前后两个元素进行比较,如果前者更大则继续。
查看链表各个元素的值:
(gdb) x/96 0x6032d0
0x6032d0 : 0x4c 0x01 0x00 0x00 0x01 0x00 0x00 0x00
0x6032d8 : 0xe0 0x32 0x60 0x00 0x00 0x00 0x00 0x00
0x6032e0 : 0xa8 0x00 0x00 0x00 0x02 0x00 0x00 0x00
0x6032e8 : 0xf0 0x32 0x60 0x00 0x00 0x00 0x00 0x00
0x6032f0 : 0x9c 0x03 0x00 0x00 0x03 0x00 0x00 0x00
0x6032f8 : 0x00 0x33 0x60 0x00 0x00 0x00 0x00 0x00
0x603300 : 0xb3 0x02 0x00 0x00 0x04 0x00 0x00 0x00
0x603308 : 0x10 0x33 0x60 0x00 0x00 0x00 0x00 0x00
0x603310 : 0xdd 0x01 0x00 0x00 0x05 0x00 0x00 0x00
0x603318 : 0x20 0x33 0x60 0x00 0x00 0x00 0x00 0x00
0x603320 : 0xbb 0x01 0x00 0x00 0x06 0x00 0x00 0x00
0x603328 : 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
按照从大到小的排序,答案应是3 4 5 6 1 2
,因为前面被7
减过,继续反推得到答案4 3 2 1 6 5
。
secret phase
最后还有一个秘密关卡,首先要找到进入这个密码关卡的方式。
直接在代码中搜索
,发现在
中调用到了这个函数。
而每次通过一关,都会调用这个
代码:
00000000004015c4 :
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
4015df: 75 5e jne 40163f
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
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
401613: 85 c0 test %eax,%eax
401615: 75 1e jne 401635
401617: bf f8 24 40 00 mov $0x4024f8,%edi
40161c: e8 ef f4 ff ff callq 400b10
401621: bf 20 25 40 00 mov $0x402520,%edi
401626: e8 e5 f4 ff ff callq 400b10
40162b: b8 00 00 00 00 mov $0x0,%eax
401630: e8 0d fc ff ff callq 401242
401635: bf 58 25 40 00 mov $0x402558,%edi
40163a: e8 d1 f4 ff ff callq 400b10
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
40164f: e8 dc f4 ff ff callq 400b30 <__stack_chk_fail@plt>
401654: 48 83 c4 78 add $0x78,%rsp
401658: c3 retq
仔细分析可以看到首先要将0x202181(%rip)
和0x6
进行比较,在调试的过程中可以看出,这里的0x202181(%rip)
其实就是关卡号。
然后调用了sscanf
,有了前面的经验,直接找到两个字符串分别为:
(gdb) x/s 0x603870
0x603870 : "1 0"
(gdb) x/s 0x402619
0x402619: "%d %d %s"
这里需要读取三个,而第一个字符串中只有两个,怎么样使得变成三个呢。从地址入手,观察最先的那个表格,发现这一段地址在.bss
段,而.bss
段一般存放未初始化或者是初始化为0
的全局变量。
在gdb
中观察附近的字符串,发现全是我们输入的字符串,那么就能猜出输入字符串就存放在这里,第四个输入的1 0
和这里的结果对上了,验证了想法。
然后又是似曾相识的一个过程,调用
对两个字符串进行比较,其中一个可以用x/s 0x402622
直接得到:
(gdb) x/s 0x402622
0x402622: "DrEvil"
另一个则是需要输入的字符串。
将答案的第四行修改为1 0 DrEvil
成功进入秘密阶段。
0000000000401242 :
401242: 53 push %rbx
401243: e8 56 02 00 00 callq 40149e
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
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
401267: e8 ce 01 00 00 callq 40143a
40126c: 89 de mov %ebx,%esi
40126e: bf f0 30 60 00 mov $0x6030f0,%edi
401273: e8 8c ff ff ff callq 401204
401278: 83 f8 02 cmp $0x2,%eax
40127b: 74 05 je 401282
40127d: e8 b8 01 00 00 callq 40143a
401282: bf 38 24 40 00 mov $0x402438,%edi
401287: e8 84 f8 ff ff callq 400b10
40128c: e8 33 03 00 00 callq 4015c4
401291: 5b pop %rbx
401292: c3 retq
进入后先是读取了一行字符串,然后调用了系统函数strtol
将字符串转化为long
,然后的要求是输入的数必须小于等于1001
,设置参数%rdi
为0x6030f0
,%rsi
为输入数后调用fun7
,并且返回值必须为2
不然就爆炸。
0000000000401204 :
401204: 48 83 ec 08 sub $0x8,%rsp
401208: 48 85 ff test %rdi,%rdi
40120b: 74 2b je 401238
40120d: 8b 17 mov (%rdi),%edx
40120f: 39 f2 cmp %esi,%edx
401211: 7e 0d jle 401220
401213: 48 8b 7f 08 mov 0x8(%rdi),%rdi
401217: e8 e8 ff ff ff callq 401204
40121c: 01 c0 add %eax,%eax
40121e: eb 1d jmp 40123d
401220: b8 00 00 00 00 mov $0x0,%eax
401225: 39 f2 cmp %esi,%edx
401227: 74 14 je 40123d
401229: 48 8b 7f 10 mov 0x10(%rdi),%rdi
40122d: e8 d2 ff ff ff callq 401204
401232: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
401236: eb 05 jmp 40123d
401238: b8 ff ff ff ff mov $0xffffffff,%eax
40123d: 48 83 c4 08 add $0x8,%rsp
401241: c3 retq
这一部分的代码比较短,也比较好读,下面调用了两次fun7
说明这也是一个递归程序,而且观察得到%rsi
的值在整个递归的过程中没有变化过,起到的只是一个比较的作用。
一开始还检测了一下%rdi
是否为0
,后面设置递归参数的时候用mov 0x8(%rdi),%rdi
,自身加上一个偏移量的间接寻址代替自身,基本可以确定%rdi
是一个指针,%rdi+0x8
和%rdi+0x10
同样也是一个指针,看到这里基本已经猜出这个数据结构就是二叉树了,之后的寻找答案也就不难了,顺着左右儿子找一下就得到答案了:
(gdb) x/24x 0x6030f0
0x6030f0 : 0x24 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x6030f8 : 0x10 0x31 0x60 0x00 0x00 0x00 0x00 0x00
0x603100 : 0x30 0x31 0x60 0x00 0x00 0x00 0x00 0x00
(gdb) x/24x 0x603110
0x603110 : 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x603118 : 0x90 0x31 0x60 0x00 0x00 0x00 0x00 0x00
0x603120 : 0x50 0x31 0x60 0x00 0x00 0x00 0x00 0x00
(gdb) x/24x 0x603150
0x603150 : 0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x603158 : 0x70 0x32 0x60 0x00 0x00 0x00 0x00 0x00
0x603160 : 0x30 0x32 0x60 0x00 0x00 0x00 0x00 0x00
答案是0x16
,也就是22
。
总结
答案:
Border relations with Canada have never been better.
1 2 4 8 16 32
1 311
1 0 DrEvil
ionefg
4 3 2 1 6 5
22
总体难度感觉没有上一章难,但是既有趣又有收获,不愧是cmu
的实验。通过这几个实验不仅对程序的整个结构有了更多的了解,还通过逆向工程了解了许多变量和数据结构的机器级表示,熟悉了程序在实际运行时真实的情况,收获非常大。