Skip to content

Commit

Permalink
加快wxinfo获取,提速30% 感谢【鱼先生啊】
Browse files Browse the repository at this point in the history
  • Loading branch information
xaoyaoo committed Jul 20, 2024
1 parent 424d6bd commit 9e3e4cb
Show file tree
Hide file tree
Showing 2 changed files with 286 additions and 20 deletions.
259 changes: 259 additions & 0 deletions pywxdump/wx_info/ctypes_utils.py
Original file line number Diff line number Diff line change
@@ -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)
47 changes: 27 additions & 20 deletions pywxdump/wx_info/get_wx_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,38 +200,43 @@ def read_key_bytes(h_process, address, address_len=8):
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"
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit 9e3e4cb

Please sign in to comment.