upx/src/p_w64pep.cpp
Markus F.X.J. Oberhumer a26c982e93 Welcome 2014.
2014-01-07 23:05:10 +01:00

1732 lines
59 KiB
C++

/* p_w64pep.cpp --
This file is part of the UPX executable compressor.
Copyright (C) 1996-2014 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1996-2014 Laszlo Molnar
All Rights Reserved.
UPX and the UCL library are free software; you can redistribute them
and/or modify them under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.
If not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Markus F.X.J. Oberhumer Laszlo Molnar
<markus@oberhumer.com> <ml1050@users.sourceforge.net>
-------------------------------------------------------------------
PE+ format extension changes (C) 2010 Stefan Widmann
*/
#include "conf.h"
#include "file.h"
#include "filter.h"
#include "packer.h"
#include "pepfile.h"
#include "p_w64pep.h"
#include "linker.h"
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
/*************************************************************************
//
**************************************************************************/
#if defined(__BORLANDC__)
# undef strcpy
# define strcpy(a,b) strcpy((char *)(a),(const char *)(b))
#endif
#if 0 //NEW: Stefan Widmann
//static
unsigned my_strlen(const char *s)
{
size_t l = strlen((const char*)s); assert((unsigned) l == l); return (unsigned) l;
}
static unsigned my_strlen(const unsigned char *s)
{
size_t l = strlen((const char*)s); assert((unsigned) l == l); return (unsigned) l;
}
#undef strlen
#define strlen my_strlen
#endif
#if (__ACC_CXX_HAVE_PLACEMENT_DELETE) || defined(__DJGPP__)
#include "bptr.h"
#define IPTR(type, var) BoundedPtr<type> var(ibuf, ibuf.getSize())
#define OPTR(type, var) BoundedPtr<type> var(obuf, obuf.getSize())
#define IPTR_I(type, var, v) BoundedPtr<type> var(ibuf, ibuf.getSize(), v)
#define OPTR_I(type, var, v) BoundedPtr<type> var(obuf, obuf.getSize(), v)
#define IPTR_C(type, var, v) const BoundedPtr<type> var(ibuf, ibuf.getSize(), v)
#define OPTR_C(type, var, v) const BoundedPtr<type> var(obuf, obuf.getSize(), v)
#else
#define IPTR(type, var) type* var = 0
#define OPTR(type, var) type* var = 0
#define IPTR_I(type, var, v) type* var = (v)
#define OPTR_I(type, var, v) type* var = (v)
#define IPTR_C(type, var, v) type* const var = (v)
#define OPTR_C(type, var, v) type* const var = (v)
#endif
static void xcheck(const void *p, size_t plen, const void *b, size_t blen)
{
const char *pp = (const char *) p;
const char *bb = (const char *) b;
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);
if (poff > blen || poff + plen > blen)
throwCantUnpack("pointer out of range; take care!");
}
#endif
#define ICHECK(x, size) xcheck(x, size, ibuf, ibuf.getSize())
#define OCHECK(x, size) xcheck(x, size, obuf, obuf.getSize())
#define imemset(a,b,c) ICHECK(a,c), memset(a,b,c)
#define omemset(a,b,c) OCHECK(a,c), memset(a,b,c)
#define imemcpy(a,b,c) ICHECK(a,c), memcpy(a,b,c)
#define omemcpy(a,b,c) OCHECK(a,c), memcpy(a,b,c)
/*************************************************************************
//
**************************************************************************/
PackW64Pep::PackW64Pep(InputFile *f) : super(f)
{
oloadconf = NULL;
soloadconf = 0;
isrtm = false;
use_dep_hack = true;
use_clear_dirty_stack = true;
use_tls_callbacks = false;
}
PackW64Pep::~PackW64Pep()
{
oimpdlls = NULL; // this is now a pointer to ImportLinker output
delete [] oloadconf;
}
const int *PackW64Pep::getCompressionMethods(int method, int level) const
{
bool small = ih.codesize + ih.datasize <= 256*1024;
return Packer::getDefaultCompressionMethods_le32(method, level, small);
}
const int *PackW64Pep::getFilters() const
{
static const int filters[] = { 0x49, FT_END };
return filters;
}
Linker* PackW64Pep::newLinker() const
{
return new ElfLinkerAMD64;
}
/*************************************************************************
// util
**************************************************************************/
int PackW64Pep::readFileHeader()
{
char buf[6];
fi->seek(0x200, SEEK_SET);
fi->readx(buf, 6);
isrtm = 0;
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];
upx_snprintf(procn, len - 1, "%s%c", (const char*) dlln, separator);
encode_name(proc, procn + strlen(procn));
return procn;
}
static const 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 <typename C>
void add(const C *dll, unsigned ordinal)
{
ACC_COMPILE_TIME_ASSERT(sizeof(C) == 1) // "char" or "unsigned char"
assert(ordinal > 0 && ordinal < 0x10000);
char ord[1+5+1];
upx_snprintf(ord, sizeof(ord), "%c%05u", ordinal_id, ordinal);
add((const char*) dll, ord, ordinal);
}
template <typename C1, typename C2>
void add(const C1 *dll, const C2 *proc)
{
ACC_COMPILE_TIME_ASSERT(sizeof(C1) == 1) // "char" or "unsigned char"
ACC_COMPILE_TIME_ASSERT(sizeof(C2) == 1) // "char" or "unsigned char"
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 <typename C1, typename C2>
upx_uint64_t getAddress(const C1 *dll, const C2 *proc) const
{
ACC_COMPILE_TIME_ASSERT(sizeof(C1) == 1) // "char" or "unsigned char"
ACC_COMPILE_TIME_ASSERT(sizeof(C2) == 1) // "char" or "unsigned char"
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 <typename C>
upx_uint64_t getAddress(const C *dll, unsigned ordinal) const
{
ACC_COMPILE_TIME_ASSERT(sizeof(C) == 1) // "char" or "unsigned char"
assert(ordinal > 0 && ordinal < 0x10000);
char ord[1+5+1];
upx_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 <typename C>
upx_uint64_t getAddress(const C *dll) const
{
ACC_COMPILE_TIME_ASSERT(sizeof(C) == 1) // "char" or "unsigned char"
tstr sdll(name_for_dll((const char*) dll, dll_name_id));
return findSection(sdll, true)->offset;
}
};
const char ImportLinker::zeros[sizeof(import_desc)] = { 0 };
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
**************************************************************************/
bool PackW64Pep::canPack()
{
//just check if machine type is 0x8664
if (!readFileHeader() || ih.cpu != 0x8664) // CPU magic of AMD64 is 0x8664
return false;
return true;
}
void PackW64Pep::buildLoader(const Filter *ft)
{
// recompute tlsindex (see pack() below)
unsigned tmp_tlsindex = tlsindex;
const unsigned oam1 = ih.objectalign - 1;
const unsigned newvsize = (ph.u_len + rvamin + ph.overlap_overhead + oam1) &~ oam1;
if (tlsindex && ((newvsize - ph.c_len - 1024 + oam1) &~ oam1) > tlsindex + 4)
tmp_tlsindex = 0;
// prepare loader
initLoader(stub_amd64_win64_pep, sizeof(stub_amd64_win64_pep), 2);
addLoader("START");
if (ih.entry && isdll)
addLoader("PEISDLL0");
addLoader(isdll ? "PEISDLL1" : "",
"PEMAIN01",
icondir_count > 1 ? (icondir_count == 2 ? "PEICONS1" : "PEICONS2") : "",
tmp_tlsindex ? "PETLSHAK" : "",
"PEMAIN02",
//ph.first_offset_found == 1 ? "PEMAIN03" : "",
M_IS_LZMA(ph.method) ? "LZMA_HEAD,LZMA_ELF00,LZMA_DEC20,LZMA_TAIL" :
M_IS_NRV2B(ph.method) ? "NRV_HEAD,NRV2B" :
M_IS_NRV2D(ph.method) ? "NRV_HEAD,NRV2D" :
M_IS_NRV2E(ph.method) ? "NRV_HEAD,NRV2E" : "UNKNOWN_COMPRESSION_METHOD",
//getDecompressorSections(),
/*multipass ? "PEMULTIP" : */ "",
"PEMAIN10",
NULL
);
addLoader(tmp_tlsindex ? "PETLSHAK2" : "");
if (ft->id)
{
const unsigned texv = ih.codebase - rvamin;
assert(ft->calls > 0);
addLoader(texv ? "PECTTPOS" : "PECTTNUL",NULL);
addLoader("PEFILTER49");
}
if (soimport)
addLoader("PEIMPORT",
importbyordinal ? "PEIBYORD" : "",
kernel32ordinal ? "PEK32ORD" : "",
importbyordinal ? "PEIMORD1" : "",
"PEIMPOR2",
isdll ? "PEIERDLL" : "PEIEREXE",
"PEIMDONE",
NULL
);
if (sorelocs)
{
addLoader(soimport == 0 || soimport + cimports != crelocs ? "PERELOC1" : "PERELOC2",
"PERELOC3",
big_relocs ? "REL64BIG" : "",
"RELOC64J",
NULL
);
if (0)
{
addLoader(big_relocs&6 ? "PERLOHI0" : "",
big_relocs&4 ? "PERELLO0" : "",
big_relocs&2 ? "PERELHI0" : "",
NULL
);
}
}
if (use_dep_hack)
addLoader("PEDEPHAK", NULL);
//NEW: TLS callback support PART 1, the callback handler installation - Stefan Widmann
if(use_tls_callbacks)
addLoader("PETLSC", NULL);
addLoader("PEMAIN20", NULL);
if (use_clear_dirty_stack)
addLoader("CLEARSTACK", NULL);
addLoader("PEMAIN21", NULL);
if (ih.entry && isdll)
addLoader("PEISDLL9");
addLoader(ih.entry ? "PEDOJUMP" : "PERETURN", NULL);
//NEW: TLS callback support PART 2, the callback handler - Stefan Widmann
if(use_tls_callbacks)
addLoader("PETLSC2", NULL);
addLoader("IDENTSTR,UPX1HEAD", NULL);
}
void PackW64Pep::pack(OutputFile *fo)
{
// FIXME: we need to think about better support for --exact
if (opt->exact)
throwCantPackExact();
const 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);
rvamin = isection[0].vaddr;
infoHeader("[Processing %s, format %s, %d sections]", fn_basename(fi->getName()), getName(), objs);
// check the PE header
// FIXME: add more checks
// subsystem check moved to switch ... case below
if (!opt->force && (
(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)
))
throwCantPack("unexpected value in PE header (try --force)");
switch(ih.subsystem) //let's take a closer look at the subsystem
{
case 1: //NATIVE
{
throwCantPack("win64/native applications are not yet supported");
break;
}
case 2: //GUI
{
//fine, continue
break;
}
case 3: //CONSOLE
{
//fine, continue
break;
}
case 5: //OS2 - are there any x64/os2 files?
{
throwCantPack("win64/os2 files are not yet supported");
break;
}
case 7: //POSIX - do win64/posix files exist?
{
throwCantPack("win64/posix files are not yet supported");
break;
}
case 9: //WINCE - same question: do they exist?
{
throwCantPack("PE32+/wince files are not yet supported");
break;
}
case 10: //EFI APPLICATION
{
throwCantPack("PE32+/EFIapplication files are not yet supported");
break;
}
case 11: //EFI BOOT SERVICE DRIVER
{
throwCantPack("PE32+/EFIbootservicedriver files are not yet supported");
break;
}
case 12: //EFI RUNTIME DRIVER
{
throwCantPack("PE32+/EFIruntimedriver files are not yet supported");
break;
}
case 13: //EFI ROM
{
throwCantPack("PE32+/EFIROM files are not yet supported");
break;
}
case 14: //XBOX - will there ever be PE32+ xbox files?
{
throwCantPack("PE32+/xbox files are not yet supported");
break;
}
case 16: //WINDOWS BOOT APPLICATION
{
throwCantPack("win64/windowsbootapplication files are not yet supported");
break;
}
default: //UNKNOWN SUBSYSTEM
{
throwCantPack("PE32+/? unknown subsystem");
break;
}
}
//remove certificate pointers from directory table
if (IDSIZE(PEDIR_SEC))
IDSIZE(PEDIR_SEC) = IDADDR(PEDIR_SEC) = 0;
//check if we have a CLR Runtime Header pointer
if (IDSIZE(PEDIR_COMRT))
throwCantPack(".NET files (win64/.net) are not yet supported");
//FIXME: Relocation stripping disabled yet - Stefan Widmann
opt->win32_pe.strip_relocs = false;
#if 0 //removed - Stefan Widmann
if (isdll)
opt->win32_pe.strip_relocs = false;
else if (opt->win32_pe.strip_relocs < 0)
opt->win32_pe.strip_relocs = (ih.imagebase >= 0x0000000140000000ULL);
if (opt->win32_pe.strip_relocs)
{
if (ih.imagebase < 0x0000000140000000ULL)
throwCantPack("--strip-relocs is not allowed when imagebase < 0x0000000140000000");
else
ih.flags |= RELOCS_STRIPPED;
}
#endif
//check if first section's name is "UPX"
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 0 //subsystem check moved to switch ... case above - Stefan Widmann
if (!opt->force && ih.subsystem == 1)
throwCantPack("subsystem 'native' is not supported (try --force)");
#endif
if (ih.filealign < 0x200)
throwCantPack("filealign < 0x200 is not yet supported");
handleStub(fi,fo,pe_offset);
const unsigned usize = ih.imagesize;
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;
}
unsigned overlay = file_size - stripDebug(overlaystart);
if (overlay >= (unsigned) file_size)
{
#if 0
if (overlay < file_size + ih.filealign)
overlay = 0;
else if (!opt->force)
throwNotCompressible("overlay problem (try --force)");
#endif
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 (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;
}
compressWithFilters(&ft, 2048, NULL_cconf, filter_strategy,
ih.codebase, rvamin, 0, NULL, 0);
// 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);
getLoaderSection("UPX1HEAD",(int*)&ic);
identsize += ic;
pe_section_t osection[3];
// section 0 : bss
// 1 : [ident + header] + packed_data + unpacker + tls + loadconf
// 2 : not compressed data
// section 2 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 section 2 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,8u) + 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;
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 +
ilinker.getAddress("kernel32.dll", "VirtualProtect"));
}
linker->defineSymbol("start_of_relocs", crelocs);
if (!isdll)
linker->defineSymbol("ExitProcess", myimport +
ilinker.getAddress("kernel32.dll", "ExitProcess"));
linker->defineSymbol("GetProcAddress", myimport +
ilinker.getAddress("kernel32.dll", "GetProcAddress"));
linker->defineSymbol("kernel32_ordinals", myimport);
linker->defineSymbol("LoadLibraryA", myimport +
ilinker.getAddress("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);
}
defineFilterSymbols(&ft);
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);
//NEW: TLS callback support - Stefan Widmann
ic = s1addr + s1size - sotls - soloadconf; //moved here, we need the address of the new TLS!
if (use_tls_callbacks)
{
linker->defineSymbol("tls_callbacks_ptr", tlscb_ptr - ih.imagebase);
linker->defineSymbol("tls_module_base", 0u - rvamin);
}
linker->defineSymbol("START", upxsection);
//linker->dumpSymbols();
relocateLoader();
const unsigned lsize = getLoaderSize();
MemBuffer loader(lsize);
memcpy(loader,getLoader(),lsize);
patchPackHeader(loader, lsize);
Reloc rel(1024); // new relocations are put here
// 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 = 3;
oh.chksum = 0;
// fill the data directory
ODADDR(PEDIR_DEBUG) = 0; //remove debug data
ODSIZE(PEDIR_DEBUG) = 0;
ODADDR(PEDIR_IAT) = 0; //remove IAT pointer
ODSIZE(PEDIR_IAT) = 0;
ODADDR(PEDIR_BOUNDIM) = 0; //remove bound import table
ODSIZE(PEDIR_BOUNDIM) = 0;
// tls & loadconf are put into section 1
//ic = s1addr + s1size - sotls - soloadconf; //ATTENTION: moved upwards to TLS callback handling - Stefan Widmann
if (use_tls_callbacks)
tls_handler_offset = linker->getSymbolOffset("PETLSC2");
processTls(&rel,&tlsiv,ic);
ODADDR(PEDIR_TLS) = sotls ? ic : 0;
ODSIZE(PEDIR_TLS) = sotls ? 0x28 : 0; // size of TLS is 0x28 in PE32+ - Stefan Widmann
ic += sotls;
processLoadConf(&rel, &loadconfiv, ic);
ODADDR(PEDIR_LOADCONF) = soloadconf ? ic : 0;
ODSIZE(PEDIR_LOADCONF) = soloadconf;
ic += soloadconf;
// these are put into section 2
ic = ncsection;
if (soresources)
processResources(&res,ic);
ODADDR(PEDIR_RESOURCE) = soresources ? ic : 0;
ODSIZE(PEDIR_RESOURCE) = soresources;
ic += soresources;
processImports(ic, 0);
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;
processRelocs(&rel);
ODADDR(PEDIR_RELOC) = soxrelocs ? ic : 0;
ODSIZE(PEDIR_RELOC) = soxrelocs;
ic += soxrelocs;
// this is computed here, because soxrelocs changes some lines above
const unsigned ncsize = soresources + soimpdlls + soexport + soxrelocs;
ic = 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,soresources ? ".rsrc" : "UPX2");
osection[0].vaddr = rvamin;
osection[1].vaddr = s1addr;
osection[2].vaddr = ncsection;
osection[0].size = 0;
osection[1].size = (s1size + ic) &~ ic;
osection[2].size = (ncsize + ic) &~ ic;
osection[0].vsize = osection[1].vaddr - osection[0].vaddr;
osection[1].vsize = (osection[1].size + oam1) &~ oam1;
osection[2].vsize = (osection[2].size + ncsize_virt_increase + oam1) &~ oam1;
osection[0].rawdataptr = (pe_offset + sizeof(oh) + sizeof(osection) + ic) &~ ic;
osection[1].rawdataptr = osection[0].rawdataptr;
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);
oh.imagesize = osection[2].vaddr + osection[2].vsize;
oh.bsssize = osection[0].vsize;
oh.datasize = osection[2].vsize;
//oh.database = osection[2].vaddr; //no base of data in PE32+ files - Stefan Widmann
oh.codesize = osection[1].vsize;
oh.codebase = osection[1].vaddr;
// oh.headersize = osection[0].rawdataptr;
oh.headersize = rvamin;
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[0].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() & 7) != 0)
fo->write(ibuf,8 - ic);
fo->write(otls,sotls);
fo->write(oloadconf, soloadconf);
if ((ic = fo->getBytesWritten() & (oh.filealign-1)) != 0)
fo->write(ibuf,oh.filealign - ic);
fo->write(oresources,soresources);
fo->write(oimpdlls,soimpdlls);
fo->write(oexport,soexport);
fo->write(oxrelocs,soxrelocs);
if ((ic = fo->getBytesWritten() & (oh.filealign-1)) != 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);
if (!checkFinalCompressionRatio(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:
<ih sizeof(pe_head)>
<pe_section_t objs*sizeof(pe_section_t)>
<start of compressed imports 4> - optional \
<start of the names from uncompressed imports> - opt /
<start of compressed relocs 4> - optional \
<relocation type indicator 1> - optional /
<icondir_count 2> - optional
<offset of extra info 4>
*/
/*
vi:ts=4:et
*/