📄 p_vmlinx.cpp
字号:
/* p_vmlinx.cpp -- pack vmlinux ET_EXEC file (before bootsect or setup) This file is part of the UPX executable compressor. Copyright (C) 2004-2007 John Reiser Copyright (C) 1996-2007 Markus Franz Xaver Johannes Oberhumer Copyright (C) 1996-2007 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 markus@oberhumer.com ml1050@users.sourceforge.net John Reiser jreiser@users.sourceforge.net */#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-linux.kernel.vmlinux.h"static const#include "stub/armeb-linux.kernel.vmlinux.h"static const#include "stub/powerpc-linux.kernel.vmlinux.h"/*************************************************************************//**************************************************************************/template <class T>PackVmlinuxBase<T>::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<BeLePolicy>();}template <class T>PackVmlinuxBase<T>::~PackVmlinuxBase(){ delete [] phdri; delete [] shdri; delete [] shstrtab;}template <class T>int PackVmlinuxBase<T>::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 <class T>int __acc_cdecl_qsortPackVmlinuxBase<T>::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 <class T>typename T::Shdr const *PackVmlinuxBase<T>::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 <class T>bool PackVmlinuxBase<T>::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 <elf.h> ? ) { 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 && 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 <class T>void PackVmlinuxBase<T>::pack(OutputFile *fo){ unsigned fo_off = 0; Ehdr ehdro; U32 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: ~28KB 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"); 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 } __attribute_packed; 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; 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -