Bomb实验
将自己的炸弹解压,发现只有bomb可执行文件,bomb.c源码和一个README文件, 再打开bomb.c源码:
在主体bomb部分,可以看到initialize函数,其作用为初始化炸弹bomb,接下来就进入了主体的bomb运行阶段。分析可知,炸弹通过若干个密码串行组成,在每一个密码破译之前不能向下运行下一个解密关卡,所有关卡中有一个与真实密码不匹配,相应的defused函数将无法成功运行,同时结束整个程序(引爆炸弹)。代码分析完毕,接下来开始尝试进行反汇编和调试部分。
指令:
实验开始之前,bomb和bomb.c文件都是白色的,不可执行,于是我们要输入命令 chomd +x 文件名使得文件可以运行,然后就进入gdb调试
objdump -d bomb > assemble.txt # 将反汇编代码重定向输入到assemble.txt中,便于后续查看
gdb -q bomb(开始调试bomb) # 需要使用gdb来查看源代码中查看不到的代码
phase_1
gdb bomb
b phase_1 打断点
r 运行
随意输入一个字符之后,我们要查看它的反汇编代码来找答案
寄存器中(%esp)(%esp+4)中存储的值(0xffee5290)(0xffee5294)作为地址,内存中存储的值是(0x0804c3e0)(0x804a144),再将这两个数据作为地址在内存中对应的值就是我们输入的字符串以及我们答案字符串。这两个字符串的地址会作为参数传入到strings_not_equal
中做比较。
我们查看x/s 0x804a144
,输出为"When a problem comes along, you must zip it "
,即是答案
phase_2
以下是phase_2的反汇编代码
08048bb4 <phase_2>:
8048bb4: 56 push %esi
8048bb5: 53 push %ebx
8048bb6: 83 ec 34 sub $0x34,%esp
8048bb9: 8d 44 24 18 lea 0x18(%esp),%eax
8048bbd: 89 44 24 04 mov %eax,0x4(%esp)
8048bc1: 8b 44 24 40 mov 0x40(%esp),%eax
8048bc5: 89 04 24 mov %eax,(%esp)
8048bc8: e8 7f 05 00 00 call 804914c <read_six_numbers>
8048bcd: 83 7c 24 18 00 cmpl $0x0,0x18(%esp)
8048bd2: 75 07 jne 8048bdb <phase_2+0x27>
8048bd4: 83 7c 24 1c 01 cmpl $0x1,0x1c(%esp)
8048bd9: 74 1f je 8048bfa <phase_2+0x46>
8048bdb: e8 45 05 00 00 call 8049125 <explode_bomb>
8048be0: eb 18 jmp 8048bfa <phase_2+0x46>
8048be2: 8b 43 f8 mov -0x8(%ebx),%eax
8048be5: 03 43 fc add -0x4(%ebx),%eax
8048be8: 39 03 cmp %eax,(%ebx)
8048bea: 74 05 je 8048bf1 <phase_2+0x3d>
8048bec: e8 34 05 00 00 call 8049125 <explode_bomb>
8048bf1: 83 c3 04 add $0x4,%ebx
8048bf4: 39 f3 cmp %esi,%ebx
8048bf6: 75 ea jne 8048be2 <phase_2+0x2e>
8048bf8: eb 0a jmp 8048c04 <phase_2+0x50>
8048bfa: 8d 5c 24 20 lea 0x20(%esp),%ebx
8048bfe: 8d 74 24 30 lea 0x30(%esp),%esi
8048c02: eb de jmp 8048be2 <phase_2+0x2e>
8048c04: 83 c4 34 add $0x34,%esp
8048c07: 5b pop %ebx
8048c08: 5e pop %esi
8048c09: c3 ret
je 表示等于就跳转,jne是不等于就跳转
jmp是汇编语言中的无条件跳转指令
查看了一下函数
发现是输入六个整型的数字,通过寻找炸弹爆炸的条件,来找到这六个数字
8048bcd: 83 7c 24 18 00 cmpl
0
x
0
,
0
x
18
(
通
过
这
一
行
我
们
知
道
了
0x0,0x18(%esp) 通过这一行我们知道了
0x0,0x18(通过这一行我们知道了esp+0x18存放的是我们
我们猜测可知,我们输入的六个数字以空格隔开,从0x18(%esp)
开始顺序存储。可以使用x/24bx $esp+0x18
进行查看,假设我们输入的数字为1 1 1 1 1 1
,那么输出为:
输进去的第一个值,需要与0比较,相同跳转,不相同就爆炸,所以第一个数字就是0
由于一开始调试我们输进去的值是乱写的,现在需要去改掉
命令行输入
set {int}($exp+0x18)=0
此处注意到esp+0x18的地址是0xbffff218,改完之后输入si,继续执行,发现他没有爆炸,跳过跳转指令来到了以下这一行
8048bd4: 83 7c 24 1c 01 cmpl
0
x
1
,
0
x
1
c
(
通
过
这
一
行
我
们
知
道
了
0x1,0x1c(%esp) 通过这一行我们知道了
0x1,0x1c(通过这一行我们知道了esp+0x1c存放的是我们输进去的第二个值,需要与1比较,相同跳转,不相同就爆炸,所以第二个数字就是1
由于一开始调试我们输进去的值是乱写的,现在需要去改掉
命令行输入
set {int}($exp+0x1c)=1
8048bd9: 74 1f je 8048bfa <phase_2+0x46>
8048bfa: 8d 5c 24 20 lea 0x20(%esp),%ebx
8048bfe: 8d 74 24 30 lea 0x30(%esp),%esi
8048c02: eb de jmp 8048be2 <phase_2+0x2e>
由于0x1c(%esp)下一个存储的位置是0x20(%esp),所以是要把0x20(%esp)的地址给ebx,0x30(%esp)则是该数组的界限,把界限的地址给esi,然后无条件跳转
8048be2: 8b 43 f8 mov -0x8(%ebx),%eax
8048be5: 03 43 fc add -0x4(%ebx),%eax
8048be8: 39 03 cmp %eax,(%ebx)
现在第三个要比较的数字在ebx存储的地址里,-0x8(%ebx)里存储的是第一个数字的地址给到eax,再将-0x4(%ebx)地址里的存储的第二个数加到eax地址里的值里面,然后与第三个数字相比,所以我们知道第三个数字应该存储前面两个之和,我们也知道前两个数之和是1
由于一开始调试我们输进去的值是乱写的,现在需要去改掉
命令行输入
set {int}$ebx=1
此时的0x20(%esp)的地址是0xbffff220,此时%ebx的地址也是0xbffff220,此时看eax与(%ebx)是否相等,则跳转是下面的指令
8048bea: 74 05 je 8048bf1 <phase_2+0x3d>
8048bec: e8 34 05 00 00 call 8049125 <explode_bomb>
8048bf1: 83 c3 04 add $0x4,%ebx
8048bf4: 39 f3 cmp %esi,%ebx
8048bf6: 75 ea jne 8048be2 <phase_2+0x2e>
8048bf1这条指令执行完,%ebx的地址也是0xbffff224,里面存储的值是1
8048bf4这条指令是为了比较跳到数组的下一个值时有没有越界,esi存储的是数组的界限值0xbffff230,由于没越界所以不相等就跳转
8048be2: 8b 43 f8 mov -0x8(%ebx),%eax
8048be5: 03 43 fc add -0x4(%ebx),%eax
8048be8: 39 03 cmp %eax,(%ebx)
-0x8(%ebx)对应的地址是0xbffff21c,里面的值是1,-0x4(%ebx)对应的地址是0xbffff220,里面的值是1,所以现在%eax地址里对应的值是2,所以现在要将%ebx的地址0xbffff224对应的值1改成2
由于一开始调试我们输进去的值是乱写的,现在需要去改掉
命令行输入
set {int}$ebx=2
8048bea: 74 05 je 8048bf1 <phase_2+0x3d>
8048bec: e8 34 05 00 00 call 8049125 <explode_bomb>
8048bf1: 83 c3 04 add $0x4,%ebx
8048bf4: 39 f3 cmp %esi,%ebx
8048bf6: 75 ea jne 8048be2 <phase_2+0x2e>
到这里我们发现跟上面的流程是一样的,就可以知道其实是循环,我们就猜是不是后面的数等于这个数字前面两个数的和,所以结果是3和5
所以最终答案是“0 1 1 2 3 5”
phase_3
以下是phase_2的反汇编代码
08048c0a <phase_3>:
8048c0a: 83 ec 2c sub $0x2c,%esp
8048c0d: 8d 44 24 1c lea 0x1c(%esp),%eax
8048c11: 89 44 24 0c mov %eax,0xc(%esp)
8048c15: 8d 44 24 18 lea 0x18(%esp),%eax
8048c19: 89 44 24 08 mov %eax,0x8(%esp)
8048c1d: c7 44 24 04 0f a3 04 movl $0x804a30f,0x4(%esp)
8048c24: 08
8048c25: 8b 44 24 30 mov 0x30(%esp),%eax
8048c29: 89 04 24 mov %eax,(%esp)
8048c2c: e8 2f fc ff ff call 8048860 <__isoc99_sscanf@plt
8048c31: 83 f8 01 cmp $0x1,%eax
8048c34: 7f 05 jg 8048c3b <phase_3+0x31>
8048c36: e8 ea 04 00 00 call 8049125 <explode_bomb>
8048c3b: 83 7c 24 18 07 cmpl $0x7,0x18(%esp)
8048c40: 77 66 ja 8048ca8 <phase_3+0x9e>
8048c42: 8b 44 24 18 mov 0x18(%esp),%eax
8048c46: ff 24 85 a0 a1 04 08 jmp *0x804a1a0(,%eax,4)
8048c4d: b8 00 00 00 00 mov $0x0,%eax
8048c52: eb 05 jmp 8048c59 <phase_3+0x4f>
8048c54: b8 1e 01 00 00 mov $0x11e,%eax
8048c59: 2d e0 03 00 00 sub $0x3e0,%eax
8048c5e: eb 05 jmp 8048c65 <phase_3+0x5b>
8048c60: b8 00 00 00 00 mov $0x0,%eax
8048c65: 05 b7 01 00 00 add $0x1b7,%eax
8048c6a: eb 05 jmp 8048c71 <phase_3+0x67>
8048c6c: b8 00 00 00 00 mov $0x0,%eax
8048c71: 2d d7 01 00 00 sub $0x1d7,%eax
8048c76: eb 05 jmp 8048c7d <phase_3+0x73>
8048c78: b8 00 00 00 00 mov $0x0,%eax
8048c7d: 05 d7 01 00 00 add $0x1d7,%eax
8048c82: eb 05 jmp 8048c89 <phase_3+0x7f>
8048c84: b8 00 00 00 00 mov $0x0,%eax
8048c89: 2d d7 01 00 00 sub $0x1d7,%eax
8048c8e: eb 05 jmp 8048c95 <phase_3+0x8b>
8048c90: b8 00 00 00 00 mov $0x0,%eax
8048c95: 05 d7 01 00 00 add $0x1d7,%eax
8048c9a: eb 05 jmp 8048ca1 <phase_3+0x97>
8048c9c: b8 00 00 00 00 mov $0x0,%eax
8048ca1: 2d d7 01 00 00 sub $0x1d7,%eax
8048ca6: eb 0a jmp 8048cb2 <phase_3+0xa8>
8048ca8: e8 78 04 00 00 call 8049125 <explode_bomb>
8048cad: b8 00 00 00 00 mov $0x0,%eax
8048cb2: 83 7c 24 18 05 cmpl $0x5,0x18(%esp)
8048cb7: 7f 06 jg 8048cbf <phase_3+0xb5>
8048cb9: 3b 44 24 1c cmp 0x1c(%esp),%eax
8048cbd: 74 05 je 8048cc4 <phase_3+0xba>
8048cbf: e8 61 04 00 00 call 8049125 <explode_bomb>
8048cc4: 83 c4 2c add $0x2c,%esp
8048cc7: c3 ret
8048c0a: 83 ec 2c sub $0x2c,%esp
8048c0d: 8d 44 24 1c lea 0x1c(%esp),%eax
8048c11: 89 44 24 0c mov %eax,0xc(%esp)
8048c15: 8d 44 24 18 lea 0x18(%esp),%eax
8048c19: 89 44 24 08 mov %eax,0x8(%esp)
8048c1d: c7 44 24 04 0f a3 04 movl $0x804a30f,0x4(%esp)
8048c24: 08
8048c25: 8b 44 24 30 mov 0x30(%esp),%eax
8048c29: 89 04 24 mov %eax,(%esp)
8048c2c: e8 2f fc ff ff call 8048860 <__isoc99_sscanf@plt>
这一段汇编代码就是在做输入的准备工作,查看$0x804a30f,是要输入两个整型的数字,__isoc99_sscanf@plt这个函数返回到eax中输入数字的个数
8048c31: 83 f8 01 cmp $0x1,%eax
8048c34: 7f 05 jg 8048c3b <phase_3+0x31>
8048c36: e8 ea 04 00 00 call 8049125 <explode_bomb>
要求输入的数字个数必须大于1个,否则就爆炸
8048c3b: 83 7c 24 18 07 cmpl $0x7,0x18(%esp)
要求输入的第一个数要小于7
8048c40: 77 66 ja 8048ca8 <phase_3+0x9e>
8048c42: 8b 44 24 18 mov 0x18(%esp),%eax
8048c46: ff 24 85 a0 a1 04 08 jmp *0x804a1a0(,%eax,4)
跳转到8048c54+4*(第一个数输入的值)
接下来运行的代码是:
8048c54: b8 1e 01 00 00 mov $0x11e,%eax
8048c59: 2d e0 03 00 00 sub $0x3e0,%eax
8048c5e: eb 05 jmp 8048c65 <phase_3+0x5b>
8048c65: 05 b7 01 00 00 add $0x1b7,%eax
8048c6a: eb 05 jmp 8048c71 <phase_3+0x67>
8048c71: 2d d7 01 00 00 sub $0x1d7,%eax
8048c76: eb 05 jmp 8048c7d <phase_3+0x73>
8048c7d: 05 d7 01 00 00 add $0x1d7,%eax
8048c82: eb 05 jmp 8048c89 <phase_3+0x7f>
8048c89: 2d d7 01 00 00 sub $0x1d7,%eax
8048c8e: eb 05 jmp 8048c95 <phase_3+0x8b>
8048ca1: 2d d7 01 00 00 sub $0x1d7,%eax
8048ca6: eb 0a jmp 8048cb2 <phase_3+0xa8>
8048cb2: 83 7c 24 18 05 cmpl $0x5,0x18(%esp)
8048cb7: 7f 06 jg 8048cbf <phase_3+0xb5>
从以上汇编代码中可以看到进行了+$0x11e,-$0x3e0,+$0x1b7,-$0x1b7,+$0x1b7,-$0x1b7,-$0x1b7,结果等于-738
得出输入的第一个数字必须小于5 ,不然就会爆炸
8048cb9: 3b 44 24 1c cmp 0x1c(%esp),%eax
8048cbd: 74 05 je 8048cc4 <phase_3+0xba>//相等跳转
如果第二个输入的数字和以上的运算结果eax不相等就会爆炸,所以在比较之前将0x1c(%esp)里面的值改成-738
得到第一个答案是0 -738
第二个答案是1 -1024
第三个答案是2 -32
第四个答案是3 -471
第五个答案是4 0
phase_4
8048d12: 83 ec 2c sub $0x2c,%esp
8048d15: 8d 44 24 18 lea 0x18(%esp),%eax
8048d19: 89 44 24 0c mov %eax,0xc(%esp)
8048d1d: 8d 44 24 1c lea 0x1c(%esp),%eax
8048d21: 89 44 24 08 mov %eax,0x8(%esp)
8048d25: c7 44 24 04 0f a3 04 movl $0x804a30f,0x4(%esp)
8048d2c: 08
8048d2d: 8b 44 24 30 mov 0x30(%esp),%eax
8048d31: 89 04 24 mov %eax,(%esp)
8048d34: e8 27 fb ff ff call 8048860 <__isoc99_sscanf@plt>
8048d39: 83 f8 02 cmp $0x2,%eax
8048d3c: 75 0c jne 8048d4a <phase_4+0x38>
8048d3e: 8b 44 24 18 mov 0x18(%esp),%eax
8048d42: 83 e8 02 sub $0x2,%eax
8048d45: 83 f8 02 cmp $0x2,%eax
8048d48: 76 05 jbe 8048d4f <phase_4+0x3d>
8048d4a: e8 d6 03 00 00 call 8049125 <explode_bomb>
8048d4f: 8b 44 24 18 mov 0x18(%esp),%eax
8048d53: 89 44 24 04 mov %eax,0x4(%esp)
8048d57: c7 04 24 05 00 00 00 movl $0x5,(%esp)
8048d5e: e8 65 ff ff ff call 8048cc8 <func4>
8048d63: 3b 44 24 1c cmp 0x1c(%esp),%eax
8048d67: 74 05 je 8048d6e <phase_4+0x5c>
8048d69: e8 b7 03 00 00 call 8049125 <explode_bomb>
8048d6e: 83 c4 2c add $0x2c,%esp
8048d71: c3 ret
通过
8048d25: c7 44 24 04 0f a3 04 movl $0x804a30f,0x4(%esp)
看到需要我们输入两个整数
8048d39: 83 f8 02 cmp $0x2,%eax
8048d3c: 75 0c jne 8048d4a <phase_4+0x38>
将我们输入的第二个数字进行比较,输入的第二个数字不是2就爆炸
8048d3e: 8b 44 24 18 mov 0x18(%esp),%eax
8048d42: 83 e8 02 sub $0x2,%eax
8048d45: 83 f8 02 cmp $0x2,%eax
8048d48: 76 05 jbe 8048d4f <phase_4+0x3d>
如果我们输入的第二个数字减2并转化为无符号数不等于2也爆炸,所以规定了我们第二个数字必须输入2
8048d4f: 8b 44 24 18 mov 0x18(%esp),%eax
8048d53: 89 44 24 04 mov %eax,0x4(%esp)
8048d57: c7 04 24 05 00 00 00 movl $0x5,(%esp)
8048d5e: e8 65 ff ff ff call 8048cc8 <func4>
将输入的第二个数字2和5放到栈的最前面,以便调用func4
func4
08048cc8 <func4>:
8048cc8: 57 push %edi
8048cc9: 56 push %esi
8048cca: 53 push %ebx
8048ccb: 83 ec 10 sub $0x10,%esp
8048cce: 8b 5c 24 20 mov 0x20(%esp),%ebx
8048cd2: 8b 74 24 24 mov 0x24(%esp),%esi
8048cd6: 85 db test %ebx,%ebx
8048cd8: 7e 2c jle 8048d06 <func4+0x3e>
8048cda: 89 f0 mov %esi,%eax
8048cdc: 83 fb 01 cmp $0x1,%ebx
8048cdf: 74 2a je 8048d0b <func4+0x43>
8048ce1: 89 74 24 04 mov %esi,0x4(%esp)
8048ce5: 8d 43 ff lea -0x1(%ebx),%eax
8048ce8: 89 04 24 mov %eax,(%esp)
8048ceb: e8 d8 ff ff ff call 8048cc8 <func4>
8048cf0: 8d 3c 30 lea (%eax,%esi,1),%edi
8048cf3: 89 74 24 04 mov %esi,0x4(%esp)
8048cf7: 83 eb 02 sub $0x2,%ebx
8048cfa: 89 1c 24 mov %ebx,(%esp)
8048cfd: e8 c6 ff ff ff call 8048cc8 <func4>
8048d02: 01 f8 add %edi,%eax
8048d04: eb 05 jmp 8048d0b <func4+0x43>
8048d06: b8 00 00 00 00 mov $0x0,%eax
8048d0b: 83 c4 10 add $0x10,%esp
8048d0e: 5b pop %ebx
8048d0f: 5e pop %esi
8048d10: 5f pop %edi
8048d11: c3 ret
通过phase_4可以得出func4调用的两个变量,形式如下
func4(5, 2)
8048cd6: 85 db test %ebx,%ebx
8048cd8: 7e 2c jle 8048d06 <func4+0x3e>
要求func4变量1不能为0,否则返回0
8048cda: 89 f0 mov %esi,%eax
8048cdc: 83 fb 01 cmp $0x1,%ebx
8048cdf: 74 2a je 8048d0b <func4+0x43>
要求func4变量1不能为1,否则返回结果
8048ce1: 89 74 24 04 mov %esi,0x4(%esp)
8048ce5: 8d 43 ff lea -0x1(%ebx),%eax
8048ce8: 89 04 24 mov %eax,(%esp)
8048ceb: e8 d8 ff ff ff call 8048cc8 <func4>
对应的代码是
v3 = func4(a1 - 1, a2) + a2;
8048cf0: 8d 3c 30 lea (%eax,%esi,1),%edi
8048cf3: 89 74 24 04 mov %esi,0x4(%esp)
8048cf7: 83 eb 02 sub $0x2,%ebx
8048cfa: 89 1c 24 mov %ebx,(%esp)
8048cfd: e8 c6 ff ff ff call 8048cc8 <func4>
对应的代码是
return v3 + func4(a1 - 2, a2);
8048d02: 01 f8 add %edi,%eax
8048d04: eb 05 jmp 8048d0b <func4+0x43>
将翻译出来的c代码进行运行,即可得到我们的答案
#include<stdio.h>
int func4(int a1,int a2)
{
int result;
int v3;
if ( a1 <= 0 )
return 0;
result = a2;
if ( a1 != 1 )
{
v3 = func4(a1 - 1, a2) + a2;
return v3 + func4(a1 - 2, a2);
}
return result;
}
int main()
{
int y;
scanf("%d",&y);
int result1 =func4(5,y);
printf("%d",result1);
getchar();
return 0;
}
所以这道题的答案就是24 2
phase_5
08048d72 <phase_5>:
8048d72: 83 ec 2c sub $0x2c,%esp
8048d75: 8d 44 24 1c lea 0x1c(%esp),%eax
8048d79: 89 44 24 0c mov %eax,0xc(%esp)
8048d7d: 8d 44 24 18 lea 0x18(%esp),%eax
8048d81: 89 44 24 08 mov %eax,0x8(%esp)
8048d85: c7 44 24 04 0f a3 04 movl $0x804a30f,0x4(%esp)
8048d8c: 08
8048d8d: 8b 44 24 30 mov 0x30(%esp),%eax
8048d91: 89 04 24 mov %eax,(%esp)
8048d94: e8 c7 fa ff ff call 8048860 <__isoc99_sscanf@plt>
8048d99: 83 f8 01 cmp $0x1,%eax
8048d9c: 7f 05 jg 8048da3 <phase_5+0x31>
8048d9e: e8 82 03 00 00 call 8049125 <explode_bomb>
8048da3: 8b 44 24 18 mov 0x18(%esp),%eax
8048da7: 83 e0 0f and $0xf,%eax
8048daa: 89 44 24 18 mov %eax,0x18(%esp)
8048dae: 83 f8 0f cmp $0xf,%eax
8048db1: 74 2a je 8048ddd <phase_5+0x6b>
8048db3: b9 00 00 00 00 mov $0x0,%ecx
8048db8: ba 00 00 00 00 mov $0x0,%edx
8048dbd: 83 c2 01 add $0x1,%edx
8048dc0: 8b 04 85 c0 a1 04 08 mov 0x804a1c0(,%eax,4),%eax
8048dc7: 01 c1 add %eax,%ecx
8048dc9: 83 f8 0f cmp $0xf,%eax
8048dcc: 75 ef jne 8048dbd <phase_5+0x4b>
8048dce: 89 44 24 18 mov %eax,0x18(%esp)
8048dd2: 83 fa 0f cmp $0xf,%edx
8048dd5: 75 06 jne 8048ddd <phase_5+0x6b>
8048dd7: 3b 4c 24 1c cmp 0x1c(%esp),%ecx
8048ddb: 74 05 je 8048de2 <phase_5+0x70>
8048ddd: e8 43 03 00 00 call 8049125 <explode_bomb>
8048de2: 83 c4 2c add $0x2c,%esp
8048de5: c3 ret
8048d85: c7 44 24 04 0f a3 04 movl $0x804a30f,0x4(%esp)
由命令 x/s 0x804a30f 可以看到是需要我们输入两个整型的数字
根据`8048d5f`和`8048d67`两个地址的指令可知,我们的两个整数存放在`0x18(%esp)`和`0x1c(%esp)`处,简单起见,我们输入`1 2`。
8048d99: 83 f8 01 cmp $0x1,%eax
8048d9c: 7f 05 jg 8048da3 <phase_5+0x31>
要求输入的第二个数字大于1
8048da3: 8b 44 24 18 mov 0x18(%esp),%eax
8048da7: 83 e0 0f and $0xf,%eax
8048daa: 89 44 24 18 mov %eax,0x18(%esp)
8048dae: 83 f8 0f cmp $0xf,%eax
8048db1: 74 2a je 8048ddd <phase_5+0x6b>
要求输入的数字不等于15
8048db3: b9 00 00 00 00 mov $0x0,%ecx
8048db8: ba 00 00 00 00 mov $0x0,%edx
8048dbd: 83 c2 01 add $0x1,%edx
8048dc0: 8b 04 85 c0 a1 04 08 mov 0x804a1c0(,%eax,4),%eax
8048dc7: 01 c1 add %eax,%ecx
8048dc9: 83 f8 0f cmp $0xf,%eax
8048dcc: 75 ef jne 8048dbd <phase_5+0x4b>
ecx是将eax里面的值加起来,edx是循环的计数器,0x804a1c0指向的是一组数组
0x804a1c0(,%eax,4),%eax 是根据输入的第一个数字当作这个数组的下标,进行数组的读取
再把读取到的值放回eax中,如果读取到的数组的值不是0xf,继续循环
8048dbd: 83 c2 01 add $0x1,%edx
8048dc0: 8b 04 85 c0 a1 04 08 mov 0x804a1c0(,%eax,4),%eax
8048dc7: 01 c1 add %eax,%ecx
8048dc9: 83 f8 0f cmp $0xf,%eax
8048dcc: 75 ef jne 8048dbd <phase_5+0x4b>
直到读取到的数组的值是0xf才跳出循环,所以无论我们第一个数输入的是什么值,在循环中一定能从数组中找到0x15
8048dce: 89 44 24 18 mov %eax,0x18(%esp)
8048dd2: 83 fa 0f cmp $0xf,%edx
8048dd5: 75 06 jne 8048ddd <phase_5+0x6b>
这里还要求循环必须15次
8048dd7: 3b 4c 24 1c cmp 0x1c(%esp),%ecx
8048ddb: 74 05 je 8048de2 <phase_5+0x70>
ecx累加的eax的值还必须等于输入的第二个值
8048ddd: e8 43 03 00 00 call 8049125 <explode_bomb>
8048de2: 83 c4 2c add $0x2c,%esp
最后我通过第一个数字遍历了0-5,5即是我找的答案
最后的答案就是5 115
phase_6
08048de6 <phase_6>:
8048de6: 56 push %esi
8048de7: 53 push %ebx
8048de8: 83 ec 44 sub $0x44,%esp
8048deb: 8d 44 24 10 lea 0x10(%esp),%eax
8048def: 89 44 24 04 mov %eax,0x4(%esp)
8048df3: 8b 44 24 50 mov 0x50(%esp),%eax
8048df7: 89 04 24 mov %eax,(%esp)
8048dfa: e8 4d 03 00 00 call 804914c <read_six_numbers>
8048dff: be 00 00 00 00 mov $0x0,%esi
8048e04: 8b 44 b4 10 mov 0x10(%esp,%esi,4),%eax
8048e08: 83 e8 01 sub $0x1,%eax
8048e0b: 83 f8 05 cmp $0x5,%eax
8048e0e: 76 05 jbe 8048e15 <phase_6+0x2f>
8048e10: e8 10 03 00 00 call 8049125 <explode_bomb>
8048e15: 83 c6 01 add $0x1,%esi
8048e18: 83 fe 06 cmp $0x6,%esi//v1
8048e1b: 75 07 jne 8048e24 <phase_6+0x3e>
8048e1d: bb 00 00 00 00 mov $0x0,%ebx
8048e22: eb 38 jmp 8048e5c <phase_6+0x76>
8048e24: 89 f3 mov %esi,%ebx
8048e26: 8b 44 9c 10 mov 0x10(%esp,%ebx,4),%eax
8048e2a: 39 44 b4 0c cmp %eax,0xc(%esp,%esi,4)
8048e2e: 75 05 jne 8048e35 <phase_6+0x4f>
8048e30: e8 f0 02 00 00 call 8049125 <explode_bomb>
8048e35: 83 c3 01 add $0x1,%ebx
8048e38: 83 fb 05 cmp $0x5,%ebx
8048e3b: 7e e9 jle 8048e26 <phase_6+0x40>
8048e3d: eb c5 jmp 8048e04 <phase_6+0x1e
8048e3f: 8b 52 08 mov 0x8(%edx),%edx
//这里跨8个字节是因为,该地址是一个结构体,第一位放的是value,第二位放的是id
8048e42: 83 c0 01 add $0x1,%eax//++v5
8048e45: 39 c8 cmp %ecx,%eax
8048e47: 75 f6 jne 8048e3f <phase_6+0x59>
8048e49: eb 05 jmp 8048e50 <phase_6+0x6a>
8048e4b: ba 3c c1 04 08 mov $0x804c13c,%edx//取node1地址
8048e50: 89 54 b4 28 mov %edx,0x28(%esp,%esi,4)
8048e54: 83 c3 01 add $0x1,%ebx//++i
8048e57: 83 fb 06 cmp $0x6,%ebx
8048e5a: 74 17 je 8048e73 <phase_6+0x8d>
8048e5c: 89 de mov %ebx,%esi//esi=i
8048e5e: 8b 4c 9c 10 mov 0x10(%esp,%ebx,4),%ecx
8048e62: 83 f9 01 cmp $0x1,%ecx
8048e65: 7e e4 jle 8048e4b <phase_6+0x65>
8048e67: b8 01 00 00 00 mov $0x1,%eax//v5=eax=1
8048e6c: ba 3c c1 04 08 mov $0x804c13c,%edx//取node1地址
8048e71: eb cc jmp 8048e3f <phase_6+0x59>
//for array[x+6]存的是id=array[x]的节点的地址;
8048e73: 8b 5c 24 28 mov 0x28(%esp),%ebx//ebx=int v15
8048e77: 8d 44 24 2c lea 0x2c(%esp),%eax//eax=char v16
8048e7b: 8d 74 24 40 lea 0x40(%esp),%esi//esi=char v17
8048e7f: 89 d9 mov %ebx,%ecx//ecx=j=v15
8048e81: 8b 10 mov (%eax),%edx
8048e83: 89 51 08 mov %edx,0x8(%ecx)
8048e86: 83 c0 04 add $0x4,%eax
8048e89: 39 f0 cmp %esi,%eax
8048e8b: 74 04 je 8048e91 <phase_6+0xab>
8048e8d: 89 d1 mov %edx,%ecx
8048e8f: eb f0 jmp 8048e81 <phase_6+0x9b>
//for 借助ecx把0x804c13c结构体按顺序排列好在ebx中
8048e91: c7 42 08 00 00 00 00 movl $0x0,0x8(%edx)
8048e98: be 05 00 00 00 mov $0x5,%esi
8048e9d: 8b 43 08 mov 0x8(%ebx),%eax
8048ea0: 8b 00 mov (%eax),%eax
8048ea2: 39 03 cmp %eax,(%ebx)
8048ea4: 7e 05 jle 8048eab <phase_6+0xc5>
8048ea6: e8 7a 02 00 00 call 8049125 <explode_bomb>
8048eab: 8b 5b 08 mov 0x8(%ebx),%ebx
8048eae: 83 ee 01 sub $0x1,%esi
8048eb1: 75 ea jne 8048e9d <phase_6+0xb7>
//while 用ebx查看结构体中的值是否是从小到大排好顺序
8048eb3: 83 c4 44 add $0x44,%esp
8048eb6: 5b pop %ebx
8048eb7: 5e pop %esi
8048eb8: c3 ret
查看该地址知道是结构体,三个成员是:value、id、next
所以我们知道array[x+6]存的是id=array[x]的节点的地址;
M(array[x+6])存的是id=array[x]的节点的value;
M(array[x+6]+8)存的是id=array[x]的节点的next;
函数执行过程是:先输入array[x]并判断是否在[1,6]且各不相同;
将id=array[x]的节点地址存到array[x+6];
将id=array[x]的节点的地址的next成员改写成id=array[x+1]的节点的地址,也就是array[x+6]->next=array[x+6+1];
判断排序后的链表节点的value成员大小是否是递增的。
所以我们输入的数,其实是6个节点的id成员,函数按照输入id的顺序,将这六个节点排序,排序后的链表是递增的。
id 1 2 3 4 5 6
value 921 214 532 545 622 216
所以我们得到答案2 6 3 4 5 1
phase_defused
08049296 <phase_defused>:
8049296: 81 ec 8c 00 00 00 sub $0x8c,%esp
804929c: 65 a1 14 00 00 00 mov %gs:0x14,%eax
80492a2: 89 44 24 7c mov %eax,0x7c(%esp)
80492a6: 31 c0 xor %eax,%eax
80492a8: 83 3d c8 c3 04 08 06 cmpl $0x6,0x804c3c8
80492af: 75 72 jne 8049323 <phase_defused+0x8d>
80492b1: 8d 44 24 2c lea 0x2c(%esp),%eax
80492b5: 89 44 24 10 mov %eax,0x10(%esp)
80492b9: 8d 44 24 28 lea 0x28(%esp),%eax
80492bd: 89 44 24 0c mov %eax,0xc(%esp)
80492c1: 8d 44 24 24 lea 0x24(%esp),%eax
80492c5: 89 44 24 08 mov %eax,0x8(%esp)
80492c9: c7 44 24 04 69 a3 04 movl $0x804a369,0x4(%esp)
80492d0: 08
80492d1: c7 04 24 d0 c4 04 08 movl $0x804c4d0,(%esp)
80492d8: e8 83 f5 ff ff call 8048860 <__isoc99_sscanf@plt>
80492dd: 83 f8 03 cmp $0x3,%eax
80492e0: 75 35 jne 8049317 <phase_defused+0x81>
80492e2: c7 44 24 04 72 a3 04 movl $0x804a372,0x4(%esp)
80492e9: 08
80492ea: 8d 44 24 2c lea 0x2c(%esp),%eax
80492ee: 89 04 24 mov %eax,(%esp)
80492f1: e8 24 fd ff ff call 804901a <strings_not_equal>
80492f6: 85 c0 test %eax,%eax
80492f8: 75 1d jne 8049317 <phase_defused+0x81>
80492fa: c7 04 24 38 a2 04 08 movl $0x804a238,(%esp)
80 49301: e8 ea f4 ff ff call 80487f0 <puts@plt>
8049306: c7 04 24 60 a2 04 08 movl $0x804a260,(%esp)
804930d: e8 de f4 ff ff call 80487f0 <puts@plt>
8049312: e8 f3 fb ff ff call 8048f0a <secret_phase>
8049317: c7 04 24 98 a2 04 08 movl $0x804a298,(%esp)
804931e: e8 cd f4 ff ff call 80487f0 <puts@plt>
8049323: 8b 44 24 7c mov 0x7c(%esp),%eax
8049327: 65 33 05 14 00 00 00 xor %gs:0x14,%eax
804932e: 74 05 je 8049335 <phase_defused+0x9f>
8049330: e8 8b f4 ff ff call 80487c0 <__stack_chk_fail@plt>
8049335: 81 c4 8c 00 00 00 add $0x8c,%esp
804933b: c3 ret
804933c: 66 90 xchg %ax,%ax
804933e: 66 90 xchg %ax,%ax
寻找隐藏关卡入口:
• 首先,使用了一个 0x804c3c8 的值,这个值从汇编代码自带的提示中可以猜想得到,是一个当前已经输入的关卡数,即字符串数。而该比较语句的含义是,只有在已完成关卡6的情况下才能进入到secret_phase。否则,将会直接跳到<+123>语句,失去进入<+108>秘密关卡函数的机会。
80492c9: c7 44 24 04 69 a3 04 movl $0x804a369,0x4(%esp)
80492d0: 08
80492d1: c7 04 24 d0 c4 04 08 movl $0x804c4d0,(%esp)
80492d8: e8 83 f5 ff ff call 8048860 <__isoc99_sscanf@plt>
• 其次,是一个sscanf语句。然而,sscanf语言的参数相比于前面的参数要多了一个。通过x/s 0x804a369语句查看,输入格式为"%d %d %s"
• 于是,我们想到sscanf语句与scanf语句的不同之处。sscanf语句可以有一个字符串参数,用于指定sscanf所需输入的字符串的源。(默认源是标准输入)
• 而这个字符串应当由我们输入触发隐藏关卡才对,为什么会由汇编语言给出?这就说明应该是我们前面的输入,保存到了这个地方。使得隐藏函数的以触发。
这是第四关的答案,说明隐藏关卡在第四关,但是还缺少一个字符串
80492e2: c7 44 24 04 72 a3 04 movl $0x804a372,0x4(%esp)
80492e9: 08
80492ea: 8d 44 24 2c lea 0x2c(%esp),%eax
80492ee: 89 04 24 mov %eax,(%esp)
80492f1: e8 24 fd ff ff call 804901a <strings_not_equal>
我们知道可以通过在第4关输入"24 2 DrEvil"作为通关密钥的同时,开启隐藏关卡。
进入调试阶段,提醒我们已经进入隐藏关卡
08048f0a <secret_phase>:
8048f0a: 53 push %ebx
8048f0b: 83 ec 18 sub $0x18,%esp
8048f0e: e8 89 02 00 00 call 804919c <read_line>
8048f13: c7 44 24 08 0a 00 00 movl $0xa,0x8(%esp) #按十进制转换
8048f1a: 00
8048f1b: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp)#将字符串要保存到的地址设置为空地址
8048f22: 00
8048f23: 89 04 24 mov %eax,(%esp)#要转换的字符串是标准输入进来的,为0x6e
8048f26: e8 a5 f9 ff ff call 80488d0 <strtol@plt>
8048f2b: 89 c3 mov %eax,%ebx #保存该转换后的数字
8048f2d: 8d 40 ff lea -0x1(%eax),%eax #将该数字-=1
8048f30: 3d e8 03 00 00 cmp $0x3e8,%eax
8048f35: 76 05 jbe 8048f3c <secret_phase+0x32>
8048f37: e8 e9 01 00 00 call 8049125 <explode_bomb>
8048f3c: 89 5c 24 04 mov %ebx,0x4(%esp)
8048f40: c7 04 24 88 c0 04 08 movl $0x804c088,(%esp)//存放根的值
8048f47: e8 6d ff ff ff call 8048eb9 <fun7>
8048f4c: 83 f8 05 cmp $0x5,%eax
//如果<fun7>的返回值%eax如果等于5我们就能成功拆除炸弹
8048f4f: 74 05 je 8048f56 <secret_phase+0x4c>
8048f51: e8 cf 01 00 00 call 8049125 <explode_bomb>
8048f56: c7 04 24 74 a1 04 08 movl $0x804a174,(%esp)
8048f5d: e8 8e f8 ff ff call 80487f0 <puts@plt>
8048f62: e8 2f 03 00 00 call 8049296 <phase_defused>
8048f67: 83 c4 18 add $0x18,%esp
8048f6a: 5b pop %ebx
8048f6b: c3 ret
8048f6c: 66 90 xchg %ax,%ax
8048f6e: 66 90 xchg %ax,%ax
8048f0e: e8 89 02 00 00 call 804919c <read_line>
8048f13: c7 44 24 08 0a 00 00 movl $0xa,0x8(%esp) #按十进制转换
8048f1a: 00
8048f1b: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp)#将字符串要保存到的地址设置为空地址
8048f22: 00
8048f23: 89 04 24 mov %eax,(%esp)#要转换的字符串是标准输入进来的
输入的值通过 x/4bx,查到是0x6e
8048f40: c7 04 24 88 c0 04 08 movl $0x804c088,(%esp)//里面的值是0x24
8048f47: e8 6d ff ff ff call 8048eb9 <fun7>
8048f4c: 83 f8 05 cmp $0x5,%eax
//如果<fun7>的返回值%eax如果等于5我们就能成功拆除炸弹
08048eb9 <fun7>:
8048eb9: 53 push %ebx
8048eba: 83 ec 18 sub $0x18,%esp
8048ebd: 8b 54 24 20 mov 0x20(%esp),%edx//edx = 根节点的地址
8048ec1: 8b 4c 24 24 mov 0x24(%esp),%ecx//ecx = 输入参数
8048ec5: 85 d2 test %edx,%edx//根节点为空时返回-1
8048ec7: 74 37 je 8048f00 <fun7+0x47>
8048ec9: 8b 1a mov (%edx),%ebx//ebx = 根节点的值
8048ecb: 39 cb cmp %ecx,%ebx# if 根节点值 <= 参数
8048ecd: 7e 13 jle 8048ee2 <fun7+0x29># 进入右子树
8048ecf: 89 4c 24 04 mov %ecx,0x4(%esp) # 否则进入左子树
8048ed3: 8b 42 04 mov 0x4(%edx),%eax
8048ed6: 89 04 24 mov %eax,(%esp)
8048ed9: e8 db ff ff ff call 8048eb9 <fun7>
8048ede: 01 c0 add %eax,%eax
8048ee0: eb 23 jmp 8048f05 <fun7+0x4c>
8048ee2: b8 00 00 00 00 mov $0x0,%eax
8048ee7: 39 cb cmp %ecx,%ebxl
8048ee9: 74 1a je 8048f05 <fun7+0x4c>
8048eeb: 89 4c 24 04 mov %ecx,0x4(%esp)
8048eef: 8b 42 08 mov 0x8(%edx),%eax
8048ef2: 89 04 24 mov %eax,(%esp)
8048ef5: e8 bf ff ff ff call 8048eb9 <fun7>
8048efa: 8d 44 00 01 lea 0x1(%eax,%eax,1),%eax
8048efe: eb 05 jmp 8048f05 <fun7+0x4c>
8048f00: b8 ff ff ff ff mov $0xffffffff,%eax
8048f05: 83 c4 18 add $0x18,%esp
8048f08: 5b pop %ebx
8048f09: c3 ret
将func7翻译成c语言
int fun7(int *address, int number) {
if (address == null)
return -1;
if (*address <= number) {
if (*address == number )
return 0;
else
2 * fun7(*(address + 8), number) + 1;
} else
2 * fun7(*(address + 4), number);
}
因为返回值要为5,推导:0->1->2->5,地址+8跳转到2 * fun7((address + 8), number) + 1;地址+4跳转到2 * fun7((address + 4), number);
所以地址变化:加8、加4、加8,依次查看(向右,向左,向右
用的是 08048f0a <secret_phase>:
8048f40: c7 04 24 88 c0 04 08 movl $0x804c088,(%esp)//里面的值是0x24
从0x804c088开始找
分析其为二叉树,画出二叉树
加8、加4、加8,依次查看(向右,向左,向右
最后答案是0x2f