#!/usr/bin/env python3 """ 超自然签名文字数量上限解锁工具 UI增强版本 """ import ctypes import ctypes.wintypes as wintypes import time import os import sys # Windows控制台颜色 class Colors: HEADER = '\033[95m' BLUE = '\033[94m' CYAN = '\033[96m' GREEN = '\033[92m' YELLOW = '\033[93m' RED = '\033[91m' BOLD = '\033[1m' UNDERLINE = '\033[4m' END = '\033[0m' # 清屏 def clear_screen(): os.system('cls') # 打印居中文字 def print_center(text, width=80): print(text.center(width)) # 打印Ye Logo def print_logo(): logo = """ ███████╗██╗ █████╗ ██████╗ ███████╗███████╗██████╗ ██╗ ██╗██╗ ██╗ ██╔════╝██║ ██╔══██╗██╔════╝ ██╔════╝██╔════╝██╔══██╗██║ ██╔╝██║ ██║ █████╗ ██║ ███████║██║ ███╗█████╗ █████╗ ██║ ██║█████╔╝ ██║ ██║ ██╔══╝ ██║ ██╔══██║██║ ██║██╔══╝ ██╔══╝ ██║ ██║██╔═██╗ ██║ ██║ ██║ ███████╗██║ ██║╚██████╔╝███████╗███████╗██████╔╝██║ ██╗╚██████╔╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═════╝ By:烨 """ print(Colors.CYAN + logo + Colors.END) # 打印分割线 def print_line(char='═', width=80): print(Colors.CYAN + char * width + Colors.END) # 打印标题框 def print_box(title, width=80): print_line('═') print_center(Colors.BOLD + Colors.YELLOW + title + Colors.END, width) print_line('═') # 等待动画 def waiting_animation(seconds=2): animation = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" idx = 0 end_time = time.time() + seconds while time.time() < end_time: print(f"\r{Colors.CYAN}正在初始化{animation[idx % len(animation)]}{Colors.END}", end='') idx += 1 time.sleep(0.1) print(f"\r{Colors.GREEN}✓ 初始化完成{Colors.END} ") PROCESS_ALL_ACCESS = 0x1F0FFF TH32CS_SNAPPROCESS = 0x00000002 class PROCESSENTRY32(ctypes.Structure): _fields_ = [ ("dwSize", wintypes.DWORD), ("cntUsage", wintypes.DWORD), ("th32ProcessID", wintypes.DWORD), ("th32DefaultHeapID", ctypes.POINTER(wintypes.ULONG)), ("th32ModuleID", wintypes.DWORD), ("cntThreads", wintypes.DWORD), ("th32ParentProcessID", wintypes.DWORD), ("pcPriClassBase", wintypes.LONG), ("dwFlags", wintypes.DWORD), ("szExeFile", wintypes.CHAR * 260) ] class MemoryModifier: def __init__(self, process_name): self.process_name = process_name self.process_handle = None self.process_id = None self.kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) self.psapi = ctypes.WinDLL('psapi', use_last_error=True) self.module_base = None def find_process(self): snapshot = self.kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) if snapshot == -1: raise Exception("创建进程快照失败") process_entry = PROCESSENTRY32() process_entry.dwSize = ctypes.sizeof(PROCESSENTRY32) if not self.kernel32.Process32First(snapshot, ctypes.byref(process_entry)): self.kernel32.CloseHandle(snapshot) raise Exception("枚举进程失败") self.process_id = None while True: if process_entry.szExeFile.decode('mbcs') == self.process_name: self.process_id = process_entry.th32ProcessID break if not self.kernel32.Process32Next(snapshot, ctypes.byref(process_entry)): break self.kernel32.CloseHandle(snapshot) if not self.process_id: raise Exception(f"未找到进程: {self.process_name}") return self.process_id def open_process(self): self.process_handle = self.kernel32.OpenProcess( PROCESS_ALL_ACCESS, False, self.process_id ) if not self.process_handle: raise Exception(f"打开进程失败,错误代码: {ctypes.get_last_error()}") def find_module_base(self, module_name): h_process = self.kernel32.OpenProcess(0x0410, False, self.process_id) if not h_process: raise Exception(f"无法打开进程查询模块信息") h_modules = (ctypes.c_void_p * 1024)() cb_needed = wintypes.DWORD() if not self.psapi.EnumProcessModulesEx( h_process, ctypes.byref(h_modules), ctypes.sizeof(h_modules), ctypes.byref(cb_needed), 0x03 ): self.kernel32.CloseHandle(h_process) raise Exception("枚举模块失败") module_count = cb_needed.value // ctypes.sizeof(ctypes.c_void_p) for i in range(module_count): h_module = ctypes.c_void_p(h_modules[i]) name_buffer = ctypes.create_unicode_buffer(260) if self.psapi.GetModuleBaseNameW(h_process, h_module, name_buffer, 260): if name_buffer.value.lower() == module_name.lower(): class MODULEINFO(ctypes.Structure): _fields_ = [ ("lpBaseOfDll", ctypes.c_void_p), ("SizeOfImage", wintypes.DWORD), ("EntryPoint", ctypes.c_void_p) ] mod_info = MODULEINFO() if self.psapi.GetModuleInformation( h_process, h_module, ctypes.byref(mod_info), ctypes.sizeof(mod_info) ): self.module_base = ctypes.cast(mod_info.lpBaseOfDll, ctypes.c_void_p).value break self.kernel32.CloseHandle(h_process) if not self.module_base: raise Exception(f"未找到模块: {module_name}") return self.module_base def read_memory(self, address, size=4): buffer = ctypes.create_string_buffer(size) bytes_read = ctypes.c_size_t(0) success = self.kernel32.ReadProcessMemory( self.process_handle, ctypes.c_void_p(address), buffer, size, ctypes.byref(bytes_read) ) if not success: return None if size == 1: byte_val = buffer[0] if isinstance(byte_val, bytes): byte_val = ord(byte_val) return byte_val elif size == 4: return ctypes.c_int32.from_buffer(buffer).value elif size == 8: return ctypes.c_int64.from_buffer(buffer).value else: return buffer.raw def write_memory(self, address, value, size=4): if size == 1: data = ctypes.c_byte(value) elif size == 4: data = ctypes.c_int32(value) else: data = ctypes.create_string_buffer(value, size) bytes_written = ctypes.c_size_t(0) success = self.kernel32.WriteProcessMemory( self.process_handle, ctypes.c_void_p(address), ctypes.byref(data), size, ctypes.byref(bytes_written) ) return success def follow_pointer_chain(self, base_address, offsets): current_ptr = self.read_memory(base_address, 8) if current_ptr is None: return None if current_ptr < 0: current_ptr = current_ptr & 0xFFFFFFFFFFFFFFFF for i, offset in enumerate(offsets[:-1]): read_addr = current_ptr + offset value = self.read_memory(read_addr, 8) if value is None: return None if value < 0: value = value & 0xFFFFFFFFFFFFFFFF current_ptr = value if current_ptr == 0: return None final_offset = offsets[-1] final_address = current_ptr + final_offset return final_address def main(): # 清屏 clear_screen() # 打印Logo print_logo() # 打印分割线 print_line('═') # 打印qq print() print_center(Colors.BOLD + Colors.GREEN + "★ 公益彩签工具 + QQ群: 1004293695 ★" + Colors.END) print() # 打印分割线 print_line('═') # 打印使用说明 print() print(Colors.YELLOW + "【使用前准备】" + Colors.END) print() print(Colors.CYAN + " 1. 请在PC上打开《超自然》游戏" + Colors.END) print(Colors.CYAN + " 2. 进入游戏后,打开【个人签名】编辑页面" + Colors.END) print(Colors.CYAN + " 3. 确保处于可以输入签名的状态" + Colors.END) print() print_line('─') # 等待用户确认 print() print(Colors.BOLD + Colors.YELLOW + " 已在签名编辑页面?请输入 " + Colors.RED + "yes" + Colors.YELLOW + " 继续: " + Colors.END, end='') user_input = input().strip().lower() if user_input != 'yes': print() print(Colors.RED + " ✗ 已取消启动" + Colors.END) input("\n按回车键退出...") return # 用户确认,开始初始化 print() print_line('═') print() PROCESS_NAME = "preternatural.exe" MODULE_NAME = "GameAssembly.dll" # 两个地址使用相同的基址 SIGNATURE_BASE_OFFSET = 0x035263A0 # 签名字数上限指针链 SIGNATURE_LIMIT_OFFSETS = [0xB8, 0xA0, 0x18, 0x20, 0x28, 0x40, 0x130] # 剩余签名字数指针链 SIGNATURE_REMAINING_OFFSETS = [0xB8, 0xA0, 0x18, 0x20, 0x28, 0x40, 0x134] try: print(Colors.CYAN + " 正在查找游戏进程..." + Colors.END) modifier = MemoryModifier(PROCESS_NAME) modifier.find_process() print(Colors.GREEN + " ✓ 找到游戏进程" + Colors.END) print(Colors.CYAN + " 正在连接游戏..." + Colors.END) modifier.open_process() print(Colors.GREEN + " ✓ 已连接到游戏" + Colors.END) print(Colors.CYAN + " 正在定位内存地址..." + Colors.END) modifier.find_module_base(MODULE_NAME) # 两个地址使用相同的基址 base = modifier.module_base + SIGNATURE_BASE_OFFSET # 分别解析两个地址 limit_address = modifier.follow_pointer_chain(base, SIGNATURE_LIMIT_OFFSETS) remaining_address = modifier.follow_pointer_chain(base, SIGNATURE_REMAINING_OFFSETS) if limit_address is None or remaining_address is None: print() print(Colors.RED + " ✗ 错误: 无法定位内存地址" + Colors.END) print() print(Colors.YELLOW + " 请确认:" + Colors.END) print(Colors.CYAN + " • 游戏是否正在运行" + Colors.END) print(Colors.CYAN + " • 是否已打开签名编辑页面" + Colors.END) input("\n按回车键退出...") return # 读取初始值 initial_limit = modifier.read_memory(limit_address, 1) initial_remaining = modifier.read_memory(remaining_address, 1) print(Colors.GREEN + " ✓ 内存地址定位成功" + Colors.END) print() print_line('═') print() # 成功启动 print(Colors.BOLD + Colors.GREEN + " ╔════════════════════════════════════════╗" + Colors.END) print(Colors.BOLD + Colors.GREEN + " ║ 签名解锁工具已成功启动 ║" + Colors.END) print(Colors.BOLD + Colors.GREEN + " ║ 字数上限已锁定为 255 ║" + Colors.END) print(Colors.BOLD + Colors.GREEN + " ╚════════════════════════════════════════╝" + Colors.END) print(Colors.BOLD + Colors.GREEN + " 输入文字即可刷新上限,套用模板diy自己的彩色签名!" + Colors.END) print() print(Colors.CYAN + " 当前状态: 上限=" + Colors.YELLOW + str(initial_limit) + Colors.CYAN + ", 剩余=" + Colors.YELLOW + str(initial_remaining) + Colors.END) print() print_line('═') print() # 提示查看文档 print(Colors.BOLD + Colors.YELLOW + " 💡 提示: 签名格式说明" + Colors.END) print() print(Colors.CYAN + " • 彩签文档位置: 超自然行动组签名格式说明.md" + Colors.END) print(Colors.CYAN + " • 无脑模板: 无脑模板.txt" + Colors.END) print() print(Colors.GREEN + " 现在可以在游戏中输入超过15个字的签名了!" + Colors.END) print() print_line('═') print() print(Colors.YELLOW + " 按 " + Colors.RED + "Ctrl+C" + Colors.YELLOW + " 停止锁定,改完彩签保存后务必停止锁定!!" + Colors.END) print() # 开始锁定循环 lock_count = 0 while True: try: modifier.write_memory(limit_address, 255, 1) modifier.write_memory(remaining_address, 255, 1) lock_count += 1 if lock_count % 100 == 0: status = f"锁定中... 已锁定 {lock_count} 次" print(f"\r{Colors.CYAN}{status}{Colors.END}", end='') sys.stdout.flush() time.sleep(0.01) except KeyboardInterrupt: print() print() print_line('═') print() print(Colors.YELLOW + " 用户停止锁定" + Colors.END) print(Colors.CYAN + f" 总共锁定: {lock_count} 次" + Colors.END) print() break except Exception as e: print() print() print(Colors.RED + f" ✗ 错误: {e}" + Colors.END) break except Exception as e: print() print() print(Colors.RED + f" ✗ 错误: {e}" + Colors.END) print() print(Colors.YELLOW + " 请确认:" + Colors.END) print(Colors.CYAN + " • 游戏是否正在运行" + Colors.END) print(Colors.CYAN + " • 是否已打开签名编辑页面" + Colors.END) finally: if modifier.process_handle: modifier.kernel32.CloseHandle(modifier.process_handle) print() print_line('═') print() print(Colors.GREEN + " 感谢使用!QQ群: 1004293695" + Colors.END) print() input("按回车键退出...") if __name__ == "__main__": main()