JSFuck 是一种深奥的 JavaScript 编程风格。以这种风格写成的代码中仅使用 [、]、(、)、! 和 + 六种字符。此编程风格的名字衍生自仅使用较少符号写代码的Brainfuck语言。与其他深奥的编程语言不同,以JSFuck风格写出的代码不需要另外的编译器或解释器来执行,无论浏览器或JavaScript引擎中的原生 JavaScript 解释器皆可直接运行。鉴于 JavaScript 是弱类型语言,编写者可以用数量有限的字符重写 JavaScript 中的所有功能,且可以用这种方式执行任何类型的表达式。(Wikipedia)
示例
其中,var content = 后的部分即为 JSFuck 编码后的代码。
解码方式
在 JSFuck 编码后的代码运行前添加如下代码:
let fuck = Function;
Reflect.defineProperty(Function.prototype, 'constructor', {
value: function (...args) {
console.error('constructor', args);
return fuck(...args);
}
});
let evil = eval;
Reflect.defineProperty(globalThis, 'eval', {
value: function (...args) {
console.error('eval', args);
return evil(...args);
}
});
效果
在浏览器的 console 中将以 error 的形式输出可能包含 JSFuck 编码前的代码
原理
https://github.com/aemkei/jsfuck/blob/1f02651e98da1c13aef76d609755a2568a1aa6b4/jsfuck.js#L329
if (wrapWithEval){
if (runInParentScope){
output = "[][" + encode("flat") + "]" +
"[" + encode("constructor") + "]" +
"(" + encode("return eval") + ")()" +
"(" + output + ")";
} else {
output = "[][" + encode("flat") + "]" +
"[" + encode("constructor") + "]" +
"(" + output + ")()";
}
}
分析 JSFuck 源码易得:
eval 模式下存在两种情况,在开启了 runInParentScope 后,将执行 []['flat']['constructor']('return eval')()(output) ,即为 Function.prototype.constructor('return eval')() ,即为 globalThis.eval(output)
另一种情况下,将执行 []['flat']['constructor'](output)() ,即为 Function.prototype.constructor(output)()
需注意 Function.prototype.constructor 与 globalThis.Function 并非完全相同。
使用 Reflect API monkey-patch globalThis.eval 和 Function.prototype.constructor ,在其中加入记录传入参数的语句即可获得其参数。
附注
本文所述之方式实际上执行了 JSFuck 编码后的代码,在浏览器中运行时由于浏览器对敏感API默认有权限等限制,安全性更好,如果在 node.js 等环境下运行可能导致恶意攻击者构造针对于 node.js 的检测脚本并在检测到 node.js 环境时破坏本地系统,如有必要在 node.js 等环境下运行建议使用虚拟机等技术保护。