JavaScript學習:JSFuck代碼閱讀筆記
來自: 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