receive video

This commit is contained in:
yincong
2026-03-13 13:29:10 +08:00
parent eb4c1c2e0e
commit 42a10fb306
7 changed files with 135 additions and 18 deletions
+34 -7
View File
@@ -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("<msg><emoji")) {
} else if (part.startsWith("<msg><emoji")) {
messages.push({type: "face", data: {text: part}});
} else if (part.startsWith("<?xml version=\"1.0\"?><msg><videomsg")) {
messages.push({type: "video", data: {text: part}});
} else {
messages.push({type: "text", data: {text: part}});
}
@@ -258,6 +283,8 @@ function getMessages(content, sender, mediaContent) {
}
} else if (content.startsWith("<msg><emoji")) {
messages.push({type: "face", data: {text: content}});
} else if (content.startsWith("<?xml version=\"1.0\"?><msg><videomsg")) {
messages.push({type: "video", data: {text: content}});
} else {
messages.push({type: "text", data: {text: content}});
}
+6 -1
View File
@@ -19,7 +19,12 @@ func Download(rawMsg []byte) error {
if beforeDownloadReq.FilePath != "" {
return nil
}
beforeDownloadReq.Media = append(beforeDownloadReq.Media, downloadReq.Media...)
if time.Now().UnixMilli()-beforeDownloadReq.LastAppendTime > 10000000 {
beforeDownloadReq.Media = downloadReq.Media
} else {
beforeDownloadReq.Media = append(beforeDownloadReq.Media, downloadReq.Media...)
}
beforeDownloadReq.LastAppendTime = time.Now().UnixMilli()
} else {
downloadReq.LastAppendTime = time.Now().UnixMilli()
+24
View File
@@ -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"` // 是否为广告
}
+34 -7
View File
@@ -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("<msg><emoji")) {
} else if (part.startsWith("<msg><emoji")) {
messages.push({type: "face", data: {text: part}});
} else if (part.startsWith("<?xml version=\"1.0\"?><msg><videomsg")) {
messages.push({type: "video", data: {text: part}});
} else {
messages.push({type: "text", data: {text: part}});
}
@@ -1580,6 +1605,8 @@ function getMessages(content, sender, mediaContent) {
}
} else if (content.startsWith("<msg><emoji")) {
messages.push({type: "face", data: {text: content}});
} else if (content.startsWith("<?xml version=\"1.0\"?><msg><videomsg")) {
messages.push({type: "video", data: {text: content}});
} else {
messages.push({type: "text", data: {text: content}});
}
+19
View File
@@ -151,6 +151,23 @@ func DetectFileFormat(data []byte) string {
}
switch {
// 视频格式
case bytes.HasPrefix(data, []byte{0x00, 0x00, 0x00}): // MP4/MOV 通常以 ftyp 开头,后面是具体类型
if len(data) > 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+ 文件具体类型
+16 -2
View File
@@ -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
}
+2 -1
View File
@@ -15,5 +15,6 @@
"uploadOnCompleteFuncAddr": "0x36BB9C0",
"downloadImagAddr": "0x4A6975C",
"startDownloadMedia": "0x494663C",
"downloadFileAddr": "0x4A084EC"
"downloadFileAddr": "0x4A084EC",
"downloadVideoAddr": "0x4A2A268"
}