What is a WMI event subscription?
WMI (Windows Management Instrumentation) can run an action when something happens. A permanent event subscription has three parts: an __EventFilter (a WQL query describing the event), an event consumer (what to do: run a command line, a VBScript/JScript, write a log line, an event log entry or an e-mail) and a __FilterToConsumerBinding that ties the two together.
Subscriptions are stored in the WMI (CIM) repository and survive reboots. Their actions run as SYSTEM — a command line started by the WMI provider host (WmiPrvSE.exe), or a script run by scrcons.exe — with nothing in a Run key or a scheduled task. That is why attackers use them for persistence (MITRE ATT&CK T1546.003), and why they are easy to miss.
Where it is stored
- C:\Windows\System32\wbem\Repository\OBJECTS.DATA — every class definition and instance, in 8 KiB pages. Freed pages keep their old content until reused.
- INDEX.BTR — a B-tree of keys such as NS_<hash>\CI_<hash>\IL_<hash>.<page>.<record>.<length>, where each hash is the SHA-256 (MD5 on XP) of an upper-cased name.
- MAPPING1.MAP, MAPPING2.MAP, MAPPING3.MAP — logical-to-physical page maps for OBJECTS.DATA and INDEX.BTR; Windows keeps three generations and uses the newest.
- Subscriptions normally live in the root\subscription namespace, but any namespace works.
What this tool shows
- Every binding joined to its filter (the trigger query) and its consumer (the command line, script, log file or event log target).
- Two ways of finding each object, always labelled: structured (through INDEX.BTR and the current MAPPING file, like python-cim) and carving (a scan of all of OBJECTS.DATA for record headers and binding text, like PyWMIPersistenceFinder).
- Deleted and older versions of objects recovered from freed pages and page slack, told apart from live ones when the MAPPING files are present.
- Findings with reasons: encoded PowerShell, user-writable paths, script consumers, persistence triggers (uptime, logon, timers), bindings outside root\subscription, unbound or broken pieces, and Windows' own defaults (SCM Event Log and BVT) recognised by name and content.
- On machines with the Configuration Manager client: CCM_RecentlyUsedApps as evidence of execution — path, user, LastUsedTime, launch count and version information, including older copies carved from freed space, with a time range and findings.
Limitations
- WMI subscription objects carry no documented timestamps. Each instance header holds two undocumented FILETIMEs, shown as leads; the subscription views have no time filter (the SCCM view does: LastUsedTime is a real time).
- Carved records have no namespace, and a record split over non-adjacent freed pages may only be partly readable.
- Without INDEX.BTR and a MAPPING file, class layouts come from a built-in copy of the standard Windows classes (validated on each record), and live and deleted records cannot be told apart.
- MOF files, the AutoRecover list and other persistence places are out of scope; the Windows XP layout is supported with less testing.
How to get the files
- Collect the whole Repository folder with KAPE (WBEM target), Velociraptor (Windows.Triage.Targets, WBEM) or from a disk image.
- On a live system, read the files from a volume shadow copy so they all come from the same instant. Never stop the WMI service to copy them.
- Keep older copies too (shadow copies, Windows.old): a deleted subscription may still be there.
Credits and method
- flare-wmi / python-cim — Willi Ballenthin, FireEye (now Mandiant). The repository structures — MAPPING files, INDEX.BTR pages, OBJECTS.DATA table of contents, class definition and instance headers — are ported from python-cim, and the structured mode follows its object resolver. Apache-2.0.
- PyWMIPersistenceFinder — David Pany, Mandiant. The string-carving pass follows its idea: find __FilterToConsumerBinding text and its EventConsumer.Name / __EventFilter.Name references anywhere in OBJECTS.DATA, and treat BVT and SCM Event Log bindings as common defaults. MIT.
- WMI Attacks, Defense and Forensics (DEF CON 23) — William Ballenthin, Matt Graeber, Claudiu Teodorescu (FireEye, 2015). The talk (and companion white paper) that documented the repository format and WMI persistence; slides and demos are kept in the flare-wmi repository.
- [MS-WMIO]: WMI Encoding Version 1.0 Protocol — Microsoft. The public specification of the object encoding inside each record: class parts, NdTable, value table, heap and encoded strings.
FAQ
Is my repository uploaded anywhere?
No. The parser is Rust compiled to WebAssembly and runs in a Web Worker in your browser. There is no upload endpoint.
Is OBJECTS.DATA enough?
Yes for finding subscriptions: carving mode scans every byte for filter, consumer and binding records, deleted ones included. With INDEX.BTR and the MAPPING files you also get namespaces and a reliable live/deleted verdict.
What do “Index”, “Carved record” and “String match” mean?
They say how an object was found. Index: through INDEX.BTR and the current MAPPING file, so it is live. Carved record: its record header was found by scanning OBJECTS.DATA; if it is not also indexed, it sits in freed space. String match: only the binding's text survived, the way PyWMIPersistenceFinder finds bindings.
Are SCM Event Log Consumer and BVTConsumer malicious?
No, when they match what Windows installs: the SCM Event Log binding ships with Windows Vista and later, and the BVT binding (cscript KernCap.vbs) is a harmless leftover on Windows 7-era images. The tool checks the content too and flags a default name with different content.
Can it tell when a subscription was created?
Not reliably. The repository stores no documented per-object timestamp. Each instance header has two FILETIMEs that often track when the instance was written; they are shown as leads. Correlate with event ID 5861 in Microsoft-Windows-WMI-Activity/Operational, which logs new permanent consumers.
What is the Evidence of execution (SCCM) view?
On machines with the Configuration Manager (SCCM / ConfigMgr) client, the repository also holds CCM_RecentlyUsedApps in root\ccm\SoftwareMeteringAgent: one record per executable and user with LastUsedTime (that user's last launch, a CIM DATETIME carrying its UTC offset), a launch count and the file's version information. The tool reads it from the same OBJECTS.DATA, carves older copies from freed space and adds a time range. Without the ConfigMgr client there is no such data.