继微信dat之后的PC端微信解密后数据库数据抽取

继微信dat之后的PC端微信解密后数据库数据抽取

月光魔力鸭

2020-09-08 00:04 阅读 11464 喜欢 22 微信数据库 PC微信db

前段时间弄了下微信的dat图片的解码,后续有同学问数据库的解码怎么弄.. 当然,后边陆陆续续的看了下,加上住院乱七八糟的事情,一直没处理,最近几天处理了下,发现微信数据库的数据格式是真的麻烦...

查看如何查看微信dat图片的请走这里

简单梳理下最近的操作:

前面两部就不讲了,我也是直接拿着大神的教程直接做的,没啥变化,也讲不出什么东西来。 本文主要是做个简单的记录整理,确实没啥很特别的东西..

先来几张图

根据拿到的64位密钥,对数据库进行解密。

得到解密后的数据库

通过sqlite 打开看到如下表

MSG数据库

以MSG.db 为例子,该数据库主要存储的是聊天记录,表MSG中为所有的好友+群组的聊天记录,如下图:

MSG表聊天记录

数据库

微信MSG下有很多数据库文件,如果我们需要的是好友+聊天记录,那么其实只需要三个数据库文件即可,为: MSG.db MediaMsg.db MicroMsg.db ,当然存储数据达到一定体积后,会有 MSG0 MSG1 等。

抽取

所以,我们只需要拿到联系人,然后根据联系人拿到对应的聊天记录,然后对聊天记录进行展示,当然,思路是比较简单的,麻烦的是微信的聊天数据格式比较多,大约十几种,分别包括:消息/图片/表情/语音/视频/地图/名片/撤回/红包/群组消息/拍一拍等等。

我们还需要根据不同的数据格式,解析成不同的内容,由于兴趣有限,只做了比较简单的消息/图片/视频这三个。除了消息外,图片和视频都是存储的路径,文件分表在 filestorage/image 和 filestorage/video 下,我们通过正则匹配到后拿到文件进行展示。

其中蓝色代表存在聊天记录的好友

样式很low ,随便写的

主要的业务代码

/****
 * 
 * 微信的所有业务查询使用
 */


const fs = require('fs');
const sqlite3 = require('sqlite3').verbose();
const lib = require('./lib');



module.exports = function (options) {
    let wxid = options.wxid;
    
    const MicroIns = new sqlite3.Database(options.MicroMsg);
    const MSGIns = options.MSG.split(',').map(str => {
        return new sqlite3.Database(str);
    });
    const MEDIAIns = new sqlite3.Database(options.MEDIA);
    return {

        /***
         * 获取微信所有联系人-朋友
         */
        getFriends: async function () {
            let list = await lib.all(MicroIns, 'select UserName,Remark,NickName,PYInitial,RemarkPYInitial from Contact where verifyFlag = 0 and (type=3 or type > 50)', []);
            //做简单排序,A 
            list.forEach(item => {
                item.zimu = (item.RemarkPYInitial || item.PYInitial);
                item.f = item.zimu.length > 0 ? item.zimu.substr(0, 1) : '-';
            });
            list.sort((a, b) => {
                return a.f.charCodeAt(0) - b.f.charCodeAt(0);
            })
            return list;
        },
        /***
         * 获取微信所有的群数据
         */
        getGroups: async function () {
            let list = await lib.all(MicroIns, 'select UserName,Remark,NickName,PYInitial,RemarkPYInitial from Contact where type=2');
            //做简单排序,A 
            list.forEach(item => {
                item.zimu = (item.RemarkPYInitial || item.PYInitial);
                item.f = item.zimu.length > 0 ? item.zimu.substr(0, 1) : '-';
            });
            list.sort((a, b) => {
                return a.f.charCodeAt(0) - b.f.charCodeAt(0);
            })
            return list;
        },
        /***
         * 获取微信内的公众号信息
         */
        getOfficial: async function () {
            let list = await lib.all(MicroIns, 'select UserName,Remark,NickName,PYInitial,RemarkPYInitial,VerifyFlag from Contact where type=3 and (VerifyFlag=24 or VerifyFlag=8)');
            //做简单排序,A 
            list.forEach(item => {
                item.zimu = (item.RemarkPYInitial || item.PYInitial);
                item.f = item.zimu.length > 0 ? item.zimu.substr(0, 1) : '-';
            });
            list.sort((a, b) => {
                return a.f.charCodeAt(0) - b.f.charCodeAt(0);
            })
            return list;
        },

        /***
         * 获取微信内群里的陌生人信息
         */
        getStrangers: async function () {
            let list = await lib.all(MicroIns, 'select UserName,Remark,NickName,PYInitial,RemarkPYInitial from Contact where type=4');
            //做简单排序,A 
            list.forEach(item => {
                item.zimu = (item.RemarkPYInitial || item.PYInitial);
                item.f = item.zimu.length > 0 ? item.zimu.substr(0, 1) : '-';
            });
            list.sort((a, b) => {
                return a.f.charCodeAt(0) - b.f.charCodeAt(0);
            })
            return list;
        },
        /***
         * 根据UserName 获得Contact的一行记录
         */
        getContactByUserName: async function (UserName) {
            let list = await lib.get(MicroIns, 'select UserName,Remark,NickName,PYInitial,RemarkPYInitial from Contact where UserName=?', UserName);
            return list;
        },
        /***
         * 根据UserName 获得 ContactHeadImgUrl 头像信息
         */
        getHeadImageByUserName: async function (UserName) {
            let list = await lib.get(MicroIns, 'select usrName,smallHeadImgUrl,bigHeadImgUrl,headImgMd5 from ContactHeadImgUrl where usrName=?', UserName);
            return list;
        },
        //获得聊天记录条数
        getRecordCount: async function (UserName) {
            let count = 0;
            for (let i in MSGIns) {
                let temp = await lib.get(MSGIns[i], 'select count(1) as num from MSG where StrTalker=?', UserName);
                count += temp.num;
            }
            return { num: count };
        },
        /**
         * 根据UserName 查询 MSG0-N 中MSG 的聊天记录数据
         * @param {String} UserName 
         */
        getRecordList: async function (UserName) {
            let list = [];
            for (let i in MSGIns) {
                let temp = await lib.all(MSGIns[i], 'select localId,TalkerId,Type,SubType,IsSender,CreateTime,Sequence,StrContent,StrTalker,BytesExtra from MSG where StrTalker=?', UserName);
                list = list.concat(temp);
            }
            list.forEach(item => {
                //type=3 ,图片消息
                //type=43 ,视频消息
                //type=47 ,表情消息
                //type=34,语音消息
                //type=1 ,普通消息
                //type=42,名片消息
                //type=48,地图定位消息
                //type=49,50 ,不确定
                //type=10000,撤回消息,消息从BytesExtra中
                //type=10002,邀请入群消息
                var info = item.BytesExtra.toString('utf8');
                if (info != null) {
                    info = info.trim();
                    var rr = info.match(/\b(.*)?\u001a/);
                    if (rr) {
                        item.from = rr[1];//来自谁发言
                    }
                }
                var type = item.Type;
                if (type == 3) {//图片消息,需要获得缩略图和大图数据
                    var info = item.BytesExtra.toString('utf8');
                    var reg = new RegExp(wxid + '.*?(\.dat)', 'g');
                    var ja = info.match(reg);
                    if (ja) {
                        item.bigImage = ja[0];
                        item.smallImage = ja[1];
                        item.BytesExtra = null;
                        item.StrContent = null;
                    } else {
                        item.StrContent = info;
                    }
                } else if (type == 43) {//视频
                    var info = item.BytesExtra.toString('utf8');
                    var reg = new RegExp(wxid + '.*?(\.jpg)', 'g');
                    var reg2 = new RegExp('' + wxid + '.*?(\.mp4)', 'g');
                    var ja = info.match(reg);
                    if (ja != null) {
                        item.videoPost = ja[0];
                        info = info.replace(ja[0], '');
                    }
                    var ja2 = info.match(reg2);
                    if (ja2 != null) {
                        item.Video = ja2[0];
                    }
                    item.StrContent = null;
                    item.BytesExtra = null;
                } else if (type == '47') {//表情消息
                    item.StrContent = null;
                    item.BytesExtra = null;
                } else if (type == 34) {//语音消息,需要将语音数据转为mp3 或直接播放
                    // console.log(item)
                    // console.log(item.BytesExtra.toString('utf8'));
                } else if (type == 1) {//普通消息
                    // console.log(item);
                    // console.log(item.StrContent)
                    //获得是谁发言

                } else if (type == 42) {//名片消息

                } else if (type == 48) {//地图定位
                    var label = item.StrContent.match(/label="(.*?)"/);
                    if (label != null) {
                        label = label[1];
                    }
                    var poiname = item.StrContent.match(/poiname="(.*?)"/);
                    if (poiname != null) {
                        poiname = poiname[1]
                    }
                    item.StrContent = label + poiname;
                    item.BytesExtra = null;
                } else if (type == 10000) {//撤回/红包/拍一拍
                } else if (type == 10002) {//邀请入群
                    item.StrContent = '邀请入群信息';
                    item.BytesExtra = null;
                } else {
                    // console.log(item);
                    if (item.SubType == 6) {//附件

                        var regExp = new RegExp('((' + wxid + ').*)');
                        if (regExp) {
                            item.filePath = regExp[0];
                        }
                    } else if (item.SubType == 5 || item.SubType == 33) {
                        var reg = new RegExp(wxid + '.*?(\.jpg)', 'g');
                        var ja = info.match(reg);
                        if (ja) {
                            item.bigImage = ja[0];
                        }
                    } else if (item.SubType == 8) {//gif
                        var reg = new RegExp(wxid + '.*?(\.gif)', 'g');
                        var ja = info.match(reg);
                        if (ja) {
                            item.bigImage = ja[0];
                        }
                    } else {
                        // console.log(info);
                        item.StrContent = item.BytesExtra.toString('utf8');
                        item.BytesExtra = null;
                    }
                    // console.log(item.BytesExtra.toString('utf8'))
                }
            })
            return list;
        },

        getMedia: async function () {
            let list = await lib.all(MEDIAIns, 'select * from media');
            return list;
        }

    };
}

其他

当然,感觉这个其实是没太有意义的,毕竟...密钥必须要__微信登录__才可以拿到.同时呢,由于微信的机制,导致数据库能看到啥,微信就能看到啥.. 所以没太大意义,除非准备把记录保存起来,后续如果真的闲的蛋疼的话,可能会吧聊天记录美化下,把不同格式都加上,尽可能的还原,然后加个pdf ?

转载请注明出处: https://chrunlee.cn/article/weixin-dat-db-data.html


感谢支持!

赞赏支持
提交评论
评论信息 (请文明评论)
暂无评论,快来快来写想法...
推荐
今天用github登录自己网站的时候,竟然意外的失败了,查了下日志,发现报错:Request forbidden by administrative rules 。
windows 环境下有时候会使用curl工具来测试一些接口或页面,这里简单记录下在windows环境下的安装步骤。
这两天换了电脑,装上了PowerShell,由于有很多nodejs的小工具,但是发现在powershell中都一直报错。。蓝瘦
构建tomcat镜像后发现项目中参数名字为乱码。
一直在做K12教育方面的产品,不过大都是学校管理层面的,对于教学一直很少触及。当然,这类的产品已经存在不少了,在功能、设计方面我们都会借鉴一些进行改进,其中有一些基础数据,如果也是从头自己收集的话,我想,可能会死的吧...比如:各个学段内的教材和章节信息。
最近一直在了解关于个人支付的问题。由于之前一直想实现个人支付,但是目前微信和支付宝的支付接口都需要企业或个体户资质,导致没办法实现,无奈只能走向这个道路。 说是免签,实际上就是拿到收款金额来做些事情。
关于自动签到,之前也有过,感觉写过好多次了,有可能也重复了,不过这次是通过puppeteer来实现的,相对于之前的phantomjs 或 casperjs 或 request 等最起码会更简单些,也不用安装这么多稀奇古怪的东西。
虽说docker已经大名鼎鼎,但在之前一直都未上手使用过,即便是前一阵子想挂下京东的京豆(使用docker)也给耽搁了,一直的感觉就是这货应该跟vm没啥区别吧,应该就是更方便更好用更适合开发者。