实验目标
拆弹,熟悉汇编语言,学习gdb调试。
实验方法
gdb查看内存,手动调试汇编代码,了解代码原理并找到密码。
实验结果
6关+隐藏关全部拆弹,引爆炸弹1次,SCORE 70.
密码:
Brownie, you are doing a heck of a job.
0 1 1 2 3 5
1 868
132 4 DrEvil
nnlccc
3 6 2 4 1 5
6
解题过程
phase_1
0000000000400ef0 <phase_1>:
400ef0: 48 83 ec 08 sub $0x8,%rsp
400ef4: be 80 24 40 00 mov $0x402480,%esi
400ef9: e8 c0 03 00 00 callq 4012be <strings_not_equal>
400efe: 85 c0 test %eax,%eax
400f00: 74 05 je 400f07 <phase_1+0x17>
400f02: e8 1d 06 00 00 callq 401524 <explode_bomb>
400f07: 48 83 c4 08 add $0x8,%rsp
400f0b: c3 retq
查看0x402480内存,得到字符串:
Brownie, you are doing a heck of a job.\0 ...
strings_not_equal()函数比较%edi(密码)和%esi字符串是否相等,相等则返回0。
故把密码设为上述字符串即可通关。
phase_2
0000000000400f0c <phase_2>:
400f0c: 55 push %rbp
400f0d: 53 push %rbx
400f0e: 48 83 ec 28 sub $0x28,%rsp
400f12: 48 89 e6 mov %rsp,%rsi
400f15: e8 40 06 00 00 callq 40155a <read_six_numbers>
read_six_numbers()读入6个int类型的数,并把数放入栈(%rsp).
400f1a: 83 3c 24 00 cmpl $0x0,(%rsp)
400f1e: 75 07 jne 400f27 <phase_2+0x1b>
400f20: 83 7c 24 04 01 cmpl $0x1,0x4(%rsp)
400f25: 74 21 je 400f48 <phase_2+0x3c>
400f27: e8 f8 05 00 00 callq 401524 <explode_bomb>
此处需确保第一个数和第二个数为0和1.
400f2c: eb 1a jmp 400f48 <phase_2+0x3c>
400f2e: 8b 43 f8 mov -0x8(%rbx),%eax
400f31: 03 43 fc add -0x4(%rbx),%eax
400f34: 39 03 cmp %eax,(%rbx)
400f36: 74 05 je 400f3d <phase_2+0x31>
400f38: e8 e7 05 00 00 callq 401524 <explode_bomb>
400f3d: 48 83 c3 04 add $0x4,%rbx
400f41: 48 39 eb cmp %rbp,%rbx
400f44: 75 e8 jne 400f2e <phase_2+0x22>
400f46: eb 0c jmp 400f54 <phase_2+0x48>
400f48: 48 8d 5c 24 08 lea 0x8(%rsp),%rbx
400f4d: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp
400f52: eb da jmp 400f2e <phase_2+0x22>
此处程序进行一个循环,从第3个数开始,依次判断前一个数是否等于后两个数之和(cmp %eax, (%rbx))。
故答案为:
0 1 1 2 3 5
phase_3
0000000000400f5b <phase_3>:
400f5b: 48 83 ec 18 sub $0x18,%rsp
400f5f: 48 8d 4c 24 08 lea 0x8(%rsp),%rcx
400f64: 48 8d 54 24 0c lea 0xc(%rsp),%rdx
400f69: be ad 27 40 00 mov $0x4027ad,%esi
400f6e: b8 00 00 00 00 mov $0x0,%eax
400f73: e8 b8 fc ff ff callq 400c30 <__isoc99_sscanf@plt>
400f78: 83 f8 01 cmp $0x1,%eax
400f7b: 7f 05 jg 400f82 <phase_3+0x27>
400f7d: e8 a2 05 00 00 callq 401524 <explode_bomb>
400f82: 83 7c 24 0c 07 cmpl $0x7,0xc(%rsp)
400f87: 77 3c ja 400fc5 <phase_3+0x6a>
400f89: 8b 44 24 0c mov 0xc(%rsp),%eax
400f8d: ff 24 c5 e0 24 40 00 jmpq *0x4024e0(,%rax,8)
读入两个数,注意最后一条指令jmpq, 依据%rax的值(第一个参数)进行条件跳转,此处随意选取第一个参数为1.
400fd1: b8 64 03 00 00 mov $0x364,%eax
400fd6: 3b 44 24 08 cmp 0x8(%rsp),%eax
400fda: 74 05 je 400fe1 <phase_3+0x86>
400fdc: e8 43 05 00 00 callq 401524 <explode_bomb>
400fe1: 48 83 c4 18 add $0x18,%rsp
400fe5: c3 retq
跳转到此处可知第二个参数为0x364=868.
phase_4
000000000040101e <phase_4>:
40101e: 48 83 ec 18 sub $0x18,%rsp
401022: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx
401027: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
40102c: be ad 27 40 00 mov $0x4027ad,%esi
401031: b8 00 00 00 00 mov $0x0,%eax
401036: e8 f5 fb ff ff callq 400c30 <__isoc99_sscanf@plt>
40103b: 83 f8 02 cmp $0x2,%eax
40103e: 75 0c jne 40104c <phase_4+0x2e>
401040: 8b 44 24 0c mov 0xc(%rsp),%eax
401044: 83 e8 02 sub $0x2,%eax
401047: 83 f8 02 cmp $0x2,%eax
40104a: 76 05 jbe 401051 <phase_4+0x33>
40104c: e8 d3 04 00 00 callq 401524 <explode_bomb>
读入两个参数,需保证第二个参数的值>=4.
401051: 8b 74 24 0c mov 0xc(%rsp),%esi
401055: bf 07 00 00 00 mov $0x7,%edi
40105a: e8 87 ff ff ff callq 400fe6 <func4>
40105f: 3b 44 24 08 cmp 0x8(%rsp),%eax
401063: 74 05 je 40106a <phase_4+0x4c>
401065: e8 ba 04 00 00 callq 401524 <explode_bomb>
40106a: 48 83 c4 18 add $0x18,%rsp
40106e: c3 retq
调用func4(),这是一个递归函数,设传入的两个参数为a,b,分析可知func(a,b)=b+func(a-1,b)+func(a-2,b),取b=4,返回值就是f(7, 4).当然此处没必要分析此函数,直接gdb运行并查看返回值即可。
第一个参数即为递归函数的返回值,此处为132.
phase_5
000000000040106f <phase_5>:
40106f: 53 push %rbx
401070: 48 89 fb mov %rdi,%rbx
401073: e8 29 02 00 00 callq 4012a1 <string_length>
401078: 83 f8 06 cmp $0x6,%eax
40107b: 74 05 je 401082 <phase_5+0x13>
40107d: e8 a2 04 00 00 callq 401524 <explode_bomb>
读入一个6字符字符串。
401082: b8 00 00 00 00 mov $0x0,%eax
401087: ba 00 00 00 00 mov $0x0,%edx
40108c: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx
401090: 83 e1 0f and $0xf,%ecx
401093: 03 14 8d 20 25 40 00 add 0x402520(,%rcx,4),%edx
40109a: 48 83 c0 01 add $0x1,%rax
40109e: 48 83 f8 06 cmp $0x6,%rax
4010a2: 75 e8 jne 40108c <phase_5+0x1d>
4010a4: 83 fa 2c cmp $0x2c,%edx
4010a7: 74 05 je 4010ae <phase_5+0x3f>
4010a9: e8 76 04 00 00 callq 401524 <explode_bomb>
4010ae: 5b pop %rbx
4010af: 90 nop
4010b0: c3 retq
查看0x402520内存,得到一个映射表:
2,a,6,1,c,0x10,9,3,4,7,e,5,b,8,f,d
依次枚举每一个字符,取末4位(&0xf)通过上述映射表得到一个数,如此得到6个数,相加之和需等于0x2c=44.
此处答案为nnlccc(15+15+11+1+1+1).
phase_6
00000000004010b1 <phase_6>:
4010b1: 41 55 push %r13
4010b3: 41 54 push %r12
4010b5: 55 push %rbp
4010b6: 53 push %rbx
4010b7: 48 83 ec 58 sub $0x58,%rsp
4010bb: 48 8d 74 24 30 lea 0x30(%rsp),%rsi
4010c0: e8 95 04 00 00 callq 40155a <read_six_numbers>
4010c5: 4c 8d 6c 24 30 lea 0x30(%rsp),%r13
4010ca: 41 bc 00 00 00 00 mov $0x0,%r12d
4010d0: 4c 89 ed mov %r13,%rbp
4010d3: 41 8b 45 00 mov 0x0(%r13),%eax
4010d7: 83 e8 01 sub $0x1,%eax
4010da: 83 f8 05 cmp $0x5,%eax
4010dd: 76 05 jbe 4010e4 <phase_6+0x33>
4010df: e8 40 04 00 00 callq 401524 <explode_bomb>
4010e4: 41 83 c4 01 add $0x1,%r12d
4010e8: 41 83 fc 06 cmp $0x6,%r12d
4010ec: 75 07 jne 4010f5 <phase_6+0x44>
4010ee: be 00 00 00 00 mov $0x0,%esi
4010f3: eb 42 jmp 401137 <phase_6+0x86>
4010f5: 44 89 e3 mov %r12d,%ebx
4010f8: 48 63 c3 movslq %ebx,%rax
4010fb: 8b 44 84 30 mov 0x30(%rsp,%rax,4),%eax
4010ff: 39 45 00 cmp %eax,0x0(%rbp)
401102: 75 05 jne 401109 <phase_6+0x58>
401104: e8 1b 04 00 00 callq 401524 <explode_bomb>
401109: 83 c3 01 add $0x1,%ebx
40110c: 83 fb 05 cmp $0x5,%ebx
40110f: 7e e7 jle 4010f8 <phase_6+0x47>
401111: 49 83 c5 04 add $0x4,%r13
401115: eb b9 jmp 4010d0 <phase_6+0x1f>
这一段读入6个整数,要求每个数互不相等且>=1,<=6,故输入为1~6的排列,不妨设为a~f.
401117: 48 8b 52 08 mov 0x8(%rdx),%rdx
40111b: 83 c0 01 add $0x1,%eax
40111e: 39 c8 cmp %ecx,%eax
401120: 75 f5 jne 401117 <phase_6+0x66>
401122: eb 05 jmp 401129 <phase_6+0x78>
401124: ba f0 42 60 00 mov $0x6042f0,%edx
401129: 48 89 14 74 mov %rdx,(%rsp,%rsi,2)
40112d: 48 83 c6 04 add $0x4,%rsi
401131: 48 83 fe 18 cmp $0x18,%rsi
401135: 74 15 je 40114c <phase_6+0x9b>
401137: 8b 4c 34 30 mov 0x30(%rsp,%rsi,1),%ecx
40113b: 83 f9 01 cmp $0x1,%ecx
40113e: 7e e4 jle 401124 <phase_6+0x73>
401140: b8 01 00 00 00 mov $0x1,%eax
401145: ba f0 42 60 00 mov $0x6042f0,%edx
40114a: eb cb jmp 401117 <phase_6+0x66>
对6个数分别分配了6个8字节变量,不妨设为a'~f',第i个数的值为0x6042f0 + (i-1)*0x10 (地址).
查看0x6042f0内存:
| address | value |
|---|---|
| 0x6042f0 | 0x0100000380 |
| 0x0000604300 | |
| 0x604300 | 0x020000028d |
| 0x0000604310 | |
| 0x604310 | 0x03000000ea |
| 0x0000604320 | |
| 0x604320 | 0x0400000372 |
| 0x0000604330 | |
| 0x604330 | 0x05000003cd |
| 0x0000604340 | |
| 0x604340 | 0x06000000eb |
| 0x0000000000 |
此处易知每个新变量对应块(16位)的第一个值为一般值,第二个值(后8位)为指向下一个地址的指针。
40114c: 48 8b 1c 24 mov (%rsp),%rbx
401150: 48 8d 44 24 08 lea 0x8(%rsp),%rax
401155: 48 8d 74 24 30 lea 0x30(%rsp),%rsi
40115a: 48 89 d9 mov %rbx,%rcx
40115d: 48 8b 10 mov (%rax),%rdx
401160: 48 89 51 08 mov %rdx,0x8(%rcx)
401164: 48 83 c0 08 add $0x8,%rax
401168: 48 39 f0 cmp %rsi,%rax
40116b: 74 05 je 401172 <phase_6+0xc1>
40116d: 48 89 d1 mov %rdx,%rcx
401170: eb eb jmp 40115d <phase_6+0xac>
401172: 48 c7 42 08 00 00 00 movq $0x0,0x8(%rdx)
按照a'~f'的值依次调整每个块的指针为哪个块,如a'的指针初始指向b'(0x604300),可更改为c'(0x604310)。
40117a: bd 05 00 00 00 mov $0x5,%ebp
40117f: 48 8b 43 08 mov 0x8(%rbx),%rax
401183: 8b 00 mov (%rax),%eax
401185: 39 03 cmp %eax,(%rbx)
401187: 7e 05 jle 40118e <phase_6+0xdd>
401189: e8 96 03 00 00 callq 401524 <explode_bomb>
40118e: 48 8b 5b 08 mov 0x8(%rbx),%rbx
401192: 83 ed 01 sub $0x1,%ebp
401195: 75 e8 jne 40117f <phase_6+0xce>
401197: 48 83 c4 58 add $0x58,%rsp
40119b: 5b pop %rbx
40119c: 5d pop %rbp
40119d: 41 5c pop %r12
40119f: 41 5d pop %r13
4011a1: c3 retq
从a'开始依次判断前一个块的值(取前32位比较)是否小于等于后一个块,所有都小于等于才能通关。
将a=1,2,...,6对应块的值重新排序如下:0x00ea, 0x00eb, 0x028d, 0x0372, 0x0380, 0x03cd,这样只需将a'设为0x00ea对应块3,再将a'的指针设为0x00eb对应块6,...。
密码为3 6 2 4 1 5.
secret_phase
进入方法:
搜索secret_phase,在phase_defused()函数可找到入口:
00000000004016c2 <phase_defused>:
4016c2: 48 83 ec 68 sub $0x68,%rsp
4016c6: bf 01 00 00 00 mov $0x1,%edi
4016cb: e8 90 fd ff ff callq 401460 <send_msg>
4016d0: 83 3d c5 30 20 00 06 cmpl $0x6,0x2030c5(%rip) # 60479c <num_input_strings>
4016d7: 75 6d jne 401746 <phase_defused+0x84>
可看出第6关破解后才能进入。
4016d9: 4c 8d 44 24 10 lea 0x10(%rsp),%r8
4016de: 48 8d 4c 24 08 lea 0x8(%rsp),%rcx
4016e3: 48 8d 54 24 0c lea 0xc(%rsp),%rdx
4016e8: be f7 27 40 00 mov $0x4027f7,%esi
4016ed: bf b0 48 60 00 mov $0x6048b0,%edi
4016f2: b8 00 00 00 00 mov $0x0,%eax
4016f7: e8 34 f5 ff ff callq 400c30 <__isoc99_sscanf@plt>
4016fc: 83 f8 03 cmp $0x3,%eax
4016ff: 75 31 jne 401732 <phase_defused+0x70>
401701: be 00 28 40 00 mov $0x402800,%esi
401706: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
40170b: e8 ae fb ff ff callq 4012be <strings_not_equal>
401710: 85 c0 test %eax,%eax
401712: 75 1e jne 401732 <phase_defused+0x70>
前部分读入一个串,查看内存知读入两个整数一个字符串,结合之前的密码知为读取phase_4的密码对应行并检查最后一个字符串其是否与答案字符串(“DrEvil")相等。
调整密码后进入隐藏关:
00000000004011e0 <secret_phase>:
4011e0: 53 push %rbx
4011e1: e8 b6 03 00 00 callq 40159c <read_line>
4011e6: ba 0a 00 00 00 mov $0xa,%edx
4011eb: be 00 00 00 00 mov $0x0,%esi
4011f0: 48 89 c7 mov %rax,%rdi
4011f3: e8 08 fa ff ff callq 400c00 <strtol@plt>
4011f8: 48 89 c3 mov %rax,%rbx
4011fb: 8d 40 ff lea -0x1(%rax),%eax
4011fe: 3d e8 03 00 00 cmp $0x3e8,%eax
401203: 76 05 jbe 40120a <secret_phase+0x2a>
401205: e8 1a 03 00 00 callq 401524 <explode_bomb>
read_line()读入一行字符,strtol()函数将一行字符串转换为10进制数并存入%rax,要求%rax<=1001.
40120a: 89 de mov %ebx,%esi
40120c: bf 10 41 60 00 mov $0x604110,%edi
401211: e8 8c ff ff ff callq 4011a2 <fun7>
401216: 85 c0 test %eax,%eax
401218: 74 05 je 40121f <secret_phase+0x3f>
40121a: e8 05 03 00 00 callq 401524 <explode_bomb>
40121f: bf a8 24 40 00 mov $0x4024a8,%edi
401224: e8 17 f9 ff ff callq 400b40 <puts@plt>
401229: e8 94 04 00 00 callq 4016c2 <phase_defused>
40122e: 5b pop %rbx
40122f: c3 retq
进入函数fun7(),若返回值为0则通关。
00000000004011a2 <fun7>:
4011a2: 48 83 ec 08 sub $0x8,%rsp
4011a6: 48 85 ff test %rdi,%rdi
4011a9: 74 2b je 4011d6 <fun7+0x34>
4011ab: 8b 17 mov (%rdi),%edx
4011ad: 39 f2 cmp %esi,%edx
4011af: 7e 0d jle 4011be <fun7+0x1c>
4011b1: 48 8b 7f 08 mov 0x8(%rdi),%rdi
4011b5: e8 e8 ff ff ff callq 4011a2 <fun7>
4011ba: 01 c0 add %eax,%eax
4011bc: eb 1d jmp 4011db <fun7+0x39>
4011be: b8 00 00 00 00 mov $0x0,%eax
4011c3: 39 f2 cmp %esi,%edx
4011c5: 74 14 je 4011db <fun7+0x39>
4011c7: 48 8b 7f 10 mov 0x10(%rdi),%rdi
4011cb: e8 d2 ff ff ff callq 4011a2 <fun7>
4011d0: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
4011d4: eb 05 jmp 4011db <fun7+0x39>
4011d6: b8 ff ff ff ff mov $0xffffffff,%eax
4011db: 48 83 c4 08 add $0x8,%rsp
4011df: c3 retq
func7()为递归转移,有些复杂,大致意思是不断比较你输入的值x和一个值y,若x>y则增大y,若x<y则减小y,y的增大还和内存有关。经过一段分析,取x=6可以得到返回值0.
得失总结
锻炼了不依赖gdb肉眼调试程序的能力;学习到了gdb的许多新用法;对汇编语言更加了解了。




Comments | NOTHING