您的位置:首页 > 其它

逆向工程——二进制炸弹(第6关补完版)

2011-06-27 17:01 211 查看
最近收到THU的同学回复:第6关似乎应该是链表。我之前也很奇怪怎么最后一关会这么简单。于是找来最高难度的phase_6版本挑战一下。

phase_6的反汇编也确实够长了(差不多两页A4)。刚开始,真有点不知从何下手。先大致浏览一遍,唯一的印象是这段代码中跳转语句达到了12个,仅仅是这一条就会晕头转向了。





根据以往的经验,首先就是选出那些不该执行的语句(explode_bomb)。如图所示,黄线标出了引爆点。另外,既然跳转这么多,那就先来看看跳转,理出个大体结构。可以看到代码中跳转分成了两大类:1)条件跳转。2)直接跳转。而进一步分析,有些条件跳转是和callexplode_bomb相关的,而这条指令不该被执行,所以就又可以确定了某些条件跳转的方向。于是,红色箭头标出了确定的跳转方向,蓝色箭头标出5个条件跳转的不确定方向。

标出了跳转方向后,程序的大致结构就比较清晰了。根据这些跳转,可以把phase_6分成两大主要语句块(红色和绿色)。这时发现红色块执行到绿色块唯一的途径是蓝色跳转语句(1)(蓝1)。大致的结构分析完了,就深入程序看看吧。

首先由call8049112<read_six_numbers>可以知道要读入6个数。那问题就是怎样的6个数?

红色代码块:

由红色的代码块,我得出结论:这6个数<=6,且均不相等。

为什么这么说呢?由蓝2和最后一条jmp8048c77的大跳转,能想到什么,是不是有点像一个大循环嵌套了一个小循环呢?那为什么大跳转是用jmp而不是条件跳转。这时,想到了唯一跳转出红色大块的蓝1,可以猜想到了语句中用到了类似goto的语句(可见goto确实很会搅局,连分析起反汇编后的代码都很晦涩)。看到红色大块的前两条,感觉和输入数据有关,于是查看-0x24(%ebp)后知道是输入的第一个数据data[0],所以之后data[i]就存在了-0x24(%ebp,i,4)=-0x24(%ebp)+4i。由此可以推断%ebx应该是索引变量,而每次%eax=data[i]。再由代码块(a)得知data[i]<=6才行,否则就爆炸了。

再往下就是%edi=%ebx+1,%edi就是下一个索引,当到了索引5时就执行蓝1跳转。由此,可以先写个外层循环的大致框架:

for(inti=0;;i++)

[code]{
%eax=data[i]

%edi=i+1

if(%edi==6)

gotoblueblock;

}

[/code]

之后的lea-0x24(%ebp,%ebx,4),%esi则是把当前数据的地址赋给%esi,即%esi=&data[i]。

mov%edi,%ebx//%ebx=%ebx+1

[code]lea-0x24(%ebp),%eax//%eax=&data[0]
%edx=&data[0]

mov-0x4(%edx,%edi,4),%eax//%eax=%edx+4%edi–4=&data[0]+4(%edi–1)。

cmp0x4(%esi),%eax//比较*(&data[i]+0x4),*(&data[0]+4(%edi–1))从这条可以隐约看出是在比较两个输入的数据,那么这两个输入的数据关系是什么呢?

[/code]

往下读,jne8048cb1可知这两个数不能相等,否则就爆炸了。

最后看到代码块(b),每次%ebx+1,%esi+4,同时以%ebx<=5作为循环条件。根据蓝2的跳转,知道每次mov-0x4(%edx,%edi,4),%eax中的%edx和%edi是不变的,所以%eax==&data[0]+4(%edi–1),而%edi为外循环初始时的%ebx+1(因为内循环%ebx每次都在累加),所以%eax==&data[0]+4%ebx==&data[i]。

而每次%esi+4,而初始%esi=-0x24(%ebp,%ebx,4)=&data[i],所以cmp0x4(%esi),%eax依次遍历data[i]后面的数据。根据这些线索,我们可以写出内循环的框架:

%esi=&data[i];

[code]for(intj=i+1;;j<=5)
{

if(data[j]!=data[i])

j++;

else

explode_bomb();

}

[/code]

最后的那条mov%edi,%ebx是把外层的循环变量复原,在此就在内外层用不同的两个变量了,最后写成C代码如下:

for(inti=0;;i++)

[code]{
if(data[i]>6)

explode_bomb();


if(i+1==6)

gotoblueblock;


for(intj=i+1;j<=5;j++)

{

if(data[j]==data[i])

explode_bomb();

}

}

[/code]

于是,根据以上的分析,我们就得出了最初的结论:这6个数<=6,且均不相等。

绿色代码:

这段代码很长,初看很没头绪。那怎么办呢?依然根据跳转语句来理出一些思路,希望能够分成更小的块,分而治之。于是我们有了四个更小的代码块c,d,e,f。

代码c:

可以知道,%ecx保存的是地址值,而%eax是一个循环变量。cmp-0x24(%ebp,%edx,4),%eax可知要取地址的次数为我们输入的数据。

代码d:

由-0x24(%ebp,%edx,4)可知%edx是索引变量。再由mov%ecx,-0x3c(%ebp,%edx,4)可知相应的地址(-0x3c(%ebp),-0x38(%ebp),-0x34(%ebp),-0x30(%ebp),-0x2c(%ebp),-0x28(%ebp))将被赋值(与代码e中的取地址对应)

根据对代码块d的理解,可以写出一个大致的c代码框架:


for(inti=0;i<6;i++)

[code]{
addr=0x804a5fc;


for(intj=0;j<data[i];j++)

addr=*(addr+0x8);


-0x3c(%ebp)+4*i=addr;

}

[/code]

代码e:

举例来说:


mov-0x3c(%ebp),%ecx

[code]mov-0x38(%ebp),%eax
mov%eax,0x8(%ecx)

[/code]

可知,-0x38(%ebp)中的地址被放入了*(-0x3c(%ebp))+0x8中,而后的各步与此相似。相当于在做一个链表的链接功能。

代码f:


mov0x8(%ebx),%edx

[code]mov(%ebx),%eax
cmp(%edx),%eax

[/code]

这三条语句可知这是当前节点的值(*%ebx)和下一个节点的值(*0x8(%ebx))在进行比较,而且当前节点的值必须大于等于下一个节点的值。由此可以写出一个c的代码框架:


structnode

[code]{
intx,y;

node*next;

};


nodea=firstNode;

for(inti=0;i<5;i++)

{

nodeb=a->next;


if(a->x>=b->x)

a=b;

else

explode_bomb();

}

[/code]

由此,我们可以知道检查0x804a5fc及其之后0x8为步长的地址是关键。

由gdb查看得知:

*0x804a5fc=0x3b7

*(0x804a5fc+8)=0x804a5f0,*0x804a5f0=0x3c6

*(0x804a5f0+8)=0x804a5e4,*0x804a5e4=0x112

*(0x804a5e4+8)=0x804a5d8,*0x804a5d8=0x3a4

*(0x804a5d8+8)=0x804a5cc,*0x804a5cc=0x5a

*(0x804a5cc+8)=0x804a5c0,*0x804a5c0=0xfe

根据这个内存的检测,我们把值进行排序,以期符合代码f中的要求,得到(括号中为所属序号):

0x3c6(2)>0x3b7(1)>0x3a4(4)>0x112(3)>0xfe(6)>0x5a(5),根据其序号得出最后的答案:214365
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: