Documentation Index
Fetch the complete documentation index at: https://docs.kong.fyi/llms.txt
Use this file to discover all available pages before exploring further.
What is obfuscation?
Obfuscated binaries don’t just lack symbols — they actively fight analysis. A function that’s three lines of logic can become two hundred lines of meaningless-looking jumps, dead branches, and encrypted constants. Kong detects five categories of obfuscation and handles each with targeted tooling.Detected techniques
Control Flow Flattening (CFF)
CFF replaces a function’s natural control flow with awhile(1)/switch(state) dispatcher. Every basic block becomes a case in the switch, and transitions happen by updating a state variable.
if/else with two branches. CFF makes it look like a complex state machine.
Bogus Control Flow
Bogus control flow inserts branches guarded by opaque predicates — conditions that always evaluate to the same value but are hard for static analysis to prove.Instruction Substitution
Simple operations are replaced with mathematically equivalent but cryptic expressions:| Original | Obfuscated |
|---|---|
a ^ b | (~a & b) | (a & ~b) |
a + b | (a ^ b) + 2 * (a & b) |
a - b | (a ^ (a ^ b)) - (a ^ b) |
String Encryption
Strings are encrypted at compile time and decrypted at runtime, hiding clues that reverse engineers rely on:VM Protection
The most aggressive technique. The original code is compiled into custom bytecode, and the function is replaced with an interpreter:The agentic deobfuscation loop
When Kong detects obfuscation in a function’s decompilation, it switches from single-shot LLM analysis to a multi-turn agentic loop. The LLM gets access to six specialized tools and can call them iteratively to peel away layers of obfuscation:| Tool | Purpose |
|---|---|
simplify_expression | Z3-based symbolic simplification — detects and resolves opaque predicates |
eliminate_dead_code | Removes unreachable branches after predicates are resolved |
trace_state_machine | Extracts CFF state transitions and exit conditions from Ghidra’s IR |
identify_crypto_constants | Scans for known constants (AES S-box, SHA-256 init values, MD5 T-table) |
get_decompilation | Re-reads decompilation from Ghidra after applying intermediate results |
get_basic_blocks | Extracts the control flow graph and basic block structure |
- LLM sees a suspicious
while(1)/switch→ callstrace_state_machine - State machine tool returns the transition graph → LLM identifies dead states
- LLM calls
simplify_expressionon a guard condition → z3 proves it’s always true - LLM calls
eliminate_dead_codewith the resolved predicate → dead branch removed - LLM calls
get_decompilationto see the cleaned-up code → produces final analysis
Z3 simplifier internals
Thesimplify_expression tool is powered by the Z3 theorem prover. Here’s how it works:
- Parse: The C expression from the decompilation is parsed into a Z3 AST (abstract syntax tree)
- Simplify: Z3 attempts algebraic simplification — reducing complex bit operations to simpler forms
- Prove: Z3 checks whether the expression is a tautology (always true) or contradiction (always false)
- Report: If it’s one of these, the expression is an opaque predicate, and the corresponding branch is dead code
(x * (x + 1)) % 2 == 0:
- Z3 recognizes that
x * (x + 1)is always even (one of two consecutive integers is even) - Z3 proves the expression is a tautology
- Kong marks the
elsebranch as dead code
(~a & b) | (a & ~b) simplifies to a ^ b through Z3’s bit-vector reasoning.
Dead code elimination
After opaque predicates are resolved, Kong prunes the dead branches. The algorithm:- Take the set of resolved predicates and their constant truth values (from the Z3 simplifier)
- Walk the decompilation AST
- For each
if/elseguarded by a resolved predicate:- If the predicate is always true: keep the
ifbody, remove theelsebody - If the predicate is always false: keep the
elsebody (if it exists), remove theifbody
- If the predicate is always true: keep the
- Remove any variables and assignments that are only referenced in deleted branches
State machine tracing for CFF
When a function uses control flow flattening,trace_state_machine reconstructs the original control flow:
- Identify the dispatcher: Find the
while(1)/switch(state)loop and the state variable - Extract transitions: For each switch case, determine what value the state variable is set to — this gives the edges in the state graph
- Find entry and exit: The initial state value is the entry point; cases that break out of the loop or return are exits
- Reconstruct flow: Build a directed graph of state transitions. The original control flow is the path through this graph from entry to exit
- Simplify: Collapse linear chains of states (A→B→C where B has no other edges) back into sequential code
Further reading
- Pipeline Overview — where deobfuscation fits in the analysis pipeline
- Context Windows — how deobfuscation results feed into context
- Analyzing a Binary — running Kong on obfuscated binaries

