anti-debugging-techniques
デバッガー対策の検出と回避に関するプレイブック。Linux・Windows上でptrace、PEBフラグ、タイミングチェック、シグナル/例外ハンドラを利用してデバッガーを検知する保護済みバイナリをリバースエンジニアリングする際に使用します。
description の原文を見る
>- Anti-debugging detection and bypass playbook. Use when reversing protected binaries that detect debuggers via ptrace, PEB flags, timing checks, or signal/exception handlers on Linux and Windows.
SKILL.md 本文
SKILL: アンチ デバッギング テクニック — 検出と回避プレイブック
AI LOAD INSTRUCTION: Linux および Windows 全体のエキスパート レベルのアンチ デバッグ テクニック。ptrace、PEB フラグ、NtQueryInformationProcess、タイミング攻撃、シグナルベースの検出、TLS コールバック、VEH トリック、および対応するすべての回避方法をカバーします。基本モデルは、ユーザー モード検出とカーネル モード検出の区別、および各検出方法の正しいパッチ戦略を見落とすことがよくあります。
0. 関連ルーティング
code-obfuscation-deobfuscationバイナリが制御フロー フラット化、VM 保護、または文字列暗号化も使用している場合vm-and-bytecode-reverseアンチ デバッグがカスタム VM ディスパッチャー内にある場合symbolic-execution-toolsアンチ デバッグ チェック全体を記号的にスキップしたい場合
高度なリファレンス
以下が必要な場合は ANTI_DEBUG_MATRIX.md も読み込んでください:
- テクニック × OS × 検出方法 × 回避方法の完全な相互参照マトリックス
- テクニックごとの信頼性評価と誤検知ノート
- ツール互換性チャート(GDB、x64dbg、WinDbg、Frida、ScyllaHide)
クイック回避選択肢
| 検出クラス | 第一回避方法 | バックアップ |
|---|---|---|
| ptrace ベース (Linux) | LD_PRELOAD hook ptrace() → return 0 | トレーサーを隠すカーネル モジュール |
| PEB.BeingDebugged (Windows) | PEB バイトを fs:[0x30]+0x2 でパッチ | ScyllaHide 自動パッチ |
| タイミング チェック (rdtsc) | rdtsc 後に条件ブレークポイント設定、レジスタ修正 | Frida hook rdtsc return |
| IsDebuggerPresent | 呼び出しを NOP / return 0 にフック | x64dbg ビルトイン隠蔽 |
| INT 2D / UD2 例外 | VEH で適切に処理するよう設定 | TitanHide ドライバー |
1. LINUX アンチ デバッギング テクニック
1.1 ptrace(PTRACE_TRACEME)
典型的な自己アタッチ:プロセスが ptrace(PTRACE_TRACEME, 0, 0, 0) を呼び出します。デバッガーが既にアタッチされている場合、呼び出しは失敗します(-1 を返す)。
if (ptrace(PTRACE_TRACEME, 0, 0, 0) == -1) {
exit(1); // デバッガー検出
}
回避方法:
| 方法 | 方法 |
|---|---|
LD_PRELOAD shim | 共有ライブラリをコンパイル:long ptrace(int r, ...) { return 0; } 並びに LD_PRELOAD を設定 |
| バイナリ パッチ | ptrace 呼び出しを NOP するか、戻り値チェックをパッチ |
| GDB catch | catch syscall ptrace → リターン時に $rax を 0 に修正 |
| カーネル モジュール | sys_ptrace をフック して複数のトレーサーを許可 |
1.2 /proc/self/status — TracerPid
FILE *f = fopen("/proc/self/status", "r");
// TracerPid をパース:ゼロ以外の場合 → デバッガーがアタッチされている
回避:/proc/self 上に FUSE ファイルシステムをマウント、または LD_PRELOAD で fopen/fread をフック して TracerPid を 0 にフィルター。
1.3 タイミング チェック (rdtsc / clock_gettime)
2 つのポイント間の経過時間を測定します。デバッガーのシングル ステップはかなりの遅延を引き起こします。
rdtsc
mov ebx, eax ; 下位 32 ビットを保存
; ... 保護されたコード ...
rdtsc
sub eax, ebx
cmp eax, 0x1000 ; 閾値
ja debugger_detected
回避:2 番目の rdtsc 後にハードウェア ブレークポイントを設定し、eax を修正して比較をパス。または Frida を使用してタイミング関数を置き換え。
1.4 シグナルベースの検出 (SIGTRAP)
volatile int caught = 0;
void handler(int sig) { caught = 1; }
signal(SIGTRAP, handler);
raise(SIGTRAP);
if (!caught) exit(1); // デバッガーがシグナルを消費
デバッガーが アタッチされている場合、SIGTRAP はハンドラーに配信されるのではなく、デバッガーによって消費されます。回避:GDB で handle SIGTRAP nostop pass を使用してシグナルを転送。
1.5 /proc/self/maps & LD_PRELOAD 検出
注入されたライブラリまたはデバッガー/インストルメンテーション特性のメモリ領域をチェック。
FILE *f = fopen("/proc/self/maps", "r");
while (fgets(buf, sizeof(buf), f)) {
if (strstr(buf, "frida") || strstr(buf, "LD_PRELOAD"))
exit(1);
}
回避:fopen("/proc/self/maps") をフック してフィルター版を返す、または Frida のエージェント ライブラリをリネーム。
1.6 環境変数チェック
保護によっては LD_PRELOAD、LINES、COLUMNS(GDB のターミナルで設定)、またはデバッガー固有の環境変数をチェック。
回避:起動前に疑わしい環境変数を削除、または getenv() をフック。
2. WINDOWS アンチ デバッギング テクニック
2.1 IsDebuggerPresent / CheckRemoteDebuggerPresent
if (IsDebuggerPresent()) ExitProcess(1);
BOOL debugged = FALSE;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &debugged);
if (debugged) ExitProcess(1);
回避:kernel32!IsDebuggerPresent をフック して 0 を返す、または PEB を直接パッチ。
2.2 PEB フラグ
| フィールド | オフセット (x64) | デバッグ値 | 通常値 |
|---|---|---|---|
BeingDebugged | PEB+0x02 | 1 | 0 |
NtGlobalFlag | PEB+0xBC | 0x70 (FLG_HEAP_*) | 0 |
ProcessHeap.Flags | Heap+0x40 | 0x40000062 | 0x00000002 |
ProcessHeap.ForceFlags | Heap+0x44 | 0x40000060 | 0 |
mov rax, gs:[0x60] ; PEB
movzx eax, byte [rax+0x02] ; BeingDebugged
test eax, eax
jnz debugger_detected
回避:4 つのフィールドすべてをゼロに設定。ScyllaHide はこれを自動的に行います。
2.3 NtQueryInformationProcess
| InfoClass | 値 | デバッグ時の戻り値 |
|---|---|---|
ProcessDebugPort | 0x07 | ゼロ以外のポート |
ProcessDebugObjectHandle | 0x1E | 有効なハンドル |
ProcessDebugFlags | 0x1F | 0 (反転!) |
回避:ntdll!NtQueryInformationProcess をフック して情報クラスごとにクリーン値を返す。
2.4 ハードウェア ブレークポイント検出
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext(GetCurrentThread(), &ctx);
if (ctx.Dr0 || ctx.Dr1 || ctx.Dr2 || ctx.Dr3)
ExitProcess(1);
回避:GetThreadContext をフック して DR0–DR3 をゼロに設定、または NtSetInformationThread(ThreadHideFromDebugger) を事前に使用(皮肉にも、アンチ デバッグ テクニック自体)。
2.5 INT 2D / INT 3 / UD2 例外トリック
INT 2D はカーネル デバッグ サービス割り込みです。デバッガーなしでは STATUS_BREAKPOINT を発生させます。デバッガーあるでは動作が異なります(バイト スキップ)。
xor eax, eax
int 2dh
nop ; デバッガーはこのバイトをスキップする可能性
; ... 異なる実行パス ...
回避:VEH で処理またはインターラプト命令をパッチ。
2.6 TLS コールバック
TLS コールバックは main() / WinMain() の前に実行されます。ここに配置されたアンチ デバッグ チェックは、デバッガーの初期ブレークの前に実行されます。
回避:x64dbg で「Break on TLS Callbacks」オプションを設定。WinDbg では sxe ld を使用してモジュール ロード時にブレーク。
2.7 NtSetInformationThread(ThreadHideFromDebugger)
NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);
この呼び出し後、スレッドはデバッガーから見えなくなります — ブレークポイントとシングル ステップが無音で停止します。
回避:NtSetInformationThread をフック して ThreadInfoClass == 0x11 の場合に NOP。
2.8 VEH ベースの検出
デバッガー固有の動作(シングル ステップ フラグ、デバッガー セマンティクスを持つ保護ページ違反)をチェックするベクター例外ハンドラーを登録します。
回避:VEH ロジックを理解し、例外チェーンがデバッグなし実行と同じに動作することを確認。
3. 高度なマルチレイヤー テクニック
3.1 自己デバッギング (fork + ptrace)
プロセスが子を fork し、ptrace を介して親にアタッチします。外部デバッガーが既にアタッチされている場合、子の ptrace は失敗します。
pid_t child = fork();
if (child == 0) {
if (ptrace(PTRACE_ATTACH, getppid(), 0, 0) == -1)
kill(getppid(), SIGKILL);
else
ptrace(PTRACE_DETACH, getppid(), 0, 0);
_exit(0);
}
wait(NULL);
回避:fork() の戻り値をパッチ、またはウォッチドッグ子を kill/detach。
3.2 マルチプロセス デバッグ検出
親と子が協働して互いのデバッグ状態をチェック し、相互ウォッチ パターンを作成。
回避:両プロセスにアタッチ(GDB follow-fork-mode、または 2 つのデバッガー インスタンス)。
3.3 複数チェックポイント付きタイミングベース
タイミング チェックを複数関数に分散し、累積ドリフトを比較。単一パッチは合計が閾値を超えるため失敗。
回避:すべてのタイミング ソース(rdtsc、clock_gettime、QueryPerformanceCounter)を Frida Interceptor.replace で置き換えて制御値を返す。
3.4 Nanomite / INT3 パッチ
元の条件分岐は INT3(0xCC)で置き換えられます。親デバッガー プロセスは各 INT3 を処理し、条件を評価し、子の EIP を設定します。
回避:すべての INT3 ハンドラーをトレース してオリジナル ジャンプ テーブルを再構成し、バイナリをパッチ。
4. 対策ツール
| ツール | プラットフォーム | 機能 |
|---|---|---|
| ScyllaHide | Windows (x64dbg/IDA/OllyDbg) | PEB 自動パッチ、NtQuery* フック、スレッド隠蔽、タイミング修正 |
| TitanHide | Windows (カーネル ドライバー) | すべてのユーザー モード チェックに対するカーネル レベル隠蔽 |
| Frida | クロスプラットフォーム | スクリプト ベースの任意関数フック、タイミング スプーフィング |
| LD_PRELOAD shims | Linux | ロード時に ptrace、getenv、fopen を置き換え |
| GDB scripts | Linux | catch syscall、条件ブレークポイント、レジスタ修正 |
| Qiling | クロスプラットフォーム | フル システム エミュレーション、すべてのハードウェア チェック回避 |
5. 体系的な回避方法
ステップ 1:静的解析 — アンチ デバッグ呼び出しを特定
└─ 検索対象:ptrace、IsDebuggerPresent、NtQuery、rdtsc、
GetTickCount、SIGTRAP、INT 2D、TLS ディレクトリ エントリ
ステップ 2:各チェックを分類
├─ API ベース → フックまたはパッチ呼び出し
├─ フラグ ベース → PEB/proc フィールドをパッチ
├─ タイミング ベース → タイム ソースをスプーフ
├─ 例外 ベース → 例外を転送/正しく処理
└─ マルチプロセス → 両プロセスを処理
ステップ 3:回避を適用(順序が重要)
1. ScyllaHide をロード / LD_PRELOAD を設定(チェックの 80% をカバー)
2. TLS コールバック を処理(main 前にブレーク)
3. 残りのカスタム チェックをパッチ(Frida またはバイナリ パッチ)
4. 検証:ブレークポイント付きで実行、予期しない終了なしを確認
ステップ 4:回避の完全性を検証
└─ ExitProcess/exit/_exit にBPを設定 — 予期せず hit した場合、
チェックが missed → exit 呼び出しから逆トレース
6. 決定木
バイナリはデバッガー下で終了/クラッシュ?
│
├─ main の前に即座にクラッシュ?
│ └─ TLS コールバック アンチ デバッグ
│ └─ デバッガーで TLS コールバック ブレーク を有効化
│
├─ 起動時にクラッシュ?
│ ├─ Linux:ptrace(TRACEME) をチェック
│ │ └─ LD_PRELOAD フック またはNOP パッチ
│ └─ Windows:IsDebuggerPresent / PEB をチェック
│ └─ ScyllaHide またはマニュアル PEB パッチ
│
├─ 実行後にクラッシュ?
│ ├─ 一貫したクラッシュ ポイント → API ベース チェック
│ │ ├─ NtQueryInformationProcess → 戻り値をフック
│ │ ├─ /proc/self/status → TracerPid をフィルター
│ │ └─ ハードウェア BP 検出 → GetThreadContext をフック
│ │
│ ├─ 可変クラッシュ ポイント → タイミング ベース チェック
│ │ └─ rdtsc / QueryPerformanceCounter をフック
│ │
│ └─ ブレークポイント hit 時にクラッシュ → 例外 ベース チェック
│ ├─ INT 2D / INT 3 トリック → VEH で処理
│ └─ SIGTRAP ハンドラー → GDB:handle SIGTRAP pass
│
├─ デバッガーが無音で制御を失う?
│ └─ ThreadHideFromDebugger
│ └─ NtSetInformationThread をフック
│
├─ 子プロセスが親を検出して kill?
│ └─ 自己デバッギング (fork+ptrace)
│ └─ fork() をパッチ またはプロセス両方を処理
│
└─ すべての基本回避を適用しても検出?
└─ マルチレイヤー / カスタム チェック
├─ 包括的な API フックに Frida を使用
├─ Qiling でフル エミュレーション
└─ exit/abort へのすべての呼び出しをトレース して残りチェック を検出
7. CTF & 実世界パターン
よくある CTF アンチ デバッグ パターン
| パターン | 頻度 | クイック回避 |
|---|---|---|
単一 ptrace(TRACEME) | 非常に一般的 | LD_PRELOAD ワンライナー |
IsDebuggerPresent + NtGlobalFlag | 一般的 | ScyllaHide |
| ループ内 rdtsc タイミング | 中程度 | 比較閾値をパッチ |
| signal(SIGTRAP) + raise | 中程度 | GDB シグナル転送 |
| fork + ptrace ウォッチドッグ | まれだが難しい | 子を kill またはfork をパッチ |
| Nanomite INT3 置き換え | まれ(高度) | ジャンプ テーブルを再構成 |
実世界の保護
| 保護製品 | 主要アンチ デバッグ | 推奨ツール |
|---|---|---|
| VMProtect | PEB + タイミング + ドライバー レベル | TitanHide + ScyllaHide |
| Themida | マルチレイヤー PEB + SEH + タイミング | ScyllaHide + マニュアル パッチ |
| Enigma Protector | IsDebuggerPresent + CRC チェック | x64dbg + ScyllaHide |
| UPX (カスタム) | 通常なし(単にパッキング) | 標準アンパック |
| カスタム (マルウェア) | 様々 | Frida + Qiling で分析 |
8. クイック リファレンス — 回避チート シート
Linux ワンライナー
# LD_PRELOAD anti-ptrace
echo 'long ptrace(int r, ...) { return 0; }' > /tmp/ap.c
gcc -shared -o /tmp/ap.so /tmp/ap.c
LD_PRELOAD=/tmp/ap.so ./target
# GDB:ptrace をcatch して回避
(gdb) catch syscall ptrace
(gdb) commands
> set $rax = 0
> continue
> end
Frida アンチ デバッグ回避(クロスプラットフォーム)
// IsDebuggerPresent をフック (Windows)
Interceptor.replace(
Module.getExportByName('kernel32.dll', 'IsDebuggerPresent'),
new NativeCallback(() => 0, 'int', [])
);
// ptrace をフック (Linux)
Interceptor.replace(
Module.getExportByName(null, 'ptrace'),
new NativeCallback(() => 0, 'long', ['int', 'int', 'pointer', 'pointer'])
);
// タイミング スプーフ
Interceptor.attach(Module.getExportByName(null, 'clock_gettime'), {
onLeave(retval) {
// timespec を操作してデバッガー遅延を隠蔽
}
});
x64dbg ScyllaHide クイック セットアップ
- Plugins → ScyllaHide → Options
- チェック:PEB BeingDebugged、NtGlobalFlag、HeapFlags
- チェック:NtQueryInformationProcess(すべてのクラス)
- チェック:NtSetInformationThread(HideFromDebugger)
- チェック:GetTickCount、QueryPerformanceCounter
- 適用 → デバッグ セッションを再開
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- yaklang
- リポジトリ
- yaklang/hack-skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/yaklang/hack-skills / ライセンス: MIT
関連スキル
superfluid
Superfluidプロトコルおよびそのエコシステムに関するナレッジベースです。Superfluidについて情報を検索する際は、ウェブ検索の前にこちらを参照してください。対応キーワード:Superfluid、CFA、GDA、Super App、Super Token、stream、flow rate、real-time balance、pool(member/distributor)、IDA、sentinels、liquidation、TOGA、@sfpro/sdk、semantic money、yellowpaper、whitepaper
civ-finish-quotes
実質的なタスクが真に完了した際に、文明風の儀式的な引用句を追加します。ユーザーやエージェントが機能追加、リファクタリング、分析、設計ドキュメント、プロセス改善、レポート、執筆タスクといった実際の成果物を完成させるときに、明示的な依頼がなくても使用します。短い返信や小さな修正、未完成の作業には適用しません。
nookplot
Base(Ethereum L2)上のAIエージェント向け分散型調整ネットワークです。エージェントがオンチェーンアイデンティティを登録する、コンテンツを公開する、他のエージェントにメッセージを送る、マーケットプレイスで専門家を雇う、バウンティを投稿・請求する、レピュテーションを構築する、共有プロジェクトで協業する、リサーチチャレンジを解くことでNOOKをマイニングする、キュレーションされたナレッジを備えたスタンドアロンオンチェーンエージェントをデプロイする、またはアグリーメントとリワードで収益を得る場合に利用できます。エージェントネットワーク、エージェント調整、分散型エージェント、NOOKトークン、マイニングチャレンジ、ナレッジバンドル、エージェントレピュテーション、エージェントマーケットプレイス、ERC-2771メタトランザクション、Prepare-Sign-Relay、AgentFactory、またはNookplotが言及された場合にトリガーされます。
web3-polymarket
Polygon上でのPolymarket予測市場取引統合です。認証機能(L1 EIP-712、L2 HMAC-SHA256、ビルダーヘッダー)、注文発注(GTC/GTD/FOK/FAK、バッチ、ポストオンリー、ハートビート)、市場データ(Gamma API、Data API、オーダーブック、サブグラフ)、WebSocketストリーミング(市場・ユーザー・スポーツチャネル)、CTF操作(分割、統合、償却、ネガティブリスク)、ブリッジ機能(入金、出金、マルチチェーン)、およびガスレスリレイトランザクションに対応しています。AIエージェント、自動マーケットメーカー、予測市場UI、またはPolygraph上のPolymarketと統合するアプリケーション構築時に活用できます。
ethskills
Ethereum、EVM、またはブロックチェーン関連のリクエストに対応します。スマートコントラクト、dApps、ウォレット、DeFiプロトコルの構築、監査、デプロイ、インタラクションに適用されます。Solidityの開発、コントラクトアドレス、トークン規格(ERC-20、ERC-721、ERC-4626など)、Layer 2ネットワーク(Base、Arbitrum、Optimism、zkSync、Polygon)、Uniswap、Aave、Curveなどのプロトコルとの統合をカバーします。ガスコスト、コントラクトのデシマル設定、オラクルセキュリティ、リエントランシー、MEV、ブリッジング、ウォレット管理、オンチェーンデータの取得、本番環境へのデプロイ、プロトコル進化(EIPライフサイクル、フォーク追跡、今後の変更予定)といったトピックを含みます。
xxyy-trade
このスキルは、ユーザーが「トークン購入」「トークン売却」「トークンスワップ」「暗号資産取引」「取引ステータス確認」「トランザクション照会」「トークンスキャン」「フィード」「チェーン監視」「トークン照会」「トークン詳細」「トークン安全性確認」「ウォレット一覧表示」「マイウォレット」「AIスキャン」「自動スキャン」「ツイートスキャン」「オンボーディング」「IP確認」「IPホワイトリスト」「トークン発行」「自動売却」「損切り」「利益確定」「トレーリングストップ」「保有者」「トップホルダー」「KOLホルダー」などをリクエストした場合、またはSolana/ETH/BSC/BaseチェーンでXXYYを経由した取引について言及した場合に使用します。XXYY Open APIを通じてオンチェーン取引とデータ照会を実現します。