We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在進入今天的主題之前,先推薦大家看一個影片 WAT:A lightning talk by Gary Bernhardt from CodeMash 2012。在這個影片裡面,講者會為大家示範 JavaScript 到底有多「神奇」。而這些神奇的特性,也會跟我們之後所要介紹的兩個東西有關。
大家有聽過 Brainfuck 嗎?顧名思義,就是會讓你超級頭痛的一個程式語言,只用下面這八個字元就可以寫出一個完整的程式:
Brainfuck
>
<
+
-
.
,
[
]
而這幾種字元如果對應到 C 的程式碼,就是:
(資料來源:wikipedia: Brainfuck) Brainfuck 內建會給你一組陣列,並且讓 ptr 指向陣列開頭,剩下的事情就交給我們自己了,舉例來說,輸出 Hello World 的程式長這樣:
ptr
Hello World
++++++++++[>+++++++>++++++++++>+++>+<<<<-] >++.>+.+++++++..+++.>++.<<+++++++++++++++. >.+++.------.--------.>+.>.
如果想看更多範例可以參考維基百科,上面有附一些說明。由於 Brainfuck 並不是今天的重點,因此只是稍微跟大家介紹一下而已。
接著就是我們今天的第一個主角:JSfuck,我們先來看一段 JSfuck 的程式碼:
JSfuck
現在你知道它為什麼取做這個名稱了吧! 與 Brainfuck 相似,JSfuck 只有六個字元:
可是 JSfuck 與 Brainfuck 最大的差別就在於,JSfuck 其實是把你的 JavaScript 程式碼轉換成這樣的形式,而不是像 Brainfuck 那樣,每一個符號都有自己代表的動作。 接著就讓我們來看看 JSfuck 的原理到底是什麼吧!
如果想要執行一段字串裡面的程式碼,可以怎麼做呢?你可能會用 eval,但其實還有一個方法,就是 Function Constructor,你可以傳入一段字串,那段字串就會被當做程式碼來運行,舉例來說:
eval
Function Constructor
new Function('alert(1)')();
上面這段程式碼就會做跟 alert(1) 一樣的事情 不只如此,其實連參數名稱都可以傳入!
alert(1)
new Function('a', 'b', 'return a+b;')(1, 2); // 3
這一段先到這邊暫時打住,等等再回來看。但是現在知道一個很重要的事實了:只要你有一段文字,就可以用 Function Constructor 的方式去執行。
那我們下一個要達成的目標就是,湊出所有的文字,並且都是用那六個字元組成,不就可以執行了嗎?
先從數字開始吧,看看怎樣可以湊出數字。但其實我們也只要湊出 0 跟 1 就好,因為其他正整數都可以透過這兩個數字拼湊起來。
+[]可以湊出 0,或者也可以換一個思路,![]會是 false,所以+![]也會是 0,有 0 之後,要變出 1 就不難了,因為 ![] 是 false,所以 !![] 就是 true。那 +!![] 就是數字的 1。
+[]
![]
false
+![]
!![]
true
+!![]
數字有了,接下來是文字跟符號,文字的話你可能會直接想到:String.fromCharCode,只要能湊出這段文字,你就能湊出其他任何文字或符號了。
String.fromCharCode
但我們先來看看一個比較特別的方法,例如說 ![] 是 false,然後 []+[] 是空字串,所以把兩個加起來,![]+[]+[]就會是字串的 "false"(其實 ![]+[] 就可以了),那我是不是可以用:"false"[0] 取得 f 這個字元?
[]+[]
![]+[]+[]
"false"
![]+[]
"false"[0]
f
把字串 "false" 用上面的那串取代,就會變成:(![]+[]+[])[0],那 0 又可以用我們上面得出的+[]取代,就變:(![]+[]+[])[+[]],這樣你就成功用這幾個字元湊出 f 這個字了,酷吧!
(![]+[]+[])[0]
(![]+[]+[])[+[]]
其他的文字跟符號也是相同原理,你可以從各個 JavaScript 的程式碼裡面找到許多文字的蹤跡,例如說 undefined,如果你想知道所有的文字是怎麼湊出來的,可以參考:JSfuck 原始碼。
undefined
現在有了 Function Constructor 跟要執行的文字,是不是就可以完成我們想做的事了。可是 new Function() 要怎麼用這六個字元湊出來呢?
new Function()
一個空的陣列[]有很多原生的 JavaScript function,像是 map 好了,[].map 就可以得到 map 這個 function,有了 function 之後,只要用 map.function.constructor,就可以拿到 Function Constructor了! 就像 "".constructor 也可以拿到 String Constructor 一樣。
[]
map
[].map
map.function.constructor
"".constructor
而且.可以用[]取代,[].map會變成[]['map'],這樣結合下來,就變成:
[]['map']
[]['map']['constructor'] []['map']['constructor']('alert(1)')(); // 可以順利執行
接著就是把 map 跟 constuctor 這兩個字用上面的方法湊出來,不是就可以了嗎?
constuctor
做到這裡,相信大家應該比較瞭解 JSfuck 的原理了,就是用許多特別的技巧湊出文字、湊出 Function Constructor 來執行那段文字。
接著,我們介紹一個原理類似,但更可愛的東西!
可愛吧!居然可以把 JavaScript 變成一堆顏文字! aaencode 跟 JSfuck 又有一點小差異了,因為aaencode可以用到的字元比較多,只是長得比較可愛而已,那既然JSfuck可以做到,aaencode沒什麼理由做不到。
aaencode
接著讓我們仔細看一段 aaencode 轉出來的程式碼: (因為有些特殊字元的關係,可能會顯示不出來,但不影響整體的閱讀,看個感覺就好)
゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); (゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o); (゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ : ((゚ω゚ノ==3) +'_') [゚Θ゚] ,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_') [゚ー゚] }; (゚Д゚) [゚Θ゚] =((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚) +'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+ (゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+ ((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ( (゚ー゚==3) +'_') [゚Θ゚]+((゚ー゚==3) +'_') [(゚ー゚) - (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+( ゚ー゚)]+ (゚Д゚) ['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=(( ゚ー゚==3) +'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o - ゚Θ゚]+ ((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\'; ( ゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';( ゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+( ゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚ Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+ (゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +( o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ ( ゚Θ゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_');
仔細觀察會發現裡面其實有很多分號,是把很多行組合在一起,我們挑裡面比較短的一行來看: (底下的程式碼因為特殊字元的關係有多做一點處理,跟原本的有些許差異)
o=(˙_˙) =_=3;
看起是顏文字,但其實沒那麼簡單,我把空格隔多一點,你就知道我在講什麼了:
o = (˙_˙) = _ = 3;
其實就是:
_ = 3; (˙_˙) = _; o = (˙_˙) ;
也就是讓 o, (˙_˙), _ 都是 3 所以 ˙_˙ 只是一個變數名稱,然後用 () 包起來變成顏文字,但這括弧在程式上其實沒有什麼意義
o
(˙_˙)
_
˙_˙
()
至於其他段的程式碼,做的事情也都大同小異,有興趣的讀者們可以自己再去分析,或者直接右鍵->檢視原始碼去看看是怎麼 encode 的。
我第一次看到 aaencode 的時候也是:「哇!」嚇了一跳,不解為什麼用顏文字也可以寫程式,後來仔細看才了解到其實顏文字本來就是一堆符號組成的,可以寫出程式也是件很正常的事情。
但每次看到今天介紹的這兩種特別的寫法,還是很佩服作者,當初怎麼想到可以用這樣子來寫程式。希望這篇文章的介紹能讓大家對程式碼有點新的想法,說不定給了你靈感,可以開發出更厲害的寫法。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
在進入今天的主題之前,先推薦大家看一個影片 WAT:A lightning talk by Gary Bernhardt from CodeMash 2012。在這個影片裡面,講者會為大家示範 JavaScript 到底有多「神奇」。而這些神奇的特性,也會跟我們之後所要介紹的兩個東西有關。
先從 Brainfuck 開始
大家有聽過
Brainfuck
嗎?顧名思義,就是會讓你超級頭痛的一個程式語言,只用下面這八個字元就可以寫出一個完整的程式:>
<
+
-
.
,
[
]
而這幾種字元如果對應到 C 的程式碼,就是:
(資料來源:wikipedia: Brainfuck)
Brainfuck 內建會給你一組陣列,並且讓
ptr
指向陣列開頭,剩下的事情就交給我們自己了,舉例來說,輸出Hello World
的程式長這樣:如果想看更多範例可以參考維基百科,上面有附一些說明。由於
Brainfuck
並不是今天的重點,因此只是稍微跟大家介紹一下而已。JSfuck
接著就是我們今天的第一個主角:
JSfuck
,我們先來看一段JSfuck
的程式碼:現在你知道它為什麼取做這個名稱了吧!
與 Brainfuck 相似,JSfuck 只有六個字元:
可是 JSfuck 與 Brainfuck 最大的差別就在於,JSfuck 其實是把你的 JavaScript 程式碼轉換成這樣的形式,而不是像 Brainfuck 那樣,每一個符號都有自己代表的動作。
接著就讓我們來看看 JSfuck 的原理到底是什麼吧!
Function Constructor
如果想要執行一段字串裡面的程式碼,可以怎麼做呢?你可能會用
eval
,但其實還有一個方法,就是Function Constructor
,你可以傳入一段字串,那段字串就會被當做程式碼來運行,舉例來說:上面這段程式碼就會做跟
alert(1)
一樣的事情不只如此,其實連參數名稱都可以傳入!
這一段先到這邊暫時打住,等等再回來看。但是現在知道一個很重要的事實了:只要你有一段文字,就可以用
Function Constructor
的方式去執行。如何湊出程式碼?
那我們下一個要達成的目標就是,湊出所有的文字,並且都是用那六個字元組成,不就可以執行了嗎?
先從數字開始吧,看看怎樣可以湊出數字。但其實我們也只要湊出 0 跟 1 就好,因為其他正整數都可以透過這兩個數字拼湊起來。
+[]
可以湊出 0,或者也可以換一個思路,![]
會是false
,所以+![]
也會是 0,有 0 之後,要變出 1 就不難了,因為![]
是false
,所以!![]
就是true
。那+!![]
就是數字的 1。數字有了,接下來是文字跟符號,文字的話你可能會直接想到:
String.fromCharCode
,只要能湊出這段文字,你就能湊出其他任何文字或符號了。但我們先來看看一個比較特別的方法,例如說
![]
是false
,然後[]+[]
是空字串,所以把兩個加起來,![]+[]+[]
就會是字串的"false"
(其實![]+[]
就可以了),那我是不是可以用:"false"[0]
取得f
這個字元?把字串
"false"
用上面的那串取代,就會變成:(![]+[]+[])[0]
,那 0 又可以用我們上面得出的+[]
取代,就變:(![]+[]+[])[+[]]
,這樣你就成功用這幾個字元湊出 f 這個字了,酷吧!其他的文字跟符號也是相同原理,你可以從各個 JavaScript 的程式碼裡面找到許多文字的蹤跡,例如說
undefined
,如果你想知道所有的文字是怎麼湊出來的,可以參考:JSfuck 原始碼。把上面結合起來
現在有了
Function Constructor
跟要執行的文字,是不是就可以完成我們想做的事了。可是new Function()
要怎麼用這六個字元湊出來呢?一個空的陣列
[]
有很多原生的 JavaScript function,像是map
好了,[].map
就可以得到map
這個 function,有了 function 之後,只要用map.function.constructor
,就可以拿到Function Constructor
了! 就像"".constructor
也可以拿到 String Constructor 一樣。而且
.
可以用[]
取代,[].map
會變成[]['map']
,這樣結合下來,就變成:接著就是把
map
跟constuctor
這兩個字用上面的方法湊出來,不是就可以了嗎?做到這裡,相信大家應該比較瞭解
JSfuck
的原理了,就是用許多特別的技巧湊出文字、湊出Function Constructor
來執行那段文字。接著,我們介紹一個原理類似,但更可愛的東西!
用顏文字寫程式
可愛吧!居然可以把 JavaScript 變成一堆顏文字!
aaencode
跟JSfuck
又有一點小差異了,因為aaencode
可以用到的字元比較多,只是長得比較可愛而已,那既然JSfuck
可以做到,aaencode
沒什麼理由做不到。接著讓我們仔細看一段
aaencode
轉出來的程式碼:(因為有些特殊字元的關係,可能會顯示不出來,但不影響整體的閱讀,看個感覺就好)
仔細觀察會發現裡面其實有很多分號,是把很多行組合在一起,我們挑裡面比較短的一行來看:
(底下的程式碼因為特殊字元的關係有多做一點處理,跟原本的有些許差異)
看起是顏文字,但其實沒那麼簡單,我把空格隔多一點,你就知道我在講什麼了:
其實就是:
也就是讓
o
,(˙_˙)
,_
都是 3所以
˙_˙
只是一個變數名稱,然後用()
包起來變成顏文字,但這括弧在程式上其實沒有什麼意義至於其他段的程式碼,做的事情也都大同小異,有興趣的讀者們可以自己再去分析,或者直接右鍵->檢視原始碼去看看是怎麼 encode 的。
結論
我第一次看到
aaencode
的時候也是:「哇!」嚇了一跳,不解為什麼用顏文字也可以寫程式,後來仔細看才了解到其實顏文字本來就是一堆符號組成的,可以寫出程式也是件很正常的事情。但每次看到今天介紹的這兩種特別的寫法,還是很佩服作者,當初怎麼想到可以用這樣子來寫程式。希望這篇文章的介紹能讓大家對程式碼有點新的想法,說不定給了你靈感,可以開發出更厲害的寫法。
The text was updated successfully, but these errors were encountered: