JavaScriptの難読化技術を探る

こんにちは, すいっちとあいぱっどがほしいmonpoke1です.

この記事はTreasure Advent Calendar 2018の6日目の飛び入り記事です.

詳しい人誰か教えてくださいというポエムの意味を込めております.

あらすじ

AdventCalendarの季節.縁あってとあるカレンダーを覗いた.

辞めていった君たちへ · GitHub

最初はAAすごいなぁと思ってたけどよく見たら.jsやん! chromeコンソールに貼り付けて実行してみると面白い.

こういうツールあるのかなぁと思って呟いたらヒントくれた. (知っている方なんだけど, 向こうはmonpoke1が誰だかわかってるのか怪しい.)

ヒントってことはツールじゃなくて自前かぁ...(画像処理は2年前に飽きてるのでやる気を失う)

JavaScript予約語からの文字抽出(([]+1/!1) => "Infinity" とか)

でもこっちちょっと面白そう. 難読化とかいっぱいブログあるんだろうなと思い調べてしまう.

JS難読化手法

seccamp15の資料に基本が載っている.以下はメモ.

前提

  • 難読化は暗号化ではない
    • 多くが"よみにくさ"を目指しているだけ(=時間稼ぎ)
    • すぐにはデコードできないものもある
    • これは確率的に保証されているものではないようだ(ソースを忘れた)

手法の種類(一部)

  • 変数名難読化
    • シンプルに変数名を長くしたり, 1文字にしたり(圧縮の意図が強そう)
  • コードを文字列として隠蔽
    • 文字コードに分解して配列にしたり
    • URLエンコードしたりrot回したり色々
    • 文字列をコードとして実行できる機能が色々あり(DOM-based XSSシンク?), コードを文字列として難読化して実行
      • evals("alert(1)")
      • setTimeout("alert(1)",0)
      • element["innerHTML"] = "<a href=hogehoge/>"
  • プロパティの使用
    • 無駄に文字列, 関数名等を変数(連想配列)に入れておく
    • 以下が等価なことを使用

      1. document.getElementById("link")
      2. document["getElementById"]("link")
      3. 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]

のように,

  1. 予約語から一文字取り出しては繋げて実行したいコードを文字列として生成し,
  2. 前述した"文字列をコードとして実行"を行う

が基本的なテンプレらしい. だいたい"constructor"って文字が欲しいがち.

infinityの他にも,

  • (![]+"") => "false"
    • 空配列の反転はfalse
    • 空文字 ""[]を連結すると文字列に(なんでやねん)
  • ({}+[]) => [object Object]
    • 外側の括弧を忘れて{}+[]は文字列ではなく0になった(なんでやねん)
  • [][[]]+[] => "undefined"
    • undefinedはかなり出しやすいので文字数稼ぎになりそう

などがよく使用されるらしい.

見つからない文字どうするんだろう -> 頑張って文字生成一覧を作ってる人がいた.

qiita.com

あとはすっごいがんばってコード長を調節すれば色々できそう.例えば再帰的に...

  1. (![]+"") => "false"
  2. (![[]-{}]+"") => "false"
  3. (![[[]-{}]-{}]+"") => "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 さんしかいないのかという気持ちになった
  • 知見をください!!

参考


明日は ぐりの趣味の話が聞けるっぽい?たのしみですね.ぐんない.