安全与风控
遇到自定义虚拟机或字节码保护程序时,如何识别调度器…
适用于 CTF 或受保护软件中实现自定义 VM、专有字节码、调度循环或迷宫式校验的逆向分析。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
遇到自定义虚拟机或字节码保护程序时,如何通用
解决思路
- 先运行二进制,观察输入输出行为,确认期望的输入格式以及成功/失败时的输出。
- 在 IDA/Ghidra 中定位主循环,寻找 while/for 循环中带 switch 或间接跳转的调度器。
- 识别 VM 组件:字节码位置、PC/IP 变量、寄存器/栈状态存储位置、I/O 处理 opcode。
- 根据二进制模式判断 VM 类型:`while(1) { switch(bytecode[pc]) }` 为 switch 调度器,`jmp [table + opcode*8]` 为表调度器,嵌套 if-else 为 if-chain 调度器,栈 push/pop 为主为栈式 VM,`reg[X] = ...` 数组操作为寄存器式 VM,2D 网格加方向输入为迷宫题。
- 对每个 case/handler 映射 opcode:记录 opcode 值、操作类型、操作数个数、操作数类型(立即数/寄存器索引/内存地址)、副作用(输出/内存写/标志位修改)。
- 从二进制中提取字节码程序:用 Python 按偏移和长度读取,或在 IDA 中用 `idc.get_bytes(bytecode_addr, bytecode_len)`。
- 编写自定义反汇编器:定义 OPCODES 字典(助记符、操作数字节数),逐字节解析并打印地址、助记符和操作数。
- 分析反汇编结果:定位 read opcode 读取输入的位置,追踪输入到比较的数据流,确定成功/失败条件,提取校验逻辑(常见为 XOR/ADD 变换后与常量比较)。
- 针对栈式 VM,按栈效果映射 PUSH/POP/ADD/SUB/MUL/XOR/CMP/JMP/JZ/PRINT/READ/HALT 等操作。
- 针对寄存器式 VM,按格式映射 MOV/ADD/SUB/XOR/CMP/JMP/JE/LOAD/STORE/SYSCALL/HALT 等操作。
- 针对 Brainfuck 类 VM,按 BF 命令映射指针移动、字节增减、输入输出和循环跳转。
- 若为迷宫题,从数据段提取 2D 网格,识别方向编码(WASD/UDLR/方向键/数字/十六进制 opcode),用 BFS/DFS 求最短路径并转换为期望输入格式。
- 若为商业保护器(VMProtect/Themida),先找 VM entry(pushad/pushfd 序列),识别 VM 上下文结构,定位 handler 表,逐个去除 junk code/opaque predicates 并记录 handler 语义,做指令级 trace 后重建原始代码。
- 若为 Tigress 混淆,按单调度 VM、分裂 handler、嵌套 VM、加密字节码、多态 handler 等特征分别处理。
- 若调度器不清晰:全 mov 指令考虑 movfuscator,加密字节码先找解密逻辑并在解码后 dump,分裂/分布式 handler 通过执行 trace 定位。
- 若存在输入校验:小输入空间用 Unicorn 模拟暴力破解,已知格式用 angr 约束求解,复杂校验则写反汇编器分析校验逻辑。
- 若为多层 VM(VM in VM),先分析外层 VM,提取内层字节码,重复分析;也可考虑用符号执行直接处理嵌套 VM。
- 对反复出现的 VM 架构,可编写 Ghidra Sleigh 处理器模块,定义 ram/register 空间和 opcode token 以支持反汇编。
- 最终求解:简单变换(XOR、ADD)手动逆向,复杂约束交给 Z3,迷宫题提取网格后跑路径搜索。
适用边界 · 注意事项
- 目标不是自定义 VM/字节码解释器,而是普通原生代码或标准编译器产物时。
- 没有能力运行或静态分析二进制、也无法提取字节码数据时。
- 面对商业保护器但缺乏相应去保护工具或动态 trace 环境时,硬分析成本极高。