/* lefile.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 */ #include "conf.h" #include "file.h" #include "lefile.h" LeFile::LeFile(InputFile *f) noexcept : fif(f) { COMPILE_TIME_ASSERT(sizeof(le_header_t) == 196) COMPILE_TIME_ASSERT(sizeof(le_object_table_entry_t) == 24) COMPILE_TIME_ASSERT(sizeof(le_pagemap_entry_t) == 4) mem_clear(&ih); mem_clear(&oh); } LeFile::~LeFile() noexcept { delete[] iobject_table; delete[] oobject_table; delete[] ifpage_table; delete[] ofpage_table; delete[] ipm_entries; delete[] opm_entries; delete[] ires_names; delete[] ores_names; delete[] ifixups; delete[] ofixups; delete[] inonres_names; delete[] ononres_names; delete[] ientries; delete[] oentries; } #define objects ih.object_table_entries #define pages ih.memory_pages #define mps ih.memory_page_size void LeFile::readObjectTable() { soobject_table = objects; iobject_table = New(le_object_table_entry_t, soobject_table); fif->seek(le_offset + ih.object_table_offset, SEEK_SET); fif->readx(iobject_table, sizeof(*iobject_table) * objects); } void LeFile::writeObjectTable() { if (fof && oobject_table) fof->write(oobject_table, sizeof(*iobject_table) * soobject_table); } void LeFile::readPageMap() { sopm_entries = pages; ipm_entries = New(le_pagemap_entry_t, sopm_entries); fif->seek(le_offset + ih.object_pagemap_offset, SEEK_SET); fif->readx(ipm_entries, sizeof(*ipm_entries) * pages); for (unsigned ic = 0; ic < pages; ic++) if ((ipm_entries[ic].type & 0xC0) != 0 && (ipm_entries[ic].type & 0xC0) != 0xC0) throwCantPack("unexpected value in page map table"); } void LeFile::writePageMap() { if (fof && opm_entries) fof->write(opm_entries, sizeof(*ipm_entries) * sopm_entries); } void LeFile::readResidentNames() { sores_names = ih.entry_table_offset - ih.resident_names_offset; ires_names = New(byte, sores_names); fif->seek(le_offset + ih.resident_names_offset, SEEK_SET); fif->readx(ires_names, sores_names); } void LeFile::writeResidentNames() { if (fof && ores_names) fof->write(ores_names, sores_names); } void LeFile::readEntryTable() { soentries = ih.fixup_page_table_offset - ih.entry_table_offset; fif->seek(le_offset + ih.entry_table_offset, SEEK_SET); ientries = New(byte, soentries); fif->readx(ientries, soentries); } void LeFile::writeEntryTable() { if (fof && oentries) fof->write(oentries, soentries); } void LeFile::readFixupPageTable() { sofpage_table = 1 + pages; ifpage_table = New(unsigned, sofpage_table); fif->seek(le_offset + ih.fixup_page_table_offset, SEEK_SET); fif->readx(ifpage_table, 4 * sofpage_table); } void LeFile::writeFixupPageTable() { if (fof && ofpage_table) fof->write(ofpage_table, 4 * sofpage_table); } void LeFile::readFixups() { sofixups = get_le32(ifpage_table + pages) - get_le32(ifpage_table); ifixups = New(byte, sofixups); fif->seek(le_offset + ih.fixup_record_table_offset, SEEK_SET); fif->readx(ifixups, sofixups); } void LeFile::writeFixups() { if (fof && ofixups) fof->write(ofixups, sofixups); } unsigned LeFile::getImageSize() const { unsigned n = 0; if (ih.memory_pages > 0) { n = (ih.memory_pages - 1) * ih.memory_page_size; n += ih.bytes_on_last_page; } return n; } void LeFile::readImage() { soimage = pages * mps; if (!soimage) // late detection, but protect against .alloc(0) throwCantPack("no soimage"); mb_iimage.alloc(soimage); mb_iimage.clear(); iimage = mb_iimage; // => now a SPAN_S unsigned ic, jc; for (ic = jc = 0; ic < pages; ic++) { if ((ipm_entries[ic].type & 0xC0) == 0) { fif->seek(ih.data_pages_offset + exe_offset + (ipm_entries[ic].m * 0x100 + ipm_entries[ic].l - 1) * mps, SEEK_SET); auto bytes = ic != pages - 1 ? mps : ih.bytes_on_last_page; fif->readx(iimage + jc, bytes); } jc += mps; } } void LeFile::writeImage() { if (fof && oimage != nullptr) fof->write(oimage, soimage); } void LeFile::readNonResidentNames() { if (ih.non_resident_name_table_length) { sononres_names = ih.non_resident_name_table_length; inonres_names = New(byte, sononres_names); fif->seek(exe_offset + ih.non_resident_name_table_offset, SEEK_SET); fif->readx(inonres_names, sononres_names); } } void LeFile::writeNonResidentNames() { if (fof && ononres_names) fof->write(ononres_names, sononres_names); } bool LeFile::readFileHeader() { #define H(x) get_le16(header + 2 * (x)) byte header[0x40]; le_offset = exe_offset = 0; int ic; for (ic = 0; ic < 20; ic++) { fif->seek(le_offset, SEEK_SET); fif->readx(header, sizeof(header)); if (memcmp(header, "MZ", 2) == 0) // normal dos exe { exe_offset = le_offset; if (H(0x18 / 2) >= 0x40 && memcmp(header + 0x19, "TIPPACH", 7)) // new format exe le_offset += H(0x3c / 2) + H(0x3e / 2) * 65536; else { le_offset += H(2) * 512 + H(1); if (H(1)) le_offset -= 512; else if (H(2) == 0) return false; } } else if (memcmp(header, "BW", 2) == 0) // used in dos4gw.exe le_offset += H(2) * 512 + H(1); else if (memcmp(header, "LE", 2) == 0) break; else if (memcmp(header, "PMW1", 4) == 0) throwCantPack("already packed with PMWLITE"); else return false; } if (ic == 20) return false; fif->seek(le_offset, SEEK_SET); fif->readx(&ih, sizeof(ih)); if (mps < 512 || mps > 2097152 || (mps & (mps - 1)) != 0) throwCantPack("file header invalid page size"); if (ih.bytes_on_last_page > mps || pages == 0) throwCantPack("bad file header"); (void) mem_size(mps, pages); // assert size return true; #undef H } void LeFile::writeFile(OutputFile *f, bool le) { fof = f; memcpy(&oh, &ih, (charptr) &oh.memory_pages - (charptr) &oh); // copy some members of the orig. header oh.memory_page_size = mps; oh.object_table_offset = sizeof(oh); oh.object_table_entries = soobject_table; oh.object_pagemap_offset = oh.object_table_offset + soobject_table * sizeof(*iobject_table); oh.resident_names_offset = oh.object_pagemap_offset + sopm_entries * sizeof(*ipm_entries); oh.entry_table_offset = oh.resident_names_offset + sores_names; oh.fixup_page_table_offset = oh.entry_table_offset + soentries; oh.fixup_record_table_offset = oh.fixup_page_table_offset + sofpage_table * 4; oh.imported_modules_name_table_offset = oh.fixup_record_table_offset + sofixups - FIXUP_EXTRA; oh.imported_procedures_name_table_offset = oh.imported_modules_name_table_offset; oh.data_pages_offset = oh.fixup_record_table_offset + sofixups + (le ? 0 : le_offset - exe_offset); if (ih.non_resident_name_table_length) { oh.non_resident_name_table_offset = oh.data_pages_offset + soimage; oh.non_resident_name_table_length = sononres_names; } oh.fixup_size = sofixups + 4 * sofpage_table; oh.loader_size = oh.fixup_size + oh.fixup_page_table_offset - sizeof(oh); fof->write(&oh, sizeof(oh)); writeObjectTable(); writePageMap(); writeResidentNames(); writeEntryTable(); writeFixupPageTable(); writeFixups(); writeImage(); writeNonResidentNames(); } void LeFile::countFixups(unsigned *counts) const { const unsigned o = objects; memset(counts, 0, mem_size(sizeof(unsigned), o + 2)); // counts[0..objects-1] - # of 32-bit offset relocations in for that objects // counts[objects] - # of selector fixups // counts[objects+1] - # of self-relative fixups const byte *fix = ifixups; const unsigned sfixups = get_le32(ifpage_table + pages); unsigned ll; while (ptr_udiff_bytes(fix, ifixups) < sfixups) { if ((fix[1] & ~0x10) != 0) throwCantPack("unsupported fixup record"); switch (*fix) { case 2: // selector fixup counts[o] += 9; fix += 5; break; case 0x12: // alias selector throwCantPack("16-bit selector alias fixup not yet supported"); case 5: // 16-bit offset fix += (fix[1] & 0x10) ? 9 : 7; break; case 6: // 16:32 pointer counts[o] += 9; // fall through case 7: // 32-bit offset counts[fix[4] - 1] += 4; fix += (fix[1] & 0x10) ? 9 : 7; break; case 0x27: // 32-bit offset list ll = fix[2]; counts[fix[3] - 1] += ll * 4; fix += (fix[1] & 0x10) ? 6 : 4; fix += ll * 2; break; case 8: // 32-bit self relative fixup counts[o + 1] += 4; fix += (fix[1] & 0x10) ? 9 : 7; break; default: throwCantPack("unsupported fixup record"); } } counts[o]++; // extra space for 'ret' counts[o + 1] += 4; // extra space for 0xFFFFFFFF } /* vim:set ts=4 sw=4 et: */