import pymem import pymem.process import tkinter as tk from tkinter import ttk, messagebox import threading import time import psutil class LianLianKanTrainer: # ===== 内存地址 ===== ADDR_LIFE = 0x0019FE68 # 生命 ADDR_HINT = 0x0019FE6C # 提示 ADDR_SCORE = 0x0019FE70 # 分数 ADDR_TIME = 0x0019F484 # 时间(新找到的) # 时间数据类型: "int" 或 "float" TIME_TYPE = "int" # 冻结时间时写入的值(按实际类型调整) TIME_FREEZE_VALUE = 99999 # 自动扫描时匹配的进程名关键词 PROC_KEYWORDS = ["lian", "llk", "link", "game", "pet", "match", "连连看"] def __init__(self, root): self.root = root self.root.title("连连看v4.92修改器") self.root.geometry("540x600") self.root.resizable(False, False) self.pm = None self.process_name = "" self.auto_thread = None self.auto_running = False self.freeze_time = False # 是否冻结时间 self.setup_ui() self.root.after(200, self.scan_processes) # ---------------- UI ---------------- def setup_ui(self): # ===== 进程选择区 ===== top = ttk.LabelFrame(self.root, text="进程选择") top.pack(fill="x", padx=10, pady=8) ttk.Label(top, text="候选进程:").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.combo_proc = ttk.Combobox(top, width=42, state="readonly") self.combo_proc.grid(row=0, column=1, columnspan=2, padx=5, pady=5, sticky="we") self.combo_proc.bind("<<ComboboxSelected>>", self.on_combo_select) ttk.Button(top, text="自动扫描", command=self.scan_processes).grid(row=1, column=0, padx=5, pady=4) ttk.Button(top, text="附加选中", command=self.attach_selected).grid(row=1, column=1, padx=5, pady=4) ttk.Button(top, text="附加 PID", command=self.attach_by_pid).grid(row=1, column=2, padx=5, pady=4) ttk.Label(top, text="手动进程名:").grid(row=2, column=0, padx=5, pady=4, sticky="w") self.entry_proc = ttk.Entry(top, width=28) self.entry_proc.grid(row=2, column=1, padx=5, pady=4, sticky="we") self.entry_proc.insert(0, "game.exe") ttk.Button(top, text="附加", command=self.attach_manual).grid(row=2, column=2, padx=5, pady=4) self.lbl_status = ttk.Label(top, text="状态: 未附加", foreground="red") self.lbl_status.grid(row=3, column=0, columnspan=3, padx=5, pady=3, sticky="w") # ===== 数值显示区 ===== info = ttk.LabelFrame(self.root, text="当前数值") info.pack(fill="x", padx=10, pady=8) self.var_life = tk.StringVar(value="-") self.var_hint = tk.StringVar(value="-") self.var_score = tk.StringVar(value="-") self.var_time = tk.StringVar(value="-") ttk.Label(info, text="生命:").grid(row=0, column=0, padx=5, pady=4, sticky="w") ttk.Label(info, textvariable=self.var_life, width=12, foreground="blue").grid(row=0, column=1, sticky="w") ttk.Label(info, text="提示:").grid(row=0, column=2, padx=5, pady=4, sticky="w") ttk.Label(info, textvariable=self.var_hint, width=12, foreground="blue").grid(row=0, column=3, sticky="w") ttk.Label(info, text="分数:").grid(row=1, column=0, padx=5, pady=4, sticky="w") ttk.Label(info, textvariable=self.var_score, width=12, foreground="blue").grid(row=1, column=1, sticky="w") ttk.Label(info, text="时间:").grid(row=1, column=2, padx=5, pady=4, sticky="w") ttk.Label(info, textvariable=self.var_time, width=12, foreground="blue").grid(row=1, column=3, sticky="w") ttk.Button(info, text="刷新数值", command=self.refresh_values).grid(row=2, column=3, padx=5, pady=4) # ===== 修改区 ===== mod = ttk.LabelFrame(self.root, text="修改") mod.pack(fill="x", padx=10, pady=8) ttk.Label(mod, text="生命:").grid(row=0, column=0, padx=5, pady=4, sticky="w") self.entry_life = ttk.Entry(mod, width=10) self.entry_life.grid(row=0, column=1, padx=5) self.entry_life.insert(0, "99") ttk.Button(mod, text="写入", command=lambda: self.write_value("life")).grid(row=0, column=2, padx=5) ttk.Label(mod, text="提示:").grid(row=1, column=0, padx=5, pady=4, sticky="w") self.entry_hint = ttk.Entry(mod, width=10) self.entry_hint.grid(row=1, column=1, padx=5) self.entry_hint.insert(0, "99") ttk.Button(mod, text="写入", command=lambda: self.write_value("hint")).grid(row=1, column=2, padx=5) ttk.Label(mod, text="分数:").grid(row=2, column=0, padx=5, pady=4, sticky="w") self.entry_score = ttk.Entry(mod, width=10) self.entry_score.grid(row=2, column=1, padx=5) self.entry_score.insert(0, "999999") ttk.Button(mod, text="写入", command=lambda: self.write_value("score")).grid(row=2, column=2, padx=5) ttk.Label(mod, text="时间:").grid(row=3, column=0, padx=5, pady=4, sticky="w") self.entry_time = ttk.Entry(mod, width=10) self.entry_time.grid(row=3, column=1, padx=5) self.entry_time.insert(0, str(self.TIME_FREEZE_VALUE)) ttk.Button(mod, text="写入", command=lambda: self.write_value("time")).grid(row=3, column=2, padx=5) # ===== 快捷操作区 ===== quick = ttk.LabelFrame(self.root, text="快捷操作") quick.pack(fill="x", padx=10, pady=8) ttk.Button(quick, text="全部拉满", command=self.max_all).grid(row=0, column=0, padx=5, pady=5) self.btn_lock = ttk.Button(quick, text="锁定数值", command=self.toggle_lock) self.btn_lock.grid(row=0, column=1, padx=5, pady=5) self.btn_freeze_time = ttk.Button(quick, text="冻结时间", command=self.toggle_freeze_time) self.btn_freeze_time.grid(row=0, column=2, padx=5, pady=5) # ===== 日志 ===== self.log_text = tk.Text(self.root, height=6, state="normal") self.log_text.pack(fill="both", padx=10, pady=5) # ---------------- 进程扫描 ---------------- def scan_processes(self): candidates = [] for proc in psutil.process_iter(['pid', 'name']): try: name = proc.info['name'] or "" low = name.lower() if any(k in low for k in self.PROC_KEYWORDS): candidates.append(f"{name} (PID {proc.info['pid']})") except (psutil.NoSuchProcess, psutil.AccessDenied): continue candidates = sorted(set(candidates), key=lambda x: x.lower()) if candidates: self.combo_proc['values'] = candidates self.combo_proc.current(0) self.log(f"扫描到 {len(candidates)} 个候选进程") else: self.combo_proc['values'] = [] self.combo_proc.set("") self.log("未扫描到匹配进程,请手动输入进程名或 PID") def on_combo_select(self, event=None): text = self.combo_proc.get() if not text: return name = text.split("(")[0].strip() self.entry_proc.delete(0, tk.END) self.entry_proc.insert(0, name) # ---------------- 附加逻辑 ---------------- def _do_attach(self, target): try: self.pm = pymem.Pymem(target) self.process_name = target self.lbl_status.config( text=f"状态: 已附加 {target} (PID {self.pm.process_id})", foreground="green" ) self.log(f"附加成功: {target} (PID {self.pm.process_id})") self.refresh_values() except Exception as e: self.pm = None self.lbl_status.config(text="状态: 附加失败", foreground="red") self.log(f"附加失败: {e}") messagebox.showerror("错误", f"无法附加进程:\n{e}") def attach_selected(self): text = self.combo_proc.get() if not text: messagebox.showwarning("提示", "请先扫描并选择一个进程") return try: pid = int(text.split("PID")[-1].strip(" ()")) self._do_attach(pid) except Exception: name = text.split("(")[0].strip() self._do_attach(name) def attach_manual(self): name = self.entry_proc.get().strip() if not name: messagebox.showwarning("提示", "请输入进程名") return self._do_attach(name) def attach_by_pid(self): pid_str = tk.simpledialog.askstring("附加 PID", "输入进程 PID:") if not pid_str: return try: pid = int(pid_str.strip()) except ValueError: messagebox.showerror("错误", "PID 必须是整数") return self._do_attach(pid) # ---------------- 数值读写 ---------------- def _read_by_type(self, addr, type_): if type_ == "float": return self.pm.read_float(addr) return self.pm.read_int(addr) def _write_by_type(self, addr, val, type_): if type_ == "float": self.pm.write_float(addr, float(val)) else: self.pm.write_int(addr, int(val)) def refresh_values(self): if not self.pm: return try: self.var_life.set(str(self.pm.read_int(self.ADDR_LIFE))) self.var_hint.set(str(self.pm.read_int(self.ADDR_HINT))) self.var_score.set(str(self.pm.read_int(self.ADDR_SCORE))) self.var_time.set(str(self._read_by_type(self.ADDR_TIME, self.TIME_TYPE))) except Exception as e: self.log(f"读取失败: {e}") def write_value(self, kind): if not self.pm: messagebox.showwarning("提示", "请先附加进程") return try: if kind == "life": self.pm.write_int(self.ADDR_LIFE, int(self.entry_life.get())) self.log(f"写入生命 = {self.entry_life.get()}") elif kind == "hint": self.pm.write_int(self.ADDR_HINT, int(self.entry_hint.get())) self.log(f"写入提示 = {self.entry_hint.get()}") elif kind == "score": self.pm.write_int(self.ADDR_SCORE, int(self.entry_score.get())) self.log(f"写入分数 = {self.entry_score.get()}") elif kind == "time": self._write_by_type(self.ADDR_TIME, self.entry_time.get(), self.TIME_TYPE) self.log(f"写入时间 = {self.entry_time.get()}") self.refresh_values() except Exception as e: self.log(f"写入失败: {e}") messagebox.showerror("错误", f"写入失败:\n{e}") def max_all(self): if not self.pm: messagebox.showwarning("提示", "请先附加进程") return try: self.pm.write_int(self.ADDR_LIFE, 99) self.pm.write_int(self.ADDR_HINT, 99) self.pm.write_int(self.ADDR_SCORE, 999999) self.log("已拉满: 生命=99, 提示=99, 分数=999999") self.refresh_values() except Exception as e: self.log(f"操作失败: {e}") # ---------------- 锁定 ---------------- def toggle_lock(self): if not self.pm: messagebox.showwarning("提示", "请先附加进程") return if self.auto_running: self.auto_running = False self.btn_lock.config(text="锁定数值") self.log("已停止锁定") else: self.auto_running = True self.btn_lock.config(text="停止锁定") self.log("开始锁定生命和提示 (每 0.5 秒刷新)") self.auto_thread = threading.Thread(target=self._lock_loop, daemon=True) self.auto_thread.start() def _lock_loop(self): while self.auto_running and self.pm: try: self.pm.write_int(self.ADDR_LIFE, 99) self.pm.write_int(self.ADDR_HINT, 99) except Exception: pass time.sleep(0.5) # ---------------- 冻结时间 ---------------- def toggle_freeze_time(self): if not self.pm: messagebox.showwarning("提示", "请先附加进程") return self.freeze_time = not self.freeze_time if self.freeze_time: self.btn_freeze_time.config(text="停止冻结时间") self.log(f"开始冻结时间 (地址 {hex(self.ADDR_TIME)})") threading.Thread(target=self._freeze_time_loop, daemon=True).start() else: self.btn_freeze_time.config(text="冻结时间") self.log("已停止冻结时间") def _freeze_time_loop(self): while self.freeze_time and self.pm: try: self._write_by_type(self.ADDR_TIME, self.TIME_FREEZE_VALUE, self.TIME_TYPE) except Exception: pass time.sleep(0.3) # ---------------- 日志 ---------------- def log(self, msg): t = time.strftime("%H:%M:%S") self.log_text.insert(tk.END, f"[{t}] {msg}\n") self.log_text.see(tk.END) if __name__ == "__main__": root = tk.Tk() app = LianLianKanTrainer(root) root.mainloop()python语言-连连看v4.92修改器源代码.zip
https://download.csdn.net/download/qq_32257509/93805406