DEV Community

TrustSig
TrustSig

Posted on

Engineering TrustSig Lab: Building a High-Performance WebAssembly Reverse Engineering Workbench

Try it for yourself

No signup, no paywall, free for everyone, forever.
Try it out here

Background

WebAssembly has fundamentally shifted how we execute high-performance code on the web. By providing a low-level, assembly-like language with a compact binary format, it allows complex applications to run at near-native speeds in the browser.

However, this execution model introduces distinct visibility challenges. When high-level logic is compiled down to a stack-machine instruction set, interpreting the behavior of that code becomes a highly specialized task. Security researchers and engineers attempting to audit these binaries often find themselves relying on heavy desktop tools or rudimentary text translation utilities.

We recognized a need for an environment that could perform complex control flow analysis and decompilation directly in the browser, without requiring software installation or compromising on analytical depth. This requirement drove the development of TrustSig Lab, our free, online WebAssembly reverse engineering tool.

The architecture bridges a highly optimized Rust analysis core with a concurrent React frontend. The following sections detail the engineering decisions, parsing strategies, and rendering techniques that make the platform function.

The Rust Analysis Engine

At the center of TrustSig Lab is a decoupled analysis engine written entirely in Rust and compiled to WebAssembly. The primary constraint we faced was managing memory efficiently. Large binaries can easily crash a browser tab if loaded entirely into memory as an Abstract Syntax Tree.

To prevent this, the core utilizes the wasmparser library, which is maintained by the Bytecode Alliance. This library allows us to perform streaming analysis of the binary. The engine reads the binary in two distinct passes rather than attempting a single, monolithic translation.

The first pass acts as a high-level survey. The parser extracts structural metadata from the binary. It categorizes type signatures, import and export sections, data segments, and any custom name sections. This structural map is stored in a lightweight module information object.

The second pass performs a deep scan of the actual instruction bodies. The engine tracks call operators and maps their targets, building reverse and forward lookups for every function. During this scan, it also monitors constant value declarations and matches their offsets against the data segments. This creates a reliable map of where specific strings and data structures are referenced throughout the code.

Memory Footprint — Streaming
Parser avoids full binary DOM loading.

Control Flow Reconstruction and Lifting

WebAssembly is a stack-based machine. Instructions push values onto a stack and pop them off to perform operations. While efficient for execution, this is incredibly difficult for humans to read. Translating this stack-based flow into readable, C-like expressions is a process known as lifting.

The lifter in our engine maintains a virtual stack of strings during analysis. When an instruction like addition is encountered, the lifter pops the top two simulated expressions off its virtual stack, wraps them in an arithmetic operation, and pushes the combined expression back.

Local variables and global assignments trigger an evaluation of the current stack state, generating discrete variable assignments in the decompiled output. Complex nested function calls are handled gracefully through recursive stack management.

Resolving indirect calls requires precise execution context. The engine references the type signatures extracted during the initial survey phase to ensure the correct number of parameters are popped from the virtual stack when evaluating table-based function dispatches. You can review the underlying type specification in the official WebAssembly Core Specification.

Constructing the control flow graph requires identifying the leaders, or starting instructions, of basic blocks. The engine flags function entry points and instructions that immediately follow control flow operations like branches, loops, and returns. The output is a highly structured directed graph that maps exact byte offsets to their corresponding basic blocks.

Frontend Architecture and Concurrency

The user interface is built using React and Next.js, prioritizing performance and responsiveness. The workspace is orchestrated using a flexible layout model, allowing users to arrange decompilers, hex viewers, and string tables into a custom multi-window dashboard.

Executing intense graphing algorithms and memory simulations on the main browser thread would cause severe interface lockups. We implemented strict thread isolation using the Web Workers API.

A singleton worker client manages all communication with a background thread. Heavy analysis tasks are dispatched to this worker, which lazily initializes the Rust-compiled WebAssembly module only when the first task is received. Communication occurs over a strict, ID-based message protocol, ensuring the user interface remains entirely fluid while the engine churns through millions of instructions in the background.

Thread Isolation — 100%
Heavy analysis offloaded to Web Workers.

Rendering the results requires equal care. A complex WebAssembly module might contain hundreds of thousands of lines of disassembled text. Writing this directly to the Document Object Model is impossible.

We employ virtualization techniques for all heavy text rendering. The interface calculates the exact pixel height of the visible scrolling container and only mounts the specific lines of text currently in view. As the user scrolls, these DOM nodes are recycled and repopulated with new instruction text.

For the control flow graphs, we utilize React Flow paired with a directed graph layout engine. Basic blocks are rendered as custom nodes containing their own scrollable, syntax-highlighted instruction lists.

Interactive Analysis and Tooling

A static display of code is insufficient for active reverse engineering. TrustSig Lab implements several interactive layers over the analyzed code.

The decompilation view uses an interactive tokenization system. Specific variables, function calls, and global references are wrapped in distinct identifiers. This allows users to hover over variables to see their context or select functions to navigate instantly to their definitions.

State synchronization ensures that renaming a variable or adding a function comment in one window immediately propagates across the entire dashboard. Selecting a function in the central table will automatically scroll the disassembly view, load the decompiler logic, and generate the control flow graph for that specific context simultaneously.

We also maintain a headless Node.js version of the analysis engine. This command-line interface shares exact feature parity with the Rust core. It allows engineers to perform automated surveys of WebAssembly binaries in continuous integration pipelines, peek into raw memory structures, and resolve complex indirect call tables programmatically.

Conclusion

Building TrustSig Lab required solving complex problems in memory management, concurrency, and graphical rendering. By combining a streaming Rust parser with a strictly decoupled React frontend, we created an environment capable of performing heavy reverse engineering tasks directly within the browser.

This technical transparency aligns with our broader engineering philosophy at TrustSig. Delivering invisible, GDPR-compliant edge security requires a deep understanding of how execution environments operate at their lowest levels. Whether we are analyzing suspicious WebAssembly payloads or optimizing our own zero-latency verification systems, treating the browser as a high-performance computing platform remains essential.

No signup, no paywall, free for everyone, forever.
Try it out here

Top comments (0)