「Visual Studio 2010 の [ツール] メニューに Dotfuscator があることをご存知だろうか?もし、利用されたことが"無い"、または "Dotfuscator 自体知らない" という方は、無償で提供されているこの製品、ぜひ以下の記事を読んで活用いただきたい。
.NETFrameworkは、開発の利便性や柔軟性を実現している反面、解析されやすい。これは.NET アプリケーション解析の手助けとなるメタデータがそのまま残っているためである。実際に逆コンパイルツールを使ってみると、ソースコードがなくても予想以上に正確なソースコードを得られることがわかる。無防備にアプリケーションを提供することはコメントの抜けたソースコードを提供しているのと大して変わらないのである。
ここではバイナリレベルの実行ファイルからソースコードを生成する逆コンパイルの技術と効果的かつ効率的な方法で.NETアプリケーションの解析を抑止するDotfuscatorの主な難読化技術について解説する。
逆コンパイラ技術の進歩と脅威
まず現在の逆コンパイラの技術がどのように進化しているかを、簡単なプログラムを使って示す。リスト1は、簡単なコンソールアプリケーションを抜粋したものである。
リスト1 ソースコード(抜粋)
static void CalcRank() { Array.Clear(count, 0, MAXNUM); // 得点別配列をクリア for (int i = 0; i < MAXNUM; i++) // 全員に対し得点別の配列をカウントアップ count[score[i]]++; int j = 0; // 最高点の人の順位(0基準) for (int i = MAXSCORE; i >= 0; i--) // 最高点から順に順位づけ { ptsrank[i] = j; j += count[i]; } for (int i = 0; i < MAXNUM; i++) rank[i] = ptsrank[score[i]]; // 得点別順位を全員に割り当てる }
図1は、リスト1で生成された実行ファイルを.NETReflectorを使って逆コンパイルしている様子である。 元のプログラムに非常に近い結果が出力されていることがわかる。
この例ではデバッグ情報を持たないReleaseビルド版を扱っているが、実行ファイルの中にデバッグ情報が残っている場合には、定数のシンボル名やローカル変数名を含め、さらに精度の高い逆コンパイル結果を得られる。
図1 .NET Reflectorによる解析画面
コンパイラはプログラムの文法に則ってソースコードを解釈し、実行用のバイナリコード(IL、中間言語)を生成する。
この変換パターンを分析して、バイナリコードから元のソースコードを推測するのが逆コンパイラの仕組みである。
コード生成のパターン分析は着実に進歩しており、LINQやラムダ式を使う最新版の.NET Frameworkにも対応している。また、.NET Frameworkがプログラミングの柔軟性を高めるために、アセンブリの中にさまざまなメタデータを埋め込んでいることも精度の高い解析に繋がっている。逆コンパイル時には「C#」「Visual Basic」など好きな言語を選択できる。「C#」や「Visual Basic」のような言語レベルに復元されてしまうと、プログラムの解析は容易である。特に「コメントがなくても分りやすいプログラミング」を心がけている場合には、解析は一層容易なものとなる。
難読化による保護
○名前変更
プログラム解析の手掛かりとなるのは、まずシンボル名である。図1では、ScoreRankという名前空間やProgramというクラス名、CalcRankという関数名がそのまま表示されていることがわかる。クラスやメソッドに意味のある名前をつけてプログラミングしている限り、それらがプログラムを理解する上で大きな手掛かりとなる。従って逆コンパイルに対する防御の第一歩はこうしたシンボル名を意味のない名前に置き換えることである。
図2 難読化後の逆コンパイル
図2は上記サンプルをDotfuscatorで難読化した後に.NET Reflectorで逆コンパイルした様子である。先ほどのシンボル名が意味のない名前に変更されていることがわかる。さらにDotfuscatorでは、オーバーロードで重複したメソッド名が使用できる事を利用して、可能な限り多くのメソッド名を同じ名前に変更している。(オーバーロード誘導TMアルゴリズム)
○制御フローの難読化
次にプログラム解析の手掛かりとなるのは元のプログラムを厳密に反映してコンパイルされた命令シークエンスである。逆コンパイラはLoopやIF文といった手がかりを探して元のプログラムを推測する。Dotfuscatorは命令ブロックの並べ替えや偽の制御フローの導入などを行って、逆コンパイラが使用する手がかりを破壊する。変換されたコードは、論理的にはオリジナルの命令シーケンスと等価であるが、逆コンパイルができなくなるか、できたとしても元のソースコードを反映していないため、逆コンパイルによるプログラムの解析は非常に難しいものとなる。 図2では関数内部が逆コンパイルできず、「難読化されたため変換できない」という意味のコメントだけが表示されていることがわかる。
○文字列の難読化
文字列は「クリアな状態」でバイナリコード内に保存されている。ハッカーなどの攻撃者たちはアプリケーション内で特定の文字列を探し出して、レジストレーションやアクティベーションなどの重要なロジックを素早く見つけ出す。Dotfuscatorは文字列を暗号化することにより、この種の攻撃に対しても効果的な防御手段を提供している。
○不要なメタデータの削除
プロパティ、イベントパラメータ名などメタデータは多くの場合、IDEや他のツールによる設計/開発時に使用されており、アプリケーション実行時にすべての情報が必要な訳ではない。これらの情報も逆コンパイラや攻撃者にヒントを与えるため、難読化の過程で取り除くことができる。 Dotfuscatorが行う難読化処理はポストビルドソリューションである。処理の対象となるのはビルド後の.NETアプリケーションであるため、ソースコードは一切不要である。
最新技術への対応
Dotfuscatorは進化を続ける.NET Frameworkのテクノロジーも積極的にサポートしている。アプリケーションの配布を容易にするClickOnceアプリケーションの難読化においては、配置マニフェストファイル(.application)をDotfuscatorの入力ファイルとして直接指定することができる。ClickOnceで必要となる証明書ファイル(.pfx)と証明書パスワードはDotfuscator上で指定するだけで複雑な手間を経ることなく、再署名済みの難読化されたClickOnceアプリケーションが自動的に生成される。
また、Silverlightアプリケーションのおいても、xapファイルを直接指定して難読化することができる。xapファイルの実体は、.NETベースのアセンブリとマニフェストがZIP形式で圧縮されたファイルである。アセンブリが難読化された場合、XAML側で参照しているメソッド名も難読化後の名称に書き換える必要があるが、このような書き換え処理も自動的に行われる。
使ってみる
Dotfuscatorの詳細や最新情報については、エージーテックのWebサイトで公開されている。
Visual Studio 2010には簡易版のDotfuscator が無償でバンドルされている。
また、製品版であるDotfuscator Professional Editionの評価版は無償で提供されているので、難読化の作業自体は難解ではないことをぜひお試しいただきたい。