Skip to content

Pipeline & ordering

Annotation order does not decide execution order. The driver resolves a stable pipeline by topological sort with conflict enforcement, so stacking annotations any way you like produces the same, correct ordering.

Execution order

The function pipeline runs in this partial order (earlier = first):

flowchart LR
    constenc --> mba --> substitution --> vcall --> split --> sdiff --> bcf --> flattening --> shield --> adec --> vm

Module-only passes run before the function pipeline:

flowchart LR
    fmerge --> strenc --> FP[function pipeline]

Why this order:

  • constenc first — encrypts constant operands so every later pass buries the materialization arithmetic further.
  • Expression passes (mba, substitution, sdiff) before CFG passes (split, bcf, flattening) so the structural passes fold over already-obfuscated data flow.
  • Post-hardening (shield, adec) near the end, so their volatile barriers and anti-decompiler gadgets protect the finished shape.
  • vm last — it virtualises the entire function, so anything that ran before it gets encoded into the bytecode. Pre-obfuscating then virtualising yields harder bytecode.

fmerge feeds the pipeline

fmerge collapses same-group= functions into one super-function first, so the merged body is then obfuscated by the whole function pipeline. See Function merging.

The vmflattening conflict

vm and flattening both restructure the whole CFG. They cannot run on the same function — the pipeline rejects the combination.

// ❌ rejected — pick one
__attribute__((annotate("obf: flattening, vm")))

// ✅ virtualise (encodes any earlier expression obfuscation)
__attribute__((annotate("obf: mba(prob=70), bcf(prob=30), vm(hardened=1)")))

Use flattening when you want a visible dispatcher in native code; use vm when you want the body gone entirely behind an interpreter. See Choosing passes.

Overriding the order

Two global flags override the default topological sort (both still run conflict checks):

Flag Effect
-obf-pipeline-ordering=<csv> Explicit order, e.g. mba,split,bcf,flattening. Listed passes run first in this order; remaining enabled passes are appended topologically. Unknown names are fatal.
-obf-pipeline-ordering-ann Use the per-function annotation order verbatim. Ignored when -obf-pipeline-ordering is set.

These are for experimentation and debugging. The default (topological) order is the tested, recommended one — reach for overrides only when you have a specific reason.