劫持微信聊天记录并分析还原 —— 解密数据库(二)

  • 本工具设计的初衷是用来获取微信账号的相关信息并解析PC版微信的数据库。

  • 程序以 Python 语言开发,可读取、解密、还原微信数据库并帮助用户查看聊天记录,还可以将其聊天记录导出为csv、html等格式用于AI训练,自动回复或备份等等作用。下面我们将深入探讨这个工具的各个方面及其工作原理。


【完整演示工具下载】

https://www.chwm.vip/index.html?aid=23


  1. 我们先根据上一篇文章《劫持微信聊天记录并分析还原 —— 帐号信息截取(一)》截取的帐号信息来尝试解密微信数据库。

  1. 详细命令:
    decrypt -k "04395c**********5e47d8" -i "C:\Users\admin\Documents\WeChat Files\wxid_b11\Msg" -o "C:\Users\admin\AppData\Local\Temp\wx_tmp"

3.参数详解:
-k 为指定key密匙 04395c******************************5e47d8
-i 为微信数据库路径 wx_dir + Msg
-o 为解密存放的路径  C:\Users\admin\AppData\Local\Temp\wx_tmp(系统临时目录)

  1. 然后我们可以看到提示解密 114 个文件,成功 36 个,失败 78 个。其中显示失败的文件我们可以忽略(因为有些并不是数据库文件),只要有一个成功的那么就是解密成功。


部分实现代码

# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name:         decryption.py
# Description:
# Author:       Rainbow
# Date:         2024/11/05
# 微信数据库采用的加密算法是256位的AES-CBC。数据库的默认的页大小是4096字节即4KB,其中每一个页都是被单独加解密的。
# 加密文件的每一个页都有一个随机的初始化向量,它被保存在每一页的末尾。
# 加密文件的每一页都存有着消息认证码,算法使用的是HMAC-SHA1(安卓数据库使用的是SHA512)。它也被保存在每一页的末尾。
# 每一个数据库文件的开头16字节都保存了一段唯一且随机的盐值,作为HMAC的验证和数据的解密。
# 为了保证数据部分长度是16字节即AES块大小的整倍数,每一页的末尾将填充一段空字节,使得保留字段的长度为48字节。
# 综上,加密文件结构为第一页4KB数据前16字节为盐值,紧接着4032字节数据,再加上16字节IV和20字节HMAC以及12字节空字节;而后的页均是4048字节长度的加密数据段和48字节的保留段。
# -------------------------------------------------------------------------------
import hmac
import hashlib
import os
from typing import Union, List
from Cryptodome.Cipher import AES
# from Crypto.Cipher import AES # 如果上面的导入失败,可以尝试使用这个from .utils import wx_core_error, wx_core_logerSQLITE_FILE_HEADER = "SQLite format 3\x00"  # SQLite文件头KEY_SIZE = 32
DEFAULT_PAGESIZE = 4096# 通过密钥解密数据库
@wx_core_error
def decrypt(key: str, db_path: str, out_path: str):"""通过密钥解密数据库:param key: 密钥 64位16进制字符串:param db_path:  待解密的数据库路径(必须是文件):param out_path:  解密后的数据库输出路径(必须是文件):return:"""if not os.path.exists(db_path) or not os.path.isfile(db_path):return False, f"[-] db_path:'{db_path}' File not found!"if not os.path.exists(os.path.dirname(out_path)):return False, f"[-] out_path:'{out_path}' File not found!"if len(key) != 64:return False, f"[-] key:'{key}' Len Error!"password = bytes.fromhex(key.strip())try:with open(db_path, "rb") as file:blist = file.read()except Exception as e:return False, f"[-] db_path:'{db_path}' {e}!"salt = blist[:16]first = blist[16:4096]if len(salt) != 16:return False, f"[-] db_path:'{db_path}' File Error!"mac_salt = bytes([(salt[i] ^ 58) for i in range(16)])byteHmac = hashlib.pbkdf2_hmac("sha1", password, salt, 64000, KEY_SIZE)mac_key = hashlib.pbkdf2_hmac("sha1", byteHmac, mac_salt, 2, KEY_SIZE)hash_mac = hmac.new(mac_key, blist[16:4064], hashlib.sha1)hash_mac.update(b'\x01\x00\x00\x00')if hash_mac.digest() != first[-32:-12]:return False, f"[-] Key Error! (key:'{key}'; db_path:'{db_path}'; out_path:'{out_path}' )"with open(out_path, "wb") as deFile:deFile.write(SQLITE_FILE_HEADER.encode())for i in range(0, len(blist), 4096):tblist = blist[i:i + 4096] if i > 0 else blist[16:i + 4096]deFile.write(AES.new(byteHmac, AES.MODE_CBC, tblist[-48:-32]).decrypt(tblist[:-48]))deFile.write(tblist[-48:])return True, [db_path, out_path, key]@wx_core_error
def batch_decrypt(key: str, db_path: Union[str, List[str]], out_path: str, is_print: bool = False):"""批量解密数据库:param key: 密钥 64位16进制字符串:param db_path: 待解密的数据库路径(文件或文件夹):param out_path: 解密后的数据库输出路径(文件夹):param is_logging: 是否打印日志:return: (bool, [[input_db_path, output_db_path, key],...])"""if not isinstance(key, str) or not isinstance(out_path, str) or not os.path.exists(out_path) or len(key) != 64:error = f"[-] (key:'{key}' or out_path:'{out_path}') Error!"wx_core_loger.error(error, exc_info=True)return False, errorprocess_list = []if isinstance(db_path, str):if not os.path.exists(db_path):error = f"[-] db_path:'{db_path}' not found!"wx_core_loger.error(error, exc_info=True)return False, errorif os.path.isfile(db_path):inpath = db_pathoutpath = os.path.join(out_path, 'de_' + os.path.basename(db_path))process_list.append([key, inpath, outpath])elif os.path.isdir(db_path):for root, dirs, files in os.walk(db_path):for file in files:inpath = os.path.join(root, file)rel = os.path.relpath(root, db_path)outpath = os.path.join(out_path, rel, 'de_' + file)if not os.path.exists(os.path.dirname(outpath)):os.makedirs(os.path.dirname(outpath))process_list.append([key, inpath, outpath])else:error = f"[-] db_path:'{db_path}' Error "wx_core_loger.error(error, exc_info=True)return False, errorelif isinstance(db_path, list):rt_path = os.path.commonprefix(db_path)if not os.path.exists(rt_path):rt_path = os.path.dirname(rt_path)for inpath in db_path:if not os.path.exists(inpath):error = f"[-] db_path:'{db_path}' not found!"wx_core_loger.error(error, exc_info=True)return False, errorinpath = os.path.normpath(inpath)rel = os.path.relpath(os.path.dirname(inpath), rt_path)outpath = os.path.join(out_path, rel, 'de_' + os.path.basename(inpath))if not os.path.exists(os.path.dirname(outpath)):os.makedirs(os.path.dirname(outpath))process_list.append([key, inpath, outpath])else:error = f"[-] db_path:'{db_path}' Error "wx_core_loger.error(error, exc_info=True)return False, errorresult = []for i in process_list:result.append(decrypt(*i))  # 解密# 删除空文件夹for root, dirs, files in os.walk(out_path, topdown=False):for dir in dirs:if not os.listdir(os.path.join(root, dir)):os.rmdir(os.path.join(root, dir))if is_print:print("=" * 32)success_count = 0fail_count = 0for code, ret in result:if code == False:print(ret)fail_count += 1else:print(f'[+] "{ret[0]}" -> "{ret[1]}"')success_count += 1print("-" * 32)print(f"[+] 共 {len(result)} 个文件, 成功 {success_count} 个, 失败 {fail_count} 个")print("=" * 32)return True, result

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/827249.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024newstarweb题解

w1 headach3会赢吗 源码flag碎片X1: ZmxhZ3tXQTB3 再次查看源码flag碎片X2: IV95NF9yM2Fs 第三个页面也是直接查看源码直接改源码flag碎片X3: MXlfR3I0c1B 下一个页面直接禁用jsflag碎片X4: fSkpKcyF9 ZmxhZ3tXQTB3IV95NF9yM2FsMXlfR3I0c1BfSkpKcyF9 base64解码即可的flag智械危…

判断一个数是不是质数(素数)

​public static boolean isPrime(int n){if (n <= 3) { return n > 1;) for(int i = 2; i < n; i++){if (n % i == 0) { return false;} ) return true;} 质数(也称为素数)是指在大于1的自然数中,除了1和它本身以外不再有其他因数的数。换句话说,质数只能被1和它…

Roaring Bitmap

Roaring Bitmap原理 Roaring Bitmaps 就是一种压缩位图索引,后文统称 RBM,RBM 的用途和 Bitmap 很差不多(比如说索引),只是说从性能、空间利用率各方面更优秀了。 RBM 的主要思想并不复杂,简单来讲,有如下三条:我们将 32-bit 的范围 ([0, n)) 划分为 2^16 个桶,每一个…

36 自然语言处理

自然语言,人类语言 将语言拆分,然后处理 语音识别:语音转文字,本质是音素识别 语音合成

力扣新手村之1342、1672、412

LeetCode: 1342[将数字变成0的操作次数] 1672[最富有客户的资产总量] 412[Fizz Buzz]1342[将数字变成0的操作次数] 题目 链接 LeetCode1342[将数字变成0的操作次数] 详情实例 实例1实例2实例3提示题解 思路 判断 num 是否为0 不为0则判断 num 是否为偶数 num 是偶数则除以2 nu…

什么是java序列化?什么情况下需要序列化?

序列化的定义 Java 序列化是为了保存各种对象在内存中的状态,并且可以把保存的对象状态再读出来。序列化是一种用于处理对象流的机制,它将对象的内容转换成一种可以在网络之间传输的形式。反序列化则是将这种形式的对象恢复成原来的对象。 实现方式 序列化是通过实现​​Seri…

国内首位聋人 Android 软件工程师体验通义灵码,“这真是太棒了”

通义灵码 @workspace 功能发布后,收到了非常多新老朋友的积极反馈,其中被一位特别的朋友留下了深刻的印象。Hi 大家好! 我就是人见人爱、Bug 闪开的通义灵码!上个月,我上线了一项新能力: 体验通义灵码 @workspace:轻松分析项目结构,结合代码仓库理解工程、查询问答等补…

诛仙3:幻心千劫|单机安装教程|虚拟机一键端|GM工具包

天给大家带来一款单机游戏的架设:诛仙3-幻心千劫-16职业。游戏版本:v4.4.0 只适用于单机娱乐,此教程是本人亲测所写,踩坑无数,如果你是小白跟着教程走也是可以搭建 亲测视频演示 https://githubs.xyz/show/297.mp4游戏安装步骤 此游戏架设需要安装虚拟机,没有虚拟机的请…

2024.11.5总结

哦哦哦A: 题目可变为:从一个点 \(x\) 走到左侧或右侧第一个 \(\geq y(y \leq x)\) 的位置需要花费 \(l_{x}\) 或 \(r_{x}\) 的代价,多次查询最短路。 首先观察一个点 \(x\) 只往 \(a\) 值高于自己的位置走能走到哪些点。 找到左侧从 \(x\) 出发的后缀 \(\max\) ,右侧从 \(x…

设计模式速览

设计模式速览前言:资料来源吉大设计模式课程,自用 只提取应试回忆关键部分,省略优缺点说明,详细应用之类,扩展挑了常出现的1. 概述 1.1 类间关系 1.1.1 依赖(dependency): ​ 一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但是B类的变化…

try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗

finally 一定会执行,即使是 catch 中 return 了,catch 中的 return 会等 finally 中的代码执行完之后,才会执行。下面提供了一段示例代码和运行结果。问题的核心在于当​​catch​​​块中有​​return​​​语句时,​​finally​​块是否还会被执行。 示例代码解析public …