BUU [ACTF新生赛2020]Universe_final_answer

[ACTF新生赛2020]Universe_final_answer

首先查壳, 64bit 无壳

ida64位打开

main()

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  __int64 v4; // [rsp+0h] [rbp-A8h] BYREF
  char v5[104]; // [rsp+20h] [rbp-88h] BYREF
  unsigned __int64 v6; // [rsp+88h] [rbp-20h]
​
  v6 = __readfsqword(0x28u);
  __printf_chk(1LL, "Please give me the key string:", a3);
  scanf("%s", v5);
  if ( (unsigned __int8)sub_860(v5) )
  {
    sub_C50(v5, &v4);
    __printf_chk(1LL, "Judgement pass! flag is actf{%s_%s}\n", v5);
  }
  else
  {
    puts("False key!");
  }
  return 0LL;
}

输入的内容是在v5里,然后在if里面要经过sub_860函数处理

sub_860()

bool __fastcall sub_860(char *a1)
{
  int v1; // ecx
  int v2; // esi
  int v3; // edx
  int v4; // er9
  int v5; // er11
  int v6; // ebp
  int v7; // ebx
  int v8; // er8
  int v9; // er10
  bool result; // al
  int v11; // [rsp+0h] [rbp-38h]
​
  v1 = a1[1];
  v2 = *a1;
  v3 = a1[2];
  v4 = a1[3];
  v5 = a1[4];
  v6 = a1[6];
  v7 = a1[5];
  v8 = a1[7];
  v9 = a1[8];
  result = 0;
  if ( -85 * v9 + 58 * v8 + 97 * v6 + v7 + -45 * v5 + 84 * v4 + 95 * v2 - 20 * v1 + 12 * v3 == 12613 )
  {
    v11 = a1[9];
    if ( 30 * v11 + -70 * v9 + -122 * v6 + -81 * v7 + -66 * v5 + -115 * v4 + -41 * v3 + -86 * v1 - 15 * v2 - 30 * v8 == -54400
      && -103 * v11 + 120 * v8 + 108 * v7 + 48 * v4 + -89 * v3 + 78 * v1 - 41 * v2 + 31 * v5 - (v6 << 6) - 120 * v9 == -10283
      && 71 * v6 + (v7 << 7) + 99 * v5 + -111 * v3 + 85 * v1 + 79 * v2 - 30 * v4 - 119 * v8 + 48 * v9 - 16 * v11 == 22855
      && 5 * v11 + 23 * v9 + 122 * v8 + -19 * v6 + 99 * v7 + -117 * v5 + -69 * v3 + 22 * v1 - 98 * v2 + 10 * v4 == -2944
      && -54 * v11 + -23 * v8 + -82 * v3 + -85 * v2 + 124 * v1 - 11 * v4 - 8 * v5 - 60 * v7 + 95 * v6 + 100 * v9 == -2222
      && -83 * v11 + -111 * v7 + -57 * v2 + 41 * v1 + 73 * v3 - 18 * v4 + 26 * v5 + 16 * v6 + 77 * v8 - 63 * v9 == -13258
      && 81 * v11 + -48 * v9 + 66 * v8 + -104 * v6 + -121 * v7 + 95 * v5 + 85 * v4 + 60 * v3 + -85 * v2 + 80 * v1 == -1559
      && 101 * v11 + -85 * v9 + 7 * v6 + 117 * v7 + -83 * v5 + -101 * v4 + 90 * v3 + -28 * v1 + 18 * v2 - v8 == 6308 )
    {
      return 99 * v11 + -28 * v9 + 5 * v8 + 93 * v6 + -18 * v7 + -127 * v5 + 6 * v4 + -9 * v3 + -93 * v1 + 58 * v2 == -1697;
    }
  }
  return result;
}

第一看看到这个函数觉得挺离谱的,一个if里面塞了这么多东西,要计算的话肯定不能自己手算,还是得用python写脚本吧,但是就这样子的脚本写起来也够呛的,逻辑挺简单的,但是难就难在怎么计算

通过观察发现这东西就是一个矩阵,十阶矩阵,要是我手算的话我肯定这辈子都算不出来。。。

查了查wp,发现他们都是使用了z3这个python的库来解决这个方程的

利用python的Z3库可以进行约束求解,即解任何方程(只要有解),常用的包括整数求解、有理数求解、位向量求解(二进制位运算求解)。

直接在vscode里面pip安装z3这个库,然后写脚本

from z3 import *
s = Solver()
v1 = Int('v1')
v2 = Int('v2')
v3 = Int('v3')
v4 = Int('v4')
v5 = Int('v5')
v6 = Int('v6')
v7 = Int('v7')
v8 = Int('v8')
v9 = Int('v9')
v11 = Int('v11')
​
s.add(-85 * v9 + 58 * v8 + 97 * v6 + v7 + -45 * v5 + 84 * v4 + 95 * v2 - 20 * v1 + 12 * v3 == 12613)
s.add(30 * v11 + -70 * v9 + -122 * v6 + -81 * v7 + -66 * v5 + -115 * v4 + -41 * v3 + -86 * v1 - 15 * v2 - 30 * v8 == -54400)
s.add(-103 * v11 + 120 * v8 + 108 * v7 + 48 * v4 + -89 * v3 + 78 * v1 - 41 * v2 + 31 * v5 - (v6 * 64) - 120 * v9 == -10283)
s.add(71 * v6 + (v7 * 128) + 99 * v5 + -111 * v3 + 85 * v1 + 79 * v2 - 30 * v4 - 119 * v8 + 48 * v9 - 16 * v11 == 22855)
s.add(5 * v11 + 23 * v9 + 122 * v8 + -19 * v6 + 99 * v7 + -117 * v5 + -69 * v3 + 22 * v1 - 98 * v2 + 10 * v4 == -2944)
s.add(-54 * v11 + -23 * v8 + -82 * v3 + -85 * v2 + 124 * v1 - 11 * v4 - 8 * v5 - 60 * v7 + 95 * v6 + 100 * v9 == -2222)
s.add(-83 * v11 + -111 * v7 + -57 * v2 + 41 * v1 + 73 * v3 - 18 * v4 + 26 * v5 + 16 * v6 + 77 * v8 - 63 * v9 == -13258)
s.add(81 * v11 + -48 * v9 + 66 * v8 + -104 * v6 + -121 * v7 + 95 * v5 + 85 * v4 + 60 * v3 + -85 * v2 + 80 * v1 == -1559)
s.add(101 * v11 + -85 * v9 + 7 * v6 + 117 * v7 + -83 * v5 + -101 * v4 + 90 * v3 + -28 * v1 + 18 * v2 - v8 == 6308)
s.add(99 * v11 + -28 * v9 + 5 * v8 + 93 * v6 + -18 * v7 + -127 * v5 + 6 * v4 + -9 * v3 + -93 * v1 + 58 * v2 == -1697)
​
if s.check() == sat:
    result = s.model()
​
print(result)
​

得到了最后的结果

[v1 = 48,
 v6 = 95,
 v2 = 70,
 v4 = 82,
 v11 = 64,
 v3 = 117,
 v5 = 84,
 v7 = 121,
 v9 = 119,
 v8 = 55]

按顺序排列,然后在转换成字符之前要注意到,v1和v2,v6和v7他们的位置需要交换一下。

得到结果

F0uRTy_7w@

接下来是sub_C50函数处理

sub_C50()

unsigned __int64 __fastcall sub_C50(const char *a1, _BYTE *a2)
{
  size_t v4; // rax
  unsigned int v5; // edx
  int v6; // edi
  int v7; // ecx
  __int64 v8; // r8
  __int128 *v9; // rsi
  unsigned int v10; // ecx
  int v11; // eax
  int v12; // edi
  int v13; // edx
  int v14; // eax
  _BYTE *v15; // rsi
  _BYTE *v16; // rcx
  _BYTE *v17; // r8
  int *i; // rax
  unsigned __int64 result; // rax
  __int128 v20[2]; // [rsp+0h] [rbp-48h] BYREF
  __int64 v21; // [rsp+20h] [rbp-28h]
  unsigned __int64 v22; // [rsp+28h] [rbp-20h]
​
  v22 = __readfsqword(0x28u);
  memset(v20, 0, sizeof(v20));
  v21 = 0LL;
  v4 = strlen(a1);                  //10
  v5 = 0;
  v6 = 9;
  while ( v5 < v4 )                 // 0 < 10
  {
    v7 = a1[v5++];
    v6 ^= v7;
  }
  if ( v6 )                         //v6初始值为64
  {
    v8 = 0LL;
    v9 = v20;                       //数组首地址
    while ( 1 )
    {
      v9 = (__int128 *)((char *)v9 + 4);    //v9 = v20[0] + 4
      v10 = v8 + 1;                         //v10 = 1LL
      v11 = v6 / 10;                        //v11 = 6
      v12 = v6 % 10;                        //v12 = 4
      *((_DWORD *)v9 - 1) = v12;            
      LOBYTE(v13) = v12;
      v6 = v11;
      if ( !v11 )
        break;
      v8 = v10;
    }
    v14 = v8 - 1;
    v15 = a2;
    v16 = &a2[v10];
    v17 = &a2[v8];
    for ( i = (int *)v20 + v14; ; --i )
    {
      *v15 = v13 + 48;
      if ( v17 == v15 )
        break;
      v13 = *i;
      ++v15;
    }
  }
  else
  {
    v16 = a2;
  }
  result = __readfsqword(0x28u) ^ v22;
  *v16 = 0;
  return result;
}

尝试静态分析,但是这个这个函数分析起来真的不太容易,回去再仔细看看main函数

 if ( (unsigned __int8)sub_860(v5) )
  {
    sub_C50(v5, &v4);
    __printf_chk(1LL, "Judgement pass! flag is actf{%s_%s}\n", v5);
  }

这里我们得到了正确的v5,经过sub_C50函数处理后,下一步是一个printf操作,直接给出我们flag,所以直接放在Ubuntu里面运行

BUU [ACTF新生赛2020]Universe_final_answer_第1张图片

得到了flag

flag{F0uRTy_7w@_42}

确实是在这道题里学到了新的东西,有些逆向不好写的算法可以直接用z3正向求解(但是确实是水题)

sub_C50函数的动态分析:

在进入sub_C50函数前下断点,然后输入F0uRTy_7w@,alt+t在16进制区域找到该字符串,然后开始F7单步步入,观察该字符串的变化,但是在循环中出现了这样的问题。

BUU [ACTF新生赛2020]Universe_final_answer_第2张图片

这边行不通,直接在printf函数处下断点,F7单步步入找到输出flag的关键跳转

BUU [ACTF新生赛2020]Universe_final_answer_第3张图片

这一步打印操作是在syscall这,但是它为什么能打印出来这些,还有为什么__printf_chk有两个%s%s却只有v5,但是却能打印出来

问题还没有完全被解决,应该还有后续,(其实是动态分析还没玩明白。。。)

你可能感兴趣的:(buuctf,reverse,ctf,安全,c语言)