吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8604|回复: 57
上一主题 下一主题
收起左侧

[CTF] 腾讯游戏安全大赛2025初赛题解

  [复制链接]
跳转到指定楼层
楼主
xia0ji233 发表于 2025-3-31 10:32 回帖奖励

记录一下今年 2025 初赛过程

<!--more-->

题目描述

小Q是一位热衷于PC客户端安全的技术爱好者,为了不断提升自己的技能,他经常参与各类CTF竞赛。某天,他收到了一封来自神秘人的邮件,内容如下:

“我可以引领你进入游戏安全的殿堂,但在此之前,你需要通过我的考验。打开这扇大门的钥匙就隐藏在附件中,你有能力找到它吗?

找到正确的flag(2分)

flag:flag{ACE_We1C0me!T0Z0Z5GamESecur1t9*CTf}

R3分析

先说结论:

  • 运行之后先加载驱动程序。
  • 输入 flag,判断是否以 ACE_ 开头。
  • base58 编码剩余的部分,进行反转之后在开头添加 @
  • sxx 密钥对上一个步骤的结果进行异或加密。
  • 封装数据,发送给驱动程序。

下面是分析过程:

静态分析

定义了一个 ACEDriverSDK 类,初始化虚表。

类的定义放 IDA 很简单

struct SDK
{
    vtable *table;
    HANDLE port;
};

虚表可以根据需要进行还原,这里给出我还原的虚表定义

struct vtable
{
    void (*init)(SDK *);
    PVOID ptr[7];
    void (*FltCommunite)(__int64 a1, int a2, const void *a3, unsigned int a4, LPVOID lpOutBuffer, DWORD dwOutBufferSize, DWORD *a7);
    int (__fastcall *LoadDriver)(SDK *);
    void (*ClosePort)(SDK *);
    void (*Test)(SDK *);
    bool (*checkflag)(__int64 SDK, __int64 a2, __int64 a3);
};

随后就是判断开头是否为 ACE_

然后初始化了异或密钥,取 ACE_ 后的字符串进行其余的加密操作,比如 base58 然后逆转。

做完这些操作后,再异或加密。

最后通过虚表调用 checkflag 函数。

checkflag 的逻辑也很简单,就是调用 SDK 的通信函数

最后就是看构造通信数据了,0x154004 显然是一个 magic 数据,作为调用功能号,其余的加密数据被追加到 magic 之后。

其实中间还漏了一个密文长度追加的逻辑,不过因为动调很容易看出来,所以这部分放另一部分说明,通信协议如下所示

(4字节功能号)
(4字节数据长度,设该值为x)
(x字节加密数据)
动态调试

以上分析均结合了动态调试的结果,下面说明一些比较长的函数逻辑判断。

首先注意到关键加密函数的一个关键操作:

断在写入指令,可以发现,最后写入的结果都小于 58,最后返回了一个包含大小写字母和数字的字符串,并且,这个临时写入的变量和最终的密文之间存在对应的关系。

这里以输入 ACE_11111111111111111111 为例。

第一次循环将 1 写入了该内存,很好理解,因为 1 的 ASCII 小于 58

然后直接跳出循环,看看最终结果。

其实这里大致可以想到 base58 编码了,拿标准 base58 试试看,主要试试相同位置的字符是否能对应上,如果能对应上那就是 base58 无疑了,最多换了码表,顺便说一下,这个地方调试可以顺带 dump 码表,我选择直接在该内存上写上 0 1 2 ...,最后观察字符串的输出,得到码表 abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789

可以发现能对应上,只是顺序反了,随后跳出该函数,返回,观察返回的字符串

可以发现相同的字符至少也是能对应上的,只是前面多了一个 @

因为异或密钥稍微跟一下就能得到,就不过多赘述,直接看到最后,在 FilterSendMessage 处下断,观察传出的数据。

这里也可以看出来了,头四个字节 0x154004,后面四个字节 0x1c 跟后面密文的长度一致。

不放心逻辑再去验证一遍,确定是对的,R3 的所有逻辑至此分析完毕。

R0分析

去混淆

静态分析,直接找通信的回调函数,应该是注册回调的时候没有加混淆,IDA直接能识别出来。但是加了混淆,由于混淆强度不高,直接特征码大法去掉所有混淆。

import idc
import idaapi
import idautils

def fill_nop(start, length):
    for i in range(length):
        patch_byte(start+i, 0x90)

def find_pattern(pattern):
    matches = []
    byte_pattern = []
    for byte in pattern.split():
        if byte == "??":
            byte_pattern.append(None)  # 通配符
        else:
            byte_pattern.append(int(byte, 16))
    pattern_length = len(byte_pattern)
    for head in range(0x140008000,0x140016000):
        match = True
        for i in range(pattern_length):
            current_byte = get_wide_byte(head + i)
            if byte_pattern[i] is not None and current_byte != byte_pattern[i]:
                match = False
                break
        if match:
            matches.append(head)
    return matches

parttern = "41 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 9C ?? ?? ?? ?? ?? ?? ?? 9D 41 FF ?? ?? 41 ??"
#parttern="41 51 4C 8D 0D ?? ?? ?? ?? 4D 8D 89 ?? ?? ?? ?? 41 FF E1 ?? 41 59"
#parttern="41 ?? 4C 8D ?? ?? ?? ?? ?? 4D 8D ?? ?? ?? ?? ?? 41 FF ?? ?? 41 ??"
#parttern="51 48 8D ?? ?? ?? ?? ?? 48 8D ?? ?? ?? ?? ?? FF ?? E8 59"
#parttern="52 48 8D ?? ?? ?? ?? ?? 48 8D ?? ?? ?? ?? ?? FF ?? E8 5A"
#parttern="?? 48 B8 ?? ?? ?? ?? ?? ?? ?? ?? 9C ?? ?? ?? ?? ?? ?? ?? 9D FF ?? ?? ??"
#parttern="41 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 9C ?? ?? ?? ?? ?? ?? ?? 9D 41 FF ?? ?? 41 ??"
#parttern="52 48 8D ?? ?? ?? ?? ?? 48 8D ?? ?? ?? ?? ?? FF ?? E8 5A"
#parttern="41 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 9C ?? ?? ?? ?? ?? ?? ?? 9D 41 FF ?? ?? 41 ??"
#parttern="41 ?? 4C 8D ?? ?? ?? ?? ?? 4D 8D ?? ?? ?? ?? ?? 41 FF ?? ?? 41 ??"
#parttern="41 51 4C 8D 0D ?? ?? ?? ?? 4D 8D 89 ?? ?? ?? ?? 41 FF E1 ?? 41 59"
#parttern="51 48 B9 ?? ?? ?? ?? ?? ?? ?? ?? 9C 48 81  ?? ?? ?? ?? ?? 9D FF E1 E8 59"
#parttern="52 48 8D ?? ?? ?? ?? ?? 48 8D ?? ?? ?? ?? ?? FF E2 E9 5A"
#parttern="E9 01 00 00 00 ??"
#parttern="50 48 8D ?? ?? ?? ?? ?? 48 8D ?? ?? ?? ?? ?? FF E0 ?? 58"
#parttern="51 48 8D ?? ?? ?? ?? ?? 48 8D ?? ?? ?? ?? ?? FF E1 ?? 59"
#parttern="?? 48 ?? ?? ?? ?? ?? ?? ?? ?? ?? 9C 48 ?? ?? ?? ?? ?? ?? 9D FF ?? ?? ??"
patch_addr_list1 = find_pattern(parttern)

def patch_flower(addr):
    fill_nop(addr,(len(parttern)+2)//3)

for addr in patch_addr_list1:
    print("[+]", hex(addr))
    patch_flower(addr)

每个特征码运行一遍大部分的函数都能进行反编译了(特征码是边分析边总结的,所以可能存在重复的)

静态分析

顺着消息回调函数找到关键调用

由于通信的时候 magic==0x154004 是确定的,另外一个分支是测试使用的,因此完全可以不用分析,只分析 1AA0 函数即可。

本场比赛的第一个需要注意的点(不能算坑,只是踩了):

可以发现 R3 传过来的数据是每两个字节为一组,每个字节零扩展成 unsigned int 类型作为 TEA 加密的明文传入。

TEA 加密看似是标版,实则解密之后会发现不对,这里可以先 dump 140004060 的数据,尝试进行 TEA 解密。其实很好判断解密是否成功,解密之后的数据异或 sxx 之后,应当得到一个 @ 开头的全 ASCII 字符,标准解密失败之后有次不小心交叉引用 TEAEnc 函数的时候发现了问题所在。

显然,该函数是被 hook 了,这里直接考虑动态调试去 dump

动态调试

找到地址直接去看看 hook 函数。

好在该 hook 也不复杂,但是直接分析汇编指令显然也不明智,把 hook 跳板拆开,将有效的指令插入原 code 中,最后修正偏移即可,这里可以直接选择区域导出十六进制值放 CyberChef 去分析,修跳转偏移也是。

修图中框选的指令偏移即可。最后直接用 IDA 反编译,得到最终结果。

根据伪代码写逆向逻辑即可。

#include<stdint.h>
#include<string.h>
#include<stdio.h>

void decrypt_tea(uint32_t* v, uint32_t* k) {
        uint32_t delta = 0x9e3779b9;
    uint32_t v0 = v[0], v1 = v[1], sum = delta*32, i;     /* set up */
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];    /* cache key */
    for (i = 0; i < 32; i++) {                                 
            uint32_t result = sum + k[(sum >> 11) & 3]; 
            v1 -= result ^ (v0 + ((16 * v0) ^ (v0 >> 5)));
            v0 -= (sum + v1) ^ (k0 + 16 * v1) ^ (k1 + (v1 >> 5));
        sum -= delta;
    }                                                           /* end cycle */
    v[0] = v0; v[1] = v1;
}
unsigned char ida_chars[200] =
{
        ...
};
char enc[100]={0};
int main(){

        uint32_t k[]={
                'A','C',
                'E','6',
        };
        for(int i=0;i<42;i++){
                decrypt_tea((uint32_t*)(&ida_chars[i*8]),k);
        } 
        for(int i=0;i<42;i++){
                enc[i]=(ida_chars[i*4]);
        }
        char key[]="sxx";
        for(int i=0;i<42;i++){
                enc[i]^=key[i%3];
                putchar(enc[i]);
        } 
        return 0; 
}
//@PksUn39kYj763ggA1HLBUCaWSZv4vs4CwSevAnQEs

手动去掉 @,然后逆转再 base58 解码,得到答案。

所以最终正确输入就是 ACE_We1C0me!T0Z0Z5GamESecur1t9*CTf

免费评分

参与人数 19吾爱币 +15 热心值 +16 收起 理由
zykang + 1 我很赞同!
zishen + 1 + 1 谢谢@Thanks!
BeginForEnd + 1 谢谢@Thanks!
KOCBT + 1 + 1 我很赞同!
SHIER1999 + 1 用心讨论,共获提升!
Ypndpll + 1 + 1 用心讨论,共获提升!
yuuxxx + 1 + 1 谢谢@Thanks!
jiangbaibai + 1 + 1 用心讨论,共获提升!
steven7x + 1 用心讨论,共获提升!
mxxzz + 1 谢谢@Thanks!
Bertus + 1 谢谢@Thanks!
120305 + 1 + 1 用心讨论,共获提升!
KnowledgeARI + 1 + 1 用心讨论,共获提升!
sarex + 1 + 1 鼓励转贴优秀软件安全工具和文档!
panda221 + 1 + 1 我很赞同!
beaster + 1 + 1 用心讨论,共获提升!
lwGoodChinese + 1 用心讨论,共获提升!
dlz0114 + 1 + 1 谢谢@Thanks!
ztw36343153 + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
SfbjZxc 发表于 2025-4-5 16:11
本帖最后由 SfbjZxc 于 2025-4-5 18:19 编辑

纯新手一个,想请教一下大佬,这个在win10 22H2的虚拟机当中不能加载sys该怎么处理啊,提示环境不对。已经关闭windows defender、vbs、hyper了。
沙发
murasame520 发表于 2025-3-31 11:05
3#
q2320069732 发表于 2025-3-31 11:22
4#
l686 发表于 2025-3-31 12:02
大佬太强了,感谢分享
5#
777444 发表于 2025-3-31 12:31
支持一下  认真学习
6#
fireflying1984 发表于 2025-3-31 12:41
膜拜大佬,萌新表示这里面的知识点太多,得花不少时间学习
7#
qj2716115 发表于 2025-3-31 13:16
大佬厉害,不明觉厉
8#
echopine 发表于 2025-3-31 14:16
逻辑分析有东西的
9#
zhengzhenhui945 发表于 2025-3-31 15:45
支持一下,根本学不完
10#
poker2025 发表于 2025-3-31 16:23
膜拜大佬,太强了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-7-16 02:01

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表