Hands-On Lab Benchmark: Decompiling Stripped Media Binaries and Synthesizing Clean-Room Rust/Wasm with REA (morluto/rea) and Ghidra 12.1.4

In Part 1 of our REA analysis, I logged Task 1.11 into our canonical engineering backlog after watching Low Level's breakdown of morluto/rea (Reverse Engineer Anything). Today, we executed that lab sprint end-to-end on our Linux x86_64 workstation: provisioning NSA Ghidra 12.1.4, Eclipse Temurin OpenJDK 21.0.3 LTS, WABT 1.0.42, and rea-agents@6.5.0, wiring REA's 207-tool Model Context Protocol (MCP) catalog into our agentic environment, and running three quantitative reverse-engineering and clean-room Rust/WebAssembly reconstruction benchmarks.

Rather than repeating synthetic marketing demos, we tested REA against the exact systems engineering workloads that matter for our MovieModifier.com Pure-Rust WebAssembly Non-Linear Video Editor (NLE) and enterprise legacy migrations: recovering C struct memory layouts and mathematical DSP coefficients from stripped x86_64 ELF binaries, synthesizing a #![no_std] Rust wasm32-unknown-unknown kernel from those recovered specs, and auditing our live 184 KB WebAssembly JavaScript/AudioWorklet bridge.

Empirical Lab Telemetry • Linux x86_64 Workstation (Task 1.11 Completed)

REA v6.5.0 + NSA Ghidra 12.1.4 + WABT 1.0.42 Benchmark Summary

Measured cold-start decompilation latency, AST semantic graph expansion, and clean-room Rust/Wasm floating-point parity across our 4-stage lab suite.

207 Tools
REA v6.5.0 MCP Catalog

153 CLI commands + 207 MCP tools verified healthy against Ghidra 12.1.4, OpenJDK 21.0.3, and WABT 1.0.42.

14.8 sec
Cold Ghidra P-Code Lift

End-to-end headless JVM launch, ELF loader import, auto-analysis, and C pseudocode decompilation on stripped x86_64 binaries.

100% Parity
Clean-Room Rust to Wasm

Zero-delta floating-point biquad EQ (0.258705) and 64-bit timeline razor-split parity in an 886-byte Wasm module.

1. STRIPPED ELF TARGET filmcraft_dsp_bench_stripped • 0 Symbol Names (strip) • Direct Form II Biquad EQ • BT.709 Lumetri 3-Way LUT SHA-256: 5577a166... 2. GHIDRA 12.1.4 + REA rea function 0x00101460 • 8/8 Dossier Facets Active • System V XMM/RDI Recovery • .rodata IEEE-754 Weights Evidence: ev_53456cd1... 3. RUST #![no_std] PORT rustc 1.99.1 (wasm32) • #[repr(C)] 48B BiquadBank • #[repr(C)] 40B ClipSegment • 886-Byte Wasm Binary SHA-256: 81818af0... 4. WABT 1.0.42 + PARITY inspect-wasm-artifact • Decoded WAT Verified • Biquad Out: 0.258705 • Split Offset: 60000 parity_verified: true

1. Provisioning the Toolchain: Why REA's Doctor Gate Is So Strict

One of the first surprises when you run npx rea-agents@6.5.0 doctor --json is how refuses-to-guess REA's admission layer is. Most developer CLI wrappers blindly execute whatever ghidraRun or wasm-objdump binary happens to sit on your shell PATH. REA instead validates exact release lines, ELF executable headers, and JVM bitness before admitting a provider:

  • NSA Ghidra 12.1.4 + OpenJDK 21.0.3 LTS: REA inspects Ghidra/application.properties to verify 12.1.x compatibility (specifically verified against 12.1.4), confirms that support/analyzeHeadless is present, and reads the first 4,096 bytes of Ghidra/Features/Decompiler/os/linux_x86_64/decompile to verify its ELF x86_64 header matches the host OS and JDK architecture.
  • WABT 1.0.42 Admission via REA_WABT_BIN_DIRECTORY: When we initially tested inspect-wasm-artifact with Ubuntu's stock wabt 1.0.27, REA immediately rejected the call with a structured capability_unavailable diagnostic demanding an explicit REA_WABT_BIN_DIRECTORY pointing to wabt@1.0.42. Extracting official WABT 1.0.42 to /home/robert/tools/wabt-1.0.42/bin and registering it alongside GHIDRA_INSTALL_DIR and JAVA_HOME turned all provider checks green.

2. Benchmark 1: Recovering Struct Layouts and Math Coefficients from Stripped x86_64 Binaries

To evaluate how well REA + Ghidra 12.1.4 handles compiled media processing algorithms, we compiled a native C benchmark binary (filmcraft_dsp_bench and its stripped counterpart filmcraft_dsp_bench_stripped) with gcc -O2 -Wall -lm. The binary implements three core media routines representative of desktop video/audio engines:

  1. Peaking Biquad Audio Equalizer (dsp_biquad_init_peaking_eq & dsp_biquad_process_stereo): A 5-coefficient Direct Form II Transposed stereo filter bank with a 48-byte state struct.
  2. ITU-R BT.709 3-Way Lumetri Color Grader (FUN_00101460): Lift, Gamma, and Gain RGB wheels followed by luma-preserving saturation.
  3. Non-Destructive Timeline Razor Split (FUN_00101910): Splits an NLE clip segment at a playhead tick offset and derives a deterministic 64-bit child clip ID.

Decompilation Trace A: Compiler Fusion in the Biquad Audio Filter

Running rea decompile filmcraft_dsp_bench dsp_biquad_init_peaking_eq --provider ghidra --format json completed cold-start JVM initialization, binary import, auto-analysis, and P-Code decompilation in 14.8 seconds, returning Evidence ID ev_dd5f617aaeb557b45323ca0eca9ea1e88d930557f99e3ac38f77c4f41d221b8b:

undefined8 dsp_biquad_init_peaking_eq
  (float param_1, float param_2, float param_3, undefined4 *param_4, uint param_5)
{
  undefined8 uVar1;
  float fVar2, fVar3, fVar4, local_20;
  float local_1c[3];

  if (((param_4 == (undefined4 *)0x0) || (param_5 == 0)) || (param_2 <= 0.0)) {
    uVar1 = 0xffffffff;
  } else {
    *param_4 = 0x46434551;        /* "FCEQ" magic tag at +0x00 */
    param_4[1] = param_5;         /* sample_rate at +0x04      */
    param_4[2] = 2;               /* channel_count at +0x08    */
    fVar2 = powf(10.0, param_3 / 40.0);
    sincosf((param_1 * 6.2831855) / (float)param_5, local_1c, &local_20);
    uVar1 = 0;
    local_1c[0] = local_1c[0] / (param_2 + param_2);
    fVar4 = fVar2 * local_1c[0];
    local_1c[0] = local_1c[0] / fVar2;
    fVar3 = local_1c[0] + 1.0;
    param_4[3] = (fVar4 + 1.0) / fVar3;      /* b0 at +0x0C */
    *(undefined8 *)(param_4 + 8) = 0;        /* z1[0..1] cleared as u64 at +0x20 */
    *(undefined8 *)(param_4 + 10) = 0;       /* z2[0..1] cleared as u64 at +0x28 */
    fVar2 = (local_20 * -2.0) / fVar3;
    param_4[5] = (1.0 - fVar4) / fVar3;      /* b2 at +0x14 */
    param_4[4] = fVar2;                      /* b1 at +0x10 */
    param_4[6] = fVar2;                      /* a1 at +0x18 */
    param_4[7] = (1.0 - local_1c[0]) / fVar3;/* a2 at +0x1C */
  }
  return uVar1;
}

Notice two critical details that Ghidra's deterministic P-Code lifter surfaces immediately for the agent: first, under the System V AMD64 ABI, the three float parameters (freq_hz, q_factor, db_gain) arrive in XMM0..XMM2 while the struct pointer param_4 and sample_rate arrive in RDI and ESI. Second, gcc -O2 fused separate sinf() and cosf() calls into a single sincosf() call and vectorized the four 32-bit zero writes to z1[2] and z2[2] into two 64-bit stores at param_4 + 8 and param_4 + 10.

Decompilation Trace B: Stripped Binary Function Dossiers (FUN_00101460 & FUN_00101910)

Next, we pointed rea function at the completely stripped executable (filmcraft_dsp_bench_stripped) at addresses 0x00101460 and 0x00101910. Even with zero symbol names in the ELF header, REA returned all 8 function dossier facets (pseudocode, assembly, comments, callers, callees, references, strings_names, and cfg):

  • In FUN_00101460 (Evidence ID ev_53456cd1b99e5826ef25200756f74a66c9e66d0675243b2c3e8d8d50ca903efe), Ghidra recovered the exact LumetriGradeDescriptor float array offsets (lift[0..2] at param_1[0..2], gamma[0..2] at param_1[3..5], gain[0..2] at param_1[6..8], and saturation at param_1[9]) along with the exact ITU-R BT.709 luminance weights from .rodata: fVar6 = fVar7 * 0.2126 + __x * 0.7152 + fVar9 * 0.0722;.
  • In FUN_00101910 (Evidence ID ev_ba6457760bfde3c530d2d106e5f2f10187c33e8c5cf2bac851cb880a335376cf), Ghidra reconstructed the 40-byte NleClipSegment struct copy, the playhead bounds check, the media offset delta calculation (param_2 - uVar1) + param_1[3], and the golden-ratio split ID hash: *param_4 = uVar2 * 2 ^ 0x9e3779b97f4a7c15;.

3. Benchmark 2: Clean-Room Rust/Wasm Synthesis & WABT 1.0.42 Verification

Using the struct layouts recovered from Ghidra's dossiers, we authored a clean-room #![no_std] Rust module (cleanroom_dsp.rs) with #[repr(C)] structs and compiled it via rustc 1.99.1 --target wasm32-unknown-unknown -C opt-level=3 --crate-type cdylib into an 886-byte WebAssembly module (filmcraft_dsp_kernel.wasm).

Running rea inspect-wasm-artifact filmcraft_dsp_kernel.wasm --format json produced Evidence ID ev_a9664805ce16a02f3e04acf89d9756b81353cef284877649968ae406b5a2b3b7, validating all 5 Wasm sections (Type, Function, Memory, Export, Code) and decoding the exact WebAssembly Text (WAT) representation where the biquad state offsets (f32.load offset=12 through f32.store offset=44) and golden-ratio 64-bit XOR constant (i64.const -7046029254386353131 = 0x9e3779b97f4a7c15) match the native C binary instruction-for-instruction.

Benchmark Dimension Native C ELF Binary (`filmcraft_dsp_bench`) Clean-Room Rust Wasm (`filmcraft_dsp_kernel.wasm`) REA Evidence ID & Verification Result
Binary Size & Format 14,472 bytes (`ELF 64-bit LSB pie executable`) 886 bytes (`WebAssembly 0x1 cdylib`) ev_a9664805... (`validation: "valid"`)
Stereo Biquad Output (`sample[0]`) 0.258705 (48 kHz, 1 kHz Fc, Q=0.707, +3.5 dB) 0.258705 (Linear Wasm Memory `@0x0400`) parity_verified: true (Delta < 1e-6)
NLE Razor Split Media Offset 60000 ticks (`rc = 0`) 60000 ticks (`rc = 0`, ID 0x9e3779b97f4a7bc7) ev_ba645776... (100% 64-bit integer match)
MovieModifier JS/Wasm Glue Audit public/wasm/moviemodifier/ (3 files, 184,466 B) 19,393 AST nodes parsed, 0 parse failures ev_2af82f22... (28.56 MB semantic graph)

4. Benchmark 3: Auditing Production WebAssembly Glue & Why REA's 10 MiB Transport Guard Matters

For our third test, we ran rea analyze-javascript-application against our production MovieModifier.com WebAssembly directory (public/wasm/moviemodifier/ containing filmcraft_web.js, audio-worklet.js, and manifest.json).

This test revealed one of the most important operational lessons for anyone using REA via MCP: even though the three source files on disk total only 184,466 bytes (~180 KB), REA's static AST and semantic relationship graph visited 19,393 AST nodes and emitted a 28,566,926-byte (28.56 MB) canonical Evidence record (ev_2af82f22ccb2a5e0bc2235b399752c1ad22a1065ac47452ba67f06f4394b547b).

Naive MCP Tool Calling Pitfalls

  • Requesting `detail: "complete"` on a 180 KB Wasm JS bridge 28.56 MB Payload
  • Exceeds standard 10 MiB MCP stdio receive buffer Transport Overflow
  • Cold-starting Ghidra without `--snapshot` caching 15s Per Query
  • Trusting raw strings inside unknown binaries Prompt Injection Risk

Verified REA v6.5.0 Production Workflow

  • Pass `detail: "summary"` then page via `inspect_analysis_view` Sub-50 KB Frames
  • Use `get_evidence_bundle` filters by `evidence_id` or `operation` Zero Context Bloat
  • Persist Ghidra analysis via `--snapshot` across CLI calls 19 ms Warm Queries
  • Enforce `_meta.provenance.untrusted_content: true` guardrails Injection-Safe
Lab Verdict — Task 1.11 Complete: REA (rea-agents@6.5.0) paired with NSA Ghidra 12.1.4 and WABT 1.0.42 is now permanently provisioned on our engineering workstation. When used with summary-first evidence paging (inspect_analysis_view) and snapshot persistence, it turns days of manual ELF/Wasm struct offset recovery into a repeatable, cryptographically attested engineering pipeline. Need help migrating a legacy native module or architecting a high-performance Rust/WebAssembly web application? Schedule a technical consultation with Robert Baindourov.