2025 腾讯游戏安全技术竞赛 PC客户端安全 初赛WP
前言
这也是本菜鸡第一次参加该比赛,之前是有看过前几年比赛的WP,感觉难度不小,这次来尝试一下,由于是纯CTF类型,做起来还算顺手,不过也是遇到不少问题。经过半天的奋战最后还是解了出来,综合体验下来收获还是不少的。
R3分析
将ACEFirstRound.exe放入IDA分析,在main函数可以看到一个虚表,根据里面各个函数内容,将每个虚表函数都重新命名为对应功能。
发现是运行了ACEDriver驱动,与r3程序之间进行通信。
尝试动调发现没办法,发现程序退出,根据CheckRemoteDebuggerPresent的交叉引用发现一个反调试函数。
找该函数的交叉调用,发现是这边启了一个线程来启动反调试。
在该函数这边进行条件断点,修改rip,让代码直接执行到函数结束处,跳过中间反调试相关代码,即可绕过反调试。
动调分析,然后这边输入flag要求是以"ACE_"开头,然后去掉前面这四个字符,首先进行Base58加密,然后再将数据倒转。这边Base58是变表,和标准不一样,提取得:
abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789
与"sxx"进行循环xor。
最后通信发送到ACEDriver,命令码是0x154004。
至此R3层分析完毕。
R0分析
IDA载入ACEDriver.sys,发现有几个消息的Callback,但是跳转过来发现有混淆和花指令。
该驱动的混淆和花指令都是一个类型的,花指令是最基础的。
以下图为例子:
对41e9按u再跳过e9字节按c还原,即可pass花指令。
然后截图处是一块,将地址计算完进行jmp,计算出来实际就是jmp到下面pop处,说明这一段是无用的,可以直接将push到pop全部nop即可。其他地方都和这地方混淆差不多类型,都直接跳过花指令后nop即可。
但有一种比较特殊,并不是跳转到下面邻近代码处,而是jmp到其他代码处,观察push和底下pop寄存器是否一致就可以判断是哪一种类型,该种就得手动计算地址然后写jmp。
手动去除大部分混淆后,就可以看到几个回调的里面代码,MessageNotifyCallback里面的代码如下:
进入几层call就可以看到这边就是判断命令码执行,下面就是接收R3发送来的密文数据。
进入call发现就是关键处,unk_140004064那边就是flag密文,使用的是tea加密,key是['A','C','E','6'],然后边加密边判断密文是否相等。
查看tea函数的交叉调用,发现有其他地方有出现。
在第四个call处,发现一个函数传入了tea加密的函数地址,然后进行了一系列变换,应该是对tea函数本体进行了修改。
将该函数代码和相关数据进行提取,对tea函数字节进行本地模拟操作。
复制代码 隐藏代码
// tea函数原字节
unsignedchar tea[149] = {
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
};
unsignedchar P_0[0x1000]{};
unsignedchar xmmword_140004000[16] = {
0x58, 0x41, 0x8B, 0xC9, 0x41, 0x8B, 0xC1, 0xC1, 0xE0, 0x04, 0xC1, 0xE9, 0x05, 0x33, 0xC8, 0x41
};
unsignedchar xmmword_140004010[16] = {
0x8B, 0xC3, 0x48, 0xC1, 0xE8, 0x0B, 0x41, 0x03, 0xC9, 0x83, 0xE0, 0x03, 0x41, 0x8B, 0x44, 0x85
};
unsignedchar xmmword_140004020[16] = {
0x00, 0x41, 0x03, 0xC3, 0x33, 0xC8, 0x44, 0x03, 0xD1, 0x48, 0x83, 0xEA, 0x01, 0x48, 0xB8, 0x00
};
unsignedchar xmmword_140004030[16] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsignedchar dword_140004040[4] = {
0x00, 0x75, 0x02, 0xFF
};
unsignedchar word_140004044[2] = {
0xE0, 0xFF
};
unsignedchar byte_140004046[2] = {
0xE1, 0x00
};
unsignedchar qword_140004048[8] = {
0x50, 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsignedchar dword_140004050[4] = {
0x00, 0x00, 0x00, 0xFF
};
unsignedchar byte_140004054[1] = {
0xE0
};
DWORD64 qword_1400041E0 = 0;
DWORD dword_1400041E8 = 0;
BYTE byte_1400041EC = 0;
// 对tea函数修改的函数
void __fastcall sub_14000A35B(__int64 a1)
{
int n64; // ebx
char* v3; // rdx
int i; // ecx
unsigned __int64 v5; // rax
char* v6; // rdx
int j; // ecx
unsigned __int64 v8; // rax
char* v9; // rdx
unsigned __int64* 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
n64 = 0;
v3 = (char*)&xmmword_140004020 + 15;
for (i = 0; i 64; i += 8)
{
v5 = (unsigned __int64)(a1 + 119) >> i;
*v3++ = v5;
}
v6 = (char*)&xmmword_140004030 + 9;
for (j = 0; j 64; j += 8)
{
v8 = (unsigned __int64)(a1 + 52) >> j;
*v6++ = v8;
}
v9 = (char*)&qword_140004048 + 3;
v10 = (unsignedlonglong*)P_0;
memcpy(P_0, xmmword_140004000, 16);
memcpy(P_0 + 16, xmmword_140004010, 16);
memcpy(P_0 + 16 * 2, xmmword_140004020, 16);
memcpy(P_0 + 16 * 3, xmmword_140004030, 16);
*((DWORD*)P_0 + 16) = *(DWORD*)dword_140004040;
*((WORD*)P_0 + 34) = *(WORD*)word_140004044;
*((BYTE*)P_0 + 70) = *(BYTE*)byte_140004046;
v11 = (unsigned __int64)P_0;
do
{
v12 = v11 >> n64;
n64 += 8;
*v9++ = v12;
} while (n64 64);
qword_1400041E0 = *(DWORD64*)(a1 + 86);
dword_1400041E8 = *(DWORD*)(a1 + 94);
byte_1400041EC = *(BYTE*)(a1 + 98);
*(DWORD64*)(a1 + 86) = *(DWORD64*)qword_140004048;
*(DWORD*)(a1 + 94) = *(DWORD*)dword_140004050;
*(BYTE*)(a1 + 98) = *(BYTE*)byte_140004054;
}
int main()
{
// 修改tea加密函数代码字节
sub_14000A35B((longlong)tea);
system("pause");
return0;
}
编译使用IDA动调,并按c分析tea数组处的代码,发现分成了三块,手动将三块合并到一块代码,并修复部分跳转的地址,即可得到完整的代码。
合并完的代码如下:
反编译得到修改后的魔改tea加密
即可编写得到对应的解密代码:
复制代码 隐藏代码
void tea_decrypt(unsigned int* Input, int* Key)
{
unsignedint v6 = Input[0];
unsignedint v8 = Input[1];
unsignedint sum = 0;
// sum从32开始
sum = (-0x61C88647) * 32;
for (int i = 0; i 32; i++)
{
v8 -= (sum + Key[(sum >> 11) & 3]) ^ (v6 + ((v6 <4) ^ (v6 >> 5)));
v6 -= (sum + v8) ^ (Key[0] + (v8 <4)) ^ (Key[1] + (v8 >> 5));
sum += 0x61C88647;
}
Input[0] = v6;
Input[1] = v8;
}
进行对unk_140004064密文解密即可,不过注意一点,这边是从-1处开始判断,也就是unk_140004064-4处开始是密文。
提取出来就是42的DWORD数据,和上面的判断数值对应上了。
解密
复制代码 隐藏代码
#include
#include
// 魔改tea解密函数
void tea_decrypt(unsigned int *Input, int *Key)
{
unsignedint v6 = Input[0];
unsignedint v8 = Input[1];
unsignedint sum = 0;
sum = (-0x61C88647) * 32;
for (int i = 0; i 32; i++)
{
v8 -= (sum + Key[(sum >> 11) & 3]) ^ (v6 + ((v6 <4) ^ (v6 >> 5)));
v6 -= (sum + v8) ^ (Key[0] + (v8 <4)) ^ (Key[1] + (v8 >> 5));
sum += 0x61C88647;
}
Input[0] = v6;
Input[1] = v8;
}
// flag密文
unsignedint enc[42] = {
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};
// tea解密key
int key[4]{'A', 'C', 'E', '6'};
int main()
{
// 42个数据,2个2个进行tea解密
for (int i = 0; i 42; i += 2)
{
tea_decrypt((unsignedint *)((DWORD64)enc + i * 4), key);
}
// 输出42个解密后数据
for (int i = 0; i 42; i++)
printf("%X ", enc[i]);
return0;
}
Out
33 28 13 0 2D 16 40 41 13 2A 12 4F 45 4B 1F 14 39 49 3B 34 3A 26 3B 19 24 2B 22 5 4C E 0 4C 3B 4 2B 1D 5 39 16 22 3D B
CyberChef
xor('sxx') -> Reverse -> Base58 Decode(换表)
密文:
We1C0me!T0Z0Z5GamESecur1t9*CTf
Flag
flag{ACE_We1C0me!T0Z0Z5GamESecur1t9*CTf}
-官方论坛
www.52pojie.cn
👆👆👆
公众号
设置“星标”,
您
不会错过
新的消息通知