Opaque Predicates
Inserting conditional branches whose outcome is known at obfuscation time but hard to resolve statically, breaking control-flow recovery in disassemblers and decompilers.
An opaque predicate is a boolean expression whose value is always known to the obfuscator (always true, always false, or context-dependent) but is expensive for an analyst or decompiler to prove. They pad the control-flow graph with unreachable or always-taken edges, defeating naive CFG recovery.
How it works
A classic always-false predicate uses a number-theoretic identity:
// 7*y*y - 1 == x*x has no integer solutions, so this branch is dead.
if (7 * y * y - 1 == x * x) {
junk_code(); // never executes, but the disassembler must consider it
}
real_code();In assembly the dead branch is fully formed, so a linear sweep disassembler decodes the junk and a recursive one cannot prove the edge is dead without a constraint solver.
Detection & bypass
- Symbolic execution / SMT: tools like angr or Triton can prove the predicate constant and prune the dead edge.
- Decompiler microcode plugins (e.g. HexRaysDeob, D-810) simplify common opaque-predicate patterns automatically.
- Look for arithmetic identities feeding a comparison that no real input can satisfy.
Opaque predicates are frequently combined with control-flow flattening and junk-byte insertion.