SOS — Symbol Obfuscation Score
A static, linguistic heuristic that measures how much an executable's symbols (class, method, field, property and event names) have been renamed into gibberish by an obfuscator. Malva.RE does not decompile or run anything — it simply judges whether identifiers look like real, pronounceable words or random noise.
Obfuscators (Dotfuscator, ConfuserEx, .NET Reactor…) rename symbols in bulk:
ComputeInvoiceTotal becomes a, Xk3, zZqWpb.
A legitimate binary keeps readable names. SOS quantifies that gap.
What it means
- SOS near 0 → readable, pronounceable symbols → likely not obfuscated;
- SOS near 1 → unreadable, unpronounceable symbols → likely obfuscated.
It is a suspicion signal, not a verdict: obfuscation isn't necessarily malicious (legitimate IP protection), but a high score is a strong triage cue.
Scope
SOS is currently computed for .NET executables. Malva.RE extracts the user-defined symbols of each type — class, method, field, property and event names — excluding external types and compiler-generated members. The scoring itself is language-agnostic, so SOS can be extended to other executable formats as their symbols become available.
How the score is computed
Each identifier is split into blocs (on separators, digit↔letter transitions
and camelCase boundaries: GetUserName2 → [Get, User, Name, 2]), and each
bloc into syllables. The score starts at 1.0 (perfectly natural)
and is multiplied down toward 0 (gibberish) by penalties:
| Per syllable | Factor |
|---|---|
| single letter | × 0.8 |
| no vowel + consonant mix | × 0.7 |
| length > 8 / > 7 / > 6 / > 5 / > 4 | × 0 / 0.1 / 0.2 / 0.4 / 0.6 |
known syllable (ou, tion, ing, ment…) | reset to 1 |
| Per identifier | Factor |
|---|---|
| digits not at the end | × 0.85 · 0.60^(n−1) |
| long blocs with no vowel (≥ 3) | × 0.75^n |
| all-uppercase blocs (≥ 2) | × 0.95 · 0.8^(n−1) |
| single-letter blocs | × 0.95 · 0.75^(n−1) |
| uppercase + single-letter combined | × 0.7 |
| fewer syllables than blocs | × 0.9 |
The result is clamped to [0, 1]: ComputeTotal stays near 1,
zZqWpb collapses toward 0. All per-symbol scores across every module are
averaged into a readability average (≈ 1 = clean, ≈ 0 = obfuscated).
From readability to SOS
The readability is inverted: SOS = round(1 − averageReadability, 2),
so high SOS = heavily obfuscated. It is mapped to a level (on SOS × 10):
| SOS | Level |
|---|---|
| < 0.20 | Very Low |
| 0.20 – 0.35 | Low |
| 0.35 – 0.65 | Medium |
| 0.65 – 0.80 | High |
| ≥ 0.80 | Very High |
Outputs
- a numeric score (0..1) and a level, attached to the analysed executable;
- a tag
SOS: x.xxthat escalates to the top-level sample; - in the report Characteristics panel: a level chip + a 10-bar gauge
(
SOS × 10filled bars).
Limits
- Heuristic, not proof — tuned for latin-script pronounceability; very short
or acronym-heavy legitimate names (
db,io,xml) can raise the score, and a dictionary-based renamer could fool it. - .NET only today.
- SOS only looks at symbol names — not control-flow obfuscation, string encryption or packing. A packed binary with few readable symbols may score low yet be heavily protected; those cases rely on other detections (PEiD, packers, entropy).