mirror of
https://github.com/upx/upx
synced 2025-09-28 19:06:07 +08:00
454 lines
13 KiB
C++
454 lines
13 KiB
C++
/* p_djgpp2.cpp --
|
|
|
|
This file is part of the UPX executable compressor.
|
|
|
|
Copyright (C) 1996-2004 Markus Franz Xaver Johannes Oberhumer
|
|
Copyright (C) 1996-2004 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
|
|
*/
|
|
|
|
|
|
#include "conf.h"
|
|
#include "file.h"
|
|
#include "filter.h"
|
|
#include "packer.h"
|
|
#include "p_djgpp2.h"
|
|
|
|
static const
|
|
#include "stub/l_djgpp2.h"
|
|
static const
|
|
#include "stub/stubify.h"
|
|
|
|
|
|
/*************************************************************************
|
|
//
|
|
**************************************************************************/
|
|
|
|
PackDjgpp2::PackDjgpp2(InputFile *f) :
|
|
super(f), coff_offset(0)
|
|
{
|
|
COMPILE_TIME_ASSERT(sizeof(external_scnhdr_t) == 40);
|
|
COMPILE_TIME_ASSERT(sizeof(coff_header_t) == 0xa8);
|
|
COMPILE_TIME_ASSERT(sizeof(stubify_stub) == 2048);
|
|
COMPILE_TIME_ASSERT_ALIGNOF(external_scnhdr_t, char)
|
|
COMPILE_TIME_ASSERT_ALIGNOF(coff_header_t, char)
|
|
COMPILE_TIME_ASSERT(STUBIFY_STUB_ADLER32 == 0xbf689ba8);
|
|
COMPILE_TIME_ASSERT(STUBIFY_STUB_CRC32 == 0x2ae982b2);
|
|
//printf("0x%08x\n", upx_adler32(stubify_stub, sizeof(stubify_stub)));
|
|
//assert(upx_adler32(stubify_stub, sizeof(stubify_stub)) == STUBIFY_STUB_ADLER32);
|
|
}
|
|
|
|
|
|
const int *PackDjgpp2::getCompressionMethods(int method, int level) const
|
|
{
|
|
return Packer::getDefaultCompressionMethods_le32(method, level);
|
|
}
|
|
|
|
|
|
const int *PackDjgpp2::getFilters() const
|
|
{
|
|
static const int filters[] = {
|
|
0x26, 0x24, 0x11, 0x14, 0x13, 0x16, 0x25, 0x15, 0x12,
|
|
-1 };
|
|
return filters;
|
|
}
|
|
|
|
|
|
unsigned PackDjgpp2::findOverlapOverhead(const upx_bytep buf,
|
|
unsigned range,
|
|
unsigned upper_limit) const
|
|
{
|
|
unsigned o = super::findOverlapOverhead(buf, range, upper_limit);
|
|
o = (o + 0x3ff) &~ 0x1ff;
|
|
return o;
|
|
}
|
|
|
|
|
|
int PackDjgpp2::buildLoader(const Filter *ft)
|
|
{
|
|
// prepare loader
|
|
initLoader(nrv_loader, sizeof(nrv_loader));
|
|
addLoader("IDENTSTR,DJ2MAIN1",
|
|
ft->id ? "DJCALLT1" : "",
|
|
"DJ2MAIN2",
|
|
getDecompressor(),
|
|
"DJ2BSS00",
|
|
NULL
|
|
);
|
|
if (ft->id)
|
|
{
|
|
assert(ft->calls > 0);
|
|
addLoader("DJCALLT2", NULL);
|
|
addFilter32(ft->id);
|
|
}
|
|
addLoader("DJRETURN,+40DXXXX,UPX1HEAD", NULL);
|
|
return getLoaderSize();
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
// util
|
|
**************************************************************************/
|
|
|
|
void PackDjgpp2::handleStub(OutputFile *fo)
|
|
{
|
|
if (fo && !opt->djgpp2_coff.coff)
|
|
{
|
|
if (coff_offset > 0)
|
|
{
|
|
// copy stub from exe
|
|
Packer::handleStub(fi, fo, coff_offset);
|
|
}
|
|
else
|
|
{
|
|
// "stubify" stub
|
|
info("Adding stub: %ld bytes", (long)sizeof(stubify_stub));
|
|
fo->write(stubify_stub, sizeof(stubify_stub));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static bool is_dlm(InputFile *fi, long coff_offset)
|
|
{
|
|
unsigned char buf[4];
|
|
long off;
|
|
|
|
try {
|
|
fi->seek(coff_offset, SEEK_SET);
|
|
fi->readx(buf, 4);
|
|
off = get_le32(buf);
|
|
if (off < 0 || off > coff_offset + 4)
|
|
return false;
|
|
fi->seek(off, SEEK_SET);
|
|
fi->readx(buf, 4);
|
|
if (memcmp(buf, "DLMF", 4) == 0)
|
|
return true;
|
|
} catch (const IOException&) {
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static void handle_allegropak(InputFile *fi, OutputFile *fo)
|
|
{
|
|
unsigned char buf[0x4000];
|
|
int pfsize = 0;
|
|
|
|
try {
|
|
fi->seek(-8, SEEK_END);
|
|
fi->readx(buf, 8);
|
|
if (memcmp(buf, "slh+", 4) != 0)
|
|
return;
|
|
pfsize = get_be32_signed(buf+4);
|
|
if (pfsize <= 8 || (off_t) pfsize >= (off_t) fi->st.st_size)
|
|
return;
|
|
fi->seek(-pfsize, SEEK_END);
|
|
} catch (const IOException&) {
|
|
return;
|
|
}
|
|
while (pfsize > 0)
|
|
{
|
|
const int len = UPX_MIN(pfsize, (int)sizeof(buf));
|
|
fi->readx(buf, len);
|
|
fo->write(buf, len);
|
|
pfsize -= len;
|
|
}
|
|
}
|
|
|
|
|
|
int PackDjgpp2::readFileHeader()
|
|
{
|
|
unsigned char hdr[0x1c];
|
|
unsigned char magic[8];
|
|
|
|
fi->seek(0, SEEK_SET);
|
|
fi->readx(hdr, sizeof(hdr));
|
|
if (get_le16(hdr) == 0x5a4d) // MZ exe signature, stubbed?
|
|
{
|
|
coff_offset = 512 * get_le16(hdr+4);
|
|
if (get_le16(hdr+2) != 0)
|
|
coff_offset += get_le16(hdr+2) - 512;
|
|
fi->seek(512, SEEK_SET);
|
|
fi->readx(magic, 8);
|
|
if (memcmp("go32stub", magic, 8) != 0)
|
|
return 0; // not V2 image
|
|
fi->seek(coff_offset, SEEK_SET);
|
|
if (fi->read(&coff_hdr, sizeof(coff_hdr)) != sizeof(coff_hdr))
|
|
throwCantPack("skipping djgpp symlink");
|
|
}
|
|
else
|
|
{
|
|
fi->seek(coff_offset, SEEK_SET);
|
|
fi->readx(&coff_hdr, 0xa8);
|
|
}
|
|
if (coff_hdr.f_magic != 0x014c) // I386MAGIC
|
|
return 0;
|
|
if ((coff_hdr.f_flags & 2) == 0) // F_EXEC - COFF executable
|
|
return 0;
|
|
if (coff_hdr.a_magic != 0413) // ZMAGIC - demand load format
|
|
return 0;
|
|
// FIXME: check for Linux etc.
|
|
|
|
text = coff_hdr.sh;
|
|
data = text + 1;
|
|
bss = data + 1;
|
|
return UPX_F_DJGPP2_COFF;
|
|
}
|
|
|
|
|
|
// "strip" debug info
|
|
void PackDjgpp2::stripDebug()
|
|
{
|
|
coff_hdr.f_symptr = 0;
|
|
coff_hdr.f_nsyms = 0;
|
|
coff_hdr.f_flags = 0x10f; // 0x100: "32 bit machine: LSB first"
|
|
memset(text->misc, 0, 12);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
//
|
|
**************************************************************************/
|
|
|
|
bool PackDjgpp2::canPack()
|
|
{
|
|
if (!readFileHeader())
|
|
return false;
|
|
if (is_dlm(fi, coff_offset))
|
|
throwCantPack("can't handle DLM");
|
|
|
|
if (opt->force == 0)
|
|
if (text->size != coff_hdr.a_tsize || data->size != coff_hdr.a_dsize)
|
|
throwAlreadyPacked();
|
|
if (text->vaddr + text->size != data->vaddr
|
|
|| data->vaddr + data->size != bss->vaddr)
|
|
{
|
|
if (text->vaddr + text->size < data->vaddr &&
|
|
data->vaddr - text->vaddr == data->scnptr - text->scnptr)
|
|
{
|
|
// This hack is needed to compress Quake 1!
|
|
text->size = coff_hdr.a_tsize = data->vaddr - text->vaddr;
|
|
}
|
|
else
|
|
throwAlreadyPacked();
|
|
}
|
|
// FIXME: check for Linux etc.
|
|
return true;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
//
|
|
**************************************************************************/
|
|
|
|
void PackDjgpp2::pack(OutputFile *fo)
|
|
{
|
|
handleStub(fo);
|
|
|
|
// patch coff header #1: "strip" debug info
|
|
stripDebug();
|
|
|
|
// read file
|
|
const unsigned size = text->size + data->size;
|
|
const unsigned tpos = text->scnptr;
|
|
const unsigned hdrsize = 20 + 28 + sizeof(external_scnhdr_t) * coff_hdr.f_nscns;
|
|
const unsigned usize = size + hdrsize;
|
|
if (hdrsize < sizeof(coff_hdr) || hdrsize > tpos)
|
|
throwCantPack("coff header error");
|
|
|
|
ibuf.alloc(usize);
|
|
obuf.allocForCompression(usize);
|
|
|
|
fi->seek(coff_offset, SEEK_SET);
|
|
fi->readx(ibuf, hdrsize); // orig. coff header
|
|
fi->seek(coff_offset + tpos, SEEK_SET);
|
|
fi->readx(ibuf + hdrsize, size);
|
|
|
|
// prepare packheader
|
|
ph.u_len = usize;
|
|
// prepare filter
|
|
Filter ft(ph.level);
|
|
ft.buf_len = usize - data->size;
|
|
ft.addvalue = text->vaddr - hdrsize;
|
|
// compress
|
|
compressWithFilters(&ft, 512);
|
|
|
|
// patch coff header #2
|
|
const unsigned lsize = getLoaderSize();
|
|
text->size = lsize; // new size of .text
|
|
data->size = ph.c_len; // new size of .data
|
|
|
|
if (bss->size < ph.overlap_overhead) // give it a .bss
|
|
bss->size = ph.overlap_overhead;
|
|
|
|
text->scnptr = sizeof(coff_hdr);
|
|
data->scnptr = text->scnptr + text->size;
|
|
data->vaddr = bss->vaddr + ((data->scnptr + data->size) & 0x1ff) - data->size + ph.overlap_overhead - 0x200;
|
|
coff_hdr.f_nscns = 3;
|
|
|
|
// prepare loader
|
|
MemBuffer loader(lsize);
|
|
memcpy(loader, getLoader(), lsize);
|
|
|
|
// patch loader
|
|
patchPackHeader(loader, lsize);
|
|
patch_le32(loader, lsize, "ENTR", coff_hdr.a_entry);
|
|
patchFilter32(loader, lsize, &ft);
|
|
patch_le32(loader, lsize, "BSSL", ph.overlap_overhead / 4);
|
|
assert(bss->vaddr == ((size + 0x1ff) &~ 0x1ff) + (text->vaddr &~ 0x1ff));
|
|
patch_le32(loader, lsize, "OUTP", text->vaddr - hdrsize);
|
|
patch_le32(loader, lsize, "INPP", data->vaddr);
|
|
|
|
// we should not overwrite our decompressor during unpacking
|
|
// the original coff header (which is put just before the
|
|
// beginning of the original .text section)
|
|
assert(text->vaddr > hdrsize + lsize + sizeof(coff_hdr));
|
|
|
|
// patch coff header #3
|
|
text->vaddr = sizeof(coff_hdr);
|
|
coff_hdr.a_entry = sizeof(coff_hdr) + getLoaderSection("DJ2MAIN1");
|
|
bss->vaddr += ph.overlap_overhead;
|
|
bss->size -= ph.overlap_overhead;
|
|
|
|
// because of a feature (bug?) in stub.asm we need some padding
|
|
memcpy(obuf+data->size, "UPX", 3);
|
|
data->size = ALIGN_UP(data->size, 4);
|
|
|
|
// write coff header, loader and compressed file
|
|
fo->write(&coff_hdr, sizeof(coff_hdr));
|
|
fo->write(loader, lsize);
|
|
fo->write(obuf, data->size);
|
|
#if 0
|
|
printf("%-13s: coff hdr : %8ld bytes\n", getName(), (long) sizeof(coff_hdr));
|
|
printf("%-13s: loader : %8ld bytes\n", getName(), (long) lsize);
|
|
printf("%-13s: compressed : %8ld bytes\n", getName(), (long) data->size);
|
|
#endif
|
|
|
|
// verify
|
|
verifyOverlappingDecompression();
|
|
|
|
// handle overlay
|
|
// FIXME: only Allegro pakfiles are supported
|
|
handle_allegropak(fi, fo);
|
|
|
|
// finally check the compression ratio
|
|
if (!checkFinalCompressionRatio(fo))
|
|
throwNotCompressible();
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
//
|
|
**************************************************************************/
|
|
|
|
int PackDjgpp2::canUnpack()
|
|
{
|
|
if (!readFileHeader())
|
|
return false;
|
|
if (is_dlm(fi, coff_offset))
|
|
throwCantUnpack("can't handle DLM");
|
|
fi->seek(coff_offset, SEEK_SET);
|
|
return readPackHeader(1024) ? 1 : -1;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
//
|
|
**************************************************************************/
|
|
|
|
void PackDjgpp2::unpack(OutputFile *fo)
|
|
{
|
|
handleStub(fo);
|
|
|
|
ibuf.alloc(ph.c_len);
|
|
obuf.allocForUncompression(ph.u_len);
|
|
|
|
fi->seek(coff_offset + ph.buf_offset + ph.getPackHeaderSize(), SEEK_SET);
|
|
fi->readx(ibuf, ph.c_len);
|
|
|
|
// decompress
|
|
decompress(ibuf, obuf);
|
|
|
|
coff_header_t *chdr = (coff_header_t*) obuf.getVoidPtr();
|
|
text = chdr->sh;
|
|
data = text + 1;
|
|
|
|
const unsigned hdrsize = 20 + 28
|
|
+ sizeof(external_scnhdr_t) * chdr->f_nscns;
|
|
|
|
unsigned addvalue;
|
|
if (ph.version >= 14)
|
|
addvalue = text->vaddr - hdrsize;
|
|
else
|
|
addvalue = text->vaddr &~ 0x1ff; // for old versions
|
|
|
|
// unfilter
|
|
if (ph.filter)
|
|
{
|
|
Filter ft(ph.level);
|
|
ft.init(ph.filter, addvalue);
|
|
ft.cto = (unsigned char) ph.filter_cto;
|
|
if (ph.version < 11)
|
|
{
|
|
unsigned char ctobuf[4];
|
|
fi->readx(ctobuf, 4);
|
|
ft.cto = (unsigned char) (get_le32(ctobuf) >> 24);
|
|
}
|
|
ft.unfilter(obuf, ph.u_len - data->size);
|
|
}
|
|
|
|
if (ph.version < 14)
|
|
{
|
|
// fixup for the aligning bug in strip 2.8+
|
|
text->scnptr &= 0x1ff;
|
|
data->scnptr = text->scnptr + text->size;
|
|
// write decompressed file
|
|
if (fo)
|
|
fo->write(obuf, ph.u_len);
|
|
}
|
|
else
|
|
{
|
|
// write the header
|
|
// some padding might be required between the end
|
|
// of the header and the start of the .text section
|
|
|
|
const unsigned padding = text->scnptr - hdrsize;
|
|
ibuf.clear(0, padding);
|
|
|
|
if (fo)
|
|
{
|
|
fo->write(obuf, hdrsize);
|
|
fo->write(ibuf, padding);
|
|
fo->write(obuf + hdrsize, ph.u_len - hdrsize);
|
|
}
|
|
}
|
|
|
|
if (fo)
|
|
handle_allegropak(fi, fo);
|
|
}
|
|
|
|
|
|
/*
|
|
vi:ts=4:et
|
|
*/
|
|
|