mirror of
https://github.com/LifeArchiveProject/WeChatDataAnalysis.git
synced 2026-02-19 14:20:51 +08:00
chore(tools): 添加解密与资源调试脚本
- 增加解密/资源/表情/媒体定位等调试脚本,便于本地排查与验证
This commit is contained in:
145
tools/debug_decrypt_file.py
Normal file
145
tools/debug_decrypt_file.py
Normal file
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env python3
|
||||
"""直接测试文件解密逻辑"""
|
||||
|
||||
import sys
|
||||
sys.path.insert(0, "src")
|
||||
|
||||
import json
|
||||
import struct
|
||||
from pathlib import Path
|
||||
|
||||
# 测试参数
|
||||
ACCOUNT_DIR = Path(r"d:\abc\PycharmProjects\WeChatDataAnalysis\output\databases\wxid_v4mbduwqtzpt22")
|
||||
TEST_FILE = Path(r"D:\abc\wechatMSG\xwechat_files\wxid_v4mbduwqtzpt22_1e7a\msg\attach\0d6a4127daada32c5e407ae7201e785a\2025-12\Img\0923ad357c321cf286b794f8e5a66333.dat")
|
||||
WXID_DIR = Path(r"D:\abc\wechatMSG\xwechat_files\wxid_v4mbduwqtzpt22_1e7a")
|
||||
|
||||
# ========== 1. 读取密钥 ==========
|
||||
print("[1] 读取密钥文件")
|
||||
keys_file = ACCOUNT_DIR / "_media_keys.json"
|
||||
if keys_file.exists():
|
||||
with open(keys_file, "r", encoding="utf-8") as f:
|
||||
keys = json.load(f)
|
||||
print(f" keys = {keys}")
|
||||
xor_key = keys.get("xor")
|
||||
aes_str = str(keys.get("aes") or "").strip()
|
||||
aes_key16 = aes_str.encode("ascii", errors="ignore")[:16] if aes_str else b""
|
||||
print(f" xor_key = {xor_key}")
|
||||
print(f" aes_key16 = {aes_key16}")
|
||||
else:
|
||||
print(" [ERROR] 密钥文件不存在")
|
||||
sys.exit(1)
|
||||
|
||||
# ========== 2. 读取测试文件 ==========
|
||||
print(f"\n[2] 读取测试文件: {TEST_FILE}")
|
||||
with open(TEST_FILE, "rb") as f:
|
||||
data = f.read()
|
||||
print(f" 文件大小: {len(data)} bytes")
|
||||
print(f" 前 16 字节: {data[:16].hex()}")
|
||||
|
||||
# ========== 3. 检测版本 ==========
|
||||
print("\n[3] 检测文件版本")
|
||||
sig = data[:6]
|
||||
if sig == b"\x07\x08V1\x08\x07":
|
||||
version = 1
|
||||
print(" 版本: V1")
|
||||
elif sig == b"\x07\x08V2\x08\x07":
|
||||
version = 2
|
||||
print(" 版本: V2")
|
||||
else:
|
||||
version = 0
|
||||
print(" 版本: V0 (纯 XOR)")
|
||||
|
||||
# ========== 4. 尝试解密 ==========
|
||||
print("\n[4] 尝试解密")
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util import Padding
|
||||
|
||||
def decrypt_v4(data: bytes, xor_key: int, aes_key: bytes) -> bytes:
|
||||
"""使用 api.py 相同的解密逻辑"""
|
||||
header, rest = data[:0xF], data[0xF:]
|
||||
print(f" 头部 (15 bytes): {header.hex()}")
|
||||
|
||||
signature, aes_size, xor_size = struct.unpack("<6sLLx", header)
|
||||
print(f" signature: {signature}")
|
||||
print(f" aes_size: {aes_size}")
|
||||
print(f" xor_size: {xor_size}")
|
||||
|
||||
# 对齐到 AES 块大小
|
||||
aes_size_aligned = aes_size + (AES.block_size - aes_size % AES.block_size) if aes_size % AES.block_size != 0 else aes_size
|
||||
print(f" aes_size_aligned: {aes_size_aligned}")
|
||||
|
||||
aes_data = rest[:aes_size_aligned]
|
||||
print(f" aes_data 长度: {len(aes_data)}")
|
||||
print(f" aes_data 前 16 字节: {aes_data[:16].hex()}")
|
||||
|
||||
cipher = AES.new(aes_key[:16], AES.MODE_ECB)
|
||||
decrypted_aes_raw = cipher.decrypt(aes_data)
|
||||
print(f" 解密后 (带 padding) 前 16 字节: {decrypted_aes_raw[:16].hex()}")
|
||||
|
||||
try:
|
||||
decrypted_data = Padding.unpad(decrypted_aes_raw, AES.block_size)
|
||||
print(f" 去 padding 后长度: {len(decrypted_data)}")
|
||||
except Exception as e:
|
||||
print(f" [WARN] unpad 失败: {e}, 使用原始数据")
|
||||
decrypted_data = decrypted_aes_raw
|
||||
|
||||
if xor_size > 0:
|
||||
raw_data = rest[aes_size_aligned:-xor_size]
|
||||
xor_data = rest[-xor_size:]
|
||||
xored_data = bytes(b ^ xor_key for b in xor_data)
|
||||
print(f" raw_data 长度: {len(raw_data)}")
|
||||
print(f" xor_data 长度: {len(xor_data)}")
|
||||
else:
|
||||
raw_data = rest[aes_size_aligned:]
|
||||
xored_data = b""
|
||||
print(f" raw_data 长度: {len(raw_data)}")
|
||||
|
||||
result = decrypted_data + raw_data + xored_data
|
||||
print(f" 最终结果长度: {len(result)}")
|
||||
print(f" 结果前 16 字节: {result[:16].hex()}")
|
||||
|
||||
# 检查是否是有效图片
|
||||
if result[:3] == b"\xff\xd8\xff":
|
||||
print(" [OK] 解密成功! 是 JPEG 图片")
|
||||
elif result[:8] == b"\x89PNG\r\n\x1a\n":
|
||||
print(" [OK] 解密成功! 是 PNG 图片")
|
||||
else:
|
||||
print(" [WARN] 解密后不是有效图片头")
|
||||
|
||||
return result
|
||||
|
||||
if version == 2 and xor_key is not None and aes_key16:
|
||||
print("\n[4.1] 使用本地 decrypt_v4 函数:")
|
||||
decrypted = decrypt_v4(data, xor_key, aes_key16)
|
||||
|
||||
# 保存解密后的文件
|
||||
output_file = Path("test_decrypted_manual.jpg")
|
||||
with open(output_file, "wb") as f:
|
||||
f.write(decrypted)
|
||||
print(f" 已保存: {output_file} ({len(decrypted)} bytes)")
|
||||
|
||||
# 使用 WxDatDecrypt 的函数
|
||||
print("\n[4.2] 使用 WxDatDecrypt 的 decrypt_dat_v4:")
|
||||
sys.path.insert(0, "WxDatDecrypt")
|
||||
from decrypt import decrypt_dat_v4 as wx_decrypt_v4
|
||||
|
||||
decrypted_wx = wx_decrypt_v4(TEST_FILE, xor_key, aes_key16)
|
||||
print(f" 结果长度: {len(decrypted_wx)}")
|
||||
print(f" 结果前 16 字节: {decrypted_wx[:16].hex()}")
|
||||
|
||||
if decrypted_wx[:3] == b"\xff\xd8\xff":
|
||||
print(" [OK] 解密成功! 是 JPEG 图片")
|
||||
elif decrypted_wx[:8] == b"\x89PNG\r\n\x1a\n":
|
||||
print(" [OK] 解密成功! 是 PNG 图片")
|
||||
else:
|
||||
print(" [WARN] 解密后不是有效图片头")
|
||||
|
||||
output_file2 = Path("test_decrypted_wxdat.jpg")
|
||||
with open(output_file2, "wb") as f:
|
||||
f.write(decrypted_wx)
|
||||
print(f" 已保存: {output_file2} ({len(decrypted_wx)} bytes)")
|
||||
else:
|
||||
print(" [ERROR] 无法解密: 缺少必要参数")
|
||||
|
||||
print("\n[Done]")
|
||||
Reference in New Issue
Block a user