bomb1:
08048ae0 <phase_1>:
8048ae0: 55 push %ebp
8048ae1: 89 e5 mov %esp,%ebp //压栈操作
8048ae3: 83 ec 18 sub $0x18,%esp //开辟栈空间(24个字节)
8048ae6: c7 44 24 04 48 a1 04 movl $0x804a148,0x4(%esp)//向栈中存储0x804a148处的数据
8048aed: 08
8048aee: 8b 45 08 mov 0x8(%ebp),%eax//将栈帧中数据传入eax(写入参数)
8048af1: 89 04 24 mov %eax,(%esp) //进栈
8048af4: e8 59 04 00 00 call 8048f52 <strings_not_equal>//调用函数
8048af9: 85 c0 test %eax,%eax //进行匹配比较
8048afb: 74 05 je 8048b02 <phase_1+0x22> //跳转到phase_2
8048afd: e8 73 06 00 00 call 8049175 <explode_bomb> //引爆炸弹
8048b02: c9 leave //释放局部变量
8048b03: c3 ret //返回函数调用
通过call可知在phase_1中调用了 strings_not_equal() 函数,其作用为判断字符串是否匹配。可知0x804a148处的字符串应该为被匹配的字符串,eax中为传入的参数,即0x804a148处字符串为该题答案。
输入查看eax中test之后为0,所以答案正确!
bomb2:
08048b04 <phase_2>:
8048b04: 55 push %ebp
8048b05: 89 e5 mov %esp,%ebp //压栈
8048b07: 56 push %esi
8048b08: 53 push %ebx //esi,ebx进栈
8048b09: 83 ec 30 sub $0x30,%esp //开辟栈空间(48字节)
8048b0c: 8d 45 e0 lea -0x20(%ebp),%eax //取地址(ebp-0x20)到eax
8048b0f: 89 44 24 04 mov %eax,0x4(%esp) //将eax中内容(地址)存入栈
8048b13: 8b 45 08 mov 0x8(%ebp),%eax //ebp+8处的元素存入eax
8048b16: 89 04 24 mov %eax,(%esp) //存入栈中
8048b19: e8 99 06 00 00 call 80491b7 <read_six_numbers>//调用函数(读6个数字)
8048b1e: 83 7d e0 31 cmpl $0x31,-0x20(%ebp) //比较0x31和ebp-0x20处的值进行比较【第一个数字a[0]放在了ebp-0x20】
8048b22: 7f 20 jg 8048b44 <phase_2+0x40> //若大于,跳转到8048b44(进入循环体),即第一个数据大于0x31
8048b24: e8 4c 06 00 00 call 8049175 <explode_bomb> //引爆炸弹
8048b29: eb 19 jmp 8048b44 <phase_2+0x40> //无条件跳转到8048b44(跳转进入循环体)
8048b2b: 8b 43 fc mov -0x4(%ebx),%eax //前一个参数放入eax
8048b2e: 8d 44 00 01 lea 0x1(%eax,%eax,1),%eax//取eax+eax*1+1放入eax
8048b32: 39 03 cmp %eax,(%ebx) //比较a[0]和eax中数据
//【a[i]=2*a[i-1]+1】
8048b34: 74 05 je 8048b3b <phase_2+0x37> //等于跳转到8048b3b
8048b36: e8 3a 06 00 00 call 8049175 <explode_bomb> //则引爆炸弹
8048b3b: 83 c3 04 add $0x4,%ebx //ebx=ebx+4
8048b3e: 39 f3 cmp %esi,%ebx //比较esi和ebx
8048b40: 75 e9 jne 8048b2b <phase_2+0x27>//不相等,跳转到8048b2b
8048b42: eb 08 jmp 8048b4c <phase_2+0x48> //无条件跳转到8048b4c
8048b44: 8d 5d e4 lea -0x1c(%ebp),%ebx //(a[1]) ebp-0x1c的地址到ebx
8048b47: 8d 75 f8 lea -0x8(%ebp),%esi
8048b4a: eb df jmp 8048b2b <phase_2+0x27>//无条件跳转到8048b2b(循环)
8048b4c: 83 c4 30 add $0x30,%esp//esp+0x30
8048b4f: 5b pop %ebx
8048b50: 5e pop %esi
8048b51: 5d pop %ebp
8048b52: c3 ret
080491b7 <read_six_numbers>:
80491b7: 55 push %ebp
80491b8: 89 e5 mov %esp,%ebp //压栈
80491ba: 83 ec 28 sub $0x28,%esp //栈指针向下移动40个字节
80491bd: 8b 45 0c mov 0xc(%ebp),%eax //数组a地址存入eax
80491c0: 8d 50 14 lea 0x14(%eax),%edx //a+5
80491c3: 89 54 24 1c mov %edx,0x1c(%esp)
80491c7: 8d 50 10 lea 0x10(%eax),%edx //a+4
80491ca: 89 54 24 18 mov %edx,0x18(%esp)
80491ce: 8d 50 0c lea 0xc(%eax),%edx //a+3
80491d1: 89 54 24 14 mov %edx,0x14(%esp)
80491d5: 8d 50 08 lea 0x8(%eax),%edx //a+2
80491d8: 89 54 24 10 mov %edx,0x10(%esp)
80491dc: 8d 50 04 lea 0x4(%eax),%edx //a+1
80491df: 89 54 24 0c mov %edx,0xc(%esp)
80491e3: 89 44 24 08 mov %eax,0x8(%esp)
80491e7: c7 44 24 04 e5 a3 04 movl $0x804a3e5,0x4(%esp)//a所在地址
80491ee: 08
80491ef: 8b 45 08 mov 0x8(%ebp),%eax
80491f2: 89 04 24 mov %eax,(%esp)
80491f5: e8 e6 f5 ff ff call 80487e0 <__isoc99_sscanf@plt> //调用sscanf
80491fa: 83 f8 05 cmp $0x5,%eax //sscanf从input中读取的数据个数
80491fd: 7f 05 jg 8049204 <read_six_numbers+0x4d>//5大于输入,跳转到8049204
80491ff: e8 71 ff ff ff call 8049175 <explode_bomb> //小于等于5,爆炸
8049204: c9 leave
8049205: c3 ret
入栈eax,ebx 可以看出是两个地址,而 read_six_numbers 可以看出这个关卡的答案是 6 个数字。
phase_2汇编到调用函数之前为以下状况,由此可知 read_six_number 的参数除输入的内容外,还有一个 ebp-0x20,结合后面代码以及考虑到栈中还有很多空间开辟后未使用,可推出读取的六个数字以类似数组形式存储在了 ebp-0x20 开始的连续空间中
可以看出,该题为一个循环。第一个数字要大于0x31即49(10),后续数字需要满足a[i]=(a[i]+a[i]*1)+1
所以,设定第一个数字为50,得到结果:50 101 203 407 815 1631(结果不唯一,第一个数字大于49即可)
成功解除炸弹2!
bomb3:
08048b53 <phase_3>:
8048b53: 55 push %ebp
8048b54: 89 e5 mov %esp,%ebp
8048b56: 83 ec 28 sub $0x28,%esp
8048b59: 8d 45 f0 lea -0x10(%ebp),%eax//第二个数
8048b5c: 89 44 24 0c mov %eax,0xc(%esp)
8048b60: 8d 45 f4 lea -0xc(%ebp),%eax//第一个数
8048b63: 89 44 24 08 mov %eax,0x8(%esp)
8048b67: c7 44 24 04 f1 a3 04 movl $0x804a3f1,0x4(%esp)//通过x/s查看为 %d %d
8048b6e: 08
8048b6f: 8b 45 08 mov 0x8(%ebp),%eax
8048b72: 89 04 24 mov %eax,(%esp)
8048b75: e8 66 fc ff ff call 80487e0 <__isoc99_sscanf@plt>//调用sscanf()
8048b7a: 83 f8 01 cmp $0x1,%eax
8048b7d: 7f 05 jg 8048b84 <phase_3+0x31>//函数输入参数个数大于1
8048b7f: e8 f1 05 00 00 call 8049175 <explode_bomb>//不然爆炸
8048b84: 83 7d f4 07 cmpl $0x7,-0xc(%ebp)
8048b88: 77 1f ja 8048ba9 <phase_3+0x56>//大于7爆炸,-0xc(%ebp)数不超过7
8048b8a: 8b 45 f4 mov -0xc(%ebp),%eax
8048b8d: ff 24 85 c0 a1 04 08 jmp *0x804a1c0(,%eax,4)//跳转到4*eax+0x804a1c0基址加比例变址->应该是一个switch跳转语句。通过x/s可得基址为98(10)
8048b94: b8 72 03 00 00 mov $0x372,%eax //0x372->eax
8048b99: eb 1f jmp 8048bba <phase_3+0x67> //跳转到8048bba
8048b9b: b8 44 02 00 00 mov $0x244,%eax
8048ba0: eb 18 jmp 8048bba <phase_3+0x67> //
8048ba2: b8 a4 03 00 00 mov $0x3a4,%eax
8048ba7: eb 11 jmp 8048bba <phase_3+0x67> //
8048ba9: e8 c7 05 00 00 call 8049175 <explode_bomb>
8048bae: b8 00 00 00 00 mov $0x0,%eax//0->eax
8048bb3: eb 30 jmp 8048be5 <phase_3+0x92>//跳转进行与操作
8048bb5: b8 16 02 00 00 mov $0x216,%eax //eax=0x216
8048bba: 83 e0 c0 and $0xffffffc0,%eax //跟踪调试为0x1f5与之与(跳转而来)
8048bbd: 3b 45 f0 cmp -0x10(%ebp),%eax //eax此时为0x1c0,查看此时的ebp-0x10中内容就是对应的第二个参数值
8048bc0: 74 28 je 8048bea <phase_3+0x97> //相等,则跳出
8048bc2: e8 ae 05 00 00 call 8049175 <explode_bomb> //不等,爆炸
8048bc7: eb 21 jmp 8048bea <phase_3+0x97> //跳出
8048bc9: b8 f5 01 00 00 mov $0x1f5,%eax
8048bce: 66 90 xchg %ax,%ax //交换操作数数据
8048bd0: eb e8 jmp 8048bba <phase_3+0x67>
8048bd2: b8 77 03 00 00 mov $0x377,%eax
8048bd7: eb e1 jmp 8048bba <phase_3+0x67>
8048bd9: b8 5c 00 00 00 mov $0x5c,%eax
8048bde: eb 05 jmp 8048be5 <phase_3+0x92>
8048be0: b8 04 01 00 00 mov $0x104,%eax
8048be5: 83 e0 7f and $0x7f,%eax
8048be8: eb d3 jmp 8048bbd <phase_3+0x6a>
8048bea: c9 leave
8048beb: c3 ret
读 movl $0x804a3f1,0x4(%esp)//通过x/s查看为 %d %d,获得读取参数
得知,密码应该是两个整数
cmpl $0x7,-0xc(%ebp) 即输入的第一个参数值必须不大于 7。然后看到 jmp *0x804a1c0(,%eax,4)这是典型的 switch 跳转语句,即跳转到以地址 * 0x804a1c0 为基址的跳转表中。用 x/s*0x804a1c0,读出 switch 跳转基值为0x98
在代码中找到该处指令,得到第一个输入为 1 时对应的第二个输入为 0x1c0, 转换成十进制为 448。经调试后结果正确。
bomb4:
08048bec <func4>:
8048bec: 55 push %ebp
8048bed: 89 e5 mov %esp,%ebp
8048bef: 56 push %esi //函数内临时变量1
8048bf0: 53 push %ebx //函数内临时变量2
8048bf1: 83 ec 10 sub $0x10,%esp //开辟空间
8048bf4: 8b 55 08 mov 0x8(%ebp),%edx //edx->a
8048bf7: 8b 45 0c mov 0xc(%ebp),%eax
8048bfa: 8b 5d 10 mov 0x10(%ebp),%ebx //ebx->b
8048bfd: 89 d9 mov %ebx,%ecx //ecx->c
8048bff: 29 c1 sub %eax,%ecx // x=c-b (用x代替ebx)*/
8048c01: 89 ce mov %ecx,%esi //
8048c03: c1 ee 1f shr $0x1f,%esi //
8048c06: 01 f1 add %esi,%ecx //
8048c08: d1 f9 sar %ecx //x = (x>>31 + x) >> 1
8048c0a: 01 c1 add %eax,%ecx //x=[(x>>31 + x) >> 1]+b
8048c0c: 39 d1 cmp %edx,%ecx //a和x比较
8048c0e: 7e 17 jle 8048c27 <func4+0x3b>//a<=x跳转.。。。。。。。。
8048c10: 83 e9 01 sub $0x1,%ecx
8048c13: 89 4c 24 08 mov %ecx,0x8(%esp) //ecx->c
8048c17: 89 44 24 04 mov %eax,0x4(%esp)
8048c1b: 89 14 24 mov %edx,(%esp) //edx->a
8048c1e: e8 c9 ff ff ff call 8048bec <func4>//调用自身(递归)
8048c23: 01 c0 add %eax,%eax
8048c25: eb 20 jmp 8048c47 <func4+0x5b>
8048c27: b8 00 00 00 00 mov $0x0,%eax //eax为返回值
8048c2c: 39 d1 cmp %edx,%ecx //a和x比较
8048c2e: 7d 17 jge 8048c47 <func4+0x5b>//a>=转移
8048c30: 89 5c 24 08 mov %ebx,0x8(%esp)
8048c34: 83 c1 01 add $0x1,%ecx
8048c37: 89 4c 24 04 mov %ecx,0x4(%esp)
8048c3b: 89 14 24 mov %edx,(%esp)
8048c3e: e8 a9 ff ff ff call 8048bec <func4>//调用自身(递归)
8048c43: 8d 44 00 01 lea 0x1(%eax,%eax,1),%eax
8048c47: 83 c4 10 add $0x10,%esp
8048c4a: 5b pop %ebx
8048c4b: 5e pop %esi
8048c4c: 5d pop %ebp
8048c4d: c3 ret
08048c4e <phase_4>:
8048c4e: 55 push %ebp
8048c4f: 89 e5 mov %esp,%ebp //压栈
8048c51: 83 ec 28 sub $0x28,%esp //开辟空间
8048c54: 8d 45 f0 lea -0x10(%ebp),%eax 参数num2
8048c57: 89 44 24 0c mov %eax,0xc(%esp) //& [ebp-0x10]->栈
8048c5b: 8d 45 f4 lea -0xc(%ebp),%eax 参数num1
8048c5e: 89 44 24 08 mov %eax,0x8(%esp) //& [ebp-0xc]->栈
8048c62: c7 44 24 04 f1 a3 04 movl $0x804a3f1,0x4(%esp)// %d,%d
8048c69: 08
8048c6a: 8b 45 08 mov 0x8(%ebp),%eax
8048c6d: 89 04 24 mov %eax,(%esp)// [val1]->栈
8048c70: e8 6b fb ff ff call 80487e0 <__isoc99_sscanf@plt>//调用sscanf()
8048c75: 83 f8 02 cmp $0x2,%eax //eax中为参数个数
8048c78: 75 06 jne 8048c80 <phase_4+0x32>//不等于2跳转爆炸
8048c7a: 83 7d f4 0e cmpl $0xe,-0xc(%ebp)
8048c7e: 76 05 jbe 8048c85 <phase_4+0x37>// val1<=0xc
8048c80: e8 f0 04 00 00 call 8049175 <explode_bomb>
8048c85: c7 44 24 08 0e 00 00 movl $0xe,0x8(%esp) //func4一个函数参数
8048c8c: 00
8048c8d: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) //func4一个函数参数
8048c94: 00
8048c95: 8b 45 f4 mov -0xc(%ebp),%eax//phase_4返回值->eax
8048c98: 89 04 24 mov %eax,(%esp) eax->地址为esp的寄存器
8048c9b: e8 4c ff ff ff call 8048bec <func4> //调用函数func4
8048ca0: 83 f8 04 cmp $0x4,%eax //eax中为0x4:func4返回值为0x4
8048ca3: 75 06 jne 8048cab <phase_4+0x5d> //不等跳转爆炸
8048ca5: 83 7d f0 04 cmpl $0x4,-0x10(%ebp) 参数必须为0x4才能结束
8048ca9: 74 05 je 8048cb0 <phase_4+0x62>
8048cab: e8 c5 04 00 00 call 8049175 <explode_bomb>
8048cb0: c9 leave
8048cb1: c3 ret
读 movl $0x804a3f1,0x4(%esp)//通过x/s查看为 %d %d,获得读取参数为2个
先从我们的输入中获取两个 int 型整数。我们命令为 num1 和 num2。接着判断 sscanf 函数的返回值,如果不等于 2 (输入为2个数)则触发炸弹,否则继续。接下来的几条指令看出[cmpl $0xe,-0xc(%ebp)],num1 必须小于等于 12,否则触发炸弹
接下来调用函数 func4,参数分别为 num1,0 和 14。
cmp $0x4,%eax检验函数func4()返回值应该为4. cmpl $0x4,-0x10(%ebp)表明num2应该为0x4.
再关注func4:
可以看到func()中有调用自己,所以为递归函数 且func4 的原型应该是 int func4(int a, int b, int c);根据计算结果,反推c语言代码如下:
要其返回值为4,所以可以得到num1应该为2
所以该题结果为2 4
bomb5:
08048cb2 <phase_5>:
8048cb2: 55 push %ebp
8048cb3: 89 e5 mov %esp,%ebp
8048cb5: 53 push %ebx
8048cb6: 83 ec 14 sub $0x14,%esp
8048cb9: 8b 5d 08 mov 0x8(%ebp),%ebx//输入的数num1
8048cbc: 89 1c 24 mov %ebx,(%esp)
8048cbf: e8 6c 02 00 00 call 8048f30 <string_length>//猜测字符串长度
8048cc4: 83 f8 06 cmp $0x6,%eax
8048cc7: 74 05 je 8048cce <phase_5+0x1c> //字符串长度为6
8048cc9: e8 a7 04 00 00 call 8049175 <explode_bomb>
8048cce: ba 00 00 00 00 mov $0x0,%edx
8048cd3: b8 00 00 00 00 mov $0x0,%eax
8048cd8: 0f b6 0c 03 movzbl (%ebx,%eax,1),%ecx //一个字节->双字 ebx+eax->ecx【对num1进行扩展为32位】
8048cdc: 83 e1 0f and $0xf,%ecx //保留后四位,前28位变为0
8048cdf: 03 14 8d e0 a1 04 08 add 0x804a1e0(,%ecx,4),%edx //基址+寄存器寻址
8048ce6: 83 c0 01 add $0x1,%eax //猜测eax为字符串数组的下标
8048ce9: 83 f8 06 cmp $0x6,%eax (循环6次)
8048cec: 75 ea jne 8048cd8 <phase_5+0x26>
8048cee: 83 fa 2e cmp $0x2e,%edx//返回值要为0x2e即46
8048cf1: 74 05 je 8048cf8 <phase_5+0x46>
8048cf3: e8 7d 04 00 00 call 8049175 <explode_bomb>
8048cf8: 83 c4 14 add $0x14,%esp
8048cfb: 5b pop %ebx
8048cfc: 5d pop %ebp
8048cfd: c3 ret
由题意可以得知为一个6个字符的字符串
循环段读取含义为分别截取每个字符的后四位放入ecx,将其作为偏移量,将0x804a1e0+(ecx*4)加入edx。6次循环后,edx中的值应该等于0x2e,即(46)10。
查看0x804a1e0开始的连续字符(32位)
可得到当选中如下数据最后和为0x2e。则偏移量ecx应该分别为(0x):1 2 4 6 8 B.即选取字符串的6个字符ascii码的二进制编码后4位需要满足上面的关系。所以,取a(0x61),b(0x62),d(0x64),f(0x66),h(0x68),k(0x6B)
运行检验运行,答案正确。
bomb6:
08048cfe <phase_6>:
8048cfe: 55 push %ebp
8048cff: 89 e5 mov %esp,%ebp
8048d01: 56 push %esi
8048d02: 53 push %ebx
8048d03: 83 ec 40 sub $0x40,%esp
8048d06: 8d 45 e0 lea -0x20(%ebp),%eax /eax=num/
8048d09: 89 44 24 04 mov %eax,0x4(%esp)
8048d0d: 8b 45 08 mov 0x8(%ebp),%eax
8048d10: 89 04 24 mov %eax,(%esp)
8048d13: e8 9f 04 00 00 call 80491b7 <read_six_numbers> //读6个数
8048d18: be 00 00 00 00 mov $0x0,%esi
8048d1d: 8b 44 b5 e0 mov -0x20(%ebp,%esi,4),%eax // a[esi]->eax
8048d21: 83 e8 01 sub $0x1,%eax //eax=a[i]-1
8048d24: 83 f8 05 cmp $0x5,%eax
8048d27: 76 05 jbe 8048d2e <phase_6+0x30>
8048d29: e8 47 04 00 00 call 8049175 <explode_bomb> //保证num[esi]<=5
8048d2e: 83 c6 01 add $0x1,%esi
8048d31: 83 fe 06 cmp $0x6,%esi //esi为计数器(循环6次)
8048d34: 74 1c je 8048d52 <phase_6+0x54>
8048d36: 89 f3 mov %esi,%ebx
8048d38: 8b 44 9d e0 mov -0x20(%ebp,%ebx,4),%eax //ebx=esi+1,eax=a[ebx]
8048d3c: 39 44 b5 dc cmp %eax,-0x24(%ebp,%esi,4)
8048d40: 75 05 jne 8048d47 <phase_6+0x49>
这里控制6个数据都互不相同(对所有进行扫描保证不同) |
8048d42: e8 2e 04 00 00 call 8049175 <explode_bomb>//确保num[ebx]!=num[esi]即两个相邻的数不相等
8048d47: 83 c3 01 add $0x1,%ebx
8048d4a: 83 fb 05 cmp $0x5,%ebx
8048d4d: 7e e9 jle 8048d38 <phase_6+0x3a>
8048d4f: 90 nop
8048d50: eb cb jmp 8048d1d <phase_6+0x1f>
8048d52: 8d 45 e0 lea -0x20(%ebp),%eax /eax=num/
8048d55: 8d 5d f8 lea -0x8(%ebp),%ebx /ebx=p/
8048d58: b9 07 00 00 00 mov $0x7,%ecx //ecx=7
8048d5d: 89 ca mov %ecx,%edx
8048d5f: 2b 10 sub (%eax),%edx
8048d61: 89 10 mov %edx,(%eax)
8048d63: 83 c0 04 add $0x4,%eax //eax=p-num+0x4
8048d66: 39 d8 cmp %ebx,%eax
8048d68: 75 f3 jne 8048d5d <phase_6+0x5f> //p(ebx)不指向eax继续计算
8048d6a: bb 00 00 00 00 mov $0x0,%ebx
8048d6f: eb 1d jmp 8048d8e <phase_6+0x90> //p(ebx)指向eax
8048d71: 8b 52 08 mov 0x8(%edx),%edx
8048d74: 83 c0 01 add $0x1,%eax
8048d77: 39 c8 cmp %ecx,%eax ecp->指向其next
8048d79: 75 f6 jne 8048d71 <phase_6+0x73> //不等重新计算
8048d7b: eb 05 jmp 8048d82 <phase_6+0x84> //等于则跳转
8048d7d: ba 54 c1 04 08 mov $0x804c154,%edx
8048d82: 89 54 b5 c8 mov %edx,-0x38(%ebp,%esi,4)
8048d86: 83 c3 01 add $0x1,%ebx //ebx+=1
8048d89: 83 fb 06 cmp $0x6,%ebx
8048d8c: 74 17 je 8048da5 <phase_6+0xa7>//6个元素之间链表建立成功,则跳转下一步。
8048d8e: 89 de mov %ebx,%esi
8048d90: 8b 4c 9d e0 mov -0x20(%ebp,%ebx,4),%ecx /ecx=p->next /
8048d94: 83 f9 01 cmp $0x1,%ecx
这里为对6个元素建立链表部分 |
8048d97: 7e e4 jle 8048d7d <phase_6+0x7f> //指向不正确,重新计算
8048d99: b8 01 00 00 00 mov $0x1,%eax
8048d9e: ba 54 c1 04 08 mov $0x804c154,%edx
8048da3: eb cc jmp 8048d71 <phase_6+0x73>
8048da5: 8b 5d c8 mov -0x38(%ebp),%ebx
8048da8: 8d 45 cc lea -0x34(%ebp),%eax
8048dab: 8d 75 e0 lea -0x20(%ebp),%esi
8048dae: 89 d9 mov %ebx,%ecx
8048db0: 8b 10 mov (%eax),%edx
8048db2: 89 51 08 mov %edx,0x8(%ecx)
8048db5: 83 c0 04 add $0x4,%eax
8048db8: 39 f0 cmp %esi,%eax
8048dba: 74 04 je 8048dc0 <phase_6+0xc2>
8048dbc: 89 d1 mov %edx,%ecx
8048dbe: eb f0 jmp 8048db0 <phase_6+0xb2>
8048dc0: c7 42 08 00 00 00 00 movl $0x0,0x8(%edx)
8048dc7: be 05 00 00 00 mov $0x5,%esi
8048dcc: 8b 43 08 mov 0x8(%ebx),%eax eax=ebx->next
8048dcf: 8b 00 mov (%eax),%eax
8048dd1: 39 03 cmp %eax,(%ebx) 比较eax和ebx->value
8048dd3: 7d 05 jge 8048dda <phase_6+0xdc>
8048dd5: e8 9b 03 00 00 call 8049175 <explode_bomb>//链表元素出现升序,爆炸【即排列好的数据要完全降序排列】
8048dda: 8b 5b 08 mov 0x8(%ebx),%ebx
8048ddd: 83 ee 01 sub $0x1,%esi
8048de0: 75 ea jne 8048dcc <phase_6+0xce>
8048de2: 83 c4 40 add $0x40,%esp
8048de5: 5b pop %ebx
8048de6: 5e pop %esi
8048de7: 5d pop %ebp
8048de8: c3 ret
输入假设样例1 2 3 4 5 6
用 gdb 查看 0x804c154 附近的内容:
可以确定结构由三个元素组成, 两个整形数据, 一个结构类型的指针, 重复操作可以发现这是一个有 6 个元素的链表, 称此结构为 node
读取顺序为从后往前,此时的链表为:
node6-> node5-> node4-> node3-> node2-> node1-> NULL
typedef struct node{
int value;
int idx;
struct node *next;
}node;
node node1={0x27e, 1, NULL, 0x73};
node node2={0x73, 2, &node1, 0x3d3};
node node3={0x3d3, 3, &node2, 0x1ab};
node node4={0x1ab, 4, &node3, 0x255};
node node5={0x255, 5, &node4, 0x248};
node node6={0x248, 6, &node5, 0x00};
分析代码为:对其按照value值0x3d3,0x255,0x27e,0x1ab,0x248,0x73由大到小排序,重新链接得到链表如下,C语言代码如下:
node3-> node1-> node5-> node6-> node4-> node2-> NULL
从后往前找最大,放在加入链表,形成降序排列。
从6--à1找,最大的0x3d3为最大,是第四个,次大之0x27e为第6个。。。。以此类推
所以,num=4 6 2 1 3 5
隐藏关:
进入方法:
搜索整个汇编代码, 发现只在 phase_defuse 函数中调用过 secret_phase 函数, 所以先分析这个函数.
0804933e <phase_defused>:
804933e: 55 push %ebp
804933f: 89 e5 mov %esp,%ebp
8049341: 81 ec 88 00 00 00 sub $0x88,%esp
8049347: c7 04 24 01 00 00 00 movl $0x1,(%esp)
804934e: e8 4b fd ff ff call 804909e <send_msg>
8049353: 83 3d e8 c7 04 08 06 cmpl $0x6,0x804c7e8 //
804935a: 75 7a jne 80493d6 <phase_defused+0x98>
804935c: 8d 45 a8 lea -0x58(%ebp),%eax
804935f: 89 44 24 10 mov %eax,0x10(%esp)
8049363: 8d 45 a0 lea -0x60(%ebp),%eax
8049366: 89 44 24 0c mov %eax,0xc(%esp)
804936a: 8d 45 a4 lea -0x5c(%ebp),%eax
804936d: 89 44 24 08 mov %eax,0x8(%esp)
8049371: c7 44 24 04 4b a4 04 movl $0x804a44b,0x4(%esp)
8049378: 08
8049379: c7 04 24 f0 c8 04 08 movl $0x804c8f0,(%esp)
8049380: e8 5b f4 ff ff call 80487e0 <__isoc99_sscanf@plt>
8049385: 83 f8 03 cmp $0x3,%eax
8049388: 75 34 jne 80493be <phase_defused+0x80>
804938a: c7 44 24 04 54 a4 04 movl $0x804a454,0x4(%esp)
8049391: 08
8049392: 8d 45 a8 lea -0x58(%ebp),%eax
8049395: 89 04 24 mov %eax,(%esp)
8049398: e8 b5 fb ff ff call 8048f52 <strings_not_equal>
804939d: 85 c0 test %eax,%eax
804939f: 75 1d jne 80493be <phase_defused+0x80>//不匹配跳过隐藏关
80493a1: c7 04 24 a0 a2 04 08 movl $0x804a2a0,(%esp)
80493a8: e8 d3 f3 ff ff call 8048780 <puts@plt>
80493ad: c7 04 24 c8 a2 04 08 movl $0x804a2c8,(%esp)
80493b4: e8 c7 f3 ff ff call 8048780 <puts@plt>
80493b9: e8 7e fa ff ff call 8048e3c <secret_phase>
80493be: c7 04 24 00 a3 04 08 movl $0x804a300,(%esp)
80493c5: e8 b6 f3 ff ff call 8048780 <puts@plt>
80493ca: c7 04 24 2c a3 04 08 movl $0x804a32c,(%esp)
80493d1: e8 aa f3 ff ff call 8048780 <puts@plt>
80493d6: c9 leave
80493d7: c3 ret
80493d8: 66 90 xchg %ax,%ax
80493da: 66 90 xchg %ax,%ax
80493dc: 66 90 xchg %ax,%ax
80493de: 66 90 xchg %ax,%ax
在 phase_defused 函数中有 cmpl $0x6,0x804c7e8 语句, 搜索 0x804c7e8 发现这个内存的值在每读入一条字符串后就会加 1,
则显然 secret_phase 是在第 6 条字符串之后输入. 又发现 cmp $0x3,%eax, 语句, 此时的 %eax 是 sscanf 读取的参数个数。sscanf 读取的参数格式保存在0x804a44b 位置处, 为 "%d %d %s", 待读取的参数存放在 0x804c8f0 内存处,
用 x/s 0x804b770 看时发现这个内存位置的字符串为 " ", 什么都没有, 从后面的调用
strings_not_equal 来看, 还要读入的字符保存在0x804a454 中, 查看为DrEvil
查看0x804a2a0发现提示:
之后在前面结果中插入DrEvil测试,发现在phase_4这样就可以正确执行到 secret_phase 函数调用位置处了.
08048e3c <secret_phase>:
8048e3c: 55 push %ebp
8048e3d: 89 e5 mov %esp,%ebp
8048e3f: 53 push %ebx
8048e40: 83 ec 14 sub $0x14,%esp
8048e43: e8 be 03 00 00 call 8049206 <read_line>//读取一行
8048e48: c7 44 24 08 0a 00 00 movl $0xa,0x8(%esp)
8048e4f: 00
8048e50: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp)
8048e57: 00
8048e58: 89 04 24 mov %eax,(%esp) //eax为read_line()返回值
8048e5b: e8 e0 f9 ff ff call 8048840 <strtol@plt>//返回值 %eax 作为函数 <strtol@plt> 的参数之一,另外两个参数分别是 0xa 和 0x0
8048e60: 89 c3 mov %eax,%ebx
8048e62: 8d 40 ff lea -0x1(%eax),%eax /*
8048e65: 3d e8 03 00 00 cmp $0x3e8,%eax
8048e6a: 76 05 jbe 8048e71 <secret_phase+0x35>
8048e6c: e8 04 03 00 00 call 8049175 <explode_bomb>输入的十进制数要小于等于 1001*/
8048e71: 89 5c 24 04 mov %ebx,0x4(%esp)
8048e75: c7 04 24 a0 c0 04 08 movl $0x804c0a0,(%esp)
8048e7c: e8 68 ff ff ff call 8048de9 <fun7>
//随后将所输入的数作为 <fun7> 的参数之一。另外一个参数来自 0x804c178,查看为 0x55。
8048e81: 83 f8 05 cmp $0x5,%eax
8048e84: 74 05 je 8048e8b <secret_phase+0x4f>
8048e86: e8 ea 02 00 00 call 8049175 <explode_bomb>
8048e8b: c7 04 24 94 a1 04 08 movl $0x804a194,(%esp)
8048e92: e8 e9 f8 ff ff call 8048780 <puts@plt>
8048e97: e8 a2 04 00 00 call 804933e <phase_defused>
8048e9c: 83 c4 14 add $0x14,%esp
8048e9f: 5b pop %ebx
8048ea0: 5d pop %ebp
8048ea1: c3 ret
8048ea2: 66 90 xchg %ax,%ax
8048ea4: 66 90 xchg %ax,%ax
8048ea6: 66 90 xchg %ax,%ax
8048ea8: 66 90 xchg %ax,%ax
8048eaa: 66 90 xchg %ax,%ax
8048eac: 66 90 xchg %ax,%ax
8048eae: 66 90 xchg %ax,%ax
08048de9 <fun7>:
8048de9: 55 push %ebp
8048dea: 89 e5 mov %esp,%ebp
8048dec: 53 push %ebx
8048ded: 83 ec 14 sub $0x14,%esp
8048df0: 8b 55 08 mov 0x8(%ebp),%edx // 第一个参数 A
8048df3: 8b 4d 0c mov 0xc(%ebp),%ecx // 第二个参数 B 即输入
8048df6: 85 d2 test %edx,%edx
8048df8: 74 37 je 8048e31 <fun7+0x48>// 递归终止,返回 %edx=0
8048dfa: 8b 1a mov (%edx),%ebx
8048dfc: 39 cb cmp %ecx,%ebx
8048dfe: 7e 13 jle 8048e13 <fun7+0x2a>// 若 * A>b,将 (A+4) 作为地址进入递归
8048e00: 89 4c 24 04 mov %ecx,0x4(%esp)
8048e04: 8b 42 04 mov 0x4(%edx),%eax
8048e07: 89 04 24 mov %eax,(%esp)
8048e0a: e8 da ff ff ff call 8048de9 <fun7>//递归
8048e0f: 01 c0 add %eax,%eax //递归返回值加倍
8048e11: eb 23 jmp 8048e36 <fun7+0x4d>
8048e13: b8 00 00 00 00 mov $0x0,%eax
8048e18: 39 cb cmp %ecx,%ebx
8048e1a: 74 1a je 8048e36 <fun7+0x4d>// 若 * A<B,将 (A+8) 作为地址进入递归
8048e1c: 89 4c 24 04 mov %ecx,0x4(%esp)
8048e20: 8b 42 08 mov 0x8(%edx),%eax
8048e23: 89 04 24 mov %eax,(%esp)
8048e26: e8 be ff ff ff call 8048de9 <fun7>
// 在此处将递归返回值加倍后在加 1
8048e2b: 8d 44 00 01 lea 0x1(%eax,%eax,1),%eax
8048e2f: eb 05 jmp 8048e36 <fun7+0x4d>
8048e31: b8 ff ff ff ff mov $0xffffffff,%eax
8048e36: 83 c4 14 add $0x14,%esp
8048e39: 5b pop %ebx
8048e3a: 5d pop %ebp
8048e3b: c3 ret
在调用完 <fun7> 之后,紧跟着 cmp $0x5,%eax,即返回值必须为 5。<fun7> 分析如上,为递归函数,与第四题十分相似。递归最深处的返回值肯定为 0,最外层返回值为 5,可得出如下反递归过程:
A*2+1=5 - ->A=2 即有 * A<B
A*2=2 -->A=1 有 * A>B
A*2+1=1 - ->A=0 即有 * A<B
也就是说在这三次递归中两次执行了 “若 * A<B 将(A+8) 作为地址进入递归”系列代码,一次执行了 “若 * A>b,将(A+4) 作为地址进入递归”系列代码。使用 gdb 查询储存值:
最后得到 0x2f,即使我们要输入的十进制值 47。
运行结果,找出了所有关的答案: