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については、!![]
(前述の通り1)をその回数だけ繰り返し、それを+
で連結すれば良い。これを踏まえると2は!![]+!![]
や!+[]+!+[]
と書くことができる。
2桁以上の場合は、1桁ずつ数字として配列に詰めたあと、それを1要素連結すればよい。例として"10"
は[1] + [0]
と書き換えることができる。前述の0と1の作り方を適用すると、これは[+!+[]]+[+[]]
となる。数値を返したい場合は、頭に+
をつける(この例では10
= +([+!+[]]+[+[]])
)
JSFuckではいくつかの文字は論理値や"NaN"、"undefined"から添字付き(角括弧の中に数字が入ったもの)で取り出すことになる。他の文字を生成するためにさらなるトリックが存在する。"1e1000"
を数字に型変換すると、Infinity
となり、y
を簡単に取り出すことができるようになる[14]。下に記したのは最もシンプルにプリミティブな値を作るコードである。
(ただし、幾つかのコンパイラでは!+[]
が警告されるため!![]
に統一している。)
値 | JSFuck |
---|---|
false |
![]
|
true |
!![]
|
NaN |
+[![]]
|
undefined |
[][[]]
|
Infinity |
+(+!![]+(!![]+[])[!![]+!![]+!![]]+[+!![]]+[+[]]+[+[]]+[+[]])
|
"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
のコンストラクタは、[]["filter"]
(Array.prototype.filter
) のように標準の関数のconstructor
プロパティから取り出すことができる。このことを踏まえると、alert(1)
は[]["filter"]["constructor"]("alert(1)")()
と書き換えることができる。
文字を作ることができる一番短いコードを下に記した。他の文字も生成できるがおそらくもっと長くなるだろう。
(ただし、幾つかのコンパイラでは!+[]
が警告されるため!![]
に統一している。)
文字 | JSFuck |
---|---|
+ | (+(+!![]+(!![]+[])[!![]+!![]+!![]]+[+!![]]+[+[]]+[+[]])+[])[!![]+!![]]
|
. | (+(+!![]+[+!![]]+(!![]+[])[!![]+!![]+!![]]+[!![]+!![]]+[+[]])+[])[+!![]]
|
0 | +[]
|
1 | +!![]
|
2 | !![]+!![]
|
3 | !![]+!![]+!![]
|
4 | !![]+!![]+!![]+!![]
|
5 | !![]+!![]+!![]+!![]+!![]
|
6 | !![]+!![]+!![]+!![]+!![]+!![]
|
7 | !![]+!![]+!![]+!![]+!![]+!![]+!![]
|
8 | !![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]
|
9 | !![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]
|
a | (![]+[])[+!![]]
|
d | ([][[]]+[])[!![]+!![]]
|
e | (!![]+[])[!![]+!![]+!![]]
|
f | (![]+[])[+[]]
|
i | ([![]]+[][[]])[+!![]+[+[]]]
|
I (i) | (+(+!![]+(!![]+[])[!![]+!![]+!![]]+(+!![])+(+[])+(+[])+(+[]))+[])[+[]]
|
l (L) | (![]+[])[!![]+!![]]
|
N | (+[![]]+[])[+[]]
|
n | ([][[]]+[])[+!![]]
|
r | (!![]+[])[+!![]]
|
s | (![]+[])[!![]+!![]+!![]]
|
t | (!![]+[])[+[]]
|
u | ([][[]]+[])[+[]]
|
y | (+[![]]+[+(+!![]+(!![]+[])[!![]+!![]+!![]]+[+!![]]+[+[]]+[+[]]+[+[]])])[+!![]+[+[]]]
|
組み合わせることができる一番短いコードを下に記した。
これらを組み合わせ、コードを単純化することができるだろう。
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に備わる"クラッキング防止システム"が存在しない[16]ため、eBayのオークションのページにJSFuckで書かれたスクリプトを埋め込むことができた[17]。