diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7034f44..4a768e2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -268,8 +268,8 @@ jobs: - { name: amd64-win64-vs2022, vsversion: 2022, os: windows-2022, C: msvc-14.3-x64, arch: amd64 } - { name: arm64-win64-vs2019, vsversion: 2019, os: windows-2019, C: msvc-14.2-arm64, arch: amd64_arm64 } - { name: arm64-win64-vs2022, vsversion: 2022, os: windows-2022, C: msvc-14.3-arm64, arch: amd64_arm64 } - - { name: arm64ec-win64-vs2022, vsversion: 2022, os: windows-2022, C: msvc-14.3-arm64ec, arch: amd64_arm64, cl_extra_flags: -arm64EC, link_machine_flags: '/machine:arm64ec' } - ####- { name: arm64x-win64-vs2022, vsversion: 2022, os: windows-2022, C: msvc-14.3-arm64x, arch: amd64_arm64, cl_extra_flags: -arm64EC, link_machine_flags: '/machine:arm64x' } + - { name: arm64ec-win64-vs2022, vsversion: 2022, os: windows-2022, C: msvc-14.3-arm64ec, arch: amd64_arm64, cl_machine_flags: -arm64EC, link_machine_flags: '/machine:arm64ec' } + ####- { name: arm64x-win64-vs2022, vsversion: 2022, os: windows-2022, C: msvc-14.3-arm64x, arch: amd64_arm64, cl_machine_flags: -arm64EC, link_machine_flags: '/machine:arm64x' } - { name: i386-win32-vs2019, vsversion: 2019, os: windows-2019, C: msvc-14.2-x86, arch: amd64_x86 } - { name: i386-win32-vs2022, vsversion: 2022, os: windows-2022, C: msvc-14.3-x86, arch: amd64_x86 } steps: @@ -293,10 +293,10 @@ jobs: run: | @REM setup directories where cl & where link - set RUN_CL=cl -MT ${{ matrix.cl_extra_flags }} + set RUN_CL=cl ${{ matrix.cl_machine_flags }} -MT set RUN_LIB=link -lib ${{ matrix.link_machine_flags }} - set BDIR=%H%\build\%C%\%B% set DEFS=-D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS + set BDIR=%H%\build\%C%\%B% git rev-parse --short=12 HEAD > %BDIR%\upx\.GITREV.txt @REM ===== build UCL ===== cd %BDIR%\ucl @@ -377,8 +377,8 @@ jobs: - { zig_target: x86_64-macos.13-none } - { zig_target: x86_64-windows-gnu } env: - # 2023-02-12 - ZIG_DIST_VERSION: 0.11.0-dev.1605+abc9530a8 + # 2023-02-19 + ZIG_DIST_VERSION: 0.11.0-dev.1650+5e7b09ce9 # for zig-cc wrapper scripts (see below): ZIG_CPPFLAGS: -DUPX_DOCTEST_CONFIG_MULTITHREADING ZIG_FLAGS: ${{ matrix.zig_flags }} diff --git a/CMakeLists.txt b/CMakeLists.txt index f62f16b8..3102402b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,9 @@ list(SORT upx_SOURCES) add_executable(upx ${upx_SOURCES}) set_property(TARGET upx PROPERTY CXX_STANDARD 17) target_link_libraries(upx upx_vendor_ucl upx_vendor_zlib) +if(NOT UPX_CONFIG_DISABLE_ZSTD) + target_link_libraries(upx upx_vendor_zstd) +endif() #*********************************************************************** # compilation flags @@ -184,8 +187,10 @@ function(upx_compile_target_debug_with_O2 t) endfunction() function(upx_sanitize_target t) - if(NOT UPX_CONFIG_DISABLE_SANITIZE AND NOT MSVC) - if(CMAKE_C_PLATFORM_ID MATCHES "^MinGW" OR MINGW OR CYGWIN) + if(NOT UPX_CONFIG_DISABLE_SANITIZE) + if(MSVC) + # msvc uses -GS (similar to -fstack-protector) by default + elseif(CMAKE_C_PLATFORM_ID MATCHES "^MinGW" OR MINGW OR CYGWIN) # avoid link errors with current MinGW-w64 versions # see https://www.mingw-w64.org/contribute/#sanitizers-asan-tsan-usan else() @@ -246,6 +251,9 @@ endif() if(UPX_CONFIG_DISABLE_WERROR) target_compile_definitions(${t} PRIVATE UPX_CONFIG_DISABLE_WERROR=1) endif() +if(NOT UPX_CONFIG_DISABLE_ZSTD) + target_compile_definitions(${t} PRIVATE WITH_ZSTD=1) +endif() #upx_compile_target_debug_with_O2(${t}) upx_sanitize_target(${t}) if(MSVC) @@ -253,10 +261,6 @@ if(MSVC) else() target_compile_options(${t} PRIVATE ${warn_Wall} ${warn_Werror}) endif() -if(NOT UPX_CONFIG_DISABLE_ZSTD) - target_compile_definitions(${t} PRIVATE WITH_ZSTD=1) - target_link_libraries(upx upx_vendor_zstd) -endif() #*********************************************************************** # "ctest" diff --git a/Makefile b/Makefile index 4b36a523..b1e0f866 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ CMAKE = cmake UPX_CMAKE_BUILD_FLAGS += --parallel ifneq ($(VERBOSE),) UPX_CMAKE_BUILD_FLAGS += --verbose + UPX_CMAKE_CONFIG_FLAGS += -DCMAKE_VERBOSE_MAKEFILE=ON endif # enable this if you prefer Ninja for the actual builds: #UPX_CMAKE_CONFIG_FLAGS += -G Ninja diff --git a/misc/rebuild-stubs-with-podman/Dockerfile b/misc/rebuild-stubs-with-podman/Dockerfile index 068533c3..e7498d9e 100644 --- a/misc/rebuild-stubs-with-podman/Dockerfile +++ b/misc/rebuild-stubs-with-podman/Dockerfile @@ -15,7 +15,7 @@ RUN dpkg --add-architecture i386 \ # the full UPX binary inside the container via CMake: 7zip bfs bzip2 cabextract chrpath cmake cpio curl elfutils fd-find file fzf g++ gdb gojq \ ht htop hyperfine jq libzstd-dev lsb-release lz4 lzip lzop moreutils ninja-build \ - p7zip patch patchelf parallel pax-utils paxctl re2c ripgrep rsync \ + p7zip parallel patch patchelf pax-utils paxctl re2c ripgrep rsync \ screen universal-ctags unzip vim zip zlib1g-dev zsh zstd \ # extra packages for compiling with "gcc -m32" and and "gcc -mx32": g++-multilib gcc-multilib \ diff --git a/src/conf.h b/src/conf.h index ec46e8a1..b5578db8 100644 --- a/src/conf.h +++ b/src/conf.h @@ -131,7 +131,7 @@ ACC_COMPILE_TIME_ASSERT_HEADER((char)(-1) == 255) // -funsigned-char #define upx_std_once_flag upx_std_atomic(size_t) template inline void upx_std_call_once(upx_std_once_flag &flag, NoexceptCallable &&f) { - if (!flag) { flag = 1; f(); } + if (__acc_unlikely(!flag)) { flag = 1; f(); } } #else #include diff --git a/src/except.cpp b/src/except.cpp index a6681630..0a2f3332 100644 --- a/src/except.cpp +++ b/src/except.cpp @@ -143,6 +143,30 @@ void throwEOFException(const char *msg, int e) { // **************************************************************************/ +template <> +void throwCantPack(const char *format, ...) { + char msg[1024]; + va_list ap; + va_start(ap, format); + (void) upx_safe_vsnprintf(msg, sizeof(msg), format, ap); + va_end(ap); + throwCantPack(msg); +} + +template <> +void throwCantUnpack(const char *format, ...) { + char msg[1024]; + va_list ap; + va_start(ap, format); + (void) upx_safe_vsnprintf(msg, sizeof(msg), format, ap); + va_end(ap); + throwCantUnpack(msg); +} + +/************************************************************************* +// +**************************************************************************/ + const char *prettyName(const char *n) noexcept { if (n == nullptr) return "(null)"; diff --git a/src/except.h b/src/except.h index 0e633d52..5fc8cb2a 100644 --- a/src/except.h +++ b/src/except.h @@ -26,8 +26,6 @@ */ #pragma once -#ifndef UPX_EXCEPT_H__ -#define UPX_EXCEPT_H__ 1 const char *prettyName(const char *n) noexcept; @@ -213,8 +211,15 @@ NORET void throwOutOfMemoryException(const char *msg = nullptr); NORET void throwIOException(const char *msg = nullptr, int e = 0); NORET void throwEOFException(const char *msg = nullptr, int e = 0); +template +void throwCantPack(const T *, ...) = delete; +template <> +NORET void throwCantPack(const char *format, ...) attribute_format(1, 2); +template +void throwCantUnpack(const T *, ...) = delete; +template <> +NORET void throwCantUnpack(const char *format, ...) attribute_format(1, 2); + #undef NORET -#endif /* already included */ - /* vim:set ts=4 sw=4 et: */ diff --git a/src/lefile.cpp b/src/lefile.cpp index 995d2505..5bf727c2 100644 --- a/src/lefile.cpp +++ b/src/lefile.cpp @@ -148,7 +148,7 @@ void LeFile::readImage() { soimage = pages * mps; mb_iimage.alloc(soimage); mb_iimage.clear(); - iimage = mb_iimage; + iimage = mb_iimage; // => now a SPAN_S unsigned ic, jc; for (ic = jc = 0; ic < pages; ic++) { diff --git a/src/p_exe.cpp b/src/p_exe.cpp index b5f4da76..b005e44e 100644 --- a/src/p_exe.cpp +++ b/src/p_exe.cpp @@ -241,7 +241,7 @@ bool PackExe::canPack() { if (!readFileHeader()) return false; if (file_size < 1024) - throwCantPack("file is too small"); + throwCantPack("file is too small for dos/exe"); fi->seek(0x3c, SEEK_SET); LE32 offs; fi->readx(&offs, sizeof(offs)); @@ -249,7 +249,7 @@ bool PackExe::canPack() { if (opt->dos_exe.force_stub) opt->overlay = opt->COPY_OVERLAY; else - throwCantPack("can't pack new-exe"); + throwCantPack("dos/exe: can't pack new-exe"); } return true; } diff --git a/src/p_tmt.cpp b/src/p_tmt.cpp index ee303478..675a0461 100644 --- a/src/p_tmt.cpp +++ b/src/p_tmt.cpp @@ -35,8 +35,6 @@ static const CLANG_FORMAT_DUMMY_STATEMENT #include "stub/i386-dos32.tmt.h" -#define EXTRA_INFO 4 // original entry point - /************************************************************************* // **************************************************************************/ @@ -100,7 +98,7 @@ int PackTmt::readFileHeader() { fi->seek(adam_offset, SEEK_SET); fi->readx(h, sizeof(h)); - if (memcmp(h, "MZ", 2) == 0) // dos exe + if (memcmp(h, "MZ", 2) == 0) // dos/exe { exe_offset = adam_offset; adam_offset += H(2) * 512 + H(1); @@ -135,14 +133,15 @@ int PackTmt::readFileHeader() { fi->seek(adam_offset, SEEK_SET); fi->readx(&ih, sizeof(ih)); - // FIXME: should add more checks for the values in 'ih' - unsigned const imagesize = ih.imagesize; - unsigned const entry = ih.entry; - unsigned const relocsize = ih.relocsize; - if (imagesize < sizeof(ih) || entry < sizeof(ih) || file_size_u <= imagesize || - file_size_u <= entry || file_size_u <= relocsize) { - printWarn(getName(), "bad header; imagesize=%#x entry=%#x relocsize=%#x", imagesize, - entry, relocsize); + + // TODO: could add more checks for the values in 'ih' + const unsigned imagesize = ih.imagesize; + const unsigned entry = ih.entry; + const unsigned rsize = ih.relocsize; + if (imagesize < sizeof(ih) || imagesize >= file_size_u || entry >= file_size_u || + rsize >= file_size_u) { + throwCantPack("%s: bad header: imagesize=%#x entry=%#x relocsize=%#x", getName(), imagesize, + entry, rsize); return 0; } @@ -168,44 +167,47 @@ void PackTmt::pack(OutputFile *fo) { const unsigned usize = ih.imagesize; const unsigned rsize = ih.relocsize; + const unsigned relocnum = rsize / 4; ibuf.alloc(usize + rsize + 128); obuf.allocForCompression(usize + rsize + 128); - MemBuffer mb_wrkmem; - mb_wrkmem.alloc(rsize + EXTRA_INFO + 4); // relocations + original entry point + relocsize - SPAN_S_VAR(upx_byte, wrkmem, mb_wrkmem); - fi->seek(adam_offset + sizeof(ih), SEEK_SET); fi->readx(ibuf, usize); - fi->readx(wrkmem + 4, rsize); - const unsigned overlay = file_size - fi->tell(); if (find_le32(ibuf, UPX_MIN(128u, usize), get_le32("UPX ")) >= 0) throwAlreadyPacked(); if (rsize == 0) throwCantPack("file is already compressed with another packer"); + MemBuffer mb_relocs(rsize); + SPAN_S_VAR(upx_byte, relocs, mb_relocs); + fi->readx(relocs, rsize); + + const unsigned overlay = file_size - fi->tell(); checkOverlay(overlay); - unsigned relocsize = 0; - // if (rsize) - { - for (unsigned ic = 4; ic <= rsize; ic += 4) - set_le32(wrkmem + ic, get_le32(wrkmem + ic) - 4); - relocsize = - optimizeReloc32(wrkmem + 4, rsize / 4, wrkmem, ibuf, file_size, true, &big_relocs); - } + for (unsigned ic = 0; ic < relocnum; ic++) + set_le32(relocs + ic * 4, get_le32(relocs + ic * 4) - 4); - wrkmem[relocsize++] = 0; - set_le32(wrkmem + relocsize, ih.entry); // save original entry point - relocsize += 4; - set_le32(wrkmem + relocsize, relocsize + 4); - relocsize += 4; - memcpy(raw_index_bytes(ibuf, usize, relocsize), wrkmem, relocsize); + MemBuffer mb_orelocs(4 * relocnum + 8192); // relocations + extra_info + SPAN_S_VAR(upx_byte, orelocs, mb_orelocs); + unsigned orelocsize = + optimizeReloc(relocnum, relocs, orelocs, ibuf, usize, 32, true, &big_relocs); + relocs.destroy(); // done + mb_relocs.dealloc(); // done + // extra_info + orelocs[orelocsize++] = 0; // why is this needed - historical oversight ??? + set_le32(orelocs + orelocsize, ih.entry); // save original entry point + orelocsize += 4; + set_le32(orelocs + orelocsize, orelocsize + 4); // save orelocsize + orelocsize += 4; + memcpy(raw_index_bytes(ibuf, usize, orelocsize), orelocs, orelocsize); + orelocs.destroy(); // done + mb_orelocs.dealloc(); // done // prepare packheader - ph.u_len = usize + relocsize; + ph.u_len = usize + orelocsize; // prepare filter Filter ft(ph.level); ft.buf_len = usize; @@ -247,7 +249,7 @@ void PackTmt::pack(OutputFile *fo) { fo->write(loader, e_len); fo->write(obuf, ph.c_len); fo->write(loader + lsize - d_len, d_len); // decompressor - char rel_entry[4]; + unsigned char rel_entry[4]; set_le32(rel_entry, 5 + s_point); fo->write(rel_entry, sizeof(rel_entry)); @@ -281,10 +283,10 @@ void PackTmt::unpack(OutputFile *fo) { // decompress decompress(ibuf, obuf); - // decode relocations - const unsigned osize = ph.u_len - get_le32(obuf + (ph.u_len - 4)); - SPAN_P_VAR(upx_byte, relocs, obuf + osize); - const unsigned origstart = get_le32(obuf + (ph.u_len - 8)); + // read extra_info + const unsigned orig_entry = mem_size(1, get_le32(obuf + ph.u_len - 8)); + const unsigned orelocsize = mem_size(1, get_le32(obuf + ph.u_len - 4)); + const unsigned osize = mem_size(1, ph.u_len - orelocsize); // unfilter if (ph.filter) { @@ -292,21 +294,23 @@ void PackTmt::unpack(OutputFile *fo) { ft.init(ph.filter, 0); ft.cto = (unsigned char) ph.filter_cto; if (ph.version < 11) - ft.cto = (unsigned char) (get_le32(obuf + (ph.u_len - 12)) >> 24); - ft.unfilter(obuf, ptr_udiff_bytes(relocs, obuf)); + ft.cto = (unsigned char) (get_le32(obuf + ph.u_len - 12) >> 24); + ft.unfilter(obuf, osize); } // decode relocations - MemBuffer mb_wrkmem; - const unsigned relocn = unoptimizeReloc32(relocs, obuf, mb_wrkmem, true); - SPAN_S_VAR(upx_byte, wrkmem, mb_wrkmem); - for (unsigned ic = 0; ic < relocn; ic++) - set_le32(wrkmem + ic * 4, get_le32(wrkmem + ic * 4) + 4); + SPAN_S_VAR(const upx_byte, orelocs, raw_index_bytes(obuf, osize, orelocsize), orelocsize); + SPAN_S_VAR(upx_byte, reloc_image, raw_index_bytes(obuf, 0, osize), osize); + MemBuffer mb_relocs; + const unsigned relocnum = unoptimizeReloc(orelocs, mb_relocs, reloc_image, osize, 32, true); + SPAN_S_VAR(upx_byte, relocs, mb_relocs); + for (unsigned ic = 0; ic < relocnum; ic++) + set_le32(relocs + ic * 4, get_le32(relocs + ic * 4) + 4); memcpy(&oh, &ih, sizeof(oh)); oh.imagesize = osize; - oh.entry = origstart; - oh.relocsize = relocn * 4; + oh.entry = orig_entry; + oh.relocsize = relocnum * 4; const unsigned overlay = file_size - adam_offset - ih.imagesize - ih.relocsize - sizeof(ih); checkOverlay(overlay); @@ -315,7 +319,7 @@ void PackTmt::unpack(OutputFile *fo) { if (fo) { fo->write(&oh, sizeof(oh)); fo->write(obuf, osize); - fo->write(raw_bytes(wrkmem, relocn * 4), relocn * 4); + fo->write(relocs, relocnum * 4); } // copy the overlay diff --git a/src/p_tmt.h b/src/p_tmt.h index 73988873..941f3a03 100644 --- a/src/p_tmt.h +++ b/src/p_tmt.h @@ -62,8 +62,8 @@ protected: virtual void buildLoader(const Filter *ft) override; virtual Linker *newLinker() const override; - unsigned adam_offset; - int big_relocs; + unsigned adam_offset = 0; + int big_relocs = 0; struct alignas(1) tmt_header_t { char _[16]; // signature,linkerversion,minversion,exesize,imagestart diff --git a/src/p_wcle.cpp b/src/p_wcle.cpp index db4450fa..1e0e160f 100644 --- a/src/p_wcle.cpp +++ b/src/p_wcle.cpp @@ -181,7 +181,7 @@ void PackWcle::encodeObjectTable() { OOT(0, base_address) = IOT(0, base_address); ic = IOT(objects - 1, my_base_address) + IOT(objects - 1, virtual_size); - jc = pages * mps + sofixups + 1024; + jc = mem_size(mps, pages, sofixups, 1024); if (ic < jc) ic = jc; @@ -247,14 +247,14 @@ void PackWcle::preprocessFixups() { throwCantPack("files without relocations are not supported"); } - MemBuffer rl_membuf(jc); + MemBuffer mb_relocs(jc); ByteArray(srf, counts[objects + 0] + 1); ByteArray(slf, counts[objects + 1] + 1); - SPAN_S_VAR(upx_byte, rl, rl_membuf); + SPAN_S_VAR(upx_byte, relocs, mb_relocs); SPAN_S_VAR(upx_byte, selector_fixups, srf_membuf); SPAN_S_VAR(upx_byte, selfrel_fixups, slf_membuf); - unsigned rc = 0; + unsigned relocnum = 0; upx_byte *fix = ifixups; for (ic = jc = 0; ic < pages; ic++) { @@ -295,7 +295,7 @@ void PackWcle::preprocessFixups() { } dputc('p', stdout); memcpy(iimage + jc + fixp2, fix + 5, (fix[1] & 0x10) ? 4 : 2); - set_le32(rl + 4 * rc++, jc + fixp2); + set_le32(relocs + 4 * relocnum++, jc + fixp2); set_le32(iimage + jc + fixp2, get_le32(iimage + jc + fixp2) + IOT(fix[4] - 1, my_base_address)); @@ -316,8 +316,8 @@ void PackWcle::preprocessFixups() { // work around a pmwunlite bug: remove duplicated fixups // FIXME: fix the other cases too - if (rc == 0 || get_le32(rl + 4 * rc - 4) != jc + fixp2) { - set_le32(rl + 4 * rc++, jc + fixp2); + if (relocnum == 0 || get_le32(relocs + 4 * relocnum - 4) != jc + fixp2) { + set_le32(relocs + 4 * relocnum++, jc + fixp2); set_le32(iimage + jc + fixp2, get_le32(iimage + jc + fixp2) + IOT(fix[4] - 1, my_base_address)); } @@ -348,11 +348,14 @@ void PackWcle::preprocessFixups() { } // resize ifixups if it's too small - if (sofixups < 1000) { + if (sofixups < 4 * relocnum + 8192) { delete[] ifixups; - ifixups = new upx_byte[1000]; + sofixups = 4 * relocnum + 8192; + ifixups = New(upx_byte, sofixups); } - fix = ifixups + optimizeReloc32(rl, rc, ifixups, iimage, file_size, 1, &big_relocs); + SPAN_S_VAR(upx_byte, orelocs, ifixups, sofixups); + fix = + ifixups + optimizeReloc(relocnum, relocs, orelocs, iimage, soimage, 32, true, &big_relocs); has_extra_code = ptr_udiff_bytes(selector_fixups, srf) != 0; // FIXME: this could be removed if has_extra_code = false // but then we'll need a flag @@ -380,7 +383,7 @@ void PackWcle::encodeImage(Filter *ft) { ifixups = nullptr; mb_oimage.allocForCompression(isize, RESERVED + 512); - oimage = mb_oimage; + oimage = mb_oimage; // => now a SPAN_S // prepare packheader ph.u_len = isize; // prepare filter [already done] @@ -424,7 +427,7 @@ void PackWcle::pack(OutputFile *fo) { preprocessFixups(); - const unsigned text_size = IOT(ih.init_cs_object - 1, npages) * mps; + const unsigned text_size = mem_size(mps, IOT(ih.init_cs_object - 1, npages)); const unsigned text_vaddr = IOT(ih.init_cs_object - 1, my_base_address); // attach some useful data at the end of preprocessed fixups @@ -437,7 +440,8 @@ void PackWcle::pack(OutputFile *fo) { set_le32(ifixups + sofixups, ih.init_esp_offset + IOT(ih.init_ss_object - 1, my_base_address)); // old stack pointer set_le32(ifixups + sofixups + 4, ih.init_eip_offset + text_vaddr); // real entry point - set_le32(ifixups + sofixups + 8, mps * pages); // virtual address of unpacked relocations + set_le32(ifixups + sofixups + 8, + mem_size(mps, pages)); // virtual address of unpacked relocations ifixups[sofixups + 12] = (unsigned char) (unsigned) objects; sofixups += 13; @@ -478,7 +482,7 @@ void PackWcle::pack(OutputFile *fo) { linker->defineSymbol("original_entry", ih.init_eip_offset + text_vaddr); linker->defineSymbol("original_stack", ih.init_esp_offset + IOT(ih.init_ss_object - 1, my_base_address)); - linker->defineSymbol("start_of_relocs", mps * pages); + linker->defineSymbol("start_of_relocs", mem_size(mps, pages)); defineDecompressorSymbols(); defineFilterSymbols(&ft); linker->defineSymbol("filter_buffer_start", text_vaddr); @@ -518,19 +522,17 @@ void PackWcle::pack(OutputFile *fo) { **************************************************************************/ void PackWcle::decodeFixups() { - SPAN_P_VAR(upx_byte, p, oimage + soimage); - // assert(p.raw_size_in_bytes() == mb_oimage.getSize()); // Span sanity check - mb_iimage.dealloc(); iimage = nullptr; - MemBuffer tmpbuf; - unsigned const fixupn = unoptimizeReloc32(p, oimage, tmpbuf, true); + SPAN_S_VAR(const upx_byte, p, oimage + soimage); + MemBuffer mb_relocs; + unsigned const fixupn = unoptimizeReloc(p, mb_relocs, oimage, soimage, 32, true); MemBuffer wrkmem(8 * fixupn + 8); unsigned ic, jc, o, r; for (ic = 0; ic < fixupn; ic++) { - jc = get_le32(tmpbuf + 4 * ic); + jc = get_le32(mb_relocs + 4 * ic); set_le32(wrkmem + ic * 8, jc); o = soobject_table; r = get_le32(oimage + jc); @@ -539,13 +541,13 @@ void PackWcle::decodeFixups() { set_le32(oimage + jc, r); } set_le32(wrkmem + ic * 8, 0xFFFFFFFF); // end of 32-bit offset fixups - tmpbuf.dealloc(); + mb_relocs.dealloc(); // done // selector fixups then self-relative fixups - SPAN_P_VAR(const upx_byte, selector_fixups, p); + SPAN_S_VAR(const upx_byte, selector_fixups, p); // Find selfrel_fixups by skipping over selector_fixups. - SPAN_P_VAR(const upx_byte, q, selector_fixups); + SPAN_S_VAR(const upx_byte, q, selector_fixups); // The code is a subroutine that ends in RET (0xC3). while (*q != 0xC3) { // Defend against tampered selector_fixups; see PackWcle::preprocessFixups(). @@ -563,10 +565,9 @@ void PackWcle::decodeFixups() { } // Guard against run-away. static unsigned char const blank[9] = {0}; - if (ptr_diff_bytes(oimage + ph.u_len - sizeof(blank), raw_bytes(q, 0)) < - 0 // catastrophic worst case - || !memcmp(blank, q, sizeof(blank)) // no-good early warning - ) { + // catastrophic worst case or no-good early warning + if (ptr_diff_bytes(oimage + ph.u_len - sizeof(blank), raw_bytes(q, 0)) < 0 || + !memcmp(blank, q, sizeof(blank))) { char msg[50]; snprintf(msg, sizeof(msg), "bad selector_fixups %d", ptr_diff_bytes(q, selector_fixups)); @@ -575,7 +576,7 @@ void PackWcle::decodeFixups() { q += 9; } unsigned selectlen = ptr_udiff_bytes(q, selector_fixups) / 9; - SPAN_P_VAR(const upx_byte, selfrel_fixups, q + 1); // Skip the 0xC3 + SPAN_S_VAR(const upx_byte, selfrel_fixups, q + 1); // Skip the 0xC3 const unsigned fbytes = fixupn * 9 + 1000 + selectlen * 5; ofixups = New(upx_byte, fbytes); @@ -611,7 +612,7 @@ void PackWcle::decodeFixups() { dputc('s', stdout); } // 32 bit offset fixups - while (get_le32(wrkmem + 4 * jc) < ic * mps) { + while (get_le32(wrkmem + 4 * jc) < mem_size(mps, ic)) { if (jc > 1 && ((get_le32(wrkmem + 4 * (jc - 2)) + 3) & (mps - 1)) < 3) // cross page fixup? { @@ -770,7 +771,7 @@ void PackWcle::unpack(OutputFile *fo) { // unfilter if (ph.filter) { - const unsigned text_size = OOT(oh.init_cs_object - 1, npages) * mps; + const unsigned text_size = mem_size(mps, OOT(oh.init_cs_object - 1, npages)); const unsigned text_vaddr = OOT(oh.init_cs_object - 1, my_base_address); Filter ft(ph.level); diff --git a/src/packer.cpp b/src/packer.cpp index dbfb49fe..0c31de36 100644 --- a/src/packer.cpp +++ b/src/packer.cpp @@ -60,7 +60,7 @@ void Packer::assertPacker() const { assert(getVersion() >= 11); assert(getVersion() <= 14); assert(strlen(getName()) <= 15); - // info: 36 is the limit for show_all_packers() in help.cpp + // info: 36 is the limit for show_all_packers() in help.cpp, but 32 should be enough assert(strlen(getFullName(opt)) <= 32); assert(strlen(getFullName(nullptr)) <= 32); if (bele == nullptr) @@ -77,8 +77,9 @@ void Packer::assertPacker() const { fprintf(stderr, "%s\n", getName()); assert(bele == format_bele); } -#if 1 +#if DEBUG Linker *l = newLinker(); + assert(l != nullptr); if (bele != l->bele) fprintf(stderr, "%s\n", getName()); assert(bele == l->bele); @@ -268,7 +269,7 @@ bool Packer::compress(SPAN_P(upx_byte) i_ptr, unsigned i_len, SPAN_P(upx_byte) o } } - // printf("\nPacker::compress: %d/%d: %7d -> %7d\n", method, ph.level, ph.u_len, ph.c_len); + NO_printf("\nPacker::compress: %d/%d: %7d -> %7d\n", method, ph.level, ph.u_len, ph.c_len); if (!checkCompressionRatio(ph.u_len, ph.c_len)) return false; // return in any case if not compressible @@ -298,14 +299,6 @@ bool Packer::compress(SPAN_P(upx_byte) i_ptr, unsigned i_len, SPAN_P(upx_byte) o return true; } -#if 0 -bool Packer::compress(upx_bytep in, upx_bytep out, - const upx_compress_config_t *cconf) -{ - return ph_compress(ph, in, out, cconf); -} -#endif - bool Packer::checkDefaultCompressionRatio(unsigned u_len, unsigned c_len) const { assert((int) u_len > 0); assert((int) c_len > 0); @@ -316,14 +309,12 @@ bool Packer::checkDefaultCompressionRatio(unsigned u_len, unsigned c_len) const if (gain < 512) // need at least 512 bytes gain return false; #if 1 - if (gain >= 4096) // ok if we have 4096 bytes gain + if (gain >= 4096) // ok if we have at least 4096 bytes gain return true; - if (gain >= u_len / 16) // ok if we have 6.25% gain +#endif + if (gain >= u_len / 16) // ok if we have at least 6.25% gain return true; return false; -#else - return true; -#endif } bool Packer::checkCompressionRatio(unsigned u_len, unsigned c_len) const { @@ -823,136 +814,6 @@ int Packer::patch_le32(void *b, int blen, const void *old, unsigned new_) { return boff; } -/************************************************************************* -// relocation util -**************************************************************************/ - -unsigned Packer::optimizeReloc(SPAN_P(upx_byte) in, unsigned relocnum, SPAN_P(upx_byte) out, - SPAN_P(upx_byte) image, unsigned headway, bool bswap, int *big, - int bits) { - if (opt->exact) - throwCantPackExact(); - - *big = 0; - if (relocnum == 0) - return 0; - qsort(raw_bytes(in, 4 * relocnum), relocnum, 4, le32_compare); - - unsigned jc, pc, oc; - SPAN_P_VAR(upx_byte, fix, out); - - pc = (unsigned) -4; - for (jc = 0; jc < relocnum; jc++) { - oc = get_le32(in + jc * 4) - pc; - if (oc == 0) - continue; - else if ((int) oc < 4) - throwCantPack("overlapping fixups"); - else if (oc < 0xF0) - *fix++ = (unsigned char) oc; - else if (oc < 0x100000) { - *fix++ = (unsigned char) (0xF0 + (oc >> 16)); - *fix++ = (unsigned char) oc; - *fix++ = (unsigned char) (oc >> 8); - } else { - *big = 1; - *fix++ = 0xf0; - *fix++ = 0; - *fix++ = 0; - set_le32(fix, oc); - fix += 4; - } - pc += oc; - if (headway <= pc) { - char msg[80]; - snprintf(msg, sizeof(msg), "bad reloc[%#x] = %#x", jc, oc); - throwCantPack(msg); - } - if (bswap) { - if (bits == 32) - set_be32(image + pc, get_le32(image + pc)); - else if (bits == 64) - set_be64(image + pc, get_le64(image + pc)); - else - throwInternalError("optimizeReloc problem"); - } - } - *fix++ = 0; - return ptr_udiff_bytes(fix, out); -} - -unsigned Packer::optimizeReloc32(SPAN_P(upx_byte) in, unsigned relocnum, SPAN_P(upx_byte) out, - SPAN_P(upx_byte) image, unsigned headway, bool bswap, int *big) { - return optimizeReloc(in, relocnum, out, image, headway, bswap, big, 32); -} - -unsigned Packer::optimizeReloc64(SPAN_P(upx_byte) in, unsigned relocnum, SPAN_P(upx_byte) out, - SPAN_P(upx_byte) image, unsigned headway, bool bswap, int *big) { - return optimizeReloc(in, relocnum, out, image, headway, bswap, big, 64); -} - -unsigned Packer::unoptimizeReloc(SPAN_P(upx_byte) & in, SPAN_P(upx_byte) image, MemBuffer &out, - bool bswap, int bits) { - SPAN_P_VAR(upx_byte, p, in); - unsigned relocn = 0; - for (; *p; p++, relocn++) - if (*p >= 0xF0) { - if (*p == 0xF0 && get_le16(p + 1) == 0) - p += 4; - p += 2; - } - SPAN_P_VAR(upx_byte, const in_end, p); - // fprintf(stderr,"relocnum=%x\n",relocn); - out.alloc(4 * (relocn + 1)); // one extra entry - SPAN_S_VAR(LE32, relocs, out); - unsigned jc = (unsigned) -4; - for (p = in; p < in_end; p++) { - if (*p < 0xF0) - jc += *p; - else { - unsigned dif = (*p & 0x0F) * 0x10000 + get_le16(p + 1); - p += 2; - if (dif == 0) { - dif = get_le32(p + 1); - p += 4; - } - jc += dif; - } - *relocs++ = jc; // FIXME: range check jc - if (!relocn--) { - break; - } - if (bswap && image != nullptr) { - if (bits == 32) { - set_be32(image + jc, get_le32(image + jc)); - if ((unsigned) ptr_diff_bytes(p, image + jc) < 4) { - // data must not overlap control - p = image + jc + (4 - 1); // -1: 'for' also increments - } - } else if (bits == 64) { - set_be64(image + jc, get_le64(image + jc)); - if ((unsigned) ptr_diff_bytes(p, image + jc) < 8) { - // data must not overlap control - p = image + jc + (8 - 1); // -1: 'for' also increments - } - } else - throwInternalError("unoptimizeReloc problem"); - } - } - in = p + 1; - return ptr_udiff_bytes(relocs, out) / 4; // return number of relocs -} - -unsigned Packer::unoptimizeReloc32(SPAN_P(upx_byte) & in, SPAN_P(upx_byte) image, MemBuffer &out, - bool bswap) { - return unoptimizeReloc(in, image, out, bswap, 32); -} - -unsigned Packer::unoptimizeReloc64(SPAN_P(upx_byte) & in, SPAN_P(upx_byte) image, MemBuffer &out, - bool bswap) { - return unoptimizeReloc(in, image, out, bswap, 64); -} - /************************************************************************* // loader util (interface to linker) **************************************************************************/ diff --git a/src/packer.h b/src/packer.h index 25cd553e..5e61fdbc 100644 --- a/src/packer.h +++ b/src/packer.h @@ -271,21 +271,12 @@ protected: void checkPatch(void *b, int blen, int boff, int size); // relocation util - static unsigned optimizeReloc(SPAN_P(upx_byte) in, unsigned relocnum, SPAN_P(upx_byte) out, - SPAN_P(upx_byte) image, unsigned headway, bool bswap, int *big, - int bits); - static unsigned unoptimizeReloc(SPAN_P(upx_byte) & in, SPAN_P(upx_byte) image, MemBuffer &out, - bool bswap, int bits); - - static unsigned optimizeReloc32(SPAN_P(upx_byte) in, unsigned relocnum, SPAN_P(upx_byte) out, - SPAN_P(upx_byte) image, unsigned headway, bool bswap, int *big); - static unsigned unoptimizeReloc32(SPAN_P(upx_byte) & in, SPAN_P(upx_byte) image, MemBuffer &out, - bool bswap); - - static unsigned optimizeReloc64(SPAN_P(upx_byte) in, unsigned relocnum, SPAN_P(upx_byte) out, - SPAN_P(upx_byte) image, unsigned headway, bool bswap, int *big); - static unsigned unoptimizeReloc64(SPAN_P(upx_byte) & in, SPAN_P(upx_byte) image, MemBuffer &out, - bool bswap); + static unsigned optimizeReloc(unsigned relocnum, SPAN_P(upx_byte) relocs, SPAN_S(upx_byte) out, + SPAN_P(upx_byte) image, unsigned image_size, int bits, bool bswap, + int *big); + static unsigned unoptimizeReloc(SPAN_S(const upx_byte) & in, MemBuffer &out, + SPAN_P(upx_byte) image, unsigned image_size, int bits, + bool bswap); // Target Endianness abstraction unsigned get_te16(const void *p) const { return bele->get16(p); } diff --git a/src/packer_r.cpp b/src/packer_r.cpp new file mode 100644 index 00000000..c3ef383a --- /dev/null +++ b/src/packer_r.cpp @@ -0,0 +1,144 @@ +/* packer_r.cpp -- Packer relocation handling + + 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 + + */ + +#include "conf.h" +#include "packer.h" + +/************************************************************************* +// sort and delta-compress relocations with optional bswap within image +// returns number of bytes written to |out| +**************************************************************************/ + +unsigned Packer::optimizeReloc(unsigned relocnum, SPAN_P(upx_byte) relocs, SPAN_S(upx_byte) out, + SPAN_P(upx_byte) image, unsigned image_size, int bits, bool bswap, + int *big) { + assert(bits == 32 || bits == 64); + mem_size_assert(1, image_size); + SPAN_P_VAR(upx_byte, fix, out); + + *big = 0; + if (opt->exact) + throwCantPackExact(); + if (relocnum == 0) + return 0; + qsort(raw_bytes(relocs, 4 * relocnum), relocnum, 4, le32_compare); + + unsigned pc = (unsigned) -4; + for (unsigned i = 0; i < relocnum; i++) { + unsigned delta = get_le32(relocs + i * 4) - pc; + if (delta == 0) + continue; + else if ((int) delta < 4) + throwCantPack("overlapping fixups"); + else if (delta < 0xf0) + *fix++ = (unsigned char) delta; + else if (delta < 0x100000) { + *fix++ = (unsigned char) (0xf0 + (delta >> 16)); + *fix++ = (unsigned char) delta; + *fix++ = (unsigned char) (delta >> 8); + } else { + *big = 1; + *fix++ = 0xf0; + *fix++ = 0; + *fix++ = 0; + set_le32(fix, delta); + fix += 4; + } + pc += delta; + if (pc + 4 >= image_size) + throwCantPack("bad reloc[%#x] = %#x", i, pc); + if (bswap) { + if (bits == 32) + set_be32(image + pc, get_le32(image + pc)); + else + set_be64(image + pc, get_le64(image + pc)); + } + } + *fix++ = 0; // end marker + return ptr_udiff_bytes(fix, out); +} + +/************************************************************************* +// delta-decompress relocations +// advances |in| +// allocates |out| and returns number of relocs written to |out| +**************************************************************************/ + +unsigned Packer::unoptimizeReloc(SPAN_S(const upx_byte) & in, MemBuffer &out, + SPAN_P(upx_byte) image, unsigned image_size, int bits, + bool bswap) { + assert(bits == 32 || bits == 64); + mem_size_assert(1, image_size); + SPAN_S_VAR(const upx_byte, fix, in); + + // count + unsigned relocnum = 0; + for (fix = in; *fix; fix++, relocnum++) { + if (*fix >= 0xf0) { + if (*fix == 0xf0 && get_le16(fix + 1) == 0) + fix += 4; + fix += 2; + } + } + NO_fprintf(stderr, "relocnum=%x\n", relocnum); + + out.alloc(4 * (relocnum + 1)); // one extra entry + SPAN_S_VAR(LE32, relocs, out); + + fix = in; + unsigned pc = (unsigned) -4; + for (unsigned i = 0; i < relocnum; i++) { + unsigned delta; + if (*fix < 0xf0) + delta = *fix++; + else { + delta = (*fix & 0x0f) * 0x10000 + get_le16(fix + 1); + fix += 3; + if (delta == 0) { + delta = get_le32(fix); + fix += 4; + } + } + if ((int) delta < 4) + throwCantUnpack("overlapping fixups"); + pc += delta; + if (pc + 4 >= image_size) + throwCantUnpack("bad reloc[%#x] = %#x", i, pc); + *relocs++ = pc; + if (bswap && image != nullptr) { + if (bits == 32) + set_be32(image + pc, get_le32(image + pc)); + else + set_be64(image + pc, get_le64(image + pc)); + } + } + in = fix + 1; // advance + assert(relocnum == ptr_udiff_bytes(relocs, out) / 4); + return relocnum; +} + +/* vim:set ts=4 sw=4 et: */ diff --git a/src/packhead.cpp b/src/packhead.cpp index f137cb25..75ed32b5 100644 --- a/src/packhead.cpp +++ b/src/packhead.cpp @@ -174,12 +174,12 @@ bool PackHeader::decodePackHeaderFromBuf(SPAN_S(const upx_byte) buf, int blen) { int boff = find_le32(raw_bytes(buf, blen), blen, UPX_MAGIC_LE32); if (boff < 0) return false; + blen -= boff; // bytes remaining in buf + if (blen < 20) + throwCantUnpack("header corrupted 1"); SPAN_S_VAR(const upx_byte, const p, buf + boff); - unsigned const headway = blen - boff; // bytes remaining in buf - if (headway < (1 + 7)) - throwCantUnpack("header corrupted 1"); version = p[4]; format = p[5]; method = p[6]; @@ -201,33 +201,25 @@ bool PackHeader::decodePackHeaderFromBuf(SPAN_S(const upx_byte) buf, int blen) { // decode the new variable length header // - unsigned off_filter = 0; + int off_filter = 0; if (format < 128) { - if (headway < 16) { - throwCantUnpack("header corrupted 2"); - } u_adler = get_le32(p + 8); c_adler = get_le32(p + 12); if (format == UPX_F_DOS_COM || format == UPX_F_DOS_SYS) { - if (headway < 20) { - throwCantUnpack("header corrupted 5"); - } 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) { - if (headway < 25) { + if (blen < 25) throwCantUnpack("header corrupted 6"); - } u_len = get_le24(p + 16); c_len = get_le24(p + 19); u_file_size = get_le24(p + 22); off_filter = 25; } else { - if (headway < (3 + 28)) { + if (blen < 31) throwCantUnpack("header corrupted 7"); - } u_len = get_le32(p + 16); c_len = get_le32(p + 20); u_file_size = get_le32(p + 24); @@ -236,9 +228,8 @@ bool PackHeader::decodePackHeaderFromBuf(SPAN_S(const upx_byte) buf, int blen) { n_mru = p[30] ? 1 + p[30] : 0; } } else { - if (headway < (3 + 28)) { + if (blen < 31) throwCantUnpack("header corrupted 8"); - } u_len = get_be32(p + 8); c_len = get_be32(p + 12); u_adler = get_be32(p + 16); @@ -250,9 +241,8 @@ bool PackHeader::decodePackHeaderFromBuf(SPAN_S(const upx_byte) buf, int blen) { } if (version >= 10) { - if (headway < (1 + off_filter)) { + if (blen < off_filter + 1) throwCantUnpack("header corrupted 9"); - } filter = p[off_filter]; } else if ((level & 128) == 0) filter = 0; @@ -273,9 +263,9 @@ bool PackHeader::decodePackHeaderFromBuf(SPAN_S(const upx_byte) buf, int blen) { if (version == 0xff) throwCantUnpack("cannot unpack UPX ;-)"); // check header_checksum - if (version > 9) { - unsigned const size = getPackHeaderSize(); // expected; based on format and version - if (headway < size || p[size - 1] != get_packheader_checksum(p, size - 1)) + if (version >= 10) { + int size = getPackHeaderSize(); // expected; based on format and version + if (size > blen || p[size - 1] != get_packheader_checksum(p, size - 1)) throwCantUnpack("header corrupted 3"); } if (c_len < 2 || u_len < 2 || !mem_size_valid_bytes(c_len) || !mem_size_valid_bytes(u_len)) diff --git a/src/pefile.cpp b/src/pefile.cpp index 54469505..da1f1f2c 100644 --- a/src/pefile.cpp +++ b/src/pefile.cpp @@ -265,8 +265,8 @@ PeFile::Reloc::Reloc(upx_byte *s, unsigned si) : start(s), size(si), rel(nullptr counts[type]++; } -PeFile::Reloc::Reloc(unsigned rnum) : start(nullptr), size(0), rel(nullptr), rel1(nullptr) { - start = new upx_byte[mem_size(4, rnum, 8192)]; // => oxrelocs +PeFile::Reloc::Reloc(unsigned relocnum) : start(nullptr), size(0), rel(nullptr), rel1(nullptr) { + start = new upx_byte[mem_size(4, relocnum, 8192)]; // => oxrelocs counts[0] = 0; } @@ -330,13 +330,13 @@ void PeFile32::processRelocs() // pass1 unsigned const skip1 = IDADDR(PEDIR_RELOC); Reloc rel(ibuf.subref("bad reloc %#x", skip1, take1), take1); const unsigned *counts = rel.getcounts(); - unsigned rnum = 0; + unsigned relocnum = 0; unsigned ic; for (ic = 1; ic < 16; ic++) - rnum += counts[ic]; + relocnum += counts[ic]; - if (opt->win32_pe.strip_relocs || rnum == 0) { + if (opt->win32_pe.strip_relocs || relocnum == 0) { if (IDSIZE(PEDIR_RELOC)) { ibuf.fill(IDADDR(PEDIR_RELOC), IDSIZE(PEDIR_RELOC), FILLVAL); ih.objects = tryremove(IDADDR(PEDIR_RELOC), ih.objects); @@ -388,17 +388,17 @@ void PeFile32::processRelocs() // pass1 } ibuf.fill(IDADDR(PEDIR_RELOC), IDSIZE(PEDIR_RELOC), FILLVAL); - mb_orelocs.alloc(mem_size(4, rnum, 1024)); // 1024 - safety - orelocs = mb_orelocs; // => orelocs now is a SPAN_S - sorelocs = optimizeReloc32((upx_byte *) fix[3], xcounts[3], orelocs, ibuf + rvamin, - ibufgood - rvamin, true, &big_relocs); + mb_orelocs.alloc(mem_size(4, relocnum, 8192)); // 8192 - safety + orelocs = mb_orelocs; // => orelocs now is a SPAN_S + sorelocs = optimizeReloc(xcounts[3], (upx_byte *) fix[3], orelocs, ibuf + rvamin, + ibufgood - rvamin, 32, true, &big_relocs); delete[] fix[3]; // Malware that hides behind UPX often has PE header info that is // deliberately corrupt. Sometimes it is even tuned to cause us trouble! // Use an extra check to avoid AccessViolation (SIGSEGV) when appending // the relocs into one array. - if ((rnum * 4 + 1024) < (sorelocs + 4 * (2 + xcounts[2] + xcounts[1]))) + if ((4 * relocnum + 8192) < (sorelocs + 4 * (2 + xcounts[2] + xcounts[1]))) throwCantUnpack("Invalid relocs"); // append relocs type "LOW" then "HIGH" @@ -426,13 +426,13 @@ void PeFile64::processRelocs() // pass1 unsigned const skip = IDADDR(PEDIR_RELOC); Reloc rel(ibuf.subref("bad reloc %#x", skip, take), take); const unsigned *counts = rel.getcounts(); - unsigned rnum = 0; + unsigned relocnum = 0; unsigned ic; for (ic = 1; ic < 16; ic++) - rnum += counts[ic]; + relocnum += counts[ic]; - if (opt->win32_pe.strip_relocs || rnum == 0) { + if (opt->win32_pe.strip_relocs || relocnum == 0) { if (IDSIZE(PEDIR_RELOC)) { ibuf.fill(IDADDR(PEDIR_RELOC), IDSIZE(PEDIR_RELOC), FILLVAL); ih.objects = tryremove(IDADDR(PEDIR_RELOC), ih.objects); @@ -487,10 +487,10 @@ void PeFile64::processRelocs() // pass1 } ibuf.fill(IDADDR(PEDIR_RELOC), IDSIZE(PEDIR_RELOC), FILLVAL); - mb_orelocs.alloc(mem_size(4, rnum, 1024)); // 1024 - safety - orelocs = mb_orelocs; // => orelocs now is a SPAN_S - sorelocs = optimizeReloc64((upx_byte *) fix[10], xcounts[10], orelocs, ibuf + rvamin, - ibufgood - rvamin, true, &big_relocs); + mb_orelocs.alloc(mem_size(4, relocnum, 8192)); // 8192 - safety + orelocs = mb_orelocs; // => orelocs now is a SPAN_S + sorelocs = optimizeReloc(xcounts[10], (upx_byte *) fix[10], orelocs, ibuf + rvamin, + ibufgood - rvamin, 64, true, &big_relocs); for (ic = 15; ic; ic--) delete[] fix[ic]; @@ -500,7 +500,7 @@ void PeFile64::processRelocs() // pass1 // deliberately corrupt. Sometimes it is even tuned to cause us trouble! // Use an extra check to avoid AccessViolation (SIGSEGV) when appending // the relocs into one array. - if ((rnum * 4 + 1024) < (sorelocs + 4*(2 + xcounts[2] + xcounts[1]))) + if ((4 * relocnum + 8192) < (sorelocs + 4*(2 + xcounts[2] + xcounts[1]))) throwCantUnpack("Invalid relocs"); // append relocs type "LOW" then "HIGH" @@ -925,7 +925,7 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1 Interval names(ibuf), iats(ibuf), lookups(ibuf); // create the preprocessed data - SPAN_P_VAR(upx_byte, ppi, oimport); // preprocessed imports + SPAN_S_VAR(upx_byte, ppi, oimport); // preprocessed imports for (ic = 0; ic < dllnum; ic++) { LEXX *tarr = idlls[ic]->lookupt; set_le32(ppi, ilinker->getAddress(idlls[ic]->name)); @@ -2203,7 +2203,7 @@ void PeFile::pack0(OutputFile *fo, ht &ih, ht &oh, unsigned subsystem_mask, ph.u_len = newvsize + soimport + sorelocs; - // some extra data for uncompression support + // some extra_info data for uncompression support unsigned s = 0; upx_byte *const p1 = ibuf.subref("bad ph.u_len %#x", ph.u_len, sizeof(ih)); memcpy(p1 + s, &ih, sizeof(ih)); @@ -2224,7 +2224,8 @@ void PeFile::pack0(OutputFile *fo, ht &ih, ht &oh, unsigned subsystem_mask, set_le16(p1 + s, icondir_count); s += 2; } - // end of extra data + // end of extra_info data + set_le32(p1 + s, ptr_diff_bytes(p1, ibuf) - rvamin); s += 4; ph.u_len += s; @@ -2535,7 +2536,7 @@ void PeFile::pack0(OutputFile *fo, ht &ih, ht &oh, unsigned subsystem_mask, // unpack **************************************************************************/ -void PeFile::rebuildRelocs(SPAN_S(upx_byte) & extrainfo, unsigned bits, unsigned flags, +void PeFile::rebuildRelocs(SPAN_S(upx_byte) & extra_info, unsigned bits, unsigned flags, upx_uint64_t imagebase) { assert(bits == 32 || bits == 64); if (!ODADDR(PEDIR_RELOC) || !ODSIZE(PEDIR_RELOC) || (flags & RELOCS_STRIPPED)) @@ -2547,13 +2548,13 @@ void PeFile::rebuildRelocs(SPAN_S(upx_byte) & extrainfo, unsigned bits, unsigned return; } - SPAN_P_VAR(upx_byte, rdata, obuf); - rdata += get_le32(extrainfo); - const upx_byte big = extrainfo[4]; - extrainfo += 5; + const unsigned orig_crelocs = mem_size(1, get_le32(extra_info)); + const upx_byte big = extra_info[4]; + extra_info += 5; + SPAN_S_VAR(const upx_byte, rdata, obuf + orig_crelocs, obuf); MemBuffer mb_wrkmem; - unsigned relocn = unoptimizeReloc(rdata, obuf, mb_wrkmem, true, bits); + unsigned relocnum = unoptimizeReloc(rdata, mb_wrkmem, obuf, orig_crelocs, bits, true); unsigned r16 = 0; if (big & 6) // 16 bit relocations { @@ -2564,10 +2565,9 @@ void PeFile::rebuildRelocs(SPAN_S(upx_byte) & extrainfo, unsigned bits, unsigned while (*++q) r16++; } - Reloc rel(relocn + r16); - + Reloc rel(relocnum + r16); if (big & 6) { - SPAN_S_VAR(LE32, q, (LE32 *) raw_bytes(rdata, 0), obuf); + SPAN_S_VAR(const LE32, q, (const LE32 *) raw_bytes(rdata, 0), obuf); while (*q) rel.add(*q++ + rvamin, (big & 4) ? 2 : 1); if ((big & 6) == 6) @@ -2577,7 +2577,7 @@ void PeFile::rebuildRelocs(SPAN_S(upx_byte) & extrainfo, unsigned bits, unsigned } SPAN_S_VAR(upx_byte, const wrkmem, mb_wrkmem); - for (unsigned ic = 0; ic < relocn; ic++) { + for (unsigned ic = 0; ic < relocnum; ic++) { OPTR_VAR(upx_byte, const p, obuf + get_le32(wrkmem + 4 * ic)); if (bits == 32) set_le32(p, get_le32(p) + imagebase + rvamin); @@ -2610,12 +2610,12 @@ void PeFile::rebuildTls() { // this is an easy one : just do nothing ;-) } -void PeFile::rebuildResources(SPAN_S(upx_byte) & extrainfo, unsigned lastvaddr) { +void PeFile::rebuildResources(SPAN_S(upx_byte) & extra_info, unsigned lastvaddr) { if (ODSIZE(PEDIR_RESOURCE) == 0 || IDSIZE(PEDIR_RESOURCE) == 0) return; - icondir_count = get_le16(extrainfo); - extrainfo += 2; + icondir_count = get_le16(extra_info); + extra_info += 2; const unsigned vaddr = IDADDR(PEDIR_RESOURCE); @@ -2646,13 +2646,13 @@ void PeFile::rebuildResources(SPAN_S(upx_byte) & extrainfo, unsigned lastvaddr) } template -void PeFile::rebuildImports(SPAN_S(upx_byte) & extrainfo, ord_mask_t ord_mask, bool set_oft) { +void PeFile::rebuildImports(SPAN_S(upx_byte) & extra_info, ord_mask_t ord_mask, bool set_oft) { if (ODADDR(PEDIR_IMPORT) == 0 || ODSIZE(PEDIR_IMPORT) <= sizeof(import_desc)) return; - OPTR_VAR(const upx_byte, const imdata, obuf + get_le32(extrainfo)); - const unsigned inamespos = get_le32(extrainfo + 4); - extrainfo += 8; + OPTR_VAR(const upx_byte, const imdata, obuf + mem_size(1, get_le32(extra_info))); + const unsigned inamespos = mem_size(1, get_le32(extra_info + 4)); + extra_info += 8; unsigned sdllnames = 0; @@ -2660,7 +2660,7 @@ void PeFile::rebuildImports(SPAN_S(upx_byte) & extrainfo, ord_mask_t ord_mask, b OPTR_VAR(const upx_byte, p, raw_bytes(imdata, 4)); for (; get_le32(p) != 0; ++p) { - const upx_byte *dname = raw_bytes(import + get_le32(p), 1); + const upx_byte *dname = raw_bytes(import + mem_size(1, get_le32(p)), 1); const unsigned dlen = strlen(dname); ICHECK(dname, dlen + 1); @@ -2768,12 +2768,12 @@ void PeFile::unpack0(OutputFile *fo, const ht &ih, ht &oh, ord_mask_t ord_mask, decompress(ibuf, obuf); unsigned skip = get_le32(obuf + (ph.u_len - 4)); unsigned take = sizeof(oh); - SPAN_S_VAR(upx_byte, extrainfo, obuf); - extrainfo = obuf.subref("bad extrainfo offset %#x", skip, take); - // upx_byte * const eistart = raw_bytes(extrainfo, 0); + SPAN_S_VAR(upx_byte, extra_info, obuf); + extra_info = obuf.subref("bad extra_info offset %#x", skip, take); + // upx_byte * const eistart = raw_bytes(extra_info, 0); - memcpy(&oh, extrainfo, take); - extrainfo += take; + memcpy(&oh, extra_info, take); + extra_info += take; skip += take; unsigned objs = oh.objects; @@ -2781,9 +2781,9 @@ void PeFile::unpack0(OutputFile *fo, const ht &ih, ht &oh, ord_mask_t ord_mask, throwCantUnpack("unexpected value in the PE header"); Array(pe_section_t, osection, objs); take = sizeof(pe_section_t) * objs; - extrainfo = obuf.subref("bad extra section size at %#x", skip, take); - memcpy(osection, extrainfo, take); - extrainfo += take; + extra_info = obuf.subref("bad extra section size at %#x", skip, take); + memcpy(osection, extra_info, take); + extra_info += take; skip += take; rvamin = osection[0].vaddr; @@ -2811,8 +2811,8 @@ void PeFile::unpack0(OutputFile *fo, const ht &ih, ht &oh, ord_mask_t ord_mask, ODSIZE(PEDIR_RELOC) = 0; } - rebuildImports(extrainfo, ord_mask, set_oft); - rebuildRelocs(extrainfo, sizeof(ih.imagebase) * 8, oh.flags, oh.imagebase); + rebuildImports(extra_info, ord_mask, set_oft); + rebuildRelocs(extra_info, sizeof(ih.imagebase) * 8, oh.flags, oh.imagebase); rebuildTls(); rebuildExports(); @@ -2824,11 +2824,11 @@ void PeFile::unpack0(OutputFile *fo, const ht &ih, ht &oh, ord_mask_t ord_mask, fi->readx(ibuf, ibufgood = isection[3].size); } - rebuildResources(extrainfo, isection[ih.objects - 1].vaddr); + rebuildResources(extra_info, isection[ih.objects - 1].vaddr); // FIXME: this does bad things if the relocation section got removed // during compression ... - // memset(eistart, 0, ptr_udiff_bytes(extrainfo, eistart) + 4); + // memset(eistart, 0, ptr_udiff_bytes(extra_info, eistart) + 4); // fill the data directory ODADDR(PEDIR_DEBUG) = 0; @@ -3033,7 +3033,7 @@ void PeFile64::processTls(Reloc *r, const Interval *iv, unsigned a) { } /* - extra info added to help uncompression: + extra_info added to help uncompression: diff --git a/src/pefile.h b/src/pefile.h index 69513fb0..e8174a65 100644 --- a/src/pefile.h +++ b/src/pefile.h @@ -92,7 +92,7 @@ protected: unsigned processImports0(ord_mask_t ord_mask); template - void rebuildImports(SPAN_S(upx_byte) & extrainfo, ord_mask_t ord_mask, bool set_oft); + void rebuildImports(SPAN_S(upx_byte) & extra_info, ord_mask_t ord_mask, bool set_oft); virtual unsigned processImports() = 0; virtual void processImports2(unsigned, unsigned); MemBuffer mb_oimport; @@ -365,7 +365,7 @@ protected: public: Reloc(upx_byte *, unsigned); - Reloc(unsigned rnum); + Reloc(unsigned relocnum); // bool next(unsigned &pos, unsigned &type); const unsigned *getcounts() const { return counts; } diff --git a/src/stub/Makefile b/src/stub/Makefile index ecd1de75..f60f617f 100644 --- a/src/stub/Makefile +++ b/src/stub/Makefile @@ -1,10 +1,9 @@ # # UPX stub Makefile - needs GNU make 3.81 or better # -# You also will need a number of special build tools like various -# cross-assemblers and cross-compilers - please see README.SRC -# for details. -# And see misc/rebuild-stubs-with-podman how to use Podman/Docker. +# Preferred: see misc/rebuild-stubs-with-podman how to use Podman/Docker. +# Otherwise you will need a number of special build tools like various +# cross-assemblers and cross-compilers - see README.SRC. # # Copyright (C) 1996-2023 Markus Franz Xaver Johannes Oberhumer # @@ -22,10 +21,6 @@ empty := space := $(empty) $(empty) tab := $(empty) $(empty) -ifneq ($(findstring $(firstword $(MAKE_VERSION)),3.77 3.78 3.78.1 3.79 3.79.1 3.80),) -$(error GNU make 3.81 or better is required) -endif - ifndef srcdir srcdir := $(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))) srcdir := $(shell echo '$(srcdir)' | sed 's,/*$$,,') @@ -207,7 +202,7 @@ tc_objdump_disasm_options = ECHO_e = /bin/echo -e ECHO_E = /bin/echo -E PERL = perl -PYTHON = python2 +PYTHON2 = python2 UNIX2DOS := $(PERL) -i -pe 's/$$/\r/;' # trim (strip) trailing whitespace @@ -240,15 +235,15 @@ define tc endef # default tools -tc.default.bin2h = $(PYTHON) $(top_srcdir)/src/stub/scripts/bin2h.py --ident=auto-stub +tc.default.bin2h = $(PYTHON2) $(top_srcdir)/src/stub/scripts/bin2h.py --ident=auto-stub ##tc.default.bin2h-c = $(call tc,bin2h) --compress=14,15,0 tc.default.bin2h-c = $(call tc,bin2h) --compress=0 -tc.default.brandelf = $(PYTHON) $(top_srcdir)/src/stub/scripts/brandelf.py $(if $(tc_bfdname),--bfdname=$(tc_bfdname)) -tc.default.gpp_inc = $(PYTHON) $(top_srcdir)/src/stub/scripts/gpp_inc.py -tc.default.gpp_mkdep = $(PYTHON) $(top_srcdir)/src/stub/scripts/gpp_inc.py -o /dev/null +tc.default.brandelf = $(PYTHON2) $(top_srcdir)/src/stub/scripts/brandelf.py $(if $(tc_bfdname),--bfdname=$(tc_bfdname)) +tc.default.gpp_inc = $(PYTHON2) $(top_srcdir)/src/stub/scripts/gpp_inc.py +tc.default.gpp_mkdep = $(PYTHON2) $(top_srcdir)/src/stub/scripts/gpp_inc.py -o /dev/null tc.default.pp-as = i386-linux-gcc-3.4.6 -E -nostdinc -x assembler-with-cpp -Wall tc.default.sstrip = sstrip-20060518 -tc.default.xstrip = $(PYTHON) $(top_srcdir)/src/stub/scripts/xstrip.py +tc.default.xstrip = $(PYTHON2) $(top_srcdir)/src/stub/scripts/xstrip.py # default multiarch-binutils tc.default.m-ar = multiarch-ar-2.17 diff --git a/src/util/membuffer.cpp b/src/util/membuffer.cpp index 9886aef5..7513e8d2 100644 --- a/src/util/membuffer.cpp +++ b/src/util/membuffer.cpp @@ -179,38 +179,42 @@ void MemBuffer::fill(unsigned off, unsigned len, int value) { void MemBuffer::checkState() const { if (!ptr) throwInternalError("block not allocated"); + assert(size_in_bytes > 0); if (use_simple_mcheck()) { - if (get_ne32(ptr - 4) != MAGIC1(ptr)) + const unsigned char *p = (const unsigned char *) ptr; + if (get_ne32(p - 4) != MAGIC1(p)) throwInternalError("memory clobbered before allocated block 1"); - if (get_ne32(ptr - 8) != size_in_bytes) + if (get_ne32(p - 8) != size_in_bytes) throwInternalError("memory clobbered before allocated block 2"); - if (get_ne32(ptr + size_in_bytes) != MAGIC2(ptr)) + if (get_ne32(p + size_in_bytes) != MAGIC2(p)) throwInternalError("memory clobbered past end of allocated block"); } } -void MemBuffer::alloc(upx_uint64_t size) { +void MemBuffer::alloc(upx_uint64_t bytes) { // NOTE: we don't automatically free a used buffer assert(ptr == nullptr); assert(size_in_bytes == 0); // - assert(size > 0); + assert(bytes > 0); debug_set(debug.last_return_address_alloc, upx_return_address()); - size_t bytes = mem_size(1, size, use_simple_mcheck() ? 32 : 0); - unsigned char *p = (unsigned char *) malloc(bytes); - NO_printf("MemBuffer::alloc %llu: %p\n", size, p); + size_t malloc_bytes = mem_size(1, bytes); + if (use_simple_mcheck()) + malloc_bytes += 32; + unsigned char *p = (unsigned char *) ::malloc(malloc_bytes); + NO_printf("MemBuffer::alloc %llu: %p\n", bytes, p); if (!p) throwOutOfMemoryException(); - ptr = p; - size_in_bytes = ACC_ICONV(unsigned, size); + size_in_bytes = ACC_ICONV(unsigned, bytes); if (use_simple_mcheck()) { - ptr = p + 16; + p += 16; // store magic constants to detect buffer overruns - set_ne32(ptr - 8, size_in_bytes); - set_ne32(ptr - 4, MAGIC1(ptr)); - set_ne32(ptr + size_in_bytes, MAGIC2(ptr)); - set_ne32(ptr + size_in_bytes + 4, stats.global_alloc_counter); + set_ne32(p - 8, size_in_bytes); + set_ne32(p - 4, MAGIC1(p)); + set_ne32(p + size_in_bytes, MAGIC2(p)); + set_ne32(p + size_in_bytes + 4, stats.global_alloc_counter); } + ptr = (pointer) (void *) p; #if !defined(__SANITIZE_ADDRESS__) && 0 fill(0, size_in_bytes, (rand() & 0xff) | 1); // debug (void) VALGRIND_MAKE_MEM_UNDEFINED(ptr, size_in_bytes); @@ -218,23 +222,29 @@ void MemBuffer::alloc(upx_uint64_t size) { stats.global_alloc_counter += 1; stats.global_total_bytes += size_in_bytes; stats.global_total_active_bytes += size_in_bytes; +#if DEBUG + checkState(); +#endif } void MemBuffer::dealloc() { if (ptr != nullptr) { debug_set(debug.last_return_address_dealloc, upx_return_address()); checkState(); + stats.global_dealloc_counter += 1; stats.global_total_active_bytes -= size_in_bytes; if (use_simple_mcheck()) { + unsigned char *p = (unsigned char *) ptr; // clear magic constants - set_ne32(ptr - 8, 0); - set_ne32(ptr - 4, 0); - set_ne32(ptr + size_in_bytes, 0); - set_ne32(ptr + size_in_bytes + 4, 0); + set_ne32(p - 8, 0); + set_ne32(p - 4, 0); + set_ne32(p + size_in_bytes, 0); + set_ne32(p + size_in_bytes + 4, 0); // - ::free(ptr - 16); - } else + ::free(p - 16); + } else { ::free(ptr); + } ptr = nullptr; size_in_bytes = 0; } else { @@ -263,16 +273,26 @@ TEST_CASE("MemBuffer") { CHECK_NOTHROW(64 + mb); CHECK_THROWS(65 + mb); #endif + CHECK_NOTHROW(mb.subref("", 0, 64)); + CHECK_NOTHROW(mb.subref("", 64, 0)); + CHECK_THROWS(mb.subref("", 1, 64)); + CHECK_THROWS(mb.subref("", 64, 1)); if (use_simple_mcheck()) { - upx_byte *b = raw_bytes(mb, 0); - unsigned magic1 = get_ne32(b - 4); - set_ne32(b - 4, magic1 ^ 1); + unsigned char *p = raw_bytes(mb, 0); + unsigned magic1 = get_ne32(p - 4); + set_ne32(p - 4, magic1 ^ 1); CHECK_THROWS(mb.checkState()); - set_ne32(b - 4, magic1); + set_ne32(p - 4, magic1); mb.checkState(); } } +TEST_CASE("MemBuffer unused") { + MemBuffer mb; + CHECK(mb.raw_ptr() == nullptr); + CHECK(mb.raw_size_in_bytes() == 0); +} + TEST_CASE("MemBuffer::getSizeForCompression") { CHECK_THROWS(MemBuffer::getSizeForCompression(0)); CHECK_THROWS(MemBuffer::getSizeForDecompression(0)); diff --git a/src/util/membuffer.h b/src/util/membuffer.h index 86ef5683..049c95a5 100644 --- a/src/util/membuffer.h +++ b/src/util/membuffer.h @@ -49,9 +49,10 @@ public: MemBufferBase() : ptr(nullptr), size_in_bytes(0) {} // NOTE: implicit conversion to underlying pointer - // NOTE: for fully bound-checked pointer use XSPAN_S from xspan.h + // HINT: for fully bound-checked pointer use XSPAN_S from xspan.h operator pointer() const { return ptr; } + // membuffer + n -> pointer template typename std::enable_if::value, pointer>::type operator+(U n) const { size_t bytes = mem_size(sizeof(T), n); // check mem_size @@ -59,7 +60,7 @@ public: } private: - // NOT allowed; use raw_bytes() instead + // membuffer - n -> pointer; not allowed - use raw_bytes() if needed template typename std::enable_if::value, pointer>::type operator-(U n) const DELETED_FUNCTION; @@ -82,13 +83,13 @@ public: // raw access class MemBuffer final : public MemBufferBase { public: MemBuffer() : MemBufferBase() {} - explicit MemBuffer(upx_uint64_t size_in_bytes); + explicit MemBuffer(upx_uint64_t bytes); ~MemBuffer(); static unsigned getSizeForCompression(unsigned uncompressed_size, unsigned extra = 0); static unsigned getSizeForDecompression(unsigned uncompressed_size, unsigned extra = 0); - void alloc(upx_uint64_t size); + void alloc(upx_uint64_t bytes); void allocForCompression(unsigned uncompressed_size, unsigned extra = 0); void allocForDecompression(unsigned uncompressed_size, unsigned extra = 0); @@ -96,7 +97,7 @@ public: void checkState() const; unsigned getSize() const { return size_in_bytes; } - // explicit converstion + // explicit conversion void *getVoidPtr() { return (void *) ptr; } const void *getVoidPtr() const { return (const void *) ptr; } @@ -119,6 +120,7 @@ private: // static debug stats struct Stats { upx_std_atomic(upx_uint32_t) global_alloc_counter; + upx_std_atomic(upx_uint32_t) global_dealloc_counter; upx_std_atomic(upx_uint64_t) global_total_bytes; upx_std_atomic(upx_uint64_t) global_total_active_bytes; }; diff --git a/src/util/util.cpp b/src/util/util.cpp index df7d1205..f842b9a6 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -46,7 +46,8 @@ ACC_COMPILE_TIME_ASSERT_HEADER(UPX_RSIZE_MAX_MEM == UPX_RSIZE_MAX) ACC_COMPILE_TIME_ASSERT_HEADER(UPX_RSIZE_MAX_STR <= UPX_RSIZE_MAX / 256) -ACC_COMPILE_TIME_ASSERT_HEADER(2ull * UPX_RSIZE_MAX * 9 / 8 + 16 * 1024 * 1024 < INT_MAX) +ACC_COMPILE_TIME_ASSERT_HEADER(2ull * UPX_RSIZE_MAX * 9 / 8 + 256 * 1024 * 1024 < INT_MAX) +ACC_COMPILE_TIME_ASSERT_HEADER(2ull * UPX_RSIZE_MAX * 10 / 8 + 128 * 1024 * 1024 <= INT_MAX + 1u) ACC_COMPILE_TIME_ASSERT_HEADER(5ull * UPX_RSIZE_MAX < UINT_MAX) ACC_COMPILE_TIME_ASSERT_HEADER(UPX_RSIZE_MAX >= 8192 * 65536) ACC_COMPILE_TIME_ASSERT_HEADER(UPX_RSIZE_MAX_STR >= 1024) diff --git a/src/util/xspan_impl_common.h b/src/util/xspan_impl_common.h index abe2d6f5..fb171ca9 100644 --- a/src/util/xspan_impl_common.h +++ b/src/util/xspan_impl_common.h @@ -95,6 +95,12 @@ forceinline pointer ensureBase() const { public: inline ~CSelf() {} +void destroy() { + assertInvariants(); + base = ptr; + size_in_bytes = 0; + assertInvariants(); +} // constructors from pointers CSelf(pointer first, XSpanCount count) : ptr(makePtr(first)), base(makeBase(first)), size_in_bytes(xspan_mem_size(count.count)) {