/* packhead.cpp -- This file is part of the UPX executable compressor. Copyright (C) 1996-2007 Markus Franz Xaver Johannes Oberhumer Copyright (C) 1996-2007 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 "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. **************************************************************************/ PackHeader::PackHeader() : version(-1), format(-1) { } /************************************************************************* // 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; } /************************************************************************* // **************************************************************************/ int PackHeader::getPackHeaderSize() const { if (format < 0 || version < 0) throwInternalError("getPackHeaderSize"); 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 < 20) throwCantUnpack("unknown header version"); return n; } /************************************************************************* // see stub/header.ash **************************************************************************/ void PackHeader::putPackHeader(upx_bytep p) { assert(get_le32(p) == UPX_MAGIC_LE32); if (get_le32(p+4) != UPX_MAGIC2_LE32) { //fprintf(stderr, "MAGIC2_LE32: %x %x\n", get_le32(p+4), UPX_MAGIC2_LE32); throwBadLoader(); } int size = 0; int old_chksum = 0; // the new variable length header if (format < 128) { if (format == UPX_F_DOS_COM || format == UPX_F_DOS_SYS) { size = 22; old_chksum = get_packheader_checksum(p, size - 1); set_le16(p+16,u_len); set_le16(p+18,c_len); p[20] = (unsigned char) filter; } else if (format == UPX_F_DOS_EXE) { size = 27; old_chksum = get_packheader_checksum(p, size - 1); set_le24(p+16,u_len); set_le24(p+19,c_len); set_le24(p+22,u_file_size); p[25] = (unsigned char) filter; } else if (format == UPX_F_DOS_EXEH) { throwInternalError("invalid format"); } else { size = 32; old_chksum = get_packheader_checksum(p, size - 1); set_le32(p+16,u_len); set_le32(p+20,c_len); set_le32(p+24,u_file_size); p[28] = (unsigned char) filter; p[29] = (unsigned char) filter_cto; assert(n_mru == 0 || (n_mru >= 2 && n_mru <= 256)); p[30] = (unsigned char) (n_mru ? n_mru - 1 : 0); } set_le32(p+8,u_adler); set_le32(p+12,c_adler); } else { size = 32; old_chksum = get_packheader_checksum(p, size - 1); set_be32(p+8,u_len); set_be32(p+12,c_len); set_be32(p+16,u_adler); set_be32(p+20,c_adler); set_be32(p+24,u_file_size); p[28] = (unsigned char) filter; p[29] = (unsigned char) filter_cto; assert(n_mru == 0 || (n_mru >= 2 && n_mru <= 256)); p[30] = (unsigned char) (n_mru ? n_mru - 1 : 0); } p[4] = (unsigned char) version; p[5] = (unsigned char) format; p[6] = (unsigned char) method; p[7] = (unsigned char) level; // header_checksum assert(size == getPackHeaderSize()); // check old header_checksum if (p[size - 1] != 0) { if (p[size - 1] != old_chksum) { //printf("old_checksum: %d %d\n", p[size - 1], old_chksum); throwBadLoader(); } } // store new header_checksum p[size - 1] = get_packheader_checksum(p, size - 1); } /************************************************************************* // **************************************************************************/ bool PackHeader::fillPackHeader(const upx_bytep buf, int blen) { int boff = find_le32(buf, blen, UPX_MAGIC_LE32); if (boff < 0) return false; if (boff + 8 <= 0 || boff + 8 > blen) throwCantUnpack("header corrupted 1"); const upx_bytep p = buf + boff; version = p[4]; format = p[5]; method = p[6]; level = p[7]; filter_cto = 0; const int size = getPackHeaderSize(); if (boff + size <= 0 || boff + size > blen) throwCantUnpack("header corrupted 2"); // // decode the new variable length header // int off_filter = 0; if (format < 128) { u_adler = get_le32(p+8); c_adler = get_le32(p+12); if (format == UPX_F_DOS_COM || format == UPX_F_DOS_SYS) { u_len = get_le16(p+16); c_len = get_le16(p+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(p+16); c_len = get_le24(p+19); u_file_size = get_le24(p+22); off_filter = 25; } else { u_len = get_le32(p+16); c_len = get_le32(p+20); u_file_size = get_le32(p+24); off_filter = 28; filter_cto = p[29]; n_mru = p[30] ? 1 + p[30] : 0; } } else { u_len = get_be32(p+8); c_len = get_be32(p+12); u_adler = get_be32(p+16); c_adler = get_be32(p+20); u_file_size = get_be32(p+24); off_filter = 28; filter_cto = p[29]; n_mru = p[30] ? 1 + p[30] : 0; } if (version >= 10) filter = p[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; // // now some checks // if (version == 0xff) throwCantUnpack("cannot unpack UPX ;-)"); // check header_checksum if (version > 9) if (p[size - 1] != get_packheader_checksum(p, size - 1)) throwCantUnpack("header corrupted 3"); // // success // this->buf_offset = boff; return true; } /* vi:ts=4:et:nowrap */