1
0
mirror of https://github.com/upx/upx.git synced 2025-08-11 22:52:30 +08:00
Files
upx/src/p_wcle.cpp
Markus F.X.J. Oberhumer dd1d5a92d2 all: welcome 2023
2023-01-01 19:49:30 +01:00

810 lines
27 KiB
C++

/* p_wcle.cpp --
This file is part of the UPX executable compressor.
Copyright (C) 1996-2023 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1996-2023 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> <ezerotven+github@gmail.com>
*/
#include "conf.h"
#include "file.h"
#include "filter.h"
#include "packer.h"
#include "lefile.h"
#include "p_wcle.h"
#include "linker.h"
static const CLANG_FORMAT_DUMMY_STATEMENT
#include "stub/i386-dos32.watcom.le.h"
#define LEOF_READ (1 << 0)
#define LEOF_WRITE (1 << 1)
#define LEOF_EXEC (1 << 2)
#define LEOF_PRELOAD (1 << 6)
#define LEOF_HUGE32 (1 << 13)
#define IOT(x, y) iobject_table[x].y
#define OOT(x, y) oobject_table[x].y
#define LE_STUB_EDI (1)
#ifdef TESTING
#define dputc(x, y) \
do { \
if (opt->debug.debug_level) \
putc(x, y); \
} while (0)
#define Opt_debug opt->debug.debug_level
#else
#define dputc(x, y) ((void) 0)
#define Opt_debug 0
#endif
#define my_base_address reserved
#define objects ih.object_table_entries
#define pages ih.memory_pages
#define mps ih.memory_page_size
#define opages oh.memory_pages
/*************************************************************************
//
**************************************************************************/
const int *PackWcle::getCompressionMethods(int method, int level) const {
return Packer::getDefaultCompressionMethods_le32(method, level);
}
const int *PackWcle::getFilters() const {
static const int filters[] = {0x26, 0x24, 0x49, 0x46, 0x16, 0x13, 0x14,
0x11, FT_ULTRA_BRUTE, 0x25, 0x15, 0x12, FT_END};
return filters;
}
Linker *PackWcle::newLinker() const { return new ElfLinkerX86; }
void PackWcle::buildLoader(const Filter *ft) {
// prepare loader
initLoader(stub_i386_dos32_watcom_le, sizeof(stub_i386_dos32_watcom_le));
addLoader("IDENTSTR,WCLEMAIN", ph.first_offset_found == 1 ? "WCLEMAIN02" : "",
"WCLEMAIN03,UPX1HEAD,WCLECUTP", nullptr);
// fake alignment for the start of the decompressor
linker->defineSymbol("WCLECUTP", 0x1000);
addLoader(getDecompressorSections(), "WCLEMAI2", nullptr);
if (ft->id) {
assert(ft->calls > 0);
addLoader(ft->addvalue ? "WCCTTPOS" : "WCCTTNUL", nullptr);
addFilter32(ft->id);
}
#if 1
// FIXME: if (has_relocation)
{ addLoader("WCRELOC1,RELOC320", big_relocs ? "REL32BIG" : "", "RELOC32J", nullptr); }
#endif
addLoader(has_extra_code ? "WCRELSEL" : "", "WCLEMAI4", nullptr);
}
/*************************************************************************
// util
**************************************************************************/
void PackWcle::handleStub(OutputFile *fo) {
if (fo && !opt->watcom_le.le)
Packer::handleStub(fi, fo, le_offset);
}
bool PackWcle::canPack() {
if (!LeFile::readFileHeader())
return false;
return true;
}
/*************************************************************************
//
**************************************************************************/
// IDEA: as all the entries go into object #1, I could create bundles with 255
// elements (of course I still have to handle empty bundles)
void PackWcle::encodeEntryTable() {
unsigned count, object, n;
upx_byte *p = ientries;
n = 0;
while (*p) {
count = *p;
n += count;
if (p[1] == 0) // unused bundle
p += 2;
else if (p[1] == 3) // 32-bit bundle
{
object = get_le16(p + 2) - 1;
set_le16(p + 2, 1);
p += 4;
for (; count; count--, p += 5)
set_le32(p + 1, IOT(object, my_base_address) + get_le32(p + 1));
} else
throwCantPack("unsupported bundle type in entry table");
}
// if (Opt_debug) printf("%d entries encoded.\n",n);
UNUSED(n);
soentries = ptr_udiff_bytes(p, ientries) + 1;
oentries = ientries;
ientries = nullptr;
}
void PackWcle::readObjectTable() {
LeFile::readObjectTable();
// temporary copy of the object descriptors
iobject_desc.alloc(objects * sizeof(*iobject_table));
memcpy(iobject_desc, iobject_table, objects * sizeof(*iobject_table));
unsigned ic, jc, virtual_size;
for (ic = jc = virtual_size = 0; ic < objects; ic++) {
jc += IOT(ic, npages);
IOT(ic, my_base_address) = virtual_size;
virtual_size += (IOT(ic, virtual_size) + mps - 1) & ~(mps - 1);
}
if (pages != jc)
throwCantPack("bad page number");
}
void PackWcle::encodeObjectTable() {
unsigned ic, jc;
oobject_table = New(le_object_table_entry_t, soobject_table = 2);
memset(oobject_table, 0, soobject_table * sizeof(*oobject_table));
// object #1:
OOT(0, base_address) = IOT(0, base_address);
ic = IOT(objects - 1, my_base_address) + IOT(objects - 1, virtual_size);
jc = pages * mps + sofixups + 1024;
if (ic < jc)
ic = jc;
unsigned csection = (ic + ph.overlap_overhead + mps - 1) & ~(mps - 1);
OOT(0, virtual_size) = csection + mps;
OOT(0, flags) = LEOF_READ | LEOF_EXEC | LEOF_HUGE32 | LEOF_PRELOAD;
OOT(0, pagemap_index) = 1;
OOT(0, npages) = opages;
// object #2: stack
OOT(1, base_address) = (OOT(0, base_address) + OOT(0, virtual_size) + mps - 1) & ~(mps - 1);
OOT(1, virtual_size) = mps + getDecompressorWrkmemSize();
OOT(1, flags) = LEOF_READ | LEOF_HUGE32 | LEOF_WRITE;
OOT(1, pagemap_index) = 1;
oh.init_cs_object = 1;
oh.init_eip_offset = neweip;
oh.init_ss_object = 2;
oh.init_esp_offset = OOT(1, virtual_size);
oh.automatic_data_object = 2;
}
void PackWcle::encodePageMap() {
opm_entries = New(le_pagemap_entry_t, sopm_entries = opages);
for (unsigned ic = 0; ic < sopm_entries; ic++) {
opm_entries[ic].l = (unsigned char) (ic + 1);
opm_entries[ic].m = (unsigned char) ((ic + 1) >> 8);
opm_entries[ic].h = 0;
opm_entries[ic].type = 0;
}
}
void PackWcle::encodeFixupPageTable() {
unsigned ic;
ofpage_table = New(unsigned, sofpage_table = 1 + opages);
for (ofpage_table[0] = ic = 0; ic < opages; ic++)
set_le32(ofpage_table + ic + 1, sofixups - FIXUP_EXTRA);
}
void PackWcle::encodeFixups() {
sofixups = 1 * 7 + FIXUP_EXTRA;
ofixups = New(upx_byte, sofixups);
memset(ofixups, 0, sofixups);
ofixups[0] = 7;
set_le16(ofixups + 2, (LE_STUB_EDI + neweip) & (mps - 1));
ofixups[4] = 1;
}
void PackWcle::preprocessFixups() {
big_relocs = 0;
unsigned ic, jc;
Array(unsigned, counts, objects + 2);
countFixups(counts);
for (ic = jc = 0; ic < objects; ic++)
jc += counts[ic];
if (jc == 0) {
// FIXME: implement this
throwCantPack("files without relocations are not supported");
}
MemBuffer rl_membuf(jc);
ByteArray(srf, counts[objects + 0] + 1);
ByteArray(slf, counts[objects + 1] + 1);
SPAN_S_VAR(upx_byte, rl, rl_membuf);
SPAN_S_VAR(upx_byte, selector_fixups, srf_membuf);
SPAN_S_VAR(upx_byte, selfrel_fixups, slf_membuf);
unsigned rc = 0;
upx_byte *fix = ifixups;
for (ic = jc = 0; ic < pages; ic++) {
while (ptr_udiff_bytes(fix, ifixups) < get_le32(ifpage_table + (ic + 1))) {
const int fixp2 = get_le16_signed(fix + 2);
unsigned value;
switch (*fix) {
case 2: // selector fixup
if (fixp2 < 0) {
// cross page selector fixup
dputc('S', stdout);
fix += 5;
break;
}
dputc('s', stdout);
memcpy(selector_fixups, "\x8C\xCB\x66\x89\x9D",
5); // mov bx, cs ; mov [xxx+ebp], bx
if (IOT(fix[4] - 1, flags) & LEOF_WRITE)
selector_fixups[1] = 0xDB; // ds
set_le32(selector_fixups + 5, jc + fixp2);
selector_fixups += 9;
fix += 5;
break;
case 5: // 16-bit offset
if ((unsigned) fixp2 < 4096 && IOT(fix[4] - 1, my_base_address) == jc)
dputc('6', stdout);
else
throwCantPack("unsupported 16-bit offset relocation");
fix += (fix[1] & 0x10) ? 9 : 7;
break;
case 6: // 16:32 pointer
if (fixp2 < 0) {
// cross page pointer fixup
dputc('P', stdout);
fix += (fix[1] & 0x10) ? 9 : 7;
break;
}
dputc('p', stdout);
memcpy(iimage + jc + fixp2, fix + 5, (fix[1] & 0x10) ? 4 : 2);
set_le32(rl + 4 * rc++, jc + fixp2);
set_le32(iimage + jc + fixp2,
get_le32(iimage + jc + fixp2) + IOT(fix[4] - 1, my_base_address));
memcpy(selector_fixups, "\x8C\xCA\x66\x89\x95", 5);
if (IOT(fix[4] - 1, flags) & LEOF_WRITE)
selector_fixups[1] = 0xDA; // ds
set_le32(selector_fixups + 5, jc + fixp2 + 4);
selector_fixups += 9;
fix += (fix[1] & 0x10) ? 9 : 7;
break;
case 7: // 32-bit offset
if (fixp2 < 0) {
fix += (fix[1] & 0x10) ? 9 : 7;
break;
}
// if (memcmp(iimage+jc+fixp2,fix+5,(fix[1] & 0x10) ? 4 : 2))
// throwCantPack("illegal fixup offset");
// work around a pmwunlite bug: remove duplicated fixups
// FIXME: fix the other cases too
if (rc == 0 || get_le32(rl + 4 * rc - 4) != jc + fixp2) {
set_le32(rl + 4 * rc++, jc + fixp2);
set_le32(iimage + jc + fixp2,
get_le32(iimage + jc + fixp2) + IOT(fix[4] - 1, my_base_address));
}
fix += (fix[1] & 0x10) ? 9 : 7;
break;
case 8: // 32-bit self relative fixup
if (fixp2 < 0) {
// cross page self relative fixup
dputc('R', stdout);
fix += (fix[1] & 0x10) ? 9 : 7;
break;
}
value = get_le32(fix + 5);
if (fix[1] == 0)
value &= 0xffff;
set_le32(iimage + jc + fixp2,
(value + IOT(fix[4] - 1, my_base_address)) - jc - fixp2 - 4);
set_le32(selfrel_fixups, jc + fixp2);
selfrel_fixups += 4;
dputc('r', stdout);
fix += (fix[1] & 0x10) ? 9 : 7;
break;
default:
throwCantPack("unsupported fixup record");
}
}
jc += mps;
}
// resize ifixups if it's too small
if (sofixups < 1000) {
delete[] ifixups;
ifixups = new upx_byte[1000];
}
fix = ifixups + optimizeReloc32(rl, rc, ifixups, iimage, file_size, 1, &big_relocs);
has_extra_code = ptr_udiff_bytes(selector_fixups, srf) != 0;
// FIXME: this could be removed if has_extra_code = false
// but then we'll need a flag
*selector_fixups++ = 0xC3; // ret
memcpy(fix, srf, ptr_udiff_bytes(selector_fixups, srf)); // copy selector fixup code
fix += ptr_udiff_bytes(selector_fixups, srf);
memcpy(fix, slf, ptr_udiff_bytes(selfrel_fixups, slf)); // copy self-relative fixup positions
fix += ptr_udiff_bytes(selfrel_fixups, slf);
set_le32(fix, 0xFFFFFFFFUL);
fix += 4;
sofixups = ptr_udiff_bytes(fix, ifixups);
}
#define RESERVED 0x1000
void PackWcle::encodeImage(Filter *ft) {
// concatenate image & preprocessed fixups
unsigned isize = soimage + sofixups;
ibuf.alloc(isize);
memcpy(ibuf, iimage, soimage);
memcpy(ibuf + soimage, ifixups, sofixups);
delete[] ifixups;
ifixups = nullptr;
mb_oimage.allocForCompression(isize, RESERVED + 512);
oimage = mb_oimage;
// prepare packheader
ph.u_len = isize;
// prepare filter [already done]
// compress
upx_compress_config_t cconf;
cconf.reset();
cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28 KiB stack
compressWithFilters(ibuf, isize, raw_bytes(oimage + RESERVED, mb_oimage.getSize() - RESERVED),
ibuf + ft->addvalue, ft->buf_len, nullptr, 0, ft, 512, &cconf, 0);
ibuf.dealloc();
soimage = ph.c_len;
while (soimage & 3)
oimage[RESERVED + soimage++] = 0;
}
void PackWcle::pack(OutputFile *fo) {
handleStub(fo);
if (ih.byte_order || ih.word_order || ih.exe_format_level || ih.cpu_type < 2 ||
ih.cpu_type > 5 || ih.target_os != 1 || ih.module_type != 0x200 ||
ih.object_iterate_data_map_offset || ih.resource_entries || ih.module_directives_entries ||
ih.imported_modules_count || ih.object_table_entries > 255)
throwCantPack("watcom/le: unexpected value in header");
readObjectTable();
readPageMap();
readResidentNames();
readEntryTable();
readFixupPageTable();
readFixups();
readImage();
readNonResidentNames();
// if (find_le32(iimage,20,get_le32("UPX ")) >= 0)
if (find_le32(raw_bytes(iimage, soimage), UPX_MIN(soimage, 256u), UPX_MAGIC_LE32) >= 0)
throwAlreadyPacked();
if (ih.init_ss_object != objects)
throwCantPack("the stack is not in the last object");
preprocessFixups();
const unsigned text_size = IOT(ih.init_cs_object - 1, npages) * mps;
const unsigned text_vaddr = IOT(ih.init_cs_object - 1, my_base_address);
// attach some useful data at the end of preprocessed fixups
ifixups[sofixups++] = (unsigned char) (ih.automatic_data_object & 0xff);
unsigned ic = objects * sizeof(*iobject_table);
memcpy(ifixups + sofixups, iobject_desc, ic);
iobject_desc.dealloc();
sofixups += ic;
set_le32(ifixups + sofixups,
ih.init_esp_offset + IOT(ih.init_ss_object - 1, my_base_address)); // old stack pointer
set_le32(ifixups + sofixups + 4, ih.init_eip_offset + text_vaddr); // real entry point
set_le32(ifixups + sofixups + 8, mps * pages); // virtual address of unpacked relocations
ifixups[sofixups + 12] = (unsigned char) (unsigned) objects;
sofixups += 13;
// prepare filter
Filter ft(ph.level);
ft.buf_len = text_size;
ft.addvalue = text_vaddr;
// compress
encodeImage(&ft);
const unsigned lsize = getLoaderSize();
neweip = getLoaderSection("WCLEMAIN");
int e_len = getLoaderSectionStart("WCLECUTP");
const unsigned d_len = lsize - e_len;
assert(e_len > 0 && e_len < RESERVED);
memmove(oimage + e_len, oimage + RESERVED, soimage);
soimage += lsize;
opages = (soimage + mps - 1) / mps;
oh.bytes_on_last_page = soimage % mps;
encodeObjectTable();
encodeFixups();
encodeFixupPageTable();
encodePageMap();
encodeEntryTable();
encodeResidentNames();
encodeNonResidentNames();
// patch loader
ic = (OOT(0, virtual_size) - d_len) & ~15;
assert(ic > ((ph.u_len + ph.overlap_overhead + 31) & ~15));
linker->defineSymbol("WCLECUTP", ic);
linker->defineSymbol("original_entry", ih.init_eip_offset + text_vaddr);
linker->defineSymbol("original_stack",
ih.init_esp_offset + IOT(ih.init_ss_object - 1, my_base_address));
linker->defineSymbol("start_of_relocs", mps * pages);
defineDecompressorSymbols();
defineFilterSymbols(&ft);
linker->defineSymbol("filter_buffer_start", text_vaddr);
unsigned jpos = (((ph.c_len + 3) & ~3) + d_len + 3) / 4;
linker->defineSymbol("words_to_copy", jpos);
linker->defineSymbol("copy_dest", ((ic + d_len + 3) & ~3) - 4);
linker->defineSymbol("copy_source", e_len + jpos * 4 - 4);
relocateLoader();
MemBuffer loader(lsize);
memcpy(loader, getLoader(), lsize);
patchPackHeader(loader, lsize);
memcpy(oimage, loader, e_len);
memcpy(oimage + soimage - d_len, loader + e_len, d_len);
writeFile(fo, opt->watcom_le.le);
// verify
verifyOverlappingDecompression(mb_oimage + e_len, mb_oimage.getSize() - e_len);
// copy the overlay
const unsigned overlaystart = ih.data_pages_offset + exe_offset + getImageSize();
const unsigned overlay = file_size - overlaystart - ih.non_resident_name_table_length;
checkOverlay(overlay);
copyOverlay(fo, overlay, mb_oimage);
// finally check the compression ratio
if (!checkFinalCompressionRatio(fo))
throwNotCompressible();
}
/*************************************************************************
//
**************************************************************************/
void PackWcle::decodeFixups() {
SPAN_P_VAR(upx_byte, p, oimage + soimage);
// assert(p.raw_size_in_bytes() == mb_oimage.getSize()); // Span sanity check
mb_iimage.dealloc();
iimage = nullptr;
MemBuffer tmpbuf;
unsigned const fixupn = unoptimizeReloc32(p, oimage, tmpbuf, true);
MemBuffer wrkmem(8 * fixupn + 8);
unsigned ic, jc, o, r;
for (ic = 0; ic < fixupn; ic++) {
jc = get_le32(tmpbuf + 4 * ic);
set_le32(wrkmem + ic * 8, jc);
o = soobject_table;
r = get_le32(oimage + jc);
virt2rela(oobject_table, &o, &r);
set_le32(wrkmem + ic * 8 + 4, OOT(o - 1, my_base_address));
set_le32(oimage + jc, r);
}
set_le32(wrkmem + ic * 8, 0xFFFFFFFF); // end of 32-bit offset fixups
tmpbuf.dealloc();
// selector fixups then self-relative fixups
SPAN_P_VAR(const upx_byte, selector_fixups, p);
// Find selfrel_fixups by skipping over selector_fixups.
SPAN_P_VAR(const upx_byte, q, selector_fixups);
// The code is a subroutine that ends in RET (0xC3).
while (*q != 0xC3) {
// Defend against tampered selector_fixups; see PackWcle::preprocessFixups().
// selector_fixups[] is x386 code with 9-byte blocks of 2 instructions each:
// "\x8C\xCB\x66\x89\x9D" // mov bx, cs ; mov [xxx+ebp], bx
// "\x8C\xCA\x66\x89\x95"
// and where byte [+1] also can be '\xDA' or '\xDB'.
if (0x8C != q[0] || 0x66 != q[2] || 0x89 != q[3]) { // Unexpected; tampering?
// Try to recover by looking for the RET.
upx_byte const *q2 = (upx_byte const *) memchr(q, 0xC3, 9);
if (q2) { // Assume recovery
q = q2;
break;
}
}
// Guard against run-away.
static unsigned char const blank[9] = {0};
if (ptr_diff_bytes(oimage + ph.u_len - sizeof(blank), raw_bytes(q, 0)) <
0 // catastrophic worst case
|| !memcmp(blank, q, sizeof(blank)) // no-good early warning
) {
char msg[50];
snprintf(msg, sizeof(msg), "bad selector_fixups %d",
ptr_diff_bytes(q, selector_fixups));
throwCantPack(msg);
}
q += 9;
}
unsigned selectlen = ptr_udiff_bytes(q, selector_fixups) / 9;
SPAN_P_VAR(const upx_byte, selfrel_fixups, q + 1); // Skip the 0xC3
const unsigned fbytes = fixupn * 9 + 1000 + selectlen * 5;
ofixups = New(upx_byte, fbytes);
SPAN_S_VAR(upx_byte, fp, ofixups, fbytes, ofixups);
for (ic = 1, jc = 0; ic <= opages; ic++) {
// self relative fixups
while ((r = get_le32(selfrel_fixups)) / mps == ic - 1) {
fp[0] = 8;
set_le16(fp + 2, r & (mps - 1));
o = 4 + get_le32(oimage + r);
set_le32(oimage + r, 0);
r += o;
o = soobject_table;
virt2rela(oobject_table, &o, &r);
fp[4] = (unsigned char) o;
set_le32(fp + 5, r);
fp[1] = (unsigned char) (r > 0xFFFF ? 0x10 : 0);
fp += fp[1] ? 9 : 7;
selfrel_fixups += 4;
dputc('r', stdout);
}
// selector fixups
while (selectlen && (r = get_le32(selector_fixups + 5)) / mps == ic - 1) {
fp[0] = 2;
fp[1] = 0;
set_le16(fp + 2, r & (mps - 1));
unsigned x = selector_fixups[1] > 0xD0 ? oh.init_ss_object : oh.init_cs_object;
fp[4] = (unsigned char) x;
fp += 5;
selector_fixups += 9;
selectlen--;
dputc('s', stdout);
}
// 32 bit offset fixups
while (get_le32(wrkmem + 4 * jc) < ic * mps) {
if (jc > 1 &&
((get_le32(wrkmem + 4 * (jc - 2)) + 3) & (mps - 1)) < 3) // cross page fixup?
{
r = get_le32(oimage + get_le32(wrkmem + 4 * (jc - 2)));
fp[0] = 7;
fp[1] = (unsigned char) (r > 0xFFFF ? 0x10 : 0);
set_le16(fp + 2, get_le32(wrkmem + 4 * (jc - 2)) | ~3);
set_le32(fp + 5, r);
o = soobject_table;
r = get_le32(wrkmem + 4 * (jc - 1));
virt2rela(oobject_table, &o, &r);
fp[4] = (unsigned char) o;
fp += fp[1] ? 9 : 7;
dputc('0', stdout);
}
o = soobject_table;
r = get_le32(wrkmem + 4 * (jc + 1));
virt2rela(oobject_table, &o, &r);
r = get_le32(oimage + get_le32(wrkmem + 4 * jc));
fp[0] = 7;
fp[1] = (unsigned char) (r > 0xFFFF ? 0x10 : 0);
set_le16(fp + 2, get_le32(wrkmem + 4 * jc) & (mps - 1));
fp[4] = (unsigned char) o;
set_le32(fp + 5, r);
fp += fp[1] ? 9 : 7;
jc += 2;
}
set_le32(ofpage_table + ic, ptr_udiff_bytes(fp, ofixups));
}
for (ic = 0; ic < FIXUP_EXTRA; ic++)
*fp++ = 0;
sofixups = ptr_udiff_bytes(fp, ofixups);
}
void PackWcle::decodeFixupPageTable() {
ofpage_table = New(unsigned, sofpage_table = 1 + opages);
set_le32(ofpage_table, 0);
// the rest of ofpage_table is filled by decodeFixups()
}
void PackWcle::decodeObjectTable() {
soobject_table = oimage[ph.u_len - 1];
oobject_table = New(le_object_table_entry_t, soobject_table);
unsigned jc, ic = soobject_table * sizeof(*oobject_table);
const unsigned extradata = ph.version == 10 ? 17 : 13;
memcpy(oobject_table, oimage + ph.u_len - extradata - ic, ic);
if (ph.version >= 12)
oh.automatic_data_object = oimage[ph.u_len - ic - 14];
for (ic = jc = 0; ic < soobject_table; ic++) {
OOT(ic, my_base_address) = jc;
jc += (OOT(ic, virtual_size) + mps - 1) & ~(mps - 1);
}
// restore original cs:eip & ss:esp
ic = soobject_table;
jc = get_le32(oimage + ph.u_len - (ph.version < 11 ? 13 : 9));
virt2rela(oobject_table, &ic, &jc);
oh.init_cs_object = ic;
oh.init_eip_offset = jc;
ic = soobject_table;
if (ph.version < 10)
jc = ih.init_esp_offset;
else
jc = get_le32(oimage + ph.u_len - (ph.version == 10 ? 17 : 13));
virt2rela(oobject_table, &ic, &jc);
oh.init_ss_object = ic;
oh.init_esp_offset = jc;
}
void PackWcle::decodeImage() {
mb_oimage.allocForDecompression(ph.u_len);
oimage = mb_oimage;
decompress(iimage + ph.buf_offset + ph.getPackHeaderSize(), oimage);
soimage = get_le32(oimage + ph.u_len - 5);
opages = soimage / mps;
oh.memory_page_size = mps;
}
void PackWcle::decodeEntryTable() {
unsigned count, object, n, r;
SPAN_S_VAR(upx_byte, p, ientries, soentries);
n = 0;
while (*p) {
count = *p;
n += count;
if (p[1] == 0) // unused bundle
p += 2;
else if (p[1] == 3) // 32-bit offset bundle
{
object = get_le16(p + 2);
if (object != 1)
throwCantUnpack("corrupted entry found");
object = soobject_table;
r = get_le32(p + 5);
virt2rela(oobject_table, &object, &r);
set_le16(p + 2, object--);
p += 4;
for (; count; count--, p += 5)
set_le32(p + 1, get_le32(p + 1) - OOT(object, my_base_address));
} else
throwCantUnpack("unsupported bundle type in entry table");
}
// if (Opt_debug) printf("\n%d entries decoded.\n",n);
UNUSED(n);
soentries = ptr_udiff_bytes(p, ientries) + 1;
oentries = ientries;
ientries = nullptr;
}
int PackWcle::canUnpack() {
if (!LeFile::readFileHeader())
return false;
fi->seek(exe_offset + ih.data_pages_offset, SEEK_SET);
// FIXME: 1024 could be too large for some files
// int len = 1024;
int len = UPX_MIN(getImageSize(), 256u);
if (len == 0)
return false;
return readPackHeader(len) ? 1 : -1;
}
void PackWcle::virt2rela(const le_object_table_entry_t *entr, unsigned *objn, unsigned *addr) {
for (; *objn > 1; objn[0]--) {
if (entr[*objn - 1].my_base_address > *addr)
continue;
*addr -= entr[*objn - 1].my_base_address;
break;
}
}
/*************************************************************************
//
**************************************************************************/
void PackWcle::unpack(OutputFile *fo) {
handleStub(fo);
readObjectTable();
iobject_desc.dealloc();
readPageMap();
readResidentNames();
readEntryTable();
readFixupPageTable();
readFixups();
readImage();
readNonResidentNames();
decodeImage();
decodeObjectTable();
// unfilter
if (ph.filter) {
const unsigned text_size = OOT(oh.init_cs_object - 1, npages) * mps;
const unsigned text_vaddr = OOT(oh.init_cs_object - 1, my_base_address);
Filter ft(ph.level);
ft.init(ph.filter, text_vaddr);
ft.cto = (unsigned char) ph.filter_cto;
if (ph.version < 11)
ft.cto = (unsigned char) (get_le32(oimage + ph.u_len - 9) >> 24);
ft.unfilter(raw_bytes(oimage + text_vaddr, text_size), text_size);
}
decodeFixupPageTable();
decodeFixups();
decodeEntryTable();
decodePageMap();
decodeResidentNames();
decodeNonResidentNames();
for (unsigned ic = 0; ic < soobject_table; ic++)
OOT(ic, my_base_address) = 0;
while (oimage[soimage - 1] == 0)
soimage--;
oh.bytes_on_last_page = soimage % mps;
// write decompressed file
if (fo)
writeFile(fo, opt->watcom_le.le);
// copy the overlay
const unsigned overlaystart = ih.data_pages_offset + exe_offset + getImageSize();
const unsigned overlay = file_size - overlaystart - ih.non_resident_name_table_length;
checkOverlay(overlay);
copyOverlay(fo, overlay, mb_oimage);
}
/* vim:set ts=4 sw=4 et: */