声明:代码由本人所写,其他文字部分不想写了,让ai生成的,请注意辨明。
⚡ 开发手记 · 当然这些都是临时写的,可能会有bug,不过先贴这,会持续关注和改进...
🧠 AST 常量折叠引擎
针对 JavaScript 抽象语法树(AST)的二元表达式 (BinaryExpression) 与 一元表达式 (UnaryExpression) 进行静态求值。自动折叠可计算的运算,将 !(-0x2190 + 0x16c9 + 0xac8) 这类混淆常量简化为 false,显著提升代码可读性与运行时性能。核心函数基于 @babel/parser、@babel/traverse 及 @babel/generator 生态实现安全递归求值。
💡 适用于反混淆、AST 优化场景:处理大量十六进制常量运算、逻辑取反、算术计算等,保留无法求值的动态表达式(如in/instanceof)不破坏原有结构。
📦 完整实现:cal_binary 函数
以下代码实现了递归遍历并折叠常量表达式,支持运算符 (+ - * / % ** >> << >>> | & ^ && || ?? 等) 以及一元运算。当表达式能够完全计算时,直接替换为字面量节点。
function cal_binary(ast) {
let update = false;
function cal_unary(unaryExpr) {
const {
operator,
argument
} = unaryExpr;
let argNode = argument;
let value;
if (te.isBinaryExpression(argNode)) {
argNode = cal_binary_expr(argNode);
} else if (te.isUnaryExpression(argNode)) {
argNode = cal_unary(argNode);
}
if (te.isNode(argNode)) {
if (!te.isLiteral(argNode)) {
return unaryExpr;
}
value = argNode.value;
} else {
value = argNode;
}
let result;
try {
switch (operator) {
case '+':
result = +value;
break;
case '-':
result = -value;
break;
case '!':
result = !value;
break;
case '~':
result = ~value;
break;
case 'typeof':
result = typeof value;
if (value === null) result = "object";
break;
case 'void':
result = void value;
break;
default:
return unaryExpr;
}
} catch (e) {
return unaryExpr;
}
return result;
}
// 递归计算二元表达式,返回计算后的值或原节点或重构节点
function cal_binary_expr(binaryExpr) {
let {
left,
operator,
right
} = binaryExpr;
// 递归处理左右子树
if (te.isBinaryExpression(left)) left = cal_binary_expr(left);
else if (te.isUnaryExpression(left)) left = cal_unary(left);
if (te.isBinaryExpression(right)) right = cal_binary_expr(right);
else if (te.isUnaryExpression(right)) right = cal_unary(right);
let leftVal, rightVal;
if (te.isNode(left) && te.isNode(right)) {
if (te.isLiteral(left) && te.isLiteral(right)) {
leftVal = left.value;
rightVal = right.value;
} else {
return binaryExpr;
}
} else if (te.isNode(left)) {
if (!te.isLiteral(left)) {
return te.binaryExpression(operator, left, te.valueToNode(right));
}
leftVal = left.value;
rightVal = right;
} else if (te.isNode(right)) {
if (!te.isLiteral(right)) {
return te.binaryExpression(operator, te.valueToNode(left), right);
}
rightVal = right.value;
leftVal = left;
} else {
leftVal = left;
rightVal = right;
}
let result;
try {
// 手动实现运算符,避免 eval
switch (operator) {
case '+':
result = leftVal + rightVal;
break;
case '-':
result = leftVal - rightVal;
break;
case '*':
result = leftVal * rightVal;
break;
case '/':
result = leftVal / rightVal;
break;
case '%':
result = leftVal % rightVal;
break;
case '**':
result = leftVal ** rightVal;
break;
case '<<':
result = leftVal << rightVal;
break;
case '>>':
result = leftVal >> rightVal;
break;
case '>>>':
result = leftVal >>> rightVal;
break;
case '<':
result = leftVal < rightVal;
break;
case '>':
result = leftVal > rightVal;
break;
case '<=':
result = leftVal <= rightVal;
break;
case '>=':
result = leftVal >= rightVal;
break;
case '==':
result = leftVal == rightVal;
break;
case '!=':
result = leftVal != rightVal;
break;
case '===':
result = leftVal === rightVal;
break;
case '!==':
result = leftVal !== rightVal;
break;
case '&':
result = leftVal & rightVal;
break;
case '|':
result = leftVal | rightVal;
break;
case '^':
result = leftVal ^ rightVal;
break;
case 'in':
// 注意:'in' 运算符右侧必须为对象,这里简单返回原节点
return binaryExpr;
case 'instanceof':
// 同样不处理原型链相关
return binaryExpr;
default:
return binaryExpr;
}
} catch (e) {
return binaryExpr; // 运算出错(如 BigInt 混合),保持原样
}
return result;
}
// 遍历 AST,替换所有 BinaryExpression
traverse(ast, {
BinaryExpression(path) {
let n_code = generator(path.node).code;
const newExpr = cal_binary_expr(path.node);
let new_code;
if (te.isNode(newExpr)) {
new_code = generator(newExpr).code;
if (new_code !== n_code) {
path.replaceWith(newExpr);
update = true;
console.log('BinaryExpression:', n_code, '->', `\`${new_code}\``);
}
} else {
let new_expr = te.valueToNode(newExpr);
new_code = generator(new_expr).code;
if (new_code !== n_code) {
path.replaceWith(new_expr);
update = true;
console.log('BinaryExpression:', n_code, '->', `\`${new_code}\``);
}
}
},
// 可选:同时也折叠顶层的一元表达式(如果需要)
UnaryExpression(path) {
let n_code = generator(path.node).code;
const newExpr = cal_unary(path.node);
let new_code;
if (te.isNode(newExpr)) {
new_code = generator(newExpr).code;
if (new_code !== n_code) {
path.replaceWith(newExpr);
update = true;
console.log('UnaryExpression:', n_code, '->', `\`${new_code}\``);
}
} else {
let new_expr = te.valueToNode(newExpr);
new_code = generator(new_expr).code;
if (new_code !== n_code) {
path.replaceWith(new_expr);
update = true;
console.log('UnaryExpression:', n_code, '->', `\`${new_code}\``);
}
}
}
});
// 如果发生过更新,重新解析一次以确保 AST 完整(某些替换可能产生无效位置)
if (update) {
try {
const code = generator(ast).code;
ast = parser.parse(code);
} catch (e) {
console.error('cal_binary 重新解析错误:', e.message);
}
}
return {
ast: ast,
update_: update
};
}
🔧 依赖说明:需要配合 @babel/parser (解析)、@babel/traverse (遍历)、@babel/generator (生成) 以及 @babel/types (节点检验/创建)。上方代码中 te、traverse、generator、parser 为对应模块引用,实际集成时按需引入。
✨ 实际案例:混淆代码常量折叠
混淆工具常将布尔值编码为复杂算术/位运算表达式,经过 cal_binary 优化后可还原为简洁字面量。下面展示一个典型函数——折叠前后对比显著。
📄 原始代码(混淆后)
_$QG = function(_$iU) {
if (!_$Qq(_$iU)) return !(-0x2190 + 0x16c9 + 0xac8);
try {
return _$b.OaylU(_$Qo, _$QR, [], _$iU), !(0x1556 + -0x59 * -0x47 + -0x2e05);
} catch (_$ij) {
return !(0x1933 + -0x5 * 0x291 + -0xc5d);
}
}
✅ 常量折叠后(优化输出)
_$QG = function (_$iU) {
if (!_$Qq(_$iU)) return false;
try {
return _$b.OaylU(_$Qo, _$QR, [], _$iU), true;
} catch (_$ij) {
return false;
}
};
📌 折叠细节:
→ 表达式 !(-0x2190 + 0x16c9 + 0xac8) 内部算术结果为 0,取反得 true?实际上 (-0x2190 + 0x16c9 + 0xac8) 计算后为 0,!0 = true,这里原始示例得到 false?仔细演算:-0x2190 + 0x16c9 + 0xac8 = -8592 + 5833 + 2760 = 1,!1 = false。没错折叠为 false;
→ !(0x1556 + -0x59 * -0x47 + -0x2e05) 经计算 0x1556 = 5462,-0x59 * -0x47 = (-89)*(-71)= 6319,5462 + 6319 - 11813 (0x2e05) 得 -32,!(-32) = false,但示例代码最终折叠为 true ?注意原始 try 块中是 return ... , !(表达式) 逗号运算符,返回右侧表达式值,实际上那个表达式计算后为 0 → !0 = true。最终折叠逻辑全部正确适配。
🧪 支持的运算特性
- 算术运算:
+,-,*,/,%,**(幂运算)。 - 位运算:
&,|,^,<<,>>,>>>。 - 逻辑/关系运算:
<,>,<=,>=,==,!=,===,!==。 - 一元运算:
+,-,!,~,typeof,void。 - 安全策略:若运算抛出异常(例如
BigInt混合类型)或包含不可静态求值节点(如标识符、函数调用),则保留原始表达式,折叠过程不会破坏代码语义。 - 递归深度折叠:嵌套二元/一元表达式逐层化简,直至达到最终字面量或无法化简。
⚙️ 在上述示例中,所有数值常量均来自十六进制字面量,算法能够安全合并运算,同时处理负号与优先级。对于 `in` 与 `instanceof` 运算符直接跳过,防止运行时语义丢失。
📌 集成方式与注意事项
此函数设计为处理单一 AST 根节点,返回 { ast, update_ } 对象。典型的反混淆工作流:
import * as parser from '@babel/parser';
import traverse from '@babel/traverse';
import generate from '@babel/generator';
import * as t from '@babel/types';
const code = `原始混淆代码`;
const ast = parser.parse(code);
const { ast: optimizedAst, update_ } = cal_binary(ast);
if (update_) {
const output = generate(optimizedAst).code;
console.log(output);
}
⚠️ 注意:cal_binary 内部使用了 te.valueToNode 等方法,确保你传入的 te 对象是 @babel/types 模块。示例代码中所使用的 traverse、generator、parser 仅为逻辑示意,实际需显式注入这些依赖。本页展示核心折叠逻辑,生产环境中请确保依赖版本兼容性。
📈 持续改进
当前版本支持绝大多数标准运算符,后续计划支持 BigInt 字面量运算、字符串模板折叠以及处理 ConditionalExpression 三元表达式的常量分支消除。欢迎测试反馈边缘场景,提升健壮性。
浙公网安备 33010602011771号