首页
社区
课程
招聘
[原创]2025腾讯游戏安全技术竞赛初赛-题解(有源码)
发表于: 2025-3-31 00:44 395

[原创]2025腾讯游戏安全技术竞赛初赛-题解(有源码)

2025-3-31 00:44
395

开源地址: GITHUB

根据题目要求 :
选择 Windows 11 22H2 系统,关闭 Windows Defender, VBS, HyperV 后,以管理员权限执行解题程序。首先打开ACEFirstRound.exe,提示请输入flag。

解题开始:

在进程启动的初期输入: lm 获得主模块的地址。

共需绕过两处检测,第一处为 R3 的基础反调试函数sub_1400010D0,第二处为驱动加载后对 R3 的修补检测。具体表现为

执行修补后 R3 程序可以正常调试。

内核中主要逻辑均被 TVM 执行了代码变异,没有全局反调试逻辑,针对主要逻辑疑有防篡改检测(或其他逻辑)。
体现为: 直接对 sub_140001000 下断,系统发生蓝屏,蓝屏模块为 ACEDriver.sys 。
不影响对内核的调试分析,不需要针对分析和绕过反调试。

分析 FilterSendMessage 在内核中的派遣函数 MessageNotifyCallback(+0x1280) :

函数逻辑被 TVM 混淆, 但强度不高, 可以静态分析, 也可以动态调试。这里结合两种方式分析。

对 MessageNotifyCallback 下断, 查看参数获得由R3传入驱动的缓冲区地址。
发现缓冲区数据为 8字节未知数据+R3传入的加密数据 如图。

对其下访问断点,目的是找到第一次访问加密数据的代码,缩小需要分析的代码范围。

对命中点函数分析,初步确定为 memcpy ,直接在 ida 中改名:

这个函数的调用方确为被 TVM 混淆的 MessageNotifyCallback ,重点关注这个函数执行后的逻辑。

重新运行驱动,对疑为 memcpy 的函数下断,确定函数的参数,分析后确定该函数为 memcpy 。
ida 中从 memcpy 开始往下看,忽略显然的混淆垃圾逻辑,可以看到一个指向 sub_140001448 的 call 。

跟踪进入,忽略显然的垃圾逻辑,发现一个指向 sub_140001AA0 的 call 。

跟踪进入,首先看到加载字符串 "ACE6" 。

在函数上下断,对函数进行动态分析。得到第一个参数( RCX )为 密钥长度 ,第二个参数( RDX )为 被传入的密钥 。

继续跟进,看到 ACEDriver+0x9bc9 处有 cmp ecx, 2Ah 逻辑,判断了密钥长度是否为 2A ,猜测正确的密钥长度为 0x2A 。
注: 后续验证密钥长度确为0x2A(42字节)。

紧接着 ja 跳转:

跟踪到 ACEDriver+0x9c56 处发现 call ACEDriver+0x1000 ,此时记录参数,可以发现 rdx=rcx+8 。

ida 中查看 ACEDriver+0x1000 为未混淆逻辑, 进入静态分析。


肉眼可见 tea 算法的 magicnumber=0x61C88647 ,对其按 x 进行引用分析。

发现 sub_140001898 处对执行了动态修补,这里的函数没有混淆。
注:如果函数混淆了也可以判断除 call tea 处有其他函数对该函数的指针进行了引用,因为 __guard_fids_table 中引用了 tea 。

sub_140001898 关闭了 CR4.CET 来避免修改 CR0.WP 时产生 #GP 异常,非常经典的无视内存属性写入的代码。
在调试器中查看被动态修补处,提取出动态修补的逻辑。

将tea函数和动态修补的shellcode代码提取,模拟修补逻辑并修复地址引用后可以编写一个相同逻辑的 tea 加密机程序(见附件)。

继续对 tea 函数进行动态分析,查看其参数可以发现 rcx 地址记录了 int 数组 0x33,0x1c,0x41,0x43,0x45,0x36 ,其中 0x33,0x1c 是我们之前密文的前两个字节, 0x41,0x43,0x45,0x36 为 "ACE6" 。

跟踪 tea 函数执行结束,返回调用处,在缓冲区中得到加密后的密文 2b c6 8e db ed cc 10 08 , "ACE6" 也就是 tea 加密的密钥了。

出函数后第一句就是对比加密后的结果( cmp dword ptr [rsp+20h], eax )。

可以知道 tea 加密后的前四个字节应该是 0xec367b8 也就是 b8 67 c3 0e 。
编写程序循环尝试前两个值直到找到可以使得加密后的前四个字节为 0xec367b8 的组合。

最终得到匹配的序列 0x33 0x28 0x41 0x43 0x45 0x36 ,也就是传入驱动时的密文前两个字应该是 0x33 0x28 ,用 sxx 密文解密后得到前两个字符应该是 @P 。

重新启动驱动,回到 tea 函数校验逻辑,修改传入的参数为 33 28 后继续执行,成功通过了第一轮的校验,进入第二轮校验。此时传入tea函数的数据为 39 00 00 00 44 00 00 00 (也就是传入驱动的密文的下两个字节)。
离开 tea 函数后又马上进入了对比逻辑。
这时候逻辑就很显然了,正确的 tea 加密后的密文被从内存中循环读入,与我们传入驱动后被 tea 加密后的密文每两个字节加密后对比一次。
很容易发现密文是通过 mov eax, dword ptr [rsi-4] 读入的,而 rsi 在之前的代码中被 +8,也就是 rsi 在第一轮对比被初始化后,每次对比中 rsi 都会+8得到正确密文被 tea 加密后的部分。


直接从 驱动模块+0x4060 的内存中得到明文对应的所有 tea 串。

编写代码对 tea 串解密后得到传入驱动时的密文经过 "sxx" 异或前的内容"@PksUn39kYj763ggA1HLBUCaWSZv4vs4CwSevAnQEs" :

经过 sxx 异或后的16进制字符串: 33 28 13 00 2d 16 40 41 13 2a 12 4f 45 4b 1f 14 39 49 3b 34 3a 26 3b 19 24 2b 22 05 4c 0e 00 4c 3b 04 2b 1d 05 39 16 22 3d 0b 。

重新启动驱动,在输入密钥前对 MessageNotifyCallback 下断,修改传入的16进制内容为新的字符串

对得到的传入驱动的正确密文进行验证:
可以看到 r3 输出了 Flag is correct ,这说明我们找到的密文是正确的,只需要再解密一次就能得到正确的明文flag。

分析 r3 的初次加密,很容易发现 base58 的加密特征,并且 r3 的 sub_140001000 处有一个修改版的 xorstr 函数,释放了显然是字符集的"abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ1234567890!@+/" 。

我们只需要自行编写 base58 解码函数,并替换 base58 原本的字符集为自定义的字符集,即可解码得到的 base58 编码字符串。

最终成功编码我们随机输入的字符串 "ACE_2580219509218592511" 为正确的结果,并且成功解码得到的编码字符串 "@PksUn39kYj763ggA1HLBUCaWSZv4vs4CwSevAnQEs" 为 "We1C0me!T0Z0Z5GamESecur1t9CTf" 。
在解码后的字符串前加上前缀"ACE_",得到 "ACE_We1C0me!T0Z0Z5GamESecur1t9
CTf" ,输入程序即可验证通过。

很不错的一道CTF题, 结合了传统CTF的思想和内核知识。关键的加密逻辑被隐藏在TVM混淆后的代码中,考察了一定程度上的混淆后的代码的分析能力。
通讯用了FilterSendMessage,在游戏安全领域并不常见(开源的少大家没得抄)。继2022年决赛ZwSetSystemInformation通讯之后又一次掏出好玩的通讯机制,大手子们又有得抄了。

eb 模块地址+10D0 C3
// ret 用于阻止R3反调试执行
eb 模块地址+1370 48 C7 C0 01 00 00 00 C3
// mov rax, 1
// ret 用于阻止R0驱动加载
eb 模块地址+10D0 C3
// ret 用于阻止R3反调试执行

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回