黑盒破解
這個題目比較硬核,輸入的地方通過比較字符串來選擇函數。首先通過構造函數找到整個數據結構的定義
| 偏移 | 值 | 類型 | 長度 | 備注 |
|---|---|---|---|---|
| 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和安卓)題目中質量最高和最(逆向過程中)有趣的~
2018DDCTF-Writeup