/* p_vmlinx.cpp -- pack vmlinux ET_EXEC file (before bootsect or setup) This file is part of the UPX executable compressor. Copyright (C) 2004-2017 John Reiser Copyright (C) 1996-2017 Markus Franz Xaver Johannes Oberhumer Copyright (C) 1996-2017 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 John Reiser */ #include "conf.h" #include "file.h" #include "filter.h" #include "packer.h" #include "p_vmlinx.h" #include "linker.h" static const #include "stub/i386-linux.kernel.vmlinux.h" static const #include "stub/amd64-linux.kernel.vmlinux.h" static const #include "stub/arm.v5a-linux.kernel.vmlinux.h" static const #include "stub/armeb.v5a-linux.kernel.vmlinux.h" static const #include "stub/powerpc-linux.kernel.vmlinux.h" /************************************************************************* // **************************************************************************/ template PackVmlinuxBase::PackVmlinuxBase(InputFile *f, unsigned e_machine, unsigned elfclass, unsigned elfdata, char const *const boot_label) : super(f), my_e_machine(e_machine), my_elfclass(elfclass), my_elfdata(elfdata), my_boot_label(boot_label), n_ptload(0), phdri(NULL), shdri(NULL), shstrtab(NULL) { ElfClass::compileTimeAssertions(); bele = N_BELE_CTP::getRTP((const BeLePolicy*) NULL); } template PackVmlinuxBase::~PackVmlinuxBase() { delete [] phdri; delete [] shdri; delete [] shstrtab; } template int PackVmlinuxBase::getStrategy(Filter &/*ft*/) { // Called just before reading and compressing each block. // Might want to adjust blocksize, etc. // If user specified the filter, then use it (-2==strategy). // Else try the first two filters, and pick the better (2==strategy). return (opt->no_filter ? -3 : ((opt->filter > 0) ? -2 : 2)); } template int __acc_cdecl_qsort PackVmlinuxBase::compare_Phdr(void const *aa, void const *bb) { Phdr const *const a = (Phdr const *)aa; Phdr const *const b = (Phdr const *)bb; unsigned const xa = a->p_type - Phdr::PT_LOAD; unsigned const xb = b->p_type - Phdr::PT_LOAD; if (xa < xb) return -1; // PT_LOAD first if (xa > xb) return 1; if (a->p_paddr < b->p_paddr) return -1; // ascending by .p_paddr if (a->p_paddr > b->p_paddr) return 1; return 0; } template typename T::Shdr const *PackVmlinuxBase::getElfSections() { Shdr const *p, *shstrsec=0; shdri = new Shdr[(unsigned) ehdri.e_shnum]; fi->seek(ehdri.e_shoff, SEEK_SET); fi->readx(shdri, ehdri.e_shnum * sizeof(*shdri)); int j; for (p = shdri, j= ehdri.e_shnum; --j>=0; ++p) { if (Shdr::SHT_STRTAB==p->sh_type && (p->sh_size + p->sh_offset) <= (unsigned) file_size && (10+ p->sh_name) <= p->sh_size // 1+ strlen(".shstrtab") ) { delete [] shstrtab; shstrtab = new char[1+ p->sh_size]; fi->seek(p->sh_offset, SEEK_SET); fi->readx(shstrtab, p->sh_size); shstrtab[p->sh_size] = '\0'; if (0==strcmp(".shstrtab", shstrtab + p->sh_name)) { shstrsec = p; break; } } } return shstrsec; } template bool PackVmlinuxBase::canPack() { fi->seek(0, SEEK_SET); fi->readx(&ehdri, sizeof(ehdri)); // now check the ELF header if (memcmp(&ehdri, "\x7f\x45\x4c\x46", 4) || ehdri.e_ident[Ehdr::EI_CLASS] != my_elfclass || ehdri.e_ident[Ehdr::EI_DATA] != my_elfdata || ehdri.e_ident[Ehdr::EI_VERSION] != Ehdr::EV_CURRENT || !memcmp(&ehdri.e_ident[8], "FreeBSD", 7) // branded || ehdri.e_machine != my_e_machine || ehdri.e_version != 1 // version || ehdri.e_ehsize != sizeof(ehdri) // different ? ) { return false; } // additional requirements for vmlinux if (ehdri.e_type != Ehdr::ET_EXEC || ehdri.e_phoff != sizeof(ehdri) // Phdr not contiguous with Ehdr || ehdri.e_phentsize!=sizeof(Phdr) || !is_valid_e_entry(ehdri.e_entry) ) { return false; } // A Linux kernel must have a __ksymtab section. [??] Shdr const *p, *const shstrsec = getElfSections(); if (0==shstrsec) { return false; } { int j; for (p = shdri, j= ehdri.e_shnum; --j>=0; ++p) { if (Shdr::SHT_PROGBITS==p->sh_type && (p->sh_name + shstrsec->sh_offset) < (unsigned)file_size && 0==strcmp("__ksymtab", p->sh_name + shstrtab)) { break; } } if (j < 0) { return false; } } phdri = new Phdr[(unsigned) ehdri.e_phnum]; fi->seek(ehdri.e_phoff, SEEK_SET); fi->readx(phdri, ehdri.e_phnum * sizeof(*phdri)); // Put PT_LOAD together at the beginning, ascending by .p_paddr. qsort(phdri, ehdri.e_phnum, sizeof(*phdri), compare_Phdr); // Find convex hull of physical addresses, and count the PT_LOAD. // Ignore ".bss": .p_filesz < .p_memsz unsigned phys_lo= ~0u, phys_hi= 0u; for (unsigned j = 0; j < ehdri.e_phnum; ++j) { if (Phdr::PT_LOAD==phdri[j].p_type) { // Check for general sanity (not necessarily required.) if (0xfff & (phdri[j].p_offset | phdri[j].p_paddr | phdri[j].p_align | phdri[j].p_vaddr) ) { return false; } if (phys_lo > phdri[j].p_paddr) { phys_lo = phdri[j].p_paddr; } if (phys_hi < (phdri[j].p_filesz + phdri[j].p_paddr)) { phys_hi = (phdri[j].p_filesz + phdri[j].p_paddr); } ++n_ptload; } } paddr_min = phys_lo; sz_ptload = phys_hi - phys_lo; return 0 < n_ptload; } #include "p_elf.h" template void PackVmlinuxBase::pack(OutputFile *fo) { unsigned fo_off = 0; Ehdr ehdro; TE32 tmp_u32; // NULL // .text(PT_LOADs) .note(1st page) .note(rest) // .shstrtab .symtab .strtab Shdr shdro[1+3+3]; memset(shdro, 0, sizeof(shdro)); ibuf.alloc(file_size); obuf.allocForCompression(file_size); // .e_ident, .e_machine, .e_version, .e_flags memcpy(&ehdro, &ehdri, sizeof(ehdro)); ehdro.e_type = Ehdr::ET_REL; ehdro.e_entry = 0; ehdro.e_phoff = 0; ehdro.e_shoff = sizeof(ehdro); ehdro.e_phentsize = 0; ehdro.e_phnum = 0; ehdro.e_shnum = 1+3+3; ehdro.e_shstrndx = 4; fo->write(&ehdro, sizeof(ehdro)); fo_off+= sizeof(ehdro); fo->write(shdro, sizeof(shdro)); fo_off+= sizeof(shdro); // Notice overlap [containment] of physical PT_LOAD[2] into PTLOAD[1] // in this vmlinux for x86_64 from Fedora Core 6 on 2007-01-07: //Program Headers: // Type Offset VirtAddr PhysAddr // FileSiz MemSiz Flags Align // LOAD 0x0000000000200000 0xffffffff80200000 0x0000000000200000 // 0x000000000034bce8 0x000000000034bce8 R E 200000 // LOAD 0x000000000054c000 0xffffffff8054c000 0x000000000054c000 // 0x00000000000ed004 0x00000000001702a4 RWE 200000 // LOAD 0x0000000000800000 0xffffffffff600000 0x00000000005f5000 // 0x0000000000000c08 0x0000000000000c08 RWE 200000 // NOTE 0x0000000000000000 0x0000000000000000 0x0000000000000000 // 0x0000000000000000 0x0000000000000000 R 8 // Therefore we must "compose" the convex hull to be loaded. ph.u_len = sz_ptload; memset(ibuf, 0, sz_ptload); for (unsigned j = 0; j < ehdri.e_phnum; ++j) { if (Phdr::PT_LOAD==phdri[j].p_type) { fi->seek(phdri[j].p_offset, SEEK_SET); fi->readx(ibuf + ((unsigned) phdri[j].p_paddr - paddr_min), phdri[j].p_filesz); } } checkAlreadyPacked(ibuf + ph.u_len - 1024, 1024); // prepare filter ph.filter = 0; Filter ft(ph.level); ft.buf_len = ph.u_len; ft.addvalue = 0; // we are independent of actual runtime address; see ckt32 upx_compress_config_t cconf; cconf.reset(); // limit stack size needed for runtime decompression cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28 KiB stack unsigned ppc32_extra = 0; if (Ehdr::EM_PPC==my_e_machine) { // output layout: // .long UPX_MAGIC_LE32 // .long L20 - L10 // L10: // b_info for Ehdr; compressed Ehdr; .balign 4 // one block only // b_info for LOAD; compressed LOAD; .balign 4 // possibly many blocks // // This allows per-block filters! // L20: // b f_decompress // +4: f_unfilter(char *buf, unsigned len, unsigned cto8, unsigned ftid) // // Code for multiple filters can "daisy chain" on ftid. // f_decompress(char const *src, unsigned src_len, // char *dst, unsigned *dst_len, int method) unsigned tmp; tmp = UPX_MAGIC_LE32; fo->write(&tmp, sizeof(tmp)); fo_off += sizeof(tmp); tmp = 0; fo->write(&tmp, sizeof(tmp)); fo_off += sizeof(tmp); ppc32_extra += 2*sizeof(tmp); unsigned const len_unc = sizeof(ehdri) + sizeof(Phdr) * ehdri.e_phnum; MemBuffer unc_hdr(len_unc); MemBuffer cpr_hdr; cpr_hdr.allocForCompression(len_unc); memcpy(&unc_hdr[0], &ehdri, sizeof(ehdri)); memcpy(&unc_hdr[sizeof(ehdri)], phdri, sizeof(Phdr) * ehdri.e_phnum); unsigned len_cpr = 0; int const r = upx_compress(unc_hdr, len_unc, cpr_hdr, &len_cpr, NULL, ph.method, 10, NULL, NULL ); if (UPX_E_OK!=r || len_unc<=len_cpr) // FIXME: allow no compression throwInternalError("Ehdr compression failed"); __packed_struct(b_info) // 12-byte header before each compressed block unsigned sz_unc; // uncompressed_size unsigned sz_cpr; // compressed_size unsigned char b_method; // compression algorithm unsigned char b_ftid; // filter id unsigned char b_cto8; // filter parameter unsigned char b_unused; // FIXME: !=0 for partial-block unfilter // unsigned f_offset, f_len; // only if partial-block unfilter __packed_struct_end() struct b_info hdr_info; set_be32(&hdr_info.sz_unc, len_unc); set_be32(&hdr_info.sz_cpr, len_cpr); hdr_info.b_method = ph.method; hdr_info.b_ftid = 0; hdr_info.b_cto8 = 0; hdr_info.b_unused = 0; fo->write(&hdr_info, sizeof(hdr_info)); fo_off += sizeof(hdr_info); unsigned const frag = (3& (0u-len_cpr)); ppc32_extra += sizeof(hdr_info) + len_cpr + frag; fo_off += len_cpr + frag; memset(&cpr_hdr[len_cpr], 0, frag); // valgrind only fo->write(cpr_hdr, len_cpr + frag); // Partial filter: .text and following contiguous SHF_EXECINSTR upx_bytep f_ptr = ibuf; unsigned f_len = 0; Shdr const *shdr = 1+ shdri; // skip empty shdr[0] if (ft.buf_len==0 // not specified yet FIXME: was set near construction && (Shdr::SHF_ALLOC & shdr->sh_flags) && (Shdr::SHF_EXECINSTR & shdr->sh_flags)) { // shdr[1] is instructions (probably .text) f_ptr = ibuf + (unsigned) (shdr->sh_offset - phdri[0].p_offset); f_len = shdr->sh_size; ++shdr; for (int j= ehdri.e_shnum - 2; --j>=0; ++shdr) { unsigned prev_end = shdr[-1].sh_size + shdr[-1].sh_offset; prev_end += ~(0u-shdr[0].sh_addralign) & (0u-prev_end); // align_up if ((Shdr::SHF_ALLOC & shdr->sh_flags) && (Shdr::SHF_EXECINSTR & shdr->sh_flags) && shdr[0].sh_offset==prev_end) { f_len += shdr->sh_size; } else { break; } } } else { // ft.buf_len already specified, or Shdr[1] not instructions f_ptr = ibuf; f_len = ph.u_len; } compressWithFilters(ibuf, ph.u_len, obuf, f_ptr, f_len, // filter range NULL, 0, // hdr_ptr, hdr_len &ft, 512, &cconf, getStrategy(ft)); set_be32(&hdr_info.sz_unc, ph.u_len); set_be32(&hdr_info.sz_cpr, ph.c_len); hdr_info.b_ftid = ft.id; hdr_info.b_cto8 = ft.cto; if (ph.u_len!=f_len) { hdr_info.b_unused = 1; // flag for partial filter } fo->write(&hdr_info, sizeof(hdr_info)); fo_off += sizeof(hdr_info); ppc32_extra += sizeof(hdr_info); if (ph.u_len!=f_len) { set_be32(&hdr_info.sz_unc, f_ptr - (upx_bytep) ibuf); set_be32(&hdr_info.sz_cpr, f_len); fo->write(&hdr_info, 2*sizeof(unsigned)); fo_off += 2*sizeof(unsigned); ppc32_extra += 2*sizeof(unsigned); } } else { compressWithFilters(&ft, 512, &cconf, getStrategy(ft)); } unsigned const txt_c_len = ph.c_len; const unsigned lsize = getLoaderSize(); defineDecompressorSymbols(); defineFilterSymbols(&ft); relocateLoader(); MemBuffer loader(lsize); memcpy(loader, getLoader(), lsize); patchPackHeader(loader, lsize); #define shstrtab local_shstrtab // avoid -Wshadow warning char const shstrtab[]= "\0.text\0.note\0.shstrtab\0.symtab\0.strtab"; char const *p = shstrtab; while (0!=*p++) ; shdro[1].sh_name = ptr_diff(p, shstrtab); shdro[1].sh_type = Shdr::SHT_PROGBITS; shdro[1].sh_flags = Shdr::SHF_ALLOC | Shdr::SHF_EXECINSTR; shdro[1].sh_offset = fo_off - ppc32_extra; shdro[1].sh_size = ppc32_extra + txt_c_len + lsize; // plus more ... shdro[1].sh_addralign = 1; // default fo_off += write_vmlinux_head(fo, &shdro[1]); fo->write(obuf, txt_c_len); fo_off += txt_c_len; unsigned const a = (shdro[1].sh_addralign -1) & (0u-(ppc32_extra + txt_c_len)); if (0!=a) { // align fo_off += a; shdro[1].sh_size += a; fo->seek(a, SEEK_CUR); } fo->write(loader, lsize); fo_off += lsize; #if 0 printf("%-13s: compressed : %8u bytes\n", getName(), txt_c_len); printf("%-13s: decompressor : %8u bytes\n", getName(), lsize); #endif verifyOverlappingDecompression(); // .note with 1st page -------------------------------- ph.u_len = phdri[0].p_offset; fi->seek(0, SEEK_SET); fi->readx(ibuf, ph.u_len); compress(ibuf, ph.u_len, obuf, &cconf); while (0!=*p++) ; shdro[2].sh_name = ptr_diff(p, shstrtab); shdro[2].sh_type = Shdr::SHT_NOTE; shdro[2].sh_offset = fo_off; shdro[2].sh_size = sizeof(ph.u_len) + ph.c_len; shdro[2].sh_addralign = 1; tmp_u32 = ph.u_len; fo->write(&tmp_u32, 4); fo->write(obuf, ph.c_len); fo_off += shdro[2].sh_size; // .note with rest -------------------------------- ph.u_len = file_size - (sz_ptload + phdri[0].p_offset); fi->seek(sz_ptload + phdri[0].p_offset, SEEK_SET); fi->readx(ibuf, ph.u_len); // Temporarily decrease ph.level by about (1+ log2(sz_rest / sz_ptload)) // to avoid spending unreasonable effort compressing large symbol tables // that are discarded 99.9% of the time anyway. int const old_level = ph.level; for (unsigned v = ((ph.u_len>>3) + ph.u_len) / sz_ptload; 0 < v; v>>=1) { if (0== --ph.level) { ph.level = 1; } } compress(ibuf, ph.u_len, obuf, &cconf); ph.level = old_level; // while (0!=*p++) ; // name is the same shdro[3].sh_name = ptr_diff(p, shstrtab); shdro[3].sh_type = Shdr::SHT_NOTE; shdro[3].sh_offset = fo_off; shdro[3].sh_size = sizeof(ph.u_len) + ph.c_len; shdro[3].sh_addralign = 1; tmp_u32 = ph.u_len; fo->write(&tmp_u32, 4); fo->write(obuf, ph.c_len); fo_off += shdro[3].sh_size; while (0!=*p++) ; shdro[4].sh_name = ptr_diff(p, shstrtab); shdro[4].sh_type = Shdr::SHT_STRTAB; shdro[4].sh_offset = fo_off; shdro[4].sh_size = sizeof(shstrtab); // already includes terminating '\0' shdro[4].sh_addralign = 1; fo->write(shstrtab, shdro[4].sh_size); fo_off += shdro[4].sh_size; fo_off = ~3 & (3+ fo_off); fo->seek(fo_off, SEEK_SET); while (0!=*p++) ; shdro[5].sh_name = ptr_diff(p, shstrtab); shdro[5].sh_type = Shdr::SHT_SYMTAB; shdro[5].sh_offset = fo_off; shdro[5].sh_size = ((Ehdr::EM_PPC==my_e_machine) + 5)*sizeof(Sym); //shdro[5].sh_flags = Shdr::SHF_INFO_LINK; shdro[5].sh_link = 6; // to .strtab for symbols shdro[5].sh_info = 1+3; // number of non-global symbols [binutils/bfd/elf.c] shdro[5].sh_addralign = 4; shdro[5].sh_entsize = sizeof(Sym); Sym sec_sym; // Symbol 0; no references, but bfd demands it. memset(&sec_sym, 0, sizeof(sec_sym)); fo->write(&sec_sym, sizeof(sec_sym)); fo_off += sizeof(sec_sym); // Each section before .shstrtab needs a symbol. sec_sym.st_info = sec_sym.make_st_info(Sym::STB_LOCAL, Sym::STT_SECTION); sec_sym.st_other = Sym::STV_DEFAULT; sec_sym.st_shndx = 1; // .text fo->write(&sec_sym, sizeof(sec_sym)); fo_off += sizeof(sec_sym); sec_sym.st_shndx = 2; // .note fo->write(&sec_sym, sizeof(sec_sym)); fo_off += sizeof(sec_sym); sec_sym.st_shndx = 3; // .note fo->write(&sec_sym, sizeof(sec_sym)); fo_off += sizeof(sec_sym); // the symbol we care about Sym unc_ker; unc_ker.st_name = 1; // 1 byte into strtab unc_ker.st_value = 0; unc_ker.st_size = ppc32_extra + txt_c_len; unc_ker.st_info = unc_ker.make_st_info(Sym::STB_GLOBAL, Sym::STT_FUNC); unc_ker.st_other = Sym::STV_DEFAULT; unc_ker.st_shndx = 1; // .text fo->write(&unc_ker, sizeof(unc_ker)); fo_off += sizeof(unc_ker); unsigned const lablen = strlen(my_boot_label); if (Ehdr::EM_PPC==my_e_machine) { unc_ker.st_name += 1+ lablen; unc_ker.st_value = unc_ker.st_size; unc_ker.st_size = 0; fo->write(&unc_ker, sizeof(unc_ker)); fo_off += sizeof(unc_ker); } while (0!=*p++) ; shdro[6].sh_name = ptr_diff(p, shstrtab); shdro[6].sh_type = Shdr::SHT_STRTAB; shdro[6].sh_offset = fo_off; shdro[6].sh_size = 2+ lablen + (Ehdr::EM_PPC==my_e_machine)*(1+ 12); // '\0' before and after shdro[6].sh_addralign = 1; fo->seek(1, SEEK_CUR); // the '\0' before fo->write(my_boot_label, 1+ lablen); // include the '\0' terminator if (Ehdr::EM_PPC==my_e_machine) { fo->write("_vmlinux_end", 1+ 12); fo_off += 1+ 12; } fo_off += 2+ lablen; fo->seek(0, SEEK_SET); fo->write(&ehdro, sizeof(ehdro)); fo->write(&shdro[0], sizeof(shdro)); if (Ehdr::EM_PPC==my_e_machine) { fo->seek(sizeof(unsigned), SEEK_CUR); set_be32(&ppc32_extra, ppc32_extra - 2*sizeof(unsigned) + txt_c_len); fo->write(&ppc32_extra, sizeof(ppc32_extra)); } if (!checkFinalCompressionRatio(fo)) throwNotCompressible(); #undef shstrtab } template int PackVmlinuxBase::canUnpack() { fi->seek(0, SEEK_SET); fi->readx(&ehdri, sizeof(ehdri)); // now check the ELF header if (memcmp(&ehdri, "\x7f\x45\x4c\x46", 4) || ehdri.e_ident[Ehdr::EI_CLASS] != my_elfclass || ehdri.e_ident[Ehdr::EI_DATA] != my_elfdata || ehdri.e_ident[Ehdr::EI_VERSION] != Ehdr::EV_CURRENT || !memcmp(&ehdri.e_ident[8], "FreeBSD", 7) // branded || ehdri.e_machine != my_e_machine || ehdri.e_version != 1 // version || ehdri.e_ehsize != sizeof(ehdri) // different ? ) return false; if (ehdri.e_type != Ehdr::ET_REL //i386 fails || ehdri.e_shoff != sizeof(ehdri) // Shdr not contiguous with Ehdr || ehdri.e_shentsize!=sizeof(Shdr) || ehdri.e_shnum < 4 || (unsigned)file_size < (ehdri.e_shnum * sizeof(Shdr) + ehdri.e_shoff) ) return false; // find the .shstrtab section Shdr const *const shstrsec = getElfSections(); if (0==shstrsec) { return false; } // check for .text .note .note and sane (.sh_size + .sh_offset) p_note0 = p_note1 = p_text = 0; int j; Shdr *p; for (p= shdri, j= ehdri.e_shnum; --j>=0; ++p) { if ((unsigned)file_size < (p->sh_size + p->sh_offset) || shstrsec->sh_size < (5+ p->sh_name) ) { continue; } if (0==strcmp(".text", shstrtab + p->sh_name)) { p_text = p; } if (0==strcmp(".note", shstrtab + p->sh_name)) { if (0==p_note0) { p_note0 = p; } else if (0==p_note1) { p_note1 = p; } } } if (0==p_text || 0==p_note0 || 0==p_note1) { return false; } char buf[1024]; fi->seek(p_text->sh_offset + p_text->sh_size - sizeof(buf), SEEK_SET); fi->readx(buf, sizeof(buf)); if (!getPackHeader(buf, sizeof(buf))) return -1; // format is known, but definitely is not packed return true; } template void PackVmlinuxBase::unpack(OutputFile *fo) { TE32 word; PackHeader const ph_tmp(ph); fi->seek(p_note0->sh_offset, SEEK_SET); fi->readx(&word, sizeof(word)); ph.u_len = word; ph.c_len = p_note0->sh_size - sizeof(word); ibuf.alloc(ph.c_len); fi->readx(ibuf, ph.c_len); obuf.allocForUncompression(ph.u_len); decompress(ibuf, obuf, false); fo->write(obuf, ph.u_len); obuf.dealloc(); ibuf.dealloc(); ph = ph_tmp; if (!has_valid_vmlinux_head()) { throwCantUnpack(".text corrupted"); } ibuf.alloc(ph.c_len); fi->readx(ibuf, ph.c_len); obuf.allocForUncompression(ph.u_len); decompress(ibuf, obuf); Filter ft(ph.level); ft.init(ph.filter, 0); ft.cto = (unsigned char) ph.filter_cto; ft.unfilter(obuf, ph.u_len); fo->write(obuf, ph.u_len); obuf.dealloc(); ibuf.dealloc(); fi->seek(p_note1->sh_offset, SEEK_SET); fi->readx(&word, sizeof(word)); ph.u_len = word; ph.c_len = p_note1->sh_size - sizeof(word); ibuf.alloc(ph.c_len); fi->readx(ibuf, p_note1->sh_size - sizeof(ph.u_len)); obuf.allocForUncompression(ph.u_len); decompress(ibuf, obuf, false); fo->write(obuf, ph.u_len); obuf.dealloc(); ibuf.dealloc(); ph = ph_tmp; } /************************************************************************* // **************************************************************************/ const int *PackVmlinuxI386::getCompressionMethods(int method, int level) const { return Packer::getDefaultCompressionMethods_le32(method, level); } const int *PackVmlinuxI386::getFilters() const { static const int filters[] = { 0x49, 0x46, FT_END }; return filters; } const int *PackVmlinuxARMEL::getCompressionMethods(int method, int level) const { return Packer::getDefaultCompressionMethods_8(method, level); } const int *PackVmlinuxARMEB::getCompressionMethods(int method, int level) const { return Packer::getDefaultCompressionMethods_8(method, level); } const int *PackVmlinuxPPC32::getCompressionMethods(int method, int level) const { // No real dependency on LE32. return Packer::getDefaultCompressionMethods_le32(method, level); } const int *PackVmlinuxPPC64LE::getCompressionMethods(int method, int level) const { // No real dependency on LE32. return Packer::getDefaultCompressionMethods_le32(method, level); } const int *PackVmlinuxARMEL::getFilters() const { static const int f50[] = { 0x50, FT_END }; return f50; } const int *PackVmlinuxARMEB::getFilters() const { static const int f51[] = { 0x51, FT_END }; return f51; } const int *PackVmlinuxPPC32::getFilters() const { static const int fd0[] = { 0xd0, FT_END }; return fd0; } const int *PackVmlinuxPPC64LE::getFilters() const { static const int fd0[] = { 0xd0, FT_END }; return fd0; } // // Examples as of 2004-07-16 [readelf --segments vmlinux # before fiddling]: // //----- kernel-2.6.7 plain [defconfig?] //Program Headers(2): // Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align // LOAD 0x001000 0x00100000 0x00100000 0x1c7e61 0x1c7e61 R E 0x1000 // LOAD 0x1c8e64 0x002c8e64 0x002c8e64 0x00000 0x00000 RW 0x1000 // //----- kernel-2.6.7-1.488 Fedora Core 3 test 1 //Program Headers(5): // Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align // LOAD 0x001000 0x02100000 0x02100000 0x202246 0x202246 R E 0x1000 // LOAD 0x204000 0xffff3000 0x02303000 0x00664 0x00664 R E 0x1000 // LOAD 0x205000 0x02304000 0x02304000 0x43562 0x43562 R 0x1000 // LOAD 0x249000 0x02348000 0x02348000 0x81800 0xcb0fc RWE 0x1000 // STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4 // //----- kernel-2.6.18-1.2778 Fedora Core 6 test 3 //Program Headers(3) // Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align // LOAD 0x001000 0xc0400000 0x00400000 0x279820 0x279820 R E 0x1000 // LOAD 0x27b000 0xc067a000 0x0067a000 0x10ee64 0x1b07e8 RWE 0x1000 // NOTE 0x000000 0x00000000 0x00000000 0x00000 0x00000 R 0x4 bool PackVmlinuxI386::is_valid_e_entry(Addr e_entry) { return 0==(0x000fffff & e_entry); // entry on whole 1 MiB } Linker* PackVmlinuxI386::newLinker() const { return new ElfLinkerX86; } void PackVmlinuxI386::buildLoader(const Filter *ft) { // prepare loader initLoader(stub_i386_linux_kernel_vmlinux, sizeof(stub_i386_linux_kernel_vmlinux)); addLoader("LINUX000", (0x40==(0xf0 & ft->id)) ? "LXCKLLT1" : (ft->id ? "LXCALLT1" : ""), "LXMOVEUP", getDecompressorSections(), NULL ); if (ft->id) { assert(ft->calls > 0); if (0x40==(0xf0 & ft->id)) { addLoader("LXCKLLT9", NULL); } else { addLoader("LXCALLT9", NULL); } addFilter32(ft->id); } addLoader("LINUX990", ((ph.first_offset_found == 1) ? "LINUX991" : ""), "LINUX992,IDENTSTR,UPX1HEAD", NULL); } void PackVmlinuxAMD64::buildLoader(const Filter *ft) { // prepare loader initLoader(stub_amd64_linux_kernel_vmlinux, sizeof(stub_amd64_linux_kernel_vmlinux)); addLoader("LINUX000", (0x40==(0xf0 & ft->id)) ? "LXCKLLT1" : (ft->id ? "LXCALLT1" : ""), "LXMOVEUP", getDecompressorSections(), NULL ); if (ft->id) { assert(ft->calls > 0); if (0x40==(0xf0 & ft->id)) { addLoader("LXCKLLT9", NULL); } else { addLoader("LXCALLT9", NULL); } addFilter32(ft->id); } addLoader("LINUX990", ((ph.first_offset_found == 1) ? "LINUX991" : ""), "LINUX992,IDENTSTR,UPX1HEAD", NULL); } bool PackVmlinuxARMEL::is_valid_e_entry(Addr e_entry) { return 0xc0008000==e_entry; } bool PackVmlinuxARMEB::is_valid_e_entry(Addr e_entry) { return 0xc0008000==e_entry; } bool PackVmlinuxPPC32::is_valid_e_entry(Addr e_entry) { return 0xc0000000==e_entry; } bool PackVmlinuxPPC64LE::is_valid_e_entry(Addr e_entry) { return 0xc0000000==e_entry; } Linker* PackVmlinuxARMEL::newLinker() const { return new ElfLinkerArmLE; } Linker* PackVmlinuxARMEB::newLinker() const { return new ElfLinkerArmBE; } Linker* PackVmlinuxPPC32::newLinker() const { return new ElfLinkerPpc32; } Linker* PackVmlinuxPPC64LE::newLinker() const { return new ElfLinkerPpc64le; } void PackVmlinuxARMEL::buildLoader(const Filter *ft) { // prepare loader initLoader(stub_arm_v5a_linux_kernel_vmlinux, sizeof(stub_arm_v5a_linux_kernel_vmlinux)); addLoader("LINUX000", NULL); if (ft->id) { assert(ft->calls > 0); addLoader("LINUX010", NULL); } addLoader("LINUX020", NULL); if (ft->id) { addFilter32(ft->id); } addLoader("LINUX030", NULL); if (ph.method == M_NRV2E_8) addLoader("NRV2E", NULL); else if (ph.method == M_NRV2B_8) addLoader("NRV2B", NULL); else if (ph.method == M_NRV2D_8) addLoader("NRV2D", NULL); else if (M_IS_LZMA(ph.method)) addLoader("LZMA_ELF00,LZMA_DEC10,LZMA_DEC30", NULL); else throwBadLoader(); addLoader("IDENTSTR,UPX1HEAD", NULL); } void PackVmlinuxARMEB::buildLoader(const Filter *ft) { // prepare loader initLoader(stub_armeb_v5a_linux_kernel_vmlinux, sizeof(stub_armeb_v5a_linux_kernel_vmlinux)); addLoader("LINUX000", NULL); if (ft->id) { assert(ft->calls > 0); addLoader("LINUX010", NULL); } addLoader("LINUX020", NULL); if (ft->id) { addFilter32(ft->id); } addLoader("LINUX030", NULL); if (ph.method == M_NRV2E_8) addLoader("NRV2E", NULL); else if (ph.method == M_NRV2B_8) addLoader("NRV2B", NULL); else if (ph.method == M_NRV2D_8) addLoader("NRV2D", NULL); else if (M_IS_LZMA(ph.method)) addLoader("LZMA_ELF00,LZMA_DEC10,LZMA_DEC30", NULL); else throwBadLoader(); addLoader("IDENTSTR,UPX1HEAD", NULL); } void PackVmlinuxPPC32::buildLoader(const Filter *ft) { // prepare loader initLoader(stub_powerpc_linux_kernel_vmlinux, sizeof(stub_powerpc_linux_kernel_vmlinux)); addLoader("LINUX000", NULL); if (ft->id) { assert(ft->calls > 0); addLoader("LINUX010", NULL); } addLoader("LINUX020", NULL); if (ft->id) { addFilter32(ft->id); } addLoader("LINUX030", NULL); if (ph.method == M_NRV2E_LE32) addLoader("NRV2E,NRV_TAIL", NULL); else if (ph.method == M_NRV2B_LE32) addLoader("NRV2B,NRV_TAIL", NULL); else if (ph.method == M_NRV2D_LE32) addLoader("NRV2D,NRV_TAIL", NULL); else if (M_IS_LZMA(ph.method)) addLoader("LZMA_ELF00,LZMA_DEC10,LZMA_DEC30", NULL); else throwBadLoader(); if (hasLoaderSection("CFLUSH")) addLoader("CFLUSH"); addLoader("IDENTSTR,UPX1HEAD", NULL); } static const #include "stub/powerpc64le-linux.kernel.vmlinux.h" void PackVmlinuxPPC64LE::buildLoader(const Filter *ft) { // prepare loader initLoader(stub_powerpc64le_linux_kernel_vmlinux, sizeof(stub_powerpc64le_linux_kernel_vmlinux)); addLoader("LINUX000", NULL); if (ft->id) { assert(ft->calls > 0); addLoader("LINUX010", NULL); } addLoader("LINUX020", NULL); if (ft->id) { addFilter32(ft->id); } addLoader("LINUX030", NULL); if (ph.method == M_NRV2E_LE32) addLoader("NRV2E,NRV_TAIL", NULL); else if (ph.method == M_NRV2B_LE32) addLoader("NRV2B,NRV_TAIL", NULL); else if (ph.method == M_NRV2D_LE32) addLoader("NRV2D,NRV_TAIL", NULL); else if (M_IS_LZMA(ph.method)) addLoader("LZMA_ELF00,LZMA_DEC10,LZMA_DEC30", NULL); else throwBadLoader(); if (hasLoaderSection("CFLUSH")) addLoader("CFLUSH"); addLoader("IDENTSTR,UPX1HEAD", NULL); } static const #include "stub/i386-linux.kernel.vmlinux-head.h" static const #include "stub/amd64-linux.kernel.vmlinux-head.h" static const #include "stub/arm.v5a-linux.kernel.vmlinux-head.h" static const #include "stub/armeb.v5a-linux.kernel.vmlinux-head.h" static const #include "stub/powerpc-linux.kernel.vmlinux-head.h" unsigned PackVmlinuxI386::write_vmlinux_head( OutputFile *fo, Shdr *stxt ) { // COMPRESSED_LENGTH fo->write(&stub_i386_linux_kernel_vmlinux_head[0], sizeof(stub_i386_linux_kernel_vmlinux_head)-(1+ 4) +1); TE32 tmp_u32; tmp_u32 = ph.c_len; fo->write(&tmp_u32, 4); stxt->sh_size += sizeof(stub_i386_linux_kernel_vmlinux_head); return sizeof(stub_i386_linux_kernel_vmlinux_head); } unsigned PackVmlinuxAMD64::write_vmlinux_head( OutputFile *fo, Shdr *stxt ) { // COMPRESSED_LENGTH fo->write(&stub_amd64_linux_kernel_vmlinux_head[0], sizeof(stub_amd64_linux_kernel_vmlinux_head)-(1+ 4) +1); TE32 tmp_u32; tmp_u32 = ph.c_len; fo->write(&tmp_u32, 4); printf(" Compressed length=0x%x\n", ph.c_len); printf("UnCompressed length=0x%x\n", ph.u_len); stxt->sh_size += sizeof(stub_amd64_linux_kernel_vmlinux_head); return sizeof(stub_amd64_linux_kernel_vmlinux_head); } void PackVmlinuxARMEL::defineDecompressorSymbols() { super::defineDecompressorSymbols(); linker->defineSymbol( "COMPRESSED_LENGTH", ph.c_len); linker->defineSymbol("UNCOMPRESSED_LENGTH", ph.u_len); linker->defineSymbol("METHOD", ph.method); } void PackVmlinuxARMEB::defineDecompressorSymbols() { super::defineDecompressorSymbols(); linker->defineSymbol( "COMPRESSED_LENGTH", ph.c_len); linker->defineSymbol("UNCOMPRESSED_LENGTH", ph.u_len); linker->defineSymbol("METHOD", ph.method); } void PackVmlinuxPPC32::defineDecompressorSymbols() { super::defineDecompressorSymbols(); // linker->defineSymbol( "COMPRESSED_LENGTH", ph.c_len); // linker->defineSymbol("UNCOMPRESSED_LENGTH", ph.u_len); // linker->defineSymbol("METHOD", ph.method); } void PackVmlinuxPPC64LE::defineDecompressorSymbols() { super::defineDecompressorSymbols(); // linker->defineSymbol( "COMPRESSED_LENGTH", ph.c_len); // linker->defineSymbol("UNCOMPRESSED_LENGTH", ph.u_len); // linker->defineSymbol("METHOD", ph.method); } void PackVmlinuxI386::defineDecompressorSymbols() { super::defineDecompressorSymbols(); linker->defineSymbol("ENTRY_POINT", phdri[0].p_paddr); linker->defineSymbol("PHYSICAL_START", phdri[0].p_paddr); } void PackVmlinuxAMD64::defineDecompressorSymbols() { super::defineDecompressorSymbols(); // We assume a 32-bit boot loader, so we use the 32-bit convention // of "enter at the beginning" (startup_32). The 64-bit convention // would be to use ehdri.e_entry (startup_64). linker->defineSymbol("ENTRY_POINT", (unsigned) phdri[0].p_paddr); linker->defineSymbol("PHYSICAL_START", (unsigned) phdri[0].p_paddr); } unsigned PackVmlinuxARMEL::write_vmlinux_head( OutputFile *fo, Shdr *stxt ) { // First word from vmlinux-head.S fo->write(&stub_arm_v5a_linux_kernel_vmlinux_head[0], 4); // Second word TE32 tmp_u32; unsigned const t = (0xff000000 & BeLePolicy::get32(&stub_arm_v5a_linux_kernel_vmlinux_head[4])) | (0x00ffffff & (0u - 1 + ((3+ ph.c_len)>>2))); tmp_u32 = t; fo->write(&tmp_u32, 4); stxt->sh_addralign = 4; stxt->sh_size += sizeof(stub_arm_v5a_linux_kernel_vmlinux_head); return sizeof(stub_arm_v5a_linux_kernel_vmlinux_head); } unsigned PackVmlinuxARMEB::write_vmlinux_head( OutputFile *fo, Shdr *stxt ) { // First word from vmlinux-head.S fo->write(&stub_armeb_v5a_linux_kernel_vmlinux_head[0], 4); // Second word TE32 tmp_u32; unsigned const t = (0xff000000 & BeLePolicy::get32(&stub_armeb_v5a_linux_kernel_vmlinux_head[4])) | (0x00ffffff & (0u - 1 + ((3+ ph.c_len)>>2))); tmp_u32 = t; fo->write(&tmp_u32, 4); stxt->sh_addralign = 4; stxt->sh_size += sizeof(stub_armeb_v5a_linux_kernel_vmlinux_head); return sizeof(stub_armeb_v5a_linux_kernel_vmlinux_head); } unsigned PackVmlinuxPPC32::write_vmlinux_head( OutputFile * /*fo*/, Shdr * /*stxt*/ ) { return 0; } unsigned PackVmlinuxPPC64LE::write_vmlinux_head( OutputFile * /*const fo*/, Shdr * /*const stxt*/ ) { return 0; } bool PackVmlinuxARMEL::has_valid_vmlinux_head() { TE32 buf[2]; fi->seek(p_text->sh_offset + sizeof(stub_arm_v5a_linux_kernel_vmlinux_head) -8, SEEK_SET); fi->readx(buf, sizeof(buf)); //unsigned const word0 = buf[0]; unsigned const word1 = buf[1]; if (0xeb==(word1>>24) && (0x00ffffff& word1)==(0u - 1 + ((3+ ph.c_len)>>2))) { return true; } return false; } bool PackVmlinuxARMEB::has_valid_vmlinux_head() { TE32 buf[2]; fi->seek(p_text->sh_offset + sizeof(stub_armeb_v5a_linux_kernel_vmlinux_head) -8, SEEK_SET); fi->readx(buf, sizeof(buf)); //unsigned const word0 = buf[0]; unsigned const word1 = buf[1]; if (0xeb==(word1>>24) && (0x00ffffff& word1)==(0u - 1 + ((3+ ph.c_len)>>2))) { return true; } return false; } bool PackVmlinuxPPC32::has_valid_vmlinux_head() { TE32 buf[2]; fi->seek(p_text->sh_offset + sizeof(stub_powerpc_linux_kernel_vmlinux_head) -8, SEEK_SET); fi->readx(buf, sizeof(buf)); //unsigned const word0 = buf[0]; unsigned const word1 = buf[1]; if (0xeb==(word1>>24) && (0x00ffffff& word1)==(0u - 1 + ((3+ ph.c_len)>>2))) { return true; } return false; } #include "stub/powerpc64le-linux.kernel.vmlinux-head.h" bool PackVmlinuxPPC64LE::has_valid_vmlinux_head() { TE64 buf[2]; fi->seek(p_text->sh_offset + sizeof(stub_powerpc64le_linux_kernel_vmlinux_head) -8, SEEK_SET); fi->readx(buf, sizeof(buf)); //unsigned const word0 = buf[0]; unsigned const word1 = buf[1]; if (0xeb==(word1>>24) && (0x00ffffff& word1)==(0u - 1 + ((3+ ph.c_len)>>2))) { return true; } return false; } bool PackVmlinuxI386::has_valid_vmlinux_head() { unsigned char buf[5]; fi->seek(p_text->sh_offset + sizeof(stub_i386_linux_kernel_vmlinux_head) -5, SEEK_SET); fi->readx(&buf[0], 5); if (0xE8!=buf[0] || BeLePolicy::get32(&buf[1]) != ph.c_len) { return false; } return true; } bool PackVmlinuxAMD64::has_valid_vmlinux_head() { unsigned char buf[5]; fi->seek(p_text->sh_offset + sizeof(stub_amd64_linux_kernel_vmlinux_head) -5, SEEK_SET); fi->readx(&buf[0], 5); if (0xE8!=buf[0] || BeLePolicy::get32(&buf[1]) != ph.c_len) { return false; } return true; } /************************************************************************* // **************************************************************************/ const int *PackVmlinuxAMD64::getCompressionMethods(int method, int level) const { return Packer::getDefaultCompressionMethods_le32(method, level); } const int *PackVmlinuxAMD64::getFilters() const { static const int filters[] = { 0x49, 0x46, -1 }; return filters; } bool PackVmlinuxAMD64::is_valid_e_entry(Addr e_entry) { return 0x200000<=e_entry; // 2 MiB } Linker* PackVmlinuxAMD64::newLinker() const { return new ElfLinkerX86; } // instantiate instances template class PackVmlinuxBase; template class PackVmlinuxBase; template class PackVmlinuxBase; /* vim:set ts=4 sw=4 et: */