From ad0ebfa7c2b9149311142db7717416538dd1fc9b Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Mon, 4 Sep 2023 18:11:50 +0200 Subject: Initial commit --- .gitignore | 2 + CMakeLists.txt | 16 ++ src/CMakeLists.txt | 22 +++ src/arch.c | 42 +++++ src/arch.h | 9 ++ src/fs.c | 202 ++++++++++++++++++++++++ src/fs.h | 29 ++++ src/inject.c | 439 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/inject.h | 10 ++ src/main.c | 52 +++++++ src/memory.c | 115 ++++++++++++++ src/memory.h | 13 ++ src/module.c | 173 +++++++++++++++++++++ src/module.h | 19 +++ src/proc.c | 14 ++ src/proc.h | 8 + 16 files changed, 1165 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 src/CMakeLists.txt create mode 100644 src/arch.c create mode 100644 src/arch.h create mode 100644 src/fs.c create mode 100644 src/fs.h create mode 100644 src/inject.c create mode 100644 src/inject.h create mode 100644 src/main.c create mode 100644 src/memory.c create mode 100644 src/memory.h create mode 100644 src/module.c create mode 100644 src/module.h create mode 100644 src/proc.c create mode 100644 src/proc.h 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 +#include +#include + +#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 + +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 +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ®s); + + //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, ®s); +#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, ®s); + + //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, ®s); + + 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 +#include + +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 +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 + +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 +#include +#include +#include +#include +#include + +#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 +#include + +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 +#include +#include + +#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 + +int process_exists(pid_t pid); + +#endif \ No newline at end of file -- cgit v1.2.3