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.

Disassembled, the impossible identity is computed at runtime and the never-taken edge still points at fully decoded junk:

asm
        ; compute 7*y*y - 1 in eax
        mov     eax, dword [rbp-8]      ; y
        imul    eax, eax                ; y*y
        imul    eax, eax, 7            ; 7*y*y
        sub     eax, 1                  ; 7*y*y - 1
        ; compute x*x in ecx
        mov     ecx, dword [rbp-4]      ; x
        imul    ecx, ecx                ; x*x
        cmp     eax, ecx                ; 7*y*y-1 == x*x ? (no integer solution)
        jne     .real_code              ; always taken
.junk:                                  ; dead block, never reached
        call    junk_code               ; disassembler must still decode this
.real_code:
        call    real_code               ; the path that always runs

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)