mirror of
https://github.com/yincongcyincong/wechat_chatter.git
synced 2026-07-15 10:26:52 +08:00
fix bug
This commit is contained in:
+1
-1
@@ -61,7 +61,7 @@ func initFlag() {
|
||||
flag.StringVar(&config.FridaGadgetAddr, "gadget_addr", "127.0.0.1:27042", "Gadget 地址: 127.0.0.1:27042 仅当 type 为 gadget 时有效")
|
||||
flag.StringVar(&config.OnebotToken, "token", "MuseBot", "OneBot Token: MuseBot")
|
||||
flag.StringVar(&config.ImagePath, "image_path", "", "图片路径: /Users/xxx/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/xxx/temp/xxx/2026-01/Img/")
|
||||
flag.StringVar(&config.WechatConf, "wechat_conf", "../wechat_version/4_1_10_53_mac.json", "微信配置文件路径: ../wechat_version/4_1_6_12_mac.json")
|
||||
flag.StringVar(&config.WechatConf, "wechat_conf", "../wechat_version/4_1_11_53_mac.json", "微信配置文件路径: ../wechat_version/4_1_6_12_mac.json")
|
||||
flag.StringVar(&config.ConnType, "conn_type", "http", "连接类型: http | websocket")
|
||||
flag.IntVar(&config.SendInterval, "send_interval", 1000, "发送间隔: ms")
|
||||
flag.IntVar(&config.WechatPid, "wechat_pid", 0, "微信进程 PID,不设置则自动查找")
|
||||
|
||||
+30
-18
@@ -11,7 +11,10 @@ func cleanExpiredDownloads() {
|
||||
now := time.Now().UnixMilli()
|
||||
userID2FileMsgMap.Range(func(key, value any) bool {
|
||||
req := value.(*DownloadRequest)
|
||||
if now-req.LastAppendTime > 24*60*60*1000 {
|
||||
req.mu.Lock()
|
||||
expired := now-req.LastAppendTime > 24*60*60*1000
|
||||
req.mu.Unlock()
|
||||
if expired {
|
||||
userID2FileMsgMap.Delete(key)
|
||||
}
|
||||
return true
|
||||
@@ -26,24 +29,33 @@ func Download(rawMsg []byte) error {
|
||||
Error("JSON解析失败", "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Info("下载文件", "file_id", downloadReq.FileID, "media_len", len(downloadReq.Media), "cdn_url", downloadReq.CDNURL)
|
||||
if downloadReqInter, ok := userID2FileMsgMap.Load(downloadReq.CDNURL); ok {
|
||||
beforeDownloadReq := downloadReqInter.(*DownloadRequest)
|
||||
if beforeDownloadReq.FilePath != "" {
|
||||
return nil
|
||||
}
|
||||
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()
|
||||
userID2FileMsgMap.Store(downloadReq.CDNURL, downloadReq)
|
||||
|
||||
// LoadOrStore 保证同一 CDNURL 只建一份,消除 Load+Store 之间的竞态。
|
||||
// 首个分片直接作为占位存入,后续分片走 append 分支。
|
||||
actual, loaded := userID2FileMsgMap.LoadOrStore(downloadReq.CDNURL, downloadReq)
|
||||
req := actual.(*DownloadRequest)
|
||||
|
||||
req.mu.Lock()
|
||||
defer req.mu.Unlock()
|
||||
|
||||
if !loaded {
|
||||
// 本次就是首个分片(downloadReq 已被存入),只更新时间戳
|
||||
req.LastAppendTime = time.Now().UnixMilli()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
if req.FilePath != "" {
|
||||
return nil
|
||||
}
|
||||
if time.Now().UnixMilli()-req.LastAppendTime > 10000000 {
|
||||
// 距上次追加过久,视为新文件,重置数据
|
||||
req.Media = append(req.Media[:0], downloadReq.Media...)
|
||||
} else {
|
||||
req.Media = append(req.Media, downloadReq.Media...)
|
||||
}
|
||||
req.LastAppendTime = time.Now().UnixMilli()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -211,6 +211,8 @@ type DownloadRequest struct {
|
||||
CDNURL string `json:"cdn_url"`
|
||||
LastAppendTime int64 `json:"last_append_time"`
|
||||
FilePath string `json:"file_path"`
|
||||
|
||||
mu sync.Mutex `json:"-"` // 保护 Media/LastAppendTime/FilePath 的并发读写
|
||||
}
|
||||
|
||||
type ScriptMessage struct {
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 把你抓到的 hexdump 直接粘到这里(支持地址列 + hex + ASCII 的完整格式)。
|
||||
const dumpInput = `
|
||||
0000000000000000 08 00 12 AD 10 08 02 12 A7 0D 08 05 12 A2 0D 08 ................
|
||||
0000000000000010 9C 0D 12 9C 0D 08 ED EA 92 D5 01 12 15 0A 13 77 ...............w
|
||||
0000000000000020 78 69 64 5F 6C 64 66 74 75 68 65 33 36 69 7A 67 xid_ldftuhe36izg
|
||||
0000000000000030 31 39 1A 16 0A 14 34 35 38 37 32 30 32 35 33 38 19....4587202538
|
||||
0000000000000040 34 40 63 68 61 74 72 6F 6F 6D 20 03 2A A0 0A 0A 4@chatroom .*...
|
||||
0000000000000050 9D 0A 3C 3F 78 6D 6C 20 76 65 72 73 69 6F 6E 3D ..<?xml version=
|
||||
0000000000000060 22 31 2E 30 22 3F 3E 0A 3C 6D 73 67 3E 0A 09 3C "1.0"?>.<msg>..<
|
||||
0000000000000070 69 6D 67 20 61 65 73 6B 65 79 3D 22 39 61 34 39 img aeskey="9a49
|
||||
0000000000000080 36 38 32 32 61 31 62 35 35 30 65 66 34 62 39 65 6822a1b550ef4b9e
|
||||
0000000000000090 63 64 31 62 30 36 31 30 34 35 38 66 22 20 65 6E cd1b0610458f" en
|
||||
00000000000000A0 63 72 79 76 65 72 3D 22 31 22 20 63 64 6E 74 68 cryver="1" cdnth
|
||||
00000000000000B0 75 6D 62 61 65 73 6B 65 79 3D 22 39 61 34 39 36 umbaeskey="9a496
|
||||
00000000000000C0 38 32 32 61 31 62 35 35 30 65 66 34 62 39 65 63 822a1b550ef4b9ec
|
||||
00000000000000D0 64 31 62 30 36 31 30 34 35 38 66 22 20 63 64 6E d1b0610458f" cdn
|
||||
00000000000000E0 74 68 75 6D 62 75 72 6C 3D 22 33 30 35 66 30 32 thumburl="305f02
|
||||
00000000000000F0 30 31 30 30 30 34 34 62 33 30 34 39 30 32 30 31 0100044b30490201
|
||||
0000000000000100 30 30 30 32 30 34 66 36 35 63 39 63 65 30 30 32 000204f65c9ce002
|
||||
0000000000000110 30 33 32 66 35 63 33 39 30 32 30 34 32 33 30 30 032f5c3902042300
|
||||
0000000000000120 30 63 37 39 30 32 30 34 36 61 34 66 34 64 63 66 0c7902046a4f4dcf
|
||||
0000000000000130 30 34 32 34 33 34 36 32 36 34 36 33 33 33 33 39 0424346264633339
|
||||
0000000000000140 33 38 33 32 32 64 33 37 33 34 33 33 33 33 32 64 38322d373433332d
|
||||
0000000000000150 33 34 33 36 36 34 36 33 32 64 36 31 33 34 33 34 343664632d613434
|
||||
0000000000000160 33 30 32 64 33 33 33 32 36 35 33 36 36 32 33 37 302d333265366237
|
||||
0000000000000170 33 30 36 34 33 33 33 37 36 32 33 31 30 32 30 34 3064333762310204
|
||||
0000000000000180 30 35 31 35 30 61 30 32 30 32 30 31 30 30 30 34 05150a0202010004
|
||||
0000000000000190 30 64 30 30 34 63 35 61 65 31 30 30 30 30 30 30 0d004c5ae1000000
|
||||
00000000000001A0 30 30 30 30 30 30 30 30 30 30 30 30 22 20 63 64 000000000000" cd
|
||||
00000000000001B0 6E 74 68 75 6D 62 6C 65 6E 67 74 68 3D 22 39 38 nthumblength="98
|
||||
00000000000001C0 37 37 22 20 63 64 6E 74 68 75 6D 62 68 65 69 67 77" cdnthumbheig
|
||||
00000000000001D0 68 74 3D 22 32 31 30 22 20 63 64 6E 74 68 75 6D ht="210" cdnthum
|
||||
00000000000001E0 62 77 69 64 74 68 3D 22 31 31 38 22 20 63 64 6E bwidth="118" cdn
|
||||
00000000000001F0 6D 69 64 68 65 69 67 68 74 3D 22 30 22 20 63 64 midheight="0" cd
|
||||
0000000000000200 6E 6D 69 64 77 69 64 74 68 3D 22 30 22 20 63 64 nmidwidth="0" cd
|
||||
0000000000000210 6E 68 64 68 65 69 67 68 74 3D 22 30 22 20 63 64 nhdheight="0" cd
|
||||
0000000000000220 6E 68 64 77 69 64 74 68 3D 22 30 22 20 63 64 6E nhdwidth="0" cdn
|
||||
0000000000000230 6D 69 64 69 6D 67 75 72 6C 3D 22 33 30 35 66 30 midimgurl="305f0
|
||||
0000000000000240 32 30 31 30 30 30 34 34 62 33 30 34 39 30 32 30 20100044b3049020
|
||||
0000000000000250 31 30 30 30 32 30 34 66 36 35 63 39 63 65 30 30 1000204f65c9ce00
|
||||
0000000000000260 32 30 33 32 66 35 63 33 39 30 32 30 34 32 33 30 2032f5c390204230
|
||||
0000000000000270 30 30 63 37 39 30 32 30 34 36 61 34 66 34 64 63 00c7902046a4f4dc
|
||||
0000000000000280 66 30 34 32 34 33 34 36 32 36 34 36 33 33 33 33 f042434626463333
|
||||
0000000000000290 39 33 38 33 32 32 64 33 37 33 34 33 33 33 33 32 938322d373433332
|
||||
00000000000002A0 64 33 34 33 36 36 34 36 33 32 64 36 31 33 34 33 d343664632d61343
|
||||
00000000000002B0 34 33 30 32 64 33 33 33 32 36 35 33 36 36 32 33 4302d33326536623
|
||||
00000000000002C0 37 33 30 36 34 33 33 33 37 36 32 33 31 30 32 30 7306433376231020
|
||||
00000000000002D0 34 30 35 31 35 30 61 30 32 30 32 30 31 30 30 30 405150a020201000
|
||||
00000000000002E0 34 30 64 30 30 34 63 35 61 65 31 30 30 30 30 30 40d004c5ae100000
|
||||
00000000000002F0 30 30 30 30 30 30 30 30 30 30 30 30 30 22 20 6C 0000000000000" l
|
||||
0000000000000300 65 6E 67 74 68 3D 22 34 37 32 35 31 22 20 6D 64 ength="47251" md
|
||||
0000000000000310 35 3D 22 36 31 62 38 32 39 62 65 34 38 62 39 39 5="61b829be48b99
|
||||
0000000000000320 65 34 65 36 36 62 65 36 65 36 64 62 61 39 33 34 e4e66be6e6dba934
|
||||
0000000000000330 63 33 32 22 20 68 65 76 63 5F 6D 69 64 5F 73 69 c32" hevc_mid_si
|
||||
0000000000000340 7A 65 3D 22 34 37 32 35 31 22 20 6F 72 69 67 69 ze="47251" origi
|
||||
0000000000000350 6E 73 6F 75 72 63 65 6D 64 35 3D 22 62 63 39 31 nsourcemd5="bc91
|
||||
0000000000000360 66 37 62 34 61 39 39 64 31 37 65 36 31 66 36 32 f7b4a99d17e61f62
|
||||
0000000000000370 35 37 64 31 32 66 39 32 33 30 36 30 22 3E 0A 09 57d12f923060">..
|
||||
0000000000000380 09 3C 73 65 63 48 61 73 68 49 6E 66 6F 42 61 73 .<secHashInfoBas
|
||||
0000000000000390 65 36 34 3E 65 79 4A 77 61 47 46 7A 61 43 49 36 e64>eyJwaGFzaCI6
|
||||
00000000000003A0 49 6A 45 77 4F 54 45 32 4D 6A 45 77 4E 54 49 79 IjEwOTE2MjEwNTIy
|
||||
00000000000003B0 4D 44 45 77 4E 44 41 69 4C 43 4A 77 5A 48 46 6F MDEwNDAiLCJwZHFo
|
||||
00000000000003C0 59 58 4E 6F 49 6A 6F 69 4F 44 51 77 59 7A 55 33 YXNoIjoiODQwYzU3
|
||||
00000000000003D0 4D 47 49 33 59 54 4D 77 4D 57 45 7A 5A 57 45 32 MGI3YTMwMWEzZWE2
|
||||
00000000000003E0 4E 6A 4E 6C 4F 57 59 32 4E 54 6B 77 5A 6A 56 6D NjNlOWY2NTkwZjVm
|
||||
00000000000003F0 4F 44 68 68 4E 6D 45 7A 5A 57 45 33 4D 47 59 34 ODhhNmEzZWE3MGY4
|
||||
0000000000000400 4E 47 51 77 4E 57 4D 7A 4D 44 63 77 4D 47 59 34 NGQwNWMzMDcwMGY4
|
||||
0000000000000410 4D 44 4D 33 4F 44 4E 6D 5A 6D 5A 6D 5A 43 4A 39 MDM3ODNmZmZmZCJ9
|
||||
0000000000000420 3C 2F 73 65 63 48 61 73 68 49 6E 66 6F 42 61 73 </secHashInfoBas
|
||||
0000000000000430 65 36 34 3E 0A 09 09 3C 6C 69 76 65 3E 0A 09 09 e64>...<live>...
|
||||
0000000000000440 09 3C 64 75 72 61 74 69 6F 6E 3E 30 3C 2F 64 75 .<duration>0</du
|
||||
0000000000000450 72 61 74 69 6F 6E 3E 0A 09 09 09 3C 73 69 7A 65 ration>....<size
|
||||
0000000000000460 3E 30 3C 2F 73 69 7A 65 3E 0A 09 09 09 3C 6D 64 >0</size>....<md
|
||||
0000000000000470 35 20 2F 3E 0A 09 09 09 3C 66 69 6C 65 69 64 20 5 />....<fileid
|
||||
0000000000000480 2F 3E 0A 09 09 09 3C 68 64 73 69 7A 65 3E 30 3C />....<hdsize>0<
|
||||
0000000000000490 2F 68 64 73 69 7A 65 3E 0A 09 09 09 3C 68 64 6D /hdsize>....<hdm
|
||||
00000000000004A0 64 35 20 2F 3E 0A 09 09 09 3C 68 64 66 69 6C 65 d5 />....<hdfile
|
||||
00000000000004B0 69 64 20 2F 3E 0A 09 09 09 3C 73 74 69 6C 6C 69 id />....<stilli
|
||||
00000000000004C0 6D 61 67 65 74 69 6D 65 6D 73 3E 30 3C 2F 73 74 magetimems>0</st
|
||||
00000000000004D0 69 6C 6C 69 6D 61 67 65 74 69 6D 65 6D 73 3E 0A illimagetimems>.
|
||||
00000000000004E0 09 09 3C 2F 6C 69 76 65 3E 0A 09 3C 2F 69 6D 67 ..</live>..</img
|
||||
00000000000004F0 3E 0A 09 3C 70 6C 61 74 66 6F 72 6D 5F 73 69 67 >..<platform_sig
|
||||
0000000000000500 6E 61 74 75 72 65 20 2F 3E 0A 09 3C 69 6D 67 64 nature />..<imgd
|
||||
0000000000000510 61 74 61 68 61 73 68 20 2F 3E 0A 09 3C 49 6D 67 atahash />..<Img
|
||||
0000000000000520 53 6F 75 72 63 65 49 6E 66 6F 3E 0A 09 09 3C 49 SourceInfo>...<I
|
||||
0000000000000530 6D 67 53 6F 75 72 63 65 55 72 6C 20 2F 3E 0A 09 mgSourceUrl />..
|
||||
0000000000000540 09 3C 42 69 7A 54 79 70 65 3E 30 3C 2F 42 69 7A .<BizType>0</Biz
|
||||
0000000000000550 54 79 70 65 3E 0A 09 3C 2F 49 6D 67 53 6F 75 72 Type>..</ImgSour
|
||||
0000000000000560 63 65 49 6E 66 6F 3E 0A 3C 2F 6D 73 67 3E 0A 30 ceInfo>.</msg>.0
|
||||
0000000000000570 03 38 02 42 02 08 00 48 E4 EF BD D2 06 52 9B 02 .8.B...H.....R..
|
||||
0000000000000580 3C 6D 73 67 73 6F 75 72 63 65 3E 0A 09 3C 62 69 <msgsource>..<bi
|
||||
0000000000000590 7A 66 6C 61 67 3E 30 3C 2F 62 69 7A 66 6C 61 67 zflag>0</bizflag
|
||||
00000000000005A0 3E 0A 09 3C 73 65 63 5F 6D 73 67 5F 6E 6F 64 65 >..<sec_msg_node
|
||||
00000000000005B0 3E 0A 09 09 3C 75 75 69 64 3E 38 62 31 31 62 33 >...<uuid>8b11b3
|
||||
00000000000005C0 66 63 32 33 35 34 30 33 62 36 63 65 34 37 32 33 fc235403b6ce4723
|
||||
00000000000005D0 62 62 65 62 31 39 62 64 31 35 5F 3C 2F 75 75 69 bbeb19bd15_</uui
|
||||
00000000000005E0 64 3E 0A 09 09 3C 72 69 73 6B 2D 66 69 6C 65 2D d>...<risk-file-
|
||||
00000000000005F0 66 6C 61 67 20 2F 3E 0A 09 09 3C 72 69 73 6B 2D flag />...<risk-
|
||||
0000000000000600 66 69 6C 65 2D 6D 64 35 2D 6C 69 73 74 20 2F 3E file-md5-list />
|
||||
0000000000000610 0A 09 3C 2F 73 65 63 5F 6D 73 67 5F 6E 6F 64 65 ..</sec_msg_node
|
||||
0000000000000620 3E 0A 09 3C 73 69 67 6E 61 74 75 72 65 3E 4E 30 >..<signature>N0
|
||||
0000000000000630 5F 56 31 5F 6D 74 6F 4A 64 6D 49 35 7C 76 31 5F _V1_mtoJdmI5|v1_
|
||||
0000000000000640 6E 6B 4D 6F 79 50 6F 55 3C 2F 73 69 67 6E 61 74 nkMoyPoU</signat
|
||||
0000000000000650 75 72 65 3E 0A 09 3C 74 6D 70 5F 6E 6F 64 65 3E ure>..<tmp_node>
|
||||
0000000000000660 0A 09 09 3C 70 75 62 6C 69 73 68 65 72 2D 69 64 ...<publisher-id
|
||||
0000000000000670 3E 3C 2F 70 75 62 6C 69 73 68 65 72 2D 69 64 3E ></publisher-id>
|
||||
0000000000000680 0A 09 3C 2F 74 6D 70 5F 6E 6F 64 65 3E 0A 3C 2F ..</tmp_node>.</
|
||||
0000000000000690 6D 73 67 73 6F 75 72 63 65 3E 0A 60 FC 96 DB BC msgsource>.....
|
||||
00000000000006A0 D3 F9 C6 BB 65 68 F8 B8 AC D4 02 72 02 08 00 78 ....eh.....r...x
|
||||
00000000000006B0 00 12 FE 02 08 05 12 F9 02 08 F3 02 12 F3 02 08 ................
|
||||
00000000000006C0 8E F9 82 DC 06 12 15 0A 13 77 78 69 64 5F 6C 64 .........wxid_ld
|
||||
00000000000006D0 66 74 75 68 65 33 36 69 7A 67 31 39 1A 16 0A 14 ftuhe36izg19....
|
||||
00000000000006E0 34 35 38 37 32 30 32 35 33 38 34 40 63 68 61 74 45872025384@chat
|
||||
00000000000006F0 72 6F 6F 6D 20 33 2A 9D 01 0A 9A 01 3C 6D 73 67 room 3*.....<msg
|
||||
0000000000000700 3E 0A 3C 6F 70 20 69 64 3D 27 31 27 3E 0A 3C 75 >.<op id='1'>.<u
|
||||
0000000000000710 73 65 72 6E 61 6D 65 3E 34 35 38 37 32 30 32 35 sername>45872025
|
||||
0000000000000720 33 38 34 40 63 68 61 74 72 6F 6F 6D 3C 2F 75 73 384@chatroom</us
|
||||
0000000000000730 65 72 6E 61 6D 65 3E 0A 3C 6E 61 6D 65 3E 6C 61 ername>.<name>la
|
||||
0000000000000740 73 74 4D 65 73 73 61 67 65 3C 2F 6E 61 6D 65 3E stMessage</name>
|
||||
0000000000000750 0A 3C 61 72 67 3E 7B 22 6D 65 73 73 61 67 65 53 .<arg>{"messageS
|
||||
0000000000000760 76 72 49 64 22 3A 30 2C 22 4D 73 67 43 72 65 61 vrId":0,"MsgCrea
|
||||
0000000000000770 74 65 54 69 6D 65 22 3A 31 37 38 33 35 39 32 39 teTime":17835929
|
||||
0000000000000780 33 32 7D 3C 2F 61 72 67 3E 0A 3C 2F 6F 70 3E 0A 32}</arg>.</op>.
|
||||
0000000000000790 3C 2F 6D 73 67 3E 30 03 38 01 42 02 08 00 48 E5 </msg>0.8.B...H.
|
||||
00000000000007A0 EF BD D2 06 52 76 3C 6D 73 67 73 6F 75 72 63 65 ....Rv<msgsource
|
||||
00000000000007B0 3E 0A 09 3C 73 69 67 6E 61 74 75 72 65 3E 76 31 >..<signature>v1
|
||||
00000000000007C0 5F 71 38 43 30 51 43 64 78 3C 2F 73 69 67 6E 61 _q8C0QCdx</signa
|
||||
00000000000007D0 74 75 72 65 3E 0A 09 3C 74 6D 70 5F 6E 6F 64 65 ture>..<tmp_node
|
||||
00000000000007E0 3E 0A 09 09 3C 70 75 62 6C 69 73 68 65 72 2D 69 >...<publisher-i
|
||||
00000000000007F0 64 3E 3C 2F 70 75 62 6C 69 73 68 65 72 2D 69 64 d></publisher-id
|
||||
0000000000000800 3E 0A 09 3C 2F 74 6D 70 5F 6E 6F 64 65 3E 0A 3C >..</tmp_node>.<
|
||||
0000000000000810 2F 6D 73 67 73 6F 75 72 63 65 3E 0A 60 E2 BB AE /msgsource>....
|
||||
0000000000000820 8D D0 B7 A7 ED 1A 68 F9 B8 AC D4 02 72 02 08 00 ......h.....r...
|
||||
0000000000000830 78 00 18 00 22 EC 02 08 E6 02 12 E6 02 08 2F 12 x..."........./.
|
||||
0000000000000840 08 08 01 10 B6 9F FA 8D 03 12 04 08 65 10 00 12 ............e...
|
||||
0000000000000850 08 08 02 10 F9 B8 AC D4 02 12 04 08 66 10 00 12 ............f...
|
||||
0000000000000860 04 08 0D 10 00 12 05 08 CC 01 10 00 12 08 08 03 ................
|
||||
0000000000000870 10 8E 91 FA 8D 03 12 04 08 67 10 00 12 08 08 0B .........g......
|
||||
0000000000000880 10 F2 9D FA 8D 03 12 04 08 6F 10 00 12 04 08 04 .........o......
|
||||
0000000000000890 10 00 12 04 08 68 10 00 12 04 08 05 10 00 12 04 .....h..........
|
||||
00000000000008A0 08 69 10 00 12 04 08 07 10 00 12 04 08 6B 10 00 .i...........k..
|
||||
00000000000008B0 12 04 08 08 10 00 12 04 08 6C 10 00 12 08 08 09 .........l......
|
||||
00000000000008C0 10 96 9F FA 8D 03 12 04 08 6D 10 00 12 08 08 16 .........m......
|
||||
00000000000008D0 10 A1 9F FA 8D 03 12 04 08 06 10 00 12 04 08 7A ...............z
|
||||
00000000000008E0 10 00 12 04 08 6A 10 00 12 04 08 10 10 00 12 04 .....j..........
|
||||
00000000000008F0 08 11 10 00 12 04 08 72 10 00 12 08 08 0E 10 CA .......r........
|
||||
0000000000000900 ED F9 8D 03 12 04 08 70 10 00 12 06 08 13 10 D9 .......p........
|
||||
0000000000000910 EB 02 12 04 08 1A 10 00 12 06 08 1B 10 8B B7 02 ................
|
||||
0000000000000920 12 08 08 18 10 E5 EF BD D2 06 12 08 08 17 10 D9 ................
|
||||
0000000000000930 EF BD D2 06 12 08 08 19 10 F9 B8 AC D4 02 12 04 ................
|
||||
0000000000000940 08 0A 10 00 12 09 08 C9 01 10 E5 EF BD D2 06 12 ................
|
||||
0000000000000950 05 08 CD 01 10 00 12 05 08 CA 01 10 00 12 09 08 ................
|
||||
0000000000000960 CB 01 10 E3 92 A7 D2 06 12 05 08 CE 01 10 67 12 ..............g.
|
||||
0000000000000970 05 08 CF 01 10 00 12 04 08 14 10 00 12 04 08 15 ................
|
||||
0000000000000980 10 00 12 09 08 E8 07 10 98 BF BD D2 06 12 09 08 ................
|
||||
0000000000000990 E9 07 10 D9 DB BD D2 06 12 09 08 D3 0F 10 92 F4 ................
|
||||
00000000000009A0 BC D2 06 28 01 30 DA E0 DA DA 02 38 E5 EF BD D2 ...(.0.....8....
|
||||
00000000000009B0 06 42 15 08 11 12 11 0A 0F 08 02 08 1A 08 1B 10 .B..............
|
||||
00000000000009C0 BF C0 B4 A7 0A 18 E9 07 ........
|
||||
`
|
||||
|
||||
// TestProbeProtobufDump 直接测试 HandleProtobufMsg:
|
||||
// 把 hexdump 还原成字节 → 按 JS 侧一样封装成 {"data": [byte...]} 的 payload
|
||||
// → 调用 HandleProtobufMsg,打印它返回的 WechatMessage JSON 或错误。
|
||||
func TestProbeProtobufDump(t *testing.T) {
|
||||
rawBytes := parseDump(t, dumpInput)
|
||||
t.Logf("解析出的字节数: %d", len(rawBytes))
|
||||
|
||||
// 构造 HandleProtobufMsg 期望的 payload:data 是 []interface{}{float64,...}
|
||||
dataArr := make([]interface{}, len(rawBytes))
|
||||
for i, b := range rawBytes {
|
||||
dataArr[i] = float64(b)
|
||||
}
|
||||
payload := map[string]interface{}{"data": dataArr}
|
||||
|
||||
jsonList, err := HandleProtobufMsg(payload)
|
||||
if err != nil {
|
||||
t.Fatalf("HandleProtobufMsg 失败: %v", err)
|
||||
}
|
||||
if len(jsonList) == 0 {
|
||||
t.Fatalf("HandleProtobufMsg 返回空(消息被全部跳过)")
|
||||
}
|
||||
|
||||
fmt.Printf("========== HandleProtobufMsg 返回 %d 条消息 ==========\n", len(jsonList))
|
||||
for idx, jsonData := range jsonList {
|
||||
var pretty bytes.Buffer
|
||||
if err := json.Indent(&pretty, jsonData, "", " "); err != nil {
|
||||
fmt.Printf("--- 消息 %d ---\n%s\n", idx, string(jsonData))
|
||||
} else {
|
||||
fmt.Printf("--- 消息 %d ---\n%s\n", idx, pretty.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parseDump 从 hexdump 里抽出真正的字节。
|
||||
// 针对 lldb / hexdump -C 的固定列宽格式:
|
||||
//
|
||||
// 地址(16 hex) + 2空格 + hex列(16字节) + 2空格 + ASCII列
|
||||
//
|
||||
// 第 0..17 字符是地址列,第 18 起是 hex 列(16 字节 = 48 字符宽,
|
||||
// 中缝再占 1 空格)。按固定列宽切片,彻底避开 ASCII 列被误当字节。
|
||||
func parseDump(t *testing.T, dump string) []byte {
|
||||
t.Helper()
|
||||
const addrCols = 18 // 16 hex 地址 + 2 空格
|
||||
const hexCols = 49 // 8字节*3 + 中缝1 + 8字节*3 = 49(含末尾空格,Fields 会忽略)
|
||||
var out []byte
|
||||
for _, line := range strings.Split(dump, "\n") {
|
||||
if strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
if len(line) <= addrCols {
|
||||
continue
|
||||
}
|
||||
end := addrCols + hexCols
|
||||
if end > len(line) {
|
||||
end = len(line)
|
||||
}
|
||||
for _, tk := range strings.Fields(line[addrCols:end]) {
|
||||
if len(tk) == 2 && isHexTok(tk) {
|
||||
var v int
|
||||
fmt.Sscanf(tk, "%02x", &v)
|
||||
out = append(out, byte(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func isHexTok(s string) bool {
|
||||
for _, c := range s {
|
||||
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return len(s) > 0
|
||||
}
|
||||
+88
-30
@@ -8,11 +8,12 @@ import (
|
||||
"time"
|
||||
|
||||
wxproto "github.com/yincongcyincong/weixin-macos/onebot/proto"
|
||||
"google.golang.org/protobuf/encoding/protowire"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func HandleProtobufMsgAndSend(payload map[string]interface{}) {
|
||||
jsonData, err := HandleProtobufMsg(payload)
|
||||
jsonList, err := HandleProtobufMsg(payload)
|
||||
if err != nil {
|
||||
if isUnsupportedProtobufMessage(err) {
|
||||
Warn("跳过不匹配的protobuf消息", "err", err)
|
||||
@@ -21,15 +22,17 @@ func HandleProtobufMsgAndSend(payload map[string]interface{}) {
|
||||
Error("protobuf消息处理失败", "err", err)
|
||||
return
|
||||
}
|
||||
if jsonData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// jsonData 是 WechatMessage JSON, 直接交给现有的发送流程处理
|
||||
if config.ConnType == "http" {
|
||||
SendHttpReq(jsonData)
|
||||
} else {
|
||||
SendWebSocketMsg(jsonData)
|
||||
// 一个数据帧可能打包了多条消息,逐条下发
|
||||
for _, jsonData := range jsonList {
|
||||
if jsonData == nil {
|
||||
continue
|
||||
}
|
||||
if config.ConnType == "http" {
|
||||
SendHttpReq(jsonData)
|
||||
} else {
|
||||
SendWebSocketMsg(jsonData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +48,57 @@ func isUnsupportedProtobufMessage(err error) bool {
|
||||
strings.Contains(message, "no messages found")
|
||||
}
|
||||
|
||||
func HandleProtobufMsg(payload map[string]interface{}) ([]byte, error) {
|
||||
// consumeBytesFields 从一段 protobuf 原始字节里,取出指定字段号、且 wiretype 为
|
||||
// bytes(2) 的所有值。用于遍历"重复出现的 singular 字段"(proto.Unmarshal 只会保留
|
||||
// 最后一个,这里手动全部取出)。
|
||||
func consumeBytesFields(raw []byte, field protowire.Number) [][]byte {
|
||||
var out [][]byte
|
||||
for len(raw) > 0 {
|
||||
num, typ, n := protowire.ConsumeTag(raw)
|
||||
if n < 0 {
|
||||
break
|
||||
}
|
||||
raw = raw[n:]
|
||||
if typ == protowire.BytesType {
|
||||
v, m := protowire.ConsumeBytes(raw)
|
||||
if m < 0 {
|
||||
break
|
||||
}
|
||||
if num == field {
|
||||
out = append(out, v)
|
||||
}
|
||||
raw = raw[m:]
|
||||
continue
|
||||
}
|
||||
m := protowire.ConsumeFieldValue(num, typ, raw)
|
||||
if m < 0 {
|
||||
break
|
||||
}
|
||||
raw = raw[m:]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// parseAllRecvData 从 WxRecvMsg 原始字节里提取所有消息数据。
|
||||
// 结构: WxRecvMsg.wrapper(field2) -> wrapper.body(field2, 可重复) -> body.content.data
|
||||
// 一个 wrapper 里可能打包多条 body,需要全部取出。
|
||||
func parseAllRecvData(rawBytes []byte) []*wxproto.WxRecvMsgData {
|
||||
var result []*wxproto.WxRecvMsgData
|
||||
for _, wrapperRaw := range consumeBytesFields(rawBytes, 2) {
|
||||
for _, bodyRaw := range consumeBytesFields(wrapperRaw, 2) {
|
||||
body := &wxproto.WxRecvMsgBody{}
|
||||
if err := proto.Unmarshal(bodyRaw, body); err != nil {
|
||||
continue
|
||||
}
|
||||
if body.Content != nil && body.Content.Data != nil {
|
||||
result = append(result, body.Content.Data)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func HandleProtobufMsg(payload map[string]interface{}) ([][]byte, error) {
|
||||
dataInter, ok := payload["data"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("protobuf_msg: missing data field")
|
||||
@@ -65,17 +118,35 @@ func HandleProtobufMsg(payload map[string]interface{}) ([]byte, error) {
|
||||
rawBytes[i] = byte(int(num))
|
||||
}
|
||||
|
||||
msg := &wxproto.WxRecvMsg{}
|
||||
err := proto.Unmarshal(rawBytes, msg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("protobuf unmarshal failed (data_len=%d): %w", len(rawBytes), err)
|
||||
}
|
||||
//fmt.Println("[receive protobuf data]", HexDump(rawBytes, 0))
|
||||
|
||||
data := getWxMsgData(msg)
|
||||
if data == nil {
|
||||
dataList := parseAllRecvData(rawBytes)
|
||||
if len(dataList) == 0 {
|
||||
return nil, fmt.Errorf("protobuf_msg: cannot extract message data")
|
||||
}
|
||||
|
||||
var jsonList [][]byte
|
||||
for _, data := range dataList {
|
||||
jsonData, err := buildWechatMessageJSON(data)
|
||||
if err != nil {
|
||||
// 单条消息(如系统通知/字段不全)不阻断其他消息
|
||||
Warn("跳过单条protobuf消息", "err", err)
|
||||
continue
|
||||
}
|
||||
if jsonData != nil {
|
||||
jsonList = append(jsonList, jsonData)
|
||||
}
|
||||
}
|
||||
|
||||
if len(jsonList) == 0 {
|
||||
return nil, fmt.Errorf("protobuf_msg: no messages found")
|
||||
}
|
||||
|
||||
return jsonList, nil
|
||||
}
|
||||
|
||||
// buildWechatMessageJSON 把单条 WxRecvMsgData 转成 WechatMessage JSON。
|
||||
func buildWechatMessageJSON(data *wxproto.WxRecvMsgData) ([]byte, error) {
|
||||
sender := ""
|
||||
receiver := ""
|
||||
content := ""
|
||||
@@ -177,19 +248,6 @@ func HandleProtobufMsg(payload map[string]interface{}) ([]byte, error) {
|
||||
return json.Marshal(wechatMsg)
|
||||
}
|
||||
|
||||
func getWxMsgData(msg *wxproto.WxRecvMsg) *wxproto.WxRecvMsgData {
|
||||
if msg == nil || msg.Wrapper == nil {
|
||||
return nil
|
||||
}
|
||||
if msg.Wrapper.Body == nil {
|
||||
return nil
|
||||
}
|
||||
if msg.Wrapper.Body.Content == nil {
|
||||
return nil
|
||||
}
|
||||
return msg.Wrapper.Body.Content.Data
|
||||
}
|
||||
|
||||
func getMessagesFromProto(content, sender string, mediaContent []byte) []*Message {
|
||||
var messages []*Message
|
||||
|
||||
|
||||
+19
-31
@@ -135,7 +135,6 @@ function generateAESKey() {
|
||||
}
|
||||
|
||||
const MAX_FRIDA_MESSAGE_BYTES = 4 * 1024 * 1024;
|
||||
const DOWNLOAD_CHUNK_BYTES = 256 * 1024;
|
||||
|
||||
function isReadablePointer(addr) {
|
||||
try {
|
||||
@@ -169,7 +168,7 @@ function readUtf8StringIfReadable(addr) {
|
||||
if (!isReadablePointer(addr)) {
|
||||
return "";
|
||||
}
|
||||
return addr.readUtf8String(4096);
|
||||
return addr.readUtf8String();
|
||||
} catch (e) {
|
||||
return "";
|
||||
}
|
||||
@@ -190,23 +189,18 @@ function sendDownloadChunks(dataPtr, dataLen, fileId, cdnUrl) {
|
||||
if (!cdnUrl || dataLen <= 0) {
|
||||
return;
|
||||
}
|
||||
const safeFileId = fileId || "";
|
||||
|
||||
for (let offset = 0; offset < dataLen; offset += DOWNLOAD_CHUNK_BYTES) {
|
||||
const chunkLen = Math.min(DOWNLOAD_CHUNK_BYTES, dataLen - offset);
|
||||
const buffer = readByteArrayIfReadable(dataPtr.add(offset), chunkLen);
|
||||
if (!buffer) {
|
||||
return;
|
||||
}
|
||||
const uint8Array = new Uint8Array(buffer);
|
||||
if (dataLen > 0 && dataLen <= 10 * 1024 * 1024) {
|
||||
var buffer = dataPtr.readByteArray(dataLen);
|
||||
var uint8Array = new Uint8Array(buffer);
|
||||
|
||||
send({
|
||||
type: "download",
|
||||
media: Array.from(uint8Array),
|
||||
file_id: safeFileId,
|
||||
cdn_url: cdnUrl,
|
||||
});
|
||||
}
|
||||
send({
|
||||
type: "download",
|
||||
media: Array.from(uint8Array),
|
||||
file_id: fileId,
|
||||
cdn_url: cdnUrl,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function fillUploadX1AndStart(idAddr, pathAddr, x1Buffer, receiver, md5, filePath, payloadHex) {
|
||||
@@ -1059,14 +1053,15 @@ function setupDownloadFileDynamic() {
|
||||
|
||||
|
||||
function setReceiver() {
|
||||
Interceptor.attach(buf2RespAddr, {
|
||||
onEnter: function (args) {
|
||||
// 通过 SP+0x140 读取当前 buf2resp 对应的 taskId
|
||||
var respTaskId = this.context.sp.add(0x140).readS32();
|
||||
Interceptor.attach(buf2RespAddr, {
|
||||
onEnter: function (args) {
|
||||
// 通过 SP+0x140 读取当前 buf2resp 对应的 taskId
|
||||
var respTaskId = this.context.sp.add(0x140).readS32();
|
||||
const currentPtr = this.context.x20;
|
||||
const x2 = this.context.x0.toInt32();
|
||||
if (!isReadablePointer(currentPtr)) {
|
||||
return;
|
||||
if (!isReadablePointer(currentPtr) || x2 < 4 || x2 > MAX_FRIDA_MESSAGE_BYTES) {
|
||||
console.error("[-] buf2resp: pointer 不可读 或 x2 大小不正确, ptr=" + currentPtr + " x2=" + x2);
|
||||
return;
|
||||
}
|
||||
|
||||
// 判断是否是我们发送的消息的 ack
|
||||
@@ -1092,14 +1087,7 @@ function setReceiver() {
|
||||
|
||||
pendingBuf2RespTaskId = 0;
|
||||
pendingSendMsgType = "";
|
||||
}
|
||||
|
||||
if (x2 < 4) {
|
||||
return;
|
||||
}
|
||||
if (x2 > MAX_FRIDA_MESSAGE_BYTES) {
|
||||
console.warn("[skip] protobuf_msg length out of range: " + x2);
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
const mem = readByteArrayIfReadable(currentPtr, x2);
|
||||
|
||||
+24
-9
@@ -354,24 +354,39 @@ func SilkToMp3(silkBytes []byte) ([]byte, error) {
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
func GetFilePath(data []byte, key []byte) (string, error) {
|
||||
// GetFilePath 解密 CDN 数据并落盘。
|
||||
// - extHint: 消息 XML 里的 fileext(如 "txt"),优先级最高——有就直接用它当扩展名,
|
||||
// 不再靠 magic bytes 猜(文本文件没有 magic,猜不出来)。
|
||||
// - totalLen: 明文真实长度(消息里的 totallen)。>0 时按它截断,去掉 AES 补齐的尾部字节。
|
||||
func GetFilePath(data []byte, key []byte, extHint string, totalLen int) (string, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(data) == 0 || len(data)%block.BlockSize() != 0 {
|
||||
return "", fmt.Errorf("invalid encrypted data length: %d, block_size: %d", len(data), block.BlockSize())
|
||||
// 只解密对齐部分,丢弃末尾不足一个块的残余(调用方通常已补齐/截齐)。
|
||||
bs := block.BlockSize()
|
||||
aligned := len(data) - len(data)%bs
|
||||
if aligned == 0 {
|
||||
return "", fmt.Errorf("invalid encrypted data length: %d, block_size: %d", len(data), bs)
|
||||
}
|
||||
|
||||
decrypted := make([]byte, len(data))
|
||||
bs := block.BlockSize()
|
||||
for i := 0; i < len(data); i += bs {
|
||||
decrypted := make([]byte, aligned)
|
||||
for i := 0; i < aligned; i += bs {
|
||||
block.Decrypt(decrypted[i:i+bs], data[i:i+bs])
|
||||
}
|
||||
|
||||
ext := DetectFileFormat(decrypted)
|
||||
if ext == "unknown" {
|
||||
return "", fmt.Errorf("无法解析的文件数据")
|
||||
// 按真实长度截断,去掉补齐的尾部字节
|
||||
if totalLen > 0 && totalLen <= len(decrypted) {
|
||||
decrypted = decrypted[:totalLen]
|
||||
}
|
||||
|
||||
// 扩展名:优先用消息里的 fileext,其次靠 magic 检测,都没有则存为 bin(不丢数据)
|
||||
ext := strings.TrimPrefix(strings.ToLower(extHint), ".")
|
||||
if ext == "" {
|
||||
ext = DetectFileFormat(decrypted)
|
||||
if ext == "unknown" {
|
||||
ext = "bin"
|
||||
}
|
||||
}
|
||||
|
||||
return SaveFileToFile(ext, decrypted)
|
||||
|
||||
+34
-18
@@ -10,6 +10,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
@@ -364,7 +366,7 @@ func HandleMsg(jsonData []byte) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path, err := GetDownloadPath(fileMsg.Image.MidImgURL, fileMsg.Image.AesKey)
|
||||
path, err := GetDownloadPath(fileMsg.Image.MidImgURL, fileMsg.Image.AesKey, "", 0)
|
||||
if err != nil {
|
||||
Error("获取文件路径失败", "err", err)
|
||||
return nil, err
|
||||
@@ -379,7 +381,8 @@ func HandleMsg(jsonData []byte) ([]byte, error) {
|
||||
Error("XML解析失败", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
path, err := GetDownloadPath(fileMsg.AppMsg.AppAttach.CdnAttachURL, fileMsg.AppMsg.AppAttach.AesKey)
|
||||
totalLen, _ := strconv.Atoi(strings.TrimSpace(fileMsg.AppMsg.AppAttach.TotalLen))
|
||||
path, err := GetDownloadPath(fileMsg.AppMsg.AppAttach.CdnAttachURL, fileMsg.AppMsg.AppAttach.AesKey, fileMsg.AppMsg.AppAttach.FileExt, totalLen)
|
||||
if err != nil {
|
||||
Error("获取文件路径失败", "err", err)
|
||||
return nil, err
|
||||
@@ -393,7 +396,7 @@ func HandleMsg(jsonData []byte) ([]byte, error) {
|
||||
Error("XML解析失败", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
path, err := GetDownloadPath(fileMsg.Video.CdnVideoUrl, fileMsg.Video.AesKey)
|
||||
path, err := GetDownloadPath(fileMsg.Video.CdnVideoUrl, fileMsg.Video.AesKey, "mp4", int(fileMsg.Video.Length))
|
||||
if err != nil {
|
||||
Error("获取文件路径失败", "err", err)
|
||||
return nil, err
|
||||
@@ -435,52 +438,65 @@ func HandleMsg(jsonData []byte) ([]byte, error) {
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func GetDownloadPath(cdnUrl, aesKeyStr string) (string, error) {
|
||||
for i := 0; i < 10; i++ {
|
||||
func GetDownloadPath(cdnUrl, aesKeyStr, extHint string, totalLen int) (string, error) {
|
||||
for i := 0; i < 30; i++ {
|
||||
if downloadMsgInter, ok := userID2FileMsgMap.Load(cdnUrl); ok {
|
||||
downloadReq := downloadMsgInter.(*DownloadRequest)
|
||||
|
||||
downloadReq.mu.Lock()
|
||||
|
||||
if downloadReq.FilePath != "" {
|
||||
return downloadReq.FilePath, nil
|
||||
fp := downloadReq.FilePath
|
||||
downloadReq.mu.Unlock()
|
||||
return fp, nil
|
||||
}
|
||||
|
||||
// 检查数据是否还在接收中
|
||||
timeSinceLastAppend := time.Now().UnixMilli() - downloadReq.LastAppendTime
|
||||
Info("文件等待下载", "url", cdnUrl, "times", i, "last_append_time", timeSinceLastAppend)
|
||||
|
||||
// 如果数据仍在接收中(1秒内有新数据),继续等待
|
||||
if timeSinceLastAppend < 1000 && i < 9 {
|
||||
// 如果数据仍在接收中(3秒内有新数据),继续等待
|
||||
if timeSinceLastAppend < 2000 && i < 29 {
|
||||
downloadReq.mu.Unlock()
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// 数据接收完成,尝试解密
|
||||
if len(downloadReq.Media) > 0 {
|
||||
if len(downloadReq.Media)%aes.BlockSize != 0 {
|
||||
Info("文件数据仍未对齐 AES 块,继续等待", "url", cdnUrl, "times", i, "media_len", len(downloadReq.Media), "block_size", aes.BlockSize)
|
||||
if i < 9 {
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
}
|
||||
userID2FileMsgMap.Delete(cdnUrl)
|
||||
return "", fmt.Errorf("文件数据长度不是 AES 块大小倍数: media_len=%d block_size=%d", len(downloadReq.Media), aes.BlockSize)
|
||||
media := downloadReq.Media
|
||||
// AES 块对不齐时,末尾补 0 到整块(微信 CDN 密文尾部可能带
|
||||
// 非整块残余,补齐后按整块解密,避免丢字节)。
|
||||
if rem := len(media) % aes.BlockSize; rem != 0 {
|
||||
pad := aes.BlockSize - rem
|
||||
Info("文件数据未对齐 AES 块,末尾补 0 到整块",
|
||||
"url", cdnUrl, "media_len", len(media), "pad", pad, "block_size", aes.BlockSize)
|
||||
padded := make([]byte, len(media)+pad)
|
||||
copy(padded, media)
|
||||
media = padded
|
||||
}
|
||||
|
||||
aesKey, err := hex.DecodeString(aesKeyStr)
|
||||
if err != nil {
|
||||
downloadReq.mu.Unlock()
|
||||
Error("AES key 解码失败", "err", err)
|
||||
return "", err
|
||||
}
|
||||
filePath, err := GetFilePath(downloadReq.Media, aesKey)
|
||||
filePath, err := GetFilePath(media, aesKey, extHint, totalLen)
|
||||
if err != nil {
|
||||
Error("获取文件路径失败", "err", err, "media_len", len(downloadReq.Media))
|
||||
downloadReq.mu.Unlock()
|
||||
Error("获取文件路径失败", "err", err, "media_len", len(media), "aes_key", aesKeyStr)
|
||||
userID2FileMsgMap.Delete(cdnUrl)
|
||||
return "", err
|
||||
}
|
||||
|
||||
downloadReq.FilePath = filePath
|
||||
downloadReq.Media = nil
|
||||
downloadReq.mu.Unlock()
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
downloadReq.mu.Unlock()
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"sendFuncAddr": "0x5120fd8",
|
||||
"req2bufEnterAddr": "0x3e58e8c",
|
||||
"req2bufExitAddr": "0x3E59DE0",
|
||||
"buf2RespAddr": "0x3e7e670",
|
||||
"blrX8Addr": "0x3e58f0c",
|
||||
"autoBufferWriteFunc": "0x3E7FA8C",
|
||||
|
||||
"uploadImageAddr": "0x529bd7c",
|
||||
"cndOnCompleteAddr": "0x3E1575C",
|
||||
"uploadGetCallbackWrapperAddr": "0x525ab20",
|
||||
"uploadGetCallbackWrapperFuncAddr": "0x3E14FF8",
|
||||
"uploadOnCompleteAddr": "0x525B2D8",
|
||||
"uploadOnCompleteFuncAddr": "0x3E1617C",
|
||||
|
||||
"startDownloadMedia": "0x529c9e4",
|
||||
"downloadImagAddr": "0x53c0004",
|
||||
"downloadFileAddr": "0x535ea98",
|
||||
"downloadVideoAddr": "0x5378a18"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user