diff --git a/lib/pyda/base.py b/lib/pyda/base.py index 427bfe3..75c32e5 100644 --- a/lib/pyda/base.py +++ b/lib/pyda/base.py @@ -19,7 +19,7 @@ def process(**kwargs): INIT = True if "pwndbg" in sys.modules: pwndbg_compat.patch_pwndbg(sys.modules["pwndbg"], proc) - + return proc def xinfo(addr): diff --git a/lib/pyda/hacks/gdb.py b/lib/pyda/hacks/gdb.py index 5c82fa5..16aa9b1 100644 --- a/lib/pyda/hacks/gdb.py +++ b/lib/pyda/hacks/gdb.py @@ -61,21 +61,21 @@ def __init__(self, sz, signed, float=False): self.sz = sz self.signed = signed self.float = float - + def pointer(self): return Pointer(self) - + @property def sizeof(self): return self.sz - + @property def alignof(self): return self.sz - + def array(self, n): return Array(self, n) - + def __eq__(self, other): if not isinstance(other, Type): return False @@ -90,7 +90,7 @@ class Pointer(Type): def __init__(self, t): super().__init__(8, False) self._points_to = t - + def __eq__(self, other): if not isinstance(other, Pointer): return False @@ -102,13 +102,13 @@ def __init__(self, t, n): super().__init__(t.sz * n, t.signed) self._points_to = t self._n = n - + def __eq__(self, other): if not isinstance(other, Array): return False return self._points_to == other._points_to and self._n == other._n - + def target(self): return self._points_to @@ -116,12 +116,12 @@ class Value: def __init__(self, v): self.v = v self.type = Type(0, False) - + def cast(self, t): v = Value(self.v) v.type = t return v - + def __int__(self): assert not isinstance(self.type, Array) assert not self.type.float @@ -131,7 +131,7 @@ def __int__(self): return int.from_bytes(self.v, pyda.arch.endianness()) else: raise NotImplementedError(f"Value: {self.v}") - + def __getitem__(self, idx): assert isinstance(self.type, Array), f"type: {self.type.__class__} {Array}" assert type(self.v) is bytes @@ -157,7 +157,7 @@ def __init__(self): class error(BaseException): def __init__(self, s): self.s = s - + def __str__(self): return self.s @@ -210,7 +210,7 @@ def lookup_type(s): class Thread(): def __init__(self, tid): self.tid = tid - + @property def global_num(self): return self.tid diff --git a/lib/pyda/hacks/pwndbg_compat.py b/lib/pyda/hacks/pwndbg_compat.py index 486e842..e9f93b2 100644 --- a/lib/pyda/hacks/pwndbg_compat.py +++ b/lib/pyda/hacks/pwndbg_compat.py @@ -22,25 +22,25 @@ def sharedlibrary_paths(self): class GDBLibFile(): def __init__(self): pass - + def get_file(self, path, **kwargs): p = Path(path) if p.is_file(): return str(p) return None - + class GDBLibSymbol(): def __init__(self): pass - + def static_linkage_symbol_address(self, name): return None - + def address(self, name): return None - + def get(self, addr): res = pyda_core.get_module_for_addr(int(addr)) if res[0] != 'unknown': @@ -56,25 +56,25 @@ def get_glibc_section_address(section): addr = pyda_core.get_base(l) + off print(f"glibc addr: {hex(addr)}") return addr - + return None class GDBLibMemory(): def __init__(self, proc): self._p = proc - + def is_readable_address(self, addr): try: self._p.read(addr, 1) return True except: return False - + def poi(self, t, addr): v = self._p.read(addr, t.sizeof) # print(f"poi: {hex(addr)} => {v.hex()}") return Value(v).cast(t) - + def u32(self, addr): return int.from_bytes(self._p.read(addr, 4), pyda.arch.endianness()) @@ -86,14 +86,14 @@ def u64(self, addr): def s64(self, addr): return int.from_bytes(self._p.read(addr, 8), pyda.arch.endianness(), signed=True) - + def pvoid(self, addr): assert pyda.arch.ptrsize() == 8 return self.u64(addr) - + def peek(self, addr): return chr(self._p.read(addr, 1)[0]) - + def read(self, addr, size): return self._p.read(addr, size) @@ -102,18 +102,18 @@ def read(self, addr, size): class Page(): def __init__(self, map: pyda.Map) -> None: self._map = map - + @property def end(self): return self._map.end - + @property def start(self): return self._map.start - + def __contains__(self, addr): return self._map.start <= addr < self._map.end - + @property def objfile(self): return self._map.path @@ -133,18 +133,18 @@ def rwx(self): class GDBLibVMMap(): def __init__(self, proc): pass - + def find(self, addr): info = pyda.xinfo(int(addr)) return Page(info) - + def get(self): return [] class GDBLibArch(): def __init__(self, proc): pass - + @property def endian(self): return pyda.arch.endianness() @@ -152,7 +152,7 @@ def endian(self): @property def ptrsize(self): return pyda.arch.ptrsize() - + def __getattr__(self, name): print(f"Arch: {name}") raise AttributeError(f"Arch: {name}") @@ -172,7 +172,7 @@ def patch_pwndbg(pwndbg, proc): class GDBLibConfig(): def __init__(self): self._d = {} - + def __getattr__(self, name): if name == "_d": return super().__getattr__(name) @@ -180,7 +180,7 @@ def __getattr__(self, name): return self._d[name] else: return 0 - + def __setattr__(self, name, value): if name == "_d": super().__setattr__(name, value) @@ -190,7 +190,7 @@ def __setattr__(self, name, value): class GDBRegs(): def __init__(self, proc): self._p = proc - + def __getattr__(self, name): return self._p.regs[name] @@ -199,7 +199,7 @@ def patch_gdblib(gdblib, proc): gdblib.file = GDBLibFile() gdblib.symbol = GDBLibSymbol() gdblib.config = GDBLibConfig() - + old_mem = gdblib.memory gdblib.memory = GDBLibMemory(proc) gdblib.memory.string = old_mem.string diff --git a/lib/pyda/process.py b/lib/pyda/process.py index 62add18..3cee308 100644 --- a/lib/pyda/process.py +++ b/lib/pyda/process.py @@ -20,17 +20,17 @@ def __init__(self, handle, io=False): self._registered_syscall_pre_hook = False self._registered_syscall_post_hook = False self._has_run = False - + def _hook_dispatch(self, addr): for h in self._hooks[addr]: h(self) - + def _syscall_pre_hook_dispatch(self, syscall_num): if syscall_num in self._syscall_pre_hooks: results = [] for h in self._syscall_pre_hooks[syscall_num]: results.append(h(self, syscall_num)) - + if False in results and True in results: raise RuntimeError("Cannot have mixed return values from syscall pre-hooks") elif False in results: @@ -51,14 +51,14 @@ def hook(self, addr, callback): self._hooks[addr] = [callback] else: self._hooks[addr].append(callback) - + def unhook(self, addr, callback=None): self._hooks[addr] = [c for c in self._hooks[addr] if c != callback] if callback is None or len(self._hooks[addr]) == 0: del self._hooks[addr] self._p.unregister_hook(addr) - + def hook_after_call(self, addr, callback): def call_hook(p): retaddr = int.from_bytes(p.read(p.regs.rsp, 8), "little") @@ -69,7 +69,7 @@ def after_call_hook(p): self.hook(retaddr, after_call_hook) self.hook(addr, call_hook) - + def syscall_pre(self, syscall_num, callback): if self._has_run: raise RuntimeError("Cannot add syscall hooks after process has started") @@ -95,16 +95,16 @@ def syscall_post(self, syscall_num, callback): self._syscall_post_hooks[syscall_num] = [callback] else: self._syscall_post_hooks[syscall_num].append(callback) - + def set_thread_entry(self, callback): self._p.set_thread_init_hook(lambda p: callback(self)) - + def read(self, addr, size): return self._p.read(addr, size) def write(self, addr, data): return self._p.write(addr, data) - + def __getattr__(self, name): # TODO: Move these into CPython extension? if name == "regs": @@ -117,13 +117,13 @@ def __getattr__(self, name): return self._p.get_main_module() raise AttributeError(f"Invalid attribute '{name}'. Did you mean 'regs.{name}'?") - + def __setattr__(self, name, value): if not name.startswith("_") and name not in ["timeout", "buffer", "closed"]: raise AttributeError(f"Cannot set attribute '{name}'") - + super().__setattr__(name, value) - + def run(self): self._has_run = True self._p.run() @@ -131,19 +131,19 @@ def run(self): def run_until(self, addr): self._has_run = True self._p.run_until_pc(addr) - - + + @property def tid(self): # This returns the thread id of the currently executing thread return pyda_core.get_current_thread_id() - + # Jumps to "start" and runs until "end" is reached # NOTE: This cannot be used from hooks def run_from_to(self, start, end): self.regs.rip = start self.run_until(end) - + # Returns a function that calls into (instrumented) target code # NOTE: This cannot be used from hooks def callable(self, addr): @@ -186,7 +186,7 @@ def call(*args): self._p.pop_state() return call - + def set_regs_for_call_linux_x86(p, args): if len(args) > 6: raise NotImplementedError(">6 args not supported yet") @@ -220,9 +220,9 @@ def __getitem__(self, name): if val is not None: return val - + raise AttributeError(f"Invalid register name '{name}'") - + def __setitem__(self, name, value): reg_id = getattr(pyda_core, "REG_"+name.upper(), None) if reg_id: @@ -232,7 +232,7 @@ def __setitem__(self, name, value): def __getattr__(self, name): return self[name] - + def __setattr__(self, name, value): if name != "_p": self[name] = value @@ -250,7 +250,7 @@ def __getitem__(self, key): step = key.step if step is not None and step != 1: raise ValueError("ProcessMemory: Step must be 1") - + if stop is not None: return self._p.read(start, stop - start) else: @@ -261,11 +261,11 @@ def __getitem__(self, key): class ProcessMaps(): def __init__(self, p): self._p = p - + def __getitem__(self, key): return Map(vaddr=pyda_core.get_base(key), size=0, path=key, perms=None) - + @dataclass class Map: vaddr: int @@ -284,17 +284,17 @@ def start(self): @property def end(self): return self.base + self.size - + @property def executable(self): return self.perms & 1 - + @property def writable(self): return self.perms & 2 - + @property def readable(self): return self.perms & 4 - + diff --git a/lib/pyda/tube.py b/lib/pyda/tube.py index 7697251..04a541f 100644 --- a/lib/pyda/tube.py +++ b/lib/pyda/tube.py @@ -44,7 +44,7 @@ def recv_raw(self, numb, *a): if self.closed["recv"]: raise EOFError - + if len(a) > 0: raise NotImplementedError("recv_raw() with flags not implemented") @@ -148,7 +148,7 @@ def fileno(self): def shutdown_raw(self, direction): pass - + # This code is taken from pwnlib.tubes def interactive(self, prompt=term.text.bold_red('$') + ' '): if not self._captured: