rea-reverse

WASM 模块分析与 WASM 转 WAT

打开 .wasm 文件,查看段(section)、导入、导出、工具链、特性和体积最大的函数;需要看代码时,再用 wabt 转成可读的 WAT 文本。

文件不会离开你的浏览器. 解析和反汇编都在一个禁止联网的 Web Worker 中本地完成,不上传任何内容。 如何自己验证

在单独页面打开工具(无广告、无统计)

WebAssembly 模块是一个紧凑的二进制文件,但它并不神秘。它的结构按编号的 section 排列:用到哪些类型、从宿主环境导入哪些函数、导出哪些函数和内存、有哪些数据段,往往还有自定义段,写着它是哪个编译器生成的、各个函数叫什么名字。拿到一个陌生的 .wasm 文件,想知道的大部分信息都能从这些结构里读出来,一条指令都不用执行。

这个工具就在你的浏览器里读取这些结构。选择一个 .wasm 文件,马上就能看到结构化的分析报告;如果需要完整的文本格式,点一下就能把模块转成 WAT。文件不会被上传。

使用步骤

  1. 拿到 .wasm 文件:来自你的构建产物、npm 包,或者 DevTools 的 Network 面板(按 Wasm 筛选后保存响应)。
  2. 把它拖到上方的拖放区,或点“选择文件”。报告马上出来;需要文本格式时再点“生成 WAT”。
  3. 先看导出(JavaScript 一侧调用的函数),再看导入、各段大小、工具链依据和函数体最大的函数。

真实输出示例

我们打开 xxhash-wasm npm 包里的 workerd/xxhash.wasm,点“生成 WAT”,再把页面显示的结果复制下来。排版做了压缩(标签和值放在同一行,表格重新对齐,省去空列和按钮),所有值都原样保留。

样本: xxhash-wasm 1.1.0 npm 包(MIT)中的 xxhash.wasm(3,105 字节,SHA-256 70c5a91a…3c2a),2026-11-02 获取 (www.npmjs.com/package/xxhash-wasm/v/1.1.0)

模块概要
文件大小      3.0 KB
版本          1
类型          8
函数          导入 0 个,定义 10 个
代码          10 个函数体,2.9 KB
有名字的函数  0
内存          min 1
数据段        0 段,0 B
识别出的工具链  无
producers 段    无

段(Section) (5)
ID  名称      偏移  大小
1   type      0xa   48 B
3   function  0x3c  11 B
5   memory    0x49  3 B
7   export    0x4e  85 B
10  code      0xa6  2.9 KB

导出 (9)
名称      类别    索引  类型
mem       memory  0
xxh32     func    0     (i32, i32, i32) -> (i32)
init32    func    2     (i32, i32) -> ()
…
xxh64     func    5     (i32, i32, i64) -> (i64)
…
digest64  func    9     (i32) -> (i64)

函数体最大的函数
索引  大小
8     628 B
3     579 B
5     511 B
…

WAT 文本
(module
  (type $t0 (func (param i32 i32 i32) (result i32)))
  …
  (func $xxh32 (type $t0) (param $p0 i32) (param $p1 i32) (param $p2 i32) (result i32)
    (local $l3 i32) (local $l4 i32) (local $l5 i32) (local $l6 i32)
    local.get $p0
    local.get $p1
    i32.add
…

能做什么

输入。 一个 .wasm 文件,分析报告最大支持 256 MiB,WAT 转换最大支持 32 MiB。只支持 WebAssembly 核心模块(二进制版本 1),不支持 Component Model 组件。

分析报告。 页面自带一个专门编写的轻量二进制解析器来读取模块结构。它不执行模块,也不解码函数体。报告包括:

  • 模块概要:文件大小、二进制版本,以及类型、导入函数、定义函数、表、内存、全局变量、tag、start 函数、元素段、数据段的数量。
  • section 表:每个 section 的 id、名称、字节偏移和大小,包括 name、producers、target_features 这类自定义段。
  • 导入表和导出表:模块需要宿主提供什么、它向宿主提供什么。光看导入的模块名往往就能判断运行环境,例如 wasi_snapshot_preview1 或 env。
  • 工具链识别:在 Rust、wasm-bindgen、Emscripten、Clang、Go、TinyGo、AssemblyScript、WASI、.NET、Zig 之中给出推测,并附上每条推测所依据的证据(某个导入名、某种导出模式、某条 producers 记录)。
  • producers 和 target_features:存在时列出这两个自定义段的内容。
  • post-MVP 特性:能从模块结构上看出来的后续特性,例如多内存、用于异常处理的 tag、特定形式的导入和类型。
  • 最大的函数:按字节大小排名前 20 的函数体;模块带有 name section 时同时显示函数名。
  • 警告:解析器无法解码的 section 会作为警告列出,而不是悄悄跳过。

WAT 转换。 点击 生成 WAT 按钮,把模块转换成 WebAssembly 文本格式。只有点击时,页面才会加载 wabt.js 1.0.39(gzip 后约 218 KB,wabt 自身的 wasm 以 base64 形式内嵌在脚本里)。结果显示在一个 <pre> 块里,可以复制,也可以下载为 .wat 文件。wabt 支持的特性全部启用,包括 SIMD、threads、GC、memory64、exceptions 等,并读取 name section 生成可读的名字。输出超过 500 万字符时会截断并给出提示,更大的输出请用下载按钮或下文的命令行工具。

原理

WebAssembly 二进制格式以魔数 \0asm 和 4 字节版本号开头,后面是一个个 section。每个 section 有 1 字节的 id 和一个 LEB128 编码的长度,所以解析器不需要理解内容,也能从一个 section 跳到下一个。页面的解析器正是这样建立 section 表,然后解码描述模块“形状”的那些 section:类型、导入、函数、表、内存、全局、导出、start、元素、data count、tag,以及自定义段 name、producers 和 target_features。对于代码段,它只读取每个函数体的大小,并据此给最大的函数排名,从不解释指令。

工具链识别是作用在这些数据上的一组规则。每种工具链都会留下可辨认的指纹:wasm-bindgen 会添加特征性的导入和导出,Emscripten 从 env 导入辅助函数,Go 的运行时从 gojs 导入,WASI 模块从 wasi_snapshot_preview1 导入,很多编译器还会在 producers 段里写上自己的名字。报告会列出命中的证据,方便你自己判断每条推测是否可信。

WAT 转换用的是 wabt 的读取器和文本输出器,和命令行工具 wasm2wat 是同一套代码。

隐私是怎么保证的

文件不会被上传:

  • 分析器运行在一个单独的框架(iframe)里,来源是 tools.rea-reverse.com;分析在框架里的 Web Worker 中、在你的设备上进行。Worker 的内容安全策略(CSP)是 connect-src 'none',浏览器根本不允许它发出任何网络请求。
  • 页面加载完成后,分析报告在断网状态下也能使用。
  • WAT 转换所需的 wabt 脚本,只有在你点击 生成 WAT 时才会从 tools.rea-reverse.com 下载。如果你打开页面后断了网,又是第一次点击这个按钮,转换就会失败。这是有意的取舍:大多数访客只需要分析报告,不应该为了一个用不到的功能下载约 800 KB 的工具代码。
  • 这个页面在正文下方展示一个广告。广告脚本运行在 rea-reverse.com 上,而框架属于另一个源(origin),浏览器不允许广告脚本或本页的任何其他脚本读取你选择的文件或分析结果。
  • 框架的内容安全策略拦截它的脚本能发起的所有请求(connect-src 'none',也不加载任何第三方资源)。框架不能自行打开窗口或让本页跳转;只有你点击报告里的链接时才会打开页面。它传给本页的只有自身高度和下面这些统计事件。
  • 统计事件只有 wasm_analyze 和 wasm_wat,只包含成功或错误码、文件大小区间和耗时区间,绝不包含文件名或任何文件内容。框架把事件交给本页,由本页发送。完整清单见隐私政策。
  • 不想要任何广告和统计?在单独页面打开分析器。

如何自己验证

  1. 打开浏览器开发者工具的 Network(网络) 面板,加载本页。发往 tools.rea-reverse.com 的请求就是框架:它的 HTML、样式、字体和脚本,以及 Worker 脚本 /_astro/wasm-worker-*.js。发往 Google 域名的请求是本页的广告。
  2. 清空日志,然后选择文件做一次分析,愿意的话再生成一次 WAT。在过滤框里输入 domain:tools.rea-reverse.com:新出现的请求只有为下一个文件预先启动的新 Worker,以及点击 生成 WAT 后下载的 wabt 脚本 /_astro/wasm-worker-chunk-*.js,都来自 tools.rea-reverse.com。它们都不带你的文件。广告请求可能还会继续,但它们来自本页,不带框架里的任何内容。
  3. 点开来自 tools.rea-reverse.com 的 wasm-to-wat/ 文档,查看 Headers:它的 Content-Security-Policy 里有 connect-src 'none' 和 frame-ancestors https://rea-reverse.com。在 Chrome 里,Application(应用)> Frames 也能看到框架的源和策略。Worker 运行在 default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'none' 这条策略下。
  4. 检查隔离:在 Console(控制台) 里,上下文选 top,运行 document.getElementById('tool-frame').contentDocument,结果是 null;frames[0].document 会抛出 SecurityError。广告脚本拿到的也是这个结果。
  5. 或者先打开页面再断网,然后做分析,照样能用;只有第一次 生成 WAT 需要联网。

局限

  • 不反编译成高级语言。 你得到的是结构信息和 WAT,不是 C、Rust 或 JavaScript 源码。
  • WAT 很长。 即使是小模块也会产生成千上万行,而且文本格式几乎就是指令级别的。先用分析报告决定从哪里看起。
  • 不做验证。 wabt 只负责读取模块、输出文本,不运行验证器(validator),所以无效的模块也可能转换成功。转换成功不代表模块合法。
  • 不支持 Component Model。 组件(较新的分层格式)不受支持,只支持版本 1 的核心模块。
  • 剥离过的模块没有名字。 压缩或混淆过的模块通常没有 name section,函数只能以编号显示。
  • 看不到运行时行为。 模块从不执行,因此运行时才决定的东西,例如实际调用了哪些导入、加载了什么数据,都看不到。
  • 工具链识别是启发式的。 它依赖指纹,可能漏判或误判,经过后处理的模块尤其如此。

合规说明

请只用本工具分析你自己构建的模块、你获得授权分析的模块,或者公开分发的模块(例如检查你访问的网页加载了什么)。请遵守所分析软件的许可协议和适用于你的法律法规,包括其中限制逆向工程的条款。本工具不提供任何绕过授权、DRM 或其他保护措施的建议。

对应的命令行

用 wabt 1.0.39 做同样的检查,这里通过 wabt npm 包里的 wasm-objdump 和 wasm2wat 运行(原生安装的 wabt 参数相同)。段头,与“段”表一致:

$ wasm-objdump -h xxhash.wasm
xxhash.wasm:	file format wasm 0x1

Sections:

     Type start=0x0000000a end=0x0000003a (size=0x00000030) count: 8
 Function start=0x0000003c end=0x00000047 (size=0x0000000b) count: 10
   Memory start=0x00000049 end=0x0000004c (size=0x00000003) count: 1
   Export start=0x0000004e end=0x000000a3 (size=0x00000055) count: 9
     Code start=0x000000a6 end=0x00000c21 (size=0x00000b7b) count: 10
$ wasm-objdump -x -j Export xxhash.wasm
…
Export[9]:
 - memory[0] -> "mem"
 - func[0] <xxh32> -> "xxh32"
 - func[2] <init32> -> "init32"
 - func[3] <update32> -> "update32"
 - func[4] <digest32> -> "digest32"
 - func[5] <xxh64> -> "xxh64"
 - func[7] <init64> -> "init64"
 - func[8] <update64> -> "update64"
 - func[9] <digest64> -> "digest64"

页面生成的 WAT 与 wasm2wat --generate-names 一致;不加这个参数时,wabt 会用编号((;0;))代替名字:

$ wasm2wat --generate-names xxhash.wasm -o xxhash.wat
$ head -11 xxhash.wat
(module
  (type $t0 (func (param i32 i32 i32) (result i32)))
  (type $t1 (func (param i32 i32 i32)))
  …
  (type $t7 (func (param i32) (result i64)))
  (func $xxh32 (type $t0) (param $p0 i32) (param $p1 i32) (param $p2 i32) (result i32)
    (local $l3 i32) (local $l4 i32) (local $l5 i32) (local $l6 i32)

常见问题

怎样在线把 WASM 转成 WAT,又不上传文件?

把 .wasm 拖到本页,点“生成 WAT”。模块在你的浏览器里读取;唯一额外的请求是第一次点按钮时从本站加载的 wabt.js 脚本,文件本身不会发到任何地方。

这是 WASM 反编译器吗?

不是。WAT 是同一批指令的文本形式,不是还原出来的 C、Rust 或 JavaScript。报告帮你判断先看哪里;想要伪代码,可以在本地运行 wabt 自带的 wasm-decompile。

为什么看不到函数名?

函数名来自 name 自定义段,发布构建经常把它去掉。WAT 里导出的函数仍会按导出名命名,比如上面的 $xxh32;其余函数只能按编号显示。

为什么断网时“生成 WAT”失败了?

wabt 脚本只在你第一次点按钮时才下载。如果在那之前页面就断了网,脚本无法加载。重新联网再试即可;分析报告本身不依赖它。

能打开 Component Model 的 .wasm 吗?

不能。只支持二进制版本 1 的核心模块。组件的文件头中 layer 字段不同,会被拒绝。

WAT 输出提示被截断了,怎么拿到完整文本?

用“下载 .wat”按钮,或者在本地运行 wasm2wat module.wasm -o module.wat。页面上的显示在 500 万字符处截止,是为了不让浏览器卡死。

更新于

相关教程