From 06fae0591052ae0a8a4bcbabce89d506cd5336fe Mon Sep 17 00:00:00 2001 From: John Reiser Date: Wed, 22 Mar 2023 10:44:17 -0700 Subject: [PATCH] 32-bit ARM Android shlib: DT_INIT_ARRAY without DT_INIT De-compressor needs to catch up. modified: p_elf_enum.h modified: p_lx_elf.cpp modified: p_lx_elf.h modified: stub/src/i386-linux.elf-so_main.c --- src/p_elf_enum.h | 6 ++ src/p_lx_elf.cpp | 125 +++++++++++++++++++------- src/p_lx_elf.h | 10 ++- src/stub/src/i386-linux.elf-so_main.c | 47 ++++------ 4 files changed, 124 insertions(+), 64 deletions(-) diff --git a/src/p_elf_enum.h b/src/p_elf_enum.h index 31ff0b17..6769f8be 100644 --- a/src/p_elf_enum.h +++ b/src/p_elf_enum.h @@ -229,6 +229,12 @@ #undef WANT_REL_ENUM static inline unsigned ELF32_R_TYPE(unsigned x) { return 0xff & x; } static inline unsigned ELF64_R_TYPE(upx_uint64_t x) { return 0xffffffff & (unsigned)x; } + static inline unsigned ELF32_R_SYM(unsigned x) { return x >> 8; } + static inline unsigned ELF64_R_SYM(upx_uint64_t x) { return x >> 32; } + static inline unsigned ELF32_R_INFO(unsigned sym, unsigned type) + { return (sym << 8) + (type & 0xff); } + static inline upx_int64_t ELF64_R_INFO(unsigned sym, unsigned type) + { return ((upx_uint64_t)sym << 32) + type; } # undef R_PPC_RELATIVE # undef R_PPC64_RELATIVE diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index e8873880..132a18ea 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -252,6 +252,7 @@ PackLinuxElf::PackLinuxElf(InputFile *f) { memset(dt_table, 0, sizeof(dt_table)); symnum_end = 0; + user_init_rp = nullptr; } PackLinuxElf::~PackLinuxElf() @@ -346,7 +347,7 @@ PackLinuxElf32::PackLinuxElf32help1(InputFile *f) for (int j = e_phnum; --j>=0; ++phdr) if (Elf32_Phdr::PT_DYNAMIC==get_te32(&phdr->p_type)) { unsigned offset = check_pt_dynamic(phdr); - dynseg= (Elf32_Dyn const *)(offset + file_image); + dynseg= (Elf32_Dyn *)(offset + file_image); invert_pt_dynamic(dynseg, umin(get_te32(&phdr->p_filesz), file_size - offset)); } @@ -426,7 +427,6 @@ off_t PackLinuxElf::pack3(OutputFile *fo, Filter &ft) // return length of output super::pack3(fo, ft); // append the decompressor set_te16(&linfo.l_lsize, up4( // MATCH03: up4 get_te16(&linfo.l_lsize) + len - sz_pack2a)); - total_out = fpad4(fo, total_out); // MATCH03 return total_out; } @@ -525,10 +525,13 @@ off_t PackLinuxElf32::pack3(OutputFile *fo, Filter &ft) elfout.phdr[C_TEXT].p_paddr = elfout.phdr[C_TEXT].p_vaddr; set_te32(&elfout.ehdr.e_entry, abrk + get_te32(&elfout.ehdr.e_entry) - vbase); } - if (0!=xct_off) { // shared library + if (0!=xct_off) { // shared library unsigned word = (Elf32_Ehdr::EM_ARM==e_machine) + load_va + sz_pack2; // Thumb mode set_te32(&file_image[user_init_off], word); // set the hook + Elf32_Dyn *dynp = (Elf32_Dyn *)elf_find_dynptr(Elf32_Dyn::DT_NULL); + set_te32(&dynp->d_val, (char *)user_init_rp - (char *)&file_image[0]); + Elf32_Phdr *const phdr0 = (Elf32_Phdr *)lowmem.subref( "bad e_phoff", e_phoff, e_phnum * sizeof(Elf32_Phdr)); Elf32_Phdr *phdr = phdr0; @@ -584,6 +587,13 @@ off_t PackLinuxElf32::pack3(OutputFile *fo, Filter &ft) fo->write(&file_image[ioff], len); off += len; total_out = off; + + if ((user_init_off - ioff) < len) { + fo->seek(user_init_off + so_slide, SEEK_SET); + set_te32(&word, word); + fo->rewrite(&word, sizeof(word)); + fo->seek(0, SEEK_END); + } } continue; // all done with this PT_LOAD } @@ -617,7 +627,7 @@ off_t PackLinuxElf32::pack3(OutputFile *fo, Filter &ft) fo->seek(new_off, SEEK_SET); fo->rewrite(&file_image[old_off], n_jmp_slot * 4); } - } + } } fo->seek(total_out, SEEK_SET); if (xct_off < e_shoff) { @@ -975,7 +985,7 @@ PackLinuxElf64::PackLinuxElf64help1(InputFile *f) for (int j = e_phnum; --j>=0; ++phdr) if (Elf64_Phdr::PT_DYNAMIC==get_te32(&phdr->p_type)) { upx_uint64_t offset = check_pt_dynamic(phdr); - dynseg= (Elf64_Dyn const *)(offset + file_image); + dynseg= (Elf64_Dyn *)(offset + file_image); invert_pt_dynamic(dynseg, umin(get_te64(&phdr->p_filesz), file_size - offset)); } @@ -2530,6 +2540,7 @@ bool PackLinuxElf32::canPack() sec_strndx = nullptr; shstrtab = nullptr; if (e_shnum) { + e_shstrndx = get_te16(&ehdr->e_shstrndx); if (e_shstrndx) { if (e_shnum <= e_shstrndx) { char msg[40]; snprintf(msg, sizeof(msg), @@ -2573,7 +2584,7 @@ bool PackLinuxElf32::canPack() for (int j= e_phnum; --j>=0; ++phdr) if (Elf32_Phdr::PT_DYNAMIC==get_te32(&phdr->p_type)) { unsigned offset = check_pt_dynamic(phdr); - dynseg= (Elf32_Dyn const *)(offset + file_image); + dynseg= (Elf32_Dyn *)(offset + file_image); invert_pt_dynamic(dynseg, umin(get_te32(&phdr->p_filesz), file_size - offset)); } @@ -2651,10 +2662,11 @@ bool PackLinuxElf32::canPack() xct_va = umin(xct_va, get_te32(&shdr->sh_addr)); } // Hook the first slot of DT_PREINIT_ARRAY or DT_INIT_ARRAY. - if (( Elf32_Dyn::DT_PREINIT_ARRAY==upx_dt_init + if (!user_init_rp && ( + ( Elf32_Dyn::DT_PREINIT_ARRAY==upx_dt_init && Elf32_Shdr::SHT_PREINIT_ARRAY==sh_type) || ( Elf32_Dyn::DT_INIT_ARRAY ==upx_dt_init - && Elf32_Shdr::SHT_INIT_ARRAY ==sh_type) ) { + && Elf32_Shdr::SHT_INIT_ARRAY ==sh_type) )) { unsigned user_init_ava = get_te32(&shdr->sh_addr); user_init_off = get_te32(&shdr->sh_offset); if ((u32_t)file_size <= user_init_off) { @@ -2664,7 +2676,7 @@ bool PackLinuxElf32::canPack() throwCantPack(msg); } // Check that &file_image[user_init_off] has - // *_RELATIVE relocation, and fetch user_init_va. + // *_RELATIVE or *_ABS* relocation, and fetch user_init_va. // If Elf32_Rela then the actual value is in Rela.r_addend. int z_rel = dt_table[Elf32_Dyn::DT_REL]; int z_rsz = dt_table[Elf32_Dyn::DT_RELSZ]; @@ -2688,13 +2700,39 @@ bool PackLinuxElf32::canPack() for (; rp < last; ++rp) { unsigned r_va = get_te32(&rp->r_offset); if (r_va == user_init_ava) { // found the Elf32_Rel + user_init_rp = rp; unsigned r_info = get_te32(&rp->r_info); unsigned r_type = ELF32_R_TYPE(r_info); - if ((Elf32_Ehdr::EM_ARM == e_machine && R_ARM_RELATIVE == r_type) - || (Elf32_Ehdr::EM_386 == e_machine && R_386_RELATIVE == r_type) ) { - user_init_va = get_te32(&file_image[user_init_off]); + if (Elf32_Ehdr::EM_ARM == e_machine) { + if (R_ARM_RELATIVE == r_type) { + user_init_va = get_te32(&file_image[user_init_off]); + } + else if (R_ARM_ABS32 == r_type) { + unsigned symj = ELF32_R_SYM(r_info); + user_init_va = get_te32(&dynsym[symj].st_value); + set_te32(&rp->r_info, ELF32_R_INFO(0, R_ARM_RELATIVE)); + // pack3() will set &file_image[user_init_off] + } + else { + goto bad; + } + } + else if (Elf32_Ehdr::EM_386 == e_machine) { + if (R_386_RELATIVE == r_type) { + user_init_va = get_te32(&file_image[user_init_off]); + } + else if (R_386_32 == r_type) { + unsigned symj = ELF32_R_SYM(r_info); + user_init_va = get_te32(&dynsym[symj].st_value); + set_te32(&rp->r_info, ELF32_R_INFO(0, R_386_RELATIVE)); + // pack3() will set &file_image[user_init_off] + } + else { + goto bad; + } } else { +bad: char msg[50]; snprintf(msg, sizeof(msg), "bad relocation %#x DT_INIT_ARRAY[0]", r_info); @@ -2847,9 +2885,6 @@ PackLinuxElf64::canPack() } u; COMPILE_TIME_ASSERT(sizeof(u) <= (2*1024)) - // See explanation at PackLinuxElf32::canPack - opt->o_unix.android_shlib = 0; - fi->readx(u.buf, sizeof(u.buf)); fi->seek(0, SEEK_SET); Elf64_Ehdr const *const ehdr = (Elf64_Ehdr *) u.buf; @@ -2972,7 +3007,7 @@ PackLinuxElf64::canPack() for (int j= e_phnum; --j>=0; ++phdr) if (Elf64_Phdr::PT_DYNAMIC==get_te32(&phdr->p_type)) { upx_uint64_t offset = check_pt_dynamic(phdr); - dynseg= (Elf64_Dyn const *)(offset + file_image); + dynseg= (Elf64_Dyn *)(offset + file_image); invert_pt_dynamic(dynseg, umin(get_te64(&phdr->p_filesz), file_size - offset)); } @@ -4901,14 +4936,26 @@ int PackLinuxElf32::pack2(OutputFile *fo, Filter &ft) } else { // Just copy up to xct_off x.size = umin(x.size, xct_off - x.offset); - fi->seek(x.offset, SEEK_SET); - fi->readx(ibuf, x.size); - total_in += x.size; + if (0) { // DEBUG paranoia + fi->seek(x.offset, SEEK_SET); + fi->readx(ibuf, x.size); + total_in += x.size; + unsigned const *fip = (unsigned const *)file_image.getVoidPtr(); + unsigned const *ibp = (unsigned const *)ibuf.getVoidPtr(); + for (unsigned j = 0; j < x.size>>2; ++j) { + if (fip[j] != ibp[j]) { + printf("[%#x]: file_image %#x ibuf %#x\n", j, fip[j], ibp[j]); + } + } + } // FIXME: adler2 ? fo->seek(x.offset, SEEK_SET); - fo->write(ibuf, x.size); + fo->write(&file_image[x.offset], x.size); total_out += x.size; + // Kepp the input side in sync + total_in += x.size; + fi->seek(x.size + x.offset, SEEK_SET); } if (hdr_u_len) { // first time linfo.l_checksum = 0; // preliminary @@ -5537,7 +5584,7 @@ void PackLinuxElf32::pack4(OutputFile *fo, Filter &ft) fo->rewrite(&tmp, sizeof(tmp)); } } - else { + else { // not shlib Elf32_Phdr *phdr = &elfout.phdr[C_NOTE]; if (PT_NOTE32== get_te32(&phdr->p_type)) { upx_uint32_t const reloc = get_te32(&elfout.phdr[C_TEXT].p_vaddr); @@ -5851,6 +5898,8 @@ PackLinuxElf32::un_asl_dynsym( // ibuf has the input // overlay_offset: // 5. p_info (12 bytes) // 6. compressed original Elf headers (prefixed by b_info as usual) +// 6a. un-compressed copy of input after Elf headers until xct_off. +// *user_init_rp has been modified if no DT_INIT // 7. compressed remainder of PT_LOAD above xct_off // 8. compressed read-only PT_LOAD above xct_off (if any) // FIXME: check decompressor // 9. uncompressed Read-Write PT_LOAD (slide down N pages) @@ -7035,18 +7084,32 @@ PackLinuxElf32::check_pt_dynamic(Elf32_Phdr const *const phdr) return t; } -void const * +Elf32_Dyn *PackLinuxElf32::elf_find_dynptr(unsigned int key) const +{ + Elf32_Dyn *dynp= dynseg; + if (dynp) { + Elf32_Dyn *const last = (Elf32_Dyn *)(sz_dynseg + (char *)dynseg); + for (; dynp < last; ++dynp) { + if (get_te32(&dynp->d_tag)==key) { + return dynp; + } + if (Elf32_Dyn::DT_NULL == dynp->d_tag) { + return nullptr; + } + } + } + return nullptr; +} + +void * PackLinuxElf32::elf_find_dynamic(unsigned int key) const { - Elf32_Dyn const *dynp= dynseg; - if (dynp) - for (; (unsigned)((char const *)dynp - (char const *)dynseg) < sz_dynseg - && Elf32_Dyn::DT_NULL!=dynp->d_tag; ++dynp) if (get_te32(&dynp->d_tag)==key) { + Elf32_Dyn const *dynp= elf_find_dynptr(key); + if (dynp) { unsigned const t= elf_get_offset_from_address(get_te32(&dynp->d_val)); if (t && t < (unsigned)file_size) { return t + file_image; } - break; } return nullptr; } @@ -7054,10 +7117,8 @@ PackLinuxElf32::elf_find_dynamic(unsigned int key) const upx_uint64_t PackLinuxElf32::elf_unsigned_dynamic(unsigned int key) const { - Elf32_Dyn const *dynp= dynseg; - if (dynp) - for (; (unsigned)((char const *)dynp - (char const *)dynseg) < sz_dynseg - && Elf32_Dyn::DT_NULL!=dynp->d_tag; ++dynp) if (get_te32(&dynp->d_tag)==key) { + Elf32_Dyn const *dynp= elf_find_dynptr(key); + if (dynp) { return get_te32(&dynp->d_val); } return 0; @@ -7396,7 +7457,7 @@ PackLinuxElf64::invert_pt_dynamic(Elf64_Dyn const *dynp, upx_uint64_t headway) } } -void const * +void * PackLinuxElf64::elf_find_dynamic(unsigned int key) const { Elf64_Dyn const *dynp= dynseg; diff --git a/src/p_lx_elf.h b/src/p_lx_elf.h index 7809b74d..70227a6e 100644 --- a/src/p_lx_elf.h +++ b/src/p_lx_elf.h @@ -104,6 +104,7 @@ protected: upx_uint64_t xct_va; // minimum SHT_EXECINSTR virtual address upx_uint64_t jni_onload_va; // runtime &JNI_OnLoad upx_uint64_t user_init_va; + void *user_init_rp; // Elf32_Rel *, Elf64_Rela *, ... upx_uint64_t plt_va, plt_off; unsigned user_init_off; // within file_image unsigned linfo_off; @@ -200,6 +201,7 @@ protected: Elf32_Phdr const *elf_find_ptype(unsigned type, Elf32_Phdr const *phdr0, unsigned phnum); Elf32_Shdr const *elf_find_section_name(char const *) const; Elf32_Shdr *elf_find_section_type(unsigned) const; + Elf32_Dyn *elf_find_dynptr(unsigned) const; unsigned elf_find_table_size(unsigned dt_type, unsigned sh_type); void sort_DT32_offsets(Elf32_Dyn const *const dynp0); @@ -207,7 +209,7 @@ protected: unsigned check_pt_load(Elf32_Phdr const *); unsigned check_pt_dynamic(Elf32_Phdr const *); void invert_pt_dynamic(Elf32_Dyn const *, unsigned dt_filesz); - void const *elf_find_dynamic(unsigned) const; + void *elf_find_dynamic(unsigned) const; Elf32_Dyn const *elf_has_dynamic(unsigned) const; virtual upx_uint64_t elf_unsigned_dynamic(unsigned) const override; unsigned find_dt_ndx(unsigned rva); @@ -228,7 +230,7 @@ protected: unsigned plt_off; unsigned page_mask; // AND clears the offset-within-page - Elf32_Dyn const *dynseg; // from PT_DYNAMIC + Elf32_Dyn *dynseg; // from PT_DYNAMIC unsigned int const *hashtab, *hashend; // from DT_HASH unsigned int const *gashtab, *gashend; // from DT_GNU_HASH Elf32_Sym const *dynsym; // from DT_SYMTAB @@ -360,7 +362,7 @@ protected: upx_uint64_t check_pt_load(Elf64_Phdr const *); upx_uint64_t check_pt_dynamic(Elf64_Phdr const *); void invert_pt_dynamic(Elf64_Dyn const *, upx_uint64_t dt_filesz); - void const *elf_find_dynamic(unsigned) const; + void *elf_find_dynamic(unsigned) const; Elf64_Dyn const *elf_has_dynamic(unsigned) const; virtual upx_uint64_t elf_unsigned_dynamic(unsigned) const override; unsigned find_dt_ndx(u64_t rva); @@ -380,7 +382,7 @@ protected: unsigned n_jmp_slot; upx_uint64_t page_mask; // AND clears the offset-within-page - Elf64_Dyn const *dynseg; // from PT_DYNAMIC + Elf64_Dyn *dynseg; // from PT_DYNAMIC unsigned int const *hashtab, *hashend; // from DT_HASH unsigned int const *gashtab, *gashend; // from DT_GNU_HASH Elf64_Sym const *dynsym; // from DT_SYMTAB diff --git a/src/stub/src/i386-linux.elf-so_main.c b/src/stub/src/i386-linux.elf-so_main.c index 4ae19c35..b5566a14 100644 --- a/src/stub/src/i386-linux.elf-so_main.c +++ b/src/stub/src/i386-linux.elf-so_main.c @@ -34,7 +34,7 @@ extern void my_bkpt(void const *arg1, ...); -#define DEBUG 0 +#define DEBUG 1 void *mmap(void *, size_t, int, int, int, off_t); #if defined(__i386__) || defined(__mips__) || defined(__powerpc__) //{ @@ -525,37 +525,26 @@ upx_so_main( // returns &escape_hatch Extent x0 = {binfo->sz_cpr + sizeof(*binfo), (char *)binfo}; // source unpackExtent(&x0, &x1); // de-compress Elf headers; x0.buf is updated - // Count PT_LOAD; n_LOAD < 3 is special (old binutils PT_LOAD layout) - unsigned n_phdr; - Elf32_Phdr const *phdr; - n_phdr = ((Elf32_Ehdr *)(void *)va_load)->e_phnum; - phdr = (Elf32_Phdr *)(1+ (Elf32_Ehdr *)(void *)va_load); - unsigned n_LOAD = 0; - for (; n_phdr > 0; --n_phdr, ++phdr) { - n_LOAD += (PT_LOAD == phdr->p_type); - } - // Old-style binutils with only 2 PT_LOAD: (r-x) and (rw-) - // has xct_off in middle of first PT_LOAD. - // New-style binutils has xct-off at beginning of 2nd PT_LOAD. - Elf32_Addr pfx = (n_LOAD <= 2) ? so_infc.off_xct_off : 0; - // Process each read-only PT_LOAD. // A read+write PT_LOAD might be relocated by rtld before de-compression, // so it cannot be compressed. struct b_info al_bi; // for aligned data from binfo void *hatch = nullptr; - unsigned not_first = 0; - n_phdr = ((Elf32_Ehdr *)(void *)va_load)->e_phnum; - phdr = (Elf32_Phdr *)(1+ (Elf32_Ehdr *)(void *)va_load); + Elf32_Phdr const *phdr = (Elf32_Phdr *)(1+ (Elf32_Ehdr *)(void *)va_load); + unsigned n_phdr = ((Elf32_Ehdr *)(void *)va_load)->e_phnum; for (; n_phdr > 0; --n_phdr, ++phdr) - if ( phdr->p_type == PT_LOAD - && !(phdr->p_flags & PF_W) - && (not_first++ || n_LOAD < 3) - ) { - DPRINTF("phdr@%%p .p_vaddr=%%p .p_filesz=%%p .p_memsz=%%p n_LOAD=%%p binfo=%%p\\n", - phdr, phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz, n_LOAD, x0.buf); + if ( phdr->p_type == PT_LOAD && !(phdr->p_flags & PF_W)) { + DPRINTF("phdr@%%p p_offset=%%p p_vaddr=%%p p_filesz=%%p p_memsz=%%p binfo=%%p\\n", + phdr, phdr->p_offset, phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz, x0.buf); + if ((phdr->p_filesz + phdr->p_offset) <= so_infc.off_xct_off) { + continue; // below compressed region + } + Elf32_Addr pfx = so_infc.off_xct_off - phdr->p_offset; + if ( so_infc.off_xct_off < phdr->p_offset) { + pfx = 0; // no more partially-compressed PT_LOAD + } x0.size = sizeof(struct b_info); xread(&x0, (char *)&al_bi, x0.size); // aligned binfo x0.buf -= sizeof(al_bi); // back up (the xread() was a peek) @@ -563,9 +552,11 @@ upx_so_main( // returns &escape_hatch al_bi.sz_unc, al_bi.sz_cpr, *(unsigned *)(void *)&al_bi.b_method); // Using .p_memsz implicitly handles .bss via MAP_ANONYMOUS. + // Omit any not-compressed prefix (below xct_off) x1.buf = phdr->p_vaddr + pfx + va_load; x1.size = phdr->p_memsz - pfx; - pfx = (phdr->p_vaddr + pfx) & ~PAGE_MASK; + + pfx = (phdr->p_vaddr + pfx) & ~PAGE_MASK; // lo fragment on page x1.buf -= pfx; x1.size += pfx; DPRINTF("mmap(%%p %%p) xct_off=%%x pfx=%%x\\n", x1.buf, x1.size, xct_off, pfx); @@ -575,7 +566,6 @@ upx_so_main( // returns &escape_hatch x1.size = al_bi.sz_unc; x0.size = al_bi.sz_cpr + sizeof(struct b_info); unpackExtent(&x0, &x1); // updates x0 and x1 - pfx = 0; // consider xct_off at most once if (!hatch && phdr->p_flags & PF_X) { //#define PAGE_MASK ~0xFFFull @@ -587,8 +577,9 @@ upx_so_main( // returns &escape_hatch hatch = make_hatch_i386(phdr, (Elf32_Addr)va_load); #endif //} } - DPRINTF("mprotect %%p (%%p %%p)\\n", phdr, phdr->p_vaddr + va_load, phdr->p_memsz); - mprotect(phdr->p_vaddr + va_load, phdr->p_memsz, PF_TO_PROT(phdr->p_flags)); + DPRINTF("mprotect %%p (%%p %%p %%x)\\n", + phdr, phdr->p_vaddr + va_load, phdr->p_memsz, PF_TO_PROT(phdr->p_flags)); + mprotect( phdr->p_vaddr + va_load, phdr->p_memsz, PF_TO_PROT(phdr->p_flags)); } typedef void (*Dt_init)(int argc, char *argv[], char *envp[]);