summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hypodermic/memory.py8
-rw-r--r--hypodermic/process.py129
-rw-r--r--setup.py2
-rw-r--r--wrapper/ptrace.c72
4 files changed, 205 insertions, 6 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.
diff --git a/setup.py b/setup.py
index 58ca457..4788a81 100644
--- a/setup.py
+++ b/setup.py
@@ -30,7 +30,7 @@ setup(
packages=["hypodermic"],
include_package_data=True,
ext_modules=[lib],
- install_requires=[],
+ install_requires=["pyelftools"],
extras_require={},
tests_require=[],
entry_points={"console_scripts": ["hypodermic = hypodermic.main:main"]},
diff --git a/wrapper/ptrace.c b/wrapper/ptrace.c
index 706b13e..7e5dfa8 100644
--- a/wrapper/ptrace.c
+++ b/wrapper/ptrace.c
@@ -84,3 +84,75 @@ int cont(int pid) {
return 0;
}
+
+
+/* user_regs_struct is copied from sys/user.h so that we can debug a
+ 32-bit executable on a 64-bit platform. */
+struct amd64_user_regs_struct {
+ __extension__ unsigned long long r15;
+ __extension__ unsigned long long r14;
+ __extension__ unsigned long long r13;
+ __extension__ unsigned long long r12;
+ __extension__ unsigned long long rbp;
+ __extension__ unsigned long long rbx;
+ __extension__ unsigned long long r11;
+ __extension__ unsigned long long r10;
+ __extension__ unsigned long long r9;
+ __extension__ unsigned long long r8;
+ __extension__ unsigned long long rax;
+ __extension__ unsigned long long rcx;
+ __extension__ unsigned long long rdx;
+ __extension__ unsigned long long rsi;
+ __extension__ unsigned long long rdi;
+ __extension__ unsigned long long orig_rax;
+ __extension__ unsigned long long rip;
+ __extension__ unsigned long long cs;
+ __extension__ unsigned long long eflags;
+ __extension__ unsigned long long rsp;
+ __extension__ unsigned long long ss;
+ __extension__ unsigned long long fs_base;
+ __extension__ unsigned long long gs_base;
+ __extension__ unsigned long long ds;
+ __extension__ unsigned long long es;
+ __extension__ unsigned long long fs;
+ __extension__ unsigned long long gs;
+};
+
+
+struct i386_user_regs_struct {
+ unsigned long ebx;
+ unsigned long ecx;
+ unsigned long edx;
+ unsigned long esi;
+ unsigned long edi;
+ unsigned long ebp;
+ unsigned long eax;
+ unsigned long xds;
+ unsigned long xes;
+ unsigned long xfs;
+ unsigned long xgs;
+ unsigned long orig_eax;
+ unsigned long eip;
+ unsigned long xcs;
+ unsigned long eflags;
+ unsigned long esp;
+ unsigned long xss;
+};
+
+
+unsigned long long getreg64(int pid, int idx) {
+ struct amd64_user_regs_struct regs;
+
+ ptrace(PTRACE_GETREGS, pid, NULL, &regs);
+
+ return ((unsigned long long *) &regs)[idx];
+}
+
+
+unsigned long getreg32(int pid, int idx) {
+ struct i386_user_regs_struct regs;
+
+ ptrace(PTRACE_GETREGS, pid, NULL, &regs);
+
+ return ((unsigned long *) &regs)[idx];
+}