1
0
mirror of https://github.com/upx/upx synced 2025-09-28 19:06:07 +08:00
upx/src/p_vmlinz.cpp
Markus F.X.J. Oberhumer e787805b81 Extended gzip signature to 3 bytes.
committer: mfx <mfx> 977403238 +0000
2000-12-21 12:53:58 +00:00

385 lines
11 KiB
C++

/* p_vmlinz.cpp --
This file is part of the UPX executable compressor.
Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1996-2000 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@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu
*/
#include "conf.h"
#include "file.h"
#include "filter.h"
#include "packer.h"
#include "p_vmlinz.h"
#include <zlib.h>
static const
#include "stub/l_vmlinz.h"
static const unsigned kernel_entry = 0x100000;
static const unsigned stack_during_uncompression = 0x90000;
// from /usr/src/linux/arch/i386/boot/compressed/Makefile
static const unsigned zimage_offset = 0x1000;
static const unsigned bzimage_offset = 0x100000;
/*************************************************************************
//
**************************************************************************/
PackVmlinuzI386::PackVmlinuzI386(InputFile *f) :
super(f)
{
assert(sizeof(boot_sect_t) == 0x218);
}
int PackVmlinuzI386::getCompressionMethod() const
{
if (M_IS_NRV2B(opt->method))
return M_NRV2B_LE32;
if (M_IS_NRV2D(opt->method))
return M_NRV2D_LE32;
return opt->level > 1 ? M_NRV2D_LE32 : M_NRV2B_LE32;
}
const int *PackVmlinuzI386::getFilters() const
{
static const int filters[] = { 0x26, 0x24, 0x11, 0x14, 0x13, 0x16,
0x25, 0x15, 0x12, -1 };
return filters;
}
bool PackVmlinuzI386::canPack()
{
return readFileHeader() == getFormat();
}
/*************************************************************************
// common util routines
**************************************************************************/
int PackVmlinuzI386::readFileHeader()
{
boot_sect_t h;
fi->readx(&h, sizeof(h));
if (h.boot_flag != 0xAA55)
return 0;
setup_size = (1 + (h.setup_sects ? h.setup_sects : 4)) * 0x200;
if (setup_size <= 0 || setup_size >= file_size)
return 0;
int format = UPX_F_VMLINUZ_i386;
unsigned sys_size = ALIGN_UP(file_size, 16) - setup_size;
if (memcmp(h.hdrs, "HdrS", 4) == 0 && (h.load_flags & 1) != 0)
{
format = UPX_F_BVMLINUZ_i386;
// account for 16-bit h.sys_size, wraparound at 20 bits
sys_size &= (1 << 20) - 1;
}
if (16 * h.sys_size != sys_size)
return 0;
// FIXME: add more checks for a valid kernel
return format;
}
// read full kernel into obuf, gzip-uncompress into ibuf,
// return uncompressed size
int PackVmlinuzI386::uncompressKernel()
{
// read whole kernel image
obuf.alloc(file_size);
fi->seek(0, SEEK_SET);
fi->readx(obuf, file_size);
// estimate gzip-uncompressed kernel size & alloc buffer
ibuf.alloc((file_size - setup_size) * 3);
for (int gzoff = setup_size; gzoff < file_size; gzoff++)
{
// find gzip header (2 bytes magic, 1 byte method "deflated")
int off = find(obuf + gzoff, file_size - gzoff, "\x1F\x8B\x08", 3);
if (off < 0)
break;
gzoff += off;
// try to decompress
fi->seek(gzoff, SEEK_SET);
gzFile zf = gzdopen(fi->getFd(), "r");
if (zf == 0)
break;
int klen = gzread(zf, ibuf, ibuf.getSize());
if (klen <= file_size)
continue;
// FIXME: check for special magic bytes in ibuf ???
// FIXME: check for kernel architecture ???
// FIXME: check for special klen size, e.g. (klen & 0xfff) == 0 ???
return klen;
}
return 0;
}
void PackVmlinuzI386::readKernel()
{
int klen = uncompressKernel();
if (klen <= 0)
throwCantPack("kernel decompression failed");
if (klen >= (int) ibuf.getSize())
throwCantPack("kernel decompression failed -- too big");
//OutputFile::dump("kernel.img", ibuf, klen);
// copy the setup boot code
setup_buf.alloc(setup_size);
memcpy(setup_buf, obuf, setup_size);
//OutputFile::dump("setup.img", setup_buf, setup_size);
obuf.free();
obuf.allocForCompression(klen);
ph.u_len = klen;
ph.filter = 0;
}
/*************************************************************************
// vmlinuz specific
**************************************************************************/
int PackVmlinuzI386::buildLoader(const Filter *ft)
{
// prepare loader
initLoader(nrv_loader, sizeof(nrv_loader));
addLoader("LINUZ000",
ft->id ? "LZCALLT1" : "",
"LZIMAGE0",
getDecompressor(),
NULL
);
if (ft->id)
{
assert(ft->calls > 0);
addLoader("LZCALLT9", NULL);
addFilter32(ft->id);
}
addLoader("LINUZ990""IDENTSTR""UPX1HEAD", NULL);
return getLoaderSize();
}
void PackVmlinuzI386::pack(OutputFile *fo)
{
readKernel();
// prepare filter
Filter ft(opt->level);
ft.buf_len = ph.u_len;
ft.addvalue = kernel_entry;
// prepare other settings
const unsigned overlap_range = 1 << 20;
unsigned overlapoh;
int strategy = -1; // try the first working filter
if (opt->filter >= 0 && isValidFilter(opt->filter))
// try opt->filter or 0 if that fails
strategy = -2;
else if (opt->all_filters)
// choose best from all available filters
strategy = 0;
compressWithFilters(&ft, &overlapoh, overlap_range, strategy);
const unsigned lsize = getLoaderSize();
MemBuffer loader(lsize);
memcpy(loader, getLoader(), lsize);
patchPackHeader(loader, lsize);
patchFilter32(loader, lsize, &ft);
patch_le32(loader, lsize, "ESI1", zimage_offset + lsize);
patch_le32(loader, lsize, "KEIP", kernel_entry);
patch_le32(loader, lsize, "STAK", stack_during_uncompression);
boot_sect_t *bs = (boot_sect_t *) ((unsigned char *) setup_buf);
bs->sys_size = ALIGN_UP(lsize + ph.c_len, 16) / 16;
fo->write(setup_buf, setup_buf.getSize());
fo->write(loader, lsize);
fo->write(obuf, ph.c_len);
#if 0
printf("%-13s: setup : %8ld bytes\n", getName(), (long) setup_buf.getSize());
printf("%-13s: loader : %8ld bytes\n", getName(), (long) lsize);
printf("%-13s: compressed : %8ld bytes\n", getName(), (long) ph.c_len);
#endif
// finally check the compression ratio
if (!checkFinalCompressionRatio(fo))
throwNotCompressible();
}
/*************************************************************************
// bvmlinuz specific
**************************************************************************/
int PackBvmlinuzI386::buildLoader(const Filter *ft)
{
// prepare loader
initLoader(nrv_loader, sizeof(nrv_loader));
addLoader("LINUZ000",
ft->id ? "LZCALLT1" : "",
"LBZIMAGE""IDENTSTR",
"+40D++++", // align the stuff to 4 byte boundary
"UPX1HEAD", // 32 byte
"LZCUTPOI""+0000000",
getDecompressor(),
NULL
);
if (ft->id)
{
assert(ft->calls > 0);
addLoader("LZCALLT9", NULL);
addFilter32(ft->id);
}
addLoader("LINUZ990", NULL);
return getLoaderSize();
}
void PackBvmlinuzI386::pack(OutputFile *fo)
{
readKernel();
// prepare filter
Filter ft(opt->level);
ft.buf_len = ph.u_len;
ft.addvalue = kernel_entry;
// prepare other settings
const unsigned overlap_range = 512;
unsigned overlapoh;
int strategy = -1; // try the first working filter
if (opt->filter >= 0 && isValidFilter(opt->filter))
// try opt->filter or 0 if that fails
strategy = -2;
else if (opt->all_filters)
// choose best from all available filters
strategy = 0;
compressWithFilters(&ft, &overlapoh, overlap_range, strategy);
// align everything to dword boundary - it is easier to handle
unsigned clen = ph.c_len;
memset(obuf + clen, 0, 4);
clen = ALIGN_UP(clen, 4);
const unsigned lsize = getLoaderSize();
MemBuffer loader(lsize);
memcpy(loader, getLoader(), lsize);
patchFilter32(loader, lsize, &ft);
patchPackHeader(loader, lsize);
const int e_len = getLoaderSectionStart("LZCUTPOI");
assert(e_len > 0);
const unsigned d_len4 = ALIGN_UP(lsize - e_len, 4);
const unsigned decompr_pos = ALIGN_UP(ph.u_len + overlapoh, 16);
const unsigned copy_size = clen + d_len4;
const unsigned edi = decompr_pos + d_len4 - 4; // copy to
const unsigned esi = ALIGN_UP(clen + lsize, 4) - 4; // copy from
unsigned jpos = find_le32(loader, e_len, get_le32("JMPD"));
patch_le32(loader, e_len, "JMPD", decompr_pos - jpos - 4);
patch_le32(loader, e_len, "ESI1", bzimage_offset + decompr_pos - clen);
patch_le32(loader, e_len, "ECX0", copy_size / 4);
patch_le32(loader, e_len, "EDI0", bzimage_offset + edi);
patch_le32(loader, e_len, "ESI0", bzimage_offset + esi);
patch_le32(loader, e_len, "KEIP", kernel_entry);
patch_le32(loader, e_len, "STAK", stack_during_uncompression);
boot_sect_t *bs = (boot_sect_t *) ((unsigned char *) setup_buf);
bs->sys_size = ALIGN_UP(lsize + clen, 16) / 16;
fo->write(setup_buf, setup_buf.getSize());
fo->write(loader, e_len);
fo->write(obuf, clen);
fo->write(loader + e_len, lsize - e_len);
#if 0
printf("%-13s: setup : %8ld bytes\n", getName(), (long) setup_buf.getSize());
printf("%-13s: entry : %8ld bytes\n", getName(), (long) e_len);
printf("%-13s: compressed : %8ld bytes\n", getName(), (long) clen);
printf("%-13s: decompressor : %8ld bytes\n", getName(), (long) (lsize - e_len));
#endif
// verify
verifyOverlappingDecompression(&obuf, overlapoh);
// finally check the compression ratio
if (!checkFinalCompressionRatio(fo))
throwNotCompressible();
}
/*************************************************************************
// unpack
**************************************************************************/
int PackVmlinuzI386::canUnpack()
{
if (readFileHeader() != getFormat())
return false;
fi->seek(setup_size, SEEK_SET);
return readPackHeader(1024) ? 1 : -1;
}
void PackVmlinuzI386::unpack(OutputFile *)
{
// no uncompression support for this format, so that
// it is possible to remove the original deflate code (>10KB)
// FIXME: but we could write the uncompressed "vmlinux" image
throwCantUnpack("build a new kernel instead :-)");
}
/*
vi:ts=4:et
*/