From d2b31f0120652beae84ba82d9c93e02a15eabf15 Mon Sep 17 00:00:00 2001 From: xaoyo Date: Mon, 9 Oct 2023 11:30:51 +0800 Subject: [PATCH] =?UTF-8?q?=E8=8E=B7=E5=8F=96key=E5=9F=BA=E5=9D=80?= =?UTF-8?q?=E5=81=8F=E7=A7=BB=E5=8F=AF=E4=BB=A5=E6=A0=B9=E6=8D=AE=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=96=87=E4=BB=B6=E5=A4=B9=E8=8E=B7=E5=8F=96=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E9=9C=80=E8=A6=81=E8=BE=93=E5=85=A5key?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Program/get_base_addr.py | 137 ++++++++++++++++++++++++++++++++------- README.md | 6 +- 2 files changed, 120 insertions(+), 23 deletions(-) diff --git a/Program/get_base_addr.py b/Program/get_base_addr.py index c9aa19c..f0a67e0 100644 --- a/Program/get_base_addr.py +++ b/Program/get_base_addr.py @@ -7,25 +7,47 @@ # ------------------------------------------------------------------------------- import argparse import ctypes +import hashlib import json +import multiprocessing +import os import re import time +import winreg +import threading import psutil import win32api from pymem import Pymem import pymem +import hmac ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory void_p = ctypes.c_void_p +KEY_SIZE = 32 +DEFAULT_PAGESIZE = 4096 +DEFAULT_ITER = 64000 + + +def validate_key(key, salt, first, mac_salt): + byteKey = hashlib.pbkdf2_hmac("sha1", key, salt, DEFAULT_ITER, KEY_SIZE) + 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 True + else: + return False class BaseAddr: - def __init__(self, account, mobile, name, key): + def __init__(self, account, mobile, name, key, db_path): self.account = account.encode("utf-8") self.mobile = mobile.encode("utf-8") self.name = name.encode("utf-8") - self.key = bytes.fromhex(key) + self.key = bytes.fromhex(key) if key else b"" + self.db_path = db_path if db_path else "" self.process_name = "WeChat.exe" self.module_name = "WeChatWin.dll" @@ -66,22 +88,20 @@ class BaseAddr: pid = self.pm.process_id # print(self.pm.process_base.lpBaseOfDll, self.pm.process_base.SizeOfImage) - batch = 4096 - module_start_addr = 34199871460642 module_end_addr = 0 - for process in psutil.process_iter(['name', 'exe', 'pid', 'cmdline']): - if process.name() == self.process_name: - for module in process.memory_maps(grouped=False): - if "WeChat" in module.path: - start_addr = int(module.addr, 16) - end_addr = start_addr + module.rss + process = psutil.Process(pid) + for module in process.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 + if module_start_addr > start_addr: + module_start_addr = start_addr + if module_end_addr < end_addr: + module_end_addr = end_addr + batch = 4096 Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, pid) array = ctypes.create_string_buffer(batch) key_addr = 0 @@ -93,19 +113,91 @@ class BaseAddr: if len(key_addr) > 0: key_addr = key_addr[0] break - # print(key_addr) + + # print(hex(key_addr)) key = key_addr.to_bytes(8, byteorder='little') + # print(key.hex()) result = self.search_memory_value(key, self.module_name) return result + def get_key_bias(self, wx_db_path): + wx_db_path = os.path.join(wx_db_path, "Msg", "MicroMsg.db") + if not os.path.exists(wx_db_path): + return False + + module_name = "WeChatWin.dll" + pm = self.pm + module = pymem.process.module_from_name(pm.process_handle, module_name) + start_addr = module.lpBaseOfDll + size = module.SizeOfImage + mem_data = pm.read_bytes(start_addr, size) + + min_addr = 0xffffffffffffffffffffffff + max_addr = 0 + for module1 in pm.list_modules(): + if module1.lpBaseOfDll < min_addr: + min_addr = module1.lpBaseOfDll + if module1.lpBaseOfDll > max_addr: + max_addr = module1.lpBaseOfDll + + def read_key(addr): + key = ctypes.create_string_buffer(35) + if ReadProcessMemory(pm.process_handle, void_p(addr - 1), key, 35, 0) == 0: + return b"" + + if b"\x00\x00" in key.raw[1:33]: + return b"" + + if b"\x00\x00" == key.raw[33:35] and b"\x90" == key.raw[0:1]: + return key.raw[1:33] + return b"" + + def get_maybe_key(mem_data): + maybe_key = [] + for i in range(0, len(mem_data), 8): + addr = mem_data[i:i + 8] + addr = int.from_bytes(addr, byteorder='little') + # 去掉不可能的地址 + if min_addr < addr < max_addr: + key = read_key(addr) + if key == b"": + continue + maybe_key.append([key, i]) + return maybe_key + + def verify_key(keys, wx_db_path): + + with open(wx_db_path, "rb") as file: + blist = file.read(5000) + salt = blist[:16] + first = blist[16:DEFAULT_PAGESIZE] + mac_salt = bytes([(salt[i] ^ 58) for i in range(16)]) + + with multiprocessing.Pool(processes=8) as pool: + results = [pool.apply_async(validate_key, args=(key, salt, first, mac_salt)) for key, i in keys[-1::-1]] + results = [p.get() for p in results] + for i, result in enumerate(results[-1::-1]): + if result: + return keys[i] + return b"", 0 + + maybe_key = get_maybe_key(mem_data) + key, bais = verify_key(maybe_key, wx_db_path) + return bais + def run(self): self.version = self.get_file_version(self.process_name) if not self.islogin: return "[-] WeChat No Run" - key_bias = self.search_key(self.key) mobile_bias = self.search_memory_value(self.mobile) name_bias = self.search_memory_value(self.name) account_bias = self.search_memory_value(self.account) + if self.key: + key_bias = self.search_key(self.key) + elif self.db_path: + key_bias = self.get_key_bias(self.db_path) + else: + key_bias = 0 return {self.version: [name_bias, account_bias, mobile_bias, 0, key_bias]} @@ -116,24 +208,25 @@ if __name__ == '__main__': parser.add_argument("--name", type=str, help="微信昵称") parser.add_argument("--account", type=str, help="微信账号") parser.add_argument("--key", type=str, help="密钥") + parser.add_argument("--db_path", 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("缺少必要的命令行参数!请提供手机号、微信昵称、微信账号和密钥。") - + if not args.mobile or not args.name or not args.account: + raise ValueError("缺少必要的命令行参数!请提供手机号、微信昵称、微信账号。") + if not args.key and not args.db_path: + raise ValueError("缺少必要的命令行参数!请提供密钥或加密数据库路径。") # 从命令行参数获取值 mobile = args.mobile name = args.name account = args.account key = args.key + db_path = args.db_path # 调用 run 函数,并传入参数 - # rdata = run(mobile, name, account, key) - # print(rdata) - rdata = BaseAddr(account, mobile, name, key).run() + rdata = BaseAddr(account, mobile, name, key, db_path).run() print(rdata) diff --git a/README.md b/README.md index a688154..30cd2d4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ #
PyWxDump
* 更新日志(如果有[version_list.json](./Program/version_list.json)缺少的版本,请帮忙添加。) + * 2023.10.09 获取key基址偏移可以根据微信文件夹获取,不需要输入key * 2023.10.09 优化代码,删减没必要代码,重新修改获取基址代码,加快运行速度(需要安装新的库 pymem) * 2023.10.07 修改获取基址内存搜索方式,防止进入死循环 * 2023.10.07 增加了3.9.7.29版本的偏移地址 @@ -113,7 +114,10 @@ print(data) **3.1 通过python脚本获取** ```shell -python get_base_addr.py --mobile 152******** --name ****** --account ****** --key ********************************************** +python get_base_addr.py --mobile 152***** --name **** --account *** --key ********** --db_path "****\WeChat Files\wxid_******" +``` + +# mobile、name、account为必填参数, key、db_path二者必填其一,使用key时,执行速度快,db_path时,执行速度慢(大约需要10s-60s,也可能更长) # return:{'3.9.7.29': [63486984, 63488320, 63486792, 0, 63488256]} # (十进制)按顺序代表:微信昵称、微信账号、微信手机号、微信邮箱(默认0)、微信KEY