本文深入解析一段看似混乱的 JavaScript 混淆代码,揭示其如何利用数组原型链、类型强制转换及位运算构造字符串。通过拆解变量赋值与索引操作,逐步还原代码如何从空数组和对象中提取字符,最终组合成 sort 和 alert 方法并执行弹窗。
JavaScript 混淆代码解析:利用原型链与类型转换实现 alert(1)
代码如下:
($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+
($$=($_=!''+$)[_/_]+$_[+$])])()[__[_/_]+__
[_+~$]+$_[_]+$$](_/_) 请注意,上述代码虽然视觉上显得杂乱无章,但这并非由自动换行导致,而是刻意编写的三行代码(当然,将其写在一行也是合法的)。在浏览器中运行该页面(注:部分旧版 IE 浏览器可能无法执行),你会发现这段代码的实际功能等价于:
alert(1)
代码拆解与变量推导
为何会产生这样的结果?我们将这段代码拆解为多个部分进行详细分析。
$=[] // 将 $ 赋值为空数组,因此 !$ 的布尔值为 false。
__ = !$ + $ // 加法运算符会将 !$ 和 $ 强制转换为字符串,因此 __ 的值变为字符串 "false"。
_ = -~-~-~$ // 此处涉及 ~ 按位取反运算符,其逻辑为 -($+1)。因此 -~$ 的值为 1,进而 _ 的值为 3。
基于上述推导,我们可以得出以下中间结果:
代码如下:
(__ = !$ + $ )[ _ = -~-~-~$] => ("false")[_] => ("false")[3] => "false"[3] = "s"
({} + $)[_/_] => ("[object Object]")[_/_] => ("[object Object]")[1] => "[object Object]"[1] = "o" 接下来继续拆解子表达式 $$=($_=!''+$)[_/_]+$_[+$] :
$_=!''+$ // 注意,!'' 中包含两个单引号,即对空字符串进行非运算。因此变量 $_ 被赋值为字符串 "true"。
由此可进一步推导:
$$=($_=!''+$)[_/_]+$_[+$] => $$ = ( "true")[1] + "true"[0] => "r" + "t" = "rt"
因此,表达式 (__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+($$=($_=!''+$)[_/_]+$_[+$]) 的组合结果为 "s" + "o" + "rt",即字符串 "sort"。
表达式简化与执行流程
所以,原始的复杂表达式:
代码如下:
($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+
($$=($_=!''+$)[_/_]+$_[+$])])()[__[_/_]+__
[_+~$]+$_[_]+$$](_/_) 可以被简化替换为:
代码如下:
($=[[]]["sort"])()[__[_/_]+__
[_+~$]+$_[_]+$$](_/_) 接下来我们分析 [__[_/_]+__[_+~$]+$_[_]+$$](_/_) 这部分代码的含义。
前文我们已经确认了以下变量值:
__ = "false"
_ = 3
~$ = -1
$_ = "true"
$$ = "rt"
因此,[__[_/_]+__[_+~$]+$_[_]+$$](_/_) 的推导过程为:["false"[1] + "false"[3-1] + "true"[3] + "rt"](3/3) => ["a" + "l" + "e" + "rt"](1) => ["alert"](1)。
所以,原始表达式最终可以替换为:
($=[[]]["sort"])()["alert"](1)
这段代码的具体执行流程如下:
a = [[]] // 创建一个包含空数组的数组
b = a["sort"] // 获取数组的 sort 方法引用
c = b() // 调用数组的 sort 方法,此处 b() 返回 window 对象
d = c["alert"] // 获取 window.alert 方法
d(1) // 调用 window.alert 方法。
综上所述,这堆复杂的表达式最终执行结果等同于 window.alert(1)。
更多详细信息请参阅原文及 Reddit 上的相关讨论。
原文评论区中,也有开发者分享了一个日本开发者编写的小工具,可将 JavaScript 代码编码为各种表情符号并执行,供爱好者参考。
此外,上述代码除了可能用于 XSS 攻击测试外,实际业务价值有限,但通过分析此代码,可以学习数据类型转换的相关知识,并领略 JavaScript 语言的灵活性。
详细代码逐行解析
详细代码解析:
代码如下:
(
$=[$=[]] // $ = []
[
(__= !$ + $) // __ = "false"
[_=-~-~-~$] // _ = 3
// (__)[3] = "s"
+
({} + $) // ({} + $) = [object Object]
[_/_] // _/_ = 1
+ // ([object Object])[1] = "o"
($$ = //
($_=!'' + $) // !''+ $ = "true" ; $_ = "true"
[_/_] // _/_ = 1;
// $_[1] = "r"
+
$_[+$] // +$ = 0; $_[0] = "t"
) // $$= "rt"
] // ["sort"]
// []["sort"] = [].sort = function sort() { [native code] }
// $ = []["sort"]
)() // ($)() = [object Window]
[
__[_/_] // __ = "false";
// __[1] = "a"
+
__ [_+~$] //_ = 3; ~$ = -1; _ + ~$ = 2
// __[2] = "l"
+
$_[_] // $_ = "true" ; _ = 3;
// $_[3] = "e"
+
$$ // $$ = "rt"
](_/_); // _ / _ = 1
// window["alert"](1) 关键知识点总结
这里需要注意的几点:
1. javascript 里面 $, _ 均可以作为合法的变量名
2. 函数调用还可以采用方括号语法:比如 [1,2,4,1,9,1].sort() 可以写成 [1,2,4,1,9,1]["sort"]();
3. ~ 运算符对数字进行按位取反操作
4. javascript 中在对不同类型的变量使用 + 运算符时的类型转换规则
5. 最后一个针对某些 native code 编写的方法,执行时会返回 window 对象,例如
var s = [].sort ; t=s(); 则 t 是 window