From 41355ff614fafcc5e100a71e55b0e0abc9718797 Mon Sep 17 00:00:00 2001 From: sys64738 Date: Sun, 27 Dec 2020 20:43:19 +0100 Subject: [PATCH] initial proof-of-concept --- .gitignore | 1 + Makefile | 13 + dynso.c | 686 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 700 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 dynso.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c7e3b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/dynso diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6c122df --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ + +CFLAGS += -std=gnu17 -fPIC -fPIE -g -Wall -fsanitize=address + +LDFLAGS += -ldl -lc -lm -pie -g -fsanitize=address +#LDFLAGS += -Wl,-I,$(HOME)/src/glibc/glibc-build/elf/ld-linux-x86-64.so.2 + +dynso: dynso.c + +clean: + $(RM) dynso + +.PHONY: clean + diff --git a/dynso.c b/dynso.c new file mode 100644 index 0000000..627d5be --- /dev/null +++ b/dynso.c @@ -0,0 +1,686 @@ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define BREAK() asm volatile("int3":::"memory") + +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 + // 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; + + // 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! + + // 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) { + printf("[!] E: can't get priv2 offset, bailing out...\n"); + 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); + 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); + + // 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) { + printf("[!] E: can't find VERSYM, bailing out...\n"); + exit(1); + } + + 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) { + printf("[!] E: can't get priv3 offset, bailing out...\n"); + 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); + return r; +} +static struct link_map_priv3* priv2_to_priv3(struct link_map_priv2* l2) { + static ptrdiff_t d = -1; + if (d < 0) d = get_priv2_priv3_offset(); + + 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) { + 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)); + struct link_map* glob = _r_debug.r_map; + 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); + + 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); + 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); + } + if (dyn->d_tag == DT_STRSZ) { + strsz = dyn->d_un.d_val; + printf(" - [%zu] strsz\n", j); + } + if (dyn->d_tag == DT_SYMENT) { + syment = dyn->d_un.d_val; + printf(" - [%zu] syment\n", j); + } + } + + if (!memcmp((const void*)ldso_base, ELFMAG, SELFMAG)) { + printf("[-] ldso is ELF\n"); + + FILE* fff = fopen(glob->l_name, "rb"); + if (!fff) {printf("no file\n");exit(1);} + + 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); + + 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); + char* shstrtab = (char*)malloc(shdr->sh_size); + fseek(fff, shdr->sh_offset, SEEK_SET); + fread(shstrtab, 1, shdr->sh_size, fff); + + 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); + + //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... + sym_malloc = true; + symtab = (ElfW(Sym)*)malloc(shdr->sh_size); + fseek(fff, shdr->sh_offset, SEEK_SET); + fread(symtab, 1, shdr->sh_size, fff); + 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); + str_malloc = true; + strtab = (char*)malloc(shdr->sh_size); + fseek(fff, shdr->sh_offset, SEEK_SET); + fread(strtab, 1, shdr->sh_size, fff); + strsz = shdr->sh_size; + } + } + + free(shstrtab); + } else printf("wut\n"); + + fclose(fff); + } + + printf(" [-] symtab=%p\tstrtab=%p\tstrsz=0x%zx\tsyment=0x%zx\n", + symtab, strtab, strsz, syment); + printf("[-] resolving hidden needed symbols\n"); + + 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("wtf\n"); + 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); + +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"); + return ((gnu_ifunc_f)(r))(); + } else { + return r; + } +} +static void link_private_rtld_syms(void) { + struct rtldinfo rtldinf = get_rtld_symtab(); + 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")) { + 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); + } + 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); + + _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); + + _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); +} + +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); + + 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; + } + + // 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*)); + + 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; + + 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*/ +} + +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; +}; + +static void a_function_impl(void) { + printf("hello world!\n"); +} + +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 +}; + +// 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 }, + + (ElfW(Dyn)){.d_tag = DT_NULL , .d_un.d_val = 0} +}; + + +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); + + lm->l_addr = 0x06900000; + + // not needed apparently! + //l2->l_phdr = my_phdr; + //l2->l_phnum = sizeof(my_phdr)/sizeof(my_phdr[0]); + + 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]); + + //BREAK(); + + //printf("[*] lm=%p\n", lm); + + // 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 + + _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_nbuckets = 1; + l2->l_buckets = &my_idxtab[1]; + l2->l_chain = &my_idxtab[1]; + l3->l_initfini = NULL; + + _dl_add_to_namespace_list(lm, LM_ID_BASE); + + print_link_map(_r_debug.r_map); + + 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); + + /*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); + + 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(); + + //printf("sin(argc) = %f\n", sin(argc)); + + inject_undo(rrr); + l2->l_removed = 1; + return 0; +} +