diff --git a/pywxdump/wx_info/ctypes_utils.py b/pywxdump/wx_info/ctypes_utils.py index abaedf0..f2371e5 100644 --- a/pywxdump/wx_info/ctypes_utils.py +++ b/pywxdump/wx_info/ctypes_utils.py @@ -9,6 +9,27 @@ PROCESS_QUERY_INFORMATION = 0x0400 PROCESS_VM_READ = 0x0010 +# MEMORY_BASIC_INFORMATION 结构体定义 +class MEMORY_BASIC_INFORMATION(ctypes.Structure): + _fields_ = [ + ('BaseAddress', ctypes.wintypes.LPVOID), + ('AllocationBase', ctypes.wintypes.LPVOID), + ('AllocationProtect', ctypes.wintypes.DWORD), + ('RegionSize', ctypes.c_size_t), + ('State', ctypes.wintypes.DWORD), + ('Protect', ctypes.wintypes.DWORD), + ('Type', ctypes.wintypes.DWORD) + ] + + +class MODULEINFO(ctypes.Structure): + _fields_ = [ + ("lpBaseOfDll", ctypes.c_void_p), # remote pointer + ("SizeOfImage", ctypes.c_ulong), + ("EntryPoint", ctypes.c_void_p), # remote pointer + ] + + # 定义PROCESSENTRY32结构 class PROCESSENTRY32(ctypes.Structure): _fields_ = [("dwSize", ctypes.wintypes.DWORD), @@ -74,23 +95,15 @@ VerQueryValueW.argtypes = [ctypes.c_void_p, ctypes.wintypes.LPCWSTR, ctypes.POIN ctypes.POINTER(ctypes.wintypes.UINT)] VerQueryValueW.restype = ctypes.wintypes.BOOL +# 获取模块信息 +GetModuleInformation = psapi.GetModuleInformation +GetModuleInformation.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.HMODULE, ctypes.POINTER(MODULEINFO), + ctypes.wintypes.DWORD] +GetModuleInformation.restype = ctypes.c_bool + # 读取进程内存 ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory - -# MEMORY_BASIC_INFORMATION 结构体定义 -class MEMORY_BASIC_INFORMATION(ctypes.Structure): - _fields_ = [ - ('BaseAddress', ctypes.wintypes.LPVOID), - ('AllocationBase', ctypes.wintypes.LPVOID), - ('AllocationProtect', ctypes.wintypes.DWORD), - ('RegionSize', ctypes.c_size_t), - ('State', ctypes.wintypes.DWORD), - ('Protect', ctypes.wintypes.DWORD), - ('Type', ctypes.wintypes.DWORD) - ] - - # 定义VirtualQueryEx函数 VirtualQueryEx = kernel32.VirtualQueryEx VirtualQueryEx.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.LPCVOID, ctypes.POINTER(MEMORY_BASIC_INFORMATION), @@ -139,6 +152,10 @@ def get_memory_maps(pid): else: file_name = None + # module_info = MODULEINFO() + # if GetModuleInformation(hProcess, mbi.BaseAddress, ctypes.byref(module_info), ctypes.sizeof(module_info)): + # file_name = get_file_version_info(module_info.lpBaseOfDll) + memory_maps.append({ 'BaseAddress': mbi.BaseAddress, 'RegionSize': mbi.RegionSize, diff --git a/pywxdump/wx_info/get_wx_info.py b/pywxdump/wx_info/get_wx_info.py index 2520059..c94ebcc 100644 --- a/pywxdump/wx_info/get_wx_info.py +++ b/pywxdump/wx_info/get_wx_info.py @@ -14,8 +14,23 @@ import psutil import pymem from typing import List, Union from .utils import pattern_scan_all, verify_key, get_exe_version, get_exe_bit, info_error +from .memory_search import search_memory +import ctypes.wintypes as wintypes -ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory +# 定义常量 +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] + +ReadProcessMemory = kernel32.ReadProcessMemory void_p = ctypes.c_void_p @@ -59,7 +74,7 @@ def get_info_name(h_process, address, address_len=8, n_size=64): @info_error def get_info_wxid(h_process): find_num = 100 - addrs = pattern_scan_all(h_process, br'\\Msg\\FTSContact', return_multiple=True, find_num=find_num) + addrs = search_memory(h_process, br'\\Msg\\FTSContact', max_num=find_num) wxids = [] for addr in addrs: array = ctypes.create_string_buffer(80) @@ -69,6 +84,7 @@ def get_info_wxid(h_process): array = array.split(b"\\")[-1] wxids.append(array.decode('utf-8', errors='ignore')) wxid = max(wxids, key=wxids.count) if wxids else "None" + CloseHandle(h_process) return wxid @@ -76,7 +92,7 @@ def get_info_wxid(h_process): @info_error def get_info_filePath_base_wxid(h_process, wxid=""): find_num = 10 - addrs = pattern_scan_all(h_process, wxid.encode() + br'\\Msg\\FTSContact', return_multiple=True, find_num=find_num) + addrs = search_memory(h_process, wxid.encode() + br'\\Msg\\FTSContact', max_num=find_num) filePath = [] for addr in addrs: win_addr_len = 260 @@ -86,6 +102,7 @@ def get_info_filePath_base_wxid(h_process, wxid=""): array = array.split(b"\00")[-1] filePath.append(array.decode('utf-8', errors='ignore')) filePath = max(filePath, key=filePath.count) if filePath else "None" + CloseHandle(h_process) return filePath @@ -157,7 +174,6 @@ def get_key(pid, db_path, addr_len): :param addr_len: 地址长度 :return: 返回key """ - 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" @@ -171,11 +187,27 @@ def get_key(pid, db_path, addr_len): phone_type2 = "android\x00" phone_type3 = "ipad\x00" - pm = pymem.Pymem(pid) - module_name = "WeChatWin.dll" - MicroMsg_path = os.path.join(db_path, "MSG", "MicroMsg.db") + # start_adress = 0 + # end_adress = 0x7FFFFFFFFFFFFFFF + # + # memory_maps = get_memory_maps(pid) + # for module in memory_maps: + # if module.FileName and 'WeChatWin.dll' in module.FileName: + # start_adress = module.BaseAddress + # end_adress = module.BaseAddress + module.RegionSize + # # print(start_adress, end_adress) + # hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid) + # type1_addrs = search_memory(hProcess, phone_type1.encode(), start_adress, end_adress) + # type2_addrs = search_memory(hProcess, phone_type2.encode(), start_adress, end_adress) + # type3_addrs = search_memory(hProcess, phone_type3.encode(), start_adress, end_adress) + # + # print(type1_addrs, type2_addrs, type3_addrs) + + + pm = pymem.Pymem(pid) + module_name = "WeChatWin.dll" 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) @@ -210,9 +242,10 @@ def get_details(pid, version_list: dict = None, is_logging: bool = False): "account": "None", "mobile": "None", "name": "None", "mail": "None", "wxid": "None", "key": "None", "filePath": "None"} try: - Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, pid) bias_list = version_list.get(rd['version'], None) + Handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid) + addrLen = get_exe_bit(path) // 8 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)" @@ -240,19 +273,22 @@ def get_details(pid, version_list: dict = None, is_logging: bool = False): rd['mobile'] = get_info_string(Handle, mobile_baseaddr, 64) if bias_list[2] != 0 else "None" rd['name'] = get_info_name(Handle, name_baseaddr, addrLen, 64) if bias_list[0] != 0 else "None" rd['mail'] = get_info_string(Handle, mail_baseaddr, 64) if bias_list[3] != 0 else "None" - rd['key'] = get_info_with_key(Handle, key_baseaddr, addrLen) if bias_list[4] != 0 else "None" + # rd['key'] = get_info_with_key(Handle, key_baseaddr, addrLen) if bias_list[4] != 0 else "None" rd['wxid'] = get_info_wxid(Handle) rd['filePath'] = get_info_filePath(rd['wxid']) if rd['wxid'] != "None" else "None" if rd['wxid'] != "None" and rd['filePath'] == "None": # 通过wxid获取filePath,如果filePath为空则通过wxid获取filePath - rd['filePath'] = get_info_filePath_base_wxid(Handle, rd['wxid']) + rd['filePath'] = get_info_filePath_base_wxid(Handle, wxid=rd['wxid']) - isKey = verify_key(bytes.fromhex(rd["key"]), - os.path.join(rd['filePath'], "MSG", "MicroMsg.db")) if rd['key'] != "None" and rd[ + isKey = verify_key( + bytes.fromhex(rd["key"]), + os.path.join(rd['filePath'], "MSG", "MicroMsg.db")) if rd['key'] != "None" and rd[ 'filePath'] != "None" else False + if rd['filePath'] != "None" and rd['key'] == "None" and not isKey: rd['key'] = get_key(rd['pid'], rd['filePath'], addrLen) + CloseHandle(Handle) except Exception as e: error = f"[-] WeChat Get Info Error:{e}" if is_logging: print(error) diff --git a/pywxdump/wx_info/memory_search.py b/pywxdump/wx_info/memory_search.py new file mode 100644 index 0000000..4d65736 --- /dev/null +++ b/pywxdump/wx_info/memory_search.py @@ -0,0 +1,118 @@ +import ctypes +import ctypes.wintypes as wintypes +import logging +from ctypes.wintypes import HANDLE +import re +import sys + +# 定义常量 +PROCESS_QUERY_INFORMATION = 0x0400 +PROCESS_VM_READ = 0x0010 + +PAGE_EXECUTE = 0x10 +PAGE_EXECUTE_READ = 0x20 +PAGE_EXECUTE_READWRITE = 0x40 +PAGE_EXECUTE_WRITECOPY = 0x80 +PAGE_NOACCESS = 0x01 +PAGE_READONLY = 0x02 +PAGE_READWRITE = 0x04 +PAGE_WRITECOPY = 0x08 +PAGE_GUARD = 0x100 +PAGE_NOCACHE = 0x200 +PAGE_WRITECOMBINE = 0x400 + +MEM_COMMIT = 0x1000 +MEM_FREE = 0x10000 +MEM_RESERVE = 0x2000 +MEM_DECOMMIT = 0x4000 +MEM_RELEASE = 0x8000 + + +# 定义结构体 +class MEMORY_BASIC_INFORMATION(ctypes.Structure): + _fields_ = [ + ("BaseAddress", ctypes.c_void_p), + ("AllocationBase", ctypes.c_void_p), + ("AllocationProtect", wintypes.DWORD), + ("RegionSize", ctypes.c_size_t), + ("State", wintypes.DWORD), + ("Protect", wintypes.DWORD), + ("Type", wintypes.DWORD), + ] + + +# 加载Windows API函数 +kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) + +OpenProcess = kernel32.OpenProcess +OpenProcess.restype = wintypes.HANDLE +OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD] + +ReadProcessMemory = kernel32.ReadProcessMemory + +VirtualQueryEx = kernel32.VirtualQueryEx +VirtualQueryEx.restype = ctypes.c_size_t +VirtualQueryEx.argtypes = [wintypes.HANDLE, ctypes.c_void_p, ctypes.POINTER(MEMORY_BASIC_INFORMATION), ctypes.c_size_t] + +CloseHandle = kernel32.CloseHandle +CloseHandle.restype = wintypes.BOOL +CloseHandle.argtypes = [wintypes.HANDLE] + + +def search_memory(hProcess, pattern=br'\\Msg\\FTSContact', max_num=100,start_address=0x0,end_address=0x7FFFFFFFFFFFFFFF): + """ + 在进程内存中搜索字符串 + :param p: 进程ID或者进程句柄 + :param pattern: 要搜索的字符串 + :param max_num: 最多找到的数量 + """ + result = [] + # 打开进程 + if not hProcess: + raise ctypes.WinError(ctypes.get_last_error()) + + mbi = MEMORY_BASIC_INFORMATION() + + address = start_address + max_address = end_address if sys.maxsize > 2 ** 32 else 0x7fff0000 + pattern = re.compile(pattern) + + while address < max_address: + if VirtualQueryEx(hProcess, address, ctypes.byref(mbi), ctypes.sizeof(mbi)) == 0: + break + # 读取内存数据 + allowed_protections = [PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_READWRITE, PAGE_READONLY, ] + if mbi.State != MEM_COMMIT or mbi.Protect not in allowed_protections: + address += mbi.RegionSize + continue + + # 使用正确的类型来避免OverflowError + base_address_c = ctypes.c_ulonglong(mbi.BaseAddress) + region_size_c = ctypes.c_size_t(mbi.RegionSize) + + page_bytes = ctypes.create_string_buffer(mbi.RegionSize) + bytes_read = ctypes.c_size_t() + + if ReadProcessMemory(hProcess, base_address_c, page_bytes, region_size_c, ctypes.byref(bytes_read)) == 0: + address += mbi.RegionSize + continue + # 搜索字符串 re print(page_bytes.raw) + find = [address + match.start() for match in pattern.finditer(page_bytes, re.DOTALL)] + if find: + result.extend(find) + if len(result) >= max_num: + break + address += mbi.RegionSize + return result + + +if __name__ == '__main__': + # 示例用法 + pid = 29320 # 将此替换为你要查询的进程ID + try: + maps = search_memory(pid) + print(len(maps)) + for m in maps: + print(hex(m)) + except Exception as e: + logging.error(e, exc_info=True)