2019 安洵杯 Re 部分WP

0x01.EasyEncryption

测试文件:https://www.lanzous.com/i7soysb

 

1.IDA打开

int sub_416560()
{
  int v0; // eax
  int v1; // edx
  int v2; // edx
  int v3; // ecx
  int v4; // ST08_4
  char v6[4]; // [esp+310h] [ebp-7E0h]
  char Str; // [esp+700h] [ebp-3F0h]
  int v8; // [esp+AECh] [ebp-4h]
  int savedregs; // [esp+AF0h] [ebp+0h]


  sub_41132A((int)&unk_424091);
  sub_411294(std::cout, "输入flag: ");
  sub_41113B(std::cin, &Str);
  sub_4111F4(&Str, (int)v6);                    // v6="artqkoehqpkbihv"
  if ( (unsigned __int8)sub_4112B7(v6) )
  {
    sub_41105F("you are right\n");
    system("pause");
    sub_411339(v3, v2);
  }
  else
  {
    v0 = sub_411294(std::cout, "wrong");
    sub_41114A(v0, 10);
  }
  v4 = v1;
  sub_41137A(&savedregs, &dword_416660, 0);
  return sub_411339((unsigned int)&savedregs ^ v8, v4);
}

 

2.代码分析

输入flag之后,首先经过sub_4111F4(&Str, (int)v6)函数处理,再经过sub_4112B7(v6)函数判断。因为v6未知,所以首先通过最后一个函数找到v6 

 

2.1 sub_4112B7函数分析

打开sub_4112B7函数

int __cdecl sub_413980(char *Str)
{
  int v1; // edx
  int v2; // ecx
  int v3; // edx
  int v4; // ecx
  int v5; // edx
  int v6; // ecx
  char *Str1; // [esp+D0h] [ebp-8h]


  sub_41132A((int)&unk_42405B);
  Str1 = (char *)sub_4112D5(Str);               // Base64
  if ( !j_strcmp(Str1, "YXJ0cWtvZWhxcGtiaWh2") )
  {
    free(Str1);
    sub_411339(v6, v5);
  }
  else
  {
    free(Str1);
    sub_411339(v2, v1);
  }
  return sub_411339(v4, v3);
}

通过分析得到,sub_4112D5函数是Base6加密,因此这个函数实际上就是将Str进行Base64加密,得到字符串Str1与"XJ0cWtvZWhxcGtiaWh2"比较。将"XJ0cWtvZWhxcGtiaWh2"进行Base64解密,得到Str,即v6="artqkoehqpkbihv"

 

2.2 sub_4111F4函数分析

接着,打开sub_4111F4函数,太长了,就不贴出来了。这个函数,分为三个部分:

0x01 第一部分:

 for ( i = 0; byte_41EC80[i]; ++i )
  {
    v16 = (unsigned __int8)byte_41EC80[i] - 97;
    v2 = j_abs(v16);
    v15[i] = v2;
  }

 

byte_41EC80已知,可以写出脚本变换,得到v15

Serial = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"


Li = []


for i in Serial:
    Li.append(abs(ord(i) - 97)

 

0x02 第二部分

 v10 = j_strlen(Str);
  if ( v10 % i <= 0 )
  {
    v3 = v10 % i;                               // 余数
    v9 = v10 / i;                               //
  }
  else
  {
    v3 = v10 % i;
    v9 = v10 / i + 1;
  }

 

0x03 第三部分

  for ( j = 0; j < v9; ++j )
  {
    for ( k = 0; k < i; ++k )
    {
      if ( Str[v14] )                           // 输入的字符值不能为0
      {
        if ( Str[v14] < 97 || Str[v14] > 122 )  // 输入只能是小写字母
          exit(1);
        if ( (Str[v14] + v15[k] - 97) % 26 + 97 > 122 )
          v4 = (Str[v14] + v15[k] - 97) % 26 + 71;
        else
          v4 = (Str[v14] + v15[k] - 97) % 26 + 97;
        v8 = v4;
        v3 = v14 + a2;
        *(_BYTE *)(v14++ + a2) = v8;
      }
    }
  }

第二部分,第三部分和v6,结合分析,可以得到输入的flag应该是15,那么v9=1,v3=15,而第三部分实际上就是一个根据输入字符,进行选择变换得到v14(即v6),因为输入只能是小写字母,那我逆向变换的时候,只要得到的是小写字母,那就行了

 

3.脚本解密

Serial = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"


Li = []


for i in Serial:
    Li.append(abs(ord(i) - 97))
print(Li)


t1 = len(Li) - 1


enc = "artqkoehqpkbihv"
v10 = len(enc)
v14 = 0
Str = ''
for j in range(1):
    for k in range(v10):
        for n in range(10):
            tmp1 = ord(enc[v14]) - 71 + 26 * n + 97 - Li[k]
            tmp2 = ord(enc[v14]) - 97 + 26 * n + 97 - Li[k]
            if tmp1 >= 97 and tmp1 <= 122:
                print ("SUCCESS!")
                Str += chr(tmp1)
                break
            elif tmp2 >= 97 and tmp2 <= 122:
                print ("SUCCESS!")
                Str += chr(tmp2)
                break
            else:
                print("ERROR!")
        v14 = v14 + 1
print (Str)
 
 

4.get flag!

flag{umpnineissogood}

 

 0x02 crackme

测试文件:https://www.lanzous.com/i7sucre

 

1.函数定位

首先定位到起始的函数sub_412AB0

 1 int __stdcall sub_412AB0(int a1, int a2, int a3, int a4)
 2 {
 3   size_t i; // [esp+D8h] [ebp-8h]
 4 
 5   for ( i = 0; i < j_strlen(BASE64_table_41A080); ++i )
 6   {
 7     if ( BASE64_table_41A080[i] <= 122 && BASE64_table_41A080[i] >= 97 )
 8     {
 9       BASE64_table_41A080[i] -= 32;
10     }
11     else if ( BASE64_table_41A080[i] <= 90 && BASE64_table_41A080[i] >= 65 )
12     {
13       BASE64_table_41A080[i] += 32;
14     }
15   }
16   MessageBoxA(0, "hooked", "successed", 0);
17   AddVectoredExceptionHandler(0, Handler);
18   return 0;
19 }

 

2.程序分析

2.1 Base64变表1

根据起始函数的第一个for循环,这里首先对Base64的表进行的变换,将大小写进行对换。

#include 
#include <string>


using namespace std;

string Str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

int main()
{
    char v2;

    size_t i; // [esp+D8h] [ebp-8h]

    for (i = 0; i < Str.length(); ++i)
    {
        if (Str[i] <= 122 && Str[i] >= 97)
        {
            Str[i] -= 32;
        }
        else if (Str[i] <= 90 && Str[i] >= 65)
        {
            Str[i] += 32;
        }
    }

    cout << Str << endl;

    system("PAUSE");
    return 0;
}
Base64大小写变换

得到新base64表"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"

 

2.2 向量化异常处理

SetUnhandledExceptionFilter()函数:https://baike.baidu.com/item/SetUnhandledExceptionFilter/2334228?fr=aladdin

AddVectoredExceptionHandler()函数:https://www.cnblogs.com/suanguade/p/6674232.html

 

接着使用向量化异常处理,进入 Handler

 1 int __stdcall Handler_0(_DWORD **a1)
 2 {
 3   char v2; // [esp+D0h] [ebp-18h]
 4   char v3; // [esp+D1h] [ebp-17h]
 5   char v4; // [esp+D2h] [ebp-16h]
 6   char v5; // [esp+D3h] [ebp-15h]
 7   char v6; // [esp+D4h] [ebp-14h]
 8   char v7; // [esp+D5h] [ebp-13h]
 9   char v8; // [esp+D6h] [ebp-12h]
10   char v9; // [esp+D7h] [ebp-11h]
11   char v10; // [esp+D8h] [ebp-10h]
12   char v11; // [esp+D9h] [ebp-Fh]
13   char v12; // [esp+DAh] [ebp-Eh]
14   char v13; // [esp+DBh] [ebp-Dh]
15   char v14; // [esp+DCh] [ebp-Ch]
16   char v15; // [esp+DDh] [ebp-Bh]
17   char v16; // [esp+DEh] [ebp-Ah]
18   char v17; // [esp+DFh] [ebp-9h]
19 
20   if ( **a1 == -1073741819 )
21   {
22     v2 = 'w';
23     v3 = 'h';
24     v4 = 'e';
25     v5 = 'r';
26     v6 = 'e';
27     v7 = '_';
28     v8 = 'a';
29     v9 = 'r';
30     v10 = 'e';
31     v11 = '_';
32     v12 = 'u';
33     v13 = '_';
34     v14 = 'n';
35     v15 = 'o';
36     v16 = 'w';
37     v17 = '?';
38     sm4_func((int)&unk_41A218, (int)&v2);
39     SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)TopLevelExceptionFilter);
40   }
41   return 0;
42 }

 

2.2.1 sm4加密

pysm4包下载安装教程:https://www.cnblogs.com/Mayfly-nymph/p/11939336.html

进入sm4_func函数(这个我改过名了)

int __cdecl sub_411F50(int a1, unsigned int *a2)
{
  unsigned int v2; // eax
  int v3; // ecx
  int result; // eax
  unsigned int v5; // [esp+D0h] [ebp-B8h]
  unsigned int v6; // [esp+DCh] [ebp-ACh]
  unsigned int v7; // [esp+E0h] [ebp-A8h]
  unsigned int v8; // [esp+E4h] [ebp-A4h]
  int v9; // [esp+E8h] [ebp-A0h]
  int v10[34]; // [esp+ECh] [ebp-9Ch]
  unsigned int v11; // [esp+174h] [ebp-14h]
  unsigned int v12; // [esp+178h] [ebp-10h]
  unsigned int v13; // [esp+17Ch] [ebp-Ch]
  unsigned int v14; // [esp+180h] [ebp-8h]

  v5 = 0;
  v11 = _byteswap_ulong(*a2);
  v12 = _byteswap_ulong(a2[1]);
  v13 = _byteswap_ulong(a2[2]);
  v2 = _byteswap_ulong(a2[3]);
  v14 = v2;
  v6 = v11 ^ 0xA3B1BAC6;
  v7 = dword_417A68[1] ^ v12;
  v8 = dword_417A68[2] ^ v13;
  v3 = dword_417A68[3] ^ v2;
  result = 12;
  v9 = v3;
  while ( v5 < 0x20 )
  {
    v10[v5] = *(&v6 + v5) ^ sub_4114E0(dword_417A78[v5] ^ *(&v9 + v5) ^ *(&v8 + v5) ^ *(&v7 + v5));
    *(_DWORD *)(a1 + 4 * v5) = v10[v5];
    result = v5++ + 1;
  }
  return result;
}

Google搜了下0xA3B1BAC6

 2019 安洵杯 Re 部分WP_第1张图片

发现了这个函数应该是sm4加密

 

2.2.2 异常处理

接着,使用SetUnhandledExceptionFilter捕获异常,进入TopLevelExceptionFilter

 1 _DWORD *__cdecl sub_412C30(_DWORD *a1)
 2 {
 3   _DWORD *result; // eax
 4   char v2; // STD7_1
 5   size_t i; // [esp+DCh] [ebp-8h]
 6 
 7   result = a1;
 8   if ( *(_DWORD *)*a1 == -1073741819 )
 9   {
10     for ( i = 0; i < j_strlen(Str2); i += 2 )
11     {
12       v2 = Str2[i];
13       Str2[i] = Str2[i + 1];
14       Str2[i + 1] = v2;
15     }
16     Str1 = sub_41126C(byte_41A180);
17     *(_DWORD *)(a1[1] + 176) = *(_DWORD *)(*a1 + 20);
18     *(_DWORD *)(a1[1] + 164) = *(_DWORD *)(*a1 + 24);
19     *(_DWORD *)(a1[1] + 172) = *(_DWORD *)(*a1 + 28);
20     *(_DWORD *)(a1[1] + 168) = *(_DWORD *)(*a1 + 32);
21     *(_DWORD *)(a1[1] + 156) = *(_DWORD *)(*a1 + 36);
22     *(_DWORD *)(a1[1] + 160) = *(_DWORD *)(*a1 + 40);
23     *(_DWORD *)(a1[1] + 184) = sub_411136;
24     result = (_DWORD *)-1;
25   }
26   return result;
27 }

第一个for循环就是在将Str2相邻两位互换

#include 
#include <string>

using namespace std;

string Str2 = "1UTAOIkpyOSWGv/mOYFY4R!!";

int main()
{
    char v2;

    for (int i = 0; i < Str2.length(); i += 2)
    {
        v2 = Str2[i];
        Str2[i] = Str2[i + 1];
        Str2[i + 1] = v2;
    }

    cout << Str2 << endl;

    system("PAUSE");
    return 0;
}
Str2相邻位变换

 

接着通过sub_41126C函数传入byte_41A180,返回值为Str2,最后将Str1与Str2比较是否相同,输出success或者error,这就很明显了,肯定要输出‘success’,那Str2就要等于'U1ATIOpkOyWSvGm/YOYFR4!!'

 

2.2.3 Base64变表2

打开sub_41126C函数

 1 _BYTE *__cdecl sub_413090(char *Str)
 2 {
 3   int k; // [esp+E4h] [ebp-5Ch]
 4   int v3; // [esp+F0h] [ebp-50h]
 5   signed int j; // [esp+FCh] [ebp-44h]
 6   int v5; // [esp+108h] [ebp-38h]
 7   signed int i; // [esp+114h] [ebp-2Ch]
 8   _BYTE *v7; // [esp+120h] [ebp-20h]
 9   signed int v8; // [esp+12Ch] [ebp-14h]
10   int v9; // [esp+138h] [ebp-8h]
11 
12   v5 = 0;
13   v8 = j_strlen(Str);                           // Str长度
14   if ( v8 % 3 )                                 // 设置字节长度
15     v9 = 4 * (v8 / 3) + 4;
16   else
17     v9 = 4 * (v8 / 3);
18   v7 = malloc((v9 + 1) | -__CFADD__(v9, 1));    // 申请空间
19   v7[v9] = 0;
20   for ( i = 0; i < v8; i += 3 )                 // 三位三位的处理,base64?
21   {
22     v3 = 0;
23     for ( j = 0; j < 3; ++j )
24       v3 |= (unsigned __int8)Str[j + i] << 8 * (2 - j);
25     for ( k = 0; k < 4; ++k )
26     {
27       if ( k >= 4 - (i + 3 - v8) && i + 3 > v8 )// 长度大于等于4的部分使用'!'补齐
28         v7[v5] = '!';
29       else
30         v7[v5] = ::Str[sub_4110FF((v3 >> 6 * (3 - k)) & 0x3F)];
31       ++v5;
32     }
33   }
34   return v7;
35 }

在函数sub_4110FF有一步不同的操作

int __cdecl sub_412760(int a1)
{
  return (a1 + 24) % 64;
}

这里+24,相当于变表的值为原表取值右移24位

Str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"

Str1 = ''

for i in Str:
    Str1 += Str[(Str.find(i)+24) % len(Str)]
print (Str1)

得到新的Base64表"yzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/abcdefghijklmnopqrstuvwx"

 

到这我们整个过程就分析完毕了,接下来只需要逆向操作就行了。

 

3.逆向解密

# encoding: utf-8
from pysm4 import decrypt,encrypt
from base64 import b64decode

Str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"
Str_ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="

Str1 = ''

# 变换之后的Base64表
for i in Str:
    Str1 += Str[(Str.find(i)+24) % len(Str)]
print (Str1)

# 之前是用变换之后的表进行Base64加密
# 现在转换为正常的Base64加密,
# 再解密得到sm4加密之后的字符串
enc = "U1ATIOpkOyWSvGm/YOYFR4!!"
dec1 = ''
for i in range(len(enc)):
    if enc[i] == '!':
        dec1 += '='
    else:
        dec1 += Str_[Str1.find(enc[i])]
print (dec1)

dec2 = b64decode(dec1)
print (dec2)

import codecs

# 先进行hex编码,再转换为十进制整型
encode_hex = codecs.getencoder("hex_codec")
dec3 = int(encode_hex(dec2)[0],16)
print (dec3)

# 将key转换为十进制
key = "where_are_u_now?"
key = int(encode_hex(key)[0],16)
print (key)

# 将解密的字符串转换会hex
decode_hex = codecs.getdecoder("hex_codec")
dec4 = decrypt(dec3, key)
dec4 = str(hex(dec4)[2:-1])

print (decode_hex(dec4)[0])

 

4. get flag!

 flag{SM4foRExcepioN?!}

 

官方公布源码:https://github.com/D0g3-Lab/i-SOON_CTF_2019

其他WP:https://blog.csdn.net/Zarcs_233/article/details/103338006

 

这次进入线下,希望能够好好发挥,加油!

你可能感兴趣的:(2019 安洵杯 Re 部分WP)