better libc detection, use DT_TEXTREL when possible instead of making a segment RWX

This commit is contained in:
Triss 2021-03-23 14:53:06 +01:00
parent 00632e9548
commit c3248c6801
1 changed files with 71 additions and 7 deletions

View File

@ -6,9 +6,9 @@
#include <elf.h>
#include <link.h>
#if (sizeof(ElfW(Ehdr)) != sizeof(Elf64_Ehdr))
/*#if sizeof(ElfW(Ehdr)) != sizeof(Elf64_Ehdr)
#error "Need 64-bit ELF for now, sorry"
#endif
#endif*/
static bool manip_elf(ElfW(Ehdr)* elf, const char* symname) {
if (elf->e_ident[0] != ELFMAG0 || elf->e_ident[1] != ELFMAG1
@ -33,13 +33,23 @@ static bool manip_elf(ElfW(Ehdr)* elf, const char* symname) {
ElfW(Phdr)* phdr = (ElfW(Phdr)*)(elf->e_phoff + (size_t)elf), *ph_loadexec;
ElfW(Dyn)* dyn = NULL;
bool textrel = false;
enum {
libc_unk, libc_g /* glibc */, libc_musl, libc_uc /* uclibc */
} libc = libc_unk;
// first, try to determine the libc used
for (size_t i = 0; i < elf->e_phnum;
++i, phdr = (ElfW(Phdr)*)((size_t)phdr + elf->e_phentsize)) {
if (phdr->p_type == PT_LOAD) {
// need this at runtime for the relocs to work (yeah, sorry)
if (phdr->p_flags & PF_X) {
ph_loadexec = phdr;
phdr->p_flags |= PF_W;
if (phdr->p_type == PT_INTERP) {
const char* str = (char*)elf + phdr->p_offset;
if (!strcmp(str, "/lib64/ld-linux-x86-64.so.2")) {
libc = libc_g;
printf("\tglibc\n");
} else if (!strcmp(str, "/lib/ld-musl-x86_64.so.1")) {
libc = libc_musl;
printf("\tmusl\n");
} else {
printf("\tunknown libc: '%s'\n", str);
}
}
@ -49,6 +59,55 @@ static bool manip_elf(ElfW(Ehdr)* elf, const char* symname) {
}
}
if (libc == libc_unk) {
ElfW(Dyn)* dd = dyn;
const char* ddstr = NULL;
for (size_t i = 0; dd->d_tag != DT_NULL; ++i, ++dd) {
if (dd->d_tag == DT_STRTAB)
ddstr = (const char*)((size_t)elf + dd->d_un.d_ptr);
}
dd = dyn;
for (size_t i = 0; dd->d_tag != DT_NULL; ++i, ++dd) {
if (dd->d_tag == DT_NEEDED) {
const char* s = ddstr + dd->d_un.d_val;
if (strstr(s, "libc.so") != s) continue;
if (!strcmp(s, "libc.so.6")) {
libc = libc_g;
printf("\tglibc\n");
break;
} else if (!strcmp(s, "libc.so.0")) {
libc = libc_uc;
printf("\tuclibc\n");
break;
} else if (!strcmp(s, "libc.so")) {
libc = libc_musl;
printf("\tmusl\n");
break;
} else {
printf("\tunknown libc: '%s'\n", s);
}
}
}
}
phdr = (ElfW(Phdr)*)(elf->e_phoff + (size_t)elf);
for (size_t i = 0; i < elf->e_phnum;
++i, phdr = (ElfW(Phdr)*)((size_t)phdr + elf->e_phentsize)) {
if (phdr->p_type == PT_LOAD) {
// need this at runtime for the relocs to work (yeah, sorry)
if (phdr->p_flags & PF_X) {
ph_loadexec = phdr;
if (libc == libc_g || libc == libc_uc) {
textrel = true;
} else {
phdr->p_flags |= PF_W;
}
}
}
}
if (dyn == NULL) {
fprintf(stderr, "No DYNAMIC table! bailing out...\n");
return true;
@ -63,6 +122,7 @@ static bool manip_elf(ElfW(Ehdr)* elf, const char* symname) {
ElfW(Addr) relaoff = 0;
for (size_t i = 0; dyn->d_tag != DT_NULL; ++i, ++dyn) {
if (dyn->d_tag == DT_TEXTREL) textrel = false;
if (dyn->d_tag == DT_RELA) {
relatab = (ElfW(Rela)*)((size_t)elf + dyn->d_un.d_ptr);
relaoff = dyn->d_un.d_ptr;
@ -80,6 +140,10 @@ static bool manip_elf(ElfW(Ehdr)* elf, const char* symname) {
if (dyn->d_tag == DT_SYMENT) syment = dyn->d_un.d_val;
if (dyn->d_tag == DT_STRSZ ) strsz = dyn->d_un.d_val;
}
if (textrel) {
dyn->d_tag = DT_TEXTREL; // need this one
dyn[1].d_tag = DT_NULL;
}
if (!relasz || !relaent || !relatab) {
fprintf(stderr, "No RELA/RELASZ/RELAENT in DYN!\n");