mirror of
https://github.com/LC044/WeChatMsg
synced 2025-02-22 19:02:17 +08:00
更新解密文件
This commit is contained in:
parent
d7ebe9c1d5
commit
5bee934ed2
@ -1,12 +1,24 @@
|
|||||||
import hashlib
|
# -*- coding: utf-8 -*-#
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Name: getwxinfo.py
|
||||||
|
# Description:
|
||||||
|
# Author: xaoyaoo
|
||||||
|
# Date: 2023/08/21
|
||||||
|
# 微信数据库采用的加密算法是256位的AES-CBC。数据库的默认的页大小是4096字节即4KB,其中每一个页都是被单独加解密的。
|
||||||
|
# 加密文件的每一个页都有一个随机的初始化向量,它被保存在每一页的末尾。
|
||||||
|
# 加密文件的每一页都存有着消息认证码,算法使用的是HMAC-SHA1(安卓数据库使用的是SHA512)。它也被保存在每一页的末尾。
|
||||||
|
# 每一个数据库文件的开头16字节都保存了一段唯一且随机的盐值,作为HMAC的验证和数据的解密。
|
||||||
|
# 用来计算HMAC的key与解密的key是不同的,解密用的密钥是主密钥和之前提到的16字节的盐值通过PKCS5_PBKF2_HMAC1密钥扩展算法迭代64000次计算得到的。而计算HMAC的密钥是刚提到的解密密钥和16字节盐值异或0x3a的值通过PKCS5_PBKF2_HMAC1密钥扩展算法迭代2次计算得到的。
|
||||||
|
# 为了保证数据部分长度是16字节即AES块大小的整倍数,每一页的末尾将填充一段空字节,使得保留字段的长度为48字节。
|
||||||
|
# 综上,加密文件结构为第一页4KB数据前16字节为盐值,紧接着4032字节数据,再加上16字节IV和20字节HMAC以及12字节空字节;而后的页均是4048字节长度的加密数据段和48字节的保留段。
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
import argparse
|
||||||
import hmac
|
import hmac
|
||||||
|
import hashlib
|
||||||
import os
|
import os
|
||||||
from typing import Union, List
|
from typing import Union, List
|
||||||
|
|
||||||
from Cryptodome.Cipher import AES
|
from Cryptodome.Cipher import AES
|
||||||
|
|
||||||
from app.log import log, logger
|
|
||||||
|
|
||||||
# from Crypto.Cipher import AES # 如果上面的导入失败,可以尝试使用这个
|
# from Crypto.Cipher import AES # 如果上面的导入失败,可以尝试使用这个
|
||||||
|
|
||||||
SQLITE_FILE_HEADER = "SQLite format 3\x00" # SQLite文件头
|
SQLITE_FILE_HEADER = "SQLite format 3\x00" # SQLite文件头
|
||||||
@ -17,15 +29,22 @@ DEFAULT_ITER = 64000
|
|||||||
|
|
||||||
|
|
||||||
# 通过密钥解密数据库
|
# 通过密钥解密数据库
|
||||||
@log
|
|
||||||
def decrypt(key: str, db_path, out_path):
|
def decrypt(key: str, db_path, out_path):
|
||||||
if not os.path.exists(db_path):
|
"""
|
||||||
return f"[-] db_path:'{db_path}' File not found!"
|
通过密钥解密数据库
|
||||||
|
: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)):
|
if not os.path.exists(os.path.dirname(out_path)):
|
||||||
return f"[-] out_path:'{out_path}' File not found!"
|
return False, f"[-] out_path:'{out_path}' File not found!"
|
||||||
|
|
||||||
if len(key) != 64:
|
if len(key) != 64:
|
||||||
logger.error(f"[-] key:'{key}' Error!")
|
return False, f"[-] key:'{key}' Len Error!"
|
||||||
return -1
|
|
||||||
password = bytes.fromhex(key.strip())
|
password = bytes.fromhex(key.strip())
|
||||||
with open(db_path, "rb") as file:
|
with open(db_path, "rb") as file:
|
||||||
blist = file.read()
|
blist = file.read()
|
||||||
@ -33,6 +52,8 @@ def decrypt(key: str, db_path, out_path):
|
|||||||
salt = blist[:16]
|
salt = blist[:16]
|
||||||
byteKey = hashlib.pbkdf2_hmac("sha1", password, salt, DEFAULT_ITER, KEY_SIZE)
|
byteKey = hashlib.pbkdf2_hmac("sha1", password, salt, DEFAULT_ITER, KEY_SIZE)
|
||||||
first = blist[16:DEFAULT_PAGESIZE]
|
first = blist[16:DEFAULT_PAGESIZE]
|
||||||
|
if len(salt) != 16:
|
||||||
|
return False, f"[-] db_path:'{db_path}' File Error!"
|
||||||
|
|
||||||
mac_salt = bytes([(salt[i] ^ 58) for i in range(16)])
|
mac_salt = bytes([(salt[i] ^ 58) for i in range(16)])
|
||||||
mac_key = hashlib.pbkdf2_hmac("sha1", byteKey, mac_salt, 2, KEY_SIZE)
|
mac_key = hashlib.pbkdf2_hmac("sha1", byteKey, mac_salt, 2, KEY_SIZE)
|
||||||
@ -40,8 +61,7 @@ def decrypt(key: str, db_path, out_path):
|
|||||||
hash_mac.update(b'\x01\x00\x00\x00')
|
hash_mac.update(b'\x01\x00\x00\x00')
|
||||||
|
|
||||||
if hash_mac.digest() != first[-32:-12]:
|
if hash_mac.digest() != first[-32:-12]:
|
||||||
logger.error(f"[-] Password Error! (key:'{key}'; db_path:'{db_path}'; out_path:'{out_path}' )")
|
return False, f"[-] Key Error! (key:'{key}'; db_path:'{db_path}'; out_path:'{out_path}' )"
|
||||||
return -1
|
|
||||||
|
|
||||||
newblist = [blist[i:i + DEFAULT_PAGESIZE] for i in range(DEFAULT_PAGESIZE, len(blist), DEFAULT_PAGESIZE)]
|
newblist = [blist[i:i + DEFAULT_PAGESIZE] for i in range(DEFAULT_PAGESIZE, len(blist), DEFAULT_PAGESIZE)]
|
||||||
|
|
||||||
@ -57,19 +77,22 @@ def decrypt(key: str, db_path, out_path):
|
|||||||
decrypted = t.decrypt(i[:-48])
|
decrypted = t.decrypt(i[:-48])
|
||||||
deFile.write(decrypted)
|
deFile.write(decrypted)
|
||||||
deFile.write(i[-48:])
|
deFile.write(i[-48:])
|
||||||
return [True, db_path, out_path, key]
|
return True, [db_path, out_path, key]
|
||||||
|
|
||||||
|
|
||||||
@log
|
def batch_decrypt(key: str, db_path: Union[str, List[str]], out_path: str, is_logging: bool = False):
|
||||||
def batch_decrypt(key: str, db_path: Union[str, List[str]], out_path: str):
|
|
||||||
if not isinstance(key, str) or not isinstance(out_path, str) or not os.path.exists(out_path) or len(key) != 64:
|
if not isinstance(key, str) or not isinstance(out_path, str) or not os.path.exists(out_path) or len(key) != 64:
|
||||||
return f"[-] (key:'{key}' or out_path:'{out_path}') Error!"
|
error = f"[-] (key:'{key}' or out_path:'{out_path}') Error!"
|
||||||
|
if is_logging: print(error)
|
||||||
|
return False, error
|
||||||
|
|
||||||
process_list = []
|
process_list = []
|
||||||
|
|
||||||
if isinstance(db_path, str):
|
if isinstance(db_path, str):
|
||||||
if not os.path.exists(db_path):
|
if not os.path.exists(db_path):
|
||||||
return f"[-] db_path:'{db_path}' not found!"
|
error = f"[-] db_path:'{db_path}' not found!"
|
||||||
|
if is_logging: print(error)
|
||||||
|
return False, error
|
||||||
|
|
||||||
if os.path.isfile(db_path):
|
if os.path.isfile(db_path):
|
||||||
inpath = db_path
|
inpath = db_path
|
||||||
@ -87,7 +110,10 @@ def batch_decrypt(key: str, db_path: Union[str, List[str]], out_path: str):
|
|||||||
os.makedirs(os.path.dirname(outpath))
|
os.makedirs(os.path.dirname(outpath))
|
||||||
process_list.append([key, inpath, outpath])
|
process_list.append([key, inpath, outpath])
|
||||||
else:
|
else:
|
||||||
return f"[-] db_path:'{db_path}' Error "
|
error = f"[-] db_path:'{db_path}' Error "
|
||||||
|
if is_logging: print(error)
|
||||||
|
return False, error
|
||||||
|
|
||||||
elif isinstance(db_path, list):
|
elif isinstance(db_path, list):
|
||||||
rt_path = os.path.commonprefix(db_path)
|
rt_path = os.path.commonprefix(db_path)
|
||||||
if not os.path.exists(rt_path):
|
if not os.path.exists(rt_path):
|
||||||
@ -95,7 +121,9 @@ def batch_decrypt(key: str, db_path: Union[str, List[str]], out_path: str):
|
|||||||
|
|
||||||
for inpath in db_path:
|
for inpath in db_path:
|
||||||
if not os.path.exists(inpath):
|
if not os.path.exists(inpath):
|
||||||
return f"[-] db_path:'{db_path}' not found!"
|
erreor = f"[-] db_path:'{db_path}' not found!"
|
||||||
|
if is_logging: print(erreor)
|
||||||
|
return False, erreor
|
||||||
|
|
||||||
inpath = os.path.normpath(inpath)
|
inpath = os.path.normpath(inpath)
|
||||||
rel = os.path.relpath(os.path.dirname(inpath), rt_path)
|
rel = os.path.relpath(os.path.dirname(inpath), rt_path)
|
||||||
@ -104,7 +132,9 @@ def batch_decrypt(key: str, db_path: Union[str, List[str]], out_path: str):
|
|||||||
os.makedirs(os.path.dirname(outpath))
|
os.makedirs(os.path.dirname(outpath))
|
||||||
process_list.append([key, inpath, outpath])
|
process_list.append([key, inpath, outpath])
|
||||||
else:
|
else:
|
||||||
return f"[-] db_path:'{db_path}' Error "
|
error = f"[-] db_path:'{db_path}' Error "
|
||||||
|
if is_logging: print(error)
|
||||||
|
return False, error
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
for i in process_list:
|
for i in process_list:
|
||||||
@ -115,12 +145,63 @@ def batch_decrypt(key: str, db_path: Union[str, List[str]], out_path: str):
|
|||||||
for dir in dirs:
|
for dir in dirs:
|
||||||
if not os.listdir(os.path.join(root, dir)):
|
if not os.listdir(os.path.join(root, dir)):
|
||||||
os.rmdir(os.path.join(root, dir))
|
os.rmdir(os.path.join(root, dir))
|
||||||
return result
|
|
||||||
|
if is_logging:
|
||||||
|
print("=" * 32)
|
||||||
|
success_count = 0
|
||||||
|
fail_count = 0
|
||||||
|
for code, ret in result:
|
||||||
|
if code == False:
|
||||||
|
print(ret)
|
||||||
|
fail_count += 1
|
||||||
|
else:
|
||||||
|
print(f'[+] "{ret[0]}" -> "{ret[1]}"')
|
||||||
|
success_count += 1
|
||||||
|
print("-" * 32)
|
||||||
|
print(f"[+] 共 {len(result)} 个文件, 成功 {success_count} 个, 失败 {fail_count} 个")
|
||||||
|
print("=" * 32)
|
||||||
|
return True, result
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def encrypt(key: str, db_path, out_path):
|
||||||
# 调用 decrypt 函数,并传入参数
|
"""
|
||||||
key = "2aafab10af7940328bb92ac9d2a8ab5fc07a685646b14f2e9ae6948a7060c0fc"
|
通过密钥加密数据库
|
||||||
db_path = "E:\86390\Documents\WeChat Files\wxid_27hqbq7vx5hf22\FileStorage\CustomEmotion\\71\\71CE49ED3CE9E57E43E07F802983BF45"
|
:param key: 密钥 64位16进制字符串
|
||||||
out_path = "./test/1.png"
|
:param db_path: 待加密的数据库路径(必须是文件)
|
||||||
print(decrypt(key, db_path, out_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())
|
||||||
|
with open(db_path, "rb") as file:
|
||||||
|
blist = file.read()
|
||||||
|
|
||||||
|
salt = os.urandom(16) # 生成随机盐值
|
||||||
|
byteKey = hashlib.pbkdf2_hmac("sha1", password, salt, DEFAULT_ITER, KEY_SIZE)
|
||||||
|
|
||||||
|
# 计算消息认证码
|
||||||
|
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, blist[:-32], hashlib.sha1)
|
||||||
|
hash_mac.update(b'\x01\x00\x00\x00')
|
||||||
|
mac_digest = hash_mac.digest()
|
||||||
|
|
||||||
|
newblist = [blist[i:i + DEFAULT_PAGESIZE] for i in range(DEFAULT_PAGESIZE, len(blist), DEFAULT_PAGESIZE)]
|
||||||
|
|
||||||
|
with open(out_path, "wb") as enFile:
|
||||||
|
enFile.write(salt) # 写入盐值
|
||||||
|
enFile.write(mac_digest) # 写入消息认证码
|
||||||
|
|
||||||
|
for i in newblist:
|
||||||
|
t = AES.new(byteKey, AES.MODE_CBC, os.urandom(16)) # 生成随机的初始向量
|
||||||
|
encrypted = t.encrypt(i) # 加密数据块
|
||||||
|
enFile.write(encrypted)
|
||||||
|
|
||||||
|
return True, [db_path, out_path, key]
|
@ -5,24 +5,58 @@
|
|||||||
# Author: xaoyaoo
|
# Author: xaoyaoo
|
||||||
# Date: 2023/08/21
|
# Date: 2023/08/21
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
import ctypes
|
import ctypes
|
||||||
import json
|
import os
|
||||||
|
import re
|
||||||
import psutil
|
import winreg
|
||||||
import pymem
|
import pymem
|
||||||
from win32com.client import Dispatch
|
from win32com.client import Dispatch
|
||||||
|
import psutil
|
||||||
from app.log import log
|
import sys
|
||||||
|
from typing import List, Union
|
||||||
|
|
||||||
ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory
|
ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory
|
||||||
void_p = ctypes.c_void_p
|
void_p = ctypes.c_void_p
|
||||||
|
|
||||||
ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory
|
|
||||||
void_p = ctypes.c_void_p
|
# 获取exe文件的位数
|
||||||
|
def get_exe_bit(file_path):
|
||||||
|
"""
|
||||||
|
获取 PE 文件的位数: 32 位或 64 位
|
||||||
|
:param file_path: PE 文件路径(可执行文件)
|
||||||
|
:return: 如果遇到错误则返回 64
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(file_path, 'rb') as f:
|
||||||
|
dos_header = f.read(2)
|
||||||
|
if dos_header != b'MZ':
|
||||||
|
print('get exe bit error: Invalid PE file')
|
||||||
|
return 64
|
||||||
|
# Seek to the offset of the PE signature
|
||||||
|
f.seek(60)
|
||||||
|
pe_offset_bytes = f.read(4)
|
||||||
|
pe_offset = int.from_bytes(pe_offset_bytes, byteorder='little')
|
||||||
|
|
||||||
|
# Seek to the Machine field in the PE header
|
||||||
|
f.seek(pe_offset + 4)
|
||||||
|
machine_bytes = f.read(2)
|
||||||
|
machine = int.from_bytes(machine_bytes, byteorder='little')
|
||||||
|
|
||||||
|
if machine == 0x14c:
|
||||||
|
return 32
|
||||||
|
elif machine == 0x8664:
|
||||||
|
return 64
|
||||||
|
else:
|
||||||
|
print('get exe bit error: Unknown architecture: %s' % hex(machine))
|
||||||
|
return 64
|
||||||
|
except IOError:
|
||||||
|
print('get exe bit error: File not found or cannot be opened')
|
||||||
|
return 64
|
||||||
|
|
||||||
|
|
||||||
# 读取内存中的字符串(非key部分)
|
# 读取内存中的字符串(非key部分)
|
||||||
@log
|
|
||||||
def get_info_without_key(h_process, address, n_size=64):
|
def get_info_without_key(h_process, address, n_size=64):
|
||||||
array = ctypes.create_string_buffer(n_size)
|
array = ctypes.create_string_buffer(n_size)
|
||||||
if ReadProcessMemory(h_process, void_p(address), array, n_size, 0) == 0: return "None"
|
if ReadProcessMemory(h_process, void_p(address), array, n_size, 0) == 0: return "None"
|
||||||
@ -31,7 +65,6 @@ def get_info_without_key(h_process, address, n_size=64):
|
|||||||
return text.strip() if text.strip() != "" else "None"
|
return text.strip() if text.strip() != "" else "None"
|
||||||
|
|
||||||
|
|
||||||
@log
|
|
||||||
def pattern_scan_all(handle, pattern, *, return_multiple=False, find_num=100):
|
def pattern_scan_all(handle, pattern, *, return_multiple=False, find_num=100):
|
||||||
next_region = 0
|
next_region = 0
|
||||||
found = []
|
found = []
|
||||||
@ -56,39 +89,117 @@ def pattern_scan_all(handle, pattern, *, return_multiple=False, find_num=100):
|
|||||||
return found
|
return found
|
||||||
|
|
||||||
|
|
||||||
@log
|
|
||||||
def get_info_wxid(h_process):
|
def get_info_wxid(h_process):
|
||||||
find_num = 100
|
find_num = 100
|
||||||
addrs = pattern_scan_all(h_process, br'\\FileStorage', return_multiple=True, find_num=find_num)
|
addrs = pattern_scan_all(h_process, br'\\Msg\\FTSContact', return_multiple=True, find_num=find_num)
|
||||||
wxids = []
|
wxids = []
|
||||||
for addr in addrs:
|
for addr in addrs:
|
||||||
array = ctypes.create_string_buffer(33)
|
array = ctypes.create_string_buffer(80)
|
||||||
if ReadProcessMemory(h_process, void_p(addr - 21), array, 33, 0) == 0: return "None"
|
if ReadProcessMemory(h_process, void_p(addr - 30), array, 80, 0) == 0: return "None"
|
||||||
array = bytes(array) # .decode('utf-8', errors='ignore')
|
array = bytes(array) # .split(b"\\")[0]
|
||||||
array = array.split(br'\FileStorage')[0]
|
array = array.split(b"\\Msg")[0]
|
||||||
for part in [b'}', b'\x7f', b'\\']:
|
array = array.split(b"\\")[-1]
|
||||||
if part in array:
|
|
||||||
array = array.split(part)[1]
|
|
||||||
wxids.append(array.decode('utf-8', errors='ignore'))
|
wxids.append(array.decode('utf-8', errors='ignore'))
|
||||||
break
|
|
||||||
wxid = max(wxids, key=wxids.count) if wxids else "None"
|
wxid = max(wxids, key=wxids.count) if wxids else "None"
|
||||||
return wxid
|
return wxid
|
||||||
|
|
||||||
|
|
||||||
# 读取内存中的key
|
def get_info_filePath(wxid="all"):
|
||||||
@log
|
if not wxid:
|
||||||
def get_key(h_process, address, address_len=8):
|
return "None"
|
||||||
|
try:
|
||||||
|
user_profile = os.environ.get("USERPROFILE")
|
||||||
|
path_3ebffe94 = os.path.join(user_profile, "AppData", "Roaming", "Tencent", "WeChat", "All Users", "config",
|
||||||
|
"3ebffe94.ini")
|
||||||
|
with open(path_3ebffe94, "r", encoding="utf-8") as f:
|
||||||
|
w_dir = f.read()
|
||||||
|
except Exception as e:
|
||||||
|
w_dir = "MyDocument:"
|
||||||
|
|
||||||
|
if w_dir == "MyDocument:":
|
||||||
|
try:
|
||||||
|
# 打开注册表路径
|
||||||
|
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
|
||||||
|
r"Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders")
|
||||||
|
documents_path = winreg.QueryValueEx(key, "Personal")[0] # 读取文档实际目录路径
|
||||||
|
winreg.CloseKey(key) # 关闭注册表
|
||||||
|
documents_paths = os.path.split(documents_path)
|
||||||
|
if "%" in documents_paths[0]:
|
||||||
|
w_dir = os.environ.get(documents_paths[0].replace("%", ""))
|
||||||
|
w_dir = os.path.join(w_dir, os.path.join(*documents_paths[1:]))
|
||||||
|
# print(1, w_dir)
|
||||||
|
else:
|
||||||
|
w_dir = documents_path
|
||||||
|
except Exception as e:
|
||||||
|
profile = os.environ.get("USERPROFILE")
|
||||||
|
w_dir = os.path.join(profile, "Documents")
|
||||||
|
|
||||||
|
msg_dir = os.path.join(w_dir, "WeChat Files")
|
||||||
|
|
||||||
|
if wxid == "all" and os.path.exists(msg_dir):
|
||||||
|
return msg_dir
|
||||||
|
|
||||||
|
filePath = os.path.join(msg_dir, wxid)
|
||||||
|
return filePath if os.path.exists(filePath) else "None"
|
||||||
|
|
||||||
|
|
||||||
|
def get_key(db_path, addr_len):
|
||||||
|
def read_key_bytes(h_process, address, address_len=8):
|
||||||
array = ctypes.create_string_buffer(address_len)
|
array = ctypes.create_string_buffer(address_len)
|
||||||
if ReadProcessMemory(h_process, void_p(address), array, address_len, 0) == 0: return "None"
|
if ReadProcessMemory(h_process, void_p(address), array, address_len, 0) == 0: return "None"
|
||||||
address = int.from_bytes(array, byteorder='little') # 逆序转换为int地址(key地址)
|
address = int.from_bytes(array, byteorder='little') # 逆序转换为int地址(key地址)
|
||||||
key = ctypes.create_string_buffer(32)
|
key = ctypes.create_string_buffer(32)
|
||||||
if ReadProcessMemory(h_process, void_p(address), key, 32, 0) == 0: return "None"
|
if ReadProcessMemory(h_process, void_p(address), key, 32, 0) == 0: return "None"
|
||||||
key_string = bytes(key).hex()
|
key_bytes = bytes(key)
|
||||||
return key_string
|
return key_bytes
|
||||||
|
|
||||||
|
def verify_key(key, wx_db_path):
|
||||||
|
KEY_SIZE = 32
|
||||||
|
DEFAULT_PAGESIZE = 4096
|
||||||
|
DEFAULT_ITER = 64000
|
||||||
|
with open(wx_db_path, "rb") as file:
|
||||||
|
blist = file.read(5000)
|
||||||
|
salt = blist[:16]
|
||||||
|
byteKey = hashlib.pbkdf2_hmac("sha1", key, 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]:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
phone_type1 = "iphone\x00"
|
||||||
|
phone_type2 = "android\x00"
|
||||||
|
phone_type3 = "ipad\x00"
|
||||||
|
|
||||||
|
pm = pymem.Pymem("WeChat.exe")
|
||||||
|
module_name = "WeChatWin.dll"
|
||||||
|
|
||||||
|
MicroMsg_path = os.path.join(db_path, "MSG", "MicroMsg.db")
|
||||||
|
|
||||||
|
type1_addrs = pm.pattern_scan_module(phone_type1.encode(), module_name, return_multiple=True)
|
||||||
|
type2_addrs = pm.pattern_scan_module(phone_type2.encode(), module_name, return_multiple=True)
|
||||||
|
type3_addrs = pm.pattern_scan_module(phone_type3.encode(), module_name, return_multiple=True)
|
||||||
|
type_addrs = type1_addrs if len(type1_addrs) >= 2 else type2_addrs if len(type2_addrs) >= 2 else type3_addrs if len(
|
||||||
|
type3_addrs) >= 2 else "None"
|
||||||
|
# print(type_addrs)
|
||||||
|
if type_addrs == "None":
|
||||||
|
return "None"
|
||||||
|
for i in type_addrs[::-1]:
|
||||||
|
for j in range(i, i - 2000, -addr_len):
|
||||||
|
key_bytes = read_key_bytes(pm.process_handle, j, addr_len)
|
||||||
|
if key_bytes == "None":
|
||||||
|
continue
|
||||||
|
if verify_key(key_bytes, MicroMsg_path):
|
||||||
|
return key_bytes.hex()
|
||||||
|
return "None"
|
||||||
|
|
||||||
|
|
||||||
# 读取微信信息(account,mobile,name,mail,wxid,key)
|
# 读取微信信息(account,mobile,name,mail,wxid,key)
|
||||||
@log
|
|
||||||
def read_info(version_list, is_logging=False):
|
def read_info(version_list, is_logging=False):
|
||||||
wechat_process = []
|
wechat_process = []
|
||||||
result = []
|
result = []
|
||||||
@ -108,12 +219,6 @@ def read_info(version_list, is_logging=False):
|
|||||||
tmp_rd['pid'] = process.pid
|
tmp_rd['pid'] = process.pid
|
||||||
tmp_rd['version'] = Dispatch("Scripting.FileSystemObject").GetFileVersion(process.exe())
|
tmp_rd['version'] = Dispatch("Scripting.FileSystemObject").GetFileVersion(process.exe())
|
||||||
|
|
||||||
bias_list = version_list.get(tmp_rd['version'], None)
|
|
||||||
if not isinstance(bias_list, list):
|
|
||||||
error = f"[-] WeChat Current Version {tmp_rd['version']} Is Not Supported"
|
|
||||||
if is_logging: print(error)
|
|
||||||
return -2
|
|
||||||
|
|
||||||
wechat_base_address = 0
|
wechat_base_address = 0
|
||||||
for module in process.memory_maps(grouped=False):
|
for module in process.memory_maps(grouped=False):
|
||||||
if module.path and 'WeChatWin.dll' in module.path:
|
if module.path and 'WeChatWin.dll' in module.path:
|
||||||
@ -126,20 +231,32 @@ def read_info(version_list, is_logging=False):
|
|||||||
|
|
||||||
Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, process.pid)
|
Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, process.pid)
|
||||||
|
|
||||||
|
bias_list = version_list.get(tmp_rd['version'], None)
|
||||||
|
if not isinstance(bias_list, list) or len(bias_list) <= 4:
|
||||||
|
error = f"[-] WeChat Current Version Is Not Supported(maybe not get account,mobile,name,mail)"
|
||||||
|
if is_logging: print(error)
|
||||||
|
tmp_rd['account'] = "None"
|
||||||
|
tmp_rd['mobile'] = "None"
|
||||||
|
tmp_rd['name'] = "None"
|
||||||
|
tmp_rd['mail'] = "None"
|
||||||
|
return -2
|
||||||
|
else:
|
||||||
name_baseaddr = wechat_base_address + bias_list[0]
|
name_baseaddr = wechat_base_address + bias_list[0]
|
||||||
account__baseaddr = wechat_base_address + bias_list[1]
|
account__baseaddr = wechat_base_address + bias_list[1]
|
||||||
mobile_baseaddr = wechat_base_address + bias_list[2]
|
mobile_baseaddr = wechat_base_address + bias_list[2]
|
||||||
mail_baseaddr = wechat_base_address + bias_list[3]
|
mail_baseaddr = wechat_base_address + bias_list[3]
|
||||||
key_baseaddr = wechat_base_address + bias_list[4]
|
# key_baseaddr = wechat_base_address + bias_list[4]
|
||||||
|
|
||||||
addrLen = 4 if tmp_rd['version'] in ["3.9.2.23", "3.9.2.26"] else 8
|
|
||||||
|
|
||||||
tmp_rd['account'] = get_info_without_key(Handle, account__baseaddr, 32) if bias_list[1] != 0 else "None"
|
tmp_rd['account'] = get_info_without_key(Handle, account__baseaddr, 32) if bias_list[1] != 0 else "None"
|
||||||
tmp_rd['mobile'] = get_info_without_key(Handle, mobile_baseaddr, 64) if bias_list[2] != 0 else "None"
|
tmp_rd['mobile'] = get_info_without_key(Handle, mobile_baseaddr, 64) if bias_list[2] != 0 else "None"
|
||||||
tmp_rd['name'] = get_info_without_key(Handle, name_baseaddr, 64) if bias_list[0] != 0 else "None"
|
tmp_rd['name'] = get_info_without_key(Handle, name_baseaddr, 64) if bias_list[0] != 0 else "None"
|
||||||
tmp_rd['mail'] = get_info_without_key(Handle, mail_baseaddr, 64) if bias_list[3] != 0 else "None"
|
tmp_rd['mail'] = get_info_without_key(Handle, mail_baseaddr, 64) if bias_list[3] != 0 else "None"
|
||||||
|
|
||||||
|
addrLen = get_exe_bit(process.exe()) // 8
|
||||||
|
|
||||||
tmp_rd['wxid'] = get_info_wxid(Handle)
|
tmp_rd['wxid'] = get_info_wxid(Handle)
|
||||||
tmp_rd['key'] = get_key(Handle, key_baseaddr, addrLen) if bias_list[4] != 0 else "None"
|
tmp_rd['filePath'] = get_info_filePath(tmp_rd['wxid']) if tmp_rd['wxid'] != "None" else "None"
|
||||||
|
tmp_rd['key'] = get_key(tmp_rd['filePath'], addrLen) if tmp_rd['filePath'] != "None" else "None"
|
||||||
result.append(tmp_rd)
|
result.append(tmp_rd)
|
||||||
|
|
||||||
if is_logging:
|
if is_logging:
|
||||||
@ -154,8 +271,6 @@ def read_info(version_list, is_logging=False):
|
|||||||
print("=" * 32)
|
print("=" * 32)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@ -166,7 +281,7 @@ def resource_path(relative_path):
|
|||||||
return os.path.join(base_path, relative_path)
|
return os.path.join(base_path, relative_path)
|
||||||
|
|
||||||
|
|
||||||
@log
|
|
||||||
def get_info(VERSION_LIST):
|
def get_info(VERSION_LIST):
|
||||||
result = read_info(VERSION_LIST, True) # 读取微信信息
|
result = read_info(VERSION_LIST, True) # 读取微信信息
|
||||||
return result
|
return result
|
||||||
|
Loading…
Reference in New Issue
Block a user