访客数— ◆阅读量— 文章数11

CTF

阅读 —

2025腾讯游戏安全技术竞赛PC端初赛wp

腾讯游戏安全竞赛 PC 初赛题解,记录可执行文件与驱动的静态分析。

还不是很会调驱动,所以这次都是做的静态分析

ACEFirstRound.exe

挂载完驱动后,首先对输入进行前四位的判断,要求前四位为“ACE_”

然后对输入进行base58

base58函数中使用了自定义的base58表,同时在base58后对结果进行了反转

查看base58表的交叉引用,发现在sub_140001000进行了初始化,动态调试dump出表abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ1234567890!@+/

动态调试前需要绕过140001c90处的反调,这里是开了一个线程来反调,直接patch掉函数ret就行

后面对base58编码后的结果进行了异或

异或的数组来自v46,v46中数据为{0x18,0xa8,0xb1}异或{0x6b,0xd0,0xc9}后的结果,即”sxx”

最后调用虚表中的函数进行check

一路跟进发现是调用了FilterSendMessage与驱动进行通信

ACEDriver.sys

发现驱动层有tvm混淆,但混淆强度不大,都是简单的花指令,大致为把跳转地址存入寄存器中再jmp,用特征码大法把push和pop中间的代码nop掉就可以了(有些混淆的跳转地址是跳转到别的地方,这里也是直接nop掉了,但不影响分析程序的大致逻辑)

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
#patch脚本
from idc import get_wide_byte

def add_patch(idx):
# r8~r15
if get_wide_byte(idx-12)==0x41:
for i in range(idx-12,idx+15):
patch_byte(i,0x90)
return idx+15

# rax
else:
for i in range(idx-11,idx+13):
patch_byte(i,0x90)
return idx+13

def lea_patch_r8(idx):
for i in range(idx-2,idx+20):
patch_byte(i,0x90)
return idx+20

def lea_patch_rax(idx):
for i in range(idx-1,idx+18):
patch_byte(i,0x90)
return idx+18

if __name__ == "__main__":
idx = 0x140001000
while idx < 0x140015194:
if get_wide_byte(idx) == 0x9c and get_wide_byte(idx+8) == 0x9d:
idx = add_patch(idx)
elif get_wide_byte(idx) == 0x4c and get_wide_byte(idx+1) == 0x8d and get_wide_byte(idx+7) == 0x4d and get_wide_byte(idx+8) == 0x8d and get_wide_byte(idx-2) == 0x41:
idx = lea_patch_r8(idx)
elif get_wide_byte(idx) == 0x48 and get_wide_byte(idx+1) == 0x8d and get_wide_byte(idx+7) == 0x48 and get_wide_byte(idx+8) == 0x8d and get_wide_byte(idx+14) == 0xff:
idx = lea_patch_rax(idx)
else:
idx += 1

随便乱点发现140001000有一个tea加密,交叉引用可以看到tea的调用,其中密钥和密文已知,注意这里是每次传入tea函数的是一个byte而不是dword

溯源tea的调用函数,可以看到1400087F1有一个0x154004的判断,和在r3层传入的数据相同,这里应该是一个操作码的判断

交叉引用发现还有一处地方引用了tea函数,发现14000A35B对tea函数进行了hook

由于不会调驱动,这里只能把tea函数和hook函数整个dump下来然后自己跑一遍😓

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define _DWORD int
#define _BYTE char
#define _QWORD long long
#define _OWORD __int128
#define _WORD short

unsigned char unk_140004000[] =
{
0x58, 0x41, 0x8B, 0xC9, 0x41, 0x8B, 0xC1, 0xC1, 0xE0, 0x04,
0xC1, 0xE9, 0x05, 0x33, 0xC8, 0x41, 0x8B, 0xC3, 0x48, 0xC1,
0xE8, 0x0B, 0x41, 0x03, 0xC9, 0x83, 0xE0, 0x03, 0x41, 0x8B,
0x44, 0x85, 0x00, 0x41, 0x03, 0xC3, 0x33, 0xC8, 0x44, 0x03,
0xD1, 0x48, 0x83, 0xEA, 0x01, 0x48, 0xB8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xB9, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x02, 0xFF, 0xE0, 0xFF,
0xE1, 0x00, 0x50, 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x67, 0xC3, 0x0E,
0x44, 0x90, 0xDA, 0xC9, 0xEB, 0x2D, 0x6C, 0xDA, 0xC3, 0xC9,
0xDD, 0x88, 0x75, 0x15, 0xA0, 0x32, 0xB4, 0xD0, 0x1D, 0x23,
0x74, 0x8A, 0x9E, 0x4B, 0x74, 0x3E, 0x5D, 0xD7, 0x12, 0x87,
0xAB, 0xEA, 0x88, 0xE8, 0x04, 0xE7, 0xAC, 0x31, 0x1A, 0xE0,
0x5C, 0x20, 0xAE, 0xEC, 0x67, 0x74, 0xBE, 0xA7, 0xA3, 0x52,
0x62, 0x0C, 0x4E, 0xEC, 0xEF, 0x1A, 0x44, 0xED, 0x0D, 0xC4,
0xCC, 0x42, 0xC8, 0xC3, 0x0E, 0x0C, 0x4A, 0xDE, 0xFC, 0xF3,
0x24, 0x7C, 0x01, 0xD0, 0xB8, 0x8F, 0x6E, 0x3E, 0x15, 0x11,
0x5C, 0xD1, 0x0E, 0x53, 0x11, 0x48, 0x21, 0xF4, 0xE0, 0x17,
0xB5, 0xBE, 0x34, 0x16, 0xF9, 0x63, 0xA5, 0xF8, 0x96, 0x4D,
0xC8, 0xEA, 0x23, 0xFE, 0xDF, 0x7A, 0x60, 0x2C, 0x5C, 0xD8,
0x43, 0xCC, 0x5B, 0x6C, 0x18, 0xFF, 0xA5, 0xE1, 0x63, 0x87,
0x58, 0xBD, 0x87, 0x91, 0x9B, 0x06, 0xD1, 0x87, 0x7B, 0x8D,
0x87, 0xD7, 0x68, 0x6B, 0x6E, 0x83, 0x3F, 0xC6, 0xA0, 0x55,
0xB3, 0xFD, 0x79, 0xD9, 0xEE, 0x4D, 0x52, 0x3E, 0x82, 0x5C,
0xB3, 0x7A, 0x8D, 0xDA, 0xF4, 0xA2, 0x4C, 0xBA, 0x08, 0x17,
0xE6, 0x53, 0x06, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x32, 0xA2, 0xDF, 0x2D, 0x99, 0x2B, 0x00, 0x00,
0xCD, 0x5D, 0x20, 0xD2, 0x66, 0xD4, 0xFF, 0xFF, 0x30, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x31, 0x00, 0x40,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00,
0x18, 0x44, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x50, 0x41, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

unsigned char mem[0x1000] = { 0 };

unsigned char tea_encrypt[] =
{
0x48, 0x8B, 0xC4, 0x48, 0x89, 0x58, 0x08, 0x48, 0x89, 0x68,
0x10, 0x48, 0x89, 0x70, 0x18, 0x48, 0x89, 0x78, 0x20, 0x41,
0x55, 0x4C, 0x8B, 0xEA, 0x8B, 0x1A, 0x45, 0x33, 0xDB, 0x8B,
0x7A, 0x04, 0x4C, 0x8B, 0xC1, 0x8B, 0x72, 0x08, 0x8B, 0x6A,
0x0C, 0x44, 0x8B, 0x09, 0x41, 0x8D, 0x53, 0x20, 0x44, 0x8B,
0x51, 0x04, 0x41, 0x8B, 0xCA, 0x45, 0x8D, 0x9B, 0xB9, 0x79,
0x37, 0x9E, 0xC1, 0xE9, 0x05, 0x41, 0x8B, 0xC2, 0x03, 0xCF,
0xC1, 0xE0, 0x04, 0x03, 0xC3, 0x33, 0xC8, 0x43, 0x8D, 0x04,
0x13, 0x33, 0xC8, 0x44, 0x03, 0xC9, 0x41, 0x8B, 0xC9, 0x41,
0x8B, 0xC1, 0xC1, 0xE9, 0x05, 0xC1, 0xE0, 0x04, 0x03, 0xCD,
0x03, 0xC6, 0x33, 0xC8, 0x43, 0x8D, 0x04, 0x0B, 0x33, 0xC8,
0x44, 0x03, 0xD1, 0x48, 0x83, 0xEA, 0x01, 0x75, 0xBD, 0x41,
0x5D, 0x48, 0x8B, 0x5C, 0x24, 0x08, 0x48, 0x8B, 0x6C, 0x24,
0x10, 0x48, 0x8B, 0x74, 0x24, 0x18, 0x48, 0x8B, 0x7C, 0x24,
0x20, 0x45, 0x89, 0x08, 0x45, 0x89, 0x50, 0x04, 0xC3
};

void __fastcall tea_hook(__int64 a1)
{
int v2; // ebx
char* v3; // rdx
int i; // ecx
unsigned __int64 v5; // rax
char* v6; // rdx
int j; // ecx
unsigned __int64 v8; // rax
char* v9; // rdx
_OWORD* v10; // rcx
unsigned __int64 v11; // r8
unsigned __int64 v12; // rax
unsigned __int8 CurrentIrql; // dl
unsigned __int64 v14; // rcx
unsigned __int64 v15; // rax
unsigned __int64 v16; // rax

*(_QWORD*)&unk_140004000[0x1d8] = (_QWORD)malloc(0x1000);
v2 = 0;
v3 = (char*)(&unk_140004000[0x20] + 15);
for (i = 0; i < 64; i += 8)
{
v5 = (unsigned __int64)(a1 + 0x77) >> i;
*v3++ = v5;
}
v6 = (char*)(&unk_140004000[0x30] + 9);
for (j = 0; j < 64; j += 8)
{
v8 = (unsigned __int64)(a1 + 0x34) >> j;
*v6++ = v8;
}
v9 = (char*)&unk_140004000[0x48] + 3;



v10 = (_OWORD*)*(_QWORD*)&unk_140004000[0x1d8];
v10[0] = *(_OWORD*)unk_140004000;//xmmword_140004000;
v10[1] = *(_OWORD*)&unk_140004000[0x10];
v10[2] = *(_OWORD*)&unk_140004000[0x20];
v10[3] = *(_OWORD*)&unk_140004000[0x30];
*((_DWORD*)v10 + 16) = *(_DWORD*)&unk_140004000[0x40];
*((_WORD*)v10 + 34) = *(_WORD*)&unk_140004000[0x44];
*((_BYTE*)v10 + 70) = *(_BYTE*)&unk_140004000[0x46];
v11 = *(unsigned __int64*)&unk_140004000[0x1d8];
do
{
v12 = v11 >> v2;
v2 += 8;
*v9++ = v12;
} while (v2 < 64);
*(_QWORD*)&unk_140004000[0x1e0] = *(_QWORD*)(a1 + 86);
*(_DWORD*)&unk_140004000[0x1e8] = *(_DWORD*)(a1 + 94);
*(_BYTE*)&unk_140004000[0x1ec] = *(_BYTE*)(a1 + 98);
// CurrentIrql = KeGetCurrentIrql();
// __writecr8(0xDui64);
// _disable();
// v14 = __readcr4();
// __writecr4(v14 & 0xFFFFFFFFFF7FFFFFui64);
// v15 = __readcr0();
// __writecr0(v15 & 0xFFFFFFFFFFFEFFFFui64);
*(_QWORD*)(a1 + 86) = *(_QWORD*)&unk_140004000[0x48];
*(_DWORD*)(a1 + 94) = *(_DWORD*)&unk_140004000[0x50];
*(_BYTE*)(a1 + 98) = *(_BYTE*)&unk_140004000[0x54];
// v16 = __readcr0();
// __writecr0(v16 | 0x10000);
// __writecr4(v14);
// _enable();
// __writecr8(CurrentIrql);
// *((_BYTE*)P + 340) = 1;
}

int main()
{
tea_hook((__int64)tea_encrypt);
return 0;
}

然后ida动态调试,hook过后的代码仍然有混淆,一共有三部分,这里把这三部分手动dump下来,去混淆并修复jmp地址

最终还原效果如下,把tea和xtea结合起来了:

然后写解密脚本得到base58后的字符串@PksUn39kYj763ggA1HLBUCaWSZv4vs4CwSevAnQEs

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
#include <stdio.h>
#include <string.h>
#define DELTA 0x9e3779b9

void tea_decrypt(unsigned int* text, int* key)
{
unsigned int v0 = text[0];
unsigned int v1 = text[1];
int sum = 0xC6EF3720;
for (int i = 0;i < 32;i++)
{
v1 -= (sum + key[(sum >> 11) & 3]) ^ (v0 + ((16 * v0) ^ (v0 >> 5)));
v0 -= ((v1 << 4) + key[0]) ^ (v1 + sum) ^ ((v1 >> 5) + key[1]);
sum -= DELTA;
}
text[0] = v0;
text[1] = v1;
}

int main()
{
unsigned int text[] =
{
0x0EC367B8, 0xC9DA9044, 0xDA6C2DEB, 0x88DDC9C3, 0x32A01575, 0x231DD0B4, 0x4B9E8A74, 0xD75D3E74, 0xEAAB8712,
0xE704E888, 0xE01A31AC, 0xECAE205C, 0xA7BE7467, 0x0C6252A3, 0x1AEFEC4E, 0xC40DED44, 0xC3C842CC,
0xDE4A0C0E, 0x7C24F3FC, 0x8FB8D001, 0x11153E6E, 0x530ED15C, 0xF4214811, 0xBEB517E0, 0x63F91634,
0x4D96F8A5, 0xFE23EAC8, 0x2C607ADF, 0xCC43D85C, 0xFF186C5B, 0x8763E1A5, 0x9187BD58, 0x87D1069B,
0xD7878D7B, 0x836E6B68, 0x55A0C63F, 0xD979FDB3, 0x3E524DEE, 0x7AB35C82, 0xA2F4DA8D, 0x1708BA4C,
0x710653E6, 0x00000000, 0x00000000
};
int key[4] = { 'A','C','E','6' };
int n = 42;

for (int i = 0;i < n;i += 2)
tea_decrypt(&text[i], key);

char xornum[] = { 0x18,0xa8,0xb1 };
xornum[0] ^= 0x6b;
xornum[1] ^= 0xd0;
xornum[2] ^= 0xc9;
for (int i = 0;i < n;i++)
text[i] ^= xornum[i % 3];
for (int i = 0;i < n;i++)
printf("%c", text[i]);
return 0;
}

扔到cyberchef解码即得到flag:ACE_We1C0me!T0Z0Z5GamESecur1t9*CTf