PC微信数据库结构分析

2023年12月9日 219点热度 2人点赞 0条评论

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}


参考资料:

微信PC端各个数据库文件结构与功能简述 - Multi文件夹_微信各个文件夹说明-CSDN博客

WeChatMsg

王谷雨

一个苟且偷生的java程序员

文章评论