使用WMPFDebugger调试高版本微信小程序

WMPFDebugger 踩坑日记

我用的是使用微信 windows 4.0.5.27

https://github.com/cscnk52/wechat-windows-versions/releases

要有 node 环境,推荐使用 LTS v22

安装 yarn

1
npm install --global yarn

https://github.com/evi0s/WMPFDebugger

1
2
cd WMPFDebugger
yarn

但直接安装可能会报错,需要有 vs 的构建环境

https://visualstudio.microsoft.com/zh-hans/downloads/

选择 c++ 开发

在构建的时候可能 frida 会构建出错

可以直接先单独安装 frida,项目使用的是 frida 16.6.6

1
yarn add frida@16.6.6

启动(要先打开微信)

1
npx ts-node src/index.ts

选择要调试的小程序,打开浏览器,访问 devtools://devtools/bundled/inspector.html?ws=127.0.0.1:62000 即可

由于为了方便日常微信使用,把低版本微信客户端放到了虚拟机中,但默认默认监听的是 localhost

修改 index.ts 监听所有网段,可以通过局域网访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import { promises } from "node:fs";
import { EventEmitter } from "node:events";
import path from "node:path";
import * as frida from "frida";
import WebSocket, { WebSocketServer } from "ws";

const codex = require("./third-party/RemoteDebugCodex.js");
const messageProto = require("./third-party/WARemoteDebugProtobuf.js");

class DebugMessageEmitter extends EventEmitter {};


const DEBUG_PORT = 9421;

const CDP_PORT = 62000;

const DEBUG = false;

const debugMessageEmitter = new DebugMessageEmitter();

const **bufferToHexString** = (_buffer_: ArrayBuffer) => {
return Array.from(new Uint8Array(_buffer_)).map(_byte_ => _byte_.toString(16).padStart(2, '0')).join("");
}

const **debug_server** = () => {
const wss = new WebSocketServer({ port: DEBUG_PORT, host: '0.0.0.0' });
console.log(`[server] debug server running on ws://0.0.0.0:${DEBUG_PORT}`);

let messageCounter = 0;

const **onMessage** = (_message_: ArrayBuffer) => {
DEBUG && console.log(`[client] received raw message (hex): ${bufferToHexString(_message_)}`);
let unwrappedData: any = null;
try {
const decodedData = messageProto.mmbizwxadevremote.WARemoteDebug_DebugMessage.decode(_message_);
unwrappedData = codex.unwrapDebugMessageData(decodedData);
DEBUG && console.log(`[client] [DEBUG] decoded data:`);
DEBUG && console.dir(unwrappedData)
} catch (e) {
console.error(`[client] err: ${e}`);
}

if (unwrappedData === null) {
return;
}

if (unwrappedData.category === "chromeDevtoolsResult") {
_// need to proxy to CDP client_
debugMessageEmitter.emit("cdpmessage", unwrappedData.data.payload);
}
}

wss.on("connection", (_ws_: WebSocket) => {
console.log("[conn] miniapp client connected");
_ws_.on("message", onMessage);
_ws_.on("error", (_err_) => {console.error("[client] err:", _err_)});
_ws_.on("close", () => {console.log("[client] client disconnected")});
});

debugMessageEmitter.on("proxymessage", (_message_: string) => {
wss && wss.clients.forEach(_client_ => {
if (_client_.readyState === WebSocket.OPEN) {
_// encode CDP and send to miniapp_
_// wrapDebugMessageData(data, category, compressAlgo)_
const rawPayload = {
jscontext_id: "",
op_id: Math.round(100 * Math.random()),
payload: _message_.toString()
};
DEBUG && console.log(rawPayload);
const wrappedData = codex.wrapDebugMessageData(rawPayload, "chromeDevtools", 0);
const outData = {
seq: ++messageCounter,
category: "chromeDevtools",
data: wrappedData.buffer,
compressAlgo: 0,
originalSize: wrappedData.originalSize
}
const encodedData = messageProto.mmbizwxadevremote.WARemoteDebug_DebugMessage.encode(outData).finish();
_client_.send(encodedData, { binary: true });
}
});
});
}

const **proxy_server** = () => {
const wss = new WebSocketServer({ port: CDP_PORT, host: '0.0.0.0' });
console.log(`[server] proxy server running on ws://0.0.0.0:${CDP_PORT}`);

const **onMessage** = (_message_: string) => {
debugMessageEmitter.emit("proxymessage", _message_);
}

wss.on("connection", (_ws_: WebSocket) => {
console.log("[conn] CDP client connected");
_ws_.on("message", onMessage);
_ws_.on("error", (_err_) => {console.error("[client] CDP err:", _err_)});
_ws_.on("close", () => {console.log("[client] CDP client disconnected")});
});

debugMessageEmitter.on("cdpmessage", (_message_: string) => {
wss && wss.clients.forEach(_client_ => {
if (_client_.readyState === WebSocket.OPEN) {
_// send CDP message to devtools_
_client_.send(_message_);
}
});
});
}

const **frida_server** = async () => {
const localDevice = await frida.getLocalDevice();
const processes = await localDevice.enumerateProcesses({scope: frida.Scope.Metadata});
const wmpfProcesses = processes.filter(_process_ => _process_.name === "WeChatAppEx.exe");
const wmpfPids = wmpfProcesses.map(_p_ => _p_.parameters.ppid ? _p_.parameters.ppid : 0);

_// find the parent process_
const wmpfPid = wmpfPids.sort((_a_, _b_) => wmpfPids.filter(_v_ => _v_ === _a_).length - wmpfPids.filter(_v_ => _v_ === _b_).length).pop();
if (wmpfPid === undefined) {
throw new Error("[frida] WeChatAppEx.exe process not found");
return;
}
const wmpfProcess = processes.filter(_process_ => _process_.pid === wmpfPid)[0];
const wmpfVersionMatch = wmpfProcess.parameters.path ? wmpfProcess.parameters.path.match(/\d+/g) : "";
const wmpfVersion = wmpfVersionMatch ? new Number(wmpfVersionMatch.pop()) : 0;
if (wmpfVersion === 0) {
throw new Error("[frida] error in find wmpf version");
return;
}

_// attach to process_
const session = await localDevice.attach(wmpfPid);

_// find hook script_
const projectRoot = path.join(path.dirname(require.main && require.main.filename || process.mainModule && process.mainModule.filename || process.cwd()), "..");
let scriptContent: string | null = null;
try {
scriptContent = (await promises.readFile(path.join(projectRoot, "frida/hook.js"))).toString();
} catch (e) {
throw new Error("[frida] hook script not found");
return;
}

let configContent: string | null = null;
try {
configContent = (await promises.readFile(path.join(projectRoot, "frida/config", `addresses.${wmpfVersion}.json`))).toString();
configContent = JSON.stringify(JSON.parse(configContent));
} catch(e) {
throw new Error(`[frida] version config not found: ${wmpfVersion}`);
}

if (scriptContent === null || configContent === null) {
throw new Error("[frida] unable to find hook script");
return;
}

_// load script_
const script = await session.createScript(scriptContent.replace("@@CONFIG@@", configContent));
script.message.connect(_message_ => {
console.log("[frida client]", _message_);
});
await script.load();
console.log(`[frida] script loaded, WMPF version: ${wmpfVersion}, pid: ${wmpfPid}`);
}

const **main** = async () => {
debug_server();
proxy_server();
frida_server();
}

(async () => {
await main();
})();

如果提示 标签页将处于非活动状态 ,需要将小程序进程移动桌面最上层


使用WMPFDebugger调试高版本微信小程序
http://example.com/2025/08/30/使用WMPFDebugger调试高版本微信小程序/
作者
Gilgamesh
发布于
2025年8月30日
许可协议