summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan200101 <sentrycraft123@gmail.com>2023-09-04 18:11:50 +0200
committerJan200101 <sentrycraft123@gmail.com>2023-09-04 18:11:50 +0200
commitad0ebfa7c2b9149311142db7717416538dd1fc9b (patch)
tree2f76ac2b4693dbdabd6406e4231fe52dab3a2990
downloadinject_so-ad0ebfa7c2b9149311142db7717416538dd1fc9b.tar.gz
inject_so-ad0ebfa7c2b9149311142db7717416538dd1fc9b.zip
Initial commit
-rw-r--r--.gitignore2
-rw-r--r--CMakeLists.txt16
-rw-r--r--src/CMakeLists.txt22
-rw-r--r--src/arch.c42
-rw-r--r--src/arch.h9
-rw-r--r--src/fs.c202
-rw-r--r--src/fs.h29
-rw-r--r--src/inject.c439
-rw-r--r--src/inject.h10
-rw-r--r--src/main.c52
-rw-r--r--src/memory.c115
-rw-r--r--src/memory.h13
-rw-r--r--src/module.c173
-rw-r--r--src/module.h19
-rw-r--r--src/proc.c14
-rw-r--r--src/proc.h8
16 files changed, 1165 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..babfa07
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+build*
+*.so
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..288ba2d
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.14)
+
+if(CMAKE_POLICY_DEFAULT_CMP0017 OR CMAKE_POLICY_DEFAULT_CMP0020)
+ # touch these to remove warnings
+endif()
+
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE "Release" CACHE STRING
+ "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE)
+endif()
+
+project(inject_so VERSION 0.0.0 LANGUAGES C)
+
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
+
+add_subdirectory(src)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..0a31644
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,22 @@
+
+# get all modules first
+
+list(APPEND
+ HOOK_SOURCES
+ ${CMAKE_CURRENT_SOURCE_DIR}/arch.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/arch.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/fs.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/fs.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/inject.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/inject.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/main.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/memory.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/memory.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/module.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/module.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/proc.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/proc.h
+)
+
+add_executable(hook ${HOOK_SOURCES})
+target_compile_options(hook PUBLIC ${CFLAGS})
diff --git a/src/arch.c b/src/arch.c
new file mode 100644
index 0000000..19c37e9
--- /dev/null
+++ b/src/arch.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <limits.h>
+#include <sys/types.h>
+
+#include "arch.h"
+
+unsigned char getProcessBits(pid_t pid)
+{
+ char exe_path[PATH_MAX];
+ snprintf(exe_path, sizeof(exe_path), "/proc/%i/exe", pid);
+
+ return getElfBits(exe_path);
+}
+
+unsigned char getElfBits(const char* path)
+{
+ if (!path)
+ return 0;
+
+ FILE* fd = fopen(path, "rb");
+ if (!fd)
+ return 0;
+
+ char indent[5];
+ fread(indent, sizeof(*indent), sizeof(indent), fd);
+ fclose(fd);
+
+ if (indent[0] != 0x7f)
+ return 0;
+
+ if (indent[1] != 'E' ||
+ indent[2] != 'L' ||
+ indent[3] != 'F')
+ return 0;
+
+ if (indent[4] == 1)
+ return 32;
+ else if (indent[4] == 2)
+ return 64;
+
+ return 0;
+} \ No newline at end of file
diff --git a/src/arch.h b/src/arch.h
new file mode 100644
index 0000000..8e81ad8
--- /dev/null
+++ b/src/arch.h
@@ -0,0 +1,9 @@
+#ifndef ARCH_H
+#define ARCH_H
+
+#include <sys/types.h>
+
+unsigned char getProcessBits(pid_t pid);
+unsigned char getElfBits(const char* path);
+
+#endif \ No newline at end of file
diff --git a/src/fs.c b/src/fs.c
new file mode 100644
index 0000000..a7f7888
--- /dev/null
+++ b/src/fs.c
@@ -0,0 +1,202 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <shlwapi.h>
+#endif
+
+#include "fs.h"
+
+#ifdef _WIN32
+#define mkdir(path, perm) mkdir(path)
+#endif
+
+static struct stat getStat(const char* path)
+{
+ // fill with 0s by default in the case stat fails
+ struct stat sb = {0};
+
+ // the return value signifies if stat failes (e.g. file not found)
+ // unimportant for us if it fails it won't touch sb
+ stat(path, &sb);
+
+ return sb;
+}
+
+int isFile(const char* path)
+{
+ struct stat sb = getStat(path);
+
+#ifndef _WIN32
+ if (S_ISLNK(sb.st_mode))
+ {
+ char buf[PATH_MAX];
+ readlink(path, buf, sizeof(buf));
+
+ return isFile(buf);
+ }
+#endif
+ return S_ISREG(sb.st_mode);
+}
+
+int isDir(const char* path)
+{
+ struct stat sb = getStat(path);
+
+#ifndef _WIN32
+ if (S_ISLNK(sb.st_mode))
+ {
+ char buf[PATH_MAX];
+ readlink(path, buf, sizeof(buf));
+
+ return isDir(buf);
+ }
+#endif
+ return S_ISDIR(sb.st_mode);
+}
+
+int isRelativePath(const char* path)
+{
+ if (!path)
+ return 0;
+ else if (*path == *OS_PATH_SEP)
+ return 0;
+#ifdef _WIN32
+ else if (!PathIsRelativeA(path))
+ return 0;
+#endif
+ return 1;
+}
+
+int leavesRelativePath(const char* path)
+{
+ if (!path || !isRelativePath(path))
+ return 0;
+
+ int depth = 0;
+ const char* head = path;
+ const char* tail = head;
+
+ while (*tail)
+ {
+ ++tail;
+ if (*tail == *OS_PATH_SEP || *tail == '\0')
+ {
+ size_t size = (size_t)(tail-head);
+ if (!size)
+ continue;
+ else if (!strncmp(head, "..", size))
+ depth -= 1;
+ else if (strncmp(head, ".", size))
+ depth += 1;
+
+ if (depth < 0)
+ return 1;
+
+ head = tail + 1;
+ }
+ }
+ return 0;
+}
+
+char* normalizeUnixPath(char* path)
+{
+ char* head = path;
+ if (head)
+ {
+ while (*head)
+ {
+ if (*head == '/')
+ *head = *OS_PATH_SEP;
+
+ ++head;
+ }
+ }
+
+ return path;
+}
+
+int makeDir(const char* path)
+{
+ char pathcpy[PATH_MAX];
+ char *index;
+
+ strncpy(pathcpy, path, PATH_MAX-1); // make a mutable copy of the path
+
+ for(index = pathcpy+1; *index; ++index)
+ {
+
+ if (*index == *OS_PATH_SEP)
+ {
+ *index = '\0';
+
+ if (mkdir(pathcpy, 0755) != 0)
+ {
+ if (errno != EEXIST)
+ return -1;
+ }
+
+ *index = *OS_PATH_SEP;
+ }
+ }
+
+ return mkdir(path, 0755);
+}
+
+int removeDir(const char* path)
+{
+ DIR *d = opendir(path);
+ size_t path_len = strlen(path);
+ int r = -1;
+
+ if (d) {
+ struct dirent *p;
+
+ r = 0;
+ while (!r && (p = readdir(d))) {
+ char *buf;
+ size_t len;
+
+ // Skip the names "." and ".." as we don't want to recurse on them.
+ if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, ".."))
+ continue;
+
+ len = path_len + strlen(p->d_name) + 2;
+ buf = malloc(len);
+
+ if (buf) {
+ struct stat statbuf = {0};
+
+ snprintf(buf, len, "%s/%s", path, p->d_name);
+ if (!stat(buf, &statbuf)) {
+ if (S_ISDIR(statbuf.st_mode))
+ r = removeDir(buf);
+#ifndef _WIN32
+ else if (S_ISLNK(statbuf.st_mode))
+ r = unlink(buf);
+#endif
+ else
+ r = remove(buf);
+ }
+ else // it is very likely that we found a dangling symlink which is not detected by stat
+ {
+ r = unlink(buf);
+ }
+ free(buf);
+ }
+ }
+ closedir(d);
+ }
+
+ if (!r)
+ r = rmdir(path);
+
+ return r;
+}
diff --git a/src/fs.h b/src/fs.h
new file mode 100644
index 0000000..109f5ed
--- /dev/null
+++ b/src/fs.h
@@ -0,0 +1,29 @@
+#ifndef FS_H
+#define FS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WIN32
+#define OS_PATH_SEP "\\"
+#else
+#define OS_PATH_SEP "/"
+#endif
+
+int isFile(const char*);
+int isDir(const char*);
+
+int isRelativePath(const char*);
+int leavesRelativePath(const char*);
+
+char* normalizeUnixPath(char* path);
+
+int makeDir(const char*);
+int removeDir(const char*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/src/inject.c b/src/inject.c
new file mode 100644
index 0000000..96012b6
--- /dev/null
+++ b/src/inject.c
@@ -0,0 +1,439 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <sys/ptrace.h>
+#include <sys/mman.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <elf.h>
+
+#include "inject.h"
+#include "memory.h"
+#include "module.h"
+#include "arch.h"
+
+#define Elf64W(type) Elf64_ ## type
+#define Elf32W(type) Elf32_ ## type
+
+#define Elf64Ehdr Elf64W(Ehdr)
+#define Elf32Ehdr Elf32W(Ehdr)
+
+#define Elf64Shdr Elf64W(Shdr)
+#define Elf32Shdr Elf32W(Shdr)
+
+#define Elf64Sym Elf64W(Sym)
+#define Elf32Sym Elf32W(Sym)
+
+static uintptr_t getSymbolOffset(const char* elf_path, const char* symbol_name)
+{
+ if (!elf_path)
+ return 0;
+
+ unsigned char bits = getElfBits(elf_path);
+
+ FILE* fd = fopen(elf_path, "rb");
+ fseek(fd, 0, SEEK_END);
+ size_t elf_size = (size_t) ftell(fd);
+ rewind(fd);
+
+ char* elf_body = malloc(elf_size);
+ fread(elf_body, 1, elf_size, fd);
+ fclose(fd);
+
+ if (bits == 64)
+ {
+ Elf64Ehdr* header = (Elf64Ehdr*)elf_body;
+
+ Elf64Shdr* section = (Elf64Shdr*)(elf_body + header->e_shoff);
+ Elf64Shdr* symtab_section = NULL;
+ for (uintptr_t i = 0; i <= header->e_shnum; i++)
+ {
+ if (i == header->e_shnum)
+ return 0;
+
+ if (section[i].sh_type == SHT_SYMTAB)
+ {
+ symtab_section = section+i;
+ break;
+ }
+ }
+
+ Elf64Sym* symtab = (Elf64Sym*)(elf_body + symtab_section->sh_offset);
+ size_t symbol_num = symtab_section->sh_size / symtab_section->sh_entsize;
+ char *symbol_names = (char *)(elf_body + section[symtab_section->sh_link].sh_offset);
+
+ for (size_t j = 0; j < symbol_num; ++j)
+ {
+ char* name = symbol_names + symtab[j].st_name;
+ size_t symbol_name_len = strlen(symbol_name);
+ if (strncmp(name, symbol_name, symbol_name_len))
+ continue;
+ if (name[symbol_name_len] != '\0' && name[symbol_name_len] != '@')
+ continue;
+
+ if (symtab[j].st_value > 0)
+ {
+ uintptr_t value = symtab[j].st_value;
+ free(elf_body);
+ return value;
+ }
+ }
+ }
+ else if (bits == 32)
+ {
+ Elf32Ehdr* header = (Elf32Ehdr*)elf_body;
+
+ Elf32Shdr* section = (Elf32Shdr*)(elf_body + header->e_shoff);
+ Elf32Shdr* symtab_section = NULL;
+ for (uintptr_t i = 0; i <= header->e_shnum; i++)
+ {
+ if (i == header->e_shnum)
+ return 0;
+
+ if (section[i].sh_type == SHT_SYMTAB)
+ {
+ symtab_section = section+i;
+ break;
+ }
+ }
+
+ Elf32Sym* symtab = (Elf32Sym*)(elf_body + symtab_section->sh_offset);
+ size_t symbol_num = symtab_section->sh_size / symtab_section->sh_entsize;
+ char *symbol_names = (char *)(elf_body + section[symtab_section->sh_link].sh_offset);
+
+ for (size_t j = 0; j < symbol_num; ++j)
+ {
+ char* name = symbol_names + symtab[j].st_name;
+ size_t symbol_name_len = strlen(symbol_name);
+ if (strncmp(name, symbol_name, symbol_name_len))
+ continue;
+ if (name[symbol_name_len] != '\0' && name[symbol_name_len] != '@')
+ continue;
+
+ if (symtab[j].st_value > 0)
+ {
+ uintptr_t value = symtab[j].st_value;
+ free(elf_body);
+ return value;
+ }
+ }
+ }
+
+ free(elf_body);
+
+ return 0;
+}
+
+void* inject_syscall(
+ pid_t pid,
+ uintptr_t syscall_n,
+ void* arg0,
+ void* arg1,
+ void* arg2,
+ void* arg3,
+ void* arg4,
+ void* arg5
+){
+ void* ret = (void*)-1;
+ int status;
+ struct user_regs_struct old_regs, regs;
+ void* injection_addr = (void*)-1;
+
+ //This buffer is our payload, which will run a syscall properly on x86/x64
+ unsigned char injection_buf[] =
+ {
+ 0xff, 0xff, // placerholder
+ /* these nops are here because
+ * we're going to write memory using
+ * ptrace, and it always writes the size
+ * of a word, which means we have to make
+ * sure the buffer is long enough
+ */
+ 0x90, //nop
+ 0x90, //nop
+ 0x90, //nop
+ 0x90, //nop
+ 0x90, //nop
+ 0x90 //nop
+ };
+
+ unsigned char bits = getProcessBits(pid);
+ if (bits == 64)
+ {
+ //syscall
+ injection_buf[0] = 0x0f;
+ injection_buf[1] = 0x05;
+ }
+ else if (bits == 32)
+ {
+ //int80 (syscall)
+ injection_buf[0] = 0xcd;
+ injection_buf[1] = 0x80;
+ }
+ else
+ {
+ return NULL;
+ }
+
+ //As ptrace will always write a uintptr_t, let's make sure we're using proper buffers
+ uintptr_t old_data;
+ uintptr_t injection_buffer;
+ memcpy(&injection_buffer, injection_buf, sizeof(injection_buffer));
+
+ //Attach to process using 'PTRACE_ATTACH'
+ ptrace(PTRACE_ATTACH, pid, NULL, NULL);
+ wait(&status);
+
+ /* Get the current registers using 'PTRACE_GETREGS' so that
+ * we can restore the execution later
+ * and also modify the bytes of EIP/RIP
+ */
+
+ ptrace(PTRACE_GETREGS, pid, NULL, &old_regs);
+ regs = old_regs;
+
+ //Now, let's set up the registers that will be injected into the tracee
+
+#if defined(__i386__)
+ regs.eax = (uintptr_t)syscall_n;
+ regs.ebx = (uintptr_t)arg0;
+ regs.ecx = (uintptr_t)arg1;
+ regs.edx = (uintptr_t)arg2;
+ regs.esi = (uintptr_t)arg3;
+ regs.edi = (uintptr_t)arg4;
+ regs.ebp = (uintptr_t)arg5;
+ injection_addr = (void*)regs.eip;
+#elif defined(__x86_64__)
+ if (bits == 64)
+ {
+ regs.rax = (uintptr_t)syscall_n;
+ regs.rdi = (uintptr_t)arg0;
+ regs.rsi = (uintptr_t)arg1;
+ regs.rdx = (uintptr_t)arg2;
+ regs.r10 = (uintptr_t)arg3;
+ regs.r8 = (uintptr_t)arg4;
+ regs.r9 = (uintptr_t)arg5;
+ injection_addr = (void*)regs.rip;
+ }
+ else if (bits == 32)
+ {
+ regs.rax = (uintptr_t)syscall_n;
+ regs.rbx = (uintptr_t)arg0;
+ regs.rcx = (uintptr_t)arg1;
+ regs.rdx = (uintptr_t)arg2;
+ regs.rsi = (uintptr_t)arg3;
+ regs.rdi = (uintptr_t)arg4;
+ regs.rbp = (uintptr_t)arg5;
+ injection_addr = (void*)regs.rip;
+ }
+#endif
+
+ //Let's store the buffer at EIP/RIP that we're going to modify into 'old_data' using 'PTRACE_PEEKDATA'
+ old_data = (uintptr_t)ptrace(PTRACE_PEEKDATA, pid, injection_addr, NULL);
+
+ //Let's write our payload into the EIP/RIP of the target process using 'PTRACE_POKEDATA'
+ ptrace(PTRACE_POKEDATA, pid, injection_addr, injection_buffer);
+
+ //Let's inject our modified registers into the target process using 'PTRACE_SETREGS'
+ ptrace(PTRACE_SETREGS, pid, NULL, &regs);
+
+ //Let's run a single step in the target process (execute one assembly instruction)
+ ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);
+ waitpid(pid, &status, WSTOPPED); //Wait for the instruction to run
+
+ //Let's get the registers after the syscall to store the return value
+ ptrace(PTRACE_GETREGS, pid, NULL, &regs);
+#if defined(__i386__)
+ ret = (void*)regs.eax;
+#elif defined(__x86_64__)
+ ret = (void*)regs.rax;
+#endif
+
+ long long ret_int = (long long)ret;
+
+ if (ret_int < 0)
+ fprintf(stderr, "syscall error: %s\n", strerror((int)-ret_int));
+
+ //Let's write the old data at EIP/RIP
+ ptrace(PTRACE_POKEDATA, pid, (void*)injection_addr, old_data);
+
+ //Let's restore the old registers to continue the normal execution
+ ptrace(PTRACE_SETREGS, pid, NULL, &old_regs);
+ ptrace(PTRACE_DETACH, pid, NULL, NULL); //Detach and continue the execution
+
+ return ret;
+}
+
+
+/**
+ * Return values:
+ * 1 already loaded
+ * 2 arch error
+ */
+int load_library(pid_t pid, char* lib_path)
+{
+ /* Let's get the address of the 'libc_dlopen_mode' of the target process
+ * and store it on 'dlopen_ex' by loading the LIBC of the target process
+ * on here and then getting the offset of its own '__libc_dlopen_mode'.
+ * Then we sum this offset to the base of the external LIBC module
+ */
+ struct module_s lib_mod = getModule(pid, lib_path);
+ if (lib_mod.size)
+ return 1;
+
+ struct module_s libc_ex = getModule(pid, "libshim.so");
+ uintptr_t offset = getSymbolOffset(libc_ex.path, "dlopen");
+
+ // fallback
+ if (!offset)
+ offset = getSymbolOffset(libc_ex.path, "__libc_dlopen_mode");
+
+ if (!offset)
+ return 2;
+
+
+ //Get the external '__libc_dlopen_mode' by summing the offset to the libc_ex.base
+ void* dlopen_ex = (void*)((uintptr_t)libc_ex.base + offset);
+
+ printf("[?] dlopen found %s@0x%lx(0x%lx)\n", libc_ex.name, dlopen_ex, offset);
+
+ freeModule(&libc_ex);
+
+ //--- Now let's go to the injection part
+
+ int status;
+ struct user_regs_struct old_regs, regs;
+ unsigned char inj_buf_x64[] =
+ {
+ /* On 'x64', we dont have to pass anything to the stack, as we're only
+ * using 2 parameters, which will be stored on RDI (library path address) and
+ * RSI (flags, in this case RTLD_LAZY).
+ * This means we just have to call the __libc_dlopen_mode function, which
+ * will be on RAX.
+ */
+
+ 0xFF, 0xD0, //call rax
+ 0xCC, //int3 (SIGTRAP)
+ };
+
+ unsigned char inj_buf_x86[] =
+ {
+ /* We have to pass the parameters to the stack (in reversed order)
+ * The register 'ebx' will store the library path address and the
+ * register 'ecx' will store the flag (RTLD_LAZY)
+ * After pushing the parameters to the stack, we will call EAX, which
+ * will store the address of '__libc_dlopen_mode'
+ */
+ 0x51, //push ecx
+ 0x53, //push ebx
+ 0xFF, 0xD0, //call eax
+ 0xCC, //int3 (SIGTRAP)
+ };
+
+ unsigned char* inj_buf = NULL;
+ size_t sizeof_inj_buf = 0;
+
+ unsigned char bits = getProcessBits(pid);
+ if (bits == 64)
+ {
+ inj_buf = inj_buf_x64;
+ sizeof_inj_buf = sizeof(inj_buf_x64);
+ }
+ else if (bits == 32)
+ {
+ inj_buf = inj_buf_x86;
+ sizeof_inj_buf = sizeof(inj_buf_x86);
+ }
+ else
+ {
+ fprintf(stderr, "Could not figure out what injection buffer to use\n");
+ return 2;
+ }
+
+ //Let's allocate memory for the payload and the library path
+ size_t lib_path_len = strlen(lib_path) + 1;
+ size_t inj_size = sizeof_inj_buf + lib_path_len;
+ void* inj_addr = allocate_memory(pid, inj_size, PROT_EXEC | PROT_READ | PROT_WRITE);
+ void* path_addr = (void*)((uintptr_t)inj_addr + sizeof_inj_buf);
+
+ //Write the memory to our allocated address
+ write_memory(pid, inj_addr, inj_buf, sizeof_inj_buf);
+ write_memory(pid, path_addr, (void*)lib_path, lib_path_len);
+
+ //Attach to the target process
+ if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0)
+ return 3;
+
+ waitpid(pid, &status, 0);
+
+ //Get the current registers to restore later
+ ptrace(PTRACE_GETREGS, pid, NULL, &old_regs);
+ regs = old_regs;
+
+ //Let's setup the registers according to our payload
+# if defined(__i386__)
+ regs.eax = (long)dlopen_ex;
+ regs.ebx = (long)path_addr;
+ regs.ecx = (long)RTLD_NOW;
+ regs.eip = (long)inj_addr; //The execution will continue from 'inj_addr' (EIP)
+# elif defined(__x86_64__)
+ if (bits == 64)
+ {
+ regs.rax = (uintptr_t)dlopen_ex;
+ regs.rdi = (uintptr_t)path_addr;
+ regs.rsi = (uintptr_t)RTLD_NOW;
+ regs.rip = (uintptr_t)inj_addr; //The execution will continue from 'inj_addr' (RIP)
+ }
+ else if (bits == 32)
+ {
+ regs.rax = (uintptr_t)dlopen_ex;
+ regs.rbx = (uintptr_t)path_addr;
+ regs.rcx = (uintptr_t)RTLD_NOW;
+ regs.rip = (uintptr_t)inj_addr; //The execution will continue from 'inj_addr' (RIP)
+ }
+# endif
+
+ //Inject the modified registers to the target process
+ ptrace(PTRACE_SETREGS, pid, NULL, &regs);
+
+ //Continue the execution
+ ptrace(PTRACE_CONT, pid, NULL, NULL);
+
+ //Wait for the int3 (SIGTRAP) breakpoint
+ waitpid(pid, &status, WSTOPPED);
+
+ //Get the value
+ ptrace(PTRACE_GETREGS, pid, NULL, &regs);
+
+ void* ret;
+#if defined(__i386__)
+ printf("eax %p\n", regs.eax);
+ printf("edx %p\n", regs.edx);
+ ret = (void*)regs.eax;
+#elif defined(__x86_64__)
+ printf("rax %p\n", regs.rax);
+ printf("rdx %p\n", regs.rdx);
+ ret = (void*)regs.rax;
+#endif
+
+ //Set back the old registers
+ ptrace(PTRACE_SETREGS, pid, NULL, &old_regs);
+
+ //Detach from the process and continue the execution
+ ptrace(PTRACE_DETACH, pid, NULL, NULL);
+
+ //Deallocate the memory we allocated for the injection buffer and the library path
+ deallocate_memory(pid, inj_addr, inj_size);
+
+ if (ret)
+ return 0;
+
+ printf("[!] dlopen failed\n");
+ return 3;
+}
+
+
+
diff --git a/src/inject.h b/src/inject.h
new file mode 100644
index 0000000..e82c445
--- /dev/null
+++ b/src/inject.h
@@ -0,0 +1,10 @@
+#ifndef INJECT_H
+#define INJECT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+void* inject_syscall(pid_t pid, uintptr_t syscall_n, void*, void*, void*, void*, void*, void*);
+int load_library(pid_t pid, char* lib_path);
+
+#endif \ No newline at end of file
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..8edfe30
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "fs.h"
+#include "inject.h"
+#include "proc.h"
+
+int main(int argc, char** argv)
+{
+ if (argc < 3)
+ {
+ printf("inject_so [pid] [so files ...]\n");
+ return -1;
+ }
+
+ pid_t pid;
+ char** so_files = argv+2;
+
+ sscanf(argv[1], "%li", &pid);
+
+ if (!process_exists(pid))
+ {
+ printf("%li is not a running process\n", pid);
+ return -1;
+ }
+
+ char so_path[PATH_MAX];
+ char** so_file = so_files;
+ while (*so_file)
+ {
+ if (!isFile(*so_file))
+ {
+ printf("[!] %s is not a file\n", *so_file);
+ //++so_file;
+ //continue;
+ }
+
+ realpath(*so_file, so_path);
+ printf("[*] Injecting %s\n", so_path);
+
+ int ret = load_library(pid, so_path);
+ if (!ret)
+ printf("[*] Success\n");
+ else if (ret == 1)
+ printf("[!] library already loaded\n");
+ else
+ printf("[!] could not load libary\n");
+
+ ++so_file;
+ }
+} \ No newline at end of file
diff --git a/src/memory.c b/src/memory.c
new file mode 100644
index 0000000..eedf728
--- /dev/null
+++ b/src/memory.c
@@ -0,0 +1,115 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <sys/syscall.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#include "memory.h"
+#include "inject.h"
+#include "arch.h"
+
+#define mmap_x64 9
+#define munmap_x64 11
+#define mmap2_x86 192
+#define munmap_x86 91
+
+void read_memory(pid_t pid, void* src, void* dst, size_t size)
+{
+ /*
+ pid = target process id
+ src = address to read from on the target process
+ dst = address to write to on the caller process
+ size = size of the buffer that will be read
+ */
+
+ struct iovec iosrc;
+ struct iovec iodst;
+ iodst.iov_base = dst;
+ iodst.iov_len = size;
+ iosrc.iov_base = src;
+ iosrc.iov_len = size;
+
+ if (process_vm_readv(pid, &iodst, 1, &iosrc, 1, 0) == -1)
+ fprintf(stderr, "process_vm_readv: %s\n", strerror(errno));
+}
+
+void write_memory(pid_t pid, void* dst, void* src, size_t size)
+{
+ /*
+ pid = target process id
+ dst = address to write to on the target process
+ src = address to read from on the caller process
+ size = size of the buffer that will be read
+ */
+
+ struct iovec iosrc;
+ struct iovec iodst;
+ iosrc.iov_base = src;
+ iosrc.iov_len = size;
+ iodst.iov_base = dst;
+ iodst.iov_len = size;
+
+ if(process_vm_writev(pid, &iosrc, 1, &iodst, 1, 0) == -1)
+ fprintf(stderr, "process_vm_writev: %s\n", strerror(errno));
+}
+
+void* allocate_memory(pid_t pid, size_t size, int protection)
+{
+ //mmap template:
+ //void *mmap (void *__addr, size_t __len, int __prot, int __flags, int __fd, __off_t __offset);
+
+ void* ret = (void*)-1;
+
+ unsigned char bits = getProcessBits(pid);
+
+ if (bits == 32)
+ {
+ ret = inject_syscall(
+ pid,
+ mmap2_x86,
+ //arguments
+ (void*)0,
+ (void*)size,
+ (void*)(uintptr_t)protection,
+ (void*)(MAP_ANON | MAP_PRIVATE),
+ (void*)-1,
+ (void*)0
+ );
+ }
+ else if (bits == 64)
+ {
+ ret = inject_syscall(
+ pid,
+ mmap_x64,
+ //arguments
+ (void*)0,
+ (void*)size,
+ (void*)(uintptr_t)protection,
+ (void*)(MAP_ANON | MAP_PRIVATE),
+ (void*)-1,
+ (void*)0
+ );
+ }
+
+ return ret;
+}
+
+void deallocate_memory(pid_t pid, void* src, size_t size)
+{
+ unsigned char bits = getProcessBits(pid);
+ if (bits == 64)
+ inject_syscall(pid, munmap_x64, src, (void*)size, NULL, NULL, NULL, NULL);
+ else if (bits == 32)
+ inject_syscall(pid, munmap_x86, src, (void*)size, NULL, NULL, NULL, NULL);
+}
+
+void* protect_memory(pid_t pid, void* src, size_t size, int protection)
+{
+ //mprotect template
+ //int mprotect (void *__addr, size_t __len, int __prot);
+ return inject_syscall(pid, __NR_mprotect, src, (void*)size, (void*)(uintptr_t)protection, NULL, NULL, NULL);
+}
diff --git a/src/memory.h b/src/memory.h
new file mode 100644
index 0000000..cf1f74c
--- /dev/null
+++ b/src/memory.h
@@ -0,0 +1,13 @@
+#ifndef MEMORY_H
+#define MEMORY_H
+
+#include <sys/types.h>
+
+void read_memory(pid_t pid, void* src, void* dst, size_t size);
+void write_memory(pid_t pid, void* dst, void* src, size_t size);
+
+void* allocate_memory(pid_t pid, size_t size, int protection);
+void deallocate_memory(pid_t pid, void* src, size_t size);
+void* protect_memory(pid_t pid, void* src, size_t size, int protection);
+
+#endif \ No newline at end of file
diff --git a/src/module.c b/src/module.c
new file mode 100644
index 0000000..4381096
--- /dev/null
+++ b/src/module.c
@@ -0,0 +1,173 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include "module.h"
+#include "arch.h"
+
+#define NOTFOUND (size_t)(-1)
+#define PROCMAPS_LINE_MAX_LENGTH (PATH_MAX + 100)
+
+static void* strtoptr(pid_t pid, const char* nptr, char** endptr, int base)
+{
+ unsigned char bits = getProcessBits(pid);
+ if (bits == 64)
+ return (void*)strtoull(nptr, endptr, base);
+ else if (bits == 32)
+ return (void*)strtoul(nptr, endptr, base);
+
+ return NULL;
+
+}
+static size_t find(const char* a, const char* b, uintptr_t offset)
+{
+ const char* pos = a + offset;
+
+ while (*pos && strncmp(pos, b, strlen(b)))
+ ++pos;
+
+ if (!*pos)
+ return NOTFOUND;
+
+ return (size_t)(pos - a);
+}
+
+static size_t rfind(const char* a, const char* b, size_t offset)
+{
+ const char* pos = a + (strlen(a) - strlen(b) - offset);
+
+ while (a <= pos && strncmp(pos, b, strlen(b)))
+ --pos;
+
+ if (pos < a)
+ return NOTFOUND;
+
+ return (size_t)(pos - a);
+}
+
+static char* substr(const char* str, size_t pos, size_t len)
+{
+ str = str + pos;
+
+ if (len == NOTFOUND)
+ len = strlen(str);
+
+ char* buf = malloc(len+1);
+ strncpy(buf, str, len);
+ buf[len] = '\0';
+
+ return buf;
+}
+
+struct module_s getModule(pid_t pid, const char* module_name)
+{
+ struct module_s mod = {0};
+
+ char maps_file_path[PATH_MAX];
+ snprintf(maps_file_path, sizeof(maps_file_path), "/proc/%i/maps", pid);
+ printf("[?] Maps file path: %s\n", maps_file_path);
+
+ FILE* maps_file_fs = fopen(maps_file_path, "rb");
+ if (!maps_file_fs) return mod;
+ errno = 0;
+
+ char maps_file[PROCMAPS_LINE_MAX_LENGTH];
+ while( !feof(maps_file_fs) ){
+ if (fgets(maps_file, sizeof(maps_file), maps_file_fs) == NULL)
+ {
+ if (errno != 0)
+ printf("[?] fgets failed, %s\n",strerror(errno));
+ return mod;
+ }
+ size_t module_path_pos = 0;
+ size_t module_path_end = 0;
+
+ //Get the first slash in the line of the module name
+ module_path_pos = find(maps_file, module_name, 0);
+ if (module_path_pos == NOTFOUND)
+ continue;
+
+ module_path_pos = find(maps_file, "/", 0);
+
+ //Get the end of the line of the module name
+ module_path_end = find(maps_file, "\n", module_path_pos);
+
+ if(module_path_pos == NOTFOUND || module_path_end == NOTFOUND) continue;
+
+ //Module path substring
+ char* module_path_str = substr(maps_file, module_path_pos, module_path_end - module_path_pos);
+
+ printf("[?] Module path string: %s\n", module_path_str);
+
+ //--- Module name
+
+
+ char* module_name_str = substr(module_path_str,
+ rfind(module_path_str, "/", 0) + 1, //Substring from the last '/' to the end of the string
+ NOTFOUND
+ );
+
+ printf("[?] Module name: %s\n", module_name_str);
+
+ //--- Base Address
+
+ size_t base_address_pos = rfind(maps_file, "\n", module_path_pos) + 1;
+ size_t base_address_end = find(maps_file, "-", base_address_pos);
+ if(base_address_pos == NOTFOUND || base_address_end == NOTFOUND) continue;
+ char* base_address_str = substr(maps_file, base_address_pos, base_address_end - base_address_pos);
+ void* base_address = (void*)strtoptr(pid, base_address_str, NULL, 16);;
+ if (!base_address) return mod;
+
+ printf("[?] Base Address: %p\n", base_address);
+
+ //--- End Address
+ size_t end_address_pos;
+ size_t end_address_end;
+ char* end_address_str;
+ void* end_address;
+
+ //Get end address pos
+ end_address_pos = rfind(maps_file, module_path_str, 0);
+ end_address_pos = rfind(maps_file, "\n", end_address_pos) + 1;
+ end_address_pos = find(maps_file, "-", end_address_pos) + 1;
+
+ //Find first space from end_address_pos
+ end_address_end = find(maps_file, " ", end_address_pos);
+
+ if(end_address_pos == NOTFOUND || end_address_end == NOTFOUND) continue;
+
+ //End address substring
+ end_address_str = substr(maps_file, end_address_pos, end_address_end - end_address_pos);
+ end_address = (void*)strtoptr(pid, end_address_str, NULL, 16);
+ free(end_address_str);
+
+ printf("[?] End Address: %p\n", end_address);
+
+ //--- Module size
+
+ uintptr_t module_size = (uintptr_t)end_address - (uintptr_t)base_address;
+ printf("[?] Module size: 0x%lx\n", module_size);
+
+ mod.name = module_name_str;
+ mod.path = module_path_str;
+ mod.base = base_address;
+ mod.size = module_size;
+ mod.end = end_address;
+
+ break;
+ }
+
+ fclose(maps_file_fs);
+ maps_file_fs = NULL;
+
+ return mod;
+}
+
+void freeModule(struct module_s* mod)
+{
+ free(mod->name);
+ free(mod->path);
+}
diff --git a/src/module.h b/src/module.h
new file mode 100644
index 0000000..a77a565
--- /dev/null
+++ b/src/module.h
@@ -0,0 +1,19 @@
+#ifndef MODULE_H
+#define MODULE_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+struct module_s
+{
+ char* name;
+ char* path;
+ void* base;
+ void* end;
+ uintptr_t size;
+};
+
+struct module_s getModule(pid_t pid, const char* module_name);
+void freeModule(struct module_s* mod);
+
+#endif \ No newline at end of file
diff --git a/src/proc.c b/src/proc.c
new file mode 100644
index 0000000..f6c8d52
--- /dev/null
+++ b/src/proc.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <sys/stat.h>
+#include <limits.h>
+
+#include "proc.h"
+#include "fs.h"
+
+int process_exists(pid_t pid)
+{
+ char path[PATH_MAX];
+ snprintf(path, PATH_MAX, "/proc/%li", pid);
+
+ return isDir(path);
+} \ No newline at end of file
diff --git a/src/proc.h b/src/proc.h
new file mode 100644
index 0000000..b4ba48d
--- /dev/null
+++ b/src/proc.h
@@ -0,0 +1,8 @@
+#ifndef PROC_H
+#define PROC_H
+
+#include <sys/types.h>
+
+int process_exists(pid_t pid);
+
+#endif \ No newline at end of file