diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index 999f7fbd..bda0f78f 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -38,7 +38,8 @@ #include "p_lx_exc.h" #include "p_lx_elf.h" -#define PT_LOAD Elf32_Phdr::PT_LOAD +#define PT_LOAD32 Elf32_Phdr::PT_LOAD +#define PT_LOAD64 Elf64_Phdr::PT_LOAD int @@ -89,6 +90,62 @@ PackLinuxElf32::checkEhdr( return 0; } +int +PackLinuxElf64::checkEhdr( + Elf64_Ehdr const *ehdr, + unsigned char e_machine, + unsigned char ei_class, + unsigned char ei_data +) const +{ + const unsigned char * const buf = ehdr->e_ident; + + if (0!=memcmp(buf, "\x7f\x45\x4c\x46", 4) // "\177ELF" + || buf[Elf64_Ehdr::EI_CLASS]!=ei_class + || buf[Elf64_Ehdr::EI_DATA] !=ei_data ) { + return -1; + } + if (!memcmp(buf+8, "FreeBSD", 7)) // branded + return 1; + + if (get_native16(&ehdr->e_type) != Elf64_Ehdr::ET_EXEC) + return 2; + if (get_native16(&ehdr->e_machine) != e_machine) + return 3; + if (get_native32(&ehdr->e_version) != Elf64_Ehdr::EV_CURRENT) + return 4; + if (get_native16(&ehdr->e_phnum) < 1) + return 5; + if (get_native16(&ehdr->e_phentsize) != sizeof(Elf64_Phdr)) + return 6; + + // check for Linux kernels + acc_uint64l_t const entry = get_native64(&ehdr->e_entry); + if (entry == 0xC0100000) // uncompressed vmlinux + return 1000; + if (entry == 0x00001000) // compressed vmlinux + return 1001; + if (entry == 0x00100000) // compressed bvmlinux + return 1002; + + // FIXME: add more checks for kernels + + // FIXME: add special checks for other ELF i386 formats, like + // NetBSD, OpenBSD, Solaris, .... + + // success + return 0; +} + +PackLinuxElf::PackLinuxElf(InputFile *f) + : super(f) +{ +} + +PackLinuxElf::~PackLinuxElf() +{ +} + PackLinuxElf32::PackLinuxElf32(InputFile *f) : super(f), phdri(NULL) { @@ -99,8 +156,18 @@ PackLinuxElf32::~PackLinuxElf32() delete[] phdri; } +PackLinuxElf64::PackLinuxElf64(InputFile *f) + : super(f), phdri(NULL) +{ +} + +PackLinuxElf64::~PackLinuxElf64() +{ + delete[] phdri; +} + int const * -PackLinuxElf32::getCompressionMethods(int method, int level) const +PackLinuxElf::getCompressionMethods(int method, int level) const { // No real dependency on LE32. return Packer::getDefaultCompressionMethods_le32(method, level); @@ -119,6 +186,19 @@ UNUSED(method); UNUSED(level); UNUSED(m_nrv2b); return m_nrv2e; } +int const * +PackLinuxElf64amd::getCompressionMethods(int method, int level) const +{ + // No real dependency on LE32. + static const int m_nrv2e[] = { M_NRV2E_LE32, -1 }; + static const int m_nrv2b[] = { M_NRV2B_LE32, -1 }; + + /*return Packer::getDefaultCompressionMethods_le32(method, level);*/ + // 2005-04-23 FIXME: stub/l_lx_elfppc32.S hardwires ppc_d_nrv2e.S + UNUSED(method); UNUSED(level); UNUSED(m_nrv2b); + return m_nrv2e; +} + int const * PackLinuxElf32ppc::getFilters() const { @@ -128,7 +208,22 @@ PackLinuxElf32ppc::getFilters() const return filters; } -void PackLinuxElf32::patchLoader() { } +int const * +PackLinuxElf64amd::getFilters() const +{ + static const int filters[] = { + 0x49, -1 + }; + return filters; +} + +void PackLinuxElf32::patchLoader() +{ +} + +void PackLinuxElf64::patchLoader() +{ +} void PackLinuxElf32::updateLoader(OutputFile *fo) { @@ -136,16 +231,10 @@ void PackLinuxElf32::updateLoader(OutputFile *fo) get_native32(&elfout.phdr[0].p_vaddr)); } -int -PackLinuxElf32::buildLinuxLoader( - upx_byte const *const /*proto*/, - unsigned const /*szproto*/, - upx_byte const *const /*fold*/, - unsigned const /*szfold*/, - Filter const */*ft*/ -) +void PackLinuxElf64::updateLoader(OutputFile *fo) { - return 0; + set_native64(&elfout.ehdr.e_entry, fo->getBytesWritten() + + get_native64(&elfout.phdr[0].p_vaddr)); } PackLinuxElf32ppc::PackLinuxElf32ppc(InputFile *f) @@ -157,6 +246,15 @@ PackLinuxElf32ppc::~PackLinuxElf32ppc() { } +PackLinuxElf64amd::PackLinuxElf64amd(InputFile *f) + : super(f) +{ +} + +PackLinuxElf64amd::~PackLinuxElf64amd() +{ +} + static unsigned umax(unsigned a, unsigned b) { @@ -218,6 +316,55 @@ PackLinuxElf32ppc::buildLinuxLoader( return getLoaderSize(); } +int +PackLinuxElf64amd::buildLinuxLoader( + upx_byte const *const proto, + unsigned const szproto, + upx_byte const *const fold, + unsigned const szfold, + Filter const */*ft*/ +) +{ + int eof_empty = -1; + initLoader(&eof_empty, 9000, 0, 0); + + struct b_info h; memset(&h, 0, sizeof(h)); + unsigned fold_hdrlen = 0; + unsigned sz_unc=0, sz_cpr; + if (0 < szfold) { + cprElfHdr1 const *const hf = (cprElfHdr1 const *)fold; + fold_hdrlen = umax(0x80, sizeof(hf->ehdr) + + get_native16(&hf->ehdr.e_phentsize) * get_native16(&hf->ehdr.e_phnum) + + sizeof(l_info) ); + sz_unc = ((szfold < fold_hdrlen) ? 0 : (szfold - fold_hdrlen)); + set_native32(&h.sz_unc, ((szfold < fold_hdrlen) ? 0 : (szfold - fold_hdrlen))); + h.b_method = (unsigned char) ph.method; + h.b_ftid = (unsigned char) ph.filter; + h.b_cto8 = (unsigned char) ph.filter_cto; + } + unsigned char const *const uncLoader = fold_hdrlen + fold; + + unsigned char *const cprLoader = new unsigned char[sizeof(h) + sz_unc]; + if (0 < szfold) { + int r = upx_compress(uncLoader, sz_unc, sizeof(h) + cprLoader, &sz_cpr, + NULL, ph.method, 10, NULL, NULL ); + set_native32(&h.sz_cpr, sz_cpr); + if (r != UPX_E_OK || sz_cpr >= sz_unc) + throwInternalError("loader compression failed"); + } + memcpy(cprLoader, &h, sizeof(h)); + + // This adds the definition to the "library", to be used later. + linker->addSection("FOLDEXEC", cprLoader, sizeof(h) + sz_cpr); + delete [] cprLoader; + + linker->addSection("ELF64AMD", proto, szproto); + + addLoader("ELF64AMD", 0); + addLoader("FOLDEXEC", 0); + return getLoaderSize(); +} + static const #include "stub/l_lx_elfppc32.h" @@ -232,6 +379,20 @@ PackLinuxElf32ppc::buildLoader(const Filter *ft) linux_elfppc32_fold, sizeof(linux_elfppc32_fold), ft ); } +static const +#include "stub/l_lx_elf64amd.h" + +static const +#include "stub/fold_elf64amd.h" + +int +PackLinuxElf64amd::buildLoader(const Filter *ft) +{ + return buildLinuxLoader( + linux_elf64amd_loader, sizeof(linux_elf64amd_loader), + linux_elf64amd_fold, sizeof(linux_elf64amd_fold), ft ); +} + bool PackLinuxElf32ppc::canPack() { @@ -260,13 +421,13 @@ PackLinuxElf32ppc::canPack() return false; } - // The first PT_LOAD must cover the beginning of the file (0==p_offset). + // The first PT_LOAD32 must cover the beginning of the file (0==p_offset). unsigned const e_phnum = get_native16(&ehdr->e_phnum); Elf32_Phdr const *phdr = (Elf32_Phdr const *)(buf + e_phoff); for (unsigned j=0; j < e_phnum; ++phdr, ++j) { if (j >= 14) return false; - if (phdr->PT_LOAD == get_native32(&phdr->p_type)) { + if (phdr->PT_LOAD32 == get_native32(&phdr->p_type)) { // Just avoid the "rewind" when unpacking? //if (phdr->p_offset != 0) { // throwCantPack("invalid Phdr p_offset; try `--force-execve'"); @@ -294,12 +455,74 @@ PackLinuxElf32ppc::canPack() return true; } +bool +PackLinuxElf64amd::canPack() +{ + unsigned char buf[sizeof(Elf64_Ehdr) + 14*sizeof(Elf64_Phdr)]; + COMPILE_TIME_ASSERT(sizeof(buf) <= 1024); + + exetype = 0; + + fi->readx(buf, sizeof(buf)); + fi->seek(0, SEEK_SET); + Elf64_Ehdr const *const ehdr = (Elf64_Ehdr const *)buf; + + // now check the ELF header + if (checkEhdr(ehdr, Elf64_Ehdr::EM_X86_64, + Elf64_Ehdr::ELFCLASS64, Elf64_Ehdr::ELFDATA2LSB) != 0) + return false; + + // additional requirements for linux/elf386 + if (get_native16(&ehdr->e_ehsize) != sizeof(*ehdr)) { + throwCantPack("invalid Ehdr e_ehsize; try `--force-execve'"); + return false; + } + acc_uint64l_t const e_phoff = get_native64(&ehdr->e_phoff); + if (e_phoff != sizeof(*ehdr)) {// Phdrs not contiguous with Ehdr + throwCantPack("non-contiguous Ehdr/Phdr; try `--force-execve'"); + return false; + } + + // The first PT_LOAD64 must cover the beginning of the file (0==p_offset). + unsigned const e_phnum = get_native16(&ehdr->e_phnum); + Elf64_Phdr const *phdr = (Elf64_Phdr const *)(buf + e_phoff); + for (unsigned j=0; j < e_phnum; ++phdr, ++j) { + if (j >= 14) + return false; + if (phdr->PT_LOAD64 == get_native32(&phdr->p_type)) { + // Just avoid the "rewind" when unpacking? + //if (phdr->p_offset != 0) { + // throwCantPack("invalid Phdr p_offset; try `--force-execve'"); + // return false; + //} + + // detect possible conflict upon invocation + acc_uint64l_t const p_vaddr = get_native64(&phdr->p_vaddr); + if (p_vaddr < (unsigned)(0x100000 + file_size) + || p_vaddr < (unsigned)(0x100000 + file_size) ) { + throwAlreadyPackedByUPX(); // not necessarily, but mostly true + return false; + } + exetype = 1; + break; + } + } + + if (!super::canPack()) + return false; + assert(exetype == 1); + + // set options + opt->o_unix.blocksize = blocksize = file_size; + return true; +} + off_t PackLinuxElf32::getbrk(const Elf32_Phdr *phdr, int e_phnum) const { off_t brka = 0; for (int j = 0; j < e_phnum; ++phdr, ++j) { - if (PT_LOAD == get_native32(&phdr->p_type)) { + if (PT_LOAD32 == get_native32(&phdr->p_type)) { off_t b = get_native32(&phdr->p_vaddr) + get_native32(&phdr->p_memsz); if (b > brka) brka = b; @@ -308,6 +531,20 @@ PackLinuxElf32::getbrk(const Elf32_Phdr *phdr, int e_phnum) const return brka; } +off_t +PackLinuxElf64::getbrk(const Elf64_Phdr *phdr, int e_phnum) const +{ + off_t brka = 0; + for (int j = 0; j < e_phnum; ++phdr, ++j) { + if (PT_LOAD64 == get_native32(&phdr->p_type)) { + off_t b = get_native64(&phdr->p_vaddr) + get_native64(&phdr->p_memsz); + if (b > brka) + brka = b; + } + } + return brka; +} + void PackLinuxElf32::generateElfHdr( OutputFile */*fo*/, @@ -337,7 +574,7 @@ PackLinuxElf32::generateElfHdr( // Info for OS kernel to set the brk() if (brka) { - set_native32(&h2->phdr[1].p_type, PT_LOAD); // be sure + set_native32(&h2->phdr[1].p_type, PT_LOAD32); // be sure set_native32(&h2->phdr[1].p_offset, 0xfff&brka); set_native32(&h2->phdr[1].p_vaddr, brka); set_native32(&h2->phdr[1].p_paddr, brka); @@ -346,6 +583,44 @@ PackLinuxElf32::generateElfHdr( } } +void +PackLinuxElf64::generateElfHdr( + OutputFile */*fo*/, + void const *proto, + unsigned const brka +) +{ + cprElfHdr2 *const h2 = (cprElfHdr2 *)&elfout; + cprElfHdr3 *const h3 = (cprElfHdr3 *)&elfout; + memcpy(h3, proto, sizeof(*h3)); // reads beyond, but OK + + assert(get_native32(&h2->ehdr.e_phoff) == sizeof(Elf64_Ehdr)); + h2->ehdr.e_shoff = 0; + assert(get_native16(&h2->ehdr.e_ehsize) == sizeof(Elf64_Ehdr)); + assert(get_native16(&h2->ehdr.e_phentsize) == sizeof(Elf64_Phdr)); + h2->ehdr.e_shentsize = 0; + h2->ehdr.e_shnum = 0; + h2->ehdr.e_shstrndx = 0; + +#if 0 //{ + unsigned identsize; + char const *const ident = getIdentstr(&identsize); +#endif //} + sz_elf_hdrs = sizeof(*h2) - sizeof(linfo); // default + set_native64(&h2->phdr[0].p_filesz, sizeof(*h2)); // + identsize; + h2->phdr[0].p_memsz = h2->phdr[0].p_filesz; + + // Info for OS kernel to set the brk() + if (brka) { + set_native32(&h2->phdr[1].p_type, PT_LOAD32); // be sure + set_native64(&h2->phdr[1].p_offset, 0xfff&brka); + set_native64(&h2->phdr[1].p_vaddr, brka); + set_native64(&h2->phdr[1].p_paddr, brka); + h2->phdr[1].p_filesz = 0; + h2->phdr[1].p_memsz = 0; + } +} + void PackLinuxElf32ppc::generateElfHdr( OutputFile *fo, @@ -367,6 +642,27 @@ PackLinuxElf32ppc::generateElfHdr( } } +void +PackLinuxElf64amd::generateElfHdr( + OutputFile *fo, + void const *proto, + unsigned const brka +) +{ + super::generateElfHdr(fo, proto, brka); + + if (ph.format==UPX_F_LINUX_ELF64_AMD) { + cprElfHdr2 *const h2 = (cprElfHdr2 *)&elfout; + assert(2==get_native16(&h2->ehdr.e_phnum)); + set_native32(&h2->phdr[0].p_flags, Elf64_Phdr::PF_W | get_native32(&h2->phdr[0].p_flags)); + memset(&h2->linfo, 0, sizeof(h2->linfo)); + fo->write(h2, sizeof(*h2)); + } + else { + assert(false); // unknown ph.format, PackLinuxElf64amd::generateElfHdr + } +} + static const #include "stub/l_lx_elf86.h" static const @@ -388,6 +684,22 @@ void PackLinuxElf32::pack1(OutputFile *fo, Filter &/*ft*/) generateElfHdr(fo, linux_elfppc32_fold, getbrk(phdri, e_phnum) ); } +void PackLinuxElf64::pack1(OutputFile *fo, Filter &/*ft*/) +{ + fi->seek(0, SEEK_SET); + fi->readx(&ehdri, sizeof(ehdri)); + unsigned const e_phoff = get_native32(&ehdri.e_phoff); + unsigned const e_phnum = get_native16(&ehdri.e_phnum); + assert(e_phoff == sizeof(Elf64_Ehdr)); // checked by canPack() + sz_phdrs = e_phnum * get_native16(&ehdri.e_phentsize); + + phdri = new Elf64_Phdr[e_phnum]; + fi->seek(e_phoff, SEEK_SET); + fi->readx(phdri, sz_phdrs); + + generateElfHdr(fo, linux_elf64amd_fold, getbrk(phdri, e_phnum) ); +} + void PackLinuxElf32::pack2(OutputFile *fo, Filter &ft) { Extent x; @@ -398,7 +710,7 @@ void PackLinuxElf32::pack2(OutputFile *fo, Filter &ft) off_t ptload0hi = 0, ptload1lo = 0, ptload1sz = 0; unsigned const e_phnum = get_native16(&ehdri.e_phnum); for (k = 0; k < e_phnum; ++k) { - if (PT_LOAD == get_native32(&phdri[k].p_type)) { + if (PT_LOAD32 == get_native32(&phdri[k].p_type)) { x.offset = get_native32(&phdri[k].p_offset); x.size = get_native32(&phdri[k].p_filesz); if (0 == ptload0hi) { @@ -431,13 +743,13 @@ void PackLinuxElf32::pack2(OutputFile *fo, Filter &ft) ft.addvalue = 0; int nx = 0; - for (k = 0; k < e_phnum; ++k) if (PT_LOAD==get_native32(&phdri[k].p_type)) { + for (k = 0; k < e_phnum; ++k) if (PT_LOAD32==get_native32(&phdri[k].p_type)) { if (ft.id < 0x40) { // FIXME: ?? ft.addvalue = phdri[k].p_vaddr; } x.offset = get_native32(&phdri[k].p_offset); x.size = get_native32(&phdri[k].p_filesz); - if (0 == nx) { // 1st PT_LOAD must cover Ehdr at 0==p_offset + if (0 == nx) { // 1st PT_LOAD32 must cover Ehdr at 0==p_offset unsigned const delta = sizeof(Elf32_Ehdr) + sz_phdrs; if (ft.id < 0x40) { // FIXME: ?? ft.addvalue += delta; @@ -458,7 +770,7 @@ void PackLinuxElf32::pack2(OutputFile *fo, Filter &ft) x.size = ptload1lo - ptload0hi; packExtent(x, total_in, total_out, 0, fo); } - if ((off_t)total_in < file_size) { // non-PT_LOAD stuff + if ((off_t)total_in < file_size) { // non-PT_LOAD32 stuff x.offset = total_in; x.size = file_size - total_in; packExtent(x, total_in, total_out, 0, fo); @@ -469,6 +781,87 @@ void PackLinuxElf32::pack2(OutputFile *fo, Filter &ft) set_native32(&elfout.phdr[0].p_filesz, fo->getBytesWritten()); } +void PackLinuxElf64::pack2(OutputFile *fo, Filter &ft) +{ + Extent x; + unsigned k; + + // count passes, set ptload vars + ui_total_passes = 0; + off_t ptload0hi = 0, ptload1lo = 0, ptload1sz = 0; + unsigned const e_phnum = get_native16(&ehdri.e_phnum); + for (k = 0; k < e_phnum; ++k) { + if (PT_LOAD64 == get_native32(&phdri[k].p_type)) { + x.offset = get_native64(&phdri[k].p_offset); + x.size = get_native64(&phdri[k].p_filesz); + if (0 == ptload0hi) { + ptload0hi = x.offset + x.size; + } + else if (0 == ptload1lo) { + ptload1lo = x.offset; + ptload1sz = x.size; + } + ui_total_passes++; + } + } + if (0!=ptload1sz && ptload0hi < ptload1lo) + ui_total_passes++; + + // compress extents + unsigned total_in = 0; + unsigned total_out = 0; + + ui_pass = -1; // Compressing Elf headers is invisible to UI. + x.offset = 0; + x.size = sizeof(Elf64_Ehdr) + sz_phdrs; + { + int const old_level = ph.level; ph.level = 10; + packExtent(x, total_in, total_out, 0, fo); + ph.level = old_level; + } + + ui_pass = 0; + ft.addvalue = 0; + + int nx = 0; + for (k = 0; k < e_phnum; ++k) if (PT_LOAD64==get_native32(&phdri[k].p_type)) { + if (ft.id < 0x40) { + // FIXME: ?? ft.addvalue = phdri[k].p_vaddr; + } + x.offset = get_native64(&phdri[k].p_offset); + x.size = get_native64(&phdri[k].p_filesz); + if (0 == nx) { // 1st PT_LOAD64 must cover Ehdr at 0==p_offset + unsigned const delta = sizeof(Elf64_Ehdr) + sz_phdrs; + if (ft.id < 0x40) { + // FIXME: ?? ft.addvalue += delta; + } + x.offset += delta; + x.size -= delta; + } + // compressWithFilters() always assumes a "loader", so would + // throw NotCompressible for small .data Extents, which PowerPC + // sometimes marks as PF_X anyway. So filter only first segment. + packExtent(x, total_in, total_out, + ((0==nx && (Elf64_Phdr::PF_X & get_native64(&phdri[k].p_flags))) + ? &ft : 0 ), fo ); + ++nx; + } + if (0!=ptload1sz && ptload0hi < ptload1lo) { // alignment hole? + x.offset = ptload0hi; + x.size = ptload1lo - ptload0hi; + packExtent(x, total_in, total_out, 0, fo); + } + if ((off_t)total_in < file_size) { // non-PT_LOAD64 stuff + x.offset = total_in; + x.size = file_size - total_in; + packExtent(x, total_in, total_out, 0, fo); + } + + if ((off_t)total_in != file_size) + throwEOFException(); + set_native64(&elfout.phdr[0].p_filesz, fo->getBytesWritten()); +} + void PackLinuxElf32ppc::pack3(OutputFile *fo, Filter &ft) { unsigned disp; @@ -482,6 +875,26 @@ void PackLinuxElf32ppc::pack3(OutputFile *fo, Filter &ft) super::pack3(fo, ft); } +void PackLinuxElf64amd::pack3(OutputFile *fo, Filter &ft) +{ + unsigned disp; // 32 bits wide + unsigned const zero = 0; + unsigned len = fo->getBytesWritten(); + fo->write(&zero, 3& -len); // align to 0 mod 4 + len += (3& -len) + sizeof(disp); + + // 5: sizeof(CALL instruction at _start which precedes f_decompress + set_native32(&disp, 5+ len - sz_elf_hdrs); + + fo->write(&disp, sizeof(disp)); + + super::pack3(fo, ft); +} + +void PackLinuxElf::pack4(OutputFile *fo, Filter &ft) +{ + super::pack4(fo, ft); +} void PackLinuxElf32::pack4(OutputFile *fo, Filter &ft) { @@ -513,6 +926,27 @@ void PackLinuxElf32::pack4(OutputFile *fo, Filter &ft) fo->rewrite(&linfo, sizeof(linfo)); } +void PackLinuxElf64::pack4(OutputFile *fo, Filter &ft) +{ + overlay_offset = sz_elf_hdrs + sizeof(linfo); + unsigned const zero = 0; + unsigned len = fo->getBytesWritten(); + fo->write(&zero, 3& -len); // align to 0 mod 4 + len += 3& -len; + set_native64(&elfout.phdr[0].p_filesz, len); + super::pack4(fo, ft); // write PackHeader and overlay_offset + +#define PAGE_MASK (~0u<<12) + // pre-calculate for benefit of runtime disappearing act via munmap() + set_native64(&elfout.phdr[0].p_memsz, PAGE_MASK & (~PAGE_MASK + len)); +#undef PAGE_MASK + + // rewrite Elf header + fo->seek(0, SEEK_SET); + fo->rewrite(&elfout, sz_elf_hdrs); + fo->rewrite(&linfo, sizeof(linfo)); +} + void PackLinuxElf32::unpack(OutputFile *fo) { #define MAX_ELF_HDR 512 @@ -556,12 +990,12 @@ void PackLinuxElf32::unpack(OutputFile *fo) unsigned u_adler = upx_adler32(NULL, 0); off_t ptload0hi=0, ptload1lo=0, ptload1sz=0; - // decompress PT_LOAD + // decompress PT_LOAD32 bool first_PF_X = true; unsigned const phnum = get_native16(&ehdr->e_phnum); fi->seek(- (off_t) (szb_info + ph.c_len), SEEK_CUR); for (unsigned j=0; j < phnum; ++phdr, ++j) { - if (PT_LOAD==get_native32(&phdr->p_type)) { + if (PT_LOAD32==get_native32(&phdr->p_type)) { unsigned const filesz = get_native32(&phdr->p_filesz); unsigned const offset = get_native32(&phdr->p_offset); if (0==ptload0hi) { @@ -591,7 +1025,120 @@ void PackLinuxElf32::unpack(OutputFile *fo) unpackExtent(ptload1lo - ptload0hi, fo, total_in, total_out, c_adler, u_adler, false, szb_info); } - if (total_out != orig_file_size) { // non-PT_LOAD stuff + if (total_out != orig_file_size) { // non-PT_LOAD32 stuff + if (fo) + fo->seek(0, SEEK_END); + unpackExtent(orig_file_size - total_out, fo, total_in, total_out, + c_adler, u_adler, false, szb_info); + } + + // check for end-of-file + fi->readx(&bhdr, szb_info); + unsigned const sz_unc = ph.u_len = get_native32(&bhdr.sz_unc); + + if (sz_unc == 0) { // uncompressed size 0 -> EOF + // note: magic is always stored le32 + unsigned const sz_cpr = get_le32(&bhdr.sz_cpr); + if (sz_cpr != UPX_MAGIC_LE32) // sz_cpr must be h->magic + throwCompressedDataViolation(); + } + else { // extra bytes after end? + throwCompressedDataViolation(); + } + + // update header with totals + ph.c_len = total_in; + ph.u_len = total_out; + + // all bytes must be written + if (total_out != orig_file_size) + throwEOFException(); + + // finally test the checksums + if (ph.c_adler != c_adler || ph.u_adler != u_adler) + throwChecksumError(); +#undef MAX_ELF_HDR +} + +void PackLinuxElf64::unpack(OutputFile *fo) +{ +#define MAX_ELF_HDR 512 + char bufehdr[MAX_ELF_HDR]; + Elf64_Ehdr *const ehdr = (Elf64_Ehdr *)bufehdr; + Elf64_Phdr const *phdr = (Elf64_Phdr *)(1+ehdr); + + unsigned szb_info = sizeof(b_info); + { + fi->seek(0, SEEK_SET); + fi->readx(bufehdr, MAX_ELF_HDR); + acc_uint64l_t const e_entry = get_native64(&ehdr->e_entry); + if (e_entry < 0x401180 + && ehdr->e_machine==Elf64_Ehdr::EM_386) { /* old style, 8-byte b_info */ + szb_info = 2*sizeof(unsigned); + } + } + + fi->seek(overlay_offset, SEEK_SET); + p_info hbuf; + fi->readx(&hbuf, sizeof(hbuf)); + unsigned orig_file_size = get_native32(&hbuf.p_filesize); + blocksize = get_native32(&hbuf.p_blocksize); + if (file_size > (off_t)orig_file_size || blocksize > orig_file_size) + throwCantUnpack("file header corrupted"); + + ibuf.alloc(blocksize + OVERHEAD); + b_info bhdr; memset(&bhdr, 0, sizeof(bhdr)); + fi->readx(&bhdr, szb_info); + ph.u_len = get_native32(&bhdr.sz_unc); + ph.c_len = get_native32(&bhdr.sz_cpr); + ph.filter_cto = bhdr.b_cto8; + + // Uncompress Ehdr and Phdrs. + fi->readx(ibuf, ph.c_len); + decompress(ibuf, (upx_byte *)ehdr, false); + + unsigned total_in = 0; + unsigned total_out = 0; + unsigned c_adler = upx_adler32(NULL, 0); + unsigned u_adler = upx_adler32(NULL, 0); + off_t ptload0hi=0, ptload1lo=0, ptload1sz=0; + + // decompress PT_LOAD32 + bool first_PF_X = true; + unsigned const phnum = get_native16(&ehdr->e_phnum); + fi->seek(- (off_t) (szb_info + ph.c_len), SEEK_CUR); + for (unsigned j=0; j < phnum; ++phdr, ++j) { + if (PT_LOAD32==get_native32(&phdr->p_type)) { + acc_uint64l_t const filesz = get_native64(&phdr->p_filesz); + acc_uint64l_t const offset = get_native64(&phdr->p_offset); + if (0==ptload0hi) { + ptload0hi = filesz + offset; + } + else if (0==ptload1lo) { + ptload1lo = offset; + ptload1sz = filesz; + } + if (fo) + fo->seek(offset, SEEK_SET); + if (Elf64_Phdr::PF_X & get_native32(&phdr->p_flags)) { + unpackExtent(filesz, fo, total_in, total_out, + c_adler, u_adler, first_PF_X, szb_info); + first_PF_X = false; + } + else { + unpackExtent(filesz, fo, total_in, total_out, + c_adler, u_adler, false, szb_info); + } + } + } + + if (0!=ptload1sz && ptload0hi < ptload1lo) { // alignment hole? + if (fo) + fo->seek(ptload0hi, SEEK_SET); + unpackExtent(ptload1lo - ptload0hi, fo, total_in, total_out, + c_adler, u_adler, false, szb_info); + } + if (total_out != orig_file_size) { // non-PT_LOAD32 stuff if (fo) fo->seek(0, SEEK_END); unpackExtent(orig_file_size - total_out, fo, total_in, total_out, @@ -717,12 +1264,12 @@ bool PackLinuxI386elf::canPack() return false; } - // The first PT_LOAD must cover the beginning of the file (0==p_offset). + // The first PT_LOAD32 must cover the beginning of the file (0==p_offset). Elf32_Phdr const *phdr = (Elf32_Phdr const *)(buf + ehdr->e_phoff); for (unsigned j=0; j < ehdr->e_phnum; ++phdr, ++j) { if (j >= 14) // 512 bytes holds Elf32_Ehdr + Elf32_Phdr[0..13] return false; - if (phdr->PT_LOAD == phdr->p_type) { + if (phdr->PT_LOAD32 == phdr->p_type) { if (phdr->p_offset != 0) { throwCantPack("invalid Phdr p_offset; try `--force-execve'"); return false; @@ -763,7 +1310,7 @@ bool PackLinuxI386elf::canPack() phdri= (Elf32_Phdr *)(ehdr->e_phoff + file_image); // do not free() !! int j= ehdr->e_phnum; - Elf32_Phdr const *phdr= phdri; + phdr= phdri; for (; --j>=0; ++phdr) if (Elf32_Phdr::PT_DYNAMIC==phdr->p_type) { dynseg= (Elf32_Dyn const *)(phdr->p_offset + file_image); break; @@ -804,7 +1351,7 @@ PackLinuxI386elf::elf_get_offset_from_address(unsigned const addr) const { Elf32_Phdr const *phdr = phdri; int j = ehdri.e_phnum; - for (; --j>=0; ++phdr) if (PT_LOAD == phdr->p_type) { + for (; --j>=0; ++phdr) if (PT_LOAD32 == phdr->p_type) { unsigned const t = addr - phdr->p_vaddr; if (t < phdr->p_filesz) { return t + phdr->p_offset; @@ -884,7 +1431,7 @@ void PackLinuxI386elf::pack2(OutputFile *fo, Filter &ft) ui_total_passes = 0; off_t ptload0hi = 0, ptload1lo = 0, ptload1sz = 0; for (k = 0; k < ehdri.e_phnum; ++k) { - if (PT_LOAD == phdri[k].p_type) { + if (PT_LOAD32 == phdri[k].p_type) { x.offset = phdri[k].p_offset; x.size = phdri[k].p_filesz; if (0 == ptload0hi) { @@ -917,13 +1464,13 @@ void PackLinuxI386elf::pack2(OutputFile *fo, Filter &ft) ft.addvalue = 0; int nx = 0; - for (k = 0; k < ehdri.e_phnum; ++k) if (PT_LOAD==phdri[k].p_type) { + for (k = 0; k < ehdri.e_phnum; ++k) if (PT_LOAD32==phdri[k].p_type) { if (ft.id < 0x40) { // FIXME: ?? ft.addvalue = phdri[k].p_vaddr; } x.offset = phdri[k].p_offset; x.size = phdri[k].p_filesz; - if (0 == nx) { // 1st PT_LOAD must cover Ehdr at 0==p_offset + if (0 == nx) { // 1st PT_LOAD32 must cover Ehdr at 0==p_offset unsigned const delta = sizeof(Elf32_Ehdr) + sz_phdrs; if (ft.id < 0x40) { // FIXME: ?? ft.addvalue += delta; @@ -941,7 +1488,7 @@ void PackLinuxI386elf::pack2(OutputFile *fo, Filter &ft) x.size = ptload1lo - ptload0hi; packExtent(x, total_in, total_out, 0, fo); } - if ((off_t)total_in < file_size) { // non-PT_LOAD stuff + if ((off_t)total_in < file_size) { // non-PT_LOAD32 stuff x.offset = total_in; x.size = file_size - total_in; packExtent(x, total_in, total_out, 0, fo); @@ -1054,11 +1601,11 @@ void PackLinuxI386elf::unpack(OutputFile *fo) unsigned u_adler = upx_adler32(NULL, 0); off_t ptload0hi=0, ptload1lo=0, ptload1sz=0; - // decompress PT_LOAD + // decompress PT_LOAD32 bool first_PF_X = true; fi->seek(- (off_t) (szb_info + ph.c_len), SEEK_CUR); for (unsigned j=0; j < ehdr->e_phnum; ++phdr, ++j) { - if (PT_LOAD==phdr->p_type) { + if (PT_LOAD32==phdr->p_type) { if (0==ptload0hi) { ptload0hi = phdr->p_filesz + phdr->p_offset; } @@ -1086,7 +1633,7 @@ void PackLinuxI386elf::unpack(OutputFile *fo) unpackExtent(ptload1lo - ptload0hi, fo, total_in, total_out, c_adler, u_adler, false, szb_info); } - if (total_out != orig_file_size) { // non-PT_LOAD stuff + if (total_out != orig_file_size) { // non-PT_LOAD32 stuff if (fo) fo->seek(0, SEEK_END); unpackExtent(orig_file_size - total_out, fo, total_in, total_out, diff --git a/src/p_lx_elf.h b/src/p_lx_elf.h index f64d95b7..ebd791b7 100644 --- a/src/p_lx_elf.h +++ b/src/p_lx_elf.h @@ -31,15 +31,51 @@ #define __UPX_P_LX_ELF_H -class PackLinuxElf32 : public PackUnix +class PackLinuxElf : public PackUnix { typedef PackUnix super; public: - PackLinuxElf32(InputFile *f); - virtual ~PackLinuxElf32(); + PackLinuxElf(InputFile *f); + virtual ~PackLinuxElf(); /*virtual int buildLoader(const Filter *);*/ virtual bool canUnpackVersion(int version) const { return (version >= 11); } +protected: + virtual const int *getCompressionMethods(int method, int level) const; + + // All other virtual functions in this class must be pure virtual + // because they depend on Elf32 or Elf64 data structures, which differ. + + virtual void pack1(OutputFile *, Filter &) = 0; // generate executable header + virtual void pack2(OutputFile *, Filter &) = 0; // append compressed data + //virtual void pack3(OutputFile *, Filter &) = 0; // append loader + virtual void pack4(OutputFile *, Filter &) = 0; // append pack header + + virtual void generateElfHdr( + OutputFile *, + void const *proto, + unsigned const brka + ) = 0; + virtual int buildLinuxLoader( + upx_byte const *const proto, // assembly-only sections + unsigned const szproto, + upx_byte const *const fold, // linked assembly + C section + unsigned const szfold, + Filter const *ft + ) = 0; + virtual void unpack(OutputFile *fo) = 0; + +protected: + unsigned sz_phdrs; // sizeof Phdr[] + unsigned sz_elf_hdrs; // all Elf headers +}; + +class PackLinuxElf32 : public PackLinuxElf +{ + typedef PackLinuxElf super; +public: + PackLinuxElf32(InputFile *f); + virtual ~PackLinuxElf32(); protected: virtual int checkEhdr( Elf32_Ehdr const *ehdr, @@ -47,35 +83,24 @@ protected: unsigned char ei_class, unsigned char ei_data) const; - virtual const int *getCompressionMethods(int method, int level) const; - virtual void pack1(OutputFile *, Filter &); // generate executable header virtual void pack2(OutputFile *, Filter &); // append compressed data //virtual void pack3(OutputFile *, Filter &); // append loader virtual void pack4(OutputFile *, Filter &); // append pack header + virtual void unpack(OutputFile *fo); - virtual off_t getbrk(const Elf32_Phdr *phdr, int e_phnum) const; virtual void generateElfHdr( OutputFile *, void const *proto, unsigned const brka ); - virtual int buildLinuxLoader( - upx_byte const *const proto, // assembly-only sections - unsigned const szproto, - upx_byte const *const fold, // linked assembly + C section - unsigned const szfold, - Filter const *ft - ); + virtual off_t getbrk(const Elf32_Phdr *phdr, int e_phnum) const; virtual void patchLoader(); virtual void updateLoader(OutputFile *fo); - virtual void unpack(OutputFile *fo); protected: Elf32_Ehdr ehdri; // from input file Elf32_Phdr *phdri; // for input file - unsigned sz_phdrs; // sizeof Phdr[] - unsigned sz_elf_hdrs; // all Elf headers struct cprElfHdr1 { Elf32_Ehdr ehdr; @@ -101,18 +126,125 @@ protected: cprElfHdr3 elfout; }; + +class PackLinuxElf64 : public PackLinuxElf +{ + typedef PackLinuxElf super; +public: + PackLinuxElf64(InputFile *f); + virtual ~PackLinuxElf64(); + /*virtual int buildLoader(const Filter *);*/ + +protected: + virtual int checkEhdr( + Elf64_Ehdr const *ehdr, + unsigned char e_machine, + unsigned char ei_class, + unsigned char ei_data) const; + + virtual void pack1(OutputFile *, Filter &); // generate executable header + virtual void pack2(OutputFile *, Filter &); // append compressed data + //virtual void pack3(OutputFile *, Filter &); // append loader + virtual void pack4(OutputFile *, Filter &); // append pack header + virtual void unpack(OutputFile *fo); + + virtual void generateElfHdr( + OutputFile *, + void const *proto, + unsigned const brka + ); + virtual off_t getbrk(const Elf64_Phdr *phdr, int e_phnum) const; + virtual void patchLoader(); + virtual void updateLoader(OutputFile *fo); + +protected: + Elf64_Ehdr ehdri; // from input file + Elf64_Phdr *phdri; // for input file + + struct cprElfHdr1 { + Elf64_Ehdr ehdr; + Elf64_Phdr phdr[1]; + l_info linfo; + } + __attribute_packed; + + struct cprElfHdr2 { + Elf64_Ehdr ehdr; + Elf64_Phdr phdr[2]; + l_info linfo; + } + __attribute_packed; + + struct cprElfHdr3 { + Elf64_Ehdr ehdr; + Elf64_Phdr phdr[3]; + l_info linfo; + } + __attribute_packed; + + cprElfHdr3 elfout; +}; + class PackLinuxElf32Be : public PackLinuxElf32 { typedef PackLinuxElf32 super; protected: PackLinuxElf32Be(InputFile *f) : super(f) { } + virtual acc_uint64l_t get_native64(const void *b) const { return get_be64(b); } virtual unsigned get_native32(const void *b) const { return get_be32(b); } virtual unsigned get_native16(const void *b) const { return get_be16(b); } + virtual void set_native64(void *b, acc_uint64l_t v) const { set_be64(b, v); } virtual void set_native32(void *b, unsigned v) const { set_be32(b, v); } virtual void set_native16(void *b, unsigned v) const { set_be16(b, v); } }; +class PackLinuxElf64Le : public PackLinuxElf64 +{ + typedef PackLinuxElf64 super; +protected: + PackLinuxElf64Le(InputFile *f) : super(f) { } + + virtual acc_uint64l_t get_native64(const void *b) const { return get_le64(b); } + virtual unsigned get_native32(const void *b) const { return get_le32(b); } + virtual unsigned get_native16(const void *b) const { return get_le16(b); } + virtual void set_native64(void *b, unsigned long long v) const { set_le64(b, v); } + virtual void set_native32(void *b, unsigned v) const { set_le32(b, v); } + virtual void set_native16(void *b, unsigned v) const { set_le16(b, v); } +}; + +/************************************************************************* +// linux/elf64amd +**************************************************************************/ + +class PackLinuxElf64amd : public PackLinuxElf64Le +{ + typedef PackLinuxElf64Le super; +public: + PackLinuxElf64amd(InputFile *f); + virtual ~PackLinuxElf64amd(); + virtual int getFormat() const { return UPX_F_LINUX_ELF64_AMD; } + virtual const char *getName() const { return "linux/ElfAMD"; } + virtual const int *getFilters() const; + virtual bool canPack(); +protected: + virtual void pack3(OutputFile *, Filter &); // append loader + virtual const int *getCompressionMethods(int method, int level) const; + virtual int buildLinuxLoader( + upx_byte const *const proto, // assembly-only sections + unsigned const szproto, + upx_byte const *const fold, // linked assembly + C section + unsigned const szfold, + Filter const *ft + ); + virtual int buildLoader(const Filter *); + virtual void generateElfHdr( + OutputFile *, + void const *proto, + unsigned const brka + ); +}; + /************************************************************************* // linux/elf32ppc **************************************************************************/ diff --git a/src/packmast.cpp b/src/packmast.cpp index ed49683d..67f69742 100644 --- a/src/packmast.cpp +++ b/src/packmast.cpp @@ -215,6 +215,8 @@ static Packer* try_packers(InputFile *f, try_function func) if ((p = func(new PackLinuxI386interp(f),f)) != NULL) return p; } + if ((p = func(new PackLinuxElf64amd(f),f)) != NULL) + return p; if ((p = func(new PackLinuxElf32ppc(f),f)) != NULL) return p; if ((p = func(new PackLinuxI386elf(f),f)) != NULL)