強網擬態 2021 showyourflag Writeup


題目簡介

題目給出程序 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 插件來調這道題會輕松很多。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM