0%

2025腾讯游戏安全竞赛-PC客户端-题解

2025腾讯游戏安全初赛PC

1 .exe初探

下发了一个.exe和一个.sys,在真机不太敢直接调.sys文件,丢到调试环境里面去

image.png

在调试环境里面能跑起来,随便给点输入回显Flag not correct!

.exe丢进IDA看看

找到输入逻辑和检测前缀的逻辑

image.png

然后发现后面一堆混淆,静态不太好看,调试一下.exe程序看看

发现调试的时候经常进一个abort()

image.png

怀疑有反调试,稍微找找常见的调试检测函数,发现用了CheckRemoteDebuggerPresent

image.png

把所有引用都扬了,就可以欢乐的调试了

调试过程中拿到这里的key_maybe是字符串”sxx”

image.png

这里的new_input是输入去掉前缀”ACE_“ 之后的字符串

image.png

encode函数通过这里的经典特征%58和/58可以看出来有用到base58

image.png

同时调试一下可以看出来输入的流程是

input → base58 → 字符串反转

顺带一提base58是经过变表的,变表的逻辑在SEH里面,

image.png

image.png

然后继续调试可以发现输入又和上面提到的key_maybe变量进行了异或,异或之后被送到驱动里面进行check,所以完整的逻辑是

input → base58 → reverse → xor “sxx” → check

image.png

image.png

这里的vtable结构如下,前面几个应该是注册服务的函数,由于和flag没什么关系就没细致分析

image.png

最后通过FilterSendMessage函数和驱动交互

image.png

2 分析.sys

所以进一步就要去.sys文件里面分析逻辑了

不过没什么头绪,DriverEntry有点太干净了,没头绪就一顿找,找到注册消息回调函数的地方

image.png

MessageNotifyCallback就是处理.exe里面发的函数,但是点进去发现

image.png

image.png

注意到段名是tvm0,搜索一下发现是TX自家的壳,github上可以找到一个xx_tvm

https://github.com/wbaby/xx_tvm

但是跑不动,疑似tvm升级了,没办法只能手动改花指令

这里用到的花指令基本上都如下这种

image.png

把jmp改成两个数相加或者相减然后再jmp寄存器,干扰ida,一般来说从一个push XXX 到下一个 pop XXX之间的都是可以扬掉的无用指令,有些jmp的比较远的就得自己手动计算一下还原了

还原后找到处理指令码的地方,这里的0x154004就是我们FilterSendMessage发过去的指令码

image.png

然后逻辑就很清晰了大概

image.png

image.png

encrypt像个tea,手动还原一下发现不对

之所以能确定不对,是因为输入的是uint8,但是把密文解密回去发现是个完整的uint32

3 调试.sys

首先怀疑是有程序修改了密文,或者encrypt里面的密钥,结果找了一圈也没找到,但是再找encrypt引用的时候,发现

image.png

image.png

在某个地方似乎对encrypt的代码做了修改,照着它改看看

image.png

….

这去哪了?

0FFFFB0035F716000h是个什么鬼地方,找了半天也没找到这个数字出现在哪里

(注:看别的师傅的题解说是可以静态把这个代码填回去,不太清楚怎么静态看的,以后再看看)

没办法只能调试一下.sys函数了

用windbg调.sys,真机加载调试环境里面的虚拟机

在encrypt函数里面下个断点,然后运行.exe文件,输入flag后如愿以偿的断下来了,至少出题人没在这里整选手一手

image.png

看一下0FFFFB0035F716000h的地址,发现了新的代码

image.png

把这些代码在.sys源文件里面随便找个地方patch回去,再改改之前的jmp地址,可以看到新的encrypt函数

image.png

好家伙完全不一样呢

根据改后的encrypt写出脚本

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
#include<bits/stdc++.h> 	// C++头文件 
#include"defs.h" // IDA头文件,一些定义要用
using namespace std;
void __fastcall decrypt(unsigned int *a1, int *a2) // 直接从IDA复制的函数
{
int v3; // ebx
unsigned int v4; // r11d
int v5; // edi
unsigned int v6; // r9d
__int64 v7; // rdx
unsigned int v8; // r10d

v3 = *a2;
v4 = -1640531527 * 32;
v5 = a2[1];
v6 = *a1;
v7 = 32;
v8 = a1[1];
do
{
// v4 -= 1640531527;
// v6 += (v4 + v8) ^ (v3 + 16 * v8) ^ (v5 + (v8 >> 5));
// v8 += (v4 + a2[(v4 >> 11) & 3]) ^ (v6 + ((16 * v6) ^ (v6 >> 5)));
// --v7;
// ↑ 加密逻辑
// ↓ 解密逻辑
--v7;
v8 -= (v4 + a2[(v4 >> 11) & 3]) ^ (v6 + ((16 * v6) ^ (v6 >> 5)));
v6 -= (v4 + v8) ^ (v3 + 16 * v8) ^ (v5 + (v8 >> 5));
v4 += 1640531527;
}
while ( v7 );
*a1 = v6;
a1[1] = v8;
}
unsigned int res[43] = { // 密文
0xEC367B8,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,
};
int key[4]={0x41,0x43,0x45,0x36}; // 密钥
int main(){
for(int i=0;i<=40;i+=2){
//TODO
decrypt(&res[i],key);
printf("%d,%d,",res[i],res[i+1]); // 解密的结果应该是 0-255之间的十进制数
}
}

然后把这套流程返回去

input ← base58 ← reverse ← xor “sxx”

image.png

1
ACE_We1C0me!T0Z0Z5GamESecur1t9*CTf

image.png