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

A technical deep dive into the architecture of TrustSig Lab, our browser-native WebAssembly analysis tool. We explore the Rust-based analysis engine, stack-to-expression lifting, and concurrent frontend architecture.

Try it for yourself

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

Wasm is fast to run and hard to read

WebAssembly buys near-native speed in a browser tab and a compact binary format, and the price is that the result is almost unreadable afterwards. High-level logic compiles down to a stack-machine instruction set: values get pushed, popped and combined by operations that say nothing about what the original code was for. Reading one back is specialist work.
So Wasm tooling splits in two. Either you load the binary into a heavy desktop disassembler, or you paste it into something that prints the text format and stops there. We wanted control flow analysis and decompilation that run in the browser, with nothing to install and no analytical depth traded away to get there. That is TrustSig Lab, our free online WebAssembly reverse engineering tool, and it sits alongside our other open utilities like the fraud search tool.

The engine reads every binary twice

The analysis core is Rust, compiled to WebAssembly, decoupled from everything that draws. Memory is the constraint that shaped it. Build an Abstract Syntax Tree for a large binary and the tab dies before you see a single function.
So the core sits on wasmparser, the streaming parser maintained by the Bytecode Alliance, and reads each binary in two passes rather than one monolithic translation.
The first pass is a survey. It pulls structural metadata out of the binary and categorizes type signatures, import and export sections, data segments and any custom name sections into a lightweight module information object.
The second pass goes into the instruction bodies. The engine tracks call operators and maps their targets, building forward and reverse lookups for every function. It watches constant value declarations as it goes and matches their offsets against the data segments, which gives a reliable map of where particular strings and data structures get referenced.
Memory Footprint
Streaming
Parser avoids full binary DOM loading

Lifting a stack machine into expressions

A stack machine is fine for an engine and miserable for a person. Translating that flow into readable, C-like expressions is known as lifting.
The lifter in our engine keeps a virtual stack of strings during analysis. On an addition it pops the top two simulated expressions, wraps them in the arithmetic operation, and pushes the combined expression back. Local variables and global assignments force an evaluation of the current stack state, which is where the discrete variable assignments in the decompiled output come from. Nested calls fall out of the same recursive stack management.
Indirect calls need more context than that. Table-based dispatch only pops the right number of parameters if you already know the signature, so the lifter goes back to the type signatures the survey pass collected. The WebAssembly Core Specification carries the underlying type rules.
The control flow graph comes from finding leaders, the instructions that start a basic block. The engine flags function entry points and anything that immediately follows a branch, loop or return. What comes out is a structured directed graph with exact byte offsets mapped to their blocks.

Nothing heavy touches the main thread

The interface is React and Next.js. The workspace is a flexible layout model, so decompilers, hex viewers and string tables get arranged into whatever multi-window dashboard the binary in front of you needs.
Graphing algorithms and memory simulations would lock that interface solid if they ran on the main browser thread, so none of them do. We use strict thread isolation over the Web Workers API. A singleton worker client owns all communication with the background thread and dispatches the heavy tasks to it. The worker initializes the Rust-compiled WebAssembly module lazily, on the first task it receives, and everything after that goes over a strict ID-based message protocol. The interface stays fluid while the engine churns through millions of instructions behind it.
Thread Isolation
100%
Heavy analysis offloaded to Web Workers
Rendering has its own ceiling. A complex module can disassemble to hundreds of thousands of lines of text, which is not something you can write to the Document Object Model.
Every heavy text view is virtualized instead. The interface measures the exact pixel height of the visible scrolling container, mounts only the lines currently in view, then recycles those DOM nodes with new instruction text as the user scrolls.
Control flow graphs use React Flow paired with a directed graph layout engine, where each basic block is a custom node carrying its own scrollable, syntax-highlighted instruction list.

Renaming a variable updates every window

Most of the work in a real session is annotation and navigation, so the analyzed code carries several interactive layers.
The decompilation view tokenizes as it renders. Variables, function calls and global references each get a distinct identifier, which is what lets you hover a variable for its context or select a function to jump straight to its definition.
State is synchronized across the dashboard. Renaming a variable or adding a function comment in one window propagates the change to all of them. Select a function in the central table and the disassembly view scrolls to it, the decompiler logic loads, and the control flow graph for that context is generated, all at once.
There is also a headless Node.js build of the analysis engine. The command-line interface shares exact feature parity with the Rust core, so engineers can run automated surveys of WebAssembly binaries in continuous integration pipelines, peek into raw memory structures and resolve complex indirect call tables programmatically.

Why a security company ships a decompiler

Treating the browser as a high-performance computing platform is the same habit the rest of our work runs on. Delivering invisible, GDPR-compliant edge security means knowing how execution environments behave at their lowest levels, whether we are analyzing a suspicious WebAssembly payload, tracking reported CAPTCHA vulnerabilities or optimizing our own zero-latency verification systems.
No signup, no paywall, free for everyone, forever. Try it out here