diff --git a/src/linker.cpp b/src/linker.cpp index 73c96d47..fc78e658 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -53,6 +53,7 @@ Linker::Linker(const void *pdata, int plen, int pinfo) oloader = new char[plen]; olen = 0; align_hack = 0; + align_offset = 0; info = pinfo; njumps = nsections = frozen = 0; jumps = new jump[200]; @@ -100,6 +101,16 @@ Linker::~Linker() } +void Linker::setLoaderAlignOffset(int offset) +{ + align_offset = offset; +} + +static int hex(char c) +{ + return (c & 0xf) + (c > '9' ? 9 : 0); +} + int Linker::addSection(const char *sect) { int ic; @@ -108,14 +119,13 @@ int Linker::addSection(const char *sect) if (*sect == '+') // alignment { if (sect[1] == '0') - align_hack = olen; + align_hack = olen + align_offset; else { - ic = (sect[1] & 0xf) + (sect[1] > '9' ? 9 : 0); - ic = (ic + (sect[2] & 0xf) + (sect[2] > '9' ? 9 : 0) - - (olen - align_hack) % ic) % ic; - memset(oloader+olen,sect[3] == 'C' ? 0x90 : 0,ic); - olen += ic; + unsigned j = hex(sect[1]); + j = (hex(sect[2]) - ((olen + align_offset) - align_hack) ) % j; + memset(oloader+olen, (sect[3] == 'C' ? 0x90 : 0), j); + olen += j; } } else diff --git a/src/linker.h b/src/linker.h index 02d24d8e..d8925747 100644 --- a/src/linker.h +++ b/src/linker.h @@ -40,6 +40,7 @@ public: const char *getLoader(int *llen); int getSection(const char *name, int *slen) const; int getLoaderSize() const { return olen; } + void setLoaderAlignOffset(int phase); protected: // little endian @@ -59,6 +60,7 @@ private: int nsections; int frozen; int align_hack; + int align_offset; private: // disable copy and assignment diff --git a/src/mem.cpp b/src/mem.cpp index 05968c71..2d9bb0ba 100644 --- a/src/mem.cpp +++ b/src/mem.cpp @@ -47,15 +47,6 @@ MemBuffer::~MemBuffer() this->free(); } -MemBufferIO::MemBufferIO(unsigned size) : - MemBuffer(size) -{ -} - -MemBufferIO::~MemBufferIO() -{ -} - void MemBuffer::free() { if (alloc_ptr) @@ -75,39 +66,6 @@ unsigned MemBuffer::getSize() const } -unsigned MemBufferIO::seek(unsigned offset, int whence) -{ - switch (whence) { - default: { - assert(false); - } break; - case SEEK_SET: { - assert(offset<=alloc_size); - ptr = offset + alloc_ptr; - } break; - case SEEK_CUR: { - assert((offset + (ptr - alloc_ptr))<=alloc_size); - ptr += offset; - } break; - case SEEK_END: { - assert((offset + alloc_size)<=alloc_size); - ptr = offset + alloc_size + alloc_ptr; - } break; - } - return ptr - alloc_ptr; -} - -unsigned MemBufferIO::write(void const *data, unsigned size) -{ - unsigned const avail = getSize(); - unsigned const len = UPX_MIN(size, avail); - if (data!=ptr) { - memmove(ptr, data, len); - } - ptr += len; - return len; -} - void MemBuffer::alloc(unsigned size, unsigned base_offset) { #if 0 diff --git a/src/mem.h b/src/mem.h index 53aad038..4fb4b437 100644 --- a/src/mem.h +++ b/src/mem.h @@ -72,15 +72,6 @@ private: //static void operator delete[] (void *) {} }; -class MemBufferIO : public MemBuffer { -public: - MemBufferIO(unsigned size=0); - ~MemBufferIO(); - - unsigned seek(unsigned offset, int whence); // returns new position - unsigned write(void const *data, unsigned size); // returns xfer count -}; - #endif /* already included */ diff --git a/src/p_elf.h b/src/p_elf.h index ffbf1c04..4ebbd8e0 100644 --- a/src/p_elf.h +++ b/src/p_elf.h @@ -67,7 +67,11 @@ struct Elf_LE32_Phdr LE32 p_align; /* Segment alignment */ // Values for p_type - enum { PT_LOAD = 1 }; /* Loadable program segment */ + enum { + PT_LOAD = 1, /* Loadable program segment */ + PT_DYNAMIC = 2, /* Dynamic linking information */ + PT_PHDR = 6 /* Entry for header table itself */ + }; // Values for p_flags enum { PF_X = (1 << 0) }; /* Segment is executable */ @@ -76,6 +80,18 @@ struct Elf_LE32_Phdr }; +struct Elf_LE32_Dyn +{ + LE32 d_tag; + LE32 d_val; + + enum { // tags + DT_NULL = 0, /* End flag */ + DT_NEEDED = 1, /* Name of needed library */ + DT_STRTAB = 5, /* String table */ + DT_STRSZ = 10 /* Sizeof string table */ + }; +}; #endif /* already included */ diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index 6424ba92..eed9c096 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -80,35 +80,11 @@ PackLinuxI386elf::buildLoader(const Filter *ft) { return buildLinuxLoader( linux_i386elf_loader, sizeof(linux_i386elf_loader), - linux_i386elf_fold, sizeof(linux_i386elf_fold), - ft, getbrk(phdri, ehdri.e_phnum) ); + linux_i386elf_fold, sizeof(linux_i386elf_fold), ft ); } -void PackLinuxI386elf::updateLoader(OutputFile *fo) -{ -#define PAGE_MASK (~0<<12) - upx_byte *const ptr = const_cast(getLoader()); - Elf_LE32_Phdr *const phdro = (Elf_LE32_Phdr *)(sizeof(Elf_LE32_Ehdr) + ptr); - off_t const totlen = fo->getBytesWritten(); - phdro->p_filesz = totlen; - - // pre-calculate for benefit of runtime disappearing act via munmap() - phdro->p_memsz = PAGE_MASK & (~PAGE_MASK + totlen); - - patchLoaderChecksum(); -#undef PAGE_MASK -} - -void PackLinuxI386elf::patchLoader() -{ - unsigned char *const ptr = const_cast(getLoader()); - lsize = getLoaderSize(); - assert(lsize > 128 && lsize < 4096); - patchVersion(ptr, lsize); - - patchLoaderChecksum(); -} +void PackLinuxI386elf::patchLoader() { } bool PackLinuxI386elf::canPack() @@ -146,17 +122,12 @@ bool PackLinuxI386elf::canPack() throwCantPack("invalid Phdr p_offset; try `--force-execve'"); return false; } -#if 1 - // FIXME: what about these checks ? - if (phdr->p_vaddr != 0x08048000) { + // detect possible conflict upon invocation + if (phdr->p_vaddr < (unsigned)(0x400000 + file_size) + || phdr->p_paddr < (unsigned)(0x400000 + file_size) ) { throwCantPack("invalid Phdr p_vaddr; try `--force-execve'"); return false; } - if (phdr->p_paddr != 0x08048000) { - throwCantPack("invalid Phdr p_paddr; try `--force-execve'"); - return false; - } -#endif exetype = 1; break; } @@ -165,100 +136,105 @@ bool PackLinuxI386elf::canPack() return super::canPack(); } -struct cprBlkHdr { - unsigned sz_unc; // uncompressed size (0 means EOF) - unsigned sz_cpr; // (compressed_size<<8) | cto8 -}; - void PackLinuxI386elf::packExtent( Extent const &x, unsigned &total_in, unsigned &total_out, - Filter *ft + Filter *ft, + OutputFile *fo ) { fi->seek(x.offset, SEEK_SET); - for (off_t rest = x.size; 0 != rest; ) - { + for (off_t rest = x.size; 0 != rest; ) { + int const strategy = getStrategy(*ft); int l = fi->readx(ibuf, UPX_MIN(rest, (off_t)blocksize)); - if (l == 0) + if (l == 0) { break; + } rest -= l; // Note: compression for a block can fail if the // file is e.g. blocksize + 1 bytes long // compress - unsigned char *const hdrptr = obuf; - obuf.seek(sizeof(cprBlkHdr), SEEK_CUR); ph.u_len = l; ph.overlap_overhead = 0; + unsigned end_u_adler; if (ft) { + // compressWithFilters() updates u_adler _inside_ compress(); + // that is, AFTER filtering. We want BEFORE filtering, + // so that decompression checks the end-to-end checksum. + end_u_adler = upx_adler32(ph.u_adler, ibuf, ph.u_len); ft->buf_len = l; - compressWithFilters(ft, OVERHEAD, ((opt->filter > 0) ? -2 : 2)); + compressWithFilters(ft, OVERHEAD, strategy); } else { (void) compress(ibuf, obuf); // ignore return value } - if (ph.c_len < ph.u_len) - { + if (ph.c_len < ph.u_len) { ph.overlap_overhead = OVERHEAD; - if (!testOverlappingDecompression(obuf, ph.overlap_overhead)) + if (!testOverlappingDecompression(obuf, ph.overlap_overhead)) { throwNotCompressible(); + } } - else - { - ph.c_len = ph.u_len; + else { + ph. c_len = ph.u_len; // must update checksum of compressed data ph.c_adler = upx_adler32(ph.c_adler, ibuf, ph.u_len); } // write block sizes - set_native32(0+hdrptr, ph.u_len); - set_native32(4+hdrptr, (ph.c_len<<8) | ph.filter_cto); + b_info tmp; memset(&tmp, 0, sizeof(tmp)); + set_native32(&tmp.sz_unc, ph.u_len); + set_native32(&tmp.sz_cpr, ph.c_len); + tmp.b_method = ph.method; + if (ft) { + tmp.b_ftid = ft->id; + tmp.b_cto8 = ft->cto; + } + fo->write(&tmp, sizeof(tmp)); + ph.b_len += sizeof(b_info); // write compressed data - if (ph.c_len < ph.u_len) - { - obuf.write(obuf, ph.c_len); - // FIXME: obuf is not discardable! - // verifyOverlappingDecompression(); + if (ph.c_len < ph.u_len) { + fo->write(obuf, ph.c_len); + // Checks ph.u_adler after decompression but before unfiltering + verifyOverlappingDecompression(); + } + else { + fo->write(ibuf, ph.u_len); } - else - obuf.write(ibuf, ph.u_len); + if (ft) { + ph.u_adler = end_u_adler; + } total_in += ph.u_len; total_out += ph.c_len; } } -void PackLinuxI386elf::pack(OutputFile *fo) +void PackLinuxI386elf::pack1(OutputFile *fo, Filter &) { // set options opt->unix.blocksize = blocksize = file_size; - progid = 0; // not used + fi->seek(0, SEEK_SET); fi->readx(&ehdri, sizeof(ehdri)); assert(ehdri.e_phoff == sizeof(Elf_LE32_Ehdr)); // checked by canPack() - off_t const sz_phdrs = ehdri.e_phnum * ehdri.e_phentsize; + sz_phdrs = ehdri.e_phnum * ehdri.e_phentsize; phdri = new Elf_LE32_Phdr[ehdri.e_phnum]; fi->seek(ehdri.e_phoff, SEEK_SET); fi->readx(phdri, sz_phdrs); - // init compression buffers - ibuf.alloc(blocksize); - obuf.allocForCompression(blocksize); - { - p_info hbuf; - set_native32(&hbuf.p_progid, progid); - set_native32(&hbuf.p_filesize, file_size); - set_native32(&hbuf.p_blocksize, blocksize); - obuf.write(&hbuf, sizeof(hbuf)); - } + generateElfHdr(fo, linux_i386elf_fold, phdri, ehdri.e_phnum, + getbrk(phdri, ehdri.e_phnum) ); +} +void PackLinuxI386elf::pack2(OutputFile *fo, Filter &ft) +{ Extent x; unsigned k; @@ -294,12 +270,11 @@ void PackLinuxI386elf::pack(OutputFile *fo) x.size = sizeof(Elf_LE32_Ehdr) + sz_phdrs; { int const old_level = ph.level; ph.level = 10; - packExtent(x, total_in, total_out, 0); + packExtent(x, total_in, total_out, 0, fo); ph.level = old_level; } ui_pass = 0; - Filter ft(ph.level); ft.addvalue = 0; nx = 0; @@ -319,53 +294,22 @@ void PackLinuxI386elf::pack(OutputFile *fo) } packExtent(x, total_in, total_out, ((Elf_LE32_Phdr::PF_X & phdri[k].p_flags) - ? &ft : 0 ) ); + ? &ft : 0 ), fo ); ++nx; } if (ptload0hi < ptload1lo) { // alignment hole? x.offset = ptload0hi; x.size = ptload1lo - ptload0hi; - packExtent(x, total_in, total_out, 0); + packExtent(x, total_in, total_out, 0, fo); } if ((off_t)total_in < file_size) { // non-PT_LOAD stuff x.offset = total_in; x.size = file_size - total_in; - packExtent(x, total_in, total_out, 0); + packExtent(x, total_in, total_out, 0, fo); } if ((off_t)total_in != file_size) throwEOFException(); - - // write block end marker (uncompressed size 0) - set_native32(obuf, 0); - obuf.write(obuf, 4); - - // update header with totals - ph.u_len = total_in; - ph.c_len = total_out; - - upx_byte const *p = getLoader(); - lsize = getLoaderSize(); - patchFilter32(const_cast(p), lsize, &ft); - fo->write(p, lsize); - - unsigned pos = obuf.seek(0, SEEK_CUR); - fo->write(obuf - pos, pos); - - // write packheader - writePackHeader(fo); - - // write overlay offset (needed for decompression) - set_native32(obuf, lsize); - fo->write(obuf, 4); - - updateLoader(fo); - fo->seek(0, SEEK_SET); - fo->rewrite(p, lsize); - - // finally check the compression ratio - if (!checkFinalCompressionRatio(fo)) - throwNotCompressible(); } @@ -376,11 +320,11 @@ void PackLinuxI386elf::unpackExtent(unsigned wanted, OutputFile *fo, ) { while (wanted) { - fi->readx(ibuf, 8); - int const sz_unc = ph.u_len = get_native32(ibuf+0); - unsigned const tmp = get_native32(ibuf+4); - int const sz_cpr = ph.c_len = tmp>>8; - ph.filter_cto = tmp<<24; + b_info hdr; + fi->readx(&hdr, sizeof(hdr)); + int const sz_unc = ph.u_len = get_native32(&hdr.sz_unc); + int const sz_cpr = ph.c_len = get_native32(&hdr.sz_cpr); + ph.filter_cto = hdr.b_cto8; if (sz_unc == 0) { // must never happen while 0!=wanted throwCompressedDataViolation(); @@ -438,11 +382,11 @@ void PackLinuxI386elf::unpack(OutputFile *fo) throwCantUnpack("file header corrupted"); ibuf.alloc(blocksize + OVERHEAD); - fi->readx(ibuf, 2*4); - ph.u_len = get_native32(0+ibuf); - unsigned const tmp = get_native32(4+ibuf); - ph.c_len = tmp>>8; - ph.filter_cto = tmp<<24; + b_info bhdr; + fi->readx(&bhdr, sizeof(bhdr)); + 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); @@ -456,7 +400,7 @@ void PackLinuxI386elf::unpack(OutputFile *fo) // decompress PT_LOAD bool first_PF_X = true; - fi->seek(- (off_t) (2*4 + ph.c_len), SEEK_CUR); + fi->seek(- (off_t) (sizeof(bhdr) + ph.c_len), SEEK_CUR); for (unsigned j=0; j < ehdr->e_phnum; ++phdr, ++j) { if (PT_LOAD==phdr->p_type) { if (0==ptload0hi) { @@ -493,12 +437,12 @@ void PackLinuxI386elf::unpack(OutputFile *fo) } // check for end-of-file - fi->readx(ibuf, 2*4); - unsigned const sz_unc = ph.u_len = get_native32(ibuf+0); + fi->readx(&bhdr, sizeof(bhdr)); + 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(ibuf+4); + unsigned const sz_cpr = get_le32(&bhdr.sz_cpr); if (sz_cpr != UPX_MAGIC_LE32) // sz_cpr must be h->magic throwCompressedDataViolation(); } diff --git a/src/p_lx_elf.h b/src/p_lx_elf.h index 0f8b63f2..3ebd2878 100644 --- a/src/p_lx_elf.h +++ b/src/p_lx_elf.h @@ -47,7 +47,6 @@ public: virtual const int *getFilters() const; virtual int buildLoader(const Filter *); - virtual void pack(OutputFile *fo); virtual void unpack(OutputFile *fo); virtual bool canPack(); @@ -60,22 +59,20 @@ protected: off_t size; }; virtual void packExtent(Extent const &x, - unsigned &total_in, unsigned &total_out, Filter *); + unsigned &total_in, unsigned &total_out, Filter *, OutputFile *); virtual void unpackExtent(unsigned wanted, OutputFile *fo, unsigned &total_in, unsigned &total_out, unsigned &c_adler, unsigned &u_adler, bool first_PF_X); protected: -#if 0 //{ - virtual const upx_byte *getLoader() const; - virtual int getLoaderSize() const; -#endif //} + virtual void pack1(OutputFile *, Filter &); // generate executable header + virtual void pack2(OutputFile *, Filter &); // append compressed data virtual void patchLoader(); - virtual void updateLoader(OutputFile *); Elf_LE32_Ehdr ehdri; // from input file Elf_LE32_Phdr *phdri; // for input file + unsigned sz_phdrs; // sizeof Phdr[] }; diff --git a/src/p_lx_exc.cpp b/src/p_lx_exc.cpp index 45e8e471..641f0eea 100644 --- a/src/p_lx_exc.cpp +++ b/src/p_lx_exc.cpp @@ -38,6 +38,11 @@ #include "p_lx_exc.h" #define PT_LOAD Elf_LE32_Phdr::PT_LOAD +#define PT_DYNAMIC Elf_LE32_Phdr::PT_DYNAMIC +#define DT_NULL Elf_LE32_Dyn::DT_NULL +#define DT_NEEDED Elf_LE32_Dyn::DT_NEEDED +#define DT_STRTAB Elf_LE32_Dyn::DT_STRTAB +#define DT_STRSZ Elf_LE32_Dyn::DT_STRSZ /************************************************************************* @@ -75,11 +80,225 @@ PackLinuxI386::getFilters() const return filters; } -struct cprElfhdr { - struct Elf_LE32_Ehdr ehdr; - struct Elf_LE32_Phdr phdr[2]; - struct PackUnix::l_info linfo; -}; +Elf_LE32_Phdr const * +PackLinuxI386::find_DYNAMIC(Elf_LE32_Phdr const *phdr, unsigned n) +{ + if (n) do if (PT_DYNAMIC==phdr->p_type) { + return phdr; + } while (++phdr, 0!=--n); + return 0; +} + +unsigned +PackLinuxI386::find_file_offset( + unsigned const vaddr, + Elf_LE32_Phdr const *phdr, + unsigned e_phnum +) +{ + if (e_phnum) do { + unsigned t = vaddr - phdr->p_vaddr; + if (t < phdr->p_memsz) { + return t + phdr->p_offset; + } + } while (++phdr, 0!=--e_phnum); + return ~0u; +} + +void +PackLinuxI386::generateElfHdr( + OutputFile *const fo, + void const *const proto, + Elf_LE32_Phdr const *const phdr0, + unsigned e_phnum, + unsigned const brka +) +{ + cprElfHdr1 *const h1 = (cprElfHdr1 *)&elfout; + cprElfHdr2 *const h2 = (cprElfHdr2 *)&elfout; + cprElfHdr3 *const h3 = (cprElfHdr3 *)&elfout; + memcpy(h2, proto, sizeof(*h2)); + + assert(h2->ehdr.e_phoff == sizeof(Elf_LE32_Ehdr)); + assert(h2->ehdr.e_shoff == 0); + assert(h2->ehdr.e_ehsize == sizeof(Elf_LE32_Ehdr)); + assert(h2->ehdr.e_phentsize == sizeof(Elf_LE32_Phdr)); + assert(h2->ehdr.e_shnum == 0); + +#if 0 //{ + unsigned identsize; + char const *const ident = identstr(identsize); +#endif //} + 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) { + h2->phdr[1].p_type = PT_LOAD; // be sure + h2->phdr[1].p_offset = 0xfff&brka; + h2->phdr[1].p_vaddr = brka; + h2->phdr[1].p_paddr = brka; + h2->phdr[1].p_filesz = 0; + h2->phdr[1].p_memsz = 0; + } + + if (ph.format==UPX_F_LINUX_i386 ) { + assert(h1->ehdr.e_phnum==1); + memset(&h1->linfo, 0, sizeof(h1->linfo)); + fo->write(h1, sizeof(*h1)); + } + else if (ph.format==UPX_F_LINUX_ELF_i386) { + int const j = ((char *)&h3->phdr[2]) - (char *)h3; + memcpy(&h3->phdr[2], j + (char const *)proto, sizeof(h3->phdr[2])); + + assert(h3->ehdr.e_phnum==3); + memset(&h3->linfo, 0, sizeof(h3->linfo)); + fo->write(h3, sizeof(*h3)); + } + else if (ph.format==UPX_F_LINUX_SH_i386) { + assert(h2->ehdr.e_phnum==1); + h2->ehdr.e_phnum = 2; + memset(&h2->linfo, 0, sizeof(h2->linfo)); + fo->write(h2, sizeof(*h2)); + } + else { + assert(false); // unknown ph.format, PackUnix::generateElfHdr + } + + Elf_LE32_Phdr const *const phdrdyn = find_DYNAMIC(phdr0, e_phnum); + if (phdrdyn) { // propagate DT_NEEDED + MemBuffer dynhdr(phdrdyn->p_memsz); + fi->seek(phdrdyn->p_offset, SEEK_SET); + fi->read(dynhdr, phdrdyn->p_memsz); + + unsigned strtabx = ~0u; + unsigned strsz = 0; + int j; + + Elf_LE32_Dyn const *p = (Elf_LE32_Dyn const *)(unsigned char *)dynhdr; + int so_needed = 0; + for (j = phdrdyn->p_memsz / sizeof(*p); --j>=0; ++p) { + if (p->d_tag==DT_NEEDED) { + so_needed++; + } + if (p->d_tag==DT_STRTAB) { + strtabx = find_file_offset(p->d_val, phdr0, e_phnum); + } + if (p->d_tag==DT_STRSZ) { + strsz= p->d_val; + } + } + + if (so_needed) { + assert(0!=strsz && ~0u!=strtabx); + MemBuffer strtab(strsz); + fi->seek(strtabx, SEEK_SET); + fi->read(strtab, strsz); + + int c_needed = 1; // index 0 is reserved + p = (Elf_LE32_Dyn const *)(unsigned char *)dynhdr; + for (j = phdrdyn->p_memsz / sizeof(*p); --j>=0; ++p) { + if (p->d_tag==DT_NEEDED) { + c_needed += 1+ strlen(p->d_val + strtab); + } + } + + MemBuffer newtab(c_needed); + unsigned char *cp = newtab; + *cp++ = 0; + p = (Elf_LE32_Dyn const *)(unsigned char *)dynhdr; + for (j = phdrdyn->p_memsz / sizeof(*p); --j>=0; ++p) { + if (p->d_tag==DT_NEEDED) { + unsigned char const *const str = p->d_val + strtab; + unsigned const len = 1+ strlen(str); + memcpy(cp, str, len); + cp += len; + } + } + Elf_LE32_Dyn outdyn[3 + so_needed], *q = &outdyn[0]; + q->d_tag = DT_STRSZ; + q->d_val = c_needed; // + identsize; + ++q; + q->d_tag = DT_STRTAB; + q->d_val = sizeof(*h3) + sizeof(outdyn) + h3->phdr[0].p_vaddr; + ++q; + cp = 1+ newtab; + for (j= so_needed; --j>=0; ++q) { + q->d_tag = DT_NEEDED; q->d_val = cp - newtab; + cp += 1+ strlen(cp); + } + q->d_tag = DT_NULL; q->d_val = 0; + h3->phdr[2].p_type = PT_DYNAMIC; + h3->phdr[2].p_offset = sizeof(*h3); + h3->phdr[2].p_vaddr = sizeof(*h3) + h3->phdr[0].p_vaddr; + h3->phdr[2].p_paddr = h3->phdr[2].p_vaddr; + h3->phdr[2].p_filesz = sizeof(outdyn) + c_needed; // + identsize; + h3->phdr[2].p_memsz = h3->phdr[2].p_filesz; + h3->phdr[2].p_flags = Elf_LE32_Phdr::PF_R; + h3->phdr[2].p_align = 4; + pt_dynamic.alloc(h3->phdr[2].p_filesz); + + j = 0; + memcpy(j + pt_dynamic, &outdyn[0], sizeof(outdyn)); + j += sizeof(outdyn); + memcpy(j + pt_dynamic, newtab, c_needed); + j += c_needed; +#if 0 //{ + memcpy(j + pt_dynamic, ident, identsize); + // FIXME ?? patchVersion(j + pt_dynamic, identsize); + j += identsize; +#endif //} + sz_dynamic = j; + +#if 0 //{ debugging: see the results early + fo->seek(0, SEEK_SET); + fo->write(h3, sizeof(*h3)); +#endif //} + fo->write(pt_dynamic, sz_dynamic); + } + } +} + +void +PackLinuxI386::pack1(OutputFile *fo, Filter &) +{ + // create a pseudo-unique program id for our paranoid stub + progid = getRandomId(); + + generateElfHdr(fo, linux_i386exec_fold, 0, 0, 0); +} + +void +PackLinuxI386::pack4(OutputFile *fo, Filter &ft) +{ + overlay_offset = sizeof(elfout.ehdr) + + (elfout.ehdr.e_phentsize * elfout.ehdr.e_phnum) + + sizeof(l_info) + + ((elfout.ehdr.e_phnum==3) ? elfout.phdr[2].p_memsz : 0) ; + super::pack4(fo, ft); // write PackHeader and overlay_offset + + elfout.phdr[0].p_filesz = fo->getBytesWritten(); + +#define PAGE_MASK (~0<<12) + // pre-calculate for benefit of runtime disappearing act via munmap() + elfout.phdr[0].p_memsz = PAGE_MASK & (~PAGE_MASK + elfout.phdr[0].p_filesz); +#undef PAGE_MASK + + // rewrite Elf header + fo->seek(0, SEEK_SET); + fo->rewrite(&elfout, sizeof(elfout.ehdr) + + elfout.ehdr.e_phnum * sizeof(elfout.phdr[0]) + + sizeof(l_info) ); +} + +static unsigned +umax(unsigned a, unsigned b) +{ + if (a <= b) { + return b; + } + return a; +} int PackLinuxI386::buildLinuxLoader( @@ -87,52 +306,22 @@ PackLinuxI386::buildLinuxLoader( unsigned const szproto, upx_byte const *const fold, unsigned const szfold, - Filter const *ft, - unsigned const brka + Filter const *ft ) { initLoader(proto, szproto); - struct cprElfhdr elfhdr = *(struct cprElfhdr const *)fold; - memset(&elfhdr.linfo, 0, sizeof(elfhdr.linfo)); + cprElfHdr1 const *const hf = (cprElfHdr1 const *)fold; + unsigned const fold_hdrlen = umax(0x80, sizeof(hf->ehdr) + + hf->ehdr.e_phentsize * hf->ehdr.e_phnum + sizeof(l_info) ); + struct b_info h; memset(&h, 0, sizeof(h)); + h.sz_unc = szfold - fold_hdrlen; + h.b_method = ph.method; + h.b_ftid = ph.filter; + h.b_cto8 = ph.filter_cto; + unsigned char const *const uncLoader = fold_hdrlen + fold; - // Info for OS kernel to set the brk() - if (brka) { - assert(ph.format==UPX_F_LINUX_ELF_i386 - || ph.format==UPX_F_LINUX_SH_i386 ); - if (ph.format==UPX_F_LINUX_SH_i386) { - assert(elfhdr.ehdr.e_phnum==1); - elfhdr.ehdr.e_phnum = 2; - } - elfhdr.phdr[1].p_offset = 0xfff&brka; - elfhdr.phdr[1].p_vaddr = brka; - elfhdr.phdr[1].p_paddr = brka; - elfhdr.phdr[1].p_filesz = 0; - elfhdr.phdr[1].p_memsz = 0; - } - - // The beginning of our loader consists of a elf_hdr (52 bytes) and - // two sections elf_phdr (2 * 32 byte), so we have 12 free bytes - // from offset 116 to the program start at offset 128. - assert(elfhdr.ehdr.e_phoff == sizeof(Elf_LE32_Ehdr)); - assert(elfhdr.ehdr.e_shoff == 0); - assert(elfhdr.ehdr.e_ehsize == sizeof(Elf_LE32_Ehdr)); - assert(elfhdr.ehdr.e_phentsize == sizeof(Elf_LE32_Phdr)); - assert(elfhdr.ehdr.e_phnum == (unsigned)(1+ (0!=brka))); - assert(elfhdr.ehdr.e_shnum == 0); - linker->addSection("ELFHEADX", (unsigned char const *)&elfhdr, sizeof(elfhdr)); - addLoader("ELFHEADX", 0); - - struct { - upx_uint sz_unc; // uncompressed - upx_uint sz_cpr; // compressed - } h = { - szfold - sizeof(elfhdr), 0 - }; - unsigned char const *const uncLoader = (unsigned char const *)(1+ - (struct cprElfhdr const *)fold ); unsigned char *const cprLoader = new unsigned char[sizeof(h) + h.sz_unc]; - int r = upx_compress(uncLoader, h.sz_unc, sizeof(h) + cprLoader, &h.sz_cpr, NULL, ph.method, 10, NULL, NULL ); if (r != UPX_E_OK || h.sz_cpr >= h.sz_unc) @@ -143,7 +332,21 @@ PackLinuxI386::buildLinuxLoader( n_mru = ft->n_mru; - addLoader("IDENTSTR", 0); + // Here is a quick summary of the format of the output file: + linker->setLoaderAlignOffset( + // Elf32_Edhr + sizeof(elfout.ehdr) + + // Elf32_Phdr: 1 for exec86, 2 for sh86, 3 for elf86 + (elfout.ehdr.e_phentsize * elfout.ehdr.e_phnum) + + // checksum UPX! lsize version format + sizeof(l_info) + + // PT_DYNAMIC with DT_NEEDED "forwarded" from original file + ((elfout.ehdr.e_phnum==3) ? elfout.phdr[2].p_memsz : 0) + + // p_progid, p_filesize, p_blocksize + sizeof(p_info) + + // compressed data + ph.b_len + ph.c_len ); + // entry to stub addLoader("LEXEC000", 0); if (ft->id) { @@ -206,21 +409,18 @@ PackLinuxI386::buildLinuxLoader( } } + addLoader("IDENTSTR", 0); addLoader("LEXEC020", 0); addLoader("FOLDEXEC", 0); - struct Elf_LE32_Ehdr *const ldrEhdr = - (struct Elf_LE32_Ehdr *)const_cast(getLoader()); - unsigned e_entry = getLoaderSectionStart("LEXEC000"); - ldrEhdr->e_entry = e_entry + elfhdr.phdr[0].p_vaddr; - - char *ptr_cto = e_entry + (char *)ldrEhdr; - int sz_cto = getLoaderSize() - e_entry; + char *ptr_cto = (char *)const_cast(getLoader()); + int sz_cto = getLoaderSize(); if (0x20==(ft->id & 0xF0) || 0x30==(ft->id & 0xF0)) { // push byte '?' ; cto8 patch_le16(ptr_cto, sz_cto, "\x6a?", 0x6a + (ft->cto << 8)); } + // PackHeader and overlay_offset at the end of the output file, + // after the compressed data. - // FIXME ?? patchVersion((char *)ldrEhdr, e_entry); return getLoaderSize(); } @@ -247,12 +447,12 @@ PackLinuxI386::buildLoader(Filter const *ft) // filter optimizeFilter(&fold_ft, buf, sz_fold); - bool success = fold_ft.filter(buf + sizeof(cprElfhdr), sz_fold - sizeof(cprElfhdr)); + bool success = fold_ft.filter(buf + sizeof(cprElfHdr2), sz_fold - sizeof(cprElfHdr2)); (void)success; return buildLinuxLoader( linux_i386exec_loader, sizeof(linux_i386exec_loader), - buf, sz_fold, ft, 0 ); + buf, sz_fold, ft ); } int PackLinuxI386::getLoaderPrefixSize() const @@ -366,15 +566,14 @@ bool PackLinuxI386::canPack() } -void PackLinuxI386::patchLoader() -{ -} +void PackLinuxI386::patchLoader() { } void PackLinuxI386::patchLoaderChecksum() { unsigned char *const ptr = const_cast(getLoader()); - l_info *const lp = (l_info *)(ptr + getLoaderPrefixSize()); + l_info *const lp = (l_info *)(sizeof(elfout.ehdr) + + (elfout.ehdr.e_phnum * elfout.ehdr.e_phentsize) + (char *)&elfout ); // checksum for loader + p_info lp->l_checksum = 0; lp->l_magic = UPX_ELF_MAGIC; @@ -383,36 +582,14 @@ void PackLinuxI386::patchLoaderChecksum() lp->l_format = (unsigned char) ph.format; // INFO: lp->l_checksum is currently unused unsigned adler = upx_adler32(0,NULL,0); - adler = upx_adler32(adler, ptr, lsize + sizeof(p_info)); + adler = upx_adler32(adler, ptr, lsize); lp->l_checksum = adler; } void PackLinuxI386::updateLoader(OutputFile *fo) { -#define PAGE_MASK (~0<<12) - Elf_LE32_Ehdr *const ehdr = (Elf_LE32_Ehdr *)const_cast(getLoader()); - Elf_LE32_Phdr *const phdr = (Elf_LE32_Phdr *)(1+ehdr); - - ehdr->e_phnum = 2; - phdr->p_filesz = lsize; - // phdr->p_memsz is the decompressed size - assert(lsize > 128 && lsize < 4096); - - // The first Phdr maps the stub (instructions, data, bss) rwx. - // The second Phdr maps the overlay r--, - // to defend against /usr/bin/strip removing the overlay. - Elf_LE32_Phdr *const phdro = 1+phdr; - - phdro->p_type = PT_LOAD; - phdro->p_offset = lsize; - phdro->p_paddr = phdro->p_vaddr = 0x00400000 + (lsize &~ PAGE_MASK); - phdro->p_memsz = phdro->p_filesz = fo->getBytesWritten() - lsize; - phdro->p_flags = phdro->PF_R; - phdro->p_align = (unsigned) (-PAGE_MASK); - - patchLoaderChecksum(); -#undef PAGE_MASK + elfout.ehdr.e_entry = fo->getBytesWritten() + elfout.phdr[0].p_vaddr; } diff --git a/src/p_lx_exc.h b/src/p_lx_exc.h index 2f38360d..20d0bd6e 100644 --- a/src/p_lx_exc.h +++ b/src/p_lx_exc.h @@ -39,6 +39,13 @@ class PackLinuxI386 : public PackUnixLe32 typedef PackUnixLe32 super; public: PackLinuxI386(InputFile *f) : super(f) { } + virtual void generateElfHdr( + OutputFile *, + void const *proto, + Elf_LE32_Phdr const *const phdr0, + unsigned e_phnum, + unsigned brka + ); virtual int getFormat() const { return UPX_F_LINUX_i386; } virtual const char *getName() const { return "linux/386"; } virtual const int *getCompressionMethods(int method, int level) const; @@ -48,6 +55,18 @@ public: virtual bool canPack(); protected: + 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 PackHeader + + unsigned find_file_offset( + unsigned vaddr, + Elf_LE32_Phdr const *phdr, + unsigned e_phnum + ); + Elf_LE32_Phdr const *find_DYNAMIC(Elf_LE32_Phdr const *phdr, unsigned n); + // loader util virtual int getLoaderPrefixSize() const; virtual int buildLinuxLoader( @@ -55,16 +74,9 @@ protected: unsigned const szproto, upx_byte const *const fold, // linked assembly + C section unsigned const szfold, - Filter const *ft, - unsigned const brka + Filter const *ft ); - struct cprElfhdr { - struct Elf_LE32_Ehdr ehdr; - struct Elf_LE32_Phdr phdr[2]; - struct PackUnix::l_info linfo; - }; - // patch util virtual void patchLoader(); virtual void patchLoaderChecksum(); @@ -79,6 +91,25 @@ protected: }; unsigned n_mru; + + struct cprElfHdr1 { + struct Elf_LE32_Ehdr ehdr; + struct Elf_LE32_Phdr phdr[1]; + struct PackUnix::l_info linfo; + }; + struct cprElfHdr2 { + struct Elf_LE32_Ehdr ehdr; + struct Elf_LE32_Phdr phdr[2]; + struct PackUnix::l_info linfo; + }; + struct cprElfHdr3 { + struct Elf_LE32_Ehdr ehdr; + struct Elf_LE32_Phdr phdr[3]; + struct PackUnix::l_info linfo; + }; + + cprElfHdr3 elfout; + }; diff --git a/src/p_lx_sh.cpp b/src/p_lx_sh.cpp index 195de3a7..6b5a6c8d 100644 --- a/src/p_lx_sh.cpp +++ b/src/p_lx_sh.cpp @@ -78,17 +78,15 @@ PackLinuxI386sh::buildLoader(Filter const *ft) // filter optimizeFilter(&fold_ft, buf, sz_fold); - bool success = fold_ft.filter(buf + sizeof(cprElfhdr), sz_fold - sizeof(cprElfhdr)); + bool success = fold_ft.filter(buf + sizeof(cprElfHdr2), sz_fold - sizeof(cprElfHdr2)); (void)success; return buildLinuxLoader( linux_i386sh_loader, sizeof(linux_i386sh_loader), - buf, sz_fold, ft, 0x08048000 ); + buf, sz_fold, ft ); } -void PackLinuxI386sh::patchLoader() -{ -} +void PackLinuxI386sh::patchLoader() { } bool PackLinuxI386sh::getShellName(char *buf) @@ -104,7 +102,6 @@ bool PackLinuxI386sh::getShellName(char *buf) }; for (int j=0; 0 != shname[j]; ++j) { if (0==strcmp(shname[j], basename)) { - o_shname += 3; // space for "-c\x00" return super::canPack(); } } @@ -134,34 +131,12 @@ bool PackLinuxI386sh::canPack() } -void PackLinuxI386sh::pack(OutputFile *fo) +void +PackLinuxI386sh::pack1(OutputFile *fo, Filter &) { -#define PAGE_MASK (~0<<12) - opt->unix.blocksize = blocksize = file_size; - PackUnix::pack(fo); - - // update loader - Elf_LE32_Ehdr *const ehdr = (Elf_LE32_Ehdr *)const_cast(getLoader()); - Elf_LE32_Phdr *const phdro = (Elf_LE32_Phdr *)(1+ehdr); - off_t const totlen = fo->getBytesWritten(); - phdro[0].p_filesz = totlen; - phdro[0].p_memsz = PAGE_MASK & (~PAGE_MASK + totlen); - - // Try to make brk() work by faking it for exec(). - unsigned const brka = 0x08048000; - phdro[1].p_offset = 0xfff&brka; - phdro[1].p_vaddr = brka; - phdro[1].p_paddr = brka; - phdro[1].p_filesz = 0; - phdro[1].p_memsz = 0; - - patchLoaderChecksum(); - fo->seek(0, SEEK_SET); - fo->rewrite(ehdr, sizeof(cprElfhdr)); -#undef PAGE_MASK + generateElfHdr(fo, linux_i386sh_fold, 0, 0, 0x08048000); } - /* vi:ts=4:et */ diff --git a/src/p_lx_sh.h b/src/p_lx_sh.h index c56ca94d..c79909b6 100644 --- a/src/p_lx_sh.h +++ b/src/p_lx_sh.h @@ -47,7 +47,7 @@ public: virtual const int *getFilters() const { return NULL; } virtual int buildLoader(const Filter *); - virtual void pack(OutputFile *fo); + virtual void pack1(OutputFile *fo, Filter &ft); virtual bool canPack(); // virtual void unpack(OutputFile *fo) { super::unpack(fo); } diff --git a/src/p_unix.cpp b/src/p_unix.cpp index e90931b2..2ef3b782 100644 --- a/src/p_unix.cpp +++ b/src/p_unix.cpp @@ -114,29 +114,24 @@ bool PackUnix::checkCompressionRatio(unsigned, unsigned) const return true; } - -void PackUnix::pack(OutputFile *fo) +void PackUnix::pack1(OutputFile */*fo*/, Filter &/*ft*/) { - // set options - blocksize = opt->unix.blocksize; - if (blocksize <= 0) - blocksize = BLOCKSIZE; - if ((off_t)blocksize > file_size) - blocksize = file_size; - // create a pseudo-unique program id for our paranoid stub - progid = getRandomId(); + // derived class usually provides this +} - // init compression buffers - ibuf.alloc(blocksize); - obuf.allocForCompression(blocksize); - { - p_info hbuf; - set_native32(&hbuf.p_progid, progid); - set_native32(&hbuf.p_filesize, file_size); - set_native32(&hbuf.p_blocksize, blocksize); - obuf.write(&hbuf, sizeof(hbuf)); - } +int +PackUnix::getStrategy(Filter &/*ft*/) +{ + // Called just before reading and compressing each block. + // Might want to adjust blocksize, etc. + // If user specified the filter, then use it (-2==strategy). + // Else try the first two filters, and pick the better (2==strategy). + return ((opt->filter > 0) ? -2 : 2); +} + +void PackUnix::pack2(OutputFile *fo, Filter &ft) +{ // compress blocks unsigned total_in = 0; unsigned total_out = 0; @@ -144,93 +139,128 @@ void PackUnix::pack(OutputFile *fo) if (ui_total_passes == 1) ui_total_passes = 0; - Filter ft(ph.level); - ft.addvalue = 0; - - fi->seek(0, SEEK_SET); - for (;;) - { + for (;;) { + int const strategy = getStrategy(ft); // might adjust blocksize, etc. int l = fi->read(ibuf, blocksize); - if (l == 0) + if (l == 0) { break; + } // Note: compression for a block can fail if the // file is e.g. blocksize + 1 bytes long // compress - ph.c_len = ph.u_len = l; ph.overlap_overhead = 0; + ph.c_len = ph.u_len = l; ft.buf_len = l; - // leave room for block sizes - unsigned char size[8]; - set_native32(size+0, ph.u_len); - set_native32(size+4, ph.c_len); // will be rewritten - obuf.write(size, 8); + // compressWithFilters() updates u_adler _inside_ compress(); + // that is, AFTER filtering. We want BEFORE filtering, + // so that decompression checks the end-to-end checksum. + unsigned const end_u_adler = upx_adler32(ph.u_adler, ibuf, ph.u_len); + compressWithFilters(&ft, OVERHEAD, strategy); - // If user specified the filter, then use it (-2==strategy). - // Else try the first two filters, and pick the better (2==strategy). - compressWithFilters(&ft, OVERHEAD, ((opt->filter > 0) ? -2 : 2)); - - if (ph.c_len < ph.u_len) - { + if (ph.c_len < ph.u_len) { ph.overlap_overhead = OVERHEAD; if (!testOverlappingDecompression(obuf, ph.overlap_overhead)) throwNotCompressible(); } - else - { + else { // block is not compressible ph.c_len = ph.u_len; // must manually update checksum of compressed data ph.c_adler = upx_adler32(ph.c_adler, ibuf, ph.u_len); } - // update compressed size - set_native32(obuf - 4, ph.c_len); + // write block header + b_info blk_info; memset(&blk_info, 0, sizeof(blk_info)); + set_native32(&blk_info.sz_unc, ph.u_len); + set_native32(&blk_info.sz_cpr, ph.c_len); + blk_info.b_method = ph.method; + blk_info.b_ftid = ph.filter; + blk_info.b_cto8 = ph.filter_cto; + fo->write(&blk_info, sizeof(blk_info)); + ph.b_len += sizeof(b_info); // write compressed data - if (ph.c_len < ph.u_len) - { - obuf.write(obuf, ph.c_len); - // FIXME: obuf is not discardable! - // verifyOverlappingDecompression(); + if (ph.c_len < ph.u_len) { + fo->write(obuf, ph.c_len); + verifyOverlappingDecompression(); // uses ph.u_adler } - else - obuf.write(ibuf, ph.u_len); + else { + fo->write(ibuf, ph.u_len); + } + ph.u_adler = end_u_adler; total_in += ph.u_len; total_out += ph.c_len; } - if ((off_t)total_in != file_size) - throwEOFException(); - - // write block end marker (uncompressed size 0) - set_native32(obuf, 0); - obuf.write(obuf, 4); // update header with totals ph.u_len = total_in; ph.c_len = total_out; + if ((off_t)total_in != file_size) { + throwEOFException(); + } +} + +void PackUnix::pack3(OutputFile *fo, Filter &ft) +{ upx_byte const *p = getLoader(); lsize = getLoaderSize(); patchFilter32(const_cast(p), lsize, &ft); + updateLoader(fo); + patchLoaderChecksum(); fo->write(p, lsize); +} - unsigned pos = obuf.seek(0, SEEK_CUR); - fo->write(obuf - pos, pos); - - // write packheader +void PackUnix::pack4(OutputFile *fo, Filter &) +{ writePackHeader(fo); - // write overlay offset (needed for decompression) - set_native32(obuf, lsize); - fo->write(obuf, 4); + unsigned tmp; + set_native32(&tmp, overlay_offset); + fo->write(&tmp, sizeof(tmp)); +} - updateLoader(fo); - fo->seek(0, SEEK_SET); - fo->rewrite(p, lsize); +void PackUnix::pack(OutputFile *fo) +{ + Filter ft(ph.level); + ft.addvalue = 0; + ph.b_len = 0; + progid = 0; + + // set options + blocksize = opt->unix.blocksize; + if (blocksize <= 0) + blocksize = BLOCKSIZE; + if ((off_t)blocksize > file_size) + blocksize = file_size; + + // init compression buffers + ibuf.alloc(blocksize); + obuf.allocForCompression(blocksize); + + fi->seek(0, SEEK_SET); + pack1(fo, ft); // generate Elf header, etc. + + p_info hbuf; + set_native32(&hbuf.p_progid, progid); + set_native32(&hbuf.p_filesize, file_size); + set_native32(&hbuf.p_blocksize, blocksize); + fo->write(&hbuf, sizeof(hbuf)); + + pack2(fo, ft); // append the compressed body + + // write block end marker (uncompressed size 0) + b_info hdr; memset(&hdr, 0, sizeof(hdr)); + set_le32(&hdr.sz_cpr, UPX_MAGIC_LE32); + fo->write(&hdr, sizeof(hdr)); + + pack3(fo, ft); // append loader + + pack4(fo, ft); // append PackHeader and overlay_offset; update Elf header // finally check the compression ratio if (!checkFinalCompressionRatio(fo)) @@ -305,42 +335,48 @@ void PackUnix::unpack(OutputFile *fo) { #define buf ibuf int i; - int size[2]; + b_info bhdr; + unsigned sz_unc, sz_cpr; - fi->readx(buf, 8); - ph.u_len = size[0] = get_native32(buf+0); - ph.c_len = size[1] = get_native32(buf+4); + fi->readx(&bhdr, sizeof(bhdr)); + ph.u_len = sz_unc = get_native32(&bhdr.sz_unc); + ph.c_len = sz_cpr = get_native32(&bhdr.sz_cpr); - if (size[0] == 0) // uncompressed size 0 -> EOF + if (sz_unc == 0) // uncompressed size 0 -> EOF { - // note: must reload size[1] as magic is always stored le32 - size[1] = get_le32(buf+4); - if (size[1] != UPX_MAGIC_LE32) // size[1] must be h->magic + // note: must reload sz_cpr as magic is always stored le32 + sz_cpr = get_le32(&bhdr.sz_cpr); + if (sz_cpr != UPX_MAGIC_LE32) // sz_cpr must be h->magic throwCompressedDataViolation(); break; } - if (size[0] <= 0 || size[1] <= 0) + if (sz_unc <= 0 || sz_cpr <= 0) throwCompressedDataViolation(); - if (size[1] > size[0] || size[0] > (int)blocksize) + if (sz_cpr > sz_unc || sz_unc > blocksize) throwCompressedDataViolation(); - i = blocksize + OVERHEAD - size[1]; - fi->readx(buf+i, size[1]); + i = blocksize + OVERHEAD - sz_cpr; + fi->readx(buf+i, sz_cpr); // update checksum of compressed data - c_adler = upx_adler32(c_adler, buf + i, size[1]); + c_adler = upx_adler32(c_adler, buf + i, sz_cpr); // decompress - if (size[1] < size[0]) - { + if (sz_cpr < sz_unc) { decompress(buf+i, buf, false); + if (0!=bhdr.b_ftid) { + Filter ft(ph.level); + ft.init(bhdr.b_ftid); + ft.cto = bhdr.b_cto8; + ft.unfilter(buf, sz_unc); + } i = 0; } // update checksum of uncompressed data - u_adler = upx_adler32(u_adler, buf + i, size[0]); - total_in += size[1]; - total_out += size[0]; + u_adler = upx_adler32(u_adler, buf + i, sz_unc); + total_in += sz_cpr; + total_out += sz_unc; // write block if (fo) - fo->write(buf + i, size[0]); + fo->write(buf + i, sz_unc); #undef buf } diff --git a/src/p_unix.h b/src/p_unix.h index af3c6034..316d5519 100644 --- a/src/p_unix.h +++ b/src/p_unix.h @@ -43,6 +43,7 @@ protected: public: virtual int getVersion() const { return 11; } virtual const int *getFilters() const { return NULL; } + virtual int getStrategy(Filter &); virtual void pack(OutputFile *fo); virtual void unpack(OutputFile *fo); @@ -52,6 +53,11 @@ public: protected: // called by the generic pack() + 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 PackHeader + virtual void patchLoader() = 0; virtual void patchLoaderChecksum() {} virtual void updateLoader(OutputFile *) = 0; @@ -72,7 +78,19 @@ protected: MemBuffer loader; int lsize; + MemBuffer pt_dynamic; + int sz_dynamic; + // must agree with stub/linux.hh + struct b_info { // 12-byte header before each compressed block + unsigned sz_unc; // uncompressed_size + unsigned sz_cpr; // compressed_size + // FIXME: Unfortunately, endian-ness can BSWAP these four: + unsigned char b_method; // compression algorithm + unsigned char b_ftid; // filter id + unsigned char b_cto8; // filter parameter + unsigned char b_unused; + }; struct l_info { // 12-byte trailer in header for loader unsigned l_checksum; unsigned l_magic; diff --git a/src/packer.cpp b/src/packer.cpp index 187c8785..6bd0db43 100644 --- a/src/packer.cpp +++ b/src/packer.cpp @@ -923,17 +923,8 @@ unsigned Packer::unoptimizeReloc32(upx_byte **in, upx_byte *image, // loader util **************************************************************************/ -void Packer::initLoader(const void *pdata, int plen, int pinfo) +char const *Packer::identstr(unsigned &size) { - if (pinfo < 0) - pinfo = ~3 & (3 + get_le16(pdata, plen - 2)); - - delete linker; - if (getFormat() < 128) - linker = new Linker(pdata, plen, pinfo); // little endian - else - linker = new BeLinker(pdata, plen, pinfo); // big endian - static const char identbig[] = "\n\0" "$Info: This file is packed with the UPX executable packer http://upx.tsx.org $" @@ -946,10 +937,30 @@ void Packer::initLoader(const void *pdata, int plen, int pinfo) "$Id: UPX (C) 1996-2001 the UPX Team. All Rights Reserved. http://upx.tsx.org $" "\n"; - if (opt->small) - linker->addSection("IDENTSTR",identsmall,sizeof(identsmall)); + if (opt->small) { + size = sizeof(identsmall); + return identsmall; + } + else { + size = sizeof(identbig); + return identbig; + } +} + +void Packer::initLoader(const void *pdata, int plen, int pinfo) +{ + if (pinfo < 0) + pinfo = ~3 & (3 + get_le16(pdata, plen - 2)); + + delete linker; + if (getFormat() < 128) + linker = new Linker(pdata, plen, pinfo); // little endian else - linker->addSection("IDENTSTR",identbig,sizeof(identbig)); + linker = new BeLinker(pdata, plen, pinfo); // big endian + + unsigned size; + char const *const ident = identstr(size); + linker->addSection("IDENTSTR",ident,size); } @@ -1453,9 +1464,6 @@ void Packer::compressWithFilters(Filter *parm_ft, // filter optimizeFilter(&ft, ibuf + filter_off, filter_len); - unsigned char *const save = new unsigned char[filter_len]; - memcpy(save, ibuf + filter_off, filter_len); - bool success = ft.filter(ibuf + filter_off, filter_len); if (ft.id != 0 && ft.calls == 0) { @@ -1529,11 +1537,6 @@ void Packer::compressWithFilters(Filter *parm_ft, } // restore ibuf[] - unfilter with verify ft.unfilter(ibuf + filter_off, filter_len, true); - for (unsigned k = 0; k < filter_len; ++k) { - if ((ibuf + filter_off)[k] != save[k]) { - printf("mismatch at %d\n", k); - } - } // if (strategy < 0) break; diff --git a/src/packer.h b/src/packer.h index b771c32b..a6d1ba37 100644 --- a/src/packer.h +++ b/src/packer.h @@ -68,6 +68,7 @@ public: int level; // compresison level 1..10 unsigned u_len; unsigned c_len; + unsigned b_len; // total length of b_info blocks unsigned u_adler; unsigned c_adler; off_t u_file_size; @@ -212,6 +213,7 @@ protected: virtual int getLoaderSectionStart(const char *name, int *slen=NULL) const; virtual void addFilter32(int filter_id); virtual const char *getDecompressor() const; + char const *identstr(unsigned &size); // stub and overlay util static void handleStub(InputFile *fi, OutputFile *fo, long size); @@ -252,7 +254,7 @@ protected: // compression buffers MemBuffer ibuf; // input - MemBufferIO obuf; // output + MemBuffer obuf; // output // UI handler UiPacker *uip; diff --git a/src/stub/fold_elf86.asm b/src/stub/fold_elf86.asm index 702b52a9..4bec2919 100644 --- a/src/stub/fold_elf86.asm +++ b/src/stub/fold_elf86.asm @@ -26,7 +26,10 @@ %define szElf32_Ehdr 0x34 %define szElf32_Phdr 8*4 +%define e_entry (16 + 2*2 + 4) %define p_memsz 5*4 +%define szl_info 12 +%define szp_info 12 %define a_val 4 %define __NR_munmap 91 @@ -34,7 +37,7 @@ ;; control just falls through, after this part and compiled C code ;; are uncompressed. -fold_begin: +fold_begin: ; enter: %ebx= &Elf32_Ehdr of this program ; patchLoader will modify to be ; dword sz_uncompressed, sz_compressed ; byte compressed_data... @@ -45,6 +48,7 @@ fold_begin: ; Move argc,argv,envp down so that we can insert more Elf_auxv entries. ; ld-linux.so.2 depends on AT_PHDR and AT_ENTRY, for instance +%define PAGE_SIZE ( 1<<12) %define OVERHEAD 2048 %define MAX_ELF_HDR 512 @@ -53,18 +57,19 @@ fold_begin: mov edi, esp call do_auxv + mov eax, [p_memsz + 2*szElf32_Phdr + szElf32_Ehdr + ebx] ; size of PT_DYNAMIC sub esp, dword MAX_ELF_HDR + OVERHEAD + mov ecx, [e_entry + ebx] ; beyond compressed data push esp ; argument: temp space + lea eax, [szElf32_Ehdr + 3*szElf32_Phdr + szl_info + szp_info + ebx + eax] ; 1st &b_info push edi ; argument: AT_next + sub ecx, eax ; length of compressed data push ebp ; argument: &decompress - push edx ; argument: my_elfhdr - add edx, [p_memsz + szElf32_Ehdr + edx] - push edx ; argument: uncbuf + push ecx ; argument: sz_compressed + push eax ; argument: 1st &b_info EXTERN upx_main - call upx_main ; entry = upx_main(uncbuf, my_elfhdr, &decompress, AT_next, tmp_ehdr) - pop esi ; decompression buffer == (p_vaddr + p_memsz) of stub - pop ebx ; my_elfhdr - add esp, dword 3*4 + MAX_ELF_HDR + OVERHEAD ; remove 3 params, temp space + call upx_main ; entry = upx_main(b1st_info, sz_cpr, &decompress, AT_next, tmp_ehdr) + add esp, dword 5*4 + MAX_ELF_HDR + OVERHEAD ; remove 5 params, temp space push eax ; save entry address mov edi, [a_val + edi] ; AT_PHDR @@ -90,9 +95,10 @@ EXTERN make_hatch xor eax,eax rep stosd - mov ecx,esi ; my p_vaddr + p_memsz - mov bh,0 ; round down to 64KB boundary - sub ecx,ebx ; length to unmap + xor ecx, ecx ; 0 + mov ch, PAGE_SIZE>>8 ; 0x1000 + add ecx, [p_memsz + szElf32_Ehdr + ebx] ; length to unmap + mov bh, 0 ; from 0x401000 to 0x400000 push byte __NR_munmap pop eax jmp edx ; unmap ourselves via escape hatch, then goto entry diff --git a/src/stub/fold_exec86.asm b/src/stub/fold_exec86.asm index 8d811462..0f0610c2 100644 --- a/src/stub/fold_exec86.asm +++ b/src/stub/fold_exec86.asm @@ -27,18 +27,26 @@ ;; control just falls through, after this part and compiled C code ;; are uncompressed. -fold_begin: ;; this label is known to the Makefile +%define szElf32_Ehdr 0x34 +%define szElf32_Phdr 8*4 +%define e_entry (16 + 2*2 + 4) +%define p_vaddr 2*4 +%define p_memsz 5*4 +%define szl_info 12 +%define szp_info 12 + +fold_begin: ; enter: %ebx= &Elf32_Ehdr of this program pop eax ; discard &dstlen pop eax ; discard dstlen pop eax ; Pop the argument count mov ecx, esp ; argv starts just at the current stack top - lea edx, [ecx+eax*4+4] ; envp = &argv[argc + 1] - push eax ; Restore the stack - push ebp ; argument: &decompress - push ebx ; argument: &my_elfhdr - push edx ; argument: envp - push ecx ; argument: argv + lea edx, [esp+eax*4+4] ; envp = &argv[argc + 1] + mov esi, [e_entry + ebx] + add ebx, szElf32_Ehdr + szElf32_Phdr + szl_info + sub esi, ebx ; length + lea edi, [2 + ebp] ; f_unfilter, maybe + pusha ; (f_unf, cprLen, f_decpr, xx, cprSrc, envp, argv, argc) EXTERN upx_main call upx_main ; Call the UPX main function hlt ; Crash if somehow upx_main does return diff --git a/src/stub/fold_sh86.asm b/src/stub/fold_sh86.asm index df43e6d8..b9ab81bf 100644 --- a/src/stub/fold_sh86.asm +++ b/src/stub/fold_sh86.asm @@ -30,11 +30,15 @@ BITS 32 SECTION .text -fold_begin: - ; patchLoader will modify to be - ; dword sz_uncompressed, sz_compressed - ; byte compressed_data... +%define szElf32_Ehdr 0x34 +%define szElf32_Phdr 8*4 +%define e_entry (16 + 2*2 + 4) +%define p_memsz 5*4 +%define szl_info 12 +%define szp_info 12 +fold_begin: ; enter: %ebx= uncDst + ; also edx= szElf32_Ehdr + 2*szElf32_Phdr + &Elf32_Ehdr pop eax ; discard &sz_uncompressed pop eax ; discard sz_uncompressed @@ -48,39 +52,40 @@ fold_begin: mov esi, esp sub esp, byte 6*8 ; AT_PHENT, AT_PHNUM, AT_PAGESZ, AT_ENTRY, AT_PHDR, AT_NULL mov edi, esp - call do_auxv + call do_auxv ; edi= &AT_NEXT sub esp, dword MAX_ELF_HDR + OVERHEAD - push esp ; argument: temp space - push edi ; argument: AT_next - push ebp ; argument: &decompress - push edx ; argument: my_elfhdr - add ecx, PAGE_SIZE ; uncompressed stub fits in this - push ecx ; argument: uncbuf + + xchg eax, ebx ; eax= uncDst + lea edx, [szl_info + szp_info + edx] ; cprSrc + mov ecx, [ edx] ; sz_unc + mov ebx, [4+ edx] ; sz_cpr + mov esi, eax ; extra copy of uncDst + pusha ; (&AT_NEXT,uncDst,f_decpr,&ehdr,{sz_cpr,cprSrc},{sz_unc,uncDst}) EXTERN upx_main - call upx_main ; entry = upx_main(uncbuf, my_elfhdr, &decompress, AT_next, tmp_ehdr) - pop esi ; decompression buffer - pop ebx ; my_elfhdr - add esp, dword 3*4 + MAX_ELF_HDR + OVERHEAD ; remove 3 params, temp space + call upx_main ; entry = upx_main(...) + pop ecx ; junk + push eax ; save entry address + popa ; edi= entry address; esi= uncDst + add esp, dword MAX_ELF_HDR + OVERHEAD ; remove temp space pop ecx ; argc pop edx ; $0 filename, to become argv[0] push edx ; restore $0 filename - add esi, byte 3 inc ecx push esi ; &uncompressed shell script sub esi, byte 3 mov [esi], word 0x632d ; "-c" inc ecx - push esi ; "-c" + push esi ; &"-c" inc ecx push edx ; argv[0] is duplicate of $0 push ecx ; new argc - push eax ; save entry address + push edi ; save entry address ; _dl_start and company (ld-linux.so.2) assumes that it has virgin stack, ; and does not initialize all its stack local variables to zero. diff --git a/src/stub/l_lx_elf.c b/src/stub/l_lx_elf.c index b96c5572..f227761d 100644 --- a/src/stub/l_lx_elf.c +++ b/src/stub/l_lx_elf.c @@ -120,18 +120,12 @@ unpackExtent( ) { while (xo->size) { - unsigned cto8; - struct { - int32_t sz_unc; // uncompressed - int32_t sz_cpr; // compressed - } h; + struct b_info h; // Note: if h.sz_unc == h.sz_cpr then the block was not // compressible and is stored in its uncompressed form. // Read and check block sizes. xread(xi, (char *)&h, sizeof(h)); - cto8 = h.sz_cpr; - h.sz_cpr >>= 8; if (h.sz_unc == 0) { // uncompressed size 0 -> EOF if (h.sz_cpr != UPX_MAGIC_LE32) // h.sz_cpr must be h->magic err_exit(2); @@ -144,7 +138,7 @@ unpackExtent( ERR_LAB } if (h.sz_cpr > h.sz_unc - || h.sz_unc > (int32_t)xo->size ) { + || h.sz_unc > xo->size ) { err_exit(5); } // Now we have: @@ -162,7 +156,7 @@ ERR_LAB && ((512 < out_len) // this block is longer than Ehdr+Phdrs || (xo->size==(unsigned)h.sz_unc) ) // block is last in Extent ) { - (*f_unf)(xo->buf, out_len, cto8); + (*f_unf)(xo->buf, out_len, h.b_cto8); } xi->buf += h.sz_cpr; xi->size -= h.sz_cpr; @@ -322,46 +316,43 @@ ERR_LAB **************************************************************************/ void *upx_main( - char *const uncbuf, - Elf32_Ehdr const *const my_ehdr, + char /*const*/ *const b1st_info, + unsigned const sz_compressed, f_expand *const f_decompress, Elf32_auxv_t *const av, Elf32_Ehdr *const ehdr ) __asm__("upx_main"); void *upx_main( - char *const uncbuf, - Elf32_Ehdr const *const my_ehdr, // to get compressed size and data + char /*const*/ *const b1st_info, + unsigned const sz_compressed, f_expand *const f_decompress, Elf32_auxv_t *const av, Elf32_Ehdr *const ehdr // temp char[MAX_ELF_HDR+OVERHEAD] ) { - struct cprElfhdr { - Elf32_Ehdr ehdr; - Elf32_Phdr phdr[2]; - struct l_info linfo; - }; - size_t const lsize = ((struct cprElfhdr const *)my_ehdr)->linfo.l_lsize; Elf32_Phdr const *phdr = (Elf32_Phdr const *)(1+ehdr); Elf32_Addr entry; struct Extent xo; - struct Extent xi = { 0, sizeof(struct p_info) + lsize + CONST_CAST(char *, my_ehdr) }; + struct Extent xi = { 0, b1st_info }; // location of 1st b_info - size_t const sz_elfhdrs = ((size_t *)xi.buf)[0]; // sizeof(Ehdr+Phdrs), uncompressed - size_t const sz_pckhdrs = ((size_t *)xi.buf)[1]>>8; // sizeof(Ehdr+Phdrs), compressed + // sizeof(Ehdr+Phdrs), uncompressed + size_t const sz_elfhdrs = ((size_t *)xi.buf)[0]; + + // sizeof(Ehdr+Phdrs), compressed; including b_info header + size_t const sz_pckhdrs = sizeof(struct b_info) + ((size_t *)xi.buf)[1]; - (void)uncbuf; // used by l_lx_sh.c // Uncompress Ehdr and Phdrs. - xo.size = sz_elfhdrs; xo.buf = (char *)ehdr; - xi.size = 2*sizeof(size_t) + sz_pckhdrs; + xo.size = sz_elfhdrs; xo.buf = (char *)ehdr; + xi.size = sz_pckhdrs; unpackExtent(&xi, &xo, f_decompress, 0); // Prepare to decompress the Elf headers again, into the first PT_LOAD. - xi.buf -= 2*sizeof(size_t) + sz_pckhdrs; - xi.size = ((Elf32_Phdr const *)(1 + my_ehdr))->p_filesz - lsize; + xi.buf -= sz_pckhdrs; + xi.size = sz_compressed; - // av[0].a_un.a_val is set again by do_xmap if PT_PHDR is present + // av[0].a_un.a_val is set again by do_xmap if PT_PHDR is present. + // Caller of upx_main assumes that AT_PHDR will be set into av[0] . av[0].a_type = AT_PHDR; av[0].a_un.a_ptr = 1+(Elf32_Ehdr *)phdr->p_vaddr; av[1].a_type = AT_PHENT; av[1].a_un.a_val = ehdr->e_phentsize; av[2].a_type = AT_PHNUM; av[2].a_un.a_val = ehdr->e_phnum; diff --git a/src/stub/l_lx_elf86.asm b/src/stub/l_lx_elf86.asm index f38efc6a..9b47a7d9 100644 --- a/src/stub/l_lx_elf86.asm +++ b/src/stub/l_lx_elf86.asm @@ -116,7 +116,6 @@ decompress: ;__LEXEC020__ -%define PAGE_MASK (~0<<12) %define PAGE_SIZE ( 1<<12) %define MAP_FIXED 0x10 @@ -126,7 +125,6 @@ decompress: %define PROT_WRITE 2 %define PROT_EXEC 4 %define __NR_mmap 90 -%define __NR_munmap 91 ; Decompress the rest of this loader, and jump to it unfold: @@ -135,10 +133,7 @@ unfold: lodsd push eax ; sz_uncompressed (junk, actually) push esp ; &sz_uncompressed - mov eax, ebp ; &decompress - and eax, dword PAGE_MASK ; &my_elfhdr - mov edx, eax ; need my_elfhdr later - mov ah,0 ; round down to 64KB boundary + mov eax, 0x400000 push eax ; &destination ; mmap a page to hold the decompressed program @@ -148,16 +143,19 @@ unfold: mov ch, PAGE_SIZE >> 8 push byte MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS push byte PROT_READ | PROT_WRITE | PROT_EXEC - push ecx + push ecx ; length push eax ; destination + mov ebx, esp ; address of parameter vector for __NR_mmap push byte __NR_mmap pop eax - mov ebx, esp int 0x80 + xchg eax, ebx + mov bh, PAGE_SIZE>>8 ; ebx= 0x401000 add esp, byte 6*4 ; discard args to mmap lodsd push eax ; sz_compressed + lodsd ; junk cto8, algo, unused[2] push esi ; &compressed_data call ebp ; decompress(&src, srclen, &dst, &dstlen) pop eax ; discard &compressed_data diff --git a/src/stub/l_lx_elf86.lds b/src/stub/l_lx_elf86.lds index 984355c9..9f0f088d 100644 --- a/src/stub/l_lx_elf86.lds +++ b/src/stub/l_lx_elf86.lds @@ -28,17 +28,23 @@ OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") OUTPUT_ARCH(i386) /*ENTRY(_start)*/ +PHDRS +{ + text PT_LOAD FILEHDR PHDRS ; + data PT_LOAD ; /* for setting brk(0) */ + null PT_NULL; /* changed to PT_DYNAMIC for ldd */ +} SECTIONS { /* 0x00401000: l_lx_elf86.asm assumes 1 page up from 64KB boundary */ - . = 0x00401000 + SIZEOF_HEADERS; - . = ALIGN(0x80); + . = 0x00401000 + SIZEOF_HEADERS + 12; /* 12==sizeof(l_info) */ .text : { *(.text) *(.data) - } + } : text /* 0x08048000: customary Linux/x86 Elf .text start */ + /* PackLinuxI386::buildLinuxLoader will overwrite the address anyway. */ . = 0x08048000 + (0xfff & .); .data : { - } + } : data } diff --git a/src/stub/l_lx_exec.c b/src/stub/l_lx_exec.c index 93fd4edc..8e118dae 100644 --- a/src/stub/l_lx_exec.c +++ b/src/stub/l_lx_exec.c @@ -139,7 +139,7 @@ do_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) static int go_self(char const *tmpname, char *argv[], char *envp[]) { - // FIXME: why not use "/proc/self/fd/XX"? *BSD doesn't have it? + // Old FreeBSD does not have /proc/self, so use /proc/ instead. // Open the temp file. int const fdi = open(tmpname, O_RDONLY, 0); @@ -181,6 +181,11 @@ go_self(char const *tmpname, char *argv[], char *envp[]) // UPX & NRV stuff **************************************************************************/ +typedef void f_unfilter( + nrv_byte *, // also addvalue + nrv_uint, + unsigned cto8 // junk in high 24 bits +); typedef int f_expand( const nrv_byte *src, nrv_uint src_len, nrv_byte *dst, nrv_uint *dst_len ); @@ -193,16 +198,24 @@ typedef int f_expand( **************************************************************************/ void upx_main( - char *argv[], + f_unfilter *const f_unf, + unsigned cprLen, + f_expand *const f_decompress, + int junk2, + char /*const*/ *cprSrc, char *envp[], - Elf32_Ehdr const *const my_ehdr, - f_expand *const f_decompress + char *argv[], + int argc ) __asm__("upx_main"); void upx_main( - char *argv[], + f_unfilter *const f_unf, + unsigned cprLen, + f_expand *const f_decompress, + int junk, + char /*const*/ *cprSrc, char *envp[], - Elf32_Ehdr const *const my_ehdr, - f_expand *const f_decompress + char *argv[], + int argc ) { // file descriptor @@ -213,9 +226,7 @@ void upx_main( char *tmpname; - Elf32_Phdr const *const phdr = (Elf32_Phdr const *) - (my_ehdr->e_phoff + (char const *)my_ehdr); - struct Extent xi = { phdr[1].p_memsz, (char *)phdr[1].p_vaddr }; + struct Extent xi = { cprLen, cprSrc }; char *next_unmap = (char *)(PAGE_MASK & (unsigned)xi.buf); struct p_info header; @@ -223,6 +234,8 @@ void upx_main( // temporary file name char tmpname_buf[20]; + (void)junk; + // // ----- Step 0: set /proc/self using /proc/ ----- // @@ -347,18 +360,13 @@ void upx_main( for (;;) { - struct { - int32_t sz_unc; // uncompressed - int32_t sz_cpr; // compressed - } h; - // Note: if h.sz_unc == h.sz_cpr then the block was not - // compressible and is stored in its uncompressed form. + struct b_info h; int i; // Read and check block sizes. { register char *__d0, *__d1; - __asm__ __volatile__( "movsl; movsl" + __asm__ __volatile__( "movsl; movsl; movsl" : "=&D" (__d0), "=&S" (__d1) : "0" (&h), "1" (xi.buf) : "memory"); @@ -373,9 +381,10 @@ void upx_main( goto error; break; } - if (h.sz_cpr <= 0) - goto error; - if (h.sz_cpr > h.sz_unc || h.sz_unc > (int32_t)header.p_blocksize) + // Note: if sz_unc == sz_cpr then the block was not + // compressible and is stored in its uncompressed form. + + if (h.sz_cpr > h.sz_unc || h.sz_cpr > header.p_blocksize) goto error; // Now we have: // assert(h.sz_cpr <= h.sz_unc); @@ -387,6 +396,9 @@ void upx_main( i = (*f_decompress)(xi.buf, h.sz_cpr, buf, &out_len); if (i != 0 || out_len != (nrv_uint)h.sz_unc) goto error; + // Right now, unfilter is combined with decompression. + // (*f_unfilter)(buf, out_len, cto8); + (void)f_unf; } else { @@ -486,6 +498,7 @@ void upx_main( waitpid(-1, (int *)0, 0); // Execute the original program. + (void)argc; execve(tmpname, argv, envp); diff --git a/src/stub/l_lx_exec86.asm b/src/stub/l_lx_exec86.asm index d893fb82..7098cb86 100644 --- a/src/stub/l_lx_exec86.asm +++ b/src/stub/l_lx_exec86.asm @@ -138,49 +138,53 @@ decompress: ;__LEXEC020__ -%define PAGE_MASK (~0<<12) %define PAGE_SIZE ( 1<<12) +%define MAP_FIXED 0x10 +%define MAP_PRIVATE 0x02 +%define MAP_ANONYMOUS 0x20 +%define PROT_READ 1 +%define PROT_WRITE 2 +%define PROT_EXEC 4 +%define __NR_mmap 90 + ; Decompress the rest of this loader, and jump to it unfold: pop esi ; &{ sz_uncompressed, sz_compressed, compressed_data...} - mov ecx, PAGE_MASK - push esi ; &dst - mov ebx, ebp ; &decompress - and ebx, ecx ; &my_elfhdr - neg ecx ; ecx= PAGE_SIZE - cld - lodsd ; sz_uncompressed - lodsd ; sz_compressed + lodsd + push eax ; sz_uncompressed (junk, actually) + push esp ; &sz_uncompressed + mov eax, 0x400000 + push eax ; &destination -;; Compressed code now begins at fold_begin. -;; We want decompressed code to begin at fold_begin, too. -;; Move the compressed code to the high end of the page. -;; Assume non-overlapping so that forward movsb is OK. - - lea edi, [ecx + ebx] ; high end of page - push eax ; srclen (of both movsb and decompress) - sub edi, eax ; dst of movsb - push edi ; &src for decompression (after movsb) - xchg ecx, eax ; ecx= len of movsb - rep movsb - call ebp ; decompress(&src, srclen, &dst, &dstlen) - pop eax ; discard &src - pop eax ; discard srclen - pop eax ; &dst == fold_begin - -;; icache lookahead of compressed code after "call unfold" is still there. -;; Synchronize with dcache of decompressed code. - pushf - push cs - push eax - iret ; back to fold_begin! + ; mmap a page to hold the decompressed program + xor ecx, ecx + push ecx + push ecx + mov ch, PAGE_SIZE >> 8 + push byte MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS + push byte PROT_READ | PROT_WRITE | PROT_EXEC + push ecx ; length + push eax ; destination + mov ebx, esp ; address of parameter vector for __NR_mmap + push byte __NR_mmap + pop eax + int 0x80 + xchg eax, ebx + mov bh, PAGE_SIZE>>8 ; ebx= 0x401000 + add esp, byte 6*4 ; discard args to mmap + lodsd + push eax ; sz_compressed + lodsd ; junk cto8, algo, unused[2] + push esi ; &compressed_data + call ebp ; decompress(&src, srclen, &dst, &dstlen) + pop eax ; discard &compressed_data + pop eax ; discard sz_compressed + ret ; &destination main: pop ebp ; &decompress - push eax ; sz_uncompressed (junk, actually) - push esp ; &sz_uncompressed call unfold eof: @@ -190,3 +194,4 @@ eof: dw eof ; vi:ts=8:et:nowrap + diff --git a/src/stub/l_lx_exec86.lds b/src/stub/l_lx_exec86.lds index 4e6c59c8..6e3f742d 100644 --- a/src/stub/l_lx_exec86.lds +++ b/src/stub/l_lx_exec86.lds @@ -30,9 +30,8 @@ OUTPUT_ARCH(i386) /*ENTRY(_start)*/ SECTIONS { - /* 0x08048000: customary Linux/x86 Elf .text start */ - . = 0x08048000 + SIZEOF_HEADERS; - . = ALIGN(0x80); /* room for Ehdr, 2*Phdr, l_info */ + /* 0x00401000: l_lx_exec86.asm assumes 1 page up from 64KB boundary */ + . = 0x00401000 + SIZEOF_HEADERS + 12; /* 12==sizeof(l_info) */ .data : { /* put everything together in one Phdr */ *(.text) *(.rodata) diff --git a/src/stub/l_lx_sh.c b/src/stub/l_lx_sh.c index 9125bf5d..18ad098c 100644 --- a/src/stub/l_lx_sh.c +++ b/src/stub/l_lx_sh.c @@ -114,10 +114,7 @@ unpackExtent( ) { while (xo->size) { - struct { - int32_t sz_unc; // uncompressed - int32_t sz_cpr; // compressed - } h; + struct b_info h; // Note: if h.sz_unc == h.sz_cpr then the block was not // compressible and is stored in its uncompressed form. @@ -135,7 +132,7 @@ unpackExtent( ERR_LAB } if (h.sz_cpr > h.sz_unc - || h.sz_unc > (int32_t)xo->size ) { + || h.sz_unc > xo->size ) { err_exit(5); } // Now we have: @@ -272,48 +269,34 @@ ERR_LAB **************************************************************************/ void *upx_main( - char *const uncbuf, - Elf32_Ehdr const *const my_ehdr, - f_expand *const f_decompress, Elf32_auxv_t *const av, - Elf32_Ehdr *const ehdr + unsigned const junk, + f_expand *const f_decompress, + Elf32_Ehdr *const ehdr, // temp char[MAX_ELF_HDR] + struct Extent xi, + struct Extent xo ) __asm__("upx_main"); void *upx_main( - char *const uncbuf, // place to put decompressed shell script - Elf32_Ehdr const *const my_ehdr, // to get compressed size and data - f_expand *const f_decompress, Elf32_auxv_t *const av, - Elf32_Ehdr *const ehdr // temp char[MAX_ELF_HDR] + unsigned const junk, + f_expand *const f_decompress, + Elf32_Ehdr *const ehdr, // temp char[MAX_ELF_HDR] + struct Extent xi, + struct Extent xo ) { + // 'fn' and 'efn' must not suffer constant-propagation by gcc + // UPX2 = offset to name_of_shell + // UPX3 = strlen(name_of_shell) + char * /*const*/ volatile fn = UPX2 + xo.buf; // past "-c" and "#!" + char * /*const*/ volatile efn = UPX3 + fn; // &terminator Elf32_Addr entry; - size_t const lsize = sizeof(struct p_info) + - *(unsigned short const *)(0x7c + (char const *)my_ehdr); - struct Extent xi = { // describe compressed shell script - ((Elf32_Phdr const *)(1 + my_ehdr))->p_filesz - lsize, - lsize + CONST_CAST(char *, my_ehdr) - }; - struct Extent xo = { ((struct p_info *)xi.buf)[-1].p_filesize, uncbuf }; - // Allocate space for decompressed shell script. - // "1+": guarantee '\0' terminator at end of decompressed script - if (xo.buf != do_mmap(xo.buf, 1+3+xo.size, PROT_READ | PROT_WRITE, - MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0)) { - err_exit(20); -ERR_LAB - } - - // Uncompress shell script - xo.buf += 3; // leave room for "-c" argument + (void)junk; unpackExtent(&xi, &xo, f_decompress); { // Map shell program - // 'fn' and 'efn' must not suffer constant-propagation by gcc - // UPX2 = 3 + offset to name_of_shell - // UPX3 = strlen(name_of_shell) - char * /*const*/ volatile fn = UPX2 + uncbuf; // past "-c" and "#!" - char * /*const*/ volatile efn = UPX3 + fn; // &terminator char const c = *efn; *efn = 0; // terminator entry = getexec(fn, ehdr, av); *efn = c; // replace terminator character diff --git a/src/stub/l_lx_sh86.asm b/src/stub/l_lx_sh86.asm index 5a24f28e..3f704d01 100644 --- a/src/stub/l_lx_sh86.asm +++ b/src/stub/l_lx_sh86.asm @@ -106,12 +106,8 @@ decompress: ;__LEXEC020__ -%define PAGE_MASK (~0<<12) %define PAGE_SIZE ( 1<<12) -%define szElf32_Ehdr 0x34 -%define p_memsz 5*4 - %define MAP_FIXED 0x10 %define MAP_PRIVATE 0x02 %define MAP_ANONYMOUS 0x20 @@ -120,42 +116,52 @@ decompress: %define PROT_EXEC 4 %define __NR_mmap 90 +%define szElf32_Ehdr 0x34 +%define szElf32_Phdr 8*4 +%define e_entry (16 + 2*2 + 4) +%define p_memsz 5*4 +%define szl_info 12 +%define szp_info 12 +%define p_filesize 4 + ; Decompress the rest of this loader, and jump to it unfold: pop esi ; &{ sz_uncompressed, sz_compressed, compressed_data...} cld lodsd - push eax ; sz_uncompressed (junk, actually) + push eax ; sz_uncompressed of folded stub (junk, actually) push esp ; &sz_uncompressed - mov eax, ebp ; &decompress - and eax, dword PAGE_MASK ; &my_elfhdr - mov edx, eax ; need my_elfhdr later - add eax, [p_memsz + szElf32_Ehdr + eax] + mov edx, 0x00800000 ; origin of this program + mov eax, [p_memsz + szElf32_Ehdr + edx] ; length of loaded pages + add eax, edx + add edx, szElf32_Ehdr + 2*szElf32_Phdr ; convenient ptr push eax ; &destination - ; mmap a page to hold the decompressed program - xor ecx,ecx - push ecx - push ecx - mov ch, PAGE_SIZE >> 8 + ; mmap space for unfolded stub, and uncompressed script + mov ecx, [szl_info + p_filesize + edx] ; script size + add ecx, 1+ 3+ (3 -1)+ PAGE_SIZE ; '\0' + "-c" + decompr_overrun + stub + + push eax ; offset (ignored when MAP_ANONYMOUS) + push eax ; fd (ignored when MAP_ANONYMOUS) push byte MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS push byte PROT_READ | PROT_WRITE | PROT_EXEC - push ecx + push ecx ; length push eax ; destination + mov ebx, esp ; address of parameter vector for __NR_mmap push byte __NR_mmap pop eax - mov ebx, esp int 0x80 + lea ebx, [3+ PAGE_SIZE + eax] ; place for uncompressed script add esp, byte 6*4 ; discard args to mmap lodsd - push eax ; sz_compressed + push eax ; sz_compressed of folded stub + lodsd ; junk cto8, algo, unused[2] push esi ; &compressed_data call ebp ; decompress(&src, srclen, &dst, &dstlen) - pop ecx ; discard &compressed_data - pop ecx ; discard sz_compressed - pop ecx ; &destination - jmp ecx ; goto fold_begin at p_vaddr + p_memsz + pop eax ; discard &compressed_data + pop eax ; discard sz_compressed + ret ; &destination main: pop ebp ; &decompress call unfold diff --git a/src/stub/l_lx_sh86.lds b/src/stub/l_lx_sh86.lds index d7d0dc93..06d57437 100644 --- a/src/stub/l_lx_sh86.lds +++ b/src/stub/l_lx_sh86.lds @@ -46,10 +46,4 @@ SECTIONS *(.bss) *(COMMON) } - /* 0x08048000: customary Linux/x86 Elf .text start */ -/* - . = 0x08048000 + (0xfff & .); - .data : { - } -*/ } diff --git a/src/stub/linux.hh b/src/stub/linux.hh index 29dfe5b6..af9fce6b 100644 --- a/src/stub/linux.hh +++ b/src/stub/linux.hh @@ -88,6 +88,15 @@ typedef unsigned int nrv_uint32; // From ../p_unix.h +struct b_info { // 12-byte header before each compressed block + unsigned sz_unc; // uncompressed_size + unsigned sz_cpr; // compressed_size + unsigned char b_method; // compression algorithm + unsigned char b_ftid; // filter id + unsigned char b_cto8; // filter parameter + unsigned char b_unused; +}; + struct l_info // 12-byte trailer in header for loader (offset 116) { uint32_t l_checksum;