使用python重构
This commit is contained in:
parent
93a0c7e43b
commit
6bccd9a9d5
@ -187,7 +187,7 @@ def read_info(version_list):
|
||||
return rd
|
||||
|
||||
if __name__ == "__main__":
|
||||
version_list = json.load(open("version_list.json", "r", encoding="utf-8"))
|
||||
version_list = json.load(open("../version_list.json", "r", encoding="utf-8"))
|
||||
rd = read_info(version_list)
|
||||
for i in rd:
|
||||
for k, v in i.items():
|
11
decrypted/__init__.py
Normal file
11
decrypted/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
# -*- coding: utf-8 -*-#
|
||||
# -------------------------------------------------------------------------------
|
||||
# Name: __init__.py.py
|
||||
# Description:
|
||||
# Author: xaoyaoo
|
||||
# Date: 2023/08/21
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
301
decrypted/decrypt1.py
Normal file
301
decrypted/decrypt1.py
Normal file
@ -0,0 +1,301 @@
|
||||
import glob
|
||||
import os
|
||||
import hmac
|
||||
import hashlib
|
||||
import re
|
||||
import shutil
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import winreg
|
||||
|
||||
from Cryptodome.Cipher import AES
|
||||
|
||||
SQLITE_FILE_HEADER = "SQLite format 3\x00"
|
||||
IV_SIZE = 16
|
||||
HMAC_SHA1_SIZE = 20
|
||||
KEY_SIZE = 32
|
||||
DEFAULT_PAGESIZE = 4096
|
||||
DEFAULT_ITER = 64000
|
||||
|
||||
|
||||
# 通过密钥解密数据库
|
||||
def decrypt(key, filePath, decryptedPath):
|
||||
password = bytes.fromhex(key.replace(" ", ""))
|
||||
with open(filePath, "rb") as file:
|
||||
blist = file.read()
|
||||
|
||||
salt = blist[:16]
|
||||
byteKey = hashlib.pbkdf2_hmac("sha1", password, salt, DEFAULT_ITER, KEY_SIZE)
|
||||
first = blist[16:DEFAULT_PAGESIZE]
|
||||
|
||||
mac_salt = bytes([(salt[i] ^ 58) for i in range(16)])
|
||||
mac_key = hashlib.pbkdf2_hmac("sha1", byteKey, mac_salt, 2, KEY_SIZE)
|
||||
hash_mac = hmac.new(mac_key, first[:-32], hashlib.sha1)
|
||||
hash_mac.update(b'\x01\x00\x00\x00')
|
||||
|
||||
if hash_mac.digest() == first[-32:-12]:
|
||||
print("Decryption Success")
|
||||
else:
|
||||
print("Password Error")
|
||||
|
||||
newblist = [blist[i:i + DEFAULT_PAGESIZE] for i in range(DEFAULT_PAGESIZE, len(blist), DEFAULT_PAGESIZE)]
|
||||
|
||||
with open(decryptedPath, "wb") as deFile:
|
||||
deFile.write(SQLITE_FILE_HEADER.encode())
|
||||
t = AES.new(byteKey, AES.MODE_CBC, first[-48:-32])
|
||||
decrypted = t.decrypt(first[:-48])
|
||||
deFile.write(decrypted)
|
||||
deFile.write(first[-48:])
|
||||
|
||||
for i in newblist:
|
||||
t = AES.new(byteKey, AES.MODE_CBC, i[-48:-32])
|
||||
decrypted = t.decrypt(i[:-48])
|
||||
deFile.write(decrypted)
|
||||
deFile.write(i[-48:])
|
||||
|
||||
|
||||
# 通过外部程序获取微信数据库的key
|
||||
def get_wx_key():
|
||||
"""
|
||||
执行 GoWxDump.exe -wxinfo 获取微信数据库的key
|
||||
:return:
|
||||
"""
|
||||
# 获取当前文件路径的上一级目录
|
||||
current_path = os.path.dirname(os.path.abspath(__file__))
|
||||
current_path = os.path.abspath(os.path.join(current_path, "../.."))
|
||||
# 获取GoWxDump.exe的路径
|
||||
gowxdump_path = os.path.join(current_path, "Release", "GoWxDump.exe")
|
||||
# 判断GoWxDump.exe是否存在
|
||||
if not os.path.exists(gowxdump_path):
|
||||
print("GoWxDump.exe not found")
|
||||
return
|
||||
command = gowxdump_path + " -wxinfo"
|
||||
output = subprocess.check_output(command, shell=True, encoding='latin-1')
|
||||
|
||||
wx_key = output.split("WeChat Key:")[-1].strip()
|
||||
return wx_key
|
||||
|
||||
|
||||
# 获取微信数据根目录
|
||||
def get_wechat_dir():
|
||||
"""
|
||||
读取注册表获取微信消息目录
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
# 打开注册表的微信路径:HKEY_CURRENT_USER\Software\Tencent\WeChat\FileSavePath
|
||||
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Tencent\WeChat", 0, winreg.KEY_READ)
|
||||
# 获取key的值
|
||||
value, _ = winreg.QueryValueEx(key, "FileSavePath")
|
||||
# 关闭注册表项
|
||||
winreg.CloseKey(key)
|
||||
w_dir = value
|
||||
except Exception as e:
|
||||
print("读取注册表错误:", str(e))
|
||||
return str(e)
|
||||
|
||||
# 如果 w_dir 为 "MyDocument:"
|
||||
if w_dir == "MyDocument:":
|
||||
# 获取 %USERPROFILE%/Documents 目录
|
||||
profile = os.path.expanduser("~")
|
||||
# 获取微信消息目录
|
||||
msg_dir = os.path.join(profile, "Documents", "WeChat Files")
|
||||
else:
|
||||
# 获取微信消息目录
|
||||
msg_dir = os.path.join(w_dir, "WeChat Files")
|
||||
# 判断目录是否存在
|
||||
if not os.path.exists(msg_dir):
|
||||
raise FileNotFoundError("目录不存在")
|
||||
return msg_dir
|
||||
|
||||
|
||||
# 获取微信消息目录下的所有用户目录
|
||||
def get_wechat_user_dir(wechat_root):
|
||||
"""
|
||||
// 获取微信消息目录下的所有用户目录,排除All Users目录和Applet目录,返回一个map,key用户id,value用户目录
|
||||
:param wechat_root: 微信消息目录
|
||||
:return:
|
||||
"""
|
||||
user_dirs = {}
|
||||
# 获取微信消息目录下的所有用户目录
|
||||
files = os.listdir(wechat_root)
|
||||
for file_name in files:
|
||||
# 排除All Users目录和Applet目录
|
||||
if file_name == "All Users" or file_name == "Applet" or file_name == "WMPF":
|
||||
continue
|
||||
user_dirs[file_name] = os.path.join(wechat_root, file_name)
|
||||
return user_dirs
|
||||
|
||||
|
||||
# copy msg.db到tmp目录,并创建decrypted目录
|
||||
def copy_msg_db(data_dir):
|
||||
# 判断目录是否存在
|
||||
if not os.path.exists(data_dir):
|
||||
raise FileNotFoundError("目录不存在")
|
||||
|
||||
# 判断运行目录是否存在tmp目录,如果不存在则创建
|
||||
tmp_dir = os.path.join(os.getcwd(), "tmp")
|
||||
if not os.path.exists(tmp_dir):
|
||||
os.mkdir(tmp_dir)
|
||||
|
||||
# 正则匹配,将所有MSG数字.db文件拷贝到tmp目录,不扫描子目录
|
||||
for root, dirs, files in os.walk(data_dir):
|
||||
for file_name in files:
|
||||
if re.match(r".*MSG.*\.db", file_name):
|
||||
src_path = os.path.join(root, file_name)
|
||||
dst_path = os.path.join(tmp_dir, file_name)
|
||||
shutil.copyfile(src_path, dst_path)
|
||||
|
||||
if "MicroMsg.db" in files:
|
||||
src_path = os.path.join(root, "MicroMsg.db")
|
||||
dst_path = os.path.join(tmp_dir, "MicroMsg.db")
|
||||
shutil.copyfile(src_path, dst_path)
|
||||
|
||||
# 如果不存在decrypted目录则创建
|
||||
decrypted_dir = os.path.join(os.getcwd(), "")
|
||||
if not os.path.exists(decrypted_dir):
|
||||
os.mkdir(decrypted_dir)
|
||||
return tmp_dir, decrypted_dir
|
||||
|
||||
|
||||
# 合并相同名称的数据库
|
||||
def merge_db(db_path):
|
||||
dbs_paths = {}
|
||||
for root, dirs, files in os.walk(db_path):
|
||||
for file_name in files:
|
||||
if "db-shm" in file_name or "db-wal" in file_name:
|
||||
continue
|
||||
if "FTSMSG" in file_name:
|
||||
src_path = os.path.join(root, file_name)
|
||||
dbs_paths["FTSMSG_all.db"] = dbs_paths.get("FTSMSG_all.db", [])
|
||||
dbs_paths["FTSMSG_all.db"].append(src_path)
|
||||
elif "MediaMSG" in file_name:
|
||||
src_path = os.path.join(root, file_name)
|
||||
dbs_paths["MediaMSG_all.db"] = dbs_paths.get("MediaMSG_all.db", [])
|
||||
dbs_paths["MediaMSG_all.db"].append(src_path)
|
||||
elif "MSG" in file_name:
|
||||
src_path = os.path.join(root, file_name)
|
||||
dbs_paths["MSG_all.db"] = dbs_paths.get("MSG_all.db", [])
|
||||
dbs_paths["MSG_all.db"].append(src_path)
|
||||
|
||||
for db_name, db_files in dbs_paths.items():
|
||||
if db_name != "MSG_all.db":
|
||||
continue
|
||||
|
||||
save_path = os.path.join(db_path, db_name)
|
||||
merged_conn = sqlite3.connect(save_path)
|
||||
merged_cursor = merged_conn.cursor()
|
||||
|
||||
for db_file in db_files:
|
||||
c0 = merged_cursor.execute("select tbl_name from sqlite_master where type='table'")
|
||||
r0 = c0.fetchall()
|
||||
r0 = [row[0] for row in r0]
|
||||
|
||||
conn = sqlite3.connect(db_file)
|
||||
cursor = conn.cursor()
|
||||
c = cursor.execute("select tbl_name,sql from sqlite_master where type='table'")
|
||||
tbls = []
|
||||
for row in c:
|
||||
if row[0] == "sqlite_sequence":
|
||||
continue
|
||||
if "mmTokenizer" in row[1]:
|
||||
continue
|
||||
tbls.append(row[0])
|
||||
if row[0] in r0:
|
||||
continue
|
||||
try:
|
||||
merged_cursor.execute(row[1])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(db_file)
|
||||
print(row[1])
|
||||
print(r0)
|
||||
raise e
|
||||
merged_conn.commit()
|
||||
for row in tbls:
|
||||
c1 = cursor.execute("select * from " + row)
|
||||
for r in c1:
|
||||
columns = conn.execute("PRAGMA table_info(" + row + ")").fetchall()
|
||||
if len(columns) > 1:
|
||||
columns = [column[1] for column in columns[1:]]
|
||||
values = r[1:]
|
||||
# query = "INSERT INTO " + row + " (" + ",".join(columns) + ") VALUES (" + ",".join(
|
||||
# ["?" for _ in range(len(values))]) + ")"
|
||||
else:
|
||||
columns = [columns[0][1]]
|
||||
values = [r[0]]
|
||||
query_1 = "select * from " + row + " where " + columns[0] + "=?"
|
||||
c2 = merged_cursor.execute(query_1, values)
|
||||
if len(c2.fetchall()) > 0:
|
||||
continue
|
||||
query = "INSERT INTO " + row + " (" + ",".join(columns) + ") VALUES (" + ",".join(
|
||||
["?" for _ in range(len(values))]) + ")"
|
||||
|
||||
try:
|
||||
merged_cursor.execute(query, values)
|
||||
except Exception as e:
|
||||
print()
|
||||
print("error")
|
||||
print(e)
|
||||
print(db_file)
|
||||
print(query, values)
|
||||
print(len(values))
|
||||
raise e
|
||||
merged_conn.commit()
|
||||
|
||||
conn.close()
|
||||
print(db_file)
|
||||
|
||||
merged_conn.close()
|
||||
# merge_databases(save_path, db_file)
|
||||
|
||||
|
||||
def merge_databases(db1, db2):
|
||||
con3 = sqlite3.connect(db1)
|
||||
|
||||
con3.execute("ATTACH DATABASE '" + db2 + "' as dba")
|
||||
|
||||
con3.execute("BEGIN")
|
||||
for row in con3.execute("SELECT * FROM dba.sqlite_master WHERE type='table'"):
|
||||
# 此处的ignore就是为了忽略重复ID导致的异常
|
||||
combine = "INSERT OR IGNORE INTO " + row[1] + " SELECT * FROM dba." + row[1]
|
||||
print(combine)
|
||||
con3.execute(combine)
|
||||
con3.commit()
|
||||
con3.execute("detach database dba")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取微信数据库的key
|
||||
wx_key = get_wx_key()
|
||||
|
||||
# 获取微信消息目录
|
||||
wechat_msg_dir = get_wechat_dir()
|
||||
user_msg_dirs = get_wechat_user_dir(wechat_msg_dir)
|
||||
if len(user_msg_dirs) == 1:
|
||||
data_dir = list(user_msg_dirs.values())[0]
|
||||
else:
|
||||
for i, user_dir in enumerate(user_msg_dirs):
|
||||
print(i, user_dir)
|
||||
index = int(input("请选择要导出的用户:"))
|
||||
data_dir = list(user_msg_dirs.values())[index]
|
||||
|
||||
print("复制微信的msg数据文件...")
|
||||
# 复制微信的msg数据文件
|
||||
tmp_dir, decrypted_dir = copy_msg_db(os.path.join(data_dir, "Msg"))
|
||||
|
||||
print("解密数据库...")
|
||||
# 解密数据库
|
||||
for file_name in os.listdir(tmp_dir):
|
||||
if re.match(r".*\.db$", file_name):
|
||||
src_path = os.path.join(tmp_dir, file_name)
|
||||
dst_path = os.path.join(decrypted_dir, file_name)
|
||||
decrypt(wx_key, src_path, dst_path)
|
||||
|
||||
# 删除临时目录
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
||||
# decrypted_dir = os.path.join(os.getcwd(), "decrypted")
|
||||
print("合并数据库...")
|
||||
# 合并数据库
|
||||
merge_db(decrypted_dir)
|
Loading…
Reference in New Issue
Block a user