Ghidra / IDA Pro 对 WebAssembly 二进制的逆向分析：如何识别 Wasm 函数、数据段，并将其转换为伪代码？

admin
Wed, 23 Jul 2025 10:51:04 +0000
Javascript, ghidra, ida, pro

各位同学，晚上好！今天咱们来聊聊 WebAssembly 的逆向分析，目标是用 Ghidra 或者 IDA Pro 这两个大杀器，把 Wasm 里的函数、数据段扒个精光，再把它们变成我们看得懂的伪代码。准备好了吗？Let’s rock! 第一部分：Wasm 基础回顾——温故而知新 在深入逆向分析之前，咱们先简单回顾一下 WebAssembly 的基本结构，省得一会儿对着一堆 0 和 1 抓瞎。 模块（Module）： Wasm 的基本单元，相当于一个库或者一个程序。一个模块包含多个部分，比如类型定义、函数、表、内存、全局变量、导出和导入。 类型定义（Types）： 定义了函数的签名，包括参数类型和返回值类型。 函数（Functions）： 实际的代码逻辑。每个函数都有一个类型索引，指向类型定义中的函数签名。 表（Tables）： 存储函数指针，用于实现函数调用。 内存（Memory）： 线性内存，Wasm 程序可以读写这块内存。 全局变量（Globals）： 存储全局数据。 导出（Exports）： 声明哪些函数、内存等可以被外部访问。 导入（Imports）： 声明需要从 …

继续阅读“Ghidra / IDA Pro 对 WebAssembly 二进制的逆向分析：如何识别 Wasm 函数、数据段，并将其转换为伪代码？”

The post Ghidra / IDA Pro 对 WebAssembly 二进制的逆向分析：如何识别 Wasm 函数、数据段，并将其转换为伪代码？ first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

各位同学，晚上好！今天咱们来聊聊 WebAssembly 的逆向分析，目标是用 Ghidra 或者 IDA Pro 这两个大杀器，把 Wasm 里的函数、数据段扒个精光，再把它们变成我们看得懂的伪代码。准备好了吗？Let’s rock!

第一部分：Wasm 基础回顾——温故而知新

在深入逆向分析之前，咱们先简单回顾一下 WebAssembly 的基本结构，省得一会儿对着一堆 0 和 1 抓瞎。

1. 模块（Module）： Wasm 的基本单元，相当于一个库或者一个程序。一个模块包含多个部分，比如类型定义、函数、表、内存、全局变量、导出和导入。

2. 类型定义（Types）： 定义了函数的签名，包括参数类型和返回值类型。

3. 函数（Functions）： 实际的代码逻辑。每个函数都有一个类型索引，指向类型定义中的函数签名。

4. 表（Tables）： 存储函数指针，用于实现函数调用。

5. 内存（Memory）： 线性内存，Wasm 程序可以读写这块内存。

6. 全局变量（Globals）： 存储全局数据。

7. 导出（Exports）： 声明哪些函数、内存等可以被外部访问。

8. 导入（Imports）： 声明需要从外部导入的函数、内存等。

记住这些概念，对我们理解 Wasm 的结构至关重要。

第二部分：Ghidra 与 IDA Pro 的配置——工欲善其事

想用 Ghidra 或者 IDA Pro 分析 Wasm，首先得确保它们支持 Wasm。一般来说，最新版本的 Ghidra 和 IDA Pro 都自带了 Wasm 模块。如果不行，可能需要安装对应的插件。

Ghidra： Ghidra 默认支持 Wasm，无需额外配置。

IDA Pro： IDA Pro 需要安装 Wasm 插件。通常情况下，安装目录下的 plugins 文件夹里会有，或者你可以从网上下载。

第三部分：识别 Wasm 函数——大海捞针，也要捞出来

现在，我们开始实战。首先，我们要识别 Wasm 文件中的函数。

1. Ghidra:

导入 Wasm 文件： 打开 Ghidra，创建一个新的项目，然后把 Wasm 文件拖进去。Ghidra 会自动识别文件类型。

自动分析： Ghidra 会提示你是否要进行自动分析，选择 "Yes"。在分析选项中，确保选择了 "WebAssembly Analyzer"。

函数列表： 分析完成后，在 "Symbol Tree" 窗口中，展开 "Functions" 节点，就能看到 Ghidra 识别出来的所有函数了。

2. IDA Pro:

导入 Wasm 文件： 打开 IDA Pro，选择 "File" -> "Open"，打开 Wasm 文件。

自动分析： IDA Pro 也会自动分析文件。

函数窗口： 在 IDA Pro 中，按 "Shift+F2" 可以打开 "Functions window"，里面列出了所有识别出来的函数。

代码示例：

咱们用一个简单的 Wasm 例子来演示。假设我们有这样一个 Wasm 文件（add.wasm）：

(module
  (func $add (param $p1 i32) (param $p2 i32) (result i32)
    local.get $p1
    local.get $p2
    i32.add)
  (export "add" (func $add)))

这个 Wasm 文件定义了一个 add 函数，接收两个 i32 类型的参数，返回它们的和。

在 Ghidra 或者 IDA Pro 中打开这个文件，你会看到类似这样的函数列表：

工具 | 函数名 | 地址
Ghidra | FUN_00010000 | 00010000
IDA Pro | sub_10000 | 00010000

注意：Ghidra 和 IDA Pro 可能会给函数分配默认的名字，比如 FUN_00010000 或者 sub_10000。我们需要根据上下文来重命名函数，让它们更有意义。

第四部分：识别 Wasm 数据段——数据才是王道

Wasm 的数据段存储了程序使用的数据，比如字符串、数组等。识别数据段对于理解程序的行为至关重要。

1. Ghidra:

数据类型管理器： 在 Ghidra 的 "Data Type Manager" 窗口中，可以查看 Ghidra 识别出来的数据类型。

内存映射： 在 "Listing" 窗口中，可以看到 Wasm 的内存映射。数据段通常会被标记为 "data" 或者 "initialized data"。

手动定义数据： 如果 Ghidra 没有正确识别数据类型，可以手动定义数据类型。选中一段内存，右键选择 "Data" -> "Define"，然后选择合适的数据类型。

2. IDA Pro:

Segments 窗口： 在 IDA Pro 中，按 "Shift+F7" 可以打开 "Segments window"，里面列出了 Wasm 文件的所有段。

Strings 窗口： 按 "Shift+F12" 可以打开 "Strings window"，里面列出了 Wasm 文件中的所有字符串。

手动定义数据： 类似于 Ghidra，IDA Pro 也允许手动定义数据类型。选中一段内存，按 "D" 键，然后选择合适的数据类型。

代码示例：

假设我们的 Wasm 文件包含一个字符串：

(module
  (memory (import "env" "memory") 1)
  (data (i32.const 0) "Hello, WebAssembly!")
  (func $get_string (result i32)
    i32.const 0)
  (export "get_string" (func $get_string)))

这个 Wasm 文件在内存的 0 地址处存储了字符串 "Hello, WebAssembly!"。

在 Ghidra 或者 IDA Pro 中打开这个文件，你会发现：

Ghidra 会在内存映射中显示 "Hello, WebAssembly!" 字符串，并将其标记为 "string"。

IDA Pro 会在 "Strings window" 中显示 "Hello, WebAssembly!" 字符串。

第五部分：将 Wasm 转换为伪代码——拨开迷雾见真相

识别了函数和数据段之后，下一步就是把 Wasm 代码转换为伪代码，让我们能够更容易地理解程序的逻辑。

1. Ghidra:

反汇编视图： 在 Ghidra 的 "Listing" 窗口中，可以看到 Wasm 代码的反汇编形式。

Decompile： Ghidra 提供了强大的反编译功能。选中一个函数，右键选择 "Decompile"，Ghidra 会尝试将 Wasm 代码转换为 C 语言风格的伪代码。

代码注释： 在反汇编视图中，可以添加代码注释，帮助理解代码的含义。

2. IDA Pro:

反汇编视图： 在 IDA Pro 中，可以看到 Wasm 代码的反汇编形式。

Decompiler： IDA Pro 也提供了反编译功能。选中一个函数，按 "F5" 键，IDA Pro 会尝试将 Wasm 代码转换为 C 语言风格的伪代码。

代码注释： 类似于 Ghidra，IDA Pro 也允许添加代码注释。

代码示例：

回到我们的 add.wasm 例子：

(module
  (func $add (param $p1 i32) (param $p2 i32) (result i32)
    local.get $p1
    local.get $p2
    i32.add)
  (export "add" (func $add)))

在 Ghidra 中，反编译 add 函数，你可能会看到这样的伪代码：

int add(int p1, int p2) {
  return p1 + p2;
}

在 IDA Pro 中，反编译 sub_10000 函数，你可能会看到类似的伪代码。

第六部分：实战案例——从简单到复杂

理论讲了一堆，现在咱们来个稍微复杂点的例子，看看如何应用这些技巧。

假设我们有一个 Wasm 文件（factorial.wasm），实现了计算阶乘的功能：

(module
  (func $factorial (param $n i32) (result i32)
    (local $result i32)
    (local.set $result (i32.const 1))
    (block $B0
      (loop $L1
        (br_if $B0
          (i32.eqz
            (local.get $n)
          )
        )
        (local.set $result
          (i32.mul
            (local.get $result)
            (local.get $n)
          )
        )
        (local.set $n
          (i32.sub
            (local.get $n)
            (i32.const 1)
          )
        )
        (br $L1)
      )
    )
    (local.get $result)
  )
  (export "factorial" (func $factorial)))

这个 Wasm 文件定义了一个 factorial 函数，接收一个 i32 类型的参数，返回它的阶乘。

1. 识别函数：

在 Ghidra 或者 IDA Pro 中打开 factorial.wasm，你会看到一个名为 factorial 或者 sub_10000 的函数。

2. 反编译：

反编译 factorial 函数，你可能会看到这样的伪代码：

int factorial(int n) {
  int result = 1;
  while (n != 0) {
    result = result * n;
    n = n - 1;
  }
  return result;
}

3. 分析伪代码：

从伪代码中，我们可以很容易地看出 factorial 函数的逻辑：它使用一个循环来计算阶乘。

第七部分：高级技巧——更上一层楼

除了基本的操作之外，还有一些高级技巧可以帮助我们更好地进行 Wasm 逆向分析。

动态分析： 使用调试器（比如 lldb 或者 gdb）来动态分析 Wasm 代码。可以设置断点、查看变量的值，等等。

符号执行： 使用符号执行工具（比如 Triton 或者 Manticore）来自动分析 Wasm 代码，寻找漏洞。

模糊测试： 使用模糊测试工具（比如 AFL 或者 Honggfuzz）来生成随机的 Wasm 输入，测试程序的健壮性。

第八部分：常见问题与解决方案——避坑指南

在 Wasm 逆向分析过程中，可能会遇到一些常见问题。

混淆： 一些 Wasm 文件可能会被混淆，增加逆向分析的难度。可以使用反混淆工具来去除混淆。

代码优化： Wasm 代码可能会被优化，导致伪代码难以理解。可以尝试关闭优化选项，或者手动分析汇编代码。

符号信息缺失： Wasm 文件可能没有符号信息，导致 Ghidra 和 IDA Pro 无法识别函数名。可以尝试手动添加符号信息。

第九部分：总结——天下没有难逆的 Wasm

今天我们学习了如何使用 Ghidra 和 IDA Pro 进行 Wasm 逆向分析。我们了解了 Wasm 的基本结构，学习了如何识别函数和数据段，以及如何将 Wasm 代码转换为伪代码。

记住，逆向分析是一个不断学习和实践的过程。多做练习，多查资料，你就能成为 Wasm 逆向分析的高手！

好了，今天的讲座就到这里。希望大家有所收获！下次再见！