+
-
+
---
## 功能特性
-- 通过 Frida 注入微信客户端,转发小程序调试协议
-- CDP 代理桥接,支持 Chrome DevTools 直接连接调试
-- 路由枚举与一键跳转导航
-- 云函数调用监控与参数分析
-- UserScript 自动注入(支持按 URL 匹配、run-at 时机控制)
-- 内置安全扫描模块,生成扫描报告
+- Frida 动态注入微信客户端,转发小程序调试协议
+- CDP 代理桥接,Chrome DevTools 直连调试
+- 路由枚举、分类与一键跳转
+- 云函数调用监控与参数分析(动态 Hook + 静态扫描)
+- UserScript 自动注入(支持 URL 匹配、run-at 时机控制)
+- wxapkg 解密解包 + 敏感信息扫描(IP / 密钥 / 云存储 / JWT 等)
- 深色 / 浅色主题切换
-- GUI(PySide6)与 CLI 双模式运行
+- GUI(PySide6)与 CLI 双模式
---
@@ -50,6 +54,7 @@
| websockets | >= 12.0 |
| protobuf | >= 4.0.0 |
| PySide6 | >= 6.5.0 |
+| pycryptodome | 最新版 |
安装依赖:
@@ -59,26 +64,45 @@ pip install -r requirements.txt
---
-## 微信版本要求
+## 支持的微信版本
-- WMPF版本支持列表:11581, 11633, 13331, 13341, 13487, 13639, 13655, 13871, 13909, 14161, 14199, 14315, 16133, 16203, 16389, 16467, 16771,
- 16815, 16965, 17037, 17071, 17127, 18055, 18151, 18787, 18891, 18955, 19027, 19201
-- 推荐微信版本为4.1.0.30
-- 下载地址[weixin/4.1.0.30](https://github.com/vs-olitus/wx-version/releases/tag/4.1.0.30)
+### Windows
-## 使用方法
+- WMPF 版本:11581, 11633, 13331, 13341, 13487, 13639, 13655, 13871, 13909, 14161, 14199, 14315, 16133, 16203, 16389, 16467, 16771, 16815, 16965, 17037, 17071, 17127, 18055, 18151, 18787, 18891, 18955, 19027, 19201
+- 推荐微信版本:**4.1.0.30**
+- 下载地址:[weixin/4.1.0.30](https://github.com/vs-olitus/wx-version/releases/tag/4.1.0.30)
-### GUI 模式(推荐)
+### macOS
+
+- WMPF 版本:18152, 18788
+- 推荐微信版本:**4.1.7.30**
+- 下载地址:[weixin/4.1.7.30](https://github.com/vs-olitus/wx-version/releases/tag/4.1.7.30)
+
+---
+
+## 快速开始
+
+### Windows
+
+**一键启动:** 双击 `启动.bat`,首次运行会自动安装依赖。
+
+**手动启动:**
```bash
python gui.py
```
-双击启动.bat
-启动后在主界面点击 **启动调试** 即可开始调试。
-### 小程序页面操作
+### macOS
-点击启动调试前请勿打开小程序, **启动调试**打开后再次启动**小程序**即可。
+**一键启动:** 终端执行 `./启动.sh`,首次运行会自动安装依赖。
+
+**手动启动:**
+
+```bash
+python3 gui.py
+```
+
+启动后在主界面点击 **启动调试**,然后再打开小程序即可(请勿在启动调试前打开小程序)。
### CLI 模式
@@ -92,6 +116,7 @@ python main.py --cdp-port 62000
# 开启详细日志
python main.py --debug-main --debug-frida
```
+
### 连接 Chrome DevTools
启动后,在 Chrome 地址栏输入:
@@ -100,7 +125,25 @@ python main.py --debug-main --debug-frida
devtools://devtools/bundled/inspector.html?ws=127.0.0.1:62000
```
-### 参数说明
+---
+
+## macOS 注意事项
+
+如果 Frida 注入报错,需要解除系统对进程附加的限制,任选其一:
+
+**方案一:关闭 SIP(系统完整性保护)**
+
+> 关闭后 Frida 才能正常注入进程。参考教程:[macOS SIP 开启关闭教程](https://cloud.tencent.com/developer/article/1496058)
+
+**方案二:强制重签名 WeChat**
+
+```bash
+sudo codesign --force --deep --sign - /Applications/WeChat.app
+```
+
+---
+
+## 参数说明
| 参数 | 默认值 | 说明 |
|------|--------|------|
@@ -111,11 +154,11 @@ devtools://devtools/bundled/inspector.html?ws=127.0.0.1:62000
| `--scripts-dir` | `./userscripts` | UserScript 目录路径 |
| `--script` | — | 指定单个 .js 文件注入(可多次使用) |
-### UserScript 注入
+---
-将 `.js` 脚本放入 `userscripts/` 目录,工具启动时自动加载并按规则注入。
+## UserScript 注入
-也可手动指定文件:
+将 `.js` 脚本放入 `userscripts/` 目录,启动时自动加载并注入。也可手动指定:
```bash
python main.py --script ./my_hook.js --script ./another.js
@@ -131,21 +174,27 @@ pyinstaller WMPFDebugger.spec
---
-## 常见问题解决办法
+## 常见问题
-1. Frida 已显示连接,但小程序端显示未连接或无法断点调试
+**Q: Frida 已连接,但小程序端显示未连接或无法断点调试?**
-若操作顺序无误,建议先彻底卸载微信并重启电脑(如有重要聊天记录请提前备份)。
-删除路径C:\Users\用户名\AppData\Roaming\Tencent\xwechat\XPlugin\Plugins\RadiumWMPF下所有以数字命名的文件夹,再次重启电脑后,安装微信 4.1.0.30 版本。安装完成后检查上述路径,确认文件夹编号为 16389。
+确认操作顺序无误后,尝试以下步骤:
+
+1. 彻底卸载微信并重启电脑(重要聊天记录请提前备份)
+2. 删除 `C:\Users\用户名\AppData\Roaming\Tencent\xwechat\XPlugin\Plugins\RadiumWMPF` 下所有数字命名的文件夹
+3. 再次重启后安装微信 4.1.0.30 版本
+4. 检查上述路径,确认文件夹编号为 `16389`
+
+---
## 参考项目
-本项目参考并学习了以下优秀工具的实现思路:
-
- [evi0s/WMPFDebugger](https://github.com/evi0s/WMPFDebugger)
- [0xsdeo/HeartK](https://github.com/0xsdeo/HeartK)
- [残笑/FindSomething](https://github.com/momosecurity/FindSomething)
-- [进击的HACK/JSRPC与调用wx.cloud](https://mp.weixin.qq.com/s/hTlekrCPiMJCvsHYx7CAxw)
+- [进击的HACK / JSRPC 与调用 wx.cloud](https://mp.weixin.qq.com/s/hTlekrCPiMJCvsHYx7CAxw)
+- [linguo2625469/WMPFDebugger-mac](https://github.com/linguo2625469/WMPFDebugger-mac)
+
---
## 致谢
@@ -153,9 +202,14 @@ pyinstaller WMPFDebugger.spec
感谢 **0xsdeo** 师傅的大力支持与思路提供。
---
+
## 交流群
-群满200变成手动挡了。加我拉
-
+
+群满 200 人后需要手动邀请,请加我拉群:
+
+
+
+---
## 免责声明
diff --git a/frida/config/mac/addresses.18152.json b/frida/config/mac/addresses.18152.json
new file mode 100644
index 0000000..09288f1
--- /dev/null
+++ b/frida/config/mac/addresses.18152.json
@@ -0,0 +1,20 @@
+{
+ "Version": 18152,
+ "Arch": {
+ "arm64": {
+ "LoadStartHookOffset": "0x4F43880",
+ "LoadStartHookOffset2": "0x8394F24",
+ "CDPFilterHookOffset": "0x8385F44",
+ "ResourceCachePolicyHookOffset": "0x4FA746C",
+ "StructOffset": 1376
+ },
+ "x64": {
+ "LoadStartHookOffset": "0x5971FF0",
+ "LoadStartHookOffset2": "0x91D58F0",
+ "CDPFilterHookOffset": "0x91C68D0",
+ "ResourceCachePolicyHookOffset": "0x59E0950",
+ "StructOffset": 1376
+ }
+ }
+ }
+
\ No newline at end of file
diff --git a/frida/config/mac/addresses.18788.json b/frida/config/mac/addresses.18788.json
new file mode 100644
index 0000000..ffba541
--- /dev/null
+++ b/frida/config/mac/addresses.18788.json
@@ -0,0 +1,19 @@
+{
+ "Version": 18788,
+ "Arch": {
+ "arm64": {
+ "LoadStartHookOffset": "0x4F6454C",
+ "LoadStartHookOffset2": "0x83BC8C8",
+ "CDPFilterHookOffset": "0x83AE884",
+ "ResourceCachePolicyHookOffset": "0x4FCAD90",
+ "StructOffset": 1368
+ },
+ "x64": {
+ "LoadStartHookOffset": "0x5994BA0",
+ "LoadStartHookOffset2": "0x91FFB90",
+ "CDPFilterHookOffset": "0x91F1C20",
+ "ResourceCachePolicyHookOffset": "0x5A066E0",
+ "StructOffset": 1368
+ }
+ }
+}
diff --git a/frida/config/addresses.11581.json b/frida/config/win/addresses.11581.json
similarity index 100%
rename from frida/config/addresses.11581.json
rename to frida/config/win/addresses.11581.json
diff --git a/frida/config/addresses.11633.json b/frida/config/win/addresses.11633.json
similarity index 100%
rename from frida/config/addresses.11633.json
rename to frida/config/win/addresses.11633.json
diff --git a/frida/config/addresses.13331.json b/frida/config/win/addresses.13331.json
similarity index 100%
rename from frida/config/addresses.13331.json
rename to frida/config/win/addresses.13331.json
diff --git a/frida/config/addresses.13341.json b/frida/config/win/addresses.13341.json
similarity index 100%
rename from frida/config/addresses.13341.json
rename to frida/config/win/addresses.13341.json
diff --git a/frida/config/addresses.13487.json b/frida/config/win/addresses.13487.json
similarity index 100%
rename from frida/config/addresses.13487.json
rename to frida/config/win/addresses.13487.json
diff --git a/frida/config/addresses.13639.json b/frida/config/win/addresses.13639.json
similarity index 100%
rename from frida/config/addresses.13639.json
rename to frida/config/win/addresses.13639.json
diff --git a/frida/config/addresses.13655.json b/frida/config/win/addresses.13655.json
similarity index 100%
rename from frida/config/addresses.13655.json
rename to frida/config/win/addresses.13655.json
diff --git a/frida/config/addresses.13871.json b/frida/config/win/addresses.13871.json
similarity index 100%
rename from frida/config/addresses.13871.json
rename to frida/config/win/addresses.13871.json
diff --git a/frida/config/addresses.13909.json b/frida/config/win/addresses.13909.json
similarity index 100%
rename from frida/config/addresses.13909.json
rename to frida/config/win/addresses.13909.json
diff --git a/frida/config/addresses.14161.json b/frida/config/win/addresses.14161.json
similarity index 100%
rename from frida/config/addresses.14161.json
rename to frida/config/win/addresses.14161.json
diff --git a/frida/config/addresses.14199.json b/frida/config/win/addresses.14199.json
similarity index 100%
rename from frida/config/addresses.14199.json
rename to frida/config/win/addresses.14199.json
diff --git a/frida/config/addresses.14315.json b/frida/config/win/addresses.14315.json
similarity index 100%
rename from frida/config/addresses.14315.json
rename to frida/config/win/addresses.14315.json
diff --git a/frida/config/addresses.16133.json b/frida/config/win/addresses.16133.json
similarity index 100%
rename from frida/config/addresses.16133.json
rename to frida/config/win/addresses.16133.json
diff --git a/frida/config/addresses.16203.json b/frida/config/win/addresses.16203.json
similarity index 100%
rename from frida/config/addresses.16203.json
rename to frida/config/win/addresses.16203.json
diff --git a/frida/config/addresses.16389.json b/frida/config/win/addresses.16389.json
similarity index 100%
rename from frida/config/addresses.16389.json
rename to frida/config/win/addresses.16389.json
diff --git a/frida/config/addresses.16467.json b/frida/config/win/addresses.16467.json
similarity index 100%
rename from frida/config/addresses.16467.json
rename to frida/config/win/addresses.16467.json
diff --git a/frida/config/addresses.16771.json b/frida/config/win/addresses.16771.json
similarity index 100%
rename from frida/config/addresses.16771.json
rename to frida/config/win/addresses.16771.json
diff --git a/frida/config/addresses.16815.json b/frida/config/win/addresses.16815.json
similarity index 100%
rename from frida/config/addresses.16815.json
rename to frida/config/win/addresses.16815.json
diff --git a/frida/config/addresses.16965.json b/frida/config/win/addresses.16965.json
similarity index 100%
rename from frida/config/addresses.16965.json
rename to frida/config/win/addresses.16965.json
diff --git a/frida/config/addresses.17037.json b/frida/config/win/addresses.17037.json
similarity index 100%
rename from frida/config/addresses.17037.json
rename to frida/config/win/addresses.17037.json
diff --git a/frida/config/addresses.17071.json b/frida/config/win/addresses.17071.json
similarity index 100%
rename from frida/config/addresses.17071.json
rename to frida/config/win/addresses.17071.json
diff --git a/frida/config/addresses.17127.json b/frida/config/win/addresses.17127.json
similarity index 100%
rename from frida/config/addresses.17127.json
rename to frida/config/win/addresses.17127.json
diff --git a/frida/config/addresses.18055.json b/frida/config/win/addresses.18055.json
similarity index 100%
rename from frida/config/addresses.18055.json
rename to frida/config/win/addresses.18055.json
diff --git a/frida/config/addresses.18151.json b/frida/config/win/addresses.18151.json
similarity index 100%
rename from frida/config/addresses.18151.json
rename to frida/config/win/addresses.18151.json
diff --git a/frida/config/addresses.18787.json b/frida/config/win/addresses.18787.json
similarity index 100%
rename from frida/config/addresses.18787.json
rename to frida/config/win/addresses.18787.json
diff --git a/frida/config/addresses.18891.json b/frida/config/win/addresses.18891.json
similarity index 100%
rename from frida/config/addresses.18891.json
rename to frida/config/win/addresses.18891.json
diff --git a/frida/config/addresses.18955.json b/frida/config/win/addresses.18955.json
similarity index 100%
rename from frida/config/addresses.18955.json
rename to frida/config/win/addresses.18955.json
diff --git a/frida/config/addresses.19027.json b/frida/config/win/addresses.19027.json
similarity index 100%
rename from frida/config/addresses.19027.json
rename to frida/config/win/addresses.19027.json
diff --git a/frida/config/addresses.19201.json b/frida/config/win/addresses.19201.json
similarity index 100%
rename from frida/config/addresses.19201.json
rename to frida/config/win/addresses.19201.json
diff --git a/frida/hook.js b/frida/hook.js
index c37d6e1..e675de3 100644
--- a/frida/hook.js
+++ b/frida/hook.js
@@ -1,119 +1,232 @@
+const VERBOSE = false;
+const PLATFORM = Process.platform; // 'darwin' | 'windows'
+const ARCH = Process.arch; // 'arm64' | 'x64' | 'x86_64' | ...
+
+// ── Module Resolution ──
+
const getMainModule = (version) => {
+ if (PLATFORM === "darwin") {
+ return Process.findModuleByName("WeChatAppEx Framework");
+ }
+ // Windows: flue.dll for newer versions
if (version >= 13331) {
return Process.findModuleByName("flue.dll");
}
return Process.findModuleByName("WeChatAppEx.exe");
};
-const patchCDPFilter = (base, config) => {
- // xref: SendToClientFilter OR devtools_message_filter_applet_webview.cc
- const offset = config.CDPFilterHookOffset;
+// ── Scene Values ──
+
+const SCENE_WHITELIST = [
+ 1005, 1007, 1008, 1012, 1027, 1035, 1053, 1074,
+ 1145, 1168, 1178, 1256, 1260, 1302, 1308,
+];
+
+// ── CDP Filter Patch (shared logic, platform-specific pointer access) ──
+
+const patchCDPFilter = (base, offset) => {
+ // xref: SendToClientFilter / devtools_message_filter_applet_webview.cc
Interceptor.attach(base.add(offset), {
onEnter(args) {
- send(
- `[patch] CDP filter on enter, original value of input: ${args[0].readPointer()}`,
- );
- this.inputValue = args[0];
+ if (PLATFORM === "windows") {
+ this.inputValue = args[0];
+ }
},
onLeave(retval) {
- const inputValue = this.inputValue.readPointer();
- if (inputValue.isNull() || inputValue.add(8).isNull()) {
- // there's a chance the value could be null
- // return here to avoid crash
- return;
+ let target;
+ if (PLATFORM === "darwin") {
+ // macOS: retval points directly to the struct
+ if (!retval || retval.isNull()) return;
+ target = retval.add(8);
+ } else {
+ // Windows: dereference args[0] to get the struct pointer
+ const ptr = this.inputValue.readPointer();
+ if (ptr.isNull() || ptr.add(8).isNull()) return;
+ target = ptr.add(8);
}
-
- send(
- `[patch] CDP filter on leave, patch input, now value: ${inputValue}; ` +
- `*(input + 8) = ${inputValue.add(8).readU32()}`,
- );
- if (inputValue.add(8).readU32() == 6) {
- inputValue.add(8).writeU32(0x0);
+ VERBOSE && console.log(`[patch] CDP filter v8[2]: ${target.readU32()}`);
+ if (target.readU32() === 6) {
+ target.writeU32(0x0);
}
},
});
};
-const hookOnLoadScene = (a1, sceneOffsets) => {
- const miniappConfigPtr = a1
- .add(56)
- .readPointer()
- .add(sceneOffsets[0])
- .readPointer();
- const miniappScenePtr = miniappConfigPtr
- .add(8)
- .readPointer()
- .add(sceneOffsets[1])
- .readPointer()
- .add(16)
- .readPointer()
- .add(488);
- send(`[hook] scene: ${miniappScenePtr.readInt()}`);
+// ── Resource Cache Policy Patch (darwin only) ──
- // 1000: from issue #83 <-- will crash the process
- // 1007: from issue #80
- // 1008: from issue #53
- // 1027: from issue #78
- // 1035: from issue #78
- // 1053: from issue #25
- // 1074: from issue #32
- // 1145: from search
- // 1178: from phone (issue #117)
- // 1256: from recent
- // 1260: from frequently used
- // 1302: from services
- // 1308: minigame?
- const sceneNumberArray = [
- 1005, 1007, 1008, 1027, 1035, 1053, 1074, 1145, 1178, 1256, 1260, 1302,
- 1308,
- ];
- if (!sceneNumberArray.includes(miniappScenePtr.readInt())) {
- return;
- }
- send("[hook] hook scene condition -> 1101");
- miniappScenePtr.writeInt(1101);
+const patchResourceCachePolicy = (base, offset) => {
+ // xref: WAPCAdapterAppIndex.js
+ Interceptor.attach(base.add(offset), {
+ onLeave(retval) {
+ VERBOSE && console.log(`[patch] cache policy retval: ${retval.toInt32()} -> 0`);
+ retval.replace(0x0);
+ },
+ });
};
-const patchOnLoadStart = (base, config) => {
- // xref: AppletIndexContainer::OnLoadStart
- Interceptor.attach(base.add(config.LoadStartHookOffset), {
+// ── OnLoadStart: Darwin ──
+
+const darwinPatchLoadStart = (base, offset) => {
+ // xref: AppletIndexContainer::OnLoadStart (first match)
+ // Force debug mode by setting second param to true
+ Interceptor.attach(base.add(offset), {
+ onEnter() {
+ console.log("[hook] AppletIndexContainer::OnLoadStart onEnter");
+ if (ARCH === "arm64") {
+ this.context.x1 = (this.context.x1 & ~0xff) | 0x1;
+ } else {
+ // x64 / x86_64: System V ABI, second param = RSI
+ this.context.rsi = (this.context.rsi & ~0xff) | 0x1;
+ }
+ },
+ onLeave() {},
+ });
+};
+
+const darwinPatchLoadStart2 = (base, offset, structOffset) => {
+ // xref: AppletIndexContainer::OnLoadStart (last function)
+ // Hijack scene value via struct pointer chain
+ Interceptor.attach(base.add(offset), {
onEnter(args) {
- send(
- `[inteceptor] AppletIndexContainer::OnLoadStart onEnter, ` +
- `indexContainer.this: ${this.context.rcx}`,
- );
- // write dl to 0x1
+ try {
+ const v4 = args[0].add(8).readPointer();
+ if (!v4 || v4.isNull()) return;
+ const q1 = v4.add(structOffset).readPointer();
+ if (!q1 || q1.isNull()) return;
+ const q2 = q1.add(16).readPointer();
+ if (!q2 || q2.isNull()) return;
+ const scenePtr = q2.add(488);
+ const scene = scenePtr.readInt();
+ console.log(`[hook] scene: ${scene}`);
+ if (SCENE_WHITELIST.includes(scene)) {
+ console.log("[hook] hook scene -> 1101");
+ scenePtr.writeInt(1101);
+ }
+ } catch (e) {
+ console.error(`[hook] onLoadStart2 error: ${e}`);
+ }
+ },
+ onLeave() {},
+ });
+};
+
+// ── OnLoadStart: Windows ──
+
+const winPatchLoadStart = (base, config) => {
+ // xref: AppletIndexContainer::OnLoadStart
+ // Windows combines debug flag + scene hijack in one hook
+ Interceptor.attach(base.add(config.LoadStartHookOffset), {
+ onEnter() {
+ // Microsoft x64 ABI: second param = RDX, force debug mode
if ((this.context.rdx & 0xff) !== 1) {
this.context.rdx = (this.context.rdx & ~0xff) | 0x1;
}
- // handle onLoad scene
- hookOnLoadScene(this.context.rcx, config.SceneOffsets);
- },
- onLeave(retval) {
- // do nothing
+ // Scene hijack via SceneOffsets pointer chain
+ try {
+ const offsets = config.SceneOffsets;
+ const ptr1 = this.context.rcx.add(56).readPointer().add(offsets[0]).readPointer();
+ const scenePtr = ptr1.add(8).readPointer().add(offsets[1]).readPointer().add(16).readPointer().add(488);
+ const scene = scenePtr.readInt();
+ send(`[hook] scene: ${scene}`);
+ if (SCENE_WHITELIST.includes(scene)) {
+ send("[hook] hook scene -> 1101");
+ scenePtr.writeInt(1101);
+ }
+ } catch (e) {
+ send(`[hook] scene hook error: ${e}`);
+ }
},
+ onLeave() {},
});
};
+// ── Config Parsing ──
+
const parseConfig = () => {
const rawConfig = `@@CONFIG@@`;
if (rawConfig.includes("@@")) {
- // test addresses
+ // Fallback test config
+ if (PLATFORM === "darwin") {
+ return {
+ Version: 17078,
+ Arch: {
+ arm64: {
+ LoadStartHookOffset: "0x4F0620C",
+ LoadStartHookOffset2: "0x81CEC08",
+ CDPFilterHookOffset: "0x81BFC04",
+ ResourceCachePolicyHookOffset: "0x4F699E8",
+ StructOffset: 1376,
+ },
+ },
+ };
+ }
return {
Version: 18955,
LoadStartHookOffset: "0x25B52C0",
CDPFilterHookOffset: "0x30248B0",
- SceneOffsets: [1408, 1344],
+ SceneOffsets: [1408, 1344, 488],
};
}
return JSON.parse(rawConfig);
};
+const resolveArchConfig = (config) => {
+ // Windows configs have offsets at top level — return directly
+ if (config && config.LoadStartHookOffset) {
+ return config;
+ }
+ // Mac configs nest under Arch.{arm64|x64}
+ const table = config?.Arch || config?.arch;
+ if (!table) return null;
+
+ const candidates = [ARCH];
+ if (ARCH === "x86_64") candidates.push("x64");
+ if (ARCH === "amd64") candidates.push("x64");
+
+ for (const key of candidates) {
+ const picked = table[key];
+ if (picked && picked.LoadStartHookOffset) {
+ return { ...picked, Version: config.Version, __arch: key };
+ }
+ }
+ return null;
+};
+
+// ── Main ──
+
const main = () => {
- const config = parseConfig();
+ const rawConfig = parseConfig();
+ const config = resolveArchConfig(rawConfig);
+
+ if (!config) {
+ console.error(`[frida] no config for platform=${PLATFORM} arch=${ARCH}`);
+ return;
+ }
+
const mainModule = getMainModule(config.Version);
- patchOnLoadStart(mainModule.base, config);
- patchCDPFilter(mainModule.base, config);
+ if (!mainModule) {
+ const expected = PLATFORM === "darwin" ? "WeChatAppEx Framework" : "flue.dll / WeChatAppEx.exe";
+ console.error(`[frida] module not found: ${expected}`);
+ return;
+ }
+
+ console.log(`[frida] version=${config.Version} platform=${PLATFORM} arch=${config.__arch || ARCH}`);
+ console.log(`[frida] module base: ${mainModule.base}`);
+
+ const base = mainModule.base;
+
+ // CDP filter patch — both platforms
+ patchCDPFilter(base, config.CDPFilterHookOffset);
+
+ if (PLATFORM === "darwin") {
+ darwinPatchLoadStart(base, config.LoadStartHookOffset);
+ darwinPatchLoadStart2(base, config.LoadStartHookOffset2, config.StructOffset);
+ if (config.ResourceCachePolicyHookOffset) {
+ patchResourceCachePolicy(base, config.ResourceCachePolicyHookOffset);
+ }
+ } else {
+ winPatchLoadStart(base, config);
+ }
};
main();
diff --git a/gui.py b/gui.py
index 3f175d3..790781a 100644
--- a/gui.py
+++ b/gui.py
@@ -641,8 +641,9 @@ def _make_entry(placeholder="", width=None):
class App(QMainWindow):
def __init__(self):
super().__init__()
- self.setWindowTitle("First")
- _ico = os.path.join(_BASE_DIR, "icon.ico")
+ self._os_tag = "macOS" if sys.platform == "darwin" else "Windows"
+ self.setWindowTitle(f"First-{self._os_tag}")
+ _ico = os.path.join(_BASE_DIR, "icon.png")
if os.path.exists(_ico):
self.setWindowIcon(QIcon(_ico))
self.resize(960, 620)
@@ -726,14 +727,14 @@ class App(QMainWindow):
logo_row.setContentsMargins(0, 0, 0, 0)
logo_row.setSpacing(6)
logo_row.addStretch()
- _ico_path = os.path.join(_BASE_DIR, "icon.ico")
+ _ico_path = os.path.join(_BASE_DIR, "icon.png")
if os.path.exists(_ico_path):
_ico_lbl = QLabel()
_pix = QPixmap(_ico_path).scaled(20, 20, Qt.KeepAspectRatio, Qt.SmoothTransformation)
_ico_lbl.setPixmap(_pix)
_ico_lbl.setFixedSize(20, 20)
logo_row.addWidget(_ico_lbl)
- self._sb_logo = QLabel("First")
+ self._sb_logo = QLabel(f"First-{self._os_tag}")
self._sb_logo.setObjectName("sb_logo")
logo_row.addWidget(self._sb_logo)
logo_row.addStretch()
@@ -818,6 +819,11 @@ class App(QMainWindow):
QDesktopServices.openUrl(QUrl("https://github.com/Spade-sec/First")),
self._log_add("info", "[gui] 已打开 GitHub 页面"))
sb_lay.addWidget(sb_gh)
+ sb_ver = QLabel("v1.0.7")
+ sb_ver.setObjectName("sb_theme")
+ sb_ver.setAlignment(Qt.AlignCenter)
+ sb_ver.setFont(QFont(_FN, 7))
+ sb_lay.addWidget(sb_ver)
sb_lay.addSpacing(12)
self._update_theme_label()
@@ -1366,15 +1372,8 @@ class App(QMainWindow):
path_row.addWidget(_make_label("Applet目录", bold=True))
self._ext_path_ent = _make_entry("wxapkg 包目录路径...")
# 自动检测默认路径
- default_pkg = ""
- if os.name == "nt":
- user_home = os.path.expanduser("~")
- default_pkg = os.path.join(
- user_home, "AppData", "Roaming", "Tencent",
- "xwechat", "radium", "Applet", "packages"
- )
- if not os.path.isdir(default_pkg):
- default_pkg = ""
+ from src.wxapkg import get_default_packages_dir
+ default_pkg = get_default_packages_dir() or ""
saved_path = self._cfg.get("extract_packages_dir", "")
if saved_path:
self._ext_path_ent.setText(saved_path)
@@ -1635,23 +1634,22 @@ class App(QMainWindow):
def _ext_auto_detect(self):
"""自动检测默认路径"""
- if os.name == "nt":
- user_home = os.path.expanduser("~")
- default_pkg = os.path.join(
- user_home, "AppData", "Roaming", "Tencent",
- "xwechat", "radium", "Applet", "packages"
- )
- if os.path.isdir(default_pkg):
- if self._ext_path_ent.text().strip() == default_pkg:
- # 路径相同但强制刷新
- self._ext_refresh_apps()
- else:
- self._ext_path_ent.setText(default_pkg)
- self._ext_log("已自动检测到 Applet 目录")
+ from src.wxapkg import get_default_packages_dir
+ default_pkg = get_default_packages_dir()
+ if default_pkg:
+ if self._ext_path_ent.text().strip() == default_pkg:
+ self._ext_refresh_apps()
else:
- self._ext_log("未找到默认 Applet 目录,请手动选择")
+ self._ext_path_ent.setText(default_pkg)
+ self._ext_log(f"已自动检测到 Applet 目录: {default_pkg}")
else:
- self._ext_log("非 Windows 系统,请手动选择目录")
+ hint = ("~/Library/Containers/com.tencent.xinWeChat/Data/Documents/"
+ "app_data/radium/users/<微信用户ID>/applet/packages"
+ if sys.platform == "darwin" else
+ "未找到默认 Applet 目录")
+ self._ext_log(f"未自动检测到目录,请手动选择。macOS 参考路径: {hint}"
+ if sys.platform == "darwin" else
+ f"{hint},请手动选择")
def _ext_auto_process_new(self, new_ids):
"""对新增的小程序自动执行反编译/扫描(按开关状态)"""
@@ -3939,7 +3937,7 @@ if __name__ == "__main__":
app = QApplication(sys.argv)
app.setFont(QFont(_FN, 9))
- _ico = os.path.join(_BASE_DIR, "icon.ico")
+ _ico = os.path.join(_BASE_DIR, "icon.png")
if os.path.exists(_ico):
app.setWindowIcon(QIcon(_ico))
window = App()
diff --git a/icon.png b/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..6796bb304b622c9d100c5db04f7d9c6903869943
GIT binary patch
literal 1535251
zcmeFae|VhLdFOqeXJ%xLB;!GrWn&jJb_ZE??8HEUmMCE)oKy--S4&
z4hvvJ0_>WJR0Nli`a1MiLW|}kJf}Nc)}x{evlW&^1)3eb%bFp5ArXiaLfppOLdRrt
ziy>fit_<>v%FfVaUH1YvgNlQw5&L+$r)#&wV{yt`Epk??Mh~A!`W28Lr3batzcReD
zS3x2EGKV{iZvZLJi_7bXCmg2aztpoBq)2O#q~O8PY*)}Zp%@7o9kJYwfo#;hBeg!e
zIJiIo6&Kt<_GnscnkpxQ0uly}8+m}qt?b1nLk$?nx4AcTp&B4-x;HhcMJPiu!}_oW
z^Nb~mxLvlZyb=~k$`}Y`qlEfvby^QakZVMHF%9RY6}AF^A>j<|hyjR}-kbEzuWQ!A
z@7UtjLl0P)UnTcbk+QhfL)o*_xh6Hw8p!{MlmQtV;*vFQ?Qy3IWFhS|=^ divou$>C^`ce%8c=fEbNMh0%G=
zA7|tCnk=5d$RUi`&sfo*(OP6ieE)pun><9- ;%n5hhvd5JA&@E9J
z?xKZ75rc*vy>juTKKBp0Gj{yKHQS>09{l%_L%su+_D}qlnf>1MA@i!fORGG8u>S0u
z7iKRxJf~sR?8bZQk58??*md6J-3RZT^-8?g1Wq5V3Ly(9KDYb-{9LBaS@!|cuFf@a1@6WKF`P+