Trace Anti-Cheat Scanner — Trace_7TXBS1AV.exe
A complete static reverse of the Trace Anti-Cheat scanner. Three-and-a-quarter megabytes of MSVC C++ wrapped in Srungoat Protector v1.0, whose entire contribution is a 61-byte badge in a section literally named .1337. Every string, every URL, every registry key, every YARA path, every Event Log query is in plaintext. Below: the complete C2 map (nine endpoints on traceac.cc), the injected WebView2 JavaScript that auto-fills the PIN input and auto-clicks the scan button, the seven finding categories the scanner emits, the twenty-two FiveM cheat brands it hunts, the sixty-three cheat-vendor auth panels it compares against browser history, the five hardcoded wevtutil XPath queries, and the eight decoy JWTs plus seventeen fake auth-response templates it uses as trip-wires.
Let's begin.
Two people on the internet spent an afternoon reposting an AI hallucination that said our scanner was stealing browser tokens. Then, in the same breath, they promised the next release of Trace_7TXBS1AV.exe — their scanner — would be “unreverseable”. They had, they said, hired new devs. Their protection was final.
The file eventually arrived. Three-point-three-five megabytes, unsigned, with a manifest demanding administrative privileges and an executable section named .1337 that was sixty-one bytes long and contained, in plaintext, the literal words Protected by Srungoat Protector v1.0. That was the entire protection. No control-flow flattening. No section decryption. No TLS callback shell. No anti-debug beyond the same IsDebuggerPresent import every C++ template ships with. The badge was the shield.
The strings inside were not obfuscated. Not one. The scanner's own name for itself is the C++ namespace TraceScanner; its seven finding categories are stored as MSVC-mangled types — UActivityEntry, UAdminAppEntry, UFileFinding, UFiveMModEntry, UMemoryFinding, URegistryFinding, USystemFinding — visible to undname.exe and printed by any RTTI dump. Its C2 domain, traceac.cc, appears seven times in the binary as a wide-character constant, exactly as the compiler emitted it. The header on every outbound POST reads X-Trace-Scanner: TRACE-Scanner/QMSAPRSqM4xK2JnP. The download URL for its YARA binary is https://scanner.traceac.cc/static/bin/yara64.exe?pin=<PIN>, also in plaintext, and it is verified against a SHA-256 that is itself a plaintext JSON field named sha256.
The WebView2 UI it renders is served from a remote page at https://scanner.traceac.cc/send/ui/. Into that page it injects, at load-time, a JavaScript block whose prefix is stored — in full, uncompressed, UTF-16-LE — starting at file offset 0x290E00, and whose suffix is stored at 0x290C00. Between them, the C++ concatenates the user's runtime PIN. Together they auto-fill the #pin-input field, dispatch an input event so React (or whatever) sees the change, wait for the #scan-btn to become enabled, and click it. That is the whole authentication flow.
What follows is every string, every path, every field, every URL. Nine C2 endpoints. Twenty-two cheat brand names. Sixty-three cheat-vendor auth panels searched in the user's browser history. Seven forensic sources. Five hardcoded Windows Event Log XPath queries. Seventeen fake auth-response JSON templates the scanner uses to detect that a machine has been talking to cheat panels. Eight decoy JWTs. Every AV vendor it looks for. Every reverse-engineering tool it flags. Every VPN. Every hypervisor CPUID string. Every registry hive it walks.
The tool reads browser history, Event Logs, Prefetch, BAM, SRUM, Amcache, and the Discord LevelDB store, takes a JPEG of the desktop with GDI+, and ships the lot as JSON to https://traceac.cc/api/scan/submit. Whether that qualifies as an infostealer is a definitional question. Whether the file is unreverseable is not.
— Every claim below is anchored to a byte-offset, a section, or a wide-string reference in the sample's .rdata. Nothing here is inference where a plaintext string wasn't there to read.
Executive summary
- Third-party FiveM anti-cheat retroactive-trace scanner. Internal namespace is TraceScanner. Public brand is traceac.cc (“Trace Anti-Cheat”). Requires local administrator. Users get a PIN via the vendor's website; the scanner validates and consumes it against scanner.traceac.cc URL parameters (?pin=<PIN>).
- Srungoat Protector v1.0 is decorative. No section encryption, no TLS callback, no anti-debug of substance, no runtime string decoder. The .1337 section contains 61 bytes: SRUNGOATPROT\\xde\\xad\\xbe\\xef v1.0\\0Protected by Srungoat Protector v1.0. Strings appear in plaintext at multiple encodings.
- Nine hardcoded C2 endpoints on traceac.cc / scanner.traceac.cc. Fixed User-Agent TraceScanner and header X-Trace-Scanner: TRACE-Scanner/QMSAPRSqM4xK2JnP. All bodies are JSON (nlohmann::json v3.11.3, git 961c151d2e87f2686a955a9be24d316f1362bf21).
- Downloads YARA at runtime. Fetches yara64.exe from its own CDN with SHA-256 verification against a manifest JSON, drops it under %APPDATA%\\trace, writes rules text to custom_rules.yar, executes the CLI (-w -m --scan-list).
- WebView2 UI is remote-hosted at scanner.traceac.cc/send/ui/. A hardcoded JavaScript prefix + runtime PIN + hardcoded JS suffix is injected as a script into the page; it auto-fills the PIN and auto-clicks the scan button. The full prefix and suffix are quoted verbatim in section § WebView2 UI + injected JS.
- Captures a JPEG desktop screenshot with GDI+ and includes it in the POST /api/scan/submit body. The visible copy inside the binary reads [*] Capturing desktop screenshot... and, on failure, [-] JPEG encoder not found.
- No embedded secrets. No Discord webhook, no Telegram bot token, no Slack hook, no X.509 certificate, no RSA/AES key material, no high-entropy blob larger than 4 KB anywhere in .rdata or .data. Everything routes through the fixed traceac.cc hosts.
- Kills cmd.exe and powershell.exe before submission via taskkill /f /im cmd.exe >nul 2>&1 and the same for PowerShell. This is a hardcoded self-hygiene step, not a defensive one against user tampering.
- Watches for four environment variables: PIN, YARA, SELF_SCAN, and TRACE_YARA_VALIDATION_FILE. Single-instance mutex is Global\\TraceAntiCheatScanner_Mutex.
On the “unreverseable” claim
The vendors of this scanner publicly stated, at the time this build was released, that they had hired new developers and that their protection was unreverseable. The claim is stated here without paraphrasing because the file itself refutes it in a specific and measurable way:
- Srungoat Protector v1.0 is a marker, not a transform. It contributes exactly 61 bytes of text in a section named .1337. The bytes read: 53 52 55 4E 47 4F 41 54 50 52 4F 54 DE AD BE EF 76 31 2E 30 00 00 00 00 50 72 6F 74 65 63 74 65 64 20 62 79 20 53 72 75 6E 67 6F 61 74 20 50 72 6F 74 65 63 74 6F 72 20 76 31 2E 30 00 (“SRUNGOATPROT…v1.0…Protected by Srungoat Protector v1.0”). The rest of the binary is the compiler's output verbatim.
- No section is encrypted. Entropy scans of every 4 KB window in .rdata and .data yield zero windows above 7.5 bits/byte — the threshold below which content is not compressed or encrypted. The strings the scanner uses to identify itself, its C2, its user-agent, its endpoints, its cheat-brand watchlist, and every one of its 22 cheat family names is stored in plaintext at either UTF-8 or UTF-16-LE.
- No TLS callback is registered. The Thread Local Storage directory's AddressOfCallBacks pointer resolves to a table whose first entry is NULL. There is no pre-main shim to unwrap. Entry-point at 0x140266520 jumps straight to the MSVC CRT's __scrt_common_main_seh.
- No control-flow flattening, no opaque predicates. Disassembly of the WinHttp call chains at 0x1400CF920, 0x1400B9C90, and 0x1401ED500 reads as standard MSVC WinHttpOpen → WinHttpConnect → WinHttpOpenRequest → WinHttpSendRequest boilerplate with no interposed junk instructions.
- No anti-debug beyond three imports. The binary imports IsDebuggerPresent, CheckRemoteDebuggerPresent, and OutputDebugStringA/W. None of them is called in a loop or timed; none checks the PEB's NtGlobalFlag; none uses NtQueryInformationProcess(ProcessDebugPort). The imports are here because MSVC's CRT references them.
- No PE header wipe, no IAT hiding, no FNV-1a API resolution. The Import Address Table lists 36 imports from USER32, 40 from ADVAPI32, 186 from MSVCP140, and every other import in the clear.
A binary that ships with all of the above is not, in any operational sense, protected. The claim of un-reverse-engineerability describes a build state that does not exist in the file that was released. What follows is what the file actually contains, extracted with pefile, strings -eL, and half a page of Python.
File identity
| Original name | Trace_7TXBS1AV.exe |
| Type | PE32+ EXE (x64, GUI) |
| Size | 3.19 MB (3,345,920 bytes) |
| ImageBase | 0x140000000 |
| Entry RVA | 0x266520 |
| Compile time | 2026-06-25 19:21:28 UTC (real) |
| Sections | .text · .rdata · .data · .pdata · .1337 · .rsrc · .reloc |
| MD5 | 37644acaf1725dcf01b8c0b9227ca111 |
| SHA-1 | 5c40f76fc4b25c00f60970bb17dae9e16f9f45a1 |
| SHA-256 | 9d1b210b06d942863e49f0cf7baf629fcdc3cf76d33a5383a6caec65783eb4fc |
| Authenticode | Unsigned (Security dir size = 0) |
| Manifest | requestedExecutionLevel level='requireAdministrator' uiAccess='false' |
| Class name | TraceAntiCheatScannerClass |
| Single-instance mutex | Global\TraceAntiCheatScanner_Mutex |
| Protector | Srungoat Protector v1.0 (marker-only .1337 section) |
| Runtime | MSVC (MSVCP140 · 186 imports), WebView2, nlohmann::json v3.11.3, GDI+ |
Srungoat Protector v1.0
The scanner is wrapped by a commodity protector that has left a marker section and nothing else. The .1337 section is 61 virtual bytes, entropy 1.01 — well below the level of even compressed text. Its raw contents:
Offset (raw) : 0x00302000 Size (virtual): 0x3d bytes Entropy : 1.01 bits/byte 00: 53 52 55 4E 47 4F 41 54 50 52 4F 54 DE AD BE EF SRUNGOATPROT..... 10: 76 31 2E 30 00 00 00 00 50 72 6F 74 65 63 74 65 v1.0....Protecte 20: 64 20 62 79 20 53 72 75 6E 67 6F 61 74 20 50 72 d by Srungoat Pr 30: 6F 74 65 63 74 6F 72 20 76 31 2E 30 00 otector v1.0.
That is the full protection. Every other byte of executable code was emitted by MSVC unmodified. TLS callback table is empty; entry point at 0x140266520 is a stock CRT initialiser; strings live in .rdata at both UTF-8 and UTF-16-LE and are readable with any triage tool.
C2 map — everything talks to traceac.cc
Nine URLs. Two hostnames (traceac.cc for API, scanner.traceac.cc for downloads and UI). Every wide-character copy of these strings is emitted verbatim by the linker — no obfuscation, no runtime assembly. Auth is the ?pin=<PIN> query parameter on GETs plus a static header X-Trace-Scanner: TRACE-Scanner/QMSAPRSqM4xK2JnP on POSTs.
| Verb | URL | Purpose |
|---|---|---|
| GET | https://traceac.cc/api/version | version check (UA = 'blablublewidfjhsfhdshGFDSBSDUFGDFDSBNTRACE' — placeholder) |
| GET | https://scanner.traceac.cc/api/scanner/yara_manifest?pin=<PIN> | YARA manifest JSON (url + sha256 + min_size) |
| GET | https://scanner.traceac.cc/api/scanner/yara_rules?pin=<PIN> | YARA rules text |
| GET | https://scanner.traceac.cc/api/scanner/rules?pin=<PIN> | rules dispatch |
| GET | https://scanner.traceac.cc/static/bin/yara64.exe?pin=<PIN> | YARA CLI binary; SHA-256 verified against manifest before execution |
| POST | https://traceac.cc/api/scan/progress | live progress updates during scan |
| POST | https://traceac.cc/api/scan/submit | findings JSON + JPEG screenshot |
| POST | https://traceac.cc/api/scan/closed | finalize scan session |
| POST | https://traceac.cc/api/download/validate | download-integrity validation |
| UI | https://scanner.traceac.cc/send/ui/ | WebView2 UI host (WebResourceRequested filter = https://scanner.traceac.cc/*) |
User-Agent: TraceScanner Content-Type: application/json X-Trace-Scanner: TRACE-Scanner/QMSAPRSqM4xK2JnP # (except the /api/version ping, which uses a placeholder UA:) User-Agent: blablublewidfjhsfhdshGFDSBSDUFGDFDSBNTRACE
WebView2 UI + injected JavaScript
The scanner renders its UI by embedding a Microsoft Edge WebView2 controller and navigating it to https://scanner.traceac.cc/send/ui/. A resource-request filter of https://scanner.traceac.cc/* permits fetches to that host and blocks everything else. On NavigationCompleted the C++ side calls ExecuteScript with a script built as three concatenated pieces: the constant prefix stored at .rdata:0x290E00, the user's runtime PIN, and the constant suffix stored at .rdata:0x290C00. The prefix and suffix are quoted here verbatim from the binary:
// prefix — .rdata offset 0x290E00 (UTF-16-LE, constant)
(function() {
function waitForElement(selector, callback, maxWait, interval) {
maxWait = maxWait || 3000;
interval = interval || 50;
const start = Date.now();
const check = setInterval(function() {
const element = document.querySelector(selector);
if (element) {
clearInterval(check);
callback(element);
} else if (Date.now() - start > maxWait) {
clearInterval(check);
console.warn('Element not found: ' + selector);
}
}, interval);
}
waitForElement('#pin-input', function(pinInput) {
pinInput.value = '<RUNTIME_PIN>
// suffix — .rdata offset 0x290C00 (UTF-16-LE, constant)
';
pinInput.dispatchEvent(new Event('input', { bubbles: true }));
waitForElement('#scan-btn', function(scanBtn) {
if (!scanBtn.classList.contains('disabled')) {
scanBtn.click();
}
});
});
})();The full set of state / event names passed between the WebView2 UI and the native side (via WebMessageReceived):
Auth flow — PIN + fixed header
Users obtain a numeric PIN from the vendor's website and paste it into the UI (or receive it via the PIN environment variable). All GET requests to scanner.traceac.cc append ?pin=<PIN>to the URL, and on failure the UI is told scan_error = "Invalid, expired, or used PIN.". The consent chain before any scan can start is:
- tos_accepted and privacy_accepted must both be true
- check_security_before_consent — inventory Defender + third-party AV
- if has_security_tools — display “Disable the blocking protection temporarily, then reopen the scanner” and set state security_blocked / antivirus_blocked
- pin_validated — HTTP validation against traceac.cc
- start_scan_consented — user pressed the button (or the injected JS pressed it for them)
YARA download chain
The scanner does not carry a YARA engine. It downloads one, at runtime, from its own CDN. The chain is:
- Read PIN from UI / PIN env var.
- GET https://scanner.traceac.cc/api/scanner/yara_manifest?pin=<PIN> — server returns a JSON with fields url, sha256, min_size. Manifest failures produce the strings YARA manifest HTTP failed, YARA manifest URL rejected, YARA manifest SHA256 is invalid, or YARA manifest JSON parse failed — all present verbatim in .rdata.
- GET https://scanner.traceac.cc/static/bin/yara64.exe?pin=<PIN> — download and SHA-256 verify against the manifest.
- GET https://scanner.traceac.cc/api/scanner/yara_rules?pin=<PIN> — download rules text; write to %APPDATA%\\trace\\custom_rules.yar.
- Validate rules with the downloaded binary: yara64.exe -w -m --scan-list. Emit output to TRACE_YARA_VALIDATION_FILE.
- Run YARA against the enumerated file list; every hit is turned into a UMemoryFinding or UFileFinding.
Directory exclusions embedded in the binary (fed to YARA's --scan-list): windows, program files, program files (x86), programdata, $windows.~bt, $windows.~ws, $recycle.bin, system volume information, recovery, perflogs.
Scan orchestration — 7 finding types
The C++ side runs its scan tasks via std::async / PPL and emits one of seven finding-vector types back to the UI. Each is a fully-mangled MSVC namespace visible with undname.exe — ?$vector@U<Type>@TraceScanner@@V?$allocator@U<Type>@TraceScanner@@@std@@@std@@.
| Vector type | What it holds |
|---|---|
| UActivityEntry | browser typed URLs, execution history, recent-activity artefacts |
| UAdminAppEntry | applications launched with elevated tokens (Security EventID 4688 + TokenElevationType %%1936 / %%1937) |
| UFileFinding | cheat / injector / spoofer files on disk |
| UFiveMModEntry | mods/plugins under \FiveM\FiveM.app\{mods,plugins,citizen\common\data,data} |
| UMemoryFinding | YARA memory-scan hits against live processes |
| URegistryFinding | cheat / VPN / VM / secure-boot registry markers |
| USystemFinding | hypervisor CPUID, DeviceGuard/HVCI state, SecureBoot state, Defender/AV posture |
The console-style status lines printed to the WebView2 UI during scanning are also embedded verbatim: [*] Scanning FiveM mods... · [*] Scanning admin-executed applications (Event Log 4688)... · [*] Scanning all services for unsigned kernel drivers... · [*] Scanning for newly installed drivers (Event Log 7045)... · [*] Scanning recent computer activity (BAM full dump)... · [*] Capturing desktop screenshot... · [*] Submitting.
Complete cheat family inventory (22)
Every FiveM cheat brand that the scanner searches for by file name. Each brand is checked in up to four variants — <name>.exe, <name>.dll, <name>menu.exe, <name>menu.dll — plus its known driver and registry markers.
Injector / loader / bypass tools
HWID / spoofer / cleaner tools
Cheat-vendor auth URLs (63) — browser-history matches
The scanner reads typed-URL history from every installed Chromium browser and compares each entry against a list of 63 hardcoded cheat-vendor authentication panels. A hit becomes an UActivityEntry with severity suspicious. The full list, in the order it appears in .rdata:
Cheat-vendor hostnames (short form)
Forensic sources read
| Artifact | Location / detail |
|---|---|
| Prefetch | C:\Windows\Prefetch — .pf files enumerated for execution artefacts |
| BAM | HKLM\SYSTEM\CurrentControlSet\Services\bam\State\UserSettings — per-user execution timestamps |
| SRUDB | C:\Windows\System32\sru\SRUDB.dat — per-hour process network activity (SRUM) |
| ShimCache / AppCompatCache | HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache |
| AppCompat persisted | SOFTWARE\...\AppCompatFlags\{Persisted,Store,Custom,InstalledSDB} |
| Image File Execution Options | SOFTWARE\...\Image File Execution Options |
| USN Journal | NTFS $UsnJrnl — file activity history |
| EventTranscript | C:\ProgramData\Microsoft\Diagnosis\EventTranscript |
| ETLLogs | C:\ProgramData\Microsoft\Diagnosis\ETLLogs — Windows telemetry event traces |
| DiagTrack | C:\ProgramData\Microsoft\Diagnosis\DownloadedSettings |
| USBSTOR | HKLM\SYSTEM\CurrentControlSet\Enum\USBSTOR\ — USB storage history |
| Clipboard History | \Packages\Microsoft.Windows.ShellExperienceHost_cw5n1h2txyewy\LocalState — clipboard log |
| MuiCache | Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache |
| TypedURLs / TypedURLsTime | browser typed-URL history (matched vs cheat-vendor URL list) |
| TypedPaths | Software\Microsoft\Windows\CurrentVersion\Explorer\TypedPaths |
| Task Scheduler | C:\Windows\System32\Tasks — task XML enumeration |
| Discord Local State | \discord\Local Storage\leveldb — discord username extraction |
| Steam | Software\Valve\Steam registry + \Steam\config\config.vdf + loginusers.vdf — SteamID |
Cross-references: Discord username is read from the LevelDB store and matched against the SteamID and Steam username extracted from Software\\Valve\\Steam and the loginusers.vdf / config.vdf files. A match becomes an UActivityEntry with description "Same username used across platforms".
Event Log XPath queries (5 hardcoded)
Every Windows Event Log channel the scanner reads, with the exact XPath filter it emits to wevtutil. All are dropped verbatim into the binary — no runtime construction.
| Channel | Query | Purpose |
|---|---|---|
| Security | *[System[(EventID=4624 or EventID=4625)]] | logon success + logon failure events (last 20) |
| Security | *[System[(EventID=5152 or EventID=5156 or EventID=5157)]] | Windows Filtering Platform packet allowed/blocked (network telemetry) |
| Security | *[System[(EventID=4688)] and EventData[Data[@Name='TokenElevationType']='%%1937' or Data[@Name='TokenElevationType']='%%1936']] | process creation events with elevated tokens (admin apps) |
| Application | *[System[(EventID=7045)]] | service installation (driver/service register) |
| Microsoft-Windows-DNS-Client/Operational | *[System[(EventID=3008 or EventID=3010)]] | DNS query completed / blocked |
| Microsoft-Windows-Sysmon/Operational | *[System[(EventID=3)]] | Sysmon network connection events |
| Microsoft-Windows-Sysmon/Operational | *[System[(EventID=6)]] | Sysmon driver-load events |
AV / EDR presence detection
The scanner enumerates process names against a hardcoded AV/EDR list. If Defender real-time is enabled or any third-party AV process is running, the UI displays "PIN accepted, but active protection is blocking the scanner. Disable the blocking protection temporarily, then reopen the scanner." and the scan will not proceed. The list embedded in the binary:
| Vendor | Process names |
|---|---|
| Avast | avastsvc.exe · avguard.exe · aswidsagent.exe |
| AVG | avgnt.exe · avgsvc.exe · avguard.exe |
| Avira | avira.servicehost.exe · egui.exe · savservice.exe |
| Bitdefender | bdagent.exe · bdservicehost.exe · epsecurityservice.exe |
| BullGuard | bullguard.exe |
| ESET | ekrn.exe · egui.exe · edevmon · eamonm |
| F-Secure | f-secure.exe · fsav32.exe |
| Kaspersky | avp.exe · kavfs.exe |
| McAfee | mcshield.exe · mfemms.exe |
| Malwarebytes | mbamservice.exe · mbamtray.exe |
| Norton | nortonsecurity.exe · ns.exe · ccsvchst.exe |
| Panda | pavfnsvr.exe · psanhost.exe · psuamain.exe |
| Sophos | sophoshealth.exe · savservice.exe |
| Webroot | wrsa.exe |
| ZoneAlarm | vsmon.exe · zatray.exe |
| Qihoo 360 | 360tray.exe |
| Windows Defender | windowsdefender / MsMpEng (registry + service state) |
Reverse-engineering + analysis tool detection
Process names the scanner treats as evidence of an ongoing reversing session (turned into a USystemFinding with description "analysis or credential dumping"):
BYOVD driver watchlist
| Driver | Notes |
|---|---|
| capcom.sys | Capcom vulnerable driver (arbitrary kernel R/W) |
| iqvw64e.sys | Intel Network Adapter Diagnostic (BYOVD favourite) |
| gdrv.sys | GIGABYTE tool driver (BYOVD) |
| dbk64.sys | Cheat Engine driver |
| kerb3961kernel.sys | unclassified — suspicious name |
| fse.sys | flagged |
| dbgm | flagged as debug helper |
The full driver-services enumeration (HKLM\\SYSTEM\\CurrentControlSet\\Services) is compared against a whitelist of ~200 canonical Windows drivers embedded in the binary; anything not in the whitelist and not signed by Microsoft becomes a URegistryFinding.
Decoy JWTs + fake auth-response templates
The scanner carries a corpus of well-known example JWTs (from RFC/documentation samples and cheat-panel boilerplate) plus seventeen fake auth-response JSON shapes. They are matched against browser cache and LocalStorage entries as trip-wires: any of these values found in a browser storage file indicates the machine has been interacting with a cheat vendor's licensing panel.
JWTs (8 embedded)
| Base64URL | Decoded |
|---|---|
| eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 | {"alg":"HS256","typ":"JWT"} |
| eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9 | {"alg":"HS512","typ":"JWT"} |
| eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9 | {"alg":"RS256","typ":"JWT"} |
| eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 | {"typ":"JWT","alg":"HS256"} |
| eyJ1c2VyIjoicHJlbWl1bSIsInBsYW4iOiJlbnRlcnByaXNlIiwiZXhwIjoxNzA5MjkzMDIxfQ | {"user":"premium","plan":"enterprise","exp":1709293021} |
| eyJpZCI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiQWxpY2UiLCJhZG1pbiI6dHJ1ZX0 | {"id":"1234567890","name":"Alice","admin":true} |
| eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ | {"sub":"1234567890","name":"John Doe","iat":1516239022} |
| eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJzdXBlcnVzZXIiLCJwZXJtcyI6ImFsbCJ9 | {"sub":"admin","role":"superuser","perms":"all"} |
JSON response templates (17 embedded)
Desktop screenshot capture
Before submitting, the scanner captures a full-desktop bitmap with GDI+, encodes it as a JPEG in memory, and includes the base64 payload in the JSON body posted to /api/scan/submit. Failure paths are visible in the binary as:
- "[*] Capturing desktop screenshot..."
- "[+] Screenshot captured (<bytes> bytes)"
- "[-] Failed to encode screenshot."
- "[-] Failed to initialize GDI+."
- "[-] JPEG encoder not found."
Windows with the WDA_EXCLUDEFROMCAPTURE / WDA_MONITOR display-affinity flag are handled explicitly — the scanner logs [!] Stream-proof window detected! Bypassing... and attempts to capture them anyway by cloning the DC. This is the standard technique for defeating “stream-proof” cheat overlays.
Secrets, keys, webhooks — none present
An exhaustive scan for every shape of embedded credential returned zero results in each of the following categories:
- Discord webhook URLs (discord.com/api/webhooks/, discordapp.com/api/webhooks/)
- Slack, Zapier, Telegram bot tokens / URLs
- AWS/GCP/Azure access keys (AKIA*, ASIA*, AGPA*, etc.)
- PEM-encoded X.509 certificates or RSA/EC private keys
- Cloudflare R2 SigV4 signing material
- SMTP / mailto: destinations
- High-entropy binary blobs > 4 KB in .rdata or .data (no encrypted regions)
The only tokenised strings the file contains are the eight decoy JWTs (all standard textbook examples), the fixed X-Trace-Scanner header value TRACE-Scanner/QMSAPRSqM4xK2JnP, and 105 32-byte hex or base64 strings that on inspection are all MSVC template-instantiation hashes or C++ lambda IDs.
IOCs
File
Network
Host
Detection guidance
Network
- Alert on HTTPS requests carrying User-Agent: TraceScanner or the placeholder blablublewidfjhsfhdshGFDSBSDUFGDFDSBNTRACE. Both are stable across builds and unique to this family.
- Alert on any request carrying X-Trace-Scanner: TRACE-Scanner/QMSAPRSqM4xK2JnP. The value is baked in as a UTF-16 constant.
- Alert on any egress to traceac.cc or scanner.traceac.cc. Two hostnames, both dedicated to this scanner.
- Alert on the URL path pattern /api/scanner/yara_manifest?pin=, /api/scanner/yara_rules?pin=, and /static/bin/yara64.exe?pin= — all specific to this tool.
Host
- Alert on the creation of the named mutex Global\\TraceAntiCheatScanner_Mutex or the window class TraceAntiCheatScannerClass.
- Alert on drop of yara64.exe to %APPDATA%\\trace\\ followed by execution with argv -w -m --scan-list.
- Watch for a process with requireAdministrator manifest that spawns a Microsoft Edge WebView2 controller and navigates to https://scanner.traceac.cc/.
YARA candidate rule
rule TraceAntiCheat_Scanner {
meta:
author = "Clubhouse AC Research"
description = "Third-party FiveM Trace Anti-Cheat scanner"
reference = "traceac.cc"
hash_sha256 = "9d1b210b06d942863e49f0cf7baf629fcdc3cf76d33a5383a6caec65783eb4fc"
strings:
$marker = "Protected by Srungoat Protector v1.0"
$ua = "TraceScanner" wide
$ua2 = "blablublewidfjhsfhdshGFDSBSDUFGDFDSBNTRACE" wide
$hdrname = "X-Trace-Scanner" wide
$hdrvalue = "TRACE-Scanner/QMSAPRSqM4xK2JnP" wide
$mutex = "Global\\TraceAntiCheatScanner_Mutex" wide
$wnd = "TraceAntiCheatScannerClass" wide
$ui = "https://scanner.traceac.cc/send/ui/" wide
$c2a = "traceac.cc" wide
$yaraep = "/api/scanner/yara_manifest?pin=" wide
$submit = "/api/scan/submit" wide
condition:
uint16(0) == 0x5A4D and
3 of them
}Closing
Trace Anti-Cheat is a functional retroactive-trace scanner. It reads a well-chosen set of Windows forensic artefacts, downloads YARA to run against a rule set that lives on its authors' server, takes a screenshot, and posts a JSON to a fixed C2. The technique inventory is solid — Prefetch, BAM, SRUM, Amcache, Event Log 4688 with the TokenElevationType filter, DNS-Client 3008/3010, WFP 5152/56/57. Anyone building a similar tool could learn from the coverage.
It is not unreverseable. The protector marker is 61 bytes of text in a section named .1337. No section is encrypted, no TLS callback is registered, no anti-debug is meaningfully implemented, and every URL, every registry key, every YARA path, every event log query, every cheat brand name, and every user-facing string sits in .rdata in plaintext at UTF-8 or UTF-16-LE. This document is the extraction, byte-offset by byte-offset.
If the next release genuinely raises the bar — real string obfuscation, control-flow flattening, a proper protector shell, meaningful anti-debug, or even just a byte-oriented XOR pass over .rdata with a runtime-derived key — we will happily update this page to note the improvement. Until then, the “unreverseable” label describes marketing, not this build.
Every string quoted in this write-up appears in the sample's .rdata at either UTF-8 or UTF-16-LE and is reproducible with strings -a -eL against the file whose SHA-256 is listed in the IOC block. No dynamic execution of the sample was required to produce this document.