---
brand: mobula
audience: practitioners
indexable: true
claims: []
---

# The dash/Unicode LOLBIN parser-acceptance matrix

Which in-box Windows binaries accept a Unicode dash- or slash-like character in place of an
ordinary hyphen (`-`) or forward slash (`/`) at the start of a command-line switch. Where a
character is **accepted**, a detection that matches only on the literal `-` or `/` misses the
invocation entirely.

- **Measured on:** Windows build **26100** (see `probe/` to reproduce on any build).
- **Method:** every switch is built from raw Unicode code points and handed to the real binary
  twice as a control — once with the binary's own canonical prefix, once with a character
  (`@`) that is never a switch introducer. A test character is **ACCEPTED** only when its
  response reproduces the *canonical* control in a form the binary is proven to accept, and
  **REJECTED** when it matches the never-a-prefix control. Anything the instrument cannot
  separate is **INDETERMINATE** and is never rounded into either. Full rule and rationale:
  `probe/derive.mjs`.
- **Targets:** 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.

## Legend

`A` = accepted (the parser treats this character as a switch introducer) ·
`R` = rejected ·
`·` = indeterminate (the binary does not support the switch in the form tested, so no
conclusion about the character can be drawn).

## The character vocabulary

| Column | Character | Code point(s) | Class |
|---|---|---|---|
| `hyphen-minus` | `-` | U+002D | canonical (control) |
| `solidus` | `/` | U+002F | canonical (control) |
| `dash-space-flag` | `-␠` | U+002D+U+0020 | structural variant |
| `double-hyphen` | `--` | U+002D+U+002D | structural variant |
| `en-dash` | `–` | U+2013 | Unicode look-alike |
| `em-dash` | `—` | U+2014 | Unicode look-alike |
| `horizontal-bar` | `―` | U+2015 | Unicode look-alike |
| `figure-dash` | `‒` | U+2012 | Unicode look-alike |
| `minus-sign` | `−` | U+2212 | Unicode look-alike |
| `non-breaking-hyphen` | `‑` | U+2011 | Unicode look-alike |
| `small-hyphen-minus` | `﹣` | U+FE63 | Unicode look-alike |
| `fullwidth-hyphen-minus` | `－` | U+FF0D | Unicode look-alike |
| `division-slash` | `∕` | U+2215 | Unicode look-alike |
| `fraction-slash` | `⁄` | U+2044 | Unicode look-alike |
| `fullwidth-solidus` | `／` | U+FF0F | Unicode look-alike |

## The matrix

Columns are the characters above, in the same order.

| Binary | Switch | `-` | `/` | `-␠` | `--` | `–` | `—` | `―` | `‒` | `−` | `‑` | `﹣` | `－` | `∕` | `⁄` | `／` |
|---|---|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| `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 |

## Per-binary summary

"Accepted (non-standard)" lists only characters that are **not** the plain `-` or `/` — i.e. the ones a hyphen-only detection would miss.

| Binary | Switch | Accepted (non-standard) | How tested | Date tested |
|---|---|---|---|---|
| `certutil.exe` | `decode` | `–` `—` `−` | direct + cmd.exe process launch, invalid target | 2026-08-24 |
| `certutil.exe` | `decodehex` | `–` `—` `−` | direct + cmd.exe process launch, invalid target | 2026-08-24 |
| `certutil.exe` | `encode` | `–` `—` `−` | direct + cmd.exe process launch, invalid target | 2026-08-24 |
| `expand.exe` | `f` | `―` `‒` `−` `‑` `－` `∕` `⁄` `／` | direct + cmd.exe process launch, invalid target | 2026-08-24 |
| `findstr.exe` | `i` | `–` `—` `―` `‒` `−` `‑` `－` `∕` `⁄` `／` | direct + cmd.exe process launch, invalid target | 2026-08-24 |
| `reg.exe` | `d` | `﹣` | direct + cmd.exe process launch, invalid target | 2026-08-24 |
| `reg.exe` | `f` | `﹣` | direct + cmd.exe process launch, invalid target | 2026-08-24 |
| `reg.exe` | `s` | `﹣` | direct + cmd.exe process launch, invalid target | 2026-08-24 |
| `reg.exe` | `t` | _none_ | direct + cmd.exe process launch, invalid target | 2026-08-24 |
| `reg.exe` | `v` | `﹣` | direct + cmd.exe process launch, invalid target | 2026-08-24 |
| `schtasks.exe` | `tn` | _none_ | direct + cmd.exe process launch, invalid target | 2026-08-24 |
| `schtasks.exe` | `xml` | _none_ | direct + cmd.exe process launch, invalid target | 2026-08-24 |
| `tar.exe` | `x` | `―` `‒` `−` `‑` `－` | direct + cmd.exe process launch, invalid target | 2026-08-24 |
| `tasklist.exe` | `m` | _none_ | direct + cmd.exe process launch, invalid target | 2026-08-24 |

## Reading the totals

Counting the cells in the table above: **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 `-`/`/` — each one a place a literal-hyphen detection is blind.

## The self-correction

The first version of the verdict rule was wrong, and the code still carries the account in full (`probe/derive.mjs`). It read "the test response matches the canonical-prefix control" as proof of acceptance. `tasklist /m` exposed the flaw: its canonical control `-m=<value>` does **not** parse (tasklist rejects the `=` separator), and the binary echoes the offending argument back with the Unicode dash already normalised to `-` in its own error text — so two *failures* produced byte-identical output and were read as a shared success. The corrected rule first proves the canonical control itself parsed in the exact form under test; only then can a character be judged, and everything else is INDETERMINATE. Publishing that correction is the point: the numbers are only worth as much as the discipline that produced them.

## Reproduce it

See `probe/README` in the parent `README.md`. In short: run `probe/guest-runner.ps1` on a disposable Windows box to produce `results.ndjson`, then `node probe/rederive.mjs --run <dir>` to grade it offline with no network. The grader re-derives every verdict from the stored raw responses, so the rule can be audited and re-run without re-measuring.
