BUUCTF 刮开有奖1题解

这个题目是我目前为止做过的最难的逆向题目了,相当的恶心。不过适应了也还好。不说了,拿到程序后丢进ida,发现是一个 32 位程序,先进WinMain函数,然后F5反汇编。最终我们拿到了这样的一个程序:

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){  DialogBoxParamA(hInstance, (LPCSTR)0x67, 0, DialogFunc, 0);  return 0;}

其中DialogFunc就是这个窗口的内部程序的程序过程。所以双击函数名进入。然后大致浏览,可以得到这个是该程序的主要逻辑部分。

可以发现,函数sub_4010F0对一个数组进行了预处理,同时还有一个函数sub_401000对输入进行了加密。

通过判断条件

if ( String == v7 + 34        && v19 == v11        && 4 * v20 - 141 == 3 * v9        && v21 / 4 == 2 * (v14 / 9)        && !strcmp(v4, "ak1w")        && !strcmp(v5, "V1Ax") )      {        MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);      }

我们可以知道输入的字符串前半部分对部分预处理结果进行对比判断,然后将输入的后半部分编码和几个已知的字符串进行对比判断。 所以接下来我们的重点是看看这两个函数究竟干了什么。

函数 sub_4010F0

这个函数由于是预处理,我们可以直接将这段伪代码转换成能执行的 C 语言代码,运行求解即可。转换后的代码:

int 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 = *(int *)(4 * i + a1);    if (a2 < result && i < result)    {      do      {        if (v6 > *(int *)(a1 + 4 * result))        {          if (i >= result)            break;          ++i;          *(int *)(v5 + a1) = *(int *)(a1 + 4 * result);          if (i >= result)            break;          while (*(int *)(a1 + 4 * i) <= v6)          {            if (++i >= result)              goto LABEL_13;          }          if (i >= result)            break;          v5 = 4 * i;          *(int *)(a1 + 4 * result) = *(int *)(4 * i + a1);        }        --result;      } while (i < result);    }  LABEL_13:    *(int *)(a1 + 4 * result) = v6;    sub_4010F0(a1, a2, i - 1);    result = a3;    ++i;  }  return result;}

至于转换得到方法,其实也很简单,就是将一些不属于 C 的变量类型换掉就可以了,这个网上有不少博客都有总结,可以自行百度。

这个程序因为是 32 位程序,所以这个函数只能在32位环境下才能正常运行,要不然会因为64位指针类型和32位指针类型占用的空间大小不一样,可能导致运行出错或是无法编译。

64位系统要如何编译32位程序可以看下面这个博客 https://blog.csdn.net/x356982611/article/details/79056089

运行后用 debug 查看结果如下:

https://i.loli.net/2021/01/18/IuvoYNyViszgkwS.png

然后根据判断条件,就得到了开头的几个输入。

没准这个函数就是排序函数呢,不过这么大串代码不会真的有人去读吧

函数 sub_401000

接下来我们就来分析这个函数。这个函数因为是对输入加密,所以比较复杂,但是这个函数的代码也是相当复杂,如果我们对这个函数的程序一行一行去读,估计也是读不出来什么的。所以我们按照上面所说的,将函数的伪代码转换成能运行的C代码。