Virtual call — vcall¶
Virtualises selected direct calls through synthetic vtables and indirection layers. Each transformed call site looks up its target through a generated table, optionally with decoy entries and per-callsite index-expression variation — so a static reader can no longer read the callee off the call instruction.
Options¶
| Key | Default | Range | Meaning |
|---|---|---|---|
prob |
30 | 0–100 | Probability (%) to virtualise an eligible call site. |
maxSites / max / sites |
0 | 0–100000 | Cap on call sites (0 = auto). |
opaqueVTableNames |
1 | 0/1 | Hash-based naming for generated globals. |
addDecoyEntries |
1 | 0/1 | Add decoy vtable entries pointing to safe stubs. |
decoyMin |
2 | 0–64 | Minimum decoy entries per table. |
decoyMax |
4 | 0–64 | Maximum decoy entries per table (must be ≥ decoyMin). |
varyIndexPerCallsite |
1 | 0/1 | Vary the index expression per call site. |
indexStrength |
2 | 0–3 | Index obfuscation strength level. |
mergeVTables |
0 | 0/1 | Merge vtables across compatible callees (experimental). |
encryptTable / encTable |
0 | 0/1 | Encrypt vtable entries, filled at runtime by a ctor (non-merged only). |
Example¶
__attribute__((annotate("obf: vcall(prob=35,indexStrength=2,decoyMin=2,decoyMax=6)")))
int dispatch(int op) { return handler(op); }
Decoys + encrypted tables
addDecoyEntries=1 seeds each table with plausible-but-wrong targets; encryptTable=1 keeps the
real entries as ciphertext until a load-time constructor fills them, so the table is not readable
at rest. Combine with a high indexStrength to obscure which slot is actually selected.