本帖最后由 xiaofeifei112 于 2018-7-26 10:36 编辑
什么是jsfuck?
想象一下自己第一次见到代码的样子,满屏的奇怪字符?WTF?而当我看到jsfuck的时候,再次体会到了这种感觉。请看下面的代码:[JavaScript] 纯文本查看 复制代码 [][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()
WTF???(黑人问号脸)
没错!这确实是一串js代码,你可以直接放到控制台去执行,最后执行的结果是alert(1)。JS就是这么一门神奇的语言。本着对知识的渴求与对未知事物的探索精神(雾),下面我们就一起来研究下jsfuck的原理。
在讲jsfuck之前,先要说起一个js的经典面试题:[]==![]
这道题是考验js基础的,利用的是js的一些基本原理——类型转换。类型转换大家都了解,比如简单的 ‘a’+1结果就是’a1’,在这个计算过程中1被隐式转换成string类型,这道面试题也是这个原理。Js语法中’==’两边如果不是同一类型则会进行类型转换。
但是相信很多接触过前端的同事见到这个题目时候的第一反应,是这个表达式的结果是false,然而拿到控制台执行之后却发现是true,这让很多人十分不解。特别是当我们在控制台打印代码的时候,会出现一些令人费解的事情。如:
[JavaScript] 纯文本查看 复制代码 Boolean([]) //true
Boolean(![]) //false 其实上述代码,我们进行了强制转换,在js中在把对象做布尔值转换的时候,会把所有对象都转换为true(包括创建的new Boolean(false)对象转换之后也是true,具体可参考犀牛书3.8.3)。而相等运算符却不是这么计算的,在相等运算符中,如果运算符两边的值有一个是布尔类型,则会把true转换成1,false则转换为0。而在这道题目中![]的结果肯定是布尔值false,则会转换为0。
[]==0
这时候解释起来就简单了很多,相等运算符如果有一边是数字,而另一边是对象,则会把对象转换成数字。而空的数组对象转换成数字是0(犀牛书3.8)那么结果便是:
0==0 //true
讲了这么多,终于可以进入正题了。之所以开篇讲这么一个题目,是为了更好理解jsfuck的原理。在官网上(直接百度jsfuck就行),我们能看到官方给出的jsfuck的Basics,下面我们来逐条分析。(水平有限,如果有发现错误可以指出,大家一起研究)
false => ![]
//非运算符会把[]的布尔值取反
true => !![]
//再取反则是true
undefined => [][[]]
//这一个就比较复杂,在js中数组的索引可以为负数和非整数,而在这种情况下会将索引转换成字符串,作为对象的属性访问,我们知道[]转换为字符串为空字符串,则上面等价于:[][‘’]。然而数组对象并没有名为’’的属性,在js中访问一个对象并不存在的属性时,会给出undefined
NaN => +[![]]
//这里利用了+运算符的特性,这里+不是加号运算符(姑且这么叫)而是一元运算符的一种,一元加法,用于把操作数转换为数字或者NaN并返回这个数字。上式等价于+[false],而根据类型转换的规则我们知道,这样得到的结果为NaN
0 => +[]
//根据类型转换规则,[]转换为数字为0
1 => +!+[]
//本式为上面的变式,根据运算符的优先级,先执行+[]为0,再转换成布尔取反为true,true转换为数字为1
2 => !+[]+!+[]
//中间一个+为加法运算符优先级比较低,等价于true+true,值为2
10 => [+!+[]]+[+[]]
//可拆分成[+!+[]]加[+[]],左边为[1],右边为[0],+作为连接字符串运算符,而根据数组转换成字符串我们可以知道上面等价于‘1’+‘0’,得到的是字符串‘10’
Array => []
//[]就是数组对象的事例,用于获取字符串’Array’(暂时没有弄清楚作者的意思,这个Array对象的获取相当复杂,利用了js对象的原型链,其代码非常之繁琐,我会在最后一篇人肉翻译jsfuck代码中详细讲述。如果有大神了解原作者在此只放一个‘[]’的原因,请告诉我,我会对博客进行更改,并附上大神的名字提名感谢)
Number => +[]
//与上式相同
String => []+[]
//与上式相同
Boolean => ![]
//与上式相同
Function => []["filter"]
//与上式相同
eval => []["filter"]["constructor"]( CODE )()
//这里比较复杂,我们先来看前半部分[]["filter"]["constructor"],这一部分可以看到拿的是一个数组对象的‘filter’属性下面的‘constructor’属性,‘filter’属性是数组的一个方法,但在这里并没有用到该方法,你把它替换成别的方法也可以照常运行(例如‘map’),关键是下面这一步,filter的constructor属性,我们知道在js中任何方法都可以看做是Function对象的一个实例,而Function就作为所有函数的构造函数。这里拿到的就是函数的Function()构造函数。而Function()构造函数的最后一个参数会作为函数体执行。因此这段语句会执行‘CODE里的内容’。
window => []["filter"]["constructor"]("return this")()
//与上式相同,拿到的是函数的构造函数,第一个括号内便是函数体。并且Fuction()构造函数所创建的函数并不使用词法作用域(划重点!),他是直接执行在顶层函数中的,也就是全局作用域,因此this返回window。
参考:
https://blog.csdn.net/qq_36539075/article/details/79946099
|