From cc48892f629c3f315334e7757d7566d24d590585 Mon Sep 17 00:00:00 2001 From: yincong Date: Thu, 19 Mar 2026 09:33:03 +0800 Subject: [PATCH] 8.28 --- frida-wechat.md | 1 + frida/auto.js | 24 +++++++++++------- frida/button_trigger.js | 21 ---------------- frida/file.js | 43 +++++++++++++++++++------------- frida/init.js | 7 ++++++ frida/receiver.js | 40 ++++++++++++++--------------- frida/text.js | 26 +++++++++++-------- onebot/Makefile | 17 +++++++++++++ onebot/main.go | 4 ++- onebot/param.go | 4 +-- onebot/utils.go | 33 ++++++++++++++++++++++-- onebot/worker.go | 2 +- wechat_version/4_1_8_28_mac.json | 22 ++++++++++++++++ 13 files changed, 159 insertions(+), 85 deletions(-) delete mode 100644 frida/button_trigger.js create mode 100644 frida/init.js create mode 100644 onebot/Makefile create mode 100644 wechat_version/4_1_8_28_mac.json diff --git a/frida-wechat.md b/frida-wechat.md index 2928301..c787f3e 100644 --- a/frida-wechat.md +++ b/frida-wechat.md @@ -2,5 +2,6 @@ 图片hook: startUploadMedia (ida搜), OnUploadCompleted (ce查) startUploadMedia 上传图片,OnUploadCompleted 上传图片完成后的回调 加密后的图片在_OnRecvFileData +download系列都是_OnRecvFileData,在不同的文件下面 WeChatExt中会有导致idapro出问题的代码 \ No newline at end of file diff --git a/frida/auto.js b/frida/auto.js index 2179f78..a4eb84a 100644 --- a/frida/auto.js +++ b/frida/auto.js @@ -1,10 +1,12 @@ +let module; +Process.enumerateModules().forEach(function (m) { + if (m.name.toLowerCase().includes("wechat.dylib")) { + module = m; + } +}); +console.log("module:", module.name, module.base, module.size); function scanPatterns() { - var module = Process.getModuleByName("WeChat"); - if (!module) { - console.error("[!] 找不到 WeChat 模块基址,请检查进程名。"); - } - myPatterns.forEach((item, index) => { const { name, pattern } = item; @@ -26,7 +28,7 @@ function scanPatterns() { const myPatterns = [ { name: "cndOnCompleteAddr", - pattern: "08 19 40 F9 E1 03 14 AA E2 03 15 AA 00 01 3F D6" + pattern: "68 42 00 91 29 00 80 52 08 01 29 F8 88 12 40 B9" }, { name: "uploadGetCallbackWrapperAddr", @@ -38,7 +40,7 @@ const myPatterns = [ }, { name: "downloadFileAddr", - pattern: "68 52 41 F9 19 8D 41 F9 1A 00 14 8B 5F 03 19 EB" + pattern: "F5 03 00 AA 68 4A 41 F9 08 41 46 39 60 22 01 91 69 52 41 F9" }, { name: "downloadImageAddr", @@ -50,7 +52,7 @@ const myPatterns = [ }, { name: "sendFuncAddr", - pattern: "FF 03 05 D1 FC 6F 11 A9 F4 4F 12 A9 FD 7B 13 A9 FD C3 04 91 F3 03 01 AA F4 03 00 AA A8 EB 01 D0 08 71 44 F9 08 01 40 F9 A8 83 1D F8" + pattern: "FF 03 05 D1 FC 6F 11 A9 F4 4F 12 A9 FD 7B 13 A9 FD C3 04 91 F3 03 01 AA F4 03 00 AA A8 EB 01 D0 08 71 44 F9 08 01 40 F9 A8 83 1D F8" }, { name: "buf2RespAddr", @@ -58,11 +60,15 @@ const myPatterns = [ }, { name: "startDownloadMedia", - pattern: "B6 E3 02 D1 68 E1 01 D0 08 71 44 F9 08 01 40 F9 A8 83 1C F8 28 7C 42 39 09 1D 00 13 2A 48 40 F9 3F 01 00 71 48 B1 88 9A" + pattern: "08 01 40 F9 A8 83 1C F8 28 7C 42 39 09 1D 00 13 2A 48 40 F9" }, { name: "downloadVideoAddr", pattern: "E0 03 07 91 01 00 80 52 45 C1 80 52 06 00 80 52 07 00 80 D2" + }, + { + name: "startUploadMedia", + pattern: "08 01 40 F9 A8 83 1A F8 28 9C 40 B9 1F 0D 00 71" } ]; diff --git a/frida/button_trigger.js b/frida/button_trigger.js deleted file mode 100644 index 65f2c35..0000000 --- a/frida/button_trigger.js +++ /dev/null @@ -1,21 +0,0 @@ -Interceptor.attach( - ObjC.classes.NSEvent["- keyCode"].implementation, - { - onLeave(retval) { - try { - // 36 = Return(Enter) - if (retval.toInt32() === 36) { - console.log("\n===== ENTER PRESSED (System Level) ====="); - - // 打印调用栈 - console.log( - Thread.backtrace(this.context, Backtracer.ACCURATE) - .map(DebugSymbol.fromAddress) - .join("\n") - ); - console.log("========================================\n"); - } - } catch (e) {} - } - } -); diff --git a/frida/file.js b/frida/file.js index e4fa783..98bfb23 100644 --- a/frida/file.js +++ b/frida/file.js @@ -1,9 +1,16 @@ // 1. 获取微信主模块的基地址 -var baseAddr = Process.getModuleByName("WeChat").base; -if (!baseAddr) { - console.error("[!] 找不到 WeChat 模块基址,请检查进程名。"); +var baseAddr = ptr(0); +Process.enumerateModules().forEach(function (m) { + if (m.name.toLowerCase().includes("wechat.dylib")) { + baseAddr = m.base; + } +}); + +if (baseAddr === 0x0) { + console.log("wechat.dylib not found"); } -console.log("[+] WeChat base address: " + baseAddr); + +console.log("baseAddr: " + baseAddr); // -------------------------基础函数分区------------------------- function toVarint(n) { @@ -101,14 +108,14 @@ function generateBytes(n) { // 双方公共使用的地址 var triggerX1Payload; var triggerX0; -var req2bufEnterAddr = baseAddr.add(0x36FC204); -var req2bufExitAddr = baseAddr.add(0x36FD3DC); -var sendFuncAddr = baseAddr.add(0x47FE448); +var req2bufEnterAddr = baseAddr.add(0x3806b30); +var req2bufExitAddr = baseAddr.add(0x3807C48); +var sendFuncAddr = baseAddr.add(0x498D2E0); var insertMsgAddr = ptr(0); var sendMsgType = ""; // 图片消息全局变量 -var imageCallbackFuncAddr = baseAddr.add(0x245D110); +var imageCallbackFuncAddr = baseAddr.add(0x251AD40); var imgProtobufAddr = imageCallbackFuncAddr.add(0x54); var patchImgProtobufFunc1 = imageCallbackFuncAddr.add(0x10); var patchImgProtobufFunc1Byte; @@ -118,7 +125,7 @@ var imgProtobufDeleteAddr = imageCallbackFuncAddr.add(0x6c); var imgProtobufDeleteAddrByte; // 视频消息全局变量 -var videoCallbackFuncAddr = baseAddr.add(0x24C76C8); +var videoCallbackFuncAddr = baseAddr.add(0x2587FD0); var videoProtobufAddr = videoCallbackFuncAddr.add(0x54); var patchVideoProtobufFunc1 = videoCallbackFuncAddr.add(0x10); var patchVideoProtobufFunc1Byte; @@ -127,10 +134,10 @@ var patchVideoProtobufFunc2Byte; var videoProtobufDeleteAddr = videoCallbackFuncAddr.add(0x6c); var videoProtobufDeleteAddrByte; -var uploadImageAddr = baseAddr.add(0x49459C8); -var CndOnCompleteAddr = baseAddr.add(0x36BAFC0); -var imgMessageCallbackFunc1 = baseAddr.add(0x8790DD8); -var videoMessageCallbackFunc1 = baseAddr.add(0x8793B78); +var uploadImageAddr = baseAddr.add(0x4ad4860); +var cndOnCompleteAddr = baseAddr.add(0x37C40E0); +var imgMessageCallbackFunc1 = baseAddr.add(0x89159F0); +var videoMessageCallbackFunc1 = baseAddr.add(0x8918790); var uploadImageX1 = ptr(0); var imgCgiAddr = ptr(0); @@ -1159,7 +1166,7 @@ function attachUploadMedia() { setImmediate(attachUploadMedia); function patchCdnOnComplete() { - Interceptor.attach(CndOnCompleteAddr, { + Interceptor.attach(cndOnCompleteAddr, { onEnter: function (args) { try { @@ -1235,7 +1242,7 @@ function patchCdnOnComplete() { setImmediate(patchCdnOnComplete) function attachGetCallbackFromWrapper() { - Interceptor.attach(baseAddr.add(0x491348C), { + Interceptor.attach(baseAddr.add(0x4aa2324), { onEnter: function (args) { const tmpFileId = this.context.x1.readPointer().readUtf8String(); const imageFileId = imageIdAddr.readUtf8String(); @@ -1245,13 +1252,13 @@ function attachGetCallbackFromWrapper() { return } - uploadCallback.add(0x10).writePointer(baseAddr.add(0x36BA7DC)); + uploadCallback.add(0x10).writePointer(baseAddr.add(0x37C392C)); this.context.x8 = uploadCallback; console.log("[+] GetCallbackFromWrapper x8: " + this.context.x8); } }) - Interceptor.attach(baseAddr.add(0x4913A88), { + Interceptor.attach(baseAddr.add(0x4AA2920), { onEnter: function (args) { const tmpFileId = this.context.x1.readPointer().readUtf8String(); const imageFileId = imageIdAddr.readUtf8String(); @@ -1261,7 +1268,7 @@ function attachGetCallbackFromWrapper() { return } - uploadCallback.add(0x30).writePointer(baseAddr.add(0x36BB9C0)); + uploadCallback.add(0x30).writePointer(baseAddr.add(0x37C4B10)); this.context.x8 = uploadCallback; console.log("[+] OnComplete x8: " + this.context.x8); } diff --git a/frida/init.js b/frida/init.js new file mode 100644 index 0000000..bca1eab --- /dev/null +++ b/frida/init.js @@ -0,0 +1,7 @@ +var baseAddr = ptr(0); +Process.enumerateModules().forEach(function (m) { + if (m.name.toLowerCase().includes("wechat.dylib")) { + baseAddr = m.base; + } +}); +console.log("Base address of wechat.dylib: " + baseAddr); \ No newline at end of file diff --git a/frida/receiver.js b/frida/receiver.js index 97ab2b6..e074258 100644 --- a/frida/receiver.js +++ b/frida/receiver.js @@ -1,13 +1,20 @@ -var baseAddr = Process.getModuleByName("WeChat").base; -if (!baseAddr) { - console.error("[!] 找不到 WeChat 模块基址,请检查进程名。"); -} +var baseAddr = ptr(0); +Process.enumerateModules().forEach(function (m) { + if (m.name.toLowerCase().includes("wechat.dylib")) { + baseAddr = m.base; + } +}); -var buf2RespAddr = baseAddr.add(0x3721FA0) -var downloadImagAddr = baseAddr.add(0x4A6975C) -var startDownloadMedia = baseAddr.add(0x494663C) -var downloadFileAddr = baseAddr.add(0x4A084EC) -var downloadVideoAddr = baseAddr.add(0x4A2A268) +if (baseAddr === 0x0) { + console.log("wechat.dylib not found"); +} +console.log("baseAddr: " + baseAddr); + +var buf2RespAddr = baseAddr.add(0x382c1b4) +var downloadImagAddr = baseAddr.add(0x4bf8608) // image_download +var downloadFileAddr = baseAddr.add(0x4B97384) // c2c_download +var downloadVideoAddr = baseAddr.add(0x4bb9100) // hdvideo_streaming +var startDownloadMedia = baseAddr.add(0x4AD54D4) var downloadGlobalX0; var downloadFileX1 = ptr(0) @@ -156,12 +163,7 @@ function setReceiver() { var buffer = dataPtr.readByteArray(dataLen); var uint8Array = new Uint8Array(buffer); - send({ - type: "download", - media: Array.from(uint8Array), - file_id: fileId, - cdn_url: cdnUrl, - }) + console.log("file", uint8Array.length, fileId, cdnUrl) } } }); @@ -177,12 +179,7 @@ function setReceiver() { var buffer = dataPtr.readByteArray(dataLen); var uint8Array = new Uint8Array(buffer); - send({ - type: "download", - media: Array.from(uint8Array), - file_id: fileId, - cdn_url: cdnUrl, - }) + console.log("image", uint8Array.length, fileId, cdnUrl) } } }); @@ -197,6 +194,7 @@ function setReceiver() { if (dataLen > 0) { var buffer = dataPtr.readByteArray(dataLen); var uint8Array = new Uint8Array(buffer); + console.log("video", uint8Array.length, fileId, cdnUrl) // send({ // type: "download", diff --git a/frida/text.js b/frida/text.js index c1f1513..d203e0b 100644 --- a/frida/text.js +++ b/frida/text.js @@ -1,9 +1,15 @@ -// 1. 获取微信主模块的基地址 -var baseAddr = Process.getModuleByName("WeChat").base; -if (!baseAddr) { - console.error("[!] 找不到 WeChat 模块基址,请检查进程名。"); +var baseAddr = ptr(0); +Process.enumerateModules().forEach(function (m) { + if (m.name.toLowerCase().includes("wechat.dylib")) { + baseAddr = m.base; + } +}); + +if (baseAddr === 0x0) { + console.log("wechat.dylib not found"); } -console.log("[+] WeChat base address: " + baseAddr); +console.log("baseAddr: " + baseAddr); + // -------------------------基础函数分区------------------------- function toVarint(n) { @@ -78,7 +84,7 @@ function patchString(addr, plainStr) { // -------------------------全局变量分区------------------------- // 文本消息全局变量 -var textCallbackFuncAddr = baseAddr.add(0x246655C); +var textCallbackFuncAddr = baseAddr.add(0x2524140); var protobufAddr = textCallbackFuncAddr.add(0x44); var patchTextProtobufAddr = textCallbackFuncAddr.add(0x20); var patchTextProtobufByte @@ -90,15 +96,15 @@ var sendTextMessageAddr = ptr(0); var textMessageAddr = ptr(0); var textProtoX1PayloadAddr = ptr(0); var sendMessageCallbackFunc = ptr(0); -var messageCallbackFunc1 = baseAddr.add(0x8791310); +var messageCallbackFunc1 = baseAddr.add(0x8915F28); // 双方公共使用的地址 var triggerX1Payload; var triggerX0; -var req2bufEnterAddr = baseAddr.add(0x36FC204); -var req2bufExitAddr = baseAddr.add(0x36FD3DC); -var sendFuncAddr = baseAddr.add(0x47FE448); +var req2bufEnterAddr = baseAddr.add(0x3806b30); +var req2bufExitAddr = baseAddr.add(0x3807C48); +var sendFuncAddr = baseAddr.add(0x498D2E0); var insertMsgAddr = ptr(0); var sendMsgType = ""; diff --git a/onebot/Makefile b/onebot/Makefile new file mode 100644 index 0000000..5e660fa --- /dev/null +++ b/onebot/Makefile @@ -0,0 +1,17 @@ +.PHONY: build run clean pprof + +# 设置 CC 为系统自带的 gcc (macOS) +export CC = /usr/bin/gcc + +build: + @echo "编译 onebot..." + @go env -w CC=/usr/bin/gcc 2>/dev/null || true + @go build -o onebot . + +run: build + @echo "启动 onebot..." + @./onebot + +clean: + @echo "清理构建文件..." + @rm -f onebot/onebot diff --git a/onebot/main.go b/onebot/main.go index b35470c..1922882 100644 --- a/onebot/main.go +++ b/onebot/main.go @@ -6,13 +6,14 @@ import ( "flag" "fmt" "net/http" + _ "net/http/pprof" "os" "os/signal" "runtime/debug" "strings" "syscall" "text/template" - + "github.com/frida/frida-go/frida" ) @@ -111,6 +112,7 @@ func initFrida() { Fatal("未发现正在运行的微信进程") } Info("微信进程 PID", "PID", pid) + MonitorProcessExit(pid) session, err = device.Attach(pid, nil) if err != nil { diff --git a/onebot/param.go b/onebot/param.go index 53dfb44..d97f7cf 100644 --- a/onebot/param.go +++ b/onebot/param.go @@ -15,8 +15,8 @@ var ( taskId = int64(0x20000000) myWechatId = "" - msgChan = make(chan *SendMsg, 10) - finishChan = make(chan struct{}) + msgChan = make(chan *SendMsg, 100) + finishChan = make(chan struct{}, 100) config = &Config{} diff --git a/onebot/utils.go b/onebot/utils.go index dff38d7..404c099 100644 --- a/onebot/utils.go +++ b/onebot/utils.go @@ -17,8 +17,9 @@ import ( "path/filepath" "strconv" "strings" + "syscall" "time" - + "github.com/wdvxdr1123/go-silk" ) @@ -349,7 +350,35 @@ func DetectAndSaveImage(data []byte) (string, error) { if ext == "unknown" { return "", fmt.Errorf("无法识别的图片格式") } - + // 调用保存函数 return SaveImageToFile(ext, data) } + +// MonitorProcessExit 监控指定 PID 的进程是否退出 +// 如果进程退出,则发送 SIGTERM 停止当前进程 +func MonitorProcessExit(pid int) { + go func() { + ticker := time.NewTicker(2 * time.Second) + defer ticker.Stop() + + for range ticker.C { + proc, err := os.FindProcess(pid) + if err != nil { + // 进程不存在,发送 SIGTERM 退出 + Info("监控的进程已退出,正在停止当前服务...") + syscall.Kill(syscall.Getpid(), syscall.SIGTERM) + return + } + + // 检查进程是否存活 + err = proc.Signal(syscall.Signal(0)) + if err != nil { + // 进程不存在或已退出,发送 SIGTERM 退出 + Info("监控的进程已退出,正在停止当前服务...") + syscall.Kill(syscall.Getpid(), syscall.SIGTERM) + return + } + } + }() +} diff --git a/onebot/worker.go b/onebot/worker.go index b0daf9f..81f2fd2 100644 --- a/onebot/worker.go +++ b/onebot/worker.go @@ -36,7 +36,7 @@ func SendWorker() { func SendWechatMsg(m *SendMsg) { time.Sleep(time.Duration(config.SendInterval) * time.Millisecond) currTaskId := atomic.AddInt64(&taskId, 1) - Info("📩 收到任务", "task_id", currTaskId, "", m.) + Info("📩 收到任务", "task_id", currTaskId, "type", m.Type) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() diff --git a/wechat_version/4_1_8_28_mac.json b/wechat_version/4_1_8_28_mac.json new file mode 100644 index 0000000..68f6646 --- /dev/null +++ b/wechat_version/4_1_8_28_mac.json @@ -0,0 +1,22 @@ +{ + "textCallbackFuncAddr": "", + "sendMessageCallbackFunc": "", + "sendFuncAddr": "", + "req2bufEnterAddr": "", + "req2bufExitAddr": "", + "imageCallbackFuncAddr": "", + "imgMessageCallbackFunc": "", + "uploadImageAddr": "", + "cndOnCompleteAddr": "", + "buf2RespAddr": "", + "uploadGetCallbackWrapperAddr": "", + "uploadGetCallbackWrapperFuncAddr": "", + "uploadOnCompleteAddr": "", + "uploadOnCompleteFuncAddr": "", + "downloadImagAddr": "", + "startDownloadMedia": "", + "downloadFileAddr": "", + "downloadVideoAddr": "", + "videoCallbackFuncAddr": "", + "videoMessageCallbackFunc": "" +} \ No newline at end of file