ソースコードがないのに、ソースコードが公開されてしまう!?
もし、開発プロジェクトに対し「実行ファイルには常にソースコードを添付すること」という条件が課せられたとしたら、「なんて非常識な!」と、とまどうプロジェクトリーダーや関係者は多いでしょう。オープンソース・プロジェクトならばともかく、実行ファイルの中にはユーザーの個人情報を処理するコード、IDやパスワードというセンシティブな情報を扱う部分、時間をかけて開発した高度な分析ロジックなどがあり、ソースコードが開示されれば保護すべきものが保護できなくなってしまうおそれがあります。
企業のコンプライアンスや情報セキュリティが重視される昨今では、ソースコードを厳重に管理し、勝手に持ち出したり、逆に個人コンピューターの持ち込みを厳しく制限している企業も少なくありません。特にパッケージソフトやゲームソフトにおけるソースコード保護の重要性は言うまでもないでしょう。
しかし、.NET Frameworkで開発されたアプリケーションは、ソースコードがなくても、アプリケーション(実行ファイル)から元のソースコードへ、容易に逆コンパイルできてしまいます。.NETでは、「アセンブリ」と呼ばれる構成単位が、相互作用できる情報(メタデータ)を保持しているため、これを使って内部を解析しやすくなっているのです (注1)。
Javaでも同様です。逆に、C/C++言語用の逆コンパイルツールは存在しますが、それほど高精度ではありません。
逆コンパイラ技術の進歩と脅威
現在、逆コンパイラの技術がどのように進化しているかを、簡単なプログラムを使って示します。リスト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]]; // 得点別順位を全員に割り当てる }
ここでは、.NET Reflectorという.NET用の逆コンパイラツールを使います 。図1は、リスト1で生成された実行ファイルを逆コンパイルしている様子です。ここではデバッグ情報を持たないReleaseビルド版を扱っていますが、デバッグ情報が残っていれば定数シンボルやローカル変数の名前を含めて、さらに正確な逆コンパイルリストが得られます。しかも、逆コンパイル時には言語を「C#」や「Visual Basic」など好きな言語を選択できます。
コンパイラは、選んだ言語の文法に沿ってプログラムコードから.NET用のコード(IL、中間言語)を生成します。この変換の仕方を分析し、生成されたコードから元のプログラムを推測するのが逆コンパイラの仕組みです。コード生成のパターン分析は着実に進歩しており、LINQやラムダ式を使う最新版の.NET Frameworkにも対応しています。また、.NET Frameworkが、プログラミングの柔軟性を高めるために、アセンブリ中にさまざまなメタデータを埋め込んでいることも精度の高い解析に役立っています。
C#やVisual Basicのような言語レベルに復元されてしまうと、プログラムの理解は容易です。とくに「コメントがなくても分かりやすいプログラム」を書いている場合は、ソースコードが公開されているのと似たような状態だと言えます。つまり、開発プロジェクトの中で、ソースコードの持ち出しを厳しく制限していても、知的財産の保護や情報セキュリティの面でリスクにさらされてしまうのです。
.NET Reflectorの単体版は無料です。開発中のプロジェクトがあれば、手元の実行ファイルで逆コンパイルを試してみてもよいでしょう。おそらく想像以上に正確なソースコードが得られるはずです。
難読化による防御
.NET Frameworkが利便性のために実装している仕組みによって、逆コンパイルされやすくなること、そのことが知的財産やセキュリティ上の問題になることは当初から認識されていました。このため、.NET Frameworkが初めてリリースされた2002年の翌年から、Visual Studioのすべての製品版にはDotfuscatorという難読化ツールが提供されてきたのです (注2)。
聞き慣れないかもしれませんが、「難読化」は英語ではobfuscateと言い、「意図的に分かりにくくする」という意味を持つ古くからある言葉です。
Visual Studio .NET 2003以降。無償版など、一部のエディションを除きます。
解析の手掛かりになるのは、まずシンボル名です。図1では、ScoreRankという名前空間やProgramというクラス名、CalcRankという関数名がそのまま表示されています。クラスやメソッドに意味のある名前を付けてプログラミングしている限り、それらがプログラムを理解する大きな手掛かりとなります。Visual Studioに付属のDotfuscatorはこうしたシンボルを意味のない短い名前に置き換えます。
しかし、逆コンパイルでソースコードが得られれば、そこで.NET Frameworkの機能が呼び出されたり、「Login」や「パスワード」といった重要な文字列が見つかることで、センシティブな情報を扱う部分であることが突き止められてしまう恐れがあります。
製品版のDotfuscatorは、より実用的な難読化処理を施します。まず、生成された実行コードを論理的に同等な異なる制御フローに置き換えます。通常、コンパイラが生成しないコードになるため、逆コンパイルができなくなるか、できたとしても元の制御フローよりもわかりにくいものになります。上記のサンプルを製品版Dotfuscatorで難読化して、.NET Reflectorで逆コンパイルしたのが図2です。
ここでは、関数内部が逆コンパイルできず、「難読化されたため変換できない」という意味のコメントだけが表示されています。これにより、ソースコードレベルでの理解は難しくなります。
一方、このような難読化を施しても、IL(中間言語)への逆アセンブルを抑止することはできません。前述のような重要な文字列が使われている場所を見つけられてしまうかもしれません。そこで、製品版Dotfuscatorには、文字列の暗号化という機能があります。文字列を暗号化すれば、実行ファイルから元の文字列を検索して見つけだすことはできなくなります(注3)。
文字列が暗号化されたアセンブリは、実行時に文字列復号のためのオーバーヘッドが発生します。そのため、この機能はデフォルトでオフになっています。暗号化が必要なモジュールだけを選択して暗号化してください。
SilverlightやClickOnceへの対応
Dotfuscator製品版では、Silverlightアプリケーションも難読化できます。Silverlightアプリケーション(.xap)の実体は、.NETベースのアセンブリとマニフェストがZIP形式で圧縮されたファイルです。Silverlightでは、ユーザーインターフェイスの定義がXAML形式でリソースに埋め込まれていますが、DotfuscatorではXAPファイルを直接入力ソースとして使用することで、XAML側の定義も自動的に難読化されたアセンブリと一致した名称に加工されます。
アプリケーションの配布を容易にする.NET Frameworkの技術であるClickOnceアプリケーションも難読化できます。配置マニフェストファイル(.application)を指定し、そのプロパティとしてClickOnceを生成する際に使用した証明書ファイル(および証明書パスワード)を設定すれば、自動的に難読化して再署名されたClickOnceアプリケーションが生成されます。
この他、アセンブリの再署名対応、改ざんの検出など、.NET Frameworkの最新技術に対応した難読化を提供しています。
開発プロジェクトへの責任
逆コンパイルなどの解析技術は日々進歩しています。ソースコードを厳重に管理する必要を感じているなら、バイナリからの解析に無防備であってはなりません。「自分のソフトウェアが狙われることはない」という保証はどこにもありません。いったん問題が発生したら、後からこれを回復することは困難であり、結果として企業の信頼を揺るがすことにもなります。アプリケーションに対して十分な難読化を施すことによって、はじめて知的財産の保護や情報セキュリティに対する責任を果たせるのです。
.NETアプリケーション難読化ツール「Dotfuscator」の詳細や最新情報については、エージ―テックのWebサイトを参照してください。また、難読化を体験できる無料評価版をご用意しておりますので、ぜひお試しください。