JavaScriptの難読化技術を探る
こんにちは, すいっちとあいぱっどがほしいmonpoke1です.
この記事はTreasure Advent Calendar 2018の6日目の飛び入り記事です.
詳しい人誰か教えてくださいというポエムの意味を込めております.
あらすじ
AdventCalendarの季節.縁あってとあるカレンダーを覗いた.
最初はAAすごいなぁと思ってたけどよく見たら.jsやん! chromeコンソールに貼り付けて実行してみると面白い.
こういうツールあるのかなぁと思って呟いたらヒントくれた. (知っている方なんだけど, 向こうはmonpoke1が誰だかわかってるのか怪しい.)
お、ではヒントを!
— R.Yokoji (@yokoji) 2018年12月2日
・JavaScriptの予約語からの文字抽出(([]+1/!1) => "Infinity" とか)
・画像の2値化・ビットマップ化・密度計算
・からの文字列長&画像サイズ調整したら、文字をビットマップ割当
・半角文字の縦横比は要考慮
・改行、空白でJavaScriptとして解釈不能にならないように
ヒントってことはツールじゃなくて自前かぁ...(画像処理は2年前に飽きてるのでやる気を失う)
JavaScriptの予約語からの文字抽出(([]+1/!1) => "Infinity" とか)
でもこっちちょっと面白そう. 難読化とかいっぱいブログあるんだろうなと思い調べてしまう.
JS難読化手法
seccamp15の資料に基本が載っている.以下はメモ.
前提
- 難読化は暗号化ではない
- 多くが"よみにくさ"を目指しているだけ(=時間稼ぎ)
- すぐにはデコードできないものもある
- これは確率的に保証されているものではないようだ(ソースを忘れた)
手法の種類(一部)
- 変数名難読化
- シンプルに変数名を長くしたり, 1文字にしたり(圧縮の意図が強そう)
- コードを文字列として隠蔽
- プロパティの使用
- 無駄に文字列, 関数名等を変数(連想配列)に入れておく
以下が等価なことを使用
-
document.getElementById("link")
-
document["getElementById"]("link")
-
x="getElementById"; document[x]("link")
-
- jjencode
- いわゆる"記号プログラミング"ってやつっぽい
記号プログラミンっ
jjencodeとJSF*ck
JavaScriptの予約語からの文字抽出(([]+1/!1) => "Infinity" とか)
このような手法は, JS記号プログラミングでよく使う手口らしい.
JSF*ckというJavaScriptインタプリタ上で(も)動く記号のみの難読言語があり, これは jjencodeとほぼほぼ同じ原理を踏襲している.
JSFu*kからJS記号プログラミングの知見がちょっとだけ得られた. (検索履歴が汚れるけど, 最近js fu🌟kってよく思うのでちょうどよかった.)
基本的なコード構築
([]+1/!1)[2] => "f" //"Infinity"[2]
のように,
- 予約語から一文字取り出しては繋げて実行したいコードを文字列として生成し,
- 前述した"文字列をコードとして実行"を行う
が基本的なテンプレらしい. だいたい"constructor"って文字が欲しいがち.
infinityの他にも,
(![]+"") => "false"
- 空配列の反転はfalse
- 空文字
""
や[]
を連結すると文字列に(なんでやねん)
({}+[]) => [object Object]
- 外側の括弧を忘れて
{}+[]
は文字列ではなく0になった(なんでやねん)
- 外側の括弧を忘れて
[][[]]+[] => "undefined"
- undefinedはかなり出しやすいので文字数稼ぎになりそう
などがよく使用されるらしい.
見つからない文字どうするんだろう -> 頑張って文字生成一覧を作ってる人がいた.
あとはすっごいがんばってコード長を調節すれば色々できそう.例えば再帰的に...
(![]+"") => "false"
(![[]-{}]+"") => "false"
(![[[]-{}]-{}]+"") => "false"
ツバメマークに見る記号ぷろぐらみんぐ
ここまでもかなり調べにくかったけど, 結局ツバメマーク作れそうな記号と1だけの, (さらに$記号を使わない)JS難読化が全く見つからなかったのでタイムアップ.
コードを眺めて見つけた冗長化をメモ
1|1>>1|1 => 1
- 演算子の優先順位にドキッとするやつ
>>
->|
の順に評価される- 文字列を長くでき, 見た目の密度が変えられる
11111.1%11.1*111e11|!1 => 7
- 何が起きてるかよくわからなかったやつ
((11111.1%11.1)*111e11)|!1
の順番7.985612171423826|!1 => 7
という知見が得られた(?)
1/[]+[] => "Infinity"
- そうなんだ...
人間なので上側の羽で飽きました.
おわり
- JSの謎の仕様は難読化のためにあるのではないかとすら思えてきた
- 実際にはjjencodeの発明がだいぶ後
- 難読化周りを調べるにはノイズが多い
- ひたすらツールの紹介
- なぜ難読化が必要なの?みたいなさいと
- 原理紹介全然見つからん
- いい感じの本も(しら)ない...
- ラボに刺さってたサイ本(O-REILLY)とかに難読化の項目なかった
- 有名そうな論文も全然見つからない
- こちらも"どうparseするか"などばかり
- この辺で諦め始める
- この分野 @hasegawayosuke さんしかいないのかという気持ちになった
- 知見をください!!
参考
- JavaScript難読化読経
- 記号だけのJavaScriptプログラミングの基本原理
- (」・ω・)」うー!(/・ω・)/にゃー!encode
- JSFuck - GitHub
- 演算子の優先順位
- JSFuckから理解するECMAScriptの仕様
明日は ぐりの趣味の話が聞けるっぽい?たのしみですね.ぐんない.