本文转载自程序员专栏

在电子取证过程中,也会遇到提取PC版微信数据的情况,看雪、52破解和CSDN等网上的PC版微信数据库破解文章实在是太简略了,大多数只有结果没有过程。经过反复试验终于成功解密了数据库,现在把详细过程记录下来,希望大家不要继续在已经解决的问题上过度浪费时间,以便更投入地研究尚未解决的问题。

通过查阅资料得知,与安卓手机版微信的7位密码不同,PC版微信的密码是32字节(64位),加密算法没有说明,但是可以通过OllyDbg工具从内存中获取到这个密码,然后通过一段C++代码进行解密。

首先下载OllyDbg 2.01汉化版,我用的版本如下图所示:

运行OllyDbg,然后运行PC版微信(需要下载客户端的,不是网页版)。先不要点击登录按钮。

切换到Ollydbg界面:

点击文件菜单,选择“附加”,在弹出的对话框中找到名称为WeChat的进程,其窗口名称为“登录”。然后点击“附加”。

附加成功后OllyDbg开始加载,成功加载后可以看到最上面OllyDbg后面有WeChat.exe的字样:

在查看菜单中选择“可执行模块”:

找到名称为WeChatWin的模块,双击选中。为了方便观察,在窗口菜单中选择水平平铺。在CPU窗口标题栏可以看到“模块WeChatWin”字样。

在插件中选择“StrFinder字符查找”中的“查找ASCII字符串”(注意如果下载的OllyDbg版本不对,可能没有相关插件,因此一定要找对版本),要稍微等一会儿,会出现搜索结果的窗口。

在此窗口点击鼠标右键,选择“Find”,在搜索框中输入“DBFactory::encryptDB”。

会自动定位在第一处,但我们需要的是第二处,即“encryptDB %s DBKey can’t be null”下面这一处。可以用鼠标点击滚动条向下,找到第二处,用鼠标双击此处。

在CPU窗口中可以看到已经定位到了相应的位置。用鼠标点击滚动条向下翻。

下面第六行应该是TEST EDX,EDX,就是用来比对密码的汇编语言代码。在最前面地址位置(本文中是0F9712BA)双击设置断点(设置断点成功则地址会被标红,而且可以在断点窗口中看到设置成功的断点)

点击“运行”按钮(或者在调试菜单中选择“运行”),这时寄存器窗口中的EDX的值应该是00000000。

切换到微信登录页面,点击登录,然后到手机端确认登录。这是OllyDbg界面中的数据不断滚动,直到EDX不再为全0并且各个窗口内容停止滚动为止。

在EDX的值上面点击鼠标右键,在弹出的菜单里面选择“数据窗口中跟随”,则数据窗口中显示的就是EDX的内容。

图示中从0B946A80(这个数值是变化的,不但每台电脑不同,每次调试也可能完全不同)到0B946A9F共32个字节就是微信的加密密码,本图中就是:

“53E9BFB23B724195A2BC6EB5BFEB0610DC2164756B9B4279BA32157639A40BB1”

一共32个字节,共64位。

得到这个之后,就可以关闭OllyDbg了,微信也会自动被关闭。

接下来就是解密过程。在看雪、52破解等多个论坛中都有相关的C++源码,开始企图使用Dev-C++或者C-Free等轻量级IDE进行编译,也使用过Visual C++ 6.0绿色精简版,结果多次尝试出现各种错误,反复失败,最终不得已使用Visual Studio,并对代码进行了一定的修正,终于调试成功。

正好Visual Studio 2019刚刚发布直接到官方网站下载了社区版。

根据查到的资料,需要先安装openssl,为了省事直接下载了最新的Win64OpenSSL-1_1_1b,安装后发现各种报错,继续查找资料发现原来sqlcipher使用的是低版本的openssl,之后找到了一个Win64OpenSSL-1_0_2r也报错,最后发现还是官方这个直接解压缩的版本靠谱:

https://www.openssl.org/source/openssl-1.0.2r.tar.gz

把压缩包直接解压到任意目录,比如c:\openssl-1.0.2r

启动Visual Studio 2019社区版(估计Visual Studio 2008以后的都应该可以,懒得找就直接官网下载最新的吧)

在启动界面右下方选择“创建新项目”

滚动下拉条,在窗口中选择C++控制台应用:

给项目随便起个名字,选择保存位置:

然后点击“创建”,即可完成新项目创建。生成默认的Hello World代码:

先要做好项目的基础配置,之前调试失败主要问题就出在这里了。

在项目菜单中最下面选择项目属性“dewechat属性”(这个跟设置的项目名称一致)

对话框最左上角的配置后面,可以选择配置的是Debug模式还是Release模式(Release模式不包含调试信息,编译完成的exe文件更小一些,但如果是自己用,这两个模式没有区别,配置了哪个,后面就要用哪个模式编译,否则会报错)

先选择C/C++下面的“常规”选项:

右边第一条是“附加包含目录”,点击右侧空白处。在下拉框里选择“编辑…”,在对话框中点击四个图标按钮最左侧的“新行”按钮,会生成一个空白行,点击右侧的“…”:

在弹出的对话框里选择刚刚安装的openssl目录(本文是c:\openssl-1.0.2r)中的include目录。

设置完成后如下:

然后选择左侧“链接器”下面的“常规”:

在中间位置,有一个“附加库目录”,点击右侧空白处,选择openssl目录下的lib目录,设置完成后如下:

最后点击链接器下面的“输入”:

右侧最上面有“附加依赖项”,默认已经有一些系统库,点击右侧内容,选择“编辑…”

这个没有增加新行的按钮,只能手工录入或者拷贝文件名进去,需要增加上图所示的两个库名称。

设置完成后如下:

现在所有的设置都OK了,可以把代码放进来编译了。

由于太多网站转载,而且很多有错漏,已经搞不清原始代码是哪位大神写的了,其中有一些已经被废弃的代码,根据系统报错提示进行了替换,另外做了一个主要的变化就是之前的代码是把数据库名写在变量中,但由于需要解密很多库,为了灵活,改为输入参数的方法,即在运行时带参数运行或者根据提示输入需要解密的数据库文件名。

using namespace std;#include <Windows.h>#include <iostream>#include <openssl/rand.h>#include <openssl/evp.h>#include <openssl/aes.h>#include <openssl/hmac.h>
#undef _UNICODE#define SQLITE_FILE_HEADER "SQLite format 3" #define IV_SIZE 16#define HMAC_SHA1_SIZE 20#define KEY_SIZE 32
#define SL3SIGNLEN 20
#ifndef ANDROID_WECHAT#define DEFAULT_PAGESIZE 4096       //4048数据 + 16IV + 20 HMAC + 12#define DEFAULT_ITER 64000#else#define NO_USE_HMAC_SHA1#define DEFAULT_PAGESIZE 1024#define DEFAULT_ITER 4000#endif//pc端密码是经过OllyDbg得到的32位pass。unsigned char pass[] = { 0x53,0xE9,0xBF,0xB2,0x3B,0x72,0x41,0x95,0xA2,0xBC,0x6E,0xB5,0xBF,0xEB,0x06,0x10,0xDC,0x21,0x64,0x75,0x6B,0x9B,0x42,0x79,0xBA,0x32,0x15,0x76,0x39,0xA4,0x0B,0xB1 };char dbfilename[50];int Decryptdb();int CheckKey();int CheckAESKey();int main(int argc, char* argv[]){    if (argc >= 2)    //第二个参数argv[1]是文件名        strcpy_s(dbfilename, argv[1]);  //复制               //没有提供文件名,则提示用户输入    else {        cout << "请输入文件名:" << endl;        cin >> dbfilename;    }    Decryptdb();    return 0;}
int Decryptdb(){    FILE* fpdb;    fopen_s(&fpdb, dbfilename, "rb+");    if (!fpdb)    {        printf("打开文件错!");        getchar();        return 0;    }    fseek(fpdb, 0, SEEK_END);    long nFileSize = ftell(fpdb);    fseek(fpdb, 0, SEEK_SET);    unsigned char* pDbBuffer = new unsigned char[nFileSize];    fread(pDbBuffer, 1, nFileSize, fpdb);    fclose(fpdb);
    unsigned char salt[16] = { 0 };    memcpy(salt, pDbBuffer, 16);
#ifndef NO_USE_HMAC_SHA1    unsigned char mac_salt[16] = { 0 };    memcpy(mac_salt, salt, 16);    for (int i = 0; i < sizeof(salt); i++)    {        mac_salt[i] ^= 0x3a;    }#endif
    int reserve = IV_SIZE;      //校验码长度,PC端每4096字节有48字节#ifndef NO_USE_HMAC_SHA1    reserve += HMAC_SHA1_SIZE;#endif    reserve = ((reserve % AES_BLOCK_SIZE) == 0) ? reserve : ((reserve / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;
    unsigned char key[KEY_SIZE] = { 0 };    unsigned char mac_key[KEY_SIZE] = { 0 };
    OpenSSL_add_all_algorithms();    PKCS5_PBKDF2_HMAC_SHA1((const char*)pass, sizeof(pass), salt, sizeof(salt), DEFAULT_ITER, sizeof(key), key);#ifndef NO_USE_HMAC_SHA1    PKCS5_PBKDF2_HMAC_SHA1((const char*)key, sizeof(key), mac_salt, sizeof(mac_salt), 2, sizeof(mac_key), mac_key);#endif
    unsigned char* pTemp = pDbBuffer;    unsigned char pDecryptPerPageBuffer[DEFAULT_PAGESIZE];    int nPage = 1;    int offset = 16;    while (pTemp < pDbBuffer + nFileSize)    {        printf("解密数据页:%d/%d \n", nPage, nFileSize / DEFAULT_PAGESIZE);
#ifndef NO_USE_HMAC_SHA1        unsigned char hash_mac[HMAC_SHA1_SIZE] = { 0 };        unsigned int hash_len = 0;        HMAC_CTX hctx;        HMAC_CTX_init(&hctx);        HMAC_Init_ex(&hctx, mac_key, sizeof(mac_key), EVP_sha1(), NULL);        HMAC_Update(&hctx, pTemp + offset, DEFAULT_PAGESIZE - reserve - offset + IV_SIZE);        HMAC_Update(&hctx, (const unsigned char*)& nPage, sizeof(nPage));        HMAC_Final(&hctx, hash_mac, &hash_len);        HMAC_CTX_cleanup(&hctx);        if (0 != memcmp(hash_mac, pTemp + DEFAULT_PAGESIZE - reserve + IV_SIZE, sizeof(hash_mac)))        {            printf("\n 哈希值错误! \n");            getchar();            return 0;        }#endif        //        if (nPage == 1)        {            memcpy(pDecryptPerPageBuffer, SQLITE_FILE_HEADER, offset);        }
        EVP_CIPHER_CTX* ectx = EVP_CIPHER_CTX_new();        EVP_CipherInit_ex(ectx, EVP_get_cipherbyname("aes-256-cbc"), NULL, NULL, NULL, 0);        EVP_CIPHER_CTX_set_padding(ectx, 0);        EVP_CipherInit_ex(ectx, NULL, NULL, key, pTemp + (DEFAULT_PAGESIZE - reserve), 0);
        int nDecryptLen = 0;        int nTotal = 0;        EVP_CipherUpdate(ectx, pDecryptPerPageBuffer + offset, &nDecryptLen, pTemp + offset, DEFAULT_PAGESIZE - reserve - offset);        nTotal = nDecryptLen;        EVP_CipherFinal_ex(ectx, pDecryptPerPageBuffer + offset + nDecryptLen, &nDecryptLen);        nTotal += nDecryptLen;        EVP_CIPHER_CTX_free(ectx);
        memcpy(pDecryptPerPageBuffer + DEFAULT_PAGESIZE - reserve, pTemp + DEFAULT_PAGESIZE - reserve, reserve);        char decFile[1024] = { 0 };        sprintf_s(decFile, "dec_%s", dbfilename);        FILE * fp;        fopen_s(&fp, decFile, "ab+");        {            fwrite(pDecryptPerPageBuffer, 1, DEFAULT_PAGESIZE, fp);            fclose(fp);        }
        nPage++;        offset = 0;        pTemp += DEFAULT_PAGESIZE;    }    printf("\n 解密成功! \n");    return 0;}

将之前默认的代码全部清除,将以上代码拷贝进去,保存。然后在工具条栏中选择是Debug还是Release模式,是x86还是x64(需要跟之前配置匹配,如果选了没配置的模式会报错。测试发现几个选项没有太大区别,建议默认),之后点击“本地windows调试器”(或者按F5键),如果前面的步骤操作都正确,应该可以完成编译并自动运行,弹出一个命令行窗口,提示需要输入文件名:

最下方显示了生成的exe文件路径,将这个文件拷贝到微信数据库所在的目录,一般是:

C:\Users\Administrator\Documents\WeChat Files\********\Msg

其中********位置为需要解密的微信id,目录内容如下:

如果要解密ChatMsg.db,则在命令行窗口输入指令dewechat ChatMsg.db回车即可。

解密成功后,会在目录中生成de_ChatMsg.db,用sqlite数据库管理软件打开即可。

PC微信逆向:破解聊天记录文件!相关推荐

  1. Exp1 PC平台逆向破解(5)M

    Exp1 PC平台逆向破解(5)M 实践内容 实践对象是一个可执行文件pwn1 本程序的正常执行顺序应该是main函数调用foo函数,foo函数输出用户输入的字符串. 本程序还包含了另一个代码片段ge ...

  2. 2018-2019-2 20165234 《网络对抗技术》 Exp1 PC平台逆向破解

    实验一 PC平台逆向破解 实验目的 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串. 该程序同时包含另 ...

  3. 关于电脑版/PC微信如何恢复聊天记录【实践可行】

    再次试验,发现好像不行.不过组后一段是可以的. # 关于电脑版/PC微信如何恢复聊天记录[实践可行] 你是否在重装电脑,或者升级微信版本,或者误设置的时候发现,打开的微信里的聊天记录空空如也? 但是去 ...

  4. 20155232《网络对抗》 Exp1 PC平台逆向破解(5)M

    20155232<网络对抗> Exp1 PC平台逆向破解(5)M 实验内容 (1).掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码(1分) (2)掌握反汇编与十六进制编程 ...

  5. 20155220 吴思其 《网络攻防》 Exp1 PC平台逆向破解(5)M

    20155220 <网络攻防> Exp1 PC平台逆向破解(5)M 实践内容 通过对实践对象--20155220pwn1的linux可执行文件的修改或输入,完成以下三块: 手工修改可执行文 ...

  6. 20145330 《网络对抗》PC平台逆向破解:注入shellcode 和 Return-to-libc 攻击实验

    20145330 <网络对抗>PC平台逆向破解:注入shellcode 实验步骤 1.用于获取shellcode的C语言代码 2.设置环境 Bof攻击防御技术 需要手动设置环境使注入的sh ...

  7. 2019-2020-2 20175227张雪莹《网络对抗技术》 Exp1 PC平台逆向破解

    2019-2020-2 20175227张雪莹<网络对抗技术> Exp1 PC平台逆向破解 目录 0. 实验目标 1. 实验内容 1.1 直接修改程序机器指令,改变程序执行流程 1.2 通 ...

  8. PC微信逆向--调用sqlite3_exec执行SQL

    文章目录 写在前面 回顾 sqlite3_exec 回调函数 函数指针 编写代码 注入的DLL 注入程序 输出结果 写在后面 写在前面 上一篇文章,实现了定位保存微信数据库句柄的容器和微信内部的sql ...

  9. PC微信逆向:两种姿势教你解密数据库文件

    文章目录 定位数据库文件密码 定位数据库密钥的思路 获取数据库密钥的实战分析 CreateFileW断点 常见错误 排查堆栈 排查堆栈地址 单步跟踪 用代码实现解密数据库 编译选项 解密代码 实际效果 ...

  10. PC微信逆向--定位sqlite3_exec和数据库句柄

    写在前面 最近在做PC端微信逆向,搞定了基本的收发消息,通讯录获取等,这期间遇到一个小小的问题,从通讯录获取到的内容不全,除非登录后手动点击过某个好友,不然获取不到头像.V3等,所以产生了解密数据库的 ...

最新文章

  1. Et.parse(xml) #39gbk#39 codec cant decode byte
  2. python编码规范手册-PEP8 Python 编码规范整理
  3. 【架构】技术-工具-平台-语言框架
  4. Codeforces 757C - Felicity is Coming!
  5. Uipath 学习栏目基础教学:2Uipath变量介绍
  6. 逻辑漏洞挖掘初步总结篇
  7. 【转】MySQL日期函数与日期转换格式化函数大全
  8. PGpool 编译出错
  9. 请收下这10篇2020上半年最受欢迎的数据库技术文章
  10. java getcause_java - 如果在异常上调用getCause(),为什么还要处理Throwable - 堆栈内存溢出...
  11. OA+CRM+ERP
  12. MySQL INSERT ... ON DUPLICATE KEY UPDATE语句
  13. 离奇的 FRM-18108 ,FRM-10102
  14. 旷视发布招股书,一起看看CV四小龙中的三小龙那些有意思的事情
  15. 列宽一字符等于多少厘米_Excel中行高多少等于1厘米?列宽多少等于1厘米?-列宽为2厘米...
  16. 诺基亚7plus支持html,【诺基亚7Plus评测】诺基亚7Plus评测:三蔡司镜头“全面”来袭(全文)_诺基亚 7 Plus(4GB RAM/全网通)_手机评测-中关村在线...
  17. Excel怎么转换成PDF?这两种转换方法看到就是赚到
  18. error C2144: syntax error: 'int' should be preceded by ';'
  19. 阿里服务器配置随笔记 centos 服务器 Linux 部分命令合集
  20. RF-测试目录以及库引用、变量使用

热门文章

  1. FreeCAD源码分析:FreeCADApp模块
  2. FTP,SFTP,FTPS,TF区别
  3. 如何简单地设置一个LoRa网关?
  4. 程序员王不留到底干了啥?!
  5. 硬件工程师学习英语必备
  6. pyserial的踩坑记录
  7. EXFO 收购RF over CPRI 测试方案提供商Absolute Analysis
  8. springboot实现简单的单点登录
  9. Pycharm破解(学习python的day01)
  10. MFC学生信息管理系统