黑盒破解

这个题目比较硬核,输入的地方通过比较字符串来选择函数。首先通过构造函数找到整个数据结构的定义

偏移 类型 长度 备注
a1 sth_p q 0x100
a1+8 char_table_0_p q 0x100 0x6030e0
a1+16 input c 100
a1+272 rand%50
a1+280 char_table_0_p-sth_p q
a1+288+8 char_table_2 d 8 (a1+8)[72+l] 6030e0[l+255]
a1+408 char_table_1 b 255 0x603700
a1+672 func_addr q 255 (a1+8)[84+i] 603200+i(+=)
a1+672+8 func_table q 8 (a1+8)[84+6030e0[l+255]]

输入函数形式为:

for i in range(len(input)):
    *(a1+664) = input[i+1]
    for j in range(8):
        if(f[input[i]] == (a1 + 408)[(a1+8)[72+j]]):
            call (a1+8)[84 + (a1+8)[j+72]] ( a1 )

可以看到,实际上就是令Input[i]作为下标取数组f的值,然后遍历char_table_1中的8个值,如有相等的则取func_addr中对应的函数来调用。

一共8个函数,根据提示语可以定位到其中的一个函数,查看交叉引用则能找到另外8个函数的函数表:

逐个反编译发现:

函数名 执行条件 表达式 功能
func_0 (a1+288)<(a1+292) (a1+665) = char_table[a1+288] m=c[index]
func_1 (a1+288)<(a1+292) char_table[a1+288] = (a1+665) c[index]=m
func_2 (a1+665) = (a1+665) + (a1+664) – 33 m+=[next]-33
func_3 (a1+665) = (a1+665) – ((a1+664) – 33) + 1 m-=[next]-33
func_4 (a1+288)++ index++
check_func *(a1+664)==’s’ s = char_table_0[(a1+288)], len=20,puts(s) check(s)
func_6 (a1+288)– index–
func_7 后一个参<=0x59 char_table_0[a1+288] = input[*(a1+288) + *(a1+664) – 48] – 49

其中用到的变量一共有4个:

a1+292 = 255
a1+664 = [next](即input[i+1])
a1+665 = m(临时变量)
a1+288 = index

在check_func中会输出s,s是从char_table_0中以index为起点取的0x20个值。如果s满足三个方程则通过校验,返回成功。

而实际上那三个方程是不需要逆的—题目中明示了只要输出“Binggo”即可得到flag。因此目标显然是在char_table_0中获得Binggo的字符串,将其dump出来输出了一下发现并字符顺序并没有合适的,甚至上述5个字母都不齐。以及一个最关键的问题,check_func中取了0x20个值赋给s,这显然不符合”Binggo”的要求,因此第七个字符必须给上”使其截断才行。

分析其余7个函数,发现0和1可以交换char_table_0中的字符的位置,2、3和7则可以修改char_table_0中字符的值,4和6则是用来移动下标的,最后check_func加’s’来结束并输出。在构造输入之前,先要找到函数对应的输入值。

逆向一下发现char_table中还被更改了值,IDA动态调试断在函数调用处调用idc脚本,即可得到对应值:

auto i, j, v14, p, q;
for(i=0;i<8;i++)
{
    p = Byte(0x6030e0+255+i);
    v14 = 0x400dc1;
    //for ( j = 0; j <= p; ++j )
    {
      v14 = Dword(0x91d440+8+8*(p+0x54));
    }
    for(j=0;j<255;j++)
    {
        if(Byte(0x603900+j)==Byte(0x91d5d8+p))
        {
            q = j;
            break;
        }
        //Message("Not Found : %x", Byte(0x603700+p));
    }
    Message("%xt%ct%xn",q , q, v14);
}
24  $   400dc1  
38  8   400e7a  
43  C   400f3a  
74  t   401064  *  
30  0   4011c9  
45  E   40133d  
75  u   4012f3  *  
23  #   4014b9  

得到这8个输入字符即可开始构造了。

由于函数功能很多样,因此构造方法很多,在此仅表述我的构造方法:

由于输入buffer有限,因此不适合向右移动指针太多来找寻合适的字符。所以我就原地变换—毕竟将一个字符变成另一个字符满打满算也只要4个输入,移动指针可就轻而易举几十上百了。

下列计划中push表示将char_table中的值取入m,A->B表示将A通过func_2和3变换成B,->1表示指针后移1位

push P    # $
P->B    # t/
pop B    # 8
#111(用于填充make,其实1个就够,懒得算了233)
B->i    # CH
->1        # 0
pop i    # 8
i->n    # C&
->1        # 0
pop n    # 8
->1        # 0
n->g    # t(
pop g    # 8
->1        # 0
pop g    # 8
g->o    # C)
->1        # 0
pop o    # 8
->1        # 0
make x00    # #0
<-6        # uuuuuu
End        # Es

其中的111是为了make x00,在指针指向第七个字符时直接构造,提交给服务器即可获得flag。相对而言我觉得这题是所有(re和安卓)题目中质量最高和最(逆向过程中)有趣的~

本文章首发在 网安wangan.com 网站上。

上一篇 下一篇
讨论数量: 0
只看当前版本


暂无话题~