Files
WechatHookBot/WechatHook/loader.py
2025-12-03 15:48:44 +08:00

312 lines
8.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
NoveLoader - DLL 加载器和函数封装
基于个微大客户版 Loader.dll 的 Python 封装
"""
import ctypes
import os
from ctypes import WinDLL, create_string_buffer, WINFUNCTYPE
from typing import Callable
from loguru import logger
def c_string(data: str) -> ctypes.c_char_p:
"""将 Python 字符串转换为 C 字符串"""
return ctypes.c_char_p(data.encode('utf-8'))
def create_shared_memory():
"""创建共享内存用于DLL通信"""
try:
kernel32 = ctypes.WinDLL('kernel32')
# 创建文件映射
file_handle = kernel32.CreateFileMappingA(
-1,
None,
4, # PAGE_READWRITE
0,
33,
"windows_shell_global__".encode('utf-8')
)
if not file_handle:
logger.warning("创建共享内存失败")
return None, None
# 映射到内存
data_address = kernel32.MapViewOfFile(
file_handle,
983071, # FILE_MAP_ALL_ACCESS
0,
0,
0
)
if not data_address:
logger.warning("映射共享内存失败")
kernel32.CloseHandle(file_handle)
return None, None
# 写入Key数据
key = "3101b223dca7715b0154924f0eeeee20".encode('utf-8')
kernel32.RtlMoveMemory(data_address, key, len(key))
logger.success("共享内存创建成功")
return file_handle, data_address
except Exception as e:
logger.warning(f"创建共享内存异常: {e}")
return None, None
class NoveLoader:
"""
Loader.dll 封装类
通过内存偏移调用 DLL 中的未导出函数
"""
# 加载器模块基址
loader_module_base: int = 0
# 函数偏移地址(基于 Loader.dll
_InitWeChatSocket: int = 0xB080
_GetUserWeChatVersion: int = 0xCB80
_InjectWeChat: int = 0xCC10
_SendWeChatData: int = 0xAF90
_DestroyWeChat: int = 0xC540
_UseUtf8: int = 0xC680
_InjectWeChat2: int = 0x14D7
_InjectWeChatPid: int = 0xB750
_InjectWeChatMultiOpen: int = 0x33B2
def __init__(self, loader_path: str):
"""
初始化 Loader
Args:
loader_path: Loader.dll 的路径
"""
loader_path = os.path.realpath(loader_path)
if not os.path.exists(loader_path):
logger.error(f'Loader.dll 不存在: {loader_path}')
raise FileNotFoundError(f'Loader.dll 不存在: {loader_path}')
logger.info(f"加载 Loader.dll: {loader_path}")
loader_module = WinDLL(loader_path)
self.loader_module_base = loader_module._handle
# 使用 UTF-8 编码
self.UseUtf8()
logger.success("Loader.dll 加载成功")
# 初始化回调
from WechatHook.callbacks import (
wechat_connect_callback,
wechat_recv_callback,
wechat_close_callback
)
self.InitWeChatSocket(
wechat_connect_callback,
wechat_recv_callback,
wechat_close_callback
)
def __get_non_exported_func(self, offset: int, arg_types, return_type):
"""
通过内存偏移获取未导出的函数
Args:
offset: 函数相对于模块基址的偏移
arg_types: 参数类型列表
return_type: 返回值类型
Returns:
可调用的函数对象
"""
func_addr = self.loader_module_base + offset
if arg_types:
func_type = ctypes.WINFUNCTYPE(return_type, *arg_types)
else:
func_type = ctypes.WINFUNCTYPE(return_type)
return func_type(func_addr)
def InitWeChatSocket(
self,
connect_callback: Callable,
recv_callback: Callable,
close_callback: Callable
) -> bool:
"""
初始化微信 Socket 回调
Args:
connect_callback: 连接回调函数
recv_callback: 接收消息回调函数
close_callback: 断开连接回调函数
Returns:
是否初始化成功
"""
func = self.__get_non_exported_func(
self._InitWeChatSocket,
[ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p],
ctypes.c_bool
)
result = func(connect_callback, recv_callback, close_callback)
logger.info(f"InitWeChatSocket: {result}")
return result
def GetUserWeChatVersion(self) -> str:
"""
获取用户电脑上安装的微信版本
Returns:
微信版本号,如 "3.9.10.19"
"""
func = self.__get_non_exported_func(
self._GetUserWeChatVersion,
[ctypes.c_char_p],
ctypes.c_bool
)
out = create_string_buffer(30)
if func(out):
version = out.value.decode('utf-8')
logger.info(f"微信版本: {version}")
return version
else:
logger.warning("获取微信版本失败")
return ''
def InjectWeChat(self, dll_path: str) -> int:
"""
注入微信(智能多开)
Args:
dll_path: Helper.dll 的路径
Returns:
客户端 ID进程 ID失败返回 0
"""
dll_path = os.path.realpath(dll_path)
if not os.path.exists(dll_path):
logger.error(f'Helper.dll 不存在: {dll_path}')
return 0
func = self.__get_non_exported_func(
self._InjectWeChat,
[ctypes.c_char_p],
ctypes.c_uint32
)
client_id = func(c_string(dll_path))
if client_id:
logger.success(f"注入微信成功,客户端 ID: {client_id}")
else:
logger.error("注入微信失败")
return client_id
def SendWeChatData(self, client_id: int, message: str) -> bool:
"""
向微信发送数据
Args:
client_id: 客户端 ID
message: JSON 格式的消息
Returns:
是否发送成功
"""
func = self.__get_non_exported_func(
self._SendWeChatData,
[ctypes.c_uint32, ctypes.c_char_p],
ctypes.c_bool
)
result = func(client_id, c_string(message))
return result
def DestroyWeChat(self) -> bool:
"""
销毁微信连接
Returns:
是否成功
"""
func = self.__get_non_exported_func(
self._DestroyWeChat,
None,
ctypes.c_bool
)
result = func()
logger.info(f"DestroyWeChat: {result}")
return result
def UseUtf8(self) -> bool:
"""
设置使用 UTF-8 编码
Returns:
是否成功
"""
func = self.__get_non_exported_func(
self._UseUtf8,
None,
ctypes.c_bool
)
return func()
def InjectWeChat2(self, dll_path: str, exe_path: str) -> int:
"""
注入微信(指定微信路径)
Args:
dll_path: Helper.dll 的路径
exe_path: WeChat.exe 的路径
Returns:
客户端 ID失败返回 0
"""
func = self.__get_non_exported_func(
self._InjectWeChat2,
[ctypes.c_char_p, ctypes.c_char_p],
ctypes.c_uint32
)
return func(c_string(dll_path), c_string(exe_path))
def InjectWeChatPid(self, pid: int, dll_path: str) -> int:
"""
注入指定的微信进程
Args:
pid: 微信进程 ID
dll_path: Helper.dll 的路径
Returns:
客户端 ID失败返回 0
"""
func = self.__get_non_exported_func(
self._InjectWeChatPid,
[ctypes.c_uint32, ctypes.c_char_p],
ctypes.c_uint32
)
return func(pid, c_string(dll_path))
def InjectWeChatMultiOpen(self, dll_path: str, exe_path: str = "") -> int:
"""
多开一个新的微信进程并注入
Args:
dll_path: Helper.dll 的路径
exe_path: WeChat.exe 的路径(可选)
Returns:
客户端 ID失败返回 0
"""
func = self.__get_non_exported_func(
self._InjectWeChatMultiOpen,
[ctypes.c_char_p, ctypes.c_char_p],
ctypes.c_uint32
)
return func(c_string(dll_path), c_string(exe_path) if exe_path else c_string(""))