/* p_wcle.cpp -- This file is part of the UPX executable compressor. Copyright (C) 1996-2025 Markus Franz Xaver Johannes Oberhumer Copyright (C) 1996-2025 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 */ #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"); // fake alignment for the start of the decompressor linker->defineSymbol("WCLECUTP", 0x1000); addLoader(getDecompressorSections(), "WCLEMAI2"); if (ft->id) { assert(ft->calls > 0); addLoader(ft->addvalue ? "WCCTTPOS" : "WCCTTNUL"); addFilter32(ft->id); } #if 1 // FIXME: if (has_relocation) { addLoader("WCRELOC1,RELOC320", big_relocs ? "REL32BIG" : "", "RELOC32J"); } #endif addLoader(has_extra_code ? "WCRELSEL" : "", "WCLEMAI4"); } /************************************************************************* // util **************************************************************************/ void PackWcle::handleStub(OutputFile *fo) { if (fo && !opt->watcom_le.le) Packer::handleStub(fi, fo, le_offset); } tribool 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; 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 if (!objects) // late detection, but protect against .alloc(0) throwCantPack("no objects"); 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; soobject_table = 2; oobject_table = New(le_object_table_entry_t, soobject_table); memset(oobject_table, 0, mem_size(sizeof(*oobject_table), soobject_table)); // object #1: OOT(0, base_address) = IOT(0, base_address); ic = IOT(objects - 1, my_base_address) + IOT(objects - 1, virtual_size); jc = mem_size(mps, pages, 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 = (byte) (ic + 1); opm_entries[ic].m = (byte) ((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(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 mb_relocs(jc); ByteArray(srf, counts[objects + 0] + 1); ByteArray(slf, counts[objects + 1] + 1); SPAN_S_VAR(byte, relocs, mb_relocs); SPAN_S_VAR(byte, selector_fixups, srf_membuf); SPAN_S_VAR(byte, selfrel_fixups, slf_membuf); unsigned relocnum = 0; 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(relocs + 4 * relocnum++, 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 (relocnum == 0 || get_le32(relocs + 4 * relocnum - 4) != jc + fixp2) { set_le32(relocs + 4 * relocnum++, 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 < 4 * relocnum + 8192) { delete[] ifixups; sofixups = 4 * relocnum + 8192; ifixups = New(byte, sofixups); } SPAN_S_VAR(byte, orelocs, ifixups, sofixups); fix = ifixups + optimizeReloc(relocnum, relocs, orelocs, iimage, soimage, 32, true, &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; // => now a SPAN_S // 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 = mem_size(mps, IOT(ih.init_cs_object - 1, npages)); 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++] = (byte) (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, mem_size(mps, pages)); // virtual address of unpacked relocations ifixups[sofixups + 12] = (byte) (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", mem_size(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() { mb_iimage.dealloc(); iimage = nullptr; SPAN_S_VAR(const byte, p, oimage + soimage); MemBuffer mb_relocs; const unsigned fixupn = unoptimizeReloc(p, mb_relocs, oimage, soimage, 32, true); MemBuffer wrkmem(8 * fixupn + 8); unsigned ic, jc, o, r; for (ic = 0; ic < fixupn; ic++) { jc = get_le32(mb_relocs + 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 mb_relocs.dealloc(); // done // selector fixups then self-relative fixups SPAN_S_VAR(const byte, selector_fixups, p); // Find selfrel_fixups by skipping over selector_fixups. SPAN_S_VAR(const 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. const byte *q2 = (const byte *) memchr(q, 0xC3, 9); if (q2) { // Assume recovery q = q2; break; } } // Guard against run-away. static byte const blank[9] = {0}; // catastrophic worst case or no-good early warning if (ptr_diff_bytes(oimage + ph.u_len - sizeof(blank), raw_bytes(q, 0)) < 0 || !memcmp(blank, q, sizeof(blank))) { 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_S_VAR(const byte, selfrel_fixups, q + 1); // Skip the 0xC3 const unsigned fbytes = fixupn * 9 + 1000 + selectlen * 5; ofixups = New(byte, fbytes); SPAN_S_VAR(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] = (byte) o; set_le32(fp + 5, r); fp[1] = (byte) (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] = (byte) x; fp += 5; selector_fixups += 9; selectlen--; dputc('s', stdout); } // 32 bit offset fixups while (get_le32(wrkmem + 4 * jc) < mem_size(mps, ic)) { 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] = (byte) (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] = (byte) 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] = (byte) (r > 0xFFFF ? 0x10 : 0); set_le16(fp + 2, get_le32(wrkmem + 4 * jc) & (mps - 1)); fp[4] = (byte) 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; // => now a SPAN_S 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(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; } tribool 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; } /*static*/ 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 = mem_size(mps, OOT(oh.init_cs_object - 1, npages)); 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 = (byte) ph.filter_cto; if (ph.version < 11) ft.cto = (byte) (get_le32(oimage + ph.u_len - 9) >> 24); ft.unfilter(oimage + text_vaddr, 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: */