Malware Forensics · False-Positive Anatomy

Anatomy of an ML false positive: why Defender flags a clean MinGW binary as Wacatac.B!ml

Verdict: Textbook false positive, and an instructive one. Microsoft Defender's machine-learning model fired on a legitimate, commercially-shipped game executable not because of any malicious code — there is none — but because the binary sits at the intersection of four independently-innocent features the model has learned to distrust: unsigned, MinGW/GCC-compiled, carrying an API set that resembles a shellcode loader, and exposing a TLS callback. The APIs the model keyed on are the MinGW C runtime's exception handler doing its ordinary job. The APIs a real trojan would need are entirely absent. The deeper lesson: an ML verdict can feed the OS reputation graph and harden from "scan warning" into an unbypassable launch block.

Scope: a static PE-level audit of one flagged executable — a MinGW-w64 build of a commercial game — explaining mechanistically why Defender's !ml model scored it malicious, why it is not, and how the block escalated from Defender to Smart App Control. Static analysis only; no dynamic execution. Prepared from a source-level teardown of the binary.

1What !ml actually means

The .B!ml suffix on Trojan:Win32/Wacatac.B!ml is the tell. !ml means the verdict came from a machine-learning classifier, not a signature. There is no specific byte-pattern being matched — so there is nothing to "find" by disassembling the file looking for the malware. The model scored a cluster of features over a decision threshold. To understand the false positive you have to reconstruct the cluster.

2The four features that fired the model

FeatureWhy the model weights it "bad"What it actually is here
Unsigned (cert table size = 0)Legitimate commercial software is almost always Authenticode-signed; unsigned raises the baseline suspicion score.The developer simply didn't sign this build. Common for indie/hobby releases.
MinGW-w64 / GCC toolchainMost goodware in the training set is MSVC-compiled; a disproportionate share of MinGW console binaries in the wild are malware. The toolchain itself is a weak "bad" correlate.Confirmed from symbols: _gnu_exception_handler, __mingw_oldexcpt_handler, libgcc, emutls.c. Just a GCC-built Windows binary.
Loader-shaped API setVirtualAlloc + VirtualProtect + GetThreadContext + SetThreadContext + ResumeThread + IsDebuggerPresent + LoadLibraryA/GetProcAddress is the fingerprint of process hollowing / injection.It's the MinGW C runtime. GCC's structured-exception unwinding manipulates thread contexts and flips page protections; IsDebuggerPresent is called during CRT startup; LoadLibrary/GetProcAddress resolve game_engine.dll and GL functions.
TLS callbackMalware uses TLS callbacks to run code before main(), ahead of a debugger's entry breakpoint.MinGW's emulated-TLS (emutls). Entirely routine.

Each feature is benign in isolation. The model does not judge them in isolation — it weights their co-occurrence, and this binary happens to present all four at once. That is the whole mechanism of the false positive.

3The decisive evidence it is NOT Wacatac

A real injection/loader trojan is defined not by the APIs above but by the ones it needs to act — and every one of those is absent:

Read: the model saw unsigned + MinGW + context-manipulation APIs + TLS callback and scored the combination over threshold. There is no malicious code to chase in a disassembler, because the flagged APIs are the runtime, not the author's code — and the capabilities that would make it a trojan were never there.

4The escalation: from scanner warning to launch block

The instructive twist is what happened after the verdict. The user added a Defender exclusion — and the program still would not start. The reason is that two separate enforcement layers were involved:

LayerModelConsequence
Defender scannerBlock-by-detection. An exclusion suppresses future scans of a path.The exclusion silenced the scanner but touched nothing else. (Also: an exclusion does not un-quarantine an already-removed file — that needs an explicit Restore.)
Smart App Control (SAC)Allow-by-trust. Blocks any executable that is both unsigned and unknown/badly-reputed to Microsoft's cloud.SAC never consults Defender exclusions. It has no per-app allowlist — it is all-or-nothing, and turning it off is a one-way door (re-enabling requires a full Windows reset).

The launch error — "An application control policy has blocked this file. Malicious binary reputation." (0x11CC) — is the key artifact. It shows the !ml verdict had fed the Intelligent Security Graph: the binary was no longer merely "unknown" to SAC, it was actively marked bad by reputation. SAC looks up the exact hash, the cloud says malicious, and it hard-blocks at process creation — which is why launching through Steam changed nothing. An ML scan warning had propagated into an OS-level launch prohibition.

5Remediation, highest-leverage first

Confidence & caveats