完善项目,添加 数据库解密脚本,添加自动获取当前登录微信的数据库并解密,增加偏移地址脚本获取方式,可以一件获取偏移。
This commit is contained in:
parent
6bccd9a9d5
commit
ab17f7fe64
280
Program/get_base_addr.py
Normal file
280
Program/get_base_addr.py
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
# -*- coding: utf-8 -*-#
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Name: get_base_addr.py
|
||||||
|
# Description:
|
||||||
|
# Author: xaoyaoo
|
||||||
|
# Date: 2023/08/22
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
import argparse
|
||||||
|
import ctypes
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
import psutil
|
||||||
|
import win32api
|
||||||
|
|
||||||
|
|
||||||
|
def hex2dec(hex):
|
||||||
|
return int(hex, 16)
|
||||||
|
|
||||||
|
|
||||||
|
def dec2hex(dec):
|
||||||
|
return hex(dec)
|
||||||
|
|
||||||
|
|
||||||
|
def hex_add(hex1, hex2, base1=16, base2=16):
|
||||||
|
"""
|
||||||
|
两个任意进制数相加
|
||||||
|
:param hex1:
|
||||||
|
:param hex2:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return hex(int(hex1, base1) + int(hex2, base2))
|
||||||
|
|
||||||
|
|
||||||
|
def hex_sub(hex1, hex2, base1=16, base2=16):
|
||||||
|
"""
|
||||||
|
两个任意进制数相减
|
||||||
|
:param hex1:
|
||||||
|
:param hex2:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return hex(int(hex1, base1) - int(hex2, base2))
|
||||||
|
|
||||||
|
|
||||||
|
def get_pid(keyword):
|
||||||
|
"""
|
||||||
|
获取进程id
|
||||||
|
:param keyword: 关键字
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pids = {}
|
||||||
|
for proc in psutil.process_iter():
|
||||||
|
if keyword in proc.name():
|
||||||
|
pids[proc.pid] = proc
|
||||||
|
return pids
|
||||||
|
|
||||||
|
|
||||||
|
class BaseAddr:
|
||||||
|
def __init__(self, pid, proc_module_name="WeChatWin.dll"):
|
||||||
|
self.pid = pid
|
||||||
|
self.module_name = proc_module_name
|
||||||
|
self.proc = psutil.Process(self.pid)
|
||||||
|
self.version = self.get_app_version(self.proc.exe())
|
||||||
|
self.base_address = 0
|
||||||
|
self.end_address = 0
|
||||||
|
self.batch = 0
|
||||||
|
|
||||||
|
self.key_start_addr = 0
|
||||||
|
self.key_end_addr = 0
|
||||||
|
|
||||||
|
self.mobile_addr = []
|
||||||
|
self.name_addr = []
|
||||||
|
self.account_addr = []
|
||||||
|
# self.key_addr = []
|
||||||
|
|
||||||
|
self.get_base_addr()
|
||||||
|
|
||||||
|
def get_app_version(self, executable_path):
|
||||||
|
info = win32api.GetFileVersionInfo(executable_path, "\\")
|
||||||
|
version = info['FileVersionMS'] >> 16, info['FileVersionMS'] & 0xFFFF, \
|
||||||
|
info['FileVersionLS'] >> 16, info['FileVersionLS'] & 0xFFFF
|
||||||
|
version_str = ".".join(map(str, version))
|
||||||
|
|
||||||
|
return version_str
|
||||||
|
|
||||||
|
def get_base_addr(self):
|
||||||
|
"""
|
||||||
|
获取模块基址
|
||||||
|
:param pid: 进程id
|
||||||
|
:param module_name: 模块名
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
base_address = 0
|
||||||
|
end_address = 0
|
||||||
|
batch = 0
|
||||||
|
n = 0
|
||||||
|
for module in self.proc.memory_maps(grouped=False):
|
||||||
|
if self.module_name in module.path:
|
||||||
|
if n == 0:
|
||||||
|
base_address = int(module.addr, 16)
|
||||||
|
batch = module.rss
|
||||||
|
n += 1
|
||||||
|
end_address = int(module.addr, 16) + module.rss
|
||||||
|
|
||||||
|
self.base_address = base_address
|
||||||
|
self.end_address = end_address
|
||||||
|
self.batch = batch
|
||||||
|
# self.batch = end_address - base_address
|
||||||
|
|
||||||
|
def find_all(self, c, string):
|
||||||
|
"""
|
||||||
|
查找字符串中所有子串的位置
|
||||||
|
:param c: 子串 b'123'
|
||||||
|
:param string: 字符串 b'123456789123'
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return [m.start() for m in re.finditer(re.escape(c), string)]
|
||||||
|
|
||||||
|
# 搜索内存地址范围内的值
|
||||||
|
def search_memory_value(self, mobile, name, account):
|
||||||
|
mobile = mobile.encode("utf-8")
|
||||||
|
name = name.encode("utf-8")
|
||||||
|
account = account.encode("utf-8")
|
||||||
|
|
||||||
|
Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, self.pid)
|
||||||
|
|
||||||
|
mobile_addr = []
|
||||||
|
name_addr = []
|
||||||
|
account_addr = []
|
||||||
|
|
||||||
|
array = ctypes.create_string_buffer(self.batch)
|
||||||
|
for i in range(self.base_address, self.end_address, self.batch):
|
||||||
|
if ctypes.windll.kernel32.ReadProcessMemory(Handle, ctypes.c_void_p(i), array, self.batch, None) == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
hex_string = array.raw # 读取到的内存数据
|
||||||
|
|
||||||
|
if mobile in hex_string:
|
||||||
|
mobile_addr = mobile_addr + [m.start() + i for m in re.finditer(re.escape(mobile), hex_string)]
|
||||||
|
if name in hex_string:
|
||||||
|
name_addr = name_addr + [m.start() + i for m in re.finditer(re.escape(name), hex_string)]
|
||||||
|
if account in hex_string:
|
||||||
|
account_addr = account_addr + [m.start() + i for m in re.finditer(re.escape(account), hex_string)]
|
||||||
|
|
||||||
|
self.mobile_addr = mobile_addr
|
||||||
|
self.name_addr = name_addr
|
||||||
|
self.account_addr = account_addr
|
||||||
|
return mobile_addr, name_addr, account_addr
|
||||||
|
|
||||||
|
def get_key_addr(self, key):
|
||||||
|
"""
|
||||||
|
获取key的地址
|
||||||
|
:param key:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
key = bytes.fromhex(key)
|
||||||
|
|
||||||
|
module_start_addr = 34199871460642
|
||||||
|
module_end_addr = 0
|
||||||
|
for module in self.proc.memory_maps(grouped=False):
|
||||||
|
if "WeChat" in module.path:
|
||||||
|
start_addr = int(module.addr, 16)
|
||||||
|
end_addr = start_addr + module.rss
|
||||||
|
|
||||||
|
if module_start_addr > start_addr:
|
||||||
|
module_start_addr = start_addr
|
||||||
|
if module_end_addr < end_addr:
|
||||||
|
module_end_addr = end_addr
|
||||||
|
|
||||||
|
Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, self.pid)
|
||||||
|
array = ctypes.create_string_buffer(self.batch)
|
||||||
|
|
||||||
|
for i in range(module_start_addr, module_end_addr, self.batch):
|
||||||
|
if ctypes.windll.kernel32.ReadProcessMemory(Handle, ctypes.c_void_p(i), array, self.batch, None) == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
hex_string = array.raw # 读取到的内存数据
|
||||||
|
if key in hex_string:
|
||||||
|
self.key_addr_tmp = i + hex_string.find(key)
|
||||||
|
break
|
||||||
|
|
||||||
|
array_key = []
|
||||||
|
for i in range(8):
|
||||||
|
byte_value = (self.key_addr_tmp >> (i * 8)) & 0xFF
|
||||||
|
hex_string = format(byte_value, '02x')
|
||||||
|
byte_obj = bytes.fromhex(hex_string)
|
||||||
|
array_key.append(byte_obj)
|
||||||
|
# 合并数组
|
||||||
|
array_key = b''.join(array_key)
|
||||||
|
|
||||||
|
array = ctypes.create_string_buffer(self.batch)
|
||||||
|
for i in range(self.base_address, self.end_address, self.batch):
|
||||||
|
if ctypes.windll.kernel32.ReadProcessMemory(Handle, ctypes.c_void_p(i), array, self.batch, None) == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
hex_string = array.raw # 读取到的内存数据
|
||||||
|
if array_key in hex_string:
|
||||||
|
self.key_addr = i + hex_string.find(array_key)
|
||||||
|
return self.key_addr
|
||||||
|
|
||||||
|
def calculate_offset(self, addr):
|
||||||
|
"""
|
||||||
|
计算偏移量
|
||||||
|
:param addr:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
offset = addr - self.base_address
|
||||||
|
return offset
|
||||||
|
|
||||||
|
def get_offset(self):
|
||||||
|
"""
|
||||||
|
计算偏移量
|
||||||
|
:param addr:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
mobile_offset = 0
|
||||||
|
name_offset = 0
|
||||||
|
account_offset = 0
|
||||||
|
key_offset = 0
|
||||||
|
if len(self.mobile_addr) >= 1:
|
||||||
|
mobile_offset = self.calculate_offset(self.mobile_addr[0])
|
||||||
|
if len(self.name_addr) >= 1:
|
||||||
|
name_offset = self.calculate_offset(self.name_addr[0])
|
||||||
|
if len(self.account_addr) >= 1:
|
||||||
|
account_offset = self.calculate_offset(self.account_addr[1])
|
||||||
|
|
||||||
|
key_offset = self.calculate_offset(self.key_addr)
|
||||||
|
|
||||||
|
self.key_offset = key_offset
|
||||||
|
self.mobile_offset = mobile_offset
|
||||||
|
self.name_offset = name_offset
|
||||||
|
self.account_offset = account_offset
|
||||||
|
return name_offset, account_offset, mobile_offset, 0, key_offset
|
||||||
|
|
||||||
|
|
||||||
|
def run(mobile, name, account, key):
|
||||||
|
proc_name = "WeChat.exe"
|
||||||
|
proc_module_name = "WeChatWin.dll"
|
||||||
|
|
||||||
|
pids = get_pid(proc_name)
|
||||||
|
for pid, proc in pids.items():
|
||||||
|
ba = BaseAddr(pid, proc_module_name)
|
||||||
|
ba.search_memory_value(mobile, name, account)
|
||||||
|
ba.get_key_addr(key)
|
||||||
|
name_offset, account_offset, mobile_offset, _, key_offset = ba.get_offset()
|
||||||
|
rdata = {ba.version: [name_offset, account_offset, mobile_offset, 0, key_offset]}
|
||||||
|
return rdata
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 创建命令行参数解析器
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--mobile", type=str, help="手机号")
|
||||||
|
parser.add_argument("--name", type=str, help="微信昵称")
|
||||||
|
parser.add_argument("--account", type=str, help="微信账号")
|
||||||
|
parser.add_argument("--key", type=str, help="密钥")
|
||||||
|
|
||||||
|
# 解析命令行参数
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# 检查是否缺少必要参数,并抛出错误
|
||||||
|
if not args.mobile or not args.name or not args.account or not args.key:
|
||||||
|
raise ValueError("缺少必要的命令行参数!请提供手机号、微信昵称、微信账号和密钥。")
|
||||||
|
|
||||||
|
# 从命令行参数获取值
|
||||||
|
mobile = args.mobile
|
||||||
|
name = args.name
|
||||||
|
account = args.account
|
||||||
|
key = args.key
|
||||||
|
|
||||||
|
# 调用 run 函数,并传入参数
|
||||||
|
rdata = run(mobile, name, account, key)
|
||||||
|
print(rdata)
|
||||||
|
|
||||||
|
# 添加到version_list.json
|
||||||
|
with open("version_list.json", "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
data.update(rdata)
|
||||||
|
with open("version_list.json", "w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, ensure_ascii=False, indent=4)
|
@ -8,7 +8,7 @@
|
|||||||
import binascii
|
import binascii
|
||||||
import json
|
import json
|
||||||
import ctypes
|
import ctypes
|
||||||
|
import win32api
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
|
|
||||||
@ -40,7 +40,6 @@ def get_account(pid, base_address, n_size=100):
|
|||||||
null_index = i
|
null_index = i
|
||||||
break
|
break
|
||||||
text = ctypes.string_at(ctypes.byref(array), null_index).decode('utf-8', errors='ignore')
|
text = ctypes.string_at(ctypes.byref(array), null_index).decode('utf-8', errors='ignore')
|
||||||
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
@ -85,6 +84,7 @@ def get_hex(h_process, lp_base_address):
|
|||||||
|
|
||||||
num = 32
|
num = 32
|
||||||
array2 = (ctypes.c_ubyte * num)()
|
array2 = (ctypes.c_ubyte * num)()
|
||||||
|
|
||||||
lp_base_address2 = (
|
lp_base_address2 = (
|
||||||
(int(binascii.hexlify(array[7]), 16) << 56) +
|
(int(binascii.hexlify(array[7]), 16) << 56) +
|
||||||
(int(binascii.hexlify(array[6]), 16) << 48) +
|
(int(binascii.hexlify(array[6]), 16) << 48) +
|
||||||
@ -95,17 +95,15 @@ def get_hex(h_process, lp_base_address):
|
|||||||
(int(binascii.hexlify(array[1]), 16) << 8) +
|
(int(binascii.hexlify(array[1]), 16) << 8) +
|
||||||
int(binascii.hexlify(array[0]), 16)
|
int(binascii.hexlify(array[0]), 16)
|
||||||
)
|
)
|
||||||
|
|
||||||
if ctypes.windll.kernel32.ReadProcessMemory(h_process, ctypes.c_void_p(lp_base_address2), ctypes.byref(array2), num,
|
if ctypes.windll.kernel32.ReadProcessMemory(h_process, ctypes.c_void_p(lp_base_address2), ctypes.byref(array2), num,
|
||||||
0) == 0:
|
0) == 0:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
hex_string = binascii.hexlify(bytes(array2))
|
hex_string = binascii.hexlify(bytes(array2))
|
||||||
return hex_string.decode('utf-8')
|
return hex_string.decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
def get_file_version(file_path):
|
def get_file_version(file_path):
|
||||||
import win32api
|
|
||||||
info = win32api.GetFileVersionInfo(file_path, "\\")
|
info = win32api.GetFileVersionInfo(file_path, "\\")
|
||||||
ms = info['FileVersionMS']
|
ms = info['FileVersionMS']
|
||||||
ls = info['FileVersionLS']
|
ls = info['FileVersionLS']
|
||||||
@ -113,6 +111,9 @@ def get_file_version(file_path):
|
|||||||
# version = parse(file_version)
|
# version = parse(file_version)
|
||||||
return file_version
|
return file_version
|
||||||
|
|
||||||
|
# def get_wx_id(h_process, lp_base_address):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def read_info(version_list):
|
def read_info(version_list):
|
||||||
support_list = None
|
support_list = None
|
||||||
@ -121,7 +122,7 @@ def read_info(version_list):
|
|||||||
rd = []
|
rd = []
|
||||||
|
|
||||||
for process in psutil.process_iter(['name', 'exe', 'pid', 'cmdline']):
|
for process in psutil.process_iter(['name', 'exe', 'pid', 'cmdline']):
|
||||||
if process.info['name'] == 'WeChat.exe':
|
if process.name() == 'WeChat.exe':
|
||||||
tmp_rd = {}
|
tmp_rd = {}
|
||||||
wechat_process = process
|
wechat_process = process
|
||||||
tmp_rd['pid'] = wechat_process.pid
|
tmp_rd['pid'] = wechat_process.pid
|
||||||
@ -186,11 +187,12 @@ def read_info(version_list):
|
|||||||
return "[-] WeChat No Run"
|
return "[-] WeChat No Run"
|
||||||
return rd
|
return rd
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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)
|
rd = read_info(version_list)
|
||||||
|
# print(rd)
|
||||||
for i in rd:
|
for i in rd:
|
||||||
for k, v in i.items():
|
for k, v in i.items():
|
||||||
print(f"[+] {k}: {v}")
|
print(f"[+] {k}: {v}")
|
||||||
|
|
||||||
print("=====================================")
|
print("=====================================")
|
||||||
|
303
Program/version_list.json
Normal file
303
Program/version_list.json
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
{
|
||||||
|
"3.2.1.154": [
|
||||||
|
328121948,
|
||||||
|
328122328,
|
||||||
|
328123056,
|
||||||
|
328121976,
|
||||||
|
328123020
|
||||||
|
],
|
||||||
|
"3.3.0.115": [
|
||||||
|
31323364,
|
||||||
|
31323744,
|
||||||
|
31324472,
|
||||||
|
31323392,
|
||||||
|
31324436
|
||||||
|
],
|
||||||
|
"3.3.0.84": [
|
||||||
|
31315212,
|
||||||
|
31315592,
|
||||||
|
31316320,
|
||||||
|
31315240,
|
||||||
|
31316284
|
||||||
|
],
|
||||||
|
"3.3.0.93": [
|
||||||
|
31323364,
|
||||||
|
31323744,
|
||||||
|
31324472,
|
||||||
|
31323392,
|
||||||
|
31324436
|
||||||
|
],
|
||||||
|
"3.3.5.34": [
|
||||||
|
30603028,
|
||||||
|
30603408,
|
||||||
|
30604120,
|
||||||
|
30603056,
|
||||||
|
30604100
|
||||||
|
],
|
||||||
|
"3.3.5.42": [
|
||||||
|
30603012,
|
||||||
|
30603392,
|
||||||
|
30604120,
|
||||||
|
30603040,
|
||||||
|
30604084
|
||||||
|
],
|
||||||
|
"3.3.5.46": [
|
||||||
|
30578372,
|
||||||
|
30578752,
|
||||||
|
30579480,
|
||||||
|
30578400,
|
||||||
|
30579444
|
||||||
|
],
|
||||||
|
"3.4.0.37": [
|
||||||
|
31608116,
|
||||||
|
31608496,
|
||||||
|
31609224,
|
||||||
|
31608144,
|
||||||
|
31609188
|
||||||
|
],
|
||||||
|
"3.4.0.38": [
|
||||||
|
31604044,
|
||||||
|
31604424,
|
||||||
|
31605152,
|
||||||
|
31604072,
|
||||||
|
31605116
|
||||||
|
],
|
||||||
|
"3.4.0.50": [
|
||||||
|
31688500,
|
||||||
|
31688880,
|
||||||
|
31689608,
|
||||||
|
31688528,
|
||||||
|
31689572
|
||||||
|
],
|
||||||
|
"3.4.0.54": [
|
||||||
|
31700852,
|
||||||
|
31701248,
|
||||||
|
31700920,
|
||||||
|
31700880,
|
||||||
|
31701924
|
||||||
|
],
|
||||||
|
"3.4.5.27": [
|
||||||
|
32133788,
|
||||||
|
32134168,
|
||||||
|
32134896,
|
||||||
|
32133816,
|
||||||
|
32134860
|
||||||
|
],
|
||||||
|
"3.4.5.45": [
|
||||||
|
32147012,
|
||||||
|
32147392,
|
||||||
|
32147064,
|
||||||
|
32147040,
|
||||||
|
32148084
|
||||||
|
],
|
||||||
|
"3.5.0.20": [
|
||||||
|
35494484,
|
||||||
|
35494864,
|
||||||
|
35494536,
|
||||||
|
35494512,
|
||||||
|
35495556
|
||||||
|
],
|
||||||
|
"3.5.0.29": [
|
||||||
|
35507980,
|
||||||
|
35508360,
|
||||||
|
35508032,
|
||||||
|
35508008,
|
||||||
|
35509052
|
||||||
|
],
|
||||||
|
"3.5.0.33": [
|
||||||
|
35512140,
|
||||||
|
35512520,
|
||||||
|
35512192,
|
||||||
|
35512168,
|
||||||
|
35513212
|
||||||
|
],
|
||||||
|
"3.5.0.39": [
|
||||||
|
35516236,
|
||||||
|
35516616,
|
||||||
|
35516288,
|
||||||
|
35516264,
|
||||||
|
35517308
|
||||||
|
],
|
||||||
|
"3.5.0.42": [
|
||||||
|
35512140,
|
||||||
|
35512520,
|
||||||
|
35512192,
|
||||||
|
35512168,
|
||||||
|
35513212
|
||||||
|
],
|
||||||
|
"3.5.0.44": [
|
||||||
|
35510836,
|
||||||
|
35511216,
|
||||||
|
35510896,
|
||||||
|
35510864,
|
||||||
|
35511908
|
||||||
|
],
|
||||||
|
"3.5.0.46": [
|
||||||
|
35506740,
|
||||||
|
35507120,
|
||||||
|
35506800,
|
||||||
|
35506768,
|
||||||
|
35507812
|
||||||
|
],
|
||||||
|
"3.6.0.18": [
|
||||||
|
35842996,
|
||||||
|
35843376,
|
||||||
|
35843048,
|
||||||
|
35843024,
|
||||||
|
35844068
|
||||||
|
],
|
||||||
|
"3.6.5.7": [
|
||||||
|
35864356,
|
||||||
|
35864736,
|
||||||
|
35864408,
|
||||||
|
35864384,
|
||||||
|
35865428
|
||||||
|
],
|
||||||
|
"3.6.5.16": [
|
||||||
|
35909428,
|
||||||
|
35909808,
|
||||||
|
35909480,
|
||||||
|
35909456,
|
||||||
|
35910500
|
||||||
|
],
|
||||||
|
"3.7.0.26": [
|
||||||
|
37105908,
|
||||||
|
37106288,
|
||||||
|
37105960,
|
||||||
|
37105936,
|
||||||
|
37106980
|
||||||
|
],
|
||||||
|
"3.7.0.29": [
|
||||||
|
37105908,
|
||||||
|
37106288,
|
||||||
|
37105960,
|
||||||
|
37105936,
|
||||||
|
37106980
|
||||||
|
],
|
||||||
|
"3.7.0.30": [
|
||||||
|
37118196,
|
||||||
|
37118576,
|
||||||
|
37118248,
|
||||||
|
37118224,
|
||||||
|
37119268
|
||||||
|
],
|
||||||
|
"3.7.5.11": [
|
||||||
|
37883280,
|
||||||
|
37884088,
|
||||||
|
37883136,
|
||||||
|
37883008,
|
||||||
|
37884052
|
||||||
|
],
|
||||||
|
"3.7.5.23": [
|
||||||
|
37895736,
|
||||||
|
37896544,
|
||||||
|
37895592,
|
||||||
|
37883008,
|
||||||
|
37896508
|
||||||
|
],
|
||||||
|
"3.7.5.27": [
|
||||||
|
37895736,
|
||||||
|
37896544,
|
||||||
|
37895592,
|
||||||
|
37895464,
|
||||||
|
37896508
|
||||||
|
],
|
||||||
|
"3.7.5.31": [
|
||||||
|
37903928,
|
||||||
|
37904736,
|
||||||
|
37903784,
|
||||||
|
37903656,
|
||||||
|
37904700
|
||||||
|
],
|
||||||
|
"3.7.6.24": [
|
||||||
|
38978840,
|
||||||
|
38979648,
|
||||||
|
38978696,
|
||||||
|
38978604,
|
||||||
|
38979612
|
||||||
|
],
|
||||||
|
"3.7.6.29": [
|
||||||
|
38986376,
|
||||||
|
38987184,
|
||||||
|
38986232,
|
||||||
|
38986104,
|
||||||
|
38987148
|
||||||
|
],
|
||||||
|
"3.7.6.44": [
|
||||||
|
39016520,
|
||||||
|
39017328,
|
||||||
|
39016376,
|
||||||
|
38986104,
|
||||||
|
39017292
|
||||||
|
],
|
||||||
|
"3.8.0.31": [
|
||||||
|
46064088,
|
||||||
|
46064912,
|
||||||
|
46063944,
|
||||||
|
38986104,
|
||||||
|
46064876
|
||||||
|
],
|
||||||
|
"3.8.0.33": [
|
||||||
|
46059992,
|
||||||
|
46060816,
|
||||||
|
46059848,
|
||||||
|
38986104,
|
||||||
|
46060780
|
||||||
|
],
|
||||||
|
"3.8.0.41": [
|
||||||
|
46064024,
|
||||||
|
46064848,
|
||||||
|
46063880,
|
||||||
|
38986104,
|
||||||
|
46064812
|
||||||
|
],
|
||||||
|
"3.8.1.26": [
|
||||||
|
46409448,
|
||||||
|
46410272,
|
||||||
|
46409304,
|
||||||
|
38986104,
|
||||||
|
46410236
|
||||||
|
],
|
||||||
|
"3.9.0.28": [
|
||||||
|
48418376,
|
||||||
|
48419280,
|
||||||
|
48418232,
|
||||||
|
38986104,
|
||||||
|
48419244
|
||||||
|
],
|
||||||
|
"3.9.2.23": [
|
||||||
|
50320784,
|
||||||
|
50321712,
|
||||||
|
50320640,
|
||||||
|
38986104,
|
||||||
|
50321676
|
||||||
|
],
|
||||||
|
"3.9.2.26": [
|
||||||
|
50329040,
|
||||||
|
50329968,
|
||||||
|
50328896,
|
||||||
|
38986104,
|
||||||
|
50329932
|
||||||
|
],
|
||||||
|
"3.9.5.91": [
|
||||||
|
61654904,
|
||||||
|
61654680,
|
||||||
|
61654712,
|
||||||
|
38986104,
|
||||||
|
61656176
|
||||||
|
],
|
||||||
|
"3.9.6.19": [
|
||||||
|
61997688,
|
||||||
|
61997464,
|
||||||
|
61997496,
|
||||||
|
38986104,
|
||||||
|
61998960
|
||||||
|
],
|
||||||
|
"3.9.6.33": [
|
||||||
|
62030600,
|
||||||
|
62031936,
|
||||||
|
62030408,
|
||||||
|
0,
|
||||||
|
62031872
|
||||||
|
]
|
||||||
|
}
|
178
README.md
178
README.md
@ -1,50 +1,176 @@
|
|||||||
## SharpWxDump
|
# <center>SharpWxDump - Python</center>
|
||||||
如何获取指定版本基址:https://github.com/AdminTest0/SharpWxDump/blob/master/CE%E8%8E%B7%E5%8F%96%E5%9F%BA%E5%9D%80.md
|
|
||||||
|
|
||||||
## 特别说明
|
## 一、项目介绍
|
||||||
该分支是<a href="https://github.com/AdminTest0/SharpWxDump">SharpWxDump</a>的python语言版本。
|
|
||||||
同时添加了一些新的功能。
|
|
||||||
|
|
||||||
**使用方法**
|
本项目可以获取微信基本信息,以及key,通过key可以解密微信数据库,获取聊天记录,好友信息,群信息等。
|
||||||
|
|
||||||
|
该分支是[SharpWxDump](https://github.com/AdminTest0/SharpWxDump)的经过重构python语言版本,同时添加了一些新的功能。
|
||||||
|
|
||||||
|
## 二、使用方法
|
||||||
|
|
||||||
|
### 1. 安装依赖
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
|
||||||
|
1. requirements.txt中的包可能不全,如果运行报错,请自行安装缺少的包
|
||||||
|
2. 如果运行报错,请检查python版本,本项目使用的是python3.10
|
||||||
|
3. 安装pycryptodome时可能会报错,可以使用下面的命令安装,自行搜索解决方案(该包为解密的核心包)
|
||||||
|
|
||||||
|
### 2. 获取微信基本信息
|
||||||
|
|
||||||
|
获取微信的信息,获取到几个,取决于现在登录的几个微信。
|
||||||
|
|
||||||
|
**2.1 shell获取微信基本信息**
|
||||||
|
|
||||||
|
```shell script
|
||||||
cd Program
|
cd Program
|
||||||
python3 Program.py
|
python get_wx_info.py
|
||||||
|
|
||||||
# 也可以import 调用
|
|
||||||
import Program
|
|
||||||
Program.read_info(version)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**支持功能**
|
结果
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
[+] pid: 2365 <!--进程ID-->
|
||||||
|
[+] version: *.*.*.* <!--微信版本-->
|
||||||
|
[+] key: ******************************************d <!--数据库密钥-->
|
||||||
|
[+] name: ***** <!--昵称-->
|
||||||
|
[+] account: ******** <!--账号-->
|
||||||
|
[+] mobile: ****** <!--手机号-->
|
||||||
|
[+] mail: ***** <!--邮箱:在微信3.7版本以上无效-->
|
||||||
|
========================================
|
||||||
|
[+] pid: 2365
|
||||||
|
[+] version: *.*.*.*
|
||||||
|
[+] key: ******************************************d
|
||||||
|
[+] name: *****
|
||||||
|
[+] account: ********
|
||||||
|
[+] mobile: ******
|
||||||
|
[+] mail: *****
|
||||||
|
========================================
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
**2.2 import 调用**
|
||||||
|
|
||||||
|
```python
|
||||||
|
import json
|
||||||
|
from Program.get_wx_info import read_info
|
||||||
|
|
||||||
|
version_list = json.load(open("version_list.json", "r", encoding="utf-8"))
|
||||||
|
data = read_info(version_list)
|
||||||
|
print(data)
|
||||||
|
```
|
||||||
|
|
||||||
|
结果:
|
||||||
|
|
||||||
|
```list
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'pid': 5632,
|
||||||
|
'version': '*.*.*.*',
|
||||||
|
'key': '***************************************',
|
||||||
|
'name': '******',
|
||||||
|
'account': '******',
|
||||||
|
'mobile': '135********',
|
||||||
|
'mail': '********'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'pid': 5632,
|
||||||
|
'version': '*.*.*.*',
|
||||||
|
'key': '***************************************',
|
||||||
|
'name': '******',
|
||||||
|
'account': '******',
|
||||||
|
'mobile': '135********',
|
||||||
|
'mail': '********'
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**说明**: 每个字段具体含义,参看上一条shell获取微信基本信息
|
||||||
|
|
||||||
|
### 3. 获取偏移地址
|
||||||
|
|
||||||
|
* 该方法一般不需要,只有当[version_list.json](./Program/version_list.json)没有对应的微信版本时,可以通过该方法获取偏移地址
|
||||||
|
* 如果需要请参考下面的方法获取
|
||||||
|
|
||||||
|
**3.1 通过python脚本获取**
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python get_base_addr.py --mobile 152******** --name ****** --account ****** --key **********************************************
|
||||||
|
```
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
|
||||||
|
mobile = "152********" # 手机号
|
||||||
|
name = "******" # 微信昵称
|
||||||
|
account = "******" # 微信账号
|
||||||
|
# 以上信息可以通过微信客户端获取
|
||||||
|
|
||||||
|
key = '**********************************************' # key
|
||||||
|
# 需要降低版本使用get_wx_info.py获取key,也可以通过CheatEngine等工具获取
|
||||||
|
# 最好是保存之前同微信使用过的key,非常方便
|
||||||
|
|
||||||
|
**3.2 通过CheatEngine等工具获取**
|
||||||
|
|
||||||
|
具体请查看:[CE获取基址.md](./CE%E8%8E%B7%E5%8F%96%E5%9F%BA%E5%9D%80.md)
|
||||||
|
|
||||||
|
* 该方法获取到的偏移地址需要手动添加到[version_list.json](./Program/version_list.json)中
|
||||||
|
|
||||||
|
**3.3 最简单获取方法**
|
||||||
|
|
||||||
|
最简单的方法当然是运行
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git clone https://github.com/xaoyaoo/SharpWxDump.git
|
||||||
|
```
|
||||||
|
|
||||||
|
重新拉取一份新的啦~
|
||||||
|
|
||||||
|
* ps: 该方法不一定能获取到最新的版本
|
||||||
|
* 如果需要最新的版本,可以通过上面的方法获取
|
||||||
|
* 你也可以提交Issues,分分钟给你更新
|
||||||
|
|
||||||
|
## 三、获取解密数据库
|
||||||
|
|
||||||
|
* [decrypt.py](./decrypted/decrypt.py) : 数据库解密脚本
|
||||||
|
* [get_wx_decrypted_db.py](./decrypted/get_wx_decrypted_db.py) :直接读取当前登录微信的数据库,解密后保存到当前目录下的decrypted文件夹中
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
* 解密后可拖入数据库工具查找敏感信息
|
||||||
|
* 还有一份数据的说明文档,但是我累了,不想写了
|
||||||
|
|
||||||
|
**方法**
|
||||||
|
|
||||||
|
```shell
|
||||||
|
```shell
|
||||||
|
# 累了。。。不想写了,自己看代码吧
|
||||||
|
```
|
||||||
|
|
||||||
|
## 四、支持功能
|
||||||
|
|
||||||
1. 支持微信多开场景,获取多用户信息等
|
1. 支持微信多开场景,获取多用户信息等
|
||||||
2. 微信需要登录状态才能获取数据库密钥
|
2. 微信需要登录状态才能获取数据库密钥
|
||||||
3. 没有动态获取功能,已将偏移地址写入version_list.josn内,会不定期更新,**如有需要的版本请提交Issues**
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
**版本差异**
|
**版本差异**
|
||||||
|
|
||||||
1. 版本 < 3.7.0.30 只运行不登录能获取个人信息,登录后可以获取数据库密钥
|
1. 版本 < 3.7.0.30 只运行不登录能获取个人信息,登录后可以获取数据库密钥
|
||||||
2. 版本 > 3.7.0.30 只运行不登录不能获取个人信息,登录后都能获取
|
2. 版本 > 3.7.0.30 只运行不登录不能获取个人信息,登录后都能获取
|
||||||
|
|
||||||
**利用场景**
|
**利用场景**
|
||||||
|
|
||||||
1. 钓鱼攻击(通过钓鱼控到的机器通常都是登录状态)
|
1. 钓鱼攻击(通过钓鱼控到的机器通常都是登录状态)
|
||||||
2. 渗透到运维机器(有些运维机器会日常登录自己的微信)
|
2. 渗透到运维机器(有些运维机器会日常登录自己的微信)
|
||||||
3. 某些工作需要取证(数据库需要拷贝到本地)
|
3. 某些工作需要取证(数据库需要拷贝到本地)
|
||||||
4. 自行备份(日常备份自己留存)
|
4. 自行备份(日常备份自己留存)
|
||||||
5. 等等...............
|
5. 等等...............
|
||||||
|
|
||||||
**数据库解密**
|
## 五、免责声明(非常重要!!!!!!!)
|
||||||
|
|
||||||
解密后可拖入数据库工具查找敏感信息
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
**参考地址**
|
|
||||||
|
|
||||||
数据库解密脚本:https://mp.weixin.qq.com/s/4DbXOS5jDjJzM2PN0Mp2JA
|
|
||||||
|
|
||||||
|
|
||||||
## 免责声明
|
|
||||||
本项目仅允许在授权情况下对数据库进行备份,严禁用于非法目的,否则自行承担所有相关责任。使用该工具则代表默认同意该条款;
|
本项目仅允许在授权情况下对数据库进行备份,严禁用于非法目的,否则自行承担所有相关责任。使用该工具则代表默认同意该条款;
|
||||||
|
|
||||||
请勿利用本项目的相关技术从事非法测试,如因此产生的一切不良后果与项目作者无关。
|
请勿利用本项目的相关技术从事非法测试,如因此产生的一切不良后果与项目作者无关。
|
||||||
|
50
decrypted/decrypt.py
Normal file
50
decrypted/decrypt.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
from Cryptodome.Cipher import AES
|
||||||
|
|
||||||
|
SQLITE_FILE_HEADER = "SQLite format 3\x00" # SQLite文件头
|
||||||
|
|
||||||
|
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")
|
||||||
|
return False
|
||||||
|
|
||||||
|
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:])
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
@ -1,301 +0,0 @@
|
|||||||
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)
|
|
293
decrypted/get_wx_decrypted_db.py
Normal file
293
decrypted/get_wx_decrypted_db.py
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
# -*- coding: utf-8 -*-#
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Name: get_wx_decrypted_db.py
|
||||||
|
# Description:
|
||||||
|
# Author: xaoyaoo
|
||||||
|
# Date: 2023/08/25
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import sqlite3
|
||||||
|
import winreg
|
||||||
|
|
||||||
|
from .decrypt import decrypt
|
||||||
|
|
||||||
|
|
||||||
|
# 开始获取微信数据库
|
||||||
|
def get_wechat_db():
|
||||||
|
try:
|
||||||
|
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Tencent\WeChat", 0, winreg.KEY_READ)
|
||||||
|
value, _ = winreg.QueryValueEx(key, "FileSavePath")
|
||||||
|
winreg.CloseKey(key)
|
||||||
|
w_dir = value
|
||||||
|
except Exception as e:
|
||||||
|
try:
|
||||||
|
w_dir = "MyDocument:"
|
||||||
|
except Exception as e:
|
||||||
|
print("读取注册表错误:", str(e))
|
||||||
|
return str(e)
|
||||||
|
|
||||||
|
if w_dir == "MyDocument:":
|
||||||
|
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):
|
||||||
|
return FileNotFoundError("目录不存在")
|
||||||
|
user_dirs = {} # wx用户目录
|
||||||
|
files = os.listdir(msg_dir)
|
||||||
|
for file_name in files:
|
||||||
|
if file_name == "All Users" or file_name == "Applet" or file_name == "WMPF":
|
||||||
|
continue
|
||||||
|
user_dirs[file_name] = os.path.join(msg_dir, file_name)
|
||||||
|
|
||||||
|
# 获取数据库路径
|
||||||
|
for user, user_dir in user_dirs.items():
|
||||||
|
Media_p = []
|
||||||
|
Micro_p = []
|
||||||
|
FTS_p = []
|
||||||
|
Sns_p = []
|
||||||
|
Msg = []
|
||||||
|
Emotion_p = []
|
||||||
|
for root, dirs, files in os.walk(user_dir):
|
||||||
|
for file_name in files:
|
||||||
|
if re.match(r".*MediaMSG.*\.db$", file_name):
|
||||||
|
src_path = os.path.join(root, file_name)
|
||||||
|
Media_p.append(src_path)
|
||||||
|
elif re.match(r".*MicroMsg.*\.db$", file_name):
|
||||||
|
src_path = os.path.join(root, file_name)
|
||||||
|
Micro_p.append(src_path)
|
||||||
|
elif re.match(r".*FTSMSG.*\.db$", file_name):
|
||||||
|
src_path = os.path.join(root, file_name)
|
||||||
|
FTS_p.append(src_path)
|
||||||
|
elif re.match(r".*MSG.*\.db$", file_name):
|
||||||
|
src_path = os.path.join(root, file_name)
|
||||||
|
Msg.append(src_path)
|
||||||
|
elif re.match(r".*Sns.*\.db$", file_name):
|
||||||
|
src_path = os.path.join(root, file_name)
|
||||||
|
Sns_p.append(src_path)
|
||||||
|
elif re.match(r".*Emotion.*\.db$", file_name):
|
||||||
|
src_path = os.path.join(root, file_name)
|
||||||
|
Emotion_p.append(src_path)
|
||||||
|
Media_p.sort()
|
||||||
|
Msg.sort()
|
||||||
|
Micro_p.sort()
|
||||||
|
# FTS_p.sort()
|
||||||
|
user_dirs[user] = {"MicroMsg": Micro_p, "Msg": Msg, "MediaMSG": Media_p, "Sns": Sns_p, "Emotion": Emotion_p}
|
||||||
|
return user_dirs
|
||||||
|
|
||||||
|
|
||||||
|
# 解密所有数据库 paths(文件) 到 decrypted_path(目录)
|
||||||
|
def all_decrypt(keys, paths, decrypted_path):
|
||||||
|
decrypted_paths = []
|
||||||
|
|
||||||
|
for key in keys:
|
||||||
|
for path in paths:
|
||||||
|
|
||||||
|
name = os.path.basename(path) # 文件名
|
||||||
|
dtp = os.path.join(decrypted_path, name) # 解密后的路径
|
||||||
|
if not decrypt(key, path, dtp):
|
||||||
|
break
|
||||||
|
decrypted_paths.append(dtp)
|
||||||
|
else: # for循环正常结束,没有break
|
||||||
|
break # 跳出while循环
|
||||||
|
else:
|
||||||
|
return False # while循环正常结束,没有break 解密失败
|
||||||
|
return decrypted_paths
|
||||||
|
|
||||||
|
|
||||||
|
def merge_copy_msg_db(db_path, save_path):
|
||||||
|
if isinstance(db_path, list) and len(db_path) == 1:
|
||||||
|
db_path = db_path[0]
|
||||||
|
if not os.path.exists(db_path):
|
||||||
|
raise FileNotFoundError("目录不存在")
|
||||||
|
shutil.move(db_path, save_path)
|
||||||
|
|
||||||
|
|
||||||
|
# 合并相同名称的数据库
|
||||||
|
def merge_msg_db(db_path: list, save_path: str, CreateTime: int = 0): # CreateTime: 从这个时间开始的消息 10位时间戳
|
||||||
|
|
||||||
|
merged_conn = sqlite3.connect(save_path)
|
||||||
|
merged_cursor = merged_conn.cursor()
|
||||||
|
|
||||||
|
for db_file in db_path:
|
||||||
|
c_tabels = merged_cursor.execute(
|
||||||
|
"select tbl_name from sqlite_master where type='table' and tbl_name!='sqlite_sequence'")
|
||||||
|
tabels_all = c_tabels.fetchall() # 所有表名
|
||||||
|
tabels_all = [row[0] for row in tabels_all]
|
||||||
|
|
||||||
|
conn = sqlite3.connect(db_file)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# 创建表
|
||||||
|
if len(tabels_all) < 4:
|
||||||
|
cursor.execute(
|
||||||
|
"select tbl_name,sql from sqlite_master where type='table' and tbl_name!='sqlite_sequence'")
|
||||||
|
c_part = cursor.fetchall()
|
||||||
|
|
||||||
|
for tbl_name, sql in c_part:
|
||||||
|
if tbl_name in tabels_all:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
merged_cursor.execute(sql)
|
||||||
|
tabels_all.append(tbl_name)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"error: {db_file}\n{tbl_name}\n{sql}\n{e}\n**********")
|
||||||
|
raise e
|
||||||
|
merged_conn.commit()
|
||||||
|
|
||||||
|
# 写入数据
|
||||||
|
for tbl_name in tabels_all:
|
||||||
|
if tbl_name == "MSG":
|
||||||
|
MsgSvrIDs = merged_cursor.execute(
|
||||||
|
f"select MsgSvrID from MSG where CreateTime>{CreateTime} and MsgSvrID!=0").fetchall()
|
||||||
|
|
||||||
|
cursor.execute(f"PRAGMA table_info({tbl_name})")
|
||||||
|
columns = cursor.fetchall()
|
||||||
|
columns = [column[1] for column in columns[1:]]
|
||||||
|
|
||||||
|
ex_sql = f"select {','.join(columns)} from {tbl_name} where CreateTime>{CreateTime} and MsgSvrID not in ({','.join([str(MsgSvrID[0]) for MsgSvrID in MsgSvrIDs])})"
|
||||||
|
cursor.execute(ex_sql)
|
||||||
|
|
||||||
|
insert_sql = f"INSERT INTO {tbl_name} ({','.join(columns)}) VALUES ({','.join(['?' for _ in range(len(columns))])})"
|
||||||
|
try:
|
||||||
|
merged_cursor.executemany(insert_sql, cursor.fetchall())
|
||||||
|
except Exception as e:
|
||||||
|
print(
|
||||||
|
f"error: {db_file}\n{tbl_name}\n{insert_sql}\n{cursor.fetchall()}\n{len(cursor.fetchall())}\n{e}\n**********")
|
||||||
|
raise e
|
||||||
|
merged_conn.commit()
|
||||||
|
else:
|
||||||
|
ex_sql = f"select * from {tbl_name}"
|
||||||
|
cursor.execute(ex_sql)
|
||||||
|
|
||||||
|
for r in cursor.fetchall():
|
||||||
|
cursor.execute(f"PRAGMA table_info({tbl_name})")
|
||||||
|
columns = cursor.fetchall()
|
||||||
|
if len(columns) > 1:
|
||||||
|
columns = [column[1] for column in columns[1:]]
|
||||||
|
values = r[1:]
|
||||||
|
else:
|
||||||
|
columns = [columns[0][1]]
|
||||||
|
values = [r[0]]
|
||||||
|
|
||||||
|
query_1 = "select * from " + tbl_name + " where " + columns[0] + "=?" # 查询语句 用于判断是否存在
|
||||||
|
c2 = merged_cursor.execute(query_1, values)
|
||||||
|
if len(c2.fetchall()) > 0: # 已存在
|
||||||
|
continue
|
||||||
|
query = "INSERT INTO " + tbl_name + " (" + ",".join(columns) + ") VALUES (" + ",".join(
|
||||||
|
["?" for _ in range(len(values))]) + ")"
|
||||||
|
|
||||||
|
try:
|
||||||
|
merged_cursor.execute(query, values)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"error: {db_file}\n{tbl_name}\n{query}\n{values}\n{len(values)}\n{e}\n**********")
|
||||||
|
raise e
|
||||||
|
merged_conn.commit()
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
sql = '''delete from MSG where localId in (SELECT localId from MSG
|
||||||
|
where MsgSvrID != 0 and MsgSvrID in (select MsgSvrID from MSG
|
||||||
|
where MsgSvrID != 0 GROUP BY MsgSvrID HAVING COUNT(*) > 1)
|
||||||
|
and localId not in (select min(localId) from MSG
|
||||||
|
where MsgSvrID != 0 GROUP BY MsgSvrID HAVING COUNT(*) > 1))'''
|
||||||
|
c = merged_cursor.execute(sql)
|
||||||
|
merged_conn.commit()
|
||||||
|
merged_conn.close()
|
||||||
|
return save_path
|
||||||
|
|
||||||
|
|
||||||
|
def merge_media_msg_db(db_path: list, save_path: str):
|
||||||
|
merged_conn = sqlite3.connect(save_path)
|
||||||
|
merged_cursor = merged_conn.cursor()
|
||||||
|
|
||||||
|
for db_file in db_path:
|
||||||
|
|
||||||
|
s = "select tbl_name,sql from sqlite_master where type='table' and tbl_name!='sqlite_sequence'"
|
||||||
|
have_tables = merged_cursor.execute(s).fetchall()
|
||||||
|
have_tables = [row[0] for row in have_tables]
|
||||||
|
|
||||||
|
conn_part = sqlite3.connect(db_file)
|
||||||
|
cursor = conn_part.cursor()
|
||||||
|
|
||||||
|
if len(have_tables) < 1:
|
||||||
|
cursor.execute(s)
|
||||||
|
table_part = cursor.fetchall()
|
||||||
|
tblname, sql = table_part[0]
|
||||||
|
|
||||||
|
sql = "CREATE TABLE Media(localId INTEGER PRIMARY KEY AUTOINCREMENT,Key TEXT,Reserved0 INT,Buf BLOB,Reserved1 INT,Reserved2 TEXT)"
|
||||||
|
try:
|
||||||
|
merged_cursor.execute(sql)
|
||||||
|
have_tables.append(tblname)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"error: {db_file}\n{tblname}\n{sql}\n{e}\n**********")
|
||||||
|
raise e
|
||||||
|
merged_conn.commit()
|
||||||
|
|
||||||
|
for tblname in have_tables:
|
||||||
|
s = "select Reserved0 from " + tblname
|
||||||
|
merged_cursor.execute(s)
|
||||||
|
r0 = merged_cursor.fetchall()
|
||||||
|
|
||||||
|
ex_sql = f"select * from {tblname} where Reserved0 not in ({','.join([str(r[0]) for r in r0])})"
|
||||||
|
cursor.execute(ex_sql)
|
||||||
|
data = cursor.fetchall()
|
||||||
|
|
||||||
|
insert_sql = f"INSERT INTO {tblname} (Key,Reserved0,Buf,Reserved1,Reserved2) VALUES ({','.join(['?' for _ in range(5)])})"
|
||||||
|
try:
|
||||||
|
merged_cursor.executemany(insert_sql, data)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"error: {db_file}\n{tblname}\n{insert_sql}\n{data}\n{len(data)}\n{e}\n**********")
|
||||||
|
raise e
|
||||||
|
merged_conn.commit()
|
||||||
|
conn_part.close()
|
||||||
|
|
||||||
|
merged_conn.close()
|
||||||
|
return save_path
|
||||||
|
|
||||||
|
|
||||||
|
def main(keys: list = None):
|
||||||
|
decrypted_ROOT = os.path.join(os.getcwd(), "decrypted")
|
||||||
|
|
||||||
|
|
||||||
|
user_dirs = get_wechat_db()
|
||||||
|
for user, db_path in user_dirs.items(): # 遍历用户
|
||||||
|
MicroMsgPaths = db_path["MicroMsg"]
|
||||||
|
MsgPaths = db_path["Msg"]
|
||||||
|
MediaMSGPaths = db_path["MediaMSG"]
|
||||||
|
# FTSMSGPaths = db_path["FTSMSG"]
|
||||||
|
SnsPaths = db_path["Sns"]
|
||||||
|
EmotionPaths = db_path["Emotion"]
|
||||||
|
|
||||||
|
decrypted_path_tmp = os.path.join(decrypted_ROOT, user, "tmp") # 解密后的目录
|
||||||
|
if not os.path.exists(decrypted_path_tmp):
|
||||||
|
os.makedirs(decrypted_path_tmp)
|
||||||
|
|
||||||
|
MicroMsgDecryptPaths = all_decrypt(keys, MicroMsgPaths, decrypted_path_tmp)
|
||||||
|
MsgDecryptPaths = all_decrypt(keys, MsgPaths, decrypted_path_tmp)
|
||||||
|
MediaMSGDecryptPaths = all_decrypt(keys, MediaMSGPaths, decrypted_path_tmp)
|
||||||
|
SnsDecryptPaths = all_decrypt(keys, SnsPaths, decrypted_path_tmp)
|
||||||
|
EmotionDecryptPaths = all_decrypt(keys, EmotionPaths, decrypted_path_tmp)
|
||||||
|
|
||||||
|
# 合并数据库
|
||||||
|
decrypted_path = os.path.join(decrypted_ROOT, user) # 解密后的目录
|
||||||
|
|
||||||
|
MicroMsgDbPath = os.path.join(decrypted_path, "MicroMsg.db")
|
||||||
|
MsgDbPath = os.path.join(decrypted_path, "MSG_all.db")
|
||||||
|
MediaMSGDbPath = os.path.join(decrypted_path, "MediaMSG_all.db")
|
||||||
|
SnsDbPath = os.path.join(decrypted_path, "Sns_all.db")
|
||||||
|
EmmotionDbPath = os.path.join(decrypted_path, "Emotion_all.db")
|
||||||
|
|
||||||
|
merge_copy_msg_db(MicroMsgDecryptPaths, MicroMsgDbPath)
|
||||||
|
merge_msg_db(MsgDecryptPaths, MsgDbPath, 0)
|
||||||
|
merge_media_msg_db(MediaMSGDecryptPaths, MediaMSGDbPath)
|
||||||
|
merge_copy_msg_db(SnsDecryptPaths, SnsDbPath)
|
||||||
|
merge_copy_msg_db(EmotionDecryptPaths, EmmotionDbPath)
|
||||||
|
|
||||||
|
shutil.rmtree(decrypted_path_tmp) # 删除临时文件
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
psutil
|
||||||
|
pycryptodome
|
||||||
|
pywin32
|
@ -1,303 +0,0 @@
|
|||||||
{
|
|
||||||
"3.2.1.154": [
|
|
||||||
328121948,
|
|
||||||
328122328,
|
|
||||||
328123056,
|
|
||||||
328121976,
|
|
||||||
328123020
|
|
||||||
],
|
|
||||||
"3.3.0.115": [
|
|
||||||
31323364,
|
|
||||||
31323744,
|
|
||||||
31324472,
|
|
||||||
31323392,
|
|
||||||
31324436
|
|
||||||
],
|
|
||||||
"3.3.0.84": [
|
|
||||||
31315212,
|
|
||||||
31315592,
|
|
||||||
31316320,
|
|
||||||
31315240,
|
|
||||||
31316284
|
|
||||||
],
|
|
||||||
"3.3.0.93": [
|
|
||||||
31323364,
|
|
||||||
31323744,
|
|
||||||
31324472,
|
|
||||||
31323392,
|
|
||||||
31324436
|
|
||||||
],
|
|
||||||
"3.3.5.34": [
|
|
||||||
30603028,
|
|
||||||
30603408,
|
|
||||||
30604120,
|
|
||||||
30603056,
|
|
||||||
30604100
|
|
||||||
],
|
|
||||||
"3.3.5.42": [
|
|
||||||
30603012,
|
|
||||||
30603392,
|
|
||||||
30604120,
|
|
||||||
30603040,
|
|
||||||
30604084
|
|
||||||
],
|
|
||||||
"3.3.5.46": [
|
|
||||||
30578372,
|
|
||||||
30578752,
|
|
||||||
30579480,
|
|
||||||
30578400,
|
|
||||||
30579444
|
|
||||||
],
|
|
||||||
"3.4.0.37": [
|
|
||||||
31608116,
|
|
||||||
31608496,
|
|
||||||
31609224,
|
|
||||||
31608144,
|
|
||||||
31609188
|
|
||||||
],
|
|
||||||
"3.4.0.38": [
|
|
||||||
31604044,
|
|
||||||
31604424,
|
|
||||||
31605152,
|
|
||||||
31604072,
|
|
||||||
31605116
|
|
||||||
],
|
|
||||||
"3.4.0.50": [
|
|
||||||
31688500,
|
|
||||||
31688880,
|
|
||||||
31689608,
|
|
||||||
31688528,
|
|
||||||
31689572
|
|
||||||
],
|
|
||||||
"3.4.0.54": [
|
|
||||||
31700852,
|
|
||||||
31701248,
|
|
||||||
31700920,
|
|
||||||
31700880,
|
|
||||||
31701924
|
|
||||||
],
|
|
||||||
"3.4.5.27": [
|
|
||||||
32133788,
|
|
||||||
32134168,
|
|
||||||
32134896,
|
|
||||||
32133816,
|
|
||||||
32134860
|
|
||||||
],
|
|
||||||
"3.4.5.45": [
|
|
||||||
32147012,
|
|
||||||
32147392,
|
|
||||||
32147064,
|
|
||||||
32147040,
|
|
||||||
32148084
|
|
||||||
],
|
|
||||||
"3.5.0.20": [
|
|
||||||
35494484,
|
|
||||||
35494864,
|
|
||||||
35494536,
|
|
||||||
35494512,
|
|
||||||
35495556
|
|
||||||
],
|
|
||||||
"3.5.0.29": [
|
|
||||||
35507980,
|
|
||||||
35508360,
|
|
||||||
35508032,
|
|
||||||
35508008,
|
|
||||||
35509052
|
|
||||||
],
|
|
||||||
"3.5.0.33": [
|
|
||||||
35512140,
|
|
||||||
35512520,
|
|
||||||
35512192,
|
|
||||||
35512168,
|
|
||||||
35513212
|
|
||||||
],
|
|
||||||
"3.5.0.39": [
|
|
||||||
35516236,
|
|
||||||
35516616,
|
|
||||||
35516288,
|
|
||||||
35516264,
|
|
||||||
35517308
|
|
||||||
],
|
|
||||||
"3.5.0.42": [
|
|
||||||
35512140,
|
|
||||||
35512520,
|
|
||||||
35512192,
|
|
||||||
35512168,
|
|
||||||
35513212
|
|
||||||
],
|
|
||||||
"3.5.0.44": [
|
|
||||||
35510836,
|
|
||||||
35511216,
|
|
||||||
35510896,
|
|
||||||
35510864,
|
|
||||||
35511908
|
|
||||||
],
|
|
||||||
"3.5.0.46": [
|
|
||||||
35506740,
|
|
||||||
35507120,
|
|
||||||
35506800,
|
|
||||||
35506768,
|
|
||||||
35507812
|
|
||||||
],
|
|
||||||
"3.6.0.18": [
|
|
||||||
35842996,
|
|
||||||
35843376,
|
|
||||||
35843048,
|
|
||||||
35843024,
|
|
||||||
35844068
|
|
||||||
],
|
|
||||||
"3.6.5.7": [
|
|
||||||
35864356,
|
|
||||||
35864736,
|
|
||||||
35864408,
|
|
||||||
35864384,
|
|
||||||
35865428
|
|
||||||
],
|
|
||||||
"3.6.5.16": [
|
|
||||||
35909428,
|
|
||||||
35909808,
|
|
||||||
35909480,
|
|
||||||
35909456,
|
|
||||||
35910500
|
|
||||||
],
|
|
||||||
"3.7.0.26": [
|
|
||||||
37105908,
|
|
||||||
37106288,
|
|
||||||
37105960,
|
|
||||||
37105936,
|
|
||||||
37106980
|
|
||||||
],
|
|
||||||
"3.7.0.29": [
|
|
||||||
37105908,
|
|
||||||
37106288,
|
|
||||||
37105960,
|
|
||||||
37105936,
|
|
||||||
37106980
|
|
||||||
],
|
|
||||||
"3.7.0.30": [
|
|
||||||
37118196,
|
|
||||||
37118576,
|
|
||||||
37118248,
|
|
||||||
37118224,
|
|
||||||
37119268
|
|
||||||
],
|
|
||||||
"3.7.5.11": [
|
|
||||||
37883280,
|
|
||||||
37884088,
|
|
||||||
37883136,
|
|
||||||
37883008,
|
|
||||||
37884052
|
|
||||||
],
|
|
||||||
"3.7.5.23": [
|
|
||||||
37895736,
|
|
||||||
37896544,
|
|
||||||
37895592,
|
|
||||||
37883008,
|
|
||||||
37896508
|
|
||||||
],
|
|
||||||
"3.7.5.27": [
|
|
||||||
37895736,
|
|
||||||
37896544,
|
|
||||||
37895592,
|
|
||||||
37895464,
|
|
||||||
37896508
|
|
||||||
],
|
|
||||||
"3.7.5.31": [
|
|
||||||
37903928,
|
|
||||||
37904736,
|
|
||||||
37903784,
|
|
||||||
37903656,
|
|
||||||
37904700
|
|
||||||
],
|
|
||||||
"3.7.6.24": [
|
|
||||||
38978840,
|
|
||||||
38979648,
|
|
||||||
38978696,
|
|
||||||
38978604,
|
|
||||||
38979612
|
|
||||||
],
|
|
||||||
"3.7.6.29": [
|
|
||||||
38986376,
|
|
||||||
38987184,
|
|
||||||
38986232,
|
|
||||||
38986104,
|
|
||||||
38987148
|
|
||||||
],
|
|
||||||
"3.7.6.44": [
|
|
||||||
39016520,
|
|
||||||
39017328,
|
|
||||||
39016376,
|
|
||||||
38986104,
|
|
||||||
39017292
|
|
||||||
],
|
|
||||||
"3.8.0.31": [
|
|
||||||
46064088,
|
|
||||||
46064912,
|
|
||||||
46063944,
|
|
||||||
38986104,
|
|
||||||
46064876
|
|
||||||
],
|
|
||||||
"3.8.0.33": [
|
|
||||||
46059992,
|
|
||||||
46060816,
|
|
||||||
46059848,
|
|
||||||
38986104,
|
|
||||||
46060780
|
|
||||||
],
|
|
||||||
"3.8.0.41": [
|
|
||||||
46064024,
|
|
||||||
46064848,
|
|
||||||
46063880,
|
|
||||||
38986104,
|
|
||||||
46064812
|
|
||||||
],
|
|
||||||
"3.8.1.26": [
|
|
||||||
46409448,
|
|
||||||
46410272,
|
|
||||||
46409304,
|
|
||||||
38986104,
|
|
||||||
46410236
|
|
||||||
],
|
|
||||||
"3.9.0.28": [
|
|
||||||
48418376,
|
|
||||||
48419280,
|
|
||||||
48418232,
|
|
||||||
38986104,
|
|
||||||
48419244
|
|
||||||
],
|
|
||||||
"3.9.2.23": [
|
|
||||||
50320784,
|
|
||||||
50321712,
|
|
||||||
50320640,
|
|
||||||
38986104,
|
|
||||||
50321676
|
|
||||||
],
|
|
||||||
"3.9.2.26": [
|
|
||||||
50329040,
|
|
||||||
50329968,
|
|
||||||
50328896,
|
|
||||||
38986104,
|
|
||||||
50329932
|
|
||||||
],
|
|
||||||
"3.9.5.91": [
|
|
||||||
61654904,
|
|
||||||
61654680,
|
|
||||||
61654712,
|
|
||||||
38986104,
|
|
||||||
61656176
|
|
||||||
],
|
|
||||||
"3.9.6.19": [
|
|
||||||
61997688,
|
|
||||||
61997464,
|
|
||||||
61997496,
|
|
||||||
38986104,
|
|
||||||
61998960
|
|
||||||
],
|
|
||||||
"3.9.6.33": [
|
|
||||||
62030600,
|
|
||||||
62031936,
|
|
||||||
62030408,
|
|
||||||
38986104,
|
|
||||||
62031872
|
|
||||||
]
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user