强网拟态 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 插件来调这道题会轻松很多。

posted @ 2021-10-26 05:50  Byaidu  阅读(192)  评论(0编辑  收藏  举报