シンボルのリネーム
Babel Obfuscator は、名前空間、型、メソッド、プロパティ、イベント、フィールドをリネームして、リバースエンジニアリングを困難にします。既定ではリネームに ASCII を使用しますが、Unicode を選択すると、シンボルをさらに読みにくくできます。
Babel は内部のシンボルを自動的にリネームしますが、ほかのアセンブリから参照できるシンボルはリネームしません。つまり、プライベートおよび internal のシンボルはリネームされ、パブリックおよびプロテクトのメンバーは、カスタムルールでリネームを強制しないかぎり変更されません。
パブリックシンボルをリネームすると、外部のコードがそれらのシンボルの名前に依存している場合があるため、コンポーネントのパブリックインターフェイスを壊すおそれがある点に注意してください。一般には、外部から参照できるシンボルのリネームは避け、内部の実装の詳細を難読化することに重点を置くことをお勧めします。
外部から使用することを想定していないシンボルは、internal として宣言してください。そうすれば、アセンブリの外部からは参照できなくなり、パブリックインターフェイスの一部にもなりません。
パブリックな型の可視性を internal に下げると、コンポーネントのパブリックインターフェイスを維持したまま、難読化されるメンバーの総数を増やせます。
シンボルのリネームの設定
リネームは、Babel Desktop、コマンドライン、MSBuild タスクのいずれかで設定できます。
Babel Desktop
Babel Desktop でリネームを設定するには、プロジェクトを開くか作成し、プロジェクトのキャンバスでアセンブリを選択して、プロパティパネルの「リネーム」グループでオプションを設定します。このグループには、型とメンバーのフラグ(ObfuscateTypes、ObfuscateMethods、ObfuscateProperties、ObfuscateEvents、ObfuscateFields、ObfuscateParameters)、名前空間のフラット化(FlattenNamespaces)、Unicode の名前(UnicodeNormalization)、名前の長さとプレフィックス(NameLength、NamePrefix)、オーバーロードリネーム(OverloadedRenaming)があります。Babel Desktop でのプロジェクトの仕組みについては、難読化プロジェクトを参照してください。
コマンドライン
CLI スクリプトを使用している場合、コマンドラインインターフェイス(CLI)から Babel Obfuscator のリネームを設定すると、強力かつ柔軟な方法で難読化をビルドプロセスに組み込めます。
Babel Obfuscator でリネームを設定するには、Windows のコマンドプロンプト、または macOS や Linux のターミナルで、プロジェクトのある場所に移動し、次のように入力します。
babel myapp.exe --types --methods --properties --events --fields --parametersmyapp.exe は、難読化するビルド済みアセンブリの名前に置き換えてください。その後に続く CLI スイッチは、リネームするシンボルの種類を示します。
各スイッチ(--types、--methods など)は、アセンブリ内のそのカテゴリのシンボルについてリネームを有効にします。これらのスイッチを指定すると、型、メソッド、プロパティ、イベント、フィールド、パラメーターにリネームを適用するよう Babel Obfuscator に指示することになります。特定のカテゴリをリネームから除外する必要がある場合は、シンボルの種類にプレフィックス --no を付けます。たとえば、型のリネームを無効にするには --notypes を使用します。これはどのシンボルの種類にも使用でき、難読化する要素をカスタマイズできます。
特定のリネームスイッチのコマンドラインヘルプを表示するには、次のコマンドを入力します。
babel --help types--types スイッチの詳細なヘルプ情報が表示されます。
MSBuild の Babel タスク
MSBuild の Babel タスクは、Babel Obfuscator をビルドのワークフローに統合し、難読化の設定をきめ細かく制御できるようにします。コードのどの要素を難読化するかを正確に指定して、難読化の処理を固有のニーズに合わせられます。
<PropertyGroup>
<ObfuscateTypes>true</ObfuscateTypes>
<ObfuscateMethods>true</ObfuscateMethods>
<ObfuscateProperties>true</ObfuscateProperties>
<ObfuscateEvents>true</ObfuscateEvents>
<ObfuscateFields>true</ObfuscateFields>
<ObfuscateParameters>true</ObfuscateParameters>
</PropertyGroup>
<Babel ObfuscateTypes="$(ObfuscateTypes)" ObfuscateMethods="$(ObfuscateMethods)" ObfuscateProperties="$(ObfuscateProperties)" ObfuscateEvents="$(ObfuscateEvents)" ObfuscateFields="$(ObfuscateFields)" ObfuscateParameters="$(ObfuscateParameters)" />名前のプレフィックス
Babel Obfuscator では、生成されるすべてのシンボル名に名前のプレフィックスを付けられます。プレフィックスは、型、メソッド、プロパティ、イベント、フィールド、パラメーターなど、特定の種類のシンボルに対して指定できます。プレフィックスは、--nameprefix または --prefix に続けて、使用するプレフィックス文字列を指定します。また、—prefix [kind]=[prefix] という構文で、プレフィックスを適用するシンボルの種類を指定できます。
Babel はプレフィックス「$name」を受け付けます。これは、リネーム時に元のシンボル名に置き換えられます。デバッグや、リネームが原因で発生する問題の特定に役立ちます。
コマンドライン
babel myapp.exe --prefix xy_ --prefix types=tp_ --prefix fields=m_名前のプレフィックスのコマンドラインスイッチについて詳しくは、次のように入力します。
babel --help prefixMSBuild の Babel タスク
<PropertyGroup>
<NamePrefix>xy_;types=tp_;fields=m_</NamePrefix>
</PropertyGroup>
<Babel NamePrefix="$(NamePrefix)" />名前の長さ
Babel Obfuscator では、--namelength オプションに続けて長さを指定することで、生成される難読化された名前の最小の長さを指定できます。難読化された名前を一定の長さにしたい場合に便利です。また、—namelength [kind]=[value] という構文で、名前の長さを適用するシンボルの種類を指定できます。
コマンドライン
babel myapp.exe --namelength 3 --namelength types=8名前の長さのコマンドラインスイッチについて詳しくは、次のように入力します。
babel --help namelengthMSBuild の Babel タスク
<PropertyGroup>
<NameLength>3;types=8</NameLength>
</PropertyGroup>
<Babel NameLength="$(NameLength)" />Unicode 文字セット
Babel では、難読化された名前の生成に使用する Unicode 文字セットを指定できます。16 進数、10 進数、または文字そのもので表した Unicode 文字や文字の範囲を、コンマ区切りで指定します。
コマンドライン
babel myapp.exe --unicode 10,13,a-z,0x4E00-0x4FFFUnicode リネームを無効にするには、次のように入力します。
babel myapp.exe --nounicodeMSBuild の Babel タスク
<PropertyGroup>
<UnicodeNormalization>10,13,a-z,0x4E00-0x4FFF</UnicodeNormalization>
</PropertyGroup>
<Babel UnicodeNormalization="$(UnicodeNormalization)" />名前空間のフラット化
名前空間のフラット化を有効にすると、Babel は難読化されたアセンブリからすべての名前空間情報を取り除き、リネームされたすべての型をグローバル名前空間に移動します。これにより、攻撃者がコードの構成を理解したり、アセンブリ内の特定の型やメンバーを見つけたりすることが難しくなります。
必要に応じて、リネームされたすべての型をまとめる入れ物として Babel が使用する名前空間の名前を指定できます。
Babel では、名前空間のフラット化の設定に従って名前空間名が更新されるのは、リネームされた型だけである点に注意してください。
コマンドライン
babel myapp.exe --flatns
babel myapp.exe --flatns glbns名前空間のフラット化を無効にするには、次のように入力します。
babel myapp.exe --noflatnsMSBuild の Babel タスク
<PropertyGroup>
<FlattenNamespaces>true</FlattenNamespaces>
</PropertyGroup>
<Babel FlattenNamespaces="$(FlattenNamespaces)" />オーバーロードリネーム
オーバーロードリネームは、.NET Framework の設計規則が許す範囲で、シグネチャの異なるメソッドに同じ名前を付ける Babel の手法です。これにより、攻撃者がコードを理解してリバースエンジニアリングすることが難しくなります。さらに Babel は、メソッドの戻り値の型だけが異なる場合でもオーバーロードとしてリネームします。C# や VB.NET などの高級言語では戻り値の型によるオーバーロードが許されていないため、コードをこれらの言語に完全にデコンパイルすることはできなくなります。
コマンドライン
babel myapp.exe --overloadedオーバーロードリネームを無効にするには、次のように入力します。
babel myapp.exe --nooverloadedMSBuild の Babel タスク
<PropertyGroup>
<OverloadedRenaming>true</OverloadedRenaming>
</PropertyGroup>
<Babel OverloadedRenaming="$(OverloadedRenaming)" />仮想関数
Babel は、仮想関数としての性質を保ったままリネームすることで、仮想関数を難読化できます。つまり、難読化の後も、元の仮想関数テーブル(vtable)のレイアウトが維持されます。仮想関数は多くのオブジェクト指向プログラムで重要な役割を果たすため、プログラムが引き続き正しく動作するうえで、これは重要です。仮想関数を難読化することで、攻撃者がプログラムの動作を理解したり、脆弱性を悪用したりすることが難しくなります。
コマンドライン
babel myapp.exe --virtuals仮想関数の難読化を無効にするには、次のように入力します。
babel myapp.exe --novirtualsMSBuild の Babel タスク
<PropertyGroup>
<VirtualFunctions>true</VirtualFunctions>
</PropertyGroup>
<Babel VirtualFunctions="$(VirtualFunctions)" />ローカル変数のリネーム
.NET のメタデータには、ローカル変数名は明示的には格納されません。代わりに、デコンパイラーがさまざまな要素から推測します。たとえば、次のように変数の型が明示的に指定されている場合です。
var list1 = new List<string>();この場合、デコンパイラーは代入文から変数名を「list1」と判断できます。
また、ローカル変数名は、アセンブリのプログラムデータベース(PDB)ファイルから読み込むこともできます。PDB ファイルには変数名などの追加のデバッグ情報が含まれており、デコンパイラーはそれを利用して元の名前を復元できます。
ローカル変数名の露出を抑えるために、Babel Obfuscator には、外部の型に対する動的プロキシの生成を有効にするオプションがあります。
babel myapp.exe --proxy externalこの機能は、List<string> などの元の型を、リネームされるプロキシ型でラップし、変数の型を実質的に隠します。
デコンパイラーが PDB ファイルを読み込むと元の変数名を復元できてしまうため、PDB ファイルが読み込まれないようにすることが不可欠です。PDB の読み込みを無効にするか、PDB ファイルを削除すれば、デコンパイラーが利用できるデバッグ情報は限られます。
メソッドのコードを包括的に保護するには、可能なかぎりコード暗号化を有効にすることをお勧めします。コード暗号化はメソッドのコードを暗号化された形式に変換するため、たとえ元の変数名を取得できたとしても、リバースエンジニアリングを試みる者がコードを理解したり変更したりすることは格段に難しくなります。