From 42a10fb3065fbe7cf4ba1bee6562b117dec97805 Mon Sep 17 00:00:00 2001 From: yincong Date: Fri, 13 Mar 2026 13:29:10 +0800 Subject: [PATCH] receive video --- frida/receiver.js | 41 ++++++++++++++++++++++++++------ onebot/msg.go | 7 +++++- onebot/param.go | 24 +++++++++++++++++++ onebot/script.js | 41 ++++++++++++++++++++++++++------ onebot/utils.go | 19 +++++++++++++++ onebot/worker.go | 18 ++++++++++++-- wechat_version/4_1_7_57_mac.json | 3 ++- 7 files changed, 135 insertions(+), 18 deletions(-) diff --git a/frida/receiver.js b/frida/receiver.js index b1ddc4a..a89119a 100644 --- a/frida/receiver.js +++ b/frida/receiver.js @@ -7,6 +7,7 @@ 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) var downloadGlobalX0; var downloadFileX1 = ptr(0) @@ -144,14 +145,15 @@ function setReceiver() { downloadGlobalX0 = this.context.x0; var fileIDAddr = this.context.x1.add(0x40).readPointer(); var fileId = fileIDAddr?.readUtf8String(); - if (!fileId.endsWith("_1")) { - return - } - - console.log(" [+] download file: ", fileId); const t = this.context.x1.add(0xA0).readU32() + console.log(" [+] download file: ", fileId, " type", t); if (t === 3) { - this.context.x1.add(0xA0).writeU32(0x02); + if (fileId.endsWith("_1")) { + this.context.x1.add(0xA0).writeU32(0x02); + } + if (fileId.endsWith("_31")) { + this.context.x1.add(0xA0).writeU32(0x04); + } } } }) @@ -197,6 +199,27 @@ function setReceiver() { } } }); + + Interceptor.attach(downloadVideoAddr, { + onEnter: function (args) { + var dataPtr = this.context.x1; + var dataLen = this.context.x2.toInt32(); + var fileId = this.context.x22.add(0x40).readPointer().readUtf8String(); + var cdnUrl = this.context.x22.add(0x58).readPointer().readUtf8String(); + + if (dataLen > 0) { + var buffer = dataPtr.readByteArray(dataLen); + var uint8Array = new Uint8Array(buffer); + + send({ + type: "download", + media: Array.from(uint8Array), + file_id: fileId, + cdn_url: cdnUrl, + }) + } + } + }); } @@ -228,8 +251,10 @@ function getMessages(content, sender, mediaContent) { break } } - } else if (content.startsWith(" 10000000 { + beforeDownloadReq.Media = downloadReq.Media + } else { + beforeDownloadReq.Media = append(beforeDownloadReq.Media, downloadReq.Media...) + } + beforeDownloadReq.LastAppendTime = time.Now().UnixMilli() } else { downloadReq.LastAppendTime = time.Now().UnixMilli() diff --git a/onebot/param.go b/onebot/param.go index 569ce8d..fa4c696 100644 --- a/onebot/param.go +++ b/onebot/param.go @@ -118,6 +118,7 @@ type FileMsg struct { XMLName xml.Name `xml:"msg"` Image Image `xml:"img"` Emoji Emoji `xml:"emoji"` + Video VideoMsg `xml:"videomsg"` GameExt GameExt `xml:"gameext"` AppMsg AppMsg `xml:"appmsg"` ExtCommonInfo ExtCommonInfo `xml:"extcommoninfo"` @@ -240,3 +241,26 @@ type AppInfo struct { Version string `xml:"version"` AppName string `xml:"appname"` } + +type VideoMsg struct { + AesKey string `xml:"aeskey,attr"` // 视频解密 Key + CdnVideoUrl string `xml:"cdnvideourl,attr"` // 视频 CDN 地址 + CdnThumbAesKey string `xml:"cdnthumbaeskey,attr"` // 缩略图解密 Key + CdnThumbUrl string `xml:"cdnthumburl,attr"` // 缩略图 CDN 地址 + Length int64 `xml:"length,attr"` // 视频文件大小 (字节) + PlayLength int `xml:"playlength,attr"` // 播放时长 (秒) + CdnThumbLength int `xml:"cdnthumblength,attr"` // 缩略图大小 + CdnThumbWidth int `xml:"cdnthumbwidth,attr"` // 缩略图宽度 + CdnThumbHeight int `xml:"cdnthumbheight,attr"` // 缩略图高度 + FromUsername string `xml:"fromusername,attr"` // 发送者 ID + Md5 string `xml:"md5,attr"` // 视频 MD5 + NewMd5 string `xml:"newmd5,attr"` // 新版 MD5 + IsPlaceholder int `xml:"isplaceholder,attr"` // 是否占位符 + RawMd5 string `xml:"rawmd5,attr"` // 原始 MD5 + RawLength int64 `xml:"rawlength,attr"` // 原始长度 + CdnRawVideoUrl string `xml:"cdnrawvideourl,attr"` // 原始视频 CDN 地址 + CdnRawVideoAesKey string `xml:"cdnrawvideoaeskey,attr"` // 原始视频解密 Key + OverwriteNewMsgId int64 `xml:"overwritenewmsgid,attr"` // 覆盖消息 ID + OriginSourceMd5 string `xml:"originsourcemd5,attr"` // 原始源 MD5 + IsAd int `xml:"isad,attr"` // 是否为广告 +} diff --git a/onebot/script.js b/onebot/script.js index ab19f4e..288e9d0 100644 --- a/onebot/script.js +++ b/onebot/script.js @@ -301,6 +301,7 @@ var uploadOnCompleteFuncAddr = baseAddr.add({{.uploadOnCompleteFuncAddr}}); var downloadImagAddr = baseAddr.add({{.downloadImagAddr}}); var startDownloadMedia = baseAddr.add({{.startDownloadMedia}}) var downloadFileAddr = baseAddr.add({{.downloadFileAddr}}) +var downloadVideoAddr = baseAddr.add({{.downloadVideoAddr}}) var downloadGlobalX0; var downloadFileX1 = ptr(0) @@ -1313,14 +1314,15 @@ function setReceiver() { downloadGlobalX0 = this.context.x0; var fileIDAddr = this.context.x1.add(0x40).readPointer(); var fileId = fileIDAddr?.readUtf8String(); - if (!fileId.endsWith("_1")) { - return - } - - console.log(" [+] download file: ", fileId); const t = this.context.x1.add(0xA0).readU32() + console.log(" [+] download file: ", fileId, " type", t); if (t === 3) { - this.context.x1.add(0xA0).writeU32(0x02); + if (fileId.endsWith("_1")) { + this.context.x1.add(0xA0).writeU32(0x02); + } + if (fileId.endsWith("_31")) { + this.context.x1.add(0xA0).writeU32(0x04); + } } } }) @@ -1366,6 +1368,27 @@ function setReceiver() { } } }); + + Interceptor.attach(downloadVideoAddr, { + onEnter: function (args) { + var dataPtr = this.context.x1; + var dataLen = this.context.x2.toInt32(); + var fileId = this.context.x22.add(0x40).readPointer().readUtf8String(); + var cdnUrl = this.context.x22.add(0x58).readPointer().readUtf8String(); + + if (dataLen > 0) { + var buffer = dataPtr.readByteArray(dataLen); + var uint8Array = new Uint8Array(buffer); + + send({ + type: "download", + media: Array.from(uint8Array), + file_id: fileId, + cdn_url: cdnUrl, + }) + } + } + }); } setImmediate(setReceiver) @@ -1550,8 +1573,10 @@ function getMessages(content, sender, mediaContent) { break } } - } else if (content.startsWith(" 4 { + switch string(data[4:8]) { + case "ftyp", "moov", "mdat", "wide", "free": + return "mp4" + case "isom", "mp41", "mp42", "M4V ", "M4A ", "M4P ": + return "mp4" + } + } + case bytes.HasPrefix(data, []byte("FLV\x01")): // FLV + return "flv" + case bytes.HasPrefix(data, []byte{0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11}): // ASF/WMV/WMA + if len(data) > 8 && bytes.HasPrefix(data[8:], []byte{0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}) { + return "wmv" + } + // 图片格式 case bytes.HasPrefix(data, []byte{0xFF, 0xD8, 0xFF}): return "jpg" @@ -184,6 +201,8 @@ func DetectFileFormat(data []byte) string { default: return "unknown" } + + return "unknown" } // detectOfficeFormat 检测 Office 2007+ 文件具体类型 diff --git a/onebot/worker.go b/onebot/worker.go index 2acbf08..2e2ef06 100644 --- a/onebot/worker.go +++ b/onebot/worker.go @@ -130,6 +130,20 @@ func HandleMsg(jsonData []byte) ([]byte, error) { return nil, err } + msg.Data.URL = "file://" + path + case "video": + var fileMsg FileMsg + err = xml.Unmarshal([]byte(msg.Data.Text), &fileMsg) + if err != nil { + Error("XML解析失败", "err", err) + return nil, err + } + path, err := GetDownloadPath(fileMsg.Video.CdnVideoUrl, fileMsg.Video.AesKey) + if err != nil { + Error("获取文件路径失败", "err", err) + return nil, err + } + msg.Data.URL = "file://" + path case "face": var fileMsg FileMsg @@ -158,7 +172,7 @@ func HandleMsg(jsonData []byte) ([]byte, error) { } func GetDownloadPath(cdnUrl, aesKeyStr string) (string, error) { - for i := 0; i < 5; i++ { + for i := 0; i < 10; i++ { if downloadMsgInter, ok := userID2FileMsgMap.Load(cdnUrl); ok { downloadReq := downloadMsgInter.(*DownloadRequest) if downloadReq.FilePath != "" { @@ -170,7 +184,7 @@ func GetDownloadPath(cdnUrl, aesKeyStr string) (string, error) { Info("文件等待下载", "url", cdnUrl, "times", i, "last_append_time", timeSinceLastAppend) // 如果数据仍在接收中(1秒内有新数据),继续等待 - if timeSinceLastAppend < 1000 && i < 4 { + if timeSinceLastAppend < 1000 && i < 9 { time.Sleep(2 * time.Second) continue } diff --git a/wechat_version/4_1_7_57_mac.json b/wechat_version/4_1_7_57_mac.json index 64b6b5a..3c20401 100644 --- a/wechat_version/4_1_7_57_mac.json +++ b/wechat_version/4_1_7_57_mac.json @@ -15,5 +15,6 @@ "uploadOnCompleteFuncAddr": "0x36BB9C0", "downloadImagAddr": "0x4A6975C", "startDownloadMedia": "0x494663C", - "downloadFileAddr": "0x4A084EC" + "downloadFileAddr": "0x4A084EC", + "downloadVideoAddr": "0x4A2A268" } \ No newline at end of file