ドキュメント
/
レシピ
/

VM 防御のテレメトリと反応

VM 防御のテレメトリと反応

Pro
v7.4.0+

vmDefenseHook で VM 防御の検知をバックエンドに報告し、vmDefenseReaction で検知カテゴリーごとの反応を調整します。まったく壊れないテレメトリ専用のビルドから、盗まれたバンドルを即座に壊すビルドまで構成できます。

視聴する

Obfuscator.io Defense Reactions: Break, Decoy, and the VM Defense Hook

YouTube で視聴

問題

VM 防御(vmSelfDefending、vmDebugProtection、vmDomainLock)はローカルで動作します。デバッガー、自動化ツール、改ざんされた環境、または許可されていないドメインが検知されると、保護されたコードは壊れるか、密かに自身の結果を汚染します。これは攻撃者を止めますが、デフォルトではあなたはそれを一切知ることができません。バンドルがどのくらいの頻度で調べられているか、どの検知機能が発火したか、あるいは防御が正規のユーザーを壊していないかを把握することはできません。

2 つのオプションがそのギャップを埋めます。どちらも防御を有効化するものではなく、すでに有効にしている防御を観測し、方向付けるだけです。

  • vmDefenseHook:防御が何かを検知するたびにシグナルオブジェクトを受け取るグローバルコールバックです。バックエンドにテレメトリを送信するために使用します。
  • vmDefenseReaction:有効な防御がどのように反応するか(壊す、decoy、またはローカルでは何もしない)を選択する、カテゴリーごとのマップです。

どちらのオプションも v7.1.0 で導入されましたが、ここでの例はすべてオブジェクト形式の vmDefenseHook: { name } を使っており、これには v7.4.0 が必要です。それより前のバージョンは素の文字列(vmDefenseHook: '__vmDetection')を受け付けていましたが、この形式は v8.0.0 以降は拒否されるため、常にオブジェクト形式を使ってください。

レシピ 1:検知をバックエンドに報告する

ステップ 1:難読化されたバンドルが読み込まれる前に、グローバルフック関数を登録する

VM ランタイムとその防御は、保護されたプログラムよりも前に動作するため、多くの検知は起動時に発火します。フックは、難読化されたスクリプトタグよりも前に、ホストページ内の素のグローバルとして定義してください。

HTML

ステップ 2:vmDefenseHook をそれに向ける

このオプションは、name に呼び出すグローバル関数を指定するオブジェクトです(aliases は任意です。下記参照)。

JavaScript

ダッシュボードでは、少なくとも 1 つの防御(vmSelfDefending、vmDebugProtection、または vmDomainLock)が有効になると、高度な保護 セクションに VM Defense Hook フィールドが表示されます。

ステップ 3:バックエンドでシグナルを受け取る

すべての検知は、単一の signal オブジェクトでフックを呼び出します。

  • source:具体的な検知機能です。headless、node、agent、agentBrowser、domain、debugger、sandbox、nativeHook、timing、または integrity。v7.4.0 以降、以前の env および inspector 検知機能は source: 'debugger' として報告されます。agentBrowser は category: 'automation' として報告され、vmDebugProtection を有効にしたブラウザーターゲットで実行されます(v7.9.0 以降)。
  • category:automation、debugger、sandbox、domain、tamper、または integrity。node ソースは category: 'debugger' として報告されます(v7.4.0 以降)。
  • score、threshold:検知スコアと、それが超えたしきい値

最小限の受信エンドポイントの例です(Express を示しますが、POST を受け付けるバックエンドであれば何でも動作します)。ボディを配列に正規化するため、下記のバッファパターンが送信するバッチ形式も処理できます。

JavaScript

フックは報告専用です。その戻り値は無視され、存在しないフックや例外を投げるフックは、何もしない no-op になります。防御を無効化することは決してできないため、攻撃者があなたのフックを削除したり壊したりしても、何も得られません。防御の動作を変えるには、vmDefenseReaction を使用します(レシピ 2)。

フックは難読化されたソースの内部ではなく、ホストページで定義する

テレメトリではすべての検知を捕捉したいものですが、その多くは起動時に発火します。難読化されたバンドルの内部で定義されたフックは、それらを捕捉するには登録が遅すぎ、VM コンパイルされた場合はプログラムが実行されるまで到達できません。どちらの場合も安全は保たれます(存在しないフックは no-op になり、それ自体が検知を引き起こしたフックが再帰的に再度呼び出されることはありません)が、完全なカバレッジのためには、ホストページの先頭で登録してください。

唯一の例外は、実行時の検知のみに反応するフック(使用中にデバッガーが開いたときのクリーンアップなど)です。そのフックは難読化されたバンドル内に置くことができます。レシピ 3 を参照してください。

それでも報告ロジックを保護したい場合は、登録するフックを 1 行のバッファにとどめ、難読化されたコードからそれを排出してください。

JavaScript

JavaScript

シグナルフィールドのリネーム(aliases)

デフォルトの source / category の値は説明的な名前であるため、コールバックを計測している人(または出力を読む人)は、それがどの保護であり、どの検知機能が発火したかを認識できます。aliases はシグナルフィールドを任意の不透明なトークンにリネームします。これはシグナルが発行される前に VM 内部で適用されるため、それらの名前が出力に現れることも、コールバックに到達することもありません。あなたのアプリは自身のマッピングを把握しており、トークンをバックエンドに転送します。

エイリアスはフィールドごとに設定し、それぞれが key(コールバックが受け取るプロパティ名)を取ります。文字列の名前フィールドである source と category は values マップも取りますが、score / threshold は数値であり key のみを取ります。設定されていないエントリはデフォルトの名前を保持します。

JavaScript

ダッシュボードでは、シグナルのエイリアス セクションが VM Defense Hook フィールドの下にあります。

これは秘匿性ではなく、フィンガープリントの回避です。マッピングは繰り返しのテストによって推測される可能性があるため、その唯一の利点は、安定した自己説明的な名前を露出させないことです。

レシピ 2:デフォルトの反応を調整する

vmDefenseReaction は、各検知カテゴリーがどのように反応するかを設定します。これは何かを有効化するものではありません。防御そのものは vmSelfDefending、vmDebugProtection、vmDomainLock によって有効になり、このオプションは有効な防御がどのように反応するかを選択するだけです。カテゴリーが制御の単位です。カテゴリー内のすべての検知機能はそのカテゴリーの反応を実行し、オプションが無効なカテゴリーに設定された反応は、単に効果を持ちません。

カテゴリー有効化するオプション反応する条件
automationvmSelfDefending または vmDebugProtectionコードが人間ではなくソフトウェアによって操作されている(ヘッドレスまたは自動化されたブラウザー、スクレイピング / テストフレームワーク、またはページをステップ実行する AI コーディングエージェント)。
debuggervmDebugProtection または vmSelfDefending誰かがデバッガー、またはブラウザーの開発者ツールのインスペクターを開き、実行中のコードを理解するためにステップ実行している。
sandboxvmDebugProtectionコードが実際のブラウザーでまったく実行されておらず、オフラインで実行・解析するために、エミュレートされた、またはスクリプト化された JavaScript 環境に持ち込まれている。
domainvmDomainLock許可していないサイトでコードが実行されている(vmDomainLock の許可リストに含まれていないホスト。例えば、他人のドメインにコピーされたバンドル)。
tampervmSelfDefendingVM を監視または乗っ取るために、VM を取り巻く JavaScript 環境が改変されている。例えば、ネイティブのブラウザー組み込み機能が計測用のバージョンに置き換えられているなど。
integrityvmSelfDefending保護されたバンドル自身のコードが、生成後に編集またはパッチされている。

キーはこれら 6 つのカテゴリー名、または default(指定されていないカテゴリーのフォールバック)です。値は次のとおりです。

  • break:即座に壊します
  • decoy:汚染された状態で実行を続け、密かに誤った結果を生成します。decoy には、ブラウザーターゲットで vmDebugProtection または vmDomainLock が有効になっている必要があります。そうでない場合は break として動作します。
  • none:ローカルでは何もしません(テレメトリのみ)

設定しなかったカテゴリーは、組み込みのデフォルトにフォールバックします。

JavaScript

default は、integrity と tamper を含むすべてのカテゴリーに及びます。そのため { default: 'none' } は、本当に何も壊さない、テレメトリ専用のビルドになります。

JavaScript

JavaScript

ダッシュボードでは、防御が有効になると、高度な保護 セクションに VM Defense Reactions のセレクトが表示されます。各カテゴリーは、その検知機能を持つ防御が有効な間だけ編集可能です。

レシピ 3:防御が壊れる前に独自のロジックを実行する

フックは報告のためだけのものではありません。防御が反応する前に独自の処理を実行できる、唯一信頼できる場所でもあります。実行中のページでデバッガーが開いたとき、コードが壊れる前に、画面に表示されているものをクリアしたり、ビューを 404 ページに差し替えたりしたい場合があります。

なぜアプリ内の他の場所のコードではなくフックなのでしょうか。break は後続のすべてのバイトコードを停止するため、防御が発火した後に実行されるティアダウン(特にそれ自体が VM 難読化されている場合)は、まさに break によって実行を妨げられます。フックは検知箇所で、反応が実行される前に同期的に発火します。そのため、フックが呼び出す同期関数が最初に完了し、その後 break が VM を停止します。

この処理を vmDefenseHook として定義してください。debugger の検知は、プログラムが読み込まれてフックを定義した後の実行時に発火するため、フックは難読化されたソースの一部にでき、バンドルの残りの部分と一緒にバイトコード化されます。signal.category で分岐して各条件に適切な処理を行い、処理を同期的に保ち、その後反応を実行させてください。

JavaScript

JavaScript

これはアプリの実行中に発火する検知に適用されます。下記のフックのバイトコード化が機能する場合を参照してください。

次の点に留意してください。

  • 最初に完了することが保証されるのは、同期処理だけです。 反応は、フックが return した直後の文で実行されます。すぐに引き渡す fire-and-forget な呼び出しは問題ありません(navigator.sendBeacon、同期的な DOM や canvas の編集)。後で実行するようにスケジュールする処理(setTimeout、Promise の継続、await)はそうではなく、さらに VM バイトコードを必要とするものは実行されません。それこそが break が停止するものだからです。
  • フックは反応の前に実行されますが、反応を置き換えるものではありません。 その戻り値は無視され、反応の動作をキャンセル、遅延、変更することはできません。break を拒否するためではなく、break の前に動作するために使用してください。反応そのものを変えるには、vmDefenseReaction を使用します(レシピ 2)。

フックのバイトコード化が機能する場合

このようにフックを難読化されたバンドル内に置くことが機能するのは、debugger の検知が実行時に発火するからにほかなりません。VM は、プログラムが読み込まれてフックを定義した後、VM がまだ生きている間に vmDefenseHook を発火させます。そのため、バイトコード化されたフックが最初にデコードされて実行され、その後 break になります。これがフック自身のソースを保護するものです。

これは、起動時に発火する検知(automation、sandbox、domain、またはページ読み込み時にすでに開いているデバッガー)に対しては機能しません。その時点ではバイトコード化されたフックがまだ定義されていないため、防御は呼び出す関数を見つけられないからです。それらについては、代わりにレシピ 1 のように、フックをホストページ内の素のグローバルとして登録してください。迷ったら、ホストページの素のグローバルを使えば、フックに到達するすべての検知をカバーできます。バイトコード化は、フック自身のソースに対する保護を追加するだけであり、それも実行時の検知に対してのみです。

テレメトリから強制へ

可視性と強制を同時に出荷する必要はありません。防御は 2 段階で展開してください。まず報告のみを行うビルドを出荷し、テレメトリがクリーンに見えたら反応するビルドを出荷します。

ステップ 1:観測専用のビルドを出荷する

使用する予定のすべての防御を有効にし、vmDefenseHook をエンドポイントに向け、すべての反応をオフにします。すべての検知機能は依然として動作し、各ヒットをバックエンドに報告しますが、何も壊れません。

JavaScript

ステップ 2:収集したシグナルを確認する

ビルドが実際のトラフィックを経験したら、正規の使用が引き起こした検知を探してください。最もよくあるのは次の 2 つです。

  • 自分自身のエンドツーエンドテストや稼働監視からの automation ヒット:本番環境でそのカテゴリーを許容する代わりに、それらの成果物を防御なしでビルドしてください。
  • vmDomainLock の許可リストに含め忘れたステージングやプレビューのホストからの domain ヒット:そのホストを追加してください。

反応を緩めるよりも、原因を修正することを優先してください。none のままにされた各カテゴリーは、攻撃者が安全に無視できる検知機能です。

ステップ 3:反応を有効にする

default: 'none' の上書きを削除すると、組み込みのカテゴリーごとの反応が適用されます。変更はその 1 行だけです。あるカテゴリーが排除できない誤検知を出し続ける場合は、そのカテゴリーだけを none のままにし(例:vmDefenseReaction: { automation: 'none' })、残りを強制してください。

強制を有効にした後も vmDefenseHook は設定したままにしてください。フックは反応に関わらず発火するため、防御が動作している間も、誰があなたのバンドルを調べているかを引き続き把握できます。