/* p_exe.cpp -- dos/exe executable format This file is part of the UPX executable compressor. Copyright (C) 1996-2024 Markus Franz Xaver Johannes Oberhumer Copyright (C) 1996-2024 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 "p_exe.h" #include "linker.h" static const CLANG_FORMAT_DUMMY_STATEMENT #include "stub/i086-dos16.exe.h" #define MAXMATCH 0x2000 #define MAXRELOCSIZE (0x8000 - MAXMATCH) #define DI_LIMIT 0xff00 // see the assembly why /************************************************************************* // **************************************************************************/ PackExe::PackExe(InputFile *f) : super(f) { bele = &N_BELE_RTP::le_policy; COMPILE_TIME_ASSERT(sizeof(exe_header_t) == 32) COMPILE_TIME_ASSERT_ALIGNED1(exe_header_t) } Linker *PackExe::newLinker() const { return new ElfLinkerX86(); } const int *PackExe::getCompressionMethods(int method, int level) const { bool small = ih_imagesize <= 256 * 1024; // disable lzma for "--brute" unless explicitly given "--lzma" // (note that class PackMaster creates per-file local options) if (opt->all_methods_use_lzma == 1 && !opt->method_lzma_seen) opt->all_methods_use_lzma = 0; return Packer::getDefaultCompressionMethods_8(method, level, small); } const int *PackExe::getFilters() const { return nullptr; } int PackExe::fillExeHeader(struct exe_header_t *eh) const { #define oh (*eh) // fill new exe header int flag = 0; if (!opt->dos_exe.no_reloc && !M_IS_LZMA(ph.method)) flag |= USEJUMP; if (ih.relocs == 0) flag |= NORELOC; mem_clear(&oh); oh.ident = 'M' + 'Z' * 256; oh.headsize16 = 2; unsigned minsp = 0x200; if (M_IS_LZMA(ph.method)) minsp = stack_for_lzma; minsp = ALIGN_UP(minsp, 16u); assert(minsp < 0xff00); if (oh.sp > minsp) minsp = oh.sp; if (minsp < 0xff00 - 2) minsp = ALIGN_UP(minsp, 2u); oh.sp = minsp; unsigned destpara = (ph.u_len + ph.overlap_overhead - ph.c_len + 31) / 16; oh.ss = ph.c_len / 16 + destpara; if (ih.ss * 16 + ih.sp < 0x100000 && ih.ss > oh.ss && ih.sp > 0x200) oh.ss = ih.ss; if (oh.ss * 16 + 0x50 < ih.ss * 16 + ih.sp && oh.ss * 16 + 0x200 > ih.ss * 16 + ih.sp) oh.ss += 0x20; if (oh.ss != ih.ss) flag |= SS; if (oh.sp != ih.sp || M_IS_LZMA(ph.method)) flag |= SP; return flag; #undef oh } void PackExe::addLoaderEpilogue(int flag) { addLoader("EXEMAIN5"); if (relocsize) addLoader(ph.u_len <= DI_LIMIT || (ph.u_len & 0x7fff) >= relocsize ? "EXENOADJ" : "EXEADJUS", "EXERELO1", has_9a ? "EXEREL9A" : "", "EXERELO2", ih_exesize > 0xFE00 ? "EXEREBIG" : "", "EXERELO3"); addLoader("EXEMAIN8", device_driver ? "DEVICEEND" : "", (flag & SS) ? "EXESTACK" : "", (flag & SP) ? "EXESTASP" : "", (flag & USEJUMP) ? "EXEJUMPF" : ""); if (!(flag & USEJUMP)) addLoader(ih.cs ? "EXERCSPO" : "", "EXERETIP"); linker->defineSymbol("original_cs", ih.cs); linker->defineSymbol("original_ip", ih.ip); linker->defineSymbol("original_sp", ih.sp); linker->defineSymbol("original_ss", ih.ss); linker->defineSymbol( "reloc_size", (ph.u_len <= DI_LIMIT || (ph.u_len & 0x7fff) >= relocsize ? 0 : MAXRELOCSIZE) - relocsize); } void PackExe::buildLoader(const Filter *) { // get flag exe_header_t dummy_oh; int flag = fillExeHeader(&dummy_oh); initLoader(stub_i086_dos16_exe, sizeof(stub_i086_dos16_exe)); if (M_IS_LZMA(ph.method)) { addLoader("LZMA_DEC00", opt->small ? "LZMA_DEC10" : "LZMA_DEC20", "LZMA_DEC30", use_clear_dirty_stack ? "LZMA_DEC31" : "", "LZMA_DEC32", ph.u_len > 0xffff ? "LZMA_DEC33" : ""); addLoaderEpilogue(flag); defineDecompressorSymbols(); const unsigned lsize0 = getLoaderSize(); // Lzma decompression code starts at ss:0x10, and its size is // lsize bytes. It also needs getDecompressorWrkmemSize() bytes // during uncompression. It also uses some stack, so 0x100 // more bytes are allocated stack_for_lzma = 0x10 + lsize0 + getDecompressorWrkmemSize() + 0x100; stack_for_lzma = ALIGN_UP(stack_for_lzma, 16u); unsigned clear_dirty_stack_low = 0x10 + lsize0; clear_dirty_stack_low = ALIGN_UP(clear_dirty_stack_low, 2u); if (use_clear_dirty_stack) linker->defineSymbol("clear_dirty_stack_low", clear_dirty_stack_low); relocateLoader(); const unsigned lsize = getLoaderSize(); assert(lsize0 == lsize); MemBuffer loader(lsize); memcpy(loader, getLoader(), lsize); MemBuffer compressed_lzma; compressed_lzma.allocForCompression(lsize); unsigned c_len_lzma = MemBuffer::getSizeForCompression(lsize); int r = upx_compress(loader, lsize, compressed_lzma, &c_len_lzma, nullptr, M_NRV2B_LE16, 9, nullptr, nullptr); assert(r == UPX_E_OK); assert(c_len_lzma < lsize); info("lzma+relocator code compressed: %u -> %u", lsize, c_len_lzma); // reinit the loader initLoader(stub_i086_dos16_exe, sizeof(stub_i086_dos16_exe)); // prepare loader if (device_driver) addLoader("DEVICEENTRY,LZMADEVICE,DEVICEENTRY2"); linker->addSection("COMPRESSED_LZMA", compressed_lzma, c_len_lzma, 0); addLoader("LZMAENTRY,NRV2B160,NRVDDONE,NRVDECO1,NRVGTD00,NRVDECO2"); } else if (device_driver) addLoader("DEVICEENTRY,DEVICEENTRY2"); addLoader("EXEENTRY", M_IS_LZMA(ph.method) && device_driver ? "LONGSUB" : "SHORTSUB", "JNCDOCOPY", relocsize ? "EXERELPU" : "", "EXEMAIN4", M_IS_LZMA(ph.method) ? "" : "EXEMAIN4B", "EXEMAIN4C", M_IS_LZMA(ph.method) ? "COMPRESSED_LZMA_START,COMPRESSED_LZMA" : "", "+G5DXXXX,UPX1HEAD,EXECUTPO"); if (ph.method == M_NRV2B_8) addLoader("NRV2B16S", // decompressor ph.u_len > DI_LIMIT ? "N2B64K01" : "", "NRV2BEX1", opt->cpu_x86 == opt->CPU_8086 ? "N2BX8601" : "N2B28601", "NRV2BEX2", opt->cpu_x86 == opt->CPU_8086 ? "N2BX8602" : "N2B28602", "NRV2BEX3", ph.c_len > 0xffff ? "N2B64K02" : "", "NRV2BEX9"); else if (ph.method == M_NRV2D_8) addLoader("NRV2D16S", ph.u_len > DI_LIMIT ? "N2D64K01" : "", "NRV2DEX1", opt->cpu_x86 == opt->CPU_8086 ? "N2DX8601" : "N2D28601", "NRV2DEX2", opt->cpu_x86 == opt->CPU_8086 ? "N2DX8602" : "N2D28602", "NRV2DEX3", ph.c_len > 0xffff ? "N2D64K02" : "", "NRV2DEX9"); else if (ph.method == M_NRV2E_8) addLoader("NRV2E16S", ph.u_len > DI_LIMIT ? "N2E64K01" : "", "NRV2EEX1", opt->cpu_x86 == opt->CPU_8086 ? "N2EX8601" : "N2E28601", "NRV2EEX2", opt->cpu_x86 == opt->CPU_8086 ? "N2EX8602" : "N2E28602", "NRV2EEX3", ph.c_len > 0xffff ? "N2E64K02" : "", "NRV2EEX9"); else if M_IS_LZMA (ph.method) return; else throwInternalError("unknown compression method"); addLoaderEpilogue(flag); } /************************************************************************* // **************************************************************************/ int PackExe::readFileHeader() { ih_exesize = ih_imagesize = ih_overlay = 0; fi->readx(&ih, sizeof(ih)); if (ih.ident != 'M' + 'Z' * 256 && ih.ident != 'Z' + 'M' * 256) return 0; ih_exesize = ih.m512 + ih.p512 * 512 - (ih.m512 ? 512 : 0); if (ih_exesize == 0) ih_exesize = file_size; ih_imagesize = ih_exesize - ih.headsize16 * 16; ih_overlay = file_size - ih_exesize; if (file_size_u < sizeof(ih) || ((ih.m512 | ih.p512) && ih.m512 + ih.p512 * 512u < sizeof(ih))) throwCantPack("illegal exe header"); if (ih_exesize > file_size_u || ih_imagesize < 4 || ih_imagesize > ih_exesize) throwCantPack("exe header corrupted"); NO_printf("dos/exe header: %d %d %d\n", ih_exesize, ih_imagesize, ih_overlay); return UPX_F_DOS_EXE; } tribool PackExe::canPack() { if (fn_has_ext(fi->getName(), "sys")) // dos/sys return false; if (!readFileHeader()) return false; if (file_size < 1024 || ih_imagesize < 512) throwCantPack("file is too small for dos/exe"); fi->seek(0x3c, SEEK_SET); LE32 offs; fi->readx(&offs, sizeof(offs)); if (ih.relocoffs >= 0x40 && offs) { if (opt->dos_exe.force_stub) opt->overlay = opt->COPY_OVERLAY; else throwCantPack("dos/exe: can't pack new-exe"); } return true; } /************************************************************************* // **************************************************************************/ static unsigned optimize_relocs(SPAN_S(byte) image, const unsigned image_size, SPAN_S(const byte) relocs, const unsigned relocnum, SPAN_S(byte) crel, bool *has_9a) { #if WITH_XSPAN >= 2 ptr_check_no_overlap(image.data(image_size), image_size, relocs.data(), relocs.size_bytes(), crel.data(), crel.size_bytes()); #endif if (opt->exact) throwCantPackExact(); SPAN_S_VAR(byte, const crel_start, crel); unsigned seg_high = 0; #if 0 unsigned seg_low = 0xffffffff; unsigned off_low = 0xffffffff; unsigned off_high = 0; unsigned linear_low = 0xffffffff; unsigned linear_high = 0; #endif // pass 1 - find 0x9a bounds in image for (unsigned i = 0; i < relocnum; i++) { unsigned addr = get_le32(relocs + 4 * i); if (addr >= image_size - 1) throwCantPack("unexpected relocation 1"); if (addr >= 3 && image[addr - 3] == 0x9a) { unsigned seg = get_le16(image + addr); if (seg > seg_high) seg_high = seg; #if 0 if (seg < seg_low) seg_low = seg; unsigned off = get_le16(image + addr - 2); if (off < off_low) off_low = off; if (off > off_high) off_high = off; unsigned l = (seg << 4) + off; if (l < linear_low) linear_low = l; if (l > linear_high) linear_high = l; #endif } } // printf("%d %d\n", seg_low, seg_high); // printf("%d %d\n", off_low, off_high); // printf("%d %d\n", linear_low, linear_high); // pass 2 - reloc crel += 4; // to be filled in later unsigned ones = 0; unsigned es = 0; for (unsigned i = 0; i < relocnum;) { unsigned addr = get_le32(relocs + 4 * i); unsigned di = addr & 0x0f; set_le16(crel + 0, di); set_le16(crel + 2, (addr >> 4) - es); crel += 4; es = addr >> 4; for (++i; i < relocnum; i++) { unsigned t; addr = get_le32(relocs + 4 * i); NO_printf("%x\n", es * 16 + di); if ((addr - es * 16 > 0xfffe) || (i == relocnum - 1 && addr - es * 16 > 0xff00)) { // segment change t = 1 + (0xffff - di) / 254; memset(crel, 1, t); crel += t; ones += t - 1; // -1 is used to help the assembly stuff break; } unsigned offs = addr - es * 16; if (offs >= 3 && image[es * 16 + offs - 3] == 0x9a && offs > di + 3) { for (t = di; t < offs - 3; t++) if (image[es * 16 + t] == 0x9a && get_le16(image + es * 16 + t + 3) <= seg_high) break; if (t == offs - 3) { // code 0: search for 0x9a *crel++ = 0; di = offs; *has_9a = true; continue; } } t = offs - di; if ((int) t < 2) throwCantPack("unexpected relocation 2"); while (t >= 256) { // code 1: add 254, don't reloc *crel++ = 1; t -= 254; ones++; } *crel++ = (byte) t; di = offs; } } *crel++ = 1; ones++; set_le16(crel_start, ones); set_le16(crel_start + 2, seg_high); // OutputFile::dump("x.rel", crel_start, ptr_udiff_bytes(crel, crel_start)); return ptr_udiff_bytes(crel, crel_start); } /************************************************************************* // **************************************************************************/ void PackExe::pack(OutputFile *fo) { unsigned ic; const unsigned relocnum = ih.relocs; if (relocnum > MAXRELOCSIZE) // early check throwCantPack("too many relocations"); checkOverlay(ih_overlay); // read image // image + space for optimized relocs + safety/alignments ibuf.alloc(ih_imagesize + 4 * relocnum + 1024); fi->seek(ih.headsize16 * 16, SEEK_SET); fi->readx(ibuf, ih_imagesize); checkAlreadyPacked(ibuf, UPX_MIN(ih_imagesize, 127u)); device_driver = get_le32(ibuf) == 0xffffffffu; // relocations relocsize = 0; has_9a = false; if (relocnum) { MemBuffer mb_relocs(4 * relocnum); SPAN_S_VAR(byte, relocs, mb_relocs); fi->seek(ih.relocoffs, SEEK_SET); fi->readx(relocs, 4 * relocnum); // dos/exe runs in real-mode, so convert to linear addresses for (ic = 0; ic < relocnum; ic++) { unsigned jc = get_le32(relocs + 4 * ic); set_le32(relocs + 4 * ic, ((jc >> 16) * 16 + (jc & 0xffff)) & 0xfffff); } upx_qsort(raw_bytes(relocs, 4 * relocnum), relocnum, 4, le32_compare); SPAN_S_VAR(byte, image, ibuf + 0, ih_imagesize); SPAN_S_VAR(byte, crel, ibuf + ih_imagesize, ibuf); relocsize = optimize_relocs(image, ih_imagesize, relocs, relocnum, crel, &has_9a); set_le16(crel + relocsize, relocsize + 2); relocsize += 2; assert(relocsize >= 11); if (relocsize > MAXRELOCSIZE) // optimize_relocs did not help throwCantPack("too many relocations"); #if TESTING && 0 unsigned rout_len = MemBuffer::getSizeForCompression(relocsize); MemBuffer rout(rout_len); ucl_nrv2b_99_compress(raw_bytes(crel, relocsize), relocsize, rout, &rout_len, nullptr, 9, nullptr, nullptr); printf("dos/exe reloc compress: %d -> %d\n", relocsize, rout_len); #endif } // prepare packheader ph.u_len = ih_imagesize + relocsize; obuf.allocForCompression(ph.u_len); // prepare filter Filter ft(ph.level); // compress (max_match = 8192) upx_compress_config_t cconf; cconf.reset(); cconf.conf_ucl.max_match = MAXMATCH; cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28 KiB stack compressWithFilters(&ft, 32, &cconf); if (M_IS_NRV2B(ph.method) || M_IS_NRV2D(ph.method) || M_IS_NRV2E(ph.method)) if (ph.max_run_found + ph.max_match_found > 0x8000) throwCantPack("decompressor limit exceeded, send a bugreport"); #if TESTING if (opt->debug.debug_level) { printf("image+relocs %d -> %d\n", ih_imagesize + relocsize, ph.c_len); printf("offsets: %d - %d\nmatches: %d - %d\nruns: %d - %d\n", 0 /*ph.min_offset_found*/, ph.max_offset_found, 0 /*ph.min_match_found*/, ph.max_match_found, 0 /*ph.min_run_found*/, ph.max_run_found); } #endif int flag = fillExeHeader(&oh); const unsigned lsize = getLoaderSize(); MemBuffer loader(lsize); memcpy(loader, getLoader(), lsize); // OutputFile::dump("xxloader.dat", loader, lsize); // patch loader const unsigned packedsize = ph.c_len; const unsigned e_len = getLoaderSectionStart("EXECUTPO"); const unsigned d_len = lsize - e_len; assert((e_len & 15) == 0); const unsigned copysize = (1 + packedsize + d_len) & ~1; const unsigned firstcopy = copysize % 0x10000 ? copysize % 0x10000 : 0x10000; // set oh.min & oh.max ic = ih.min * 16 + ih_imagesize; if (ic < oh.ss * 16u + oh.sp) ic = oh.ss * 16u + oh.sp; oh.min = (ic - (packedsize + lsize)) / 16; ic = oh.min + (ih.max - ih.min); oh.max = ic < 0xffff && ih.max != 0xffff ? ic : 0xffff; // set extra info byte extra_info[9]; unsigned eisize = 0; if (oh.ss != ih.ss) { set_le16(extra_info + eisize, ih.ss); eisize += 2; assert((flag & SS) != 0); // set in fillExeHeader() } if (oh.sp != ih.sp) { set_le16(extra_info + eisize, ih.sp); eisize += 2; assert((flag & SP) != 0); // set in fillExeHeader() } if (ih.min != oh.min) { set_le16(extra_info + eisize, ih.min); eisize += 2; flag |= MINMEM; } if (ih.max != oh.max) { set_le16(extra_info + eisize, ih.max); eisize += 2; flag |= MAXMEM; } extra_info[eisize++] = (byte) flag; if (M_IS_NRV2B(ph.method) || M_IS_NRV2D(ph.method) || M_IS_NRV2E(ph.method)) linker->defineSymbol("bx_magic", 0x7FFF + 0x10 * ((packedsize & 15) + 1)); unsigned decompressor_entry = 1 + (packedsize & 15); if (M_IS_LZMA(ph.method)) decompressor_entry = 0x10; linker->defineSymbol("decompressor_entry", decompressor_entry); // patch loader if (flag & USEJUMP) { // I use a relocation entry to set the original cs unsigned n = getLoaderSectionStart("EXEJUMPF") + 1; n += packedsize + 2; oh.relocs = 1; oh.firstreloc = (n & 0xf) + ((n >> 4) << 16); } else { oh.relocs = 0; oh.firstreloc = ih.cs * 0x10000 + ih.ip; } oh.relocoffs = offsetof(exe_header_t, firstreloc); linker->defineSymbol("destination_segment", oh.ss - ph.c_len / 16 - e_len / 16); linker->defineSymbol("source_segment", e_len / 16 + (copysize - firstcopy) / 16); linker->defineSymbol("copy_offset", firstcopy - 2); linker->defineSymbol("words_to_copy", firstcopy / 2); linker->defineSymbol("exe_stack_sp", oh.sp); linker->defineSymbol("exe_stack_ss", oh.ss); linker->defineSymbol("interrupt", get_le16(ibuf + 8)); linker->defineSymbol("attribute", get_le16(ibuf + 4)); linker->defineSymbol("orig_strategy", get_le16(ibuf + 6)); const unsigned outputlen = sizeof(oh) + e_len + packedsize + d_len + eisize; oh.m512 = outputlen & 511; oh.p512 = (outputlen + 511) >> 9; const char *exeentry = M_IS_LZMA(ph.method) ? "LZMAENTRY" : "EXEENTRY"; oh.ip = device_driver ? getLoaderSection(exeentry) - 2 : 0; defineDecompressorSymbols(); relocateLoader(); memcpy(loader, getLoader(), lsize); patchPackHeader(loader, e_len); NO_fprintf(stderr, "\ne_len=%x d_len=%x c_len=%x oo=%x ulen=%x copysize=%x imagesize=%x", e_len, d_len, packedsize, ph.overlap_overhead, ph.u_len, copysize, ih_imagesize); // write header + write loader + compressed file #if TESTING if (opt->debug.debug_level) printf("\n%d %d %d %d\n", (int) sizeof(oh), e_len, packedsize, d_len); #endif fo->write(&oh, sizeof(oh)); // program header fo->write(loader, e_len); // entry code fo->write(obuf, packedsize); // compressed data fo->write(loader + e_len, d_len); // decompressor code fo->write(extra_info, eisize); // extra info for unpacking assert(eisize <= 9); NO_printf("%-13s: program hdr : %8u bytes\n", getName(), usizeof(oh)); NO_printf("%-13s: entry : %8u bytes\n", getName(), e_len); NO_printf("%-13s: compressed : %8u bytes\n", getName(), packedsize); NO_printf("%-13s: decompressor : %8u bytes\n", getName(), d_len); NO_printf("%-13s: extra info : %8u bytes\n", getName(), eisize); // verify verifyOverlappingDecompression(); // copy the overlay copyOverlay(fo, ih_overlay, obuf); NO_fprintf(stderr, "dos/exe %x %x\n", relocsize, ph.u_len); // finally check the compression ratio if (!checkFinalCompressionRatio(fo)) throwNotCompressible(); } /************************************************************************* // **************************************************************************/ tribool PackExe::canUnpack() { if (!readFileHeader()) return false; const unsigned off = ih.headsize16 * 16; fi->seek(off, SEEK_SET); bool b = readPackHeader(4096); return b && (off + ph.c_len <= file_size_u); } /************************************************************************* // **************************************************************************/ void PackExe::unpack(OutputFile *fo) { ibuf.alloc(file_size); obuf.allocForDecompression(ph.u_len); // read the file fi->seek(ih.headsize16 * 16, SEEK_SET); fi->readx(ibuf, ih_imagesize); // get compressed data offset unsigned e_len = ph.buf_offset + ph.getPackHeaderSize(); if (ih_imagesize <= e_len + ph.c_len) throwCantUnpack("file damaged"); checkOverlay(ih_overlay); // decompress decompress(ibuf + e_len, obuf); unsigned imagesize = ih_imagesize; imagesize -= 1; const byte flag = ibuf[imagesize]; // relocations unsigned relocnum = 0; SPAN_S_VAR(const byte, relocstart, obuf + ph.u_len, obuf); MemBuffer mb_relocs; SPAN_0_VAR(byte, relocs, nullptr); if (!(flag & NORELOC)) { mb_relocs.alloc(4 * MAXRELOCSIZE); relocs = mb_relocs; // => now a SPAN_S relocsize = get_le16(obuf + ph.u_len - 2); ph.u_len -= 2; if (relocsize < 11 || relocsize > MAXRELOCSIZE || relocsize >= imagesize) throwCantUnpack("bad relocations"); relocstart -= relocsize; // unoptimize_relocs unsigned ones = get_le16(relocstart); const unsigned seg_high = get_le16(relocstart + 2); SPAN_S_VAR(const byte, p, relocstart + 4); unsigned es = 0; while (ones) { unsigned di = get_le16(p); es += get_le16(p + 2); bool dorel = true; for (p += 4; ones && di < 0x10000; p++) { if (dorel) { set_le16(relocs + (4 * relocnum + 0), di); set_le16(relocs + (4 * relocnum + 2), es); NO_printf("dos/exe unreloc %4d %6x\n", relocnum, es * 16 + di); relocnum++; } dorel = true; if (*p == 0) { SPAN_S_VAR(const byte, q, obuf + (es * 16 + di), obuf); while (!(*q == 0x9a && get_le16(q + 3) <= seg_high)) q++; di = ptr_udiff_bytes(q, obuf + (es * 16)) + 3; } else if (*p == 1) { di += 254; if (di < 0x10000) ones--; dorel = false; } else di += *p; } } } // fill new exe header mem_clear(&oh); oh.ident = 'M' + 'Z' * 256; if (relocnum) { oh.relocs = relocnum; while (relocnum & 3) // paragraph align set_le32(relocs + (4 * relocnum++), 0); } unsigned outputlen = sizeof(oh) + 4 * relocnum + ptr_udiff_bytes(relocstart, obuf); oh.m512 = outputlen & 511; oh.p512 = (outputlen + 511) >> 9; oh.headsize16 = 2 + relocnum / 4; oh.max = ih.max; oh.min = ih.min; oh.sp = ih.sp; oh.ss = ih.ss; if (flag & MAXMEM) { imagesize -= 2; oh.max = get_le16(ibuf + imagesize); } if (flag & MINMEM) { imagesize -= 2; oh.min = get_le16(ibuf + imagesize); } if (flag & SP) { imagesize -= 2; oh.sp = get_le16(ibuf + imagesize); } if (flag & SS) { imagesize -= 2; oh.ss = get_le16(ibuf + imagesize); } unsigned ip = (flag & USEJUMP) ? get_le32(ibuf + imagesize - 4) : (unsigned) ih.firstreloc; oh.ip = ip & 0xffff; oh.cs = ip >> 16; oh.relocoffs = sizeof(oh); oh.firstreloc = 0; if (!fo) return; // write header + relocations + uncompressed file fo->write(&oh, sizeof(oh)); if (relocnum) fo->write(relocs, 4 * relocnum); fo->write(obuf, ptr_udiff_bytes(relocstart, obuf)); // copy the overlay copyOverlay(fo, ih_overlay, obuf); } /* memory layout at decompression time =================================== normal exe ---------- a, at load time (e - copying code, C - compressed data, d - decompressor+relocator, x - not specified, U - uncompressed code+data, R uncompressed relocation) eeCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCdddd ^ CS:0 ^ SS:0 b, after copying xxxxxxxxxxxxxxxCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCdddd ^ES:DI=0 ^ DS:SI=0 ^ CS=SS, IP in range 0..0xf c, after uncompression UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUURRdddd ^ ES:DI device driver ------------- the file has 2 entry points, CS:0 in device driver mode, and CS:exe_as_device_entry in normal mode. the code in section DEVICEENTRY sets up the same environment for section EXEENTRY, as it would see in normal execution mode. lzma uncompression for normal exes ---------------------------------- (n - nrv2b uncompressor, l - nrv2b compressed lzma + relocator code) a, at load time nneelllCCCCCCCCCCCCCCCCCCCCCCCCC ^ CS:0 ^ SS:0 b, after nrv2b nneelllCCCCCCCCCCCCCCCCCCCCCCCCC dddd ^ CS:0 ^ SS:0x10 after this, normal ee code runs lzma + device driver -------------------- (D - device driver adapter) a, at load time DDnneelllCCCCCCCCCCCCCCCCCCCCCCCCC */ /* vim:set ts=4 sw=4 et: */