From 67a77f18809d4d3fa8ae29f23026e351419a31a3 Mon Sep 17 00:00:00 2001 From: John Reiser Date: Sun, 20 Feb 2005 01:46:38 +0000 Subject: [PATCH] Packer for Macintosh Darwin Mach-o PowerPC32 Added Files: p_mach.cpp p_mach.h committer: jreiser 1108863998 +0000 --- src/p_mach.cpp | 376 +++++++++++++++++++++++++++++++++++++++++++++++++ src/p_mach.h | 207 +++++++++++++++++++++++++++ 2 files changed, 583 insertions(+) create mode 100644 src/p_mach.cpp create mode 100644 src/p_mach.h diff --git a/src/p_mach.cpp b/src/p_mach.cpp new file mode 100644 index 00000000..135e65c0 --- /dev/null +++ b/src/p_mach.cpp @@ -0,0 +1,376 @@ +/* p_mach.cpp -- pack Mach Object executable + + This file is part of the UPX executable compressor. + + Copyright (C) 2004 John Reiser + 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. + + John Reiser + jreiser@users.sourceforge.net + */ + + +#include "conf.h" + +#include "file.h" +#include "filter.h" +#include "linker.h" +#include "packer.h" +#include "p_mach.h" + +static const +#include "stub/l_mac_ppc32.h" + +static const +#include "stub/fold_machppc32.h" + +PackMachPPC32::PackMachPPC32(InputFile *f) : + super(f), n_segment(0), rawmseg(0), msegcmd(0) +{ +} + +PackMachPPC32::~PackMachPPC32() +{ + delete [] msegcmd; + delete [] rawmseg; +} + +const int *PackMachPPC32::getCompressionMethods(int /*method*/, int /*level*/) const +{ + // There really is no LE bias in M_NRV2E_LE32. + static const int m_nrv2e[] = { M_NRV2E_LE32, -1 }; + return m_nrv2e; +} + + +const int *PackMachPPC32::getFilters() const +{ + static const int filters[] = { 0xd0, -1 }; + return filters; +} + +int +PackMachPPC32::buildMachLoader( + upx_byte const *const proto, + unsigned const szproto, + upx_byte const *const fold, + unsigned const szfold, + Filter const */*ft*/ +) +{ + int eof_empty = -1; + initLoader(&eof_empty, 4096, 0, 0); + + struct b_info h; memset(&h, 0, sizeof(h)); + unsigned fold_hdrlen = 0; + if (0 < szfold) { + h.sz_unc = (szfold < fold_hdrlen) ? 0 : (szfold - fold_hdrlen); + h.b_method = (unsigned char) ph.method; + h.b_ftid = (unsigned char) ph.filter; + h.b_cto8 = (unsigned char) ph.filter_cto; + } + unsigned char const *const uncLoader = fold_hdrlen + fold; + + unsigned char *const cprLoader = new unsigned char[sizeof(h) + h.sz_unc]; + if (0 < szfold) { + unsigned sz_cpr; + int r = upx_compress(uncLoader, h.sz_unc, sizeof(h) + cprLoader, &sz_cpr, + NULL, ph.method, 10, NULL, NULL ); + h.sz_cpr = sz_cpr; + if (r != UPX_E_OK || h.sz_cpr >= h.sz_unc) + throwInternalError("loader compression failed"); + } + memcpy(cprLoader, &h, sizeof(h)); + + // This adds the definition to the "library", to be used later. + linker->addSection("FOLDEXEC", cprLoader, sizeof(h) + h.sz_cpr); + delete [] cprLoader; + + int const GAP = 128; // must match stub/l_mac_ppc.S + segcmdo.vmsize += h.sz_unc - h.sz_cpr + GAP + 64; + + linker->addSection("MACOS000", proto, szproto); + + addLoader("MACOS000", 0); + addLoader("FOLDEXEC", 0); + return getLoaderSize(); +} + +int +PackMachPPC32::buildLoader(const Filter *ft) +{ + return buildMachLoader( + l_mac_ppc32_loader, sizeof(l_mac_ppc32_loader), + fold_machppc32, sizeof(fold_machppc32), ft ); +} +void PackMachPPC32::patchLoader() { } +void PackMachPPC32::updateLoader(OutputFile *) {} + +void +PackMachPPC32::patchLoaderChecksum() +{ + unsigned char *const ptr = const_cast(getLoader()); + l_info *const lp = &linfo; + // checksum for loader; also some PackHeader info + lp->l_checksum = 0; + lp->l_magic = UPX_MAGIC_LE32; // LE32 always + lp->l_lsize = (unsigned short) lsize; + lp->l_version = (unsigned char) ph.version; + lp->l_format = (unsigned char) ph.format; + // INFO: lp->l_checksum is currently unused + lp->l_checksum = upx_adler32(ptr, lsize); +} + +static int __acc_cdecl_qsort +compare_segment_command(void const *const aa, void const *const bb) +{ + Mach_segment_command const *const a = (Mach_segment_command const *)aa; + Mach_segment_command const *const b = (Mach_segment_command const *)bb; + unsigned const xa = a->cmd - Mach_segment_command::LC_SEGMENT; + unsigned const xb = b->cmd - Mach_segment_command::LC_SEGMENT; + if (xa < xb) return -1; // LC_SEGMENT first + if (xa > xb) return 1; + if (a->vmaddr < b->vmaddr) return -1; // ascending by .vmaddr + if (a->vmaddr > b->vmaddr) return 1; + return 0; +} + +void +PackMachPPC32::pack4(OutputFile *fo, Filter &ft) // append PackHeader +{ + // offset of p_info in compressed file + overlay_offset = sizeof(mhdro) + sizeof(segcmdo) + sizeof(threado) + sizeof(linfo); + + super::pack4(fo, ft); + segcmdo.filesize = fo->getBytesWritten(); + segcmdo.vmsize += segcmdo.filesize; + fo->seek(sizeof(mhdro), SEEK_SET); + fo->write(&segcmdo, sizeof(segcmdo)); + fo->write(&threado, sizeof(threado)); + fo->write(&linfo, sizeof(linfo)); +} + +void +PackMachPPC32::pack3(OutputFile *fo, Filter &ft) // append loader +{ + BE32 disp; + unsigned const zero = 0; + unsigned len = fo->getBytesWritten(); + fo->write(&zero, 3& -len); + len += (3& -len) + sizeof(disp); + set_be32(&disp, 4+ len - sz_mach_headers); // 4: sizeof(instruction) + fo->write(&disp, sizeof(disp)); + + threado.state.srr0 = len + segcmdo.vmaddr; /* entry address */ + super::pack3(fo, ft); +} + +void +PackMachPPC32::pack2(OutputFile *fo, Filter &ft) // append compressed body +{ + Extent x; + unsigned k; + + ui_total_passes = n_segment; + + // compress extents + unsigned total_in = 0; + unsigned total_out = 0; + + ui_pass = -1; // Compressing Mach headers is invisible to UI. + x.offset = 0; + x.size = mhdri.sizeofcmds; + { + int const old_level = ph.level; ph.level = 10; + packExtent(x, total_in, total_out, 0, fo); + ph.level = old_level; + } + + ui_pass = 0; + ft.addvalue = 0; + + int nx = 0; + for (k = 0; k < n_segment; ++k) + if (Mach_segment_command::LC_SEGMENT==msegcmd[k].cmd + && 0!=msegcmd[k].filesize ) { + x.offset = msegcmd[k].fileoff; + x.size = msegcmd[k].filesize; + if (0 == nx) { // 1st LC_SEGMENT must cover Mach_header at 0==fileoffset + unsigned const delta = mhdri.sizeofcmds; + x.offset += delta; + x.size -= delta; + } + packExtent(x, total_in, total_out, + ((Mach_segment_command::VM_PROT_EXECUTE & msegcmd[k].initprot) + ? &ft : 0 ), fo ); + ++nx; + } + if ((off_t)total_in < file_size) { // non-LC_SEGMENT stuff + x.offset = total_in; + x.size = file_size - total_in; + packExtent(x, total_in, total_out, 0, fo); + } + + if ((off_t)total_in != file_size) + throwEOFException(); + segcmdo.filesize = fo->getBytesWritten(); +} + +#define PAGE_MASK (~0u<<12) +#define PAGE_SIZE -PAGE_MASK +void +PackMachPPC32::pack1(OutputFile *fo, Filter &/*ft*/) // generate executable header +{ + mhdro = mhdri; + mhdro.ncmds = 2; + mhdro.sizeofcmds = sizeof(segcmdo) + sizeof(threado); + mhdro.flags = Mach_header::MH_NOUNDEFS; + fo->write(&mhdro, sizeof(mhdro)); + + segcmdo.cmd = Mach_segment_command::LC_SEGMENT; + segcmdo.cmdsize = sizeof(segcmdo); + strncpy((char *)&segcmdo.segname, "__TEXT", sizeof(segcmdo.segname)); + segcmdo.vmaddr = PAGE_MASK & (~PAGE_MASK + + msegcmd[n_segment -1].vmsize + msegcmd[n_segment -1].vmaddr ); + segcmdo.vmsize = 0; // adjust later + segcmdo.fileoff = 0; + segcmdo.filesize = 0; // adjust later + segcmdo.initprot = segcmdo.maxprot = + Mach_segment_command::VM_PROT_READ | + Mach_segment_command::VM_PROT_WRITE | + Mach_segment_command::VM_PROT_EXECUTE; + segcmdo.nsects = 0; + segcmdo.flags = 0; + fo->write(&segcmdo, sizeof(segcmdo)); + + threado.cmd = Mach_segment_command::LC_UNIXTHREAD; + threado.cmdsize = sizeof(threado); + threado.flavor = Mach_thread_command::PPC_THREAD_STATE; + threado.count = Mach_thread_command::PPC_THREAD_STATE_COUNT; + memset(&threado.state, 0, sizeof(threado.state)); + fo->write(&threado, sizeof(threado)); + sz_mach_headers = fo->getBytesWritten(); + + memset((char *)&linfo, 0, sizeof(linfo)); + fo->write(&linfo, sizeof(linfo)); + + return; +} + +void +PackMachPPC32::unpack(OutputFile *fo) +{ + fi->seek(overlay_offset, SEEK_SET); + p_info hbuf; + fi->readx(&hbuf, sizeof(hbuf)); + unsigned orig_file_size = get_native32(&hbuf.p_filesize); + blocksize = get_native32(&hbuf.p_blocksize); + if (file_size > (off_t)orig_file_size || blocksize > orig_file_size) + throwCantUnpack("file header corrupted"); + + ibuf.alloc(blocksize + OVERHEAD); + b_info bhdr; memset(&bhdr, 0, sizeof(bhdr)); + fi->readx(&bhdr, sizeof(bhdr)); + ph.u_len = get_native32(&bhdr.sz_unc); + ph.c_len = get_native32(&bhdr.sz_cpr); + ph.filter_cto = bhdr.b_cto8; + + // Uncompress Macho headers + fi->readx(ibuf, ph.c_len); + Mach_header *const mhdr = (Mach_header *)new upx_byte[ph.u_len]; + decompress(ibuf, (upx_byte *)mhdr, false); + + unsigned total_in = 0; + unsigned total_out = 0; + unsigned c_adler = upx_adler32(NULL, 0); + unsigned u_adler = upx_adler32(NULL, 0); + Mach_segment_command const *sc = (Mach_segment_command const *)(1+ mhdr); + unsigned k; + + fi->seek(- (off_t)(sizeof(bhdr) + ph.c_len), SEEK_CUR); + for ( + k = 0; + k < mhdr->ncmds; + (++k), (sc = (Mach_segment_command const *)(sc->cmdsize + (char const *)sc)) + ) { + if (Mach_segment_command::LC_SEGMENT==sc->cmd + && 0!=sc->filesize ) { + unsigned filesize = get_be32(&sc->filesize); + unpackExtent(filesize, fo, total_in, total_out, c_adler, u_adler, false, sizeof(bhdr)); + } + } + unsigned const rest = orig_file_size - total_out; + if (0!=rest) { // non-LC_SEGMENT stuff + if (fo) + fo->seek(0, SEEK_END); + unpackExtent(rest, fo, total_in, total_out, c_adler, u_adler, false, sizeof(bhdr)); + } +} + + +bool +PackMachPPC32::canPack() +{ + fi->seek(0, SEEK_SET); + fi->readx(&mhdri, sizeof(mhdri)); + + if (Mach_header::MH_MAGIC !=mhdri.magic + || Mach_header::CPU_TYPE_POWERPC !=mhdri.cputype + || Mach_header::MH_EXECUTE !=mhdri.filetype + ) + return false; + + rawmseg = (Mach_segment_command *)new char[mhdri.sizeofcmds]; + fi->readx(rawmseg, mhdri.sizeofcmds); + + msegcmd = new Mach_segment_command[mhdri.ncmds]; + unsigned char *ptr = (unsigned char *)rawmseg; + for (unsigned j= 0; j < mhdri.ncmds; ++j) { + msegcmd[j] = *(Mach_segment_command *)ptr; + ptr += ((Mach_segment_command *)ptr)->cmdsize; + } + + + // Put LC_SEGMENT together at the beginning, ascending by .vmaddr. + qsort(msegcmd, mhdri.ncmds, sizeof(*msegcmd), compare_segment_command); + + // Check that LC_SEGMENTs form one contiguous chunk of the file. + for (unsigned j= 0; j < mhdri.ncmds; ++j) { + if (Mach_segment_command::LC_SEGMENT==msegcmd[j].cmd) { + if (0xfff & (msegcmd[j].fileoff | msegcmd[j].vmaddr)) { + return false; + } + if (0 < j) { + unsigned const sz = ~0xfff + & (0xfff + msegcmd[j-1].filesize); + if ((sz + msegcmd[j-1].fileoff)!=msegcmd[j].fileoff) { + return false; + } + } + ++n_segment; + sz_segment = msegcmd[j].filesize + msegcmd[j].fileoff - msegcmd[0].fileoff; + } + } + // set options + opt->o_unix.blocksize = file_size; + return 0 < n_segment; +} + +/* +vi:ts=4:et +*/ diff --git a/src/p_mach.h b/src/p_mach.h new file mode 100644 index 00000000..0d1f0d7e --- /dev/null +++ b/src/p_mach.h @@ -0,0 +1,207 @@ +/* p_mach.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2004 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2004 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@cdata.tvnet.hu + */ + + +#ifndef __UPX_P_MACHO_H +#define __UPX_P_MACHO_H + +/************************************************************************* +// Mach Mach Object executable +**************************************************************************/ + +struct Mach_header { + BE32 magic; + enum { + MH_MAGIC = 0xfeedface + }; + BE32 cputype; + enum { + CPU_TYPE_POWERPC = 18 + }; + BE32 cpysubtype; + BE32 filetype; + enum { + MH_EXECUTE = 2 + }; + BE32 ncmds; + BE32 sizeofcmds; + BE32 flags; + enum { + MH_NOUNDEFS = 1 + }; +}; + +struct Mach_segment_command { + BE32 cmd; + enum { + LC_SEGMENT = 0x1, + LC_THREAD = 0x4, + LC_UNIXTHREAD = 0x5, + LC_LOAD_DYLINKER = 0xe + }; + BE32 cmdsize; + char segname[16]; + BE32 vmaddr; + BE32 vmsize; + BE32 fileoff; + BE32 filesize; + BE32 maxprot; + enum { + VM_PROT_READ = 1, + VM_PROT_WRITE = 2, + VM_PROT_EXECUTE = 4 + }; + BE32 initprot; + BE32 nsects; + BE32 flags; +}; + +struct Mach_section { + char sectname[16]; + char segname[16]; + BE32 addr; /* memory address */ + BE32 size; /* size in bytes */ + BE32 offset; /* file offset */ + BE32 align; /* power of 2 */ + BE32 reloff; /* file offset of relocation entries */ + BE32 nreloc; /* number of relocation entries */ + BE32 flags; /* section type and attributes */ + enum { + S_REGULAR = 0, + S_ZEROFILL, + S_CSTRING_LITERALS, + S_4BYTE_LITERALS, + S_8BYTE_LITERALS, + S_LITERAL_POINTERS, + S_NON_LAZY_SYMBOL_POINTERS, + S_LAZY_SYMBOL_POINTERS, + S_SYMBOL_STUBS, + S_MOD_INIT_FUNC_POINTERS, + S_MOD_TERM_FNC_POINTERS, + S_COALESCED + }; + enum { + S_ATTR_PURE_INSTRUCTIONS = 0x80000000, + S_ATTR_NO_TOC = 0x40000000, + S_ATTR_STRIP_STATIC_SYMS = 0x20000000, + S_ATTR_SOME_INSTRUCTIONS = 0x00000400, + S_ATTR_EXT_RELOC = 0x00000200, + S_ATTR_LOC_RELOC = 0x00000100 + }; + BE32 reserved1; + BE32 reserved2; + +}; + +struct Mach_ppc_thread_state { + BE32 srr0; /* Instruction address register (PC; entry addr) */ + BE32 srr1; /* Machine state register (supervisor) */ + BE32 r0, r1, r2, r3, r4, r5, r6, r7; + BE32 r8, r9,r10,r11,r12,r13,r14,r15; + BE32 r16,r17,r18,r19,r20,r21,r22,r23; + BE32 r24,r25,r26,r27,r28,r29,r30,r31; + + BE32 cr; /* Condition register */ + BE32 xer; /* User's integer exception register */ + BE32 lr; /* Link register */ + BE32 ctr; /* Count register */ + BE32 mq; /* MQ register (601 only) */ + + BE32 vrsave; /* Vector Save Register */ +}; + +struct Mach_thread_command { + BE32 cmd; /* LC_THREAD or LC_UNIXTHREAD */ + BE32 cmdsize; /* total size of this command */ + BE32 flavor; + enum { + PPC_THREAD_STATE = 1 + }; + BE32 count; /* sizeof(following_thread_state)/4 */ + enum { + PPC_THREAD_STATE_COUNT = sizeof(struct Mach_ppc_thread_state)/4 + }; + struct Mach_ppc_thread_state state; +}; + +#include "p_unix.h" + +class PackMachPPC32 : public PackUnixBe32 +{ + typedef PackUnixBe32 super; +public: + PackMachPPC32(InputFile *f); + virtual ~PackMachPPC32(); + virtual int getVersion() const { return 13; } + virtual int getFormat() const { return UPX_F_MACH_PPC32; } + virtual const char *getName() const { return "Mach/ppc32"; } + virtual const int *getCompressionMethods(int method, int level) const; + virtual const int *getFilters() const; + + // called by the generic pack() + virtual void pack1(OutputFile *, Filter &); // generate executable header + virtual void pack2(OutputFile *, Filter &); // append compressed data + virtual void pack3(OutputFile *, Filter &); // append loader + virtual void pack4(OutputFile *, Filter &); // append PackHeader + + virtual void unpack(OutputFile *fo); + + virtual bool canPack(); + +protected: + virtual int buildLoader(const Filter *ft); + virtual void patchLoader(); + virtual void patchLoaderChecksum(); + virtual void updateLoader(OutputFile *); + virtual int buildMachLoader( + upx_byte const *const proto, + unsigned const szproto, + upx_byte const *const fold, + unsigned const szfold, + Filter const *ft ); + + unsigned n_segment; + unsigned sz_segment; + unsigned sz_mach_headers; + Mach_segment_command *rawmseg; // as input, with sections + Mach_segment_command *msegcmd; // LC_SEGMENT first, without sections + Mach_header mhdri; + + Mach_header mhdro; + Mach_segment_command segcmdo; + Mach_thread_command threado; + struct l_info linfo; +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ +