Week 12 · Block IV Limits
How generic can deobfuscation be?
The one week with no new evidence. What transferred between implementations, what did not, and the protocol for choosing a method when nobody tells you what was done to the binary.
Given an unfamiliar obfuscated function, what would you do first, and what would you have to check?
No new specimens this week. Everything below has already appeared; the work is putting it in one place and drawing the conclusion it supports.
Everything, on one table#
From obfuscated code to a working rewrite
All eight inputs below are runnable originals; the output claims differ sharply.
| Case | Recovered / demonstrated | Rewritten original PE | Qualification |
|---|---|---|---|
| OLLVM SUB | Selected local expressions | NOT PRODUCED | msynth recovery is not binary rewriting. |
| Polaris MBA | Local recovery; passing Mergen lift | NOT PRODUCED | Substantial MBA remains in lifted IR. |
| OLLVM BCF O0 | 22 proved gates patched | PASS | Predicates and dead clone bytes remain. |
| Polaris BCF O0 | 16 proved gates patched | PASS | Same generic proof workflow transfers. |
| OLLVM FLA O0 | 25/25 successor pairs | PASS | Graph need not match clean exactly. |
| Hikari FLA O0 | 28/28 successor pairs | PASS | O0 success does not extend to O2 here. |
| Polaris FLA O0 | 14/22 true pairs + 1 false pair | WRONG SEMANTICS | Legitimate switch cases collapse. |
| Tigress Flatten O0 | Stock discovery incomplete | NO OUTPUT | Fails before symbolic successor analysis. |
A semantic result is not automatically a working executable, and a valid PE is not automatically correct.
PASS refers to retained validation. Successor-pair scores are not universal proofs of program equivalence.Open the original lecture figure (SVG)
Eight cases. All eight inputs were working programs. The output column is where they diverge, and it has four distinct values in it — produced, not produced, wrong, and nothing at all.
What transferred#
Three things crossed an implementation boundary in this corpus.
The branch proof plus a separate patcher. A generic symbolic harness proved thirty-eight injected gates one-sided across OLLVM-16 and Polaris, while leaving sixteen genuine conditions two-sided, with no false positives. A separate program turned those proofs into changed bytes. 9,189 differential triples, zero mismatches.
Local arithmetic recovery. Ten of twelve isolated sites recovered across three different implementations, every accepted candidate carrying an independent 32-bit equivalence proof, and the two that could not be proved recorded as UNKNOWN rather than accepted.
Stock unflattening, within a lineage. From one LLVM-family implementation to a closely related one: 25/25 and 28/28 successor pairs, two working executables.
Look at what those three share. Each one carries an explicit model — of memory and path prefix, of bit width, of dispatcher and state layout — and each one has a separate validation step that could have said no. In the case of the third, on a fourth specimen, it did.
What did not#
- A seed is not a reconstruction key. Two fresh runs of the same command with the same RNG seed produced IR with different hashes for one pass. The exact retained artefact is the specimen, not the command that made it.
- Local recovery is not whole-function recovery. Ten of twelve isolated sites; zero of twenty composed return expressions.
- An
-O0result is not an-O2result. Two bogus-control-flow lanes are indistinguishable from clean by-O1. One unflattening success does not survive its own implementation’s-O2. - A mathematical identity is not a machine identity. Not until you have checked it at the width the machine uses.
- An initialised-state invariant is not a claim about arbitrary state. Zero of three gates from generic entry state; three of three from one captured state.
- A discovery failure is not a solver failure. Six blocks found where thirty-eight exist, before any analysis ran.
The pattern is uncomfortable and it is the course’s thesis: the methods that travelled were the ones that modelled semantics and validated separately. The methods that did not travel were the ones that recognised a shape.
A seed is not always a reconstruction key
Archive what actually ran, not only the command that was meant to reproduce it.
| Tested lane | Repeat observation | What to retain |
|---|---|---|
| OLLVM / Hikari SUB | REPEATED IDENTICALLYSeeded IR in the retained probe | Exact flags, compiler identity, IR and PE hashes. |
| Polaris selected IR passes | REPEATED IDENTICALLYSUB/BCF/FLA/indirection/gvenc smoke variants | Exact artifacts; not a universal seed guarantee. |
| Polaris MBA / backend | VARIEDSame requested RNG seed was insufficient | The exact generated artifact is the specimen. |
| Tigress | WHOLE FILES VARIEDIdentifier salts / ordering were not pinned | Exact generated C, then reuse it across optimization levels. |
| BinProtect | NO EXPOSED SEEDOutputs varied | Exact PE for static analysis; validity failed here. |
Reproducibility has several levels: same source, same generated representation and same executable bytes.
Tigress target bodies were more stable than whole generated files; whole-file variation is not proof of semantic variation.Open the original lecture figure (SVG)
Four traps, with the specimen that sets each one off#
Four tempting conclusions to avoid
Keep visual appearance, representation validity and semantic correctness separate.
| Tempting conclusion | Retained counterexample | What the evidence says |
|---|---|---|
| Smaller CFG = correct | Polaris FLA: 56 to 10 blocks | WRONGF9CD8332 instead of DBEFFCE7; exit 1. |
| Valid IR = faithful lift | Mergen on valid OLLVM SUB input | WRONG3,917 / 4,118 mismatches in every lifted stage. |
| Six discovered blocks = six actual blocks | Tigress Flatten | INCOMPLETEThe jump-table-aware reference has 38 blocks. |
| Matching PDB ID = valid instruction map | Rewritten BCF / FLA PEs | STALE METADATAOld RSDS references survive. Use exports / RVAs. |
Validate the output at the level of the claim: expression, CFG, lifted function or complete executable.
Original OLLVM SUB passes native checks. FLA whole-function rewriting moves internal instructions.Open the original lecture figure (SVG)

A working rewrite: the dispatcher network becomes a compact branching graph, and the retained execution checks pass.

An incorrect rewrite: the dispatcher network becomes an even smaller graph, and the program returns the wrong value.
Those two images are the final exam of the course, in the sense that matters. Neither one, on its own, tells you which is which. You need the runtime record, or the successor score, or the region map. The picture is not the evidence.
A protocol, not a checklist#
Given a target function and no information about what was done to it, the order below is what this corpus supports. It is a protocol because each step names what it assumes, and you get to say so out loud.
- Get a matched comparator. Same compiler family, same optimisation level. Without it, every count you take is unanchored. Most of the bad claims in this field die here.
- Decide what is being hidden. Expression complexity, branch outcome, or successor relation? Those need different methods and produce different outputs. Look at block count against expression size: a one-block function with 678 instructions is not the same problem as a 60-block dispatcher.
- Establish ground truth where you can. A clean build, a source file, a canonical output, a test vector. If ground truth exists, use it, and say that you had it — a result checked against known answers is worth more than one that could not be.
- Choose the method by the representation, not the vendor. Expression recovery for arithmetic, symbolic branch proof for gates, successor recovery for dispatchers, lifting when you want the whole function in one representation. Naming the obfuscator buys nothing; in this corpus one filename was wrong about which binary it even showed.
- Write the assumption down before you run anything. Which values are symbolic. What the memory model covers. What bit width. Which path prefix. If you cannot write it, you will not be able to report the result.
- Validate at the level of the claim. An expression claim needs an equivalence proof at the right width. A CFG claim needs a convention and a comparator in the same convention. A lifted-function claim needs test vectors. An executable claim needs the executable run.
- Write down what you could not establish. Not as a disclaimer. As the part of the result that tells the next person where to start.
- Analysis success
- A tool ran, produced output, and did not error.
- Semantic proof
- A property holds — under a stated model, state and path prefix.
- Binary rewriting
- Bytes changed and a file still loads.
- Runtime correctness
- The program answered correctly, on the inputs that were actually run.
Two places this course stops#
The lane that never ran. One post-link tool applied four transformations to two inputs and returned exit code 0 for all eight invocations. Seven of the eight protected executables terminate with an access violation and the eighth hangs. Its transformations are visible and real; its outputs support static observations and nothing else. There is no end-to-end deobfuscation lane there because there was never a working protected program to deobfuscate.
Virtualization. Translating protected behaviour into custom bytecode run by an embedded interpreter is where a lot of commercial protection actually lives, and this corpus contains no virtualization or devirtualization experiment. It was intentionally not tested. So the course can tell you what the problem is and cannot tell you anything measured about it, and week 12 is not going to pretend otherwise by reasoning from the results above. If you want to work on it, that is a research question with an empty results section, which is the best kind.
- post-link protected outputs valid at runtime — seven crashes, one hang
- 0 / 8post-link protected outputs valid at runtime — seven crashes, one hangbinprotect_validation.md
- virtualization experiments in this corpus
- 0virtualization experiments in this corpusEXPERIMENTAL_CORPUS.md
- screenshots withheld because their contents contradict their filenames
- 2screenshots withheld because their contents contradict their filenamesVISUAL_INDEX.md
- 483-line program behind every result on this site
- 1483-line program behind every result on this siteSHOWCASE_VERSION.txt
That last number is the honest boundary of the whole course. Everything here is about one small C program, compiled sixty-eight ways. The reasoning generalises. The measurements do not, and a page that blurred those two would have failed its own standard.
Studio: your capstone protocol, out loud#
Bring your sample and whatever you have. Twenty minutes each, in pairs, then a round of the whole room.
- State what you think is being hidden in your function, and the evidence for that. One minute.
- Name the two representations you will work in and why those two.
- For each method you plan to use, state the assumption it depends on. Your partner’s job is to find the assumption you left out.
- For each conclusion you expect to reach, name the level you will validate it at and how.
- Read your limitations section aloud. This is the part people skip and the part markers read first, and hearing your own draft is the fastest way to notice that it says nothing.
Then one question to the room, which is the question the course has been building towards for twelve weeks: what would make you abandon your current conclusion? If there is no answer, there is no conclusion yet.
- end_to_end_results.mdall eight cases, recovered against produced, with qualifications14 kB
- FINAL_RESEARCH_OVERVIEW.mdthe thirteen-section synthesis, including sections 9, 10 and 1312 kB
- CLAIMS_AND_CAVEATS.mdevery safe formulation beside the overclaim it is confused with10 kB
- selected_metrics.mdevery headline number with its convention and provenance6 kB
- BINARY_GUIDE.mdper-specimen runtime status and export RVAs, for the capstone6 kB
- BIBLIOGRAPHY.mdthe upstream tools and the background papers, with what each is and is not6 kB
Come to the studio with
The studio assumes you have done these. Each is written so you can tell whether you have, without asking.
- you have a draft capstone limitations section, however rough
- you can name, from memory, the four outcomes this course keeps apart
- you have picked the two representations your capstone will work in