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.
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.
| Binary / switch | -002D | /002F | -␣002D 0020 | --002D 002D | –2013 | —2014 | ―2015 | ‒2012 | −2212 | ‑2011 | ﹣FE63 | -FF0D | ∕2215 | ⁄2044 | /FF0F |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| certutil.exe /decode | A | A | R | R | A | A | R | R | A | R | R | R | R | R | R |
| certutil.exe /decodehex | A | A | R | R | A | A | R | R | A | R | R | R | R | R | R |
| certutil.exe /encode | A | A | R | R | A | A | R | R | A | R | R | R | R | R | R |
| expand.exe /f | A | A | R | R | R | R | A | A | A | A | R | A | A | A | A |
| findstr.exe /i | A | A | R | R | A | A | A | A | A | A | R | A | A | A | A |
| reg.exe /d | A | A | R | R | R | R | R | R | R | R | A | R | R | R | R |
| reg.exe /f | A | A | R | R | R | R | R | R | R | R | A | R | R | R | R |
| reg.exe /s | A | A | R | R | R | R | R | R | R | R | A | R | R | R | R |
| reg.exe /t | · | · | · | · | · | · | · | · | · | · | · | · | · | · | · |
| reg.exe /v | A | A | R | R | R | R | R | R | R | R | A | R | R | R | R |
| schtasks.exe /tn | A | A | R | R | R | R | R | R | R | R | R | R | R | R | R |
| schtasks.exe /xml | A | A | R | R | R | R | R | R | R | R | R | R | R | R | R |
| tar.exe /x | A | R | R | R | R | R | A | A | A | A | R | A | R | R | R |
| tasklist.exe /m | A | A | R | R | R | R | R | R | R | R | R | R | R | R | R |
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.
| Binary | Switch | Accepted (non-standard) | How tested |
|---|---|---|---|
| certutil.exe | decode | –2013—2014−2212 | Direct and cmd.exe process launch, invalid target |
| certutil.exe | decodehex | –2013—2014−2212 | Direct and cmd.exe process launch, invalid target |
| certutil.exe | encode | –2013—2014−2212 | Direct and cmd.exe process launch, invalid target |
| expand.exe | f | ―2015‒2012−2212‑2011-FF0D∕2215⁄2044/FF0F | Direct and cmd.exe process launch, invalid target |
| findstr.exe | i | –2013—2014―2015‒2012−2212‑2011-FF0D∕2215⁄2044/FF0F | Direct and cmd.exe process launch, invalid target |
| reg.exe | d | ﹣FE63 | Direct and cmd.exe process launch, invalid target |
| reg.exe | f | ﹣FE63 | Direct and cmd.exe process launch, invalid target |
| reg.exe | s | ﹣FE63 | Direct and cmd.exe process launch, invalid target |
| reg.exe | t | none | Direct and cmd.exe process launch, invalid target |
| reg.exe | v | ﹣FE63 | Direct and cmd.exe process launch, invalid target |
| schtasks.exe | tn | none | Direct and cmd.exe process launch, invalid target |
| schtasks.exe | xml | none | Direct and cmd.exe process launch, invalid target |
| tar.exe | x | ―2015‒2012−2212‑2011-FF0D | Direct and cmd.exe process launch, invalid target |
| tasklist.exe | m | none | Direct 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.
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
STEP 2Grade offline, on any machine with Node.js
Re-derives every verdict from the stored responses and writes
cells.json,terminators.jsonandsummary.jsonbesideresults.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
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.