WhisperDocs
Endpoint

The Windows sensor

Every capability sentence on this page comes out of a transcript from one live Windows Server 2022 Standard guest, and the transcripts are quoted as they landed. Where a run proved something, the finding is here verbatim. Where a run found a defect in our own code, the defect is here too, with its FAIL, because a page that publishes only the passes is not evidence, it is advertising. The sensor itself is one user-mode process reading Event Tracing for Windows over four Microsoft providers, with no kernel driver anywhere. On that stream sit the behavioural correlators for ransomware, credential access and defense evasion, plus a Sigma evaluator that carries community rules and names their authors on the wire.

One host, one Windows version, a few dozen deliberate triggers. That is the whole sample, so there is not a percentage anywhere on this page and there is no comparison to anybody else's product. Two defects are open and both are named below, with the runs that found them.

4
Microsoft providers on one session
0
kernel drivers
0
detections from nine read-only admin commands
0.55 → 0.90
confidence as the command line corroborates
2
open defects named here

How to read the transcripts

Two conventions run through every run below, and they are worth more than any individual finding.

A zero is only worth reading if something in the same run is non-zero. A detector that is starved and a detector that is correctly silent produce exactly the same empty spool. So every zero claimed here is paired with a count from the same path that proves the events actually arrived. The credential-access run says so in its own verdict header:

VERDICT (asserted by name; every zero has a same-path non-empty control)
  [PASS] NEG control: the lone dump-FILE leg reached the spool
  [PASS] NEG: a lone dump-file leg is SILENT (corroboration is demanded)

Every claimed improvement is a differential, not an assertion. Where a fix is claimed, the same trigger is also run against a build without it, on the same box inside the same sitting, and both numbers are kept.

Two more things belong up front. The lab guest's host name is redacted as [redacted] in every quoted line; nothing else in a quote is altered. And the transcripts split into two kinds of run: the detection runs drove the sensor binary itself, while the raw telemetry runs drove a separate proving tool that constructs the same collector. Which is which is stated at each number.

i

Scope. The network-layer half of Whisper, the routable identity, the off-host revoke and the resolution policy, has its own pages: Containment and The /128 identity. Nothing on this page is evidence for those, and none of them depends on this sensor.

The sensor: one ETW session, four providers, five streams

The Windows sensor is the same whisper binary every other platform ships, observing through the operating system's own telemetry facility. It opens one real-time session with a fixed, recognizable name, WhisperSensorETW, so an operator can find it and stop an orphaned one with stock logman. That name is not a design document, it is what the runs check for; every re-proof ends by asserting the session is gone:

==== POST-RUN ====
  leftover sensor procs: 0
  orphaned ETW session: False

Into that session it enables four public Microsoft manifest providers, each with a narrow keyword mask, so only the events the sensor consumes are delivered:

StreamProviderEvents consumedKeyword scope
execMicrosoft-Windows-Kernel-ProcessProcessStart (1)the PROCESS keyword only
image-loadMicrosoft-Windows-Kernel-ProcessImageLoad (5); ImageUnload (6) filtered before decodethe IMAGE keyword only
fileMicrosoft-Windows-Kernel-FileWrite (16), DeletePath (26), RenamePath (27), CreateNewFile (30); Create (12), Cleanup (13), Close (14) and NameDelete (11) feed the FileObject-to-name cacheper-op keywords: create, write, delete-path, rename-path, filename, file-io. Read and directory-enumeration traffic is never enabled
connMicrosoft-Windows-Kernel-Networkoutbound TCP connect (12 v4, 28 v6)IPv4 and IPv6 keywords only
dnsMicrosoft-Windows-DNS-Clientquery started (3006)whole provider, it is low volume
One ETW session named WhisperSensorETW enables four Microsoft manifest providers: Kernel-Process for process starts and image loads, Kernel-File for the create, modify and move verbs, Kernel-Network for outbound connects and DNS-Client for query starts. All four fold into one five-verb envelope, which feeds the correlators that fired on this host: defense evasion escalating from 0.55 to 0.90 on command-line corroboration, credential access at 0.90 tool-first and 0.75 file-first, ransomware at the capped 1.0 from a canary plus mass write, and a Sigma community rule match at 0.80. Every transcript on this page ends at the local NDJSON spool, because that is where the evidence ends. ONE REAL-TIME SESSION · WhisperSensorETW · 4 MICROSOFT MANIFEST PROVIDERS Kernel-Process exec (1) image-load (5) Kernel-File create · modify · move FileObject-to-name cache Kernel-Network outbound connect 12 (v4) · 28 (v6) DNS-Client query started (3006) the question, not the answer one five-verb envelope, one vocabulary ENGINES THAT FIRED ON THIS HOST, IN THESE TRANSCRIPTS T1562 defense evasion 0.55 → 0.90, escalating as argv corroborates T1003.001 credential access 0.90 tool-first · 0.75 file-first · silent on one leg T1486 ransomware 1.00 capped, canary + mass_write, names the writer T1070.001 Sigma match 0.80, community rule id + title + authors on the wire local NDJSON spool · every transcript on this page is read from here
The picture stops where the evidence stops. No run on this page shows a finding leaving the host, so the last box is the local spool and not a console.

A subscribed provider still emits far more than that, and the callback discards the rest before any decode work. You can watch both halves at once, because a debug flag prints a per-provider, per-event tally at close. This is the real tally from a 45 second proving run, abridged to the five vocabulary events out of the 43 provider and event pairs it listed:

whisper-etw: observed (provider,event) counts (dropped=0 faults=0):
  provider=1 event=1 count=21          <- Kernel-Process ProcessStart
  provider=1 event=5 count=755         <- Kernel-Process ImageLoad
  provider=2 event=12 count=7142       <- Kernel-File Create, the name-cache seed
  provider=2 event=16 count=4485       <- Kernel-File Write
  provider=3 event=12 count=2          <- Kernel-Network connect, IPv4
  provider=4 event=3006 count=2        <- DNS-Client, query started

Read dropped=0 faults=0 with the counts beside it: nothing was shed and no decode panicked, in a run that saw tens of thousands of events. That pairing is the whole point of printing the tally.

wrote 2500 files in 00:00:04.5267385
renamed 1250 files in 00:00:02.9083176
...
==== CONTROL: burst files actually named in the telemetry ====
total NDJSON lines: 10087
file lines naming a b790tally path: 10003
lines naming a t<N>.dat burst file: 10000
lines naming a renamed .locked file: 1250
---- sample (first 3 burst file lines) ----
{"kind":"file","ts":1787831469965070,"pid":1772,"event":"create","path":"C:\\...\\churn\\t0.dat","host":"[redacted]"}
{"kind":"file","ts":1787831469965206,"pid":1772,"event":"modify","path":"C:\\...\\churn\\t0.dat","host":"[redacted]"}
{"kind":"file","ts":1787831469967849,"pid":1772,"event":"create","path":"C:\\...\\churn\\t1.dat","host":"[redacted]"}

1,250 renames produced exactly 1,250 lines naming a renamed file. That run drove the proving tool rather than the shipped service, so read it as a statement about the collector, which is the same one the sensor constructs. The delete verb is wired and subscribed, but no run in this set exercised it, so this page does not claim it.

Command lines for short-lived native processes

Every command-line matcher below stands on this, so it gets its own section. A short-lived native Win32 tool can exit before its command line can be read, and the older build simply had none for those processes. The same script fired the same triggers against a fixed build and a pre-fix build. Both arms matched on the process name, so the record appears in both and only the field differs:

HEAD     [exec] pid=4408 comm=wevtutil.exe cmdline=wevtutil  cl Application
CONTROL [exec] pid=6968 comm=wevtutil.exe cmdline=<EMPTY>

HEAD     [exec] pid=1268 comm=rundll32.exe cmdline=rundll32.exe  C:\Windows\System32\comsvcs.dll, MiniDump 0 "...\bogus.dmp" full
CONTROL [exec] pid=3720 comm=rundll32.exe cmdline=<EMPTY>

The cmd.exe parents are the stronger half of that result. The harness filter matches on process name, image path or command line, and no filter term is a substring of cmd.exe or of its image path, so a cmd.exe record can only surface by matching its recovered command line. Two of them appear in the fixed arm. None appears anywhere in the control:

HEAD     [exec] pid=6340 comm=cmd.exe cmdline="C:\Windows\system32\cmd.exe" /c "wevtutil cl Application"
CONTROL (no cmd.exe record present)

Three honest notes belong with that.

Both arms ran at Mandatory Label\High Mandatory Level, so privilege is controlled for and cannot explain the difference between them. A real-time ETW session needs Administrator or SYSTEM, and this is where you see that requirement being met.

One binary, three layers, one envelope

Under the providers sits a three-layer split, and only the first layer is Windows code at all:

The Windows decode pipeline. An ETW real-time session with four narrowly-scoped providers feeds a thin Windows-only callback that filters by provider and event id before any decode, then decodes wanted events and hands them to a bounded channel 16,384 records deep that sheds the newest and counts it. Two OS-neutral layers, unit-tested on Linux, reshape the property bag into a typed record and then into the cross-platform envelope, before the process graph and the local NDJSON spool where every transcript on this page is read. ETW real-time session · WhisperSensorETW 4 Microsoft manifest providers · narrow keyword masks WINDOWS-ONLY · THIN event callback filter by (provider, event id) BEFORE any decode decode wanted events · bounded hand-off (16,384, shed-newest, counted) OS-NEUTRAL · TESTED ON LINUX CI property bag → typed record field-for-field reshape · a record with no subject is dropped, never invented OS-NEUTRAL · TESTED ON LINUX CI cross-platform envelope · exec / file / conn / dns / image-load the same five verbs the Linux and macOS producers emit process graph · local NDJSON spool
Only the top two boxes are Windows code, and they are kept thin on purpose: session lifecycle, a callback, and raw property extraction. Everything below them is pure Go with zero Windows imports, cross-compiled and unit-tested on Linux CI with no Windows host in the loop.

The reshape layer carries decode rules an analyst can lean on, and one of them is visible in the transcripts above rather than only in the source:

The transcripts on this page all end at the local NDJSON spool, because that is where the evidence ends. No run here shows a finding leaving the host, so this page makes no claim about a console, an API or graph enrichment.

Deep detection, on this box, in these runs

The correlators are the same OS-neutral code the Linux build runs, judging the same envelope. What follows is what they did on this host.

Defense evasion: the command line decides, not the executable name

Six trigger commands, run twice back to back on the same box: once against the shipped build, once against the same tree built without the Windows breadth matcher. On the shipped build, four detections, and the confidence climbs as each new command line corroborates the last:

{"kind":"detection","ts":1787842775274534,"technique":"T1562","pid":3960,"comm":"auditpol.exe","confidence":0.8,
 "signals":["evasion:clear_logs","impair:disable_logging","impair:disable_protection","evasion:total_scope","t1562.002"],
 "host":"[redacted]"}
{"kind":"detection","ts":1787842777295126,"technique":"T1562","pid":2892,"comm":"vssadmin.exe","confidence":0.9,
 "signals":["evasion:clear_logs","evasion:destroy_recovery","impair:disable_logging","impair:disable_protection",
            "evasion:total_scope","t1562.002","t1490"],"host":"[redacted]"}

  -> 4 detections, escalating 0.55 -> 0.80 -> 0.90 -> 0.90.

The corroborating signal is read out of the actor's own arguments. evasion:total_scope fires off the literal /category:* in auditpol /set /category:* and the /all in vssadmin delete shadows /all, never off the fact that a process is called auditpol.exe.

The same six triggers on the control build, which saw all six execs with their full command lines:

  -> 1 detection, 0.55, two classes. auditpol / vssadmin / fsutil arrived with
     full command lines and produced NO class at all.

So the breadth matcher is not what introduced coverage of this technique; the control still fired once at 0.55. What it adds is the escalation and the extra classes, impair:disable_logging, evasion:destroy_recovery and evasion:tamper_forensics.

Then the false-positive run: nine read-only and restorative administrator commands, using the same executables as the firing ones.

  wevtutil qe Application      wevtutil el                auditpol /get /category:*
  vssadmin list shadows        netsh advfirewall show     fsutil usn queryjournal C:
  Get-MpPreference (read)      Set-MpPreference -DisableRealtimeMonitoring $false
  sc query WinDefend

  -> spool lines 16; exec records present for all of wevtutil(2), auditpol(1),
     vssadmin(1), netsh(1), fsutil(1), MpPreference(2), sc.exe(1);
     DETECTIONS: 0.

     The exec counts are the control for that zero.

The exec counts are what make that zero readable. The executables that appear in the firing run produced nothing at all here, so the discrimination lives in the command line.

One more thing that run showed, working in both directions inside a single transcript. The first detection carries an advisory signal, admin_maintenance_baseline, for a shape that looks like ordinary maintenance. It is graded rather than screened out, and it carries no weight: the detection holding it scores 0.55, byte for byte what the control build scored without it. The next detection drops the advisory the moment /category:* corroborates. An advisory that changed the score would be a thumb on the scale; this one is demonstrably not.

The engine is on by default. A later re-proof asserted exactly that against a config with no defense-evasion field present at all:

  ASSERT default-ON: a T1562 detection landed with no defenseevasion field = True

The run left the box as it found it, which is stated rather than assumed:

  defender realtime enabled : True
  firewall Domain/Private/Public : True / True / True
  audit Logon/Logoff : Success and Failure
  leftover sensor processes : 0

A false positive we found in our own detector, and the differential that proves it gone

Reading a file's timestamps is not tampering with them. An older build could not tell the two apart, so a pair of harmless timestamp reads inside an already-firing process tree added a forensic-tampering class that did not belong there. The re-proof runs the same reads twice, against the fixed build and against the pre-fix binary:

  ASSERT t1070.006 ABSENT on HEAD = True   (tamper ABSENT = True)
{"kind":"detection","ts":1787849686122550,"technique":"T1562","pid":5376,"comm":"powershell.exe","confidence":0.8,
 "signals":["evasion:clear_logs","evasion:tamper_forensics","impair:disable_protection","evasion:total_scope","t1070.006"],
 "host":"[redacted]"}
  ASSERT the FP is real on the control: t1070.006 PRESENT here = True

An absence is only meaningful if something else in that run was present, so the same leg asserts the tree fired on its real classes first, and a fourth scenario checks the fix did not simply blind the detector. A timestamp setter in the same shape still convicts:

  [PASS] D guard intact: the SETTER still carries t1070.006 on HEAD

Sigma community rules, with their authors on the wire

The event-log clearing conviction in those runs is not ours, and the finding says so itself. The rule id, the title and the upstream author list ride in the signal set:

{"kind":"detection","ts":1787849591640810,"technique":"T1070.001","pid":6160,"comm":"wevtutil.exe","confidence":0.8,
 "signals":["sigma_match","rule:cc36992a-4671-4f21-a91d-6c2b72a2edf5",
            "title:Suspicious Eventlog Clearing or Configuration Change Activity",
            "author:Ecco, Daniil Yugoslavskiy, oscd.community, D3F7A5105, Swachchhanda Shrawan Poudel (Nextron Systems)"],
 "paths":["\\Device\\HarddiskVolume3\\Windows\\System32\\wevtutil.exe"],"host":"[redacted]"}

That id resolves to a real rule file in the shipped bundle, and two more community rules fired in the same capture set, one for process memory dumping through comsvcs.dll and one for suspicious rundll32 activity, each with its own id and authors. Attribution is on the wire because presenting a community rule's work as proprietary detection would be a lie by omission.

Credential access: corroboration is demanded, and order does not matter

Four legs, each with its own control. First the negative: a lone dump-shaped file, no tool and no module load anywhere in the tree.

  lone dump-file leg captured (control) = True; T1003.001 = 0
  [PASS] NEG: a lone dump-file leg is SILENT (corroboration is demanded)

The file leg reached the spool and the detector still said nothing. Then the two orders, which convict either way round:

  [PASS] A: the tool-first order CONVICTS T1003.001
  [PASS] B: the file-first / tool-last order ALSO convicts (order-insensitive)

The file-first fold is the interesting one. It correlates across a parent and child process edge, names the parent shell that actually wrote the dump, and lists both the dump and the tool in its evidence:

{"kind":"detection","ts":1787851908770968,"technique":"T1003.001","pid":340,"confidence":0.75,
 "signals":["lsass_dump_tool_unconfirmed_target","lsass_dump_file_created"],
 "paths":["C:\\Users\\Administrator\\w824reprove\\b\\watch\\evidence-b.dmp",
          "\\Device\\HarddiskVolume3\\Windows\\System32\\rundll32.exe"],"host":"[redacted]"}

The run also corrected the premise it was filed under. rundll32 loading comsvcs.dll for a MiniDump was expected to need a dump file to corroborate it; it does not, because the module load itself is corroboration, at 0.9:

  [PASS] MOD (premise corrected): MiniDump 0 FIRES - it is NOT uncorroboratable
  [PASS] MOD: it fires via the comsvcs.dll load (lsass_dump_module_loaded), no file needed

Read the signal name before you read the technique id. Every one of these findings carries lsass_dump_tool_unconfirmed_target, and the name is literal: the target in these runs was a decoy process, never a real credential store. What is proven is that the correlator convicts the dumping shape from two independent legs, in either order, and stays silent on one leg alone. Detection of a real credential theft is not what these transcripts show.

Ransomware: the tripwire, the arithmetic, and a defect that is still open

The sensor plants its own decoy files in each watched directory, five of them, 131 bytes each, sorted to sit at both ends of a directory walk:

==== planted canaries (the sensor's own decoys) ====

Name                             Length
----                             ------
!important-passwords-backup.docx    131
0-crypto-wallet-keys.txt            131
ledger-records-7464.pdf             131
mortgage-summary-5424.xlsx          131
zzz-tax-and-bank-records.xlsx       131

canary count: 5

Three of those names are fixed strings in the shipped engine and two are derived per host, which is why a decoy list an attacker greps out of the binary does not cover the whole set. The true positive, a canary touch correlating with a mass-write burst on one process, reproduced across two independent runs at different PIDs:

{"kind":"detection","ts":1787831158722911,"technique":"T1486","pid":5420,"comm":"powershell.exe","confidence":1,
 "signals":["canary","mass_write"],
 "paths":["C:\\Users\\Administrator\\whisper-e2e\\b790head\\churn\\!important-passwords-backup.docx",
          "C:\\Users\\Administrator\\whisper-e2e\\b790head\\churn\\b2499.dat",
          "C:\\Users\\Administrator\\whisper-e2e\\b790head\\churn\\b2498.dat",
          "C:\\Users\\Administrator\\whisper-e2e\\b790head\\churn\\b2497.dat"],"host":"[redacted]"}

That confidence of 1 is not a probability and not a model score. It is a capped sum of fixed weights: the canary signal carries 0.95 and mass-write carries 0.35, which is 1.30, clamped to 1. The firing threshold is 0.80. The weights are constants in the engine, the same on every platform, and the arithmetic is worth knowing before anybody reads a 1 as certainty.

The run that produced that finding also produced two bad ones, and they are the reason this section is long.

The mis-attribution. Windows raises a file write in the context of the thread that issued the request, so an application's own cached write lands under the application, and the cache manager's lazy writer replays the same dirty pages a second later from the System process. The whole 2,500 file burst therefore appeared twice:

file lines by PID
  pid=7104  lines=5001  distinct paths=2501     <- powershell, the real writer
  pid=4     lines=2509  distinct paths=2506     <- System

That produced a spurious full-confidence finding naming pid 4 beside the correct one. Both pid-4 findings carry no process name at all, because opening the System process is denied, which is what a responder actually sees: an unnamed process they cannot act on. Worse, the earliest pid-4 finding fired 10.8 seconds before the burst, on the sensor's own freshly planted decoys. The sensor was detecting itself.

Two changes, measured across three runs of the identical burst:

                            before      attribution fix   + canary flush
  file lines under pid 4      2509            10                3
  distinct paths under pid 4  2506             6                1
  T1486 findings                 3             2                1
  findings naming pid 4          2             1                0
  findings naming the writer     1             1                1

The writer's distinct path count is 2,501 in all three runs, so the recovery changed who the writes belong to and never how many files were seen. The three surviving System lines name the churn directory, not a file, which is the filesystem flushing its own index on a handle System genuinely owns. That is honestly attributed and deliberately left alone: one directory cannot approach the thirty distinct paths the mass-write signal needs, and a directory is never a decoy path. The end state is one finding:

{"kind":"detection","ts":1787842516358554,"technique":"T1486","pid":6852,"comm":"powershell.exe",
 "confidence":1,"signals":["canary","mass_write"],
 "paths":["...\\churn\\!important-passwords-backup.docx","...\\churn\\b2499.dat",
          "...\\churn\\b2498.dat","...\\churn\\b2497.dat"],"host":"[redacted]"}

Those two runs used dedicated proof binaries rather than the shipped one, so read the table as the diagnosis it is. The one corroboration from the shipped build is a single clause in a later run: [PASS] ENC on HEAD: NO detection is attributed to pid 4 (System).

And the defect that is still open. A benign encrypted-vault sync shape, 600 new files each written once, is still convicted as ransomware, and the fix believed to address it changes nothing measurable on this scenario. The run's own verdict is FAIL:

  CONTROL: file lines naming vault-<N>.bin  create=600 modify=1200 distinct-paths=600
  T1486 detections: 1
    {"kind":"detection","ts":1787850923234528,"technique":"T1486","pid":3660,"comm":"powershell.exe",
     "confidence":0.85,"signals":["high_entropy","mass_write"],"paths":[...],"host":"[redacted]"}
  [FAIL] FP on HEAD: ZERO T1486 on the create-only high-entropy burst

REPROVE871: FAIL

The pre-fix control build produced exactly one finding too, at the same 0.85, with the same signal set and the same writer. On this scenario the two builds are indistinguishable. Recall is intact in the same run, which is why the fix is not simply being reverted: overwriting 120 pre-existing files in place still convicts, names the encrypting process, and attributes nothing to System.

Both legs carry spool controls, so the FAIL is a measurement rather than a broken harness.

What these transcripts do not show

This list is part of the evidence, not a disclaimer bolted onto it.

Two open defects

Named here rather than left for a reader to find.

1. A benign create-only high-entropy burst is convicted as ransomware. Detailed above. Verdict FAIL on the current build, identical output on the pre-fix build, with a control proving 600 distinct paths reached the spool.

2. The sensor does not exit on Ctrl+C on the console path. It survives a real CTRL_C_EVENT past the 20 second bound, has to be force-killed, and the shutdown tally and the graph teardown are lost with it:

  sensor exited=False after 20.34s (bound 20s)
  force-killing (Close did NOT run - the tally below WILL be absent; that is the defect, not a flake)
  tally lines present: 0/3; graph-teardown line: False

The cause was isolated by changing one variable. The identical binary, started the identical way, exits in 0.4 seconds on a CTRL_BREAK event with all three tally lines and the teardown line present:

  sensor exited=True after 0.4s (bound 20s)
  tally lines present: 3/3; graph-teardown line: True
  [PASS] L3b ISOLATION: the SAME prefix binary EXITS on CTRL_BREAK in 0.4s
  [PASS] L3b so the shutdown path is healthy; the fault was purely CTRL_C delivery

So the shutdown path was healthy the whole time and the fault is purely in how the Ctrl+C event is delivered to a process that inherited an ignore flag from its parent. The consequence is not cosmetic: a force-killed sensor leaves an orphaned ETW kernel session behind, and that orphan was observed contaminating the start of later runs.

The reason this defect is still listed rather than closed is the more useful lesson. A previous round fixed it: the function was written, committed and covered by three passing unit tests. It was never called from the production entry point, so the shipped binary behaved exactly as if no fix existed.

ROOT CAUSE: RestoreConsoleCtrlC() is defined + unit-tested but NEVER
CALLED from the sensor run entrypoint - a shipped-but-unreachable fix.

The mechanism itself is proven: a binary built with the call genuinely wired onto the console path exits in 0.01 seconds on a real CTRL_C_EVENT and passes all eight assertions, against a control from the same tree that fails six of them. The call site now exists in the tree, with a guard test that parses the real entry point and fails if the wiring is removed. But a green guard test is precisely what the previous round also had, and no run on a live box has yet stopped a wired binary with Ctrl+C. Until one has, this page says the defect is open.

No kernel driver, no dependencies

Nothing of ours runs in the kernel. The whole sensor is one user-mode process consuming a standard OS telemetry facility. There is no filter driver, no boot-start component, no driver package in the tree at all, and therefore no driver of ours that can fault a kernel. Its failure domain is one process the Service Control Manager restarts.

The ETW binding is self-contained too. The Win32 tracing and decoding entry points are not covered by Go's standard Windows syscall package, and rather than adopt a third-party ETW library or cgo, the sensor carries its own thin binding:

Bounds and safety: the numbers the runs actually printed

whisper: sensor: calm footprint: unmeasured (no cgroup v2 self-accounting) under the published SLA
(cpu 25%, mem 157286400B soft / 262144000B hard); deferrable submitted=15 completed=13 dropped=0
promoted=0 pending=2

150 MiB soft, 250 MB hard, a quarter of a core. On Windows there is no self-accounting to measure against it, so the line says unmeasured instead of printing a flattering number. The deferrable work scheduler shed nothing during the burst, though pending=2 is a snapshot at an abnormal termination rather than a drained steady state.

One clear privilege gate. A real-time session needs Administrator or SYSTEM; the verified runs all show Mandatory Label\High Mandatory Level. A non-elevated run degrades at construction with one line naming which collector this platform lacks, and everything else keeps serving.

Install as a Windows service

The CLI installs from the same channel the rest of the toolchain uses on Windows:

# PowerShell: install the whisper CLI (one static binary, no dependencies)
PS> scoop bucket add whisper https://github.com/whisper-sec/scoop-bucket
PS> scoop install whisper

Then, from an elevated PowerShell, the same cross-platform service manager that drives systemd on Linux and launchd on macOS drives the Windows Service Control Manager:

PS> whisper service install --sensor
PS> whisper service start --sensor
# or both at once
PS> whisper service install --sensor --now
i

One honest note about all of that. Every transcript on this page ran the sensor as a console process, not under the Service Control Manager. The service path is shipped code, but it is not what these runs exercised, and this page does not claim a result it did not produce.

Verify it is working

The first proof needs no key, no sensor and no trust in us, because it is the network plane's proof and it is the same from any operating system. The example is a live identity, so every line here reproduces. This output was captured by running it:

# reverse DNS names the address, the identity in public
PS> dig -x 2a04:2a01:b69a:6717:e3b0:51ff:3bf7:f478 +short
ae3b051ff3bf7f478.tdc38e7c55bad3306a92b830f9bb1e4f9.agents.whisper.online.

# and the name validates to the IANA root, so our API is not in the trust path.
# point delv at a validating resolver; a local stub that strips DNSSEC data
# will report a broken chain that is its own, not the zone's.
PS> delv @1.1.1.1 -x 2a04:2a01:b69a:6717:e3b0:51ff:3bf7:f478
; fully validated
8.7.4.f.7.f.b.3.f.f.1.5.0.b.3.e.7.1.7.6.a.9.6.b.1.0.a.2.4.0.a.2.ip6.arpa. 60 IN PTR ae3b051ff3bf7f478.tdc38e7c55bad3306a92b830f9bb1e4f9.agents.whisper.online.
8.7.4.f.7.f.b.3.f.f.1.5.0.b.3.e.7.1.7.6.a.9.6.b.1.0.a.2.4.0.a.2.ip6.arpa. 60 IN RRSIG PTR 13 34 60 ...

The second proof is the running sensor, in stock tooling. These are the commands; this page does not print an expected transcript for them, because the only Windows outputs it publishes are ones a run actually produced:

PS> sc query whisper-sensor
PS> logman query "WhisperSensorETW" -ets
PS> whisper service status --sensor
PS> Get-Content C:\ProgramData\Whisper\logs\whisper-sensor.log -Tail 20 -Wait

The habit worth copying from these runs is the third command's companion. Before you believe any zero the sensor reports, count something in the same spool that must be non-zero:

==== spool accounting (the CONTROL for the zeros above) ====
spool total lines: 7518
file lines: 7510
file lines naming a burst file (b<N>.dat): 7501
---kind breakdown---
 7510 file
    3 detection
    2 dns
    2 conn
    1 exec

Those five counts reconcile exactly with the total, which is what tells you the spool is whole and not truncated.

Status, and what the evidence for it is

CapabilityStatusThe evidence
ETW telemetry: exec, file, conn, DNS, image-loadlive, provenOne session, four providers, a 43-pair observed tally with dropped=0 faults=0, and 2,500 writes plus 1,250 renames counted back by name in the spool.
Command-line recovery for short-lived native processesproven by differentialSame trigger, two builds: full argv on the fixed build, cmdline=<EMPTY> on the control, with cmd.exe records that can only appear via a recovered command line.
Defense-evasion classifier (T1562 family, T1490, T1070.006)live, provenFour detections escalating 0.55 to 0.90 on argv corroboration, zero detections from nine read-only commands with the exec counts as control, and an A/B against a build without the matcher.
Sigma community-rule evaluationlive, provenThree distinct community rules matched on this host, each finding carrying the rule id, title and upstream authors on the wire.
Credential-access correlation (T1003.001)proven, decoy targets onlySilent on one leg, convicts on two in either order (0.9 tool-first, 0.75 file-first across a parent and child edge). Every finding is tagged lsass_dump_tool_unconfirmed_target; no real credential store was touched.
Ransomware correlator (T1486)live, open false positiveCanary plus mass-write convicts at the capped 1.0 naming the writer, reproduced across runs; in-place encryption of 120 files still convicts. A benign create-only high-entropy burst also convicts at 0.85, and that run's verdict is FAIL.
Write attribution under the Windows cache managerfixed, measured on proof binariesFindings naming System went 2 to 1 to 0 across three runs of the same burst while findings naming the real writer stayed 1, with the writer's distinct path count unchanged at 2,501. The shipped-build corroboration is one assertion, not the full table.
Graceful shutdown on Ctrl+Copen defectForce-killed at the 20 second bound in every run in this set, losing the shutdown tally and the teardown line and orphaning an ETW session. Root cause isolated; the wiring is in the tree with a guard test; no live run has yet proven it.
Process-injection region sweep (T1055)runs, not proven hereIt produced findings in these runs, but only the known baseline shape on a stock host. No injection was detonated, so nothing here shows it catching one.
Detections leaving the hostnot shown hereEvery transcript on this page reads a local spool. No run shows a finding reaching a console, an API or the graph.
Kernel-level operation-time injection feedin the tree, darkThe decode exists, but subscribing needs a Microsoft anti-malware attestation, which is an external gate. It has never run on a box, and no run here exercises it.

Held to one sentence: on this Windows host, the telemetry, the command-line recovery and the defense-evasion, credential-access and ransomware correlators all fired with their controls beside them, and the two defects the same runs found are named above rather than rounded away. Platform support owns the full matrix and never rounds a status up.

i

Why the page is written this way. Anybody can print a detection. What is worth reading is whether the run that produced it also proved the zeros around it, kept the failures, and said which numbers came from a proving harness rather than the shipped binary. That is what these transcripts do, and it is the standard the next Windows run on this page will have to meet.