bool PackTos::canPack() { if (!readFileHeader()) return false; unsigned char buf[768]; fi->readx(buf, sizeof(buf)); checkAlreadyPacked(buf, sizeof(buf)); if (!checkFileHeader()) throwCantPack("unsupported header flags"); if (file_size < 1024) throwCantPack("program is too small"); return true; }
bool PackCom::canPack() { unsigned char buf[128]; fi->readx(buf, sizeof(buf)); if (memcmp(buf,"MZ",2) == 0 || memcmp(buf,"ZM",2) == 0 // .exe || memcmp (buf,"\xff\xff\xff\xff",4) == 0) // .sys return false; if (!fn_has_ext(fi->getName(),"com")) return false; checkAlreadyPacked(buf, sizeof(buf)); if (file_size < 1024) throwCantPack("file is too small"); if (file_size > 0xFF00) throwCantPack("file is too big for dos/com"); return true; }
// common part of canPack(), enhanced by subclasses bool PackUnix::canPack() { if (exetype == 0) return false; #if defined(__unix__) // must be executable by owner if ((fi->st.st_mode & S_IXUSR) == 0) throwCantPack("file not executable; try 'chmod +x'"); #endif if (file_size < 4096) throwCantPack("file is too small"); // info: currently the header is 36 (32+4) bytes before EOF unsigned char buf[256]; fi->seek(-(off_t)sizeof(buf), SEEK_END); fi->readx(buf, sizeof(buf)); checkAlreadyPacked(buf, sizeof(buf)); return true; }
static const #include "stub/i086-dos16.sys.h" /************************************************************************* // **************************************************************************/ bool PackSys::canPack() { unsigned char buf[128]; fi->readx(buf, sizeof(buf)); if (memcmp (buf,"\xff\xff\xff\xff",4) != 0) return false; if (!fn_has_ext(fi->getName(),"sys")) return false; checkAlreadyPacked(buf, sizeof(buf)); if (file_size < 1024) throwCantPack("file is too small"); if (file_size > 0x10000) throwCantPack("file is too big for dos/sys"); return true; }
// read full kernel into obuf[], gzip-decompress into ibuf[], // return decompressed size int PackVmlinuzI386::decompressKernel() { // read whole kernel image obuf.alloc(file_size); fi->seek(0, SEEK_SET); fi->readx(obuf, file_size); { const upx_byte *base = NULL; unsigned relocated = 0; // See startup_32: in linux/arch/i386/boot/compressed/head.S const upx_byte *p = &obuf[setup_size]; unsigned cpa_0 = 0; unsigned cpa_1 = 0; int j; if (0x205<=h.version) { cpa_0 = h.kernel_alignment; cpa_1 = 0u - cpa_0; } else for ((p = &obuf[setup_size]), (j= 0); j < 0x200; ++j, ++p) { if (0==memcmp("\x89\xeb\x81\xc3", p, 4) && 0==memcmp("\x81\xe3", 8+ p, 2)) { // movl %ebp,%ebx // addl $imm.w,%ebx // andl $imm.w,%ebx cpa_0 = 1+ get_te32( 4+ p); cpa_1 = get_te32(10+ p); break; } } for ((p = &obuf[setup_size]), (j= 0); j < 0x200; ++j, ++p) { if (0==memcmp("\x8d\x83", p, 2) // leal d32(%ebx),%eax && 0==memcmp("\xff\xe0", 6+ p, 2) // jmp *%eax ) { relocated = get_te32(2+ p); } if (0==memcmp("\xE8\x00\x00\x00\x00\x5D", p, 6)) { // "call 1f; 1f: pop %ebp" determines actual execution address. // linux-2.6.21 (spring 2007) and later; upx stub needs work // unless LOAD_PHYSICAL_ADDR is known. // Allowed code is: linux-2.6.23/arch/x86/head_32.S 2008-01-01 // call 1f // 1: popl %ebp // subl $1b, %ebp # 32-bit immediate // movl $LOAD_PHYSICAL_ADDR, %ebx // if (0==memcmp("\x81\xed", 6+ p, 2) // subl $imm.w,%ebp && 0==memcmp("\xbb", 12+ p, 1) ) { // movl $imm.w,%ebx physical_start = get_te32(13+ p); } else if (0==memcmp("\x81\xed", 6+ p, 2) // subl $imm.w,%ebp && is_pow2(cpa_0) && (0u-cpa_0)==cpa_1) { base = (5+ p) - get_te32(8+ p); config_physical_align = cpa_0; } else { throwCantPack("Unrecognized relocatable kernel"); } } // Find "ljmp $__BOOT_CS,$__PHYSICAL_START" if any. if (0==memcmp("\xEA\x00\x00", p, 3) && 0==(0xf & p[3]) && 0==p[4]) { /* whole megabyte < 16 MiB */ physical_start = get_te32(1+ p); break; } } if (base && relocated) { p = base + relocated; for (j = 0; j < 0x200; ++j, ++p) { if (0==memcmp("\x01\x9c\x0b", p, 3) // addl %ebx,d32(%ebx,%ecx) ) { page_offset = 0u - get_te32(3+ p); } if (0==memcmp("\x89\xeb", p, 2) // movl %ebp,%ebx && 0==memcmp("\x81\xeb", 2+ p, 2) // subl $imm32,%ebx ) { physical_start = get_te32(4+ p); } } } } checkAlreadyPacked(obuf + setup_size, UPX_MIN(file_size - setup_size, (off_t)1024)); int gzoff = setup_size; if (0x208<=h.version) { gzoff += h.payload_offset; } for (; gzoff < file_size; gzoff++) { // find gzip header (2 bytes magic + 1 byte method "deflated") int off = find(obuf + gzoff, file_size - gzoff, "\x1F\x8B\x08", 3); if (off < 0) break; gzoff += off; const int gzlen = (h.version < 0x208) ? (file_size - gzoff) : h.payload_length; if (gzlen < 256) break; // check gzip flag byte unsigned char flags = obuf[gzoff + 3]; if ((flags & 0xe0) != 0) // reserved bits set continue; //printf("found gzip header at offset %d\n", gzoff); // try to decompress int klen; int fd; off_t fd_pos; for (;;) { klen = -1; fd = -1; fd_pos = -1; // open fi->seek(gzoff, SEEK_SET); fd = dup(fi->getFd()); if (fd < 0) break; gzFile zf = gzdopen(fd, "rb"); if (zf == NULL) break; // estimate gzip-decompressed kernel size & alloc buffer if (ibuf.getSize() == 0) ibuf.alloc(gzlen * 3); // decompress klen = gzread(zf, ibuf, ibuf.getSize()); fd_pos = lseek(fd, 0, SEEK_CUR); gzclose(zf); fd = -1; if (klen != (int)ibuf.getSize()) break; // realloc and try again unsigned s = ibuf.getSize(); ibuf.dealloc(); ibuf.alloc(3 * s / 2); } if (fd >= 0) (void) close(fd); if (klen <= 0) continue; if (klen <= gzlen) continue; if (0x208<=h.version && 0==memcmp("\177ELF", ibuf, 4)) { // Full ELF in theory; for now, try to handle as .bin at physical_start. // Check for PT_LOAD.p_paddr being ascending and adjacent. Elf_LE32_Ehdr const *const ehdr = (Elf_LE32_Ehdr const *)(void const *)ibuf; Elf_LE32_Phdr const *phdr = (Elf_LE32_Phdr const *)(ehdr->e_phoff + (char const *)ehdr); Elf_LE32_Shdr const *shdr = (Elf_LE32_Shdr const *)(ehdr->e_shoff + (char const *)ehdr); unsigned hi_paddr = 0, lo_paddr = 0; unsigned delta_off = 0; for (unsigned j=0; j < ehdr->e_phnum; ++j, ++phdr) { if (phdr->PT_LOAD==phdr->p_type) { unsigned step = (hi_paddr + phdr->p_align - 1) & ~(phdr->p_align - 1); if (0==hi_paddr) { // first PT_LOAD if (physical_start!=phdr->p_paddr) { return 0; } delta_off = phdr->p_paddr - phdr->p_offset; lo_paddr = phdr->p_paddr; hi_paddr = phdr->p_filesz + phdr->p_paddr; } else if (step==phdr->p_paddr && delta_off==(phdr->p_paddr - phdr->p_offset)) { hi_paddr = phdr->p_filesz + phdr->p_paddr; } else { return 0; // Not equivalent to a .bin. Too complex for now. } } } // FIXME: ascending order is only a convention; might need sorting. for (unsigned j=1; j < ehdr->e_shnum; ++j) { if (shdr->SHT_PROGBITS==shdr->sh_type) { // SHT_REL might be intermixed if (shdr->SHF_EXECINSTR & shdr[j].sh_flags) { filter_len += shdr[j].sh_size; // FIXME: include sh_addralign } else { break; } } } memmove(ibuf, (lo_paddr - delta_off) + ibuf, hi_paddr - lo_paddr); // FIXME: set_size // FIXME: .bss ? Apparently handled by head.S } if (opt->force > 0) return klen; // some checks if (fd_pos != file_size) { //printf("fd_pos: %ld, file_size: %ld\n", (long)fd_pos, (long)file_size); // linux-2.6.21.5/arch/i386/boot/compressed/vmlinux.lds // puts .data.compressed ahead of .text, .rodata, etc; // so piggy.o need not be last in bzImage. Alas. //throwCantPack("trailing bytes after kernel image; use option '-f' to force packing"); } // see /usr/src/linux/arch/i386/kernel/head.S // 2.4.x: [cli;] cld; mov $...,%eax if (memcmp(ibuf, "\xFC\xB8", 2) == 0) goto head_ok; if (memcmp(ibuf, "\xFA\xFC\xB8", 3) == 0) goto head_ok; // 2.6.21.5 CONFIG_PARAVIRT mov %cs,%eax; test $3,%eax; jne ...; if (memcmp(ibuf, "\x8c\xc8\xa9\x03\x00\x00\x00\x0f\x85", 9) == 0) goto head_ok; if (memcmp(ibuf, "\x8c\xc8\xa8\x03\x0f\x85", 6) == 0) goto head_ok; // 2.6.x: [cli;] cld; lgdt ... if (memcmp(ibuf, "\xFC\x0F\x01", 3) == 0) goto head_ok; if (memcmp(ibuf, "\xFA\xFC\x0F\x01", 4) == 0) goto head_ok; // 2.6.x+grsecurity+strongswan+openwall+trustix: ljmp $0x10,... if (ibuf[0] == 0xEA && memcmp(ibuf+5, "\x10\x00", 2) == 0) goto head_ok; // x86_64 2.6.x if (0xB8==ibuf[0] // mov $...,%eax && 0x8E==ibuf[5] && 0xD8==ibuf[6] // mov %eax,%ds && 0x0F==ibuf[7] && 0x01==ibuf[8] && 020==(070 & ibuf[9]) // lgdtl && 0xB8==ibuf[14] // mov $...,%eax && 0x0F==ibuf[19] && 0xA2==ibuf[20] // cpuid ) goto head_ok; // cmpw $0x207,0x206(%esi) Debian vmlinuz-2.6.24-12-generic if (0==memcmp("\x66\x81\xbe\x06\x02\x00\x00\x07\x02", ibuf, 9)) goto head_ok; // testb $0x40,0x211(%esi) Fedora vmlinuz-2.6.25-0.218.rc8.git7.fc9.i686 if (0==memcmp("\xf6\x86\x11\x02\x00\x00\x40", ibuf, 7)) goto head_ok; // rex.W prefix for x86_64 if (0x48==ibuf[0]) throwCantPack("x86_64 bzImage is not yet supported"); throwCantPack("unrecognized kernel architecture; use option '-f' to force packing"); head_ok: // FIXME: more checks for special magic bytes in ibuf ??? // FIXME: more checks for kernel architecture ??? return klen; } return 0; }