update readme

This commit is contained in:
2026-07-07 04:37:29 +01:00
commit 50cbc601d5
69 changed files with 15056 additions and 0 deletions
+100
View File
@@ -0,0 +1,100 @@
# Evasion Chain
## Architecture
Evasion lives in two layers:
- **Go binary** — reflective PE loader, sleep obfuscation, cgocall bridge
- **Zig DLL** — syscall dispatch, API resolution, AMSI/ETW/EDR evasion, module stomping
## Loading Phase
### Reflective DLL Loading
How the Go loader reads PE headers, allocates memory via VirtualAlloc, copies sections, applies relocations, resolves imports, sets section protections, resolves exports, calls DllMain and init_evasion. No file on disk, no LoadLibrary callback.
## Initialization Phase
### init_evasion → syscall.init_syscall
- Walks PEB to find ntdll base
- Scans ntdll for `0F 05 C3` (syscall;ret) gadgets, stores addresses in pool
- Seeds PRNG
- Caches exception directory for HAL's Gate fallback
- Builds FreshyCalls table: walks ntdll export directory, collects Nt* exports,
sorts by RVA, SSN = position. Immune to inline hooks.
Reference: https://0xdbgman.github.io/posts/edr-internals-research-and-bypass/
- Scans ntdll for `41 FF E2` (jmp r10) gadget — used as CFG-safe IC neutralizer
### api.ensure()
- Resolves ~100 Windows API function pointers by walking PEB → module export tables
- kernel32, ntdll, advapi32, winhttp, bcrypt
- Used by evasion functions for non-syscall operations (VirtualProtect, LoadLibrary, etc.)
## Evasion Phase
### stomp_evasion
- Copies DLL .text into a signed Microsoft DLL (CryptoAPI/dwrite/msvcp_win)
- Changes target protection to RWX, copies, restores to RX
- Zeros original PE headers (DOS header + NT headers)
- Memory scanners see legitimate signed DLL code at execution addresses
- Base passed from Go (reflective DLL not in PEB module list)
### patch_etw (three-tier)
- Tier 1: NtTraceControl to stop EDR ETW providers (MDE, kernel-process, security-mitigations)
- Tier 2: Hardware breakpoint on EtwEventWrite via VEH + debug registers
- Tier 3: RET patch fallback — writes 0xC3 to EtwEventWrite's first byte
- On Windows 10/11 where NtTraceControl is forwarded to api-ms-win DLLs, extract_ssn skips Method 1 (forward RVA guard) and falls through to HAL's Gate Method 2
### patch_amsi
- Loads amsi.dll, resolves AmsiScanBuffer address
- Installs Vectored Exception Handler (VEH) as first-in-chain
- Sets DR0 hardware breakpoint to AmsiScanBuffer address
- Sets Dr7 to enable DR0 locally
- SuspendThread → SetThreadContext → ResumeThread (proper thread context modification per MS docs)
- On hit: VEH handler sets RAX=0 (AMSI_RESULT_CLEAN), sets RIP to return address, pops stack
- No bytes modified in amsi.dll — invisible to EDR tamper detection
### remove_edr_callbacks
- Escalates SeDebugPrivilege (best-effort, silently skips if fails)
- Calls NtSetInformationProcess with InfoClass=40 (ProcessInstrumentationCallback)
- CFG-aware: on CFG-enabled systems (Win10 1709+), kernel refuses null callback pointer.
Uses `jmp r10` gadget address instead — IC fires but immediately returns, neutralizing it.
Reference: https://cirosec.de/en/news/windows-instrumentation-callbacks-part-4/
- On non-CFG systems: sets Callback=NULL, removing the EDR's instrumentation callback
- Runs LAST in startup chain (the call itself is an IoC — but nothing follows it)
- Defanged on Windows 11 23H2+ (restricted to kernel-mode callers)
## Sleep Phase
### EvasionSleep (Go-side)
- Gated by dllLoaded flag (prevents protection before DLL fully initialized)
- Pre-saved .text bounds from section scan during loading
- VirtualProtect(DLL .text, PAGE_NOACCESS) — DLL code unreadable during idle
- time.Sleep(duration) — Go runtime, not DLL code
- VirtualProtect(restore original protection) — DLL executable again
- DLL .text at different address than Go binary .text — Go code executes fine during protected period
## Syscall Dispatch
### FreshyCalls: SSN Extraction
- Walks ntdll's export directory, collects all Nt* exports with real code addresses
- Sorts by RVA ascending — SSN = position in sorted order
- Immune to inline hooking: EDRs can patch stub bytes but cannot change linker RVA order
- HAL's Gate fallback: binary search exception directory for forwarded exports
not in ntdll's direct export table
- Reference: https://0xdbgman.github.io/posts/edr-internals-research-and-bypass/
### Indirect Syscall Execution
- Random gadget from g_syscall_addrs pool (0F 05 C3 in ntdll)
- hells_gate assembly: encrypts SSN, stores gadget address globally
- hell_descent assembly: decrypts SSN into eax, sets up r10 (first arg), jumps to gadget
- Gadget's `syscall` runs with eax=SSN — kernel processes it
- Gadget's `ret` returns to hell_descent caller, unwinding through normal call chain
- Never calls the hooked export stub — EDR sees a syscall from a random ntdll address
### No Call Stack Spoofing
- Go's cgocall requires clean stack returns
- NtContinue-based ROP chains corrupt Go's goroutine scheduler
- All dispatch is non-spoofed
## Key Fixes Applied
- PE struct offsets corrected (SizeOfImage at 56, not 36)
- imageImportDescriptor corrected to 20 bytes
- @intCast→ntstatus helper (usize to NTSTATUS via @bitCast)
- extract_ssn forwarded export guard (prevents @intCast panic)
- CONTEXT_DEBUG_REGISTERS = 0x100010 (was 0x10, missing AMD64 flag)
- SuspendThread/ResumeThread before SetThreadContext
- stomp_evasion(base) parameter from Go
- dllLoaded flag guards EvasionSleep