Skip to content

Cheat sheet — Anti-Analysis Techniques

Companion to Module 10 — Anti-Analysis Techniques · CC BY 4.0 — print it, pin it, share it.

Last reviewed: 2026-07

Only run live malware in an isolated, disposable analysis VM with no bridged network. Every bypass here still detonates the sample — reset to clean afterward.

The three families — what to recognise

Family Example check Bypass
Environment /proc/cpuinfo hypervisor flag, sandbox user modify the environment, not binary
Timing delta between two timestamps; stall loops freeze the clock (LD_PRELOAD)
Code-flow exception handlers, self-modifying, TLS callbacks manual patch / dynamic dump

Detect the check with strace

strace -f ./sample 2>&1 | grep -E 'ptrace|cpuid|clock_gettime|gettimeofday|nanosleep'
strace -e trace=ptrace ./sample        # see the PTRACE_TRACEME probe fire
  • ptrace(PTRACE_TRACEME) is the canonical Linux debugger check: only one tracer at a time, so if it returns -1 the process is already being debugged and the malware bails. (Windows: IsDebuggerPresent(), NtQueryInformationProcess class 7.)

LD_PRELOAD hook — defeat ptrace and freeze the clock

// hooks.c — return success for ptrace, zero the timing delta
#define _GNU_SOURCE
#include <sys/ptrace.h>
#include <time.h>

long ptrace(int req, ...) { return 0; }              // pretend "not traced"

int clock_gettime(clockid_t c, struct timespec *t) { // freeze the clock
    t->tv_sec = 0; t->tv_nsec = 0; return 0;         // elapsed delta → 0
}
gcc -shared -fPIC -o hooks.so hooks.c
LD_PRELOAD=./hooks.so ./sample         # libc wrappers hit our stubs first
  • LD_PRELOAD intercepts the libc wrapper before the syscall — clean and no debugger needed. Timing checks compare two timestamps; freeze either side and the comparison breaks in your favour.

Environment bypass — hypervisor flags

grep -E 'hypervisor|vmware|kvm' /proc/cpuinfo        # what the sample scans for
# CPUID/proc reflect REAL hardware — you change the environment, not the binary:
#   - present a modified /proc/cpuinfo (bind-mount / overlay)
#   - VMware: set  hypervisor.cpuid.v0 = FALSE
#   - or run the sample on bare metal

Bypass hierarchy — work up it

1. strace          — intercepts syscalls without a full debugger; some ptrace checks won't fire
2. LD_PRELOAD .so  — return success from ptrace(); hook clock_gettime for timing
3. manual patch    — NOP the check / flip the jump when the above fail (static-linked binaries)

Author the detection — flag the evasion itself

rule linux_anti_analysis {
    meta: attck = "T1497"
    strings:
        $p = "PTRACE_TRACEME"
        $c = "/proc/cpuinfo"
        $h = "hypervisor"
    condition:
        2 of them                       // fire on the demo binary, quiet on benign
}

Gotchas worth remembering

  • "The sample did nothing" is the most dangerous result to report uncritically. "Did nothing" and "detected me and went quiet" look identical and mean opposite things — the first clears it, the second is a missed live threat.
  • LD_PRELOAD only works on dynamically linked binaries. Go and Rust malware often statically links libc — the hook does nothing and you need kernel-level interception or a manual patch.
  • The sample can detect strace itself. Choosing the wrong bypass wastes the run — a ptrace check may see the tracer and bail.
  • Hypervisor flags are real hardware data, not something the binary invents — you bypass by changing the environment (/proc/cpuinfo, hypervisor.cpuid.v0=FALSE, bare metal), never by patching the binary.
  • A suggested bypass is not a working one. Confirm each by running the demo binary with and without it — reading the AI's suggestion is not confirmation.

Comments

Sign in with GitHub to comment. Choose the type: Feedback (errors or suggestions on this page) · Hints (help for fellow learners — no spoilers) · General (anything else).