チェックとは、アプリケーションの不正使用を検出して対応する、実行時の検証です。たとえば、デバッグ チェックは、デバッガーを起動した状態でアプリケーションが実行されているかどうかを検出します。
Dotfuscator では、コードの差し込みによってアプリケーションにチェックを追加できます。ユーザーがコードを記述する必要はありません。Dotfuscator は適切な検証ロジックを自動的に差し込み、チェックが不正な使用を検出したタイミングに事前に作成したレスポンスを差し込むこともできます。また、検証結果をアプリケーションに通知するように、チェックを構成することもできます。
難読化が、「静的解析」(つまり、アセンブリ ファイルの解析)からアプリケーションを保護するのに対し、チェックは、実行されている間の「動的解析」からアプリケーションを保護します。難読化とチェックを一緒に使用することで、アプリケーション コードとビジネス データの両方を攻撃から防御する、アプリケーション保護の階層化アプローチが提供されます。
チェックの構成
Dotfuscator でチェックを差し込むには、まずチェックを有効にします。
必要であれば、チェック属性を使ってソース コードにアノテーションを付けることにより、チェックを指定することもできます。これらのどちらの方法でも、チェックの動作を決めるさまざまなプロパティを指定できます。詳細な一覧は、チェック属性のページを参照してください。
Dotfuscator でアセンブリを処理した後、結果として生じるアプリケーションは、指定されたメソッドが実行時に呼び出されると自動的にチェックを実行します。
チェックの種類
どのチェックも特定の種類に属しています。チェックの種類ごとに異なる検証のセットを実行するので、構成するプロパティは多少異なります。1 つのアプリケーションで同じ種類のチェックを複数個持つことができますが、それらチェックの対象となる場所は異なっていなければなりません。
以下のチェックの種類があります。
- 改ざんチェック - アプリケーション コードが変更されているかどうかを検出します。
- デバッグ チェック - アプリケーションにデバッガーがアタッチされていないかどうかを検出します。
- Shelf Life チェック - アプリケーションが有効期限後も実行されていないかどうかを検出します。
- ルート チェック - アプリケーションがルート化された Android デバイスで実行されているかどうかを検出します
各チェックの種類の詳細については、リンクが設定されている上記の対応ページを参照してください。このページの残り部分では、複数の種類のチェックに該当する要素について説明します。
場所
各チェックは、アプリケーション コード内の 1 つまたは複数のメソッドを対象とします。これらのメソッドは、チェックの場所と呼ばれます。
実行時に場所が呼び出されるたびに、関連するチェックが実行され、その瞬間のアプリケーションの状態をチェックの種類ごとに検証し、チェックのプロパティに従って対応します。
1 つの場所に、異なる種類の複数のチェックを関連付けることができます。そのような場所が呼び出された場合は、関連付けられているチェックのそれぞれが順番に実行されます。
1 つのチェックで複数の場所を対象とすることができます。そのように構成した場合は、対象の場所のいずれかが呼び出されるたびに、そのチェックが実行されます。
チェックは実行後、別の場所が呼び出されるまで再度実行されることはありません。たとえば、デバッグ チェックが 1 つの場所、LoadFile(String path) を対象とするとします。この場所は、ユーザーがアプリケーションでファイルを読み込むたびに呼び出されます。攻撃者が当該のアプリケーションを実行し、ファイルを読み込んでデバッガーをアタッチした場合、そのデバッガーは、攻撃者が別のファイルを再度読み込むまで検出されません。
プロパティ
各チェックは、不正な状態にどのように対応するかなど、チェックの動作を決定するさまざまなプロパティを使用して構成できます。使用可能なプロパティはチェックの種類によって異なるため、詳細な一覧については、チェック属性ページを参照してください。
同じ種類でも、チェックが異なれば、異なるプロパティ値を持つことができます。たとえば、注目する 3 つのメソッド、UserLogin()、AdminLogin()、SupportLogin() を持つアプリケーションについて考えてみましょう。通常のユーザーおよびローカルの管理者がソフトウェアにデバッガーをアタッチすることは禁止しますが、場合によっては、サポート担当者が当該ソフトウェアをデバッガーで実行してもよいとします。以下の 2 つのデバッグ チェックを構成できます。
- 1 つ目のデバッグ チェックは UserLogin() と AdminLogin() を対象とします。チェックの Action プロパティを Exit に設定し、ユーザーまたは管理者がデバッガーを起動した状態でアプリケーションを実行したら、即座にアプリケーションを終了するようにします。
- 2 つ目のデバッグ チェックは SupportLogin() を対象とします。チェックの Action プロパティを None に設定し、サポート メンバーがデバッガーを起動した状態でアプリケーションを実行した場合には、実行を続けられるようにします(アプリケーション通知を構成して遠隔測定を送信することで、サポート担当者でないユーザーがサポート アカウントを使用していないかどうかを監視することもできます)。
アプリケーション通知
チェックでは、その結果をアプリケーション コードに通知できます。これにより、アプリケーションは不正な状態に対し、アプリケーションの機能を無効にしたり、サード パーティ製の遠隔測定を送信したりするなど、カスタマイズされた方法で対応することができます。
この通知は、指定されたチェック操作の前に行われます。
以下のチェックのプロパティは、アプリケーションに通知する方法を設定するものです。
- ApplicationNotificationSinkElement
- ApplicationNotificationSinkName
- ApplicationNotificationSinkOwner
これらのプロパティは、ブール値を受け取る、アプリケーション コード内のシンクを指定します。不正な状態が検出された場合は true、検出されなかった場合は false を受け取ります。つまり、シンクを指定した場合は、不正な状態が検出されない結果であっても、シンクは常にチェックによって呼び出されます。シンクの指定方法の詳細については、チェック属性ページの関連プロパティを参照してください。
例として、次の ApplicationNotificationExample クラスの改ざんチェックとサンプル コードを検討してみましょう。
<sos />
<extattributes>
</extattribute>
<extattribute name="PreEmptive.Attributes.TamperCheckAttribute">
<type name="ChecksExample.ApplicationNotificationExample">
<method name="TamperCheckLocation" signature="void()" />
</type>
<propertylist>
<property name="Action" value="None" />
<property name="ActionProbability" value="1" />
<property name="ApplicationNotificationSinkName" value="TamperCheckSink" />
<property name="ApplicationNotificationSinkOwner" value="" />
<property name="ApplicationNotificationSinkElement" value="Method" />
</propertylist>
</extattribute>internal class ApplicationNotificationExample
{
private bool? myFlag;
public void Run()
{
Console.WriteLine("Application logic...");
TamperCheckLocation();
Console.WriteLine("More Application logic...");
Console.WriteLine($"The CheckResult was '{myFlag}'.");
}
private void TamperCheckLocation()
{
Console.WriteLine("This method is a location of a Tamper Check, which will run
before this text.");
}
private void TamperCheckSink(bool tamperingDetected)
{
Console.WriteLine("This method is a sink for the Tamper Check.");
if (tamperingDetected)
{
Console.WriteLine("This application has been tampered!");
}
else
{
Console.WriteLine("This application has not been tampered.");
}
myFlag = tamperingDetected;
}
}Dotfuscator がチェック コードを差し込んだ後、別のアプリケーション コードによって Run() が呼び出されると、以下の処理が行われます。
- Run() がコンソールに書き込みを行い、TamperCheckLocation() を呼び出します。
- TamperCheckLocation() が実行される前に、改ざんチェックは以下のことを行います。
- Dotfuscator によってアプリケーションが処理された以降に、アプリケーションが変更されたかどうかを判断します。
- そのアプリケーション通知として TamperCheckSink(bool) メソッドを呼び出し、改ざんが検出されていれば true を、検出されていなければ false を引数として渡します。
- TamperCheckSink(bool) は、この改ざんチェックに関する情報をコンソールに書き込み、myFlag フィールドをチェックの結果に設定して後で使用できるようにし、チェックに制御を返します。
- チェックの実行を終了し、TamperCheckLocation() メソッドに制御を返します。
- TamperCheckLocation() が実行され、Run() に制御を返します。
- Run() は、myFlag フィールドの使用状況を含む追加情報をコンソールに書き込みます。
- Run() が呼び出し元に制御を返します。
チェック操作
チェックは、アプリケーションを終了する方法を用いて、チェック自体で不正なアプリケーションの状態に対応することができます。Dotfuscator Professional では、例外をスローするなどの追加対応も使用可能です。このような対応は、チェック操作と呼ばれます。
チェック操作は、チェックのアプリケーション通知動作の後に行われます。
各チェックは、1 つのチェック操作のみを持つことができます。複雑な動作を定義するには、アプリケーション通知を使用してアプリケーションにその動作を実行させます。
チェックで使用されるチェック操作の種類は、チェックの Action プロパティによって決定されます。
Dotfuscator Community で使用可能なチェック操作は、次のとおりです。
- None:チェックは、不正なアプリケーションの状態を検出した場合でも、実行後にアプリケーションに制御を返します。
- Exit:チェックが不正なアプリケーションの状態を検出した場合、アプリケーションは終了コード 0 で直ちに終了します。
追加のチェック操作(実行しているスレッドをハングさせる、ランダムな例外をスローするなど)は、Dotfuscator Professional で使用可能です。
操作の確率
Dotfuscator Professional では、チェック操作をランダムに発生させるように構成することができます。これにより、アプリケーションの動作が攻撃者にとっていら立たしく予測し難いものとなります。ただ、たまにアプリケーションが「誤動作」するだけで、それ以外のときは正常に動作します。
チェックが実行されるときにチェック操作が発生する確率は、チェックの ActionProbability プロパティによって決定されます。Dotfuscator Community では、このプロパティは常に 1.00 です。つまり、チェックが実行されるときに必ずチェック操作が発生するということです。