題目簡介
題目給出程序 showyourflag
以及用該程序加密后的文件 yourflag
。
可以通過命令行執行:
showyourflag [infile] [outfile]
其中 infile
為輸入文件,outfile
為輸出文件。
加密分析
字節變換
程序首先對輸入文件的內容進行逐字節變換,位於函數 sub_401FC0
。
v8 = ~__ROL1__(v8, 1);
子串壓縮
隨后實現了一個類似於壓縮的算法,位於函數 sub_4023D0
。
函數先對子串開頭的三個字節進行哈希,隨后在字典中查找之前是否出現過相同哈希值的子串。
若存在則求出兩個子串相同部分長度的最大值,隨后將兩個子串相差的距離 dis
以及匹配的長度 len
進行編碼,並將編碼結果存儲到輸出文件中。
若不存在則直接將子串內容存儲到輸出文件中。
while ( 1 )
{
tri_bytes = *now_ptr & 0xFFFFFF;
hash = (0x9E3779B9 * tri_bytes) >> 18;
last_pos = dict[hash];
dict[hash] = now_ptr - a1;
last_ptr = &a1[last_pos];
dis = now_ptr - last_ptr;
if ( (now_ptr - last_ptr) <= 0x1FFF )
{
if ( now_ptr >= tail_13 )
goto LABEL_21;
v10 = (now_ptr + 1);
if ( tri_bytes != (*last_ptr & 0xFFFFFF) )
goto LABEL_10;
if ( tail_13 <= v10 )
goto LABEL_21;
if ( v9 < now_ptr )
copy_raw(now_ptr - v9, v9, output_);
len_d2 = len_prefix(last_ptr + 3, (now_ptr + 3), tail_4);
dis_d1 = dis - 1;
len_d2_remain = len_d2;
for ( hi_dis_d1 = (dis - 1) >> 8; len_d2_remain > 0x106; *(output - 1) = dis - 1 )
{ // case len_d2 : 0x107 ~ 0x1FFF
// split for 0x106 bytes
*output = hi_dis_d1 - 0x20; // out[0] = high_byte(dis_d1) - 0x20
// 0xE0 ~ 0xFF
len_d2_remain -= 0x106; // len_d2 -= 0x106
output += 3;
*(output - 2) = 0xFD; // out[1] = 0x106 - 2 - 7
// 0xFD
} // out[2] = low_byte(dis_d1)
if ( len_d2_remain > 6 ) // 0x0 ~ 0xFF
{
output[2] = dis_d1; // case len_d2 : 0x7 ~ 0x106
output_ = output + 3;
*output = hi_dis_d1 - 0x20; // out[0] = high_byte(dis_d1) - 0x20
// 0xE0 ~ 0xFF
output[1] = len_d2_remain - 7; // out[1] = len_d2 - n * 0x106 - 7
// 0x0 ~ 0xFF
} // out[2] = low_byte(dis_d1)
else // 0x0 ~ 0xFF
{
output[1] = dis_d1; // case len_d2 : 0x1 ~ 0x6
output_ = output + 2;
*output = hi_dis_d1 + 0x20 * len_d2_remain;//
// out[0] = high_byte(dis_d1) + 0x20 * len_d2
// 0x20 ~ 0xDF
} // out[1] = low_byte(dis_d1)
// 0x0 ~ 0xFF
v22 = (v18 + len_d2);
v23 = *v22;
now_ptr = (v22 + 2);
dict[(0x9E3779B9 * (v23 & 0xFFFFFF)) >> 18] = v22 - a1;
v9 = v22 + 2;
dict[(0x9E3779B9 * (v23 >> 8)) >> 18] = v22 + 1 - a1;
if ( tail_13 <= v22 + 2 )
goto LABEL_21;
}
else
{
if ( now_ptr >= tail_13 )
goto LABEL_21;
v10 = (now_ptr + 1);
LABEL_10:
now_ptr = v10;
}
}
LABEL_21:
return result;
解密分析
提取子串相差的距離 dis
以及匹配的長度 len
后還原原串內容即可。
#include <cstdio>
#include <cstdlib>
int main(){
FILE *pFile, *pFile2;
long lSize;
unsigned char *buffer,*buffer2;
size_t result;
pFile = fopen ( "yourflag" , "rb" );
pFile2 = fopen ( "flag.png" , "wb+" );
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
buffer = (unsigned char*) malloc (sizeof(char)*lSize);
buffer2 = (unsigned char*) malloc (sizeof(char)*lSize);
result = fread (buffer,1,lSize,pFile);
int i=0,dis=0,len,cnt=0;
while(i<lSize-2){
printf("%02X ",buffer[i]);
if (0<=buffer[i]&&buffer[i]<0x20){
for (int j=1;j<=buffer[i]+1;j++){
buffer2[cnt++]=buffer[i+j];
}
i+=buffer[i]+2;
dis=0;
len=0;
}
else if (0x20<=buffer[i]&&buffer[i]<0xE0){
len=(buffer[i]&0xE0)/0x20+2;
dis=buffer[i+1]+((buffer[i]&0x1F)<<8)+1;
i+=2;
}
else if (0xE0<=buffer[i]&&buffer[i]<0x100){
len=buffer[i+1]+9;
dis=((buffer[i]-0xE0)<<8)+buffer[i+2]+1;
i+=3;
}
for (int j=0;j<len;j++){
buffer2[cnt]=buffer2[cnt-dis];
cnt++;
}
}
for (int i=0;i<lSize;i++){
int t=~buffer2[i];
buffer2[i]=((t&0xFE)>>1)+((t&0x01)<<7);
}
fwrite(buffer2,1,lSize,pFile2);
fclose (pFile);
fclose (pFile2);
free (buffer);
return 0;
}
運行后得到解密的 flag.png
,內容如下:
后記
結合 Tenet
插件來調這道題會輕松很多。