Aegis vs automated reverse engineering: the semantic shortcuts that survived
One Aegis masked-island experiment expanded a short arithmetic chain into 123,200 bytes and 19,555 instructions. A source-informed probe recovered its 40 operations in a recorded 3.976 seconds. An independent reverse-engineering agent also produced a standalone predictor, although it exceeded its assigned time budget. The failure was not that the masks were too small. The emitted state kept exposing a cheap way to recover the same underlying computation.
The objective was extraction cost, not an invisible program
Aegis explores binary transformation, virtualized execution, per-build representations, and runtime diversification. The adversarial model gives the analyst the complete local artifact, chosen inputs, disassembly, execution traces, emulation, and patching. An AI agent can coordinate those tools, but it does not need a special privilege that a conventional analyst lacks.
Under that model, changing the physical representation can still be useful. Different opcode maps, handler layouts, state encodings, and continuation structures may make a previous extractor stop working. The mistake is to treat that observation as the result of the experiment. The stronger question is whether a new analysis can normalize the representation and recover a predictor that no longer needs the protected binary.
The workflow therefore separates artifact A, which the analyst can inspect, from artifact B, generated with a different build seed and used only after the recovered model is frozen. B tests whether the result transferred beyond A’s particular byte layout. That is more informative than asking whether two output files look different.
The first useful counterexample was an XOR projection
The masked native island represented its accumulator using three shares. At source-operation boundaries, the full word could be recovered by XORing those shares. Let the physical state be s and define the projection:
Q(s) = s0 XOR s1 XOR s2
Q(T(s)) = f(Q(s))Here T is the emitted physical transition and f is the original arithmetic operation. If the relation holds at each boundary, the analyst does not have to reconstruct the purpose of every intermediate gate. They can identify the checkpoints and execute the much smaller chain in the projected domain.
A refresh can change the shares without changing this observation. For example, XORing the same refresh word into two shares cancels when all shares are combined. More elaborate carry networks can add substantial instruction count while still returning to the same complete-word checkpoint.
The related variable-carrier experiments make the distinction especially clear. A three-, five-, or seven-share representation can still expose an all-share XOR aggregate. For bitwise AND, a complete product expansion has the identity below; if all relevant inputs and outputs are visible, the observer can reason about the aggregate rather than every physical term.
(a0 XOR ... XOR ak) AND (b0 XOR ... XOR bm)
= XOR over every pair (ai AND bj)This is a statement about the experiment’s full-observation boundary. It is not a claim that masking has no value under a different, restricted observation model. In this local software experiment, however, the analyst could observe the jointly sufficient state.
What the extraction actually recovered
The deeper fixture exposes one u64 → u64 arithmetic function. Its command-line entry calls that export and prints the result; unlike an older functional fixture, it does not contain a duplicate reference implementation in main. This removed an obvious source of accidental ground-truth leakage.
The first probe was written with knowledge of the emitter. During extraction, its inputs were artifact-only: it read A, inferred the ordinary-VM prefix from observations, located the island in executable bytes, and identified constant-share groups and accumulator checkpoints. It did not read B, source files, maps, or manifests during that extraction. Calling this a blind attack would still be wrong, because the probe author already knew the design.
The recorded extraction reduced the island to a bounded Add/Sub/Xor formula. The predictor used no PE parser or emulator at prediction time. It matched 2,048 fresh native A inputs. A separate coherent replacement test skipped the island on 64 fresh inputs while retaining the surrounding VM and the continuation state. That matters: replacing an entire process with a reference answer is easier than substituting a recovered computation inside the real execution path.
| Frozen masked-island experiment | Recorded observation |
|---|---|
| Physical island | 123,200 bytes · 19,555 instructions |
| Recovered island chain | 40 operations |
| Source-informed extraction | 3.976 s, single recorded run |
| Fresh A behavior checks | 2,048 matches |
| Coherent in-VM replacements | 64 checks |
| Frozen predictor against B | 2,048 matches |
The normal traces contained 32,652–32,803 instructions; the replacement traces contained 13,098–13,249. The shorter traces show that the replacement really bypassed work. They do not establish a general speedup or universal correctness for arbitrary protected programs.
The independent agent result needs its time limit attached
A separate reverse-engineering agent received a copied A, the public ABI, and the command-line contract. It independently recovered an initial modular addition and 16 XOR/add-or-subtract rounds, then froze a Python predictor using only the standard library. The predictor no longer read the executable.
The agent used 231 exploratory queries and passed 128 post-freeze A queries. Its elapsed time was 16 minutes 50 seconds against a requested ten-minute budget. The outcome is therefore semantic recovery, but not an in-budget success. Dropping the budget overrun would change the meaning of the result.
After the agent finished, the frozen script and its hash were reviewed. The unchanged predictor was checked against B and the original native fixture using 2,048 new random inputs plus four edge cases for each. Those were post-freeze oracle checks, not an opportunity to train or refit on B. The result provides concrete evidence of cross-seed transfer for this fixture, not proof of equivalence over every possible input.
Normalization fixed inflated work counts, not the leak
The next implementation change was to simplify supported immediate-64 arithmetic runs before emitting a share network. Zero identities disappear; adjacent translations and XOR constants combine where the algebra permits it. A particularly useful case is the most-significant-bit toggle:
x XOR 2^63 = x + 2^63 (mod 2^64)
ADD a
XOR 2^63
SUB a
// Equivalent supported run:
XOR 2^63Adding the highest bit modulo 2^64 toggles that bit and cannot change any lower bit. This specific translation commutes with addition and XOR. The reducer tracks its parity, including carries from combined low-63-bit translations, and folds it into the remaining operation. It does not move arbitrary XOR constants across addition; those can interact with carries and change the function.
The admission accounting then distinguishes source operations from effective operations and elided work. An explicit masked request that collapses to an identity, one operation, or an XOR-only chain cannot claim a multi-operation masked region. The original semantic span still has to be preserved for VM replacement, with checked bounds.
The recorded normalization regression executes 6,336 native vectors and checks unwinding at 39,768 instruction boundaries. It also constructs 64 independent pairs of equivalent source chains and requires identical generated code at fixed seeds. That protects against a compiler claiming diversity by emitting removable noise.
It did not repair the complete-word projection. The normalized artifact still yielded a formula, in a recorded 4.764 seconds, and passed the same kinds of native and coherent-replacement checks. Its island had 32 effective operations, 121,690 bytes, and 19,351 instructions. The two extraction timings came from different concurrent loads and are not a performance comparison. The relevant result is that the semantic shortcut survived.
A million instructions can still share one useful boundary
The ordinary contextual VM had a related problem. A frozen A-only audit observed 1,402,432 instructions and 15,508 blocks on one execution. NOPs were only 0.596% of the trace, so this was not simply a padding exercise.
The trace nevertheless found a stable contextual producer, visited 58 times in that execution. It repeatedly used the same read helper and binding mixer before reaching a common dispatcher. The dispatch boundary advanced the VM instruction pointer and explicitly combined two state words before choosing an arithmetic lease.
Removing inactive cells, random gaps, construction slots, and unused handlers was legitimate liveness work. It did not remove the repeated semantic interface. Copying the producer or changing its byte signature would also leave an analyst able to group equivalent operations by reads, writes, and data dependencies.
One proposed refresh was rejected before implementation because its invariant was visible directly:
E = R14 + R15 + sum(witness[i]) (mod 2^32)
R14' = R14 + a
R15' = R15 - a - b
w[q]' = w[q] + b
E' = EThe choice of a, b, or witness index does not matter to that cancellation. Moving the refresh into different edge byte sequences changes representation, not the projected computation. This was a design rejection, not a new successful black-box attack timing.
The word candidate failed without that particular XOR trick
A later width-closed word candidate tested whether better ownership, live fragmentation, physical section permutation, and unwind support changed the extraction boundary. The target was an emitted DLL, not a codec object accidentally exposing its internal graph.
An artifact-only extractor followed one concrete A execution in Unicorn while shadowing instructions with fixed-width bit-vector expressions. Equivalent expressions were hash-consed; after the trace, slicing backward from the return value removed unreachable graph nodes. The shadow values were compared with emulator state at instruction boundaries.
The execution contained 124,101 instructions. The full graph had 67,557 nodes, and the return-value slice retained 34,242 nodes, serialized into 760,226 bytes. The recovered representation used constants, four input variables, and ordinary truncate/extend/extract/rotate/Boolean/arithmetic nodes. Prediction did not depend on PE bytes, addresses, an emulator, or the original A/B artifacts.
The crucial observations were zero input-tainted conditional branches and zero input-tainted memory addresses in the analyzed trace. The emitted arithmetic behaved like a fixed scalar circuit along that execution. Layout diversity did not force the extractor to solve a different semantic graph. Sixty-four fresh A checks and 2,048 native execute-only B checks matched the frozen standalone model.
The original 3,072 native comparisons, 77 unwind ranges, and 12 live-fragment faults remained useful engineering evidence. They did not earn anti-lifting credit. This candidate was also marked HARDENING_REJECTED.
The concrete production fix was to stop publishing known failures
The admission review found a policy mismatch: reports and counterexample tests said “rejected,” while normal APIs or the PE compiler could still emit the same families. That is a separate implementation bug with a concrete remedy.
The masked generator now rejects before consuming randomness. The historical emitter is reachable only through an explicitly rejected research constructor with a distinct result type. The planner rejects an eligible masked run before random selection, and the protection engine treats the hardening failure as fatal even when strict is false. A failed request must leave no PE, manifest, or map sidecar behind.
Equivalent gates cover the documented coded-multiplication and exact-Boolean/direct-predicate failures. The new word candidate stays compiler-private with no production selector. These gates reduce the admitted feature set until a replacement earns evidence; they do not make the rejected algorithm secure.
| A green test in this project | What it actually means |
|---|---|
| Behavior or unwind regression | The controlled implementation contract holds |
| Counterexample reproduction | The known attack still succeeds; the family remains rejected |
| Production-admission regression | The rejected family cannot publish an artifact |
Gate-only tests explicitly record when a historical attack was not rerun. Otherwise, renaming an old attack test into a new admission test can make the test dashboard tell a much stronger story than the run supports.
What the next design has to demonstrate
The unresolved task is to remove the cheap complete projection and common semantic handoff, not just rename masks or increase the number of rounds. The selected research direction asks for predecessor/successor-specific encoded transitions spanning at least two real operations, with representation state participating in the actual arithmetic and continuation rather than only in a guard.
That direction is not an accepted production replacement. A single concrete trace should not immediately define the entire reusable predictor, and the cost of combining multiple traces must be measured rather than assumed. The analyst still owns the local program. If the product needs an authority or a secret the analyst cannot reproduce, the trust boundary has to change; a local encoding alone cannot supply an external authority.
The practical lesson from these iterations is that an AI-assisted analyst does not have to recover the designer’s intended representation. It only needs a cheaper sufficient one. For Aegis, the useful progress was reproducing that fact, making the evidence harder to overstate, removing known failures from ordinary output paths, and turning the missing architectural property into an explicit acceptance condition.
Evidence note: this log draws on retained masked-XOR, common-boundary, word-candidate, transition-design, and production-admission reports. It distinguishes source-informed tooling, independent-agent observations, and gate-only tests. No historical attack, external model run, or protector regression was rerun for this article revision.
Related development logs
Building an honest AI reverse-engineering evaluation harness · Kernel64 compatibility and unwind validation · PE rebuilding and header growth