jsfuck 6个字符写js代码(一)

JavaScript是一门灵活的语言,表达力很丰富。JSFuck使得我们可以只使用[,],(,),!,+等6个字符来写JavaScript代码。虽然实际应用中根本不会有人这么写代码,但是这种做法很有趣,对其原理的探讨也很有助于加深我们对JavaScript的理解。

举个小例子:

alert(1)

以上代码,用这6个字符,可以这么写:

[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()

打开浏览器控制台(console),复制粘贴以上代码,就可以看到弹出一个内容为‘1’的对话框。看起来好像挺不可思议的,但是仔细分析后,我们就能理解上面的写法了。

我们从JavaScript类型转换说起。

JavaScript变量数据可以发生类型转换。类型转换有分为“强制转换”和“自动转换”两种。“强制转换”主要指使用Number、String和Boolean三个构造函数,手动将各种类型的值,转换成数字、字符串或者布尔值。“自动转换”是指预期什么类型的值,就调用该类型的强制转换函数,自动转换。

以 + 运算符为例:

【1】运算子之中存在字符串
只要两个运算子中有一个是字符串,则不管另一个什么类型,都会被自动转换为字符串,然后执行字符串的连接运算;

【2】运算子均为数值或布尔值
两个运算子都是数值或者布尔值,布尔值转换为数值(true 为 1,false 为 0),然后执行加法运算;

【3】运算子之中存在对象
两个运算子中存在对象(非原始类型数据),先调用该对象的valueOf方法,如果返回值为原始类型,然后按上面2条规则执行;否则调用该对象的toString方法,然后按上面2条规则执行。

说到这里, 我们需要记住valueOf和toString方法的区别:

数值运算中优先调用valueOf(),如Number()函数优先调用对象的valueOf();字符串运算中优先调用toString(),如String()函数优先调用对象的toString()。valueOf() 返回对象的原始值;toString()返回对象的原始字符串表示。

再看4个特殊的表达式。

(1) 空数组 + 空数组

[] + []

两个运算子都是非原始类型的数组(对象),先对两个 [] 均执行 valueOf 方法,返回该 [] 本身(其实,默认情况下,对象的valueOf方法都返回该对象本身),然后对 [] 调用toString方法方法(数组执行toString方法一般返回数组的字符串形式,如 [1,2,3].toString() 返回 “1,2,3” ),返回 “”,所以相当于 “” + “”,最终结果为 “”,空字符串。

(2) 空数组 + 空对象

[] + {}

分析方法同上,不过默认情况下,对象的toString方法,返回 “[object Object]”,所以,以上表达式相当于 “” + “[object Object]”,最终结果为 “[object Object]”。

(3) 空对象 + 空对象

{} + {}
({}) + {}

首先看 {} + {},JavaScript引擎将第一个空对象视为一个空代码块,忽略,变成了 +{},这里的 + 为一元运算符,它相当于调用Number方法,强制将运算子转为数值。而这里的运算子是对象{},当Number方法的参数是对象时,处理流程如下:

【1】调用valueOf方法
调用对象自身的valueOf方法。如果返回原始类型的值,则直接对该值使用Number函数,不再进行后续步骤;

【2】调用toString方法
如果valueOf方法返回的还是对象,则改为调用对象自身的toString方法。如果返回原始类型的值,则对该值使用Number函数,不再进行后续步骤。

【3】报错
如果toString方法返回的是对象,就报错。

所以,+{} 相当于,先对{}调用valueOf方法得到其自身,然后调用toString方法,得到”[object Object]”,最后相当于 +”[object Object]”,这个字符串转换为数值为 NaN,即最终结果是 NaN。

再看 ({}) + {},两个{}均调用toString方法,相当于”[object Object]” + “[object Object]”,最终结果是 “[object Object][object Object]”。

(4) 空对象 + 空数组

{} + []
({}) + []

先看 {} + [],{}视作空代码块被忽略,相对于 +[] ,[] 也是对象的一种,所以调用toString方法,相当 +””,最终结果为 0。

再看 ({}) + [],两个运算子均为对象,分别调用toString方法,相当于”[object Object]” + “”,最终结果为 “[object Object]”。

以上是最基本的数组类型转换,也是jsFuck的基本原理之一。暂时先到这里,后面我们会继续探讨这个话题。

参考:
[1] http://javascript.ruanyifeng.com/grammar/conversion.html
[2] http://www.jsfuck.com/
[3] https://gold.xitu.io/entry/5834a964570c35006c4ac205

views