好的,正文开始~
变量重命名是 JavaScript 代码混淆中最简单且最常用的方法之一。这种方法的基本思想是将所有可能包含敏感信息的变量重命名为无意义的字符串。例如,将 username
替换为 a
,将 password
替换为 b
。将变量名替换为无意义的字符串或者短字符,使得人类阅读难度大大增加。例如:
// 没有代码混淆
var username = "user123";
var password = "pass456";
function login(user, pass) {
if (user === username && pass === password) {
console.log("Login successful!");
} else {
console.log("Login failed!");
}
}
login(username, password);
// 使用变量名混淆进行代码混淆
var a = "user123";
var b = "pass456";
function c(d, e) {
if (d === a && e === b) {
console.log("Login successful!");
} else {
console.log("Login failed!");
}
}
c(a, b);
但这种只是相当于简化了可读性,实际混淆效果很差,代码占用倒是会变小很多。
函数名混淆是另一种常用的 JavaScript 代码混淆技术。它的基本思想是将所有函数名替换为随机的、无意义的字符串,从而使代码更难被理解和调试。
以下是一个示例:
// 没有代码混淆
function add(x, y) {
return x + y;
}
console.log(add(2, 3));
// 使用函数名混淆进行代码混淆
var a = function(b, c) {
return b + c;
}
console.log(a(2, 3));
这种本质还是和上面介绍的变量重命名一样。
压缩是另一种常见的JavaScript代码混淆技术。通过使用各种压缩算法,可以将JavaScript代码文件缩小到原始大小的一半以下。同时也使用了上面介绍的变量名或者函数名混淆(下方示例的压缩后的代码并未对函数名进行压缩)例如:
function calculateSum(num1, num2) {
var sum = num1 + num2;
return sum;
}
var result = calculateSum(3, 4);
console.log(result);
可以通过压缩代码将其变为以下形式:
function calculateSum(a,b){return a+b}console.log(calculateSum(3,4));
虽然这样的代码难以阅读,但对于需要快速加载和运行的Web应用程序来说非常有用。
//文本编码为base64
const text = "Hello, world!";
const base64 = btoa(text);
console.log(base64); // 输出 "SGVsbG8sIHdvcmxkIQ=="
//base64解码为文本
const base64 = "SGVsbG8sIHdvcmxkIQ==";
const text = atob(base64);
console.log(text); // 输出 "Hello, world!"
将字符串进行编码,不易被人读取。例如,将"Hello, world!"转换为"SGVsbG8sIHdvcmxkIQ=="。解密时使用base64解码即可。
除了base64还可以使用十六进制码的形式表示了字符串中各个字符的 ASCII 码值
具体来说,\x 是一个转义序列,后面跟随两个十六进制数字,表示一个 ASCII 码值。因此,\x48 表示的是字符 "H" 的 ASCII 码值,\x65 表示的是字符 "e" 的 ASCII 码值,依次类推。
例如:
可以将代码
var message = "Hello World!";
alert(message);
使用十六进制码的形式表示了字符串中各个字符的 ASCII 码值
var _0x3d9d=["\x48\x65\x6C\x6C\x6F\x20\x57\x6F\x72\x6C\x64\x21"];alert(_0x3d9d[0]);
将这些 ASCII 码值拼接起来,可以得到完整的字符串 "Hello World!"。
JS 控制流混淆是一种消除JavaScript代码的可预测性的技术,通过使用控制流混淆算法(如JScrambler),改变程序结构来防止代码被轻易地理解和分析的技术。简单来说,就是通过修改代码中的控制流语句(如 if、while 等)的顺序、嵌套、去除等方式来增加代码的复杂性,从而使得代码难以被反编译、破解或者逆向工程攻击。以下是一个简短的示例:
const a = 1;
const b = 2;
let c = 0;
if (a === 1) {
if (b === 2) {
c = 3;
}
} else {
c = 4;
}
这段代码可以通过控制流混淆的方式进行优化。例如,可以将 if
块内部的语句交错、嵌套,增加运算符的数量,达到混淆的效果:
const a = 1;
const b = 2;
let c = 0;
if (!(a !== 1)) {
if (!(![])) c = 3;
} else {
if (!(!{})) c = 4;
}
可以看到,经过混淆之后的代码,不仅代码结构产生了较大的变化,同时还加入了无用的计算和类型转换等操作,使得代码更深奥难懂。
当需要保护JavaScript代码的时候,常见的方式是进行混淆。通过混淆,可以使代码难以被阅读和修改,从而保护代码的安全性。在本文中下半部分,我们将介绍如何使用AST语法树来进行JavaScript代码混淆。
AST(Abstract Syntax Tree)语法树是将代码转换为树形结构的一种方式。它能够表示出代码的结构和语义,可以被用于代码分析、优化和转换。AST语法树通常由编译器或解释器生成,用于在代码执行之前对其进行处理。
在JavaScript中,AST语法树可以使用工具库如Esprima或Babel来生成。这些库将JavaScript代码解析为AST语法树,并提供了简单易用的API来让开发者进行代码分析和操作。
AST语法树提供了一种强大的工具来进行JavaScript代码混淆。通过对AST语法树进行操作,我们可以改变代码的结构和语义,使其变得更加难以理解和修改。
esprima、babel都可以实现
下面是一些常见的AST语法树操作:
// 变量重命名示例
const esprima = require('esprima');
const estraverse = require('estraverse');
const code = `
function add(a, b) {
let c = a + b;
return c;
}
`;
const ast = esprima.parseScript(code);
const varMap = {
'a': 'x',
'b': 'y',
'c': 'z'
};
estraverse.replace(ast, {
enter(node) {
if (node.type === 'Identifier' && varMap[node.name]) {
node.name = varMap[node.name];
}
}
});
const newCode = escodegen.generate(ast);
console.log(newCode);
// 将加号替换成减号
const code = `
const sum = (a, b) => a + b;
const result = sum(1, 2);
`;
const ast = esprima.parseScript(code);
const newAst = replaceCode(ast, "+", "-");
console.log(generate(newAst));
//babel实现将代码中的所有值为false的节点并替换为![]
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;
// 原始代码
const code = `let flag = false;
if (flag) {
console.log("Flag is true");
} else {
console.log("Flag is false");
}`;
const ast = parser.parse(code);
traverse(ast, {
enter(path) {
if (path.isBooleanLiteral({ value: false })) {
path.replaceWith(parser.parse("![]").program.body[0].expression);
}
}
});
// 将AST语法树重新生成为代码字符串
const newCode = generator(ast).code;
// 输出新代码
console.log(newCode);
// 新代码为:
// let flag = ![];
// if (flag) {
// console.log("Flag is true");
// } else {
// console.log("Flag is ![]");
// }
篇幅有限,对于babel将js代码解析为ast语法树这一部分后续在进行着重介绍。
如果您对AST语法树的结构不是很了解的话,推荐您前往safekodo官网使用在线的js=>ast语法树工具,在网站中的编辑器输入JavaScript代码即可在右侧看到响应的AST语法树结构,当然相应的还有ast=>js工具,非常的直观
需要注意的是,JavaScript 代码混淆并不能完全保证代码的安全性,因为熟练的开发人员仍然可以通过不同的手段逆向工程出原始代码。因此,除了代码混淆之外,我们还应该采取其他措施来保护我们的 JavaScript 代码,例如加密算法和访问控制等。