MSG 文件
表 MSG 存放聊天记录
普通文本类型
Type = 1,内容存放在 StrContent
图片类型
Type = 3 该条消息是图片类型
StrContent 内容如下
<?xml version="1.0"?>
<msg>
<img aeskey="73eeda8dc2259b8075695036551e3eb4" encryver="1" cdnthumbaeskey="73eeda8dc2259b8075695036551e3eb4" cdnthumburl="3057020100044b3049020100020414203e3302032df08e02041dca817b02046572bbb0042462303235363331622d653662642d346235392d383337382d3930303731353131333333640204052438010201000405004c54a200" cdnthumblength="12688" cdnthumbheight="84" cdnthumbwidth="150" cdnmidheight="0" cdnmidwidth="0" cdnhdheight="0" cdnhdwidth="0" cdnmidimgurl="3057020100044b3049020100020414203e3302032df08e02041dca817b02046572bbb0042462303235363331622d653662642d346235392d383337382d3930303731353131333333640204052438010201000405004c54a200" length="2360562" md5="d3db52c6cd6aa11a155a210235a37d87" hevc_mid_size="0" originsourcemd5="d3db52c6cd6aa11a155a210235a37d87"/>
</msg>
拿到 MD5 为 d3db52c6cd6aa11a155a210235a37d87
再去 HardLinkImage 文件查询,MD5 字段要经过转换,将 16 进制字符串转成二进制
python API 为 binascii.unhexlify(md5)
go API 为 hex.DecodeString
SELECT
Md5Hash,
MD5,
FileName,
HardLinkImageID.Dir AS dirName1,
HardLinkImageID2.Dir AS dirName2
FROM
HardLinkImageAttribute
JOIN HardLinkImageID ON HardLinkImageAttribute.DirID1 = HardLinkImageID.DirID
JOIN HardLinkImageID AS HardLinkImageID2 ON HardLinkImageAttribute.DirID2 = HardLinkImageID2.DirID
WHERE
MD5 = ?;
最终,文件为 FileStorage/MsgAttach/${dirName1}/Image/${dirName2}/${FileName}
,例如 FileStorage/MsgAttach/5b910b8613395904cb9b37fc33005604/Image/2023-12/344b296fa93bd9e0f6e1c3d5db7b121d.dat
最后需要将 .dat 文件转成图片, 自行百度 微信dat文件转换图片
语音类型
Type=34,内容存放在 MediaMSG 文件的 Mdeia 表,匹配条件如下
MSG表的MsgSvrID = Media表的Reserved0
Mdeia 表的 Buf 字段就是语言数据,格式为 SILK 魔改,具体转化可以看以下项目:pilk
import pilk
import os
def decodeSilk(path):
# silk 转 pcm
pilk.decode(path, "test.pcm", 44100)
# pcm 转 mp3
os.system(f"ffmpeg.exe -y -f s16le -i test.pcm -ar 44100 -ac 1 test.mp3")
# 将 Buf 字段内容写入 test.silk 文件
path = "D:/test/test.silk"
decodeSilk(path) # 开始解码
视频类型
Type=43,有两种方式获取文件路径
– 方式一:
存放在 BytesExtra 字段中,需要用 protobuf 解码,以下是解码内容节选
```text
3 {
1: 3
2: "wxid_xxx\\\\FileStorage\\\\Video\\\\2023-12\\\\6396911ae3950b5487ea4bb500f8fd26.jpg"
}
3 {
1: 4
2: "wxid_xxx\\\\FileStorage\\\\Video\\\\2023-12\\\\6396911ae3950b5487ea4bb500f8fd26.mp4"
}
```
- 方式二:
跟图片类型的解析差不多,从 StrContent 字段中获取 MD5,再去 HardLinkVideo 文件根据 MD5 查询,同样的,MD5 要转成二进制
SELECT Md5Hash, MD5, FileName, HardLinkVideoID.Dir AS dirName2 FROM HardLinkVideoAttribute JOIN HardLinkVideoID ON HardLinkVideoAttribute.DirID2 = HardLinkVideoID.DirID WHERE MD5 = ?
最终,视频为
FileStorage/Video/${dirName2}/${FileName}
动画表情(第三方开发的表情包)
Type=47,内容存放在 StrContent 字段,xml 格式,以下是 xml 的字段解析
- androidmd 5、md 5:文件的 md5
- thumburl:封面的下载 URL
- cdnurl:文件的下载 URL
如果是 gif 表情,thumburl 是静态图片,cdnurl 是 gif 文件
如果是自己发送的表情,可能没有 thumburl 和 cdnurl,需要通过文件 md5 去 Emotion 文件的 CustomEmotion 表查询获取 cndurl
表情文件的存放路径是 FileStorage/CustomEmotion
,不知道怎么解密,有大神知道希望告知,关键词 v1mmwx
带引用的文本类型
Type=49 AND SubType=57 ,内容存放在 CompressContent 字段中,该字段被压缩过,压缩算法用的是 zl4
,解压出来的内容是 XML 格式。以下是解压代码
import lz4.block as lb
unzipStr = lb.decompress(CompressContent, uncompressed_size=0x10004)
text = unzipStr.decode('utf-8')
print(text )
以下是简化后的 xml 内容
<?xml version="1.0"?>
<msg>
<fromusername>消息发送者</fromusername>
<appmsg appid="" sdkver="0">
<title>消息</title>
<refermsg>
<fromusr>如果是群聊,则为房间id,否则与fromusername字段一致</fromusr>
<chatusr>被引用者</chatusr>
<createtime>1702000060</createtime>
<displayname>被引用者昵称</displayname>
<content>引用消息</content>
</refermsg>
</appmsg>
</msg>
附件
Type=49 AND SubType=6 ,同样有两种方式获取
- 方式一
内容存放在 BytesExtra 字段中,需要用 protobuf 解码,以下是解码内容节选
{ 1: 4 2: "wxid_xxx\\\\FileStorage\\\\File\\\\2023-12\\\\xxx.zip" }
- 方式二
参考图片类型的获取,从 CompressContent 字段中获取 MD5,再去 HardLinkFile 文件根据 MD5 查询,别忘了 MD5 要转成二进制
SELECT
Md5Hash,
MD5,
FileName,
HardLinkFileID2.Dir AS dirName2
FROM
HardLinkFileAttribute
JOIN HardLinkFileID AS HardLinkFileID2 ON HardLinkFileAttribute.DirID2 = HardLinkFileID2.DirID
WHERE
MD5 = ?
最终文件为 FileStorage/File/${dirName2}/${FileName}
参考资料: