From 05976ee1a74c3ef88b05454ad67aff7f61cde98e Mon Sep 17 00:00:00 2001 From: John Reiser Date: Tue, 31 May 2022 11:52:10 -0700 Subject: [PATCH] Try harder to de-compress corrupted Mach-o file. https://github.com/upx/upx/issues/579 modified: p_mach.cpp --- src/p_mach.cpp | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/p_mach.cpp b/src/p_mach.cpp index 48fc1e32..286b893f 100644 --- a/src/p_mach.cpp +++ b/src/p_mach.cpp @@ -1735,8 +1735,11 @@ int PackMachBase::canUnpack() fi->seek(offLINK - bufsize - sizeof(PackHeader), SEEK_SET); } MemBuffer buf(bufsize); + MemBuffer buf3(bufsize); fi->readx(buf, bufsize); + // Do not overwrite buf[]; For scratch space, then use buf3 instead. + int i = bufsize; while (i > small && 0 == buf[--i]) { } i -= small; @@ -1747,11 +1750,13 @@ int PackMachBase::canUnpack() upx_uint64_t const rip_off = ptrTEXT ? (rip - ptrTEXT->vmaddr) : 0; if (ptrTEXT && rip && rip_off < ptrTEXT->vmsize) { fi->seek(ptrTEXT->fileoff + rip_off, SEEK_SET); - fi->readx(buf, bufsize); - unsigned char const *b = &buf[0]; + fi->readx(buf3, bufsize); + unsigned char const *b = &buf3[0]; unsigned disp = *(TE32 const *)&b[1]; // Emulate the code if (0xe8==b[0] && disp < bufsize + // This has been obsoleted by amd64-darwin.macho-entry.S + // searching for "executable_path=" etc. && 0x5d==b[5+disp] && 0xe8==b[6+disp]) { unsigned disp2 = 0u - *(TE32 const *)&b[7+disp]; if (disp2 < (12+disp) && 0x5b==b[11+disp-disp2]) { @@ -1769,6 +1774,31 @@ int PackMachBase::canUnpack() } } } + if (395 == style) { // Desperation + infoWarning("file corrupted"); + fi->seek(file_size - bufsize, SEEK_SET); + fi->readx(buf3, bufsize); + unsigned const *p = (unsigned const *)&buf3[bufsize]; + for (; buf3 < (void const *)--p; ) { + unsigned x = *p; + if (x) { + if (!(3& x) && x < bufsize) { + fi->seek(0, SEEK_SET); + fi->readx(buf3, bufsize); + p = (unsigned const *)&buf3[x]; + if (0 == p[0] && 0 != p[1] && p[1] == p[2] // p_info + && sz_mach_headers < p[3] && p[4] < p[3] // b_info + ) { + overlay_offset = x; + infoWarning("attempting recovery, overlay_offset = %#x", + overlay_offset); + return true; + } + } + break; + } + } + } } if (391==style) { TE32 const *uptr = (TE32 const *)&buf[bufsize]; @@ -1786,8 +1816,8 @@ int PackMachBase::canUnpack() // 0== .p_progid // .p_filesize == .p_blocksize fi->seek(overlay_offset, SEEK_SET); - fi->readx(buf, bufsize); - struct p_info const *const p_ptr = (struct p_info const *)&buf[0]; + fi->readx(buf3, bufsize); + struct p_info const *const p_ptr = (struct p_info const *)&buf3[0]; struct b_info const *const b_ptr = (struct b_info const *)(1+ p_ptr); TE32 const *uptr = (TE32 const *)(1+ b_ptr); if (b_ptr->sz_unc < 0x4000 @@ -1812,16 +1842,16 @@ int PackMachBase::canUnpack() // is the total length of compressed data which precedes it //(distance to l_info), so that's another method. fi->seek(offLINK - 0x1000, SEEK_SET); - fi->readx(buf, 0x1000); - unsigned const *const lo = (unsigned const *)&buf[0]; + fi->readx(buf3, 0x1000); + unsigned const *const lo = (unsigned const *)&buf3[0]; unsigned const *p; - for (p = (unsigned const *)&buf[0x1000]; p > lo; ) if (*--p) { + for (p = (unsigned const *)&buf3[0x1000]; p > lo; ) if (*--p) { overlay_offset = *(TE32 const *)p; if ((off_t)overlay_offset < offLINK) { overlay_offset = ((char const *)p - (char const *)lo) + (offLINK - 0x1000) - overlay_offset + sizeof(l_info); fi->seek(overlay_offset, SEEK_SET); - fi->readx(buf, bufsize); + fi->readx(buf3, bufsize); if (b_ptr->sz_unc < 0x4000 && b_ptr->sz_cpr < b_ptr->sz_unc ) { return true;