diff options
Diffstat (limited to 'hypodermic')
| -rw-r--r-- | hypodermic/main.py | 20 | ||||
| -rw-r--r-- | hypodermic/process.py | 53 |
2 files changed, 62 insertions, 11 deletions
diff --git a/hypodermic/main.py b/hypodermic/main.py index 74c4486..7ce9be6 100644 --- a/hypodermic/main.py +++ b/hypodermic/main.py @@ -108,6 +108,26 @@ def main(): if args.create: alert("Creating process at path '{}'...".format(args.create)) p = Process(path=args.create) + + shellcode = b"\x48\xc7\xc0\x01\x00\x00\x00\x48\xc7\xc7\x01\x00" + \ + b"\x00\x00\x48\xc7\xc2\x29\x00\x00\x00\x48\x8d\x35" + \ + b"\x00\x00\x00\x00\x48\x81\xc6\x0d\x00\x00\x00\x0f" + \ + b"\x05\x90\x90\x90\xcc\x61\x6d\x64\x36\x34\x20\x4c" + \ + b"\x69\x6e\x75\x78\x20\x73\x79\x73\x5f\x77\x72\x69" + \ + b"\x74\x65\x20\x73\x68\x65\x6c\x6c\x63\x6f\x64\x65" + \ + b"\x20\x62\x79\x20\x4a\x61\x6b\x6f\x62\x0a" + + old_rip = p.get_register("rip") + alert("%rip at {}".format(hex(old_rip))) + old_code = p.read_bytes(old_rip, len(shellcode)) + p.write_bytes(old_rip, shellcode) + while p.read_bytes(p.get_register("rip"), 1) != b'\xcc': + p.single_step() + alert("Hit breakpoint!") + p.write_bytes(old_rip, old_code) + p.set_register("rip", old_rip) + alert("%rip reset to {}".format(hex(p.get_register("rip")))) + p.continue_until_haulted() else: alert("Attaching to process with pid {}...".format(args.attach)) p = Process(pid=args.attach) diff --git a/hypodermic/process.py b/hypodermic/process.py index 8307e08..c8a2823 100644 --- a/hypodermic/process.py +++ b/hypodermic/process.py @@ -21,11 +21,9 @@ import ctypes import os.path import re -from elftools.elf.elffile import ELFFile - from hypodermic.memory import Region, maps -AMD64_INDICES = { +_AMD64_INDICES = { "r15": 0, "r14": 1, "r13": 2, @@ -55,7 +53,7 @@ AMD64_INDICES = { "gs": 26 } -I386_INDICES = { +_I386_INDICES = { "ebx": 0, "ecx": 1, "edx": 2, @@ -136,7 +134,9 @@ class Process(object): self._attach = self._so.attach self._detach = self._so.detach self._cont = self._so.cont + self._step = self._so.step self._isamd64 = self._so.is_amd64 + self._setreg = self._so.setreg self._getreg = self._so.getreg self._getreg.restype = ctypes.c_ulonglong @@ -158,6 +158,15 @@ class Process(object): if self._cont(ctypes.c_int(self.pid)): raise OSError("Could not continue") + def single_step(self): + """Execute a single instruction. + + Raises: + OSError: If the process cannot be put into single step mode. + """ + if self._step(ctypes.c_int(self.pid)): + raise OSError("Could not continue") + def write_bytes(self, address: int, src: bytes) -> int: """Writes data into process memory. @@ -210,24 +219,46 @@ class Process(object): """Returns the value of the given register. Note: - Registers are tied to the host processor, not the target - processor. For example, a 32-bit ELF will still have 64-bit - registers on 64-bit Linux. + Registers names are tied to the host processor, not the + target processor. For example, a 32-bit ELF will still have + 64-bit registers on 64-bit Linux. It would be wise to query + the `arch` property of the Process object. Args: reg (str): The register to inspect. (e.g. "rax") Returns: An integer representing the value of the register. - """ - regs = AMD64_INDICES if self._isamd64 else I386_INDICES + regs = _AMD64_INDICES if self._isamd64 else _I386_INDICES if reg not in regs: raise ValueError("{} is not a valid register".format(reg)) return self._getreg(self.pid, regs.get(reg)) + def set_register(self, reg: str, val: int): + """Sets the value of the given register. + + Note: + Registers names are tied to the host processor, not the + target processor. For example, a 32-bit ELF will still have + 64-bit registers on 64-bit Linux. It would be wise to query + the `arch` property of the Process object. + + Args: + reg (str): The register to modify. (e.g. "rax") + val (int): The new value for the register. + """ + regs = _AMD64_INDICES if self._isamd64 else _I386_INDICES + + if reg not in regs: + raise ValueError("{} is not a valid register".format(reg)) + + if self._isamd64: + return self._setreg(self.pid, regs.get(reg), ctypes.c_ulonglong(val)) + return self._setreg(self.pid, regs.get(reg), ctypes.c_ulong(val)) + @property def arch(self) -> str: """Returns the architecture of the host processor. @@ -235,8 +266,8 @@ class Process(object): Note: The architecture of the host platform is not necessarily the architecture of the target executable. However, this - value will accurately represent how registers should be - addressed. + value will accurately represent which registers are + available. Returns: A string representing the host processor. As of now, only |