noita_entangled_worlds/scripts/gdb_ext.py

115 lines
2.9 KiB
Python
Raw Normal View History

2025-11-16 16:36:23 +03:00
import gdb
2025-11-16 19:45:01 +03:00
import json
2025-11-16 16:36:23 +03:00
patches = dict()
class PatchNop(gdb.Command):
def __init__ (self):
super().__init__ ("patch-nop", gdb.COMMAND_USER)
2025-11-16 19:45:01 +03:00
def invoke(self, arg, from_tty):
2025-11-16 16:36:23 +03:00
addr = int(arg, base=0)
if addr in patches:
print("Already patched")
return
print("Patching", hex(addr))
inferior = gdb.selected_inferior()
ln = 5
mem = inferior.read_memory(addr, ln)
print("Current memory", mem.hex())
patches[addr] = mem
inferior.write_memory(addr, bytes([0x90])*ln)
print("Ok")
class PatchRevert(gdb.Command):
def __init__ (self):
super().__init__ ("patch-revert", gdb.COMMAND_USER)
2025-11-16 19:45:01 +03:00
def invoke(self, arg, from_tty):
2025-11-16 16:36:23 +03:00
inferior = gdb.selected_inferior()
for addr, mem in patches.items():
print("Restoring", hex(addr))
inferior.write_memory(addr, mem)
patches.clear()
2025-11-16 19:45:01 +03:00
_vtables_cache = None
def get_vtables():
global _vtables_cache
if _vtables_cache is None:
print("Loading VTables")
_vtables_cache = {}
for k, v in json.load(open("gdb_data/vtables.json")).items():
_vtables_cache[int(k)] = v
return _vtables_cache
class MemReader:
def __init__(self):
self.inferior = gdb.selected_inferior()
def read_uint(self, addr: int):
try:
return int.from_bytes(self.inferior.read_memory(addr, 4), byteorder="little", signed=False)
except gdb.MemoryError:
return None
class ValueIdentifier:
def __init__(self, addr: int):
self.addr = addr
self.mem = MemReader()
self.vtables = get_vtables()
self.type = None
self.name = None
self.identify()
def identify(self):
if self.addr == 0:
self.type = "null"
return
if self.addr in self.vtables:
self.type = "pointer to vtable"
self.name = self.vtables[self.addr]
return
val = self.mem.read_uint(self.addr)
if val is None:
self.type = "unreadable pointer"
else:
#print(f"Checking if {hex(val)} is a vtable")
if val in self.vtables:
self.type = "pointer to value of type"
self.name = self.vtables[val]
return
class Identify(gdb.Command):
def __init__ (self):
super().__init__ ("identify", gdb.COMMAND_USER)
def invoke(self, arg, from_tty):
addr = int(arg, base=0)
vi = ValueIdentifier(addr)
print(vi.type, vi.name)
class Layout(gdb.Command):
def __init__ (self):
super().__init__ ("layout", gdb.COMMAND_USER)
def invoke(self, arg, from_tty):
splt = arg.split()
addr = int(splt[0], base=0)
amount = 1024
if len(splt) > 1:
amount = int(splt[1])
mem = MemReader()
for i in range(amount):
p = mem.read_uint(addr+i*4)
if p is not None:
vi = ValueIdentifier(p)
if vi.type is not None and vi.type != "null" and vi.type != "unreadable pointer":
print(f"Field {hex(i*4)} ({hex(addr+i*4)}, {hex(p)})", vi.type, vi.name)
2025-11-16 16:36:23 +03:00
PatchNop()
PatchRevert()
2025-11-16 19:45:01 +03:00
Identify()
Layout()