Skip to content
Obfuscationadvanced

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:

c
// 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.

Votes

Comments(0)