スタックトレースのデコード
アプリケーションを難読化すると、知的財産に保護レイヤーが加わり、リバースエンジニアリングが難しくなることで競争上の優位性が得られます。しかし、このセキュリティの強化は、エラーの報告に課題をもたらします。難読化するとスタックトレースが読めないシンボルの羅列になることが多く、クラッシュの解析が難しくなり、デバッグの作業が複雑になります。
.NET でのスタックトレースのデコード
.NET の開発者は通常、アプリケーションのエラーの原因を特定するためにスタックトレースを利用します。しかし難読化を行うと、このトレースは判読できないものになります。Babel Obfuscator を使えば、難読化されたスタックトレースを簡単にデコードできます。デコードには、難読化の過程で生成されるマップファイルが必要です。
マップファイルの生成
Babel Obfuscator で XML マップファイルの生成を有効にすると、マップファイルは既定で、難読化されたアセンブリと同じディレクトリに生成されます。ファイル名は、元のアセンブリの名前に .map.xml を付けたものです(例:MyApp.exe.map.xml)。
生成されたマップファイルには、難読化されたシンボル名と元のシンボル名の対応関係が含まれます。
オーバーロードリネームは無効にしてください(--nooverloaded)。オーバーロードリネームは異なるメソッドに同じ名前を割り当てるため、スタックトレースをデコードするときにあいまいさが生じることがあります。オーバーロードリネームが有効な場合、Babel はデコード時に、一致する可能性のあるメソッドをすべて示します。
マップファイルの出力先は、Babel の使い方に応じた方法で変更できます。
コマンドライン
babel MyApp.exe --mapout CustomPath/MyMapping.xmlファイルパスのパラメーターを省略すると、ターゲットのディレクトリに既定の命名規則でファイルが作成されます。
MSBuild タスク
<Babel GenerateMapOutFile="true" MapOutFile="CustomPath/MyMapping.xml" />Babel Obfuscator の NuGet パッケージ
<PropertyGroup>
<GenerateMapOutFile>true</GenerateMapOutFile>
<BabelMapOutFile>CustomPath/MyMapping.xml</BabelMapOutFile>
</PropertyGroup>Babel Desktop
Babel Desktop では、アセンブリを選択し、プロパティパネルの「Output & diagnostics」グループで GenerateMapOutFile を有効にします。マップファイルを任意のパスに書き出すには、MapOutFile を設定します。難読化プロジェクトを参照してください。
難読化されたスタックトレースのデコード
難読化されたアプリケーションがクラッシュして、難読化されたスタックトレースが出力された場合は、Babel を使ってデコードします。Babel Obfuscator には、マップファイルを使って難読化されたスタックトレースをデコードするツールが組み込まれています。
Babel Desktop
次の手順は概要です。完全なガイドについては、スタックデコーダーを参照してください。
スタックデコーダーを開く
コマンドパレットを開き、「ツール: スタック デコーダー」を実行します。
マップファイルを追加する
「マップ ファイルを追加…」で XML マップファイルを追加するか、デコーダーにドロップします。トレースが複数のアセンブリにまたがる場合は、複数のマップファイルを追加できます。
スタックトレースを貼り付ける
難読化されたスタックトレースを貼り付けるか、「トレースを開く…」でファイルから読み込みます。
デコードする
「デコード」を押すと、デコードされたスタックトレースが表示されます。
コマンドライン
または、babel コマンドラインツールを使用します。
babel --stacktrace StackTrace.txt --mapin MyApp.exe.map.xml --mapin Library.dll.map.xmlスタックトレースに複数のアセンブリが関係する場合は、複数のマップファイルを指定できます。
動的プロキシのフレーム
動的プロキシを有効にすると、Babel は対象となる呼び出しを、生成したブリッジメソッド経由にします。各ブリッジは余分なフレームとして現れるため、デコードされたトレースでは、自分のメソッドと Babel の内部処理のフレームが交互に並びます。Babel は、各ブリッジが代理しているメソッドをマップファイルに記録し、デコーダーがそれを出力します。
at Acme.Backup.ZipReader.OpenZip(System.String filename)
at (System.String ) -> proxy for System.IO.Compression.ZipFile::OpenRead
at Acme.Backup.BackupReader.IsBackupFile(System.String path)-> proxy for <DeclaringType>::<Method> という注釈から、ブリッジが呼び出していたメソッドが正確にわかります。シグネチャはフレームにすでに表示されているため、注釈では繰り返されません。以前のバージョンの Babel が生成したマップファイルにはプロキシの呼び出し先が記録されていないため、従来どおりにデコードされ、代わりに汎用の -> babel generated code という注記が表示されます。
Babel が生成したフレームの非表示
自分のコードの呼び出しチェーンだけを確認したい場合、Babel は自身が生成したすべてのフレーム、つまりプロキシのブリッジと、それらが経由する匿名の実行時フレームを取り除けます。その結果、難読化していないアプリケーションで得られるものと同じトレースが残ります。
コマンドラインでは、--stacktrace オプションに frames=user キーを追加します。
babel --stacktrace StackTrace.txt --stacktrace frames=user --mapin MyApp.exe.map.xmlframes=all が既定で、すべてのフレームを残します。Babel Desktop のスタックデコーダーは、既定でこれらのフレームを非表示にします。「Babel が追加したフレームを非表示」がオンになっています。すべてのフレームを残すには、オフにします。
生成されたフレームを非表示にすると、上記のトレースは次のようになります。
at System.IO.Compression.ZipFile.Open(System.String archiveFileName)
at Acme.Backup.ZipReader.OpenZip(System.String filename)
at Acme.Backup.BackupReader.IsBackupFile(System.String path)スタックトレースの難読化解除の自動化
Babel Obfuscator には、スタックトレースのデコードを自動化するためのインターフェイスがあります。.NET アセンブリに babel.dll(従来の .NET Framework アプリケーションの場合は babel.exe)への参照を追加し、提供されている API を使って、プログラムからスタックトレースをデコードします。
次のコードスニペットは、外部ファイルから読み込んだスタックトレースの難読化を解除するコンソールアプリケーションの構成例です。
// Example of setting up the console application
static int Main(string[] args)
{
if (args.Length < 2)
{
Console.WriteLine("Usage: stackdecode.exe <filename> <xmlmapfile1> [xmlmapfile2 ...]");
return 1;
}
StackTraceDeobfuscator stk = new StackTraceDeobfuscator();
foreach (var xmlMapFile in args.Skip(1))
{
try
{
stk.AddMapFile(xmlMapFile);
}
catch (Exception ex)
{
Console.WriteLine(String.Format("Error reading XML map file '{0}':n{1}", xmlMapFile, ex.Message));
return 1;
}
}
try
{
StreamReader sr = new StreamReader(args[0]);
Console.WriteLine("Deobfuscated Stack Trace:");
Console.WriteLine(stk.DeobfuscateStack(sr));
}
catch (Exception ex)
{
Console.WriteLine(String.Format("Could not decode stack trace file '{0}':n{1}", args[0], ex.Message));
return 1;
}
return 0;
}プログラムからデコードするときに Babel が生成したフレームを取り除くには(Babel が生成したフレームの非表示を参照)、DeobfuscateStack を呼び出す前に、難読化解除オブジェクトの HideGeneratedFrames を設定します。
StackTraceDeobfuscator stk = new StackTraceDeobfuscator();
stk.HideGeneratedFrames = true;プログラムから利用できるこのインターフェイスでスタックトレースの難読化を解除すれば、デバッグの作業を無理なく強化できます。この仕組みをアプリケーションに組み込むことで、デコードの処理を自動化し、難読化されたスタックトレースを効率よく扱いながら、コードベースの整合性とセキュリティを維持できます。
難読化されたスタックトレースでの PDB ファイルの使用
必要に応じて、本番環境で PDB ファイルを使用すると、デコードされたスタックトレースでソースファイルと行番号を取得できます。Babel は PDB 内のファイル名とシンボル名を暗号化できるため、それらはスタックトレースに暗号化された状態で現れ、機密性の高いファイルパスの情報が漏れるのを防げます。
この機能を有効にするには、PDB のパスワードを設定します。安全なパスワードを選んでください。このパスワードは PDB 内のファイル名を暗号化し、後でスタックトレースの難読化を解除するときに必要になります。
- コマンドライン:
--pdbpwdオプションを使用します。たとえばbabel MyApp.exe --debug --pdbpwd <password>のように指定します。 - MSBuild:Babel タスクの
PdbPwdプロパティを設定します。 - Babel Desktop:アセンブリを選択し、プロパティパネルの「Advanced」グループで
PdbPwdを設定します。パスワードは現在のセッションの間だけ保持されます。プロジェクトには保存されないため、Babel Desktop の再起動後はもう一度入力する必要があります。
この機能を有効にして PDB のパスワードを設定すると、Babel は難読化の過程でファイル名などの関連情報を暗号化します。その結果、スタックトレースでは、ファイル名が元のファイルパスではなく暗号化された文字列として表示されます。
System.Exception: (0x80131904): A network-related or instance-specific error occurred while establishing a connection to the server.
at System.Data.SqlClient.SqlConnection.Open()
at b.a(String g)
at c.b() in <GFpQHv9iwQzX1Zmh+… >:line 21
at c.a(String h)
at Acme.ViewModel.MainViewModel.get_Message() in <GFpQHv9iwQzX1Zmh+… >:line 28 復号に必要なデータはすべて XML マップファイルに格納されるため、デコードに PDB ファイルは必要ありません。デコードされたスタックトレースには、復号されたファイル名が含まれます。
System.Exception: (0x80131904): A network-related or instance-specific error occurred while establishing a connection to the server.
at System.Data.SqlClient.SqlConnection.Open()
at Acme.Entities.DatabaseContext.ConnectToDatabase(System.String connectionString)
at Acme.ViewModel.RS.CheckResourceLoaded() in C:AcmeAcme.ViewModelRS.cs:line 21
at Acme.ViewModel.RS.GetString(System.String name)
at Acme.ViewModel.MainViewModel.get_Message() in C:AcmeAcme.ViewModelViewModelMainViewModel.cs:line 28この方法は、セキュリティと保守性のバランスが取れています。ファイル構成に含まれる知的財産を保護しながら、デバッグやエラーの解析に役立つ情報をスタックトレースから得られます。
PDB の暗号化を設定しておけば、トラブルシューティングやサポートのために PDB を共有する場合でも、アプリケーションの内部構造とファイル構成の安全性と機密性が保たれます。