我的解题过程

  1. 下载程序后,发现没有后缀名,猜测为ELF文件,使用Hex Editor打开,0x7f454c46为ELF魔数

  2. 使用Exeinfo进行分析,发现未加壳

    • NOT Win EXE - .o - ELF executable [ 64bit obj. Exe file - CPU : AMD x86-64 - OS/ABI: unspecified ]
    • -> Compiler : GCC: (Ubuntu 5.4.1-2ubuntu1~14.04) 5.4.1 20160904
  3. 使用IDA打开进行分析,main函数如下:

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      char v3; 
      __int64 v5; 
      int i; 
      FILE *stream; 
      char filename[8];
       
      LODWORD(v5) = 0;
      while ( (signed int)v5 < strlen(s) )
      {
        if ( v5 & 1 )
          v3 = 1;
        else
          v3 = -1;
        *(&t + (signed int)v5 + 10) = s[(signed int)v5] + v3;
        LODWORD(v5) = v5 + 1;
      }
      strcpy(filename, "/tmp/flag.txt");
      stream = fopen(filename, "w");
      fprintf(stream, "%s\n", u, v5);
      for ( i = 0; i < strlen(&t); ++i )
      {
        fseek(stream, p[i], 0);
        fputc(*(&t + p[i]), stream);
        fseek(stream, 0LL, 0);
        fprintf(stream, "%s\n", u);
      }
      fclose(stream);
      remove(filename);
      return 0;
    }
    
    • 使用IDA远程动态调试发现有一个char[]会在计算过程中出现harifCTF{b70c59275fcfa8aebf2d5911223c6589},提交之后答案错误,继续分析
    • 第一次while循环会计算出来一个char,循环次数为s字符串的长度,s字符串内容为c61b68366edeb7bdce3c6820314b7498。经过分析后发现第一个循环是将s[]中每个char进行运算后存储到harifCTF{}中,t仅是一个基址,先向后偏移10,到达{后的第一个存储空间,之后v5最为数组索引继续向下遍历
    • 之后程序打开/tmp/flag.txt文件,使用fprintf函数将u写入文件,但是u是44个*,所以查看文件应该也没有作用
    • 第二个while应该是将flag逐字符写入了文件,写入之后关闭并且删除了文件,所以只要动态调试断在删除之前,应该可以查看,但是实际操作后文件中并没有flag
    • 分析内存后发现harifCTF字符串紧跟在t变量声明后面,而t变量仅为一个char类型,但是在反汇编程序中它很显然是一个数组,所以将t的数据类型更改为char[]类型,之后发现该数组多了一个S,所以之前的答案错误仅是因为第一个字符被识别为char类型的t
    • 提交答案SharifCTF{b70c59275fcfa8aebf2d5911223c6589},正确

别人的解题过程

  1. 他们采用了linux系统下的file工具检查文件

    root@kali:~# file <文件名>
    root@kali:~# 12: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=e389cd7a4b9272ba80f85d7eb604176f6106c61e, not stripped
    

    stripped: 将程序中的符号表的信息剔除掉,可执行文件体积较小

    not stripped:保留了符号表信息,便于调试

    在本程序中执行之后,没有弹出窗口和任何提示以及报错,所以在本题中没有作用,应该会在其他题目中用到

  2. 别人分析了第二个while循环,即写入文件的循环

    • 每次循环调用了两次fseek函数,该函数声明如下*int fseek(FILE *stream, long int offset, int whence)**。stream为指向file对象的指针;offset是相对于whence的偏移量以字节为单位;whence表示添加偏移offset的位置,一般为SEEK_SET(文件开头)、SEEK_CUR (文件指针当前位置)、SEEK_END(文件末尾)。所以可以看出每次循环都会在p[i]位置写入flag的一个字符,之后从文件开头写入u,也就是44个,覆盖掉了之前的flag字符,所以可以分析出来,t变量实际上存储了完整的flag,这部分分析也正是我欠缺的。

    • 每次循环调用了一次fputc函数,该函数声明如下int fputc(int char, FILE *stream),char为被写入的字符,stream为指向file对象的指针,该函数把参数char指定的字符写入到指定的流stream中,并把位置标识符往后移动。

    • int fprintf(FILE *stream, const char *format, …),用于向文件中写入格式化的数据