Easy AntiCheat Full Driver Hiding

good morning. it’s seb, and today i’m diving into something more advanced, hiding a driver from both memory scans and nmi-based stackwalks using a combination of mmcopymemory hooking and patchguard bypassing.

this blog is mostly focused on my thought process: how i figured out where eac looks, what it uses, and how i built around that.

please note that anything listed in these articles has already been disclosed to the epic team before posting and patched.


background

i’ve been doing a lot of work lately around memory visibility, mostly trying to figure out how anti-cheats like eac locate drivers once they’re loaded. most of the time, if you’re in physical memory and not in the module list, you’re already invisible to most usermode detection.

but anti-cheats are different. they have full kernel drivers and often use:

  • MmCopyMemory to scan physical memory

so i wanted to stop both. the goal was: even if my driver is active, you can't find it, not from memory reads, and not from a stackwalk.


mmcopymemory trick

i started with MmCopyMemory. eac uses it for its physical memory scans. it skips page tables, skips virtual address tricks, and just reads raw data.

if they call MmCopyMemory, they can technically read my driver’s code even if i’m unlinked from the module list. so the fix was simple in theory: hook it.

but there’s a problem: patchguard.

patchguard protects MmCopyMemory, so just writing over it will crash the system eventually. to get around this, i used a patchguard bypass i had been sitting on. not gonna explain the bypass here fully (for obvious reasons, maybe later on in my documentation), but it let me hook MmCopyMemory safely, without getting caught by integrity checks.

so the problem here was, they could easily check the integrity of mmcopymemory. what did i do about this? simply put, i saved the bytes before i did any overriding and when mmcopymemory was called on, well, mmcopymemory, it would return the original bytes. this way, it would look completely clean to eac. i then force it to skip over my drivers memory by passing that in during the init of the hook.


how this affects eac

eac uses MmCopyMemory to scan memory regions. by faking clean bytes on read, you stop signature scanning, heuristic checks, and any byte-pattern matching they try to run.

you’re basically making the memory read lie.

so now we’ve killed memory detection, which is good, but the driver can still be located (depending on ur communication method)


stackwalk problem

even if memory scans are blocked, kernel-mode anti-cheats (like eac) also use nmi-based stack walking. this means they can interrupt the system via nmi, freeze a core, and inspect all stack frames, then look for return addresses that land in unknown or suspicious regions.

that includes:

  • manually mapped drivers

  • hidden code

  • trampoline hooks

  • shellcode in weird places

and yes, even if your memory is hidden from MmCopyMemory, stackframes still fuck with you.


stackwalk solution

so the next step was blocking nmi stackwalks from landing on my driver. this one took longer. there’s less public info about the exact functions used, but ended up with these:

  • KeQueryPerformanceCounter

  • KeCapturePersistentThreadState

i ended up hooking one of the functions that runs during nmi-based captures. not naming it directly here, but the idea was:

  • if the return address points into my driver, reroute it

  • if the address could land inside my range, mask it

  • prevent anything from walking back to my module base

by doing this, even when eac triggers an nmi and tries to scan all cores, they see no trace of my driver in any stack frame.


what this achieves

by combining both:

  1. hooking MmCopyMemory (using a patchguard bypass) to return fake memory

  2. intercepting nmi stackwalks to avoid returning into my driver

i was able to make a driver that:

  • doesn’t show up in physical memory scans

  • doesn’t leave traces in stackwalks

  • still functions normally

  • and doesn’t crash the system over time

it’s not a universal solution, but it’s a solid layer in a proper stealth system, especially against anti-cheats that rely on fixed detection logic.


final thoughts

this was one of those setups that took multiple systems working together: patchguard evasion, memory faking, and return address control. however, it's worth it in the end :)

there’s more to come on detection evasion in the kernel. this was just about memory and stack visibility. next time, we’ll look at how to counter more behavioral hooks.

of course, there are still api calls coming from the driver which they can trace the return addresses to, but these are nothing hard to deal with and possibly may be explained in a later document.

Last updated