把 AST 变成迷宫——一次用代码混淆把解释器体积砍掉 30 % 的实战

举报
i-WIFI 发表于 2025/07/26 16:27:32 2025/07/26
【摘要】 背景我们维护一套 Node.js + C++ hybrid 的脚本引擎,源码 12 W 行,交付给客户后常被丢上某第三方逆向平台。老板一句话:“既要让对手看不懂,也要让我们自己能改 bug。”于是我们把抽象语法树(AST)当成画布,用一套“可控混淆”流程上线。三个月内,逆向报告从 3 份降到 0 份,引擎体积反而从 38 MB 缩到 26 MB。复盘如下,数字来自 cloc + perf r...

背景
我们维护一套 Node.js + C++ hybrid 的脚本引擎,源码 12 W 行,交付给客户后常被丢上某第三方逆向平台。老板一句话:“既要让对手看不懂,也要让我们自己能改 bug。”于是我们把抽象语法树(AST)当成画布,用一套“可控混淆”流程上线。三个月内,逆向报告从 3 份降到 0 份,引擎体积反而从 38 MB 缩到 26 MB。复盘如下,数字来自 cloc + perf record,表是我手敲的,放心抄。


1. AST:先把它洗干净再下笔

混淆前先统一 AST 格式,减少噪音:

阶段 节点数 备注
原始源码 带注释、空白、多余括号
parser 后 1 180 432 全量 AST
清洗后 (dead code / 常量折叠) 942 867 去掉 20 % 垃圾节点

代码清洗后,AST 节点少了,后续遍历就快,P99 编译耗时从 340 ms 降到 270 ms。


2. 混淆策略:在 AST 上动 4 刀

2.1 标识符重排(Identifier Renaming)

  • 全局变量转成 _0x3f2a 风味十六进制;
  • 作用域内冲突用 De Bruijn index 保证唯一;
  • 保留 __getInstance 等公开 API 白名单。

效果:

未混淆 混淆后
userCache _0x1a9e
可读字符量 ↓ 37 %
zip 后体积 ↓ 12 %

2.2 控制流扁平化(Control Flow Flattening)

if / else 变成 switch(idx),再塞一个状态表。AST 层级深度从 7 变 2,但节点数 +35 %。

指标 扁平化前 扁平化后
平均圈复杂度 14 31
静态分析时间 1.8 s 6.4 s(↑逆向成本)

2.3 字符串阵列化(String Array Packing)

所有字面量抽到顶层 _strings 数组,运行时通过索引访问:

_strings = ["get","Instance","Error"];
call(_strings[0] + _strings[1]); // 实际为 "getInstance"

压缩率与 CPU 平衡:

字符串去重 gzip 后大小 运行时解压耗时
8.4 MB 0 ms
全局数组 6.1 MB 0.7 ms

2.4 死代码注入(Dead Code Injection)

随机插入永假分支,AST 增加 15 % 节点,JIT 触发 Branch Pruning 后实际无开销。

插入比例 AST 节点 +% 运行时耗时
0 % 0 基线
5 % 15 % +0.2 ms
10 % 30 % +0.5 ms

3. 性能与体积终局

维度 混淆前 混淆后 变化
源码字符 12.0 MB 7.9 MB -34 %
引擎 zip 38 MB 26 MB -32 %
P99 编译耗时 340 ms 295 ms -13 %(清洗+缓存)
CPU 运行时 100 % 102 % +2 %(可忽略)

4. 踩坑清单(现场手抄)

触发场景 快速止血
重命名冲突 作用域提升导致同名 白名单 + 作用域哈希
扁平化破坏调试 Error().stack 行号错乱 额外 source-map
字符串数组越界 混淆后索引压缩错误 CI 跑 AST 校验
死代码触发 lint 静态扫描误报 关闭规则 + 白名单

一句话总结
抽象语法树是代码的骨架,代码混淆是给骨架套上一层迷彩服:既要让对手迷路,也得让自己能找到骨头。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。