記号だけで書けちゃうJavaScript 2025/08/04 俺たちの勉強会 #3 #orestudy
自己紹介:murasuke ・株式会社 ツールラボ 開発部 3年ほど前に転職して、勉強会に出る時間ができました ・前回、俺たちの勉強会#2 で「人生初のLT」をやりました👏 ・6月の社員旅行(in 熱海)で 「呑みながらLT会」やりました (盛り上がったので多分来年もやります!)
これはJavaScriptの式です。(ブラウザで)実行できます
_=
({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]
+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![
]+[])[-~[]];__=
(!![]+[])[-~[]]+({}+[])[-~-~-~-~[]]+(!![]+[])[+[]]+([]['']+[])[+[]]+(!![]+[])[-~[]]+([]['']+
[])[-~[]];[][_][_]((![]+[])[-~[]]+(![]+[])[-~-~[]]+(!![] + [])[-~-~-~[]]+(!![] +
[])[-~[]]+(!![] + [])[+[]]+'('+"'"+[][_][_](__+'
"\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~-~[]<<-~-~[])+'"')()+(!![] +
[])[-~-~-~[]]+(![]+[])[-~-~[]]+(![]+[])[-~-~[]]+({} + [])[-~[]]+','+[][_][_](__+'
"\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~-~-~[])+(-~-~-~-~-~-~-~[])+'"')()+(
{} + [])[-~[]]+(!![] + [])[-~[]]+(![]+[])[-~-~[]]+([][[]] + [])[-~-~[]]+'!'+"'"+')')()
ブラウザのコンソールで実行できます! ちゃんと?「 alert(’Hello, world!’)」が実行できました
なぜ [] や ![]や[_] だけでコードが書けるのか JavaScriptは ● 「記号」と「文字列」のみで(キーワードを使わずに) 「実行」する方法があります ● 「任意の文字 」は「記号」の組み合わせで作ることができます ⇒ つまり「 スクリプト」から「文字」を排除することが可能です 1. 実行する処理を「記号」+「文字列」で表現するテクニック 2. 「文字列」を「記号」に置き換えるテクニック について解説します
実行する処理を「記号」+「文字列」で表現する 「Functionコンストラクタ」を使います (eval()ではなく ) new Function('実行する処理')(); // newはJSの仕様で省略可 空配列のリテラルからプロトタイプチェーンをたどることで取得できます ⇐ Arrayのコンストラクタ ⇐ これがFunctionコンストラクタ 任意の文字列の実行を行う処理は、このように書くことができます [].constructor.constructor('実行する処理')
実行する処理を「記号」+「文字列」で表現する ● 「プロパティ」は「配列のアクセス」に書き換えることができます [][‘constructor’][‘constructor’]('実行する処理')() (任意の文字列を実行する処理を、キーワードを使わずに) 「文字列」と「記号」で表現することができました ● 次は「文字列」を「記号」で表す方法を解説します!
「文字列」を「記号」に置き換えるテクニック 「記号」から「文字」の作り方を順に見ていきます 1. 記号の組み合わせで「 数字」を作る 2. 評価結果 (例:false)を文字列化して「 文字」を切り出す 3. 文字コードから (なんとかして )「文字」を生成する
記号の組み合わせで「数字」を作る 評価結果が0になる式を作る (空配列に+をつけることで 数値(0)として評価される) > +[] 0 0の補数を取ることで-1を作る > ~+[] -1 マイナス符号を付けて1にする > -~+[] 1 1の補数をとると「-2」 繰り返して大きな数字を作る > ~-~+[] -2 シフト演算で大きな数を作る > -~-~[]<<-~-~[] 8
評価結果(例:false)を文字列化して「文字」を切り出す 評価結果がfalseになる式を作る > ![] false 評価結果を文字列化(+[])する > ![]+[] ’false’ 評価結果から1文字取り出す > (![]+[])[0] ’f’ 0になる式(+[])と合わせる > (![]+[])[+[]] ’f’ ここまでで、「‘f’, ‘a’, ‘l’, ‘s’, ‘e’」 の5文字を作ることができました
評価結果(例:false)を文字列化して「文字」を切り出す
評価結果から文字を切り出す際に使える式の例
![]+[]; // 'false'
!![]+[]; // 'true'
{}+[]; // '[object Object]'
[][[]]+[]; // 'undefined'
-~[]/[]+[]; // 'Infinity'
+{}+[]; // 'NaN'
※残念ながら、すべてのアルファベットを作ることはできません
文字コードから(なんとかして)「文字」を生成する
前ページで作れない文字は、文字エスケープ(\uHHHH)から作ります
> Function('return "\u0041"')()
'A'
Functionコンストラクタは、既出なので置き換えます
> []['constructor']['constructor']('return "\u0041"')()
'A'
'constructor'、'return'、'\u0041'は、前ページで作った文字から作れるので・・・
> ({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]
+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]]
'constructor'
> (!![]+[])[-~[]]+({}+[])[-~-~-~-~[]]+(!![]+[])[+[]]+([]['']+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[-~[]]
'return'
> ' "\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~[])+'"'
' "\\u0041"'
文字コードから(なんとかして)「文字」を生成する
全てを組み合わせることで、(なんとか)「A」を作ることができました!
>
[][(({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!
![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[
]])][(({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+
(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[~[]])]((!![]+[])[-~[]]+({}+[])[-~-~-~-~[]]+(!![]+[])[+[]]+([]['']+[])[+[]]+(!![]+[])[-~[]]+([][''
]+[])[-~[]]+' "\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~[])+'"')()
'A'
まとめ
●
Functionコンストラクタで (キーワードを使わず )「任意の js文字」が実行できる
> []['constructor']['constructor']('任意のjs文字列')()
●
Functionコンストラクタの「 'constructor'」を「記号」に置き換えることができる
> [][({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]
+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]]][({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(
!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]]]('任意のjs文字列')()
●
「`任意のjs文字列`」も「記号化が可能」(置き換えるだけ)
これで明日から「記号」で書いて同僚に迷惑をかけることができますね!
変換プログラム:https://github.com/murasuke/js-obfuscator