Research

Dash / Unicode LOLBIN parser-acceptance matrix

Which in-box Windows binaries accept a Unicode dash or slash look-alike in place of the ordinary hyphen or slash at the start of a command-line switch. Where a character is accepted, a detection that matches only the literal switch character misses the invocation entirely.

Matrix 1.0.0Measured on Windows build 26100CyRay researchReproducible: probe published below
61
Accepted cells
134
Rejected cells
15
Indeterminate
36
Accepted look-alikes
14
Binary / switch pairs
15
Characters

01Why a detection engineer cares

Argument-parser leniency is a detection blind spot. Many Windows binaries normalise a Unicode look-alike (an en dash, a fullwidth solidus, a non-breaking hyphen) back to the ASCII switch character inside their own parser, so the switch runs exactly as intended while a rule or filter matching the literal - or / sees nothing it recognises. This matrix tells you, per binary and per switch, which of those substitutions actually parse - the difference between a detection that holds and one that is walked straight past.

02The matrix

Binaries down, characters across. The two leftmost characters are the canonical controls; the next two are structural variants; the rest are Unicode look-alikes. A cell is coloured by what the parser did with that character in front of that switch.

Accepted - the parser treats the character as a switch introducer; a hyphen-only detection misses it Rejected Indeterminate - the binary does not support the switch in the form tested, so nothing about the character can be concluded
Binary / switch-002D/002F-␣002D 0020--002D 002D201320142015201222122011FE63FF0D22152044FF0F
certutil.exe /decodeAARRAARRARRRRRR
certutil.exe /decodehexAARRAARRARRRRRR
certutil.exe /encodeAARRAARRARRRRRR
expand.exe /fAARRRRAAAARAAAA
findstr.exe /iAARRAAAAAARAAAA
reg.exe /dAARRRRRRRRARRRR
reg.exe /fAARRRRRRRRARRRR
reg.exe /sAARRRRRRRRARRRR
reg.exe /t···············
reg.exe /vAARRRRRRRRARRRR
schtasks.exe /tnAARRRRRRRRRRRRR
schtasks.exe /xmlAARRRRRRRRRRRRR
tar.exe /xARRRRRAAAARARRR
tasklist.exe /mAARRRRRRRRRRRRR

Counting the cells: 61 accepted, 134 rejected, 15 indeterminate, across 14 (binary, switch) pairs and 15 characters. Of the accepted cells, 36 are a Unicode look-alike or a structural variant rather than the plain - or / - each one a place a literal-hyphen detection is blind.

03Per-binary summary

Only characters that are not the plain - or / are listed - the ones a hyphen-only detection would miss.

BinarySwitchAccepted (non-standard)How tested
certutil.exedecode201320142212Direct and cmd.exe process launch, invalid target
certutil.exedecodehex201320142212Direct and cmd.exe process launch, invalid target
certutil.exeencode201320142212Direct and cmd.exe process launch, invalid target
expand.exef2015201222122011FF0D22152044FF0FDirect and cmd.exe process launch, invalid target
findstr.exei201320142015201222122011FF0D22152044FF0FDirect and cmd.exe process launch, invalid target
reg.exedFE63Direct and cmd.exe process launch, invalid target
reg.exefFE63Direct and cmd.exe process launch, invalid target
reg.exesFE63Direct and cmd.exe process launch, invalid target
reg.exetnoneDirect and cmd.exe process launch, invalid target
reg.exevFE63Direct and cmd.exe process launch, invalid target
schtasks.exetnnoneDirect and cmd.exe process launch, invalid target
schtasks.exexmlnoneDirect and cmd.exe process launch, invalid target
tar.exex2015201222122011FF0DDirect and cmd.exe process launch, invalid target
tasklist.exemnoneDirect and cmd.exe process launch, invalid target

04How a cell is decided

Every switch is built from raw Unicode code points on the box itself and handed to the real binary. Each row is a (binary, switch) pair chosen so the switch is valid for a read-only verb given a value or target that cannot resolve - the binary can only fail, and the measurement is in how it fails.

Acceptance is decided against two controls run for every cell: the binary's own canonical prefix (what "this switch parsed" looks like) and @, a character that is never a switch introducer (what "not parsed" looks like). A test character is accepted only when it reproduces the canonical control in a form the binary is proven to accept, rejected when it matches the never-a-prefix control, and indeterminate when the instrument cannot separate the two. Indeterminate is a real outcome and is never folded into the other two.

05The self-correction

The first version of the verdict rule was wrong, and the published code keeps the full account. It read "the test response matches the canonical control" as proof of acceptance.

tasklist /m exposed the flaw. Its canonical control, -m=<value>, does not parse at all - tasklist rejects the = separator - and tasklist echoes the offending argument back with the Unicode dash already normalised to - in its own error text. Two failures produced byte-identical output and were read as a shared success.

The corrected rule first establishes that the canonical control itself parsed in the exact form under test. Only then is a character judged, and everything the instrument cannot separate is marked indeterminate. Because the raw response of every probe and of both its controls is stored, the whole run was re-graded without measuring anything again.

The correction is published deliberately. Primary measurement is only worth as much as the discipline that catches its own errors.

06Run the probe yourself

Two steps. The grading step needs no network and no Windows.

  1. STEP 1Measure, on a disposable Windows box

    A VM or a sandbox, never a machine you care about: the probe launches real binaries. Every target is an invalid value or a path that does not exist, so each binary can only fail. Every row records the hex of the exact UTF-16 argument string that reached the process, so a dash that silently arrived as a hyphen cannot masquerade as a result.

    powershell -ExecutionPolicy Bypass -File probe/guest-runner.ps1 `
      -MatrixPath probe/matrix.json -OutPath runs/results.ndjson
  2. STEP 2Grade offline, on any machine with Node.js

    Re-derives every verdict from the stored responses and writes cells.json, terminators.json and summary.json beside results.ndjson. The verdict rule can be audited, corrected, and the run re-graded without re-measuring.

    node probe/rederive.mjs --run runs

07Download the instrument and the data

08Provenance and licence

Measured on Windows build 26100. Reproduce on any build with the steps above.
Published by CyRay research. No customer, host or person is named anywhere in the asset.
No third-party detection content is bundled and no Sigma rule or SigmaHQ content is referenced, so no Sigma attribution is required. If you extend the matrix into detections derived from a Sigma rule, credit the author, link the rule, and honour its DRL 1.1 licence.

Licence: MIT - the matrix data and the probe, Copyright (c) 2026 CyRay. Full text in LICENSE. MIT is the least-friction choice for a small tool plus a data table a stranger should be able to run, copy into their own detection tests, and build on without asking.

Mobula

Detections that hold against what the parser actually accepts.

Research like this is how Mobula's managed detection content is engineered: measured on the real binary, corrected in the open, and delivered to every customer environment.

Book a POC