upx/src/packhead.cpp

264 lines
6.8 KiB
C++
Raw Normal View History

2000-05-20 00:04:55 +08:00
/* packhead.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
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 "packer.h"
/*************************************************************************
// packheader
//
// We try to be able to unpack UPX 0.7x (versions 8 & 9) and at
// least to detect older versions, so this is a little bit messy.
**************************************************************************/
// simple checksum for the header itself (since version 10)
static unsigned char get_packheader_checksum(const upx_bytep buf, int len)
{
assert(get_le32(buf) == UPX_MAGIC_LE32);
//printf("1 %d\n", len);
buf += 4;
len -= 4;
unsigned c = 0;
while (len-- > 0)
c += *buf++;
c %= 251;
//printf("2 %d\n", c);
return (unsigned char) c;
}
/*************************************************************************
//
**************************************************************************/
static int get_packheader_size(int version, int format)
{
int n = 0;
if (version <= 3)
n = 24;
else if (version <= 9)
{
if (format == UPX_F_DOS_COM || format == UPX_F_DOS_SYS)
n = 20;
else if (format == UPX_F_DOS_EXE || format == UPX_F_DOS_EXEH)
n = 25;
else
n = 28;
}
else
{
if (format == UPX_F_DOS_COM || format == UPX_F_DOS_SYS)
n = 22;
else if (format == UPX_F_DOS_EXE || format == UPX_F_DOS_EXEH)
n = 27;
else
n = 32;
}
if (n == 0)
throwCantUnpack("unknown header version");
return n;
}
int PackHeader::getPackHeaderSize() const
{
return get_packheader_size(version, format);
}
/*************************************************************************
//
**************************************************************************/
void PackHeader::putPackHeader(upx_bytep buf, unsigned len)
{
#if defined(UNUPX)
throwBadLoader();
#else
upx_bytep l = find_le32(buf,len,magic);
if (l == 0)
throwBadLoader();
l[4] = (unsigned char) version;
l[5] = (unsigned char) format;
l[6] = (unsigned char) method;
l[7] = (unsigned char) level;
// the new variable length header
if (format < 128)
{
set_le32(l+8,u_adler);
set_le32(l+12,c_adler);
if (format == UPX_F_DOS_COM || format == UPX_F_DOS_SYS)
{
set_le16(l+16,u_len);
set_le16(l+18,c_len);
l[20] = (unsigned char) filter;
}
else if (format == UPX_F_DOS_EXE || format == UPX_F_DOS_EXEH)
{
set_le24(l+16,u_len);
set_le24(l+19,c_len);
set_le24(l+22,u_file_size);
l[25] = (unsigned char) filter;
}
else
{
set_le32(l+16,u_len);
set_le32(l+20,c_len);
set_le32(l+24,u_file_size);
l[28] = (unsigned char) filter;
l[29] = (unsigned char) filter_cto;
l[30] = 0;
}
}
else
{
set_be32(l+8,u_len);
set_be32(l+12,c_len);
set_be32(l+16,u_adler);
set_be32(l+20,c_adler);
set_be32(l+24,u_file_size);
l[28] = (unsigned char) filter;
l[29] = (unsigned char) filter_cto;
l[30] = 0;
}
// store header_checksum
const int hs = getPackHeaderSize();
l[hs - 1] = get_packheader_checksum(l, hs - 1);
#endif /* UNUPX */
}
/*************************************************************************
//
**************************************************************************/
bool PackHeader::fillPackHeader(upx_bytep buf, unsigned len)
{
upx_bytep l = find_le32(buf,len,magic);
if (l == 0)
return false;
buf_offset = l - buf;
const int hlen = len - buf_offset;
if (hlen < 8)
return false;
2000-05-20 00:04:55 +08:00
version = l[4];
format = l[5];
method = l[6];
level = l[7];
filter_cto = 0;
const int hs = getPackHeaderSize();
if (hs > hlen)
throwCantUnpack("header corrupted");
2000-05-20 00:04:55 +08:00
// the new variable length header
int off_filter = 0;
if (format < 128)
{
u_adler = get_le32(l+8);
c_adler = get_le32(l+12);
if (format == UPX_F_DOS_COM || format == UPX_F_DOS_SYS)
{
u_len = get_le16(l+16);
c_len = get_le16(l+18);
u_file_size = u_len;
off_filter = 20;
}
else if (format == UPX_F_DOS_EXE || format == UPX_F_DOS_EXEH)
{
u_len = get_le24(l+16);
c_len = get_le24(l+19);
u_file_size = get_le24(l+22);
off_filter = 25;
}
else
{
u_len = get_le32(l+16);
c_len = get_le32(l+20);
u_file_size = get_le32(l+24);
off_filter = 28;
filter_cto = l[29];
}
}
else
{
u_len = get_be32(l+8);
c_len = get_be32(l+12);
u_adler = get_be32(l+16);
c_adler = get_be32(l+20);
u_file_size = get_be32(l+24);
off_filter = 28;
filter_cto = l[29];
}
if (version >= 10)
filter = l[off_filter];
else if ((level & 128) == 0)
filter = 0;
else
{
// convert old flags to new filter id
level &= 127;
if (format == UPX_F_DOS_COM || format == UPX_F_DOS_SYS)
filter = 0x06;
else
filter = 0x26;
}
level &= 15;
return true;
}
bool PackHeader::checkPackHeader(const upx_bytep hbuf, int hlen) const
{
if (version == 0xff)
throwCantUnpack("cannot unpack UPX ;-)");
const int hs = getPackHeaderSize();
if (hlen <= 0 || hs > hlen)
throwCantUnpack("header corrupted");
// check header_checksum
if (version > 9)
if (hbuf[hs - 1] != get_packheader_checksum(hbuf, hs - 1))
throwCantUnpack("header corrupted");
return true;
}
/*
vi:ts=4:et:nowrap
*/