前情回顾
上回说到了前4题,接下来说phase_4
phase_4递归
本来以为网上的很多教程都有讲解,实在不行看看别人的教程,可是、、、看了好久也没有和我的一样的题型,本题我认为是最难的一个,没有之一。好了,回顾一下心酸的历程。
08049660 <func4>:
8049660: 55 push %ebp
8049661: 89 e5 mov %esp,%ebp
8049663: 83 ec 18 sub $0x18,%esp
8049666: 8b 55 0c mov 0xc(%ebp),%edx
8049669: 8b 45 10 mov 0x10(%ebp),%eax
804966c: 01 d0 add %edx,%eax
804966e: 89 c2 mov %eax,%edx
8049670: c1 ea 1f shr $0x1f,%edx
8049673: 01 d0 add %edx,%eax
8049675: d1 f8 sar %eax
8049677: 89 45 f4 mov %eax,-0xc(%ebp)
804967a: 8b 45 0c mov 0xc(%ebp),%eax
804967d: 3b 45 10 cmp 0x10(%ebp),%eax
8049680: 7c 13 jl 8049695 <func4+0x35>
8049682: 8b 45 10 mov 0x10(%ebp),%eax
8049685: 8d 14 85 00 00 00 00 lea 0x0(,%eax,4),%edx
804968c: 8b 45 08 mov 0x8(%ebp),%eax
804968f: 01 d0 add %edx,%eax
8049691: 8b 00 mov (%eax),%eax
8049693: eb 3c jmp 80496d1 <func4+0x71>
8049695: 83 ec 04 sub $0x4,%esp
8049698: ff 75 f4 pushl -0xc(%ebp)
804969b: ff 75 0c pushl 0xc(%ebp)
804969e: ff 75 08 pushl 0x8(%ebp)
80496a1: e8 ba ff ff ff call 8049660 <func4>
80496a6: 83 c4 10 add $0x10,%esp
80496a9: 89 45 f0 mov %eax,-0x10(%ebp)
80496ac: 8b 45 f4 mov -0xc(%ebp),%eax
80496af: 83 c0 01 add $0x1,%eax
80496b2: 83 ec 04 sub $0x4,%esp
80496b5: ff 75 10 pushl 0x10(%ebp)
80496b8: 50 push %eax
80496b9: ff 75 08 pushl 0x8(%ebp)
80496bc: e8 9f ff ff ff call 8049660 <func4>
80496c1: 83 c4 10 add $0x10,%esp
80496c4: 89 45 ec mov %eax,-0x14(%ebp)
80496c7: 8b 45 ec mov -0x14(%ebp),%eax
80496ca: 39 45 f0 cmp %eax,-0x10(%ebp)
80496cd: 0f 4d 45 f0 cmovge -0x10(%ebp),%eax
80496d1: c9 leave
80496d2: c3 ret
080496d3 <phase_4>:
80496d3: 55 push %ebp
80496d4: 89 e5 mov %esp,%ebp
80496d6: 57 push %edi
80496d7: 56 push %esi
80496d8: 53 push %ebx
80496d9: 81 ec bc 00 00 00 sub $0xbc,%esp
80496df: 8d 85 40 ff ff ff lea -0xc0(%ebp),%eax
80496e5: bb 40 b2 04 08 mov $0x804b240,%ebx
80496ea: ba 29 00 00 00 mov $0x29,%edx
80496ef: 89 c7 mov %eax,%edi
80496f1: 89 de mov %ebx,%esi
80496f3: 89 d1 mov %edx,%ecx
80496f5: f3 a5 rep movsl %ds:(%esi),%es:(%edi)
80496f7: 8d 85 38 ff ff ff lea -0xc8(%ebp),%eax
80496fd: 50 push %eax
80496fe: 8d 85 3c ff ff ff lea -0xc4(%ebp),%eax
8049704: 50 push %eax
8049705: 68 ff b1 04 08 push $0x804b1ff
804970a: ff 75 08 pushl 0x8(%ebp)
804970d: e8 be f9 ff ff call 80490d0 <__isoc99_sscanf@plt>
8049712: 83 c4 10 add $0x10,%esp
8049715: 89 45 e4 mov %eax,-0x1c(%ebp)
8049718: 83 7d e4 02 cmpl $0x2,-0x1c(%ebp)
804971c: 74 0f je 804972d <phase_4+0x5a>
804971e: e8 c5 07 00 00 call 8049ee8 <explode_bomb>
8049723: b8 00 00 00 00 mov $0x0,%eax
8049728: e9 bc 00 00 00 jmp 80497e9 <phase_4+0x116>
804972d: 8b 95 38 ff ff ff mov -0xc8(%ebp),%edx
8049733: 8b 85 3c ff ff ff mov -0xc4(%ebp),%eax
8049739: 83 ec 04 sub $0x4,%esp
804973c: 52 push %edx
804973d: 50 push %eax
804973e: 8d 85 40 ff ff ff lea -0xc0(%ebp),%eax
8049744: 50 push %eax
8049745: e8 16 ff ff ff call 8049660 <func4>
804974a: 83 c4 10 add $0x10,%esp
804974d: 3d e9 01 00 00 cmp $0x1e9,%eax
8049752: 74 0f je 8049763 <phase_4+0x90>
8049754: e8 8f 07 00 00 call 8049ee8 <explode_bomb>
8049759: b8 00 00 00 00 mov $0x0,%eax
804975e: e9 86 00 00 00 jmp 80497e9 <phase_4+0x116>
8049763: 8b 85 3c ff ff ff mov -0xc4(%ebp),%eax
8049769: 85 c0 test %eax,%eax
804976b: 7e 36 jle 80497a3 <phase_4+0xd0>
804976d: 8b 85 38 ff ff ff mov -0xc8(%ebp),%eax
8049773: 8b 95 3c ff ff ff mov -0xc4(%ebp),%edx
8049779: 83 ea 01 sub $0x1,%edx
804977c: 83 ec 04 sub $0x4,%esp
804977f: 50 push %eax
8049780: 52 push %edx
8049781: 8d 85 40 ff ff ff lea -0xc0(%ebp),%eax
8049787: 50 push %eax
8049788: e8 d3 fe ff ff call 8049660 <func4>
804978d: 83 c4 10 add $0x10,%esp
8049790: 3d e9 01 00 00 cmp $0x1e9,%eax
8049795: 75 0c jne 80497a3 <phase_4+0xd0>
8049797: e8 4c 07 00 00 call 8049ee8 <explode_bomb>
804979c: b8 00 00 00 00 mov $0x0,%eax
80497a1: eb 46 jmp 80497e9 <phase_4+0x116>
80497a3: 8b 85 38 ff ff ff mov -0xc8(%ebp),%eax
80497a9: 83 f8 27 cmp $0x27,%eax
80497ac: 7f 36 jg 80497e4 <phase_4+0x111>
80497ae: 8b 85 38 ff ff ff mov -0xc8(%ebp),%eax
80497b4: 8d 50 01 lea 0x1(%eax),%edx
80497b7: 8b 85 3c ff ff ff mov -0xc4(%ebp),%eax
80497bd: 83 ec 04 sub $0x4,%esp
80497c0: 52 push %edx
80497c1: 50 push %eax
80497c2: 8d 85 40 ff ff ff lea -0xc0(%ebp),%eax
80497c8: 50 push %eax
80497c9: e8 92 fe ff ff call 8049660 <func4>
80497ce: 83 c4 10 add $0x10,%esp
80497d1: 3d e9 01 00 00 cmp $0x1e9,%eax
80497d6: 75 0c jne 80497e4 <phase_4+0x111>
80497d8: e8 0b 07 00 00 call 8049ee8 <explode_bomb>
80497dd: b8 00 00 00 00 mov $0x0,%eax
80497e2: eb 05 jmp 80497e9 <phase_4+0x116>
80497e4: b8 01 00 00 00 mov $0x1,%eax
80497e9: 8d 65 f4 lea -0xc(%ebp),%esp
80497ec: 5b pop %ebx
80497ed: 5e pop %esi
80497ee: 5f pop %edi
80497ef: 5d pop %ebp
80497f0: c3 ret
当时看完发现这么长的代码已经做好了心理准备。可是没想到还是让人崩溃,哭辽。。。
80496e5: bb 40 b2 04 08 mov $0x804b240,%ebx
发现明码地址先看看情况。
啥也不是
还有一个明码地址,有点熟悉,前面几个题出现过
8049705: 68 ff b1 04 08 push $0x804b1ff
也就是说,这一题需要输入两个整数,然后调用fun4,fun4是一个递归函数,功能是需要这两个数之间满足某种联系才可以,所以需要仔细分析fun4才可以。
后来发现fun4真TMD难。
看代码我没能完整写出来,万般无奈打开了IDA,来吧一起看看
打开以后按F5调试伪代码。
于是有一下内容
首先可以确定的是,v5是判断是不是两个数,然后进行下一步操作。可以看到有三个if语句,每一个判断条件都是一个递归函数,仔细看看能看懂嘛。
好了,不能,这才是最伤心的。有伪代码都看不懂。。。。
主要是这三个参数分别是什么呢,&v4,v2 v3
再来看看fun4
看起来没有那么难啊,可是仔细一看却无从下手。
现在的目的是找到这三个参数的来源,然后看看能不能调试的时候查看一下。
答案很简单,不能
80496e5: bb 40 b2 04 08 mov $0x804b240,%ebx
80496ea: ba 29 00 00 00 mov $0x29,%edx
这两行是什么意思呢,就是说,先开辟了一段空间,然后放一个数组进去,长度是0x29也就是41.
调试看一下
这里发现ecx从41递减一直到0
然后一直调试41行才可以完成这一步
气人的是还看不到数组是什么。。。悲伤
继续往后看吧。发现有两个比较,0和39
有什么用呢?
要不胸口碎大石吧,从0和39一个一个尝试,hhhh加油,看好你(我试过了。。。。。)
好了,别想有一点的侥幸。老实的分析吧。
很容易看出
804974d: 3d e9 01 00 00 cmp $0x1e9,%eax
8049752: 74 0f je 8049763 <phase_4+0x90>
返回值要和489比较,
然后运行调试
这里我使用的第一个数是1 第二个39
有点小惊喜,第一个比较通过了,难道这就好了吗???
并没有,第二个还是会boom
至此,思路全无,洗洗睡吧,晚安
怎么说呢,这么一个一个尝试这么也不是办法,分析思路才是正道。
于是分析fun4的功能,最后写c++程序
#include<iostream>
using namespace std;
int func4(int num[], int first, int last)
{
int max_f, max_l;
int mid = (first + last) / 2;
if (first >= last)
return num[last];
//if ((first+1) == last)
// return (num[first] >= num[last])? num[first] : num[last];
max_f = func4(num, first, mid);
max_l = func4(num, mid+1, last);
return (max_f >= max_l)? max_f : max_l;
}
int main(){
int num[] = {
1,2,3,4,5,6,7,8,2,3,4,5,6,7,8,2,3,4,5,6,7,8,2,3,4,5,6,7,8,2,3,4,5,6,7,8,1,2,1,1,1};
//int phase_4(char *input) {
int first, last;
int temp;
for (int i=1;i<40;i++){
temp=i;
num[i-1]=1;
num[i]=489;
for (int k=0;k<40;k++){
first =k;
for(int j=0;j<40;j++){
last=j;
while (first>0 && func4(num,first-1,temp)==489)
first = first - 1;
last = temp;
while (last<40 && func4(num,temp,last+1)==489)
last = last + 1;
cout<<first<<"_"<<last<<"\t";
}
}
}
return 0;
}
简单介绍一下,代码里也设置了一个数组,不过是随便设置的,因为不知道那个特定的489的位置,所以用了一个for循环,一个一个暴力拆解。
然后按照题目的两个数的范围,再用两个for一个一个找。
最后程序直接输出两个答案。
总的来说,这题的功能是递归求数组最值。
答案0 40
非常艰难的解决了这一题。好了我们继续。
phase_5
080497f1 <phase_5>:
80497f1: 55 push %ebp
80497f2: 89 e5 mov %esp,%ebp
80497f4: 83 ec 18 sub $0x18,%esp
80497f7: 83 ec 0c sub $0xc,%esp
80497fa: ff 75 08 pushl 0x8(%ebp)
80497fd: e8 52 04 00 00 call 8049c54 <string_length>
8049802: 83 c4 10 add $0x10,%esp
8049805: 89 45 ec mov %eax,-0x14(%ebp)
8049808: 83 7d ec 06 cmpl $0x6,-0x14(%ebp)
//比较是不是6个字符长度
804980c: 74 0c je 804981a <phase_5+0x29>
//相等跳转
804980e: e8 d5 06 00 00 call 8049ee8 <explode_bomb>
8049813: b8 00 00 00 00 mov $0x0,%eax
8049818: eb 4c jmp 8049866 <phase_5+0x75>
804981a: c7 45 f0 00 00 00 00 movl $0x0,-0x10(%ebp)
8049821: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp)
8049828: eb 1f jmp 8049849 <phase_5+0x58>
804982a: 8b 55 f4 mov -0xc(%ebp),%edx
804982d: 8b 45 08 mov 0x8(%ebp),%eax
8049830: 01 d0 add %edx,%eax
8049832: 0f b6 00 movzbl (%eax),%eax
8049835: 0f be c0 movsbl %al,%eax
8049838: 83 e0 0f and $0xf,%eax
//按位与操作,只取最低位
804983b: 8b 04 85 40 d2 04 08 mov 0x804d240(,%eax,4),%eax
8049842: 01 45 f0 add %eax,-0x10(%ebp)
8049845: 83 45 f4 01 addl $0x1,-0xc(%ebp)
8049849: 83 7d f4 05 cmpl $0x5,-0xc(%ebp)
//将结果与5进行比较
804984d: 7e db jle 804982a <phase_5+0x39>
//小于跳转
804984f: 83 7d f0 27 cmpl $0x27,-0x10(%ebp)
8049853: 74 0c je 8049861 <phase_5+0x70>
//相等跳转
8049855: e8 8e 06 00 00 call 8049ee8 <explode_bomb>
804985a: b8 00 00 00 00 mov $0x0,%eax
804985f: eb 05 jmp 8049866 <phase_5+0x75>
8049861: b8 01 00 00 00 mov $0x1,%eax
8049866: c9 leave
8049867: c3 ret
}
这一题是字符串破解。
很有意思,但是不难。
这部分程序的功能是指针考察。该阶段使用输入字符串中字符作为数组索引进行计算,要求输入字符串满足计算结果要求。对本阶段函数的反汇编结果进行总体分析。分析针对输入字符串的处理过程。相应构造输入字符串。
重新打开终端,这次断点打在phase_5 。
本题类似于密码解密,要求输入6个字符每一个字符对应密码以后,得到满足题意的结果。movzbl (%eax),%eax对于这样的指令格式遇到不止一次了,x86/x64 寻址方式众多,什么直接寻址、间接寻址、基址寻址、基址变址寻址等等让人眼花缭乱,而 AT&T 语法对内存寻址方式做了一个很好的统一,其格式为section:displacement(base, index, scale),其中section 是段地址,displacement 是位移,base 是基址寄存器,index是索引,scale 是缩放因子,其计算方式为线性地址=section + displacement + base + index*scale,最重要的是,可以省略以上格式中的一个或多个部分,比如 movw 4, %ax 就是把内存地址 4 中的值移动到 ax 寄存器中,movw 4(%esp), %ax 就是把 esp+4 指向的地址中的值移动到 ax 寄存器中,依此类推。
解题关键是这一行
8049838: 83 e0 0f and $0xf,%eax
//按位与操作,只取最低位
要熟悉理解按位操作,才可解决这一题的核心算法。
也就是说,输入的6个字符串的每个字母的ASCII码对应的应该是一个特定的数字,然后加在一起是5.
继续进行分析以后查询ASCII表,可知第一个英文字母的小写a是97,然后进行按位与操作,只取最低位,变成1,同理bcd…等等可以分别分析出对应的数字。然后经过以下操作
804983b: 8b 04 85 40 d2 04 08 mov 0x804d240(,%eax,4),%eax
8049842: 01 45 f0 add %eax,-0x10(%ebp)
8049845: 83 45 f4 01 addl $0x1,-0xc(%ebp)
发现a对应6,c对应7,从而为了满足最后的条件,
804984f: 83 7d f0 27 cmpl $0x27,-0x10(%ebp)
即相加为39,只需要3个a,3个c即可
可以取aaaccc,并且不要求顺序。而且答案不唯一
phase_6
这部分程序的功能是考察链表/指针/结构。该阶段要求输入一串数字用以调整一个链表中各结构的顺序,使其满足特定顺序要求。对本阶段函数的反汇编结果进行总体分析。分析程序的链表处理逻辑和内置链表数据进行分析。相应构造输入数串。
重新打开终端,这次断点打在phase_6 。
// Code Block
08049868 <phase_6>:
8049868: 55 push %ebp
8049869: 89 e5 mov %esp,%ebp
804986b: 83 ec 58 sub $0x58,%esp//内存开辟
804986e: c7 45 e8 78 d1 04 08 movl $0x804d178,-0x18(%ebp)
8049875: 83 ec 04 sub $0x4,%esp
8049878: 6a 08 push $0x8//入栈
804987a: 8d 45 c8 lea -0x38(%ebp),%eax
804987d: 50 push %eax
804987e: ff 75 08 pushl 0x8(%ebp)//入栈
8049881: e8 40 03 00 00 call 8049bc6 <read_n_numbers>
//调用函数,读取n个数字
8049886: 83 c4 10 add $0x10,%esp
8049889: 85 c0 test %eax,%eax
//判断每一个数字是不是大于0
804988b: 75 0a jne 8049897 <phase_6+0x2f>
//不满足跳转
804988d: b8 00 00 00 00 mov $0x0,%eax
8049892: e9 5f 01 00 00 jmp 80499f6 <phase_6+0x18e>
8049897: c7 45 f0 00 00 00 00 movl $0x0,-0x10(%ebp)
804989e: eb 60 jmp 8049900 <phase_6+0x98>
80498a0: 8b 45 f0 mov -0x10(%ebp),%eax
80498a3: 8b 44 85 c8 mov -0x38(%ebp,%eax,4),%eax
80498a7: 85 c0 test %eax,%eax
//判断每一个数字是不是大于0
80498a9: 7e 0c jle 80498b7 <phase_6+0x4f>//跳转
80498ab: 8b 45 f0 mov -0x10(%ebp),%eax
80498ae: 8b 44 85 c8 mov -0x38(%ebp,%eax,4),%eax
80498b2: 83 f8 08 cmp $0x8,%eax
//从这里看看可以分析到是要读取8个数字。
80498b5: 7e 0f jle 80498c6 <phase_6+0x5e>
80498b7: e8 2c 06 00 00 call 8049ee8 <explode_bomb>
80498bc: b8 00 00 00 00 mov $0x0,%eax
80498c1: e9 30 01 00 00 jmp 80499f6 <phase_6+0x18e>
80498c6: 8b 45 f0 mov -0x10(%ebp),%eax
80498c9: 83 c0 01 add $0x1,%eax
80498cc: 89 45 ec mov %eax,-0x14(%ebp)
80498cf: eb 25 jmp 80498f6 <phase_6+0x8e>
80498d1: 8b 45 f0 mov -0x10(%ebp),%eax
80498d4: 8b 54 85 c8 mov -0x38(%ebp,%eax,4),%edx
80498d8: 8b 45 ec mov -0x14(%ebp),%eax
80498db: 8b 44 85 c8 mov -0x38(%ebp,%eax,4),%eax
80498df: 39 c2 cmp %eax,%edx
//比较两个数
80498e1: 75 0f jne 80498f2 <phase_6+0x8a>
80498e3: e8 00 06 00 00 call 8049ee8 <explode_bomb>
80498e8: b8 00 00 00 00 mov $0x0,%eax
80498ed: e9 04 01 00 00 jmp 80499f6 <phase_6+0x18e>
80498f2: 83 45 ec 01 addl $0x1,-0x14(%ebp)
80498f6: 83 7d ec 07 cmpl $0x7,-0x14(%ebp)
//循环的判断
80498fa: 7e d5 jle 80498d1 <phase_6+0x69>
80498fc: 83 45 f0 01 addl $0x1,-0x10(%ebp)
8049900: 83 7d f0 07 cmpl $0x7,-0x10(%ebp)
8049904: 7e 9a jle 80498a0 <phase_6+0x38>
8049906: c7 45 f0 00 00 00 00 movl $0x0,-0x10(%ebp)
804990d: eb 19 jmp 8049928 <phase_6+0xc0>
804990f: 8b 45 f0 mov -0x10(%ebp),%eax
8049912: 8b 44 85 c8 mov -0x38(%ebp,%eax,4),%eax
8049916: ba 09 00 00 00 mov $0x9,%edx
804991b: 29 c2 sub %eax,%edx?//减法操作
804991d: 8b 45 f0 mov -0x10(%ebp),%eax
8049920: 89 54 85 c8 mov %edx,-0x38(%ebp,%eax,4)
8049924: 83 45 f0 01 addl $0x1,-0x10(%ebp)
8049928: 83 7d f0 07 cmpl $0x7,-0x10(%ebp)
804992c: 7e e1 jle 804990f <phase_6+0xa7>
804992e: c7 45 f0 00 00 00 00 movl $0x0,-0x10(%ebp)
8049935: eb 36 jmp 804996d <phase_6+0x105>
8049937: 8b 45 e8 mov -0x18(%ebp),%eax
804993a: 89 45 f4 mov %eax,-0xc(%ebp)
804993d: c7 45 ec 01 00 00 00 movl $0x1,-0x14(%ebp)
8049944: eb 0d jmp 8049953 <phase_6+0xeb>
8049946: 8b 45 f4 mov -0xc(%ebp),%eax
8049949: 8b 40 08 mov 0x8(%eax),%eax
804994c: 89 45 f4 mov %eax,-0xc(%ebp)
804994f: 83 45 ec 01 addl $0x1,-0x14(%ebp)
8049953: 8b 45 f0 mov -0x10(%ebp),%eax
8049956: 8b 44 85 c8 mov -0x38(%ebp,%eax,4),%eax
804995a: 39 45 ec cmp %eax,-0x14(%ebp)//比较
804995d: 7c e7 jl 8049946 <phase_6+0xde>
804995f: 8b 45 f0 mov -0x10(%ebp),%eax
8049962: 8b 55 f4 mov -0xc(%ebp),%edx
8049965: 89 54 85 a8 mov %edx,-0x58(%ebp,%eax,4)
8049969: 83 45 f0 01 addl $0x1,-0x10(%ebp)
804996d: 83 7d f0 07 cmpl $0x7,-0x10(%ebp)
8049971: 7e c4 jle 8049937 <phase_6+0xcf>
8049973: 8b 45 a8 mov -0x58(%ebp),%eax
8049976: 89 45 e8 mov %eax,-0x18(%ebp)
8049979: 8b 45 e8 mov -0x18(%ebp),%eax
804997c: 89 45 f4 mov %eax,-0xc(%ebp)
804997f: c7 45 f0 01 00 00 00 movl $0x1,-0x10(%ebp)
8049986: eb 1a jmp 80499a2 <phase_6+0x13a>
8049988: 8b 45 f0 mov -0x10(%ebp),%eax
804998b: 8b 54 85 a8 mov -0x58(%ebp,%eax,4),%edx
804998f: 8b 45 f4 mov -0xc(%ebp),%eax
8049992: 89 50 08 mov %edx,0x8(%eax)
8049995: 8b 45 f4 mov -0xc(%ebp),%eax
8049998: 8b 40 08 mov 0x8(%eax),%eax
//以上有多步内存操作
804999b: 89 45 f4 mov %eax,-0xc(%ebp)
804999e: 83 45 f0 01 addl $0x1,-0x10(%ebp)
80499a2: 83 7d f0 07 cmpl $0x7,-0x10(%ebp)
80499a6: 7e e0 jle 8049988 <phase_6+0x120>
80499a8: 8b 45 f4 mov -0xc(%ebp),%eax
80499ab: c7 40 08 00 00 00 00 movl $0x0,0x8(%eax)
80499b2: 8b 45 e8 mov -0x18(%ebp),%eax
80499b5: 89 45 f4 mov %eax,-0xc(%ebp)
80499b8: c7 45 f0 00 00 00 00 movl $0x0,-0x10(%ebp)
80499bf: eb 2a jmp 80499eb <phase_6+0x183>
80499c1: 8b 45 f4 mov -0xc(%ebp),%eax
80499c4: 8b 10 mov (%eax),%edx
80499c6: 8b 45 f4 mov -0xc(%ebp),%eax
80499c9: 8b 40 08 mov 0x8(%eax),%eax
80499cc: 8b 00 mov (%eax),%eax
80499ce: 39 c2 cmp %eax,%edx
//比较两个数
80499d0: 7d 0c jge 80499de <phase_6+0x176>
80499d2: e8 11 05 00 00 call 8049ee8 <explode_bomb>
80499d7: b8 00 00 00 00 mov $0x0,%eax
80499dc: eb 18 jmp 80499f6 <phase_6+0x18e>
80499de: 8b 45 f4 mov -0xc(%ebp),%eax
80499e1: 8b 40 08 mov 0x8(%eax),%eax
80499e4: 89 45 f4 mov %eax,-0xc(%ebp)
80499e7: 83 45 f0 01 addl $0x1,-0x10(%ebp)
80499eb: 83 7d f0 06 cmpl $0x6,-0x10(%ebp)
80499ef: 7e d0 jle 80499c1 <phase_6+0x159>
80499f1: b8 01 00 00 00 mov $0x1,%eax
//还原内存,保护数据,离开
80499f6: c9 leave
80499f7: c3 ret
这一部分的汇编语言经过以上的分析以后,可以写出程序如下;
```c
// Code Block
int __cdecl phase_6(char *s)
{
int v2[8]; // [esp+0h] [ebp-58h]
int v3[8]; // [esp+20h] [ebp-38h]
_DWORD *v4; // [esp+40h] [ebp-18h]
int j; // [esp+44h] [ebp-14h]
int i; // [esp+48h] [ebp-10h]
_DWORD *v7; // [esp+4Ch] [ebp-Ch]
v4 = &node1;
if ( !read_n_numbers(s, (int)v3, 8) )
return 0;
for ( i = 0; i <= 7; ++i )
{
if ( v3[i] <= 0 || v3[i] > 8 )
{
explode_bomb();
return 0;
}
for ( j = i + 1; j <= 7; ++j )
{
if ( v3[i] == v3[j] )
{
explode_bomb();
return 0;
}
}
}
for ( i = 0; i <= 7; ++i )
v3[i] = 9 - v3[i];
for ( i = 0; i <= 7; ++i )
{
v7 = v4;
for ( j = 1; j < v3[i]; ++j )
v7 = v7[2];
v2[i] = (int)v7;
}
v4 = v2[0];
v7 = v2[0];
for ( i = 1; i <= 7; ++i )
{
v7[2] = v2[i];
v7 = v7[2];
}
v7[2] = 0;
v7 = v4;
for ( i = 0; i <= 6; ++i )
{
if ( *v7 < v7[2] )
{
explode_bomb();
return 0;
}
v7 = v7[2];
}
return 1;
}
然后可以看出,不同链表的指向;使用一下方式更加直接了当:
(
```bash
gdb) x/3x 0x804d178
0x804d178 <node1>: 0x00000005 0x00000001 0x0804d16c
(gdb) x/3x 0x0804d16c
0x804d16c <node2>: 0x00000002 0x00000002 0x0804d160
(gdb) x/3x 0x0804d160
0x804d160 <node3>: 0x00000001 0x00000003 0x0804d154
(gdb) x/3x 0x0804d154
0x804d154 <node4>: 0x00000009 0x00000004 0x0804d148
(gdb) x/3x 0x0804d148
0x804d148 <node5>: 0x00000007 0x00000005 0x0804d13c
(gdb) x/3x 0x0804d13c
0x804d13c <node6>: 0x00000006 0x00000006 0x0804d130
(gdb) x/3x 0x0804d130
0x804d130 <node7>: 0x00000008 0x00000007 0x0804d124
(gdb) x/3x 0x0804d130
0x804d130 <node7>: 0x00000008 0x00000007 0x0804d124
(gdb) x/3x 0x0804d124
0x804d124 <node8>: 0x00000004 0x00000008 0x00000000
然后根据第一个0x数字的大小,将链表元素按value值由大到小排序,但是并不是最后结果因为还要用9减去每一个数字才可。结果是5 2 4 3 8 1 7 6。
Phase secret:
这部分程序的功能是综合功能。
重新打开终端,这次断点打在secret_phase。
// Code Block
08049a5b <secret_phase>:
8049a5b: 55 push %ebp
8049a5c: 89 e5 mov %esp,%ebp
8049a5e: 83 ec 18 sub $0x18,%esp// Integer Subtraction
8049a61: e8 3f 03 00 00 call 8049da5 <read_line>
//Call Procedure
8049a66: 89 45 f4 mov %eax,-0xc(%ebp)
8049a69: 83 ec 0c sub $0xc,%esp
8049a6c: ff 75 f4 pushl -0xc(%ebp)
8049a6f: e8 8c f6 ff ff call 8049100 <atoi@plt>
8049a74: 83 c4 10 add $0x10,%esp//加法操作
8049a77: 89 45 f0 mov %eax,-0x10(%ebp)
8049a7a: 83 7d f0 00 cmpl $0x0,-0x10(%ebp)//比较两个数
8049a7e: 7e 09 jle 8049a89 <secret_phase+0x2e>
8049a80: 81 7d f0 e9 03 00 00 cmpl $0x3e9,-0x10(%ebp)
8049a87: 7e 0c jle 8049a95 <secret_phase+0x3a>
8049a89: e8 5a 04 00 00 call 8049ee8 <explode_bomb>
8049a8e: b8 00 00 00 00 mov $0x0,%eax//内存操作
8049a93: eb 42 jmp 8049ad7 <secret_phase+0x7c>
8049a95: 83 ec 08 sub $0x8,%esp//减法操作
8049a98: ff 75 f0 pushl -0x10(%ebp)
8049a9b: 68 2c d2 04 08 push $0x804d22c
8049aa0: e8 53 ff ff ff call 80499f8 <fun7>
8049aa5: 83 c4 10 add $0x10,%esp
8049aa8: 89 45 ec mov %eax,-0x14(%ebp)
8049aab: 83 7d ec 02 cmpl $0x2,-0x14(%ebp)
8049aaf: 74 0c je 8049abd <secret_phase+0x62>
//判断是否相等
8049ab1: e8 32 04 00 00 call 8049ee8 <explode_bomb>
8049ab6: b8 00 00 00 00 mov $0x0,%eax
8049abb: eb 1a jmp 8049ad7 <secret_phase+0x7c>
8049abd: 83 ec 0c sub $0xc,%esp//减法操作
8049ac0: 68 e4 b2 04 08 push $0x804b2e4
8049ac5: e8 c6 f5 ff ff call 8049090 <puts@plt>
8049aca: 83 c4 10 add $0x10,%esp
8049acd: e8 3f 04 00 00 call 8049f11 <phase_defused>
8049ad2: b8 01 00 00 00 mov $0x1,%eax
8049ad7: c9 leave
8049ad8: c3 ret //Return Near from Procedure
}
代码很多,先看明码地址。发现很多有意思的事情。但是要进入secret phase呢就要再输入一个austinpower的字符串。那我在拆炸弹的时候,在拆phase4的时候多输入一个austinpower,然后就成功进入了secret phase。
在这一关我使用了IDA进行分析
得到以下伪代码
int __cdecl fun7(_DWORD *a1, int a2)
{
if ( !a1 )
return -1;
if ( a2 < *a1 )
return 2 * fun7(a1[1], a2);
if ( a2 == *a1 )
return 0;
return 2 * fun7(a1[2], a2) + 1;
}
ret signed int secret_phase()
{
char *nptr; // ST1C_4
signed int result; // eax
int v2; // [esp+8h] [ebp-10h]
nptr = (char *)read_line();
v2 = atoi(nptr);
if ( v2 > 0 && v2 <= 1001 )
{
if ( fun7(&n1, v2) == 2 )
{
puts("Wow! You've defused the secret stage!");
phase_defused();
result = 1;
}
else
{
explode_bomb();
result = 0;
}
}
else
{
explode_bomb();
result = 0;
}
return result;
}
通过明码地址可以一步一步摸索到这一字符串。然后分析fun7的作用。有了以上代码,思路清晰但是比较繁琐最后经过手动计算得到答案是20.
作业小总结
可能因为比较笨,所以花的时间比较长才解决。
总之,加油鸭!