reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
Apache License 2.0 — see the repository root LICENSE file for the full text.
|
||||
|
||||
Copyright the AnalystAIPack contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
name: identifying-anti-debugging-techniques
|
||||
description: 'Identifies and bypasses anti-debugging and anti-analysis checks in malware: PEB
|
||||
flags, debugger-detection APIs, timing checks, and exception tricks, then neutralizes them
|
||||
to continue analysis. Activates for requests to identify anti-debugging, bypass anti-debug
|
||||
checks, or analyze evasion that blocks a debugger.'
|
||||
domain: cybersecurity
|
||||
subdomain: reverse-engineering
|
||||
tags:
|
||||
- reverse-engineering
|
||||
- anti-debugging
|
||||
- evasion
|
||||
- peb
|
||||
- debugging
|
||||
- windows
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1622
|
||||
- T1497.001
|
||||
- T1027
|
||||
d3fend:
|
||||
- D3-PSA
|
||||
- D3-FCR
|
||||
references:
|
||||
- 'Microsoft debugging APIs (IsDebuggerPresent, CheckRemoteDebuggerPresent) — https://learn.microsoft.com/windows/win32/api/debugapi/'
|
||||
- 'PEB structure (BeingDebugged, NtGlobalFlag) — https://learn.microsoft.com/windows-hardware/drivers/'
|
||||
---
|
||||
|
||||
# Identifying Anti-Debugging Techniques
|
||||
|
||||
## When to Use
|
||||
|
||||
- A sample behaves differently (or exits) under a debugger and you need to find the checks.
|
||||
- You are stuck at a point where execution diverges when analysis tooling is present.
|
||||
- You want to neutralize anti-debug logic to continue dynamic analysis.
|
||||
|
||||
**Do not use** brute removal of every check — some are tied to control flow or decryption keys;
|
||||
faking the expected result is usually safer than deleting the check.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- x64dbg (with anti-debug plugins like ScyllaHide) inside the victim VM.
|
||||
- A disassembler to locate checks statically.
|
||||
- Familiarity with the Windows PEB and debug APIs.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Enumerate likely checks statically
|
||||
|
||||
Scan imports and code for common anti-debug primitives:
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py scan sample.bin
|
||||
```
|
||||
|
||||
```text
|
||||
API-based : IsDebuggerPresent, CheckRemoteDebuggerPresent, NtQueryInformationProcess
|
||||
PEB-based : BeingDebugged (PEB+0x2), NtGlobalFlag (PEB+0xBC/0x68)
|
||||
Timing : rdtsc, GetTickCount/QueryPerformanceCounter deltas around code
|
||||
Exceptions : INT3/INT2D, SetUnhandledExceptionFilter, single-step traps
|
||||
Self-checks : CRC of own code, breakpoint (0xCC) scanning
|
||||
```
|
||||
|
||||
### Step 2: Confirm at runtime
|
||||
|
||||
Set breakpoints on the detection APIs and observe how the result is used (a conditional jump
|
||||
that leads to exit vs. real code).
|
||||
|
||||
### Step 3: Neutralize
|
||||
|
||||
Prefer faking results over deletion: force `IsDebuggerPresent` to return 0, clear the PEB
|
||||
`BeingDebugged` flag, or use ScyllaHide to hook the common checks automatically. For timing,
|
||||
patch the comparison or reduce measured deltas.
|
||||
|
||||
### Step 4: Re-run and continue
|
||||
|
||||
With checks neutralized, proceed to unpacking/behavior analysis. Re-scan in case later stages
|
||||
add more checks.
|
||||
|
||||
## Validation
|
||||
|
||||
- After neutralization, execution follows the same path as on a non-debugged run.
|
||||
- Faked check results do not break decryption or control flow (no corrupted code paths).
|
||||
- The sample reaches and reveals its real behavior.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Patching a check that feeds a decryption key, corrupting later code. Understand the use
|
||||
before editing.
|
||||
- Missing PEB-direct checks because you only looked at imported APIs.
|
||||
- Stopping after one check; samples chain several across stages.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the anti-debug scanner.
|
||||
- Microsoft debug APIs and PEB documentation (linked in frontmatter).
|
||||
@@ -0,0 +1,42 @@
|
||||
# API Reference: Identifying Anti-Debugging Techniques
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` scans a binary for anti-debug API names and instruction-byte patterns
|
||||
(rdtsc, int2d, PEB access) and returns findings with brief neutralization guidance. It is a
|
||||
static scan and never executes the sample.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| (stdlib) | — | regex, file IO |
|
||||
|
||||
## External tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| x64dbg | Confirm checks at runtime |
|
||||
| ScyllaHide | Hook and hide common anti-debug checks automatically |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `scan(path)`
|
||||
Returns detected API-based checks, instruction-byte patterns, per-finding guidance, and a
|
||||
likelihood flag.
|
||||
|
||||
### `ANTIDBG_APIS` / `BYTE_PATTERNS`
|
||||
The API-name and instruction-pattern signatures used for detection.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py scan sample.bin
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- Microsoft debug APIs (IsDebuggerPresent, CheckRemoteDebuggerPresent) —
|
||||
https://learn.microsoft.com/windows/win32/api/debugapi/
|
||||
- PEB fields BeingDebugged / NtGlobalFlag (Windows internals documentation)
|
||||
- Common anti-debug instruction patterns (rdtsc timing, int2d, PEB access)
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Scan a binary for anti-debugging indicators.
|
||||
|
||||
Looks for anti-debug API names and instruction-byte patterns (rdtsc, int3/int2d, PEB access)
|
||||
in file bytes, and reports what was found with brief guidance. Static scan only; it never
|
||||
executes the sample.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ANTIDBG_APIS = [
|
||||
b"IsDebuggerPresent", b"CheckRemoteDebuggerPresent", b"NtQueryInformationProcess",
|
||||
b"OutputDebugStringA", b"NtSetInformationThread", b"DebugActiveProcess",
|
||||
b"SetUnhandledExceptionFilter", b"QueryPerformanceCounter", b"GetTickCount",
|
||||
b"NtQuerySystemInformation", b"NtClose", b"BlockInput",
|
||||
]
|
||||
|
||||
# instruction-byte patterns (heuristic)
|
||||
BYTE_PATTERNS = {
|
||||
"rdtsc": rb"\x0f\x31",
|
||||
"int3 (0xCC)": rb"\xcc\xcc\xcc",
|
||||
"int2d": rb"\xcd\x2d",
|
||||
"cpuid": rb"\x0f\xa2",
|
||||
"peb_fs30": rb"\x64\xa1\x30\x00\x00\x00", # mov eax, fs:[30h]
|
||||
"peb_gs60": rb"\x65\x48\x8b.{0,2}\x60", # mov rax, gs:[60h] (x64, loose)
|
||||
}
|
||||
|
||||
GUIDANCE = {
|
||||
"IsDebuggerPresent": "force return 0 / hook with ScyllaHide",
|
||||
"CheckRemoteDebuggerPresent": "force the out param to FALSE",
|
||||
"NtQueryInformationProcess": "watch ProcessDebugPort/Flags classes; fake results",
|
||||
"rdtsc": "timing check; patch the delta comparison",
|
||||
"int2d": "anti-debug exception trick; handle/skip the exception",
|
||||
"peb_fs30": "PEB access; clear BeingDebugged / NtGlobalFlag",
|
||||
}
|
||||
|
||||
|
||||
def scan(path: str) -> dict:
|
||||
data = Path(path).read_bytes()
|
||||
apis = sorted({a.decode() for a in ANTIDBG_APIS if a in data})
|
||||
patterns = sorted({name for name, pat in BYTE_PATTERNS.items()
|
||||
if re.search(pat, data)})
|
||||
findings = apis + patterns
|
||||
return {
|
||||
"api_checks": apis,
|
||||
"instruction_patterns": patterns,
|
||||
"guidance": {f: GUIDANCE[f] for f in findings if f in GUIDANCE},
|
||||
"anti_debug_likely": len(findings) >= 2,
|
||||
}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Scan for anti-debugging")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("scan")
|
||||
p.add_argument("file")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(scan(args.file), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user