diff --git a/src/p_w32pe.cpp b/src/p_w32pe.cpp index f35e21d4..305abcc1 100644 --- a/src/p_w32pe.cpp +++ b/src/p_w32pe.cpp @@ -118,9 +118,7 @@ PackW32Pe::PackW32Pe(InputFile *f) : super(f) PackW32Pe::~PackW32Pe() -{ - delete [] oloadconf; -} +{} const int *PackW32Pe::getCompressionMethods(int method, int level) const @@ -858,20 +856,6 @@ void PackW32Pe::pack(OutputFile *fo) throwNotCompressible(); } -/* - extra info added to help uncompression: - - - - - optional \ - - opt / - - optional \ - - optional / - - optional - -*/ - - /* vi:ts=4:et */ diff --git a/src/p_w64pep.cpp b/src/p_w64pep.cpp index a6202b13..dabebd29 100644 --- a/src/p_w64pep.cpp +++ b/src/p_w64pep.cpp @@ -35,7 +35,7 @@ #include "file.h" #include "filter.h" #include "packer.h" -#include "pepfile.h" +#include "pefile.h" #include "p_w64pep.h" #include "linker.h" @@ -93,6 +93,7 @@ static unsigned my_strlen(const unsigned char *s) #define OPTR_C(type, var, v) type* const var = (v) #endif +#if 0 static void xcheck(const void *p, size_t plen, const void *b, size_t blen) { const char *pp = (const char *) p; @@ -100,7 +101,6 @@ static void xcheck(const void *p, size_t plen, const void *b, size_t blen) if (pp < bb || pp > bb + blen || pp + plen > bb + blen) throwCantUnpack("pointer out of range; take care!"); } -#if 0 static void xcheck(size_t poff, size_t plen, const void *b, size_t blen) { ACC_UNUSED(b); @@ -126,7 +126,6 @@ PackW64Pep::PackW64Pep(InputFile *f) : super(f) isrtm = false; use_dep_hack = true; use_clear_dirty_stack = true; - use_tls_callbacks = false; } @@ -167,671 +166,6 @@ int PackW64Pep::readFileHeader() return super::readFileHeader(); } -/************************************************************************* -// import handling -**************************************************************************/ - -__packed_struct(import_desc) - LE32 oft; // orig first thunk - char _[8]; - LE32 dllname; - LE32 iat; // import address table -__packed_struct_end() - -/* - ImportLinker: 32 and 64 bit import table building. - Import entries (dll name + proc name/ordinal pairs) can be - added in arbitrary order. - - Internally it works by creating sections with special names, - and adding relocation entries between those sections. The special - names ensure that when the import table is built in the memory - from those sections, a correct table can be generated simply by - sorting the sections by name, and adding all of them to the output - in the sorted order. - */ - -class ImportLinker : public ElfLinkerAMD64 -{ - struct tstr : private ::noncopyable - { - char *s; - explicit tstr(char *str) : s(str) {} - ~tstr() { delete [] s; } - operator char *() const { return s; } - }; - - // encoding of dll and proc names are required, so that our special - // control characters in the name of sections can work as intended - static char *encode_name(const char *name, char *buf) - { - char *b = buf; - while (*name) - { - *b++ = 'a' + ((*name >> 4) & 0xf); - *b++ = 'a' + (*name & 0xf); - name++; - } - *b = 0; - return buf; - } - - static char *name_for_dll(const char *dll, char first_char) - { - assert(dll); - unsigned l = strlen(dll); - assert(l > 0); - - char *name = new char[3 * l + 2]; - assert(name); - name[0] = first_char; - char *n = name + 1 + 2 * l; - do { - *n++ = tolower(*dll); - } while(*dll++); - return encode_name(name + 1 + 2 * l, name + 1) - 1; - } - - static char *name_for_proc(const char *dll, const char *proc, - char first_char, char separator) - { - unsigned len = 1 + 2 * strlen(dll) + 1 + 2 * strlen(proc) + 1 + 1; - tstr dlln(name_for_dll(dll, first_char)); - char *procn = new char[len]; - snprintf(procn, len - 1, "%s%c", (const char*) dlln, separator); - encode_name(proc, procn + strlen(procn)); - return procn; - } - - static char zeros[sizeof(import_desc)]; - - enum { - // the order of identifiers is very important below!! - descriptor_id = 'D', - thunk_id, - dll_name_id, - proc_name_id, - ordinal_id, - - thunk_separator_first, - thunk_separator, - thunk_separator_last, - procname_separator, - }; - - unsigned thunk_size; // 4 or 8 bytes - - void add(const char *dll, const char *proc, unsigned ordinal) - { - tstr sdll(name_for_dll(dll, dll_name_id)); - tstr desc_name(name_for_dll(dll, descriptor_id)); - - char tsep = thunk_separator; - if (findSection(sdll, false) == NULL) - { - tsep = thunk_separator_first; - addSection(sdll, dll, strlen(dll) + 1, 0); // name of the dll - addSymbol(sdll, sdll, 0); - - addSection(desc_name, zeros, sizeof(zeros), 0); // descriptor - addRelocation(desc_name, offsetof(import_desc, dllname), - "R_X86_64_32", sdll, 0); - } - tstr thunk(name_for_proc(dll, proc, thunk_id, tsep)); - addSection(thunk, zeros, thunk_size, 0); - addSymbol(thunk, thunk, 0); - if (tsep == thunk_separator_first) - { - addRelocation(desc_name, offsetof(import_desc, iat), - "R_X86_64_32", thunk, 0); - - tstr last_thunk(name_for_proc(dll, "X", thunk_id, thunk_separator_last)); - addSection(last_thunk, zeros, thunk_size, 0); - } - - const char *reltype = thunk_size == 4 ? "R_X86_64_32" : "R_X86_64_64"; - if (ordinal != 0u) - { - addRelocation(thunk, 0, reltype, "*UND*", - ordinal | (1ull << (thunk_size * 8 - 1))); - } - else - { - tstr proc_name(name_for_proc(dll, proc, proc_name_id, procname_separator)); - addSection(proc_name, zeros, 2, 1); // 2 bytes of word aligned "hint" - addSymbol(proc_name, proc_name, 0); - addRelocation(thunk, 0, reltype, proc_name, 0); - - strcat(proc_name, "X"); - addSection(proc_name, proc, strlen(proc), 0); // the name of the symbol - } - } - - static int __acc_cdecl_qsort compare(const void *p1, const void *p2) - { - const Section *s1 = * (const Section * const *) p1; - const Section *s2 = * (const Section * const *) p2; - return strcmp(s1->name, s2->name); - } - - virtual void alignCode(unsigned len) { alignWithByte(len, 0); } - - const Section *getThunk(const char *dll, const char *proc, char tsep) const - { - assert(dll); - assert(proc); - tstr thunk(name_for_proc(dll, proc, thunk_id, tsep)); - return findSection(thunk, false); - } - -public: - explicit ImportLinker(unsigned thunk_size_) : thunk_size(thunk_size_) - { - assert(thunk_size == 4 || thunk_size == 8); - addSection("*UND*", NULL, 0, 0); - addSymbol("*UND*", "*UND*", 0); - addSection("*ZSTART", NULL, 0, 0); - addSymbol("*ZSTART", "*ZSTART", 0); - Section *s = addSection("Dzero", zeros, sizeof(import_desc), 0); - assert(s->name[0] == descriptor_id); - - // one trailing 00 byte after the last proc name - addSection("Zzero", zeros, 1, 0); - } - - template - void add(const C *dll, unsigned ordinal) - { - assert(ordinal > 0 && ordinal < 0x10000); - char ord[20]; - snprintf(ord, sizeof(ord), "%c%05u", ordinal_id, ordinal); - add((const char*) dll, ord, ordinal); - } - - template - void add(const C1 *dll, const C2 *proc) - { - assert(proc); - add((const char*) dll, (const char*) proc, 0); - } - - unsigned build() - { - assert(output == NULL); - int osize = 4 + 2 * nsections; // upper limit for alignments - for (unsigned ic = 0; ic < nsections; ic++) - osize += sections[ic]->size; - output = new upx_byte[osize]; - - // sort the sections by name before adding them all - qsort(sections, nsections, sizeof (Section*), ImportLinker::compare); - - for (unsigned ic = 0; ic < nsections; ic++) - addLoader(sections[ic]->name); - addLoader("+40D"); - assert(outputlen <= osize); - - //OutputFile::dump("il0.imp", output, outputlen); - return outputlen; - } - - void relocate(unsigned myimport) - { - assert(nsections > 0); - assert(output); - defineSymbol("*ZSTART", /*0xffffffffff1000ull + 0 * */ myimport); - ElfLinkerAMD64::relocate(); - //OutputFile::dump("il1.imp", output, outputlen); - } - - template - upx_uint64_t getAddress(const C1 *dll, const C2 *proc) const - { - const Section *s = getThunk((const char*) dll, (const char*) proc, - thunk_separator_first); - if (s == NULL && (s = getThunk((const char*) dll,(const char*) proc, - thunk_separator)) == NULL) - throwInternalError("entry not found"); - return s->offset; - } - - template - upx_uint64_t getAddress(const C1 *dll, unsigned ordinal) const - { - assert(ordinal > 0 && ordinal < 0x10000); - char ord[20]; - snprintf(ord, sizeof(ord), "%c%05u", ordinal_id, ordinal); - - const Section *s = getThunk((const char*) dll, ord, thunk_separator_first); - if (s == NULL - && (s = getThunk((const char*) dll, ord, thunk_separator)) == NULL) - throwInternalError("entry not found"); - return s->offset; - } - - template - upx_uint64_t getAddress(const C1 *dll) const - { - tstr sdll(name_for_dll((const char*) dll, dll_name_id)); - return findSection(sdll, true)->offset; - } -}; -char ImportLinker::zeros[sizeof(import_desc)]; - -ImportLinker ilinker(8); - -void PackW64Pep::processImports(unsigned myimport, unsigned) // pass 2 -{ - COMPILE_TIME_ASSERT(sizeof(import_desc) == 20); - - ilinker.relocate(myimport); - int len; - oimpdlls = ilinker.getLoader(&len); - assert(len == (int) soimpdlls); - //OutputFile::dump("x1.imp", oimpdlls, soimpdlls); -} - -unsigned PackW64Pep::processImports() // pass 1 -{ - static const unsigned char kernel32dll[] = "KERNEL32.DLL"; - - unsigned dllnum = 0; - import_desc *im = (import_desc*) (ibuf + IDADDR(PEDIR_IMPORT)); - import_desc * const im_save = im; - if (IDADDR(PEDIR_IMPORT)) - { - while (im->dllname) - dllnum++, im++; - im = im_save; - } - - struct udll - { - const upx_byte *name; - const upx_byte *shname; - unsigned ordinal; - unsigned iat; - LE64 *lookupt; - unsigned original_position; - bool isk32; - - static int __acc_cdecl_qsort compare(const void *p1, const void *p2) - { - const udll *u1 = * (const udll * const *) p1; - const udll *u2 = * (const udll * const *) p2; - if (u1->isk32) return -1; - if (u2->isk32) return 1; - if (!*u1->lookupt) return 1; - if (!*u2->lookupt) return -1; - int rc = strcasecmp(u1->name,u2->name); - if (rc) return rc; - if (u1->ordinal) return -1; - if (u2->ordinal) return 1; - if (!u1->shname) return 1; - if (!u2->shname) return -1; - return strlen(u1->shname) - strlen(u2->shname); - } - }; - - // +1 for dllnum=0 - Array(struct udll, dlls, dllnum+1); - Array(struct udll *, idlls, dllnum+1); - - soimport = 1024; // safety - - unsigned ic; - for (ic = 0; dllnum && im->dllname; ic++, im++) - { - idlls[ic] = dlls + ic; - dlls[ic].name = ibuf + im->dllname; - dlls[ic].shname = NULL; - dlls[ic].ordinal = 0; - dlls[ic].iat = im->iat; - dlls[ic].lookupt = (LE64*) (ibuf + (im->oft ? im->oft : im->iat)); - dlls[ic].original_position = ic; - dlls[ic].isk32 = strcasecmp(kernel32dll,dlls[ic].name) == 0; - - soimport += strlen(dlls[ic].name) + 1 + 4; - - // FIXME use IPTR_I as in p32pe.cpp - for (LE64 *tarr = dlls[ic].lookupt; *tarr; tarr++) - { - if (*tarr & (1ULL << 63)) - { - importbyordinal = true; - soimport += 2; // ordinal num: 2 bytes - dlls[ic].ordinal = *tarr & 0xffff; - } - else //it's an import by name - { - unsigned len = strlen(ibuf + *tarr + 2); - soimport += len + 1; - if (dlls[ic].shname == NULL || len < strlen (dlls[ic].shname)) - dlls[ic].shname = ibuf + *tarr + 2; - } - soimport++; // separator - } - } - oimport = new upx_byte[soimport]; - memset(oimport,0,soimport); - - qsort(idlls,dllnum,sizeof (udll*),udll::compare); - - info("Processing imports: %d DLLs", dllnum); - - // create the new import table - ilinker.add(kernel32dll, "LoadLibraryA"); - ilinker.add(kernel32dll, "GetProcAddress"); - if (!isdll) - ilinker.add(kernel32dll, "ExitProcess"); - ilinker.add(kernel32dll, "VirtualProtect"); - - for (ic = 0; ic < dllnum; ic++) - { - if (idlls[ic]->isk32) - { - // for kernel32.dll we need to put all the imported - // ordinals into the output import table, as on - // some versions of windows GetProcAddress does not resolve them - if (idlls[ic]->ordinal) - for (LE64 *tarr = idlls[ic]->lookupt; *tarr; tarr++) - if (*tarr & (1ULL << 63)) - { - ilinker.add(kernel32dll, *tarr & 0xffff); - kernel32ordinal = true; - } - } - else - { - if (idlls[ic]->ordinal) - ilinker.add(idlls[ic]->name, idlls[ic]->ordinal); - else if (idlls[ic]->shname) - ilinker.add(idlls[ic]->name, idlls[ic]->shname); - else - throwInternalError("should not happen"); - } - } - - soimpdlls = ilinker.build(); - - Interval names(ibuf),iats(ibuf),lookups(ibuf); - - // create the preprocessed data - upx_byte *ppi = oimport; // preprocessed imports - for (ic = 0; ic < dllnum; ic++) - { - LE64 *tarr = idlls[ic]->lookupt; -#if 0 && ENABLE_THIS_AND_UNCOMPRESSION_WILL_BREAK // FIXME - if (!*tarr) // no imports from this dll - continue; -#endif - set_le32(ppi, ilinker.getAddress(idlls[ic]->name)); - set_le32(ppi+4,idlls[ic]->iat - rvamin); - ppi += 8; - for (; *tarr; tarr++) - if (*tarr & (1ULL << 63)) - { - unsigned ord = *tarr & 0xffff; - if (idlls[ic]->isk32) - { - *ppi++ = 0xfe; // signed + odd parity - set_le32(ppi, ilinker.getAddress(idlls[ic]->name, ord)); - ppi += 4; - } - else - { - *ppi++ = 0xff; - set_le16(ppi, ord); - ppi += 2; - } - } - else - { - *ppi++ = 1; - unsigned len = strlen(ibuf + *tarr + 2) + 1; - memcpy(ppi,ibuf + *tarr + 2,len); - ppi += len; - names.add(*tarr,len + 2 + 1); - } - ppi++; - - unsigned esize = ptr_diff((char *)tarr, (char *)idlls[ic]->lookupt); - lookups.add(idlls[ic]->lookupt,esize); - if (ptr_diff(ibuf + idlls[ic]->iat, (char *)idlls[ic]->lookupt)) - { - memcpy(ibuf + idlls[ic]->iat, idlls[ic]->lookupt, esize); - iats.add(idlls[ic]->iat,esize); - } - names.add(idlls[ic]->name,strlen(idlls[ic]->name) + 1 + 1); - } - ppi += 4; - assert(ppi < oimport+soimport); - soimport = ptr_diff(ppi,oimport); - - if (soimport == 4) - soimport = 0; - - OutputFile::dump("x0.imp", oimport, soimport); - - unsigned ilen = 0; - names.flatten(); - if (names.ivnum > 1) - { - // The area occupied by the dll and imported names is not continuous - // so to still support uncompression, I can't zero the iat area. - // This decreases compression ratio, so FIXME somehow. - infoWarning("can't remove unneeded imports"); - ilen += sizeof(import_desc) * dllnum; -#if defined(DEBUG) - if (opt->verbose > 3) - names.dump(); -#endif - // do some work for the unpacker - im = im_save; - for (ic = 0; ic < dllnum; ic++, im++) - { - memset(im,FILLVAL,sizeof(*im)); - im->dllname = ptr_diff(dlls[idlls[ic]->original_position].name,ibuf); - } - } - else - { - iats.add(im_save,sizeof(import_desc) * dllnum); - // zero unneeded data - iats.clear(); - lookups.clear(); - } - names.clear(); - - iats.add(&names); - iats.add(&lookups); - iats.flatten(); - for (ic = 0; ic < iats.ivnum; ic++) - ilen += iats.ivarr[ic].len; - - info("Imports: original size: %u bytes, preprocessed size: %u bytes",ilen,soimport); - return names.ivnum == 1 ? names.ivarr[0].start : 0; -} - -/************************************************************************* -// TLS handling -**************************************************************************/ - -// thanks for theowl for providing me some docs, so that now I understand -// what I'm doing here :) - -// 1999-10-17: this was tricky to find: -// when the fixup records and the tls area are on the same page, then -// the tls area is not relocated, because the relocation is done by -// the virtual memory manager only for pages which are not yet loaded. -// of course it was impossible to debug this ;-) - -#define TLS_CB_ALIGNMENT 8u // alignment of tls callbacks - -__packed_struct(tls) - LE64 datastart; // VA tls init data start - LE64 dataend; // VA tls init data end - LE64 tlsindex; // VA tls index - LE64 callbacks; // VA tls callbacks - char _[8]; // zero init, characteristics -__packed_struct_end() - -void PackW64Pep::processTls(Interval *iv) // pass 1 -{ - COMPILE_TIME_ASSERT(sizeof(tls) == 40) //size of TLS structure is 40 byte now - COMPILE_TIME_ASSERT_ALIGNED1(tls) - - if ((sotls = ALIGN_UP(IDSIZE(PEDIR_TLS),4)) == 0) - return; - - const tls * const tlsp = (const tls*) (ibuf + IDADDR(PEDIR_TLS)); - // note: TLS callbacks are not implemented in Windows 95/98/ME - if (tlsp->callbacks) - { - if (tlsp->callbacks < ih.imagebase) - throwCantPack("invalid TLS callback"); - else if (tlsp->callbacks - ih.imagebase + 4 >= ih.imagesize) - throwCantPack("invalid TLS callback"); - upx_uint64_t v = get_le64(ibuf + (tlsp->callbacks - ih.imagebase)); - if (v != 0) - { - //count number of callbacks, just for information string - Stefan Widmann - unsigned num_callbacks = 0; - unsigned callback_offset = 0; - while(get_le64(ibuf + (tlsp->callbacks - ih.imagebase) + callback_offset)) - { - //increment number of callbacks - num_callbacks++; - //increment pointer by 8 - callback_offset += 8; - } - info("TLS: %u callback(s) found, adding TLS callback handler", num_callbacks); - //set flag to include necessary sections in loader - use_tls_callbacks = true; - //define linker symbols - tlscb_ptr = tlsp->callbacks; - } - } - - const unsigned tlsdatastart = tlsp->datastart - ih.imagebase; - const unsigned tlsdataend = tlsp->dataend - ih.imagebase; - - // now some ugly stuff: find the relocation entries in the tls data area - unsigned pos,type; - Reloc rel(ibuf + IDADDR(PEDIR_RELOC),IDSIZE(PEDIR_RELOC)); - while (rel.next(pos,type)) - if (pos >= tlsdatastart && pos < tlsdataend) - iv->add(pos,type); - - sotls = sizeof(tls) + tlsdataend - tlsdatastart; - // if TLS callbacks are used, we need two more QWORDS at the end of the TLS - // ... and those qwords should be correctly aligned - if (use_tls_callbacks) - sotls = ALIGN_UP(sotls, TLS_CB_ALIGNMENT) + 16; - - // the PE loader wants this stuff uncompressed - otls = new upx_byte[sotls]; - memset(otls,0,sotls); - memcpy(otls,ibuf + IDADDR(PEDIR_TLS), sizeof(tls)); - // WARNING: this can acces data in BSS - memcpy(otls + sizeof(tls),ibuf + tlsdatastart,sotls - sizeof(tls)); - tlsindex = tlsp->tlsindex - ih.imagebase; - info("TLS: %u bytes tls data and %u relocations added",sotls - (unsigned) sizeof(tls) - (use_tls_callbacks ? 16 : 0),iv->ivnum); - - // makes sure tls index is zero after decompression - if (tlsindex && tlsindex < ih.imagesize) - set_le32(ibuf + tlsindex, 0); -} - -void PackW64Pep::processTls(Reloc *rel,const Interval *iv,unsigned newaddr) // pass 2 -{ - if (sotls == 0) - return; - // add new relocation entries - unsigned ic; - for (ic = 0; ic < (use_tls_callbacks ? 32u : 24u); ic += 8) - rel->add(newaddr + ic,10); - - tls * const tlsp = (tls*) otls; - // now the relocation entries in the tls data area - - - // FIXME check this code below!!! - for (ic = 0; ic < iv->ivnum; ic += 4) - { - void *p = otls + iv->ivarr[ic].start - (tlsp->datastart - ih.imagebase) + sizeof(tls); - upx_uint64_t kc = get_le64(p); //changed to LE64 - Stefan Widmann - if (kc < tlsp->dataend && kc >= tlsp->datastart) - { - kc += newaddr + sizeof(tls) - tlsp->datastart; - set_le64(p,kc + ih.imagebase); //changed to LE64 - Stefan Widmann - rel->add(kc,iv->ivarr[ic].len); - } - else - rel->add(kc - ih.imagebase,iv->ivarr[ic].len); - } - - const unsigned tls_data_size = tlsp->dataend - tlsp->datastart; - tlsp->datastart = newaddr + sizeof(tls) + ih.imagebase; - tlsp->dataend = tlsp->datastart + tls_data_size; - - tlsp->callbacks = (use_tls_callbacks ? newaddr + sotls + ih.imagebase - 16 : 0); - - if (use_tls_callbacks) - { - //set handler offset - set_le64(otls + sotls - 16, tls_handler_offset + ih.imagebase); - //add relocation for TLS handler offset - rel->add(newaddr + sotls - 16, 10); - } -} - - -/************************************************************************* -// Load Configuration handling -**************************************************************************/ - -void PackW64Pep::processLoadConf(Interval *iv) // pass 1 -{ - if (IDSIZE(PEDIR_LOADCONF) == 0) - return; - - const unsigned lcaddr = IDADDR(PEDIR_LOADCONF); - const upx_byte * const loadconf = ibuf + lcaddr; - soloadconf = get_le32(loadconf); - if (soloadconf == 0) - return; - if (soloadconf > 256) - throwCantPack("size of Load Configuration directory unexpected"); - - // if there were relocation entries referring to the load config table - // then we need them for the copy of the table too - unsigned pos,type; - Reloc rel(ibuf + IDADDR(PEDIR_RELOC), IDSIZE(PEDIR_RELOC)); - while (rel.next(pos, type)) - if (pos >= lcaddr && pos < lcaddr + soloadconf) - { - iv->add(pos - lcaddr, type); - // printf("loadconf reloc detected: %x\n", pos); - } - - oloadconf = new upx_byte[soloadconf]; - memcpy(oloadconf, loadconf, soloadconf); -} - -void PackW64Pep::processLoadConf(Reloc *rel, const Interval *iv, - unsigned newaddr) // pass2 -{ - // now we have the address of the new load config table - // so we can create the new relocation entries - for (unsigned ic = 0; ic < iv->ivnum; ic++) - { - rel->add(iv->ivarr[ic].start + newaddr, iv->ivarr[ic].len); - //printf("loadconf reloc added: %x %d\n", - // iv->ivarr[ic].start + newaddr, iv->ivarr[ic].len); - } -} - /************************************************************************* // pack @@ -1288,17 +622,17 @@ void PackW64Pep::pack(OutputFile *fo) 0x2000 : 0x1000); // 2 pages or 1 page linker->defineSymbol("vp_base", addr &~ 0xfff); // page mask linker->defineSymbol("VirtualProtect", myimport + - ilinker.getAddress("kernel32.dll", "VirtualProtect")); + ilinkerGetAddress("kernel32.dll", "VirtualProtect")); } linker->defineSymbol("start_of_relocs", crelocs); if (!isdll) linker->defineSymbol("ExitProcess", myimport + - ilinker.getAddress("kernel32.dll", "ExitProcess")); + ilinkerGetAddress("kernel32.dll", "ExitProcess")); linker->defineSymbol("GetProcAddress", myimport + - ilinker.getAddress("kernel32.dll", "GetProcAddress")); + ilinkerGetAddress("kernel32.dll", "GetProcAddress")); linker->defineSymbol("kernel32_ordinals", myimport); linker->defineSymbol("LoadLibraryA", myimport + - ilinker.getAddress("kernel32.dll", "LoadLibraryA")); + ilinkerGetAddress("kernel32.dll", "LoadLibraryA")); linker->defineSymbol("start_of_imports", myimport); linker->defineSymbol("compressed_imports", cimports); @@ -1384,7 +718,7 @@ void PackW64Pep::pack(OutputFile *fo) ODSIZE(PEDIR_RESOURCE) = soresources; ic += soresources; - processImports(ic, 0); + PeFile::processImports(ic, 0); ODADDR(PEDIR_IMPORT) = ic; ODSIZE(PEDIR_IMPORT) = soimpdlls; ic += soimpdlls; @@ -1399,7 +733,7 @@ void PackW64Pep::pack(OutputFile *fo) } ic += soexport; - processRelocs(&rel); + PeFile::processRelocs(&rel); ODADDR(PEDIR_RELOC) = soxrelocs ? ic : 0; ODSIZE(PEDIR_RELOC) = soxrelocs; ic += soxrelocs; @@ -1524,195 +858,6 @@ void PackW64Pep::pack(OutputFile *fo) throwNotCompressible(); } -/************************************************************************* -// unpack -**************************************************************************/ - -int PackW64Pep::canUnpack() -{ - if (!readFileHeader() || ih.cpu != 0x8664) - return false; - - unsigned objs = ih.objects; - isection = new pe_section_t[objs]; - fi->seek(pe_offset+sizeof(ih),SEEK_SET); - fi->readx(isection,sizeof(pe_section_t)*objs); - if (ih.objects < 3) - return -1; - bool is_packed = (ih.objects == 3 && - (IDSIZE(15) || ih.entry > isection[1].vaddr)); - bool found_ph = false; - if (memcmp(isection[0].name,"UPX",3) == 0) - { - // current version - fi->seek(isection[1].rawdataptr - 64, SEEK_SET); - found_ph = readPackHeader(1024); - if (!found_ph) - { - // old versions - fi->seek(isection[2].rawdataptr, SEEK_SET); - found_ph = readPackHeader(1024); - } - } - if (is_packed && found_ph) - return true; - if (!is_packed && !found_ph) - return -1; - if (is_packed && ih.entry < isection[2].vaddr) - { - unsigned char buf[256]; - bool x = false; - - memset(buf, 0, sizeof(buf)); - try { - fi->seek(ih.entry - isection[1].vaddr + isection[1].rawdataptr, SEEK_SET); - fi->read(buf, sizeof(buf)); - - static const unsigned char magic[] = "\x8b\x1e\x83\xee\xfc\x11\xdb"; - // mov ebx, [esi]; sub esi, -4; adc ebx,ebx - - int offset = find(buf, sizeof(buf), magic, 7); - if (offset >= 0 && find(buf + offset + 1, sizeof(buf) - offset - 1, magic, 7) >= 0) - x = true; - } catch (...) { - //x = true; - } - if (x) - throwCantUnpack("file is modified/hacked/protected; take care!!!"); - else - throwCantUnpack("file is possibly modified/hacked/protected; take care!"); - return false; // not reached - } - - // FIXME: what should we say here ? - //throwCantUnpack("file is possibly modified/hacked/protected; take care!"); - return false; -} - - -void PackW64Pep::rebuildImports(upx_byte *& extrainfo) -{ - if (ODADDR(PEDIR_IMPORT) == 0 - || ODSIZE(PEDIR_IMPORT) <= sizeof(import_desc)) - return; - -// const upx_byte * const idata = obuf + get_le32(extrainfo); - OPTR_C(const upx_byte, idata, obuf + get_le32(extrainfo)); - const unsigned inamespos = get_le32(extrainfo + 4); - extrainfo += 8; - - unsigned sdllnames = 0; - -// const upx_byte *import = ibuf + IDADDR(PEDIR_IMPORT) - isection[2].vaddr; -// const upx_byte *p; - IPTR_I(const upx_byte, import, ibuf + IDADDR(PEDIR_IMPORT) - isection[2].vaddr); - OPTR(const upx_byte, p); - - for (p = idata; get_le32(p) != 0; ++p) - { - const upx_byte *dname = get_le32(p) + import; - ICHECK(dname, 1); - const unsigned dlen = strlen(dname); - ICHECK(dname, dlen + 1); - - sdllnames += dlen + 1; - for (p += 8; *p;) - if (*p == 1) - p += strlen(++p) + 1; - else if (*p == 0xff) - p += 3; // ordinal - else - p += 5; - } - sdllnames = ALIGN_UP(sdllnames, 2u); - - upx_byte * const Obuf = obuf - rvamin; - import_desc * const im0 = (import_desc*) (Obuf + ODADDR(PEDIR_IMPORT)); - import_desc *im = im0; - upx_byte *dllnames = Obuf + inamespos; - upx_byte *importednames = dllnames + sdllnames; - upx_byte * const importednames_start = importednames; - - for (p = idata; get_le32(p) != 0; ++p) - { - // restore the name of the dll - const upx_byte *dname = get_le32(p) + import; - ICHECK(dname, 1); - const unsigned dlen = strlen(dname); - ICHECK(dname, dlen + 1); - - const unsigned iatoffs = get_le32(p + 4) + rvamin; - if (inamespos) - { - // now I rebuild the dll names - OCHECK(dllnames, dlen + 1); - strcpy(dllnames, dname); - im->dllname = ptr_diff(dllnames,Obuf); - //;;;printf("\ndll: %s:",dllnames); - dllnames += dlen + 1; - } - else - { - OCHECK(Obuf + im->dllname, dlen + 1); - strcpy(Obuf + im->dllname, dname); - } - im->iat = iatoffs; - - OPTR_I(LE64, newiat, (LE64 *) (Obuf + iatoffs)); - - // restore the imported names+ordinals - for (p += 8; *p; ++newiat) - if (*p == 1) - { - const unsigned ilen = strlen(++p) + 1; - if (inamespos) - { - if (ptr_diff(importednames, importednames_start) & 1) - importednames -= 1; - omemcpy(importednames + 2, p, ilen); - //;;;printf(" %s",importednames+2); - *newiat = ptr_diff(importednames, Obuf); - importednames += 2 + ilen; - } - else - { - OCHECK(Obuf + (*newiat + 2), ilen + 1); - strcpy(Obuf + (*newiat + 2), p); - } - p += ilen; - } - else if (*p == 0xff) - { - *newiat = get_le16(p + 1) + (1ULL << 63); - //;;;printf(" %llx",(unsigned long long)*newiat); - p += 3; - } - else - { - *newiat = get_le64(get_le32(p + 1) + import); - assert(*newiat & (1ULL << 63)); - p += 5; - } - *newiat = 0; - im++; - } - //memset(idata,0,p - idata); -} - -/* - extra info added to help uncompression: - - - - - optional \ - - opt / - - optional \ - - optional / - - optional - -*/ - - /* vi:ts=4:et */ diff --git a/src/p_w64pep.h b/src/p_w64pep.h index 3ef32b2b..5ed2c3d8 100644 --- a/src/p_w64pep.h +++ b/src/p_w64pep.h @@ -33,9 +33,9 @@ // w64/pep **************************************************************************/ -class PackW64Pep : public PepFile +class PackW64Pep : public PeFile64 { - typedef PepFile super; + typedef PeFile64 super; public: PackW64Pep(InputFile *f); @@ -49,7 +49,6 @@ public: virtual void pack(OutputFile *fo); virtual bool canPack(); - virtual int canUnpack(); protected: virtual int readFileHeader(); @@ -57,25 +56,9 @@ protected: virtual void buildLoader(const Filter *ft); virtual Linker* newLinker() const; - virtual unsigned processImports(); - virtual void processImports(unsigned, unsigned); - virtual void rebuildImports(upx_byte *&); - - virtual void processTls(Interval *); //NEW: TLS callback handling - Stefan Widmann - void processTls(Reloc *, const Interval *, unsigned); //NEW: TLS callback handling - Stefan Widmann - - void processLoadConf(Reloc *, const Interval *, unsigned); - void processLoadConf(Interval *); - upx_byte *oloadconf; - unsigned soloadconf; - - unsigned tlscb_ptr; //NEW: TLS callback handling - Stefan Widmann - unsigned tls_handler_offset; - bool isrtm; bool use_dep_hack; bool use_clear_dirty_stack; - bool use_tls_callbacks; //NEW: TLS callback handling - Stefan Widmann }; diff --git a/src/pefile.cpp b/src/pefile.cpp index f59d6464..bf568df8 100644 --- a/src/pefile.cpp +++ b/src/pefile.cpp @@ -142,21 +142,6 @@ PeFile::PeFile(InputFile *f) : super(f) } -PeFile::~PeFile() -{ - delete [] isection; - delete [] orelocs; - delete [] oimport; - oimpdlls = NULL; - delete [] oexport; - delete [] otls; - delete [] oresources; - delete [] oxrelocs; - delete [] oloadconf; - //delete res; -} - - bool PeFile::testUnpackVersion(int version) const { if (version != ph_version && ph_version != -1) @@ -478,6 +463,107 @@ void PeFile32::processRelocs() // pass1 info("Relocations: original size: %u bytes, preprocessed size: %u bytes",(unsigned) IDSIZE(PEDIR_RELOC),sorelocs); } +void PeFile64::processRelocs() // pass1 +{ + big_relocs = 0; + + Reloc rel(ibuf + IDADDR(PEDIR_RELOC),IDSIZE(PEDIR_RELOC)); + const unsigned *counts = rel.getcounts(); + unsigned rnum = 0; + + unsigned ic; + for (ic = 1; ic < 16; ic++) + rnum += counts[ic]; + + if ((opt->win32_pe.strip_relocs && !isdll) || rnum == 0) + { + if (IDSIZE(PEDIR_RELOC)) + ibuf.fill(IDADDR(PEDIR_RELOC), IDSIZE(PEDIR_RELOC), FILLVAL); + orelocs = new upx_byte [1]; + sorelocs = 0; + return; + } + + for (ic = 15; ic; ic--) + if (ic != 10 && counts[ic]) + infoWarning("skipping unsupported relocation type %d (%d)",ic,counts[ic]); + + LE32 *fix[16]; + for (ic = 15; ic; ic--) + fix[ic] = new LE32 [counts[ic]]; + + unsigned xcounts[16]; + memset(xcounts, 0, sizeof(xcounts)); + + // prepare sorting + unsigned pos,type; + while (rel.next(pos,type)) + { + // FIXME add check for relocations which try to modify the + // PE header or other relocation records + + if (pos >= ih.imagesize) + continue; // skip out-of-bounds record + if (type < 16) + fix[type][xcounts[type]++] = pos - rvamin; + } + + // remove duplicated records + for (ic = 1; ic <= 15; ic++) + { + qsort(fix[ic], xcounts[ic], 4, le32_compare); + unsigned prev = ~0; + unsigned jc = 0; + for (unsigned kc = 0; kc < xcounts[ic]; kc++) + if (fix[ic][kc] != prev) + prev = fix[ic][jc++] = fix[ic][kc]; + + //printf("xcounts[%u] %u->%u\n", ic, xcounts[ic], jc); + xcounts[ic] = jc; + } + + // preprocess "type 10" relocation records + for (ic = 0; ic < xcounts[10]; ic++) + { + pos = fix[10][ic] + rvamin; + set_le64(ibuf + pos, get_le64(ibuf + pos) - ih.imagebase - rvamin); + } + + ibuf.fill(IDADDR(PEDIR_RELOC), IDSIZE(PEDIR_RELOC), FILLVAL); + orelocs = new upx_byte [rnum * 4 + 1024]; // 1024 - safety + sorelocs = ptr_diff(optimizeReloc64((upx_byte*) fix[10], xcounts[10], + orelocs, ibuf + rvamin,1, &big_relocs), + orelocs); + + for (ic = 15; ic; ic--) + delete [] fix[ic]; + +#if 0 + // Malware that hides behind UPX often has PE header info that is + // deliberately corrupt. Sometimes it is even tuned to cause us trouble! + // Use an extra check to avoid AccessViolation (SIGSEGV) when appending + // the relocs into one array. + if ((rnum * 4 + 1024) < (sorelocs + 4*(2 + xcounts[2] + xcounts[1]))) + throwCantUnpack("Invalid relocs"); + + // append relocs type "LOW" then "HIGH" + for (ic = 2; ic ; ic--) + { + memcpy(orelocs + sorelocs,fix[ic],4 * xcounts[ic]); + sorelocs += 4 * xcounts[ic]; + delete [] fix[ic]; + + set_le32(orelocs + sorelocs,0); + if (xcounts[ic]) + { + sorelocs += 4; + big_relocs |= 2 * ic; + } + } +#endif + info("Relocations: original size: %u bytes, preprocessed size: %u bytes",(unsigned) IDSIZE(PEDIR_RELOC),sorelocs); +} + /************************************************************************* // import handling **************************************************************************/ @@ -850,6 +936,7 @@ unsigned PeFile::processImports(ord_mask_t ord_mask) // pass 1 info("Processing imports: %d DLLs", dllnum); + ilinker = new ImportLinker(sizeof(LEXX)); // create the new import table addKernelImports(); @@ -1152,6 +1239,23 @@ struct PeFile::tls_traits static const unsigned reloc_type = 3; }; +template <> +struct PeFile::tls_traits +{ + __packed_struct(tls) + LE64 datastart; // VA tls init data start + LE64 dataend; // VA tls init data end + LE64 tlsindex; // VA tls index + LE64 callbacks; // VA tls callbacks + char _[8]; // zero init, characteristics + __packed_struct_end() + + static const unsigned sotls = 40; + static const unsigned cb_size = 8; + typedef upx_uint64_t cb_value_t; + static const unsigned reloc_type = 10; +}; + template void PeFile::processTls1(Interval *iv, typename tls_traits::cb_value_t imagebase, @@ -2101,8 +2205,8 @@ void PeFile::rebuildImports(upx_byte *& extrainfo, } else { - OCHECK(Obuf + *newiat + 2, ilen + 1); - strcpy(Obuf + *newiat + 2, p); + OCHECK(Obuf + (*newiat + 2), ilen + 1); + strcpy(Obuf + (*newiat + 2), p); } p += ilen; } @@ -2307,6 +2411,26 @@ int PeFile::canUnpack0(unsigned max_sections, LE16 &ih_objects, return false; } +upx_uint64_t PeFile::ilinkerGetAddress(const char *d, const char *n) const +{ + return ilinker->getAddress(d, n); +} + +PeFile::~PeFile() +{ + delete [] isection; + delete [] orelocs; + delete [] oimport; + oimpdlls = NULL; + delete [] oexport; + delete [] otls; + delete [] oresources; + delete [] oxrelocs; + delete [] oloadconf; + delete ilinker; + //delete res; +} + /************************************************************************* // PeFile32 @@ -2319,14 +2443,10 @@ PeFile32::PeFile32(InputFile *f) : super(f) iddirs = ih.ddirs; oddirs = oh.ddirs; - - ilinker = new ImportLinker(4); } PeFile32::~PeFile32() -{ - delete ilinker; -} +{} void PeFile32::readPeHeader() { @@ -2361,6 +2481,53 @@ void PeFile32::processTls(Reloc *r, const Interval *iv, unsigned a) super::processTls2(r, iv, a, ih.imagebase); } +/************************************************************************* +// PeFile64 +**************************************************************************/ + +PeFile64::PeFile64(InputFile *f) : super(f) +{ + COMPILE_TIME_ASSERT(sizeof(pe_header_t) == 264) + COMPILE_TIME_ASSERT_ALIGNED1(pe_header_t) + + iddirs = ih.ddirs; + oddirs = oh.ddirs; +} + +PeFile64::~PeFile64() +{} + +void PeFile64::readPeHeader() +{ + fi->readx(&ih,sizeof(ih)); + isdll = ((ih.flags & DLL_FLAG) != 0); +} + +void PeFile64::unpack(OutputFile *fo) +{ + super::unpack(fo, ih, oh, 1ULL << 63, false); +} + +int PeFile64::canUnpack() +{ + return canUnpack0(3, ih.objects, ih.entry, sizeof(ih)); +} + +unsigned PeFile64::processImports() // pass 1 +{ + return super::processImports(1ULL << 63); +} + +void PeFile64::processTls(Interval *iv) +{ + super::processTls1(iv, ih.imagebase, ih.imagesize); +} + +void PeFile64::processTls(Reloc *r, const Interval *iv, unsigned a) +{ + super::processTls2(r, iv, a, ih.imagebase); +} + /* extra info added to help uncompression: diff --git a/src/pefile.h b/src/pefile.h index d5c3ad12..e5096b84 100644 --- a/src/pefile.h +++ b/src/pefile.h @@ -80,6 +80,7 @@ protected: ImportLinker *ilinker; void addKernelImport(const char *, const char *); virtual void addKernelImports(); + upx_uint64_t ilinkerGetAddress(const char *, const char *) const; virtual void processRelocs() = 0; void processRelocs(Reloc *); @@ -428,6 +429,64 @@ protected: pe_header_t ih, oh; }; +class PeFile64 : public PeFile +{ + typedef PeFile super; +protected: + PeFile64(InputFile *f); + virtual ~PeFile64(); + virtual void unpack(OutputFile *fo); + virtual int canUnpack(); + + virtual void readPeHeader(); + + virtual unsigned processImports(); + virtual void processRelocs(); + virtual void processTls(Interval *); + void processTls(Reloc *, const Interval *, unsigned); + + __packed_struct(pe_header_t) + // 0x0 + char _[4]; // pemagic + LE16 cpu; + LE16 objects; // number of sections + char __[12]; // timestamp + reserved + LE16 opthdrsize; + LE16 flags; // characteristics + // optional header + LE16 coffmagic; // NEW: Stefan Widmann + char ___[2]; // linkerversion + LE32 codesize; + // 0x20 + LE32 datasize; + LE32 bsssize; + LE32 entry; // still a 32 bit RVA + LE32 codebase; + // 0x30 + //LE32 database; // field does not exist in PE+! + // nt specific fields + LE64 imagebase; // LE32 -> LE64 - Stefan Widmann standard is 0x0000000140000000 + LE32 objectalign; + LE32 filealign; // should set to 0x200 ? + // 0x40 + char ____[16]; // versions + // 0x50 + LE32 imagesize; + LE32 headersize; + LE32 chksum; // should set to 0 + LE16 subsystem; + LE16 dllflags; + // 0x60 + char _____[36]; // stack + heap sizes + loader flag + // 0x84 + LE32 ddirsentries; // usually 16 + + ddirs_t ddirs[16]; + __packed_struct_end() + + pe_header_t ih, oh; +}; + #endif /* already included */ diff --git a/src/pepfile.cpp b/src/pepfile.cpp index 94a48ec4..1ca98819 100644 --- a/src/pepfile.cpp +++ b/src/pepfile.cpp @@ -30,7 +30,7 @@ changes in: */ - +#if 0 #include "conf.h" #include "file.h" #include "filter.h" @@ -1731,7 +1731,7 @@ void PepFile::unpack(OutputFile *fo) */ - +#endif /* vi:ts=4:et */ diff --git a/src/pepfile.h b/src/pepfile.h index 769bdf48..2bca0129 100644 --- a/src/pepfile.h +++ b/src/pepfile.h @@ -35,7 +35,7 @@ #ifndef __UPX_PEPFILE_H #define __UPX_PEPFILE_H 1 - +#if 0 /************************************************************************* // general/pe handling **************************************************************************/ @@ -378,6 +378,7 @@ protected: }; +#endif #endif /* already included */