diff options
Diffstat (limited to 'hypodermic')
| -rw-r--r-- | hypodermic/memory.py | 8 | ||||
| -rw-r--r-- | hypodermic/process.py | 129 |
2 files changed, 132 insertions, 5 deletions
diff --git a/hypodermic/memory.py b/hypodermic/memory.py index 6ffa5d1..0d86855 100644 --- a/hypodermic/memory.py +++ b/hypodermic/memory.py @@ -42,7 +42,7 @@ def parse_device(line: str) -> Device: object. Args: - line(str): The line to parse. + line (str): The line to parse. Returns: The parsed Device object. @@ -56,7 +56,7 @@ def parse_perms(line: str) -> Perms: object. Args: - line(str): The line to parse. + line (str): The line to parse. Returns: The parsed Perms object. @@ -69,7 +69,7 @@ def parse_region(line: str) -> Region: object. Args: - line(str): The line to parse. + line (str): The line to parse. Returns: The parsed Region object. @@ -90,7 +90,7 @@ def maps(pid: int) -> list: Args: pid (int): The pid of the process to get memory mapping - information for. + information for. Raises: TypeError: If the pid argument is not an int. diff --git a/hypodermic/process.py b/hypodermic/process.py index 6fc1519..9a321e0 100644 --- a/hypodermic/process.py +++ b/hypodermic/process.py @@ -21,8 +21,60 @@ import ctypes import os.path import re +from elftools.elf.elffile import ELFFile + from hypodermic.memory import Region, maps +AMD64_INDICES = { + "r15": 0, + "r14": 1, + "r13": 2, + "r12": 3, + "rbp": 4, + "rbx": 5, + "r11": 6, + "r10": 7, + "r9": 8, + "r8": 9, + "rax": 10, + "rcx": 11, + "rdx": 12, + "rsi": 13, + "rdi": 14, + "orig_rax": 15, + "rip": 16, + "cs": 17, + "eflags": 18, + "rsp": 19, + "ss": 20, + "fs_base": 21, + "gs_base": 22, + "ds": 23, + "es": 24, + "fs": 25, + "gs": 26 +} + +I386_INDICES = { + "ebx": 0, + "ecx": 1, + "edx": 2, + "esi": 3, + "edi": 4, + "ebp": 5, + "eax": 6, + "xds": 7, + "xes": 8, + "xfs": 9, + "xgs": 10, + "orig_eax": 11, + "eip": 12, + "xcs": 13, + "eflags": 14, + "esp": 15, + "xss": 16 +} + class Process(object): """Process attached via ptrace. @@ -84,6 +136,10 @@ class Process(object): self._attach = self._so.attach self._detach = self._so.detach self._cont = self._so.cont + self._getreg32 = self._so.getreg32 + self._getreg32.restype = ctypes.c_ulong + self._getreg64 = self._so.getreg64 + self._getreg64.restype = ctypes.c_ulonglong def detach(self): """Explicitly detaches from the process. @@ -94,7 +150,7 @@ class Process(object): if not self._is_parent and self._detach(ctypes.c_int(self.pid)): raise OSError("Could not detach from pid {}".format(self.pid)) - def cont(self): + def continue_until_haulted(self): """Continues until the program is haulted. Raises: @@ -103,6 +159,77 @@ class Process(object): if self._cont(ctypes.c_int(self.pid)): raise OSError("Could not continue") + def write_bytes(self, address: int, src: bytes) -> int: + """Writes data into process memory. + + Args: + address (int): The address at which to write the bytes. + src (:obj:`bytes`): The bytes to write. + + Raises: + ValueError: If the address does not exist in the process + address space. + + Returns: + The number of bytes written. + """ + for region in self.maps: + if address >= region.start and address + len(src) < region.end: + break + else: + raise ValueError("address was not in the process address space") + + with open("/proc/{}/mem".format(self.pid), "wb") as mem: + mem.seek(address) + return mem.write(src) + + def read_bytes(self, address: int, n: int) -> bytes: + """Reads data from process memory. + + Args: + address (int): The address at which to read from. + n (int): The number of bytes to read. + + Raises: + ValueError: If the address does not exist in the process + address space. + + Returns: + A `bytes` object containing the bytes read. + """ + for region in self.maps: + if address >= region.start and address + n < region.end: + break + else: + raise ValueError("address was not in the process address space") + + with open("/proc/{}/mem".format(self.pid), "rb") as mem: + mem.seek(address) + return mem.read(n) + + def get_register(self, reg: str) -> int: + """Returns the value of the given register. + + Args: + reg (str): The register to inspect. (e.g. "rax") + + Returns: + An integer representing the value of the register. + """ + regs = AMD64_INDICES if self.arch == "x64" else I386_INDICES + + if reg not in regs: + raise ValueError("{} is not a valid register".format(reg)) + + if self.arch == "x64": + return self._getreg64(self.pid, regs.get(reg)) + return self._getreg32(self.pid, regs.get(reg)) + + @property + def arch(self) -> str: + with open("/proc/{}/exe".format(self.pid), "rb") as elf: + return ELFFile(elf).get_machine_arch() + @property def maps(self) -> list: """Obtain the process' memory map. |