[JavaScript]解读:[][(![] + [])[+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + ...]...是什么?
好多小伙伴都看到过这样一段全是由各种括号,以及有限的几个操作符组成的代码,完整代码贴在下面。为了好看,我给它格式化了一下:
[]
[
(![] + [])[+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[] + !+[]] +
(!![] + [])[+[]] +
(!![] + [])[!+[] + !+[] + !+[]] +
(!![] + [])[+!+[]]
]
[
(
[][
(![] + [])[+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[] + !+[]] +
(!![] + [])[+[]] +
(!![] + [])[!+[] + !+[] + !+[]] +
(!![] + [])[+!+[]]] + []
)[!+[] + !+[] + !+[]] +
(
!![] +
[][(![] + [])[+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[] + !+[]] +
(!![] + [])[+[]] +
(!![] + [])[!+[] + !+[] + !+[]] +
(!![] + [])[+!+[]]]
)[+!+[] + [+[]]] +
([][[]] + [])[+!+[]] +
(![] + [])[!+[] + !+[] + !+[]] +
(!![] + [])[+[]] +
(!![] + [])[+!+[]] +
([][[]] + [])[+[]] +
(
[][(![] + [])[+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[] + !+[]] +
(!![] + [])[+[]] +
(!![] + [])[!+[] + !+[] + !+[]] +
(!![] + [])[+!+[]]] + []
)[!+[] + !+[] + !+[]] +
(!![] + [])[+[]] +
(
!![] +
[][(![] + [])[+[]] +
([![]] + [][[]])[+!+[] +
[+[]]] +
(![] + [])[!+[] + !+[]] +
(!![] + [])[+[]] + (!![] + [])[!+[] + !+[] + !+[]] +
(!![] + [])[+!+[]]]
)[+!+[] + [+[]]] +
(!![] + [])[+!+[]]
]
(
(![] + [])[+!+[]] +
(![] + [])[!+[] + !+[]] +
(!![] + [])[!+[] + !+[] + !+[]] +
(!![] + [])[+!+[]] +
(!![] + [])[+[]] +
(
![] +
[][
(![] + [])[+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[] + !+[]] +
(!![] + [])[+[]] +
(!![] + [])[!+[] + !+[] + !+[]] +
(!![] + [])[+!+[]]
]
)[!+[] + !+[] + [+[]]] +
[+!+[]] +
(
!![] +
[][(![] + [])[+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[] + !+[]] +
(!![] + [])[+[]] +
(!![] + [])[!+[] + !+[] + !+[]] +
(!![] + [])[+!+[]]]
)[!+[] + !+[] + [+[]]]
)
();
这是一坨什么玩意儿?看得人脑瓜子嗡嗡的。
今天上班第一天,不想干活,只想摸鱼。趁着这个时间娱乐一下,看看这段代码到底是什么东西。
从上面的代码中可以看出来,我已经把它的主要结构展示地非常清晰:
//① ② ③ ④ ⑤
[][...][...](...)();
非常明显,第一个[]
肯定是提供了一个存在于它的原型链上的 方法 或者 属性 供第二个[]
使用。既然是用方括号调用方法或者属性,那么第二个[]
里面必定是一个字符串,就像这样:
[]['map'];
//等价于
Array.prototype.map;
当然,第二个[]
内的字符串不一定是map
,我只是用map
举个栗子,具体内容还要进一步分析。
以此类推,第三个[]
类似第二个[]
,举例来说就是这样:
[]['map']['name'] //'map'
上面那行示例代码获取到了函数Array.prototype.map
的name
属性,但本文讨论的代码究竟获取到了什么东西,还是留待进一步分析。
咱们继续往下看。第四部分是一个小括号()
。小括号除了分块外,最大的作用是调用函数。综合上文的分析,第三部分应该是返回一个方法,而第四部分的这个小括号就是调用第三部分返回的方法,小括号里面的内容呢,那肯定就是参数了。
至于第五部分也就是最后一个小括号,很明显是调用方法。既然这样,那么前四部分整体返回的就是一个方法,也就是说,第四部分执行了一个返回方法的方法。
什么方法会返回一个方法呢?继续深入分析。
第一部分[]
是一个空的数组实例,除了继承了Array
的一些属性和方法,其他没什么可分析的。
第二部分是由5个+
操作符连接的语句。下面以第二部分的第一行为例进行分析。
第一行是(![] + [])[+[]]
,又可以分解成(![] + [])
和[+[]]
,其中涉及到的知识就是隐式转换。其中:
![]
//等价于
!''
结果是false
,于是
(![] + [])
//等价于
(false + [])
//等价于
(false + '')
//等价于
('false' + '')
//等于
'false'
隐式转换的具体规则网上已经有很多文章了,这里就不重复了。
而
[+[]]
//等价于
[+'']
//等价于
[0]
所以,第一行最后的结果是
'false'[0]
//等于
'f'
搞了半天,原来是为了获取'f'
这个字符。
实际上,分析完上面这行代码,其他的就不言自明了,套路都是一样的,第二部分和第三部分的目的,是为了拼凑'filter'
和'constructor'
这两个字符串。
经过分析,整个代码相当于:
[]['filter']['constructor']('alert(1)')();
等于
[].filter.constructor('alert(1)')()
那么为啥拼凑'filter'
这个字符串,而不是拼凑'map'
、'sort'
……呢?这里必须要好好说道说道,这是因为,'filter'
这个字符串,它容易得到……
[].filter.constructor
指向的是构造函数Function
,利用它,可以使用字符串构造函数,类似于eval()
,所以[].filter.constructor('alert(1)')
相当于
Function('alert(1)')
执行Function('alert(1)')
,就会弹出 1 了:
Function('alert(1)')()
好了,本文到此结束了。
End