Defensive use only. Detection methodologies published for server administrators, DFIR practitioners, and anti-cheat researchers. No evasion guidance is provided.
File Identity
The sample is a 64-bit Windows GUI executable, 16.70 MB on disk. It circulates under arbitrary randomised filenames (no fixed distribution name). All hashes below are verified against the production sample.
5d876d046d49eaa0fce206b334320f9d7d21afb05a2511a59c21b614b5ec80d06eca53ec9a868d89f0344ab7f1300300a0725244c5748d73151a604cea932f5717984978f5b335adf5038d9b21ece4ac984db11c2dd7238ab0c4ee093d533b2fe57075407052cce9ce7f95c0687fc6d7fd804673f29505b1b3695f6af1e6c55794f2ea21- Compiler: Microsoft Visual C/C++ 19.36.35721 (Visual Studio 2022 v17.6), LTCG release build
- Linker: Microsoft Linker 14.36.35721
- Architecture: PE32+ (x86-64), GUI subsystem
- Compile timestamp:
0x69568ccb→ 2026-01-01 15:03:39 UTC — New Year's Day, almost certainly manipulated - Checksum:
0x00000000(zeroed — standard for tampered or tool-built PE files) - SizeOfImage:
0x01B29000(28,606,464 bytes) — this value appears in Windows AppCompatCache/PcaSVC records alongside the file path and is a useful secondary indicator - ASLR / DEP: DYNAMIC_BASE, HIGH_ENTROPY_VA, NX_COMPAT all enabled
- Manifest:
requireAdministrator— UAC elevation is demanded on launch - VT detections: 51/70 at time of analysis; tagged
detect-debug-environment
Developer Intelligence — PDB Artifact
The PE debug directory retains a full, unstripped CodeView RSDS entry. The developer never configured their release build to strip symbols. This single omission exposes the project name, directory layout, and local machine username.
- Developer username:
apx— local Windows account on the build machine. - Project name:
ApateonDecoy— the Visual Studio solution name. The word "Decoy" is intentional branding; the binary is designed to present a decoy surface to analysts while the real payload executes from the encrypted.zywsection. - Sub-project:
ApateonDecoy - Self— the "Self" suffix suggests a self-injecting variant (as opposed to a separate loader targeting an external process). - Output binary:
DecoyLoader.exe(renamed before distribution). - Build config: x64 Release (LTCG) — fully optimised, no debug info was supposed to ship. The PDB path leaked despite this because the developer did not set
/DEBUG:NONEor strip the debug directory post-link.
52 53 44 53{6e6be30e-e46c-42b5-ba88-ad0df8fcbea5}11 (0x0B)The GUID uniquely identifies this specific build. Future variants from the same solution will carry a different GUID but the same project path structure and DecoyLoader.pdb filename until the developer corrects their build pipeline.
PE Section Analysis — Hollowed Standard Sections, Encrypted .zyw Payload
Nine sections are declared in the section table. All four conventional MSVC sections — .text, .rdata, .data, and .pdata — have a virtual size of zero and carry no file data. Their headers exist solely to maintain the appearance of a normal PE layout for superficial scanners. All actual code, read-only data, and the import table reside in the custom .zyw section.
| Section | Raw size | Entropy | Notes |
|---|---|---|---|
| .text | 0 bytes | — | Hollowed — header present, no content |
| .rdata | 0 bytes | — | Hollowed — header present, no content |
| .data | 0 bytes | — | Hollowed — header present, no content |
| .pdata | 0 bytes | — | Hollowed — header present, no content |
| .64X | 0 bytes | — | Hollow +RX section (10.1 MB virtual) — receives decrypted payload at runtime |
| .3L9 | 3,072 B | 0.36 | IAT (FirstThunk array) for all 19 import DLL entries — receives resolved fn ptrs from loader |
| .zyw | 17.5 MB | 7.82 | Encrypted payload — entry point at 47.3% offset (RVA 0x0125b7c9) |
| .reloc | 512 B | 2.59 | Base relocation table — normal |
| .rsrc | 512 B | 4.77 | Manifest (requireAdministrator) |
The .zyw section records an entropy of 7.82 out of a theoretical maximum of 8.0 (measured: 7.8167). This is near-perfect randomness, consistent with AES-CBC, ChaCha20, or a strong stream cipher applied to the full payload. The encrypted region spans 0x010B0E00 bytes (17,501,696 raw). The binary's entry point (RVA 0x0125B7C9, file offset 0x007E67C9) lands 47.3% into this section — deep inside the ciphertext.
CALL-slalom bootstrap: The first instruction at the entry point is a forward CALL +0x24B132 (to RVA 0x014A6900). At that landing site, another CALL +0x06A5F9 fires (to RVA 0x01510EFE), which patches the return-address stack then calls the decryptor entry at RVA 0x012D81AC. This four-hop CALL-chain "slaloms" through the encrypted data, landing at 5-byte CALL windows embedded among the ciphertext. The actual decryption stub begins at the fourth hop. Everything between the CALL instructions is encrypted payload data that is never executed. Disassemblers produce garbage output for all bytes except the CALL opcodes.
.3L9 is the IAT (Import Address Table / FirstThunk array) for all 19 imported DLLs. Its 3,072 bytes hold 19 sequentially-laid FirstThunk sub-arrays, each terminated by a zero QWORD. Pre-load values in .3L9 are hint/name RVAs pointing into plaintext islands within .zyw where the import function names (e.g. Sleep, MessageBoxW, CryptCreateHash) are stored verbatim. The Windows loader reads those name strings, resolves the addresses, and overwrites .3L9 in place. The import directory itself (IDD at RVA 0x01285F90) and all OriginalFirstThunk arrays are also embedded inside .zyw — so the entire import subsystem lives inside the encrypted section, with .3L9 as the only small unencrypted landing pad.
OLEAUT32.dll!VariantInit is imported by ordinal 8 (not by name) — a deliberate choice to reduce the number of readable API name strings. The CRT bridge DLLs (8 entries) are present but import only one function each, confirming these are MSVC runtime stubs, not direct calls from the payload.
The .64X section (10.1 MB virtual, no raw data, Read+Execute) is the hollow landing zone. The decryption routine writes the real payload into .64X at runtime, then transfers control there. The CFG check function pointer in the Load Config directory (RVA 0x014D8BD4) also falls inside .zyw, reinforcing that the payload is not decrypted by the OS loader — the stub handles all fixups before CFG is involved.
The section names .3L9, .64X, and .zyw are all non-standard and serve as reliable static indicators on their own. No legitimate software ships with these names.
Import Analysis — Full IAT Recovered (19 Imported Libraries)
Static analysis of the IAT embedded inside .zyw reveals 19 imported DLL records — KERNEL32.dll appears three times in separate IDD entries (Sleep alone, GetSystemTimeAsFileTime alone, and a six-function block), giving 8 KERNEL32 functions total. All remaining APIs are resolved at runtime via GetProcAddress; every function listed below is present in the static stub IAT.
Dynamic resolution bootstrap
KERNEL32.dll!LoadLibraryA— load arbitrary DLLs at runtimeKERNEL32.dll!GetProcAddress— resolve any export by name without static importKERNEL32.dll!GetModuleHandleA— locate already-loaded modulesKERNEL32.dll!HeapAlloc/HeapFree— direct heap management for unpacking buffersKERNEL32.dll!ExitProcess— hard exit on detection or license failure
These five APIs are sufficient to reconstruct the full Windows API surface at runtime. Every other call in the real payload is resolved this way, leaving no further trace in the static IAT.
Sandbox / timing evasion
KERNEL32.dll!Sleep— timed delay to defeat sandbox time-acceleration analysisKERNEL32.dll!GetSystemTimeAsFileTime— high-resolution timestamp for timing checks and seed generation
HWID & system fingerprinting
IPHLPAPI.DLL!GetAdaptersInfo— enumerates network adapters to extract MAC addresses; MAC-based HWID generationADVAPI32.dll!CryptCreateHash— hashes collected hardware identifiers (SHA-1 or MD5 of MAC/volume serial) to produce a stable HWID token for license validation
COM / WMI system query
ole32.dll!CoSetProxyBlanket— sets authentication level on a COM proxy; standard pattern before issuing WMI queries (Win32_ComputerSystem, Win32_BIOS) for VM/sandbox detectionOLEAUT32.dll!VariantInit— initialises VARIANT structures for WMI result parsing (named import, not ordinal)
C2 / license check
WININET.dll!InternetCloseHandle— HTTP session teardown; presence confirms outbound HTTP; opening/request handles resolved dynamically via GetProcAddress
User interaction
USER32.dll!MessageBoxW— modal dialog for license error, HWID mismatch, or ban notification
C++ runtime (MSVCP140 / VCRUNTIME140 / CRT stubs)
MSVCP140.dll!std::basic_ios::basic_ios()— C++ iostream construction; confirms C++ STL usage for string/stream objectsVCRUNTIME140_1.dll!__CxxFrameHandler4/VCRUNTIME140.dll!__current_exception_context— C++ exception handling (SEH/EH4)api-ms-win-crt-*(8 entries):free,rand,__stdio_common_vsprintf_s,strlen,_time64,___lc_codepage_func,terminate,__setusermatherr— CRT bridge stubs;rand+_time64together confirm time-seeded pseudo-random generation
Entry Point & CALL-Slalom Bootstrap
The entry point sits at RVA 0x0125B7C9 (file offset 0x007E67C9), 47.3% into the encrypted .zyw section. This is structurally distinct from conventional packers that place a decryptor stub in a separate small section. Here the bootstrap is woven into the ciphertext itself.
Execution chain (four confirmed hops):
The core decryptor at RVA 0x016BC10C processes the payload DWORD-by-DWORD using a multi-step transform: each 4-byte word is XOR'd with a rolling key (r10d), negated, offset by constant 0x4AEECBF1, rotated left 3 bits (ROL ecx, 3), then byte-swapped (BSWAP ecx) before being XOR'd into the output buffer. The decryptor body itself is obfuscated by a code virtualizer: after every ~40 real instructions, an indirect JMP transfers to a scattered code fragment at a discontiguous address (observed targets: 0x141ABA1E5, 0x1418A883D, 0x1418CE842). This pattern defeats linear disassembly of the decryption loop and substantially raises the cost of manual decompilation.
The CALL-slalom technique embeds 5-byte CALL instructions at specific offsets within the ciphertext. Each CALL pushes a return address onto the stack and jumps to the next hop. The surrounding bytes (garbage from encryption) are never executed. Disassemblers that do not follow the CALL chain output pure noise. After four hops, the stub uses LEA RSP, [RSP+0x18] to unwind the entire layered frame built by the CALL chain — no GetModuleHandle or image-base query is needed because the accumulated return addresses already encode the runtime load address.
The .3L9 section is the IAT — its pre-load QWORD values are hint/name RVAs into .zyw pointing to plaintext import name strings. First six entries (all terminator-separated, 16 bytes each):
Anti-Analysis Behaviour
VirusTotal's dynamic sandbox tags this binary with detect-debug-environment. Based on the import surface and binary structure, the following anti-analysis techniques are present or strongly implied:
- Encrypted payload (.zyw, entropy 7.82): The entire real codebase is ciphertext at rest. Static disassembly of the binary produces only the decryption stub — the actual bypass logic is never visible without dynamic execution or key extraction.
- CALL-slalom bootstrap (four hops): The entry point and three forward-trampoline CALLs are embedded inside the ciphertext. Standard disassemblers linearise from the EP and output garbage. Only a recursive CALL-following analysis reaches the decryptor. This defeats most static CFG-based scanners.
- Direct NT syscall (SYSCALL at RVA 0x012D81C4): Immediately after calling the core decryptor in Hop 3, a bare
SYSCALLinstruction invokes the Windows kernel without going throughntdll.dll. EDR products and AV engines that hook userland APIs (NtCreateSection,NtWriteVirtualMemory,NtResumeThread) at the ntdll layer will not observe these calls. This is a tier-2 EDR-evasion technique. - VM-obfuscated decryptor (code virtualizer at RVA 0x016BC10C): The decryption loop body is run through a code virtualizer. Every ~40 real instructions, an indirect JMP transfers to a scattered code fragment at a discontiguous address. Observed dispatcher targets include
0x141ABA1E5,0x1418A883D, and0x1418CE842. Decompilers produce incomplete output and the function cannot be meaningfully statically analysed without emulating the entire dispatcher chain. - Debugger detection: Confirmed by VT behavioral tag. Likely uses
IsDebuggerPresent,CheckRemoteDebuggerPresent, orNtQueryInformationProcess(ProcessDebugPort), all resolved dynamically so they do not appear in the static IAT. - Timing evasion via Sleep + GetSystemTimeAsFileTime: Extended sleep delays defeat sandbox environments that time-accelerate execution.
GetSystemTimeAsFileTimeprovides a high-resolution timestamp for timing delta checks (detect x100 time acceleration). - VM / sandbox detection via CoSetProxyBlanket + WMI: WMI queries to
Win32_ComputerSystemandWin32_BIOSidentify Hyper-V, VMware, VirtualBox, and sandboxie environments by manufacturer strings and BIOS version fields. The process exits cleanly if a virtual environment is detected. - UAC elevation required: The
requireAdministratormanifest prevents execution in low-integrity analysis environments. Automated sandboxes running as standard user will trigger a UAC prompt and halt. - Zeroed PE checksum: The optional header checksum field is
0x00000000. Windows does not enforce this for user-mode executables, but security tools that validate PE integrity can flag it as a tamper indicator. - MEM_NOT_PAGED on .zyw (0x08000000 characteristic): The encrypted payload section is marked non-pageable, keeping it in physical RAM and preventing it from being swapped to the pagefile. Memory-forensics tools that harvest pagefile data to find decrypted payloads will not find .zyw content there. This flag is normally used by kernel drivers — its presence in user-mode code is a hard anti-forensics indicator.
- No exports + hollow standard sections: The binary exports nothing (no export directory) and has four hollow standard sections (
.text,.rdata,.data,.pdata). Tools that inspect exports or standard section content return empty results.
Timestamp Analysis — Fake New Year's Day Compile Date
The PE COFF header compile timestamp is 0x69568CCB, which decodes to 2026-01-01 15:03:39 UTC. A timestamp set to the first day of a calendar year is a near-universal indicator of deliberate manipulation — legitimate builds are distributed immediately after compilation, not held for an exact calendar boundary.
This timestamp is stored in AppCompatCache (ShimCache) and the PCA/PcaSVC compatibility database alongside the executable's path and SizeOfImage. When reviewing AppCompat records, an investigator will see:
The SizeOfImage value 0x01B29000 is the PE optional header field recording the total virtual size of the loaded image. It appears verbatim in PcaSVC/AppCompatCache records and serves as a complementary indicator — the combination of the round-date timestamp and this specific image size uniquely identifies the binary even if it has been renamed and the file deleted from disk.
Treat any FiveM-adjacent executable with a PE timestamp on 2026-01-01 as a confirmed timestomping indicator. Escalate for full hash verification immediately.
Primary IOCs & Detection Checklist
SHA-256 hash (definitive)
Compute SHA-256 of any suspected binary. A match against 9a868d89f0344ab7f1300300a0725244c5748d73151a604cea932f5717984978 is definitive attribution regardless of filename or path. Additional hashes: MD5 5d876d046d49eaa0fce206b334320f9d, SHA-1 7d21afb05a2511a59c21b614b5ec80d06eca53ec.
Full PDB path in PE debug directory
The complete string C:\Users\apx\Downloads\ApateonDecoy - Self\ApateonDecoy - Self\x64\Release\DecoyLoader.pdb is present in the CodeView RSDS debug entry (RSDS signature 52 53 44 53 at file offset 0x10ADC84). Any hex editor, DIE, or strings.exe will surface it. This string cannot be removed without relinking the binary.
Three custom section names: .3L9, .64X, and .zyw
Use a PE viewer or dumpbin /headers to inspect the section table. The combination of .3L9 (IAT), .64X (hollow execution target), and .zyw (encrypted payload) is unique to this packer family. Any one alone is suspicious; all three together is definitive. The .zyw section entropy of 7.82/8.0 — near-maximum randomness — is immediately visible in any entropy-visualising tool (DIE, PEStudio, Binwalk).
PE compile timestamp 2026-01-01
In AppCompatCache or the raw PE COFF header (bytes 8–11 of the PE signature), the timestamp 0x69568CCB decodes to New Year's Day 2026. Combined with the SizeOfImage = 0x01B29000 stored in the same PcaSVC record, this uniquely identifies the binary in forensic artefacts even after the file has been removed from disk.
Imphash
The import hash f5b335adf5038d9b21ece4ac984db11c covers only the visible stub IAT. Since all real imports are resolved dynamically, variants that share the same packing stub but differ in payload will produce the same imphash. This makes imphash useful for clustering builds from the same packer — not for variant differentiation.
YARA Detection Rule
The rule below covers the current sample via three independent anchors: the full PDB developer path, the custom packer section names, and the PE compile timestamp. Any one of the first two conditions alone is sufficient for high-confidence detection. Requiring all three reduces false-positive risk to effectively zero.
rule ApateonDecoy_Wizard_Bypass {
meta:
description = "Wizard FiveM bypass — ApateonDecoy/DecoyLoader by developer apx"
author = "Clubhouse AC Research"
date = "2026-06-06"
sha256 = "9a868d89f0344ab7f1300300a0725244c5748d73151a604cea932f5717984978"
imphash = "f5b335adf5038d9b21ece4ac984db11c"
strings:
// Full unstripped PDB path — structural, cannot be trivially patched
$pdb_full = "ApateonDecoy - Self\x64\Release\DecoyLoader.pdb" ascii nocase
// Short form — matches if full path is partially overwritten
$pdb_short = "DecoyLoader.pdb" ascii nocase
// CodeView RSDS signature immediately before GUID+PDB path
$rsds = { 52 53 44 53 }
// Custom packer section names — non-standard, unique to this tool
$sec_zyw = ".zyw" ascii
$sec_3l9 = ".3L9" ascii
$sec_64x = ".64X" ascii
// CALL-slalom bootstrap bytes at entry point (build-specific)
// e8 32 b1 24 00 = CALL +0x24B132 into encrypted payload
$ep_call = { E8 32 B1 24 00 75 E2 }
// PDB GUID bytes (CodeView RSDS entry — uniquely identifies this build)
$pdb_guid = { 0E E3 6B 6E 6C E4 B5 42 BA 88 AD 0D F8 FC BE A5 }
// PE compile timestamp bytes (little-endian) at COFF offset 8
// 0x69568CCB = 2026-01-01 15:03:39 UTC
$ts = { CB 8C 56 69 }
condition:
uint16(0) == 0x5A4D and // MZ header
filesize > 15MB and // rules out stub/dropper variants
(
$pdb_full or
($pdb_short and $rsds) or
($pdb_guid) or
($sec_zyw and $sec_3l9 and $sec_64x)
)
}Add the $ts string to the condition as an additional optional anchor when hunting in forensic artefact corpuses where the file is not available for section-table inspection (e.g., prefetch imports, ShimCache entries, or MFT $DATA runs).
Detection Notes
ApateonDecoy is an above-average effort in terms of payload protection — near-maximum-entropy encryption of the real code, hollowed standard sections, and dynamic API resolution prevent meaningful static reverse engineering of the bypass logic itself. However, the developer made a catastrophic OPSEC error by shipping with an unstripped PDB debug path, leaving the project name, binary name, and their own Windows username embedded in every distributed copy.
The three detection anchors — full PDB path, custom section names, and New Year's Day PE timestamp — are independent. A future variant that strips the PDB will still expose .zyw and .3L9. A variant rebuilt with different section names will still carry the RSDS entry if the developer does not fix their build configuration. Only a full rebuild from a corrected project with proper strip flags, new section names, and a normalised timestamp would evade all three simultaneously. The YARA rule above is designed to remain valid through incremental evasion attempts.
The GetAdaptersInfo + CryptCreateHash + CoSetProxyBlanket combination in the stub IAT fingerprints the HWID and VM-check subsystem regardless of payload encryption. Any binary presenting this exact three-API combination alongside Sleep and InternetCloseHandle in a minimal IAT should be treated as a strong behavioural candidate for this family even if hashes and section names have been rotated.