
import pymem import pymem.process import tkinter as tk from tkinter import ttk, messagebox import threading import time # 罪恶都市 1.0原版 偏移 # 错误写法 # OFFSET_PLAYER_PTR 0x0094AD28 # 正确写法 OFFSET_PLAYER_PTR 0x0054AD28 # OFFSET_HEALTH 0xD5 # OFFSET_ARMOR 0xB8 OFFSET_HEALTH 0x354 # 正确 OFFSET_ARMOR 0x358 # 正确护甲紧挨着生命 OFFSET_MONEY 0x0054ADC8 OFFSET_WANTED 0x00549788 PM None BASE_ADDR 0 PROCESS_NAME gta-vc.exe def attach_game(): global PM, BASE_ADDR try: PM pymem.Pymem(PROCESS_NAME) module pymem.process.module_from_name(PM.process_handle, PROCESS_NAME) BASE_ADDR module.lpBaseOfDll messagebox.showinfo(成功, 已附加 gta-vc.exe) status_label.config(text✅ 已连接游戏) except pymem.exception.ProcessNotFound: messagebox.showerror(错误, 未找到 gta-vc.exe\n请先启动罪恶都市) status_label.config(text❌ 未找到游戏) PM None BASE_ADDR 0 except Exception as e: messagebox.showerror(异常, f附加失败{str(e)}) status_label.config(text❌ 连接失败) PM None BASE_ADDR 0 def get_player_ptr(): global PM, BASE_ADDR if PM is None or BASE_ADDR 0: return 0 try: return PM.read_int(BASE_ADDR OFFSET_PLAYER_PTR) except: return 0 def set_health(): global PM if PM is None: messagebox.showwarning(提示, 请先附加游戏) return player_ptr get_player_ptr() if player_ptr 0: messagebox.showerror(错误, 读取玩家指针失败偏移/版本不匹配) return try: val float(health_entry.get()) PM.write_float(player_ptr OFFSET_HEALTH, val) except ValueError: messagebox.showerror(错误, 请输入数字) def set_armor(): global PM if PM is None: messagebox.showwarning(提示, 请先附加游戏) return player_ptr get_player_ptr() if player_ptr 0: messagebox.showerror(错误, 读取玩家指针失败偏移/版本不匹配) return try: val float(armor_entry.get()) PM.write_float(player_ptr OFFSET_ARMOR, val) except ValueError: messagebox.showerror(错误, 请输入数字) def set_money(): global PM, BASE_ADDR if PM is None: messagebox.showwarning(提示, 请先附加游戏) return if BASE_ADDR 0: messagebox.showerror(错误,模块基址获取失败) return try: val int(money_entry.get()) PM.write_int(BASE_ADDR OFFSET_MONEY, val) except ValueError: messagebox.showerror(错误, 请输入数字) def set_wanted(): global PM, BASE_ADDR if PM is None: messagebox.showwarning(提示, 请先附加游戏) return if BASE_ADDR 0: messagebox.showerror(错误,模块基址获取失败) return try: val int(wanted_entry.get()) if val 0 or val 6: messagebox.showwarning(提示, 通缉等级范围0~6) return PM.write_int(BASE_ADDR OFFSET_WANTED, val) except ValueError: messagebox.showerror(错误, 请输入数字) def loop_refresh_info(): global PM, BASE_ADDR while True: if PM is not None and BASE_ADDR !0: try: player_ptr PM.read_int(BASE_ADDR OFFSET_PLAYER_PTR) if player_ptr ! 0: hp PM.read_float(player_ptr OFFSET_HEALTH) ar PM.read_float(player_ptr OFFSET_ARMOR) else: hp 0 ar 0 mon PM.read_int(BASE_ADDR OFFSET_MONEY) want PM.read_int(BASE_ADDR OFFSET_WANTED) root.after(0, lambda: health_var.set(f{hp:.1f})) root.after(0, lambda: armor_var.set(f{ar:.1f})) root.after(0, lambda: money_var.set(str(mon))) root.after(0, lambda: wanted_var.set(str(want))) except Exception as e: root.after(0, lambda: health_var.set(读取失败)) root.after(0, lambda: armor_var.set(读取失败)) root.after(0, lambda: money_var.set(读取失败)) root.after(0, lambda: wanted_var.set(读取失败)) # 这里不再自动置空PM防止状态错乱 # PM None # root.after(0, lambda: status_label.config(text❌ 游戏断开)) time.sleep(0.3) # 冻结生命线程 def freeze_loop(): global PM while True: if freeze_var.get() and PM is not None and BASE_ADDR !0: try: player_ptr get_player_ptr() if player_ptr ! 0: PM.write_float(player_ptr OFFSET_HEALTH, float(health_entry.get())) except: pass time.sleep(0.1) # GUI root tk.Tk() root.title(GTA罪恶都市 修改器【学习演示】) root.geometry(440x340) health_var tk.StringVar(value---) armor_var tk.StringVar(value---) money_var tk.StringVar(value---) wanted_var tk.StringVar(value---) status_label ttk.Label(root, text❌ 未连接游戏, font(Consolas,11)) status_label.pack(pady5) attach_btn ttk.Button(root, text1.附加游戏进程, commandattach_game) attach_btn.pack(pady3) frame ttk.Frame(root) frame.pack(pady10, fillx, padx20) ttk.Label(frame, text生命值).grid(row0, column0, stickyw) health_entry ttk.Entry(frame, width12) health_entry.grid(row0, column1, padx5) health_entry.insert(0, 200.0) ttk.Button(frame, text写入, commandset_health).grid(row0, column2, padx2) ttk.Label(frame, text当前).grid(row0, column3, padx3) ttk.Label(frame, textvariablehealth_var).grid(row0, column4) ttk.Label(frame, text护 甲).grid(row1, column0, stickyw) armor_entry ttk.Entry(frame, width12) armor_entry.grid(row1, column1, padx5) armor_entry.insert(0, 200.0) ttk.Button(frame, text写入, commandset_armor).grid(row1, column2, padx2) ttk.Label(frame, text当前).grid(row1, column3, padx3) ttk.Label(frame, textvariablearmor_var).grid(row1, column4) ttk.Label(frame, text金 钱).grid(row2, column0, stickyw) money_entry ttk.Entry(frame, width12) money_entry.grid(row2, column1, padx5) money_entry.insert(0, 9999999) ttk.Button(frame, text写入, commandset_money).grid(row2, column2, padx2) ttk.Label(frame, text当前).grid(row2, column3, padx3) ttk.Label(frame, textvariablemoney_var).grid(row2, column4) ttk.Label(frame, text通缉星).grid(row3, column0, stickyw) wanted_entry ttk.Entry(frame, width12) wanted_entry.grid(row3, column1, padx5) wanted_entry.insert(0, 0) ttk.Button(frame, text写入, commandset_wanted).grid(row3, column2, padx2) ttk.Label(frame, text当前).grid(row3, column3, padx3) ttk.Label(frame, textvariablewanted_var).grid(row3, column4) freeze_var tk.BooleanVar() freeze_check ttk.Checkbutton(root, text冻结生命不掉血, variablefreeze_var) freeze_check.pack() read_thread threading.Thread(targetloop_refresh_info, daemonTrue) read_thread.start() freeze_thread threading.Thread(targetfreeze_loop, daemonTrue) freeze_thread.start() root.mainloop()