From 35555bfb8a6344908b07bd33207f59975d271570 Mon Sep 17 00:00:00 2001 From: John Reiser Date: Fri, 5 Mar 2021 16:20:36 -0800 Subject: [PATCH] WIP: PackLinuxElf64::unpack working for shared libraries (non-Android) modified: p_lx_elf.cpp modified: p_lx_elf.h modified: p_unix.cpp --- src/p_lx_elf.cpp | 224 ++++++++++++++++++++++------------------------- src/p_lx_elf.h | 5 +- src/p_unix.cpp | 4 +- 3 files changed, 109 insertions(+), 124 deletions(-) diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index e6606d37..05ff7b3c 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -4568,6 +4568,7 @@ PackLinuxElf64::unRela64( void PackLinuxElf64::un_shlib_1( OutputFile *const fo, + Elf64_Phdr *const phdro, unsigned &c_adler, unsigned &u_adler, Elf64_Phdr const *const dynhdr, @@ -4637,15 +4638,16 @@ void PackLinuxElf64::un_shlib_1( c_adler, u_adler, false, szb_info); // Copy (slide) the remaining PT_LOAD; they are not compressed - Elf64_Phdr *phdr = phdri; + Elf64_Phdr *phdr = phdro; unsigned slide = 0; int first = 0; for (unsigned k = 0; k < e_phnum; ++k, ++phdr) { - if (PT_LOAD64==get_te32(&phdr->p_type)) { - unsigned vaddr = get_te64(&phdr->p_vaddr); - if (xct_off <= vaddr) { - unsigned offset = get_te64(&phdr->p_offset); - unsigned filesz = get_te64(&phdr->p_filesz); + unsigned type = get_te32(&phdr->p_type); + unsigned vaddr = get_te64(&phdr->p_vaddr); + unsigned offset = get_te64(&phdr->p_offset); + unsigned filesz = get_te64(&phdr->p_filesz); + if (xct_off <= vaddr) { + if (PT_LOAD64==type) { if (0==first++) { // the partially-compressed PT_LOAD set_te64(&phdr->p_filesz, ph.u_len + xct_off - vaddr); set_te64(&phdr->p_memsz, ph.u_len + xct_off - vaddr); @@ -4653,134 +4655,111 @@ void PackLinuxElf64::un_shlib_1( else { // subsequent PT_LOAD fi->seek(offset, SEEK_SET); fi->readx(ibuf, filesz); + total_in += filesz; if (0==slide) { slide = vaddr - offset; } - set_te64(&phdr->p_offset, slide + offset); - fo->seek(slide + offset, SEEK_SET); - fo->write(ibuf, filesz); + if (fo) { + fo->seek(slide + offset, SEEK_SET); + fo->write(ibuf, filesz); + total_out = filesz + slide + offset; // high-water mark + } } } + set_te64(&phdr->p_offset, slide + offset); } } - fi->seek(xct_off + sizeof(linfo) + sizeof(p_info) + sizeof(b_info) + up4(ph.c_len), SEEK_SET); // loader offset + if (fo) { // Rewrite Phdrs after sliding + fo->seek(sizeof(Elf64_Ehdr), SEEK_SET); + fo->rewrite(phdro, e_phnum * sizeof(Elf64_Phdr)); + } + // loader offset + fi->seek(xct_off + sizeof(linfo) + sizeof(p_info) + sizeof(b_info) + up4(ph.c_len), SEEK_SET); } void PackLinuxElf64::un_DT_INIT( - Elf64_Phdr const *phdr0, - unsigned u_phnum, unsigned old_dtinit, + Elf64_Phdr const *const phdro, + Elf64_Phdr const *const dynhdr, // in phdri OutputFile *fo, unsigned is_asl ) { // DT_INIT must be restored. - // Search the Phdrs of compressed - int n_ptload = 0; - old_data_off = 0; - old_data_len = 0; - Elf64_Phdr const *phdr = phdr0; - for (unsigned j=0; j < u_phnum; ++phdr, ++j) { - if (PT_LOAD64==get_te32(&phdr->p_type) && 0!=n_ptload++) { - old_data_off = get_te64(&phdr->p_offset); - old_data_len = get_te64(&phdr->p_filesz); - break; - } - } // If android_shlib, then the asl_delta relocations must be un-done. - upx_uint64_t load_off = 0; - phdr = phdr0; - n_ptload = 0; - for (unsigned j= 0; j < u_phnum; ++j, ++phdr) { - if (PT_LOAD64==get_te32(&phdr->p_type) && 0!=n_ptload++) { - load_off = get_te64(&phdr->p_offset); - load_va = get_te64(&phdr->p_vaddr); - fi->seek(old_data_off, SEEK_SET); - fi->readx(ibuf, old_data_len); - total_in += old_data_len; - total_out += old_data_len; + upx_uint64_t dt_pltrelsz(0), dt_jmprel(0); + upx_uint64_t dt_relasz(0), dt_rela(0); + upx_uint64_t const dyn_len = get_te64(&dynhdr->p_filesz); + upx_uint64_t const dyn_off = get_te64(&dynhdr->p_offset); + if ((unsigned long)file_size < (dyn_len + dyn_off)) { + char msg[50]; snprintf(msg, sizeof(msg), + "bad PT_DYNAMIC .p_filesz %#lx", (long unsigned)dyn_len); + throwCantUnpack(msg); + } + fi->seek(dyn_off, SEEK_SET); + fi->readx(ibuf, dyn_len); + Elf64_Dyn *dyn = (Elf64_Dyn *)(void *)ibuf; + dynseg = dyn; invert_pt_dynamic(dynseg, get_te64(&dynhdr->p_filesz)); + for (unsigned j2= 0; j2 < dyn_len; ++dyn, j2 += sizeof(*dyn)) { + upx_uint64_t const tag = get_te64(&dyn->d_tag); + upx_uint64_t val = get_te64(&dyn->d_val); + if (is_asl) switch (tag) { + case Elf64_Dyn::DT_RELASZ: { dt_relasz = val; } break; + case Elf64_Dyn::DT_RELA: { dt_rela = val; } break; + case Elf64_Dyn::DT_PLTRELSZ: { dt_pltrelsz = val; } break; + case Elf64_Dyn::DT_JMPREL: { dt_jmprel = val; } break; - Elf64_Phdr const *udynhdr = phdr0; - for (unsigned j3= 0; j3 < u_phnum; ++j3, ++udynhdr) - if (Elf64_Phdr::PT_DYNAMIC==get_te32(&udynhdr->p_type)) { - upx_uint64_t dt_pltrelsz(0), dt_jmprel(0); - upx_uint64_t dt_relasz(0), dt_rela(0); - upx_uint64_t const dyn_len = get_te64(&udynhdr->p_filesz); - upx_uint64_t const dyn_off = get_te64(&udynhdr->p_offset); - if ((unsigned long)file_size < (dyn_len + dyn_off)) { - char msg[50]; snprintf(msg, sizeof(msg), - "bad PT_DYNAMIC .p_filesz %#lx", (long unsigned)dyn_len); - throwCantUnpack(msg); - } - if (dyn_off < load_off) { - continue; // Oops. Not really is_shlib ? [built by 'rust' ?] - } - Elf64_Dyn *dyn = (Elf64_Dyn *)((unsigned char *)ibuf + - (dyn_off - load_off)); - dynseg = dyn; invert_pt_dynamic(dynseg, get_te64(&udynhdr->p_filesz)); - for (unsigned j2= 0; j2 < dyn_len; ++dyn, j2 += sizeof(*dyn)) { - upx_uint64_t const tag = get_te64(&dyn->d_tag); - upx_uint64_t val = get_te64(&dyn->d_val); - if (is_asl) switch (tag) { - case Elf64_Dyn::DT_RELASZ: { dt_relasz = val; } break; - case Elf64_Dyn::DT_RELA: { dt_rela = val; } break; - case Elf64_Dyn::DT_PLTRELSZ: { dt_pltrelsz = val; } break; - case Elf64_Dyn::DT_JMPREL: { dt_jmprel = val; } break; - - case Elf64_Dyn::DT_PLTGOT: - case Elf64_Dyn::DT_PREINIT_ARRAY: - case Elf64_Dyn::DT_INIT_ARRAY: - case Elf64_Dyn::DT_FINI_ARRAY: - case Elf64_Dyn::DT_FINI: { - set_te64(&dyn->d_val, val - asl_delta); - }; break; - } // end switch() - if (upx_dt_init == tag) { - if (Elf64_Dyn::DT_INIT == tag) { - set_te64(&dyn->d_val, old_dtinit); - if (!old_dtinit) { // compressor took the slot - dyn->d_tag = Elf64_Dyn::DT_NULL; - dyn->d_val = 0; - } - } - else if (Elf64_Dyn::DT_INIT_ARRAY == tag - || Elf64_Dyn::DT_PREINIT_ARRAY == tag) { - if (val < load_va || (long unsigned)file_size < (long unsigned)val) { - char msg[50]; snprintf(msg, sizeof(msg), - "Bad Dynamic tag %#lx %#lx", - (long unsigned)tag, (long unsigned)val); - throwCantUnpack(msg); - } - set_te64(&ibuf[val - load_va], old_dtinit - + (is_asl ? asl_delta : 0)); // counter-act unRel64 - } - } - // Modified DT_*.d_val are re-written later from ibuf[] - } - if (is_asl) { - lowmem.alloc(xct_off); - fi->seek(0, SEEK_SET); - fi->read(lowmem, xct_off); // contains relocation tables - if (dt_relasz && dt_rela) { - Elf64_Rela *const rela0 = (Elf64_Rela *)lowmem.subref( - "bad Rela offset", dt_rela, dt_relasz); - unRela64(dt_rela, rela0, dt_relasz, ibuf, load_va, old_dtinit, fo); - } - if (dt_pltrelsz && dt_jmprel) { // FIXME: overlap w/ DT_REL ? - Elf64_Rela *const jmp0 = (Elf64_Rela *)lowmem.subref( - "bad Jmprel offset", dt_jmprel, dt_pltrelsz); - unRela64(dt_jmprel, jmp0, dt_pltrelsz, ibuf, load_va, old_dtinit, fo); - } - // Modified relocation tables are re-written by unRela64 - } - } - if (fo) { - fo->seek(get_te64(&phdr->p_offset), SEEK_SET); - fo->rewrite(ibuf, old_data_len); - } - } - } + case Elf64_Dyn::DT_PLTGOT: + case Elf64_Dyn::DT_PREINIT_ARRAY: + case Elf64_Dyn::DT_INIT_ARRAY: + case Elf64_Dyn::DT_FINI_ARRAY: + case Elf64_Dyn::DT_FINI: { + set_te64(&dyn->d_val, val - asl_delta); + }; break; + } // end switch() on tag when is_asl + if (upx_dt_init == tag) { + if (Elf64_Dyn::DT_INIT == tag) { + set_te64(&dyn->d_val, old_dtinit); + if (!old_dtinit) { // compressor took the slot + dyn->d_tag = Elf64_Dyn::DT_NULL; + dyn->d_val = 0; + } + } + else if (Elf64_Dyn::DT_INIT_ARRAY == tag + || Elf64_Dyn::DT_PREINIT_ARRAY == tag) { + if (val < load_va || (long unsigned)file_size < (long unsigned)val) { + char msg[50]; snprintf(msg, sizeof(msg), + "Bad Dynamic tag %#lx %#lx", + (long unsigned)tag, (long unsigned)val); + throwCantUnpack(msg); + } + set_te64(&ibuf[val - load_va], old_dtinit + + (is_asl ? asl_delta : 0)); // counter-act unRel64 + } + } + } + if (fo) { // Write updated dt_*.val + upx_uint64_t dyn_offo = get_te64(&phdro[dynhdr - phdri].p_offset); + fo->seek(dyn_offo, SEEK_SET); + fo->rewrite(ibuf, dyn_len); + } + if (is_asl) { + lowmem.alloc(xct_off); + fi->seek(0, SEEK_SET); + fi->read(lowmem, xct_off); // contains relocation tables + if (dt_relasz && dt_rela) { + Elf64_Rela *const rela0 = (Elf64_Rela *)lowmem.subref( + "bad Rela offset", dt_rela, dt_relasz); + unRela64(dt_rela, rela0, dt_relasz, ibuf, load_va, old_dtinit, fo); + } + if (dt_pltrelsz && dt_jmprel) { // FIXME: overlap w/ DT_REL ? + Elf64_Rela *const jmp0 = (Elf64_Rela *)lowmem.subref( + "bad Jmprel offset", dt_jmprel, dt_pltrelsz); + unRela64(dt_jmprel, jmp0, dt_pltrelsz, ibuf, load_va, old_dtinit, fo); + } + // Modified relocation tables are re-written by unRela64 + } } void PackLinuxElf64::unpack(OutputFile *fo) @@ -4834,6 +4813,7 @@ void PackLinuxElf64::unpack(OutputFile *fo) unsigned u_adler = upx_adler32(nullptr, 0); unsigned is_shlib = 0; + MemBuffer phdro; Elf64_Phdr const *const dynhdr = elf_find_ptype(Elf64_Phdr::PT_DYNAMIC, phdri, c_phnum); if (dynhdr) { upx_uint64_t dyn_offset = get_te64(&dynhdr->p_offset); @@ -4843,9 +4823,10 @@ void PackLinuxElf64::unpack(OutputFile *fo) if (!(Elf64_Dyn::DF_1_PIE & elf_unsigned_dynamic(Elf64_Dyn::DT_FLAGS_1))) { is_shlib = 1; u_phnum = get_te16(&ehdri.e_phnum); - un_shlib_1(fo, c_adler, u_adler, dynhdr, orig_file_size, szb_info); + phdro.alloc(u_phnum * sizeof(Elf64_Phdr)); + memcpy(phdro, phdri, u_phnum * sizeof(*phdri)); + un_shlib_1(fo, (Elf64_Phdr *)(void *)phdro, c_adler, u_adler, dynhdr, orig_file_size, szb_info); *ehdr = ehdri; - memcpy(1+ehdr, phdri, u_phnum * sizeof(*phdri)); } } else { // main executable @@ -4873,6 +4854,8 @@ void PackLinuxElf64::unpack(OutputFile *fo) if ((umin64(MAX_ELF_HDR, ph.u_len) - sizeof(Elf64_Ehdr))/sizeof(Elf64_Phdr) < u_phnum) { throwCantUnpack("bad compressed e_phnum"); } + phdro.alloc(u_phnum * sizeof(Elf64_Phdr)); + memcpy(phdro, 1+ ehdr, u_phnum * sizeof(Elf64_Phdr)); #undef MAX_ELF_HDR // Decompress each PT_LOAD. @@ -4926,7 +4909,7 @@ void PackLinuxElf64::unpack(OutputFile *fo) } // The gaps between PT_LOAD and after last PT_LOAD - phdr = (Elf64_Phdr *)&u[sizeof(*ehdr)]; + phdr = (Elf64_Phdr *)(void *)phdro; upx_uint64_t hi_offset(0); for (unsigned j = 0; j < u_phnum; ++j) { if (PT_LOAD64==phdr[j].p_type @@ -4942,7 +4925,8 @@ void PackLinuxElf64::unpack(OutputFile *fo) fo->seek(where, SEEK_SET); unpackExtent(size, fo, c_adler, u_adler, false, szb_info, - (phdr[j].p_offset != hi_offset)); + is_shlib && ((phdr[j].p_offset != hi_offset))); + // FIXME: should not depend on is_shlib ? } } @@ -4961,7 +4945,7 @@ void PackLinuxElf64::unpack(OutputFile *fo) } if (is_shlib) { - un_DT_INIT(phdr, u_phnum, old_dtinit, fo, is_asl); + un_DT_INIT(old_dtinit, (Elf64_Phdr *)(void *)phdro, dynhdr, fo, is_asl); } // update header with totals diff --git a/src/p_lx_elf.h b/src/p_lx_elf.h index 848a119e..8521e6f0 100644 --- a/src/p_lx_elf.h +++ b/src/p_lx_elf.h @@ -271,6 +271,7 @@ protected: virtual void unpack(OutputFile *fo); virtual void un_shlib_1( OutputFile *const fo, + Elf64_Phdr *const phdro, unsigned &c_adler, unsigned &u_adler, Elf64_Phdr const *const dynhdr, @@ -278,9 +279,9 @@ protected: unsigned const szb_info ); virtual void un_DT_INIT( - Elf64_Phdr const *phdr, - unsigned u_phnum, unsigned old_dtinit, + Elf64_Phdr const *phdro, + Elf64_Phdr const *dynhdr, // in phdri OutputFile *fo, unsigned is_asl ); diff --git a/src/p_unix.cpp b/src/p_unix.cpp index 79a5e0be..c7515b4b 100644 --- a/src/p_unix.cpp +++ b/src/p_unix.cpp @@ -470,6 +470,7 @@ void PackUnix::unpackExtent(unsigned wanted, OutputFile *fo, int j = blocksize + OVERHEAD - sz_cpr; fi->readx(ibuf+j, sz_cpr); + total_in += sz_cpr; // update checksum of compressed data c_adler = upx_adler32(ibuf + j, sz_cpr, c_adler); // decompress @@ -499,8 +500,6 @@ void PackUnix::unpackExtent(unsigned wanted, OutputFile *fo, } // update checksum of uncompressed data u_adler = upx_adler32(ibuf + j, sz_unc, u_adler); - total_in += sz_cpr; - total_out += sz_unc; // write block if (fo) { if (is_rewrite) { @@ -508,6 +507,7 @@ void PackUnix::unpackExtent(unsigned wanted, OutputFile *fo, } else { fo->write(ibuf + j, sz_unc); + total_out += sz_unc; } } if (wanted < (unsigned)sz_unc)