diff --git a/src/p_elf.h b/src/p_elf.h index 8ee1ad09..8933caf9 100644 --- a/src/p_elf.h +++ b/src/p_elf.h @@ -138,6 +138,7 @@ struct Phdr PT_LOAD = 1, /* Loadable program segment */ PT_DYNAMIC = 2, /* Dynamic linking information */ PT_INTERP = 3, /* Name of program interpreter */ + PT_NOTE = 4, /* Auxiliary information (esp. OpenBSD) */ PT_PHDR = 6, /* Entry for header table itself */ }; diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index 4e0d0bef..1a34b4ff 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -622,6 +622,36 @@ PackBSDElf32x86::buildLoader(const Filter *ft) tmp, sizeof(bsd_i386elf_fold), ft ); } +#if 0 //{ re-use for OpenBSD, too +static const +#include "stub/i386-bsd.elf-entry.h" +#endif //} +static const +#include "stub/i386-openbsd.elf-fold.h" + +int +PackOpenBSDElf32x86::buildLoader(const Filter *ft) +{ + unsigned char tmp[sizeof(openbsd_i386elf_fold)]; + memcpy(tmp, openbsd_i386elf_fold, sizeof(openbsd_i386elf_fold)); + checkPatch(NULL, 0, 0, 0); // reset + if (opt->o_unix.is_ptinterp) { + unsigned j; + for (j = 0; j < sizeof(openbsd_i386elf_fold)-1; ++j) { + if (0x60==tmp[ j] + && 0x47==tmp[1+j] ) { + /* put INC EDI before PUSHA: inhibits auxv_up for PT_INTERP */ + tmp[ j] = 0x47; + tmp[1+j] = 0x60; + break; + } + } + } + return buildLinuxLoader( + bsd_i386elf_loader, sizeof(bsd_i386elf_loader), + tmp, sizeof(openbsd_i386elf_fold), ft ); +} + static const #include "stub/arm-linux.elf-entry.h" static const @@ -966,6 +996,7 @@ PackLinuxElf32::generateElfHdr( set_native32(&h2->phdr[1].p_paddr, brkb); h2->phdr[1].p_filesz = 0; h2->phdr[1].p_memsz = 0; + set_native32(&h2->phdr[1].p_flags, Elf32_Phdr::PF_R | Elf32_Phdr::PF_W); #undef PAGE_MASK } if (ph.format==getFormat()) { @@ -979,6 +1010,72 @@ PackLinuxElf32::generateElfHdr( } } +void +PackOpenBSDElf32x86::generateElfHdr( + OutputFile *fo, + void const *proto, + unsigned const brka +) +{ + cprElfHdr3 *const h3 = (cprElfHdr3 *)&elfout; + memcpy(h3, proto, sizeof(*h3)); // reads beyond, but OK + h3->ehdr.e_ident[Elf32_Ehdr::EI_OSABI] = ei_osabi; + assert(2==get_native16(&h3->ehdr.e_phnum)); + set_native16(&h3->ehdr.e_phnum, 3); + + assert(get_native32(&h3->ehdr.e_phoff) == sizeof(Elf32_Ehdr)); + h3->ehdr.e_shoff = 0; + assert(get_native16(&h3->ehdr.e_ehsize) == sizeof(Elf32_Ehdr)); + assert(get_native16(&h3->ehdr.e_phentsize) == sizeof(Elf32_Phdr)); + h3->ehdr.e_shentsize = 0; + h3->ehdr.e_shnum = 0; + h3->ehdr.e_shstrndx = 0; + +#if 0 //{ + unsigned identsize; + char const *const ident = getIdentstr(&identsize); +#endif //} + sz_elf_hdrs = sizeof(*h3) - sizeof(linfo); + unsigned const note_offset = sz_elf_hdrs; + set_native32(&h3->phdr[0].p_filesz, sizeof(*h3)+sizeof(elfnote)); // + identsize; + h3->phdr[0].p_memsz = h3->phdr[0].p_filesz; + +#define PAGE_MASK (~0u<<12) + unsigned const brkb = brka | ((0==(~PAGE_MASK & brka)) ? 0x20 : 0); + set_native32(&h3->phdr[1].p_type, PT_LOAD32); // be sure + set_native32(&h3->phdr[1].p_offset, ~PAGE_MASK & brkb); + set_native32(&h3->phdr[1].p_vaddr, brkb); + set_native32(&h3->phdr[1].p_paddr, brkb); + h3->phdr[1].p_filesz = 0; + h3->phdr[1].p_memsz = 0; + set_native32(&h3->phdr[1].p_flags, Elf32_Phdr::PF_R | Elf32_Phdr::PF_W); +#undef PAGE_MASK + + set_native32(&h3->phdr[2].p_type, Elf32_Phdr::PT_NOTE); + set_native32(&h3->phdr[2].p_offset, note_offset); + set_native32(&h3->phdr[2].p_vaddr, note_offset); + set_native32(&h3->phdr[2].p_paddr, note_offset); + set_native32(&h3->phdr[2].p_filesz, sizeof(elfnote)); + set_native32(&h3->phdr[2].p_memsz, sizeof(elfnote)); + set_native32(&h3->phdr[2].p_flags, Elf32_Phdr::PF_R); + + set_native32(&elfnote.namesz, 8); + set_native32(&elfnote.descsz, 4); + set_native32(&elfnote.type, 1); + strcpy(elfnote.text, "OpenBSD"); + elfnote.end = 0; + + if (ph.format==getFormat()) { + memset(&h3->linfo, 0, sizeof(h3->linfo)); + fo->write(h3, sizeof(*h3) - sizeof(h3->linfo)); + fo->write(&elfnote, sizeof(elfnote)); + fo->write(&h3->linfo, sizeof(h3->linfo)); + } + else { + assert(false); // unknown ph.format, PackLinuxElf32 + } +} + void PackLinuxElf64::generateElfHdr( OutputFile *fo, @@ -1618,7 +1715,18 @@ void PackLinuxElf32::pack4(OutputFile *fo, Filter &ft) //elfout.phdr[0].p_flags |= Elf32_Phdr::PF_W; } fo->seek(0, SEEK_SET); - fo->rewrite(&elfout, sz_elf_hdrs); + if (Elf32_Phdr::PT_NOTE==get_native32(&elfout.phdr[2].p_type)) { + unsigned const reloc = get_native32(&elfout.phdr[0].p_vaddr); + set_native32( &elfout.phdr[2].p_vaddr, + reloc + get_native32(&elfout.phdr[2].p_vaddr)); + set_native32( &elfout.phdr[2].p_paddr, + reloc + get_native32(&elfout.phdr[2].p_paddr)); + fo->rewrite(&elfout, sz_elf_hdrs); + fo->rewrite(&elfnote, sizeof(elfnote)); + } + else { + fo->rewrite(&elfout, sz_elf_hdrs); + } fo->rewrite(&linfo, sizeof(linfo)); } diff --git a/src/p_lx_elf.h b/src/p_lx_elf.h index dfe7bd40..8bd333b6 100644 --- a/src/p_lx_elf.h +++ b/src/p_lx_elf.h @@ -150,6 +150,14 @@ protected: __attribute_packed; cprElfHdr3 elfout; + + struct Elf32_Note { + unsigned namesz; // 8 + unsigned descsz; // 4 + unsigned type; // 1 + char text[0x18 - 4*4]; // "OpenBSD" + unsigned end; // 0 + } elfnote; }; @@ -364,6 +372,14 @@ class PackOpenBSDElf32x86 : public PackBSDElf32x86 public: PackOpenBSDElf32x86(InputFile *f); virtual ~PackOpenBSDElf32x86(); + + virtual int buildLoader(const Filter *ft); +protected: + virtual void generateElfHdr( + OutputFile *, + void const *proto, + unsigned const brka + ); }; /************************************************************************* diff --git a/src/stub/Makefile b/src/stub/Makefile index 7d08a408..792a4b73 100644 --- a/src/stub/Makefile +++ b/src/stub/Makefile @@ -49,6 +49,7 @@ STUBS += i086-dos16.exe.h STUBS += i086-dos16.sys.h STUBS += i386-bsd.elf-entry.h STUBS += i386-bsd.elf-fold.h +STUBS += i386-openbsd.elf-fold.h STUBS += i386-dos32.djgpp2.h STUBS += i386-dos32.djgpp2-stubify.h STUBS += i386-dos32.tmt.h @@ -352,6 +353,41 @@ tmp/i386-bsd.elf-main.o : $(srcdir)/src/$$T.c $(call tc,gcc) -c $< -o $@ $(call tc,objstrip) $@ +i386-openbsd.elf% : tc_list = i386-openbsd.elf arch-i386 default +i386-openbsd.elf% : tc_bfdname = elf32-i386 + +tc.i386-openbsd.elf.gcc = i386-linux-gcc-3.4.6 -m32 -march=i386 -nostdinc -MMD +tc.i386-openbsd.elf.gcc += -fno-exceptions -fno-asynchronous-unwind-tables +tc.i386-openbsd.elf.gcc += -Wall -W -Wcast-align -Wcast-qual -Wwrite-strings -Werror +tc.i386-openbsd.elf.gcc += -march=i386 -mtune=k6 +tc.i386-openbsd.elf.gcc += -Os -fno-omit-frame-pointer +tc.i386-openbsd.elf.gcc += -momit-leaf-frame-pointer +tc.i386-openbsd.elf.gcc += -fno-align-functions -fno-align-jumps -fno-align-labels -fno-align-loops +tc.i386-openbsd.elf.gcc += -mpreferred-stack-boundary=2 +tc.i386-openbsd.elf.gcc += -fweb +tc.i386-openbsd.elf.ld = $(call tc,m-ld) +tc.i386-openbsd.elf.objcopy = $(call tc,m-objcopy) +tc.i386-openbsd.elf.objdump = $(call tc,m-objdump) +tc.i386-openbsd.elf.objstrip = $(call tc,objcopy) -R .comment -R .note + +# Note the re-use of i386-bsd.elf-entry.h as output (no separate i386-openbsd.elf-entry.h). +# Note the re-use of i386-bsd.elf-fold.lds as input (no separate i386-openbsd.elf-fold.lds). + +i386-openbsd.elf-fold.h : tmp/$$T.o tmp/i386-openbsd.elf-main.o $(srcdir)/src/i386-bsd.elf-fold.lds + $(call tc,ld) -T $(srcdir)/src/i386-bsd.elf-fold.lds -Map tmp/$T.map -o tmp/$T.bin --strip-all $(filter %.o,$^) + $(call tc,objstrip) tmp/$T.bin + $(call tc,sstrip) tmp/$T.bin + $(call tc,brandelf) --elfosabi=openbsd tmp/$T.bin + $(call tc,bin2h) --ident=openbsd_i386elf_fold tmp/$T.bin $@ + +tmp/i386-openbsd.elf-fold.o : $(srcdir)/src/$$T.asm + $(call tc,nasm) -f elf -l $@.lst $< -o $@ + $(call tc,objstrip) $@ + +tmp/i386-openbsd.elf-main.o : $(srcdir)/src/$$T.c + $(call tc,gcc) -c $< -o $@ + $(call tc,objstrip) $@ + # /*********************************************************************** # // i386-dos32.djgpp2 diff --git a/src/stub/scripts/brandelf.py b/src/stub/scripts/brandelf.py index 052a798c..4d7b3daa 100644 --- a/src/stub/scripts/brandelf.py +++ b/src/stub/scripts/brandelf.py @@ -53,6 +53,9 @@ def do_file(fn): if opts.bfdname == "elf32-i386" and opts.elfosabi == "freebsd": fp.seek(7, 0) fp.write("\x09") + elif opts.bfdname == "elf32-i386" and opts.elfosabi == "openbsd": + fp.seek(7, 0) + fp.write("\x0c") elif opts.bfdname == "elf32-i386" and opts.elfosabi == "linux": fp.seek(8, 0) fp.write("Linux\x00\x00\x00") diff --git a/src/stub/src/i386-bsd.elf-fold.lds b/src/stub/src/i386-bsd.elf-fold.lds index ecf2cd11..92b44772 100644 --- a/src/stub/src/i386-bsd.elf-fold.lds +++ b/src/stub/src/i386-bsd.elf-fold.lds @@ -38,7 +38,6 @@ PHDRS } SECTIONS { - /* 0x00c01000: 12MB+4KB for Fedora Core 5 vDSO at 0xc00000 */ . = 0x00c01000 + SIZEOF_HEADERS + 12; /* 12==sizeof(l_info) */ .text : { *(.text) diff --git a/src/stub/src/i386-openbsd.elf-fold.asm b/src/stub/src/i386-openbsd.elf-fold.asm new file mode 100644 index 00000000..306d59ba --- /dev/null +++ b/src/stub/src/i386-openbsd.elf-fold.asm @@ -0,0 +1,277 @@ +; i386-openbsd.elf-fold.asm -- linkage to C code to process Elf binary +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 2000-2006 John F. 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. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; +; +; John F. Reiser +; +; + + + BITS 32 + SECTION .text + CPU 386 + +%define PAGE_SIZE ( 1<<12) +%define szElf32_Ehdr 0x34 +%define szElf32_Phdr 8*4 +%define e_type 16 +%define e_entry (16 + 2*2 + 4) +%define p_memsz 5*4 +%define sznote 0x18 +%define szb_info 12 +%define szl_info 12 +%define szp_info 12 +%define a_type 0 +%define a_val 4 +%define sz_auxv 8 + +%define __NR_munmap 73 + +;; control just falls through, after this part and compiled C code +;; are uncompressed. + +fold_begin: ; enter: %ebx= &Elf32_Ehdr of this program + ; patchLoader will modify to be + ; dword sz_uncompressed, sz_compressed + ; byte compressed_data... + +; ld-linux.so.2 depends on AT_PHDR and AT_ENTRY, for instance. +; Move argc,argv,envp down to make room for Elf_auxv table. +; Linux kernel 2.4.2 and earlier give only AT_HWCAP and AT_PLATFORM +; because we have no PT_INTERP. Linux kernel 2.4.5 (and later?) +; give not quite everything. It is simpler and smaller code for us +; to generate a "complete" table where Elf_auxv[k -1].a_type = k. +; On second thought, that wastes a lot of stack space (the entire kernel +; auxv, plus those slots that remain empty anyway). So try for minimal +; space on stack, without too much code, by doing it serially. + +%define AT_NULL 0 +%define AT_IGNORE 1 +%define AT_PHDR 3 +%define AT_PHENT 4 +%define AT_PHNUM 5 +%define AT_PAGESZ 6 +%define AT_ENTRY 9 + +%define ET_DYN 3 + + sub ecx, ecx + mov edx, (1< + + John F. Reiser + + */ + + +#include "include/bsd.h" + + +/************************************************************************* +// configuration section +**************************************************************************/ + +// In order to make it much easier to move this code at runtime and execute +// it at an address different from it load address: there must be no +// static data, and no string constants. + +#if 1 /*{*/ +#define DPRINTF(a) /* empty: no debug drivel */ +#else /*}{*/ +#include "stdarg.h" + +static int +unsimal(unsigned x, char *ptr, int n) +{ + if (10<=x) { + n = unsimal(x/10, ptr, n); + x %= 10; + } + ptr[n] = '0' + x; + return 1+ n; +} + +static int +decimal(int x, char *ptr, int n) +{ + if (x < 0) { + *ptr++ = '-'; ++n; + x = -x; + } + return unsimal(x, ptr, n); +} + +extern char const *STR_hex(); + +static int +heximal(unsigned x, char *ptr, int n) +{ + if (16<=x) { + n = heximal(x>>4, ptr, n); + x &= 0xf; + } + ptr[n] = STR_hex()[x]; + return 1+ n; +} + + +#define DPRINTF(a) dprintf a +extern char const *STR_0x(); +extern char const *STR_xread(); +extern char const *STR_unpackExtent(); +extern char const *STR_make_hatch_arm(); +extern char const *STR_auxv_up(); +extern char const *STR_xfind_pages(); +extern char const *STR_do_xmap(); +extern char const *STR_upx_main(); + +static int +dprintf(char const *fmt, ...) +{ + char c; + int n= 0; + char *ptr; + char buf[20]; + va_list va; va_start(va, fmt); + ptr= &buf[0]; + while (0!=(c= *fmt++)) if ('%'!=c) goto literal; + else switch (c= *fmt++) { + default: { +literal: + n+= write(2, fmt-1, 1); + } break; + case 0: goto done; /* early */ + case 'u': { + n+= write(2, buf, unsimal(va_arg(va, unsigned), buf, 0)); + } break; + case 'd': { + n+= write(2, buf, decimal(va_arg(va, int), buf, 0)); + } break; + case 'p': /* same as 'x'; relies on sizeof(int)==sizeof(void *) */ + case 'x': { + buf[0] = '0'; + buf[1] = 'x'; + n+= write(2, buf, heximal(va_arg(va, int), buf, 2)); + } break; + } +done: + va_end(va); + return n; +} +#endif /*}*/ + +#define MAX_ELF_HDR 512 // Elf32_Ehdr + n*Elf32_Phdr must fit in this + + +/************************************************************************* +// "file" util +**************************************************************************/ + +struct Extent { + size_t size; // must be first to match size[0] uncompressed size + char *buf; +}; + + +static void +#if (ACC_CC_GNUC >= 0x030300) && defined(__i386__) /*{*/ +__attribute__((__noinline__, __used__, regparm(3), stdcall)) +#endif /*}*/ +xread(struct Extent *x, char *buf, size_t count) +{ + char *p=x->buf, *q=buf; + size_t j; + DPRINTF((STR_xread(), x, x->size, x->buf, buf, count)); + if (x->size < count) { + exit(127); + } + for (j = count; 0!=j--; ++p, ++q) { + *q = *p; + } + x->buf += count; + x->size -= count; +} + + +/************************************************************************* +// util +**************************************************************************/ + +#if 1 //{ save space +#define ERR_LAB error: exit(127); +#define err_exit(a) goto error +#else //}{ save debugging time +#define ERR_LAB +static void +err_exit(int a) __attribute__ ((__noreturn__)); +{ + (void)a; // debugging convenience + exit(127); +} +#endif //} + +static void * +do_brk(void *addr) +{ + return brk(addr); +} + +/************************************************************************* +// UPX & NRV stuff +**************************************************************************/ + +typedef void f_unfilter( + nrv_byte *, // also addvalue + nrv_uint, + unsigned cto8, // junk in high 24 bits + unsigned ftid +); +typedef int f_expand( + const nrv_byte *, nrv_uint, + nrv_byte *, nrv_uint *, unsigned ); + +static void +unpackExtent( + struct Extent *const xi, // input + struct Extent *const xo, // output + f_expand *const f_decompress, + f_unfilter *f_unf +) +{ + DPRINTF((STR_unpackExtent(), + xi, xi->size, xi->buf, xo, xo->size, xo->buf, f_decompress, f_unf)); + while (xo->size) { + struct b_info h; + // Note: if h.sz_unc == h.sz_cpr then the block was not + // compressible and is stored in its uncompressed form. + + // Read and check block sizes. + xread(xi, (char *)&h, sizeof(h)); + if (h.sz_unc == 0) { // uncompressed size 0 -> EOF + if (h.sz_cpr != UPX_MAGIC_LE32) // h.sz_cpr must be h->magic + err_exit(2); + if (xi->size != 0) // all bytes must be written + err_exit(3); + break; + } + if (h.sz_cpr <= 0) { + err_exit(4); +ERR_LAB + } + if (h.sz_cpr > h.sz_unc + || h.sz_unc > xo->size ) { + err_exit(5); + } + // Now we have: + // assert(h.sz_cpr <= h.sz_unc); + // assert(h.sz_unc > 0 && h.sz_unc <= blocksize); + // assert(h.sz_cpr > 0 && h.sz_cpr <= blocksize); + + if (h.sz_cpr < h.sz_unc) { // Decompress block + nrv_uint out_len = h.sz_unc; // EOF for lzma + int const j = (*f_decompress)((unsigned char *)xi->buf, h.sz_cpr, + (unsigned char *)xo->buf, &out_len, *(int *)(void *)&h.b_method ); + if (j != 0 || out_len != (nrv_uint)h.sz_unc) + err_exit(7); + // Skip Ehdr+Phdrs: separate 1st block, not filtered + if (h.b_ftid!=0 && f_unf // have filter + && ((512 < out_len) // this block is longer than Ehdr+Phdrs + || (xo->size==(unsigned)h.sz_unc) ) // block is last in Extent + ) { + (*f_unf)((unsigned char *)xo->buf, out_len, h.b_cto8, h.b_ftid); + } + xi->buf += h.sz_cpr; + xi->size -= h.sz_cpr; + } + else { // copy literal block + xread(xi, xo->buf, h.sz_cpr); + } + xo->buf += h.sz_unc; + xo->size -= h.sz_unc; + } +} + +#if defined(__i386__) /*{*/ +// Create (or find) an escape hatch to use when munmapping ourselves the stub. +// Called by do_xmap to create it; remembered in AT_NULL.d_val +static void * +make_hatch_x86(Elf32_Phdr const *const phdr, unsigned const reloc) +{ + unsigned *hatch = 0; + if (phdr->p_type==PT_LOAD && phdr->p_flags & PF_X) { + // The format of the 'if' is + // if ( ( (hatch = loc1), test_loc1 ) + // || ( (hatch = loc2), test_loc2 ) ) { + // action + // } + // which uses the comma to save bytes when test_locj involves locj + // and the action is the same when either test succeeds. + + // Try page fragmentation just beyond .text . + if ( ( (hatch = (void *)(phdr->p_memsz + phdr->p_vaddr + reloc)), + ( phdr->p_memsz==phdr->p_filesz // don't pollute potential .bss + && 4<=(~PAGE_MASK & -(int)hatch) ) ) // space left on page + // Try Elf32_Ehdr.e_ident[12..15] . warning: 'const' cast away + || ( (hatch = (void *)(&((Elf32_Ehdr *)phdr->p_vaddr + reloc)->e_ident[12])), + (phdr->p_offset==0) ) ) { + // Omitting 'const' saves repeated literal in gcc. + unsigned /*const*/ escape = 0xc36180cd; // "int $0x80; popa; ret" + // Don't store into read-only page if value is already there. + if (* (volatile unsigned*) hatch != escape) { + * hatch = escape; + } + } + } + return hatch; +} +#elif defined(__arm__) /*}{*/ +static void * +make_hatch_arm(Elf32_Phdr const *const phdr, unsigned const reloc) +{ + unsigned *hatch = 0; + DPRINTF((STR_make_hatch_arm(),phdr,reloc)); + if (phdr->p_type==PT_LOAD && phdr->p_flags & PF_X) { + // The format of the 'if' is + // if ( ( (hatch = loc1), test_loc1 ) + // || ( (hatch = loc2), test_loc2 ) ) { + // action + // } + // which uses the comma to save bytes when test_locj involves locj + // and the action is the same when either test succeeds. + + // Try page fragmentation just beyond .text . + if ( ( (hatch = (void *)(phdr->p_memsz + phdr->p_vaddr + reloc)), + ( phdr->p_memsz==phdr->p_filesz // don't pollute potential .bss + && 8<=(~PAGE_MASK & -(int)hatch) ) ) // space left on page + // Try Elf32_Ehdr.e_ident[8..15] . warning: 'const' cast away + || ( (hatch = (void *)(&((Elf32_Ehdr *)phdr->p_vaddr + reloc)->e_ident[8])), + (phdr->p_offset==0) ) ) + { + hatch[0]= 0xef90005b; // syscall __NR_unmap + hatch[1]= 0xe1a0f00e; // mov pc,lr + } + } + return hatch; +} +#endif /*}*/ + +static void +#if defined(__i386__) /*{*/ +__attribute__((regparm(2), stdcall)) +#endif /*}*/ +upx_bzero(char *p, size_t len) +{ + if (len) do { + *p++= 0; + } while (--len); +} +#define bzero upx_bzero + + +static void +#if defined(__i386__) /*{*/ +__attribute__((regparm(3), stdcall)) +#endif /*}*/ +auxv_up(Elf32_auxv_t *av, unsigned const type, unsigned const value) +{ + DPRINTF((STR_auxv_up(),av,type,value)); + if (av +#if defined(__i386__) /*{*/ + && 0==(1&(int)av) /* PT_INTERP usually inhibits, except for hatch */ +#endif /*}*/ + ) + for (;; ++av) { + if (av->a_type==type || (av->a_type==AT_IGNORE && type!=AT_NULL)) { + av->a_type = type; + av->a_un.a_val = value; + return; + } + } +} + +// The PF_* and PROT_* bits are {1,2,4}; the conversion table fits in 32 bits. +#define REP8(x) \ + ((x)|((x)<<4)|((x)<<8)|((x)<<12)|((x)<<16)|((x)<<20)|((x)<<24)|((x)<<28)) +#define EXP8(y) \ + ((1&(y)) ? 0xf0f0f0f0 : (2&(y)) ? 0xff00ff00 : (4&(y)) ? 0xffff0000 : 0) +#define PF_TO_PROT(pf) \ + ((PROT_READ|PROT_WRITE|PROT_EXEC) & ( \ + ( (REP8(PROT_EXEC ) & EXP8(PF_X)) \ + |(REP8(PROT_READ ) & EXP8(PF_R)) \ + |(REP8(PROT_WRITE) & EXP8(PF_W)) \ + ) >> ((pf & (PF_R|PF_W|PF_X))<<2) )) + + +// Find convex hull of PT_LOAD (the minimal interval which covers all PT_LOAD), +// and mmap that much, to be sure that a kernel using exec-shield-randomize +// won't place the first piece in a way that leaves no room for the rest. +static unsigned long // returns relocation constant +#if defined(__i386__) /*{*/ +__attribute__((regparm(3), stdcall)) +#endif /*}*/ +xfind_pages(Elf32_Phdr const *phdr, int phnum, char **const p_brk) +{ + size_t lo= ~0, hi= 0, szlo= 0; + char *addr; + unsigned const mflags = MAP_PRIVATE | MAP_ANONYMOUS; + DPRINTF((STR_xfind_pages(), mflags, phdr, phnum, p_brk)); + for (; --phnum>=0; ++phdr) if (PT_LOAD==phdr->p_type) { + if (phdr->p_vaddr < lo) { + lo = phdr->p_vaddr; + szlo = phdr->p_filesz; + } + if (hi < (phdr->p_memsz + phdr->p_vaddr)) { + hi = phdr->p_memsz + phdr->p_vaddr; + } + } + szlo += ~PAGE_MASK & lo; // page fragment on lo edge + lo -= ~PAGE_MASK & lo; // round down to page boundary + hi = PAGE_MASK & (hi - lo - PAGE_MASK -1); // page length + szlo = PAGE_MASK & (szlo - PAGE_MASK -1); // page length + addr = mmap((void *)lo, hi, PROT_NONE, mflags, -1, 0); + *p_brk = hi + addr; // the logical value of brk(0) + //mprotect(szlo + addr, hi - szlo, PROT_NONE); // no access, but keep the frames! + return (unsigned long)addr - lo; +} + +static Elf32_Addr // entry address +do_xmap(int const fdi, Elf32_Ehdr const *const ehdr, struct Extent *const xi, + Elf32_auxv_t *const av, unsigned *p_reloc, f_unfilter *const f_unf) +{ + Elf32_Phdr const *phdr = (Elf32_Phdr const *) (ehdr->e_phoff + + (void const *)ehdr); + char *v_brk; + unsigned const reloc = (ET_EXEC==ehdr->e_type) ? + 0 : xfind_pages(phdr, ehdr->e_phnum, &v_brk); + int j; + DPRINTF((STR_do_xmap(), + fdi, ehdr, xi, (xi? xi->size: 0), (xi? xi->buf: 0), av, p_reloc, f_unf)); + for (j=0; j < ehdr->e_phnum; ++phdr, ++j) + if (PT_PHDR==phdr->p_type) { + auxv_up(av, AT_PHDR, phdr->p_vaddr + reloc); + } + else if (PT_LOAD==phdr->p_type) { + unsigned const prot = PF_TO_PROT(phdr->p_flags); + struct Extent xo; + size_t mlen = xo.size = phdr->p_filesz; + char *addr = xo.buf = (char *)(phdr->p_vaddr + reloc); + char *haddr = phdr->p_memsz + addr; + size_t frag = (int)addr &~ PAGE_MASK; + mlen += frag; + addr -= frag; + + if (addr != mmap(addr, mlen +#if defined(__i386__) /*{*/ + // Decompressor can overrun the destination by 3 bytes. + + (xi ? 3 : 0) +#endif /*}*/ + , prot | (xi ? PROT_WRITE : 0), + MAP_FIXED | MAP_PRIVATE | (xi ? MAP_ANONYMOUS : 0), + (xi ? -1 : fdi), phdr->p_offset - frag) ) { + err_exit(8); + } + if (xi) { + unpackExtent(xi, &xo, (f_expand *)fdi, + ((PROT_EXEC & prot) ? f_unf : 0) ); + } + // Linux does not fixup the low end, so neither do we. + //if (PROT_WRITE & prot) { + // bzero(addr, frag); // fragment at lo end + //} + frag = (-mlen) &~ PAGE_MASK; // distance to next page boundary + if (PROT_WRITE & prot) { // note: read-only .bss not supported here + bzero(mlen+addr, frag); // fragment at hi end + } + if (xi) { +#if defined(__i386__) /*{*/ + void *const hatch = make_hatch_x86(phdr, reloc); + if (0!=hatch) { + /* always update AT_NULL, especially for compressed PT_INTERP */ + auxv_up((Elf32_auxv_t *)(~1 & (int)av), AT_NULL, (unsigned)hatch); + } +#elif defined(__arm__) /*}{*/ + void *const hatch = make_hatch_arm(phdr, reloc); + if (0!=hatch) { + auxv_up((Elf32_auxv_t *)(void *)av, AT_NULL, (unsigned)hatch); + } +#endif /*}*/ + if (0!=mprotect(addr, mlen, prot)) { + err_exit(10); +ERR_LAB + } + } + addr += mlen + frag; /* page boundary on hi end */ + if (addr < haddr) { // need pages for .bss + if (addr != mmap(addr, haddr - addr, prot, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 ) ) { + err_exit(9); + } + } +#if defined(__i386__) /*{*/ + else if (xi) { // cleanup if decompressor overrun crosses page boundary + mlen = ~PAGE_MASK & (3+ mlen); + if (mlen<=3) { // page fragment was overrun buffer only + munmap(addr, mlen); + } + } +#endif /*}*/ + } + if (!xi) { // 2nd call (PT_INTERP); close()+check is smaller here + if (0!=close(fdi)) { + err_exit(11); + } + } + else { // 1st call (main); also have (0!=av) here + if (ET_DYN!=ehdr->e_type) { + // Needed only if compressed shell script invokes compressed shell. + do_brk(v_brk); + } + } + if (0!=p_reloc) { + *p_reloc = reloc; + } + return ehdr->e_entry + reloc; +} + + +/************************************************************************* +// upx_main - called by our entry code +// +// This function is optimized for size. +**************************************************************************/ + +void *upx_main( + Elf32_auxv_t *const av, + unsigned const sz_compressed, + f_expand *const f_decompress, + f_unfilter */*const*/ f_unfilter, + struct Extent xo, + struct Extent xi, + unsigned const volatile dynbase +) __asm__("upx_main"); + +void *upx_main( + Elf32_auxv_t *const av, + unsigned const sz_compressed, + f_expand *const f_decompress, + f_unfilter */*const*/ f_unf, + struct Extent xo, // {sz_unc, ehdr} for ELF headers + struct Extent xi, // {sz_cpr, &b_info} for ELF headers + unsigned const volatile dynbase // value+result: compiler must not change +) +{ + Elf32_Ehdr *const ehdr = (Elf32_Ehdr *)(void *)xo.buf; // temp char[MAX_ELF_HDR+OVERHEAD] + Elf32_Phdr const *phdr = (Elf32_Phdr const *)(1+ ehdr); + Elf32_Addr reloc; + Elf32_Addr entry; + + // sizeof(Ehdr+Phdrs), compressed; including b_info header + size_t const sz_pckhdrs = xi.size; + + DPRINTF((STR_upx_main(), + av, sz_compressed, f_decompress, f_unf, &xo, xo.size, xo.buf, + &xi, xi.size, xi.buf, dynbase)); +#if defined(__i386__) /*{*/ + f_unf = (f_unfilter *)(2+ (long)f_decompress); +#endif /*}*/ + + // Uncompress Ehdr and Phdrs. + unpackExtent(&xi, &xo, f_decompress, 0); + + // Prepare to decompress the Elf headers again, into the first PT_LOAD. + xi.buf -= sz_pckhdrs; + xi.size = sz_compressed; + + // Some kernels omit AT_PHNUM,AT_PHENT,AT_PHDR because this stub has no PT_INTERP. + // That is "too much" optimization. Linux 2.6.x seems to give all AT_*. + //auxv_up(av, AT_PAGESZ, PAGE_SIZE); /* ld-linux.so.2 does not need this */ + auxv_up(av, AT_PHNUM , ehdr->e_phnum); + auxv_up(av, AT_PHENT , ehdr->e_phentsize); + auxv_up(av, AT_PHDR , dynbase + (unsigned)(1+(Elf32_Ehdr *)phdr->p_vaddr)); + // AT_PHDR.a_un.a_val is set again by do_xmap if PT_PHDR is present. + // This is necessary for ET_DYN if|when we override a prelink address. + + entry = do_xmap((int)f_decompress, ehdr, &xi, av, &reloc, f_unf); + auxv_up(av, AT_ENTRY , entry); // might not be necessary? + + { // Map PT_INTERP program interpreter + int j; + for (j=0; j < ehdr->e_phnum; ++phdr, ++j) if (PT_INTERP==phdr->p_type) { + int const fdi = open(reloc + (char const *)phdr->p_vaddr, O_RDONLY, 0); + if (0 > fdi) { + err_exit(18); + } + if (MAX_ELF_HDR!=read(fdi, (void *)ehdr, MAX_ELF_HDR)) { +ERR_LAB + err_exit(19); + } + entry = do_xmap(fdi, ehdr, 0, 0, 0, 0); + break; + } + } + + return (void *)entry; +} + + +/* +vi:ts=4:et:nowrap +*/ +