phase_1
1 | 08048b30 <phase_1>: |
直接查看0x804a124地址里的值
Border relations with Canada have never been better.
phase_2
1 | 08048b51 <phase_2>: |
地址为8048b72一行和下一行表示ebp-36和0比较,如果0-(ebp-36)里面的值之后不是非负数那么就会调用bomb函数,引起爆炸,所以,ebp-36里的值只能为0。再往下看,设ebx里的值为i,地址为 8048b7d~ 8048b84三行运行之后就是eax赋值为1,地址为ebp+4i-40的值加上eax里的1相加后放到eax里。下一行是eax里的值和地址为ebp+4i-36也就是上一个单元里的值进行比较,不相等也会调用bomb函数,所以,根据这个,第一遍可以推出ebp-32的值为1,之后就可以一直如此(下一数等于其上一个数加上ebx里的值,ebx是逐一递增的)推出ebp-16~ebp-36之间的六个值分别为0 1 3 6 10 15
phase_3
1 | <phase_3>: |
首先给出了明地址8048bce,看看里面是啥 ,发现是%d %c %d
,这意味着输入一个数一个字符和一个数,再往下看,8048bde这里把scanf函数的返回值和2比较,由于我们之前已经找到了输入的格式,所以此时的eax为确定的3。接着往后,地址为ebp-14里的内容和7比较,也就是说,第一个数要小于7,初步测试用2,之后通过gdb调试发现跳到0x08048c3e,之后是地址为ebp-16的值和0x382即898比较,只有相等才不会调用bomb函数,也就是说第三个数是898,再往后跳转到8048ceb,此时的eax里是0x72,要求地址为ebp-21的值和al里的值相等,又因为之前看到输入里有一个字符,也就是说,字符的ascii码为0x72,即r
,至此,第三个字符完成。当然,如果一开始输入的不是二而是其他小于7的数,后面的各个跳转到的位置也会变化,后两个输入也会不同,也就是这个字符的答案不唯一。
phase_4
1 | 08048d08 <func4>: |
1 | static int func4(int a, int b, int c){ |
1 | 08048d64 <phase_4>: |
首先打印地址0x804a30f的内容发现是%d %d
初步判定是输入两个整型,再往下看有一个输入返回值和2的比较也就是说必须输入两个数,这就基本确定是输入两个数。再往后看,发现是调用func4函数,这个函数的参数第一个是输入的值,第二个是0,第三个是0xe。看func4的功能,func4的c函数见上。再回来看phase_4函数,发现要求返回值是5,不然就会调用bomb函数。再去看func4,是一个递归,最后一次只能是0才能回去,那就从0开始,两个选择,要么是2×返回值,要么是2×返回值+1,从零开始,先乘二加一,再乘二,再乘二加一就是5,也就是第一次要输入的数>tmp,在这里就是输入的数大于7,第二次是输入的数小于tmp,在这里是输入的数小于11,第三次是输入的数大于tmp,在这里是输入的数大于5,第四次就是输入的数等于tmp,计算出tmp=10,这就是输入的第一个参数。再往后看,很简单了,第二个参数和5比较,不相等就bomb,所以第二个参数是5.
phase_5
1 | 08048dd2 <phase_5>: |
地址为0x804a30f 的内容是%d %d ,根据之前的经验,还是输入两个整型。再往后看,这个的意思就是ecx中存放sum,edx中存放数组下标i,eax中存放a[i],a的起始地址为0x804a1c0,要求找到相应的正确起始下标并且保证值为15的数在数组下标为15的地方,打印出这个数组:12 3 7 11 13 9 4 8 0 10 1 2 14 6 15 也就是要想办法让第a[1]的值是12,mov %eax,-0x14(%ebp)
cmp $0xf,%eax
je 8048e3f <phase_5+0x6d>
这三句给了提示,输入的数组下标不超过14,从头来,试到第五个就是12开头了。也就是第一个数是5,再往后看,-0x10(%ebp),%ecx
要求第二个参数和ecx中的值一样,通过gdb调试发现执行到这步的时候,ecx值为115,也就是第二个数是115.
phase_6
1 | 08048e57 <phase_6>: |
首先是调用了读取六个数的函数,也就是要输入六个数,再往后有两个循环,一个是保证每个数都不一样,另一个是保证每个数都小于等于6,也就是说,只能输入1 2 3 4 5 6,但是顺序不确定,之后是待保输入数组的值的范围在1 ~ 6且不存在重复值;再往后用7减去输入数组的每个元素,相当于求补。之后出现了一个常数地址0x804c13c,打印发现里面的内容是281,不知道是啥,但是有个node节点,再往后看看有啥,打印一下地址,发现,是一个链表,最后的next都指向了下一个节点,最后指向NULL。而地址里有六个数,分别如下表。
1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|
281 | 438 | 714 | 78 | 962 | 875 |
之后是根据前面构建的地址数组,修改结构体数组的next的值,实现单链表的排序,要求单链表递减排序,若满足要求,那么拆弹成功。也就是说排序顺序为5 6 3 2 1 4。而且前面有一个7-x的操作也就是答案为2 1 4 5 6 3。
secret_phase
因为之前说过有个隐藏炸弹,正好在做phase_6的时候发现了一个叫secret_phase的函数,发现只有通过phase_defused这个函数才能调用这个隐藏函数,而phase_defused函数在main函数里每一次调用完相应的六个函数之后都会调用,但是在前面的拆弹过程中没有体现出来,比如说phase_1:
1 | 8048a81: e8 aa 00 00 00 call 8048b30 <phase_1> |
defused函数如下
1 | 0804930e <phase_defused>: |
分析第27行,因为 调用了string_not_equal函数,之前push了0x804a372 (DrEvil)也就是说要找一个地方输入这个字符串,又因为%d %d %s,猜测是在第四个或者第五个之后输入,在第四个尝试之后成功了。接下来就是secret-phase了。
secret_phase
1 | 08048fa4 <secret_phase>: |
根据第23行,也就是说调用fun7之后,返回值必须是3,这样就结束了。
fun7
1 | 08048f52 <fun7>: |
1 | int func7(int *a, int b){ |
这是fun7的c代码,是一个和fun4很像的递归代码。最后一个数是3,那么上一个就是1,再上一个就是0,二叉树方向:root->right->right。从节点最初在secret_phase中传入的地址作为根节点开始往回找,
也就是0x6b=107
至此所有的炸弹都拆完了。