Encode Arithmetic

Replace integer arithmetic with more complex expressions, encoded with Mixed Boolean Expressions (MBA). For example, the following identities can be used to encode integer addition:

x + y = x - ¬ y - 1
      = (x ⊕ y) + 2·(x ∧ y) 
      = (x ∨ y) + (x ∧ y) 
      = 2·(x ∨ y) - (x ⊕ y) 

For example, Tigress might replace

z = x + y + w

with

z = (((x ^ y) + ((x & y) << 1)) | w) + 
    (((x ^ y) + ((x & y) << 1)) & w);

Here's a LigerLabs video that discusses this transformation:

OptionArgumentsDescription
--Transform EncodeArithmetic Replace integer arithmetic with more complex expressions.
--EncodeArithmeticKinds builtin, plugins, generated Whether to use builtin or plugin patterns, or both. From version 3.3.3. Default=builtin.
  • builtin = Use Tigress' built-in MBA patterns
  • plugins = Use plugin MBA expressions
  • generated = Use MBA encodings computed on the fly by the linear-MBA enumerator
--EncodeArithmeticMaxLevel INTSPEC How deep to recurse into expressions. Default=1.
--EncodeArithmeticMaxTransforms INTSPEC How many transformations to perform on each expression. Default=1.
--EncodeArithmeticRepeatTimes INTSPEC How many times to repeat the rewriting process. Equivalent to calling EncodeArithmetic multiple times. From Version 3.3 Default=1.
--EncodeArithmeticDumpFileName string Name of Json file onto which we dump transformed expression. The actual file will be function-name_number_fileName.json. From version 3.3.2. Default=100.
 

Splitting Encoded Expressions

There are two parts to attacking an MBA expression: first you have to find the expression, and then you have to decode it (i.e. turn it back into something resembling it's original form). You can split up expressions to make them more diffiult to locate in the code (both statically and dynamically).

OptionArgumentsDescription
--EncodeArithmeticMaxSplit INTSPEC How many pieces in which to split the transformed expression. From Version 3.3 Default=1.
--EncodeArithmeticAddImplicitFlow BOOLSPEC Add implicit flow to the pieces split out from an encoded expression. You need to set --EncodeArithmeticMaxSplit=value greater than zero for this to take effect. From Version 3.3 Default=false.
--EncodeArithmeticImplicitFlow S-Expression The type of implicit flow to insert. See --AntiTaintAnalysisImplicitFlow for a description. Default=none.
--EncodeArithmeticAddOpaques BOOLSPEC Add opaque predicates to the pieces split out from an encoded expression. You need to set --EncodeArithmeticMaxSplit=value greater than zero for this to take effect. From Version 3.3 Default=false.
 

Diversity

For each operator, there are many possible encodings, and at transformation time, these are selected from randomly.

 

Listing and Selecting Encodings

You can list every rewrite pattern -- its name and the expression it emits -- and restrict the transformation to a chosen subset of patterns by name. Both facilities are available only in deity mode. Use --EncodeArithmeticList to discover the exact names, then pass them to --EncodeArithmeticSelect:

tigress --DeityMode=... --Transform=EncodeArithmetic \
        --EncodeArithmeticList=true --Functions=main input.c --out=out.c

tigress --DeityMode=... --Transform=EncodeArithmetic \
        --EncodeArithmeticSelect=Frobnitz_add3,Wibble_xor7 \
        --Functions=main input.c --out=out.c

OptionArgumentsDescription
--EncodeArithmeticList BOOL Print the whole rewrite-pattern catalog -- every pattern's name and the expression it emits, shown on symbolic operands -- and continue. Useful for finding the exact names to pass to --EncodeArithmeticSelect. Refused unless the deity key is supplied with --DeityMode. From version 4.1.1 Default=false.
--EncodeArithmeticSelect (Deity mode only.) Comma-separated list of exact rewrite-pattern names to draw from, restricting the rewriter to just those encodings. Names come from --EncodeArithmeticList. Refused unless the deity key is supplied with --DeityMode. From version 4.1.1 Default=none.
 

Debugging and Attacking (From version 3.3.2

There have been many recent papers on attacking MBA expressions. To facilitate such attacks you can dump all the transformed expressions onto a Json file for further processing. Simply set --EncodeArithmeticDumpFileName=filename.json.

 

Resilience against automated simplification

How well does an encoded expression resist being simplified back? We ran a set of representative encoded expressions through the C compiler (-O2) and the public Mixed Boolean-Arithmetic simplifiers SiMBA, MBA-Blast and GAMBA. A tool solves a row when its output is verified equal to the ground truth (by Z3) and reduced to ground-truth size. A row's resilience is the weakest tool that still solves it: trivial (the optimizer folds it), weak (a linear solver folds it), medium (only a general solver does), strong (survives all).

The take-away: resilience tracks the function's algebraic class, not the size of the encoding. A linear operator is trivial or weak however it is encoded; a polynomial one (a product) reaches medium; and a permutation-polynomial wrap of a linear operator still looks enormous yet stays weak — a linear solver recovers it, because the underlying function is unchanged.

EncodeArithmetic MBA resilience — which tools reduce each obfuscated form to its ground truth (z3-verified). Generated 2026-09-20.
nameclassground truthcompiler -O2SiMBAMBA-BlastGAMBAresilience
add_xor2andlinear-2x+y✓✓✓✓trivial
add_or_andlinear-2x+y✓✓✓✓trivial
sub_notpluslinear-2x-y✓✓✓✓trivial
xor_or_andlinear-2x^y✓✓✗✓trivial
and_incexclinear-2x&y✓✓✓✓trivial
add_eyrolleslinear-2x+y✗✓✓✓weak
add3_adderlinear-3x+y+z✗✗✗✓medium
add4_pairslinear-4x+y+z+w✗✓✗✓weak
add_constlinear-2cx+y+42✓✓✗✓trivial
mul_polypoly-2x*y✗✗✓✓weak
mul_poly_cpoly-2cx*y-41✗✗✗✓medium
mul_lin_zpoly-3(x+y)*z✓✗✗✓trivial
mul_xorpoly-4x*y+(z^w)✗✗✗✓medium
sq_polypoly-1x*x✓✗✓✓trivial
permpoly_nlnonlin-wrapx+y✗✓✗⌛weak
solved8/159/157/1514/15

✓ solved (output z3-equal to ground truth)   ✗ not solved / wrong   ⌛ timeout   – tool not installed.
Resilience = the weakest tool that still solves it (weakest→strongest: compiler=trivial, SiMBA/MBA-Blast=weak, GAMBA=medium; strong = survives all).

 

References

The identities are taken from the book Hacker's Delight (Henry S. Warren), from Zhou and Main's Diversity via Code Transformations, and from the linear Mixed Boolean-Arithmetic families of Ninon Eyrolles' PhD thesis (Paris-Saclay, 2017), as machine-checked in the Coq development of Blazy and Hutin, Formal Verification of a Program Obfuscation Based on Mixed Boolean-Arithmetic Expressions (CPP 2019).