BUUCTF reverse 刮开有奖

一、刮开有奖

所需工具:

  1. IDA(反编译工具)
  2. exeinfope(查壳工具)
  3. CaptfEncoder(编码转换)

题解:

  1. 拖入exeinfope 检查是否套壳[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

    发现并没有。

  2. 查看文件是32位还是64位。方法如下:使用记事本直接打开你的exe文件。只要看第二行即可,第二行开头不远处有PE两个字母,再后面两个空格后第三个字符就是标记了,如果是字母L的话,就是32位应用程序,如果是d?就表示是64位应用程序。

  3. 将.exe文件拖入IDA 32中。F5进行反编译。

  4. 在WinMain函数中找到关键函数DialogFunc

    INT_PTR __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
    {
      const char *v4; // esi
      const char *v5; // edi
      int v7[2]; // [esp+8h] [ebp-20030h] BYREF
      int v8; // [esp+10h] [ebp-20028h]
      int v9; // [esp+14h] [ebp-20024h]
      int v10; // [esp+18h] [ebp-20020h]
      int v11; // [esp+1Ch] [ebp-2001Ch]
      int v12; // [esp+20h] [ebp-20018h]
      int v13; // [esp+24h] [ebp-20014h]
      int v14; // [esp+28h] [ebp-20010h]
      int v15; // [esp+2Ch] [ebp-2000Ch]
      int v16; // [esp+30h] [ebp-20008h]
      CHAR String[65536]; // [esp+34h] [ebp-20004h] BYREF
      char v18[65536]; // [esp+10034h] [ebp-10004h] BYREF
    
      if ( a2 == 272 )
        return 1;
      if ( a2 != 273 )
        return 0;
      if ( (_WORD)a3 == 1001 )
      {
        memset(String, 0, 0xFFFFu);
        GetDlgItemTextA(hDlg, 1000, String, 0xFFFF);
        if ( strlen(String) == 8 )
        {
          v7[0] = 90;
          v7[1] = 74;
          v8 = 83;
          v9 = 69;
          v10 = 67;
          v11 = 97;
          v12 = 78;
          v13 = 72;
          v14 = 51;
          v15 = 110;
          v16 = 103;
          sub_4010F0(v7, 0, 10);
          memset(v18, 0, 0xFFFFu);
          v18[0] = String[5];
          v18[2] = String[7];
          v18[1] = String[6];
          v4 = (const char *)sub_401000(v18, strlen(v18));
          memset(v18, 0, 0xFFFFu);
          v18[1] = String[3];
          v18[0] = String[2];
          v18[2] = String[4];
          v5 = (const char *)sub_401000(v18, strlen(v18));
          if ( String[0] == v7[0] + 34
            && String[1] == v10
            && 4 * String[2] - 141 == 3 * v8
            && String[3] / 4 == 2 * (v13 / 9)
            && !strcmp(v4, "ak1w")
            && !strcmp(v5, "V1Ax") )
          {
            MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);
          }
        }
        return 0;
      }
      if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
        return 0;
      EndDialog(hDlg, (unsigned __int16)a3);
      return 1;
    }
    

    首先根据if ( strlen(String) == 8 ) 可知String 长度为8。

    观察v7-v16在寄存器中的位置是连续的:
    在这里插入图片描述

    在后续函数sub_4010F0 运行中可直接写成一个数组,为了方便后续观察将其定义为char 类型。

  5. 转入函数sub_4010F0

    int __cdecl sub_4010F0(int a1, int a2, int a3)
    {
      int result; // eax
      int i; // esi
      int v5; // ecx
      int v6; // edx
    
      result = a3;
      for ( i = a2; i <= a3; a2 = i )
      {
        v5 = 4 * i;
        v6 = *(_DWORD *)(4 * i + a1);
        if ( a2 < result && i < result )
        {
          do
          {
            if ( v6 > *(_DWORD *)(a1 + 4 * result) )
            {
              if ( i >= result )
                break;
              ++i;
              *(_DWORD *)(v5 + a1) = *(_DWORD *)(a1 + 4 * result);
              if ( i >= result )
                break;
              while ( *(_DWORD *)(a1 + 4 * i) <= v6 )
              {
                if ( ++i >= result )
                  goto LABEL_13;
              }
              if ( i >= result )
                break;
              v5 = 4 * i;
              *(_DWORD *)(a1 + 4 * result) = *(_DWORD *)(4 * i + a1);
            }
            --result;
          }
          while ( i < result );
        }
    LABEL_13:
        *(_DWORD *)(a1 + 4 * result) = v6;
        sub_4010F0(a1, a2, i - 1);
        result = a3;
        ++i;
      }
      return result;
    }
    

    代码较长,考虑直接改写成C运行。

    *(_DWORD *) 是汇编的表示,然后将各种基址+偏移的表示也换成数组的寻址,如下:

    #include <stdio.h>
    #include <string.h>
    
    int  sub_4010F0(char* a1, int a2, int a3)
    {
        int result; // eax
        int i; // esi
        int v5; // ecx
        int v6; // edx
    
        result = a3;                                 
        for (i = a2; i <= a3; a2 = i)
        {
            v5 = i;
            v6 = a1[i];
            if (a2 < result && i < result)
            {
                do
                {
                    if (v6 > a1[result])
                    {
                        if (i >= result)
                            break;
                        ++i;
                        a1[v5] = a1[result];
                        if (i >= result)
                            break;
                        while (a1[i] <= v6)
                        {
                            if (++i >= result)
                                goto LABEL_13;
                        }
                        if (i >= result)
                            break;
                        v5 = i;
                        a1[result] = a1[i];
                    }
                    --result;
                } while (i < result);
            }
        LABEL_13:
            a1[result] = v6;
            sub_4010F0(a1, a2, i - 1);
            result = a3;
            ++i;
        }
        return result;
    }
    int main()
    {
        char v[] = "ZJSECaNH3ng";
        sub_4010F0(v,0,10);
        printf("%s", v);
    	return 0;
    }
    

    获得v 的值为3CEHJNSZagn 对应回原来的变量如下:

    3CEHJ
    v7[0]v7[1]v8v9v10
  6. 返回DialogFunc 函数,发现成功条件为:

    String[0] == v7[0] + 34
        && String[1] == v10
        && 4 * String[2] - 141 == 3 * v8
        && String[3] / 4 == 2 * (v13 / 9)
        && !strcmp(v4, "ak1w")
        && !strcmp(v5, "V1Ax")
    

    很容易就知道String[0] = '3' + 34 也就是UString[1] = 'J'

    v4 == "ak1w" && v5 == "V1Ax"

  7. 返回去看到函数sub_401000 发现是Base64编码。

    转换后可得第一次时v18为jMp 通过赋值语句:
    在这里插入图片描述

    可知:String[5]-[7] = "jMp"

    同理第二次的解码可以得到Sting[2]-[4] = "WP1"

  8. 最终将String 拼接起来,获得flag: flag{UJWP1jMp}


版权声明:本文为firfis原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。