做安卓脚本变现,常碰到脚本被扒走、卡密被绕过、逻辑被改完再传出去。前一篇讲的是网络验证卡密。这篇讲编译期的代码混淆:代码被拖走之后,读起来要费很多劲。
混淆是什么,怎么装
混淆是在 js 编译期间对代码做花指令、流程更改。功能不变,阅读变难。控制流平坦化把顺序执行打乱成状态机跳转,执行路径不好还原。字符串数组把明文字符串打散到数组里,运行时再取回,直接搜关键词不好定位。死代码注入会插入永远不会执行、看起来像真代码的干扰项。RC4 编码对字符串数组做加密,静态分析时看到的是密文。
工具链要两样:Node.js(javascript-obfuscator 跑在 Node 上)和 javascript-obfuscator(npm 全局安装的混淆库)。在 cmd 或 powershell 中执行:
npm install -g javascript-obfuscator
npm install -g class-validator
装完后,用 npm root -g 查看全局安装路径,后面配置会用到。
obfuscator.json 逐项解析,开发关发布开
EC 的 IDEA 开发工具升级到 9.4.0 后,会在工程模块下自动创建一个 obfuscator.json 文件。默认不开启混淆,把 json 里的 binPath(也就是 obfuscatorBinPath)设对之后才会工作。官方默认配置(测试过可运行):
{
"nodeBinPath": "D:\\programe\\nodejs\\node.exe",
"obfuscatorBinPath": "C:\\Users\\Administrator\\AppData\\Roaming\\npm\\node_modules\\javascript-obfuscator\\bin\\javascript-obfuscator",
"target": "node",
"compact": false,
"log": true,
"optionsPreset": "high-obfuscation",
"deadCodeInjection": false,
"simplify": false,
"seed": 10,
"controlFlowFlattening": true,
"controlFlowFlatteningThreshold": 1,
"unicodeEscapeSequence": false,
"stringArray": true,
"stringArrayRotate": false,
"stringArrayShuffle": false,
"stringArrayThreshold": 1,
"stringArrayWrappersCount": 5,
"stringArrayEncoding": ["rc4"],
"stringArrayCallsTransform": false,
"selfDefending": false,
"splitStrings": false,
"splitStringsChunkLength": 1
}
注意:配置里的
//注释是方便解释用的,使用时记得删掉,不然会报错。
路径配置(必须):
| 属性 | 作用 |
|---|---|
nodeBinPath |
node.exe 的路径 |
obfuscatorBinPath |
javascript-obfuscator 可执行文件的路径 |
安装 Node.js 后,全局安装路径一般是 C:\Users\<用户名>\AppData\Roaming\npm\node_modules,在 node_modules 里找到 javascript-obfuscator 的全路径复制过来即可。混淆强度(重点):
| 属性 | 默认值 | 作用 |
|---|---|---|
optionsPreset |
high-obfuscation |
混淆程度预设 |
controlFlowFlattening |
true |
控制流平坦化,打乱执行顺序 |
controlFlowFlatteningThreshold |
1 |
平坦化比例,1 = 全部代码 |
deadCodeInjection |
false |
死代码注入,可开可关 |
simplify |
false |
代码简化(和混淆相反的方向,保持 false) |
字符串保护(重点):
| 属性 | 默认值 | 作用 |
|---|---|---|
stringArray |
true |
把字符串提取到数组中 |
stringArrayEncoding |
["rc4"] |
字符串数组编码方式,RC4 加密后静态分析只见密文 |
stringArrayThreshold |
1 |
字符串进数组的比例,1 = 全部 |
stringArrayWrappersCount |
5 |
包装函数数量,越多越难还原 |
stringArrayRotate / stringArrayShuffle |
false |
是否轮转/洗牌数组 |
其他控制项:
| 属性 | 默认值 | 作用 |
|---|---|---|
compact |
false |
是否压缩代码 |
log |
true |
编译期间展示混淆详细信息,便于确认混淆生效 |
seed |
10 |
随机种子,固定种子保证每次混淆结果可复现 |
unicodeEscapeSequence |
false |
是否转义为 unicode |
selfDefending |
false |
自防御:被格式化就失效(需谨慎开启,易误伤) |
splitStrings |
false |
拆分长字符串 |
混淆什么时候发生:obfuscator.json 里 obfuscatorBinPath 配了、而且文件存在,编译时就会混淆,js 模式和 dex 模式都包括。删掉 obfuscatorBinPath 的值,或者直接删掉 obfuscator.json,编译期间就不混淆。混淆比较吃电脑资源。平时调试关掉,发布、打包的时候再开。开发期开着,每次编译都慢,混淆后的代码也不好调试。
和卡密、指纹一起用
混淆单独用,挡不住下决心的人。官方的建议是几样一起上。网络验证卡密:卡密绑定设备、心跳验证,没有卡就跑不起来。关键代码再混淆,比如授权校验和核心算法,不必整包都混淆。包名、apk、脚本指纹验证每次打包要上传记录,用来防换壳和篡改。关键业务还可以放到远程变量和远程 js 里,发现被破解就换远程代码、停掉脚本。有人还会在远程变量里放破坏性代码,比如格式化 sdcard、删除联系人,触发之前要想清楚后果。
nodeBinPath 和 obfuscatorBinPath 两个路径配好就会生效,其余参数按上面的表调。默认的高强度方案是控制流平坦化、字符串数组、RC4 编码。开发期关、发布期开。混淆抬高的是阅读成本,不能保证永远破不开。
想要真实跑起来?
本文介绍的方案均可基于 EasyClick 能力在 iEasyClick 落地。官网提供完整文档、开发工具与自动化产品,免费体验。