summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob <jakob@memeware.net>2017-09-03 16:19:33 -0400
committerJakob <jakob@memeware.net>2017-09-03 16:19:33 -0400
commit8927b31b8ef6334a5efd28ea779db5a58ac5d445 (patch)
tree408a4a485ba00dfffe04d4ab4d0b8b4638183ab9
parent6f0a28b09a670371b22658a295403ab2573ed993 (diff)
Implemented basic machine code injection.
-rw-r--r--hypodermic/main.py20
-rw-r--r--hypodermic/process.py53
-rw-r--r--wrapper/ptrace.c32
3 files changed, 94 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
diff --git a/wrapper/ptrace.c b/wrapper/ptrace.c
index 8517519..706fd4e 100644
--- a/wrapper/ptrace.c
+++ b/wrapper/ptrace.c
@@ -81,6 +81,18 @@ int cont(int pid) {
return -1;
}
+ waitpid(pid, &s, WNOHANG);
+ return 0;
+}
+
+
+int step(int pid) {
+ int s;
+
+ if ((ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL)) < 0) {
+ return -1;
+ }
+
while (!WIFSTOPPED(s)) {
waitpid(pid, &s, WNOHANG);
}
@@ -108,6 +120,16 @@ unsigned long long getreg(int pid, int idx) {
return ((unsigned long long *) &regs)[idx];
}
+
+void setreg(int pid, int idx, unsigned long long value) {
+ struct user_regs_struct regs;
+
+ ptrace(PTRACE_GETREGS, pid, NULL, &regs);
+
+ ((unsigned long long *) &regs)[idx] = value;
+
+ ptrace(PTRACE_SETREGS, pid, NULL, &regs);
+}
#else
unsigned long getreg(int pid, int idx) {
struct user_regs_struct regs;
@@ -116,4 +138,14 @@ unsigned long getreg(int pid, int idx) {
return ((unsigned long *) &regs)[idx];
}
+
+void setreg(int pid, int idx, unsigned long value) {
+ struct user_regs_struct regs;
+
+ ptrace(PTRACE_GETREGS, pid, NULL, &regs);
+
+ ((unsigned long *) &regs)[idx] = value;
+
+ ptrace(PTRACE_SETREGS, pid, NULL, &regs);
+}
#endif