言GTA SA 固定地址修改器 v1.0 - 完整版代碼QZQ)
import pymem import pymem.process import tkinter as tk from tkinter import ttk, messagebox import threading import psutil import time # GTA SA v1.0 固定地址表 # 數(shù)據(jù)來(lái)源GTAMods Wiki / SDA Knowledge Base / 社區(qū)公認(rèn)文檔 # 適用范圍GTA SA v1.0 US (模塊基址 0x400000) # FIXED_ADDRESSES { # ---- 核心屬性 ---- 當(dāng)前金錢(qián): {addr: 0xB7CE50, type: int, desc: 玩家現(xiàn)金}, 生命上限: {addr: 0xB793E0, type: float, desc: Max Health}, 肌肉: {addr: 0xB793DC, type: float, desc: Muscle}, 體力: {addr: 0xB793D8, type: float, desc: Stamina}, 肥胖度: {addr: 0xB793D4, type: float, desc: Fat}, 性吸引力: {addr: 0xB793E4, type: float, desc: Sex Appeal}, 肺活量: {addr: 0xB791A4, type: float, desc: Lung Capacity}, # ---- 技能/熟練度 ---- 自行車(chē)技能: {addr: 0xB791B8, type: float, desc: Bike Skill}, 摩托車(chē)技能: {addr: 0xB791B4, type: float, desc: Cycle Skill}, 飛行技能: {addr: 0xB7919C, type: float, desc: Flying Skill}, 駕駛技能: {addr: 0xB790A0, type: float, desc: Driving Skill}, # ---- 游戲統(tǒng)計(jì) ---- 游戲時(shí)長(zhǎng)(ms): {addr: 0xB7CB84, type: int, desc: Playing Time}, 步行距離: {addr: 0xB7938C, type: float, desc: Distance on foot}, 駕駛距離: {addr: 0xB79390, type: float, desc: Distance in car}, 騎行距離: {addr: 0xB793EC, type: float, desc: Distance on bicycle}, # ---- 通緝等級(jí) ---- 通緝等級(jí): {addr: 0xBAA420, type: int, desc: Wanted Level 0-6}, # ---- 游戲時(shí)間/天氣 ---- 游戲小時(shí): {addr: 0xB7014C, type: int, desc: 0-23}, 游戲分鐘: {addr: 0xB70150, type: int, desc: 0-59}, 天氣: {addr: 0xC81320, type: int, desc: 0-20}, } # 動(dòng)態(tài)屬性通過(guò)玩家指針偏移計(jì)算 # 玩家指針固定地址0xB6F5F0 # 讀取方式CPed [0xB6F5F0]然后 CPed 偏移 # PLAYER_POINTER 0xB6F5F0 # 玩家 CPed 指針 PLAYER_OFFSETS { 當(dāng)前生命: 0x540, # Float 當(dāng)前護(hù)甲: 0x548, # Float } # 玩家坐標(biāo)指針鏈 # CPed - 0x14 坐標(biāo)結(jié)構(gòu) - 0x30/0x34/0x38 X/Y/Z # COORD_PTR_OFFSET 0x14 COORD_X_OFFSET 0x30 COORD_Y_OFFSET 0x34 COORD_Z_OFFSET 0x38 class GTASAFixedEditor: def __init__(self, root): self.root root self.root.title(GTA SA 固定地址修改器 v1.0 - 完整版) self.root.geometry(680x700) self.root.resizable(True, True) self.pm None self.process_id None self.player_ptr None # CPed 指針值 self.entries {} # 名稱(chēng) - 輸入框 self.locks {} # 名稱(chēng) - 是否鎖定 self.lock_threads {} # 名稱(chēng) - 線程 self.create_widgets() self.detect_game() # UI def create_widgets(self): # 頂部進(jìn)程狀態(tài) top ttk.LabelFrame(self.root, text進(jìn)程) top.pack(fillx, padx10, pady5) self.proc_var tk.StringVar(value未連接) ttk.Label(top, textvariableself.proc_var, width55).pack(sideleft, padx5) ttk.Button(top, text刷新檢測(cè), commandself.detect_game).pack(sideright, padx5) # 玩家指針狀態(tài) ptr_frame ttk.LabelFrame(self.root, text玩家指針 (CPed)) ptr_frame.pack(fillx, padx10, pady5) self.ptr_var tk.StringVar(value未獲取) ttk.Label(ptr_frame, textvariableself.ptr_var, font(Consolas, 10)).pack(sideleft, padx5) ttk.Button(ptr_frame, text刷新指針, commandself.update_player_ptr).pack(sideright, padx5) # 中部固定地址屬性列表 mid ttk.LabelFrame(self.root, text屬性列表v1.0 固定地址) mid.pack(fillboth, expandTrue, padx10, pady5) canvas tk.Canvas(mid, height350) scrollbar ttk.Scrollbar(mid, orientvertical, commandcanvas.yview) self.scrollable_frame ttk.Frame(canvas) self.scrollable_frame.bind( Configure, lambda e: canvas.configure(scrollregioncanvas.bbox(all)) ) canvas.create_window((0, 0), windowself.scrollable_frame, anchornw) canvas.configure(yscrollcommandscrollbar.set) canvas.pack(sideleft, fillboth, expandTrue) scrollbar.pack(sideright, filly) # 表頭 header ttk.Frame(self.scrollable_frame) header.grid(row0, column0, columnspan7, stickyew, pady2) ttk.Label(header, text屬性, width12, font(, 9, bold)).pack(sideleft, padx3) ttk.Label(header, text地址, width12, font(, 9, bold)).pack(sideleft, padx3) ttk.Label(header, text類(lèi)型, width6, font(, 9, bold)).pack(sideleft, padx3) ttk.Label(header, text值, width15, font(, 9, bold)).pack(sideleft, padx3) ttk.Label(header, text操作, width20, font(, 9, bold)).pack(sideleft, padx3) # 生成所有屬性行固定 動(dòng)態(tài) all_attrs list(FIXED_ADDRESSES.items()) [ (當(dāng)前生命, {addr: 動(dòng)態(tài), type: float, desc: CPed0x540}), (當(dāng)前護(hù)甲, {addr: 動(dòng)態(tài), type: float, desc: CPed0x548}), ] for i, (name, info) in enumerate(all_attrs, start1): self.create_attr_row(i, name, info) # 底部操作按鈕 bot ttk.Frame(self.root) bot.pack(fillx, padx10, pady5) ttk.Button(bot, text讀取全部, commandself.read_all).pack(sideleft, padx3) ttk.Button(bot, text一鍵滿(mǎn)血滿(mǎn)甲, commandself.max_hp_armor).pack(sideleft, padx3) ttk.Button(bot, text金錢(qián)9999999, commandlambda: self.quick_set(當(dāng)前金錢(qián), 9999999)).pack(sideleft, padx3) ttk.Button(bot, text通緝0, commandlambda: self.quick_set(通緝等級(jí), 0)).pack(sideleft, padx3) # 坐標(biāo)顯示 coord_frame ttk.LabelFrame(self.root, text玩家坐標(biāo)) coord_frame.pack(fillx, padx10, pady5) self.coord_var tk.StringVar(valueX?, Y?, Z?) ttk.Label(coord_frame, textvariableself.coord_var, font(Consolas, 10)).pack(sideleft, padx5) ttk.Button(coord_frame, text讀取坐標(biāo), commandself.read_coords).pack(sideright, padx5) # 狀態(tài)欄 self.status_var tk.StringVar(value就緒) ttk.Label(self.root, textvariableself.status_var, reliefsunken, anchorw).pack(fillx, sidebottom) def create_attr_row(self, row, name, info): 創(chuàng)建一行屬性 ttk.Label(self.scrollable_frame, textname, width12).grid( rowrow, column0, padx3, pady2, stickyw) addr info[addr] addr_text f0x{addr:08X} if isinstance(addr, int) else str(addr) ttk.Label(self.scrollable_frame, textaddr_text, font(Consolas, 9), width12).grid( rowrow, column1, padx3, pady2) ttk.Label(self.scrollable_frame, textinfo[type], width6).grid( rowrow, column2, padx3, pady2) entry ttk.Entry(self.scrollable_frame, width15) entry.grid(rowrow, column3, padx3, pady2) self.entries[name] entry # 按鈕組 btn_frame ttk.Frame(self.scrollable_frame) btn_frame.grid(rowrow, column4, padx3, pady2) ttk.Button(btn_frame, text讀, width3, commandlambda nname: self.read_value(n)).pack(sideleft, padx1) ttk.Button(btn_frame, text寫(xiě), width3, commandlambda nname: self.write_value(n)).pack(sideleft, padx1) ttk.Button(btn_frame, text鎖, width3, commandlambda nname: self.toggle_lock(n)).pack(sideleft, padx1) # 進(jìn)程操作 def detect_game(self): try: found None for p in psutil.process_iter([pid, name]): if gta_sa in (p.info[name] or ).lower(): found p.info break if not found: self.proc_var.set(未檢測(cè)到 gta_sa.exe) self.pm None return self.process_id found[pid] self.pm pymem.Pymem(self.process_id) self.proc_var.set(f已連接 {found[name]} (PID {self.process_id})) self.status_var.set(連接成功點(diǎn)“讀取全部”刷新數(shù)值) self.update_player_ptr() except Exception as e: self.pm None self.proc_var.set(f連接失敗: {e}) def update_player_ptr(self): 讀取玩家指針 if self.pm is None: messagebox.showinfo(提示, 未連接游戲進(jìn)程) return try: self.player_ptr self.pm.read_int(PLAYER_POINTER) self.ptr_var.set(fCPed 0x{self.player_ptr:08X}) self.status_var.set(f玩家指針更新: 0x{self.player_ptr:08X}) except Exception as e: self.player_ptr None self.ptr_var.set(讀取失敗) self.status_var.set(f玩家指針讀取失敗: {e}) # 讀取/寫(xiě)入 def resolve_address(self, name): 解析地址固定地址直接返回動(dòng)態(tài)屬性需要 player_ptr # 動(dòng)態(tài)屬性 if name in PLAYER_OFFSETS: if self.player_ptr is None: self.update_player_ptr() if self.player_ptr is None: return None return self.player_ptr PLAYER_OFFSETS[name] # 固定屬性 if name in FIXED_ADDRESSES: return FIXED_ADDRESSES[name][addr] return None def get_type(self, name): 獲取屬性類(lèi)型 if name in PLAYER_OFFSETS: return float if name in FIXED_ADDRESSES: return FIXED_ADDRESSES[name][type] return int def read_value(self, name): if self.pm is None: messagebox.showinfo(提示, 未連接游戲進(jìn)程) return addr self.resolve_address(name) if addr is None: self.status_var.set(f{name} 地址無(wú)效需要玩家指針) return try: t self.get_type(name) if t float: val self.pm.read_float(addr) disp f{val:.2f} else: val self.pm.read_int(addr) disp str(val) self.entries[name].delete(0, tk.END) self.entries[name].insert(0, disp) self.status_var.set(f{name} {disp}) except Exception as e: self.status_var.set(f讀取 {name} 失敗: {e}) def write_value(self, name): if self.pm is None: messagebox.showinfo(提示, 未連接游戲進(jìn)程) return addr self.resolve_address(name) if addr is None: self.status_var.set(f{name} 地址無(wú)效需要玩家指針) return try: txt self.entries[name].get().strip() t self.get_type(name) if t float: self.pm.write_float(addr, float(txt)) else: self.pm.write_int(addr, int(txt)) self.status_var.set(f{name} 已寫(xiě)入: {txt}) except ValueError: messagebox.showerror(錯(cuò)誤, 請(qǐng)輸入有效數(shù)字) except Exception as e: self.status_var.set(f寫(xiě)入 {name} 失敗: {e}) def quick_set(self, name, value): 快捷設(shè)置 if name not in self.entries: return self.entries[name].delete(0, tk.END) self.entries[name].insert(0, str(value)) self.write_value(name) def max_hp_armor(self): 一鍵滿(mǎn)血滿(mǎn)甲 if self.pm is None: messagebox.showinfo(提示, 未連接游戲進(jìn)程) return self.quick_set(當(dāng)前生命, 100) self.quick_set(當(dāng)前護(hù)甲, 100) self.status_var.set(生命和護(hù)甲已設(shè)為 100) def read_all(self): for name in list(FIXED_ADDRESSES.keys()) list(PLAYER_OFFSETS.keys()): self.read_value(name) def read_coords(self): 讀取玩家坐標(biāo) if self.pm is None: messagebox.showinfo(提示, 未連接游戲進(jìn)程) return if self.player_ptr is None: self.update_player_ptr() if self.player_ptr is None: return try: coord_ptr self.pm.read_int(self.player_ptr COORD_PTR_OFFSET) x self.pm.read_float(coord_ptr COORD_X_OFFSET) y self.pm.read_float(coord_ptr COORD_Y_OFFSET) z self.pm.read_float(coord_ptr COORD_Z_OFFSET) self.coord_var.set(fX{x:.2f}, Y{y:.2f}, Z{z:.2f}) self.status_var.set(f坐標(biāo): X{x:.2f}, Y{y:.2f}, Z{z:.2f}) except Exception as e: self.coord_var.set(讀取失敗) self.status_var.set(f讀取坐標(biāo)失敗: {e}) # 鎖定 def toggle_lock(self, name): if self.locks.get(name): self.locks[name] False self.status_var.set(f{name} 鎖定已關(guān)閉) else: self.locks[name] True t threading.Thread(targetself.lock_worker, args(name,), daemonTrue) self.lock_threads[name] t t.start() self.status_var.set(f{name} 鎖定中...) def lock_worker(self, name): while self.locks.get(name): try: if self.pm is not None: addr self.resolve_address(name) if addr is not None: txt self.entries[name].get().strip() if txt: t self.get_type(name) if t float: self.pm.write_float(addr, float(txt)) else: self.pm.write_int(addr, int(txt)) except Exception: pass time.sleep(0.3) if __name__ __main__: root tk.Tk() app GTASAFixedEditor(root) root.mainloop()