難読化の話(超!?入門編)その3 後編|セキュリティごった煮ブログ

ネットエージェント
セキュリティごった煮ブログ

 コース:こってり

難読化の話(超!?入門編)その3 後編

bubobubo

どーも、bubobuboです。

引き続き「プログラムの難読化」をテーマに、急ぎ気味で作文を行いました。

難読化をテーマとしたエントリはこれで5本目ですが、4本目の時点で飽きてきたので「後編」と題した本エントリで終了したいと思います。前回の記事から期間が開いていて、今までどこまで書いたのか忘れかけていたことと、締め切りが厳しいので手短(?)に済ませます。

過去の記事はこちら。

難読化の話(超!?入門編)
難読化の話(超!?入門編)その2
難読化の話(超!?入門編)その3 前編
難読化の話(超!?入門編)その3 中編

仮想マシン型の難読化

以前のエントリで説明したことですが、Javaや.NETで作られたプログラムはデコンパイルが容易です。なぜ元のソースコードに近いものを復元できるのかというと、作成したプログラムがネイティブコードではなく中間コードとして保持されているからです。

ネイティブコードは、ソースコードからビルドする際に、プログラムの実行には不要な情報を削除したり、冗長な処理を削るなどの最適化を行います。最適化を極限まで追求するには、アーキテクチャの特性も最大限利用する必要があります。元のソースコードを共通化したとしても、コンパイラやリンカは、それぞれのCPUやOSなどに適した最適化を行います。例えばWindows版、iOS版、ニンテンドースイッチ版の3バージョンを出したい場合は、3種類のネイティブコードを用意する必要があります。

中間コードは、テキストデータであるソースコードをバイナリデータに変換したものです(これは雑な説明です)。プログラムを動作させるために明らかに不要な情報(例えば、コメントアウトされたプログラムやメモ)は削除されますが、それ以外の情報はかなりの精度で残されています。Javaと.NETで若干違いますが、変数名や関数名などのメタデータは、プログラマが命名したものがそのまま残っています。さらに、中間コードの時点ではまだ最適化を行っていないので、大元のプログラムの特徴がほぼそのまま残っています。最適化は、プログラムの実行時に各プラットフォーム向けにカスタマイズされた仮想マシン(JavaVM、.NET Framework)が行います。

それゆえ、Javaや.NETで開発されたプログラムはデコンパイルされやすく、第三者でもオリジナルのソースコードに近いものを得ることができます。

...ここまでは以前のエントリの復唱です。

誤解のないように追記すると、中間コードではなくネイティブコードに対応したデコンパイラも同じぐらいの歴史があります。ただし、多くの情報が欠落したネイティブコードのデコンパイルは技術的に混み入っているので、実用レベルのものを目指すと、個人では買えない金額の有償ソフトしかありませんでした。

今は選択肢が増えて、商用レベルのデコンパイラが無料で手に入ります。詳しくはごった煮ブログの以下のエントリをどうぞ。

リバースエンジニアリングツール新時代?!-Ghidraのお話-
https://www.netagent.co.jp/study/blog/hard/20190530.html

話を戻します。中間コードはなぜデコンパイルされやすいのか? というと前述したように「中間コードだから」なのですが、ではなぜデコンパイラが作られやすいのかというと「技術資料は全て揃っているから」です。Javaならサンマイクロシステムズ(現オラクル)、.NETならマイクロソフトが、仮想マシンのソースコードや中間コードの仕様書を自ら公開しています。大企業が自社の技術を広く普及させようとしているのだから当然のことです。今では互換・派生環境も複数あります。

List of Java virtual machines
https://en.wikipedia.org/wiki/List_of_Java_virtual_machines

Mono (ソフトウェア)
https://ja.wikipedia.org/wiki/Mono_(%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2)

非常に長いフリになってしまいましたが「技術資料は全て揃っているから」デコンパイル(≒解析)されやすいわけです。これが「技術資料が全く開示されていない」と仮定するとどうなるでしょう。

まず、解析したい中間言語のプログラムは意味不明なデータの固まりです。既存のデバッガも使えません(仮想マシン自体はデバッグできますが、その上で動く中間言語のトレースはできません)。したがって、仮想マシンの実装や中間コードの仕様をノーヒントの状態から探り当てる必要があります。中間言語の仕様がわかったら、いきなり逆コンパイル...は難しいので、まずは逆アセンブラを自作して、逆アセンブルリストを辛抱強く読んで、プログラムがどう動いているのかを調べていく...手順を並べてみると、ものすごい労力が必要になることは自明でしょう。

この発想を使って、(変な言葉ですが)さらに難しいプログラムの難読化ツールを作れるのではないか、という動きが起こりました。

まず、独自規格の仮想マシンを設計して、難読化したいプログラムをその仮想マシンで動くコードに変換して、その変換した(=難読化した)コードを独自規格の仮想マシン上で実行するようにします。

そうすると、この難読化したプログラムを解析したい場合は、前述した「仮想マシンの実装や中間コードの仕様をノーヒントの状態から探り当てる」ところからのスタートになります。正攻法でやろうとすると、ものすごくしんどいです。

このような思想で作られた難読化ツールで、筆者が初めて見たのは『x86.Virtualizer』でした。2007年の話。

x86.Virtualizer
https://github.com/rwfpl/rewolf-x86-virtualizer

リバースエンジニアリングに関する国際会議であるREconでも同じコンセプトの発表がありました。2008年の話。

Creating Code Obfuscation Virtual Machines
https://recon.cx/2008/a/craig_smith/Neohapsis-VM-101.pdf

このスライドではP-Code(Pseudo Code:疑似コード)という単語が使われていますが、Visual Basic 4.0まではネイティブコードへのコンパイルができず、Visual Basicランタイムが解釈できるP-Codeを出力していたことを思い出しました(実質的な中間コードですが、当時「中間コード」という呼び方は無かった...はず)。

仮想マシンはただえさえマシンパワーを食うのに、当時のPCのスペックで仮想マシン的なことをやっているのだから動作はとても遅く、しかもランタイムパッケージをあらかじめインストールしないと動作しないという代物でした。

当然ですが、OllyDbgやWinDbgのようなx86専用のデバッガ・逆アセンブラではVisual BasicのP-Codeをデバッグすることができません。ここだけ見ると立派な難読化と言えそうです。

ただし、Visual Basicアプリ専用のデバッガが存在しており、P-Codeの逆アセンブル機能を持った『WKTVBDebugger』を使えば、OllyDbgと同じようにデバッグトレースが可能でした。また、Visual Basicのコード内で標準関数を使うと、いちいちランタイムパッケージを外部参照するので、DLLにフックをかけるとどのような関数が呼び出されるのかわかるので、そのログを見るだけで何をやっているのかわかったりしました。

今日のクラック対策ツールの中には、こうした仮想マシン型の難読化を行うものがあります。前述したように「(独自規格の)仮想マシンの実装や中間コードの仕様をノーヒントの状態から探り当てる」ところから始めなければならないので、正攻法でリバースエンジニアリングしようとすると、ものすごくしんどい(はず)です。と同時に、クラック対策ツールを実装する側も相当にしんどいと思います。

仮想マシン型であるということは、難読化されたコード(中間言語)をネイティブコードに直しながら実行していることになるので、計算コストは凄まじくなります(本当の意味での中間コードなら、JITコンパイラを使ってネイティブコードに変換してから実行するので十分早いですが、難読化を目的とするのであれば、事前にまとめて機械語に直す方法は採らないはずです)。

とはいえ、今のコンピュータは普及価格帯でも十分速いので、難読化することで処理が増えても程度問題となりそうです...が、以前のエントリで紹介したように、マシンスペックをフルに使うPCゲームにおいては、すでにクラック対策ツールによる悪影響が露呈しています。不正コピーは防げるかもしれないが、動作が重くなりユーザー体験は劣悪になると本末転倒です。

近年では、LLVMを応用したプログラムの難読化やその逆を行う強力なアプローチもあります。

LLVM
https://ja.wikipedia.org/wiki/LLVM

いま、LLVMという技術用語を説明なく使いましたが、技術的には非常に興味深いものの低レイヤーかつ非常に難解、そもそもどういう技術なのかをうまく一言で説明することができません。しかし、この原稿の締め切りまであと10分となってしまったので、巨大変身ヒーローのように帰ってくるかどうかわかりませんが、難読化のエントリは5部作で終わりにします。

メルマガ読者募集 採用情報 2020年卒向けインターンシップ

月別