Fix de-compression for Mach-O files (MacOS)

https://github.com/upx/upx/issues/219
	modified:   p_mach.cpp
	modified:   p_mach.h
This commit is contained in:
John Reiser
2018-09-08 11:41:38 -07:00
parent 5963f18f7c
commit 597e8fe407
2 changed files with 72 additions and 22 deletions
+71 -22
View File
@@ -1502,6 +1502,9 @@ void PackMachBase<T>::unpack(OutputFile *fo)
fi->seek(- (off_t)(sizeof(bhdr) + ph.c_len), SEEK_CUR); fi->seek(- (off_t)(sizeof(bhdr) + ph.c_len), SEEK_CUR);
for (unsigned k = 0; k < ncmds; ++k) { for (unsigned k = 0; k < ncmds; ++k) {
if (msegcmd[k].cmd==lc_seg && msegcmd[k].filesize!=0) { if (msegcmd[k].cmd==lc_seg && msegcmd[k].filesize!=0) {
if (!strcmp("__TEXT", msegcmd[k].segname)) {
segTEXT = msegcmd[k];
}
if (fo) if (fo)
fo->seek(msegcmd[k].fileoff, SEEK_SET); fo->seek(msegcmd[k].fileoff, SEEK_SET);
unpackExtent(msegcmd[k].filesize, fo, total_in, total_out, unpackExtent(msegcmd[k].filesize, fo, total_in, total_out,
@@ -1513,6 +1516,8 @@ void PackMachBase<T>::unpack(OutputFile *fo)
} }
Mach_segment_command const *sc = (Mach_segment_command const *)(void *)(1+ mhdr); Mach_segment_command const *sc = (Mach_segment_command const *)(void *)(1+ mhdr);
if (my_filetype==Mach_header::MH_DYLIB) { // rest of lc_seg are not compressed if (my_filetype==Mach_header::MH_DYLIB) { // rest of lc_seg are not compressed
upx_uint64_t cpr_mod_init_func(0);
TE32 unc_mod_init_func; *(int *)&unc_mod_init_func = 0;
Mach_segment_command const *rc = rawmseg; Mach_segment_command const *rc = rawmseg;
rc = (Mach_segment_command const *)(rc->cmdsize + (char const *)rc); rc = (Mach_segment_command const *)(rc->cmdsize + (char const *)rc);
sc = (Mach_segment_command const *)(sc->cmdsize + (char const *)sc); sc = (Mach_segment_command const *)(sc->cmdsize + (char const *)sc);
@@ -1524,12 +1529,20 @@ void PackMachBase<T>::unpack(OutputFile *fo)
) { ) {
if (lc_seg==rc->cmd if (lc_seg==rc->cmd
&& 0!=rc->filesize ) { && 0!=rc->filesize ) {
if (!strcmp("__DATA", rc->segname)) {
cpr_mod_init_func = get_mod_init_func(rc);
fi->seek(cpr_mod_init_func - 4*sizeof(TE32), SEEK_SET);
fi->readx(&unc_mod_init_func, sizeof(unc_mod_init_func));
}
fi->seek(rc->fileoff, SEEK_SET); fi->seek(rc->fileoff, SEEK_SET);
if (fo) if (fo)
fo->seek(sc->fileoff, SEEK_SET); fo->seek(sc->fileoff, SEEK_SET);
unsigned const len = rc->filesize; unsigned const len = rc->filesize;
MemBuffer data(len); MemBuffer data(len);
fi->readx(data, len); fi->readx(data, len);
if (!strcmp("__DATA", rc->segname)) {
set_te32(&data[o__mod_init_func - rc->fileoff], unc_mod_init_func);
}
if (fo) if (fo)
fo->write(data, len); fo->write(data, len);
} }
@@ -1565,6 +1578,7 @@ int PackMachBase<T>::canUnpack()
my_cpusubtype = mhdri.cpusubtype; my_cpusubtype = mhdri.cpusubtype;
int headway = (int)mhdri.sizeofcmds; int headway = (int)mhdri.sizeofcmds;
sz_mach_headers = headway + sizeof(mhdri);
if (1024 < headway) { if (1024 < headway) {
infoWarning("Mach_header.sizeofcmds(%d) > 1024", headway); infoWarning("Mach_header.sizeofcmds(%d) > 1024", headway);
} }
@@ -1632,11 +1646,16 @@ int PackMachBase<T>::canUnpack()
if (391 == style) { // PackHeader precedes __LINKEDIT if (391 == style) { // PackHeader precedes __LINKEDIT
fi->seek(offLINK - bufsize, SEEK_SET); fi->seek(offLINK - bufsize, SEEK_SET);
} else } else
if (392 == style) { // PackHeader follows loader at __LINKEDIT if (392 == style) {
if ((off_t)bufsize > (fi->st_size() - offLINK)) { if (MH_DYLIB == my_filetype) {
bufsize = fi->st_size() - offLINK; fi->seek(fi->st_size() - bufsize, SEEK_SET);
}
else { // PackHeader follows loader at __LINKEDIT
if ((off_t)bufsize > (fi->st_size() - offLINK)) {
bufsize = fi->st_size() - offLINK;
}
fi->seek(offLINK, SEEK_SET);
} }
fi->seek(offLINK, SEEK_SET);
} else } else
if (395 == style) { if (395 == style) {
fi->seek(offLINK - bufsize - sizeof(PackHeader), SEEK_SET); fi->seek(offLINK - bufsize - sizeof(PackHeader), SEEK_SET);
@@ -1735,24 +1754,64 @@ int PackMachBase<T>::canUnpack()
} }
} }
} }
overlay_offset = 0;
} }
} }
overlay_offset = 0; // impossible value
int l = ph.buf_offset + ph.getPackHeaderSize(); int l = ph.buf_offset + ph.getPackHeaderSize();
if (l < 0 || (unsigned)(l + 4) > bufsize) if (0 <= l && (unsigned)(l + sizeof(TE32)) <=bufsize) {
overlay_offset = get_te32(buf + i + l);
}
if ( overlay_offset < sz_mach_headers
|| (off_t)overlay_offset >= file_size) {
infoWarning("file corrupted");
MemBuffer buf2(umin(1<<14, file_size));
fi->seek(sz_mach_headers, SEEK_SET);
fi->readx(buf2, buf2.getSize());
unsigned const *p = (unsigned const *)&buf2[0];
unsigned const *const e_buf2 = (unsigned const *)&buf2[buf2.getSize() - 4*sizeof(*p)];
for (; p <= e_buf2; ++p)
if ( 0==p[0] // p_info.p_progid
&& 0!=p[1] // p_info.p_filesize
&& p[2]==p[1] // p_info.p_blocksize == p_info.p_filesize
&& file_size < get_te32(&p[1]) // compression was worthwhile
&& sz_mach_headers==get_te32(&p[3]) // b_info.sz_unc
) {
overlay_offset = ((char const *)p - (char const *)&buf2[0]) + sz_mach_headers;
if (!(3&overlay_offset // not word aligned
|| overlay_offset < sz_mach_headers
|| (off_t)overlay_offset >= file_size)) {
infoWarning("attempting recovery, overlay_offset = %#x", overlay_offset);
return true;
}
}
throwCantUnpack("file corrupted"); throwCantUnpack("file corrupted");
overlay_offset = get_te32(buf + i + l); }
if ((off_t)overlay_offset >= file_size)
throwCantUnpack("file corrupted");
return true; return true;
} }
#define WANT_MACH_SEGMENT_ENUM #define WANT_MACH_SEGMENT_ENUM
#define WANT_MACH_SECTION_ENUM #define WANT_MACH_SECTION_ENUM
#include "p_mach_enum.h" #include "p_mach_enum.h"
template <class T>
upx_uint64_t PackMachBase<T>::get_mod_init_func(Mach_segment_command const *segptr)
{
for (Mach_section_command const *secptr = (Mach_section_command const *)(1+ segptr);
ptr_udiff(secptr, segptr) < segptr->cmdsize;
++secptr
) {
if (sizeof(Addr) == secptr->size
&& !strcmp("__mod_init_func", secptr->sectname)) {
o__mod_init_func = secptr->offset;
fi->seek(o__mod_init_func, SEEK_SET);
Addr tmp;
fi->readx(&tmp, sizeof(Addr));
return tmp;
}
}
return 0;
}
template <class T> template <class T>
bool PackMachBase<T>::canPack() bool PackMachBase<T>::canPack()
{ {
@@ -1797,17 +1856,7 @@ bool PackMachBase<T>::canPack()
if (lc_seg == segptr->cmd) { if (lc_seg == segptr->cmd) {
msegcmd[j] = *segptr; msegcmd[j] = *segptr;
if (!strcmp("__DATA", segptr->segname)) { if (!strcmp("__DATA", segptr->segname)) {
for (Mach_section_command const *secptr = (Mach_section_command const *)(1+ segptr); prev_mod_init_func = get_mod_init_func(segptr);
ptr_udiff(secptr, segptr) < segptr->cmdsize;
++secptr
) {
if (sizeof(Addr) == secptr->size
&& !strcmp("__mod_init_func", secptr->sectname)) {
o__mod_init_func = secptr->offset;
fi->seek(o__mod_init_func, SEEK_SET);
fi->readx(&prev_mod_init_func, sizeof(Addr));
}
}
} }
} }
else { else {
+1
View File
@@ -780,6 +780,7 @@ public:
virtual bool canPack(); virtual bool canPack();
virtual int canUnpack(); virtual int canUnpack();
virtual upx_uint64_t get_mod_init_func(Mach_segment_command const *segptr);
virtual unsigned find_SEGMENT_gap(unsigned const k, unsigned pos_eof); virtual unsigned find_SEGMENT_gap(unsigned const k, unsigned pos_eof);
protected: protected: