Anti cheat signature scanning is the oldest and still the loudest detection technique in commercial anti-cheats. Every major kernel-mode AC on the market, Riot Vanguard, Easy Anti-Cheat, BattlEye, Roblox's Hyperion (formerly Byfron), and the descendants of PunkBuster, ships with a signature engine that walks loaded modules and memory regions looking for byte patterns and YARA hits. The database gets pushed on service startup, sometimes hot-refreshed mid-session, and the update cadence is the single largest predictor of how long any given cheat family stays alive.
The mechanic is mechanically simple. The engineering around it is not. A signature engine is only as good as its sample pipeline, and the pipeline is what determines whether a fresh build survives one hour or six weeks. This post walks through how the samples get harvested, how the patterns get extracted, what the on-disk rule formats actually look like, and the structural reasons signature scanning is losing ground against polymorphic build systems.
The sample pipeline: where AC vendors get cheats to fingerprint
Signatures do not fall from the sky. Every rule in a Vanguard or EAC database traces back to a sample sitting on an analyst's workstation, and vendors compete on how quickly they can acquire fresh builds. The realistic sourcing map looks like this:
- Public forum scrapes. UnknownCheats, Guided Hacking, and the long tail of Telegram and Discord servers get scraped continuously. Free releases and leaked private builds land in analyst queues within hours of posting. Vendors run automated crawlers with account rotation because the forums block known EAC and BattlEye IP ranges.
- Retail purchases. Analysts buy private cheats under burner identities using prepaid cards and residential proxies. This is the highest-value source and the reason serious cheat sellers vet buyers with voice calls, invite trees, and long-standing forum reputation.
- Honeypot accounts. Every big AC vendor runs game accounts that look like cheaters: skill-mismatched aim graphs, suspicious playtime, low trust factor. When a cheat loader phones home to a licensing server, the honeypot captures the HTTP handshake, the injected DLL, and often the loader binary itself.
- User-reported captures. Reports from in-game systems generate memory dumps around the reported player. If a private cheat is running, the dump contains the module list, the RWX regions, and enough context to reconstruct the loader.
- Researcher submissions and bounties. Independent researchers and rival cheat operators (yes, really) submit competitor samples for cash or clout. Roblox's public bug bounty formalized what EAC and Vanguard have done informally for years, and Facepunch's 2025 disclosure that Rust bans jumped 27% year over year to 338,000 total is a downstream indicator of how much fresh sample data the EAC pipeline is chewing through.
- Retail hardware seizures. In cheat-selling arrests (the Bloom/Chicken Drumstick case in China, the EngineOwning suit in Germany), law enforcement hands over servers full of build artifacts, source trees, and customer databases. That is a one-time windfall of signatures.
The KyTech engineering team tracks the observable behaviors of each of these channels because our own build pipeline has to route around all of them. A cheat that survives a scrape is not necessarily one that survives a retail purchase. The tradecraft on the reverse side, taking a shipped binary apart, is covered in reverse engineering game binaries.
Pattern extraction: IDA sig-maker byte patterns
Once a sample lands, the analyst opens it in IDA Pro or Ghidra, finds the interesting functions (aimbot targeting, memory readers, ESP renderers, hook trampolines), and extracts byte patterns around them. The classic tool is Sig Maker, which produces IDA-style patterns with wildcards for immediate operands and RIP-relative offsets.
A real IDA pattern for a stereotyped world-to-screen function looks like this:
48 8B C4 48 89 58 08 48 89 68 10 48 89 70 18 57
41 54 41 55 41 56 41 57 48 83 EC 40 48 8B 05
? ? ? ? 48 33 C4 48 89 44 24 30 0F 29 70 E8
0F 29 78 D8 44 0F 29 40 C8 F3 0F 10 3D ? ? ? ?
48 8B EA 49 8B F8
The ? bytes are wildcards. They cover things that legitimately vary between builds without changing the semantics: RIP-relative addresses to the stack cookie global, immediate offsets to string tables, the addresses of nearby floats. The non-wildcard bytes are opcodes, register encodings, and structural constants that are stable as long as the compiler options do not change dramatically.
Sig-maker's job is to find the minimum unique pattern for a given function. In practice the analyst picks a starting address, expands until the pattern is unique across the sample set, then trims wildcards where they can. The output goes into the vendor's signature database keyed by cheat family and function role.
struct SigEntry {
const char* family; // "SomeAimbotFamily"
const char* role; // "world_to_screen"
const uint8_t pattern[128];
const char mask[128]; // 'x' = literal, '?' = wildcard
uint32_t length;
uint8_t severity; // 0..255
};
At runtime the scanner walks each module's .text section, slides the pattern across it, and checks bytes where mask[i] == 'x'. Any hit above a severity threshold gets reported to the backend along with the module hash, load address, and the containing process context.
YARA rules: the string-and-condition layer
Byte patterns are strong for compiled function bodies but brittle for strings, config blobs, and multi-part indicators. That is where YARA earns its keep. YARA was authored by Victor Alvarez at VirusTotal and is the de facto pattern-matching format for malware and cheat analysts alike. Every big AC vendor ships a YARA engine or something close enough to one. Rules combine string constants, regex, hex chunks with jump wildcards, and a boolean condition tying them together.
A representative YARA rule for a hypothetical cheat family with the sort of tells analysts see every week:
rule Cheat_ExampleFamily_Loader_v3
{
meta:
author = "ac_research"
family = "ExampleFamily"
component = "usermode_loader"
created = "2026-07-14"
confidence = 90
strings:
$ua = "ExampleLoader/3." ascii
$lic_url = "/api/v3/license/validate" ascii
$key_hdr = "X-EF-Client-Key:" ascii
$mutex = "Global\\EF_LOAD_SINGLETON_{9F4A" wide
$stub = { 48 8B ?? 48 89 ?? ??
E8 ?? ?? ?? ??
48 85 C0 74 ??
48 8B ?? FF 15 ?? ?? ?? ?? }
$xor_key = { 6B 79 74 65 63 68 5F 78 6F 72 }
condition:
uint16(0) == 0x5A4D and
filesize < 4MB and
(
2 of ($ua, $lic_url, $key_hdr, $mutex) or
($stub and any of ($ua, $lic_url))
)
}
Reading top-down: the strings block enumerates indicators. ASCII substrings from the loader's HTTP client, a Unicode mutex name (single-instance guards are gold for signature writers because they cannot rotate without breaking the loader), an opcode chunk with jump wildcards, and a plausible XOR key constant. The condition requires an MZ header, a size ceiling that filters false positives against big legitimate binaries, and a logical combination of indicators.
YARA is the format because YARA scales. Vendors ship tens of thousands of rules and need bulk compilation, rule sets, external variables, and the ability to run against process memory as well as files. VirusTotal Enterprise uses YARA. Mandiant uses YARA. AC vendors did not invent this wheel.
Detection surfaces: where the scan actually runs
A cheat can hide from a bad scanner by living where the scanner does not look. Modern AC engines scan across four surfaces:
- Loaded module signatures. Every DLL and driver mapped into the game process gets hashed, pattern-scanned against the family database, and cross-checked against a known-good allowlist. This is the cheapest scan and catches everything that ships as a discrete PE.
- Memory region signatures. The scanner walks the VAD tree via
NtQueryVirtualMemory(usermode) or the equivalent PEB/VAD walks in kernel mode, and pattern-scans everyPAGE_EXECUTE_READWRITEregion. RWX pages are a giant red flag on their own. Any hit inside an RWX allocation is treated as high confidence. - Import Address Table hashes. For each loaded module the scanner hashes the sorted IAT function set. Cheats that hollow legitimate DLLs or that rebuild imports at runtime often end up with anomalous IATs that hash to known cheat families.
- String constant sweeps. ASCII and Unicode string tables inside modules and heap regions get grep'd for known indicators. Free cheats leak version strings, license URLs, discord invite links, and menu text constantly.
Kernel-mode ACs add a fifth surface: driver enumeration. Any loaded driver with a suspicious PsLoadedModuleList entry, an image signature failure, or a name matching a known signed-driver-abuse candidate gets logged. Riot Vanguard on Windows 11 requires TPM 2.0 for League of Legends (see Riot's own VAN9001 support article) precisely to make it harder for cheats to ship their own signed drivers. Riot cited older-hardware compatibility as the reason Secure Boot is not strictly enforced for League, unlike Valorant where Secure Boot is required. The distinction matters because the deeper cross-title picture, including EAC's move to ARM64 and the Vanguard On-Demand rollout that landed in June 2026, is covered in Easy Anti-Cheat explained.
Distribution and update cadence
Signatures do not ship in the game client. They ship in the AC service, which pulls updates on startup and, in the case of Vanguard and Hyperion, can hot-refresh mid-session by fetching signed rule bundles over HTTPS. The bundles are typically signed with an asymmetric key pinned in the AC binary and compressed to keep the update small.
The window between "sample lands on analyst desk" and "rule ships to every client" is the single most important number in this business. Empirically it looks like this:
| Cheat category | Time to first signature | Time to global rollout |
|---|---|---|
| Public free cheat | 1 to 24 hours | 24 to 72 hours |
| Paid public cheat | 3 to 14 days | 1 to 3 weeks |
| Private cheat with vetting | 2 to 8 weeks | 1 to 3 months |
| Hardware DMA cheat | Rarely caught by sig scan alone | N/A |
The KyTech engineering team watches this cadence closely because it dictates release windows. A build that ships on a Tuesday and gets sampled on a Thursday still has value through the weekend if the rollout is on a two-week cadence.
Pseudocode: what a signature scanner actually looks like
For clarity, the naive module scanner every AC ships as its baseline path. Written as pseudocode with real Windows APIs so the reader can trace it back to the Windows kernel driver documentation on learn.microsoft.com:
// Runs inside the AC service, elevated, with the target
// process opened via OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION).
void ScanProcess(HANDLE hProc, const SigDatabase& db) {
HMODULE mods[1024];
DWORD needed = 0;
if (!EnumProcessModulesEx(hProc, mods, sizeof(mods), &needed, LIST_MODULES_ALL))
return;
const size_t count = needed / sizeof(HMODULE);
for (size_t i = 0; i < count; ++i) {
MODULEINFO mi{};
if (!GetModuleInformation(hProc, mods[i], &mi, sizeof(mi))) continue;
// Skip Microsoft-signed system modules via WinVerifyTrust cache.
if (IsTrustedSystemModule(mods[i])) continue;
std::vector<uint8_t> buf(mi.SizeOfImage);
SIZE_T read = 0;
if (!ReadProcessMemory(hProc, mods[i], buf.data(), buf.size(), &read))
continue;
// Module-scope checks.
ReportIfKnownHash(Sha256(buf.data(), read));
ReportIfKnownIatHash(BuildIatHash(buf.data(), read));
// Byte-pattern scan across .text.
for (const auto& sig : db.byte_patterns) {
if (const uint8_t* hit = FindPattern(buf.data(), read, sig)) {
Report(sig.family, sig.role, mods[i], hit - buf.data());
}
}
// YARA scan over the raw module bytes.
db.yara.ScanMem(buf.data(), read, [&](const YaraMatch& m) {
Report(m.rule, m.family, mods[i], m.offset);
});
}
// Second pass: enumerate RWX regions outside module bounds.
ScanRwxRegions(hProc, db);
}
Kernel-mode AC drivers do the equivalent work by attaching to the target process (KeStackAttachProcess), walking the VAD tree, and using MmCopyVirtualMemory to read pages directly rather than going through ReadProcessMemory. The scan logic is identical. Only the memory access primitive changes. Note that EAC is only kernel-side on Windows; on Linux and Steam Deck it runs entirely in user space via Wine/Proton with no driver at all, which shifts the memory access model but not the signature logic.
Why polymorphism defeats naive signature scans
Every technique in this post assumes the cheat looks the same on two different machines. That assumption is what polymorphic build systems break. If every customer downloads a build with per-customer instruction scheduling, register renaming, junk instruction insertion, encrypted string tables decrypted at runtime, and a randomized IAT, then:
- The module hash is unique per install. Hash blocklists are useless.
- The IAT hash is unique per install. IAT fingerprinting is useless.
- The byte pattern from build A does not match build B. IDA sigs miss.
- YARA string constants live only in decrypted heap allocations that scanners with a naive file-scan-only pass never see.
Polymorphism does not defeat detection outright. Behavior-based detection, hypervisor telemetry, ML on gameplay traces, and hardware fingerprinting still catch cheats. Valve's VACnet, which trains on Overwatch conviction data and models aim trajectory rather than file contents, is the canonical example, and its evolution is walked through in VAC in 2026. But polymorphism does defeat the entire class of signature scans as historically deployed, and it forces AC vendors to lean on the more expensive detection surfaces they would rather keep in reserve. We covered the build-side mechanics in depth in polymorphic cheat builds, and the short version is: signature scanning is losing not because vendors are lazy but because the economics of pattern extraction do not scale against a build server that emits a fresh binary per user.
The AC industry knows this. Epic's October 2018 acquisition of Kamu (the Finnish company behind Easy Anti-Cheat), Roblox's 2022 acquisition of Byfron, and Riot's continued Vanguard hardening all point at the same conclusion. The signature layer is table stakes; the interesting engineering is on hypervisor-assisted introspection, gameplay telemetry, and hardware attestation. Signatures still catch the bottom half of the market. They no longer catch the top half.
How KyTech handles this
KyTech is a two-founder shop that started in 2025 and ships kernel-mode cheats for Apex Legends, Counter-Strike 2, Overwatch 2, Call of Duty Black Ops 7, Forza Horizon 6, and Roblox. Our detection posture is built around the reality this post describes: every static indicator we ship is a liability, and every one that stays constant across customers is a countdown to a signature.
The KyTech build pipeline emits per-user binaries with rotated instruction scheduling, encrypted string constants that never live in module memory in plaintext, and IAT construction at runtime so the module image on disk does not carry the fingerprint the scanner is looking for. Our kernel driver avoids RWX allocations in the game process entirely; anything the game process needs to see is written into read-only allocations after being staged elsewhere. We track the public sample-sourcing channels the same way AC vendors do, because a build that leaks to a public forum is a build we need to have already rotated out of production.
The Apex Legends line is currently in beta with an HWID spoofer for customers running EAC hardware bans. It is Apex-only for now while we finish qualifying the spoofer against the specific fingerprints EAC collects post-ban. KyTech Apex is where this signature-evasion architecture lives in production against a live EAC deployment. If you want the current build matrix, pricing, and supported game versions, the purchase page has the up-to-date list. If you want the reasoning behind the polymorphic build system that makes any of this work, the cross-linked post above is the companion piece.
Enough theory. get in.
The loader is one click away. Ring-0 kernel driver, polymorphic per download, memory-only injection. Six games across VAC, EAC, Ricochet, Byfron, and Warden.
Get in ›