2020-12-27 19:43:19 +00:00
|
|
|
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <elf.h>
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#include <link.h>
|
|
|
|
|
|
|
|
#include <sys/auxv.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
#include "dynso.h"
|
|
|
|
#include "dynso_internal.h"
|
|
|
|
|
2020-12-27 19:43:19 +00:00
|
|
|
#define BREAK() asm volatile("int3":::"memory")
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
/*static void print_link_map(struct link_map* glob) {
|
2020-12-27 19:43:19 +00:00
|
|
|
printf("[*] link map:\n");
|
|
|
|
for (size_t i = 0; glob; glob = glob->l_next, ++i) {
|
|
|
|
printf("- [%zu] %p -> '%s'\n", i, (void*)glob->l_addr, glob->l_name);
|
|
|
|
}
|
2020-12-27 22:59:59 +00:00
|
|
|
}*/
|
2020-12-27 19:43:19 +00:00
|
|
|
|
|
|
|
static ptrdiff_t get_priv1_priv2_offset(void) {
|
|
|
|
// the offset between the priv1 and priv2 parts of a link_map is a
|
|
|
|
// constant but only determinted at runtime, as we don't know with which
|
|
|
|
// glibc version we're running (nor do we want to hardcode stuff)
|
|
|
|
|
|
|
|
// link_map of the main executable always comes first (... I hope)
|
|
|
|
struct link_map_priv1* exe_map = (struct link_map_priv1*)_r_debug.r_map;
|
2020-12-27 22:59:59 +00:00
|
|
|
ElfW(Ehdr)* exe_ehdr = (ElfW(Ehdr)*)exe_map->l_addr;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
// priv2 contains a field with the entrypoint of an ELF
|
|
|
|
ElfW(Addr) entry = exe_ehdr->e_entry + exe_map->l_addr;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
|
|
|
// time for crimes
|
|
|
|
const ElfW(Addr)* crimes = (const ElfW(Addr)*)&exe_map->l_info[0];
|
|
|
|
|
|
|
|
for (size_t iter = 0;
|
|
|
|
*crimes != entry && iter < 4096/sizeof(*crimes);
|
|
|
|
++crimes, ++iter)
|
|
|
|
;
|
|
|
|
if (*crimes != entry) {
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("[!] E: can't get priv2 offset, bailing out...\n");
|
|
|
|
return -1;//exit(1);
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
// crimes is now &priv2->l_entry;
|
|
|
|
|
|
|
|
ptrdiff_t r = (ptrdiff_t)crimes - (ptrdiff_t)exe_map - offsetof(struct link_map_priv2, l_entry);
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("[-] offset of priv2 is %zd\n", r);
|
2020-12-27 19:43:19 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
static ptrdiff_t get_priv2_priv3_offset(void) {
|
|
|
|
struct link_map_priv1* exe_map = (struct link_map_priv1*)_r_debug.r_map;
|
|
|
|
struct link_map_priv2* exe2 = priv1_to_priv2(exe_map);
|
|
|
|
|
|
|
|
// priv3 has a 'versyms' field, which we can easily fetch from the dyn
|
|
|
|
// table for comparison. otherwise, same method as above
|
|
|
|
ElfW(Versym)* versym_good = NULL;
|
|
|
|
for (size_t i = 0; _DYNAMIC[i].d_tag != DT_NULL && i < 128; ++i) {
|
|
|
|
if (_DYNAMIC[i].d_tag == DT_VERSYM) {
|
|
|
|
versym_good = (ElfW(Versym*))_DYNAMIC[i].d_un.d_ptr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (versym_good == NULL) {
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("[!] E: can't find VERSYM, bailing out...\n");
|
|
|
|
return -1;//exit(1);
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const ElfW(Versym)** crimes = (const ElfW(Versym)**)&exe2->l_direct_opencount;
|
|
|
|
for (size_t iter = 0;
|
|
|
|
*crimes != versym_good && iter < 4096/sizeof(*crimes);
|
|
|
|
++crimes, ++iter)
|
|
|
|
;
|
|
|
|
if (*crimes != versym_good) {
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("[!] E: can't get priv3 offset, bailing out...\n");
|
|
|
|
return -1;//exit(1);
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
// crimes is now &priv3->l_versyms;
|
|
|
|
|
|
|
|
ptrdiff_t r = (ptrdiff_t)crimes - (ptrdiff_t)exe2 - offsetof(struct link_map_priv3, l_versyms);
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("[-] offset of priv3 is %zd\n", r);
|
2020-12-27 19:43:19 +00:00
|
|
|
return r;
|
|
|
|
}
|
2020-12-27 22:59:59 +00:00
|
|
|
|
|
|
|
static struct link_map_priv2* priv1_to_priv2(struct link_map_priv1* lm) {
|
|
|
|
if (!lm) return NULL;
|
|
|
|
|
|
|
|
static ptrdiff_t d = -1;
|
|
|
|
if (d < 0) {
|
|
|
|
d = get_priv1_priv2_offset();
|
|
|
|
if (d < 0) return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (struct link_map_priv2*)((ptrdiff_t)lm + d);
|
|
|
|
}
|
2020-12-27 19:43:19 +00:00
|
|
|
static struct link_map_priv3* priv2_to_priv3(struct link_map_priv2* l2) {
|
2020-12-27 22:59:59 +00:00
|
|
|
if (!l2) return NULL;
|
|
|
|
|
2020-12-27 19:43:19 +00:00
|
|
|
static ptrdiff_t d = -1;
|
2020-12-27 22:59:59 +00:00
|
|
|
if (d < 0) {
|
|
|
|
d = get_priv2_priv3_offset();
|
|
|
|
if (d < 0) return NULL;
|
|
|
|
}
|
2020-12-27 19:43:19 +00:00
|
|
|
|
|
|
|
return (struct link_map_priv3*)((ptrdiff_t)l2 + d);
|
|
|
|
}
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
static enum dynso_err get_rtld_symtab(struct rtldinfo* ret) {
|
2020-12-27 19:43:19 +00:00
|
|
|
ElfW(Addr) ldso_base = (ElfW(Addr))getauxval(AT_BASE);
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("[-] getauxval(AT_BASE) = %p\n", (void*)ldso_base);
|
|
|
|
//printf("[-] getauxval(AT_SYSINFO_EHDR) = %p\n", (void*)getauxval(AT_SYSINFO_EHDR));
|
2020-12-27 19:43:19 +00:00
|
|
|
struct link_map* glob = _r_debug.r_map;
|
2020-12-27 22:59:59 +00:00
|
|
|
//print_link_map(glob);
|
2020-12-27 19:43:19 +00:00
|
|
|
for (size_t i = 0; glob; glob = glob->l_next, ++i) {
|
|
|
|
if (glob->l_addr != ldso_base) continue;
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("[*] [%zu] %p:%s is ld.so!\n", i, (void*)glob->l_addr, glob->l_name);
|
2020-12-27 19:43:19 +00:00
|
|
|
|
|
|
|
ElfW(Sym)* symtab = NULL;
|
|
|
|
char* strtab = NULL;
|
|
|
|
size_t strsz = 0;
|
|
|
|
size_t shsz = ~(size_t)0;
|
|
|
|
ptrdiff_t syment = 0;
|
|
|
|
bool sym_malloc = false, str_malloc = false;
|
|
|
|
|
|
|
|
// gives us .dynsym, not .symtab -> kinda useless
|
|
|
|
ElfW(Dyn)* dyn = glob->l_ld;
|
|
|
|
|
|
|
|
for (size_t j = 0; dyn->d_tag != DT_NULL; ++dyn, ++j) {
|
|
|
|
if (dyn->d_tag == DT_STRTAB) {
|
|
|
|
strtab = (char*)(/*glob->l_addr +*/ dyn->d_un.d_ptr);
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf(" - [%zu] strtab\n", j);
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
if (dyn->d_tag == DT_SYMTAB) {
|
|
|
|
symtab = (ElfW(Sym) *)(/*glob->l_addr +*/ dyn->d_un.d_ptr);
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf(" - [%zu] symtab\n", j);
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
if (dyn->d_tag == DT_STRSZ) {
|
|
|
|
strsz = dyn->d_un.d_val;
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf(" - [%zu] strsz\n", j);
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
if (dyn->d_tag == DT_SYMENT) {
|
|
|
|
syment = dyn->d_un.d_val;
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf(" - [%zu] syment\n", j);
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!memcmp((const void*)ldso_base, ELFMAG, SELFMAG)) {
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("[-] ldso is ELF\n");
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
FILE* fff = fopen(glob->l_name, "rb"); // TODO: use l_origin instead?
|
|
|
|
if (!fff) goto nah;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
|
|
|
ElfW(Ehdr)* ldso = (ElfW(Ehdr)*)ldso_base;
|
|
|
|
|
|
|
|
if (ldso->e_shoff && ldso->e_shnum && ldso->e_shentsize) {
|
|
|
|
//printf(" - shoff=0x%zx shnum=0x%zx\n", ldso->e_shoff, ldso->e_shnum);
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
enum dynso_err errr = dynso_ok;
|
|
|
|
if (fseek(fff, ldso->e_shoff, SEEK_SET) < 0) errr = dynso_ldso_ioerr;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
|
|
|
uint8_t shdr_blob[ldso->e_shentsize];
|
|
|
|
ElfW(Shdr)* shdr = (ElfW(Shdr)*)shdr_blob;
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
if (fseek(fff, ldso->e_shoff + ldso->e_shstrndx*ldso->e_shentsize, SEEK_SET) < 0) errr = dynso_ldso_ioerr;
|
|
|
|
if (fread(shdr_blob, 1, sizeof(shdr_blob), fff) < 0) errr = dynso_ldso_ioerr;
|
2020-12-27 19:43:19 +00:00
|
|
|
char* shstrtab = (char*)malloc(shdr->sh_size);
|
2020-12-27 22:59:59 +00:00
|
|
|
if (shstrtab == NULL) errr = dynso_oom;
|
|
|
|
if (fseek(fff, shdr->sh_offset, SEEK_SET) < 0) errr = dynso_ldso_ioerr;
|
|
|
|
if (fread(shstrtab, 1, shdr->sh_size, fff) < 0) errr = dynso_ldso_ioerr;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
|
|
|
for (size_t j = 0; j < ldso->e_shnum; ++j) {
|
2020-12-27 22:59:59 +00:00
|
|
|
if (fseek(fff, ldso->e_shoff + j*ldso->e_shentsize, SEEK_SET) < 0) errr = dynso_ldso_ioerr;
|
|
|
|
if (fread(shdr_blob, 1, sizeof(shdr_blob), fff) < 0) errr = dynso_ldso_ioerr;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
|
|
|
//printf(" [-] shdr [%zx] -> %p, type=0x%zx\n", j, shdr, shdr->sh_type);
|
|
|
|
|
|
|
|
if (shdr->sh_type == SHT_SYMTAB) {
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf(" [-] using .symtab instead of .dynsym at offset 0x%zx\n", shdr->sh_offset);
|
|
|
|
////symtab = (ElfW(Sym)*)(ldso_base + shdr->sh_offset); // not mapped in phdrs...
|
2020-12-27 19:43:19 +00:00
|
|
|
sym_malloc = true;
|
|
|
|
symtab = (ElfW(Sym)*)malloc(shdr->sh_size);
|
2020-12-27 22:59:59 +00:00
|
|
|
if (symtab == NULL) errr = dynso_oom;
|
|
|
|
if (fseek(fff, shdr->sh_offset, SEEK_SET) < 0) errr = dynso_ldso_ioerr;
|
|
|
|
if (fread(symtab, 1, shdr->sh_size, fff) < 0) errr = dynso_ldso_ioerr;
|
2020-12-27 19:43:19 +00:00
|
|
|
shsz = shdr->sh_size;
|
|
|
|
syment = shdr->sh_entsize; // not *technically* needed, but doing this for completeness' sake
|
|
|
|
}
|
|
|
|
if (shdr->sh_type == SHT_STRTAB && !strcmp(&shstrtab[shdr->sh_name], ".strtab")) {
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf(" [-] using .strtab instead of .dynstr at offset 0x%zx\n", shdr->sh_offset);
|
2020-12-27 19:43:19 +00:00
|
|
|
str_malloc = true;
|
|
|
|
strtab = (char*)malloc(shdr->sh_size);
|
2020-12-27 22:59:59 +00:00
|
|
|
if (strtab == NULL) errr = dynso_oom;
|
|
|
|
if (fseek(fff, shdr->sh_offset, SEEK_SET) < 0) errr = dynso_ldso_ioerr;
|
|
|
|
if (fread(strtab, 1, shdr->sh_size, fff) < 0) errr = dynso_ldso_ioerr;
|
2020-12-27 19:43:19 +00:00
|
|
|
strsz = shdr->sh_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(shstrtab);
|
2020-12-27 22:59:59 +00:00
|
|
|
if (errr != dynso_ok) {
|
|
|
|
if (fff) fclose(fff);
|
|
|
|
if (str_malloc && strtab) free(strtab);
|
|
|
|
if (sym_malloc && symtab) free(symtab);
|
|
|
|
return errr;
|
|
|
|
}
|
|
|
|
} //else printf("wut\n");
|
|
|
|
}
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
nah:
|
|
|
|
if (symtab == NULL || strtab == NULL) {
|
|
|
|
return dynso_ldso_stripped;
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf(" [-] symtab=%p\tstrtab=%p\tstrsz=0x%zx\tsyment=0x%zx\n",
|
|
|
|
// symtab, strtab, strsz, syment);
|
|
|
|
//printf("[-] resolving hidden needed symbols\n");
|
|
|
|
|
|
|
|
ret->base = ldso_base;
|
|
|
|
ret->symtab = symtab;
|
|
|
|
ret->strtab = strtab;
|
|
|
|
ret->strsz = strsz;
|
|
|
|
ret->shsz = shsz;
|
|
|
|
ret->syment = syment;
|
|
|
|
ret->sym_malloc = sym_malloc;
|
|
|
|
ret->str_malloc = str_malloc;
|
|
|
|
return dynso_ok;
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("wtf\n");
|
|
|
|
return dynso_ldso_not_found;//exit(1);
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
typedef void* (*gnu_ifunc_f)(void);
|
|
|
|
|
|
|
|
static void* sym2addr(ElfW(Addr) ldso_base, ElfW(Sym)* sym) {
|
|
|
|
void* r = (void*)(ldso_base + sym->st_value);
|
|
|
|
|
|
|
|
if (ELF64_ST_TYPE(sym->st_info) == STT_GNU_IFUNC) { // sigh
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("[-] W: IFUNC. not tested.\n");
|
2020-12-27 19:43:19 +00:00
|
|
|
return ((gnu_ifunc_f)(r))();
|
2020-12-27 22:59:59 +00:00
|
|
|
} else return r;
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
2020-12-27 22:59:59 +00:00
|
|
|
static enum dynso_err link_private_rtld_syms(struct dynso_lib* l) {
|
|
|
|
struct rtldinfo rtldinf;
|
|
|
|
|
|
|
|
enum dynso_err r = get_rtld_symtab(&rtldinf);
|
|
|
|
if (r != dynso_ok) return r;
|
|
|
|
|
2020-12-27 19:43:19 +00:00
|
|
|
ElfW(Sym)* sym = rtldinf.symtab;
|
|
|
|
|
|
|
|
for (size_t j = 0; j*rtldinf.syment < rtldinf.shsz;
|
|
|
|
++j, sym = (ElfW(Sym*))((ptrdiff_t)sym + rtldinf.syment)) {
|
|
|
|
if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL) continue;
|
|
|
|
if (sym->st_value == 0) continue;
|
|
|
|
if (sym->st_name >= rtldinf.strsz) continue;
|
|
|
|
const char* name = &rtldinf.strtab[sym->st_name];
|
|
|
|
if (!*name) continue;
|
|
|
|
|
|
|
|
if (!strcmp(name, "_dl_new_object")) {
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("- [%zu] -> info=0x%x other=0x%x value=0x%zx\t'%s'\n", j,
|
|
|
|
// sym->st_info, sym->st_other, sym->st_value, name);
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
l->_dl_new_object = (dl_new_object_f)sym2addr(rtldinf.base, sym);
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
if (!strcmp(name, "_dl_add_to_namespace_list")) {
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("- [%zu] -> info=0x%x other=0x%x value=0x%zx\t'%s'\n", j,
|
|
|
|
// sym->st_info, sym->st_other, sym->st_value, name);
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
l->_dl_add_to_namespace_list = (dl_add_to_namespace_list_f)sym2addr(rtldinf.base, sym);
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
if (!strcmp(name, "_dl_setup_hash")) {
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("- [%zu] -> info=0x%x other=0x%x value=0x%zx\t'%s'\n", j,
|
|
|
|
// sym->st_info, sym->st_other, sym->st_value, name);
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
l->_dl_setup_hash = (dl_setup_hash_f)sym2addr(rtldinf.base, sym);
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rtldinf.sym_malloc) free(rtldinf.symtab);
|
|
|
|
if (rtldinf.str_malloc) free(rtldinf.strtab);
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
//printf("[*] _dl_new_object=%p\n", _dl_new_object);
|
|
|
|
//printf("[*] _dl_add_to_namespace_list=%p\n", _dl_add_to_namespace_list);
|
|
|
|
//printf("[*] _dl_setup_hash=%p\n", _dl_setup_hash);
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
if (l->_dl_new_object && l->_dl_add_to_namespace_list && l->_dl_setup_hash)
|
|
|
|
return dynso_ok;
|
|
|
|
else return dynso_no_ldso_syms;
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
static enum dynso_err inject_in_scope_of(struct inject_undo_info* r,
|
|
|
|
struct link_map_priv3* vic3, struct link_map_priv1* add) {
|
|
|
|
r->alloced = NULL;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
|
|
|
struct r_scope_elem* oldscope = vic3->l_scope[0];
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
struct r_scope_elem* scope = NULL;
|
|
|
|
scope = (struct r_scope_elem*)calloc(1, sizeof(*scope)
|
|
|
|
+ sizeof(struct link_map_priv1*)*(oldscope->r_nlist+1));
|
|
|
|
if (scope == NULL) return dynso_oom;
|
|
|
|
r->alloced = scope;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
struct link_map_priv1** rlist = (struct link_map_priv1**)(
|
|
|
|
(size_t)scope + sizeof(*scope));
|
2020-12-27 19:43:19 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < oldscope->r_nlist; ++i)
|
|
|
|
rlist[i] = oldscope->r_list[i];
|
|
|
|
rlist[oldscope->r_nlist] = add;
|
|
|
|
|
|
|
|
scope->r_list = rlist;
|
|
|
|
scope->r_nlist = oldscope->r_nlist + 1;
|
|
|
|
|
|
|
|
// now apply the new scope
|
|
|
|
vic3->l_scope[0] = scope;
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
r->dest = &vic3->l_scope[0];
|
|
|
|
r->old = oldscope;
|
|
|
|
//r->rlist = rlist;
|
|
|
|
return dynso_ok;
|
|
|
|
}
|
|
|
|
static void inject_undo(const struct inject_undo_info* inf) {
|
|
|
|
*inf->dest = inf->old;
|
|
|
|
if (inf->alloced) free(inf->alloced);
|
|
|
|
//free(inf->rlist);
|
|
|
|
}
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
enum dynso_err dynso_create(struct dynso_lib** ll, size_t address,
|
|
|
|
char* realname, const char* libname, struct link_map* calling, Lmid_t ns) {
|
|
|
|
if (!ll) return dynso_argument;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
if (calling == NULL) calling = _r_debug.r_map;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
struct dynso_lib* r = (struct dynso_lib*)calloc(1, sizeof(struct dynso_lib)
|
|
|
|
+ 16*sizeof(struct dynso_sym));
|
|
|
|
if (r == NULL) return dynso_oom;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
enum dynso_err e = link_private_rtld_syms(r);
|
|
|
|
if (e != dynso_ok) { free(r); return e; }
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
struct link_map_priv1* lm =
|
|
|
|
r->_dl_new_object(realname, libname, lt_loaded, calling, 0, ns);
|
|
|
|
if (!lm) { free(r); return dynso_no_newobj; }
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
struct link_map_priv2* l2 = priv1_to_priv2(lm);
|
|
|
|
struct link_map_priv3* l3 = priv2_to_priv3(l2);
|
|
|
|
if (!l2) { free(r); return dynso_no_priv2; }
|
|
|
|
if (!l3) { free(r); return dynso_no_priv3; }
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
l2->l_faked = 1;
|
|
|
|
l2->l_nbuckets = 0; // disables symbol lookup
|
|
|
|
l2->l_gnu_bitmask = NULL; // disable GNU hashing, use SysV hashing
|
|
|
|
l3->l_initfini = NULL;
|
|
|
|
lm->l_addr = address;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
r->ns = ns;
|
|
|
|
r->caller = (struct link_map_priv1*)calling;
|
|
|
|
r->fake1 = lm;
|
|
|
|
r->fake2 = l2;
|
|
|
|
r->fake3 = l3;
|
|
|
|
r->address = address;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
r->syms = (struct dynso_sym*)((ptrdiff_t)r + sizeof(*r));
|
|
|
|
r->nsyms = 0;
|
|
|
|
r->syms_cap = 16;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
*ll = r;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
return dynso_ok;
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
enum dynso_err dynso_add_sym_ex(struct dynso_lib* f, const char* name,
|
|
|
|
void* value, int type, size_t size) {
|
|
|
|
if (!f || f->added || f->removed) return dynso_argument;
|
|
|
|
|
|
|
|
if (f->nsyms == f->syms_cap) {
|
|
|
|
f->syms_cap <<= 1;
|
|
|
|
|
|
|
|
if (f->alloc_syms) {
|
|
|
|
f->syms = (struct dynso_sym*)realloc(
|
|
|
|
f->syms, f->syms_cap * sizeof(struct dynso_sym));
|
|
|
|
if (f->syms == NULL) return dynso_oom;
|
|
|
|
} else {
|
|
|
|
struct dynso_sym* nsym = (struct dynso_sym*)calloc(f->syms_cap, sizeof(struct dynso_sym));
|
|
|
|
if (nsym == NULL) return dynso_oom;
|
|
|
|
|
|
|
|
memcpy(nsym, f->syms, sizeof(struct dynso_sym)*f->nsyms);
|
|
|
|
f->syms = nsym;
|
2021-06-18 09:27:28 +00:00
|
|
|
f->alloc_syms = true;
|
2020-12-27 22:59:59 +00:00
|
|
|
}
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
f->syms[f->nsyms].name = name ;
|
|
|
|
f->syms[f->nsyms].value = value;
|
|
|
|
f->syms[f->nsyms].type = type;
|
|
|
|
f->syms[f->nsyms].size = size;
|
|
|
|
++f->nsyms;
|
|
|
|
|
|
|
|
return dynso_ok;
|
|
|
|
}
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
enum dynso_err dynso_bind(struct dynso_lib* l) {
|
|
|
|
if (!l || l->removed || l->added) return dynso_argument;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
struct link_map_priv1* lm = l->fake1;
|
|
|
|
struct link_map_priv2* l2 = l->fake2;
|
|
|
|
struct link_map_priv3* l3 = l->fake3;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
lm->l_addr = l->address;
|
|
|
|
|
|
|
|
size_t strsz = 2; // beginning and ending null terminators
|
|
|
|
for (size_t i = 0; i < l->nsyms; ++i) {
|
|
|
|
strsz += strlen(l->syms[i].name) + 1; // include null terminator
|
|
|
|
}
|
|
|
|
|
|
|
|
ElfW(Dyn)* dyn = (ElfW(Dyn)*)calloc(5, sizeof(ElfW(Dyn)));
|
|
|
|
if (!dyn) return dynso_oom;
|
|
|
|
Elf_Symndx* idx = (Elf_Symndx*)calloc(l->nsyms + 2, sizeof(Elf_Symndx));
|
|
|
|
if (!idx) { free(dyn); return dynso_oom; }
|
|
|
|
ElfW(Sym)* sym = (ElfW(Sym)*)calloc(l->nsyms + 2, sizeof(ElfW(Sym)));
|
|
|
|
if (!sym) { free(dyn); free(idx); return dynso_oom; }
|
|
|
|
char* str = (char*)calloc(strsz, 1);
|
|
|
|
if (!str) { free(dyn); free(idx); free(sym); return dynso_oom; }
|
|
|
|
|
|
|
|
// NOTE: should we give it a phdr? seems to work without for now...
|
|
|
|
|
|
|
|
str[0] = 0;
|
2020-12-27 19:43:19 +00:00
|
|
|
//BREAK();
|
2020-12-27 22:59:59 +00:00
|
|
|
//sym[0] = ((ElfW(Sym)){.st_name=0, .st_info=0, .st_other=0, .st_shndx=0, .st_value=0, .st_size=0};
|
|
|
|
for (size_t i = 0, strind = 1; i < l->nsyms; ++i) {
|
|
|
|
int typ = l->syms[i].type & 0xf;
|
|
|
|
if (typ == 0) typ = STT_FUNC;
|
|
|
|
|
|
|
|
sym[i+1].st_name = strind;
|
|
|
|
sym[i+1].st_info = typ | (STB_GLOBAL << 4);
|
|
|
|
sym[i+1].st_other = STV_DEFAULT;
|
|
|
|
sym[i+1].st_shndx = 0;
|
|
|
|
sym[i+1].st_value = (ElfW(Addr))l->syms[i].value - l->address;
|
|
|
|
sym[i+1].st_size = 0;
|
|
|
|
|
|
|
|
strcpy(&str[strind], l->syms[i].name);
|
|
|
|
strind += strlen(l->syms[i].name) + 1;
|
|
|
|
}
|
|
|
|
//sym[l->nsyms+1] = ((ElfW(Sym)){.st_name=0, .st_info=0, .st_other=0, .st_shndx=0, .st_value=0, .st_size=0};
|
|
|
|
str[strsz-1] = 0;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
for (size_t i = 0; i < l->nsyms; ++i) idx[i] = i + 1;
|
|
|
|
idx[l->nsyms+0] = STN_UNDEF;
|
|
|
|
idx[l->nsyms+1] = SHN_UNDEF;
|
|
|
|
|
|
|
|
dyn[0].d_tag = DT_SYMTAB;
|
|
|
|
dyn[0].d_un.d_ptr = (ElfW(Addr))sym;
|
|
|
|
dyn[1].d_tag = DT_STRTAB;
|
|
|
|
dyn[1].d_un.d_ptr = (ElfW(Addr))str;
|
|
|
|
// these two aren't strictly needed, but adding them for completeness' sake
|
|
|
|
dyn[2].d_tag = DT_STRSZ;
|
|
|
|
dyn[2].d_un.d_val = strsz;
|
|
|
|
dyn[3].d_tag = DT_SYMENT;
|
|
|
|
dyn[3].d_un.d_val = sizeof(ElfW(Sym));
|
|
|
|
|
|
|
|
dyn[4].d_tag = DT_NULL;
|
|
|
|
|
|
|
|
lm->l_ld = dyn;
|
|
|
|
size_t ldnum = 0;
|
|
|
|
for (size_t i = 0; dyn[i].d_tag != DT_NULL; ++i, ++ldnum)
|
|
|
|
lm->l_info[dyn[i].d_tag] = &dyn[i];
|
|
|
|
l2->l_ldnum = ldnum;
|
|
|
|
|
|
|
|
// lm->l_origin = "something"; // maybe? eh.
|
|
|
|
|
|
|
|
l->_dl_setup_hash(lm);
|
2020-12-27 19:43:19 +00:00
|
|
|
|
|
|
|
l2->l_relocated = 1;
|
|
|
|
l3->l_used = 1;
|
|
|
|
|
|
|
|
l2->l_global = 1;
|
|
|
|
l2->l_faked = 1;
|
2020-12-27 22:59:59 +00:00
|
|
|
//l2->l_type = lt_loaded;
|
2020-12-27 19:43:19 +00:00
|
|
|
l2->l_nbuckets = 1;
|
2020-12-27 22:59:59 +00:00
|
|
|
l2->l_buckets = idx; // TODO
|
|
|
|
l2->l_chain = idx; // TODO
|
|
|
|
l2->l_gnu_bitmask = NULL; // disable GNU hashing, use SysV legacy hashing (was easier to 'implement' on my end)
|
2020-12-27 19:43:19 +00:00
|
|
|
l3->l_initfini = NULL;
|
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
l->_dl_add_to_namespace_list(l->fake1, l->ns);
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
enum dynso_err r = inject_in_scope_of(&l->undoinf, priv1_to_priv3(l->caller), lm);
|
|
|
|
if (r == dynso_ok) {
|
|
|
|
l->dyn = dyn;
|
|
|
|
l->sym = sym;
|
|
|
|
l->idx = idx;
|
|
|
|
l->str = str;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
l->added = true;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
return dynso_ok;
|
|
|
|
}
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
free(dyn); free(idx); free(sym); free(str);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dynso_remove(struct dynso_lib* l) {
|
|
|
|
if (!l) return;
|
|
|
|
|
|
|
|
if (l->added) {
|
|
|
|
inject_undo(&l->undoinf);
|
|
|
|
|
|
|
|
for (size_t i = 0; l->dyn[i].d_tag != DT_NULL; ++i)
|
|
|
|
l->fake1->l_info[l->dyn[i].d_tag] = NULL;
|
|
|
|
|
|
|
|
l->fake1->l_ld = NULL;
|
|
|
|
l->fake2->l_ldnum = 0;
|
|
|
|
|
|
|
|
free(l->dyn);
|
|
|
|
free(l->sym);
|
|
|
|
free(l->idx);
|
|
|
|
free(l->str);
|
|
|
|
}
|
|
|
|
if (l->alloc_syms) free(l->syms);
|
|
|
|
|
|
|
|
// TODO: free dyn, symtab, strtab, idxtab
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
l->fake2->l_removed = 1;
|
|
|
|
l->fake3->l_used = 0;
|
|
|
|
l->fake2->l_global = 0;
|
|
|
|
l->fake2->l_nbuckets = 0;
|
2020-12-27 19:43:19 +00:00
|
|
|
|
2020-12-27 22:59:59 +00:00
|
|
|
l->removed = true;
|
|
|
|
free(l);
|
2020-12-27 19:43:19 +00:00
|
|
|
}
|
|
|
|
|