From 632b2ae1bac862b1a51c53b9d99701d12e50ead6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20Moln=C3=A1r?= Date: Sun, 2 Mar 2014 22:34:52 +0100 Subject: [PATCH] pe: pack() logic moved to PeFile --- src/p_armpe.cpp | 68 ++++++ src/p_armpe.h | 10 + src/p_w32pe.cpp | 118 ++++++++++- src/p_w32pe.h | 11 +- src/p_w64pep.cpp | 125 ++++++++--- src/p_w64pep.h | 8 +- src/pefile.cpp | 536 +++++++++++++++++++++++++++++++++++++++++++++++ src/pefile.h | 42 ++++ 8 files changed, 885 insertions(+), 33 deletions(-) diff --git a/src/p_armpe.cpp b/src/p_armpe.cpp index 6804f6dd..bdf2061e 100644 --- a/src/p_armpe.cpp +++ b/src/p_armpe.cpp @@ -237,9 +237,76 @@ void PackArmPe::buildLoader(const Filter *ft) addLoader("IDENTSTR,UPX1HEAD", NULL); } +bool PackArmPe::handleForceOption() +{ + return (ih.cpu != 0x1c0 && ih.cpu != 0x1c2) + || (ih.opthdrsize != 0xe0) + || ((ih.flags & EXECUTABLE) == 0) + || (ih.entry == 0 /*&& !isdll*/) + || (ih.ddirsentries != 16) +// || IDSIZE(PEDIR_EXCEPTION) // is this used on arm? +// || IDSIZE(PEDIR_COPYRIGHT) + ; +} + +void PackArmPe::callCompressWithFilters(Filter &ft, int filter_strategy, unsigned ih_codebase) +{ + // limit stack size needed for runtime decompression + upx_compress_config_t cconf; cconf.reset(); + cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28 KiB stack + compressWithFilters(&ft, 2048, &cconf, filter_strategy, + ih_codebase, rvamin, 0, NULL, 0); +} + +void PackArmPe::addNewRelocations(Reloc &rel, unsigned upxsection) +{ + static const char* symbols_to_relocate[] = { + "ONAM", "BIMP", "BREL", "FIBE", "FIBS", "ENTR", "DST0", "SRC0" + }; + for (unsigned s2r = 0; s2r < TABLESIZE(symbols_to_relocate); s2r++) + { + unsigned off = linker->getSymbolOffset(symbols_to_relocate[s2r]); + if (off != 0xdeaddead) + rel.add(off + upxsection, 3); + } +} + +unsigned PackArmPe::getProcessImportParam(unsigned upxsection) +{ + return linker->getSymbolOffset("IATT") + upxsection; +} + +void PackArmPe::defineSymbols(unsigned ncsection, unsigned, unsigned, + unsigned ic, Reloc &, unsigned s1addr) +{ + const unsigned onam = ncsection + soxrelocs + ih.imagebase; + linker->defineSymbol("start_of_dll_names", onam); + linker->defineSymbol("start_of_imports", ih.imagebase + rvamin + cimports); + linker->defineSymbol("start_of_relocs", crelocs + rvamin + ih.imagebase); + linker->defineSymbol("filter_buffer_end", ih.imagebase + ih.codebase + ih.codesize); + linker->defineSymbol("filter_buffer_start", ih.imagebase + ih.codebase); + linker->defineSymbol("original_entry", ih.entry + ih.imagebase); + linker->defineSymbol("uncompressed_length", ph.u_len); + linker->defineSymbol("start_of_uncompressed", ih.imagebase + rvamin); + linker->defineSymbol("compressed_length", ph.c_len); + linker->defineSymbol("start_of_compressed", ih.imagebase + s1addr + ic); + defineDecompressorSymbols(); +} + +void PackArmPe::setOhDataBase(const pe_section_t *osection) +{ + oh.database = osection[2].vaddr; +} + +void PackArmPe::setOhHeaderSize(const pe_section_t *osection) +{ + oh.headersize = osection[1].rawdataptr; +} void PackArmPe::pack(OutputFile *fo) { + super::pack0(fo, 1U << 9, 0x10000, true); +#if 0 // FIXME: we need to think about better support for --exact if (opt->exact) throwCantPackExact(); @@ -695,6 +762,7 @@ void PackArmPe::pack(OutputFile *fo) // finally check the compression ratio if (!checkFinalCompressionRatio(fo)) throwNotCompressible(); +#endif } /* diff --git a/src/p_armpe.h b/src/p_armpe.h index cf33b4e7..bd769d0d 100644 --- a/src/p_armpe.h +++ b/src/p_armpe.h @@ -47,6 +47,16 @@ public: virtual const int *getCompressionMethods(int method, int level) const; virtual const int *getFilters() const; + virtual bool handleForceOption(); + virtual void callCompressWithFilters(Filter &, int filter_strategy, + unsigned ih_codebase); + virtual void defineSymbols(unsigned ncsection, unsigned upxsection, + unsigned sizeof_oh, unsigned isize_isplit, + Reloc &rel, unsigned s1addr); + virtual void addNewRelocations(Reloc &, unsigned upxsection); + virtual unsigned getProcessImportParam(unsigned upxsection); + virtual void setOhDataBase(const pe_section_t *osection); + virtual void setOhHeaderSize(const pe_section_t *osection); virtual void pack(OutputFile *fo); virtual bool canPack(); diff --git a/src/p_w32pe.cpp b/src/p_w32pe.cpp index 305abcc1..3f992e69 100644 --- a/src/p_w32pe.cpp +++ b/src/p_w32pe.cpp @@ -37,9 +37,6 @@ static const #include "stub/i386-win32.pe.h" -#define FILLVAL 0 - - /************************************************************************* // **************************************************************************/ @@ -251,9 +248,123 @@ void PackW32Pe::buildLoader(const Filter *ft) } +bool PackW32Pe::handleForceOption() +{ + return (ih.cpu < 0x14c || ih.cpu > 0x150) + || (ih.opthdrsize != 0xe0) + || ((ih.flags & EXECUTABLE) == 0) + || ((ih.flags & BITS_32_MACHINE) == 0) //NEW: 32 bit machine flag must be set - Stefan Widmann + || (ih.coffmagic != 0x10B) //COFF magic is 0x10B in PE files, 0x20B in PE32+ files - Stefan Widmann + || (ih.entry == 0 && !isdll) + || (ih.ddirsentries != 16) + || IDSIZE(PEDIR_EXCEPTION) // is this used on i386? +// || IDSIZE(PEDIR_COPYRIGHT) + ; +} + +void PackW32Pe::defineSymbols(unsigned ncsection, unsigned upxsection, + unsigned sizeof_oh, unsigned ic, + Reloc &, unsigned s1addr) +{ + const unsigned myimport = ncsection + soresources - rvamin; + + // patch loader + linker->defineSymbol("original_entry", ih.entry); + if (use_dep_hack) + { + // This works around a "protection" introduced in MSVCRT80, which + // works like this: + // When the compiler detects that it would link in some code from its + // C runtime library which references some data in a read only + // section then it compiles in a runtime check whether that data is + // still in a read only section by looking at the pe header of the + // file. If this check fails the runtime does "interesting" things + // like not running the floating point initialization code - the result + // is a R6002 runtime error. + // These supposed to be read only addresses are covered by the sections + // UPX0 & UPX1 in the compressed files, so we have to patch the PE header + // in the memory. And the page on which the PE header is stored is read + // only so we must make it rw, fix the flags (i.e. clear + // PEFL_WRITE of osection[x].flags), and make it ro again. + + // rva of the most significant byte of member "flags" in section "UPX0" + const unsigned swri = pe_offset + sizeof_oh + sizeof(pe_section_t) - 1; + // make sure we only touch the minimum number of pages + const unsigned addr = 0u - rvamin + swri; + linker->defineSymbol("swri", addr & 0xfff); // page offset + // check whether osection[0].flags and osection[1].flags + // are on the same page + linker->defineSymbol("vp_size", ((addr & 0xfff) + 0x28 >= 0x1000) ? + 0x2000 : 0x1000); // 2 pages or 1 page + linker->defineSymbol("vp_base", addr &~ 0xfff); // page mask + linker->defineSymbol("VirtualProtect", myimport + + ilinkerGetAddress("kernel32.dll", "VirtualProtect")); + } + linker->defineSymbol("reloc_delt", 0u - (unsigned) ih.imagebase - rvamin); + linker->defineSymbol("start_of_relocs", crelocs); + linker->defineSymbol("ExitProcess", myimport + + ilinkerGetAddress("kernel32.dll", "ExitProcess")); + linker->defineSymbol("GetProcAddress", myimport + + ilinkerGetAddress("kernel32.dll", "GetProcAddress")); + linker->defineSymbol("kernel32_ordinals", myimport); + linker->defineSymbol("LoadLibraryA", myimport + + ilinkerGetAddress("kernel32.dll", "LoadLibraryA")); + linker->defineSymbol("start_of_imports", myimport); + linker->defineSymbol("compressed_imports", cimports); + + defineDecompressorSymbols(); + linker->defineSymbol("filter_buffer_start", ih.codebase - rvamin); + + // in case of overlapping decompression, this hack is needed, + // because windoze zeroes the word pointed by tlsindex before + // it starts programs + linker->defineSymbol("tls_value", (tlsindex + 4 > s1addr) ? + get_le32(obuf + tlsindex - s1addr - ic) : 0); + linker->defineSymbol("tls_address", tlsindex - rvamin); + + linker->defineSymbol("icon_delta", icondir_count - 1); + linker->defineSymbol("icon_offset", ncsection + icondir_offset - rvamin); + + const unsigned esi0 = s1addr + ic; + linker->defineSymbol("start_of_uncompressed", 0u - esi0 + rvamin); + linker->defineSymbol("start_of_compressed", esi0 + ih.imagebase); + + if (use_tls_callbacks) + { + //esi is ih.imagebase + rvamin + linker->defineSymbol("tls_callbacks_ptr", tlscb_ptr); + linker->defineSymbol("tls_module_base", 0u - rvamin); + } + + linker->defineSymbol(isdll ? "PEISDLL1" : "PEMAIN01", upxsection); + //linker->dumpSymbols(); +} + +void PackW32Pe::addNewRelocations(Reloc &rel, unsigned) +{ + rel.add(linker->getSymbolOffset("PEMAIN01") + 2, 3); + if (use_tls_callbacks) + { + tls_handler_offset = linker->getSymbolOffset("PETLSC2"); + //add relocation entry for TLS callback handler + rel.add(tls_handler_offset + 4, 3); + } +} + +void PackW32Pe::setOhDataBase(const pe_section_t *osection) +{ + oh.database = osection[2].vaddr; +} + +void PackW32Pe::setOhHeaderSize(const pe_section_t *) +{ + oh.headersize = rvamin; // FIXME +} void PackW32Pe::pack(OutputFile *fo) { + super::pack0(fo, 0x0c, 0x400000, false); +#if 0 // FIXME: we need to think about better support for --exact if (opt->exact) throwCantPackExact(); @@ -854,6 +965,7 @@ void PackW32Pe::pack(OutputFile *fo) // finally check the compression ratio if (!checkFinalCompressionRatio(fo)) throwNotCompressible(); +#endif } /* diff --git a/src/p_w32pe.h b/src/p_w32pe.h index 7cb19737..fed56816 100644 --- a/src/p_w32pe.h +++ b/src/p_w32pe.h @@ -47,6 +47,13 @@ public: virtual const int *getCompressionMethods(int method, int level) const; virtual const int *getFilters() const; + virtual bool handleForceOption(); + virtual void defineSymbols(unsigned ncsection, unsigned upxsection, + unsigned sizeof_oh, unsigned isize_isplit, + Reloc &rel, unsigned s1addr); + virtual void addNewRelocations(Reloc &, unsigned upxsection); + virtual void setOhDataBase(const pe_section_t *osection); + virtual void setOhHeaderSize(const pe_section_t *osection); virtual void pack(OutputFile *fo); virtual bool canPack(); @@ -56,10 +63,6 @@ protected: virtual void buildLoader(const Filter *ft); virtual Linker* newLinker() const; - - bool isrtm; - bool use_dep_hack; - bool use_clear_dirty_stack; }; diff --git a/src/p_w64pep.cpp b/src/p_w64pep.cpp index dabebd29..f1881b90 100644 --- a/src/p_w64pep.cpp +++ b/src/p_w64pep.cpp @@ -42,16 +42,6 @@ static const #include "stub/amd64-win64.pep.h" -#define IDSIZE(x) ih.ddirs[x].size -#define IDADDR(x) ih.ddirs[x].vaddr -#define ODSIZE(x) oh.ddirs[x].size -#define ODADDR(x) oh.ddirs[x].vaddr - -#define isdll ((ih.flags & DLL_FLAG) != 0) - -#define FILLVAL 0 - - /************************************************************************* // **************************************************************************/ @@ -153,20 +143,6 @@ Linker* PackW64Pep::newLinker() const } -/************************************************************************* -// util -**************************************************************************/ - -int PackW64Pep::readFileHeader() -{ - char buf[6]; - fi->seek(0x200, SEEK_SET); - fi->readx(buf, 6); - isrtm = 0; - return super::readFileHeader(); -} - - /************************************************************************* // pack **************************************************************************/ @@ -267,9 +243,109 @@ void PackW64Pep::buildLoader(const Filter *ft) addLoader("IDENTSTR,UPX1HEAD", NULL); } +bool PackW64Pep::handleForceOption() +{ + return (ih.cpu != 0x8664) //CPU magic of AMD64 is 0x8664 + || (ih.opthdrsize != 0xF0) //optional header size is 0xF0 in PE32+ files - Stefan Widmann + || (ih.coffmagic != 0x20B) //COFF magic is 0x20B in PE+ files, 0x10B in "normal" 32 bit PE files - Stefan Widmann + || ((ih.flags & EXECUTABLE) == 0) + || ((ih.flags & BITS_32_MACHINE) == 1) //NEW: 32 bit machine flag may not be set - Stefan Widmann + || (ih.entry == 0 && !isdll) + || (ih.ddirsentries != 16) + ; +} + +void PackW64Pep::defineSymbols(unsigned ncsection, unsigned upxsection, + unsigned sizeof_oh, unsigned ic, + Reloc &, unsigned s1addr) +{ + const unsigned myimport = ncsection + soresources - rvamin; + + // patch loader + linker->defineSymbol("original_entry", ih.entry); + if (use_dep_hack) + { + // This works around a "protection" introduced in MSVCRT80, which + // works like this: + // When the compiler detects that it would link in some code from its + // C runtime library which references some data in a read only + // section then it compiles in a runtime check whether that data is + // still in a read only section by looking at the pe header of the + // file. If this check fails the runtime does "interesting" things + // like not running the floating point initialization code - the result + // is a R6002 runtime error. + // These supposed to be read only addresses are covered by the sections + // UPX0 & UPX1 in the compressed files, so we have to patch the PE header + // in the memory. And the page on which the PE header is stored is read + // only so we must make it rw, fix the flags (i.e. clear + // PEFL_WRITE of osection[x].flags), and make it ro again. + + // rva of the most significant byte of member "flags" in section "UPX0" + const unsigned swri = pe_offset + sizeof_oh + sizeof(pe_section_t) - 1; + // make sure we only touch the minimum number of pages + const unsigned addr = 0u - rvamin + swri; + linker->defineSymbol("swri", addr & 0xfff); // page offset + // check whether osection[0].flags and osection[1].flags + // are on the same page + linker->defineSymbol("vp_size", ((addr & 0xfff) + 0x28 >= 0x1000) ? + 0x2000 : 0x1000); // 2 pages or 1 page + linker->defineSymbol("vp_base", addr &~ 0xfff); // page mask + linker->defineSymbol("VirtualProtect", myimport + + ilinkerGetAddress("kernel32.dll", "VirtualProtect")); + } + linker->defineSymbol("start_of_relocs", crelocs); + if (!isdll) + linker->defineSymbol("ExitProcess", myimport + + ilinkerGetAddress("kernel32.dll", "ExitProcess")); + linker->defineSymbol("GetProcAddress", myimport + + ilinkerGetAddress("kernel32.dll", "GetProcAddress")); + linker->defineSymbol("kernel32_ordinals", myimport); + linker->defineSymbol("LoadLibraryA", myimport + + ilinkerGetAddress("kernel32.dll", "LoadLibraryA")); + linker->defineSymbol("start_of_imports", myimport); + linker->defineSymbol("compressed_imports", cimports); + + if (M_IS_LZMA(ph.method)) + { + linker->defineSymbol("lzma_c_len", ph.c_len - 2); + linker->defineSymbol("lzma_u_len", ph.u_len); + } + linker->defineSymbol("filter_buffer_start", ih.codebase - rvamin); + + // in case of overlapping decompression, this hack is needed, + // because windoze zeroes the word pointed by tlsindex before + // it starts programs + linker->defineSymbol("tls_value", (tlsindex + 4 > s1addr) ? + get_le32(obuf + tlsindex - s1addr - ic) : 0); + linker->defineSymbol("tls_address", tlsindex - rvamin); + + linker->defineSymbol("icon_delta", icondir_count - 1); + linker->defineSymbol("icon_offset", ncsection + icondir_offset - rvamin); + + const unsigned esi0 = s1addr + ic; + linker->defineSymbol("start_of_uncompressed", 0u - esi0 + rvamin); + linker->defineSymbol("start_of_compressed", esi0); + + if (use_tls_callbacks) + { + linker->defineSymbol("tls_callbacks_ptr", tlscb_ptr - ih.imagebase); + linker->defineSymbol("tls_module_base", 0u - rvamin); + } + + linker->defineSymbol("START", upxsection); +} + +void PackW64Pep::setOhHeaderSize(const pe_section_t *) +{ + oh.headersize = rvamin; // FIXME +} void PackW64Pep::pack(OutputFile *fo) { + // FIXME: Relocation stripping disabled for now - Stefan Widmann + opt->win32_pe.strip_relocs = false; + super::pack0(fo, 0x0c, 0x0000000140000000ULL); +#if 0 // FIXME: we need to think about better support for --exact if (opt->exact) throwCantPackExact(); @@ -856,6 +932,7 @@ void PackW64Pep::pack(OutputFile *fo) if (!checkFinalCompressionRatio(fo)) throwNotCompressible(); +#endif } /* diff --git a/src/p_w64pep.h b/src/p_w64pep.h index 5ed2c3d8..979fc658 100644 --- a/src/p_w64pep.h +++ b/src/p_w64pep.h @@ -46,13 +46,17 @@ public: virtual const int *getCompressionMethods(int method, int level) const; virtual const int *getFilters() const; + virtual bool handleForceOption(); + virtual void defineSymbols(unsigned ncsection, unsigned upxsection, + unsigned sizeof_oh, unsigned isize_isplit, + Reloc &rel, unsigned s1addr); + virtual void setOhDataBase(const pe_section_t *) {} + virtual void setOhHeaderSize(const pe_section_t *osection); virtual void pack(OutputFile *fo); virtual bool canPack(); protected: - virtual int readFileHeader(); - virtual void buildLoader(const Filter *ft); virtual Linker* newLinker() const; diff --git a/src/pefile.cpp b/src/pefile.cpp index bf568df8..0cbffad7 100644 --- a/src/pefile.cpp +++ b/src/pefile.cpp @@ -463,6 +463,7 @@ void PeFile32::processRelocs() // pass1 info("Relocations: original size: %u bytes, preprocessed size: %u bytes",(unsigned) IDSIZE(PEDIR_RELOC),sorelocs); } +// FIXME - this is too similar to PeFile32::processRelocs void PeFile64::processRelocs() // pass1 { big_relocs = 0; @@ -1990,6 +1991,527 @@ unsigned PeFile::stripDebug(unsigned overlaystart) // pack **************************************************************************/ +void PeFile::readSectionHeaders(unsigned objs, unsigned sizeof_ih) +{ + isection = new pe_section_t[objs]; + fi->seek(pe_offset+sizeof_ih,SEEK_SET); + fi->readx(isection,sizeof(pe_section_t)*objs); + rvamin = isection[0].vaddr; + + infoHeader("[Processing %s, format %s, %d sections]", fn_basename(fi->getName()), getName(), objs); +} + +void PeFile::checkHeaderValues(unsigned subsystem, unsigned mask, + unsigned ih_entry, unsigned ih_filealign) +{ + if ((1u << subsystem) & mask) + { + char buf[100]; + upx_snprintf(buf, sizeof(buf), "PE: subsystem %u is not supported", + subsystem); + throwCantPack(buf); + } + //check CLR Runtime Header directory entry + if (IDSIZE(PEDIR_COMRT)) + throwCantPack(".NET files are not yet supported"); + + if (memcmp(isection[0].name,"UPX",3) == 0) + throwAlreadyPackedByUPX(); + + if (!opt->force && IDSIZE(15)) + throwCantPack("file is possibly packed/protected (try --force)"); + + if (ih_entry && ih_entry < rvamin) + throwCantPack("run a virus scanner on this file!"); + + if (ih_filealign < 0x200) + throwCantPack("filealign < 0x200 is not yet supported"); +} + +unsigned PeFile::handleStripRelocs(upx_uint64_t ih_imagebase, + upx_uint64_t default_imagebase, + unsigned dllflags) +{ + if (dllflags & IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE) + opt->win32_pe.strip_relocs = false; + else if (isdll) //do never strip relocations from DLLs + opt->win32_pe.strip_relocs = false; + else if (opt->win32_pe.strip_relocs < 0) + opt->win32_pe.strip_relocs = (ih_imagebase >= default_imagebase); + if (opt->win32_pe.strip_relocs) + { + if (ih_imagebase < default_imagebase) + throwCantPack("--strip-relocs is not allowed with this imagebase"); + else + return RELOCS_STRIPPED; + } + return 0; +} + +unsigned PeFile::readSections(unsigned objs, unsigned usize, + unsigned ih_filealign, unsigned ih_datasize) +{ + const unsigned xtrasize = UPX_MAX(ih_datasize, 65536u) + IDSIZE(PEDIR_IMPORT) + + IDSIZE(PEDIR_BOUNDIM) + IDSIZE(PEDIR_IAT) + IDSIZE(PEDIR_DELAYIMP) + + IDSIZE(PEDIR_RELOC); + ibuf.alloc(usize + xtrasize); + + // BOUND IMPORT support. FIXME: is this ok? + fi->seek(0,SEEK_SET); + fi->readx(ibuf,isection[0].rawdataptr); + + //Interval holes(ibuf); + + unsigned ic,jc,overlaystart = 0; + ibuf.clear(0, usize); + for (ic = jc = 0; ic < objs; ic++) + { + if (isection[ic].rawdataptr && overlaystart < isection[ic].rawdataptr + isection[ic].size) + overlaystart = ALIGN_UP(isection[ic].rawdataptr + isection[ic].size,ih_filealign); + if (isection[ic].vsize == 0) + isection[ic].vsize = isection[ic].size; + if ((isection[ic].flags & PEFL_BSS) || isection[ic].rawdataptr == 0 + || (isection[ic].flags & PEFL_INFO)) + { + //holes.add(isection[ic].vaddr,isection[ic].vsize); + continue; + } + if (isection[ic].vaddr + isection[ic].size > usize) + throwCantPack("section size problem"); + if (!isrtm && ((isection[ic].flags & (PEFL_WRITE|PEFL_SHARED)) + == (PEFL_WRITE|PEFL_SHARED))) + if (!opt->force) + throwCantPack("writable shared sections not supported (try --force)"); + if (jc && isection[ic].rawdataptr - jc > ih_filealign) + throwCantPack("superfluous data between sections"); + fi->seek(isection[ic].rawdataptr,SEEK_SET); + jc = isection[ic].size; + if (jc > isection[ic].vsize) + jc = isection[ic].vsize; + if (isection[ic].vsize == 0) // hack for some tricky programs - may this break other progs? + jc = isection[ic].vsize = isection[ic].size; + if (isection[ic].vaddr + jc > ibuf.getSize()) + throwInternalError("buffer too small 1"); + fi->readx(ibuf + isection[ic].vaddr,jc); + jc += isection[ic].rawdataptr; + } + return overlaystart; +} + +void PeFile::callCompressWithFilters(Filter &ft, int filter_strategy, unsigned ih_codebase) +{ + compressWithFilters(&ft, 2048, NULL_cconf, filter_strategy, + ih_codebase, rvamin, 0, NULL, 0); +} + +void PeFile::callProcessRelocs(Reloc &rel, unsigned &ic) +{ + // wince wants relocation data at the beginning of a section + PeFile::processRelocs(&rel); + ODADDR(PEDIR_RELOC) = soxrelocs ? ic : 0; + ODSIZE(PEDIR_RELOC) = soxrelocs; + ic += soxrelocs; +} + +void PeFile::callProcessResources(Resource &res, unsigned &ic) +{ + if (soresources) + processResources(&res,ic); + ODADDR(PEDIR_RESOURCE) = soresources ? ic : 0; + ODSIZE(PEDIR_RESOURCE) = soresources; + ic += soresources; +} + +template +void PeFile::pack0(OutputFile *fo, ht &ih, ht &oh, + unsigned subsystem_mask, upx_uint64_t default_imagebase, + bool last_section_rsrc_only) +{ + // FIXME: we need to think about better support for --exact + if (opt->exact) + throwCantPackExact(); + + const unsigned objs = ih.objects; + readSectionHeaders(objs, sizeof(ih)); + if (!opt->force && handleForceOption()) + throwCantPack("unexpected value in PE header (try --force)"); + checkHeaderValues(ih.subsystem, subsystem_mask, ih.entry, ih.filealign); + + //remove certificate directory entry + if (IDSIZE(PEDIR_SEC)) + IDSIZE(PEDIR_SEC) = IDADDR(PEDIR_SEC) = 0; + + ih.flags |= handleStripRelocs(ih.imagebase, default_imagebase, ih.dllflags); + + handleStub(fi,fo,pe_offset); + unsigned overlaystart = readSections(objs, ih.imagesize, ih.filealign, ih.datasize); + unsigned overlay = file_size - stripDebug(overlaystart); + if (overlay >= (unsigned) file_size) + overlay = 0; + checkOverlay(overlay); + + Resource res; + Interval tlsiv(ibuf); + Interval loadconfiv(ibuf); + Export xport((char*)(unsigned char*)ibuf); + + const unsigned dllstrings = processImports(); + processTls(&tlsiv); // call before processRelocs!! + processLoadConf(&loadconfiv); + processResources(&res); + processExports(&xport); + processRelocs(); + + //OutputFile::dump("x1", ibuf, usize); + + // some checks for broken linkers - disable filter if necessary + bool allow_filter = true; + if (/*FIXME ih.codebase == ih.database + ||*/ ih.codebase + ih.codesize > ih.imagesize + || (isection[virta2objnum(ih.codebase,isection,objs)].flags & PEFL_CODE) == 0) + allow_filter = false; + + const unsigned oam1 = ih.objectalign - 1; + + // FIXME: disabled: the uncompressor would not allocate enough memory + //objs = tryremove(IDADDR(PEDIR_RELOC),objs); + + // FIXME: if the last object has a bss then this won't work + // newvsize = (isection[objs-1].vaddr + isection[objs-1].size + oam1) &~ oam1; + // temporary solution: + unsigned newvsize = (isection[objs-1].vaddr + isection[objs-1].vsize + oam1) &~ oam1; + + //fprintf(stderr,"newvsize=%x objs=%d\n",newvsize,objs); + if (newvsize + soimport + sorelocs > ibuf.getSize()) + throwInternalError("buffer too small 2"); + memcpy(ibuf+newvsize,oimport,soimport); + memcpy(ibuf+newvsize+soimport,orelocs,sorelocs); + + cimports = newvsize - rvamin; // rva of preprocessed imports + crelocs = cimports + soimport; // rva of preprocessed fixups + + ph.u_len = newvsize + soimport + sorelocs; + + // some extra data for uncompression support + unsigned s = 0; + upx_byte * const p1 = ibuf + ph.u_len; + memcpy(p1 + s,&ih,sizeof (ih)); + s += sizeof (ih); + memcpy(p1 + s,isection,ih.objects * sizeof(*isection)); + s += ih.objects * sizeof(*isection); + if (soimport) + { + set_le32(p1 + s,cimports); + set_le32(p1 + s + 4,dllstrings); + s += 8; + } + if (sorelocs) + { + set_le32(p1 + s,crelocs); + p1[s + 4] = (unsigned char) (big_relocs & 6); + s += 5; + } + if (soresources) + { + set_le16(p1 + s,icondir_count); + s += 2; + } + // end of extra data + set_le32(p1 + s,ptr_diff(p1,ibuf) - rvamin); + s += 4; + ph.u_len += s; + obuf.allocForCompression(ph.u_len); + + // prepare packheader + ph.u_len -= rvamin; + // prepare filter + Filter ft(ph.level); + ft.buf_len = ih.codesize; + ft.addvalue = ih.codebase - rvamin; + // compress + int filter_strategy = allow_filter ? 0 : -3; + + // disable filters for files with broken headers + if (ih.codebase + ih.codesize > ph.u_len) + { + ft.buf_len = 1; + filter_strategy = -3; + } + + callCompressWithFilters(ft, filter_strategy, ih.codebase); +// info: see buildLoader() + newvsize = (ph.u_len + rvamin + ph.overlap_overhead + oam1) &~ oam1; + if (tlsindex && ((newvsize - ph.c_len - 1024 + oam1) &~ oam1) > tlsindex + 4) + tlsindex = 0; + + int identsize = 0; + const unsigned codesize = getLoaderSection("IDENTSTR",&identsize); + assert(identsize > 0); + unsigned ic; + getLoaderSection("UPX1HEAD",(int*)&ic); + identsize += ic; + + const unsigned oobjs = last_section_rsrc_only ? 4 : 3; + pe_section_t osection[oobjs]; + // section 0 : bss + // 1 : [ident + header] + packed_data + unpacker + tls + loadconf + // 2 : not compressed data + // 3 : resource data -- wince 5 needs a new section for this + + // the last section should start with the resource data, because lots of lame + // windoze codes assume that resources starts on the beginning of a section + + // note: there should be no data in the last section which needs fixup + + // identsplit - number of ident + (upx header) bytes to put into the PE header + int identsplit = pe_offset + sizeof(osection) + sizeof(oh); + if ((identsplit & 0x1ff) == 0) + identsplit = 0; + else if (((identsplit + identsize) ^ identsplit) < 0x200) + identsplit = identsize; + else + identsplit = ALIGN_GAP(identsplit, 0x200); + ic = identsize - identsplit; + + const unsigned c_len = ((ph.c_len + ic) & 15) == 0 ? ph.c_len : ph.c_len + 16 - ((ph.c_len + ic) & 15); + obuf.clear(ph.c_len, c_len - ph.c_len); + + const unsigned s1size = ALIGN_UP(ic + c_len + codesize, (unsigned) sizeof(LEXX)) + sotls + soloadconf; + const unsigned s1addr = (newvsize - (ic + c_len) + oam1) &~ oam1; + + const unsigned ncsection = (s1addr + s1size + oam1) &~ oam1; + const unsigned upxsection = s1addr + ic + c_len; + + Reloc rel(1024); // new relocations are put here + addNewRelocations(rel, upxsection); + + // new PE header + memcpy(&oh,&ih,sizeof(oh)); + oh.filealign = 0x200; // identsplit depends on this + memset(osection,0,sizeof(osection)); + + oh.entry = upxsection; + oh.objects = oobjs; + oh.chksum = 0; + + // fill the data directory + ODADDR(PEDIR_DEBUG) = 0; + ODSIZE(PEDIR_DEBUG) = 0; + ODADDR(PEDIR_IAT) = 0; + ODSIZE(PEDIR_IAT) = 0; + ODADDR(PEDIR_BOUNDIM) = 0; + ODSIZE(PEDIR_BOUNDIM) = 0; + + // tls & loadconf are put into section 1 + ic = s1addr + s1size - sotls - soloadconf; + processTls(&rel,&tlsiv,ic); + ODADDR(PEDIR_TLS) = sotls ? ic : 0; + ODSIZE(PEDIR_TLS) = sotls ? (sizeof(LEXX) == 4 ? 0x18 : 0x28) : 0; + ic += sotls; + + processLoadConf(&rel, &loadconfiv, ic); + ODADDR(PEDIR_LOADCONF) = soloadconf ? ic : 0; + ODSIZE(PEDIR_LOADCONF) = soloadconf; + ic += soloadconf; + + const bool rel_at_sections_start = oobjs == 4; + + ic = ncsection; + if (!last_section_rsrc_only) + callProcessResources(res, ic); + if (rel_at_sections_start) + callProcessRelocs(rel, ic); + + PeFile::processImports(ic, getProcessImportParam(upxsection)); + ODADDR(PEDIR_IMPORT) = ic; + ODSIZE(PEDIR_IMPORT) = soimpdlls; + ic += soimpdlls; + + processExports(&xport,ic); + ODADDR(PEDIR_EXPORT) = soexport ? ic : 0; + ODSIZE(PEDIR_EXPORT) = soexport; + if (!isdll && opt->win32_pe.compress_exports) + { + ODADDR(PEDIR_EXPORT) = IDADDR(PEDIR_EXPORT); + ODSIZE(PEDIR_EXPORT) = IDSIZE(PEDIR_EXPORT); + } + ic += soexport; + + if (!rel_at_sections_start) + callProcessRelocs(rel, ic); + + // when the resource is put alone into section 3 + const unsigned res_start = (ic + oam1) &~ oam1;; + if (last_section_rsrc_only) + callProcessResources(res, ic); + + defineSymbols(ncsection, upxsection, sizeof(oh), + identsize - identsplit, rel, s1addr); + defineFilterSymbols(&ft); + relocateLoader(); + const unsigned lsize = getLoaderSize(); + MemBuffer loader(lsize); + memcpy(loader,getLoader(),lsize); + patchPackHeader(loader, lsize); + + const unsigned ncsize = soxrelocs + soimpdlls + soexport + + (!last_section_rsrc_only ? soresources : 0); + const unsigned fam1 = oh.filealign - 1; + + // this one is tricky: it seems windoze touches 4 bytes after + // the end of the relocation data - so we have to increase + // the virtual size of this section + const unsigned ncsize_virt_increase = (ncsize & oam1) == 0 ? 8 : 0; + + // fill the sections + strcpy(osection[0].name,"UPX0"); + strcpy(osection[1].name,"UPX1"); + // after some windoze debugging I found that the name of the sections + // DOES matter :( .rsrc is used by oleaut32.dll (TYPELIBS) + // and because of this lame dll, the resource stuff must be the + // first in the 3rd section - the author of this dll seems to be + // too idiot to use the data directories... M$ suxx 4 ever! + // ... even worse: exploder.exe in NiceTry also depends on this to + // locate version info + + strcpy(osection[2].name, !last_section_rsrc_only && soresources ? ".rsrc" : "UPX2"); + + osection[0].vaddr = rvamin; + osection[1].vaddr = s1addr; + osection[2].vaddr = ncsection; + + osection[0].size = 0; + osection[1].size = (s1size + fam1) &~ fam1; + osection[2].size = (ncsize + fam1) &~ fam1; + + osection[0].vsize = osection[1].vaddr - osection[0].vaddr; + if (oobjs == 3) + { + osection[1].vsize = (osection[1].size + oam1) &~ oam1; + osection[2].vsize = (osection[2].size + ncsize_virt_increase + oam1) &~ oam1; + oh.imagesize = (osection[3].vaddr + osection[3].vsize + oam1) &~ oam1; + osection[0].rawdataptr = (pe_offset + sizeof(oh) + sizeof(osection) + fam1) &~ fam1; + osection[1].rawdataptr = osection[0].rawdataptr; + osection[2].rawdataptr = osection[1].rawdataptr + osection[1].size; + } + else + { + osection[1].vsize = osection[1].size; + osection[2].vsize = osection[2].size; + oh.imagesize = osection[2].vaddr + osection[2].vsize; + osection[0].rawdataptr = 0; + osection[1].rawdataptr = (pe_offset + sizeof(oh) + sizeof(osection) + fam1) &~ fam1; + } + osection[2].rawdataptr = osection[1].rawdataptr + osection[1].size; + + osection[0].flags = (unsigned) (PEFL_BSS|PEFL_EXEC|PEFL_WRITE|PEFL_READ); + osection[1].flags = (unsigned) (PEFL_DATA|PEFL_EXEC|PEFL_WRITE|PEFL_READ); + osection[2].flags = (unsigned) (PEFL_DATA|PEFL_WRITE|PEFL_READ); + + if (oobjs == 4) + { + strcpy(osection[3].name, ".rsrc"); + osection[3].vaddr = res_start; + osection[3].size = (soresources + fam1) &~ fam1; + osection[3].vsize = osection[3].size; + osection[3].rawdataptr = osection[2].rawdataptr + osection[2].size; + osection[2].flags = (unsigned) (PEFL_DATA|PEFL_READ); + osection[3].flags = (unsigned) (PEFL_DATA|PEFL_READ); + if (soresources == 0) + { + oh.objects = 3; + memset(&osection[3], 0, sizeof(osection[3])); + } + } + + oh.bsssize = osection[0].vsize; + oh.datasize = osection[2].vsize + (oobjs > 3 ? osection[3].vsize : 0); + setOhDataBase(osection); + oh.codesize = osection[1].vsize; + oh.codebase = osection[1].vaddr; + setOhHeaderSize(osection); + if (rvamin < osection[0].rawdataptr) + throwCantPack("object alignment too small"); + + if (opt->win32_pe.strip_relocs && !isdll) + oh.flags |= RELOCS_STRIPPED; + + //for (ic = 0; ic < oh.filealign; ic += 4) + // set_le32(ibuf + ic,get_le32("UPX ")); + ibuf.clear(0, oh.filealign); + + info("Image size change: %u -> %u KiB", + ih.imagesize / 1024, oh.imagesize / 1024); + + infoHeader("[Writing compressed file]"); + + // write loader + compressed file + fo->write(&oh,sizeof(oh)); + fo->write(osection,sizeof(osection)); + // some alignment + if (identsplit == identsize) + { + unsigned n = osection[oobjs == 3 ? 0 : 1].rawdataptr - fo->getBytesWritten() - identsize; + assert(n <= oh.filealign); + fo->write(ibuf, n); + } + fo->write(loader + codesize,identsize); + infoWriting("loader", fo->getBytesWritten()); + fo->write(obuf,c_len); + infoWriting("compressed data", c_len); + fo->write(loader,codesize); + if (opt->debug.dump_stub_loader) + OutputFile::dump(opt->debug.dump_stub_loader, loader, codesize); + if ((ic = fo->getBytesWritten() & (sizeof(LEXX) - 1)) != 0) + fo->write(ibuf, sizeof(LEXX) - ic); + fo->write(otls,sotls); + fo->write(oloadconf, soloadconf); + if ((ic = fo->getBytesWritten() & fam1) != 0) + fo->write(ibuf,oh.filealign - ic); + if (!last_section_rsrc_only) + fo->write(oresources,soresources); + else + fo->write(oxrelocs,soxrelocs); + fo->write(oimpdlls,soimpdlls); + fo->write(oexport,soexport); + fo->write(oxrelocs,soxrelocs); + if (!last_section_rsrc_only) + fo->write(oxrelocs,soxrelocs); + + if ((ic = fo->getBytesWritten() & fam1) != 0) + fo->write(ibuf,oh.filealign - ic); + + if (last_section_rsrc_only) + { + fo->write(oresources,soresources); + if ((ic = fo->getBytesWritten() & fam1) != 0) + fo->write(ibuf,oh.filealign - ic); + } + +#if 0 + printf("%-13s: program hdr : %8ld bytes\n", getName(), (long) sizeof(oh)); + printf("%-13s: sections : %8ld bytes\n", getName(), (long) sizeof(osection)); + printf("%-13s: ident : %8ld bytes\n", getName(), (long) identsize); + printf("%-13s: compressed : %8ld bytes\n", getName(), (long) c_len); + printf("%-13s: decompressor : %8ld bytes\n", getName(), (long) codesize); + printf("%-13s: tls : %8ld bytes\n", getName(), (long) sotls); + printf("%-13s: resources : %8ld bytes\n", getName(), (long) soresources); + printf("%-13s: imports : %8ld bytes\n", getName(), (long) soimpdlls); + printf("%-13s: exports : %8ld bytes\n", getName(), (long) soexport); + printf("%-13s: relocs : %8ld bytes\n", getName(), (long) soxrelocs); + printf("%-13s: loadconf : %8ld bytes\n", getName(), (long) soloadconf); +#endif + + // verify + verifyOverlappingDecompression(); + + // copy the overlay + copyOverlay(fo, overlay, &obuf); + + // finally check the compression ratio + if (!checkFinalCompressionRatio(fo)) + throwNotCompressible(); +} /************************************************************************* // unpack @@ -2454,6 +2976,14 @@ void PeFile32::readPeHeader() isdll = ((ih.flags & DLL_FLAG) != 0); } +void PeFile32::pack0(OutputFile *fo, unsigned subsystem_mask, + upx_uint64_t default_imagebase, + bool last_section_rsrc_only) +{ + super::pack0(fo, ih, oh, subsystem_mask, + default_imagebase, last_section_rsrc_only); +} + void PeFile32::unpack(OutputFile *fo) { bool set_oft = getFormat() == UPX_F_WINCE_ARM_PE; @@ -2503,6 +3033,12 @@ void PeFile64::readPeHeader() isdll = ((ih.flags & DLL_FLAG) != 0); } +void PeFile64::pack0(OutputFile *fo, unsigned subsystem_mask, + upx_uint64_t default_imagebase) +{ + super::pack0(fo, ih, oh, subsystem_mask, default_imagebase, false); +} + void PeFile64::unpack(OutputFile *fo) { super::unpack(fo, ih, oh, 1ULL << 63, false); diff --git a/src/pefile.h b/src/pefile.h index e5096b84..37bf6acb 100644 --- a/src/pefile.h +++ b/src/pefile.h @@ -43,11 +43,39 @@ protected: class Resource; class Export; class ImportLinker; + struct pe_section_t; PeFile(InputFile *f); virtual ~PeFile(); virtual int getVersion() const { return 13; } + void readSectionHeaders(unsigned objs, unsigned sizeof_ih); + unsigned readSections(unsigned objs, unsigned usize, + unsigned ih_filealign, unsigned ih_datasize); + void checkHeaderValues(unsigned subsystem, unsigned mask, + unsigned ih_entry, unsigned ih_filealign); + unsigned handleStripRelocs(upx_uint64_t ih_imagebase, + upx_uint64_t default_imagebase, + unsigned dllflags); + + virtual bool handleForceOption() = 0; + virtual void callCompressWithFilters(Filter &, int filter_strategy, + unsigned ih_codebase); + virtual void defineSymbols(unsigned ncsection, unsigned upxsection, + unsigned sizeof_oh, unsigned isize_isplit, + Reloc &rel, unsigned s1addr) = 0; + virtual void addNewRelocations(Reloc &, unsigned) {} + void callProcessRelocs(Reloc &rel, unsigned &ic); + void callProcessResources(Resource &res, unsigned &ic); + virtual unsigned getProcessImportParam(unsigned) { return 0; } + virtual void setOhDataBase(const pe_section_t *osection) = 0; + virtual void setOhHeaderSize(const pe_section_t *osection) = 0; + + template + void pack0(OutputFile *fo, ht &ih, ht &oh, + unsigned subsystem_mask, upx_uint64_t default_imagebase, + bool last_section_rsrc_only); + template void unpack(OutputFile *fo, const ht &ih, ht &oh, ord_mask_t ord_mask, bool set_oft); @@ -72,6 +100,7 @@ protected: template void rebuildImports(upx_byte *& extrainfo, ord_mask_t ord_mask, bool set_oft); + virtual unsigned processImports() = 0; virtual void processImports(unsigned, unsigned); upx_byte *oimport; unsigned soimport; @@ -112,6 +141,9 @@ protected: template void processTls2(Reloc *rel,const Interval *iv,unsigned newaddr, typename tls_traits::cb_value_t imagebase); // pass 2 + virtual void processTls(Interval *iv) = 0; + virtual void processTls(Reloc *r, const Interval *iv, unsigned a) = 0; + void rebuildTls(); upx_byte *otls; unsigned sotls; @@ -161,6 +193,10 @@ protected: pe_section_t *isection; bool isdll; + bool isrtm; + bool use_dep_hack; + bool use_clear_dirty_stack; + static unsigned virta2objnum (unsigned, pe_section_t *, unsigned); unsigned tryremove (unsigned, unsigned); @@ -377,6 +413,8 @@ class PeFile32 : public PeFile protected: PeFile32(InputFile *f); virtual ~PeFile32(); + void pack0(OutputFile *fo, unsigned subsystem_mask, + upx_uint64_t default_imagebase, bool last_section_rsrc_only); virtual void unpack(OutputFile *fo); virtual int canUnpack(); @@ -435,6 +473,10 @@ class PeFile64 : public PeFile protected: PeFile64(InputFile *f); virtual ~PeFile64(); + + void pack0(OutputFile *fo, unsigned subsystem_mask, + upx_uint64_t default_imagebase); + virtual void unpack(OutputFile *fo); virtual int canUnpack();