void PackUnix::unpack(OutputFile *fo) { unsigned const szb_info = sizeof(b_info); unsigned c_adler = upx_adler32(NULL, 0); unsigned u_adler = upx_adler32(NULL, 0); // defaults for ph.version == 8 unsigned orig_file_size = 0; blocksize = 512 * 1024; fi->seek(overlay_offset, SEEK_SET); if (ph.version > 8) { p_info hbuf; fi->readx(&hbuf, sizeof(hbuf)); orig_file_size = get_te32(&hbuf.p_filesize); blocksize = get_te32(&hbuf.p_blocksize); if (file_size > (off_t)orig_file_size || blocksize > orig_file_size) throwCantUnpack("file header corrupted"); } else { // skip 4 bytes (program id) fi->seek(4, SEEK_CUR); } if ((int)(blocksize + OVERHEAD) < 0) throwCantUnpack("blocksize corrupted"); ibuf.alloc(blocksize + OVERHEAD); // decompress blocks unsigned total_in = 0; unsigned total_out = 0; b_info bhdr; memset(&bhdr, 0, sizeof(bhdr)); for (;;) { #define buf ibuf int i; unsigned sz_unc, sz_cpr; fi->readx(&bhdr, szb_info); ph.u_len = sz_unc = get_te32(&bhdr.sz_unc); ph.c_len = sz_cpr = get_te32(&bhdr.sz_cpr); if (sz_unc == 0) // uncompressed size 0 -> EOF { // note: must reload sz_cpr as magic is always stored le32 sz_cpr = get_le32(&bhdr.sz_cpr); if (sz_cpr != UPX_MAGIC_LE32) // sz_cpr must be h->magic throwCompressedDataViolation(); break; } if (sz_unc <= 0 || sz_cpr <= 0) throwCompressedDataViolation(); if (sz_cpr > sz_unc || sz_unc > blocksize) throwCompressedDataViolation(); i = blocksize + OVERHEAD - sz_cpr; if (i < 0) throwCantUnpack("corrupt b_info"); fi->readx(buf+i, sz_cpr); // update checksum of compressed data c_adler = upx_adler32(buf + i, sz_cpr, c_adler); // decompress if (sz_cpr < sz_unc) { decompress(buf+i, buf, false); if (0!=bhdr.b_ftid) { Filter ft(ph.level); ft.init(bhdr.b_ftid); ft.cto = bhdr.b_cto8; ft.unfilter(buf, sz_unc); } i = 0; } // update checksum of uncompressed data u_adler = upx_adler32(buf + i, sz_unc, u_adler); total_in += sz_cpr; total_out += sz_unc; // write block if (fo) fo->write(buf + i, sz_unc); #undef buf } // update header with totals ph.c_len = total_in; ph.u_len = total_out; // all bytes must be written if (ph.version > 8 && total_out != orig_file_size) throwEOFException(); // finally test the checksums if (ph.c_adler != c_adler || ph.u_adler != u_adler) throwChecksumError(); }
void PackLinuxElf32x86interp::unpack(OutputFile *fo) { #define MAX_INTERP_HDR 512 union { unsigned char buf[MAX_INTERP_HDR]; //struct { Elf32_Ehdr ehdr; Elf32_Phdr phdr; } e; } u; Elf32_Ehdr *const ehdr = (Elf32_Ehdr *) u.buf; Elf32_Phdr const *phdr = (Elf32_Phdr *) (u.buf + sizeof(*ehdr)); unsigned szb_info = sizeof(b_info); { fi->seek(0, SEEK_SET); fi->readx(u.buf, MAX_INTERP_HDR); unsigned const e_entry = get_te32(&ehdr->e_entry); if (e_entry < 0x401180) { /* old style, 8-byte b_info */ szb_info = 2*sizeof(unsigned); } } fi->seek(overlay_offset, SEEK_SET); p_info hbuf; fi->readx(&hbuf, sizeof(hbuf)); unsigned orig_file_size = get_te32(&hbuf.p_filesize); blocksize = get_te32(&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, szb_info); ph.u_len = get_te32(&bhdr.sz_unc); ph.c_len = get_te32(&bhdr.sz_cpr); ph.filter_cto = bhdr.b_cto8; // Uncompress Ehdr and Phdrs. fi->readx(ibuf, ph.c_len); decompress(ibuf, (upx_byte *)ehdr, false); unsigned total_in = 0; unsigned total_out = 0; unsigned c_adler = upx_adler32(NULL, 0); unsigned u_adler = upx_adler32(NULL, 0); off_t ptload0hi=0, ptload1lo=0, ptload1sz=0; // decompress PT_LOAD bool first_PF_X = true; fi->seek(- (off_t) (szb_info + ph.c_len), SEEK_CUR); for (unsigned j=0; j < ehdr->e_phnum; ++phdr, ++j) { if (PT_LOAD==phdr->p_type) { if (0==ptload0hi) { ptload0hi = phdr->p_filesz + phdr->p_offset; } else if (0==ptload1lo) { ptload1lo = phdr->p_offset; ptload1sz = phdr->p_filesz; } if (fo) fo->seek(phdr->p_offset, SEEK_SET); if (Elf32_Phdr::PF_X & phdr->p_flags) { unpackExtent(phdr->p_filesz, fo, total_in, total_out, c_adler, u_adler, first_PF_X, szb_info); first_PF_X = false; } else { unpackExtent(phdr->p_filesz, fo, total_in, total_out, c_adler, u_adler, false, szb_info); } } } if (0!=ptload1sz && ptload0hi < ptload1lo) { // alignment hole? if (fo) fo->seek(ptload0hi, SEEK_SET); unpackExtent(ptload1lo - ptload0hi, fo, total_in, total_out, c_adler, u_adler, false, szb_info); } if (total_out != orig_file_size) { // non-PT_LOAD stuff if (fo) fo->seek(0, SEEK_END); unpackExtent(orig_file_size - total_out, fo, total_in, total_out, c_adler, u_adler, false, szb_info); } // check for end-of-file fi->readx(&bhdr, szb_info); unsigned const sz_unc = ph.u_len = get_te32(&bhdr.sz_unc); if (sz_unc == 0) { // uncompressed size 0 -> EOF // note: magic is always stored le32 unsigned const sz_cpr = get_le32(&bhdr.sz_cpr); if (sz_cpr != UPX_MAGIC_LE32) // sz_cpr must be h->magic throwCompressedDataViolation(); } else { // extra bytes after end? throwCompressedDataViolation(); } // update header with totals ph.c_len = total_in; ph.u_len = total_out; // all bytes must be written if (total_out != orig_file_size) throwEOFException(); // finally test the checksums if (ph.c_adler != c_adler || ph.u_adler != u_adler) throwChecksumError(); #undef MAX_INTERP_HDR }
int PackUnix::pack2(OutputFile *fo, Filter &ft) { // compress blocks unsigned total_in = 0; unsigned total_out = 0; // FIXME: ui_total_passes is not correct with multiple blocks... // ui_total_passes = (file_size + blocksize - 1) / blocksize; // if (ui_total_passes == 1) // ui_total_passes = 0; unsigned remaining = file_size; unsigned n_block = 0; while (remaining > 0) { // FIXME: disable filters if we have more than one block. // FIXME: There is only 1 un-filter in the stub [as of 2002-11-10]. // So the next block really has no choice! // This merely prevents an assert() in compressWithFilters(), // which assumes it has free choice on each call [block]. // And if the choices aren't the same on each block, // then un-filtering will give incorrect results. int filter_strategy = getStrategy(ft); if (file_size > (off_t)blocksize) filter_strategy = -3; // no filters int l = fi->readx(ibuf, UPX_MIN(blocksize, remaining)); remaining -= l; // Note: compression for a block can fail if the // file is e.g. blocksize + 1 bytes long // compress ph.overlap_overhead = 0; ph.c_len = ph.u_len = l; ft.buf_len = l; // compressWithFilters() updates u_adler _inside_ compress(); // that is, AFTER filtering. We want BEFORE filtering, // so that decompression checks the end-to-end checksum. unsigned const end_u_adler = upx_adler32(ibuf, ph.u_len, ph.u_adler); compressWithFilters(&ft, OVERHEAD, NULL_cconf, filter_strategy, !!n_block++); // check compression ratio only on first block if (ph.c_len < ph.u_len) { const upx_bytep tbuf = NULL; if (ft.id == 0) tbuf = ibuf; ph.overlap_overhead = OVERHEAD; if (!testOverlappingDecompression(obuf, tbuf, ph.overlap_overhead)) { // not in-place compressible ph.c_len = ph.u_len; } } if (ph.c_len >= ph.u_len) { // block is not compressible ph.c_len = ph.u_len; // must manually update checksum of compressed data ph.c_adler = upx_adler32(ibuf, ph.u_len, ph.saved_c_adler); } // write block header b_info blk_info; memset(&blk_info, 0, sizeof(blk_info)); set_te32(&blk_info.sz_unc, ph.u_len); set_te32(&blk_info.sz_cpr, ph.c_len); if (ph.c_len < ph.u_len) { blk_info.b_method = (unsigned char) ph.method; blk_info.b_ftid = (unsigned char) ph.filter; blk_info.b_cto8 = (unsigned char) ph.filter_cto; } fo->write(&blk_info, sizeof(blk_info)); b_len += sizeof(b_info); // write compressed data if (ph.c_len < ph.u_len) { fo->write(obuf, ph.c_len); verifyOverlappingDecompression(); // uses ph.u_adler } else { fo->write(ibuf, ph.u_len); } ph.u_adler = end_u_adler; total_in += ph.u_len; total_out += ph.c_len; } // update header with totals ph.u_len = total_in; ph.c_len = total_out; if ((off_t)total_in != file_size) { throwEOFException(); } return 1; // default: write end-of-compression bhdr next }