安全与风控
逆向被混淆二进制时如何识别并去除垃圾代码、控制流平坦化…
当你需要逆向分析被混淆保护的原生二进制(垃圾代码、不透明谓词、自修改代码、控制流平坦化、movfuscator、VMProtect/Themida 等 VM 保护、字符串加密、导入隐藏、反汇编干扰)时,按症状识别混淆类型并选用对应的静态/动态去混淆手法。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
逆向被混淆二进制时如何识别并去除垃圾代码通用
解决思路
- 先按 IDA/Ghidra 中的症状识别混淆类型:CFG 平坦且只有一个巨大 switch → 控制流平坦化,用符号执行恢复 CFG;只有 mov 指令 → movfuscator,用 demovfuscation 或基于 trace 的 lifting;pushad/pushfd 后进入 VM → VM 保护,提取 handler 表;代码执行前有 XOR 循环 → SMC/字符串加密,做动态分析并在解码后下断点;出现不可能成立的条件(不透明谓词)→ 垃圾代码插入,用模式匹配去除;所有字符串不可读 → 字符串加密,hook 解密例程或模拟执行;IAT 中没有导入 → 导入隐藏,追踪 GetProcAddress/哈希解析。
- 去除垃圾代码:用 IDA/Ghidra 数据流分析计算 def-use 链,把没有下游使用、返回值被丢弃且无副作用的指令标记为死代码并删除,最后通过 trace 对比验证删除不改变程序行为。
- 破解不透明谓词:用抽象解释证明条件为常量,用符号执行让 Z3 证明 ∀x: predicate(x) = True,用模式匹配识别已知的不透明谓词家族,或动态 trace 观察该分支从不/总是被走到;例如用 z3.BitVec('x',32) 求解 x*(x+1)%2 != 0,若返回 unsat 则该谓词恒为真。
- 处理自修改代码(SMC):先定位解密例程(在循环中向 .text 段写入的 XOR/ADD/SUB),在循环结束后下断点,在断点处 dump 已解密的内存区域,再把 dump 出的代码放回 IDA/Ghidra 重新分析;多层解密则对每一层重复该流程。
- 用 Unicorn 模拟自动脱壳:mem_map 映射内存、mem_write 写入二进制、emu_start 从解密入口跑到解密结束,再用 mem_read 读出解密后的代码。
- 恢复控制流平坦化(CFF):用 angr/Triton/miasm 做符号执行追踪所有状态转移(效果最好),或用 Pin/DynamoRIO 记录 trace 重建 CFG(只覆盖已执行路径),或写 IDA/Ghidra 脚本做模式匹配,或直接用 IDA 插件 D-810。
- 用 angr 做符号去平坦化:先 CFGFast 建 CFG,找出入度最高的基本块作为 dispatcher,再对每个 case 块用 blank_state 符号执行求解状态变量,确定真实后继块。
- 处理 movfuscator:确认函数只含 mov 指令、数据段有大量查找表、存在内存映射的标志寄存器后,用 demovfuscator 工具做静态恢复,或用 Pin/DynamoRIO 做 trace + 污点分析,或把整个函数当作约束系统做符号执行。
- 分析 VM 保护(VMProtect/Themida/Code Virtualizer):识别 VM 入口(pushad/pushfd → mov ebp,esp → sub esp,VM_LOCALS_SIZE → mov esi,bytecode_addr → jmp vm_dispatcher),找到 dispatcher(大 switch 或通过表的间接跳转),把每个 case 当作一个 VM handler,分析每个 handler 如何从字节码流(esi)读操作数、在 VM 寄存器/栈上运算、推进字节码指针并返回 dispatcher,从而建立 handler 地址到 VM 操作码的映射。
- 对 VM 做去虚拟化:可手工逆向每个 handler 建立 ISA 规范(IDA + 脚本),或用 REVEN/Pin 记录所有 handler 执行并重建程序,或用 Triton/miasm 符号化 lifting handler 到 IR,或用模式匹配把 handler 模式对应到已知 VM 家族;针对 VMProtect 还要注意 dispatcher 中的不透明谓词、每次构建都变异的 handler、多层嵌套 VM 以及集成的反调试与完整性校验。
- 解密字符串:对 XOR 循环直接 hook 或模拟 XOR 函数;对栈字符串用 IDA FLIRT 或 Ghidra 脚本重组;对 RC4 加密提取密钥后离线解密;对 AES 加密在运行时解密后 hook;对自定义编码(Base64+XOR+反转)trace 解码函数并复现。
- 用 Ghidra 脚本自动解密字符串:定位 decrypt_string 函数,用 getReferencesTo 取所有调用点,从每个调用点提取参数(加密缓冲区指针、密钥、长度),模拟解密并把明文写入注释。
- 恢复导入隐藏:先识别哈希算法(常见 CRC32、djb2、ROR13+ADD、FNV-1a),对所有已知 API 名计算哈希,建立哈希→API 名查找表,再在 IDA/Ghidra 中标注解析出的调用。
- 对抗反汇编干扰:对重叠指令从正确偏移手工重新分析;对错位跳转强制 IDA 在目标处重新分析;把条件跳转对(jz $+5; jnz $+3)改为无条件 jmp;把 push addr; ret 识别为跳转;分析异常处理链以还原基于异常的流程;对 call $+5; add [esp],N; ret 计算真实目标;在 IDA 中用右键 Undefine (U)、在正确偏移右键 Code (C)、Edit → Patch → Assemble 做永久修补。
- 按决策树选择路线:能运行就先做动态分析(在文件/网络/加密等关键 API 下断点、trace 执行、运行时 dump 解密后的代码和字符串),不能运行(嵌入式/固件/冷门架构)则只能静态分析;已知保护器(VMProtect/Themida)查对应脱保护指南,自定义混淆则组合 IDA 脚本 + Triton + 手工,CTF 题常用 angr 符号执行最快,恶意软件分析先上调试器 + API 监控做动态分析。
- 按需选用工具箱:IDA Pro + Hex-Rays 做通用反汇编/反编译/脚本,Ghidra 作为免费替代,D-810 自动去 OLLVM 风格平坦化,miasm 做基于 IR 的符号去混淆,Triton 做动态符号执行解不透明谓词和 CFF,REVEN 做全系统 trace 记录与回放分析 VM 保护,demovfuscator 还原 mov-only 二进制,x64dbg + 插件做 Windows 动态分析,Unicorn 做 CPU 模拟脱 SMC 壳,Capstone 做自定义工具,IDA FLIRT 在 stripped 二进制中识别库代码,Binary Ninja 用 MLIL/HLIL 做自动化分析。
适用边界 · 注意事项
- 二进制同时带有反调试层时,应先参考 anti-debugging-techniques 处理反调试,否则动态分析会被阻断。
- 使用 angr/Z3 做自动化去混淆时,应参考 symbolic-execution-tools;做深层 VM 保护字节码分析时应参考 vm-and-bytecode-reverse。
- 不要把加壳(packing)与混淆(obfuscation)混为一谈,也不要混淆静态去混淆与动态去混淆策略。