From 9e3e4cb5aec2b9b445c8283d61c58863f4129c6e Mon Sep 17 00:00:00 2001 From: xaoyaoo Date: Sat, 20 Jul 2024 20:19:51 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=BF=ABwxinfo=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=EF=BC=8C=E6=8F=90=E9=80=9F30%=20=E6=84=9F=E8=B0=A2=E3=80=90?= =?UTF-8?q?=E9=B1=BC=E5=85=88=E7=94=9F=E5=95=8A=E3=80=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pywxdump/wx_info/ctypes_utils.py | 259 +++++++++++++++++++++++++++++++ pywxdump/wx_info/get_wx_info.py | 47 +++--- 2 files changed, 286 insertions(+), 20 deletions(-) create mode 100644 pywxdump/wx_info/ctypes_utils.py diff --git a/pywxdump/wx_info/ctypes_utils.py b/pywxdump/wx_info/ctypes_utils.py new file mode 100644 index 0000000..abaedf0 --- /dev/null +++ b/pywxdump/wx_info/ctypes_utils.py @@ -0,0 +1,259 @@ +import ctypes +import ctypes.wintypes +from collections import namedtuple + +# 定义必要的常量 +TH32CS_SNAPPROCESS = 0x00000002 +MAX_PATH = 260 +PROCESS_QUERY_INFORMATION = 0x0400 +PROCESS_VM_READ = 0x0010 + + +# 定义PROCESSENTRY32结构 +class PROCESSENTRY32(ctypes.Structure): + _fields_ = [("dwSize", ctypes.wintypes.DWORD), + ("cntUsage", ctypes.wintypes.DWORD), + ("th32ProcessID", ctypes.wintypes.DWORD), + ("th32DefaultHeapID", ctypes.POINTER(ctypes.wintypes.ULONG)), + ("th32ModuleID", ctypes.wintypes.DWORD), + ("cntThreads", ctypes.wintypes.DWORD), + ("th32ParentProcessID", ctypes.wintypes.DWORD), + ("pcPriClassBase", ctypes.wintypes.LONG), + ("dwFlags", ctypes.wintypes.DWORD), + ("szExeFile", ctypes.c_char * MAX_PATH)] + + +# 加载dll +kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) +psapi = ctypes.WinDLL('psapi', use_last_error=True) +version = ctypes.WinDLL('version', use_last_error=True) + +# 创建进程快照 +CreateToolhelp32Snapshot = kernel32.CreateToolhelp32Snapshot +CreateToolhelp32Snapshot.argtypes = [ctypes.wintypes.DWORD, ctypes.wintypes.DWORD] +CreateToolhelp32Snapshot.restype = ctypes.wintypes.HANDLE + +# 获取第一个进程 +Process32First = kernel32.Process32First +Process32First.argtypes = [ctypes.wintypes.HANDLE, ctypes.POINTER(PROCESSENTRY32)] +Process32First.restype = ctypes.wintypes.BOOL + +# 获取下一个进程 +Process32Next = kernel32.Process32Next +Process32Next.argtypes = [ctypes.wintypes.HANDLE, ctypes.POINTER(PROCESSENTRY32)] +Process32Next.restype = ctypes.wintypes.BOOL + +# 关闭句柄 +CloseHandle = kernel32.CloseHandle +CloseHandle.argtypes = [ctypes.wintypes.HANDLE] +CloseHandle.restype = ctypes.wintypes.BOOL + +# 打开进程 +OpenProcess = kernel32.OpenProcess +OpenProcess.argtypes = [ctypes.wintypes.DWORD, ctypes.wintypes.BOOL, ctypes.wintypes.DWORD] +OpenProcess.restype = ctypes.wintypes.HANDLE + +# 获取模块文件名 +GetModuleFileNameEx = psapi.GetModuleFileNameExA +GetModuleFileNameEx.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.HANDLE, ctypes.c_char_p, ctypes.wintypes.DWORD] +GetModuleFileNameEx.restype = ctypes.wintypes.DWORD + +# 获取文件版本信息大小 +GetFileVersionInfoSizeW = version.GetFileVersionInfoSizeW +GetFileVersionInfoSizeW.argtypes = [ctypes.wintypes.LPCWSTR, ctypes.POINTER(ctypes.wintypes.DWORD)] +GetFileVersionInfoSizeW.restype = ctypes.wintypes.DWORD + +# 获取文件版本信息 +GetFileVersionInfoW = version.GetFileVersionInfoW +GetFileVersionInfoW.argtypes = [ctypes.wintypes.LPCWSTR, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.c_void_p] +GetFileVersionInfoW.restype = ctypes.wintypes.BOOL + +# 查询文件版本信息 +VerQueryValueW = version.VerQueryValueW +VerQueryValueW.argtypes = [ctypes.c_void_p, ctypes.wintypes.LPCWSTR, ctypes.POINTER(ctypes.c_void_p), + ctypes.POINTER(ctypes.wintypes.UINT)] +VerQueryValueW.restype = ctypes.wintypes.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), + ctypes.c_size_t] +VirtualQueryEx.restype = ctypes.c_size_t + +# 获取映射文件名 +GetMappedFileName = psapi.GetMappedFileNameA +GetMappedFileName.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.LPVOID, ctypes.c_char_p, ctypes.wintypes.DWORD] +GetMappedFileName.restype = ctypes.wintypes.DWORD + +GetMappedFileNameW = psapi.GetMappedFileNameW +GetMappedFileNameW.restype = ctypes.wintypes.DWORD +GetMappedFileNameW.argtypes = [ctypes.wintypes.HANDLE, ctypes.c_void_p, ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD] + + +def get_info_with_key(h_process, address, address_len=8): + array = ctypes.create_string_buffer(address_len) + if ReadProcessMemory(h_process, ctypes.c_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, ctypes.c_void_p(address), key, 32, 0) == 0: return None + key_string = bytes(key).hex() + return key_string + + +def get_memory_maps(pid): + # 打开进程 + access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ + hProcess = OpenProcess(access, False, pid) + if not hProcess: + return [] + + memory_maps = [] + base_address = 0 + mbi = MEMORY_BASIC_INFORMATION() + max_address = 0x7FFFFFFFFFFFFFFF # 64位系统的最大地址 + + while base_address < max_address: + if VirtualQueryEx(hProcess, base_address, ctypes.byref(mbi), ctypes.sizeof(mbi)) == 0: + break + + mapped_file_name = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH) + if GetMappedFileNameW(hProcess, base_address, mapped_file_name, ctypes.wintypes.MAX_PATH) > 0: + file_name = mapped_file_name.value + else: + file_name = None + + memory_maps.append({ + 'BaseAddress': mbi.BaseAddress, + 'RegionSize': mbi.RegionSize, + 'State': mbi.State, + 'Protect': mbi.Protect, + 'Type': mbi.Type, + 'FileName': file_name + }) + + base_address += mbi.RegionSize + + CloseHandle(hProcess) + MemMap = namedtuple('MemMap', ['BaseAddress', 'RegionSize', 'State', 'Protect', 'Type', 'FileName']) + return [MemMap(**m) for m in memory_maps] + + +def get_process_exe_path(process_id): + h_process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, process_id) + if not h_process: + return None + exe_path = ctypes.create_string_buffer(MAX_PATH) + if GetModuleFileNameEx(h_process, None, exe_path, MAX_PATH) > 0: + CloseHandle(h_process) + return exe_path.value.decode('utf-8', errors='ignore') + else: + CloseHandle(h_process) + return None + + +def get_file_version_info(file_path): + size = GetFileVersionInfoSizeW(file_path, None) + if size == 0: + return None + res = ctypes.create_string_buffer(size) + if not GetFileVersionInfoW(file_path, 0, size, res): + return None + + uLen = ctypes.wintypes.UINT() + lplpBuffer = ctypes.c_void_p() + + if not VerQueryValueW(res, r'\\', ctypes.byref(lplpBuffer), ctypes.byref(uLen)): + return None + + ffi = ctypes.cast(lplpBuffer, ctypes.POINTER(VS_FIXEDFILEINFO)).contents + + if ffi.dwSignature != 0xFEEF04BD: + return None + + version = ( + (ffi.dwFileVersionMS >> 16) & 0xffff, + ffi.dwFileVersionMS & 0xffff, + (ffi.dwFileVersionLS >> 16) & 0xffff, + ffi.dwFileVersionLS & 0xffff, + ) + # f"{version[0]}.{version[1]}.{version[2]}.{version[3]}" + return f"{version[0]}.{version[1]}.{version[2]}.{version[3]}" + + +class VS_FIXEDFILEINFO(ctypes.Structure): + _fields_ = [ + ('dwSignature', ctypes.wintypes.DWORD), + ('dwStrucVersion', ctypes.wintypes.DWORD), + ('dwFileVersionMS', ctypes.wintypes.DWORD), + ('dwFileVersionLS', ctypes.wintypes.DWORD), + ('dwProductVersionMS', ctypes.wintypes.DWORD), + ('dwProductVersionLS', ctypes.wintypes.DWORD), + ('dwFileFlagsMask', ctypes.wintypes.DWORD), + ('dwFileFlags', ctypes.wintypes.DWORD), + ('dwFileOS', ctypes.wintypes.DWORD), + ('dwFileType', ctypes.wintypes.DWORD), + ('dwFileSubtype', ctypes.wintypes.DWORD), + ('dwFileDateMS', ctypes.wintypes.DWORD), + ('dwFileDateLS', ctypes.wintypes.DWORD), + ] + + +def get_process_list(): + h_process_snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) + if h_process_snap == ctypes.wintypes.HANDLE(-1).value: + print("Failed to create snapshot") + return [] + + pe32 = PROCESSENTRY32() + pe32.dwSize = ctypes.sizeof(PROCESSENTRY32) + process_list = [] + + if not Process32First(h_process_snap, ctypes.byref(pe32)): + print("Failed to get first process") + CloseHandle(h_process_snap) + return [] + + while True: + # process_path = get_process_exe_path(pe32.th32ProcessID) + process_list.append((pe32.th32ProcessID, pe32.szExeFile.decode('utf-8', errors='ignore'))) + if not Process32Next(h_process_snap, ctypes.byref(pe32)): + break + + CloseHandle(h_process_snap) + return process_list + + +bias_list = [] + +if __name__ == "__main__": + processes = get_process_list() + for pid, name in processes: + if name == "WeChat.exe": + # print(f"PID: {pid}, Process Name: {name}, Exe Path: {path}") + # Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, pid) + # wechat_base_address = 0 + memory_maps = get_memory_maps(pid) + for module in memory_maps: + if module.FileName and 'WeChatWin.dll' in module.FileName: + print(module.BaseAddress) + print(module.FileName) + break + # print(wechat_base_address) + # get_info_with_key(Handle, key_baseaddr, addrLen) diff --git a/pywxdump/wx_info/get_wx_info.py b/pywxdump/wx_info/get_wx_info.py index 7365316..2520059 100644 --- a/pywxdump/wx_info/get_wx_info.py +++ b/pywxdump/wx_info/get_wx_info.py @@ -200,38 +200,43 @@ def get_key(pid, db_path, addr_len): return "None" -def get_details(process, version_list: dict = None, is_logging: bool = False): - rd = {'pid': process.pid, 'version': get_exe_version(process.exe()), +from .ctypes_utils import get_process_list, get_info_with_key, get_memory_maps, get_process_exe_path, \ + get_file_version_info + + +def get_details(pid, version_list: dict = None, is_logging: bool = False): + path = get_process_exe_path(pid) + rd = {'pid': pid, 'version': get_file_version_info(path), "account": "None", "mobile": "None", "name": "None", "mail": "None", "wxid": "None", "key": "None", "filePath": "None"} try: - Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, process.pid) - + Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, pid) bias_list = version_list.get(rd['version'], None) - addrLen = get_exe_bit(process.exe()) // 8 + 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)" if is_logging: print(error) else: wechat_base_address = 0 - for module in process.memory_maps(grouped=False): - if module.path and 'WeChatWin.dll' in module.path: - wechat_base_address = int(module.addr, 16) - rd['version'] = get_exe_version(module.path) if os.path.exists(module.path) else rd['version'] + memory_maps = get_memory_maps(pid) + for module in memory_maps: + if module.FileName and 'WeChatWin.dll' in module.FileName: + wechat_base_address = module.BaseAddress + rd['version'] = get_file_version_info(module.FileName) if os.path.exists(module.FileName) else rd[ + 'version'] + bias_list = version_list.get(rd['version'], None) break if wechat_base_address == 0: error = f"[-] WeChat WeChatWin.dll Not Found" if is_logging: print(error) - # return error - 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] mail_baseaddr = wechat_base_address + bias_list[3] key_baseaddr = wechat_base_address + bias_list[4] - rd['account'] = get_info_string(Handle, account__baseaddr, 32) if bias_list[1] != 0 else "None" + rd['account'] = get_info_string(Handle, account_baseaddr, 32) if bias_list[1] != 0 else "None" 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" @@ -268,20 +273,22 @@ def read_info(version_list: dict = None, is_logging: bool = False, save_path: st if version_list is None: version_list = {} - wechat_process = [] + wechat_pids = [] result = [] error = "" - for process in psutil.process_iter(['name', 'exe', 'pid', 'cmdline']): - if process.name() == 'WeChat.exe': - wechat_process.append(process) - if len(wechat_process) <= 0: + processes = get_process_list() + for pid, name in processes: + if name == "WeChat.exe": + wechat_pids.append(pid) + + if len(wechat_pids) <= 0: error = "[-] WeChat No Run" if is_logging: print(error) return error - for process in wechat_process: - rd = get_details(process, version_list, is_logging) + for pid in wechat_pids: + rd = get_details(pid, version_list, is_logging) result.append(rd) if is_logging: