JSFuck
6種類の文字によって構成されるJavaScriptを完全に書き換えることが可能な難解プログラミング言語 ウィキペディアから
JSFuckはJavaScriptのサブセットとして考案された難解プログラミング言語。[・]・(・)・!・+の6文字のみコードを書く。
JavaScriptの様々な言語仕様を利用することで、非常に冗長なコードになってしまうものの上記6文字だけでJavaScriptの全機能を使用できる[1]。
名前はBrainfuckに由来するが、独自のコンパイラやインタプリタを必要とするBrainfuckとは異なり、JSFuckはあくまでもJavaScriptの言語仕様に基づいているためJavaScriptの処理系(WebブラウザやJavaScriptエンジン)で動作する。
歴史
2009年7月にHasegawa YosukeがJavaScriptを[]()!+,\"$.:;_{}~=の18文字に変換するWebアプリケーションを作った[2][3]。
2010年1月には、sla.ckers.orgというWebアプリケーションセキュリティサイトの「Obfuscation」フォーラムで非公式の競争が開催され、文字数を当時必要最小限だと思われていた8文字([]()!+,/)に抑える方法が考案された。その後、どうにか , と / を使わないようにできないか模索され[4]、同年3月にはJS-NoAlnumと呼ばれる現在の6文字で表現されるエンコーダーができた[5]。同年11月にハセガワはJSF*ckと呼ばれる6文字で表現されるエンコーダーを完成させた[6][7]。2012年には、Martin Kleppe が"jsfuck"と名前をつけたプロジェクトをGitHub上で公開した[8]。そしてJSFuck.comというサイトでエンコーダーの実装を公開している[9]。
JSFuckはマルウェアをウェブサイトにクロスサイトスクリプティング (XSS)等によって埋め込むことにも使われたことがある[10]。他の潜在的な使用方法としては、難読化がある。よく使われるJavaScriptライブラリであるjQueryも、6文字で完全に置き換えられたことがある[11]。
変換の手順
要約
視点
JSFuckは非常に冗長である。JavaScriptではalert("Hello World!")としてポップアップを開くであろうコードは21字である。しかし、同じことをJSFuckでしようとすると22948字にもなる。いくつかの文字はJSFuckで表現しようとすると1000文字を超える。この節はどうやって文字を生成することができるのか説明する。
数字
0は+[]によって作られる。ここで、[]は空の配列であり、+は単項プラスである。ここに型変換が働いて、[]は0とみなされる[12]。
1は+!![] か +!+[]で、論理値true (JSFuckでは!![]か!+[]として表現される)が数字の1に変換されたものである[13]。
2から9については、!をその回数だけ繰り返し、それを+で連結すれば良い。これを踏まえると2は!![]+!![]や!+[]+!+[]と書くことができる。
2桁以上の場合は、1桁ずつ数字として配列に詰めたあと、それを1要素連結すればよい。例として"10"は[1] + [0]と書き換えることができる。前述の0と1の作り方を適用すると、これは[+!+[]]+[+[]]となる。数値を返したい場合は、頭に+をつける(この例では10 = +([+!+[]]+[+[]]))
文字
JSFuckではいくつかの文字は論理値や"NaN"、"undefined"から添字付き(角括弧の中に数字が入ったもの)で取り出すことになる。他の文字を生成するためにさらなるトリックが存在する。"1e1000"を数字に型変換すると、Infinityとなり、yを簡単に取り出すことができるようになる[14]。下に記したのは最もシンプルにプリミティブな値を作るコードである。
(ただし、幾つかのコンパイラでは!+[]が警告されるため!![]に統一している。)
| 値 | JSFuck |
|---|---|
false | ![] |
true | !![] |
NaN | +[![]] |
undefined | [][[]] |
Infinity | +(+!![]+(!![]+[])[!![]+!![]+!![]]+[+!![]]+[+[]]+[+[]]+[+[]]) |
例:"a"の生成
"a": 文字列"false"から取ることにする。"false"の2文字目は"a"である。これは"false"[1]としてアクセスできる。"false"はfalse+[]から作ることができる。すなわち、falseと空の配列を足すことになる。(false+[])[1]: falseは![]から生成できるので(論理否定を空の配列に適用する)(![]+[])[1]: 1は数字であるので、これを+trueで置き換えると(![]+[])[+true]: そしてfalseは![]でtrueは!![]なので(![]+[])[+!![]]- これで"a"を返す。
JavaScriptで実際に試してみると、alert((![]+[])[+!![]])とalert("a")は同じ出力である[15]。
他の生成
FunctionのコンストラクタはJavaScriptとして有効なコードを実行することができるので、alert(1)はFunction("alert(1)")()と同じである。Functionのコンストラクタは、[]["at"] (Array.prototype.at) のように標準の関数のconstructorプロパティから取り出すことができる。このことを踏まえると、alert(1)は[]["at"]["constructor"]("alert(1)")()と書き換えることができる。
文字表
要約
視点
文字を作ることができる一番短いコードを下に記した。他の文字も生成できるがおそらくもっと長くなるだろう。
(ただし、幾つかのコンパイラでは!+[]が警告されるため!![]に統一している。)
| 文字 | JSFuck | 意味[16] |
|---|---|---|
| + | (+(+!![]+(!![]+[])[!![]+!![]+!![]]+[+!![]]+[+[]]+[+[]])+[])[!![]+!![]] | 1e+100 の3文字目 |
| . | (+(+!![]+[+!![]]+(!![]+[])[!![]+!![]+!![]]+[!![]+!![]]+[+[]])+[])[+!![]] | 1.1e+21 の2文字目 |
| 0 | +[] | 空配列の整数変換値 |
| 1 | +!![] | true の整数変換値 |
| 2 | !![]+!![] | |
| 3 | !![]+!![]+!![] | |
| 4 | !![]+!![]+!![]+!![] | |
| 5 | !![]+!![]+!![]+!![]+!![] | |
| 6 | !![]+!![]+!![]+!![]+!![]+!![] | |
| 7 | !![]+!![]+!![]+!![]+!![]+!![]+!![] | |
| 8 | !![]+!![]+!![]+!![]+!![]+!![]+!![]+!![] | |
| 9 | !![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![] | |
| a | (![]+[])[+!![]] | false の2文字目 |
| c | ([]+[][(![]+[])[+!![]]+(!![]+[])[+[]]])[!![]+!![]+!![]] | function at() { [native code] } の4文字目 |
| d | ([][[]]+[])[!![]+!![]] | undefined の3文字目 |
| e | (!![]+[])[!![]+!![]+!![]] | true の4文字目 |
| f | (![]+[])[+[]] | false の1文字目 |
| i | ([![]]+[][[]])[+!![]+[+[]]] | falseundefined の11文字目 |
| I (i) | (+(+!![]+(!![]+[])[!![]+!![]+!![]]+(+!![])+(+[])+(+[])+(+[]))+[])[+[]] | Infinity の1文字目 |
| l (L) | (![]+[])[!![]+!![]] | false の3文字目 |
| N | (+[![]]+[])[+[]] | NaN の1文字目 |
| n | ([][[]]+[])[+!![]] | undefined の2文字目 |
| o | (!![]+[][(![]+[])[+!![]]+(!![]+[])[+[]]])[+!![]+[+[]]] | truefunction at() { [native code] } の11文字目 |
| r | (!![]+[])[+!![]] | true の2文字目 |
| s | (![]+[])[!![]+!![]+!![]] | false の4文字目 |
| t | (!![]+[])[+[]] | true の1文字目 |
| u | ([][[]]+[])[+[]] | undefined の1文字目 |
| y | (+[![]]+[+(+!![]+(!![]+[])[!![]+!![]+!![]]+[+!![]]+[+[]]+[+[]]+[+[]])])[+!![]+[+[]]] | NaNInfinity の11文字目 |
| ( | ([]+[][(![]+[])[+!![]]+(!![]+[])[+[]]])[+!![]+[]+(+!![])] | function at() { [native code] } の12文字目 |
| ) | ([]+[][(![]+[])[+!![]]+(!![]+[])[+[]]])[+!![]+[]+(!![]+!![])] | function at() { [native code] } の13文字目 |
コード表
組み合わせることができる一番短いコードを下に記した。
これらを組み合わせ、コードを単純化することができるだろう。
| JSFuck | 出力 | 型 |
|---|---|---|
[] |
[] |
object |
+[] |
0 |
number |
![] |
false |
boolean |
[[]] |
[[]] |
object |
!![] |
true |
boolean |
[]+[] |
"" |
string |
[+[]] |
[0] |
object |
[![]] |
[false] |
object |
+!![] |
1 |
number |
[[[]]] |
[[[]]] |
object |
[][[]] |
undefined |
undefined |
[]+![] |
"false" |
string |
[!![]] |
[true] |
object |
+[]+[] |
"0" |
string |
+[![]] |
NaN |
number |
[[]+[]] |
[""] |
object |
[[+[]]] |
[[0]] |
object |
[[![]]] |
[[false]] |
object |
[]+!![] |
"true" |
string |
[[[[]]]] |
[[[[]]]] |
object |
[+!![]] |
[1] |
object |
[[][[]]] |
[undefined] |
object |
[[]+![]] |
["false"] |
object |
[[!![]]] |
[[true]] |
object |
[+[]+[]] |
["0"] |
object |
[+[![]]] |
[NaN] |
object |
+!![]+[] |
"1" |
string |
セキュリティ
JSFuckのような難読化技術は、「通常の」JavaScriptに備わる"クラッキング防止システム"が存在しない[17]ため、eBayのオークションのページにJSFuckで書かれたスクリプトを埋め込むことができた[18]。
脚注
外部リンク
Wikiwand - on
Seamless Wikipedia browsing. On steroids.