bomblab

bomblab

炸弹人,炸弹魂,拆弹人是人下人。CSAPP 终于要我们做Lab02了。首先我们来介绍一下GDB

GDB

GDB指令很多,我这里只列举一下在bomblab调试中常用的指令

gdb 文件名 调试某个文件

break+函数名/地址 在某一个函数上设置断点

run 运行程序

i r 寄存器名 查看寄存器当中的内容

ni 单步执行(不会进入调用函数内部)

si 单步进入(会进入调用函数内部)

display /9i $pc 在每次运行一步之后会显示之后要执行的9行汇编代码,非常好用。

set var *地址 = 修改某个地址上的值

x/<n/f/u> <addr> 查看某个地址中的值

n:是正整数,表示需要显示的内存单元的个数,即从当前地址向后显示n个内存单元的内容,一个内存单元的大小由第三个参数u定义。

f:表示addr指向的内存内容的输出格式,s对应输出字符串

此处需特别注意输出整型数据的格式:

x 按十六进制格式显示变量。

d 按十进制格式显示变量。

u 按十六进制格式显示无符号整型。

o 按八进制格式显示变量。

t 按二进制格式显示变量。

a 按十六进制格式显示变量。

c 按字符格式显示变量。

f 按浮点数格式显示变量。

我们常用

x/s 地址 来看一个地址上存储的字符串

x/wd 地址 来看一个地址上存储的数字

x/x 地址 来看一个地址上存储的十六进制的地址 。同理,x/3x 0x54320 比如说以地址0x54320为起始地址,返回3个单元的值,输出格式为十六进制。

phase_1

1
2
3
4
5
6
7
8
9
10
11
Phase_1:   
0x08048b33 <+0>: sub $0x14,%esp
0x08048b36 <+3>: push $0x804a024
0x08048b3b <+8>: pushl 0x1c(%esp)
0x08048b3f <+12>: call 0x8049019 <strings_not_equal>
0x08048b44 <+17>: add $0x10,%esp
0x08048b47 <+20>: test %eax,%eax
0x08048b49 <+22>: je 0x8048b50 <phase_1+29>
0x08048b4b <+24>: call 0x8049110 <explode_bomb>
0x08048b50 <+29>: add $0xc,%esp
0x08048b53 <+32>: ret

我们首先用 i r esp 指令来看看当前的栈顶指针为多少:

esp 0xffffd23c 0xffffd23c

运行第一句之后,esp减去了 0x14

esp 0xffffd228 0xffffd228

运行,将地址 0x804a024 上的值压栈,用 x/s 0x804a024 来查看地址上的内容并用字符串的形式输出

0x804a024: "All your base are belong to us."

运行,phase_1函数调用了 strings_not_equal 函数,我们来看看在这个函数的内部逻辑:

strings_not_equal: 第五行、第六行是将 两个栈上的地址分别存入到 %ebx,%esi 中,

我们可以先用 i r esp 看看栈顶指针

esp 0xffffd210 0xffffd210

可以看看栈顶指针分别加上 0x10, 0x14 p/x *0xffffd220 p/x *0xffffd224

发现值:0x804a024 和 0x804c3e0

在存入寄存器后,我们发现寄存器中存入的值就是上面的两个地址,来看看这两个寄存器当中存入了什么值:

esi 0x804a024 134520868

ebx 0x804c3e0 134530016

要看看这个地址上的数据, 可以用 x/s 地址

1
2
3
4
(gdb) x/s 0x804a024
0x804a024: "All your base are belong to us."
(gdb) x/s 0x804c3e0
0x804c3e0 <input_strings>: "xuqihang"

我们看到,ebx 中存放的是我们输入的字符串, esi 中存放我们要对比的。

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
strings_not_equal:
0x08049019 <+0>: push %edi
0x0804901a <+1>: push %esi
0x0804901b <+2>: push %ebx
0x0804901c <+3>: mov 0x10(%esp),%ebx
0x08049020 <+7>: mov 0x14(%esp),%esi
0x08049024 <+11>: push %ebx
0x08049025 <+12>: call 0x8048ffa <string_length>
0x0804902a <+17>: mov %eax,%edi
0x0804902c <+19>: mov %esi,(%esp)
0x0804902f <+22>: call 0x8048ffa <string_length>
0x08049034 <+27>: add $0x4,%esp
0x08049037 <+30>: mov $0x1,%edx
0x0804903c <+35>: cmp %eax,%edi
0x0804903e <+37>: jne 0x8049078 <strings_not_equal+95>
0x08049040 <+39>: movzbl (%ebx),%eax
0x08049043 <+42>: test %al,%al
0x08049045 <+44>: je 0x8049065 <strings_not_equal+76>
0x08049047 <+46>: cmp (%esi),%al
0x08049049 <+48>: je 0x8049051 <strings_not_equal+56>
0x0804904b <+50>: jmp 0x804906c <strings_not_equal+83>
0x0804904d <+52>: cmp (%esi),%al
0x0804904f <+54>: jne 0x8049073 <strings_not_equal+90>
0x08049051 <+56>: add $0x1,%ebx
0x08049054 <+59>: add $0x1,%esi
0x08049057 <+62>: movzbl (%ebx),%eax
0x0804905a <+65>: test %al,%al
0x0804905c <+67>: jne 0x804904d <strings_not_equal+52>
0x0804905e <+69>: mov $0x0,%edx
0x08049063 <+74>: jmp 0x8049078 <strings_not_equal+95>
0x08049065 <+76>: mov $0x0,%edx
0x0804906a <+81>: jmp 0x8049078 <strings_not_equal+95>
0x0804906c <+83>: mov $0x1,%edx
0x08049071 <+88>: jmp 0x8049078 <strings_not_equal+95>
0x08049073 <+90>: mov $0x1,%edx
0x08049078 <+95>: mov %edx,%eax
0x0804907a <+97>: pop %ebx
0x0804907b <+98>: pop %esi
0x0804907c <+99>: pop %edi
0x0804907d <+100>: ret

如果相等,那么就返回1,否则返回0。 然后函数Phase_1会对返回值进行一个test,也就是按位与操作,如果返回值是1,那么就不会爆炸;如果返回值是0,那么就会爆炸。

于是我们知道第一关的密码是 All your base are belong to us.

Phase_2

1
2
3
4
5
6
7
8
9
10
11
Phase_2   
0x08048b54 <+0>: push %esi
0x08048b55 <+1>: push %ebx
0x08048b56 <+2>: sub $0x2c,%esp
0x08048b59 <+5>: mov %gs:0x14,%eax
0x08048b5f <+11>: mov %eax,0x24(%esp)
0x08048b63 <+15>: xor %eax,%eax # eax置0
0x08048b65 <+17>: lea 0xc(%esp),%eax
0x08048b69 <+21>: push %eax
0x08048b6a <+22>: pushl 0x3c(%esp)
0x08048b6e <+26>: call 0x8049135 <read_six_numbers> # 读入6个数字

我们先看看栈顶指针:esp 0xffffd23c 0xffffd23c

然后push了两个寄存器,现在 esp 变成了 0xffffd234

随后栈顶指针减去了 0x2c也就是减了44个字节,这时候esp 0xffffd208 0xffffd208

push %eax和0x3c(%esp) 之后,栈顶指针现在是 0xffffd200

然后phase_2调用了 read_six_numbers,我们进入到函数一窥究竟

栈顶指针又减去了 0xc,栈顶指针现在是0xffffd1f0

随后做的一系列操作就是将我们输入的六个数字的地址(这时候还未读入)压入栈中

在scanf函数之前,esp 已经来到了0xffffd1cc

调用scanf之后,esp现在的值是0xffffd1d0

加上0x20之后,现在 esp是 0xffffd1f0

然后会用 %eax和5作比较,%eax记录了scanf函数的返回值也就是输入数字的个数,这里我已经输入了6、5、4、3、2、1 一共6个数所以现在eax中的值是6.

最后%esp加上了0xc后返回,现在esp的值为 0xffffd1fc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
0x08049135 <+0>:     sub    $0xc,%esp
0x08049138 <+3>: mov 0x14(%esp),%eax
0x0804913c <+7>: lea 0x14(%eax),%edx
0x0804913f <+10>: push %edx
0x08049140 <+11>: lea 0x10(%eax),%edx
0x08049143 <+14>: push %edx
0x08049144 <+15>: lea 0xc(%eax),%edx
0x08049147 <+18>: push %edx
0x08049148 <+19>: lea 0x8(%eax),%edx
0x0804914b <+22>: push %edx
0x0804914c <+23>: lea 0x4(%eax),%edx
0x0804914f <+26>: push %edx
0x08049150 <+27>: push %eax
0x08049151 <+28>: push $0x804a1e3
0x08049156 <+33>: pushl 0x2c(%esp)
0x0804915a <+37>: call 0x8048810 <__isoc99_sscanf@plt>
0x0804915f <+42>: add $0x20,%esp
0x08049162 <+45>: cmp $0x5,%eax
0x08049165 <+48>: jg 0x804916c <read_six_numbers+55>
0x08049167 <+50>: call 0x8049110 <explode_bomb>
0x0804916c <+55>: add $0xc,%esp
0x0804916f <+58>: ret

回到phase_2,%esp又加上了0x10,现在 esp中的值为0xffffd210

接着让 %esp+4 地址上存放的东西和0去比,必须和0相等,否则就爆炸。那么我们来看看 %esp+4 地址上存放的是什么值:

p/x *0xffffd214 : $8 = 0x6 发现是6,所以我们推断这个地址上存放的值是我们输入的第一个数

以此类推,我们看一看 0xffffd218 , 0xffffd21c,0xffffd220,0xffffd224,0xffffd228中得知,发现分别是:5,4,3,2,1 猜想成立。

所以我们直到第一个值必须要等于0,否则就爆炸。同理,第二个值必须等于1,否则就爆炸。

现在来到了 +53 这行指令。我们把第一个参数的地址移到 %ebx中,将第5个数字移动到%esi中

+61将 0x4(%ebx) 也就是第二个参数的值移动到了%eax当中

+64 并让其和 (%ebx) 也就是第一个参数的值 相加,这就让 %eax = 第二个参数的值+第一个参数的值

+66让 0x8(%ebx) 也就是第三个参数和 %eax相比,不相等就爆炸。于是我们推测第三个参数应该等于前两个参数之和。

+76 将 %ebx加上4,也就是现在 %ebx指向了第二个参数的地址

+81 跳转回 +61 重复上述操作。

至此我们可以推断出这是一个6位数的斐波那契数列,输入的值应该为: 0 1 1 2 3 5

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
Phase_2_continue:
0x08048b73 <+31>: add $0x10,%esp
0x08048b76 <+34>: cmpl $0x0,0x4(%esp)
0x08048b7b <+39>: jne 0x8048b84 <phase_2+48>
0x08048b7d <+41>: cmpl $0x1,0x8(%esp)
0x08048b82 <+46>: je 0x8048b89 <phase_2+53>
0x08048b84 <+48>: call 0x8049110 <explode_bomb>
0x08048b89 <+53>: lea 0x4(%esp),%ebx
0x08048b8d <+57>: lea 0x14(%esp),%esi
0x08048b91 <+61>: mov 0x4(%ebx),%eax
0x08048b94 <+64>: add (%ebx),%eax
0x08048b96 <+66>: cmp %eax,0x8(%ebx)
0x08048b99 <+69>: je 0x8048ba0 <phase_2+76>
0x08048b9b <+71>: call 0x8049110 <explode_bomb>
0x08048ba0 <+76>: add $0x4,%ebx
0x08048ba3 <+79>: cmp %esi,%ebx
0x08048ba5 <+81>: jne 0x8048b91 <phase_2+61>
0x08048ba7 <+83>: mov 0x1c(%esp),%eax
0x08048bab <+87>: xor %gs:0x14,%eax
0x08048bb2 <+94>: je 0x8048bb9 <phase_2+101>
0x08048bb4 <+96>: call 0x8048790 <__stack_chk_fail@plt>
0x08048bb9 <+101>: add $0x24,%esp
0x08048bbc <+104>: pop %ebx
0x08048bbd <+105>: pop %esi
0x08048bbe <+106>: ret

Phase_3

首先我们设个断点 b phase_3 ,并随便输入2个数,我输入的是 1 43

直接看看+25 处藏着什么东西。0x804a1ef: "%d %d" ,是两个 整数,我们猜测第三关要输入两个整数。

+42 中的eax存放的是 scanf的返回值,让其与1相比,一定要大于才能继续,所以更应证了我们的猜想,即输入两个整数。

+52 开始,函数对输入的数字进行了一系列操作。因为首先是用 0x4(%esp) 和 7相比,我们就看看这个地址上存入的是什么

esp 0xffffd1c0 0xffffd1c0

0xffffd1c4: 1 0xffffd1c8: 43

我们发现我们输入的两个数存放到了栈上。

+57 说,如果above 0x7,那么就直接爆炸,那么我们知道了我们输入的第一个数是无符号整数,且要小于7

<+171>: cmpl $0x5,0x4(%esp) 说,如果第一个参数大于5,也要爆炸,所以我们直到第一个整数是无符号的,且小于等于五。

因此可以取 0,1,2,3,4,5

<+59> 是将第一个参数移到 eax中去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Phase_3
0x08048bbf <+0>: sub $0x1c,%esp
0x08048bc2 <+3>: mov %gs:0x14,%eax
0x08048bc8 <+9>: mov %eax,0xc(%esp)
0x08048bcc <+13>: xor %eax,%eax
0x08048bce <+15>: lea 0x8(%esp),%eax
0x08048bd2 <+19>: push %eax
0x08048bd3 <+20>: lea 0x8(%esp),%eax
0x08048bd7 <+24>: push %eax
0x08048bd8 <+25>: push $0x804a1ef
0x08048bdd <+30>: pushl 0x2c(%esp)
0x08048be1 <+34>: call 0x8048810 <__isoc99_sscanf@plt>
0x08048be6 <+39>: add $0x10,%esp
0x08048be9 <+42>: cmp $0x1,%eax
0x08048bec <+45>: jg 0x8048bf3 <phase_3+52>
0x08048bee <+47>: call 0x8049110 <explode_bomb>
0x08048bf3 <+52>: cmpl $0x7,0x4(%esp)
0x08048bf8 <+57>: ja 0x8048c60 <phase_3+161>
0x08048bfa <+59>: mov 0x4(%esp),%eax

+63 开始,是一个switch 语句,我们可以用 x/x 0x804a080 来查看这个地址上存放的东西。

0x804a080: 0x08048c05 发现正好是 +70

0x804a084: 0x08048c0c发现是+77

0x804a088: 0x08048c18 发现是+89

0x804a08c: 0x08048c24 发现是+101

0x804a090: 0x08048c30 发现是+113

0x804a094: 0x08048c3c 发现是+125

0x804a098: 0x08048c48 发现是 +137

0x804a09c: 0x08048c54 发现是+154

所以我们可以进行一个计算:

如果第一个参数是 0 $\rightarrow$ 跳转到 +70

如果第一个参数是 1 $\rightarrow$ 跳转到 +77

如果第一个参数是2 $\rightarrow$ 跳转到+89

如果第一个参数是3 $\rightarrow$ 跳转到+101

如果第一个参数是4$\rightarrow$ 跳转到+113

如果第一个参数是5$\rightarrow$ 跳转到+125

然后再线性运算。

因此我们只要看+178 的时候,eax中最后的数字就可以了。因为这句话的意思是让 eax和我们输入的第二个参数相比,如果不相等就爆炸;相等就成功过关

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

0x08048bfe <+63>: jmp *0x804a080(,%eax,4)
0x08048c05 <+70>: mov $0x193,%eax
0x08048c0a <+75>: jmp 0x8048c11 <phase_3+82>
0x08048c0c <+77>: mov $0x0,%eax
0x08048c11 <+82>: sub $0x191,%eax
0x08048c16 <+87>: jmp 0x8048c1d <phase_3+94>
0x08048c18 <+89>: mov $0x0,%eax
0x08048c1d <+94>: add $0x3c8,%eax
0x08048c22 <+99>: jmp 0x8048c29 <phase_3+106>
0x08048c24 <+101>: mov $0x0,%eax
0x08048c29 <+106>: sub $0x129,%eax
0x08048c2e <+111>: jmp 0x8048c35 <phase_3+118>
0x08048c30 <+113>: mov $0x0,%eax
0x08048c35 <+118>: add $0x129,%eax
0x08048c3a <+123>: jmp 0x8048c41 <phase_3+130>
0x08048c3c <+125>: mov $0x0,%eax
0x08048c41 <+130>: sub $0x129,%eax
0x08048c46 <+135>: jmp 0x8048c4d <phase_3+142>
0x08048c48 <+137>: mov $0x0,%eax
0x08048c4d <+142>: add $0x129,%eax
0x08048c52 <+147>: jmp 0x8048c59 <phase_3+154>
0x08048c54 <+149>: mov $0x0,%eax
0x08048c59 <+154>: sub $0x129,%eax
0x08048c5e <+159>: jmp 0x8048c6a <phase_3+171>
0x08048c60 <+161>: call 0x8049110 <explode_bomb>
0x08048c65 <+166>: mov $0x0,%eax
0x08048c6a <+171>: cmpl $0x5,0x4(%esp)
0x08048c6f <+176>: jg 0x8048c77 <phase_3+184>
0x08048c71 <+178>: cmp 0x8(%esp),%eax
0x08048c75 <+182>: je 0x8048c7c <phase_3+189>
0x08048c77 <+184>: call 0x8049110 <explode_bomb>
0x08048c7c <+189>: mov 0xc(%esp),%eax
0x08048c80 <+193>: xor %gs:0x14,%eax
0x08048c87 <+200>: je 0x8048c8e <phase_3+207>
0x08048c89 <+202>: call 0x8048790 <__stack_chk_fail@plt>
0x08048c8e <+207>: add $0x1c,%esp
0x08048c91 <+210>: ret

最后的结果是: 0 673 或者 1 270 或者 2 671 或者 3 -297 或者 4 0 或者 5 -297

Phase_4

phase_4 是我认为最简单的题目了,因为好像根本用不到fun4,但是这是解题博客,所以我们还是来认真看一下

我输入的值是 108 ,2

首先看一下+25, 0x804a1ef: “%d %d” 发现又是输入两个数

然后我们看 +39 ,结束以后用 i r esp 查看当前栈指针:0xffffd220

+42中,scanf的返回值也要大于等于2,这刚好印证了+25的猜想

我们用查看一下 0x4(%esp) 和 0x8(%esp),发现刚好是 2 和 108。

+47+59 对第我们输入的第二个数字进行一个范围限定

首先将其减去2,然后让其于2相比,一定要比2小且是无符号整数才不会爆炸。因此我们可以得出第二个参数的值必然为2或者3

+73 调用了fun4,然后+81 让fun4的返回值和我们输入的第一个参数相比。不相等就爆炸否则就过关。所以我看都没看就直接ni,然后看 eax中的值,发现2对应的是108而3对应的是162

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
phase_4
0x08048cd5 <+0>: sub $0x1c,%esp
0x08048cd8 <+3>: mov %gs:0x14,%eax
0x08048cde <+9>: mov %eax,0xc(%esp)
0x08048ce2 <+13>: xor %eax,%eax
0x08048ce4 <+15>: lea 0x4(%esp),%eax
0x08048ce8 <+19>: push %eax
0x08048ce9 <+20>: lea 0xc(%esp),%eax
0x08048ced <+24>: push %eax
0x08048cee <+25>: push $0x804a1ef
0x08048cf3 <+30>: pushl 0x2c(%esp)
0x08048cf7 <+34>: call 0x8048810 <__isoc99_sscanf@plt>
0x08048cfc <+39>: add $0x10,%esp
0x08048cff <+42>: cmp $0x2,%eax
0x08048d02 <+45>: jne 0x8048d10 <phase_4+59>
0x08048d04 <+47>: mov 0x4(%esp),%eax
0x08048d08 <+51>: sub $0x2,%eax
0x08048d0b <+54>: cmp $0x2,%eax
0x08048d0e <+57>: jbe 0x8048d15 <phase_4+64>
0x08048d10 <+59>: call 0x8049110 <explode_bomb>
0x08048d15 <+64>: sub $0x8,%esp
0x08048d18 <+67>: pushl 0xc(%esp)
0x08048d1c <+71>: push $0x8
0x08048d1e <+73>: call 0x8048c92 <func4>
0x08048d23 <+78>: add $0x10,%esp
0x08048d26 <+81>: cmp 0x8(%esp),%eax
0x08048d2a <+85>: je 0x8048d31 <phase_4+92>
0x08048d2c <+87>: call 0x8049110 <explode_bomb>
0x08048d31 <+92>: mov 0xc(%esp),%eax
0x08048d35 <+96>: xor %gs:0x14,%eax
0x08048d3c <+103>: je 0x8048d43 <phase_4+110>
0x08048d3e <+105>: call 0x8048790 <__stack_chk_fail@plt>
0x08048d43 <+110>: add $0x1c,%esp
0x08048d46 <+113>: ret

但是作为数据学院的学子,我们必须对自己高标准严要求(不是)。现在我们来探索一哈fun4中发生了什么不可告人的秘密

func4是一个递归函数

<+71>出我们push了一个0x8,者在后面是要用到的

+3+7 分别把 8 和我们输入的第二个数移动进了 ebxedi

然后我们判断 ebx是不是小于等于0,jle是判断$(SF)$^$(OF)|ZF$的,所以ebx=0,ZF等于1,跳转;如果ebx小于0,SF=1,OF=0,ZF=0 ,$(SF)$^$(OF)|ZF$ =1 ,跳转,如果小于0直接返回0

+15,+17 是让 ebx中的数字和1比,如果相等就跳出func4,返回值是 eax中的值

+26 的作用是将ebx(初始值为8)减去1放到eax当中,并将edi压栈

+30开始是一个递归,在这个递归中,ebx更新为减去1后的ebx(第二轮中是7),edi仍然为edi。

这样进行了很多次调用之后,最后当ebx=1的时候,函数调转到63并将ebx之类的pop出来,这时候ebx=2,edi=3

image-20201117174810348

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
func4
0x08048c92 <+0>: push %edi
0x08048c93 <+1>: push %esi
0x08048c94 <+2>: push %ebx
0x08048c95 <+3>: mov 0x10(%esp),%ebx
0x08048c99 <+7>: mov 0x14(%esp),%edi
0x08048c9d <+11>: test %ebx,%ebx
0x08048c9f <+13>: jle 0x8048ccc <func4+58>
0x08048ca1 <+15>: mov %edi,%eax
0x08048ca3 <+17>: cmp $0x1,%ebx
0x08048ca6 <+20>: je 0x8048cd1 <func4+63>
0x08048ca8 <+22>: sub $0x8,%esp
0x08048cab <+25>: push %edi
0x08048cac <+26>: lea -0x1(%ebx),%eax
0x08048caf <+29>: push %eax
0x08048cb0 <+30>: call 0x8048c92 <func4>
0x08048cb5 <+35>: add $0x8,%esp
0x08048cb8 <+38>: lea (%edi,%eax,1),%esi
0x08048cbb <+41>: push %edi
0x08048cbc <+42>: sub $0x2,%ebx
0x08048cbf <+45>: push %ebx
0x08048cc0 <+46>: call 0x8048c92 <func4>
0x08048cc5 <+51>: add $0x10,%esp
0x08048cc8 <+54>: add %esi,%eax
0x08048cca <+56>: jmp 0x8048cd1 <func4+63>
0x08048ccc <+58>: mov $0x0,%eax
0x08048cd1 <+63>: pop %ebx
0x08048cd2 <+64>: pop %esi
0x08048cd3 <+65>: pop %edi
0x08048cd4 <+66>: ret

Phase_5

第一阶段是读入数字,还是老样子。0x804a1ef是要我们读入 两个整数,+42也让scanf的返回值大于1,于是我们推断phase_5 也让我们读入两个整数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Phase_5
0x08048d47 <+0>: sub $0x1c,%esp
0x08048d4a <+3>: mov %gs:0x14,%eax
0x08048d50 <+9>: mov %eax,0xc(%esp)
0x08048d54 <+13>: xor %eax,%eax
0x08048d56 <+15>: lea 0x8(%esp),%eax
0x08048d5a <+19>: push %eax
0x08048d5b <+20>: lea 0x8(%esp),%eax
0x08048d5f <+24>: push %eax
0x08048d60 <+25>: push $0x804a1ef
0x08048d65 <+30>: pushl 0x2c(%esp)
0x08048d69 <+34>: call 0x8048810 <__isoc99_sscanf@plt>
0x08048d6e <+39>: add $0x10,%esp
0x08048d71 <+42>: cmp $0x1,%eax
0x08048d74 <+45>: jg 0x8048d7b <phase_5+52>
0x08048d76 <+47>: call 0x8049110 <explode_bomb>

我在phase_5输入了5 115 两个数,现在我们来找找看它们藏在哪:

i r esp : 0xffffd220

我们用 x/d 0xffffd224x/d 0xffffd228 分别查看栈上的两个地址,发现:
0xffffd224: 5
0xffffd228: 115

刚好是我们输入的两个值。

+52+66 是对我们输入的第一个参数进行一系列的操作:

首先将其移入 eax,并让eax和 0xf 按位与。再把结果复制到第一个参数当中。最后让这个结果和0xf比较,如果相等就爆炸。我觉得这个设置就是让我们输入的第一个数的二进制表达的低四位一定不能是1111.

1
2
3
4
5
6
Phase_5_Continued
0x08048d7b <+52>: mov 0x4(%esp),%eax
0x08048d7f <+56>: and $0xf,%eax
0x08048d82 <+59>: mov %eax,0x4(%esp)
0x08048d86 <+63>: cmp $0xf,%eax
0x08048d89 <+66>: je 0x8048db9 <phase_5+114>

接下来是正式操作了:首先是让 ecx=0,edx=1

+81是让我们跳转到某一个地址,并将地址指向的值复制到eax。具体跳转到哪里,取什么值?
我们用 x/20dw 0x804a0a0一系列指令可以将这些数字都展现出来:

1
2
3
4
5
6
(gdb) x/20dw 0x804a0a0
0x804a0a0 <array.3247>: 10 2 14 7
0x804a0b0 <array.3247+16>: 8 12 15 11
0x804a0c0 <array.3247+32>: 0 4 1 13
0x804a0d0 <array.3247+48>: 3 9 6 5
0x804a0e0: 2032168787 1948284271 1802398056 1970239776

我们发现这个数组中一共有16个数字。

+88跳完以后我们将eax加到ecx中去。并eax其和15比较。如果不相等那么跳转到78再次循环,并让edx++;如果相等那么就继续。

+103 则是让edx和15比较,如果不相等就跳到115,相等就让ecx和我们输入的第2个参数相比。

所以这个函数可以写成这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int a,b;//输入的第一个和第二个参数
int array[16] = {10,2,14,7,8,12,15,11,0,4,1,13,3,9,6,5}
int cnt= 0;//edx
int sum = 0;//ecx
while(a!=15)
{
cnt++;
a=array[a];
sum+=a;
}
if(cnt==15)
{
if(b==sum)
{
return ;
}else{
bomb!!!
}
}else
{
bomb!!!!
}

我们的目标就是确定a选什么值,在经过15次循环后能让a=15,并让b等于循环过程中a的数值之和。

我们可以逆向推断。既然要输出15,那么在第十四次循环后a就要等于6,这样才能让a[6]=15

同理,要让a=6,那么就让第十三次循环后a=14,这样才能让 a[14]=6

以此类推,我们得到了一个链:

$15\rightarrow6\rightarrow14\rightarrow2\rightarrow1\rightarrow10\rightarrow0\rightarrow8\rightarrow4\rightarrow9\rightarrow13\rightarrow11\rightarrow7\rightarrow3\rightarrow12\rightarrow5$

因为Eax是先变化之后,才被ecx累加,所以eax的初值5并没有被加到ecx中

因此,ecx =12+3+7+11+13+9+4+8+0+10+1+2+14+6+15=115

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Phase_5_Continued
0x08048d8b <+68>: mov $0x0,%ecx
0x08048d90 <+73>: mov $0x0,%edx
0x08048d95 <+78>: add $0x1,%edx
0x08048d98 <+81>: mov 0x804a0a0(,%eax,4),%eax
0x08048d9f <+88>: add %eax,%ecx
0x08048da1 <+90>: cmp $0xf,%eax
0x08048da4 <+93>: jne 0x8048d95 <phase_5+78>
0x08048da6 <+95>: movl $0xf,0x4(%esp)
0x08048dae <+103>: cmp $0xf,%edx
0x08048db1 <+106>: jne 0x8048db9 <phase_5+114>
0x08048db3 <+108>: cmp 0x8(%esp),%ecx
0x08048db7 <+112>: je 0x8048dbe <phase_5+119>
0x08048db9 <+114>: call 0x8049110 <explode_bomb>
0x08048dbe <+119>: mov 0xc(%esp),%eax
0x08048dc2 <+123>: xor %gs:0x14,%eax
0x08048dc9 <+130>: je 0x8048dd0 <phase_5+137>
0x08048dcb <+132>: call 0x8048790 <__stack_chk_fail@plt>
0x08048dd0 <+137>: add $0x1c,%esp
0x08048dd3 <+140>: ret

Phase_5的答案就是5,115.问题解决。

Phase_6

Phase_6是整个炸弹关卡最难的一关,我们将其分为3个阶段

首先来看看第一个阶段: +26是读入6个数字

+39是将当前的栈顶指针 esp+4*esi+12 地址上存储的值存放到eax当中去

那么我们具体来看看当前的栈上存放着哪些数字吧~,我填入的数字是:5 4 1 2 3 6

我们看到这6个数字,是从下到上依次存储的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(gdb) i r esp
esp 0xffffd1f0 0xffffd1f0
(gdb) x/d 0xffffd1fc
0xffffd1fc: 5
(gdb) x/d 0xffffd200
0xffffd200: 4
(gdb) x/d 0xffffd204
0xffffd204: 1
(gdb) x/d 0xffffd208
0xffffd208: 2
(gdb) x/d 0xffffd20c
0xffffd20c: 3
(gdb) x/d 0xffffd210
0xffffd210: 6

+43到+46 是将eax中的内容减去1和5比较,如果大于5就直接爆炸,所以我们推断出所有的六个数都是无符号的且是小于等于6的。

esi是个计数器,初始值为0(+34) 每次比较成功之后会自增1,如果加到了6就跳出循环。如果没有到6的话就运行 +64

+66 开始,程序做的就是将我们输入的数和后面的所有数进行比较(第一个和2、3、4、5、6相比,第二个和3、4、5、6相比),如果相等就爆炸,因此我们输入的6个数字必须是两两不等的。

其原函数类似以下逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
for(int i =0;i<6;i++)
{
if(a[i]<=6)
{
for(int j =i;j<6;j++)
{
if(a[j]==a[j+1])
//explode
}
}else{
//explode
}
}
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
Phase_6:   
0x08048dd4 <+0>: push %esi
0x08048dd5 <+1>: push %ebx
0x08048dd6 <+2>: sub $0x4c,%esp
0x08048dd9 <+5>: mov %gs:0x14,%eax
0x08048ddf <+11>: mov %eax,0x44(%esp)
0x08048de3 <+15>: xor %eax,%eax
0x08048de5 <+17>: lea 0x14(%esp),%eax
0x08048de9 <+21>: push %eax
0x08048dea <+22>: pushl 0x5c(%esp)
0x08048dee <+26>: call 0x8049135 <read_six_numbers>
0x08048df3 <+31>: add $0x10,%esp
0x08048df6 <+34>: mov $0x0,%esi
0x08048dfb <+39>: mov 0xc(%esp,%esi,4),%eax
0x08048dff <+43>: sub $0x1,%eax
0x08048e02 <+46>: cmp $0x5,%eax
0x08048e05 <+49>: jbe 0x8048e0c <phase_6+56>
0x08048e07 <+51>: call 0x8049110 <explode_bomb>
0x08048e0c <+56>: add $0x1,%esi
0x08048e0f <+59>: cmp $0x6,%esi
0x08048e12 <+62>: je 0x8048e2f <phase_6+91>
0x08048e14 <+64>: mov %esi,%ebx
0x08048e16 <+66>: mov 0xc(%esp,%ebx,4),%eax
0x08048e1a <+70>: cmp %eax,0x8(%esp,%esi,4)
0x08048e1e <+74>: jne 0x8048e25 <phase_6+81>
0x08048e20 <+76>: call 0x8049110 <explode_bomb>
0x08048e25 <+81>: add $0x1,%ebx
0x08048e28 <+84>: cmp $0x5,%ebx
0x08048e2b <+87>: jle 0x8048e16 <phase_6+66>
0x08048e2d <+89>: jmp 0x8048dfb <phase_6+39>

我们输入的满足了条件之后,来到了第二个阶段:

+91 我们将第一个参数移动到eax当中去。

+95 我们将 栈顶指针加上0x24的地址中存放的内容移动到eax当中,即a[6](超出数组空间但也可以这么写)

+99和+104 我们将ecx和edx都置为7

+106 让%edx(7)减去第一个数,得到某一个数。然后修改eax地址上存储的数(即变为7-a[0])

然后将eax地址+4,指向数组的下一个数,现在是a[1]。然后再将其值改为(7-a[1]) 以此类推

等到eax的地址指向 a[6] 的时候,跳出循环。并将 ebx置为0

+122 让我们跳转到 +146 并将当前ebx赋值给esi,即现在esi=0. 然后在+148第一个输入的值(本来是5,现在是7-5=2) 赋给ecx

+152 是将1赋值给eax

+157 是将某一个地址赋给edx

+162 是将ecx也就是我(7减去输入的第一个数) 和1比较如果大于1,就跳转到+124.否则 (等于1) 跳转到+134

跳转到124,我们发现 +124 是将edx+=8,然后将edx上存放的数据取出放到edx当中去。然后让eax+1,并和我们输入的数作比较,如果相等,就将edx中存放的数存放到栈里面去。否则就edx继续加8,继续让eax+1,一直到eax=ecx。所以最后edx中存储的数取决于我输入的数。

现在我们来看看0x804c13c 到底是什么:

1
2
3
4
5
6
7
8
9
10
11
12
(gdb) x/3x 0x804c13c 
0x804c13c <node1>: 0x00000074 0x00000001 0x0804c148 116
(gdb) x/3x *(0x804c13c+0x8)
0x804c148 <node2>: 0x00000388 0x00000002 0x0804c154 904
(gdb) x/3x *(*(0x804c13c+0x8)+0x8)
0x804c154 <node3>: 0x00000269 0x00000003 0x0804c160 617
(gdb) x/3x *(*(*(0x804c13c+0x8)+0x8)+0x8)
0x804c160 <node4>: 0x00000093 0x00000004 0x0804c16c 147
(gdb) x/3x *(*(*(*(0x804c13c+0x8)+0x8)+0x8)+0x8)
0x804c16c <node5>: 0x00000109 0x00000005 0x0804c178 265
(gdb) x/3x *(*(*(*(*(0x804c13c+0x8)+0x8)+0x8)+0x8)+0x8)
0x804c178 <node6>: 0x000001ea 0x00000006 0x00000000 490

我们发现0x804c13c 开始,是一个链表,比如说处理好的数是2,那么经过上面这个程序,会将0x388取出来放到栈里,同理,如果处理好的数是3,那么会将0x269压入栈中

这个阶段仍然是一个两层循环,目的在于将我们从链表中取到的和输入数字相应的结点地址放入栈中保存,将六个都取完之后,到下一个阶段

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
0x08048e2f <+91>:    lea    0xc(%esp),%eax
0x08048e33 <+95>: lea 0x24(%esp),%ebx
0x08048e37 <+99>: mov $0x7,%ecx
0x08048e3c <+104>: mov %ecx,%edx
0x08048e3e <+106>: sub (%eax),%edx
0x08048e40 <+108>: mov %edx,(%eax)
0x08048e42 <+110>: add $0x4,%eax
0x08048e45 <+113>: cmp %eax,%ebx
0x08048e47 <+115>: jne 0x8048e3c <phase_6+104>
0x08048e49 <+117>: mov $0x0,%ebx
0x08048e4e <+122>: jmp 0x8048e66 <phase_6+146>
0x08048e50 <+124>: mov 0x8(%edx),%edx
0x08048e53 <+127>: add $0x1,%eax
0x08048e56 <+130>: cmp %ecx,%eax
0x08048e58 <+132>: jne 0x8048e50 <phase_6+124>
0x08048e5a <+134>: mov %edx,0x24(%esp,%esi,4)
0x08048e5e <+138>: add $0x1,%ebx
0x08048e61 <+141>: cmp $0x6,%ebx
0x08048e64 <+144>: je 0x8048e7d <phase_6+169>
0x08048e66 <+146>: mov %ebx,%esi
0x08048e68 <+148>: mov 0xc(%esp,%ebx,4),%ecx
0x08048e6c <+152>: mov $0x1,%eax
0x08048e71 <+157>: mov $0x804c13c,%edx
0x08048e76 <+162>: cmp $0x1,%ecx
0x08048e79 <+165>: jg 0x8048e50 <phase_6+124>
0x08048e7b <+167>: jmp 0x8048e5a <phase_6+134>

上面的循环了6次,我们现在来到了第三阶段

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
0x08048e7d <+169>:   mov    0x24(%esp),%ebx
0x08048e81 <+173>: lea 0x24(%esp),%eax
0x08048e85 <+177>: lea 0x38(%esp),%esi
0x08048e89 <+181>: mov %ebx,%ecx
0x08048e8b <+183>: mov 0x4(%eax),%edx
0x08048e8e <+186>: mov %edx,0x8(%ecx)
0x08048e91 <+189>: add $0x4,%eax
0x08048e94 <+192>: mov %edx,%ecx
0x08048e96 <+194>: cmp %eax,%esi
0x08048e98 <+196>: jne 0x8048e8b <phase_6+183>
0x08048e9a <+198>: movl $0x0,0x8(%edx)
0x08048ea1 <+205>: mov $0x5,%esi
0x08048ea6 <+210>: mov 0x8(%ebx),%eax
0x08048ea9 <+213>: mov (%eax),%eax
0x08048eab <+215>: cmp %eax,(%ebx)
0x08048ead <+217>: jge 0x8048eb4 <phase_6+224>
0x08048eaf <+219>: call 0x8049110 <explode_bomb>
0x08048eb4 <+224>: mov 0x8(%ebx),%ebx
0x08048eb7 <+227>: sub $0x1,%esi
0x08048eba <+230>: jne 0x8048ea6 <phase_6+210>
0x08048ebc <+232>: mov 0x3c(%esp),%eax
0x08048ec0 <+236>: xor %gs:0x14,%eax
0x08048ec7 <+243>: je 0x8048ece <phase_6+250>
0x08048ec9 <+245>: call 0x8048790 <__stack_chk_fail@plt>
0x08048ece <+250>: add $0x44,%esp
0x08048ed1 <+253>: pop %ebx
0x08048ed2 <+254>: pop %esi
0x08048ed3 <+255>: ret

可以看出esi是循环计数变量,要对里面的的五个数进行判断,一共5轮,判断结点保存的六个数中相邻的数是不是都比后面一个数大,一言以蔽之,就是判断结点里面的值是不是非递增的!

所以,最后的排列数是递减的,查询链表发现:$0x388\rightarrow 0x269 \rightarrow0x1ea\rightarrow 0x93 \rightarrow 0x74$

对应链表中的位置是 :2 3 6 5 4 1

但是别忘了这是处理好的数据,没有处理过的数据是7-i,所以我们输入的数据应该是 5 3 1 2 3 6

Defused_Phase and Secret_Phase

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
phase_defused:
0x08049269 <+0>: sub $0x6c,%esp
0x0804926c <+3>: mov %gs:0x14,%eax
0x08049272 <+9>: mov %eax,0x5c(%esp)
0x08049276 <+13>: xor %eax,%eax
0x08049278 <+15>: cmpl $0x6,0x804c3cc
0x0804927f <+22>: jne 0x80492f4 <phase_defused+139>
0x08049281 <+24>: sub $0xc,%esp
0x08049284 <+27>: lea 0x18(%esp),%eax
0x08049288 <+31>: push %eax
0x08049289 <+32>: lea 0x18(%esp),%eax
0x0804928d <+36>: push %eax
0x0804928e <+37>: lea 0x18(%esp),%eax
0x08049292 <+41>: push %eax
0x08049293 <+42>: push $0x804a249
0x08049298 <+47>: push $0x804c4d0
0x0804929d <+52>: call 0x8048810 <__isoc99_sscanf@plt>
0x080492a2 <+57>: add $0x20,%esp
0x080492a5 <+60>: cmp $0x3,%eax
0x080492a8 <+63>: jne 0x80492e4 <phase_defused+123>
0x080492aa <+65>: sub $0x8,%esp
0x080492ad <+68>: push $0x804a252
0x080492b2 <+73>: lea 0x18(%esp),%eax
0x080492b6 <+77>: push %eax
0x080492b7 <+78>: call 0x8049019 <strings_not_equal>
0x080492bc <+83>: add $0x10,%esp
0x080492bf <+86>: test %eax,%eax
0x080492c1 <+88>: jne 0x80492e4 <phase_defused+123>
0x080492c3 <+90>: sub $0xc,%esp
0x080492c6 <+93>: push $0x804a118
0x080492cb <+98>: call 0x80487c0 <puts@plt>
0x080492d0 <+103>: movl $0x804a140,(%esp)
0x080492d7 <+110>: call 0x80487c0 <puts@plt>
0x080492dc <+115>: call 0x8048f25 <secret_phase>
0x080492e1 <+120>: add $0x10,%esp
0x080492e4 <+123>: sub $0xc,%esp
0x080492e7 <+126>: push $0x804a178
0x080492ec <+131>: call 0x80487c0 <puts@plt>
0x080492f1 <+136>: add $0x10,%esp
0x080492f4 <+139>: mov 0x5c(%esp),%eax
0x080492f8 <+143>: xor %gs:0x14,%eax
0x080492ff <+150>: je 0x8049306 <phase_defused+157>
0x08049301 <+152>: call 0x8048790 <__stack_chk_fail@plt>
0x08049306 <+157>: add $0x6c,%esp
0x08049309 <+160>: ret
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
secret_phase
0x08048f25 <+0>: push %ebx
0x08048f26 <+1>: sub $0x8,%esp
0x08048f29 <+4>: call 0x8049170 <read_line>
0x08048f2e <+9>: sub $0x4,%esp
0x08048f31 <+12>: push $0xa
0x08048f33 <+14>: push $0x0
0x08048f35 <+16>: push %eax
0x08048f36 <+17>: call 0x8048880 <strtol@plt>
0x08048f3b <+22>: mov %eax,%ebx
0x08048f3d <+24>: lea -0x1(%eax),%eax
0x08048f40 <+27>: add $0x10,%esp
0x08048f43 <+30>: cmp $0x3e8,%eax
0x08048f48 <+35>: jbe 0x8048f4f <secret_phase+42>
0x08048f4a <+37>: call 0x8049110 <explode_bomb>
0x08048f4f <+42>: sub $0x8,%esp
0x08048f52 <+45>: push %ebx
0x08048f53 <+46>: push $0x804c088
0x08048f58 <+51>: call 0x8048ed4 <fun7>
0x08048f5d <+56>: add $0x10,%esp
0x08048f60 <+59>: cmp $0x5,%eax
0x08048f63 <+62>: je 0x8048f6a <secret_phase+69>
0x08048f65 <+64>: call 0x8049110 <explode_bomb>
0x08048f6a <+69>: sub $0xc,%esp
0x08048f6d <+72>: push $0x804a044
0x08048f72 <+77>: call 0x80487c0 <puts@plt>
0x08048f77 <+82>: call 0x8049269 <phase_defused>
0x08048f7c <+87>: add $0x18,%esp
0x08048f7f <+90>: pop %ebx
0x08048f80 <+91>: ret
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
fun7:
0x08048ed4 <+0>: push %ebx
0x08048ed5 <+1>: sub $0x8,%esp
0x08048ed8 <+4>: mov 0x10(%esp),%edx
0x08048edc <+8>: mov 0x14(%esp),%ecx
0x08048ee0 <+12>: test %edx,%edx
0x08048ee2 <+14>: je 0x8048f1b <fun7+71>
0x08048ee4 <+16>: mov (%edx),%ebx
0x08048ee6 <+18>: cmp %ecx,%ebx
0x08048ee8 <+20>: jle 0x8048efd <fun7+41>
0x08048eea <+22>: sub $0x8,%esp
0x08048eed <+25>: push %ecx
0x08048eee <+26>: pushl 0x4(%edx)
0x08048ef1 <+29>: call 0x8048ed4 <fun7>
0x08048ef6 <+34>: add $0x10,%esp
0x08048ef9 <+37>: add %eax,%eax
0x08048efb <+39>: jmp 0x8048f20 <fun7+76>
0x08048efd <+41>: mov $0x0,%eax
0x08048f02 <+46>: cmp %ecx,%ebx
0x08048f04 <+48>: je 0x8048f20 <fun7+76>
0x08048f06 <+50>: sub $0x8,%esp
0x08048f09 <+53>: push %ecx
0x08048f0a <+54>: pushl 0x8(%edx)
0x08048f0d <+57>: call 0x8048ed4 <fun7>
0x08048f12 <+62>: add $0x10,%esp
0x08048f15 <+65>: lea 0x1(%eax,%eax,1),%eax
0x08048f19 <+69>: jmp 0x8048f20 <fun7+76>
0x08048f1b <+71>: mov $0xffffffff,%eax
0x08048f20 <+76>: add $0x8,%esp
0x08048f23 <+79>: pop %ebx
0x08048f24 <+80>: ret
-------------本文结束,感谢您的阅读-------------