20181020学习笔记——解决的第一个Reverse题目

解决的第一个Reverse题目

题目描述

题目给了一个名为rev1的文件,没有后缀。

文件的下载地址:

结题过程

第一次做Reverse题目,一脸蒙蔽,我首先尝试把后缀改为.exe,结果,不能运行。。。。可能不是Windows系统文件。

我转到虚拟机里,打开Linux系统,执行“file rev1”,结果,果然是Linux下的可执行文件,是ELF文件。

然后我在终端执行“./rev1”,结果拒绝访问。

修改权限,在终端执行“chmod 777 rev1”

再次执行“./rev1”,运行成功了,终端显示"Please input your flag:",随便输入,结果果然是wrong。

好吧,其实,上述步骤和解决这道题没有半毛钱关系,这是我在完全小白的情况下对此题的摸索,接下来进入正题。


Windows下逆向分析强有力的工具是OD+IDA,我之前的博客中提供了下载链接。题目提供的是ELF文件,是无法使用OD的,好在这道题仅仅使用IDA就可以了。

  1. 把文件拖入IDA中,执行ctrl+F5,得到程序的伪代码。20181020学习笔记——解决的第一个Reverse题目
  2. 打开伪代码查看。伪代码的核心内容如下:
    //这是main函数
    
    __int64 __fastcall main(__int64 a1, char **a2, char **a3)
    {
      __int64 result; // [email protected]
      __int64 v4; // [email protected]
      char s; // [sp+0h] [bp-30h]@1
      __int64 v6; // [sp+28h] [bp-8h]@1
    
      v6 = *MK_FP(__FS__, 40LL);
      printf("Please input your flag:", a2, a3);
      __isoc99_scanf("%32s", &s);
      if ( strlen(&s) == 32 )
      {
        if ( (unsigned int)sub_400686((__int64)&s) )
          puts("Right!");
        else
          puts("Wrong!");
        result = 0LL;
      }
      else
      {
        puts("Wrong!");
        result = 0LL;
      }
      v4 = *MK_FP(__FS__, 40LL) ^ v6;
      return result;
    }
    
    
    //main函数中调用的子函数
    
    signed __int64 __fastcall sub_400686(__int64 a1)
    {
      signed int i; // [sp+Ch] [bp-Ch]@1
    
      for ( i = 0; i <= 31; ++i )
      {
        if ( (char)(*(_BYTE *)(i + a1) ^ byte_400818[i]) != i )
          return 0LL;
      }
      return 1LL;
    }
    
    
    //定义的全局变量
    
    _BYTE byte_400818[33] =
    {
      102,
      109,
      99,
      100,
      127,
      60,
      54,
      114,
      87,
      66,
      100,
      59,
      123,
      82,
      124,
      60,
      102,
      84,
      96,
      96,
      39,
      74,
      73,
      127,
      113,
      88,
      82,
      114,
      125,
      117,
      42,
      98,
      0
    }; // idb

    分析得到,主函数中输入了一个长度为32的字符串,然后调用子函数对字符串进行处理(子函数返回一个判断),所以最关键的部分就是分析子函数到底干了啥。

  3. 我们来分析一下子函数到底干了啥。子函数的伪代码如下:

    signed __int64 __fastcall sub_400686(__int64 a1)
    {
      signed int i; // [sp+Ch] [bp-Ch]@1
    
      for ( i = 0; i <= 31; ++i )
      {
        if ( (char)(*(_BYTE *)(i + a1) ^ byte_400818[i]) != i )
          return 0LL;
      }
      return 1LL;
    }

    定义了一个变量i(i用来控制循环),然后进入了一个for循环。观察for循环中if语句的条件,“^”是异或运算,(i+a1)实际上就是在从传入的字符串变量中一次取值(第1个字符、第2个字符、第3个字符......一直到第31个字符),取出来以后和byte_400818[i](是定义的全局变量)进行了异或运算,判断异或运算的结果,满足条件,则继续循环,不满足,则返回false。所以,每个字符的值怎么求呢?其实很简单,a^b = c,那么b^c =a,所以(a1+i)=byte_400818[i]^i。

  4. 最后编程求出flag就行了。

    #include<stdio.h>
    
    int a[]={102,109,99,100,127,60,54,114,87,66,100,59,123,82,124,60,102,84,96,96,39,74,73,127,113,88,82,114,125,117,42,98,0};
    
    void test(){
    	int i;
    	for(i=0;i<32;i++){
    		printf("%c",a[i]^i);
    	}
    	printf("\n");
    }
    
    int main(){
        test();
        return 0;
    }

    flag{90u_Kn0w_r3vErs3__hiAHiah4}