Change class PackLinuxI386elf : public PackLinuxI386

into   class PackLinuxElf32x86 : public PackLinuxElf32Le : public PackLinuxElf32
which makes the code for compressing ELF (for direct-to-memory decompression)
cleaner because ELF has some of the class tree to itself.
This assumes PROT_EXEC|PROT_WRITE ought to work.
This is checked at runtime, and if it fails then
give a message on stderr and exit(127).

Modify --force-execve so that runtime avoids PROT_EXEC|PROT_WRITE.
Then executables compressed with --force-execve will run under
PaX, grSecurity, and strict SELinux.
This commit is contained in:
John Reiser
2006-05-21 13:58:24 -07:00
parent 69a00c0ab3
commit d2718e99a4
26 changed files with 1049 additions and 969 deletions
+203 -29
View File
@@ -270,6 +270,134 @@ umax(unsigned a, unsigned b)
return a;
}
int
PackLinuxElf32x86::buildLinuxLoader(
upx_byte const *const proto,
unsigned const szproto,
upx_byte const *const fold,
unsigned const szfold,
Filter const *ft
)
{
initLoader(proto, szproto);
struct b_info h; memset(&h, 0, sizeof(h));
unsigned fold_hdrlen = 0;
if (0 < szfold) {
cprElfHdr1 const *const hf = (cprElfHdr1 const *)fold;
fold_hdrlen = sizeof(hf->ehdr) + hf->ehdr.e_phentsize * hf->ehdr.e_phnum +
sizeof(l_info);
if (0 == get_le32(fold_hdrlen + fold)) {
// inconsistent SIZEOF_HEADERS in *.lds (ld, binutils)
fold_hdrlen = umax(0x80, fold_hdrlen);
}
h.sz_unc = (szfold < fold_hdrlen) ? 0 : (szfold - fold_hdrlen);
h.b_method = (unsigned char) ph.method;
h.b_ftid = (unsigned char) ph.filter;
h.b_cto8 = (unsigned char) ph.filter_cto;
}
unsigned char const *const uncLoader = fold_hdrlen + fold;
unsigned char *const cprLoader = new unsigned char[sizeof(h) + h.sz_unc];
if (0 < szfold) {
unsigned sz_cpr;
int r = upx_compress(uncLoader, h.sz_unc, sizeof(h) + cprLoader, &sz_cpr,
NULL, ph.method, 10, NULL, NULL );
if (r != UPX_E_OK || h.sz_cpr >= h.sz_unc)
throwInternalError("loader compression failed");
h.sz_cpr = sz_cpr;
}
memcpy(cprLoader, &h, sizeof(h));
// This adds the definition to the "library", to be used later.
linker->addSection("FOLDEXEC", cprLoader, sizeof(h) + h.sz_cpr);
delete [] cprLoader;
int const n_mru = ft->n_mru; // FIXME: belongs to filter? packerf?
// Here is a quick summary of the format of the output file:
linker->setLoaderAlignOffset(
// Elf32_Edhr
sizeof(elfout.ehdr) +
// Elf32_Phdr: 1 for exec86, 2 for sh86, 3 for elf86
(elfout.ehdr.e_phentsize * elfout.ehdr.e_phnum) +
// checksum UPX! lsize version format
sizeof(l_info) +
// PT_DYNAMIC with DT_NEEDED "forwarded" from original file
((elfout.ehdr.e_phnum==3) ? (unsigned) elfout.phdr[2].p_memsz : 0) +
// p_progid, p_filesize, p_blocksize
sizeof(p_info) +
// compressed data
b_len + ph.c_len );
// entry to stub
addLoader("LEXEC000", NULL);
if (ft->id) {
{ // decompr, unfilter are separate
addLoader("LXUNF000", NULL);
addLoader("LXUNF002", NULL);
if (0x80==(ft->id & 0xF0)) {
if (256==n_mru) {
addLoader("MRUBYTE0", NULL);
}
else if (n_mru) {
addLoader("LXMRU005", NULL);
}
if (n_mru) {
addLoader("LXMRU006", NULL);
}
else {
addLoader("LXMRU007", NULL);
}
}
else if (0x40==(ft->id & 0xF0)) {
addLoader("LXUNF008", NULL);
}
addLoader("LXUNF010", NULL);
}
if (n_mru) {
addLoader("LEXEC009", NULL);
}
}
addLoader("LEXEC010", NULL);
addLoader(getDecompressor(), NULL);
addLoader("LEXEC015", NULL);
if (ft->id) {
{ // decompr, unfilter are separate
if (0x80!=(ft->id & 0xF0)) {
addLoader("LXUNF042", NULL);
}
}
addFilter32(ft->id);
{ // decompr, unfilter are separate
if (0x80==(ft->id & 0xF0)) {
if (0==n_mru) {
addLoader("LXMRU058", NULL);
}
}
addLoader("LXUNF035", NULL);
}
}
else {
addLoader("LEXEC017", NULL);
}
addLoader("IDENTSTR", NULL);
addLoader("LEXEC020", NULL);
addLoader("FOLDEXEC", NULL);
char *ptr_cto = (char *)const_cast<unsigned char *>(getLoader());
int sz_cto = getLoaderSize();
if (0x20==(ft->id & 0xF0) || 0x30==(ft->id & 0xF0)) { // push byte '?' ; cto8
patch_le16(ptr_cto, sz_cto, "\x6a?", 0x6a + (ft->cto << 8));
checkPatch(NULL, 0, 0, 0); // reset
}
// PackHeader and overlay_offset at the end of the output file,
// after the compressed data.
return getLoaderSize();
}
int
PackLinuxElf32ppc::buildLinuxLoader(
upx_byte const *const proto,
@@ -280,7 +408,7 @@ PackLinuxElf32ppc::buildLinuxLoader(
)
{
{
int const MAX_LOADER_LEN = 4096;
int const MAX_LOADER_LEN = 8000;
int *const eof_empty = new int[MAX_LOADER_LEN/sizeof(int)];
eof_empty[0] = -1;
initLoader(eof_empty, MAX_LOADER_LEN, 0, 0);
@@ -320,9 +448,9 @@ PackLinuxElf32ppc::buildLinuxLoader(
//int const GAP = 128; // must match stub/l_mac_ppc.S
//segcmdo.vmsize += sz_unc - sz_cpr + GAP + 64;
linker->addSection("ELFPPC32", proto, szproto);
linker->addSection("ELF32PPC", proto, szproto);
addLoader("ELFPPC32", NULL);
addLoader("ELF32PPC", NULL);
addLoader("FOLDEXEC", NULL);
return getLoaderSize();
}
@@ -539,6 +667,22 @@ PackLinuxElf32::getbrk(const Elf32_Phdr *phdr, int e_phnum) const
return brka;
}
off_t
PackLinuxElf32::getbase(const Elf32_Phdr *phdr, int e_phnum) const
{
off_t base = ~0u;
for (int j = 0; j < e_phnum; ++phdr, ++j) {
if (phdr->PT_LOAD == phdr->p_type) {
if (phdr->p_vaddr < (unsigned) base)
base = phdr->p_vaddr;
}
}
if (0!=base) {
return base;
}
return 0x12000;
}
off_t
PackLinuxElf64::getbrk(const Elf64_Phdr *phdr, int e_phnum) const
{
@@ -637,6 +781,27 @@ PackLinuxElf64::generateElfHdr(
}
}
void
PackLinuxElf32x86::generateElfHdr(
OutputFile *fo,
void const *proto,
unsigned const brka
)
{
super::generateElfHdr(fo, proto, brka);
if (ph.format==UPX_F_LINUX_ELF_i386) {
cprElfHdr2 *const h2 = (cprElfHdr2 *)&elfout;
assert(2==get_native16(&h2->ehdr.e_phnum));
set_native32(&h2->phdr[0].p_flags, ~Elf32_Phdr::PF_W & get_native32(&h2->phdr[0].p_flags));
memset(&h2->linfo, 0, sizeof(h2->linfo));
fo->write(h2, sizeof(*h2));
}
else {
assert(false); // unknown ph.format, PackLinuxElf32x86::generateElfHdr
}
}
void
PackLinuxElf32ppc::generateElfHdr(
OutputFile *fo,
@@ -1016,10 +1181,17 @@ void PackLinuxElf32::pack4(OutputFile *fo, Filter &ft)
set_native32(&elfout.phdr[0].p_filesz, len);
super::pack4(fo, ft); // write PackHeader and overlay_offset
// Cannot pre-round .p_memsz. If .p_filesz < .p_memsz, then kernel
// tries to make .bss, which requires PF_W.
// But strict SELinux (or PaX, grSecurity) disallows PF_W with PF_X.
#if 0 /*{*/
#define PAGE_MASK (~0u<<12)
// pre-calculate for benefit of runtime disappearing act via munmap()
set_native32(&elfout.phdr[0].p_memsz, PAGE_MASK & (~PAGE_MASK + len));
#undef PAGE_MASK
#else /*}{*/
set_native32(&elfout.phdr[0].p_memsz, len);
#endif /*}*/
// rewrite Elf header
if (Elf32_Ehdr::ET_DYN==ehdri.e_type) {
@@ -1046,10 +1218,17 @@ void PackLinuxElf64::pack4(OutputFile *fo, Filter &ft)
set_native64(&elfout.phdr[0].p_filesz, len);
super::pack4(fo, ft); // write PackHeader and overlay_offset
// Cannot pre-round .p_memsz. If .p_filesz < .p_memsz, then kernel
// tries to make .bss, which requires PF_W.
// But strict SELinux (or PaX, grSecurity) disallows PF_W with PF_X.
#if 0 /*{*/
#define PAGE_MASK (~0u<<12)
// pre-calculate for benefit of runtime disappearing act via munmap()
set_native64(&elfout.phdr[0].p_memsz, PAGE_MASK & (~PAGE_MASK + len));
#undef PAGE_MASK
#else /*}{*/
set_native64(&elfout.phdr[0].p_memsz, len);
#endif /*}*/
// rewrite Elf header
fo->seek(0, SEEK_SET);
@@ -1271,20 +1450,20 @@ void PackLinuxElf64::unpack(OutputFile *fo)
**************************************************************************/
PackLinuxI386elf::PackLinuxI386elf(InputFile *f) :
PackLinuxElf32x86::PackLinuxElf32x86(InputFile *f) :
super(f), phdri(NULL),
file_image(NULL), dynseg(NULL), hashtab(NULL), dynstr(NULL), dynsym(NULL)
{
}
PackLinuxI386elf::~PackLinuxI386elf()
PackLinuxElf32x86::~PackLinuxElf32x86()
{
delete[] file_image;
delete[] phdri;
}
int const *
PackLinuxI386elf::getFilters() const
PackLinuxElf32x86::getFilters() const
{
static const int filters[] = {
0x49, 0x46,
@@ -1307,7 +1486,7 @@ PackLinuxI386elf::getFilters() const
}
int
PackLinuxI386elf::buildLoader(const Filter *ft)
PackLinuxElf32x86::buildLoader(const Filter *ft)
{
unsigned char tmp[sizeof(linux_i386elf_fold)];
memcpy(tmp, linux_i386elf_fold, sizeof(linux_i386elf_fold));
@@ -1330,10 +1509,10 @@ PackLinuxI386elf::buildLoader(const Filter *ft)
}
void PackLinuxI386elf::patchLoader() { }
void PackLinuxElf32x86::patchLoader() { }
bool PackLinuxI386elf::canPack()
bool PackLinuxElf32x86::canPack()
{
unsigned char buf[sizeof(Elf32_Ehdr) + 14*sizeof(Elf32_Phdr)];
COMPILE_TIME_ASSERT(sizeof(buf) <= 512);
@@ -1345,7 +1524,8 @@ bool PackLinuxI386elf::canPack()
Elf32_Ehdr const *const ehdr = (Elf32_Ehdr const *)buf;
// now check the ELF header
if (checkEhdr(ehdr) != 0)
if (checkEhdr(ehdr, Elf32_Ehdr::EM_386,
Elf32_Ehdr::ELFCLASS32, Elf32_Ehdr::ELFDATA2LSB) != 0)
return false;
// additional requirements for linux/elf386
@@ -1441,7 +1621,7 @@ bool PackLinuxI386elf::canPack()
}
unsigned
PackLinuxI386elf::elf_get_offset_from_address(unsigned const addr) const
PackLinuxElf32x86::elf_get_offset_from_address(unsigned const addr) const
{
Elf32_Phdr const *phdr = phdri;
int j = ehdri.e_phnum;
@@ -1455,7 +1635,7 @@ PackLinuxI386elf::elf_get_offset_from_address(unsigned const addr) const
}
void const *
PackLinuxI386elf::elf_find_dynamic(unsigned int const key) const
PackLinuxElf32x86::elf_find_dynamic(unsigned int const key) const
{
Elf32_Dyn const *dynp= dynseg;
if (dynp)
@@ -1469,7 +1649,7 @@ PackLinuxI386elf::elf_find_dynamic(unsigned int const key) const
return 0;
}
unsigned PackLinuxI386elf::elf_hash(char const *p)
unsigned PackLinuxElf32x86::elf_hash(char const *p)
{
unsigned h;
for (h= 0; 0!=*p; ++p) {
@@ -1483,7 +1663,7 @@ unsigned PackLinuxI386elf::elf_hash(char const *p)
return h;
}
Elf32_Sym const *PackLinuxI386elf::elf_lookup(char const *name) const
Elf32_Sym const *PackLinuxElf32x86::elf_lookup(char const *name) const
{
if (hashtab && dynsym && dynstr) {
unsigned const nbucket = hashtab[0];
@@ -1502,7 +1682,7 @@ Elf32_Sym const *PackLinuxI386elf::elf_lookup(char const *name) const
}
void PackLinuxI386elf::pack1(OutputFile *fo, Filter &)
void PackLinuxElf32x86::pack1(OutputFile *fo, Filter &)
{
fi->seek(0, SEEK_SET);
fi->readx(&ehdri, sizeof(ehdri));
@@ -1517,7 +1697,7 @@ void PackLinuxI386elf::pack1(OutputFile *fo, Filter &)
generateElfHdr(fo, linux_i386elf_fold, getbrk(phdri, ehdri.e_phnum) );
}
unsigned PackLinuxI386elf::find_LOAD_gap(
unsigned PackLinuxElf32x86::find_LOAD_gap(
Elf32_Phdr const *const phdri,
unsigned const k,
unsigned const e_phnum
@@ -1551,7 +1731,7 @@ unsigned PackLinuxI386elf::find_LOAD_gap(
return lo - hi;
}
void PackLinuxI386elf::pack2(OutputFile *fo, Filter &ft)
void PackLinuxElf32x86::pack2(OutputFile *fo, Filter &ft)
{
Extent x;
unsigned k;
@@ -1612,7 +1792,7 @@ void PackLinuxI386elf::pack2(OutputFile *fo, Filter &ft)
set_native32(&elfout.phdr[0].p_filesz, fo->getBytesWritten());
}
void PackLinuxI386elf::pack3(OutputFile *fo, Filter &ft)
void PackLinuxElf32x86::pack3(OutputFile *fo, Filter &ft)
{
unsigned disp;
unsigned len = fo->getBytesWritten();
@@ -1622,7 +1802,7 @@ void PackLinuxI386elf::pack3(OutputFile *fo, Filter &ft)
set_native32(&disp, len);
fo->write(&disp, sizeof(disp));
// We have packed multiple blocks, so PackLinuxI386::buildLinuxLoader
// We have packed multiple blocks, so PackLinuxElfxx::buildLinuxLoader
// needs an adjusted ph.c_len in order to get alignment correct.
unsigned const save_c_len = ph.c_len;
ph.c_len = sizeof(disp) + len - (
@@ -1644,17 +1824,10 @@ void PackLinuxI386elf::pack3(OutputFile *fo, Filter &ft)
super::pack3(fo, ft);
}
void PackLinuxI386elf::pack4(OutputFile *fo, Filter &ft)
void PackLinuxElf32x86::pack4(OutputFile *fo, Filter &ft)
{
super::pack4(fo, ft); // write PackHeader and overlay_offset
#if 0 /*{ where was this done already? FIXME */
#define PAGE_MASK (~0u<<12)
// pre-calculate for benefit of runtime disappearing act via munmap()
set_native32(&elfout.phdr[0].p_memsz, PAGE_MASK & (~PAGE_MASK + len));
#undef PAGE_MASK
#endif /*}*/
// rewrite Elf header
if (Elf32_Ehdr::ET_DYN==ehdri.e_type) {
// File being compressed was -pie.
@@ -1665,14 +1838,15 @@ void PackLinuxI386elf::pack4(OutputFile *fo, Filter &ft)
elfout.ehdr.e_entry -= base;
elfout.phdr[0].p_vaddr -= base;
elfout.phdr[0].p_paddr -= base;
elfout.phdr[0].p_flags |= Elf32_Phdr::PF_W;
// Strict SELinux (or PaX, grSecurity) disallows PF_W with PF_X
//elfout.phdr[0].p_flags |= Elf32_Phdr::PF_W;
}
fo->seek(0, SEEK_SET);
fo->rewrite(&elfout, sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr));
//fo->rewrite(&linfo, sizeof(linfo));
}
void PackLinuxI386elf::unpack(OutputFile *fo)
void PackLinuxElf32x86::unpack(OutputFile *fo)
{
#define MAX_ELF_HDR 512
char bufehdr[MAX_ELF_HDR];