now in library form

This commit is contained in:
Triss 2020-12-27 23:59:59 +01:00
parent 41355ff614
commit 8dc14dcb5f
8 changed files with 735 additions and 459 deletions

5
.gitignore vendored
View File

@ -1 +1,4 @@
/dynso *.so
*.o
*.a
*.elf

2
LICENSE Normal file
View File

@ -0,0 +1,2 @@
be gay, do crimes, death to america

View File

@ -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 #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: clean:
$(RM) dynso $(RM) *.o *.so *.a example.elf
.PHONY: clean .PHONY: clean default debug release all example example-debug example-release

65
README.md Normal file
View File

@ -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
```

761
dynso.c
View File

@ -10,159 +10,22 @@
#include <elf.h> #include <elf.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <link.h> #include <link.h>
#include <math.h>
#include <sys/auxv.h> #include <sys/auxv.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "dynso.h"
#include "dynso_internal.h"
#define BREAK() asm volatile("int3":::"memory") #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"); printf("[*] link map:\n");
for (size_t i = 0; glob; glob = glob->l_next, ++i) { for (size_t i = 0; glob; glob = glob->l_next, ++i) {
printf("- [%zu] %p -> '%s'\n", i, (void*)glob->l_addr, glob->l_name); 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) { static ptrdiff_t get_priv1_priv2_offset(void) {
// the offset between the priv1 and priv2 parts of a link_map is a // 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 // 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) // 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; 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 // priv2 contains a field with the entrypoint of an ELF
// we're the executable running this stuff (if not, we could try parsing ElfW(Addr) entry = exe_ehdr->e_entry + exe_map->l_addr;
// 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 // time for crimes
const ElfW(Addr)* crimes = (const ElfW(Addr)*)&exe_map->l_info[0]; 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) ++crimes, ++iter)
; ;
if (*crimes != entry) { if (*crimes != entry) {
printf("[!] E: can't get priv2 offset, bailing out...\n"); //printf("[!] E: can't get priv2 offset, bailing out...\n");
exit(1); return -1;//exit(1);
} }
// crimes is now &priv2->l_entry; // crimes is now &priv2->l_entry;
ptrdiff_t r = (ptrdiff_t)crimes - (ptrdiff_t)exe_map - offsetof(struct link_map_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; 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) { 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_priv1* exe_map = (struct link_map_priv1*)_r_debug.r_map;
struct link_map_priv2* exe2 = priv1_to_priv2(exe_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) { if (versym_good == NULL) {
printf("[!] E: can't find VERSYM, bailing out...\n"); //printf("[!] E: can't find VERSYM, bailing out...\n");
exit(1); return -1;//exit(1);
} }
const ElfW(Versym)** crimes = (const ElfW(Versym)**)&exe2->l_direct_opencount; 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) ++crimes, ++iter)
; ;
if (*crimes != versym_good) { if (*crimes != versym_good) {
printf("[!] E: can't get priv3 offset, bailing out...\n"); //printf("[!] E: can't get priv3 offset, bailing out...\n");
exit(1); return -1;//exit(1);
} }
// crimes is now &priv3->l_versyms; // crimes is now &priv3->l_versyms;
ptrdiff_t r = (ptrdiff_t)crimes - (ptrdiff_t)exe2 - offsetof(struct link_map_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; 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; 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); 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); ElfW(Addr) ldso_base = (ElfW(Addr))getauxval(AT_BASE);
printf("[-] getauxval(AT_BASE) = %p\n", (void*)ldso_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_SYSINFO_EHDR) = %p\n", (void*)getauxval(AT_SYSINFO_EHDR));
struct link_map* glob = _r_debug.r_map; 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) { for (size_t i = 0; glob; glob = glob->l_next, ++i) {
if (glob->l_addr != ldso_base) continue; 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; ElfW(Sym)* symtab = NULL;
char* strtab = 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) { for (size_t j = 0; dyn->d_tag != DT_NULL; ++dyn, ++j) {
if (dyn->d_tag == DT_STRTAB) { if (dyn->d_tag == DT_STRTAB) {
strtab = (char*)(/*glob->l_addr +*/ dyn->d_un.d_ptr); 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) { if (dyn->d_tag == DT_SYMTAB) {
symtab = (ElfW(Sym) *)(/*glob->l_addr +*/ dyn->d_un.d_ptr); 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) { if (dyn->d_tag == DT_STRSZ) {
strsz = dyn->d_un.d_val; strsz = dyn->d_un.d_val;
printf(" - [%zu] strsz\n", j); //printf(" - [%zu] strsz\n", j);
} }
if (dyn->d_tag == DT_SYMENT) { if (dyn->d_tag == DT_SYMENT) {
syment = dyn->d_un.d_val; syment = dyn->d_un.d_val;
printf(" - [%zu] syment\n", j); //printf(" - [%zu] syment\n", j);
} }
} }
if (!memcmp((const void*)ldso_base, ELFMAG, SELFMAG)) { 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"); FILE* fff = fopen(glob->l_name, "rb"); // TODO: use l_origin instead?
if (!fff) {printf("no file\n");exit(1);} if (!fff) goto nah;
ElfW(Ehdr)* ldso = (ElfW(Ehdr)*)ldso_base; ElfW(Ehdr)* ldso = (ElfW(Ehdr)*)ldso_base;
if (ldso->e_shoff && ldso->e_shnum && ldso->e_shentsize) { if (ldso->e_shoff && ldso->e_shnum && ldso->e_shentsize) {
//printf(" - shoff=0x%zx shnum=0x%zx\n", ldso->e_shoff, ldso->e_shnum); //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]; uint8_t shdr_blob[ldso->e_shentsize];
ElfW(Shdr)* shdr = (ElfW(Shdr)*)shdr_blob; ElfW(Shdr)* shdr = (ElfW(Shdr)*)shdr_blob;
fseek(fff, ldso->e_shoff + ldso->e_shstrndx*ldso->e_shentsize, SEEK_SET); if (fseek(fff, ldso->e_shoff + ldso->e_shstrndx*ldso->e_shentsize, SEEK_SET) < 0) errr = dynso_ldso_ioerr;
fread(shdr_blob, 1, sizeof(shdr_blob), fff); if (fread(shdr_blob, 1, sizeof(shdr_blob), fff) < 0) errr = dynso_ldso_ioerr;
char* shstrtab = (char*)malloc(shdr->sh_size); char* shstrtab = (char*)malloc(shdr->sh_size);
fseek(fff, shdr->sh_offset, SEEK_SET); if (shstrtab == NULL) errr = dynso_oom;
fread(shstrtab, 1, shdr->sh_size, fff); 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) { for (size_t j = 0; j < ldso->e_shnum; ++j) {
fseek(fff, ldso->e_shoff + j*ldso->e_shentsize, SEEK_SET); if (fseek(fff, ldso->e_shoff + j*ldso->e_shentsize, SEEK_SET) < 0) errr = dynso_ldso_ioerr;
fread(shdr_blob, 1, sizeof(shdr_blob), fff); 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); //printf(" [-] shdr [%zx] -> %p, type=0x%zx\n", j, shdr, shdr->sh_type);
if (shdr->sh_type == SHT_SYMTAB) { if (shdr->sh_type == SHT_SYMTAB) {
printf(" [-] using .symtab instead of .dynsym at offset 0x%zx\n", shdr->sh_offset); //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... ////symtab = (ElfW(Sym)*)(ldso_base + shdr->sh_offset); // not mapped in phdrs...
sym_malloc = true; sym_malloc = true;
symtab = (ElfW(Sym)*)malloc(shdr->sh_size); symtab = (ElfW(Sym)*)malloc(shdr->sh_size);
fseek(fff, shdr->sh_offset, SEEK_SET); if (symtab == NULL) errr = dynso_oom;
fread(symtab, 1, shdr->sh_size, fff); 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; shsz = shdr->sh_size;
syment = shdr->sh_entsize; // not *technically* needed, but doing this for completeness' sake 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")) { 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; str_malloc = true;
strtab = (char*)malloc(shdr->sh_size); strtab = (char*)malloc(shdr->sh_size);
fseek(fff, shdr->sh_offset, SEEK_SET); if (strtab == NULL) errr = dynso_oom;
fread(strtab, 1, shdr->sh_size, fff); 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; strsz = shdr->sh_size;
} }
} }
free(shstrtab); free(shstrtab);
} else printf("wut\n"); if (errr != dynso_ok) {
if (fff) fclose(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", nah:
symtab, strtab, strsz, syment); if (symtab == NULL || strtab == NULL) {
printf("[-] resolving hidden needed symbols\n"); return dynso_ldso_stripped;
}
struct rtldinfo ret; //printf(" [-] symtab=%p\tstrtab=%p\tstrsz=0x%zx\tsyment=0x%zx\n",
ret.base = ldso_base; // symtab, strtab, strsz, syment);
ret.symtab = symtab; //printf("[-] resolving hidden needed symbols\n");
ret.strtab = strtab;
ret.strsz = strsz; ret->base = ldso_base;
ret.shsz = shsz; ret->symtab = symtab;
ret.syment = syment; ret->strtab = strtab;
ret.sym_malloc = sym_malloc; ret->strsz = strsz;
ret.str_malloc = str_malloc; ret->shsz = shsz;
return ret; ret->syment = syment;
ret->sym_malloc = sym_malloc;
ret->str_malloc = str_malloc;
return dynso_ok;
} }
printf("wtf\n"); //printf("wtf\n");
exit(1); 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); 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); void* r = (void*)(ldso_base + sym->st_value);
if (ELF64_ST_TYPE(sym->st_info) == STT_GNU_IFUNC) { // sigh 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))(); return ((gnu_ifunc_f)(r))();
} else { } else return r;
return r;
}
} }
static void link_private_rtld_syms(void) { static enum dynso_err link_private_rtld_syms(struct dynso_lib* l) {
struct rtldinfo rtldinf = get_rtld_symtab(); struct rtldinfo rtldinf;
enum dynso_err r = get_rtld_symtab(&rtldinf);
if (r != dynso_ok) return r;
ElfW(Sym)* sym = rtldinf.symtab; ElfW(Sym)* sym = rtldinf.symtab;
for (size_t j = 0; j*rtldinf.syment < rtldinf.shsz; 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 (!*name) continue;
if (!strcmp(name, "_dl_new_object")) { if (!strcmp(name, "_dl_new_object")) {
printf("- [%zu] -> info=0x%x other=0x%x value=0x%zx\t'%s'\n", j, //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); // 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")) { if (!strcmp(name, "_dl_add_to_namespace_list")) {
printf("- [%zu] -> info=0x%x other=0x%x value=0x%zx\t'%s'\n", j, //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); // 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")) { if (!strcmp(name, "_dl_setup_hash")) {
printf("- [%zu] -> info=0x%x other=0x%x value=0x%zx\t'%s'\n", j, //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); // 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.sym_malloc) free(rtldinf.symtab);
if (rtldinf.str_malloc) free(rtldinf.strtab); if (rtldinf.str_malloc) free(rtldinf.strtab);
printf("[*] _dl_new_object=%p\n", _dl_new_object); //printf("[*] _dl_new_object=%p\n", _dl_new_object);
printf("[*] _dl_add_to_namespace_list=%p\n", _dl_add_to_namespace_list); //printf("[*] _dl_add_to_namespace_list=%p\n", _dl_add_to_namespace_list);
printf("[*] _dl_setup_hash=%p\n", _dl_setup_hash); //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 { static enum dynso_err inject_in_scope_of(struct inject_undo_info* r,
struct r_scope_elem** dest; struct link_map_priv3* vic3, struct link_map_priv1* add) {
struct r_scope_elem* old; r->alloced = NULL;
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* oldscope = vic3->l_scope[0];
struct r_scope_elem* scope; struct r_scope_elem* scope = NULL;
for (size_t i = 0; i < 3; ++i) { scope = (struct r_scope_elem*)calloc(1, sizeof(*scope)
// scope struct has the size of two entries, might hack it in here :D + sizeof(struct link_map_priv1*)*(oldscope->r_nlist+1));
if (vic3->l_scope_mem[i] == NULL && vic3->l_scope_mem[i+1] == NULL) { if (scope == NULL) return dynso_oom;
scope = (struct r_scope_elem*)&vic3->l_scope_mem[i]; r->alloced = scope;
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**)(
struct link_map_priv1** rlist = (struct link_map_priv1**)calloc( (size_t)scope + sizeof(*scope));
oldscope->r_nlist+1, sizeof(struct link_map_priv1*));
for (size_t i = 0; i < oldscope->r_nlist; ++i) for (size_t i = 0; i < oldscope->r_nlist; ++i)
rlist[i] = oldscope->r_list[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 // now apply the new scope
vic3->l_scope[0] = scope; vic3->l_scope[0] = scope;
r.dest = &vic3->l_scope[0]; r->dest = &vic3->l_scope[0];
r.old = oldscope; r->old = oldscope;
r.rlist = rlist; //r->rlist = rlist;
return r; return dynso_ok;
}
//struct link_map_priv2* add2 = priv1_to_priv2(add ); static void inject_undo(const struct inject_undo_info* inf) {
*inf->dest = inf->old;
//printf("max scope=%zu\n", vic2->l_scope_max); if (inf->alloced) free(inf->alloced);
for (size_t i = 0; vic3->l_scope[i] != NULL; ++i) { //free(inf->rlist);
/*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 : "<null>");
}*/
// 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 { enum dynso_err dynso_create(struct dynso_lib** ll, size_t address,
struct inject_undo_info undoinf; char* realname, const char* libname, struct link_map* calling, Lmid_t ns) {
struct link_map_priv1* fake1; if (!ll) return dynso_argument;
struct link_map_priv2* fake2;
struct link_map_priv3* fake3;
};
struct fakesym {
const char* name;
void* value;
};
static void a_function_impl(void) { if (calling == NULL) calling = _r_debug.r_map;
printf("hello world!\n");
}
static ElfW(Phdr) my_phdr[] = { struct dynso_lib* r = (struct dynso_lib*)calloc(1, sizeof(struct dynso_lib)
(ElfW(Phdr)){ + 16*sizeof(struct dynso_sym));
.p_type = PT_DYNAMIC, if (r == NULL) return dynso_oom;
.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: enum dynso_err e = link_private_rtld_syms(r);
// hash, pltgot, strtab, symtab, rela, rel, jmprel, versym, gnu_hash if (e != dynso_ok) { free(r); return e; }
// 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} 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_priv2* l2 = priv1_to_priv2(lm);
struct link_map_priv3* l3 = priv2_to_priv3(l2); 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! r->ns = ns;
//l2->l_phdr = my_phdr; r->caller = (struct link_map_priv1*)calling;
//l2->l_phnum = sizeof(my_phdr)/sizeof(my_phdr[0]); r->fake1 = lm;
r->fake2 = l2;
r->fake3 = l3;
r->address = address;
lm->l_ld = my_dyn; r->syms = (struct dynso_sym*)((ptrdiff_t)r + sizeof(*r));
lm->l_info[DT_SYMTAB] = &my_dyn[0]; r->nsyms = 0;
lm->l_info[DT_STRTAB] = &my_dyn[1]; r->syms_cap = 16;
l2->l_ldnum = 2;
l3->l_origin = "mwahahah";
//printf("symtab addr=%p\n", &lm->l_info[DT_SYMTAB]);
*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(); //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: strcpy(&str[strind], l->syms[i].name);
// * phdr, phnum strind += strlen(l->syms[i].name) + 1;
// * LOAD (addr, map_end, text_end) }
// * needed at all? //sym[l->nsyms+1] = ((ElfW(Sym)){.st_name=0, .st_info=0, .st_other=0, .st_shndx=0, .st_value=0, .st_size=0};
// * DYNAMIC (ld, ldnum) stuff str[strsz-1] = 0;
// * 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); 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; l2->l_relocated = 1;
l3->l_used = 1; l3->l_used = 1;
// limit symbol resolution scope
l2->l_global = 1; l2->l_global = 1;
l2->l_faked = 1; l2->l_faked = 1;
l2->l_type = lt_loaded; //l2->l_type = lt_loaded;
l2->l_nbuckets = 1; l2->l_nbuckets = 1;
l2->l_buckets = &my_idxtab[1]; l2->l_buckets = idx; // TODO
l2->l_chain = &my_idxtab[1]; 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; 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"); l->added = true;
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); return dynso_ok;
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: "); free(dyn); free(idx); free(sym); free(str);
//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)); return r;
}
inject_undo(rrr);
l2->l_removed = 1; void dynso_remove(struct dynso_lib* l) {
return 0; 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);
} }

50
dynso.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef DYNSO_H_
#define DYNSO_H_
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* for Lmid_t */
#endif
#include <dlfcn.h>
#include <link.h>
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

218
dynso_internal.h Normal file
View File

@ -0,0 +1,218 @@
#ifndef DYNSO_INTERNAL_H_
#define DYNSO_INTERNAL_H_
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <elf.h>
#include <link.h>
#include <sys/auxv.h>
#include <sys/stat.h>
// 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

54
example.c Normal file
View File

@ -0,0 +1,54 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#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;
}