乐于分享
好东西不私藏

2026腾讯游戏安全技术竞赛-安卓决赛VM分析与还原

2026腾讯游戏安全技术竞赛-安卓决赛VM分析与还原

Flag 速查表

输入屏幕左上角 8 位 hex Token,输出 3 个 flag。

完整工具:flag_tool.exe encrypt <token>

以 Tokena2d576a6为例:

产物:flag_tool.exe 源码— 支持 encrypt / decrypt / verify,16 组测试向量全通过。

01
引擎识别与资源提取

1.1 引擎识别

APK 解包后观察到assets/assets.sparsepcklib/arm64-v8a/libgodot_android.so,结合 AndroidManifest 中的com.godot.game包名,确认引擎为Godot 4.5(开源引擎)。

关键二进制:

1.2 AES 密钥提取

PCK 资源经 AES-256-CFB128 加密。IDA 中追踪密钥的完整链路:

① 定位open_and_parse:搜索 RTTI 字符串"19FileAccessEncrypted"(地址0x945101),经 typeinfo xref 定位到sub_38013D0(=FileAccessEncrypted::open_and_parse,9 处调用)。

② 确认 AES 调用sub_38013D0内部的 AES 初始化序列:

// sub_38013D0 (open_and_parse) 伪代码摘录sub_376EDC0(ctx);                                    // mbedtls_aes_initsub_376EE1C(ctx, *(this + 352), 256);                // mbedtls_aes_setkey_enc(ctx, key, 256)sub_376EF88(ctx, len, *(this + 336), in, out);       // mbedtls_aes_crypt_cfb128sub_376EDF0(ctx);                                    // mbedtls_aes_free

③ 追踪密钥来源this+352的 key 由调用者传入。反查 xref,sub_3804BEC(xref0x3804F08)和sub_3805E9C(xref0x3806140)均通过逐字节循环从byte_400EF18复制密钥:

// sub_3805E9C 伪代码摘录do {    v23 = byte_400EF18[i];    // .data 段静态密钥    key_buf[i++] = v23;while (...);sub_38013D0(fae, &file, &key_vec, 00, &iv);  // open_and_parse

④ 读取密钥:IDA 中直接读取byte_400EF18,32 字节即为 AES-256 密钥:

地址 0x400EF18:CE 4D F8 75 359 A5 A3 9A DE 58 AC 07 EF 94 7A3D A3 9F 2A F7 532 84 D5 12 17 C0 449 A0 61

1.3 CFB128 解密

APK 解包后assets/目录下的.gdc/.scn均为加密文件,格式:

[0..16]   MD5(明文校验)[16..24]  uint64 LE 明文长度[24..40]  16-byte IV[40..]    AES-256-CFB128 密文

用 1.2 提取的 key 做标准 CFB128 解密,校验MD5(明文) == 文件头 MD5通过,确认决赛使用标准 CFB128(初赛为 position-XOR 变体)。解密后得到 10 个.gdcGDSCmagic)和 7 个.scn(场景文件)。

加密输入:final/assets/(10 个 .gdc + 7 个 .scn)

解密产物:solve/decrypted/(仅 .gdc;.scn 在patch_trigger4_scn.py中就地解密处理)

CFB128 实现:patch_trigger4_scn.py中cfb128_decrypt_standard()

02
GDScript 字节码反混淆

2.1 问题发现

标准gdre_tools反编译输出乱码——关键字错位(if变成matchfunc变成signal等),说明自定义引擎对 GDScript tokenizer 的 token ID 做了重映射。

对照 Godot 4.5 开源代码modules/gdscript/gdscript_tokenizer.h的标准 GDSC 格式,定位到两处自定义修改:

标识符 XOR 加密
标准 Godot 的 identifier pool 是明文 UTF-32 LE,解密后字节码的标识符全部乱码。已知明文推断(标识符中必然包含_readyextends等),用预期 ASCII 与实际字节逐字节 XOR,得到固定密钥0xB6。
Token ID 重映射
标准枚举中FUNC=74, VAR=77, IF=58等被打乱为FUNC=71, VAR=81, IF=51等。

2.2 GDSC 二进制格式

[0..4]  "GDSC" magic → [4..8] version (0x65=Godot4.5) → [8..12] decompressed_size[12..]  zstd payload → 解压后: 标识符池 → 常量池 → 行号表 → token 流

标识符 UTF-32 LE 每字节 XOR0xB6(解密后得到合法 ASCII 变量名)。Token 流每项 4/8 字节,word1[0:7]= token type(重映射 ID),word1[8:]= pool index,word2= 行号(可选)。

2.3 Token 重映射表恢复

从最简单的脚本(label2.gdc、token.gdc)开始,利用 GDScript 语法结构逐步推断映射:

IDENTIFIER(12) / LITERAL(13)
最先确定——它们携带 pool index,出现频率最高
结构关键字
func name(args):模式 → 确定 FUNC(71)、PAREN_OPEN(88)、PAREN_CLOSE(89)、COLON(95)
控制流
if ... :/while ... :/for ... in ...→ 确定 IF(51)、WHILE(55)、FOR(54)、IN(72)
运算符
结合加密代码上下文,& 0xFF→ AMPERSAND(26),^ key→ CARET(29),<< 3→ LESS_LESS(30)

共恢复 35+ 个映射(部分摘录):

由于 token 枚举表内联在引擎解释器中,未以独立数据结构存在,无法直接从二进制提取,改用上述语义推断法逐一恢复。完整映射表见parse_gdc.py。

2.4 自定义解析器

python solve/godot/parse_gdc.py <file.gdc> --reconstruct   # 反编译为 GDScriptpython solve/godot/parse_gdc.py <file.gdc> --raw            # 原始 token 流

所有 10 个 .gdc 均成功反编译为可读 GDScript。

产物:solve/godot/parse_gdc.py

2.5 反编译结果 — 三个计分 Trigger

反编译后可读出完整 flag 生成逻辑。完整反编译源码见solve/decompiled/Trigger/。

trigger2.gd— PART1(纯 GDScript Feistel):碰撞回调_w7读取 Token,调用_fe()做 8 轮 Feistel 加密,密钥"Sec2026_Godot",输出 flag。算法完全在 GDScript 中,无 native 参与:

func _w7(_ar):var _tk := str(_lt.text).substr(7)     # "Token: a1b2c3d4" → "a1b2c3d4"var _rs := _fe(_tk)                     # Feistel 加密    _lb.text = "flag{" + _rs + "}"          # flag{sec2026_PART1_XXXXXXXX}

trigger3.gd— PART2(调 native Process):碰撞回调将 Token 的 UTF-8 字节(注意:不是 hex→bytes,是 ASCII 直传)传给GameExtension.Process(),由 libsec2026.so 的自定义 AES 加密:

func _w7(_ar):var _raw := str(_lt.text).substr(7)     # "a1b2c3d4"var _buf := _raw.to_utf8_buffer()       # ASCII 字节,不是 hex decodevar _rv := _gx.Process(_buf)            # ★ native 加密    _lb.text = "flag{sec2026_PART2_" + _rv + "}"

trigger4.gd— PART3(完全 native):没有body_entered.connect(),碰撞不由 GDScript 处理。GDScript 侧仅有_gx.Tick()每帧调用(后续逆向证实 Tick 仅做反剥离计时,见 4.3)。真正的 flag 生成由碰撞触发的 native 隐藏回调完成(见第七章 7.3):

func _ready():    _gx = GameExtension.new()func _process(_d):    _gx.Tick()      # 仅反剥离计时,不触发 PART3 计算    _rv = _d        # delta time    _tv += _d * 2.0_m3()

03
libsec2026.so 逆向基础

3.1 字符串解密

IDA 打开 libsec2026.so 后,搜不到任何明文字符串("Process"、"Tick"、"ClassDB" 等均不存在)。观察到大量函数具有相同的 prologue 特征(FFC300D1 E01700F9 E11300F9 E20F00F9 E31700B9),其反编译结果为 XOR 解密循环,将.rodata密文写入.bss段:

两种变体:

  • XOR_PLAIN
    out[i] = cipher[i] ^ key[i % 8]
  • XOR_WITH_INDEX
    out[i] = cipher[i] ^ i ^ key[i % 8]

编写 IDAPython 脚本ida_decrypt_strings.py:搜索.text段中具有上述 prologue 特征的解密函数(共 31 个),反编译每个调用者提取(dest, src, key, len)四元组,批量解密并在 IDA 中添加注释。

解密后发现关键字符串和对应功能:

通过字符串解密,定位到sub_97B6C为方法注册入口(注册 Process/Tick/input 三个 GDExtension 方法),进而追踪到 PART2 handler(sub_97704)和 Tick handler(sub_9AD68)。

3.2 CFF 去混淆

libsec2026.so 全部函数均被 CFF(Control Flow Flattening)混淆,存在两种变体。

变体 A — 集中式 CMP 树

用于辅助函数(字符串解密、反调试等)。所有基本块共享一个中央 dispatcher,state 是 32-bit 编码 hash,经EOR+ADD解码后通过 CMP 二叉搜索树匹配目标块:

block_i:    ... 业务代码 ...    MOV  W8, #0x7DDB08B6           // 下一个 state(编码 hash)    CSEL W8, W9, W8, EQ            // 条件分支:根据结果选 hash    B    dispatcherdispatcher:                         // 全函数唯一    EOR  W8, W8, #0xC3             // 解码 step 1    ADD  W8, W8, #0x71             // 解码 step 2CMP  W8, #0x1EE0E901           // CMP 二叉搜索树    B.LE lower_halfCMP  W8, #0x316195C5    B.EQ block_17                  // 匹配 → 跳转    ...

IDA 则需手动解析 hash 解码 + CMP 树。

变体 B — 内联双表派发

用于核心加密函数(AESsub_A936C、TEAsub_A9A7C等,共 ~136 个)。没有中央 dispatcher,每个块后内联一套两级查找:

prologue:    ADRL  X22, state_tab            // dword 查找表    ADRL  X23, jpt                  // qword 跳转表block_i:    ... 业务代码 ...    MOV   W8, #next_state           // state = 小整数 (0~127)    STUR  W8, [X29, #-4]    LDURSW X8, [X29, #-4]          // ① 读 state    LDRSW  X8, [X22, X8, LSL#2]    // ② state_tab[state] → jpt 索引    LDR    X8, [X23, X8, LSL#3]    // ③ jpt[idx] → 代码块地址    BR     X8                       // ④ 间接跳转(每个块都有完整副本)

两级查找的反分析效果:

state_tab[] (dword):           jpt[] (qword):┌────────┬──────┐              ┌─────┬────────────┐state0 │→ 10  │              │ [0] │ 0x130644   │state1 │→ 5   │              │ [1] │ 0x130184   │state2 │→ 26  │              │ ... │    ...      ││  ...   │ ...  │              │[44] │ 0x13031C   │└────────┴──────┘              └─────┴────────────┘① 多个 state 可映射到同一 jpt 索引(代码块共享)② jpt 中混入冗余条目,干扰静态枚举③ state 常量不直接对应跳转目标,对抗常量传播

内联派发消除了集中 dispatcher 这个单点突破口;间接表使 state 常量不再直接对应跳转目标——同时对抗两类自动化攻击。

去混淆方案

三种方案逐步演进,最终 IDA 工具链覆盖全部变体 B 函数:

IDA dispatch tail 重写覆盖率134/136(98.5%),2 个跨块寄存器传递需手动处理。花指令 NOP(patch_junk_code.py)作为预处理步骤清除干扰指令。

单层 CFF(变体 A:集中式 CMP 树 dispatcher):

双层 CFF(变体 B:内联双表state_tab[state]jpt[idx]BR):

Patch 修复前后对比(IDA 函数列表 + 反编译,修复前截断 vs 修复后完整):

还原后的控制流图(CFG 状态机可视化):


04
反调试绕过(10 分)

4.0 检测发现方法

直接 Frida attach 会被秒杀(exit_group(0)),静态分析面对全函数 CFF 混淆也很难枚举所有检测点。采用自研模拟器沙箱完整捕获 libsec2026.so 的运行时行为。

日志规模:单次运行产生 ~1.5GB trace,包含每条 syscall 的编号、参数、返回值,以及每个库函数调用的函数名和参数。

筛选过程:将 trace 日志交由 AI 分析,按 syscall 编号和参数模式自动分类——标记出openat("/proc/self/task"),openat("/proc/self/fd"),openat("/proc/self/maps"),ptrace(ATTACH),process_vm_readv,clock_gettime高频调用等异常模式。每个 AI 标记的检测点再回 IDA 中定位对应函数、反编译确认逻辑。最终梳理出 3 个检测线程共 9 项检测机制。

检测总入口sub_99094创建 3 个 pthread:

// sub_99094 — 检测线程总入口void sub_99094() {pthread_create(&t1, 0, sub_9C654, 0);  // ptrace 自保护 + 硬件断点清除pthread_create(&t2, 0, sub_9CDC4, 0);  // Frida 线程名 + 注入器检测pthread_create(&t3, 0, sub_9B7D8, 0);  // exit 反 hook + CRC32 完整性心跳 + maps 扫描}

4.1 Frida 线程检测(sub_9CDC4)

后台线程循环扫描/proc/self/task/*/status,搜索线程名gum-js-loop(Frida)/gmain(GLib),检测到则exit_group(0)

// sub_9CDC4 (0x9CDC4 ~ 0x9EA1C) — ~70 case CFF,精简后:voidfrida_detection_thread() {sleep(1);// 解密字符串// sub_97AE0 → "/proc/self/task"// sub_96848 → "gum-js-loop"// sub_9A9A8 → "gmain"    DIR *dir = opendir("/proc/self/task");while ((ent = readdir(dir)) != NULL) {snprintf(path, 256"/proc/self/task/%s/status", ent->d_name);int fd = syscall(56 /*openat*/, AT_FDCWD, path, O_RDONLY);// 逐行读取,搜索 "gum-js-loop" 或 "gmain"if (strstr(line, "gum-js-loop") || strstr(line, "gmain"))syscall(94 /*exit_group*/0);  // 杀进程    }sub_99418();  // 接着执行注入器检测}

绕过:Hookopenat,当路径含/proc/self/task时替换为无效路径。

4.2 注入器检测(sub_99418)

扫描/proc/self/fdreadlinkat读取每个 fd 的符号链接目标,搜索linjector

// sub_99418 (0x99418 ~ 0x9A1EC) — 精简后:voidinjector_detection() {// 解密: "/proc/self/fd", "linjector"    DIR *dir = opendir("/proc/self/fd");while ((ent = readdir(dir)) != NULL) {snprintf(path, 256"/proc/self/fd/%s", ent->d_name);lstat(path, &st);if ((st.st_mode & S_IFMT) == S_IFLNK) {syscall(78 /*readlinkat*/, AT_FDCWD, path, buf, 256);if (strstr(buf, "linjector"))syscall(94 /*exit_group*/0);        }    }}

绕过:同上,Hookopenat拦截/proc/self/fd

4.3 检测线程心跳 + CRC32 完整性互锁(sub_9AD68 ↔ sub_9AF98 ↔ sub_96A00)

通过逆向sub_9AD68(Tick handler)和交叉引用data_1834b8,发现它并非简单的"帧间隔检测",而是与代码完整性校验构成心跳互锁机制。

写端 — 检测线程(sub_96A00 → sub_9AF98)

sub_96A00每 ~14 秒通过dl_iterate_phdr获取 .text 段,调用sub_9AF98计算 CRC32(多项式0xEDB88320)。关键在于sub_9AF98的 CRC32 循环中,每处理 4096 字节就执行一次usleep(10μs)+clock_gettime并更新全局变量data_1834b8(心跳时间戳)。

// sub_9AF98 — CRC32 + 心跳uint32_t crc32_with_heartbeat(uint8_t *data, size_t len, uint32_t init) {    uint32_t crc = init;for (size_t i = 0; i < len; i++) {        uint32_t val = crc ^ data[i];for (int j = 0; j < 8; j++)          // 标准 CRC32val = (val & 1) ? (0xEDB88320 ^ (val >> 1)) : (val >> 1);        crc = val;if ((i & 0xFFF) == 0) {               // 每 4096 字节            usleep(10);            clock_gettime(CLOCK_MONOTONIC, &ts);            data_1834b8 = ts.tv_sec * 1000000 + ts.tv_nsec / 1000;  // 更新心跳        }    }return ~crc;}

校验结果不匹配 →exit_group(0)杀进程。

读端 — Tick handler(sub_9AD68)

每帧读取data_1834b8,与当前时间比对:

// sub_9AD68 — Tick handlervoidtick_handler() {if (data_1834b8 == 0) {                    // 首次调用:初始化clock_gettime(CLOCK_MONOTONIC, &ts);        data_1834b8 = ts.tv_sec * 1000000 + ts.tv_nsec / 1000;return;    }clock_gettime(CLOCK_MONOTONIC, &ts);int64_t now = ts.tv_sec * 1000000 + ts.tv_nsec / 1000;if (llabs(now - data_1834b8) > 10000000)   // > 10 秒return;  // 心跳超时 → 停止处理}

互锁设计

  • 检测线程存活时:CRC32 循环不断更新data_1834b8→ Tick 读到新鲜心跳 → 正常
  • 检测线程被 kill/freeze:data_1834b8停止更新 → Tick 检测到 > 10 秒 → 游戏停止处理
  • CRC32 与心跳在同一循环中交织,无法单独绕过任何一方

绕过:不 kill 检测线程,采用数据层 Patch 不改 .text 段 → CRC32 自然通过 → 心跳持续。

4.4 exit 反 hook + CRC32 完整性心跳(sub_9B7D8)

sub_9B7D8是检测最密集的线程,包含 3 种校验。

初始化阶段sleep(3)setpriority(nice=19)→ 以下检查按顺序执行:

exit() inline hook 检测:检查*(uint32_t*)exit == 0x50000058,即 exit 函数的首条 ARM64 指令是否被篡改。如果被 inline hook 则标记异常。

exit 页面写保护mprotect(page_align(&exit), page_size, PROT_READ|PROT_EXEC)— 去掉 exit 所在页的写权限,防止后续 inline hook 注入。

/proc/self/maps 段定位(sub_98564 + sub_9A470):

    • raw syscallopenat打开/proc/self/maps(防 Frida hook libc IO)
    • 逐字节读取每行,sub_98564用 sscanf 解析start-end perms
    • sub_9A470
      将段名与目标库名比较(字符串匹配),定位 .text 段基址供 CRC32 校验使用
// sub_9B7D8 — exit 反 hook + CRC32 完整性校验(去除 CFF 后还原)// @ 0x9B7D8, 子函数 0x98564, 0x9A470, 0x96A00voidintegrity_guard_thread(void* arg) {sleep(3);setpriority(PRIO_PROCESS, 019);// Step 1: exit inline hook 检测if (*(uint32_t*)exit != 0x50000058)flag_abnormal();// Step 2: 保护 exit 页面,防 inline hookvoid* exit_page = (void*)(-page_size & (uint64_t)&exit);mprotect(exit_page, page_size, PROT_READ | PROT_EXEC);// Step 3: raw syscall 打开 maps → 定位 .text 段int fd = syscall(SYS_openat, AT_FDCWD,decrypt_str(data_1682d0), 00);  // → "/proc/self/maps"char line[512]; int pos = 0char c;while (syscall(SYS_read, fd, &c, 1) == 1) {if (c == '\n') {            line[pos] = 0;locate_text_segment(line);         // sub_98564            pos = 0;        } else line[pos++] = c;    }// Step 4: CRC32 循环(每 3 秒)while (1) { crc32_check(); sleep(3); }}// sub_98564 — maps 行解析 + 段定位voidlocate_text_segment(char* line) {     // @ 0x98564uint64_t start, end; char perms[8];sscanf(line, "%lx-%lx %s", &start, &end, perms);if (perms == "r--p" || perms == "rw-p")match_and_store(start, end);       // → sub_9A470(字符串匹配,非解密)}// sub_96A00 — CRC32 完整性校验voidcrc32_check() {                       // @ 0x96A00dl_iterate_phdr(find_text_phdr, &ctx); // sub_9EFB4: 定位 .text PT_LOAD 段uint32_t crc = crc32(ctx.base, ctx.size); // sub_9AF98: 多项式 0xEDB88320if (crc != expected_crc)               // dword_1682C8syscall(SYS_exit_group, 0);        // 杀进程}

循环阶段(每 3 秒):

CRC32 完整性校验
sub_96A00dl_iterate_phdr(sub_9EFB4)定位 .text 段 →sub_9AF98计算 CRC32(多项式0xEDB88320)→ 不匹配则exit_group(0)。CRC32 循环中每次更新心跳data_1834b8(即 4.3 的写端)。

绕过:采用数据层 Patch(修改 .scn 场景资源)而非代码层 Patch,完全不改 .text 段。不 hook exit,不改内存权限,CRC32 校验自然通过,心跳持续。

4.5 ptrace 自保护 + 硬件断点清除(sub_9C654)

sub_9C654使用 fork + ptrace 自附加,实现三层反调试:

// sub_9C654 — 逆向还原voidptrace_guard_thread(void* arg) {pid_t pid = getpid();pid_t child = fork();if (child == 0) {// 子进程if (ptrace(PTRACE_ATTACH, pid, 00) == -1)exit(1);  // 已有调试器 → 退出while (1) {waitpid(pid, &status, 0);if (WIFEXITED(status)) break;if (WIFSTOPPED(status)) {int sig = WSTOPSIG(status);sub_95CC0(pid);                    // 清除硬件断点if (sig == SIGSTOP || sig == SIGTRAP)ptrace(PTRACE_CONT, pid, 00);     // 吞掉调试信号elseptrace(PTRACE_CONT, pid, 0, sig);   // 转发其他信号            }        }    }return 0;}// sub_95CC0 — 硬件断点清除voidclear_hw_breakpoints(pid_t pid) {uint32_t regs[17];struct iovec iov = {regs, 0x44};ptrace(PTRACE_GETREGSET, pid, NT_ARM_HW_BREAK/*0x402*/, &iov);int num_brps = min((regs[0] >> 12) & 0xFF8);for (int i = 0; i < num_brps; i++) {        regs[1 + i*2] = 0;  // 地址清零        regs[2 + i*2] = 7;  // 控制位:地址=0 时断点无效    }ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_BREAK, &iov);}
  • 第 1 层
    ptrace(ATTACH) 占住调试位,外部调试器无法 attach
  • 第 2 层
    每次父进程被信号暂停时,清除所有 ARM64 硬件断点寄存器(NT_ARM_HW_BREAK)
  • 第 3 层
    正确转发信号,使父进程在被跟踪状态下仍正常运行

绕过:Frida spawn 模式不依赖 ptrace 和硬件断点,不受影响。

4.6 /proc/self/maps Frida 注入检测(T3 sub_9B7D8 状态机)

属于 T3 线程sub_9B7D8状态机的一个分支(case 10 → case 8 → case 1)。与 4.1 的/proc/self/task线程名扫描不同,这里扫描/proc/self/maps寻找 Frida 注入痕迹:

// sub_9B7D8 case 10: 用 raw syscall 打开 maps(绕过 libc hook)int fd = syscall(SYS_openat/*56*/, AT_FDCWD, "/proc/self/maps"00);// case 8: 逐字节读取(绕过缓冲区级 hook)while (syscall(SYS_read/*63*/, fd, &byte1) == 1) {if (byte == '\n') {        line[pos] = '\0';        sub_98564(line);  // 解析并检测        pos = 0;    } else {        line[pos++] = byte;  // 最大 510 字节    }}

核心检测在sub_98564(maps 行解析器):

intsub_98564(constchar* maps_line) {// 3 个字符串均为 XOR 加密存储(密钥 byte_1834B0),运行时解密char* frida_str  = decrypt("frida");       // data_1682F4char* memfd_str  = decrypt("/memfd:");      // data_1682FAchar* fmt        = decrypt("%lx-%lx %s");   // data_168302unsignedlong start, end;char perms[256];sscanf(maps_line, fmt, &start, &end, perms);// 检测 1: 路径中包含 "frida"(Frida agent .so)if (strstr(maps_line, "frida"))   return 1;  // DETECTED// 检测 2: 路径中包含 "/memfd:"(Frida 匿名内存注入)if (strstr(maps_line, "/memfd:")) return 1;  // DETECTEDreturn 0;}

反分析手段

① raw syscall 绕过 LD_PRELOAD/libc hook

② 逐字节读取绕过缓冲区拦截

③ 关键字 XOR 加密 

④ CFF 状态机 ~19 个 case 混淆控制流

绕过:数据层 Patch 不注入任何 .so,maps 中无异常条目。

4.7 process_vm_readv 交叉完整性校验(libgodot_android.so → libsec2026.so)

沙箱 trace 发现libgodot_android.so中的sub_10B9E4C每帧检查libsec2026.so 的 .text 段(不是自身)——形成交叉校验,两个库互相守护:

// sub_10B9E4C (libgodot_android.so) — 每帧调用,跳过前 480 帧(~8 秒热身)void integrity_check() {// 定位 libsec2026.so 基址void* ext_init = dlsym(libsec2026, "extension_init");  // offset 0xA4074voidbase = ext_init - 0xA4074;// 用 process_vm_readv 读取 libsec2026 的 .text 段// 范围: base+0x95C70 ~ base+0x15FE80, 大小 0xCA210 (827920 字节)struct iovec local  = { heap_buf, 0xCA210 };struct iovec remote = { base + 0x95C700xCA210 };    syscall(SYS_process_vm_readv/*270*/, self_pid, &local, 1, &remote, 10);// 每帧处理 0x19000 字节(分帧校验,~9 帧完成一轮)for (i = offset; i < offset + 0x19000 && i < 0xCA210; i++)        sum = (sum + heap_buf[i]) % 0xFFFFFF;if (done_full_pass) {if (sum != 0x11FFDD) {  // 期望校验和// 篡改检出!静默关闭游戏            pthread_create(&t, 0, Main_Cleanup, 0);  // 新线程执行引擎清理        }        sum = 0;  // 重置,开始下一轮    }}

设计精妙之处

  • 跨库守护
    libgodot 校验 libsec2026 的 .text(本节),libsec2026 的 T3 校验自身 .text(4.4 的 CRC32)→ libsec2026 的 .text 受双重保护
  • 分帧处理
    每帧仅处理 100KB,不影响渲染帧率
  • 静默退出
    不调用exit_group,而是pthread_create新线程执行Main::Cleanup,游戏"正常关闭",难以定位检测点
  • 热身跳过
    前 480 帧(~8 秒)不检测,等 libsec2026 完成初始化
参数
说明
.text 偏移
0x95C70
libsec2026.so .text 段起始
.text 大小
0xCA210
(808KB)
完整 .text 段
校验算法
逐字节累加 mod 0xFFFFFF
简单但覆盖全段
期望值
0x11FFDD
存储在 libgodotdword_400A050
每帧处理量
0x19000
(100KB)
~9 帧完成一轮

绕过:数据层 Patch 不修改任何 .text 段,校验和自然通过。若必须 Patch .text,需同步修改 libgodot 中的期望值dword_400A050

4.8 策略总结

T1=sub_9C654, T2=sub_9CDC4, T3=sub_9B7D8

统一绕过策略:采用数据层 Patch(修改 .scn 场景资源触发 flag 计算),不修改任何 .text 段代码、不 hook 任何函数、不 kill 任何检测线程。9 项检测全部自然通过。

05
PART1 — Feistel 加密(15 分)

5.1 算法

反编译trigger2.gdc,_w7回调中包含完整加密代码(纯 GDScript,不涉及 native)。

8 轮 Feistel 网络,输入 4 字节(Token hex→bytes),分组 2+2 字节:

密钥 K = b"Sec2026_Godot" (13 字节)轮函数 F(block, K, r):  对每字节 j:  v = block[j] ^ K[(j+r) % 13]    // XOR 密钥               v = (v × 7 + r) & 0xFF           // 仿射变换               v = ROL3(v)                       // 8-bit 左旋 3 位加密: lo, hi = token[0:2], token[2:4]      重复 8 轮: lo, hi = hi, lo ⊕ F(hi, K, r)解密: 轮序反转 (r: 70),交换 lo/hi

5.2 验证

5.3 运行时字节码 Patch 验证

trigger2 需要碰撞触发(3D 场景中撞到 Trigger2),可通过 HookGDScriptTokenizerBuffer::set_code_buffer在运行时修改字节码,将_process()中的动画调用_m3(_d)替换为 flag 回调_w7(_d),使 flag 每帧自动生成。

关键地址(libgodot_android.so)

Patch 细节:解压后偏移0x1F80处为 Token[603]:0x00003C8C(IDENTIFIER, pidx=60 →_m3),将buf[0x1F81]0x3C改为0x30,pidx 从 60(_m3) 变为 48(_w7)。

// Hook at MOV W25, #0xB6 inside set_code_bufferHook::AddExecuteHandler(base + 0x147D4A8,    [](Hook::VContext* ctx, uintptr_t pc, bool after) -> void {if (after) return;uint8_t* buf = (uint8_t*)(ctx->x[20] - 0x10);// trigger2: offset 0x1F80 = 0x00003C8C (_m3) → _w7if (*(uint32_t*)(buf + 0x1F80) == 0x00003C8C) {        buf[0x1F81] = 0x30;  // _m3(idx=60) → _w7(idx=48)    }});

运行结果:Token8dce44a5,输出flag{sec2026_PART1_154ca922}

真机验证(MuMu 模拟器,Token8dce44a5,碰撞 Trigger2 后显示 flag):

C++ 核心实现(part1_feistel.cpp):

staticvoidround_fn(constuint8_t* block, int len, int round_num, uint8_t* out) {for (int j = 0; j < len; j++) {uint8_t v = block[j] ^ KEY[(j + round_num) % KEY_LEN];        v = (uint8_t)((v * 7 + round_num) & 0xFF);        v = (uint8_t)(((v << 3) | (v >> 5)) & 0xFF);  // ROL3        out[j] = v;    }}std::string part1::encrypt(const std::string& token_hex) {uint8_t lo[2] = {data[0], data[1]}, hi[2] = {data[2], data[3]};for (int rn = 0; rn < ROUNDS; rn++) {uint8_t fv[2], new_hi[2];round_fn(hi, 2, rn, fv);xor_bytes(lo, fv, 2, new_hi);   // new_hi = lo ^ F(hi)        lo[0] = hi[0]; lo[1] = hi[1];   // lo ← hi        hi[0] = new_hi[0]; hi[1] = new_hi[1];    }return bytes_to_hex({lo[0], lo[1], hi[0], hi[1]}, 4);}std::string part1::decrypt(const std::string& cipher_hex) {// 逆向:从最后一轮往回推,交换 lo/hi 角色for (int rn = ROUNDS - 1; rn >= 0; rn--) {round_fn(lo, 2, rn, fv);xor_bytes(hi, fv, 2, new_lo);        hi = lo; lo = new_lo;    }}

产物:part1_feistel.cpp(C++ 加密+解密),flag.py(Python)

运行:flag_tool.exe encrypt <token>/flag_tool.exe decrypt 1 <hex>

06
PART2 — 自定义 AES-128-CBC(25 分)

6.1 发现过程

反编译trigger3.gdc,_w7回调将 Token 的 UTF-8 字节传入GameExtension.Process(),返回 32 字符 hex 拼为 flag:

var _rv := _gx.Process(_buf)_lb.text = "flag{sec2026_PART2_" + _rv + "}"

6.2 IDA 逆向调用链

CFF 去混淆后追踪Process()handler(sub_97704)→ sub_A936C。

以下为 CFF 状态机精简后的等价伪代码:

// sub_A936C (0xA936C ~ 0xA9664) — PART2 加密入口__int64 sub_A936C(__int64 token, __int64 a2, __int64 output) {__int64v20 =0, v21 = 0;  // 16 字节明文缓冲    _memcpy_chk(&v20, token, 817);   // buf[0:8]  = token    _memcpy_chk(&v21, token, 89);    // buf[8:16] = token (重复!)    v17 = xmmword_58550;               // AES key    v18 = xmmword_58600;               // AES IV    sub_A7900(v19, &v17, &v18);        // 密钥扩展    sub_A7194(v19, &v20, 0x10);        // CBC 加密 16 bytes in-place// CFF 状态机: for v15=0..15, snprintf("%02x", buf[v15])// → 输出 32 字符 hex 到 output}
// sub_A7DE8 (0xA7DE8 ~ 0xA7F80) — 密钥扩展(48 words = 11 轮)void sub_A7DE8(__int64 ctx, __int64 key) {sub_A9884();                       // 生成自定义 S-box → byte_183700[256]// 复制 4 words 初始密钥 ctx[0..15] = key[0..15]// for v2 = 4 .. 47://   if (v2 % 4 == 0): RotWord + SubBytes(byte_183700) + Rcon(byte_652C9)//   ctx[4*v2+j] = ctx[4*(v2-4)+j] ^ rotated[j]}
// sub_A7194 (0xA7194 ~ 0xA7308) — AES-CBC 加密__int64 sub_A7194(__int64 ctx, __int64 pt, unsigned __int64 size) {// for each 16-byte block://   sub_AA9B0(block, iv);   // twisted IV XOR: even→^iv[15-i], odd→^iv[i]//   sub_A8D44(block, ctx);  // AES 加密单块 (11 轮, 全部非标准操作)//   iv = block;             // CBC: 密文作为下一块 IV}

6.3 算法识别:为什么是 AES

密钥扩展 sub_A7DE8(下图):

循环v2 = 4..47→ 48 words = 12 组轮密钥(11 轮 + 初始轮),比标准 AES-128 的 44 words 多一轮。

单块加密 sub_A8D44(下图):

轮循环v5 = 1..11,末轮(case 2)无 MixColumns。与标准 AES 的 SubBytes → ShiftRows → MixColumns → AddRoundKey 四步结构一致,但多了 RoundMix、首尾 Transform,轮数 11 而非 10。

确认框架是 AES 变种后,逐一定位每个非标准参数:

6.4 与标准 AES-128 的差异

参数提取:CFF 去混淆后从 IDA 伪代码逐一读取,以下为关键证据。

S-box 仿射变换sub_A8C8C)— 常数0x8F和 ROL5 直接可见:

// sub_A8C8C — S-box 仿射变换v1 = sub_A7598(a1);                           // GF(2^8) 求逆v2 = sub_A8744(v1, 1);  v3 = sub_A8744(v1, 2);v4 = sub_A8744(v1, 3);  v5 = sub_A8744(v1, 4);return sub_A8744(v2 ^ v3 ^ v4 ^ v5 ^ v1 ^ 0x8F5);   // ← affine 0x8F + ROL5

GF(2^8) 多项式sub_A96F0)—& 0x71即 reduction poly0x171

// sub_A96F0 — GF 乘法 (gf_mul)a1 = ((unsignedint)(char)v3 >> 7) & 0x71 ^ (2 * v3);   // ← poly = 0x171

MixColumns 系数sub_A6F20)—[6,3,5,2]循环矩阵:

// sub_A6F20 — MixColumnsv9[0] = gf_mul(v10,6) ^ gf_mul(v11,3) ^ gf_mul(v12,5) ^ gf_mul(v13,2);  // [6,3,5,2]v9[1] = gf_mul(v10,2) ^ gf_mul(v11,6) ^ gf_mul(v12,3) ^ gf_mul(v13,5);  // [2,6,3,5]v9[2] = gf_mul(v10,5) ^ gf_mul(v11,2) ^ gf_mul(v12,6) ^ gf_mul(v13,3);  // [5,2,6,3]v9[3] = gf_mul(v10,3) ^ gf_mul(v11,5) ^ gf_mul(v12,2) ^ gf_mul(v13,6);  // [3,5,2,6]

Rcon 表byte_652C9,IDA hex dump):

A7 A6 A5 A3 AF B7 87 E7 27 D6 45 FC 25 30 38 78

ShiftRows 置换sub_AADE8)— IDA 完整反编译(无 CFF),直接读出字节置换:

// sub_AADE8 — ShiftRows (固定置换,IDA 完整反编译)// 追踪每个赋值: out[i] ← in[src[i]]// 源索引: [0, 13, 6, 11, 4, 1, 10, 15, 8, 5, 14, 3, 12, 9, 2, 7]v1=r[5]; v2=r[1]; v3=r[13];r[13]=r[9]; r[9]=v1; r[5]=v2; r[1]=v3;   // 列 1 循环: 1←13←9←5←1v4=r[6]; v5=r[10]; v6=r[14]; v7=r[2];r[2]=v4; r[6]=v5; r[10]=v6; r[14]=v7;    // 列 2 循环: 2←6←10←14←2v8=r[11]; v9=r[3]; v10=r[15]; v11=r[7];r[3]=v8; r[11]=v9; r[7]=v10; r[15]=v11;  // 列 3 循环: 3←11←3, 7←15←7

RoundMixsub_A8F00)— CFF 混淆,通过  Unicorn 差分提取:

# 可见初始化: var_dc = 0x47 + arg2 * 0xffffff9d# 即 seed = (71 - 99 * round_num) & 0xFFdef roundmix(state, round_num):    v = (71 - 99 * round_num) & 0xFF      # PRNG 种子( 0x47 + r*0xffffff9d)    prng = []for _ in range(16):        prng.append(v)        v = (47 - 61 * v) & 0xFF           # PRNG 步进(IDA case 7: 47 - 61*v19)    old = list(state)for j in range(16):                     # 列反转 + XOR        col, row = j // 4, j % 4        state[j] = old[(3 - row) * 4 + col] ^ prng[j]

InitTransform / FinalTransform— CFF 混淆,但引用的数据地址可直接读取:

sub_A7944 (InitTransform) 引用 unk_58510:  DE 4837 C1 659 E2 73 AD 194 B8 06 D5 42→ state[i] ^= INIT_XOR[i]   (加密前)sub_A84A4 (FinalTransform) 引用 unk_58590:7C E3 28 91 A6 5D F0 14 BB 69 07 D8 435 EC 80→ state[i] ^= FINAL_XOR[i]  (加密后)

常量(从 libsec2026.so 提取):

6.5 解密实现

InvMixColumns:正向矩阵[6,3,5,2]在 GF(2^8, 0x171) 上的逆矩阵通过高斯消元法求解(part2_aes.py_compute_inv_mix_matrix()),结果为[0x80, 0xf3, 0x64, 0xaf]循环矩阵。可逆性由 GF(2^8) 的域性质保证(非零行列式)。

完整解密流程(严格逆序):

FinalTransform → AddRoundKey(11) → InvShiftRows → InvRoundMix(11) → InvSubBytes→ 循环 r=10..1: AddRoundKey(r) → InvMixColumns → InvShiftRows → InvRoundMix(r) → InvSubBytes→ AddRoundKey(0) → InitTransform → Twisted IV XOR

6.6 验证(5 组测试向量)

6.7 运行时字节码 Patch 验证

与 PART1 相同方法:Hookset_code_bufferMOV W25, #0xB6(地址0x147D4A8),在解压后修改_process()中的_m3(_d)调用为_w7(_d)

// trigger3: offset 0x1CD4 = 0x00003B8C (_m3) → _w7if (*(uint32_t*)(buf + 0x1CD4) == 0x00003B8C) {    buf[0x1CD5] = 0x29;  // _m3(idx=59) → _w7(idx=41)}

Patch 对比表

运行结果:Token1af763af,输出flag{sec2026_PART2_52f0ab0970da6f1d7c516d0813acc998}

真机验证(Token1af763af,通过字节码 Patch 自动触发,右上角显示 flag):

C++ 核心实现(part2_aes.cpp,~300 行):

// GF(2^8) 乘法 — 约化多项式 x^8+x^6+x^5+x^4+1 = 0x171(非标准 0x11B)staticuint8_tgf_mul(uint8_t a, uint8_t b) {uint8_t p = 0;for (int i = 0; i < 8; i++) {if (b & 1) p ^= a;uint8_t hi = a & 0x80;        a = (a << 1) & 0xFF;if (hi) a ^= 0x71;  // 0x171 的低 8 位        b >>= 1;    }return p;}// MixColumns 矩阵 [6,3,5,2](标准 AES 为 [2,3,1,1])staticconstuint8_t MIX[4][4] = {    {6,3,5,2}, {2,6,3,5}, {5,2,6,3}, {3,5,2,6}};// AddRoundKey — 额外 XOR (row + 91*round_idx) & 0xFFstaticvoidadd_round_key(uint8_t s[16], int rk_idx) {uint8_t extra_base = (uint8_t)((91 * rk_idx) & 0xFF);for (int i = 0; i < 16; i++) {uint8_t row = i % 4;        s[i] ^= ROUND_KEYS[rk_idx][i] ^ ((row + extra_base) & 0xFF);    }}// RoundMix — 置换 + PRNG 异或(每轮不同的伪随机扰动)staticvoidroundmix(uint8_t s[16], int rn) {uint8_t prng[16]; roundmix_prng(rn, prng);uint8_t t[16]; memcpy(t, s, 16);for (int j = 0; j < 16; j++) s[j] = t[RMIX_FWD[j]] ^ prng[j];}// Twisted IV XOR — even 位置倒序、odd 位置正序staticvoidtwisted_xor(uint8_t s[16], constuint8_t iv[16]) {for (int i = 0; i < 16; i++)        s[i] ^= (i % 2 == 0) ? iv[15 - i] : iv[i];}// 11 轮加密(标准 AES 为 10 轮)staticvoidaes_encrypt_block(uint8_t s[16]) {init_transform(s);          // INIT_XORadd_round_key(s, 0);for (int r = 1; r <= 11; r++) {sub_bytes(s);           // 自定义 S-box(affine 0x8F + ROL5)roundmix(s, r);         // 置换+PRNG(标准 AES 无此步骤)shift_rows(s);          // 自定义置换表if (r < 11) { mix_columns(s); add_round_key(s, r); }else { add_round_key(s, 11); final_transform(s); }    }}

产物:part2_aes.cpp(C++ 加密+解密, ~300 行,含完整 S-box/GF(2^8)/MixColumns),part2_aes.py(Python)

运行:flag_tool.exe encrypt <token>/flag_tool.exe decrypt 2 <hex>

07
PART3 — VM 中的 TEA 变种分组密码(50 分)

7.1 发现:Trigger4 被刻意禁用

反编译trigger4.gdc发现:GDScript 侧没有任何 flag 逻辑,没有body_entered.connect(),仅有GameExtension.new()Tick()调用。

解析town_scene.scn(Godot PackedScene RSRC v6 格式)发现 Trigger4 被刻意禁用:

反编译trigger4.gd(代码见第二章 2.5):没有body_entered.connect()signal collided_with声明但 GDScript 侧从未 emit。

碰撞回调必须由 native 层实现——通过字符串解密定位到sub_A07F4(GDExtension 类注册,下图),发现 PART3 碰撞回调注册在虚函数表中,最终指向sub_A9A7C(静态二进制中 0 xref,通过 GDExtension 虚表间接调用,IDA 无法静态追踪)。

结论:Trigger4 在游戏中既不可见也无法通过物理碰撞触发,必须修改场景才能触发。

7.2 场景 Patch

解密.scn后,在 RSRC 的 nodes 数组中修改 variant index 指针,将属性指向正确的 true/false variant:

前 4 处启用碰撞和可见性,后 3 处将 Trigger4 移到玩家出生点(开局即碰撞)。

解密 .scn:加密文件格式[md5:16][pt_len:8][iv:16][ciphertext:...],AES-256-CFB128 解密后验证md5(pt) == stored_md5pt[:4] == b"RSRC"

重加密部署

# 应用 7 处 patch 后pt = bytes(patched_plaintext)ct_new = cfb128_encrypt_standard(KEY, iv, pt + b"\x00" * pad_len)out = hashlib.md5(pt).digest() + struct.pack("<Q"len(pt)) + iv + ct_new# Round-trip 验证assert cfb128_decrypt_standard(KEY, iv, ct_new)[:len(pt)] == pt

替换 APK 中的assets/.godot/exported/133200997/export-...-town_scene.scn,重签名安装。

产物:patch_trigger4_scn.py,parse_scene.py

7.3 Native 逆向架构

方法注册(sub_97B6C)

// sub_97B6C — GDExtension 方法注册(CFF 去混淆后)// case 14: 注册 Processsub_9F4EC("Process", "Process", 0x4A85115FAA17D83FLL, 8);sub_9B5E8(v30, sub_97704, 0);     // sub_97704 = Process handler → PART2// case 0: 注册 Ticksub_97358("Tick", "Tick", 0x97A36EF7A74F5E4ELL, 5);sub_9610C(v27, sub_9AD68, 0);     // sub_9AD68 = Tick handler → 反调试计时// case 28: 注册 inputsub_9CB50("input", "input", 0x7B88A8A1801FE656LL, 6);

PART3 完整执行架构

┌──────────────────────────────────────────────────────────┐│ 后台线程 sub_9B7D8:完整性校验(详见 4.4)               ││                                                          ││  exit 反 hook + maps 段定位 + CRC32 完整性心跳           ││  CRC32 不通过 → exit_group 杀进程                        ││                                                          ││  ★ sub_A9A7C 通过 GDExtension 虚表间接调用              ││    (静态二进制中 0 xref,IDA 无法追踪)                 │└──────────────────────────────────────────────────────────┘                          │                          ▼┌──────────────────────────────────────────────────────────┐│ 每帧调用:GDScript _process() → _gx.Tick()              ││                                                          ││  sub_9AD68 (Tick handler) — 仅做反调试计时:              ││  ├─ clock_gettime(CLOCK_MONOTONIC)                       ││  ├─ 首次调用:记录初始时间 → data_1834b8                 ││  └─ 后续调用:检查帧间隔                                  ││      ├─ > 10秒 → return(反调试:暂停过久则停止)         ││      └─ ≤ 10秒 → 正常返回                                ││  ⚠ Tick 不触发 PART3 计算!                              │└──────────────────────────────────────────────────────────┘                          │                          ▼┌──────────────────────────────────────────────────────────┐│ 碰撞触发(GDExtension 虚表隐藏路径)                      ││                                                          ││  车碰撞 trigger4 (Area3D) → Godot 物理引擎               ││  → notification_func / get_virtual_func 回调              ││  → (虚表间接调用,静态 0 xref) → sub_A9A7C(token)        ││                                                          ││  sub_A9A7C (35-case CFF 状态机,IDA 反编译见下图):        ││  ├─ mutex_lock                                           ││  ├─ 检查 qword_183498 != 0(缓冲区已分配?)              ││  ├─ memcpy token(8B) → byte_1836C0                       ││  ├─ socket (port 5414 = 0x1526)                          ││  ├─ sub_1371F8: VM 初始化 (malloc 0x2030)                ││  ├─ sub_1374A0 注册 VM handlers:                         ││  │   ├─ cmd 101: VMEntry — 提供 token 字节               ││  │   └─ cmd 102: sub_AA758 — 格式化 %08x%08x            ││  ├─ sub_13CD90: VM dispatcher 执行循环                   ││  │   └─ sub_13C67C: 56-case CFF 线性 PC 解释器            ││  │       → 执行 TEA 变种算法                             ││  ├─ 清理: sub_13D374, sub_13C5C8, sub_137360             ││  ├─ mutex_unlock                                         ││  └─ return &unk_1836E0 (flag string, 16 hex)             │└──────────────────────────────────────────────────────────┘                          │                          ▼        flag{sec2026_PART3_XXXXXXXXXXXXXXXX}              (16 hex, 两个 u32: %08x%08x)

7.4 VM 算法提取:Unicorn 差分 trace

面对 56-case 解释器 + CFF 混淆,直接静态分析不现实。采用Unicorn 模拟 + 差分 taint方案:

关键前提验证:5 组不同输入的指令执行数均为20,019,435条——控制流完全数据无关,算法是固定顺序的算术操作序列。

差分方法

  • 对两组仅 1 字节不同的输入分别执行,挂UC_HOOK_MEM_WRITE记录全部写入
  • 对比同一位置的写入值差异,定位输入敏感的内存 slot(约 10 个)
  • 追踪关键 slot 的值变化序列,识别轮结构

性能优化:初版用 Python 回调实现 libc 函数,每 token 约 30s。改用 Keystone 生成 ARM64 原生 stub(memcpy/memset/strlen 等),降至 ~0.5s/token(60x 加速)。

产物:vm_fast.py(Unicorn 模拟器),vm_trace.py(差分 trace)

7.5 算法还原过程

Step 1 — 差分 taint 定位输入敏感地址

挂载UC_HOOK_MEM_WRITE,记录全部写入(地址, 值)。两组仅首字节不同的输入对比后,约 1400 万次写入中 ~5000 处值不同,聚类后集中在 10 个 slot:

Step 2 — 轮数识别

追踪 0x80329e08(v0 输出 slot)的值变化序列:

Transitionsfor0x80329e08 (input=0x00*8):[ 0]0x00000000  (初始)[ 1]0xe3546957[ 2]0x9c660d6c      ...[27]0xaf607752[28]0x268193dc  (= 最终输出高 32 位)

29 次 transition = 初始值 +28 轮更新。

Step 3 — 轮函数还原

3a. 初次追踪中间值:slot de8 的每次写入值,Round 1:

Round 1 中间值(零输入,da8 初始 = 0xc212cc99):  ① 0x3084b32640  → v1 << 6 的完整 64 位结果  ② 0xb860632e    → (v1<<6)&0xFFFFFFFF + key[3]  ③ 0xebf86938    → v1 + sum  ④ 0x53980a16    → ② XOR ③  ⑤ 0x06109664    → v1 >> 5  ⑥ 0xb0cc6341    → ⑤ + 0xaabbccdd  ⑦ 0xe3546957    → ④ XOR ⑥ = Round 1 输出

这看起来像(A + K1) ^ (B) ^ (C + K2)的 TEA 结构。

3b. 确认移位量(对多组输入交叉验证):

  • v1=0xc212cc99 时,v1<<6 = 0x3084B32640 → ✅ 匹配 trace ①
  • v1<<4 = 0xC212CC990 → ❌ 不匹配

确认:v0 update 的左移量是 6(标准 TEA 为 4)。同理 ⑤ = v1 >> 5(标准)。

3c. 错误假设:v1 不变

观察 da8 slot 在全零输入时的前几十次写入,初始值 0xc212cc99 反复出现。一度假设v1 在 28 轮中保持不变(单侧 Feistel)。在全零输入上"恰好"多轮生效,但非零输入上完全对不上。

3d. 关键发现:da8 是多用途寄存器

用输入0x4142434445464748运行,da8 在 Round 2 出现完全不同的序列——da8 不仅存储 v1,还用作三项 XOR 计算的临时缓冲。v1 在 Round 2 就被更新了。

3e. 识别两阶段轮结构

Round 2 的 de8 中间值序列比 Round 1 长一倍——两组 TEA 计算:

Round 2 de8 中间值:  -- Phase 1 (v1 update) --  d[0] = (v0<<4)            ← 注意:v0 的移位!  d[1] = (v0<<4)&M + key[1] ← 用 key[1]  d[2] = v0 + sum_new  d[3] = d[1XOR d[2]  d[4] = v0 >> 7            ← >>7 而非 >>5  d[5] = (v0>>7) + key[2]  d[6] = d[3XOR d[5]  d[7] = v1_old + d[6]      ← v1 更新!  -- Phase 2 (v0 update) --  d[8] = (v1_new<<6)        ← 用更新后的 v1  ...和 Round 1 相同的结构

至此轮结构清晰:Round 1 仅 v0 update;Round 2~28 先 v1(用 v0 的 <<4/>>7)再 v0(用新 v1 的 <<6/>>5)。

Step 4 — delta 的真实身份

4a. 初始错误:VM 字节码中最显眼的常数0xaabbccdd,以为是 delta。

4b. trace 推翻:追踪中项(v1 + sum)反推 sum:

Round 1: sum = 0xebf86938 - 0xc212cc99 = 0x29e59c9fRound 2: sum = 0x53cb393e = 2 × 0x29e59c9fRound 3: sum = 0x7db0d5dd = 3 × 0x29e59c9f

结论:delta = 0x29e59c9f(非0xaabbccdd!后者是 v0 右项加数,即 7.6 表中的 key[0])。

Step 5 — 4 个候选模型淘汰赛

Step 6 — 输入映射

用 8 组单字节输入观察 v0/v1 初始值变化:

token = 01 00 00 00 00 00 00 00  →  v0_init = 0x00000000, v1_init 变化token = 00 00 00 00 01 00 00 00  →  v0_init = 0x00000001, v1_init 也变化
  • v0_init = le32(token[4:8])
  • v1_init = f_v1(v0_init, delta) + le32(token[0:4])
    (预处理步骤)

逆映射:lo = (v1 - f_v1(v0, delta)) & Mhi = v0token = pack('<II', lo, hi)

7.6 提取结果

常量

与标准 TEA 差异

加密/解密

def encrypt(v0, v1):sum = delta    v0 += ((v1<<6)+key[3]) ^ (v1+sum) ^ ((v1>>5)+0xaabbccdd)   # Round 1for r in range(229):sum += delta        v1 += ((v0<<4)+key[1]) ^ (v0+sum) ^ ((v0>>7)+key[2])   # v1 先        v0 += ((v1<<6)+key[3]) ^ (v1+sum) ^ ((v1>>5)+0xaabbccdd# v0 后return v0 & M, v1 & Mdef decrypt(v0, v1):  # 严格逆序sum = 28 * deltafor r in range(281, -1):        v0 -= ((v1<<6)+key[3]) ^ (v1+sum) ^ ((v1>>5)+0xaabbccdd)        v1 -= ((v0<<4)+key[1]) ^ (v0+sum) ^ ((v0>>7)+key[2])sum -= delta    v0 -= ((v1<<6)+key[3]) ^ (v1+sum) ^ ((v1>>5)+0xaabbccdd)   # Round 1return v0 & M, v1 & M

7.7 验证(7 + 1 组测试向量)

7/7 正向 + 7/7 逆向 + 1 组 ASCII 真实场景,全部匹配 Unicorn ground truth。

真机验证(MuMu 模拟器,Tokena2d576a6,场景 Patch 后碰撞 Trigger4 显示 flag):

C++ 核心实现(part3_tea.cpp):

命名约定:C++ 中KEY[0]=delta=0x29e59c9fMAGIC=0xaabbccdd;VM 反编译器中key[0]=0xaabbccdd(7.6 表采用 VM 约定)。两套索引不同,常量值一致。

staticconstuint32_t KEY[4] = {0x29e59c9f0xf95d664a0x12aa364c0x33ad3cee};staticconstuint32_t MAGIC = 0xaabbccdd;  // VM 反编译器中的 key[0]staticconstuint32_t DELTA = KEY[0];  // 0x29e59c9fstaticconstint ROUNDS = 28;// 轮函数 — v0 用 <<6/>>5,v1 用 <<4/>>7(标准 TEA 均为 <<4/>>5)staticuint32_tf_v0(uint32_t v1, uint32_t s) {return ((v1 << 6) + KEY[3]) ^ (v1 + s) ^ ((v1 >> 5) + MAGIC);}staticuint32_tf_v1(uint32_t v0, uint32_t s) {return ((v0 << 4) + KEY[1]) ^ (v0 + s) ^ ((v0 >> 7) + KEY[2]);}// 非对称 Feistel — 第 1 轮仅更新 v0,第 2~28 轮先 v1 后 v0staticvoidencrypt(uint32_t& v0, uint32_t& v1) {uint32_t s = DELTA;    v0 += f_v0(v1, s);              // 第 1 轮:仅 v0for (int i = 1; i < ROUNDS; i++) {        s += DELTA;        v1 += f_v1(v0, s);          // 第 2~28 轮:先 v1        v0 += f_v0(v1, s);          // 再 v0    }}staticvoiddecrypt(uint32_t& v0, uint32_t& v1) {uint32_t s = (uint32_t)((uint64_t)ROUNDS * DELTA);for (int i = ROUNDS - 1; i >= 1; i--) {        v0 -= f_v0(v1, s);        v1 -= f_v1(v0, s);        s -= DELTA;    }    v0 -= f_v0(v1, s);              // 逆向第 1 轮}// 输入映射:token[0:4]→lo, token[4:8]→hi=v0, v1=f_v1(v0,δ)+lostaticvoidtoken_to_state(constuint8_t token[8], uint32_t& v0, uint32_t& v1) {    v0 = le32(token + 4);    v1 = f_v1(v0, DELTA) + le32(token);}

产物:part3_tea.cpp(C++ 加密+解密),part3_tea.py(Python)

运行:flag_tool.exe encrypt <token>/flag_tool.exe decrypt 3 <hex>

08
VM 逆向与静态反编译器(附加分析)

算法已在第七章通过 Unicorn 差分 trace 完全还原后,回过头来对 VM 本身进行完整逆向,构建了不依赖模拟器的纯静态反编译器,从原始字节码直接产出可读伪代码。

8.1 VM 执行架构

启动链

sub_A9A7C(token)  ├─ sub_12DE80()                        → 创建 runtime 对象  ├─ loc_119DA8(rt, 0x63DA0, 5414)       → 加载字节码到 runtime  ├─ sub_13BFD4(..., &vm_machine)        → 创建 vm_machine (0x110 字节)  │     └─ 安装 7 个 family descriptor → vm+0x100  │     └─ 安装 sub_13C430 (selector)  → vm+0xA0  ├─ sub_1371F8(0, &host_table)          → 创建 host_table (malloc 0x2030)  ├─ sub_13D278(vm, ht)                  → 绑定: vm+0x98 = host_table  ├─ sub_1374A0(ht, 101, VMEntry)        → 注册 BRIDGE cmd 101  ├─ sub_1374A0(ht, 102, sub_AA758)      → 注册 BRIDGE cmd 102  ├─ sub_13CD90(vm_machine)              → 驱动循环  │     └─ while (sub_13C67C(vm) == 0x604) {}  ├─ sub_13D374, sub_13C5C8, loc_137360  → 清理  └─ return &unk_1836E0                  → flag 字符串

sub_A9A7Ccase 33(VM 调用核心)— 启动链中每个函数调用均可在 IDA 反编译中一一对应:

sub_13CD90是驱动循环,每次调用sub_13C67C执行一条 VM 指令。返回0x604(CONTINUE)继续,0(SUCCESS)结束。额外保护:vm[6] >= vm[1]时强制退出(PC 越界)。

双对象模型

VM 运行时由两个独立对象组成:

sub_13D278绑定两者:*(vm_machine + 0x98) = host_table,使 VM 执行时能通过 BRIDGE 指令调用宿主函数。

数据结构布局

vm_machine (0x110 字节)

+0x00   runtime_ptr       指向 runtime 对象+0x08   bytecode_limit    字节码长度(PC 越界检查用)+0x30   PC counter        当前字节码 PC(= vm[6],每条指令执行后推进)+0x60   insn_count        指令计数器(= vm[0xC])+0x98   host_table_ptr    绑定的 host_table+0xA0   selector_func     sub_13C430(family 选择器函数指针)+0x100  descriptor_array  指向 7 个 FamilyDescriptor 的指针数组

host_table (0x2030 字节)

+0x0000..+0x1FFF  256 个 handler 槽位,每个 0x20 字节:    +0x00 name_ptr      handler 名字符串    +0x08 handler_func  回调函数指针    +0x10 opaque        用户数据    +0x19 registered    1=已注册+0x2000  handler_count  已注册数量+0x2008  allocator      malloc 函数指针+0x2010  free_func      free 函数指针

FamilyDescriptor (40 字节),7 个连续存放在.data:0x163948

+0x00  slot0    共享 RET stub (0x13D4F8,所有 family 相同)+0x08  slot1    各 family 独立辅助 stub+0x10  handler  ★ family handler 函数指针(BLR X8 调用目标)+0x18  (zero)+0x20  (zero)

寄存器文件(通过sub_118340从 runtime 取出):

寄存器存储在 regfile + 0x28 + reg_idx * 8,最多 32 个内存上下文在 regfile + 0x128

BRIDGE handler 伪代码

sub_AA758(VM cmd 102: PART3 结果格式化):

// sub_AA758 (0xAA758 ~ 0xAA9B0) — VM cmd 102voidsub_AA758(a1, a2, a3, a4, a5) {int64_t v0, v1;int64_t ctx = *(a3 + 8);loc_1385BC(a1, a2, ctx + 16, &v1, 8);  // 读第一个 u32loc_1385BC(a1, a2, ctx + 24, &v0, 8);  // 读第二个 u32sub_A9664("%08x%08x", ...);             // 解密格式字符串sub_AA6AC(&unk_1836E0, 32"%08x%08x", v1, v0);  // IDA 命名与 C++ 实现相反: 此处 v1 = C++ 的 v0}

VMEntry(VM cmd 101: 提供 token 字节):

// VMEntry (0xA6BEC ~ 0xA6F10) — VM 读取 token 字节int64_tVMEntry(a1, a2, a3, a4, a5, a6) {uint32_t idx = *v8;if (idx >= a6)        *v7 = 0;                     // 超出范围返回 0else        *v7 = byte_1836C0[idx];      // token 第 idx 字节sub_13879C(a1, a2, ctx + 8*idx + 16, &v30, 8);  // 写入 VM 上下文    *v8 = idx + 1;}

本质是标准线性 PC 解释器,但整体被 56-case CFF(控制流平坦化)混淆包裹,静态看起来极其复杂。IDA 完全无法反编译(SP 分析失败,仅输出 3 行),成功还原全部 56 个 CFF 状态。去掉 CFF 外壳后,每条指令的执行流程为:

sub_13C67C(vm_machine) {    1. 取指:从 bytecode[PC] 读 4 字节 header → opcode, flags, cls    2. 解码:按 cls 个数读操作数(fmt=0x04 → 4字节立即数,其他 → 1字节寄存器索引)    3. 分派:sub_13C430 按 opcode 高字节选 family → descriptor[family].handler    4. 执行:BLR X8 @ 0x139684 → 调用 family handler    5. 推进:PC += instruction_size, insn_count++    6. 返回:0x604 (CONTINUE) 或 0 (HALT)}

反编译关键 CFF 状态(地址→语义):

handler 分派点在 ARM64 层为BLR X8 @ 0x139684(动态反编译器 hook 的位置),调用前 X2 指向 112 字节指令描述符,SP+0xF0 为当前 bytecode PC。

寄存器/内存访问函数

Family handler 内部通过以下函数操作 VM 状态:

例如 Family1(算术,sub_139060)执行ADD的核心路径:

  • 读 opcode:*arg3 = 0x0100→ switch 到 ADD 分支
  • 读源操作数:reg_read(regfile, op1_slot, &val1),reg_read(regfile, op2_slot, &val2)
  • 计算:result = val1 + val2
  • 写目标:reg_write(regfile, dst_slot, result)

Family3(传送,sub_139D70)的 PUSH/POP 操作 VM 栈指针(reg[0x10]),每次移动 8 字节。LOAD/STORE 通过mem_read/mem_write访问 VM 内存。

Family selector — sub_13C430

sub_13C430*(bytecode_ptr + 1)(opcode 高字节),映射到 family:

0 → Family0 (0x138BBC)     5,6,7,8 → Family5 (0x13AC3C)1 → Family1 (0x139060)     9       → Family6 (0x13BBD4)2 → Family2 (0x139A44)3 → Family3 (0x139D70)     调用方式: (*descriptor->handler)(desc, host, bytecode, ctx)4 → Family4 (0x13A440)

Family handler 表

7 个 Family handler 按 opcode 高字节分派:

opcode 高字节 5/6/7/8 全部路由到 Family5(sub_13C430 中确认),Family5 内部再按完整 opcode 二次分派。

BRIDGE(func, nargs)是 VM↔Native 桥接指令:func=0x65 调用 VMEntry 读 token,func=0x66 调用 sub_AA758 输出结果。

8.2 SBC0 字节码格式还原

指令编码

通过 Unicorn hooksub_13C67CBLR X8(handler 分派点),截获每条指令执行前的 112 字节结构体,逆推编码格式:

Header (4 bytes):  [opcode_lo] [opcode_hi] [flags] [cls]   └────────────────┘      └───┘   └─┘16-bit opcode          标志   操作数个数Operand × cls:  [fmt] (1 byte)  fmt == 0x04 → [imm32_le] (4 bytes, 小端立即数)  fmt == 0x01 → [reg]      (1 byte, 寄存器索引)

示例ADD acc, acc, tmp.sbc:100AB):

Raw: 00 01 00 03 01 001 001 0F      ^^^^       ^^^^^ ^^^^^ ^^^^^      opcode     op0   op1   op20x0100     acc   acc   tmp      =ADD       r14   r14   r15Header: opcode=0x0100(ADD), flags=0x00, cls=0x03(3 operands)Op0: fmt=0x01(reg), idx=0x0E(acc)  → 目标Op1: fmt=0x01(reg), idx=0x0E(acc)  → 源1Op2: fmt=0x01(reg), idx=0x0F(tmp)  → 源2长度: 4 + 3×2 = 10 字节

验证方法:将推测的编码格式直接解码.rodata:0x63DA0处的 5414 字节原始数据,逐条与 Unicorn 动态 trace 的指令序列比对——684 条指令全部吻合,证明编码格式正确。

指令集总表(60+ 条)

8.3 从反汇编到反编译

Step 1 — 线性反汇编

直接按偏移解码字节流,输出 IDA 风格汇编:

.sbc:10892  06 03 00 02 01 0E 04 9F 9C E5 29    MOVALT  acc, #0x29E59C9F  ; delta.sbc:1089D  03 03 00 01 01 00                    PUSH    r0.sbc:108A3  00 03 00 02 01 00 01 0B              MOV     r0, r11.sbc:108AB  03 03 00 01 01 01                    PUSH    r1....sbc:108E7  04 03 00 01 01 01                    POP     r1.sbc:108ED  06 03 00 02 01 0F 04 09 00 01 00     MOVALT  lr, #0x10009.sbc:108F8  00 04 00 01 04 09 00 01 00           JMP     u32_truncate

问题:684 条原始指令中大量 PUSH/POP/MOV 是寄存器保存/恢复,直接看完全不可读。

Step 2 — 递归下降 + 函数识别

改为递归下降解码:从入口 0x10000 跟随控制流,遇到 JMP/条件跳转加入工作队列,跳过不可达代码。

函数识别模式MOVALT lr, #ret_addr; JMP target= 函数调用(lr 保存返回地址)。以JMP [lr]HALT为函数结束。识别出 5 个函数,其中u32_truncate被 18 处调用。

Step 3 — 模式匹配提升为伪代码

反编译器的核心是多模式识别,将固定的指令序列折叠为高级语句。

模式 A — u32 表达式块:7 个 PUSH(保存寄存器上下文)→ 算术/位运算序列 →CALL u32_truncate→ 7 个 POP。整个序列折叠为一行tN = u32(expr),其中 expr 通过栈式符号执行构建:

原始(~30 条指令):  PUSH r0; PUSH r1; ... PUSH r7  MOV r0, r11          // 操作数 1  MOVALT acc, #delta   // 操作数 2  ADD r0, acc          // r0 = r11 + delta  ...  MOVALT lr, #ret; JMP u32_truncate  POP r7; ... POP r0  MOV r13, acc         // 结果存入 r13反编译结果(1 行):  r13 = u32(r11 + delta)

模式 B — 向量操作PUSH r0; MOVALT acc, #imm; POP r0; VECPUSH r0, accvec.push(imm)。连续多个合并为vec.push(0) x 10

模式 C — 常数赋值MOVALT r0, #val; MOV rX, r0rX = val(消除 r0 中转)。

模式 D — 内存操作MOVALT tmp, #addr; STORE/LOAD r0, [tmp]mem[addr] = r0/r0 = mem[addr],已知地址替换为符号名(v0, v1, sum 等)。

模式 E — 自赋值消除:MOV 链传播后产生r0 = r0的无效赋值,直接删除。

Step 4 — 常量识别与标注

预定义 TEA 相关常量表,出现时自动标注:

KNOWN_CONSTANTS = {    0x29e59c9f: "delta",    0xf95d664a: "key[1]",    0x12aa364c: "key[2]",   0x33ad3cee: "key[3]",    0xaabbccdd: "key[0]",}

Step 5 — 交错输出

最终产出三种视图,其中交错视图将每行伪代码与其对应的原始汇编以注释形式交织,便于逐行验证:

; .sbc:10892  06 03 00 02 01 0E 04 9F 9C E5 29  MOVALT  acc, #0x29E59C9F  ; delta; .sbc:1089D  03 03 00 01 01 00                  PUSH    r0; .sbc:108A3  00 03 00 02 01 00 01 0B            MOV     r0, r11;  ... (共 ~30 条); .sbc:108F8  00 04 00 01 04 09 00 01 00         JMP     u32_truncater13 = u32(r11 + delta)

8.4 反编译结果

684 条指令 →~40 行伪代码,TEA 循环体完整可读(注意:VM 内部 v0/v1 命名与算法相反,输出时push(v1,v0)交换回来):

func main():    vec.push(0) x 10            // 分配 10 个 slotbridge(func=0x65, nargs=1)  // 读 token 字节    ...    counter = 0x1  loop:    if (counter >= 0x1C) goto done    // 28 轮    // Phase 1: 算法的 v1 更新(VM 变量名 v0,<<4, >>7, key[1], key[2])    r13 = u32(r11 + delta)            // sum += delta    t0 = u32(r12 << 0x4)    t1 = u32(t0 + key[1])    t2 = u32(r12 + r13)    t3 = u32(t1 ^ t2)    t4 = u32(r12 >> 0x7)    t5 = u32(t4 + key[2])    t6 = u32(t3 ^ t5)    v0 = u32(sum + t6)               // VM 的 v0 = 算法的 v1_new    // Phase 2: 算法的 v0 更新(VM 变量名 v1,<<6, >>5, key[3], key[0]=0xAABBCCDD)    t8  = u32(v0 << 0x6)    t9  = u32(t8 + key[3])    t10 = u32(v0 + r13)    t11 = u32(t9 ^ t10)    t12 = u32(v0 >> 0x5)    t13 = u32(t12 + key[0])          // + 0xAABBCCDD    t14 = u32(t11 ^ t13)    v1  = u32(r12 + t14)             // VM 的 v1 = 算法的 v0_new    counter++; goto loop  done:    vec.push(v1); vec.push(v0)bridge(func=0x66, nargs=1)  // 输出 %08x%08x    halt

反编译结果与第七章 Unicorn trace 还原的算法完全吻合:移位量、密钥、delta、轮数、轮结构全部一致,形成交叉验证。

产物:vm_static_disasm.py(静态反汇编+反编译),vm_decompile.py(Unicorn 动态反编译)

输出:vm_static_output.txt(交错视图),vm_disasm.txt(纯汇编),vm_decompiled.txt(纯伪代码)

8.5 关键地址表

函数

VM 基础设施

VM 寄存器/内存访问

Family handler

静态数据

运行时缓冲区

09
C++ 实现与交付物

9.1 flag_tool(C++17)

三个 PART 的加密和解密算法均用 C++ 实现,支持 Token→Flag 和 Flag→Token 双向转换。

> flag_tool.exe encrypt a2d576a6Token: a2d576a6PART1: flag{sec2026_PART1_2d55e927}PART2: flag{sec2026_PART2_be088bdac626fff5c3eb0e12265ab9d4}PART3: flag{sec2026_PART3_21441a664225fa06}> flag_tool.exe decrypt 2 be088bdac626fff5c3eb0e12265ab9d4Token: a2d576a6Verify: encrypt(a2d576a6) = be088bdac626fff5c3eb0e12265ab9d4 OK> flag_tool.exe verify=== 16 passed, 0 failed ===

编译:cl /EHsc /O2 /std:c++17 /Fe:flag_tool.exe main.cpp part1_feistel.cpp part2_aes.cpp part3_tea.cpp

9.2 源码说明

9.3 全部交付物索引

看雪ID:XCicada

https://bbs.kanxue.com/user-home-927003.htm

*本文为看雪论坛精华文章,由 XCicada原创,转载请注明来自看雪社区
第十届安全开发者峰会【议题征集】-欢迎投稿

# 往期推荐

安卓逆向基础知识之frida Hook

2025 强网杯和强网拟态部分题解

在逆向分析方面-unidbg真的适合 MCP 吗?

AI静态分析,内核模块隐藏 Frida 特征,绕过linker私有结构遍历崩溃链

某安全so库深度解析

球分享

球点赞

球在看

点击阅读原文查看更多

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-06-29 23:05:27 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/647403.html
  2. 运行时间 : 0.104039s [ 吞吐率:9.61req/s ] 内存消耗:4,942.02kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=075edac33ab5a1e17789a23799c8d986
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000703s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000709s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.001550s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.002095s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000506s ]
  6. SELECT * FROM `set` [ RunTime:0.000196s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000558s ]
  8. SELECT * FROM `article` WHERE `id` = 647403 LIMIT 1 [ RunTime:0.000643s ]
  9. UPDATE `article` SET `lasttime` = 1782745527 WHERE `id` = 647403 [ RunTime:0.008692s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000245s ]
  11. SELECT * FROM `article` WHERE `id` < 647403 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000440s ]
  12. SELECT * FROM `article` WHERE `id` > 647403 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001159s ]
  13. SELECT * FROM `article` WHERE `id` < 647403 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.003707s ]
  14. SELECT * FROM `article` WHERE `id` < 647403 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.003080s ]
  15. SELECT * FROM `article` WHERE `id` < 647403 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.000827s ]
0.105709s