2024春秋杯网络安全联赛夏季赛

2024春秋杯网络安全联赛夏季赛 Reverse wp

前言

时隔半年,年头冬季赛只会签到的笨蛋也是在夏季赛ak了,这里记录一下wp

Reverse[3/3]

snack

题目描述

题目内容:

你也想玩蛇吗?

附件下载 提取码(GAME)备用下载

简单的签到题,pyinstaller打包的程序,先pyinstxtractor.py解包,注意要用python3.8版本

然后pycdc反编译snake.pyc,把解密代码扣下来跑一遍就好了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
xor_key = 'V3rY_v3Ry_Ez'

def initialize(key):
    key_length = len(key)
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % key_length]) % 256
        S[i], S[j] = (S[j], S[i])
    return S

def generate_key_stream(S, length):
    i = 0
    j = 0
    key_stream = []
    for _ in range(length):
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = (S[j], S[i])
        key_stream.append(S[(S[i] + S[j]) % 256])
    return key_stream

def decrypt(data, key):
    S = initialize(key)
    key_stream = generate_key_stream(S, len(data))
    decrypted_data = bytes([i ^ data[i] ^ key_stream[i] for i in range(len(data))])
    return decrypted_data

key_bytes = bytes([ord(char) for char in xor_key])
data = [101, 97, 39, 125, 218, 172, 205, 3, 235, 195, 72, 125, 89, 130, 103, 213, 120, 227, 193, 67, 174, 71, 162, 248, 244, 12, 238, 92, 160, 203, 185, 155]
decrypted_data = decrypt(bytes(data), key_bytes)
print(decrypted_data.decode())
# flag{KMLTz3lT_MePUDa7A_P5LpzCBT}

HardSignin

题目描述

题目内容:

这是一道困难的签到题

附件下载 提取码(GAME)备用下载

也不是很困难,就只是修改了upx特征,套了一堆反调试花指令加密算法,耐心分析就好了

0x00 upx脱壳

用010 Editor,将0x1E0,0x208,0x3E0三个位置的VMP修改回UPX,然后upx -d即可脱壳

0x01 去除花指令

ida打开分析,发现前面有一大片红,三个Tls回调函数中有花指令,需要去除一下

0x004010420x004011340x00401042 三个地址的单个字节改成90(nop)即可

0x02 分析Tls回调函数

因为Tls回调函数早于main函数执行,所以我们需要先去分析他

TlsCallback_0

TlsCallback_0中存在一个IsDebuggerPresent反调试,nop掉即可

下面就是一个SMC,将main函数前170个字节每位异或0x66来解密

然后初始化随机数种子0x114514

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
void __stdcall TlsCallback_0(int a1, int a2, int a3)
{
  SIZE_T i; // [esp+50h] [ebp-10h]
  DWORD flOldProtect; // [esp+54h] [ebp-Ch] BYREF
  SIZE_T dwSize; // [esp+58h] [ebp-8h]
  LPVOID lpAddress; // [esp+5Ch] [ebp-4h]

  if ( a2 == 1 )
  {
    if ( IsDebuggerPresent() )
      exit(0);
    lpAddress = main;
    dwSize = 170;
    flOldProtect = 0;
    VirtualProtect(main, 0xAAu, 0x40u, &flOldProtect);
    for ( i = 0; i < dwSize; ++i )
      *((_BYTE *)lpAddress + i) ^= 0x66u;
    srand(0x114514u);
  }
}

TlsCallback_1

TlsCallback_1中也是存在一个CheckRemoteDebuggerPresent反调试,一样直接nop掉即可

下面是在打乱base64码表

注意最后又初始化随机数种子为0x1919810

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void __stdcall TlsCallback_1(int a1, int a2, int a3)
{
  HANDLE CurrentProcess; // eax
  int v4; // eax
  int i; // [esp+50h] [ebp-10h]
  int v6; // [esp+58h] [ebp-8h]
  BOOL pbDebuggerPresent; // [esp+5Ch] [ebp-4h] BYREF

  pbDebuggerPresent = 0;
  if ( a2 == 1 )
  {
    CurrentProcess = GetCurrentProcess();
    CheckRemoteDebuggerPresent(CurrentProcess, &pbDebuggerPresent);
    if ( pbDebuggerPresent )
      exit(0);
    for ( i = 0; i < 100; ++i )
    {
      v6 = rand() % 64;
      v4 = rand() % 64;
      swap(&base64_table[v6], &base64_table[v4]);
    }
    srand(0x1919810u);
  }
}

TlsCallback_2

这里前面还是一个反调试,同理

下面则是在初始化rc4和xtea的key,在后面会分析到的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int __stdcall TlsCallback_2(int a1, int a2, int a3)
{
  int result; // eax
  HANDLE CurrentProcess; // eax
  int i; // [esp+50h] [ebp-18h]
  NTSTATUS (__stdcall *NtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); // [esp+58h] [ebp-10h]
  HMODULE hModule; // [esp+5Ch] [ebp-Ch]
  int v8; // [esp+64h] [ebp-4h] BYREF

  v8 = 0;
  result = a2;
  if ( a2 == 1 )
  {
    hModule = LoadLibraryA("Ntdll.dll");
    NtQueryInformationProcess = (NTSTATUS (__stdcall *)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG))GetProcAddress(hModule, "NtQueryInformationProcess");
    CurrentProcess = GetCurrentProcess();
    NtQueryInformationProcess(CurrentProcess, ProcessDebugPort, &v8, 4, 0);
    if ( v8 )
      exit(0);
    for ( i = 0; ; ++i )
    {
      result = i;
      if ( i >= size )
        break;
      rc4_key[i] = rand() % 255;
      xtea_key[i] = rand() % 255;
    }
  }
  return result;
}

TlsCallback_3

不知道有什么用,好像并不影响做题,略过吧哈哈

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
int __stdcall TlsCallback_3(int a1, int a2, int a3)
{
  int result; // eax
  HANDLE CurrentThread; // eax
  FARPROC ZwSetInformationThread; // [esp+50h] [ebp-8h]
  HMODULE hModule; // [esp+54h] [ebp-4h]

  result = a2;
  if ( a2 == 1 )
  {
    hModule = GetModuleHandleA("Ntdll");
    ZwSetInformationThread = GetProcAddress(hModule, "ZwSetInformationThread");
    CurrentThread = GetCurrentThread();
    return ((int (__stdcall *)(HANDLE, int, _DWORD, _DWORD))ZwSetInformationThread)(CurrentThread, 17, 0, 0);
  }
  return result;
}

0x03 分析main函数和check

动态执行完SMC的解密代码,即可看到main函数的逻辑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int size; // [esp+4Ch] [ebp-6Ch]
  char input[100]; // [esp+50h] [ebp-68h] BYREF

  memset(input, 0, sizeof(input));
  printf((char *)0xF93180);
  scanf((char *)0xF93198, input);
  size = strlen(input);
  if (check(input, size) )
    printf((char *)0xF9319C);
  else
    printf((char *)0xF931B0);
  MEMORY[0xF930B8](0xF931C4);
  return 0;
}

分析一下check函数,先将输入内容进行base64编码,注意这里的base64在Tls回调函数中已被打乱

然后rc4加密,最后xtea加密,这里的key都在TlsCallback_2中初始化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
BOOL __cdecl check(BYTE *input, int a2)
{
  BYTE *data; // [esp+50h] [ebp-4h]

  data = base64(input, a2);
  rc4_crypt(data, 4 * (a2 / 3), rc4_key, size);
  xtea(rounds, (DWORD *)data, &key);
  return memcmp(data, enc_data, Size) == 0;
}

void __cdecl rc4_init(BYTE *a1, int a2)
{
  int k; // [esp+4Ch] [ebp-114h]
  int v3; // [esp+50h] [ebp-110h]
  int j; // [esp+54h] [ebp-10Ch]
  BYTE v5[256]; // [esp+58h] [ebp-108h] BYREF
  int i; // [esp+158h] [ebp-8h]

  for ( i = 0; i < 256; ++i )
    s_box[i] = i;
  memset(v5, 0, sizeof(v5));
  for ( j = 0; j < 256; ++j )
    v5[j] = a1[j % a2];
  v3 = 0;
  for ( k = 0; k < 256; ++k )
  {
    v3 = (v5[k] + v3 + s_box[k]) % 256;
    swap(&s_box[k], &s_box[v3]);
  }
}

void __cdecl rc4_crypt(BYTE *data, int ssize, BYTE *key, int size)
{
  int i; // [esp+4Ch] [ebp-14h]
  int v5; // [esp+54h] [ebp-Ch]
  int v6; // [esp+58h] [ebp-8h]

  v6 = 0;
  v5 = 0;
  rc4_init(key, size);
  for ( i = 0; i < ssize; ++i )
  {
    v6 = (v6 + 1) % 256;
    v5 = (v5 + s_box[v6]) % 256;
    swap(&s_box[v6], &s_box[v5]);
    data[i] ^= s_box[(s_box[v5] + s_box[v6]) % 256];
  }
}

void __cdecl xtea(unsigned int rounds, DWORD *data, DWORD *key)
{
  unsigned int j; // [esp+4Ch] [ebp-18h]
  unsigned int sum; // [esp+54h] [ebp-10h]
  DWORD v1; // [esp+58h] [ebp-Ch]
  DWORD v0; // [esp+5Ch] [ebp-8h]
  int i; // [esp+60h] [ebp-4h]

  for ( i = 0; i < 16; i += 2 )
  {
    v0 = data[i];
    v1 = data[i + 1];
    sum = 0;
    for ( j = 0; j < rounds; ++j )
    {
      v0 += (key[sum & 3] + sum) ^ (v1 + ((v1 >> 5) ^ (16 * v1)));
      sum -= 0x61C88647;
      v1 += (key[(sum >> 11) & 3] + sum) ^ (v0 + ((v0 >> 5) ^ (16 * v0)));
    }
    data[i] = v0;
    data[i + 1] = v1;
  }
}

0x04 exp

把代码扣下来,逆着逻辑解密即可

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#define BYTE unsigned char

unsigned int enc_data[16] = {
    0xB4FD1B59, 0xD9BEB86B, 0xD677D3B3, 0x185F65F0, 0x533A9DA0, 0x267B4A6D, 0x4E9C3A74, 0xD8194320,
    0xB595ED72, 0x5622059C, 0x91117ACB, 0x0CBC7A9F, 0xCE6D694A, 0x29ABB43D, 0x3262FA61, 0xB64CECB4
};

BYTE s_box[256];

void __cdecl swap(BYTE *a1, BYTE *a2)
{
  BYTE v2; // [esp+4Fh] [ebp-1h]

  v2 = *a1;
  *a1 = *a2;
  *a2 = v2;
}

void __cdecl rc4_init(BYTE *a1, int a2)
{
  int k;        // [esp+4Ch] [ebp-114h]
  int v3;       // [esp+50h] [ebp-110h]
  int j;        // [esp+54h] [ebp-10Ch]
  BYTE v5[256]; // [esp+58h] [ebp-108h] BYREF
  int i;        // [esp+158h] [ebp-8h]

  for (i = 0; i < 256; ++i)
    s_box[i] = i;
  memset(v5, 0, sizeof(v5));
  for (j = 0; j < 256; ++j)
    v5[j] = a1[j % a2];
  v3 = 0;
  for (k = 0; k < 256; ++k)
  {
    v3 = (v5[k] + v3 + s_box[k]) % 256;
    swap(&s_box[k], &s_box[v3]);
  }
}

void __cdecl rc4_crypt(BYTE *data, int ssize, BYTE *key, int size)
{
  int i;  // [esp+4Ch] [ebp-14h]
  int v5; // [esp+54h] [ebp-Ch]
  int v6; // [esp+58h] [ebp-8h]

  v6 = 0;
  v5 = 0;
  rc4_init(key, size);
  for (i = 0; i < ssize; ++i)
  {
    v6 = (v6 + 1) % 256;
    v5 = (v5 + s_box[v6]) % 256;
    swap(&s_box[v6], &s_box[v5]);
    data[i] ^= s_box[(s_box[v5] + s_box[v6]) % 256];
  }
}

void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t key[4])
{
  unsigned int i;
  uint32_t v0 = v[0], v1 = v[1], delta = 0x9E3779B9, sum = delta * num_rounds;
  for (i = 0; i < num_rounds; i++)
  {
    v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
    sum -= delta;
    v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
  }
  v[0] = v0;
  v[1] = v1;
}

BYTE base_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

int main()
{
  unsigned char rc4_key[16];
  unsigned char key[16];

  srand(0x114514u);
  for (size_t i = 0; i < 100; ++i)
  {
    int v6 = rand() % 64;
    int v4 = rand() % 64;
    swap(&base_table[v6], &base_table[v4]);
  }

  printf("base_table: %s\n", base_table);
  srand(0x1919810u);

  for (size_t i = 0; i < 16; ++i)
  {
    rc4_key[i] = rand() % 255;
    key[i] = rand() % 255;
  }

  for (size_t i = 0; i < 16; i += 2)
  {
    decipher(0x64, enc_data + i, (uint32_t *)key);
  }

  rc4_crypt((BYTE *)enc_data, 4 * (64 / 3), rc4_key, 16);

  for (size_t i = 0; i < 64; i++)
  {
    printf("%c", *((BYTE *)enc_data + i));
  }
}
/*
base_table: 4yZRiNP8LoK/GSA5ElWkUjXtJCz7bMYcuFfpm6+hV0rxeHIdwv32QOTnqg1BDsa9
C+vFCnHRGPghbmyQMXvFMRNd7fNCG8jcU+jcbnjRJTj2GTCOGUvgtOS0CTge7fNs
*/

最后得到了打乱后的表和base64编码后的flag,用CyberChef自定义码表解码一下就好了

flag: flag{C0ngr@tulat1on!Y0u_Re_suCces3fu1Ly_Signln!}

BEDTEA

题目描述

题目内容:

来一杯睡前tea

附件下载 提取码(GAME)备用下载

这题主要难点还是在于SSE指令吧,中间还夹杂着一些算法和数据结构,不过通过动调还是很容易分析出逻辑的

0x01 反调试

程序中有两处反调试 第一处是 IsDebuggerPresent,如果检测到在调试状态,则赋值为1,否则为3,

这处反调试决定了后面斐波那契数列的起始值,影响tea的key

1
fib_start = !IsDebuggerPresent() ? 3 : 1;

还有一处是时间反调试,如果你在这中间断了很久,超过了判定时间范围,就会认为你在调试他

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
begin_time = std::chrono::_V2::system_clock::now(v3);
...
if ( std::chrono::_V2::system_clock::now(v7) - begin_time <= 10000999 )
{
    ...
}
else  
{
    ... // 检测到被调试则执行else分支
}

0x02 加密算法分析

斐波那契数列计算key

程序首先会将输入分为三部分,用tea算法加密

并且key由计算斐波那契数列得出,三组的key都不一样

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

void main()
{
  ...
  fib_start = !IsDebuggerPresent() ? 3 : 1;     // 检测是否被调试,没有则为3,决定了斐波那契数列的起始值,影响tea的key
  tea((unsigned int *)&input);
  tea((unsigned int *)&input1);
  tea((unsigned int *)&input2);
  ...
}
void __fastcall tea(unsigned int *input)
{
  int v1; // ebp
  int *v2; // r12
  int v3; // r13d
  int v4; // ebx
  int v5; // r9d
  int v6; // edi
  int v7; // r11d
  int v8; // esi
  int v9; // eax
  int v10; // r8d
  int v11; // edx
  int v12; // eax
  unsigned int data1; // edx
  int sum; // r9d
  unsigned int data0; // r8d

  v1 = fib_start;
  v2 = &key0;
  *(__m128i *)&key0 = _mm_load_si128((const __m128i *)&xmmword_405050);
  v3 = fib_start + 4;
  do                                            // 计算斐波那契数列
  {
    v4 = v1;
    if ( v1 <= 0 )
    {
      v12 = 1;
    }
    else
    {
      v5 = 0;
      v6 = 0;
      v7 = 1;
      v8 = 1;
      v9 = 1;
      do
      {
        if ( (v4 & 1) != 0 )
        {
          v10 = v6;
          v6 = v7 * v8 + v5 * v6;
          v8 = v7 * v10 + v9 * v8;
        }
        v11 = v7 * v7;
        v7 *= v5 + v9;
        v5 = v11 + v5 * v5;
        v4 >>= 1;
        v9 = v9 * v9 + v11;
      }
      while ( v4 );
      v12 = v8;
    }
    ++v1;
    *v2++ = v12;
  }
  while ( v1 != v3 );
  fib_start = v1;
  data1 = input[1];
  sum = 0;
  data0 = *input;
  do
  {
    sum -= 0x61CBB648;
    data0 += (sum + data1) ^ (key1 + (data1 >> 4)) ^ (key0 + 32 * data1);
    data1 += (sum + data0) ^ (key3 + (data0 >> 4)) ^ (key2 + 32 * data0);
  }
  while ( sum != 0x987E55D0 );
  *input = data0;
  input[1] = data1;
}

正常在没有被检测到调试的情况下,数列起始值应该是3,那么就可以得到三组key的值了

1
2
3
4
5
6
7
8
9
unsigned int key1[4] = {
    3, 5, 8, 13
};
unsigned int key2[4] = {
    21, 34, 55, 89
};
unsigned int key3[4] = {
    144, 233, 377, 610
};

TEA

一个魔改的tea,修改了delta,rounds和移位等常量,应该一眼就能看出来

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
void decrypt(uint32_t *v, uint32_t *k)
{
    uint32_t v0 = v[0], v1 = v[1], i;
    uint32_t delta = -0x61cbb648;
    uint32_t sum = delta * 22;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
    for (i = 0; i < 22; i++)
    {
        v1 -= ((v0 << 5) + k2) ^ (v0 + sum) ^ ((v0 >> 4) + k3);
        v0 -= ((v1 << 5) + k0) ^ (v1 + sum) ^ ((v1 >> 4) + k1);
        sum -= delta;
    }
    v[0] = v0; v[1] = v1;
}

二叉树和后序遍历

没学过数据结构,看了半天发现是二叉树后就去粗略学了一下,

恢复了一下符号,先是将输入构建了一个二叉树,然后后序遍历,保存在一个全局变量里

说实话,不懂数据结构也没关系,因为他这做法只是相当于把数组逆序了一下,动调一下就能看出来…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
struct TreeNode
{
  __int16 value;
  TreeNode *left;
  TreeNode *right;
};

void main()
{
  ...
  si128 = _mm_load_si128((const __m128i *)&input);
  currIndex = 0;
  p_input = si128;
  tree = buildBinaryTree((BYTE *)&p_input, &currIndex, 24);
  if ( tree )
    postOrderTraversal(tree);
  ...
}

TreeNode *__fastcall buildBinaryTree(BYTE *inputArray, int *currentIndex, int arraySize)
{
  TreeNode *v3; // rbx
  __int64 index; // rax
  __int16 v8; // r12
  TreeNode *v9; // rax

  v3 = 0i64;
  index = *currentIndex;
  if ( (int)index < arraySize )
  {
    *currentIndex = index + 1;
    v8 = inputArray[index];
    v9 = (TreeNode *)operator new(0x18ui64);
    *(_OWORD *)&v9->left = 0i64;
    v3 = v9;
    v9->value = v8;
    v9->left = buildBinaryTree(inputArray, currentIndex, arraySize);
    v3->right = buildBinaryTree(inputArray, currentIndex, arraySize);
  }
  return v3;
}

void __fastcall postOrderTraversal(TreeNode *a1)
{
  TreeNode *left; // rcx
  TreeNode *right; // rcx
  __int64 index; // rax
  __int16 value; // cx

  left = a1->left;
  if ( left )
    postOrderTraversal(left);
  right = a1->right;
  if ( right )
    postOrderTraversal(right);
  index = g_index;
  value = a1->value;
  ++g_index;
  *((_WORD *)&my_data + index) = value;
}

异或

可能看着比较抽象的就是中间那一片奇奇怪怪的指令了,不过一眼看上去其实也就只是一个异或而已

还是一样,直接动调观察数据变化,发现这部分内容就是将数据每一位异或0x33

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
if ( std::chrono::_V2::system_clock::now(v7) - begin_time <= 10000999 )// 时间反调试
  {
    xor_key = _mm_load_si128((const __m128i *)&xmmword_405070);
    xmmword_408070 = (__int128)_mm_xor_si128(_mm_load_si128((const __m128i *)&xmmword_408070), xor_key);
    xmmword_408080 = (__int128)_mm_xor_si128(_mm_load_si128((const __m128i *)&xmmword_408080), xor_key);
    xmmword_408090 = (__int128)_mm_xor_si128(_mm_load_si128((const __m128i *)&xmmword_408090), xor_key);
    xmmword_4080A0 = (__int128)_mm_xor_si128(_mm_load_si128((const __m128i *)&xmmword_4080A0), xor_key);
    xmmword_4080B0 = (__int128)_mm_xor_si128(_mm_load_si128((const __m128i *)&xmmword_4080B0), xor_key);
    xmmword_4080C0 = (__int128)_mm_xor_si128(_mm_load_si128((const __m128i *)&xmmword_4080C0), xor_key);
    xmmword_4080D0 = (__int128)_mm_xor_si128(_mm_load_si128((const __m128i *)&xmmword_4080D0), xor_key);
    xmmword_4080E0 = (__int128)_mm_xor_si128(_mm_load_si128((const __m128i *)&xmmword_4080E0), xor_key);
    v9 = _mm_load_si128((const __m128i *)&xmmword_408110);
    xmmword_4080F0 = (__int128)_mm_xor_si128(_mm_load_si128((const __m128i *)&xmmword_4080F0), xor_key);
    v10 = _mm_xor_si128(_mm_load_si128((const __m128i *)&xmmword_408100), xor_key);
    qword_408120 ^= 0x33003300330033ui64;
  }
xmmword_408100 = (__int128)v10;
xmmword_408110 = (__int128)_mm_xor_si128(v9, xor_key);
my_data = (__int128)_mm_xor_si128(xor_key, (__m128i)my_data);

0x03 exp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include <stdint.h>

void decrypt(uint32_t *v, uint32_t *k)
{
    uint32_t v0 = v[0], v1 = v[1], i;
    uint32_t delta = -0x61cbb648;
    uint32_t sum = delta * 22;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
    for (i = 0; i < 22; i++)
    {
        v1 -= ((v0 << 5) + k2) ^ (v0 + sum) ^ ((v0 >> 4) + k3);
        v0 -= ((v1 << 5) + k0) ^ (v1 + sum) ^ ((v1 >> 4) + k1);
        sum -= delta;
    }
    v[0] = v0;
    v[1] = v1;
}

unsigned int key1[4] = {
    3, 5, 8, 13
};
unsigned int key2[4] = {
    21, 34, 55, 89
};
unsigned int key3[4] = {
    144, 233, 377, 610
};

unsigned char enc_data[] = {
    0x76, 0x71, 0x9d, 0xe7, 0x70, 0x77, 0x3f, 0xa3,
    0x02, 0xf1, 0x8d, 0xc9, 0x02, 0xc6, 0xa2, 0x4b,
    0xba, 0x19, 0x56, 0x05, 0xf2, 0x89, 0x5e, 0xe0
};

int main()
{
    // 异或0x33
    for (int i = 0; i < 24; i++) {
        enc_data[i] ^= 0x33;
    }

    // 逆序
    for (int i = 0; i < 24 / 2; i++) {
        unsigned char temp = enc_data[i];
        enc_data[i] = enc_data[24 - 1 - i];
        enc_data[24 - 1 - i] = temp;
    }

    // TEA解密
    decrypt((unsigned int *)enc_data, key1);
    decrypt((unsigned int *)enc_data + 2, key2);
    decrypt((unsigned int *)enc_data + 4, key3);

    printf("%s", enc_data);
    // flag{y0u_reallyl1ke_te@}
}
0%