summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjakob <jakob@memeware.net>2017-10-22 15:01:36 -0400
committerjakob <jakob@memeware.net>2017-10-22 15:01:36 -0400
commitebe7dd1682f79edae434b3440516f694b43bee79 (patch)
tree361e9e1b0ad67f47dc46ace4b2a5eb41bf5341f8
parent33542bda8ee6d0a75c89522daf1c20d580894a01 (diff)
Initial calling to internal _dl_openHEADmaster
-rw-r--r--README.md10
-rw-r--r--hypodermic/process.py85
-rw-r--r--hypodermic/shellcode.py40
3 files changed, 73 insertions, 62 deletions
diff --git a/README.md b/README.md
index 9c48ceb..4a4588e 100644
--- a/README.md
+++ b/README.md
@@ -14,9 +14,9 @@ redirected. There have been a few attempts at this in the past, such as
This is oftentimes unsuccessful, being very dependent upon how glibc was
compiled.
-The point of Hypodermic is to find a means of injecting a dynamic library into
+The purpose of Hypodermic is to find a means of injecting a dynamic library into
any Linux executable, even ones that are statically-linked, and transferring
-this method over to [PINCE][2] when it is stable enough. The current goal is the
+this code into [PINCE][2] when it becomes stable enough. The current goal is the
ability to inject an internal cheat into Counter-Strike: Global Offensive, such
as [AimTux][3]. This will signal that the method has reached a point of
viability.
@@ -33,9 +33,9 @@ routines. This did not work, as the process of loading an ELF library into
memory is far more complicated than calling mmap(2) on the file.
The second iteration also involves injecting code into the inferior process, but
-instead maps the Linux runtime linker into memory, if it is not already there,
-and utilizes the internal _dl_open routine. This is difficult, as it means
-mapping it the way the kernel would to ensure proper initialization of the RTLD.
+it instead maps the Linux runtime linker into memory, if it is not already
+there, and utilizes the internal `_dl_open` routine. This is difficult, as it
+requires mapping it the way the kernel would to ensure proper initialization.
## Important Resources
diff --git a/hypodermic/process.py b/hypodermic/process.py
index 35b8c1d..4740062 100644
--- a/hypodermic/process.py
+++ b/hypodermic/process.py
@@ -456,20 +456,71 @@ class Process(object):
if not self.rtld:
raise OSError("Process does not have a usable RTLD")
+ self.path_caller_check()
if self.arch == "x64":
+ ret = self.get_register("rip")
old_rax = self.get_register("rax")
- self.run_code(dlopen_shellcode(self.rtld_dl_open_addr, path),
- preserve=["rax"])
+ shellcode = dlopen_shellcode(self.rtld_dlsym("_dl_open"), ret, path)
+ self.run_code(shellcode, preserve=["rax"])
addr = self.get_register("rax")
self.set_register("rax", old_rax)
else:
+ ret = self.get_register("eip")
old_eax = self.get_register("eax")
- self.run_code(dlopen_shellcode(self.rtld_dl_open_addr, path,
- arch="i386"), preserve=["eax"])
+ shellcode = dlopen_shellcode(self.rtld_dlsym("_dl_open"), ret, path,
+ arch="i386")
+ self.run_code(shellcode, preserve=["eax"])
addr = self.get_register("eax")
self.set_register("eax", old_eax)
return addr
+ def patch_caller_check(self):
+ """Patches out a caller check security measure in the RTLD.
+
+ Note:
+ This is requried before making any invocations to `dlopen`.
+
+ Raises:
+ OSError: If the process either has no RTLD, or the caller
+ check cannot be found.
+ """
+ if not self.rtld:
+ raise OSError("Process does not have a usable RTLD")
+
+ if self.arch == "x64":
+ caller_check = self.rtld_dlsym("_dl_check_caller")
+ shellcode = assemble("movq $0x00, %rax; ret;")
+ self.write_bytes(caller_check, shellcode)
+ else:
+ caller_check = self.rtld_dlsym("_dl_check_caller")
+ shellcode = assemble("movl $0x00, %eax; ret;")
+ self.write_bytes(caller_check, shellcode)
+
+ def rtld_dlsym(self, sym: str) -> int:
+ """Obtain the absolute address of an RTLD symbol in main memory.
+
+ Raises:
+ OSError: If either the process has no instance of the RTLD,
+ or the RTLD lacks sufficient symbols.
+
+ Returns:
+ An integer containing the address.
+ """
+ if self.rtld is None:
+ raise OSError("Process has no RTLD instance")
+
+ with open(self.rtld.path, "rb") as rtld:
+ elf = ELFFile(rtld)
+ symtab = elf.get_section_by_name(".symtab")
+
+ if not isinstance(symtab, SymbolTableSection):
+ raise OSError("RTLD has no usable symbol table")
+
+ res = symtab.get_symbol_by_name(sym)
+ if len(res) < 1:
+ raise OSError("RTLD has no {} symbol".format(sym))
+ return self.rtld.start + res[0].entry.st_value
+
def page_start(self, addr: int) -> int:
return addr & ~(self.page_size - 1)
@@ -501,32 +552,6 @@ class Process(object):
return "x64" if self._isamd64 else "x86"
@property
- def rtld_dl_open_addr(self) -> int:
- """Obtain the absolute address of _dl_open in main memory.
-
- Raises:
- OSError: If either the process has no instance of the RTLD,
- or the RTLD lacks sufficient symbols.
-
- Returns:
- An integer containing the address.
- """
- if self.rtld is None:
- raise OSError("Process has no RTLD instance")
-
- with open(self.rtld.path, "rb") as rtld:
- elf = ELFFile(rtld)
- symtab = elf.get_section_by_name(".symtab")
-
- if not isinstance(symtab, SymbolTableSection):
- raise OSError("RTLD has no usable symbol table")
-
- res = symtab.get_symbol_by_name("_dl_open")
- if len(res) < 1:
- raise OSError("RTLD has no _dl_open symbol")
- return self.rtld.start + res[0].entry.st_value
-
- @property
def maps(self) -> list:
"""Obtain the process' memory map.
diff --git a/hypodermic/shellcode.py b/hypodermic/shellcode.py
index 114a4bc..38aa841 100644
--- a/hypodermic/shellcode.py
+++ b/hypodermic/shellcode.py
@@ -164,12 +164,12 @@ def munmap_shellcode(addr=0, size=0, arch="amd64"):
return assemble(asm, arch)
-# FIXME: Relative addressing is untested in i386.
-def dlopen_shellcode(addr: int, path: str, arch="amd64"):
+# TODO: i386 not implemented.
+def dlopen_shellcode(dlopen: int, ret: int, path: str, arch="amd64"):
"""Generates shellcode to invoke _dl_open in the RTLD.
Args:
- addr (int): The absolute address of _dl_open.
+ dlopen (int): The absolute address of _dl_open.
path (str): The path of the library to open.
Returns:
@@ -180,29 +180,15 @@ def dlopen_shellcode(addr: int, path: str, arch="amd64"):
"__path:" \
" .asciz \"{}\";" \
"__path_end:" \
- " leaq (%rip), %rdi;" \
- " subq $. - __path, %rdi;" \
- " movq $0x80000101, %rsi;" \
- " movq $0x00, %rdx;" \
- " movq $0x00, %rcx;" \
- " movq $0x00, %r8;" \
- " movq $0x00, %r9;" \
- " pushq $0x00;" \
- " callq ${};".format(path, addr)
+ " leaq (%rip), %rdi;" \
+ " subq $. - __path, %rdi;" \
+ " movq $0x80000101, %rsi;" \
+ " movq ${}, %rdx;" \
+ " movq $0xfffffffffffffffe, %rcx;" \
+ " movq $0x00, %r8;" \
+ " movq $0x00, %r9;" \
+ " movq $0x00, %r10;" \
+ " callq ${};".format(path, ret, dlopen)
else:
- asm = " jmp __path_end;" \
- "__path:" \
- " .asciz \"{}\";" \
- "__path_end:" \
- " call $. + 5;" \
- " popl %ebx;" \
- " subl $. - 4 - __path, %ebx;" \
- " pushl %ebx;" \
- " pushl $0x80000101;" \
- " pushl $0x00;" \
- " pushl $0x00;" \
- " pushl $0x00;" \
- " pushl $0x00;" \
- " pushl $0x00;" \
- " calll ${};".format(path, addr)
+ asm = ""
return assemble(asm, arch)