2023-08-25 11:07:57 +08:00
|
|
|
|
# -*- coding: utf-8 -*-#
|
|
|
|
|
# -------------------------------------------------------------------------------
|
|
|
|
|
# Name: get_base_addr.py
|
|
|
|
|
# Description:
|
|
|
|
|
# Author: xaoyaoo
|
|
|
|
|
# Date: 2023/08/22
|
|
|
|
|
# -------------------------------------------------------------------------------
|
|
|
|
|
import ctypes
|
|
|
|
|
import json
|
2023-10-09 11:30:51 +08:00
|
|
|
|
import os
|
2023-08-25 11:07:57 +08:00
|
|
|
|
import re
|
2023-11-30 13:13:35 +08:00
|
|
|
|
import sys
|
2024-09-11 12:14:55 +08:00
|
|
|
|
from ctypes import wintypes
|
|
|
|
|
|
2023-08-25 11:07:57 +08:00
|
|
|
|
import psutil
|
2023-10-09 01:34:30 +08:00
|
|
|
|
import pymem
|
2023-12-25 16:12:39 +08:00
|
|
|
|
|
|
|
|
|
from .utils import get_exe_version, get_exe_bit, verify_key
|
2024-09-11 12:14:55 +08:00
|
|
|
|
from .utils import get_process_list, get_memory_maps, get_process_exe_path, get_file_version_info
|
|
|
|
|
from .utils import search_memory
|
2023-10-09 01:34:30 +08:00
|
|
|
|
|
2024-08-03 00:21:16 +08:00
|
|
|
|
ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory if sys.platform == "win32" else None
|
2023-10-09 01:34:30 +08:00
|
|
|
|
void_p = ctypes.c_void_p
|
2023-11-30 13:13:35 +08:00
|
|
|
|
|
2024-09-11 12:14:55 +08:00
|
|
|
|
# 定义常量
|
|
|
|
|
PROCESS_QUERY_INFORMATION = 0x0400
|
|
|
|
|
PROCESS_VM_READ = 0x0010
|
|
|
|
|
|
|
|
|
|
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
|
|
|
|
|
OpenProcess = kernel32.OpenProcess
|
|
|
|
|
OpenProcess.restype = wintypes.HANDLE
|
|
|
|
|
OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD]
|
|
|
|
|
|
|
|
|
|
CloseHandle = kernel32.CloseHandle
|
|
|
|
|
CloseHandle.restype = wintypes.BOOL
|
|
|
|
|
CloseHandle.argtypes = [wintypes.HANDLE]
|
|
|
|
|
|
2023-11-30 13:13:35 +08:00
|
|
|
|
|
2023-10-14 21:48:35 +08:00
|
|
|
|
class BiasAddr:
|
2023-10-09 11:30:51 +08:00
|
|
|
|
def __init__(self, account, mobile, name, key, db_path):
|
2023-10-09 01:34:30 +08:00
|
|
|
|
self.account = account.encode("utf-8")
|
|
|
|
|
self.mobile = mobile.encode("utf-8")
|
|
|
|
|
self.name = name.encode("utf-8")
|
2023-10-09 11:30:51 +08:00
|
|
|
|
self.key = bytes.fromhex(key) if key else b""
|
2023-12-06 13:21:16 +08:00
|
|
|
|
self.db_path = db_path if db_path and os.path.exists(db_path) else ""
|
2023-08-25 11:07:57 +08:00
|
|
|
|
|
2023-10-09 01:34:30 +08:00
|
|
|
|
self.process_name = "WeChat.exe"
|
|
|
|
|
self.module_name = "WeChatWin.dll"
|
2023-08-25 11:07:57 +08:00
|
|
|
|
|
2023-11-30 13:13:35 +08:00
|
|
|
|
self.pm = None # Pymem 对象
|
|
|
|
|
self.is_WoW64 = None # True: 32位进程运行在64位系统上 False: 64位进程运行在64位系统上
|
|
|
|
|
self.process_handle = None # 进程句柄
|
|
|
|
|
self.pid = None # 进程ID
|
|
|
|
|
self.version = None # 微信版本号
|
|
|
|
|
self.process = None # 进程对象
|
|
|
|
|
self.exe_path = None # 微信路径
|
|
|
|
|
self.address_len = None # 4 if self.bits == 32 else 8 # 4字节或8字节
|
|
|
|
|
self.bits = 64 if sys.maxsize > 2 ** 32 else 32 # 系统:32位或64位
|
|
|
|
|
|
|
|
|
|
def get_process_handle(self):
|
|
|
|
|
try:
|
2023-12-25 16:12:39 +08:00
|
|
|
|
self.pm = pymem.Pymem(self.process_name)
|
2023-11-30 13:13:35 +08:00
|
|
|
|
self.pm.check_wow64()
|
|
|
|
|
self.is_WoW64 = self.pm.is_WoW64
|
|
|
|
|
self.process_handle = self.pm.process_handle
|
|
|
|
|
self.pid = self.pm.process_id
|
|
|
|
|
self.process = psutil.Process(self.pid)
|
|
|
|
|
self.exe_path = self.process.exe()
|
|
|
|
|
self.version = get_exe_version(self.exe_path)
|
|
|
|
|
|
|
|
|
|
version_nums = list(map(int, self.version.split("."))) # 将版本号拆分为数字列表
|
|
|
|
|
if version_nums[0] <= 3 and version_nums[1] <= 9 and version_nums[2] <= 2:
|
|
|
|
|
self.address_len = 4
|
|
|
|
|
else:
|
|
|
|
|
self.address_len = 8
|
|
|
|
|
return True, ""
|
|
|
|
|
except pymem.exception.ProcessNotFound:
|
|
|
|
|
return False, "[-] WeChat No Run"
|
2023-10-24 16:58:16 +08:00
|
|
|
|
|
2023-10-09 01:34:30 +08:00
|
|
|
|
def search_memory_value(self, value: bytes, module_name="WeChatWin.dll"):
|
2024-09-11 12:14:55 +08:00
|
|
|
|
start_adress = 0x7FFFFFFFFFFFFFFF
|
|
|
|
|
end_adress = 0
|
|
|
|
|
|
|
|
|
|
memory_maps = get_memory_maps(self.pid)
|
|
|
|
|
for module in memory_maps:
|
|
|
|
|
if module.FileName and module_name in module.FileName:
|
|
|
|
|
s = module.BaseAddress
|
|
|
|
|
e = module.BaseAddress + module.RegionSize
|
|
|
|
|
start_adress = s if s < start_adress else start_adress
|
|
|
|
|
end_adress = e if e > end_adress else end_adress
|
|
|
|
|
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, self.pid)
|
|
|
|
|
ret = search_memory(hProcess, value, max_num=3, start_address=start_adress,
|
|
|
|
|
end_address=end_adress)
|
|
|
|
|
ret = ret[-1] - start_adress if len(ret) > 0 else 0
|
|
|
|
|
|
|
|
|
|
# # 创建 Pymem 对象
|
|
|
|
|
# module = pymem.process.module_from_name(self.pm.process_handle, module_name)
|
|
|
|
|
# ret = self.pm.pattern_scan_module(value, module, return_multiple=True)
|
|
|
|
|
# ret = ret[-1] - module.lpBaseOfDll if len(ret) > 0 else 0
|
2023-11-30 13:13:35 +08:00
|
|
|
|
return ret
|
2023-10-24 16:58:16 +08:00
|
|
|
|
|
2023-11-30 13:13:35 +08:00
|
|
|
|
def get_key_bias1(self):
|
2024-01-26 17:30:34 +08:00
|
|
|
|
"""
|
|
|
|
|
2024.01.26 wx version:3.9.9.35 失效
|
|
|
|
|
:return:
|
|
|
|
|
"""
|
2023-11-30 13:13:35 +08:00
|
|
|
|
try:
|
|
|
|
|
byteLen = self.address_len # 4 if self.bits == 32 else 8 # 4字节或8字节
|
|
|
|
|
|
|
|
|
|
keyLenOffset = 0x8c if self.bits == 32 else 0xd0
|
|
|
|
|
keyWindllOffset = 0x90 if self.bits == 32 else 0xd8
|
|
|
|
|
|
|
|
|
|
module = pymem.process.module_from_name(self.process_handle, self.module_name)
|
|
|
|
|
keyBytes = b'-----BEGIN PUBLIC KEY-----\n...'
|
|
|
|
|
publicKeyList = pymem.pattern.pattern_scan_all(self.process_handle, keyBytes, return_multiple=True)
|
|
|
|
|
keyaddrs = []
|
|
|
|
|
for addr in publicKeyList:
|
|
|
|
|
keyBytes = addr.to_bytes(byteLen, byteorder="little", signed=True) # 低位在前
|
|
|
|
|
may_addrs = pymem.pattern.pattern_scan_module(self.process_handle, module, keyBytes,
|
|
|
|
|
return_multiple=True)
|
|
|
|
|
if may_addrs != 0 and len(may_addrs) > 0:
|
|
|
|
|
for addr in may_addrs:
|
|
|
|
|
keyLen = self.pm.read_uchar(addr - keyLenOffset)
|
|
|
|
|
if keyLen != 32:
|
|
|
|
|
continue
|
|
|
|
|
keyaddrs.append(addr - keyWindllOffset)
|
|
|
|
|
|
|
|
|
|
return keyaddrs[-1] - module.lpBaseOfDll if len(keyaddrs) > 0 else 0
|
|
|
|
|
except:
|
|
|
|
|
return 0
|
2023-10-09 01:34:30 +08:00
|
|
|
|
|
|
|
|
|
def search_key(self, key: bytes):
|
2023-10-31 18:04:04 +08:00
|
|
|
|
key = re.escape(key) # 转义特殊字符
|
2023-11-30 13:13:35 +08:00
|
|
|
|
key_addr = self.pm.pattern_scan_all(key, return_multiple=False)
|
|
|
|
|
key = key_addr.to_bytes(self.address_len, byteorder='little', signed=True)
|
2023-10-09 01:34:30 +08:00
|
|
|
|
result = self.search_memory_value(key, self.module_name)
|
|
|
|
|
return result
|
2023-08-25 11:07:57 +08:00
|
|
|
|
|
2023-12-06 17:52:46 +08:00
|
|
|
|
def get_key_bias2(self, wx_db_path):
|
|
|
|
|
|
|
|
|
|
addr_len = get_exe_bit(self.exe_path) // 8
|
|
|
|
|
db_path = wx_db_path
|
|
|
|
|
|
|
|
|
|
def read_key_bytes(h_process, address, address_len=8):
|
|
|
|
|
array = ctypes.create_string_buffer(address_len)
|
|
|
|
|
if ReadProcessMemory(h_process, void_p(address), array, address_len, 0) == 0: return "None"
|
|
|
|
|
address = int.from_bytes(array, byteorder='little') # 逆序转换为int地址(key地址)
|
|
|
|
|
key = ctypes.create_string_buffer(32)
|
|
|
|
|
if ReadProcessMemory(h_process, void_p(address), key, 32, 0) == 0: return "None"
|
|
|
|
|
key_bytes = bytes(key)
|
|
|
|
|
return key_bytes
|
|
|
|
|
|
2023-12-05 11:17:52 +08:00
|
|
|
|
phone_type1 = "iphone\x00"
|
|
|
|
|
phone_type2 = "android\x00"
|
2023-12-06 17:52:46 +08:00
|
|
|
|
phone_type3 = "ipad\x00"
|
|
|
|
|
|
2024-01-26 17:30:34 +08:00
|
|
|
|
pm = pymem.Pymem(self.pid)
|
2023-12-06 17:52:46 +08:00
|
|
|
|
module_name = "WeChatWin.dll"
|
|
|
|
|
|
|
|
|
|
MicroMsg_path = os.path.join(db_path, "MSG", "MicroMsg.db")
|
|
|
|
|
|
2024-01-26 17:30:34 +08:00
|
|
|
|
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 = []
|
|
|
|
|
if len(type1_addrs) >= 2: type_addrs += type1_addrs
|
|
|
|
|
if len(type2_addrs) >= 2: type_addrs += type2_addrs
|
|
|
|
|
if len(type3_addrs) >= 2: type_addrs += type3_addrs
|
|
|
|
|
if len(type_addrs) == 0: return "None"
|
|
|
|
|
|
|
|
|
|
type_addrs.sort() # 从小到大排序
|
|
|
|
|
|
2023-12-06 17:52:46 +08:00
|
|
|
|
module = pymem.process.module_from_name(pm.process_handle, module_name)
|
|
|
|
|
|
|
|
|
|
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 j - module.lpBaseOfDll
|
|
|
|
|
return 0
|
2023-12-05 11:17:52 +08:00
|
|
|
|
|
2024-08-03 00:21:16 +08:00
|
|
|
|
def run(self, logging_path=False, WX_OFFS_PATH=None):
|
2023-11-30 13:13:35 +08:00
|
|
|
|
if not self.get_process_handle()[0]:
|
|
|
|
|
return None
|
|
|
|
|
mobile_bias = self.search_memory_value(self.mobile, self.module_name)
|
|
|
|
|
name_bias = self.search_memory_value(self.name, self.module_name)
|
|
|
|
|
account_bias = self.search_memory_value(self.account, self.module_name)
|
|
|
|
|
key_bias = 0
|
2024-04-19 17:23:12 +08:00
|
|
|
|
key_bias = self.get_key_bias1() if key_bias <= 0 else key_bias
|
2023-11-30 13:13:35 +08:00
|
|
|
|
key_bias = self.search_key(self.key) if key_bias <= 0 and self.key else key_bias
|
2023-12-06 17:52:46 +08:00
|
|
|
|
key_bias = self.get_key_bias2(self.db_path) if key_bias <= 0 and self.db_path else key_bias
|
2023-10-31 18:04:04 +08:00
|
|
|
|
|
2023-11-15 15:04:45 +08:00
|
|
|
|
rdata = {self.version: [name_bias, account_bias, mobile_bias, 0, key_bias]}
|
2023-12-06 17:52:46 +08:00
|
|
|
|
|
2024-08-03 00:21:16 +08:00
|
|
|
|
if WX_OFFS_PATH and os.path.exists(WX_OFFS_PATH):
|
|
|
|
|
with open(WX_OFFS_PATH, "r", encoding="utf-8") as f:
|
2023-11-15 15:04:45 +08:00
|
|
|
|
data = json.load(f)
|
|
|
|
|
data.update(rdata)
|
2024-08-03 00:21:16 +08:00
|
|
|
|
with open(WX_OFFS_PATH, "w", encoding="utf-8") as f:
|
2023-11-15 15:04:45 +08:00
|
|
|
|
json.dump(data, f, ensure_ascii=False, indent=4)
|
2023-11-30 13:13:35 +08:00
|
|
|
|
if os.path.exists(logging_path) and isinstance(logging_path, str):
|
|
|
|
|
with open(logging_path, "a", encoding="utf-8") as f:
|
|
|
|
|
f.write("{版本号:昵称,账号,手机号,邮箱,KEY}" + "\n")
|
|
|
|
|
f.write(str(rdata) + "\n")
|
|
|
|
|
elif logging_path:
|
2023-11-15 15:04:45 +08:00
|
|
|
|
print("{版本号:昵称,账号,手机号,邮箱,KEY}")
|
|
|
|
|
print(rdata)
|
2023-12-05 11:17:52 +08:00
|
|
|
|
return rdata
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2023-12-25 16:12:39 +08:00
|
|
|
|
account, mobile, name, key, db_path = "test", "test", "test", None, r"test"
|
2023-12-05 11:17:52 +08:00
|
|
|
|
bias_addr = BiasAddr(account, mobile, name, key, db_path)
|
2023-12-06 17:52:46 +08:00
|
|
|
|
bias_addr.run(logging_path=True)
|