mirror of
https://github.com/upx/upx
synced 2025-09-28 19:06:07 +08:00

and allow for block-by-block specification of filter and parameters. linker.cpp linker.h mem.cpp mem.h p_elf.h p_lx_elf.cpp p_lx_elf.h p_lx_exc.cpp p_lx_exc.h p_lx_sh.cpp p_lx_sh.h p_unix.cpp p_unix.h packer.cpp packer.h stub/fold_elf86.asm stub/fold_exec86.asm stub/fold_sh86.asm stub/l_lx_elf.c stub/l_lx_elf86.asm stub/l_lx_elf86.lds stub/l_lx_exec.c stub/l_lx_exec86.asm stub/l_lx_exec86.lds stub/l_lx_sh.c stub/l_lx_sh86.asm stub/l_lx_sh86.lds stub/linux.hh committer: jreiser <jreiser> 981084316 +0000
472 lines
14 KiB
C++
472 lines
14 KiB
C++
/* p_lx_elf.cpp --
|
|
|
|
This file is part of the UPX executable compressor.
|
|
|
|
Copyright (C) 1996-2001 Markus Franz Xaver Johannes Oberhumer
|
|
Copyright (C) 1996-2001 Laszlo Molnar
|
|
Copyright (C) 2000-2001 John F. Reiser
|
|
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 John F. Reiser
|
|
markus@oberhumer.com ml1050@cdata.tvnet.hu jreiser@BitWagon.com
|
|
*/
|
|
|
|
|
|
#include "conf.h"
|
|
|
|
#include "file.h"
|
|
#include "filter.h"
|
|
#include "linker.h"
|
|
#include "packer.h"
|
|
#include "p_elf.h"
|
|
#include "p_unix.h"
|
|
#include "p_lx_exc.h"
|
|
#include "p_lx_elf.h"
|
|
|
|
#define PT_LOAD Elf_LE32_Phdr::PT_LOAD
|
|
|
|
|
|
/*************************************************************************
|
|
//
|
|
**************************************************************************/
|
|
|
|
static const
|
|
#include "stub/l_lx_elf86.h"
|
|
static const
|
|
#include "stub/fold_elf86.h"
|
|
|
|
|
|
PackLinuxI386elf::PackLinuxI386elf(InputFile *f) :
|
|
super(f), phdri(NULL)
|
|
{
|
|
}
|
|
|
|
PackLinuxI386elf::~PackLinuxI386elf()
|
|
{
|
|
delete[] phdri;
|
|
}
|
|
|
|
int const *
|
|
PackLinuxI386elf::getFilters() const
|
|
{
|
|
static const int filters[] = {
|
|
0x49, 0x46,
|
|
0x83, 0x36, 0x26,
|
|
0x86, 0x80,
|
|
0x84, 0x87, 0x81,
|
|
0x82, 0x85,
|
|
0x24, 0x16, 0x13, 0x14, 0x11, 0x25, 0x15, 0x12,
|
|
-1 };
|
|
return filters;
|
|
}
|
|
|
|
int
|
|
PackLinuxI386elf::buildLoader(const Filter *ft)
|
|
{
|
|
return buildLinuxLoader(
|
|
linux_i386elf_loader, sizeof(linux_i386elf_loader),
|
|
linux_i386elf_fold, sizeof(linux_i386elf_fold), ft );
|
|
}
|
|
|
|
|
|
void PackLinuxI386elf::patchLoader() { }
|
|
|
|
|
|
bool PackLinuxI386elf::canPack()
|
|
{
|
|
unsigned char buf[sizeof(Elf_LE32_Ehdr) + 14*sizeof(Elf_LE32_Phdr)];
|
|
COMPILE_TIME_ASSERT(sizeof(buf) <= 512);
|
|
|
|
exetype = 0;
|
|
|
|
fi->readx(buf, sizeof(buf));
|
|
fi->seek(0, SEEK_SET);
|
|
Elf_LE32_Ehdr const *const ehdr = (Elf_LE32_Ehdr const *)buf;
|
|
|
|
// now check the ELF header
|
|
if (checkEhdr(ehdr) != 0)
|
|
return false;
|
|
|
|
// additional requirements for linux/elf386
|
|
if (ehdr->e_ehsize != sizeof(*ehdr)) {
|
|
throwCantPack("invalid Ehdr e_ehsize; try `--force-execve'");
|
|
return false;
|
|
}
|
|
if (ehdr->e_phoff != sizeof(*ehdr)) {// Phdrs not contiguous with Ehdr
|
|
throwCantPack("non-contiguous Ehdr/Phdr; try `--force-execve'");
|
|
return false;
|
|
}
|
|
|
|
// The first PT_LOAD must cover the beginning of the file (0==p_offset).
|
|
Elf_LE32_Phdr const *phdr = (Elf_LE32_Phdr const *)(buf + ehdr->e_phoff);
|
|
for (unsigned j=0; j < ehdr->e_phnum; ++phdr, ++j) {
|
|
if (j >= 14)
|
|
return false;
|
|
if (phdr->PT_LOAD == phdr->p_type) {
|
|
if (phdr->p_offset != 0) {
|
|
throwCantPack("invalid Phdr p_offset; try `--force-execve'");
|
|
return false;
|
|
}
|
|
// detect possible conflict upon invocation
|
|
if (phdr->p_vaddr < (unsigned)(0x400000 + file_size)
|
|
|| phdr->p_paddr < (unsigned)(0x400000 + file_size) ) {
|
|
throwCantPack("invalid Phdr p_vaddr; try `--force-execve'");
|
|
return false;
|
|
}
|
|
exetype = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return super::canPack();
|
|
}
|
|
|
|
void PackLinuxI386elf::packExtent(
|
|
Extent const &x,
|
|
unsigned &total_in,
|
|
unsigned &total_out,
|
|
Filter *ft,
|
|
OutputFile *fo
|
|
)
|
|
{
|
|
fi->seek(x.offset, SEEK_SET);
|
|
for (off_t rest = x.size; 0 != rest; ) {
|
|
int const strategy = getStrategy(*ft);
|
|
int l = fi->readx(ibuf, UPX_MIN(rest, (off_t)blocksize));
|
|
if (l == 0) {
|
|
break;
|
|
}
|
|
rest -= l;
|
|
|
|
// Note: compression for a block can fail if the
|
|
// file is e.g. blocksize + 1 bytes long
|
|
|
|
// compress
|
|
ph.u_len = l;
|
|
ph.overlap_overhead = 0;
|
|
unsigned end_u_adler;
|
|
if (ft) {
|
|
// compressWithFilters() updates u_adler _inside_ compress();
|
|
// that is, AFTER filtering. We want BEFORE filtering,
|
|
// so that decompression checks the end-to-end checksum.
|
|
end_u_adler = upx_adler32(ph.u_adler, ibuf, ph.u_len);
|
|
ft->buf_len = l;
|
|
compressWithFilters(ft, OVERHEAD, strategy);
|
|
}
|
|
else {
|
|
(void) compress(ibuf, obuf); // ignore return value
|
|
}
|
|
|
|
if (ph.c_len < ph.u_len) {
|
|
ph.overlap_overhead = OVERHEAD;
|
|
if (!testOverlappingDecompression(obuf, ph.overlap_overhead)) {
|
|
throwNotCompressible();
|
|
}
|
|
}
|
|
else {
|
|
ph. c_len = ph.u_len;
|
|
// must update checksum of compressed data
|
|
ph.c_adler = upx_adler32(ph.c_adler, ibuf, ph.u_len);
|
|
}
|
|
|
|
// write block sizes
|
|
b_info tmp; memset(&tmp, 0, sizeof(tmp));
|
|
set_native32(&tmp.sz_unc, ph.u_len);
|
|
set_native32(&tmp.sz_cpr, ph.c_len);
|
|
tmp.b_method = ph.method;
|
|
if (ft) {
|
|
tmp.b_ftid = ft->id;
|
|
tmp.b_cto8 = ft->cto;
|
|
}
|
|
fo->write(&tmp, sizeof(tmp));
|
|
ph.b_len += sizeof(b_info);
|
|
|
|
// write compressed data
|
|
if (ph.c_len < ph.u_len) {
|
|
fo->write(obuf, ph.c_len);
|
|
// Checks ph.u_adler after decompression but before unfiltering
|
|
verifyOverlappingDecompression();
|
|
}
|
|
else {
|
|
fo->write(ibuf, ph.u_len);
|
|
}
|
|
|
|
if (ft) {
|
|
ph.u_adler = end_u_adler;
|
|
}
|
|
total_in += ph.u_len;
|
|
total_out += ph.c_len;
|
|
}
|
|
}
|
|
|
|
|
|
void PackLinuxI386elf::pack1(OutputFile *fo, Filter &)
|
|
{
|
|
// set options
|
|
opt->unix.blocksize = blocksize = file_size;
|
|
|
|
fi->seek(0, SEEK_SET);
|
|
fi->readx(&ehdri, sizeof(ehdri));
|
|
assert(ehdri.e_phoff == sizeof(Elf_LE32_Ehdr)); // checked by canPack()
|
|
sz_phdrs = ehdri.e_phnum * ehdri.e_phentsize;
|
|
|
|
phdri = new Elf_LE32_Phdr[ehdri.e_phnum];
|
|
fi->seek(ehdri.e_phoff, SEEK_SET);
|
|
fi->readx(phdri, sz_phdrs);
|
|
|
|
generateElfHdr(fo, linux_i386elf_fold, phdri, ehdri.e_phnum,
|
|
getbrk(phdri, ehdri.e_phnum) );
|
|
}
|
|
|
|
void PackLinuxI386elf::pack2(OutputFile *fo, Filter &ft)
|
|
{
|
|
Extent x;
|
|
unsigned k;
|
|
|
|
// count passes, set ptload vars
|
|
ui_total_passes = 0;
|
|
off_t ptload0hi = 0, ptload1lo = 0;
|
|
int nx = 0;
|
|
for (k = 0; k < ehdri.e_phnum; ++k) {
|
|
if (PT_LOAD == phdri[k].p_type) {
|
|
x.offset = phdri[k].p_offset;
|
|
x.size = phdri[k].p_filesz;
|
|
if (0 == ptload0hi) {
|
|
ptload0hi = x.offset + x.size;
|
|
}
|
|
else if (0 == ptload1lo) {
|
|
ptload1lo = x.offset;
|
|
}
|
|
ui_total_passes++;
|
|
} else {
|
|
if (nx++ == 0)
|
|
ui_total_passes++;
|
|
}
|
|
}
|
|
if (ptload0hi < ptload1lo)
|
|
ui_total_passes++;
|
|
|
|
// compress extents
|
|
unsigned total_in = 0;
|
|
unsigned total_out = 0;
|
|
|
|
ui_pass = -1;
|
|
x.offset = 0;
|
|
x.size = sizeof(Elf_LE32_Ehdr) + sz_phdrs;
|
|
{
|
|
int const old_level = ph.level; ph.level = 10;
|
|
packExtent(x, total_in, total_out, 0, fo);
|
|
ph.level = old_level;
|
|
}
|
|
|
|
ui_pass = 0;
|
|
ft.addvalue = 0;
|
|
|
|
nx = 0;
|
|
for (k = 0; k < ehdri.e_phnum; ++k) if (PT_LOAD==phdri[k].p_type) {
|
|
if (ft.id < 0x40) {
|
|
// FIXME: ?? ft.addvalue = phdri[k].p_vaddr;
|
|
}
|
|
x.offset = phdri[k].p_offset;
|
|
x.size = phdri[k].p_filesz;
|
|
if (0 == nx) { // 1st PT_LOAD must cover Ehdr at 0==p_offset
|
|
unsigned const delta = sizeof(Elf_LE32_Ehdr) + sz_phdrs;
|
|
if (ft.id < 0x40) {
|
|
// FIXME: ?? ft.addvalue += delta;
|
|
}
|
|
x.offset += delta;
|
|
x.size -= delta;
|
|
}
|
|
packExtent(x, total_in, total_out,
|
|
((Elf_LE32_Phdr::PF_X & phdri[k].p_flags)
|
|
? &ft : 0 ), fo );
|
|
++nx;
|
|
}
|
|
if (ptload0hi < ptload1lo) { // alignment hole?
|
|
x.offset = ptload0hi;
|
|
x.size = ptload1lo - ptload0hi;
|
|
packExtent(x, total_in, total_out, 0, fo);
|
|
}
|
|
if ((off_t)total_in < file_size) { // non-PT_LOAD stuff
|
|
x.offset = total_in;
|
|
x.size = file_size - total_in;
|
|
packExtent(x, total_in, total_out, 0, fo);
|
|
}
|
|
|
|
if ((off_t)total_in != file_size)
|
|
throwEOFException();
|
|
}
|
|
|
|
|
|
void PackLinuxI386elf::unpackExtent(unsigned wanted, OutputFile *fo,
|
|
unsigned &total_in, unsigned &total_out,
|
|
unsigned &c_adler, unsigned &u_adler,
|
|
bool first_PF_X
|
|
)
|
|
{
|
|
while (wanted) {
|
|
b_info hdr;
|
|
fi->readx(&hdr, sizeof(hdr));
|
|
int const sz_unc = ph.u_len = get_native32(&hdr.sz_unc);
|
|
int const sz_cpr = ph.c_len = get_native32(&hdr.sz_cpr);
|
|
ph.filter_cto = hdr.b_cto8;
|
|
|
|
if (sz_unc == 0) { // must never happen while 0!=wanted
|
|
throwCompressedDataViolation();
|
|
break;
|
|
}
|
|
if (sz_unc <= 0 || sz_cpr <= 0)
|
|
throwCompressedDataViolation();
|
|
if (sz_cpr > sz_unc || sz_unc > (int)blocksize)
|
|
throwCompressedDataViolation();
|
|
|
|
int j = blocksize + OVERHEAD - sz_cpr;
|
|
fi->readx(ibuf+j, sz_cpr);
|
|
// update checksum of compressed data
|
|
c_adler = upx_adler32(c_adler, ibuf + j, sz_cpr);
|
|
// decompress
|
|
if (sz_cpr < sz_unc)
|
|
{
|
|
decompress(ibuf+j, ibuf, false);
|
|
if (first_PF_X) { // Elf32_Ehdr is never filtered
|
|
first_PF_X = false; // but everything else might be
|
|
}
|
|
else if (ph.filter) {
|
|
Filter ft(ph.level);
|
|
ft.init(ph.filter, 0);
|
|
ft.cto = ph.filter_cto;
|
|
ft.unfilter(ibuf, sz_unc);
|
|
}
|
|
j = 0;
|
|
}
|
|
// update checksum of uncompressed data
|
|
u_adler = upx_adler32(u_adler, ibuf + j, sz_unc);
|
|
total_in += sz_cpr;
|
|
total_out += sz_unc;
|
|
// write block
|
|
if (fo)
|
|
fo->write(ibuf + j, sz_unc);
|
|
wanted -= sz_unc;
|
|
}
|
|
}
|
|
|
|
|
|
void PackLinuxI386elf::unpack(OutputFile *fo)
|
|
{
|
|
#define MAX_ELF_HDR 512
|
|
char bufehdr[MAX_ELF_HDR];
|
|
Elf_LE32_Ehdr *const ehdr = (Elf_LE32_Ehdr *)bufehdr;
|
|
Elf_LE32_Phdr const *phdr = (Elf_LE32_Phdr *)(1+ehdr);
|
|
|
|
fi->seek(overlay_offset, SEEK_SET);
|
|
p_info hbuf;
|
|
fi->readx(&hbuf, sizeof(hbuf));
|
|
unsigned orig_file_size = get_native32(&hbuf.p_filesize);
|
|
blocksize = get_native32(&hbuf.p_blocksize);
|
|
if (file_size > (off_t)orig_file_size || blocksize > orig_file_size)
|
|
throwCantUnpack("file header corrupted");
|
|
|
|
ibuf.alloc(blocksize + OVERHEAD);
|
|
b_info bhdr;
|
|
fi->readx(&bhdr, sizeof(bhdr));
|
|
ph.u_len = get_native32(&bhdr.sz_unc);
|
|
ph.c_len = get_native32(&bhdr.sz_cpr);
|
|
ph.filter_cto = bhdr.b_cto8;
|
|
|
|
// Uncompress Ehdr and Phdrs.
|
|
fi->readx(ibuf, ph.c_len);
|
|
decompress(ibuf, (upx_byte *)ehdr, false);
|
|
|
|
unsigned total_in = 0;
|
|
unsigned total_out = 0;
|
|
unsigned c_adler = upx_adler32(0, NULL, 0);
|
|
unsigned u_adler = upx_adler32(0, NULL, 0);
|
|
off_t ptload0hi=0, ptload1lo=0;
|
|
|
|
// decompress PT_LOAD
|
|
bool first_PF_X = true;
|
|
fi->seek(- (off_t) (sizeof(bhdr) + ph.c_len), SEEK_CUR);
|
|
for (unsigned j=0; j < ehdr->e_phnum; ++phdr, ++j) {
|
|
if (PT_LOAD==phdr->p_type) {
|
|
if (0==ptload0hi) {
|
|
ptload0hi = phdr->p_filesz + phdr->p_offset;
|
|
}
|
|
else if (0==ptload1lo) {
|
|
ptload1lo = phdr->p_offset;
|
|
}
|
|
if (fo)
|
|
fo->seek(phdr->p_offset, SEEK_SET);
|
|
if (Elf_LE32_Phdr::PF_X & phdr->p_flags) {
|
|
unpackExtent(phdr->p_filesz, fo, total_in, total_out,
|
|
c_adler, u_adler, first_PF_X);
|
|
first_PF_X = false;
|
|
}
|
|
else {
|
|
unpackExtent(phdr->p_filesz, fo, total_in, total_out,
|
|
c_adler, u_adler, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ptload0hi < ptload1lo) { // alignment hole?
|
|
if (fo)
|
|
fo->seek(ptload0hi, SEEK_SET);
|
|
unpackExtent(ptload1lo - ptload0hi, fo, total_in, total_out,
|
|
c_adler, u_adler, false);
|
|
}
|
|
if (total_out != orig_file_size) { // non-PT_LOAD stuff
|
|
if (fo)
|
|
fo->seek(0, SEEK_END);
|
|
unpackExtent(orig_file_size - total_out, fo, total_in, total_out,
|
|
c_adler, u_adler, false);
|
|
}
|
|
|
|
// check for end-of-file
|
|
fi->readx(&bhdr, sizeof(bhdr));
|
|
unsigned const sz_unc = ph.u_len = get_native32(&bhdr.sz_unc);
|
|
|
|
if (sz_unc == 0) { // uncompressed size 0 -> EOF
|
|
// note: magic is always stored le32
|
|
unsigned const sz_cpr = get_le32(&bhdr.sz_cpr);
|
|
if (sz_cpr != UPX_MAGIC_LE32) // sz_cpr must be h->magic
|
|
throwCompressedDataViolation();
|
|
}
|
|
else { // extra bytes after end?
|
|
throwCompressedDataViolation();
|
|
}
|
|
|
|
// update header with totals
|
|
ph.c_len = total_in;
|
|
ph.u_len = total_out;
|
|
|
|
// all bytes must be written
|
|
if (total_out != orig_file_size)
|
|
throwEOFException();
|
|
|
|
// finally test the checksums
|
|
if (ph.c_adler != c_adler || ph.u_adler != u_adler)
|
|
throwChecksumError();
|
|
#undef MAX_ELF_HDR
|
|
}
|
|
|
|
|
|
/*
|
|
vi:ts=4:et
|
|
*/
|
|
|