From 777d4f5279428d2aefebf530d5377232779f5226 Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Fri, 11 Aug 2023 03:57:20 +0200 Subject: [PATCH] src: refactoring: create packhead.h and move some methods --- src/p_lx_elf.cpp | 20 ++++----- src/p_unix.cpp | 4 +- src/packer.cpp | 94 ++---------------------------------------- src/packer.h | 69 +------------------------------ src/packer_c.cpp | 2 +- src/packhead.cpp | 103 ++++++++++++++++++++++++++++++++++++++++++++++- src/packhead.h | 99 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 217 insertions(+), 174 deletions(-) create mode 100644 src/packhead.h diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index 865034a5..64d379b6 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -1493,7 +1493,7 @@ PackLinuxElf32::buildLinuxLoader( h.sz_cpr = mb_cprLoader.getSize(); // max that upx_compress may use { int r = upx_compress(uncLoader, sz_unc, sizeof(h) + cprLoader, &sz_cpr, - nullptr, forced_method(method), 10, nullptr, nullptr ); + nullptr, ph_forced_method(method), 10, nullptr, nullptr ); h.sz_cpr = sz_cpr; // actual length used if (r != UPX_E_OK || h.sz_cpr >= h.sz_unc) throwInternalError("loader compression failed"); @@ -1514,7 +1514,7 @@ PackLinuxElf32::buildLinuxLoader( } else { addStubEntrySections(ft, (methods_used ? methods_used - : (1u << forced_method(ph.method))) ); + : (1u << ph_forced_method(ph.method))) ); if (!xct_off) { defineSymbols(ft); } @@ -1599,7 +1599,7 @@ PackLinuxElf64::buildLinuxLoader( h.sz_cpr = mb_cprLoader.getSize(); // max that upx_compress may use { int r = upx_compress(uncLoader, sz_unc, sizeof(h) + cprLoader, &sz_cpr, - nullptr, forced_method(method), 10, nullptr, nullptr ); + nullptr, ph_forced_method(method), 10, nullptr, nullptr ); h.sz_cpr = sz_cpr; // actual length used if (r != UPX_E_OK || h.sz_cpr >= h.sz_unc) throwInternalError("loader compression failed"); @@ -1620,7 +1620,7 @@ PackLinuxElf64::buildLinuxLoader( } else { addStubEntrySections(ft, (methods_used ? methods_used - : (1u << forced_method(ph.method))) ); + : (1u << ph_forced_method(ph.method))) ); if (!xct_off) { defineSymbols(ft); } @@ -3900,7 +3900,7 @@ void PackLinuxElf32::pack1(OutputFile * /*fo*/, Filter &ft) fi->readx(ibuf, filesz); ft = orig_ft; ph = orig_ph; - ph.method = force_method(methods[k]); + ph.method = ph_force_method(methods[k]); ph.u_len = filesz; compressWithFilters(&ft, OVERHEAD, NULL_cconf, 10, true); sz_this += ph.c_len; @@ -3913,7 +3913,7 @@ void PackLinuxElf32::pack1(OutputFile * /*fo*/, Filter &ft) fi->readx(ibuf, sz_tail); ft = orig_ft; ph = orig_ph; - ph.method = force_method(methods[k]); + ph.method = ph_force_method(methods[k]); ph.u_len = sz_tail; compressWithFilters(&ft, OVERHEAD, NULL_cconf, 10, true); sz_this += ph.c_len; @@ -3926,7 +3926,7 @@ void PackLinuxElf32::pack1(OutputFile * /*fo*/, Filter &ft) } ft = orig_ft; ph = orig_ph; - ph.method = force_method(method_best); + ph.method = ph_force_method(method_best); } note_size = 0; @@ -4782,7 +4782,7 @@ void PackLinuxElf64::pack1(OutputFile * /*fo*/, Filter &ft) fi->readx(ibuf, filesz); ft = orig_ft; ph = orig_ph; - ph.method = force_method(methods[k]); + ph.method = ph_force_method(methods[k]); ph.u_len = filesz; compressWithFilters(&ft, OVERHEAD, NULL_cconf, 10, true); sz_this += ph.c_len; @@ -4795,7 +4795,7 @@ void PackLinuxElf64::pack1(OutputFile * /*fo*/, Filter &ft) fi->readx(ibuf, sz_tail); ft = orig_ft; ph = orig_ph; - ph.method = force_method(methods[k]); + ph.method = ph_force_method(methods[k]); ph.u_len = sz_tail; compressWithFilters(&ft, OVERHEAD, NULL_cconf, 10, true); sz_this += ph.c_len; @@ -4808,7 +4808,7 @@ void PackLinuxElf64::pack1(OutputFile * /*fo*/, Filter &ft) } ft = orig_ft; ph = orig_ph; - ph.method = force_method(method_best); + ph.method = ph_force_method(method_best); } note_size = 0; diff --git a/src/p_unix.cpp b/src/p_unix.cpp index b696e834..0ad711dd 100644 --- a/src/p_unix.cpp +++ b/src/p_unix.cpp @@ -406,7 +406,7 @@ void PackUnix::packExtent( MemBuffer hdr_obuf; hdr_obuf.allocForCompression(hdr_u_len); int r = upx_compress(hdr_ibuf, hdr_u_len, hdr_obuf, &hdr_c_len, nullptr, - forced_method(ph.method), 10, nullptr, nullptr); + ph_forced_method(ph.method), 10, nullptr, nullptr); if (r != UPX_E_OK) throwInternalError("header compression failed"); if (hdr_c_len >= hdr_u_len) @@ -419,7 +419,7 @@ void PackUnix::packExtent( memset(&tmp, 0, sizeof(tmp)); set_te32(&tmp.sz_unc, hdr_u_len); set_te32(&tmp.sz_cpr, hdr_c_len); - tmp.b_method = (unsigned char) forced_method(ph.method); + tmp.b_method = (unsigned char) ph_forced_method(ph.method); tmp.b_extra = b_extra; fo->write(&tmp, sizeof(tmp)); total_out += sizeof(tmp); diff --git a/src/packer.cpp b/src/packer.cpp index b9cb9e1a..31f30fee 100644 --- a/src/packer.cpp +++ b/src/packer.cpp @@ -149,34 +149,6 @@ bool Packer::testUnpackFormat(int format) const { return canUnpackFormat(format); } -bool ph_skipVerify(const PackHeader &ph) noexcept { - if (M_IS_DEFLATE(ph.method)) - return false; - if (M_IS_LZMA(ph.method)) - return false; - if (ph.level > 1) - return false; - return true; -} - -int force_method(int method) noexcept // mark as forced -{ - return (0x80ul << 24) | method; -} - -int is_forced_method(int method) noexcept // predicate -{ - return -0x80 == (method >> 24); -} - -int forced_method(int method) noexcept // extract the forced method -{ - if (is_forced_method(method)) - method &= ~(0x80ul << 24); - assert_noexcept(method > 0); - return method; -} - /************************************************************************* // compress - wrap call to low-level upx_compress() **************************************************************************/ @@ -203,7 +175,7 @@ bool Packer::compress(SPAN_P(byte) i_ptr, unsigned i_len, SPAN_P(byte) o_ptr, if (cconf_parm) cconf = *cconf_parm; // cconf options - int method = forced_method(ph.method); + int method = ph_forced_method(ph.method); if (M_IS_NRV2B(method) || M_IS_NRV2D(method) || M_IS_NRV2E(method)) { if (opt->crp.crp_ucl.c_flags != -1) cconf.conf_ucl.c_flags = opt->crp.crp_ucl.c_flags; @@ -332,39 +304,6 @@ bool Packer::checkFinalCompressionRatio(const OutputFile *fo) const { // decompress **************************************************************************/ -void ph_decompress(PackHeader &ph, SPAN_P(const byte) in, SPAN_P(byte) out, bool verify_checksum, - Filter *ft) { - unsigned adler; - - // verify checksum of compressed data - if (verify_checksum) { - adler = upx_adler32(raw_bytes(in, ph.c_len), ph.c_len, ph.saved_c_adler); - if (adler != ph.c_adler) - throwChecksumError(); - } - - // decompress - if (ph.u_len < ph.c_len) { - throwCantUnpack("header corrupted"); - } - unsigned new_len = ph.u_len; - int r = upx_decompress(raw_bytes(in, ph.c_len), ph.c_len, raw_bytes(out, ph.u_len), &new_len, - forced_method(ph.method), &ph.compress_result); - if (r == UPX_E_OUT_OF_MEMORY) - throwOutOfMemoryException(); - if (r != UPX_E_OK || new_len != ph.u_len) - throwCompressedDataViolation(); - - // verify checksum of decompressed data - if (verify_checksum) { - if (ft) - ft->unfilter(raw_bytes(out, ph.u_len), ph.u_len); - adler = upx_adler32(raw_bytes(out, ph.u_len), ph.u_len, ph.saved_u_adler); - if (adler != ph.u_adler) - throwChecksumError(); - } -} - void Packer::decompress(SPAN_P(const byte) in, SPAN_P(byte) out, bool verify_checksum, Filter *ft) { ph_decompress(ph, in, out, verify_checksum, ft); } @@ -373,33 +312,6 @@ void Packer::decompress(SPAN_P(const byte) in, SPAN_P(byte) out, bool verify_che // overlapping decompression **************************************************************************/ -static bool ph_testOverlappingDecompression(const PackHeader &ph, const byte *buf, const byte *tbuf, - unsigned overlap_overhead) { - if (ph.c_len >= ph.u_len) - return false; - - assert((int) overlap_overhead >= 0); - assert((int) (ph.u_len + overlap_overhead) >= 0); - - // Because upx_test_overlap() does not use the asm_fast decompressor - // we must account for extra 3 bytes that asm_fast does use, - // or else we may fail at runtime decompression. - unsigned extra = 0; - if (M_IS_NRV2B(ph.method) || M_IS_NRV2D(ph.method) || M_IS_NRV2E(ph.method)) - extra = 3; - if (overlap_overhead <= 4 + extra) // don't waste time here - return false; - overlap_overhead -= extra; - - unsigned src_off = ph.u_len + overlap_overhead - ph.c_len; - unsigned new_len = ph.u_len; - int r = upx_test_overlap(buf - src_off, tbuf, src_off, ph.c_len, &new_len, - forced_method(ph.method), &ph.compress_result); - if (r == UPX_E_OUT_OF_MEMORY) - throwOutOfMemoryException(); - return (r == UPX_E_OK && new_len == ph.u_len); -} - bool Packer::testOverlappingDecompression(const byte *buf, const byte *tbuf, unsigned overlap_overhead) const { return ph_testOverlappingDecompression(ph, buf, tbuf, overlap_overhead); @@ -1020,7 +932,7 @@ void Packer::relocateLoader() { int Packer::prepareMethods(int *methods, int ph_method, const int *all_methods) const { int nmethods = 0; if (!opt->all_methods || all_methods == nullptr || (-0x80 == (ph_method >> 24))) { - methods[nmethods++] = forced_method(ph_method); + methods[nmethods++] = ph_forced_method(ph_method); return nmethods; } for (int mm = 0; all_methods[mm] != M_END; ++mm) { @@ -1166,7 +1078,7 @@ void Packer::compressWithFilters(byte *i_ptr, #endif // update total_passes; previous (ui_total_passes > 0) means incremental - if (!is_forced_method(ph.method)) { + if (!ph_is_forced_method(ph.method)) { if (uip->ui_total_passes > 0) uip->ui_total_passes -= 1; if (filter_strategy < 0) diff --git a/src/packer.h b/src/packer.h index 621638df..d5464018 100644 --- a/src/packer.h +++ b/src/packer.h @@ -27,77 +27,14 @@ #pragma once +#include "packhead.h" #include "util/membuffer.h" class InputFile; class OutputFile; -class PackerBase; -class Packer; class UiPacker; class Filter; -/************************************************************************* -// PackHeader -// also see stub/src/include/header.S -**************************************************************************/ - -class PackHeader final { - friend class PackerBase; - friend class Packer; - - // these are strictly private to friends PackerBase and Packer - explicit PackHeader() noexcept; - void putPackHeader(SPAN_S(byte) p); - bool decodePackHeaderFromBuf(SPAN_S(const byte) b, int blen); - -public: - int getPackHeaderSize() const; - -public: - // fields stored in compressed file - // enum { magic = UPX_MAGIC_LE32 }; - int version; - int format; // executable format - int method; // compresison method - int level; // compresison level 1..10 - unsigned u_len; - unsigned c_len; - unsigned u_adler; - unsigned c_adler; - unsigned u_file_size; - int filter; - int filter_cto; - int n_mru; // FIXME: rename to filter_misc - int header_checksum; - - // support fields for verifying decompression - unsigned saved_u_adler; - unsigned saved_c_adler; - - // info fields set by decodePackHeaderFromBuf() - unsigned buf_offset; - - // info fields set by Packer::compress() - upx_compress_result_t compress_result; - // unsigned min_offset_found; - unsigned max_offset_found; - // unsigned min_match_found; - unsigned max_match_found; - // unsigned min_run_found; - unsigned max_run_found; - unsigned first_offset_found; - // unsigned same_match_offsets_found; - - // info fields set by Packer::compressWithFilters() - unsigned overlap_overhead; -}; - -bool ph_skipVerify(const PackHeader &ph) noexcept; -void ph_decompress(PackHeader &ph, SPAN_P(const byte) in, SPAN_P(byte) out, bool verify_checksum, - Filter *ft); -bool ph_testOverlappingDecompression(const PackHeader &ph, SPAN_P(const byte) buf, - unsigned overlap_overhead); - /************************************************************************* // purely abstract minimal base class for all packers // @@ -388,8 +325,4 @@ private: Packer &operator=(Packer &&) noexcept DELETED_FUNCTION; }; -int force_method(int method) noexcept; // (0x80ul<<24)|method -int forced_method(int method) noexcept; // (0x80ul<<24)|method ==> method -int is_forced_method(int method) noexcept; // predicate - /* vim:set ts=4 sw=4 et: */ diff --git a/src/packer_c.cpp b/src/packer_c.cpp index a905cb9c..fd7dfd48 100644 --- a/src/packer_c.cpp +++ b/src/packer_c.cpp @@ -210,7 +210,7 @@ const char *Packer::getDecompressorSections() const static const char lzma_elf_fast[] = "LZMA_ELF00,LZMA_DEC20,LZMA_DEC30"; - unsigned const method = forced_method(ph.method); + unsigned const method = ph_forced_method(ph.method); if (method == M_NRV2B_LE32) return opt->small ? nrv2b_le32_small : nrv2b_le32_fast; if (method == M_NRV2D_LE32) diff --git a/src/packhead.cpp b/src/packhead.cpp index 9148257c..5d05be10 100644 --- a/src/packhead.cpp +++ b/src/packhead.cpp @@ -26,7 +26,8 @@ */ #include "conf.h" -#include "packer.h" +#include "packhead.h" +#include "filter.h" // for ft->unfilter() /************************************************************************* // PackHeader @@ -92,7 +93,7 @@ int PackHeader::getPackHeaderSize() const { // see stub/header.ash **************************************************************************/ -void PackHeader::putPackHeader(SPAN_S(byte) p) { +void PackHeader::putPackHeader(SPAN_S(byte) p) const { // NOTE: It is the caller's responsbility to ensure the buffer p has // sufficient space for the header. assert(get_le32(p) == UPX_MAGIC_LE32); @@ -277,4 +278,102 @@ bool PackHeader::decodePackHeaderFromBuf(SPAN_S(const byte) buf, int blen) { return true; } +/************************************************************************* +// ph method util +**************************************************************************/ + +bool ph_is_forced_method(int method) noexcept // predicate +{ + return (method >> 24) == -0x80; +} + +int ph_force_method(int method) noexcept // mark as forced +{ + return method | (0x80u << 24); +} + +int ph_forced_method(int method) noexcept // extract the forced method +{ + if (ph_is_forced_method(method)) + method &= ~(0x80u << 24); + assert_noexcept(method > 0); + return method; +} + +bool ph_skipVerify(const PackHeader &ph) noexcept { + if (M_IS_DEFLATE(ph.method)) + return false; + if (M_IS_LZMA(ph.method)) + return false; + if (ph.level > 1) + return false; + return true; +} + +/************************************************************************* +// ph decompress util +**************************************************************************/ + +void ph_decompress(PackHeader &ph, SPAN_P(const byte) in, SPAN_P(byte) out, bool verify_checksum, + Filter *ft) { + // verify checksum of compressed data + if (verify_checksum) { + unsigned adler = upx_adler32(raw_bytes(in, ph.c_len), ph.c_len, ph.saved_c_adler); + if (adler != ph.c_adler) + throwChecksumError(); + } + + // decompress + if (ph.u_len < ph.c_len) + throwCantUnpack("header corrupted"); + unsigned new_len = ph.u_len; + int r = upx_decompress(raw_bytes(in, ph.c_len), ph.c_len, raw_bytes(out, ph.u_len), &new_len, + ph_forced_method(ph.method), &ph.compress_result); + if (r == UPX_E_OUT_OF_MEMORY) + throwOutOfMemoryException(); + if (r != UPX_E_OK || new_len != ph.u_len) + throwCompressedDataViolation(); + + // verify checksum of decompressed data + if (verify_checksum) { + if (ft) + ft->unfilter(out, ph.u_len); + unsigned adler = upx_adler32(raw_bytes(out, ph.u_len), ph.u_len, ph.saved_u_adler); + if (adler != ph.u_adler) + throwChecksumError(); + } +} + +/************************************************************************* +// ph overlapping decompression util +**************************************************************************/ + +bool ph_testOverlappingDecompression(const PackHeader &ph, const byte *buf, const byte *tbuf, + unsigned overlap_overhead) { + if (ph.c_len >= ph.u_len) + return false; + + assert((int) overlap_overhead >= 0); + assert((int) (ph.u_len + overlap_overhead) >= 0); + const int method = ph_forced_method(ph.method); + + // Because upx_test_overlap() does not use the asm_fast decompressor + // we must account for extra 3 bytes that asm_fast does use, + // or else we may fail at runtime decompression. + unsigned extra = 0; + if (M_IS_NRV2B(method) || M_IS_NRV2D(method) || M_IS_NRV2E(method)) + extra = 3; + if (overlap_overhead <= 4 + extra) // don't waste time here + return false; + overlap_overhead -= extra; + + unsigned src_off = ph.u_len + overlap_overhead - ph.c_len; + unsigned new_len = ph.u_len; + int r = upx_test_overlap(buf - src_off, tbuf, src_off, ph.c_len, &new_len, method, + &ph.compress_result); + if (r == UPX_E_OUT_OF_MEMORY) + throwOutOfMemoryException(); + return (r == UPX_E_OK && new_len == ph.u_len); +} + /* vim:set ts=4 sw=4 et: */ diff --git a/src/packhead.h b/src/packhead.h new file mode 100644 index 00000000..1b213f24 --- /dev/null +++ b/src/packhead.h @@ -0,0 +1,99 @@ +/* packhead.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2023 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2023 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 + + */ + +#pragma once + +class Filter; + +/************************************************************************* +// PackHeader +// also see stub/src/include/header.S +**************************************************************************/ + +class PackHeader final { +public: + explicit PackHeader() noexcept; + ~PackHeader() noexcept = default; + + void putPackHeader(SPAN_S(byte) p) const; + bool decodePackHeaderFromBuf(SPAN_S(const byte) b, int blen); + + int getPackHeaderSize() const; + + // fields stored in compressed file => see header.S + // enum { magic = UPX_MAGIC_LE32 }; + int version; + int format; // executable format + int method; // compresison method + int level; // compresison level 1..10 + unsigned u_len; + unsigned c_len; + unsigned u_adler; + unsigned c_adler; + unsigned u_file_size; + int filter; + int filter_cto; + int n_mru; // FIXME: rename to filter_misc + int header_checksum; + + // support fields for verifying decompression + unsigned saved_u_adler; + unsigned saved_c_adler; + + // info fields set by decodePackHeaderFromBuf() + unsigned buf_offset; + + // info fields set by Packer::compress() + upx_compress_result_t compress_result; + // unsigned min_offset_found; + unsigned max_offset_found; + // unsigned min_match_found; + unsigned max_match_found; + // unsigned min_run_found; + unsigned max_run_found; + unsigned first_offset_found; + // unsigned same_match_offsets_found; + + // info fields set by Packer::compressWithFilters() + unsigned overlap_overhead; +}; + +/************************************************************************* +// ph default util functions +**************************************************************************/ + +bool ph_is_forced_method(int method) noexcept; // predicate +int ph_force_method(int method) noexcept; // (0x80ul<<24)|method +int ph_forced_method(int method) noexcept; // (0x80ul<<24)|method ==> method + +bool ph_skipVerify(const PackHeader &ph) noexcept; + +void ph_decompress(PackHeader &ph, SPAN_P(const byte) in, SPAN_P(byte) out, bool verify_checksum, + Filter *ft); + +bool ph_testOverlappingDecompression(const PackHeader &ph, const byte *buf, const byte *tbuf, + unsigned overlap_overhead);