summaryrefslogtreecommitdiff
path: root/hypodermic/process.py
diff options
context:
space:
mode:
Diffstat (limited to 'hypodermic/process.py')
-rw-r--r--hypodermic/process.py129
1 files changed, 128 insertions, 1 deletions
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.