Standing on the Shoulders of Giants: De-Obfuscating WebAssembly Using LLVM
Black Hat Asia 2025 · Day 2 · Briefings
Overview
In an increasingly web-centric world, WebAssembly (Wasm) has emerged as a critical technology, promising near-native performance for web applications. Its adoption by major platforms and industries, from Google Earth to blockchain, underscores its growing importance. However, this proliferation also raises significant security questions, particularly concerning the protection of intellectual property (IP) and the analysis of malicious code. This talk, presented by Vikas Gupta and Peter Gjølver from Talis, delves into the complex realm of WebAssembly obfuscation and, more importantly, its systematic de-obfuscation.

Key moments
- 0:00 Introduction: De-obfuscating WebAssembly using LLVM
- 1:20 Core questions about WebAssembly security and de-obfuscation
- 2:15 What is WebAssembly and its wide adoption
- 3:20 WebAssembly's structured design and security features
- 5:00 Understanding code obfuscation and its legitimate uses
- 5:50 Applying LLVM-based obfuscation to WebAssembly
- 6:50 Evolution of LLVM obfuscation tools: OLVM, Hikari, Polaris
- 7:40 Visual example of how obfuscation transforms code
Standing on the Shoulders of Giants: De-Obfuscating WebAssembly Using LLVM
Speakers: Vikas Gupta, Senior Security Researcher, Talis; Peter Gjølver, Principal Software Security Engineer, Talis
Conference: Black Hat Asia
YouTube: https://www.youtube.com/watch?v=Z-udrjM7Z78
Overview
In an increasingly web-centric world, WebAssembly (Wasm) has emerged as a critical technology, promising near-native performance for web applications. Its adoption by major platforms and industries, from Google Earth to blockchain, underscores its growing importance. However, this proliferation also raises significant security questions, particularly concerning the protection of intellectual property (IP) and the analysis of malicious code. This talk, presented by Vikas Gupta and Peter Gjølver from Talis, delves into the complex realm of WebAssembly obfuscation and, more importantly, its systematic de-obfuscation.
The speakers present a novel and highly effective methodology for reversing sophisticated WebAssembly obfuscation techniques by leveraging the power of the LLVM compiler infrastructure. Their approach addresses the limitations of existing tools and introduces Squanchy, an orchestration framework that combines LLVM's world-class optimizations with specialized de-obfuscation tools. This work is crucial for security researchers, incident responders, and developers alike, as it demonstrates that even heavily obfuscated WebAssembly code can be effectively analyzed, stripped of its protective layers, and restored to a more comprehensible form.
The significance of this research lies in its practical implications for enhancing WebAssembly security. By providing a robust framework for de-obfuscation, the speakers empower defenders to better understand and analyze WebAssembly binaries, whether for identifying IP theft, dissecting malware, or verifying the integrity of their own applications. Their methodology challenges the notion that obfuscation offers impregnable protection, highlighting the enduring effectiveness of compiler-based analysis in the face of evolving threats.
Background
▶ Watch: Introduction: De-obfuscating WebAssembly using LLVM (0:00)
WebAssembly, launched in 2017 after its announcement in 2015, addresses the demand for native-level performance within web applications. Conceptually, Wasm functions like a target architecture (e.g., ARM or x86) for which source code (from languages like C, C++, Go, or Rust) is compiled. Instead of executing on physical hardware, Wasm binaries run within a stack-based virtual machine embedded in web browsers. Its structured nature, with modules divided into indexed sections (exports, imports, functions, memory), and the absence of indirect jumps (in its un-obfuscated form), contribute to its initial security posture by limiting memory access and providing clear control flow.
Initial tooling for WebAssembly analysis includes projects like wasm-toolkit and wasm-tools (specifically wasm2c and wasm-mutate). For GUI-based decompilation, Ghidra, with its Wasm plugin, offers reasonable support, while commercial tools like IDA Pro and JetBrains products are noted as "hit and miss." The speakers primarily use IDA Pro for analyzing native code generated during their de-obfuscation process.
Obfuscation is the deliberate process of making code harder to read or understand while preserving its original functionality. While often associated with malware to hinder analysis, obfuscation also serves legitimate purposes, such as protecting intellectual property in applications delivered to end-users or securing Digital Rights Management (DRM) mechanisms. Obfuscation can be applied at various stages of the software development lifecycle:
- Source code level: Tools like Tiger.
- Intermediate Representation (IR) level: Specifically, LLVM IR, using tools like OLVM, Hikari, and Polaris. This approach is language-agnostic, working across C, C++, Go, and Rust.
- WebAssembly binary level: Tools like
wasm-mutate(a diversifier that performs pseudo-obfuscation).
LLVM-based obfuscation, pioneered by OLVM, leverages compiler passes to introduce transformations such as instruction substitution, control flow flattening, and the use of opaque predicates or mixed boolean-arithmetic (MBA) expressions. Examples shown in the talk vividly illustrate how simple expressions and control flow graphs can be expanded into massively complex, unreadable structures after multiple passes of obfuscation, leading to significant code size increases (e.g., from simple code to a 12 MB binary) and often breaking existing decompilation tools like Ghidra.
De-obfuscation, conversely, aims to reverse these transformations, simplifying the code to make it readable again. Historically, de-obfuscation focused on pattern-based techniques to identify and revert known assembly-level obfuscation patterns. However, with the rise of compiler-based obfuscation, a more generic, compiler-level approach became necessary. Peter Gjølver, a co-speaker, co-authored the 2019 "Saton" paper, which introduced an LLVM-based de-obfuscation framework for native binaries. This framework lifts binaries to LLVM IR, applies optimizations and custom passes, "brightens" the code for readability, and then re-emits it.
Initial attempts to apply this philosophy directly to WebAssembly using existing tools proved challenging. binarian, which lifts Wasm to a custom IR, failed to de-obfuscate even simple examples, and writing custom passes for its IR was undesirable. The wamr-compiler, while capable of emitting LLVM IR, critically lost essential runtime information—such as globals, tables, and debug symbols—making effective de-obfuscation impossible. This highlighted a key challenge: preserving Wasm's runtime context during the lifting process.
Key Findings
▶ Watch: What is WebAssembly and its wide adoption (2:15)
The central finding of this research is that sophisticated WebAssembly obfuscation can be systematically reversed by strategically combining existing, powerful compiler technologies with specialized de-obfuscation techniques. The speakers identified and addressed the critical gap in lifting WebAssembly to a usable Intermediate Representation (IR) while preserving its unique runtime characteristics.
Their solution hinges on using wasm2c as the primary lifter. wasm2c converts WebAssembly binaries into C source code, which can then be compiled into LLVM IR using clang. This intermediate C step is crucial because wasm2c generates a well-defined runtime environment, including helpers for memory initialization, globals, function tables, and load/store operations. Critically, it encapsulates all this runtime information within a W2C_instance parameter passed to all functions, and importantly, preserves the original control flow graph during the initial lift.
The advantage of using LLVM IR is its target-independent nature and the availability of world-class optimization and analysis passes. LLVM's extensive optimization pipeline, developed by top engineers, can effectively simplify complex, obfuscated code. Its rich IR and accessible API allow for precise modifications to the code and control flow graph. Moreover, LLVM's ability to normalize code after optimization is beneficial for pattern matching and signature generation, and its multiple backends allow recompilation to various targets, including native code or back to WebAssembly.
To orchestrate this complex de-obfuscation pipeline, the speakers introduced Squanchy. Squanchy is a novel tool designed to automate the various de-obfuscation steps. Its core functions include:
- Interacting with third-party tools like
Simba++andSuper. - Modeling and injecting the WebAssembly runtime into functions.
- Inlining all necessary dependencies.
- Applying LLVM optimizations with a customized pipeline that initially preserves the control flow graph.
- Cleaning up
wasm2cruntime remnants, resulting in a clean, de-obfuscated function.
The research further identified specific tools to tackle different categories of obfuscation:
- LLVM's native optimization passes (e.g., -O3) are highly effective against instruction substitution and general code simplification. The speakers emphasize the importance of adjusting LLVM's internal thresholds (e.g., for loop unrolling, dead store elimination) to allow the optimizer to spend more time on heavily obfuscated code, leading to more thorough de-obfuscation.
- Simba++ is employed to de-obfuscate Mixed Boolean-Arithmetic (MBA) expressions. Simba++ identifies MBA patterns, simplifies them using algorithms like Zimba and Gamba, and then uses SMT solvers to prove the correctness of the simplification before replacing the MBA in the LLVM IR.
- Super, a super-optimizer, proved to be an ideal solution for reversing Control Flow Flattening (CFF) and resolving opaque predicates. Super uses advanced algorithms, including synthesis and data flow analysis, to uncover optimizations that standard compilers might miss, effectively restoring the original control flow.
Collectively, these findings demonstrate a comprehensive and adaptable framework for WebAssembly de-obfuscation, capable of tackling various sophisticated obfuscation techniques and yielding significantly simplified, human-readable code.
Technical Deep Dive
▶ Watch: Understanding code obfuscation and its legitimate uses (5:00)
The technical foundation of this de-obfuscation approach lies in its multi-stage lifting and optimization pipeline, orchestrated by Squanchy.
1. Lifting WebAssembly to LLVM IR:
The process begins by converting the WebAssembly binary into a more malleable intermediate form. The speakers chose wasm2c as their lifter, which transforms the .wasm binary into C source code. This C code is then compiled into LLVM IR using clang. A critical decision here is to compile with -O0 (no optimization) initially. This is to preserve type information, which LLVM's more aggressive optimizations might discard prematurely through "opaque pointers." Retaining type information at this early stage aids subsequent folding and simplification.
The wasm2c output is particularly valuable because it generates a complete, well-defined WebAssembly runtime environment. This includes:
- Helper functions for initializing the Wasm memory, globals, and function tables.
- Helpers for load and store operations, abstracting direct memory access.
- A crucial
W2C_instanceparameter, passed to every function, which encapsulates all runtime information (memory structures, function tables, globals). This parameter and its initialization are vital for the de-obfuscation process, as they provide the necessary context for LLVM's optimizers.
2. Squanchy's Orchestration and Runtime Injection:
Squanchy's primary role is to manage the entire de-obfuscation workflow. One of its key functions is to make the runtime information, encapsulated in the W2C_instance, directly accessible to the optimizer within the target function. This is achieved through a sequence of steps:
- Local Instance Allocation: Squanchy first allocates a local
W2C_instancewithin the target function's scope. All existing references to theW2C_instanceparameter (the first argument to the function) are then replaced with references to this new local instance. - Runtime Initializer Call: A new call is inserted to the
wasm2cruntime initializer function (generated bywasm2c), passing the locally allocatedW2C_instanceas an argument. - Recursive Inlining: Squanchy then marks the runtime initializer and all
wasm2chelper functions asalways_inline. It performs a recursive inlining process, starting with the dependencies of the runtime initializer and continuing until all necessary helper functions and their dependencies are inlined directly into the target function. This step is critical because it exposes the full context of memory accesses, global variable manipulations, and table lookups to the LLVM optimizer, allowing it to perform data flow analysis and constant propagation across these runtime operations.
3. LLVM Optimization Pipeline:
Once the runtime context is fully inlined, Squanchy applies a customized LLVM optimization pipeline. While standard -O3 optimizations are used, the initial stages are configured to preserve the control flow graph (CFG). This is a strategic choice, as obfuscation pipelines often prioritize control flow protection (e.g., flattening) first, followed by instruction-level transformations. By preserving the CFG initially, Squanchy allows subsequent specialized tools to address CFF specifically, while LLVM handles other simplifications. The speakers also highlight the importance of adjusting LLVM's internal optimization thresholds (e.g., loop-unroll-threshold, dead-store-elimination-threshold). By increasing these thresholds, LLVM is prompted to spend more time on deeper analysis and more aggressive optimizations, which is essential for tackling highly obfuscated code that might hide simplification opportunities across large distances within the function.
4. Specialized De-obfuscation Tools:
- Mixed Boolean-Arithmetic (MBA) De-obfuscation with Simba++:
MBAs combine arithmetic and boolean operations in complex ways, making them difficult to simplify with standard algebraic rules. Simba++ is integrated into the pipeline to address this. It identifies MBA expressions within the LLVM IR, then uses specialized algorithms (like Zimba and Gamba) to simplify them. To ensure correctness, Simba++ employs SMT solvers to mathematically prove that the simplified expression is equivalent to the original MBA. Once proven, the original MBA is replaced with its simpler form in the LLVM IR.
- Control Flow Flattening (CFF) De-obfuscation with Super:
CFF transforms a function's natural control flow into a single, large loop with a dispatcher that jumps to basic blocks based on a state variable, often protected by opaque predicates. Super, a super-optimizer, is used to reverse CFF. Super employs advanced techniques such as program synthesis and data flow analysis to discover optimizations that even sophisticated compilers might miss. For CFF, it can effectively identify the original basic blocks and their true relationships, reconstructing the original, unflattened control flow graph, even in the presence of opaque predicates.
5. Brightening Step:
After all optimizations and specialized de-obfuscation steps are complete, Squanchy performs a "brightening" phase. This involves cleaning up any remaining artifacts from the wasm2c lifting and the runtime injection process. The local W2C_instance is replaced with the original parameter, and any leftover initialization code from wasm2c that is no longer needed after inlining and optimization is removed. The goal is to produce a clean, de-obfuscated function that is as close as possible to the original, pre-obfuscation code, making it highly readable for subsequent manual analysis.
Finally, the de-obfuscated LLVM IR can be compiled back to a native object (e.g., ARM64, x86) for analysis with tools like IDA Pro/Ghidra, or back into a WebAssembly module for execution.
Demo / Proof of Concept
▶ Watch: Applying LLVM-based obfuscation to WebAssembly (5:50)
The talk featured compelling demonstrations that validated the effectiveness of their de-obfuscation approach against various forms of WebAssembly obfuscation.
1. De-obfuscating wasm-mutate Diversified Malware:
The first demo showcased Squanchy's ability to completely reverse the effects of wasm-mutate, a tool often used to diversify WebAssembly malware and evade signature-based detection.
- Obfuscation: A simple WebAssembly example, initially comprising 200 instructions, was subjected to 3,000 "real" iterations of
wasm-mutate. The term "real iterations" acknowledges that some passes inwasm-mutatemight be broken, requiring a more robust application. - Resulting Obfuscation: The obfuscated binary produced a massive function, approximately 60,000 KB in size, which was then loaded into IDA Pro. The sheer complexity and size of the control flow graph made it immediately apparent that manual analysis would be impractical.
- De-obfuscation: Squanchy was then run on this heavily obfuscated sample. The speakers highlighted the impressive speed of the LLVM optimization process, which quickly processed the large function.
- Outcome: The de-obfuscated function was dramatically reduced from 200 instructions to just 20-23 instructions, successfully recovering the original, simple expression. This demonstrated a 100% removal of the
wasm-mutateobfuscation. The "normalized" output, as the speakers noted, is critical for signature-based detection, as it allows for consistent signatures even if the malware is diversified.
2. Real-World Malware Analysis: Kryptonite Bitcoin Miner:
To prove the real-world applicability of their method, the speakers targeted Kryptonite, a known WebAssembly-based Bitcoin miner found in the wild. This malware typically loads Wasm code in a web page to mine cryptocurrency on the visitor's PC.
- Challenge: The Kryptonite samples they encountered were also obfuscated.
- Application: Squanchy's pipeline was applied to selected functions from the obfuscated Kryptonite miner.
- Outcome: The de-obfuscated functions matched the original, un-obfuscated code 100%. In one instance, the de-obfuscated code initially appeared to have "more code" than the original. However, deeper analysis revealed that the obfuscated sample had inlined some helper functions into the target function. When compared against the original code with the inlined portions considered, the match was perfect. This confirmed that Squanchy could handle sophisticated real-world obfuscation, including function inlining, and restore the original logic.
3. Commercial Product Analysis: Edge Capture:
The team also applied their de-obfuscation technique to obfuscated WebAssembly code found in Edge Capture, a commercial product.
- Challenge: The Edge Capture Wasm modules utilized control flow flattening (CFF) obfuscation.
- Application: Squanchy was used to de-obfuscate small to medium-sized functions from Edge Capture. The demonstration showed a highly flattened control flow graph on the left and the resulting de-obfuscated, more readable code on the right.
- Outcome: While the de-obfuscated code might appear "slightly bigger" due to necessary inlining of runtime components, the key achievement was that upon decompilation, the code became "very much readable." This demonstrated the capability to reverse complex CFF in commercial software, providing clarity for analysis.
These demos collectively underscore the robustness and versatility of the Squanchy framework, proving its efficacy against both synthetic, highly iterative obfuscation and complex, real-world scenarios in malware and commercial applications.
Defensive Implications
▶ Watch: Visual example of how obfuscation transforms code (7:40)
The detailed de-obfuscation methodology presented in this talk carries significant implications for defenders operating in the WebAssembly ecosystem. It fundamentally challenges the notion that obfuscation provides robust security for WebAssembly applications.
First and foremost, this work demonstrates that WebAssembly obfuscation, even highly sophisticated forms like control flow flattening, mixed boolean-arithmetic expressions, and extensive instruction substitution, is not a foolproof security measure. Defenders armed with the right tools and techniques can effectively reverse these protective layers. This realization should prompt a re-evaluation of security strategies that overly rely on obfuscation for IP protection or to hinder reverse engineering.
For malware analysis and threat intelligence, the ability to de-obfuscate WebAssembly is transformative. Malware authors frequently use obfuscation to evade detection and analysis. By normalizing obfuscated WebAssembly code back to its original form, security researchers can:
- Develop more robust and reliable signatures: Traditional signatures are often brittle and break with even minor obfuscation changes. Squanchy's ability to normalize code (as demonstrated with
wasm-mutate) means that signatures can be built on the de-obfuscated, canonical representation of the malware, making them far more resilient to diversification. - Accelerate incident response: Understanding the true functionality of WebAssembly malware becomes much faster, enabling quicker identification of malicious intent, command-and-control mechanisms, and attack vectors.
- Improve behavioral analysis: With a clearer view of the code, dynamic analysis can be more targeted, and the observed behaviors can be more accurately mapped back to specific functions.
For intellectual property (IP) protection, organizations deploying WebAssembly applications must understand that obfuscation offers only a speed bump, not a fortress. While it may deter casual reverse engineers, a determined adversary with access to tools like Squanchy can likely recover the underlying logic. This suggests that critical IP should be protected through other means, such as server-side logic, secure enclaves, or careful architectural design, rather than relying solely on client-side obfuscation.
The speakers also emphasize a crucial practical point: existing, battle-hardened LLVM-based tooling is largely sufficient for WebAssembly de-obfuscation. There is often no need to develop entirely new, WebAssembly-specific de-obfuscation tools from scratch. Instead, by intelligently integrating and extending mature compiler infrastructure like LLVM, along with specialized optimizers like Simba++ and Super, defenders can achieve powerful results. This reduces development overhead and leverages decades of compiler engineering expertise, offering a more stable and less buggy foundation for security analysis.
In summary, the defensive implication is clear: WebAssembly applications, whether benign or malicious, are now more transparent to sophisticated analysis. Defenders should integrate de-obfuscation capabilities into their security toolchains, adjust their IP protection strategies, and leverage existing compiler technologies to stay ahead in the evolving landscape of web-based threats.
Key Takeaways
- WebAssembly obfuscation, even advanced forms, is reversible: Techniques like instruction substitution, control flow flattening, and mixed boolean-arithmetic expressions can be systematically stripped away, restoring code to a readable state.
- LLVM is a powerful foundation for Wasm de-obfuscation: By lifting WebAssembly to LLVM IR via
wasm2c, crucial runtime context (memory, globals, tables) is preserved, enabling LLVM's world-class optimizers to perform extensive simplification. - Squanchy orchestrates a comprehensive de-obfuscation pipeline: This novel tool integrates LLVM's native optimizations with specialized tools like Simba++ (for MBAs) and Super (for CFF and opaque predicates) to tackle diverse obfuscation types effectively.
- De-obfuscation normalizes code for robust analysis: The process yields a canonical representation of the code, which is invaluable for generating reliable signatures for malware detection and facilitating in-depth static analysis, even against diversified samples.
- Existing, mature tooling is often sufficient: The talk advocates for leveraging battle-hardened LLVM-based tools rather than developing new, often less robust, WebAssembly-specific de-obfuscation solutions.
- Obfuscation is not a primary security control: For WebAssembly, relying solely on obfuscation for IP protection or malware evasion is insufficient, as demonstrated by the efficacy of modern de-obfuscation techniques.
About the Speaker(s)
Vikas Gupta is a Senior Security Researcher at Talis, bringing a wealth of experience in reverse engineering and mobile security. Prior to his current role, Vikas worked as an Android Security Engineer at Google, where he honed his expertise in securing mobile platforms. His professional interests are deeply rooted in understanding complex software systems through reverse engineering and enhancing mobile application security.
Peter Gjølver is a Principal Software Security Engineer at Talis, based in Singapore. He serves as a Product Owner for the mobile security group, where he is responsible for building robust security components and developing Talis's internal obfuscation tools. Peter describes himself as a passionate reverse engineer, a pursuit he now primarily engages in during his personal time, reflecting his deep commitment to understanding software internals and security vulnerabilities.
Reviews
Dr. Zero (Offensive Security Researcher) — MUST SEE
This talk presents a robust and highly effective methodology for de-obfuscating WebAssembly binaries by leveraging the LLVM compiler infrastructure. The speakers introduce Squanchy, an orchestration framework that cleverly lifts Wasm to C via wasm2c, then to LLVM IR, preserving critical runtime context. By integrating LLVM's powerful optimizations with specialized tools like Simba++ for Mixed Boolean-Arithmetic and Super for Control Flow Flattening, they demonstrate a systematic reversal of complex obfuscation. The work normalizes code for practical analysis in malware and commercial applications, providing significant actionable insights for defenders and fundamentally challenging the…
Heather Calloway (CISO) — STRONG ACCEPT
This research delivers a critical message for anyone overseeing application security and intellectual property: WebAssembly obfuscation, even when highly sophisticated, is not a primary security control. The speakers present a robust, LLVM-based de-obfuscation framework, Squanchy, which effectively reverses complex obfuscation techniques found in malware and commercial applications. This work fundamentally changes how security leaders should approach client-side IP protection and enhances the capabilities of malware analysts, demonstrating that relying on obfuscation alone is a strategic misstep.