WASM

前言

大家在做一些涉及wasm 的js逆向,尝试使用nodejs本地调用wasm执行一些加解密操作时,可能会遇到以下错误:

RuntimeError: unreachable (syscall/js.Value).Get (syscall/js.Value).Int

或者:

LinkError: function import requires a callable

根本原因是在于 WASM 代码是按“浏览器环境”编译的。

而 Node.js:

没有 window 没有 document 没有 navigator 没有 screen 没有 location

当 Go wasm 执行:

js.Global().Get("navigator").Get("userAgent")

在浏览器里:

window.navigator.userAgent → string

在 Node 里没有这些对象、属性,然后导致代码崩溃。


一、目标

如何在 Node.js 中本地执行 wasm,并精确记录 wasm 访问了哪些浏览器环境对象?

思路:

  1. wasm_exec.js 跑 wasm

  2. Proxy 伪造浏览器环境

  3. 记录所有属性访问路径

  4. 用类型白名单避免 Go wasm 崩溃


二、wasm_exec.js

wasm_exec.js 是 Go编译器在生成WebAssembly目标代码时自动产生的JavaScript引导文件, 它负责在浏览器环境中加载和执行Go编译的WebAssembly模块,它是 Go wasm 的运行桥接层。

关键点:

  • 实现 syscall/js.*

  • 所有 Go 调用 JS 的行为都走这里

  • 核心函数:

"syscall/js.valueGet"
"syscall/js.valueSet"
"syscall/js.valueCall"

所以:

我们只要控制 JS 一侧的 global/window,就能掌握 wasm 行为。


三、proxy.js 核心机制

1️⃣ watch 是整个系统核心

function watch(obj, name)

作用:

给任意对象包一层递归 Proxy,记录所有 get / set。


2️⃣ 核心:get 拦截

get(t, p, r)

每一次 wasm 访问:

window.navigator.userAgent

都会打印:

[GET] window.navigator
[GET] window.navigator.userAgent

3️⃣ 递归代理机制

if (v0 && typeof v0 === "object")
    return makeProxy(v0, key);

保证:

  • 支持无限链式访问

  • 每一层都能被追踪


4️⃣ cache 的意义(非常重要)

const cache = new Map();

作用:

同一路径返回同一个对象

否则 Go wasm 会炸。

这是保持引用稳定性的关键。


四、对象属性类型白名单

这是整个系统能稳定运行的核心。


为什么需要类型白名单?

Go wasm 在 JS 侧做强类型判断:

  • Int

  • String

  • Bool

如果:

window.innerWidth

返回 {}

Go 会崩。


1️⃣ NUM_PROPS

const NUM_PROPS = new Set([
  "width", "height",
  "innerWidth", "innerHeight",
  "devicePixelRatio",
  "length"
]);

遇到这些属性:

t[p] = 0;

返回 number。

避免 Go int 转换 panic。


2️⃣ STR_PROPS

"userAgent"
"platform"
"cookie"
"href"

统一返回:

""

保证 Go 的 stringVal 不出错。


3️⃣ BOOL_PROPS

"webdriver"
"cookieEnabled"

统一返回:

false

防止 Go 布尔解析崩溃。


六、如何扩展这三类属性

实际逆向中:

第一次运行一定会崩。

崩溃日志里会出现:

panic: interface conversion

或者:

value is not int

这时:

第一步

看最后访问的路径日志:

[GET] window.screen.availWidth

第二步

判断类型:

  • 如果是尺寸 → NUM_PROPS

  • 如果是字符串 → STR_PROPS

  • 如果是布尔 → BOOL_PROPS

第三步

加入对应 Set

例如:

NUM_PROPS.add("availWidth");

再次运行。

不断补齐。


实战建议

按浏览器对象结构补齐:

常见数值

screen.*
window.*
performance.*

常见字符串

navigator.*
location.*
document.domain

常见布尔

navigator.webdriver
document.hidden

补齐 20~40 个属性后,基本稳定。


七、挂载伪浏览器环境

globalThis.window = fakeBrowser;
globalThis.document = fakeBrowser;
globalThis.navigator = fakeBrowser;
globalThis.location = fakeBrowser;

目的:

所有入口都指向同一个 Proxy

这样:

无论 wasm 访问哪个全局对象,都能被记录。


八、执行 wasm

var go = new Go();
go.run(instance);

执行后:

所有 JS 访问行为都会被记录。


九、最终效果

输出示例:

[GET] window.navigator
[GET] window.navigator.userAgent
[GET] window.screen.width
[GET] window.innerWidth
本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习使用,请在下载后24小时内删除,严禁商用。若由于商用引起版权纠纷,一切责任均由使用者承担。 【注意:本站发布资源来源于网络搜集,均有较强时效性,请在下载前注意查看文章资源发布或更新时间,距离当前时间太久的资源不建议下载,特别是安卓专区相关资源,会有大概率失效无法使用】