这个题的本质是让通过gdb和objdump的工具的使用,以及对汇编代码的理解找到程序在栈中已存在的数据或者汇编代码本身的代码逻辑。从而根据固有信息确定输入,使得炸弹得以解除。这个实验有6道关卡和一道隐藏关卡。由于时间原因,我并没有想方法找出隐藏关,只是把固有的6道关卡做完了。下面说一下解题步骤。
由于炸弹爆炸是要扣分的,所以最开始需要做的任务是在炸弹爆炸函数的入口设置一个断点。这样一来当发现运行到这个断点之后就可以重新开始gdb的运行,使得爆炸函数得不到运行。由于是只有通过前一关后才能做后一关所以当做到后面几关的时候,要把前几关的答案放在一个文档里,然后运行gdb时指定这个文件,就会自动通过前几关。
关于gdb的使用可以看我的另一篇文章。
上代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
08048b90 <phase_1>:
8048b90: 55 push %ebp
8048b91: 89 e5 mov %esp,%ebp
8048b93: 83 ec 18 sub $0x18,%esp
8048b96: c7 44 24 04 e4 a2 04 movl $0x804a2e4,0x4(%esp)
8048b9d: 08
8048b9e: 8b 45 08 mov 0x8(%ebp),%eax
8048ba1: 89 04 24 mov %eax,(%esp)
8048ba4: e8 a2 04 00 00 call 804904b <strings_not_equal>
8048ba9: 85 c0 test %eax,%eax
8048bab: 74 05 je 8048bb2 <phase_1+0x22>
8048bad: e8 86 07 00 00 call 8049338 <explode_bomb>
8048bb2: c9 leave
8048bb3: c3 ret
|
以上是通过用objdump工具反汇编bomb可执行程序后得到的汇编代码文件中第一关的代码。这个代码的意义非常简单。通读下来发现首先确定栈的大小。然后为函数strings_not_equal准备参数。参数总共有两个一个是用户自己输入的字符串。由于是字符串,肯定不是直接把字符串本身放在运行占空间里的,所以通过ebp寄存器可以找到存放字符串的内存地址。同理,很自然的可以知道0x804a2e4即是程序本身的一段字符串。通过匹配字符串,便可以把炸弹解开。因此此题的目的便是把字符串找出来,因此便用到了gdb工具。
首先,用p/x *(int *)0x804a2e4便可以得到一个地址。假设地址为ooxx,那么要想查看这段地址存放的字符串内容便可以通过x/s ooxx既可以得到字符串的内容。我的答案是:There are rumors on the internets.每个人的答案都不一样。哈哈。下面来看第二题。
上代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
08048bb4 <phase_2>:
8048bb4: 55 push %ebp
8048bb5: 89 e5 mov %esp,%ebp
8048bb7: 56 push %esi
8048bb8: 53 push %ebx
8048bb9: 83 ec 30 sub $0x30,%esp
8048bbc: 8d 45 e0 lea -0x20(%ebp),%eax
8048bbf: 89 44 24 04 mov %eax,0x4(%esp)
8048bc3: 8b 45 08 mov 0x8(%ebp),%eax
8048bc6: 89 04 24 mov %eax,(%esp)
8048bc9: e8 d2 08 00 00 call 80494a0 <read_six_numbers>
8048bce: 83 7d e0 01 cmpl $0x1,-0x20(%ebp)
8048bd2: 74 05 je 8048bd9 <phase_2+0x25>
8048bd4: e8 5f 07 00 00 call 8049338 <explode_bomb>
8048bd9: 8d 5d e4 lea -0x1c(%ebp),%ebx
8048bdc: 8d 75 f8 lea -0x8(%ebp),%esi
8048bdf: 8b 43 fc mov -0x4(%ebx),%eax
8048be2: 01 c0 add %eax,%eax
8048be4: 39 03 cmp %eax,(%ebx)
8048be6: 74 05 je 8048bed <phase_2+0x39>
8048be8: e8 4b 07 00 00 call 8049338 <explode_bomb>
8048bed: 83 c3 04 add $0x4,%ebx
8048bf0: 39 f3 cmp %esi,%ebx
8048bf2: 75 eb jne 8048bdf <phase_2+0x2b>
8048bf4: 83 c4 30 add $0x30,%esp
8048bf7: 5b pop %ebx
8048bf8: 5e pop %esi
8048bf9: 5d pop %ebp
8048bfa: c3 ret
|
首先可以看到这道题调用一个函数。函数名叫输入6个数。很明显,这六个书会以次放在ebp-0x20到ebp-0x8的空间内。然后再从以下的代码逻辑可以看出这6个数得满足一种关系:等比为2的等比数列。并且首元素为1.因此答案就明了了为:1 2 4 8 16 32.下面来看第三关。
上代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
08048bfb <phase_3>:
8048bfb: 55 push %ebp
8048bfc: 89 e5 mov %esp,%ebp
8048bfe: 83 ec 28 sub $0x28,%esp
8048c01: 8d 45 f0 lea -0x10(%ebp),%eax
8048c04: 89 44 24 0c mov %eax,0xc(%esp)
8048c08: 8d 45 f4 lea -0xc(%ebp),%eax
8048c0b: 89 44 24 08 mov %eax,0x8(%esp)
8048c0f: c7 44 24 04 25 a6 04 movl $0x804a625,0x4(%esp)
8048c16: 08
8048c17: 8b 45 08 mov 0x8(%ebp),%eax
8048c1a: 89 04 24 mov %eax,(%esp)
8048c1d: e8 be fb ff ff call 80487e0 <__isoc99_sscanf@plt>
8048c22: 83 f8 01 cmp $0x1,%eax
8048c25: 7f 05 jg 8048c2c <phase_3+0x31>
8048c27: e8 0c 07 00 00 call 8049338 <explode_bomb>
8048c2c: 83 7d f4 07 cmpl $0x7,-0xc(%ebp)
8048c30: 77 65 ja 8048c97 <phase_3+0x9c>
8048c32: 8b 45 f4 mov -0xc(%ebp),%eax
8048c35: ff 24 85 40 a3 04 08 jmp *0x804a340(,%eax,4)
8048c3c: b8 00 00 00 00 mov $0x0,%eax
8048c41: eb 05 jmp 8048c48 <phase_3+0x4d>
8048c43: b8 56 00 00 00 mov $0x56,%eax
8048c48: 2d 0f 02 00 00 sub $0x20f,%eax
8048c4d: eb 05 jmp 8048c54 <phase_3+0x59>
8048c4f: b8 00 00 00 00 mov $0x0,%eax
8048c54: 05 a9 03 00 00 add $0x3a9,%eax
8048c59: eb 05 jmp 8048c60 <phase_3+0x65>
8048c5b: b8 00 00 00 00 mov $0x0,%eax
8048c60: 2d e5 00 00 00 sub $0xe5,%eax
8048c65: eb 05 jmp 8048c6c <phase_3+0x71>
8048c67: b8 00 00 00 00 mov $0x0,%eax
8048c6c: 05 e5 00 00 00 add $0xe5,%eax
8048c71: eb 05 jmp 8048c78 <phase_3+0x7d>
8048c73: b8 00 00 00 00 mov $0x0,%eax
8048c78: 2d e5 00 00 00 sub $0xe5,%eax
8048c7d: eb 05 jmp 8048c84 <phase_3+0x89>
8048c7f: b8 00 00 00 00 mov $0x0,%eax
8048c84: 05 e5 00 00 00 add $0xe5,%eax
8048c89: eb 05 jmp 8048c90 <phase_3+0x95>
8048c8b: b8 00 00 00 00 mov $0x0,%eax
8048c90: 2d e5 00 00 00 sub $0xe5,%eax
8048c95: eb 0a jmp 8048ca1 <phase_3+0xa6>
8048c97: e8 9c 06 00 00 call 8049338 <explode_bomb>
8048c9c: b8 00 00 00 00 mov $0x0,%eax
8048ca1: 83 7d f4 05 cmpl $0x5,-0xc(%ebp)
8048ca5: 7f 05 jg 8048cac <phase_3+0xb1>
8048ca7: 3b 45 f0 cmp -0x10(%ebp),%eax
8048caa: 74 05 je 8048cb1 <phase_3+0xb6>
8048cac: e8 87 06 00 00 call 8049338 <explode_bomb>
8048cb1: c9 leave
8048cb2: c3 ret
|
有没有被这段代码吓到?确实有点长。依然可以看到这个题会调用一个函数,这个函数是sscanf,如果对这个函数有所了解的话,可以从它下面的一个比较看出这个函数要求输入两个变量,那么这两个变量是什么类型的呢?可以从movl $0x804a625,0x4(%esp)这句里找到信息。很明显这是个非常突兀的地址,那么这个地址是干嘛的,首先可以肯定它是sscanf函数的参数,联想到sscanf的参数,可以这段地址的内容便是变量的类型。通过x/s 0x804a625便可以得到"%d %d"这个字符串。同样从开始代码的调动,仍然可以清晰的确定这是有接受两个参数。从后面的代码逻辑可以看出,要根据第一个参数的具体值跳到不同的地址,来执行命令。而且第一个参数要小于7.可以分别假设第一个参数等于0到6,然后可以看出根据这个分支选择可以确定eax的大小,然后第二个参数等eax的值即可。而且又可以从后面的代码看出第一个参数要小于5所以只有0到4可以选择,然后假设第一个参数为4那么eax在运算后为0,因此第二个参数为0便是一个解。(可以看出这题没有唯一解)
上代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
08048d19 <phase_4>:
8048d19: 55 push %ebp
8048d1a: 89 e5 mov %esp,%ebp
8048d1c: 83 ec 28 sub $0x28,%esp
8048d1f: 8d 45 f0 lea -0x10(%ebp),%eax
8048d22: 89 44 24 0c mov %eax,0xc(%esp)
8048d26: 8d 45 f4 lea -0xc(%ebp),%eax
8048d29: 89 44 24 08 mov %eax,0x8(%esp)
8048d2d: c7 44 24 04 25 a6 04 movl $0x804a625,0x4(%esp)
8048d34: 08
8048d35: 8b 45 08 mov 0x8(%ebp),%eax
8048d38: 89 04 24 mov %eax,(%esp)
8048d3b: e8 a0 fa ff ff call 80487e0 <__isoc99_sscanf@plt>
8048d40: 83 f8 02 cmp $0x2,%eax
8048d43: 75 0c jne 8048d51 <phase_4+0x38>
8048d45: 8b 45 f4 mov -0xc(%ebp),%eax
8048d48: 85 c0 test %eax,%eax
8048d4a: 78 05 js 8048d51 <phase_4+0x38>
8048d4c: 83 f8 0e cmp $0xe,%eax
8048d4f: 7e 05 jle 8048d56 <phase_4+0x3d>
8048d51: e8 e2 05 00 00 call 8049338 <explode_bomb>
8048d56: c7 44 24 08 0e 00 00 movl $0xe,0x8(%esp)
8048d5d: 00
8048d5e: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp)
8048d65: 00
8048d66: 8b 45 f4 mov -0xc(%ebp),%eax
8048d69: 89 04 24 mov %eax,(%esp)
8048d6c: e8 42 ff ff ff call 8048cb3 <func4>
8048d71: 83 f8 25 cmp $0x25,%eax
8048d74: 75 06 jne 8048d7c <phase_4+0x63>
8048d76: 83 7d f0 25 cmpl $0x25,-0x10(%ebp)
8048d7a: 74 05 je 8048d81 <phase_4+0x68>
8048d7c: e8 b7 05 00 00 call 8049338 <explode_bomb>
8048d81: c9 leave
8048d82: c3 ret
|
看到代码里的
1
2
|
call 80487e0 __isoc99_sscanf@plt
cmp $0x2,%eax
|
就明白了要输入两个数,然后拿出输入的第一个数(mov -0xc(%ebp),%eax)首先看看其是不是负数,如果是负数,bomb!!然后将其与14进行比较,如果它比14大,则bomb!!然后将第一个参数,0,和14分别作为第1,2,3个参数传递给func4,然后调用func4.这个函数的返回值是37.因此我们要做的就是,使得输入的值传递给func4,然后让其返回37即可。下面来看看func4函数。依然上代码:
08048cb3 <func4>:
8048cb3: 55 push %ebp
8048cb4: 89 e5 mov %esp,%ebp
8048cb6: 83 ec 18 sub $0x18,%esp
8048cb9: 89 5d f8 mov %ebx,-0x8(%ebp)
8048cbc: 89 75 fc mov %esi,-0x4(%ebp)
8048cbf: 8b 45 08 mov 0x8(%ebp),%eax
8048cc2: 8b 55 0c mov 0xc(%ebp),%edx
8048cc5: 8b 75 10 mov 0x10(%ebp),%esi
8048cc8: 89 f1 mov %esi,%ecx
8048cca: 29 d1 sub %edx,%ecx
8048ccc: 89 cb mov %ecx,%ebx
8048cce: c1 eb 1f shr $0x1f,%ebx
8048cd1: 8d 0c 0b lea (%ebx,%ecx,1),%ecx
8048cd4: d1 f9 sar %ecx
8048cd6: 8d 1c 11 lea (%ecx,%edx,1),%ebx
8048cd9: 39 c3 cmp %eax,%ebx
8048cdb: 7e 17 jle 8048cf4 <func4+0x41>
8048cdd: 8d 4b ff lea -0x1(%ebx),%ecx
8048ce0: 89 4c 24 08 mov %ecx,0x8(%esp)
8048ce4: 89 54 24 04 mov %edx,0x4(%esp)
8048ce8: 89 04 24 mov %eax,(%esp)
8048ceb: e8 c3 ff ff ff call 8048cb3 <func4>
8048cf0: 01 c3 add %eax,%ebx
8048cf2: eb 19 jmp 8048d0d <func4+0x5a>
8048cf4: 39 c3 cmp %eax,%ebx
8048cf6: 7d 15 jge 8048d0d <func4+0x5a>
8048cf8: 89 74 24 08 mov %esi,0x8(%esp)
8048cfc: 8d 53 01 lea 0x1(%ebx),%edx
8048cff: 89 54 24 04 mov %edx,0x4(%esp)
8048d03: 89 04 24 mov %eax,(%esp)
8048d06: e8 a8 ff ff ff call 8048cb3 <func4>
8048d0b: 01 c3 add %eax,%ebx
8048d0d: 89 d8 mov %ebx,%eax
8048d0f: 8b 5d f8 mov -0x8(%ebp),%ebx
8048d12: 8b 75 fc mov -0x4(%ebp),%esi
8048d15: 89 ec mov %ebp,%esp
8048d17: 5d pop %ebp
8048d18: c3 ret
|
一看到这个函数里面有func4,心理就紧张了,这是要递归啊。对于这部分,有的同学是用c写了一段代码,模拟这个逻辑,把题给结出来的,我是拿笔算加推理得出的。对于此,不管你认为怎么样,我反正认为是没有必要,这个递归逻辑不复杂。
下面就来剖析func4,这个函数在确定栈之后,首先取出来传递给它的参数,依次放在eax,edx,esi.中,从一个jle和一个jge可以看出,这个递归函数跳出的条件根据func4的第二个参数和第二个参数进过种种运算的结果等于第一个参数即可。注意在递归过程中第一个参数是不变的,最后返回值是经过运算后的ebx加上第一个参数。其中根据传进参数的大小,可以大致算出如果第一个参数如果等于7,则结果明显小于37,所以选比7大的数。所以第一递归会走jge这条路,因此新的参数为输入的第一个数,8,14.然后重新调用函数。可以自己画出每次递归传进的参数图。稍微对比下可以发现传入的数为10即可满足题意.
然后继续看phase_4函数,最后有这句话 cmpl $0x25,-0x10(%ebp)可以看出只要第二个参数为37即可满足。第二个参数的要求就这么简单。是不是很无语?
因此这道题的答案为4 37.
上代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
08048d83 <phase_5>:
8048d83: 55 push %ebp
8048d84: 89 e5 mov %esp,%ebp
8048d86: 83 ec 28 sub $0x28,%esp
8048d89: 8d 45 f0 lea -0x10(%ebp),%eax
8048d8c: 89 44 24 0c mov %eax,0xc(%esp)
8048d90: 8d 45 f4 lea -0xc(%ebp),%eax
8048d93: 89 44 24 08 mov %eax,0x8(%esp)
8048d97: c7 44 24 04 25 a6 04 movl $0x804a625,0x4(%esp)
8048d9e: 08
8048d9f: 8b 45 08 mov 0x8(%ebp),%eax
8048da2: 89 04 24 mov %eax,(%esp)
8048da5: e8 36 fa ff ff call 80487e0 <__isoc99_sscanf@plt>
8048daa: 83 f8 01 cmp $0x1,%eax
8048dad: 7f 05 jg 8048db4 <phase_5+0x31>
8048daf: e8 84 05 00 00 call 8049338 <explode_bomb>
8048db4: 8b 45 f4 mov -0xc(%ebp),%eax
8048db7: 83 e0 0f and $0xf,%eax
8048dba: 89 45 f4 mov %eax,-0xc(%ebp)
8048dbd: 83 f8 0f cmp $0xf,%eax
8048dc0: 74 28 je 8048dea <phase_5+0x67>
8048dc2: b9 00 00 00 00 mov $0x0,%ecx
8048dc7: ba 00 00 00 00 mov $0x0,%edx
8048dcc: 83 c2 01 add $0x1,%edx
8048dcf: 8b 04 85 60 a3 04 08 mov 0x804a360(,%eax,4),%eax
8048dd6: 01 c1 add %eax,%ecx
8048dd8: 83 f8 0f cmp $0xf,%eax
8048ddb: 75 ef jne 8048dcc <phase_5+0x49>
8048ddd: 89 45 f4 mov %eax,-0xc(%ebp)
8048de0: 83 fa 0f cmp $0xf,%edx
8048de3: 75 05 jne 8048dea <phase_5+0x67>
8048de5: 3b 4d f0 cmp -0x10(%ebp),%ecx
8048de8: 74 05 je 8048def <phase_5+0x6c>
8048dea: e8 49 05 00 00 call 8049338 <explode_bomb>
8048def: c9 leave
8048df0: c3 ret
|
首先,看到这题不是递归,代码又这么短,心理就有种蔑视这道题的感觉。首先可以看到需要输入两个数,否则bomb。然后就对第一个数进行操作了,首先取其后四位,如果其后四位全为1则爆炸。然后看到mov $0x0,%ecx mov $0x0,%edx就可以知道下面是个循环,edx为循环变量。这个循环里会根据eax,以其为索引找以地址0x804a360开头的数组中的值。然后求和。最后和等于输入的第二个参数即可。可以通过p/x *(int *)(0x804a360)@16得到这个数组,然后拼凑即可。我选则的值为4 115.
上代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|