now in library form
This commit is contained in:
parent
41355ff614
commit
8dc14dcb5f
|
@ -1 +1,4 @@
|
|||
/dynso
|
||||
*.so
|
||||
*.o
|
||||
*.a
|
||||
*.elf
|
||||
|
|
39
Makefile
39
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
|
||||
|
||||
|
|
|
@ -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
|
||||
```
|
765
dynso.c
765
dynso.c
|
@ -10,159 +10,22 @@
|
|||
#include <elf.h>
|
||||
#include <dlfcn.h>
|
||||
#include <link.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#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");
|
||||
|
||||
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;
|
||||
nah:
|
||||
if (symtab == NULL || strtab == NULL) {
|
||||
return dynso_ldso_stripped;
|
||||
}
|
||||
|
||||
printf("wtf\n");
|
||||
exit(1);
|
||||
//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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
//printf("wtf\n");
|
||||
return dynso_ldso_not_found;//exit(1);
|
||||
}
|
||||
|
||||
// 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 : "<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;
|
||||
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);
|
||||
}
|
||||
|
||||
/*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);
|
||||
}*/
|
||||
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;
|
||||
|
||||
/*printf("inject after: i=%zu\n", i);
|
||||
__auto_type sc = (struct r_scope_elem*)calloc(1, sizeof(struct r_scope_elem));
|
||||
if (calling == NULL) calling = _r_debug.r_map;
|
||||
|
||||
sc->r_nlist = 1;
|
||||
sc->r_list = &add;
|
||||
struct dynso_lib* r = (struct dynso_lib*)calloc(1, sizeof(struct dynso_lib)
|
||||
+ 16*sizeof(struct dynso_sym));
|
||||
if (r == NULL) return dynso_oom;
|
||||
|
||||
vic2->l_scope[i+1] = NULL; // end the chain here*/
|
||||
}
|
||||
enum dynso_err e = link_private_rtld_syms(r);
|
||||
if (e != dynso_ok) { free(r); return e; }
|
||||
|
||||
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;
|
||||
};
|
||||
struct link_map_priv1* lm =
|
||||
r->_dl_new_object(realname, libname, lt_loaded, calling, 0, ns);
|
||||
if (!lm) { free(r); return dynso_no_newobj; }
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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;
|
||||
return dynso_ok;
|
||||
}
|
||||
|
||||
free(dyn); free(idx); free(sym); free(str);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void dynso_remove(struct dynso_lib* l) {
|
||||
if (!l) return;
|
||||
|
||||
if (l->added) {
|
||||
inject_undo(&l->undoinf);
|
||||
|
||||
for (size_t i = 0; l->dyn[i].d_tag != DT_NULL; ++i)
|
||||
l->fake1->l_info[l->dyn[i].d_tag] = NULL;
|
||||
|
||||
l->fake1->l_ld = NULL;
|
||||
l->fake2->l_ldnum = 0;
|
||||
|
||||
free(l->dyn);
|
||||
free(l->sym);
|
||||
free(l->idx);
|
||||
free(l->str);
|
||||
}
|
||||
if (l->alloc_syms) free(l->syms);
|
||||
|
||||
// TODO: free dyn, symtab, strtab, idxtab
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue