一开始我也有疑问,为什么 ++[[]][+[]]+[+[]]='10' ?
不得不信,于是我们要慢慢的分析:
分析基础符号
[]分析
[]有两个作用:
1. 数组
2. 访问属性和方法
例子:
[1,2,3,4] // 数组 "abc"[0] // 属性 [1,2]["length"] // 方法
+运算符的作用
1. 创建数字
2. 将两个值相加
3. 连接字符串
4. 创建字符串
两个数相加
operand + operand = result
1. 如果操作符数中有一个对象,它将转换为原始值(string
、number
或boolean
)
2. 如果操作符数中有一个字符串,第二个操作数将转换成字符串,并且连接在一起转换成一个字符串
3. 在其它情况之下,两个操作数转换为数字并且将执行加法运算
其中,对象转换的规则:
1. 如果对象类型是一个Date
,可以使用toString()
方法
2. 在其它情况下使用valueOf()
方法,它将返回一个原始值
3. 如果valueOf()
方法不能将它返回一个原始值,可以使用toString()
方法。而这种情况大部分情况下都会发生
例子:
// 数字和字符串 var result = 1 + "5"; // "15" // 数字和数组 var result = [1,3,5] + 1; // "1,3,51" // 数字和布尔值 var result = 10 + true; // 11 // 数字和对象 var result = 15 + {}; // "15[object Object]" // 数字和null var result = 8 + null; // 8 // 字符串和null var result = "queen" + null; // "queennull" // 数字和undefined var result = 12 + undefined; // NaN
下面这张图是两个变量相加的类型结果:
位于操作数之前(+x)
一元正号运算符(unary plus operator)位于其操作数前面,计算其操作数的数值,如果操作数不是一个数值,会尝试将其转换成一个数值。它可以将字符串转换成整数和浮点数形式,也可以转换非字符串值 true
,false
和
null
。小数和十六进制格式字符串也可以转换成数值。负数形式字符串也可以转换成数值(对于十六进制不适用)。如果它不能解析一个值,则计算结果为 NaN。
例子:
+3 // 3 +"3" // 3 +true // 1 +false // 0 +null // 0 +[] // 0
递增(++)
递增运算符(increment operator)为其操作数增加1,返回一个数值。
解析++[[]][+[]]+[+[]]
首先把这个表达式拆分开来,如:
++[[]][+[]] + [+[]]
由上面的基础分析可知, +[] === 0 是完全正确的,故我们可以简化如下:
++[[]][0] + [0]
[[]][0] 返回内部数组 ([])。但是由于语言规范, [[]][0] === [] 是不正确的,因此我们暂时用A来代替里面的数组。
++[[]][0] == A + 1, 因为 ++ 的意思是”+1”。
++[[]][0] === +(A + 1);这是一个数值,因为递增(++)返回的永远都是一个数值
因此,表达式可以简化如下:
+([] + 1) + [0]
由上面的基础符号分析可以简化表达式如下:
1 // 参考+在操作数前面 + "0" // 参考两个变量相加的对象转换规则
故最终结果为'10'。
用途
上面只是一个简单的例子,其实用这些字符串是可以写出真正有用的代码的,例如:
(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]] // alert(1)
可以在此生成想要的代码:http://www.jsfuck.com/
看着一些字符串可以执行,想必大家都有疑虑,用在哪些地方呢?
由于这样的代码属于混淆代码,不容易被识别,我们可以用在账户的安全校验上,比如,可以在Web端账户登陆之前从后端拿到一段这样的可执行代码,将执行结果写入cookie、token或者ajax请求里,这样可以防止一部分黑产用工具刷接口来获取数据。
由于黑产现在采用的工具都是易语言写的。基于winhttp.dll和winInet.dll的,不具备js引擎,所以如果web端在提交登陆之前获取并执行后端的一段这样的代码,一来可以以混淆的代码使得黑产不容易看懂,二来如果黑产想破解的话,就需要一个js引擎或者无头浏览器,其成本是很高的。
用于安全上只是其中的一个例子,聪明的你可能还有更好的使用场景,不妨分享出来。
参考资料
Addition (+):https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators
jsFuck: https://github.com/aemkei/jsfuck
JavaScript addition operator in details: https://rainsoft.io/javascriptss-addition-operator-demystified/
Why does ++[[]][+[]]+[+[]] return the string “10”?:https://stackoverflow.com/questions/7202157/why-does-return-the-string-10