From 8dc14dcb5f6b30f501463ec4af69a841f133e0e8 Mon Sep 17 00:00:00 2001 From: sys64738 Date: Sun, 27 Dec 2020 23:59:59 +0100 Subject: [PATCH] now in library form --- .gitignore | 5 +- LICENSE | 2 + Makefile | 39 ++- README.md | 65 ++++ dynso.c | 761 +++++++++++++++++++---------------------------- dynso.h | 50 ++++ dynso_internal.h | 218 ++++++++++++++ example.c | 54 ++++ 8 files changed, 735 insertions(+), 459 deletions(-) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 dynso.h create mode 100644 dynso_internal.h create mode 100644 example.c diff --git a/.gitignore b/.gitignore index 7c7e3b9..dd056ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -/dynso +*.so +*.o +*.a +*.elf diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..195e819 --- /dev/null +++ b/LICENSE @@ -0,0 +1,2 @@ +be gay, do crimes, death to america + diff --git a/Makefile b/Makefile index 6c122df..3cb0f9b 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,42 @@ -CFLAGS += -std=gnu17 -fPIC -fPIE -g -Wall -fsanitize=address +CFLAGS += -std=gnu17 -Wall -fPIC #-fPIE -LDFLAGS += -ldl -lc -lm -pie -g -fsanitize=address +RANLIB ?= ranlib + +LIBS += -ldl -lc -lm +LDFLAGS += -pie #LDFLAGS += -Wl,-I,$(HOME)/src/glibc/glibc-build/elf/ld-linux-x86-64.so.2 -dynso: dynso.c +default: release #debug + +example: default example.elf +example-%: % example.elf + +debug: CFLAGS += -g -Og -fsanitize=address +debug: LDFLAGS += -g -Og -fsanitize=address +debug: all + +release: CFLAGS += -O2 -flto -ffat-lto-objects +release: LDFLAGS += -O2 -flto +release: all + +all: libdynso.a libdynso.so + +dynso.o: dynso.c + $(CC) -c -o "$@" $(CFLAGS) $^ + +libdynso.a: dynso.o + $(AR) rcvs $@ $^ +# $(RANLIB) $@ + +libdynso.so: dynso.o + $(CC) -Wl,-soname=$@ -shared -o "$@" $^ $(CFLAGS) + +example.elf: dynso.c example.c + $(CC) -o "$@" $^ $(CFLAGS) $(LDFLAGS) $(LIBS) -g -Og -fsanitize=address clean: - $(RM) dynso + $(RM) *.o *.so *.a example.elf -.PHONY: clean +.PHONY: clean default debug release all example example-debug example-release diff --git a/README.md b/README.md new file mode 100644 index 0000000..1e5ad15 --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# dynso + +Define dynamic shared objects and resolvable symbols at runtime, without +creating an ELF file anywhere or touching the filesystem. + +It works by manipulating `ld.so`'s internal data structures. (That is, if it +works at all). It also only works on glibc, it will explode in your face if you +try to run it with eg. musl. + +## Usage + +If you ever use this in production, you, together with everyone else using it, +will die. + +Other than that, here's an example: + +```c +// create a library +struct dynso_lib* l; +dynso_create(&l, 0, + (char*)"this is just a display name", "libtest", /* latter is the soname */ + NULL, LM_ID_BASE /* from dlfcn.h, you need to define _GNU_SOURCE first! */); + +// define some symbols... +dynso_add_sym(l, "testsym", (void*)0x694201337); +dynso_add_sym_ex(l, "testfunction", a_function, + STT_FUNC /* from elf.h */, 32 /* symbol size */); + +// this loads all symbols into the global context, which means they can now +// be looked up by dlsym(), and be resolved by other dynamic libraries that +// depend on it. adding more symbols won't be possible anymore, though. +dynso_bind(l); + +void* x = dlsym(RTLD_DEFAULT, "testsym"); +printf(" dlsym(\"testsym\") = %p\n", x); +x = dlsym(RTLD_DEFAULT, "testfunction"); +printf(" dlsym(\"testfunction\") = %p\n", x); + +void (*somefunc)(void) = x; +printf("calling the resolved function:\n"); +somefunc(); + +// free the used memory +dynso_remove(l); +``` + +## Dependencies + +glibc, seems to work with 2.30. + +## Compilation + +```sh +make +``` + +## Installation + +no + +## License + +``` +be gay, do crimes, death to america +``` diff --git a/dynso.c b/dynso.c index 627d5be..6d4dcf6 100644 --- a/dynso.c +++ b/dynso.c @@ -10,159 +10,22 @@ #include #include #include -#include #include #include +#include "dynso.h" +#include "dynso_internal.h" + #define BREAK() asm volatile("int3":::"memory") -static void print_link_map(struct link_map* glob) { +/*static void print_link_map(struct link_map* glob) { 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); } -} +}*/ -struct rtldinfo { - ElfW(Addr) base; - ElfW(Sym)* symtab; - char* strtab; - size_t strsz; - size_t shsz; - ptrdiff_t syment; - bool sym_malloc, str_malloc; -}; - -struct link_map_priv1 { - // SVR4 - ElfW(Addr) l_addr; - char* l_name; - ElfW(Dyn)* l_ld; - struct link_map_priv1* l_next, *l_prev; - - // glibc-specific - struct link_map_priv1* l_real; - Lmid_t l_ns; - struct libname_list* l_libname; - ElfW(Dyn)* l_info[]; -}; - -struct libname_list; -struct r_found_version; -struct r_search_path_elem; -struct r_file_id { - dev_t dev; - ino64_t ino; -}; -struct r_scope_elem { - struct link_map_priv1** r_list; - unsigned int r_nlist; -}; -struct r_search_path_struct { - struct r_search_path_elem** dirs; - int malloced; -}; -struct link_map_priv2 { - const ElfW(Phdr)* l_phdr; - ElfW(Addr) l_entry; - ElfW(Half) l_phnum; - ElfW(Half) l_ldnum; - - struct r_scope_elem l_searchlist, l_symbolic_searchlist; - - struct link_map* l_loader; - - struct r_found_version* l_versions; - unsigned int l_nversions; - - Elf_Symndx l_nbuckets; - Elf32_Word l_gnu_bitmask_idxbits; - Elf32_Word l_gnu_shift; - const ElfW(Addr)* l_gnu_bitmask; - union { - const Elf32_Word* l_gnu_buckets; - const Elf_Symndx* l_chain; - }; - union { - const Elf32_Word* l_gnu_chain_zero; - const Elf_Symndx* l_buckets; - }; - - unsigned int l_direct_opencount; - enum { - lt_executable, - lt_library, - lt_loaded - } l_type : 2; - unsigned int l_relocated : 1; - unsigned int l_init_called : 1; - unsigned int l_global : 1; - unsigned int l_reserved : 2; - unsigned int l_phdr_allocated : 1; - unsigned int l_soname_added : 1; - unsigned int l_faked : 1; - unsigned int l_need_tls_init : 1; - unsigned int l_auditing : 1; - unsigned int l_audit_any_plt : 1; - unsigned int l_removed : 1; - unsigned int l_contiguous : 1; - unsigned int l_symbolic_in_local_scope : 1; - unsigned int l_free_initfini : 1; - - //bool l_nodelete_active, l_nodelete_pending; // FIXME - // ^ these two are added somewhere between glibc 2.30 (the one on my - // system) and now (current master), I should probably add a priv3 thing - // to accomodate for this, but EFFORT - -/*#if defined(__i386__) || defined(__x86_64__) - enum { - lc_unknown = 0, - lc_none = 1<<0, - lc_ibt = 1<<1, - lc_shstk = 1<<2, - lc_ibt_and_shstk = lc_ibt | lc_shstk - } l_cet : 3; -#endif*/ -}; - -struct link_map_priv3 { - struct r_search_path_struct l_rpath_dirs; - - struct reloc_result { - ElfW(Addr) addr; - struct link_map_priv1* bound; - unsigned int boundndx; - uint32_t enterexit; - unsigned int flags; - unsigned int init; - } *l_reloc_result; - - ElfW(Versym)* l_versyms; - const char* l_origin; - ElfW(Addr) l_map_start, l_map_end; - ElfW(Addr) l_text_end; - struct r_scope_elem* l_scope_mem[4]; - size_t l_scope_max; - struct r_scope_elem** l_scope; - struct r_scope_elem* l_local_scope[2]; - struct r_file_id l_file_id; - struct r_search_path_struct l_runpath_dirs; - struct link_map** l_initfini; - struct link_map_reldeps { - unsigned int act; - struct link_map* list[]; - } *l_reldeps; - unsigned int l_reldepsmax; - - unsigned int l_used; - ElfW(Word) l_feature_1, l_flags_1, l_flags; - int l_idx; - - // more stuff... -}; - -extern void _start(void) __attribute__((__noreturn__)); 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 @@ -170,12 +33,10 @@ static ptrdiff_t get_priv1_priv2_offset(void) { // 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; + ElfW(Ehdr)* exe_ehdr = (ElfW(Ehdr)*)exe_map->l_addr; - // priv2 contains a field with the entrypoint of an ELF. we know ours because - // we're the executable running this stuff (if not, we could try parsing - // the EHDR of the main executable and calculate the address from there, - // but I'm too lazy for that now) - ElfW(Addr) entry = (ElfW(Addr))_start; // entrypoint is actually _start (from crt0.o), not main! + // priv2 contains a field with the entrypoint of an ELF + ElfW(Addr) entry = exe_ehdr->e_entry + exe_map->l_addr; // time for crimes const ElfW(Addr)* crimes = (const ElfW(Addr)*)&exe_map->l_info[0]; @@ -185,21 +46,15 @@ static ptrdiff_t get_priv1_priv2_offset(void) { ++crimes, ++iter) ; if (*crimes != entry) { - printf("[!] E: can't get priv2 offset, bailing out...\n"); - exit(1); + //printf("[!] E: can't get priv2 offset, bailing out...\n"); + return -1;//exit(1); } // crimes is now &priv2->l_entry; ptrdiff_t r = (ptrdiff_t)crimes - (ptrdiff_t)exe_map - offsetof(struct link_map_priv2, l_entry); - printf("[-] offset of priv2 is %zd\n", r); + //printf("[-] offset of priv2 is %zd\n", r); return r; } -static struct link_map_priv2* priv1_to_priv2(struct link_map_priv1* lm) { - static ptrdiff_t d = -1; - if (d < 0) d = get_priv1_priv2_offset(); - - return (struct link_map_priv2*)((ptrdiff_t)lm + d); -} 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); @@ -215,8 +70,8 @@ static ptrdiff_t get_priv2_priv3_offset(void) { } if (versym_good == NULL) { - printf("[!] E: can't find VERSYM, bailing out...\n"); - exit(1); + //printf("[!] E: can't find VERSYM, bailing out...\n"); + return -1;//exit(1); } const ElfW(Versym)** crimes = (const ElfW(Versym)**)&exe2->l_direct_opencount; @@ -225,35 +80,49 @@ static ptrdiff_t get_priv2_priv3_offset(void) { ++crimes, ++iter) ; if (*crimes != versym_good) { - printf("[!] E: can't get priv3 offset, bailing out...\n"); - exit(1); + //printf("[!] E: can't get priv3 offset, bailing out...\n"); + return -1;//exit(1); } // crimes is now &priv3->l_versyms; ptrdiff_t r = (ptrdiff_t)crimes - (ptrdiff_t)exe2 - offsetof(struct link_map_priv3, l_versyms); - printf("[-] offset of priv3 is %zd\n", r); + //printf("[-] offset of priv3 is %zd\n", r); return r; } -static struct link_map_priv3* priv2_to_priv3(struct link_map_priv2* l2) { + +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_priv2_priv3_offset(); + if (d < 0) { + d = get_priv1_priv2_offset(); + if (d < 0) return NULL; + } + + return (struct link_map_priv2*)((ptrdiff_t)lm + d); +} +static struct link_map_priv3* priv2_to_priv3(struct link_map_priv2* l2) { + if (!l2) return NULL; + + static ptrdiff_t d = -1; + if (d < 0) { + d = get_priv2_priv3_offset(); + if (d < 0) return NULL; + } return (struct link_map_priv3*)((ptrdiff_t)l2 + d); } -inline static struct link_map_priv3* priv1_to_priv3(struct link_map_priv1* lm) { - return priv2_to_priv3(priv1_to_priv2(lm)); -} -static struct rtldinfo get_rtld_symtab(void) { +static enum dynso_err get_rtld_symtab(struct rtldinfo* ret) { ElfW(Addr) ldso_base = (ElfW(Addr))getauxval(AT_BASE); - printf("[-] getauxval(AT_BASE) = %p\n", (void*)ldso_base); - printf("[-] getauxval(AT_SYSINFO_EHDR) = %p\n", (void*)getauxval(AT_SYSINFO_EHDR)); + //printf("[-] getauxval(AT_BASE) = %p\n", (void*)ldso_base); + //printf("[-] getauxval(AT_SYSINFO_EHDR) = %p\n", (void*)getauxval(AT_SYSINFO_EHDR)); struct link_map* glob = _r_debug.r_map; - print_link_map(glob); + //print_link_map(glob); for (size_t i = 0; glob; glob = glob->l_next, ++i) { if (glob->l_addr != ldso_base) continue; - printf("[*] [%zu] %p:%s is ld.so!\n", i, (void*)glob->l_addr, glob->l_name); + //printf("[*] [%zu] %p:%s is ld.so!\n", i, (void*)glob->l_addr, glob->l_name); ElfW(Sym)* symtab = NULL; char* strtab = NULL; @@ -268,108 +137,108 @@ static struct rtldinfo get_rtld_symtab(void) { 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); - printf(" - [%zu] strtab\n", j); + //printf(" - [%zu] strtab\n", j); } if (dyn->d_tag == DT_SYMTAB) { symtab = (ElfW(Sym) *)(/*glob->l_addr +*/ dyn->d_un.d_ptr); - printf(" - [%zu] symtab\n", j); + //printf(" - [%zu] symtab\n", j); } if (dyn->d_tag == DT_STRSZ) { strsz = dyn->d_un.d_val; - printf(" - [%zu] strsz\n", j); + //printf(" - [%zu] strsz\n", j); } if (dyn->d_tag == DT_SYMENT) { syment = dyn->d_un.d_val; - printf(" - [%zu] syment\n", j); + //printf(" - [%zu] syment\n", j); } } if (!memcmp((const void*)ldso_base, ELFMAG, SELFMAG)) { - printf("[-] ldso is ELF\n"); + //printf("[-] ldso is ELF\n"); - FILE* fff = fopen(glob->l_name, "rb"); - if (!fff) {printf("no file\n");exit(1);} + FILE* fff = fopen(glob->l_name, "rb"); // TODO: use l_origin instead? + if (!fff) goto nah; 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); - fseek(fff, ldso->e_shoff, SEEK_SET); + enum dynso_err errr = dynso_ok; + if (fseek(fff, ldso->e_shoff, SEEK_SET) < 0) errr = dynso_ldso_ioerr; uint8_t shdr_blob[ldso->e_shentsize]; ElfW(Shdr)* shdr = (ElfW(Shdr)*)shdr_blob; - fseek(fff, ldso->e_shoff + ldso->e_shstrndx*ldso->e_shentsize, SEEK_SET); - fread(shdr_blob, 1, sizeof(shdr_blob), fff); + 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; char* shstrtab = (char*)malloc(shdr->sh_size); - fseek(fff, shdr->sh_offset, SEEK_SET); - fread(shstrtab, 1, shdr->sh_size, fff); + 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; for (size_t j = 0; j < ldso->e_shnum; ++j) { - fseek(fff, ldso->e_shoff + j*ldso->e_shentsize, SEEK_SET); - fread(shdr_blob, 1, sizeof(shdr_blob), fff); + 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; //printf(" [-] shdr [%zx] -> %p, type=0x%zx\n", j, shdr, shdr->sh_type); if (shdr->sh_type == SHT_SYMTAB) { - 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... + //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... sym_malloc = true; symtab = (ElfW(Sym)*)malloc(shdr->sh_size); - fseek(fff, shdr->sh_offset, SEEK_SET); - fread(symtab, 1, shdr->sh_size, fff); + 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; 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")) { - printf(" [-] using .strtab instead of .dynstr at offset 0x%zx\n", shdr->sh_offset); + //printf(" [-] using .strtab instead of .dynstr at offset 0x%zx\n", shdr->sh_offset); str_malloc = true; strtab = (char*)malloc(shdr->sh_size); - fseek(fff, shdr->sh_offset, SEEK_SET); - fread(strtab, 1, shdr->sh_size, fff); + 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; strsz = shdr->sh_size; } } free(shstrtab); - } else printf("wut\n"); - - fclose(fff); + 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"); } - printf(" [-] symtab=%p\tstrtab=%p\tstrsz=0x%zx\tsyment=0x%zx\n", - symtab, strtab, strsz, syment); - printf("[-] resolving hidden needed symbols\n"); + nah: + if (symtab == NULL || strtab == NULL) { + return dynso_ldso_stripped; + } - struct rtldinfo ret; - 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 ret; + //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; } - printf("wtf\n"); - exit(1); + //printf("wtf\n"); + return dynso_ldso_not_found;//exit(1); } -// type signature for _dl_new_object -typedef struct link_map_priv1* (*dl_new_object_f)(char* realname, const char* libname, - int type, struct link_map* loader, int mode, Lmid_t nsid); -static dl_new_object_f _dl_new_object; - -// type signature for _dl_add_to_namespace_list -typedef void (*dl_add_to_namespace_list_f)(struct link_map_priv1 *new, Lmid_t nsid); -static dl_add_to_namespace_list_f _dl_add_to_namespace_list; - -// type signature for _dl_setup_hash -typedef void (*dl_setup_hash_f)(struct link_map_priv1* map); -static dl_setup_hash_f _dl_setup_hash; typedef void* (*gnu_ifunc_f)(void); @@ -377,14 +246,16 @@ 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 - printf("[-] W: IFUNC. not tested.\n"); + //printf("[-] W: IFUNC. not tested.\n"); return ((gnu_ifunc_f)(r))(); - } else { - return r; - } + } else return r; } -static void link_private_rtld_syms(void) { - struct rtldinfo rtldinf = get_rtld_symtab(); +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; + ElfW(Sym)* sym = rtldinf.symtab; for (size_t j = 0; j*rtldinf.syment < rtldinf.shsz; @@ -396,70 +267,51 @@ static void link_private_rtld_syms(void) { if (!*name) continue; if (!strcmp(name, "_dl_new_object")) { - 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); + //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); - _dl_new_object = (dl_new_object_f)sym2addr(rtldinf.base, sym); + l->_dl_new_object = (dl_new_object_f)sym2addr(rtldinf.base, sym); } if (!strcmp(name, "_dl_add_to_namespace_list")) { - 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); + //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); - _dl_add_to_namespace_list = (dl_add_to_namespace_list_f)sym2addr(rtldinf.base, sym); + l->_dl_add_to_namespace_list = (dl_add_to_namespace_list_f)sym2addr(rtldinf.base, sym); } if (!strcmp(name, "_dl_setup_hash")) { - 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); + //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); - _dl_setup_hash = (dl_setup_hash_f)sym2addr(rtldinf.base, sym); + l->_dl_setup_hash = (dl_setup_hash_f)sym2addr(rtldinf.base, sym); } } if (rtldinf.sym_malloc) free(rtldinf.symtab); if (rtldinf.str_malloc) free(rtldinf.strtab); - 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); + //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); + + if (l->_dl_new_object && l->_dl_add_to_namespace_list && l->_dl_setup_hash) + return dynso_ok; + else return dynso_no_ldso_syms; } -struct inject_undo_info { - struct r_scope_elem** dest; - struct r_scope_elem* old; - struct r_scope_elem* alloced; - struct link_map_priv1** rlist; -}; -static void inject_undo(struct inject_undo_info inf) { - *inf.dest = inf.old; - if (inf.alloced) free(inf.alloced); - free(inf.rlist); -} -static struct inject_undo_info inject_in_scope_of(struct link_map_priv1* victim, struct link_map_priv1* add) { - struct inject_undo_info r; - r.alloced = NULL; - - struct link_map_priv2* vic2 = priv1_to_priv2(victim); - struct link_map_priv3* vic3 = priv2_to_priv3(vic2); +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; struct r_scope_elem* oldscope = vic3->l_scope[0]; - struct r_scope_elem* scope; - for (size_t i = 0; i < 3; ++i) { - // scope struct has the size of two entries, might hack it in here :D - if (vic3->l_scope_mem[i] == NULL && vic3->l_scope_mem[i+1] == NULL) { - scope = (struct r_scope_elem*)&vic3->l_scope_mem[i]; - break; - } - } - if (scope == NULL) { - // otherwise, let's just allocate one - scope = (struct r_scope_elem*)calloc(1, sizeof *scope); - r.alloced = scope; - } + 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; - // sadly this one won't fit anywhere :/ - struct link_map_priv1** rlist = (struct link_map_priv1**)calloc( - oldscope->r_nlist+1, sizeof(struct link_map_priv1*)); + struct link_map_priv1** rlist = (struct link_map_priv1**)( + (size_t)scope + sizeof(*scope)); for (size_t i = 0; i < oldscope->r_nlist; ++i) rlist[i] = oldscope->r_list[i]; @@ -471,216 +323,219 @@ static struct inject_undo_info inject_in_scope_of(struct link_map_priv1* victim, // now apply the new scope vic3->l_scope[0] = scope; - r.dest = &vic3->l_scope[0]; - r.old = oldscope; - r.rlist = rlist; - return r; - - //struct link_map_priv2* add2 = priv1_to_priv2(add ); - - //printf("max scope=%zu\n", vic2->l_scope_max); - for (size_t i = 0; vic3->l_scope[i] != NULL; ++i) { - /*printf("scope %zu: n=%zu p=%p\n", i, scope->r_nlist, vic2->l_scope[i]); - - for (size_t j = 0; j < scope->r_nlist; ++j) { - struct link_map_priv1* ent = scope->r_list[j]; - struct link_map_priv2* en2 = priv1_to_priv2(ent); - - printf("scope i=%zu ent=%zu: name=%s orig=%s\n", i, j, ent->l_name, - en2->l_origin ? en2->l_origin : ""); - }*/ - - // TODO: check if we're allowed to do this w/o corruping memory? - printf(" [-] injecting in scope %zu (0x%zx) at index %u\n", i, (ptrdiff_t)scope->r_list - (ptrdiff_t)victim, scope->r_nlist); - // 'scope' is often in vic3->l_scope_mem - for (size_t iii=0;iii<4;++iii) - printf("scopemem[%zu] = %p\n", iii, vic3->l_scope_mem[iii]); - - // FIXME: apparently not, asan does NOT like this. but we'll do this - // for now anyway. fix later. - scope->r_list[scope->r_nlist] = add; - ++scope->r_nlist; - } - - /*printf("scope mem:\n"); - for (size_t j = 0; j < 4; ++j) { - __auto_type m = vic2->l_scope_mem[j]; - printf("mem[%zu] m=%p\n", j, m);//m->r_nlist, m->r_list[0]->l_name); - }*/ - - /*printf("inject after: i=%zu\n", i); - __auto_type sc = (struct r_scope_elem*)calloc(1, sizeof(struct r_scope_elem)); - - sc->r_nlist = 1; - sc->r_list = &add; - - vic2->l_scope[i+1] = NULL; // end the chain here*/ + 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); } -struct fakelib { - struct inject_undo_info undoinf; - struct link_map_priv1* fake1; - struct link_map_priv2* fake2; - struct link_map_priv3* fake3; -}; -struct fakesym { - const char* name; - void* value; -}; +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; -static void a_function_impl(void) { - printf("hello world!\n"); -} + if (calling == NULL) calling = _r_debug.r_map; -static ElfW(Phdr) my_phdr[] = { - (ElfW(Phdr)){ - .p_type = PT_DYNAMIC, - .p_flags = PF_R, - .p_offset = 0, - .p_vaddr = 0x69420, - .p_paddr = 0x69420, - .p_filesz = 0, - .p_memsz = 0, - .p_align = 65536 - } -}; -static char my_strtab[] = - "\x00" // first entry is always empty - "testsym\x00" - "a_function\x00" - "\x00" // end with another null-terminator -; -#define STR2TABIDX(s) ((size_t)strstr(my_strtab, (s)) - (size_t)my_symtab) -static ElfW(Sym) my_symtab[] = { - (ElfW(Sym)){.st_name=0, .st_info=0, .st_other=0, .st_shndx=0, .st_value=0, .st_size=0}, - (ElfW(Sym)){ - .st_name = 1,//STR2TABIDX("testsym"), - .st_info = (STB_GLOBAL<<4)|STT_FUNC, - .st_other = STV_DEFAULT, - .st_shndx = 0, - .st_value = 0x1337, - .st_size = 0x69 - }, - (ElfW(Sym)){ - .st_name = 9,//STR2TABIDX("a_function"), - .st_info = (STB_GLOBAL<<4)|STT_FUNC, - .st_other = STV_DEFAULT, - .st_shndx = 0, - .st_value = (size_t)a_function_impl - 0x06900000, - .st_size = 0x420 - }, - (ElfW(Sym)){.st_name=0, .st_info=0, .st_other=0, .st_shndx=0, .st_value=0, .st_size=0}, -}; -static Elf_Symndx my_idxtab[] = { - 0, // dummy value - 1, // testsym - 2, // a_function - STN_UNDEF, SHN_UNDEF -}; + struct dynso_lib* r = (struct dynso_lib*)calloc(1, sizeof(struct dynso_lib) + + 16*sizeof(struct dynso_sym)); + if (r == NULL) return dynso_oom; -// adjusted dyn addresses by rtld: -// hash, pltgot, strtab, symtab, rela, rel, jmprel, versym, gnu_hash -// this means rtld will use the values as direct addresses instead of -// calculating their offsets etc.! -static ElfW(Dyn) my_dyn[] = { - (ElfW(Dyn)){.d_tag = DT_SYMTAB, .d_un.d_ptr = (ElfW(Addr))my_symtab }, - (ElfW(Dyn)){.d_tag = DT_STRTAB, .d_un.d_ptr = (ElfW(Addr))my_strtab }, + enum dynso_err e = link_private_rtld_syms(r); + if (e != dynso_ok) { free(r); return e; } - (ElfW(Dyn)){.d_tag = DT_NULL , .d_un.d_val = 0} -}; + struct link_map_priv1* lm = + r->_dl_new_object(realname, libname, lt_loaded, calling, 0, ns); + if (!lm) { free(r); return dynso_no_newobj; } - -int main(int argc, char* argv[]) { - (void)argc; - (void)argv; - - //my_symtab[1].st_name = STR2TABIDX("testsym"); - //my_symtab[2].st_name = STR2TABIDX("a_function"); - - // no worky - symbol is local, not global - //_dl_new_object = (dl_new_object_f)dlsym(RTLD_DEFAULT, "_dl_new_object"); - - // so let's try this instead! - link_private_rtld_syms(); - - printf("[*] performing magic...\n"); - struct link_map_priv1* lm = _dl_new_object((char*)"lol", "lol2", - 1/*lt_library*/, _r_debug.r_map, 0, LM_ID_BASE); 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; } - lm->l_addr = 0x06900000; + 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; - // not needed apparently! - //l2->l_phdr = my_phdr; - //l2->l_phnum = sizeof(my_phdr)/sizeof(my_phdr[0]); + r->ns = ns; + r->caller = (struct link_map_priv1*)calling; + r->fake1 = lm; + r->fake2 = l2; + r->fake3 = l3; + r->address = address; - lm->l_ld = my_dyn; - lm->l_info[DT_SYMTAB] = &my_dyn[0]; - lm->l_info[DT_STRTAB] = &my_dyn[1]; - l2->l_ldnum = 2; - l3->l_origin = "mwahahah"; - //printf("symtab addr=%p\n", &lm->l_info[DT_SYMTAB]); + r->syms = (struct dynso_sym*)((ptrdiff_t)r + sizeof(*r)); + r->nsyms = 0; + r->syms_cap = 16; + *ll = r; + + return dynso_ok; +} + +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; + } + } + + 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; +} + +enum dynso_err dynso_bind(struct dynso_lib* l) { + if (!l || l->removed || l->added) return dynso_argument; + + struct link_map_priv1* lm = l->fake1; + struct link_map_priv2* l2 = l->fake2; + struct link_map_priv3* l3 = l->fake3; + + 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; //BREAK(); + //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; - //printf("[*] lm=%p\n", lm); + 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; - // TODO: set up: - // * phdr, phnum - // * LOAD (addr, map_end, text_end) - // * needed at all? - // * DYNAMIC (ld, ldnum) stuff - // * DYNAMIC: symtab, strtab - // * map_start, addr, map_end, text_end - // * is this even needed???? - // * ld - // * call elf_get_dynamic_info(lm, temp_dyn_array); // temp_dyn_array: ElfW(Dyn)[DL_RO_DYN_TEMP_CNT] - // * need to do this ourselves... - // * call _dl_setup_hash(lm) - // * nah, not needed - // * relocated = 1, used = 1, local_scope[0]->r_{nlist = 1,list = &lm->l_real} - // * init lm->l_libname->name + 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; - _dl_setup_hash(lm); + 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); l2->l_relocated = 1; l3->l_used = 1; - // limit symbol resolution scope l2->l_global = 1; l2->l_faked = 1; - l2->l_type = lt_loaded; + //l2->l_type = lt_loaded; l2->l_nbuckets = 1; - l2->l_buckets = &my_idxtab[1]; - l2->l_chain = &my_idxtab[1]; + 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) l3->l_initfini = NULL; - _dl_add_to_namespace_list(lm, LM_ID_BASE); + l->_dl_add_to_namespace_list(l->fake1, l->ns); - print_link_map(_r_debug.r_map); + 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; - printf("[*] injecting into scope of main executable\n"); - struct inject_undo_info rrr = inject_in_scope_of((struct link_map_priv1*)_r_debug.r_map, lm); + l->added = true; - /*printf("scope_max offset=%zu, should be 904\n", (size_t)&l2->l_scope_max - (size_t)lm); - printf("file_id offset=%zu, should be 936\n", (size_t)&l2->l_file_id - (size_t)lm); - printf("runpath_dirs offset=%zu, should be 952\n", (size_t)&l2->l_runpath_dirs - (size_t)lm);*/ - printf("[*] trying dlsym(\"testsym\") now: "); - void* res = dlsym(RTLD_DEFAULT, "testsym"); - printf("-> %p\n", res); + return dynso_ok; + } - printf("[*] trying dlsym(\"a_function\") now: "); - //BREAK(); - void (*resf)(void) = dlsym(RTLD_DEFAULT, "a_function"); - printf("-> %p\n", resf); - printf("[*] calling a_function():\n"); - resf(); + free(dyn); free(idx); free(sym); free(str); - //printf("sin(argc) = %f\n", sin(argc)); - - inject_undo(rrr); - l2->l_removed = 1; - return 0; + 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 + + l->fake2->l_removed = 1; + l->fake3->l_used = 0; + l->fake2->l_global = 0; + l->fake2->l_nbuckets = 0; + + l->removed = true; + free(l); } diff --git a/dynso.h b/dynso.h new file mode 100644 index 0000000..9dabf1e --- /dev/null +++ b/dynso.h @@ -0,0 +1,50 @@ + +#ifndef DYNSO_H_ +#define DYNSO_H_ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* for Lmid_t */ +#endif + +#include +#include + +struct dynso_lib; + +enum dynso_err { + dynso_ok = 0, + dynso_argument, // invalid argument + dynso_oom, // out-of-memory + dynso_bound, // the library is already bound + dynso_removed, // the library is already removed + dynso_ldso_not_found, // can't find ld.so in link map + //dynso_ldso_not_elf, // ld.so is not an ELF file, somehow + //dynso_ldso_cant_open, // can't open ld.so for reading + dynso_ldso_ioerr, // IO error whie parsing ld.so + dynso_ldso_stripped, // ld.so is stripped + dysno_ldso_no_symtab, // can't find ld.so's symbol table + dynso_no_ldso_syms, // can't resolve ld.so-internal symbols + dynso_no_newobj, // can't create a new symbol + dynso_no_priv2, // can't find the offset of the priv2 part of a struct link_map + dynso_no_priv3, // can't find the offset of the priv3 part of a struct link_map +}; + +// create a library +enum dynso_err dynso_create(struct dynso_lib** l, size_t address, /* leave at 0 unless you know what you're doing */ + char* realname, const char* libname, struct link_map* owner, + Lmid_t ns); // owner can be NULL. if so, the main executable will be used (_r_debug.r_map) + +// add a symbol to a not-yet-bound library +enum dynso_err dynso_add_sym_ex(struct dynso_lib* l, const char* name, void* value, int type, size_t size); +inline static enum dynso_err dynso_add_sym(struct dynso_lib* l, const char* name, void* value) { + return dynso_add_sym_ex(l, name, value, 0, 0); +} + +// bind the library to the runtime. will perform hacks! +enum dynso_err dynso_bind(struct dynso_lib* l); + +// remove the library from places. it'll be useless after this +void dynso_remove(struct dynso_lib* l); + +#endif + diff --git a/dynso_internal.h b/dynso_internal.h new file mode 100644 index 0000000..164e893 --- /dev/null +++ b/dynso_internal.h @@ -0,0 +1,218 @@ + +#ifndef DYNSO_INTERNAL_H_ +#define DYNSO_INTERNAL_H_ + +#include +#include +#include + +#include +#include + +#include +#include + +// glibc stuff... + +struct link_map_priv1 { + // SVR4 + ElfW(Addr) l_addr; + char* l_name; + ElfW(Dyn)* l_ld; + struct link_map_priv1* l_next, *l_prev; + + // glibc-specific + struct link_map_priv1* l_real; + Lmid_t l_ns; + struct libname_list* l_libname; + ElfW(Dyn)* l_info[]; +}; + +struct libname_list; +struct r_found_version; +struct r_search_path_elem; +struct r_file_id { + dev_t dev; + ino64_t ino; +}; +struct r_scope_elem { + struct link_map_priv1** r_list; + unsigned int r_nlist; +}; +struct r_search_path_struct { + struct r_search_path_elem** dirs; + int malloced; +}; +struct link_map_priv2 { + const ElfW(Phdr)* l_phdr; + ElfW(Addr) l_entry; + ElfW(Half) l_phnum; + ElfW(Half) l_ldnum; + + struct r_scope_elem l_searchlist, l_symbolic_searchlist; + + struct link_map* l_loader; + + struct r_found_version* l_versions; + unsigned int l_nversions; + + Elf_Symndx l_nbuckets; + Elf32_Word l_gnu_bitmask_idxbits; + Elf32_Word l_gnu_shift; + const ElfW(Addr)* l_gnu_bitmask; + union { + const Elf32_Word* l_gnu_buckets; + const Elf_Symndx* l_chain; + }; + union { + const Elf32_Word* l_gnu_chain_zero; + const Elf_Symndx* l_buckets; + }; + + unsigned int l_direct_opencount; + enum { + lt_executable, + lt_library, + lt_loaded + } l_type : 2; + unsigned int l_relocated : 1; + unsigned int l_init_called : 1; + unsigned int l_global : 1; + unsigned int l_reserved : 2; + unsigned int l_phdr_allocated : 1; + unsigned int l_soname_added : 1; + unsigned int l_faked : 1; + unsigned int l_need_tls_init : 1; + unsigned int l_auditing : 1; + unsigned int l_audit_any_plt : 1; + unsigned int l_removed : 1; + unsigned int l_contiguous : 1; + unsigned int l_symbolic_in_local_scope : 1; + unsigned int l_free_initfini : 1; + + //bool l_nodelete_active, l_nodelete_pending; // FIXME + // ^ these two are added somewhere between glibc 2.30 (the one on my + // system) and now (current master), I should probably add a priv3 thing + // to accomodate for this, but EFFORT + +/*#if defined(__i386__) || defined(__x86_64__) + enum { + lc_unknown = 0, + lc_none = 1<<0, + lc_ibt = 1<<1, + lc_shstk = 1<<2, + lc_ibt_and_shstk = lc_ibt | lc_shstk + } l_cet : 3; +#endif*/ +}; + +struct link_map_priv3 { + struct r_search_path_struct l_rpath_dirs; + + struct reloc_result { + ElfW(Addr) addr; + struct link_map_priv1* bound; + unsigned int boundndx; + uint32_t enterexit; + unsigned int flags; + unsigned int init; + } *l_reloc_result; + + ElfW(Versym)* l_versyms; + const char* l_origin; + ElfW(Addr) l_map_start, l_map_end; + ElfW(Addr) l_text_end; + struct r_scope_elem* l_scope_mem[4]; + size_t l_scope_max; + struct r_scope_elem** l_scope; + struct r_scope_elem* l_local_scope[2]; + struct r_file_id l_file_id; + struct r_search_path_struct l_runpath_dirs; + struct link_map** l_initfini; + struct link_map_reldeps { + unsigned int act; + struct link_map* list[]; + } *l_reldeps; + unsigned int l_reldepsmax; + + unsigned int l_used; + ElfW(Word) l_feature_1, l_flags_1, l_flags; + int l_idx; + + // more stuff... +}; + +typedef struct link_map_priv1* (*dl_new_object_f)(char* realname, const char* libname, + int type, struct link_map* loader, int mode, Lmid_t nsid); + +typedef void (*dl_add_to_namespace_list_f)(struct link_map_priv1 *new, Lmid_t nsid); + +typedef void (*dl_setup_hash_f)(struct link_map_priv1* map); + +// actual stuff internal for this lib now + +struct rtldinfo { + ElfW(Addr) base; + ElfW(Sym)* symtab; + char* strtab; + size_t strsz; + size_t shsz; + ptrdiff_t syment; + bool sym_malloc, str_malloc; +}; + +struct inject_undo_info { + struct r_scope_elem** dest; + struct r_scope_elem* old; + struct r_scope_elem* alloced; + //struct link_map_priv1** rlist; +}; + +struct dynso_sym { + const char* name; + void* value; + size_t size; + int type; +}; +struct dynso_lib { + struct inject_undo_info undoinf; + dl_new_object_f _dl_new_object; + dl_add_to_namespace_list_f _dl_add_to_namespace_list; + dl_setup_hash_f _dl_setup_hash; + ElfW(Dyn)* dyn; + ElfW(Sym)* sym; + Elf_Symndx* idx; + char* str; + struct link_map_priv1* caller; + struct link_map_priv1* fake1; + struct link_map_priv2* fake2; + struct link_map_priv3* fake3; + struct dynso_sym* syms; + size_t address; + size_t nsyms; + size_t syms_cap; + Lmid_t ns; + bool added; + bool removed; + bool alloc_syms; +}; +//extern void _start(void) __attribute__((__noreturn__)); + +static ptrdiff_t get_priv1_priv2_offset(void); +static ptrdiff_t get_priv2_priv3_offset(void); + +static struct link_map_priv2* priv1_to_priv2(struct link_map_priv1* lm); +static struct link_map_priv3* priv2_to_priv3(struct link_map_priv2* l2); +inline static struct link_map_priv3* priv1_to_priv3(struct link_map_priv1* lm) { + return priv2_to_priv3(priv1_to_priv2(lm)); +} + +static enum dynso_err get_rtld_symtab(struct rtldinfo* inf); +static enum dynso_err link_private_rtld_syms(struct dynso_lib* l); + +static enum dynso_err inject_in_scope_of(struct inject_undo_info* r, + struct link_map_priv3* victim, struct link_map_priv1* add); +static void inject_undo(const struct inject_undo_info* inf); + +#endif + diff --git a/example.c b/example.c new file mode 100644 index 0000000..f870cbc --- /dev/null +++ b/example.c @@ -0,0 +1,54 @@ + +#define _GNU_SOURCE + +#include + +#include + +#include "dynso.h" + +static void assert_ok_base(const char* file, int line, enum dynso_err e) { + if (e != dynso_ok) { + printf("rip! at %s:%d: %d\n", file, line, e); + } +} +#define assert_ok(x) assert_ok_base(__FILE__, __LINE__, (x)) + +static void a_function(void) { + printf("hello world!\n"); +} + +int main(int argc, char* argv[]) { + void* x; + + printf("pre:\n"); + x = dlsym(RTLD_DEFAULT, "testsym"); + printf(" dlsym(\"testsym\") = %p\n", x); + x = dlsym(RTLD_DEFAULT, "testfunction"); + printf(" dlsym(\"testfunction\") = %p\n", x); + + struct dynso_lib* l; + assert_ok(dynso_create(&l, 0, + (char*)"this is just a display name", "libtest", + NULL, LM_ID_BASE)); + + assert_ok(dynso_add_sym(l, "testsym", (void*)0x694201337)); + assert_ok(dynso_add_sym(l, "testfunction", a_function)); + + assert_ok(dynso_bind(l)); + + printf("post:\n"); + x = dlsym(RTLD_DEFAULT, "testsym"); + printf(" dlsym(\"testsym\") = %p\n", x); + x = dlsym(RTLD_DEFAULT, "testfunction"); + printf(" dlsym(\"testfunction\") = %p\n", x); + + void (*somefunc)(void) = x; + printf("calling the resolved function:\n"); + somefunc(); + + dynso_remove(l); + + return 0; +} +