python-tools-windows/PVZ/PVZZZB_C4_gui.py

212 lines
9.0 KiB
Python
Raw Normal View History

import threading
import time
import tkinter as tk
from tkinter import messagebox
import win32gui
import win32process
import win32api
import ctypes
kernel32 = ctypes.windll.kernel32
PROCESS_ALL_ACCESS = (0x000F0000 | 0x00100000 | 0xFFF)
# 定义修改阳光和冷却的函数
def change_sun(Phand, sun_num):
sun_date = ctypes.c_long()
# kernel32.ReadProcessMemory(int(Phand), 0x03F8A9C0, ctypes.byref(sun_date), 4, None)
kernel32.ReadProcessMemory(int(Phand), 0x6A9EC0, ctypes.byref(sun_date), 4, None)
"""
0x03F8A9C0 是基础地址假设这里存储了一个指向阳光数值的指针
ctypes.byref(sun_date) 是一个指向sun_date变量的引用用于接收从内存中读取的数据
4 表示读取数据的大小这里是4字节即一个整数
None 是一个可选参数用来接收实际读取的字节数但在这里我们不关心这个值
"""
kernel32.ReadProcessMemory(int(Phand), sun_date.value + 0x768, ctypes.byref(sun_date), 4, None)
# 写入新的数值
new_sun_date = ctypes.c_long(sun_num)
print(sun_date)
print(new_sun_date)
return kernel32.WriteProcessMemory(int(Phand), sun_date.value + 0x5560, ctypes.byref(new_sun_date), 4, None)
def change_cooling(Phand, cooling):
"""
修改冷却
:param Phand:
:param cooling: 0 冷却 1 无冷却
:return:
"""
time.sleep(0.5)
cooling_data = ctypes.c_long()
kernel32.ReadProcessMemory(int(Phand), 0x6A9EC0, ctypes.byref(cooling_data), 4, None)
kernel32.ReadProcessMemory(int(Phand), cooling_data.value + 0x768, ctypes.byref(cooling_data), 4, None)
kernel32.ReadProcessMemory(int(Phand), cooling_data.value + 0x144, ctypes.byref(cooling_data), 4, None)
# kernel32.ReadProcessMemory(int(Phand),cooling_data.value,ctypes.byref(cooling_data),4,None)
new_cooling_date = ctypes.c_long(cooling)
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0x70, ctypes.byref(new_cooling_date), 4, None)
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0xC0, ctypes.byref(new_cooling_date), 4, None)
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0x110, ctypes.byref(new_cooling_date), 4, None)
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0x160, ctypes.byref(new_cooling_date), 4, None)
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0x1B0, ctypes.byref(new_cooling_date), 4, None)
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0x200, ctypes.byref(new_cooling_date), 4, None)
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0x250, ctypes.byref(new_cooling_date), 4, None)
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0x2A0, ctypes.byref(new_cooling_date), 4, None)
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0x2F0, ctypes.byref(new_cooling_date), 4, None)
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0x340, ctypes.byref(new_cooling_date), 4, None)
# 可以看出从第二个地址开始每个地址与其前一个地址之间的差值是固定的为0x5080。这种模式表明每个卡槽的冷却时间数据在内存中是以固定间隔排列的。
#
# 推测剩余卡槽的冷却时间地址
# 既然已知前十个卡槽的冷却时间地址遵循0x50的增量规律那么我们可以轻易地推测出后五个卡槽的冷却时间地址
#
# 第11个卡槽0x340 + 0x50 = 0x390 944
# 第12个卡槽0x390 + 0x50 = 0x3E0 992
# 第13个卡槽0x3E0 + 0x50 = 0x430 1072
# 第14个卡槽0x430 + 0x50 = 0x480 1168
# 第15个卡槽0x480 + 0x50 = 0x4D0 1232
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0x390, ctypes.byref(new_cooling_date), 4, None)
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0x3E0, ctypes.byref(new_cooling_date), 4, None)
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0x430, ctypes.byref(new_cooling_date), 4, None)
kernel32.WriteProcessMemory(int(Phand), cooling_data.value + 0x480, ctypes.byref(new_cooling_date), 4, None)
# 定义检测游戏是否运行的函数
def find_game_window():
global running # 声明running为全局变量
hwnd = win32gui.FindWindow(None, "植物大战僵尸v2.2 ")
if hwnd == 0:
messagebox.showinfo("提示", "植物大战僵尸没有运行,请先启动游戏!")
# 将启动按钮设置为禁用状态
# start_button.config(state=tk.DISABLED)
# 如果正在进行修改,则停止
if running:
running = False
start_button.config(text="启动")
return None
else:
_, pid = win32process.GetWindowThreadProcessId(hwnd)
Phand = win32api.OpenProcess(PROCESS_ALL_ACCESS, False, pid)
# 如果游戏开始运行,重新激活启动按钮
start_button.config(state=tk.NORMAL)
return Phand
# 定义修改阳光值的函数
def modify_sunlight():
sunlight_value = int(sunlight_entry.get())
Phand = find_game_window()
if Phand:
change_sun(Phand, sunlight_value)
# 定义修改冷却时间的函数
def modify_cooling():
cooling_enabled = cooling_var.get()
Phand = find_game_window()
if Phand:
change_cooling(Phand, cooling_enabled)
# 定义开始/停止修改的函数
def start_stop_modification():
global running
if running:
running = False
start_button.config(text="启动")
else:
running = True
start_button.config(text="启动中")
modification_thread = threading.Thread(target=modification_loop)
modification_thread.start()
def modification_loop():
while running:
modify_sunlight()
if cooling_var.get():
modify_cooling()
time.sleep(0.1) # 防止CPU占用过高
def get_visible_windows():
"""获取所有可见窗口的标题和PID"""
results = []
def foreach_window(hwnd, _):
if win32gui.IsWindowVisible(hwnd):
title = win32gui.GetWindowText(hwnd)
if title:
_, pid = win32process.GetWindowThreadProcessId(hwnd)
results.append((title, pid))
return True
win32gui.EnumWindows(foreach_window, None)
return results
def auto_fill_title(window_list, game_title):
"""尝试自动识别游戏窗口标题并填充"""
for title, _ in get_visible_windows():
if "植物大战僵尸" in title and "修改器" not in title:
game_title.set(title)
break
def update_window_list(window_list, game_title):
"""更新窗口列表并自动填充游戏窗口标题"""
window_list.delete(0, tk.END) # 清空列表
for title, pid in get_visible_windows():
window_list.insert(tk.END, f"{title} (PID: {pid})") # 插入新条目
auto_fill_title(window_list, game_title) # 尝试自动填充游戏标题
def enable_start_button(start_button, game_title):
"""当游戏标题非空时启用启动按钮"""
if game_title.get():
start_button.config(state=tk.NORMAL)
else:
start_button.config(state=tk.DISABLED)
if __name__ == '__main__':
running = False
# 创建主窗口
root = tk.Tk()
root.title("植物大战僵尸修改器")
root.resizable(False, False)
# 创建阳光值输入框
sunlight_label = tk.Label(root, text="阳光值:")
sunlight_label.grid(row=0, column=0, padx=(10, 5), pady=10)
sunlight_entry = tk.Entry(root)
sunlight_entry.insert(0, "9000")
sunlight_entry.grid(row=0, column=1, padx=(5, 10), pady=10)
# 创建冷却时间复选框
cooling_label = tk.Label(root, text="无冷却:")
cooling_label.grid(row=1, column=0, padx=(10, 5), pady=10)
cooling_var = tk.IntVar()
cooling_checkbutton = tk.Checkbutton(root, variable=cooling_var)
cooling_checkbutton.grid(row=1, column=1, padx=(5, 10), pady=10)
# 创建开始/停止按钮
start_button = tk.Button(root, text="启动", command=start_stop_modification)
start_button.grid(row=2, columnspan=2, padx=10, pady=10)
# 添加窗口列表框
window_list = tk.Listbox(root, height=5)
window_list.grid(row=0, column=2, rowspan=3, padx=(10, 10), pady=10)
# 添加更新窗口列表的按钮
update_button = tk.Button(root, text="刷新窗口列表", command=lambda: update_window_list(window_list, game_title))
update_button.grid(row=3, column=2, padx=10, pady=10)
# 创建游戏标题输入框
game_title_label = tk.Label(root, text="游戏窗口标题:")
game_title_label.grid(row=3, column=0, padx=(10, 5), pady=10)
game_title = tk.StringVar()
game_title_entry = tk.Entry(root, textvariable=game_title)
game_title_entry.grid(row=3, column=1, padx=(5, 10), pady=10)
game_title.trace("w", lambda *args: enable_start_button(start_button, game_title)) # 监听变化
# 确保开始按钮初始状态为禁用
start_button.config(state=tk.DISABLED)
root.mainloop()