PyWxDump/Program/get_base_addr.py
2023-10-09 12:04:44 +08:00

248 lines
9.0 KiB
Python

# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name: get_base_addr.py
# Description:
# Author: xaoyaoo
# Date: 2023/08/22
# -------------------------------------------------------------------------------
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, 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) if key else b""
self.db_path = db_path if db_path else ""
self.process_name = "WeChat.exe"
self.module_name = "WeChatWin.dll"
self.pm = Pymem("WeChat.exe")
self.islogin = True
def find_all(self, c: bytes, string: bytes, base_addr=0):
"""
查找字符串中所有子串的位置
:param c: 子串 b'123'
:param string: 字符串 b'123456789123'
:return:
"""
return [base_addr + m.start() for m in re.finditer(re.escape(c), string)]
def get_file_version(self, process_name):
for process in psutil.process_iter(['name', 'exe', 'pid', 'cmdline']):
if process.name() == process_name:
file_path = process.exe()
info = win32api.GetFileVersionInfo(file_path, "\\")
ms, ls = info['FileVersionMS'], info['FileVersionLS']
file_version = f"{win32api.HIWORD(ms)}.{win32api.LOWORD(ms)}.{win32api.HIWORD(ls)}.{win32api.LOWORD(ls)}"
return file_version
self.islogin = False
def search_memory_value(self, value: bytes, module_name="WeChatWin.dll"):
# 创建 Pymem 对象
pm = self.pm
module = pymem.process.module_from_name(pm.process_handle, module_name)
mem_data = pm.read_bytes(module.lpBaseOfDll, module.SizeOfImage)
result = self.find_all(value, mem_data)
result = result[-1] if len(result) > 0 else 0
return result
def search_key(self, key: bytes):
pid = self.pm.process_id
# print(self.pm.process_base.lpBaseOfDll, self.pm.process_base.SizeOfImage)
module_start_addr = 34199871460642
module_end_addr = 0
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
batch = 4096
Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, pid)
array = ctypes.create_string_buffer(batch)
key_addr = 0
for i in range(module_start_addr, module_end_addr, batch):
if ReadProcessMemory(Handle, void_p(i), array, batch, None) == 0:
continue
hex_string = array.raw # 读取到的内存数据
key_addr = self.find_all(key, hex_string, i)
if len(key_addr) > 0:
key_addr = key_addr[0]
break
# 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, account_bias=0):
wx_db_path = os.path.join(wx_db_path, "Msg", "MicroMsg.db")
if not os.path.exists(wx_db_path):
return False
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
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
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
if account_bias > 1:
maybe_key = []
for i in [0x24, 0x40]:
addr = start_addr + account_bias - i
mem_data = pm.read_bytes(addr, 8)
key = read_key(int.from_bytes(mem_data, byteorder='little'))
if key != b"":
maybe_key.append([key, addr - start_addr])
else:
mem_data = pm.read_bytes(start_addr, size)
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"
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, account_bias)
else:
key_bias = 0
return {self.version: [name_bias, account_bias, mobile_bias, 0, key_bias]}
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="密钥")
parser.add_argument("--db_path", type=str, help="微信文件夹(已经登录微信)路径")
# 解析命令行参数
args = parser.parse_args()
# 检查是否缺少必要参数,并抛出错误
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
st = time.time()
# 调用 run 函数,并传入参数
rdata = BaseAddr(account, mobile, name, key, db_path).run()
print(f"耗时:{time.time() - st}")
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)