From 640d5b2decb3c08e4cc82f1fd876d052a5723b26 Mon Sep 17 00:00:00 2001 From: John Reiser Date: Tue, 13 Dec 2011 17:26:03 -0800 Subject: [PATCH] Option --preserve-build-id for Gnu ELF; contributed by Nicholas Twerdochlib (SourceForge [ upx-Patches-3413335 ]) --- src/help.cpp | 5 ++ src/main.cpp | 4 + src/options.h | 1 + src/p_lx_elf.cpp | 201 +++++++++++++++++++++++++++++++++++++++++++++-- src/p_lx_elf.h | 16 ++++ 5 files changed, 222 insertions(+), 5 deletions(-) diff --git a/src/help.cpp b/src/help.cpp index 96f5b54f..f0c4587a 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -290,6 +290,11 @@ void show_help(int verbose) " --strip-relocs=0 do not strip relocations\n" " --strip-relocs=1 strip relocations [default]\n" "\n"); + con_fprintf(f,"Options for linux/elf:\n"); + fg = con_fg(f,fg); + con_fprintf(f, + " --preserve-build-id copy .gnu.note.build-id to compressed output\n" + "\n"); } con_fprintf(f, "file.. executables to (de)compress\n"); diff --git a/src/main.cpp b/src/main.cpp index 6174aaf0..5637d5ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -898,6 +898,9 @@ static int do_option(int optc, const char *arg) case 674: opt->o_unix.unmap_all_pages = true; // val ? break; + case 675: + opt->o_unix.preserve_build_id = true; + break; case '\0': return -1; @@ -1048,6 +1051,7 @@ static const struct mfx_option longopts[] = {"OpenBSD", 0x10, 0, 669}, {"openbsd", 0x10, 0, 669}, {"unmap-all-pages", 0x10, 0, 674}, // linux /proc/self/exe vanishes + {"preserve-build-id", 0, 0, 675}, // watcom/le {"le", 0x10, 0, 620}, // produce LE output // win32/pe diff --git a/src/options.h b/src/options.h index bcc516d8..6adc2cb0 100644 --- a/src/options.h +++ b/src/options.h @@ -146,6 +146,7 @@ struct options_t { unsigned char osabi0; // replacement if 0==.e_ident[EI_OSABI] enum { SCRIPT_MAX = 32 }; const char *script_name; + bool preserve_build_id; // copy the build-id to the compressed binary } o_unix; struct { bool le; diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index 3b2aba5e..415dbe4b 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -51,6 +51,12 @@ static unsigned const EF_ARM_EABI_VER4 = 0x04000000; static unsigned const EF_ARM_EABI_VER5 = 0x05000000; +unsigned char PackLinuxElf::o_shstrtab[] = { \ +/*start*/ '\0', +/*offset 1*/ '.','n','o','t','e','.','g','n','u','.','b','u','i','l','d','-','i','d','\0', +/*offset 20*/ '.','s','h','s','t','r','t','a','b','\0' +}; + static unsigned umin(unsigned a, unsigned b) { @@ -180,7 +186,8 @@ PackLinuxElf::PackLinuxElf(InputFile *f) : super(f), e_phnum(0), file_image(NULL), dynstr(NULL), sz_phdrs(0), sz_elf_hdrs(0), sz_pack2(0), sz_pack2a(0), sz_pack2b(0), lg2_page(12), page_size(1u<sh_name); + if (0==strcmp(name, &shstrtab[ndx])) { + return shdr; + } + } + return 0; +} + Elf32_Shdr const *PackLinuxElf32::elf_find_section_type( unsigned const type ) const @@ -1652,8 +1674,14 @@ PackLinuxElf32::generateElfHdr( assert(get_te16(&h2->ehdr.e_ehsize) == sizeof(Elf32_Ehdr)); assert(get_te16(&h2->ehdr.e_phentsize) == sizeof(Elf32_Phdr)); set_te16(&h2->ehdr.e_shentsize, sizeof(Elf32_Shdr)); - h2->ehdr.e_shnum = 0; - h2->ehdr.e_shstrndx = 0; + if (o_elf_shnum) { + h2->ehdr.e_shnum = o_elf_shnum; + h2->ehdr.e_shstrndx = o_elf_shnum - 1; + } + else { + h2->ehdr.e_shnum = 0; + h2->ehdr.e_shstrndx = 0; + } sz_elf_hdrs = sizeof(*h2) - sizeof(linfo); // default set_te32(&h2->phdr[0].p_filesz, sizeof(*h2)); // + identsize; @@ -1878,8 +1906,14 @@ PackLinuxElf64::generateElfHdr( assert(get_te16(&h2->ehdr.e_ehsize) == sizeof(Elf64_Ehdr)); assert(get_te16(&h2->ehdr.e_phentsize) == sizeof(Elf64_Phdr)); set_te16(&h2->ehdr.e_shentsize, sizeof(Elf64_Shdr)); - h2->ehdr.e_shnum = 0; - h2->ehdr.e_shstrndx = 0; + if (o_elf_shnum) { + h2->ehdr.e_shnum = o_elf_shnum; + h2->ehdr.e_shstrndx = o_elf_shnum - 1; + } + else { + h2->ehdr.e_shnum = 0; + h2->ehdr.e_shstrndx = 0; + } sz_elf_hdrs = sizeof(*h2) - sizeof(linfo); // default set_te64(&h2->phdr[0].p_filesz, sizeof(*h2)); // + identsize; @@ -1965,6 +1999,65 @@ void PackLinuxElf32::pack1(OutputFile *fo, Filter & /*ft*/) memset(&linfo, 0, sizeof(linfo)); fo->write(&linfo, sizeof(linfo)); } + + // if the preserve build-id option was specified + if (opt->o_unix.preserve_build_id) { + n_elf_shnum = ehdri.e_shnum; + Elf32_Shdr *shdr = NULL; + + Elf32_Shdr const *tmp = shdri; + + if (! shdri) { + shdr = new Elf32_Shdr[n_elf_shnum]; + + fi->seek(0,SEEK_SET); + fi->seek(ehdri.e_shoff,SEEK_SET); + fi->readx((void*)shdr,ehdri.e_shentsize*ehdri.e_shnum); + + // set this so we can use elf_find_section_name + shdri = (Elf32_Shdr * const)shdr; + } + + //set the shstrtab + sec_strndx = &shdr[ehdri.e_shstrndx]; + + char *strtab = new char[sec_strndx->sh_size]; + fi->seek(0,SEEK_SET); + fi->seek(sec_strndx->sh_offset,SEEK_SET); + fi->readx(strtab,sec_strndx->sh_size); + + shstrtab = (const char*)strtab; + + Elf32_Shdr const *buildid = elf_find_section_name(".note.gnu.build-id"); + if (buildid) { + unsigned char *data = new unsigned char[buildid->sh_size]; + memset(data,0,buildid->sh_size); + fi->seek(0,SEEK_SET); + fi->seek(buildid->sh_offset,SEEK_SET); + fi->readx(data,buildid->sh_size); + + buildid_data = data; + + o_elf_shnum = 3; + memset(&shdrout.shdr,0,sizeof(shdrout)); + + //setup the build-id + memcpy(&shdrout.shdr[1],buildid, sizeof(shdrout.shdr[1])); + shdrout.shdr[1].sh_name = 1; + + //setup the shstrtab + memcpy(&shdrout.shdr[2],sec_strndx, sizeof(shdrout.shdr[2])); + shdrout.shdr[2].sh_name = 20; + shdrout.shdr[2].sh_size = 29; //size of our static shstrtab + } + + // repoint shdr in case it is used by code some where else + if (shdr) { + shdri = tmp; + delete [] shdr; + shdr = NULL; + } + } } void PackLinuxElf32x86::pack1(OutputFile *fo, Filter &ft) @@ -2094,6 +2187,68 @@ void PackLinuxElf64::pack1(OutputFile *fo, Filter & /*ft*/) memset(&linfo, 0, sizeof(linfo)); fo->write(&linfo, sizeof(linfo)); } + + // only execute if option present + if (opt->o_unix.preserve_build_id) { + // set this so we can use elf_find_section_name + n_elf_shnum = ehdri.e_shnum; + + // there is a class member similar to this, but I did not + // want to assume it would be available + Elf64_Shdr const *tmp = shdri; + + Elf64_Shdr *shdr = NULL; + + if (! shdri) { + shdr = new Elf64_Shdr[n_elf_shnum]; + + fi->seek(0,SEEK_SET); + fi->seek(ehdri.e_shoff,SEEK_SET); + fi->readx((void*)shdr,ehdri.e_shentsize*ehdri.e_shnum); + + // set this so we can use elf_find_section_name + shdri = (Elf64_Shdr * const)shdr; + } + + //set the shstrtab + sec_strndx = &shdri[ehdri.e_shstrndx]; + + char *strtab = new char[sec_strndx->sh_size]; + fi->seek(0,SEEK_SET); + fi->seek(sec_strndx->sh_offset,SEEK_SET); + fi->readx(strtab,sec_strndx->sh_size); + + shstrtab = (const char*)strtab; + + Elf64_Shdr const *buildid = elf_find_section_name(".note.gnu.build-id"); + if (buildid) { + unsigned char *data = new unsigned char[buildid->sh_size]; + memset(data,0,buildid->sh_size); + fi->seek(0,SEEK_SET); + fi->seek(buildid->sh_offset,SEEK_SET); + fi->readx(data,buildid->sh_size); + + buildid_data = data; + + o_elf_shnum = 3; + memset(&shdrout.shdr,0,sizeof(shdrout)); + + //setup the build-id + memcpy(&shdrout.shdr[1],buildid, sizeof(shdrout.shdr[1])); + shdrout.shdr[1].sh_name = 1; + + //setup the shstrtab + memcpy(&shdrout.shdr[2],sec_strndx, sizeof(shdrout.shdr[2])); + shdrout.shdr[2].sh_name = 20; + shdrout.shdr[2].sh_size = 29; //size of our static shstrtab + } + + if (shdr) { + shdri = tmp; + delete [] shdr; + shdr = NULL; + } + } } void PackLinuxElf64amd::pack1(OutputFile *fo, Filter &ft) @@ -2601,6 +2756,24 @@ void PackLinuxElf32::pack4(OutputFile *fo, Filter &ft) { overlay_offset = sz_elf_hdrs + sizeof(linfo); + if (opt->o_unix.preserve_build_id) { + // calc e_shoff here and write shdrout, then o_shstrtab + //NOTE: these are pushed last to ensure nothing is stepped on + //for the UPX structure. + unsigned const len = fpad4(fo); + set_te32(&elfout.ehdr.e_shoff,len); + + int const ssize = sizeof(shdrout); + + shdrout.shdr[2].sh_offset = len+ssize; + shdrout.shdr[1].sh_offset = shdrout.shdr[2].sh_offset+shdrout.shdr[2].sh_size; + + fo->write(&shdrout, ssize); + + fo->write(o_shstrtab,shdrout.shdr[2].sh_size); + fo->write(buildid_data,shdrout.shdr[1].sh_size); + } + // 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. @@ -2650,6 +2823,24 @@ void PackLinuxElf64::pack4(OutputFile *fo, Filter &ft) { overlay_offset = sz_elf_hdrs + sizeof(linfo); + if (opt->o_unix.preserve_build_id) { + // calc e_shoff here and write shdrout, then o_shstrtab + //NOTE: these are pushed last to ensure nothing is stepped on + //for the UPX structure. + unsigned const len = fpad4(fo); + set_te64(&elfout.ehdr.e_shoff,len); + + int const ssize = sizeof(shdrout); + + shdrout.shdr[2].sh_offset = len+ssize; + shdrout.shdr[1].sh_offset = shdrout.shdr[2].sh_offset+shdrout.shdr[2].sh_size; + + fo->write(&shdrout, ssize); + + fo->write(o_shstrtab,shdrout.shdr[2].sh_size); + fo->write(buildid_data,shdrout.shdr[1].sh_size); + } + // 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. diff --git a/src/p_lx_elf.h b/src/p_lx_elf.h index be87ff03..77781519 100644 --- a/src/p_lx_elf.h +++ b/src/p_lx_elf.h @@ -88,6 +88,10 @@ protected: unsigned char ei_data; unsigned char ei_osabi; char const *osabi_note; + + unsigned char const *buildid_data; + int o_elf_shnum; // num output Shdrs + static unsigned char o_shstrtab[]; }; class PackLinuxElf32 : public PackLinuxElf @@ -186,6 +190,12 @@ protected: cprElfHdrNetBSD elfout; + __packed_struct(cprElfShdr3) + Elf32_Shdr shdr[3]; + __packed_struct_end(); + + cprElfShdr3 shdrout; + struct Elf32_Nhdr { unsigned namesz; unsigned descsz; @@ -293,6 +303,12 @@ protected: cprElfHdr4 elfout; + __packed_struct(cprElfShdr3) + Elf64_Shdr shdr[3]; + __packed_struct_end(); + + cprElfShdr3 shdrout; + static void compileTimeAssertions() { COMPILE_TIME_ASSERT(sizeof(cprElfHdr1) == 64 + 1*56 + 12) COMPILE_TIME_ASSERT(sizeof(cprElfHdr2) == 64 + 2*56 + 12)