void Filter::unfilter(upx_byte *buf_, unsigned buf_len_, bool verify_checksum) { initFilter(this, buf_, buf_len_); const FilterImp::FilterEntry * const fe = FilterImp::getFilter(id); if (fe == NULL) throwInternalError("unfilter-1"); if (fe->id == 0) return; if (buf_len < fe->min_buf_len) return; if (fe->max_buf_len && buf_len > fe->max_buf_len) return; if (!fe->do_unfilter) throwInternalError("unfilter-2"); //printf("unfilter: %02x %p %d\n", this->id, this->buf, this->buf_len); int r = (*fe->do_unfilter)(this); //printf("unfilter: %02x %d\n", fe->id, r); if (r != 0) throwInternalError("unfilter-3"); //OutputFile::dump("unfilter.dat", buf, buf_len); // verify checksum if (verify_checksum && clevel != 1) { if (this->adler != upx_adler32(this->buf, this->buf_len)) throwInternalError("unfilter-4"); } }
bool Filter::filter(upx_byte *buf_, unsigned buf_len_) { initFilter(this, buf_, buf_len_); const FilterImp::FilterEntry * const fe = FilterImp::getFilter(id); if (fe == NULL) throwInternalError("filter-1"); if (fe->id == 0) return true; if (buf_len < fe->min_buf_len) return false; if (fe->max_buf_len && buf_len > fe->max_buf_len) return false; if (!fe->do_filter) throwInternalError("filter-2"); // save checksum this->adler = 0; if (clevel != 1) this->adler = upx_adler32(this->buf, this->buf_len); //printf("filter: %02x %p %d\n", this->id, this->buf, this->buf_len); //OutputFile::dump("filter.dat", buf, buf_len); int r = (*fe->do_filter)(this); //printf("filter: %02x %d\n", fe->id, r); if (r > 0) throwFilterException(); if (r == 0) return true; return false; }
void PackUnix::patchLoaderChecksum() { unsigned char *const ptr = getLoader(); l_info *const lp = &linfo; // checksum for loader; also some PackHeader info lp->l_magic = UPX_MAGIC_LE32; // LE32 always set_te16(&lp->l_lsize, (upx_uint16_t) lsize); lp->l_version = (unsigned char) ph.version; lp->l_format = (unsigned char) ph.format; // INFO: lp->l_checksum is currently unused set_te32(&lp->l_checksum, upx_adler32(ptr, lsize)); }
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 }
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 PackUnix::unpackExtent(unsigned wanted, OutputFile *fo, unsigned &total_in, unsigned &total_out, unsigned &c_adler, unsigned &u_adler, bool first_PF_X, unsigned szb_info ) { b_info hdr; memset(&hdr, 0, sizeof(hdr)); while (wanted) { fi->readx(&hdr, szb_info); int const sz_unc = ph.u_len = get_te32(&hdr.sz_unc); int const sz_cpr = ph.c_len = get_te32(&hdr.sz_cpr); ph.filter_cto = hdr.b_cto8; if (sz_unc == 0) { // must never happen while 0!=wanted throwCantUnpack("corrupt b_info"); break; } if (sz_unc <= 0 || sz_cpr <= 0) throwCantUnpack("corrupt b_info"); if (sz_cpr > sz_unc || sz_unc > (int)blocksize) throwCantUnpack("corrupt b_info"); int j = blocksize + OVERHEAD - sz_cpr; fi->readx(ibuf+j, sz_cpr); // update checksum of compressed data c_adler = upx_adler32(ibuf + j, sz_cpr, c_adler); // decompress if (sz_cpr < sz_unc) { decompress(ibuf+j, ibuf, false); if (12==szb_info) { // modern per-block filter if (hdr.b_ftid) { Filter ft(ph.level); // FIXME: ph.level for b_info? ft.init(hdr.b_ftid, 0); ft.cto = hdr.b_cto8; ft.unfilter(ibuf, sz_unc); } } else { // ancient per-file filter if (first_PF_X) { // Elf32_Ehdr is never filtered first_PF_X = false; // but everything else might be } else if (ph.filter) { Filter ft(ph.level); ft.init(ph.filter, 0); ft.cto = (unsigned char) ph.filter_cto; ft.unfilter(ibuf, sz_unc); } } j = 0; } // update checksum of uncompressed data u_adler = upx_adler32(ibuf + j, sz_unc, u_adler); total_in += sz_cpr; total_out += sz_unc; // write block if (fo) fo->write(ibuf + j, sz_unc); if (wanted < (unsigned)sz_unc) throwCantUnpack("corrupt b_info"); wanted -= sz_unc; } }
void PackUnix::packExtent( const Extent &x, unsigned &total_in, unsigned &total_out, Filter *ft, OutputFile *fo, unsigned hdr_u_len ) { unsigned const init_u_adler = ph.u_adler; unsigned const init_c_adler = ph.c_adler; MemBuffer hdr_ibuf; if (hdr_u_len) { hdr_ibuf.alloc(hdr_u_len); fi->seek(0, SEEK_SET); int l = fi->readx(hdr_ibuf, hdr_u_len); (void)l; } fi->seek(x.offset, SEEK_SET); for (off_t rest = x.size; 0 != rest; ) { int const filter_strategy = ft ? getStrategy(*ft) : 0; int l = fi->readx(ibuf, UPX_MIN(rest, (off_t)blocksize)); if (l == 0) { break; } rest -= l; // Note: compression for a block can fail if the // file is e.g. blocksize + 1 bytes long // compress ph.c_len = ph.u_len = l; ph.overlap_overhead = 0; unsigned end_u_adler = 0; if (ft) { // compressWithFilters() updates u_adler _inside_ compress(); // that is, AFTER filtering. We want BEFORE filtering, // so that decompression checks the end-to-end checksum. end_u_adler = upx_adler32(ibuf, ph.u_len, ph.u_adler); ft->buf_len = l; // compressWithFilters() requirements? ph.filter = 0; ph.filter_cto = 0; ft->id = 0; ft->cto = 0; compressWithFilters(ft, OVERHEAD, NULL_cconf, filter_strategy, 0, 0, 0, hdr_ibuf, hdr_u_len); } else { (void) compress(ibuf, ph.u_len, obuf); // ignore return value } if (ph.c_len < ph.u_len) { const upx_bytep tbuf = NULL; if (ft == NULL || 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; memcpy(obuf, ibuf, ph.c_len); // must update checksum of compressed data ph.c_adler = upx_adler32(ibuf, ph.u_len, ph.saved_c_adler); } // write block sizes b_info tmp; if (hdr_u_len) { unsigned hdr_c_len = 0; MemBuffer hdr_obuf; hdr_obuf.allocForCompression(hdr_u_len); int r = upx_compress(hdr_ibuf, hdr_u_len, hdr_obuf, &hdr_c_len, 0, ph.method, 10, NULL, NULL); if (r != UPX_E_OK) throwInternalError("header compression failed"); if (hdr_c_len >= hdr_u_len) throwInternalError("header compression size increase"); ph.saved_u_adler = upx_adler32(hdr_ibuf, hdr_u_len, init_u_adler); ph.saved_c_adler = upx_adler32(hdr_obuf, hdr_c_len, init_c_adler); ph.u_adler = upx_adler32(ibuf, ph.u_len, ph.saved_u_adler); ph.c_adler = upx_adler32(obuf, ph.c_len, ph.saved_c_adler); end_u_adler = ph.u_adler; memset(&tmp, 0, sizeof(tmp)); set_te32(&tmp.sz_unc, hdr_u_len); set_te32(&tmp.sz_cpr, hdr_c_len); tmp.b_method = (unsigned char) ph.method; fo->write(&tmp, sizeof(tmp)); b_len += sizeof(b_info); fo->write(hdr_obuf, hdr_c_len); total_out += hdr_c_len; total_in += hdr_u_len; hdr_u_len = 0; // compress hdr one time only } memset(&tmp, 0, sizeof(tmp)); set_te32(&tmp.sz_unc, ph.u_len); set_te32(&tmp.sz_cpr, ph.c_len); if (ph.c_len < ph.u_len) { tmp.b_method = (unsigned char) ph.method; if (ft) { tmp.b_ftid = (unsigned char) ft->id; tmp.b_cto8 = ft->cto; } } fo->write(&tmp, sizeof(tmp)); b_len += sizeof(b_info); if (ft) { ph.u_adler = end_u_adler; } // write compressed data if (ph.c_len < ph.u_len) { fo->write(obuf, ph.c_len); // Checks ph.u_adler after decompression, after unfiltering verifyOverlappingDecompression(ft); } else { fo->write(ibuf, ph.u_len); } total_in += ph.u_len; total_out += ph.c_len; } }
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 }