JavaScript学习:JSFuck代码阅读笔记

cbgd 8年前

来自: http://my.oschina.net/Tsybius2014/blog/614234


JSFuck源码地址(GitHub):https://github.com/aemkei/jsfuck

JSFuck在OSC上的介绍页面:http://www.oschina.net/p/jsfuck

JSFuck可以将JavaScript代码进行转换,转换后的代码只使用6个字符([,],(,),!,+),实现的功能和转换前代码是一样的。出于好奇和学习的目的,我研究了一下JSFuck的源码。

在网站 http://www.jsfuck.com/ 中有一个例子,将JavaScript语句 alert(1) 转换为只由六种字符的版本:

将这段代码放到HTML文件的script标签下,就可以运行了:

<html>      <head>          <title>happy new year</title>      </head>      <body>      <script>   [][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()      </script>      </body>  </html>

不过,我想了解的是,为什么这段代码会起作用。研究后才发现,这段代码等价于另一段代码:

[]["filter"]["constructor"]("return eval")()("alert(1)")

在这段代码中,alert(1)是我们要转换的JavaScript源码,这段代码的意思是执行JavaScript代码alert(1)。这段代码是由四个字符串和字符[、]、(、)构成的。也就是说,我们只要能把字符串中的每一个字符,都用[,],(,),!,+这六个字符表示出来,那么我们就完全可以将任何一段JavaScript代码,找到仅用这六个字符表示的等价形式。

那么,我们来看一下各个字符的等价形式:(如果用console.log输出右边的部分,则会返回左边的字符,0-9这10个字符外面又套了一层[],这样做是为了保证在之后用+运算符进行拼接时不被系统识别为加法运算)

'0':'[+[]]'  '1':'[+!+[]]'  '2':'[!+[]+!+[]]'  '3':'[!+[]+!+[]+!+[]]'  '4':'[!+[]+!+[]+!+[]+!+[]]'  '5':'[!+[]+!+[]+!+[]+!+[]+!+[]]'  '6':'[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]'  '7':'[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]'  '8':'[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]'  '9':'[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]'  'a':'(false+"")[1]'  'b':'(Function("return{}")()+"")[2]'  'c':'([]["filter"]+"")[3]'  'd':'(undefined+"")[2]'  'e':'(true+"")[3]'  'f':'(false+"")[0]'  'g':'(false+[0]+String)[20]'  'h':'(+(101))["toString"](21)[1]'  'i':'([false]+undefined)[10]'  'j':'(Function("return{}")()+"")[10]'  'k':'(+(20))["toString"](21)'  'l':'(false+"")[2]'  'm':'(Number+"")[11]'  'n':'(undefined+"")[1]'  'o':'(true+[]["filter"])[10]'  'p':'(+(211))["toString"](31)[1]'  'q':'(+(212))["toString"](31)[1]'  'r':'(true+"")[1]'  's':'(false+"")[3]'  't':'(true+"")[0]'  'u':'(undefined+"")[0]'  'v':'(+(31))["toString"](32)'  'w':'(+(32))["toString"](33)'  'x':'(+(101))["toString"](34)[1]'  'y':'(NaN+[Infinity])[10]'  'z':'(+(35))["toString"](36)'  'A':'(+[]+Array)[10]'  'B':'(+[]+Boolean)[10]'  'C':'Function("return escape")()(("")["italics"]())[2]'  'D':'Function("return escape")()([]["filter"])["slice"]("-1")'  'E':'(RegExp+"")[12]'  'F':'(+[]+Function)[10]'  'G':'(false+Function("return Date")()())[30]'  'H':'Function("return unescape")()("%"+(48)+"")'  'I':'(Infinity+"")[0]'  'J':'Function("return unescape")()("%"+(4)+"a")'  'K':'Function("return unescape")()("%"+(4)+"b")'  'L':'Function("return unescape")()("%"+(4)+"c")'  'M':'(true+Function("return Date")()())[30]'  'N':'(NaN+"")[0]'  'O':'(NaN+Function("return{}")())[11]'  'P':'Function("return unescape")()("%"+(50)+"")'  'Q':'Function("return unescape")()("%"+(51)+"")'  'R':'(+[]+RegExp)[10]'  'S':'(+[]+String)[10]'  'T':'(NaN+Function("return Date")()())[30]'  'U':'(NaN+Function("return{}")()["toString"]["call"]())[11]'  'V':'Function("return unescape")()("%"+(56)+"")'  'W':'Function("return unescape")()("%"+(57)+"")'  'X':'Function("return unescape")()("%"+(58)+"")'  'Y':'Function("return unescape")()("%"+(59)+"")'  'Z':'Function("return unescape")()("%"+(5)+"a")'  ' ':'(NaN+[]["filter"])[11]'  '!':'Function("return unescape")()("%"+(21)+"")'  '"':'("")["fontcolor"]()[12]'  '#':'Function("return unescape")()("%"+(23)+"")'  '$':'Function("return unescape")()("%"+(24)+"")'  '%':'Function("return escape")()([]["filter"])[20]'  '&':'("")["link"](0+")[10]'  ''':'Function("return unescape")()("%"+(27)+"")'  '(':'(false+[]["filter"])[20]'  ')':'(true+[]["filter"])[20]'  '*':'Function("return unescape")()("%"+(2)+"a")'  '+':'(+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]])+[])[2]'  ',':'([]["slice"]["call"](false+"")+"")[1]'  '-':'(+(.+[0000000001])+"")[2]'  '.':'(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]'  '/':'(false+[0])["italics"]()[10]'  ':':'(RegExp()+"")[3]'  ';':'("")["link"](")[14]'  '<':'("")["italics"]()[0]'  '=':'("")["fontcolor"]()[11]'  '>':'("")["italics"]()[2]'  '?':'(RegExp()+"")[2]'  '@':'Function("return unescape")()("%"+(40)+"")'  '[':'(Function("return{}")()+"")[0]'  '\':'Function("return unescape")()("%"+(5)+"c")'  ']':'(Function("return{}")()+"")["slice"]("-1")'  '^':'Function("return unescape")()("%"+(5)+"e")'  '_':'Function("return unescape")()("%"+(5)+"f")'  '`':'Function("return unescape")()("%"+(60)+"")'  '{':'(NaN+[]["filter"])[21]'  '|':'Function("return unescape")()("%"+(7)+"c")'  '}':'([]["filter"]+"")["slice"]("-1")'  '~':'Function("return unescape")()("%"+(7)+"e")'

要将上面的字符转换成六种字符的等价形式,还需要用到下面的几个等价形式带入解决:

var SIMPLE = {    'false':      '![]',    'true':       '!![]',    'undefined':  '[][[]]',    'NaN':        '+[![]]',    'Infinity':   '+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]])' // +"1e1000"  };    var CONSTRUCTORS = {    'Array':    '[]',    'Number':   '(+[])',    'String':   '([]+[])',    'Boolean':  '(![])',    'Function': '[]["filter"]',    'RegExp':   'Function("return/"+false+"/")()'  };

使用SIMPLE和CONSTRUCTORS内的等价形式,即可推算出上面那些字符的内容。比如a的等价形式是(false+"")[1],将false替换为![],将""替换为[],将1替换为+!+[],就可以推算出a使用六个字符表示的等价形式为(![]+[])[+!+[]]。(PS:从中不难发现作者aemkei真是用心良苦)

这样看来,以alert(1)为例,每个字符的等价形式如下:

'a':'(![]+[])[+!+[]]'  'l':'(![]+[])[!+[]+!+[]]'  'e':'(!![]+[])[!+[]+!+[]+!+[]]'  'r':'(!![]+[])[+!+[]]'  't':'(!![]+[])[+[]]'  '(':'(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]'  '1':'[+!+[]]'  ')':'(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]'

将它们用符号“+”连接起来,就可以获取"alert(1)"的等价形式了!同理我们获得了"filter"、"constuctor"、"return eval"的等价形式,再回头看看这段代码:

[]["filter"]["constructor"]("return eval")()("alert(1)")

将字符串替换后,放入HTML中的script标签里面,或是放到一个js文件里面,就可以用浏览器打开HTML文件查看效果啦!

(图中浏览器版本为:Google Chrome 46.0.2490.80 m)

END