Claude
Skills
Sign in
Back

anti-reversing-techniques

Included with Lifetime
$97 forever

Understand anti-reversing, obfuscation, and protection techniques encountered during software analysis. Use this skill when analyzing malware evasion techniques, when implementing anti-debugging protections for CTF challenges, when reverse engineering packed binaries, or when building security research tools that need to detect virtualized environments.

Security

What this skill does


<!--
Portions of this file are derived from wshobson/agents
(https://github.com/wshobson/agents), MIT License.
Snapshot 2026-05-12.
-->

> **AUTHORIZED USE ONLY**: This skill contains dual-use security techniques. Before proceeding with any bypass or analysis:
>
> 1. **Verify authorization**: Confirm you have explicit written permission from the software owner, or are operating within a legitimate security context (CTF, authorized pentest, malware analysis, security research)
> 2. **Document scope**: Ensure your activities fall within the defined scope of your authorization
> 3. **Legal compliance**: Understand that unauthorized bypassing of software protection may violate laws (CFAA, DMCA anti-circumvention, etc.)
>
> **Legitimate use cases**: Malware analysis, authorized penetration testing, CTF competitions, academic security research, analyzing software you own/have rights to

# Anti-Reversing Techniques

Understanding protection mechanisms encountered during authorized software analysis, security research, and malware analysis. This knowledge helps analysts bypass protections to complete legitimate analysis tasks.

For advanced techniques, see [references/advanced-techniques.md](references/advanced-techniques.md)

---

## Input / Output

**What you provide:**

- **Binary path or sample**: the executable, DLL, or firmware image under analysis
- **Platform**: Windows x86/x64, Linux, macOS, ARM — affects which checks apply
- **Goal**: bypass for dynamic analysis, identify protection type, build detection code, implement for CTF

**What this skill produces:**

- **Protection identification**: named technique (e.g., RDTSC timing check, PEB BeingDebugged) with location in binary
- **Bypass strategy**: specific patch addresses, hook points, or tool commands to neutralize each check
- **Analysis report**: structured findings listing each protection layer, severity, and recommended bypass
- **Code artifacts**: Python/IDAPython scripts, GDB command sequences, or C stubs for bypassing or implementing checks

---

## Anti-Debugging Techniques

### Windows Anti-Debugging

#### API-Based Detection

```c
// IsDebuggerPresent
if (IsDebuggerPresent()) {
    exit(1);
}

// CheckRemoteDebuggerPresent
BOOL debugged = FALSE;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &debugged);
if (debugged) exit(1);

// NtQueryInformationProcess
typedef NTSTATUS (NTAPI *pNtQueryInformationProcess)(
    HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);

DWORD debugPort = 0;
NtQueryInformationProcess(
    GetCurrentProcess(),
    ProcessDebugPort,        // 7
    &debugPort,
    sizeof(debugPort),
    NULL
);
if (debugPort != 0) exit(1);

// Debug flags
DWORD debugFlags = 0;
NtQueryInformationProcess(
    GetCurrentProcess(),
    ProcessDebugFlags,       // 0x1F
    &debugFlags,
    sizeof(debugFlags),
    NULL
);
if (debugFlags == 0) exit(1);  // 0 means being debugged
```

**Bypass:** Use ScyllaHide plugin in x64dbg (patches all common checks automatically). Manually: force `IsDebuggerPresent` return to 0, patch `PEB.BeingDebugged` to 0, hook `NtQueryInformationProcess`. In IDA: `ida_bytes.patch_byte(check_addr, 0x90)`.

#### PEB-Based Detection

```c
// Direct PEB access
#ifdef _WIN64
    PPEB peb = (PPEB)__readgsqword(0x60);
#else
    PPEB peb = (PPEB)__readfsdword(0x30);
#endif

// BeingDebugged flag
if (peb->BeingDebugged) exit(1);

// NtGlobalFlag
// Debugged: 0x70 (FLG_HEAP_ENABLE_TAIL_CHECK |
//                 FLG_HEAP_ENABLE_FREE_CHECK |
//                 FLG_HEAP_VALIDATE_PARAMETERS)
if (peb->NtGlobalFlag & 0x70) exit(1);

// Heap flags
PDWORD heapFlags = (PDWORD)((PBYTE)peb->ProcessHeap + 0x70);
if (*heapFlags & 0x50000062) exit(1);
```

**Bypass:** In x64dbg, follow `gs:[60]` (x64) or `fs:[30]` (x86) in dump. Set `BeingDebugged` (offset +2) to 0; clear `NtGlobalFlag` (offset +0xBC on x64).

#### Timing-Based Detection

```c
// RDTSC timing
uint64_t start = __rdtsc();
// ... some code ...
uint64_t end = __rdtsc();
if ((end - start) > THRESHOLD) exit(1);

// QueryPerformanceCounter
LARGE_INTEGER start, end, freq;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&start);
// ... code ...
QueryPerformanceCounter(&end);
double elapsed = (double)(end.QuadPart - start.QuadPart) / freq.QuadPart;
if (elapsed > 0.1) exit(1);  // Too slow = debugger

// GetTickCount
DWORD start = GetTickCount();
// ... code ...
if (GetTickCount() - start > 1000) exit(1);
```

**Python script — timing-based anti-debug detection scanner:**

```python
#!/usr/bin/env python3
"""Scan a binary for common timing-based anti-debug patterns."""
import re
import sys

PATTERNS = {
    "RDTSC":              rb"\x0f\x31",                    # RDTSC opcode
    "RDTSCP":             rb"\x0f\x01\xf9",                # RDTSCP opcode
    "GetTickCount":       rb"GetTickCount\x00",
    "QueryPerfCounter":   rb"QueryPerformanceCounter\x00",
    "NtQuerySysInfo":     rb"NtQuerySystemInformation\x00",
}

def scan(path: str) -> None:
    data = open(path, "rb").read()
    print(f"Scanning: {path} ({len(data)} bytes)\n")
    for name, pattern in PATTERNS.items():
        hits = [m.start() for m in re.finditer(re.escape(pattern), data)]
        if hits:
            offsets = ", ".join(hex(h) for h in hits[:5])
            print(f"  [{name}] found at: {offsets}")
    print("\nDone. Cross-reference offsets in IDA/Ghidra to find check logic.")

if __name__ == "__main__":
    scan(sys.argv[1])
```

**Bypass:** Use hardware breakpoints (no INT3 overhead), NOP the comparison + conditional jump, freeze RDTSC via hypervisor, or hook timing APIs to return consistent values.

#### Exception-Based Detection

```c
// SEH: if debugger is attached it consumes the INT3 exception
// and execution falls through to exit(1) instead of the __except handler
__try { __asm { int 3 } }
__except(EXCEPTION_EXECUTE_HANDLER) { return; }  // Clean: exception handled here
exit(1);  // Dirty: debugger swallowed the exception

// VEH: register handler that self-handles INT3 (increments RIP past INT3)
// Debugger intercepts first, handler never runs → detected
LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS ep) {
    if (ep->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) {
        ep->ContextRecord->Rip++;
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    return EXCEPTION_CONTINUE_SEARCH;
}
```

**Bypass**: In x64dbg, set "Pass exception to program" for EXCEPTION_BREAKPOINT (Options → Exceptions → add 0x80000003).

### Linux Anti-Debugging

```c
// ptrace self-trace
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) {
    // Already being traced
    exit(1);
}

// /proc/self/status
FILE *f = fopen("/proc/self/status", "r");
char line[256];
while (fgets(line, sizeof(line), f)) {
    if (strncmp(line, "TracerPid:", 10) == 0) {
        int tracer_pid = atoi(line + 10);
        if (tracer_pid != 0) exit(1);
    }
}

// Parent process check
if (getppid() != 1 && strcmp(get_process_name(getppid()), "bash") != 0) {
    // Unusual parent (might be debugger)
}
```

**Bypass (LD_PRELOAD hook):**

```bash
# hook.c: long ptrace(int request, ...) { return 0; }
# gcc -shared -fPIC -o hook.so hook.c
LD_PRELOAD=./hook.so ./target
```

**GDB bypass command sequence:**

```gdb
# 1. Make ptrace(PTRACE_TRACEME) always return 0 (success)
catch syscall ptrace
commands
  silent
  set $rax = 0
  continue
end

# 2. Bypass check after ptrace call: find "cmp rax, 0xffffffff; je <exit>"
#    Clear ZF so the conditional jump is not taken:
#    set $eflags = $eflags & ~0x40

# 3. Bypass /proc/self/status TracerPid check at the open() level
catch syscall openat
commands
  silent
  # If arg contains "status", patch the fd result to /dev/null equivalent
  continue
end

# 4. Bypass parent process name check
set follow-fork-mode child
set detach-on-fork off
```

---

## Anti-VM Detection

### Hardware Fingerprinting

```c
// CPUID-based detection
int cpuid_info[4];
__cpuid(cpuid_info, 1);
// Check hypervisor bit (bit 31 of ECX)
if (cpuid_info[2] & (1 << 31)) 

Related in Security