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

  • 相关阅读:
    小学生都能学会的python(文件操作)
    小学生都能学会的python(深浅拷贝)
    小学生都能学会的python(小数据池)
    小学生都能学会的python(字典{ })
    小学生都能学会的python(列表[ ])
    小学生都能学会的python(编码 and 字符串)
    小学生都能学会的python(一)2018.9.3
    Ionic常用命令
    Ionic1.x项目中的Installing npm packages问题
    hdu1005
  • 原文地址:https://www.cnblogs.com/algonote/p/15464076.html
Copyright © 2020-2023  润新知