ToB企服应用市场:ToB评测及商务社交产业平台

标题: 劫持微信聊天记录并分析还原 —— 解密数据库(二) [打印本页]

作者: 王海鱼    时间: 2024-11-5 21:32
标题: 劫持微信聊天记录并分析还原 —— 解密数据库(二)





【完备演示工具下载】
https://www.chwm.vip/index.html?aid=23





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




部分实现代码
  1. # -*- coding: utf-8 -*-#
  2. # -------------------------------------------------------------------------------
  3. # Name:         decryption.py
  4. # Description:
  5. # Author:       Rainbow
  6. # Date:         2024/11/05
  7. # 微信数据库采用的加密算法是256位的AES-CBC。数据库的默认的页大小是4096字节即4KB,其中每一个页都是被单独加解密的。
  8. # 加密文件的每一个页都有一个随机的初始化向量,它被保存在每一页的末尾。
  9. # 加密文件的每一页都存有着消息认证码,算法使用的是HMAC-SHA1(安卓数据库使用的是SHA512)。它也被保存在每一页的末尾。
  10. # 每一个数据库文件的开头16字节都保存了一段唯一且随机的盐值,作为HMAC的验证和数据的解密。
  11. # 为了保证数据部分长度是16字节即AES块大小的整倍数,每一页的末尾将填充一段空字节,使得保留字段的长度为48字节。
  12. # 综上,加密文件结构为第一页4KB数据前16字节为盐值,紧接着4032字节数据,再加上16字节IV和20字节HMAC以及12字节空字节;而后的页均是4048字节长度的加密数据段和48字节的保留段。
  13. # -------------------------------------------------------------------------------
  14. import hmac
  15. import hashlib
  16. import os
  17. from typing import Union, List
  18. from Cryptodome.Cipher import AES
  19. # from Crypto.Cipher import AES # 如果上面的导入失败,可以尝试使用这个
  20. from .utils import wx_core_error, wx_core_loger
  21. SQLITE_FILE_HEADER = "SQLite format 3\x00"  # SQLite文件头
  22. KEY_SIZE = 32
  23. DEFAULT_PAGESIZE = 4096
  24. # 通过密钥解密数据库
  25. @wx_core_error
  26. def decrypt(key: str, db_path: str, out_path: str):
  27.     """
  28.     通过密钥解密数据库
  29.     :param key: 密钥 64位16进制字符串
  30.     :param db_path:  待解密的数据库路径(必须是文件)
  31.     :param out_path:  解密后的数据库输出路径(必须是文件)
  32.     :return:
  33.     """
  34.     if not os.path.exists(db_path) or not os.path.isfile(db_path):
  35.         return False, f"[-] db_path:'{db_path}' File not found!"
  36.     if not os.path.exists(os.path.dirname(out_path)):
  37.         return False, f"[-] out_path:'{out_path}' File not found!"
  38.     if len(key) != 64:
  39.         return False, f"[-] key:'{key}' Len Error!"
  40.     password = bytes.fromhex(key.strip())
  41.     try:
  42.         with open(db_path, "rb") as file:
  43.             blist = file.read()
  44.     except Exception as e:
  45.         return False, f"[-] db_path:'{db_path}' {e}!"
  46.     salt = blist[:16]
  47.     first = blist[16:4096]
  48.     if len(salt) != 16:
  49.         return False, f"[-] db_path:'{db_path}' File Error!"
  50.     mac_salt = bytes([(salt[i] ^ 58) for i in range(16)])
  51.     byteHmac = hashlib.pbkdf2_hmac("sha1", password, salt, 64000, KEY_SIZE)
  52.     mac_key = hashlib.pbkdf2_hmac("sha1", byteHmac, mac_salt, 2, KEY_SIZE)
  53.     hash_mac = hmac.new(mac_key, blist[16:4064], hashlib.sha1)
  54.     hash_mac.update(b'\x01\x00\x00\x00')
  55.     if hash_mac.digest() != first[-32:-12]:
  56.         return False, f"[-] Key Error! (key:'{key}'; db_path:'{db_path}'; out_path:'{out_path}' )"
  57.     with open(out_path, "wb") as deFile:
  58.         deFile.write(SQLITE_FILE_HEADER.encode())
  59.         for i in range(0, len(blist), 4096):
  60.             tblist = blist[i:i + 4096] if i > 0 else blist[16:i + 4096]
  61.             deFile.write(AES.new(byteHmac, AES.MODE_CBC, tblist[-48:-32]).decrypt(tblist[:-48]))
  62.             deFile.write(tblist[-48:])
  63.     return True, [db_path, out_path, key]
  64. @wx_core_error
  65. def batch_decrypt(key: str, db_path: Union[str, List[str]], out_path: str, is_print: bool = False):
  66.     """
  67.     批量解密数据库
  68.     :param key: 密钥 64位16进制字符串
  69.     :param db_path: 待解密的数据库路径(文件或文件夹)
  70.     :param out_path: 解密后的数据库输出路径(文件夹)
  71.     :param is_logging: 是否打印日志
  72.     :return: (bool, [[input_db_path, output_db_path, key],...])
  73.     """
  74.     if not isinstance(key, str) or not isinstance(out_path, str) or not os.path.exists(out_path) or len(key) != 64:
  75.         error = f"[-] (key:'{key}' or out_path:'{out_path}') Error!"
  76.         wx_core_loger.error(error, exc_info=True)
  77.         return False, error
  78.     process_list = []
  79.     if isinstance(db_path, str):
  80.         if not os.path.exists(db_path):
  81.             error = f"[-] db_path:'{db_path}' not found!"
  82.             wx_core_loger.error(error, exc_info=True)
  83.             return False, error
  84.         if os.path.isfile(db_path):
  85.             inpath = db_path
  86.             outpath = os.path.join(out_path, 'de_' + os.path.basename(db_path))
  87.             process_list.append([key, inpath, outpath])
  88.         elif os.path.isdir(db_path):
  89.             for root, dirs, files in os.walk(db_path):
  90.                 for file in files:
  91.                     inpath = os.path.join(root, file)
  92.                     rel = os.path.relpath(root, db_path)
  93.                     outpath = os.path.join(out_path, rel, 'de_' + file)
  94.                     if not os.path.exists(os.path.dirname(outpath)):
  95.                         os.makedirs(os.path.dirname(outpath))
  96.                     process_list.append([key, inpath, outpath])
  97.         else:
  98.             error = f"[-] db_path:'{db_path}' Error "
  99.             wx_core_loger.error(error, exc_info=True)
  100.             return False, error
  101.     elif isinstance(db_path, list):
  102.         rt_path = os.path.commonprefix(db_path)
  103.         if not os.path.exists(rt_path):
  104.             rt_path = os.path.dirname(rt_path)
  105.         for inpath in db_path:
  106.             if not os.path.exists(inpath):
  107.                 error = f"[-] db_path:'{db_path}' not found!"
  108.                 wx_core_loger.error(error, exc_info=True)
  109.                 return False, error
  110.             inpath = os.path.normpath(inpath)
  111.             rel = os.path.relpath(os.path.dirname(inpath), rt_path)
  112.             outpath = os.path.join(out_path, rel, 'de_' + os.path.basename(inpath))
  113.             if not os.path.exists(os.path.dirname(outpath)):
  114.                 os.makedirs(os.path.dirname(outpath))
  115.             process_list.append([key, inpath, outpath])
  116.     else:
  117.         error = f"[-] db_path:'{db_path}' Error "
  118.         wx_core_loger.error(error, exc_info=True)
  119.         return False, error
  120.     result = []
  121.     for i in process_list:
  122.         result.append(decrypt(*i))  # 解密
  123.     # 删除空文件夹
  124.     for root, dirs, files in os.walk(out_path, topdown=False):
  125.         for dir in dirs:
  126.             if not os.listdir(os.path.join(root, dir)):
  127.                 os.rmdir(os.path.join(root, dir))
  128.     if is_print:
  129.         print("=" * 32)
  130.         success_count = 0
  131.         fail_count = 0
  132.         for code, ret in result:
  133.             if code == False:
  134.                 print(ret)
  135.                 fail_count += 1
  136.             else:
  137.                 print(f'[+] "{ret[0]}" -> "{ret[1]}"')
  138.                 success_count += 1
  139.         print("-" * 32)
  140.         print(f"[+] 共 {len(result)} 个文件, 成功 {success_count} 个, 失败 {fail_count} 个")
  141.         print("=" * 32)
  142.     return True, result
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4