More work on "upx -d" de-compression

https://github.com/upx/upx/issues/629
https://github.com/upx/upx/issues/635
	modified:   p_elf_enum.h
	modified:   p_lx_elf.cpp
	modified:   p_lx_elf.h
	modified:   p_unix.cpp
This commit is contained in:
John Reiser
2022-12-19 13:49:43 -08:00
committed by Markus F.X.J. Oberhumer
parent 3649041195
commit f9f28ff176
4 changed files with 176 additions and 91 deletions
+1
View File
@@ -94,6 +94,7 @@
PT_PHDR = 6 /* Entry for header table itself */ PT_PHDR = 6 /* Entry for header table itself */
, PT_NUM = 8 /* Number of defined types in low range */ , PT_NUM = 8 /* Number of defined types in low range */
, PT_GNU_STACK = 0x6474e551 /* Indicates stack executability */ , PT_GNU_STACK = 0x6474e551 /* Indicates stack executability */
, PT_GNU_RELRO = 0x6474e552 /* Read-only after relocation */
}; };
enum { // p_flags enum { // p_flags
+151 -71
View File
@@ -51,6 +51,7 @@ typedef upx_uint64_t u64_t; // easier to type; more narrow
#define PT_NOTE64 Elf64_Phdr::PT_NOTE #define PT_NOTE64 Elf64_Phdr::PT_NOTE
#define PT_GNU_STACK32 Elf32_Phdr::PT_GNU_STACK #define PT_GNU_STACK32 Elf32_Phdr::PT_GNU_STACK
#define PT_GNU_STACK64 Elf64_Phdr::PT_GNU_STACK #define PT_GNU_STACK64 Elf64_Phdr::PT_GNU_STACK
#define PT_GNU_RELRO64 Elf64_Phdr::PT_GNU_RELRO
//static unsigned const EF_ARM_HASENTRY = 0x02; //static unsigned const EF_ARM_HASENTRY = 0x02;
static unsigned const EF_ARM_EABI_VER4 = 0x04000000; static unsigned const EF_ARM_EABI_VER4 = 0x04000000;
@@ -80,13 +81,11 @@ up4(unsigned x)
return ~3u & (3+ x); return ~3u & (3+ x);
} }
#if 0 //{ unused
static unsigned static unsigned
up8(unsigned x) up8(unsigned x)
{ {
return ~7u & (7+ x); return ~7u & (7+ x);
} }
#endif //}
static off_t static off_t
fpad4(OutputFile *fo) fpad4(OutputFile *fo)
@@ -596,9 +595,14 @@ off_t PackLinuxElf64::pack3(OutputFile *fo, Filter &ft)
continue; continue;
} }
if (PT_LOAD64 == type) { if (PT_LOAD64 == type) {
if ((xct_off - ioff) < len) { // Change length of compressed PT_LOAD. if ((sz_pack2 + lsize) < page_size // really small
set_te64(&phdr->p_filesz, sz_pack2 + lsize); && ioff <= page_size) { // first or second PT_LOAD
set_te64(&phdr->p_memsz, sz_pack2 + lsize); set_te64(&phdr->p_filesz, page_size);
set_te64(&phdr->p_memsz, page_size);
}
else if ((xct_off - ioff) < len) { // Change length of compressed PT_LOAD.
set_te64(&phdr->p_filesz, sz_pack2 + lsize - ioff);
set_te64(&phdr->p_memsz, sz_pack2 + lsize - ioff);
if (user_init_off < xct_off) { // MIPS puts PT_DYNAMIC here if (user_init_off < xct_off) { // MIPS puts PT_DYNAMIC here
// Allow for DT_INIT in a new [stolen] slot // Allow for DT_INIT in a new [stolen] slot
unsigned off2 = user_init_off - sizeof(word); unsigned off2 = user_init_off - sizeof(word);
@@ -3708,7 +3712,7 @@ void PackLinuxElf64ppc::pack1(OutputFile *fo, Filter &ft)
generateElfHdr(fo, stub_powerpc64_linux_elf_fold, getbrk(phdri, e_phnum) ); generateElfHdr(fo, stub_powerpc64_linux_elf_fold, getbrk(phdri, e_phnum) );
} }
void PackLinuxElf64::asl_pack2_Shdrs(OutputFile *fo) void PackLinuxElf64::asl_pack2_Shdrs(OutputFile *fo, unsigned pre_xct_top)
{ {
// In order to pacify the runtime linker on Android "O" ("Oreo"), // In order to pacify the runtime linker on Android "O" ("Oreo"),
// we will splice-in a 4KiB page that contains an "extra" copy // we will splice-in a 4KiB page that contains an "extra" copy
@@ -3718,6 +3722,8 @@ void PackLinuxElf64::asl_pack2_Shdrs(OutputFile *fo)
xct_va += asl_delta; xct_va += asl_delta;
//xct_off += asl_delta; // not until ::pack3() //xct_off += asl_delta; // not until ::pack3()
total_in = pre_xct_top;
// Relocate PT_DYNAMIC (in PT_LOAD with PF_W) // Relocate PT_DYNAMIC (in PT_LOAD with PF_W)
Elf64_Dyn *dyn = const_cast<Elf64_Dyn *>(dynseg); Elf64_Dyn *dyn = const_cast<Elf64_Dyn *>(dynseg);
for (; dyn->d_tag; ++dyn) { for (; dyn->d_tag; ++dyn) {
@@ -3794,9 +3800,9 @@ void PackLinuxElf64::asl_pack2_Shdrs(OutputFile *fo)
set_te64(&ehdr->e_entry, asl_delta + e_entry); set_te64(&ehdr->e_entry, asl_delta + e_entry);
} }
// Relocate Shdr; and Rela, Rel (below xct_off) // Relocate Shdr; and Rela, Rel (below xct_off)
set_te64(&ehdr->e_shoff, xct_off); set_te64(&ehdr->e_shoff, up8(pre_xct_top)); // Shdr alignment
memcpy(&lowmem[xct_off], shdri, e_shnum * sizeof(Elf64_Shdr)); memcpy(&lowmem[pre_xct_top], shdri, e_shnum * sizeof(Elf64_Shdr));
Elf64_Shdr *const shdro = (Elf64_Shdr *)&lowmem[xct_off]; Elf64_Shdr *const shdro = (Elf64_Shdr *)&lowmem[pre_xct_top];
Elf64_Shdr *shdr = shdro; Elf64_Shdr *shdr = shdro;
upx_uint64_t sz_shstrtab = get_te64(&sec_strndx->sh_size); upx_uint64_t sz_shstrtab = get_te64(&sec_strndx->sh_size);
for (unsigned j = 0; j < e_shnum; ++j, ++shdr) { for (unsigned j = 0; j < e_shnum; ++j, ++shdr) {
@@ -3917,15 +3923,17 @@ void PackLinuxElf64::asl_pack2_Shdrs(OutputFile *fo)
} }
// shstrndx will move // shstrndx will move
set_te64(&shdro[get_te16(&ehdri.e_shstrndx)].sh_offset, set_te64(&shdro[get_te16(&ehdri.e_shstrndx)].sh_offset,
len_notes + e_shnum * sizeof(Elf64_Shdr) + xct_off); len_notes + e_shnum * sizeof(Elf64_Shdr) + pre_xct_top);
// (Re-)write all changes below xct_off // ("Re-")write all changes below pre_xct_top
fo->seek(0, SEEK_SET); fo->seek(0, SEEK_SET);
fo->rewrite(lowmem, xct_off); fo->write(lowmem, pre_xct_top);
total_in = pre_xct_top;
// New copy of Shdr // New copy of Shdr
Elf64_Shdr blank; memset(&blank, 0, sizeof(blank)); Elf64_Shdr blank; memset(&blank, 0, sizeof(blank));
set_te64(&blank.sh_offset, xct_off); // hint for "upx -d" set_te64(&blank.sh_offset, xct_off); // hint for "upx -d"
fpad8(fo); // Shdr alignment
fo->write(&blank, sizeof(blank)); fo->write(&blank, sizeof(blank));
fo->write(&shdro[1], (-1+ e_shnum) * sizeof(Elf64_Shdr)); fo->write(&shdro[1], (-1+ e_shnum) * sizeof(Elf64_Shdr));
@@ -3938,8 +3946,10 @@ void PackLinuxElf64::asl_pack2_Shdrs(OutputFile *fo)
sz_elf_hdrs = fpad8(fo); sz_elf_hdrs = fpad8(fo);
total_out = sz_elf_hdrs; total_out = sz_elf_hdrs;
total_in = xct_off;
//xct_off += asl_delta; // wait until ::pack3 //xct_off += asl_delta; // wait until ::pack3
unsigned d = asl_delta + pre_xct_top - sz_elf_hdrs;
fo->seek(d, SEEK_CUR);
total_out += d;
} }
void PackLinuxElf64::pack1(OutputFile * /*fo*/, Filter &ft) void PackLinuxElf64::pack1(OutputFile * /*fo*/, Filter &ft)
@@ -4073,6 +4083,19 @@ void PackLinuxElf64::pack1(OutputFile * /*fo*/, Filter &ft)
++lg2_page; ++lg2_page;
} }
} }
if (PT_GNU_RELRO64 == type) {
// .p_align can be like 2M, which is a huge over-estimate.
// RELRO ends on a page boundary: usually close to actual page_size
unsigned offset = get_te64(&phdr->p_offset);
unsigned filesz = get_te64(&phdr->p_filesz);
if (!(0xfff & (filesz + offset))) { // a 4KiB boundary
unsigned b = 12;
while (!(~(~0u << b) & (filesz + offset))) {
++b;
}
lg2_page = umin(lg2_page, -1+ b);
}
}
if (PT_GNU_STACK64 == type) { if (PT_GNU_STACK64 == type) {
gnu_stack = phdr; gnu_stack = phdr;
} }
@@ -4319,8 +4342,9 @@ int PackLinuxElf64::pack2(OutputFile *fo, Filter &ft)
{ {
Extent x; Extent x;
unsigned k; unsigned k;
is_asl = (!!opt->o_unix.android_shlib) << 1; // bit 1 is_asl = (!!opt->o_unix.android_shlib) << 1; // bit 1; see is_shlib
unsigned const is_shlib = (0!=xct_off) | is_asl; unsigned const is_shlib = (0!=xct_off) | is_asl;
unsigned pre_xct_top = 0; // offset of end of PT_LOAD _before_ xct_off
// count passes, set ptload vars // count passes, set ptload vars
uip->ui_total_passes = 0; uip->ui_total_passes = 0;
@@ -4331,11 +4355,23 @@ int PackLinuxElf64::pack2(OutputFile *fo, Filter &ft)
} }
else { else {
unsigned p_flags = get_te32(&phdri[k].p_flags); unsigned p_flags = get_te32(&phdri[k].p_flags);
unsigned p_offset = get_te64(&phdri[k].p_offset);
unsigned p_filesz = get_te64(&phdri[k].p_filesz);
if ((xct_off - p_offset) < p_filesz) { // PT_LOAD covers xct_off
if (!pre_xct_top && xct_off != p_offset) {
pre_xct_top = xct_off;
}
}
else if (p_offset < xct_off) { // candidate for pre_xct_top
unsigned top = p_filesz + p_offset;
if (pre_xct_top < top) {
pre_xct_top = top;
}
}
if (Elf64_Phdr::PF_W & p_flags) { if (Elf64_Phdr::PF_W & p_flags) {
// rtld might write, so cannot compress // rtld might write, so cannot compress
} }
else { else {
upx_uint64_t p_filesz = get_te64(&phdri[k].p_filesz);
// First PT_LOAD (partial) only if has instructions // First PT_LOAD (partial) only if has instructions
if (k || xct_off < p_filesz) { if (k || xct_off < p_filesz) {
uip->ui_total_passes++; uip->ui_total_passes++;
@@ -4363,12 +4399,7 @@ int PackLinuxElf64::pack2(OutputFile *fo, Filter &ft)
memcpy(lowmem, file_image, xct_off); // android omits Shdr here memcpy(lowmem, file_image, xct_off); // android omits Shdr here
if (is_asl) { // Android shared library if (is_asl) { // Android shared library
sz_elf_hdrs = xct_off; asl_pack2_Shdrs(fo, pre_xct_top);
fo->write(lowmem, xct_off); // < SHF_EXECINSTR (typ: in .plt or .init)
total_in = xct_off;
total_out = xct_off;
asl_pack2_Shdrs(fo);
} }
} }
unsigned nk_f = 0; upx_uint64_t xsz_f = 0; unsigned nk_f = 0; upx_uint64_t xsz_f = 0;
@@ -4390,52 +4421,59 @@ int PackLinuxElf64::pack2(OutputFile *fo, Filter &ft)
x.offset = get_te64(&phdri[k].p_offset); x.offset = get_te64(&phdri[k].p_offset);
x.size = get_te64(&phdri[k].p_filesz); x.size = get_te64(&phdri[k].p_filesz);
if (is_shlib) { if (is_shlib) {
if (x.offset <= xct_off) { // first PT_LOAD if (x.offset <= xct_off) { // perhaps compressible: PT_LOAD[0] or PT_LOAD[1]
unsigned const len = umin(x.size, xct_off - x.offset); // Bytes below xct_off belong to rtld, so must be literal.
if (len && !is_asl) { // asl_pack2_Shdrs aleady handled // Note that asl_pack2_Shdrs() [called above when is_asl]
// copies up to xct_off, then adds extra info
if (x.offset < xct_off) { // still copying rtld data.
if (!is_asl) { // asl_pack2_Shdrs (above) already handled --android-shlib
x.size = umin(x.size, xct_off - x.offset);
fi->seek(x.offset, SEEK_SET); fi->seek(x.offset, SEEK_SET);
fi->readx(ibuf, x.size); fi->readx(ibuf, x.size);
total_in += len; total_in += x.size;
fo->seek(x.offset, SEEK_SET); fo->seek(x.offset, SEEK_SET);
fo->write(ibuf, len); fo->write(ibuf, x.size);
total_out += len; total_out += x.size;
} }
if (len != x.size) { }
if (1) { if (pre_xct_top <= total_out && hdr_u_len) { // first time
linfo.l_checksum = 0; linfo.l_checksum = 0; // preliminary
linfo.l_magic = UPX_MAGIC_LE32; linfo.l_magic = UPX_MAGIC_LE32;
set_le16(&linfo.l_lsize, lsize); // preliminary (0) set_le16(&linfo.l_lsize, lsize); // preliminary (0)
linfo.l_version = (unsigned char)ph.version; linfo.l_version = (unsigned char)ph.version;
linfo.l_format = (unsigned char)ph.format; linfo.l_format = (unsigned char)ph.format;
linfo_off = fo->tell(); linfo_off = total_out;
fo->write(&linfo, sizeof(linfo)); fo->write(&linfo, sizeof(linfo));
total_out += sizeof(linfo); total_out += sizeof(linfo);
}
overlay_offset = total_out;
p_info hbuf; p_info hbuf;
set_te32(&hbuf.p_progid, 0); set_te32(&hbuf.p_progid, 0);
set_te32(&hbuf.p_filesize, file_size); set_te32(&hbuf.p_filesize, file_size);
set_te32(&hbuf.p_blocksize, blocksize); set_te32(&hbuf.p_blocksize, blocksize);
overlay_offset = total_out;
fo->write(&hbuf, sizeof(hbuf)); fo->write(&hbuf, sizeof(hbuf));
total_out += sizeof(p_info); total_out += sizeof(hbuf);
x.offset = 0; x.offset = 0; // compress Ehdr and Phdrs
x.size = sz_elf_hdrs;
if (is_asl) {
x.size = hdr_u_len; x.size = hdr_u_len;
}
unsigned in_size = x.size; unsigned in_size = x.size;
packExtent(x, nullptr, fo, 0, 0, true); packExtent(x, nullptr, fo, 0, 0, true);
total_in -= in_size; total_in -= in_size;
// The rest of first PT_LOAD (above xct_off) hdr_u_len = 0; // no longer the first time
}
// The remainder above xct_offset in first compressible PT_LOAD
unsigned p_offset = get_te64(&phdri[k].p_offset);
unsigned p_filesz = get_te64(&phdri[k].p_filesz);
if ( p_filesz >= (xct_off - p_offset)) {
x.size = p_filesz - (xct_off - p_offset);
x.offset = xct_off; x.offset = xct_off;
x.size = get_te64(&phdri[k].p_filesz) - len;
packExtent(x, &ft, fo, 0, 0, true); packExtent(x, &ft, fo, 0, 0, true);
} }
} }
else { else { // defnitely compressible unless writeable
if (!(Elf64_Phdr::PF_W & get_te64(&phdri[k].p_flags))) { if (!(Elf64_Phdr::PF_W & get_te64(&phdri[k].p_flags))) {
// Read-only PT_LOAD, assume not written by relocations. // Read-only PT_LOAD, assume not written by relocations.
// Also assume not the source for R_*_COPY relocation, // Also assume not the source for R_*_COPY relocation,
@@ -4482,14 +4520,15 @@ int PackLinuxElf64::pack2(OutputFile *fo, Filter &ft)
else { else {
total_in += x.size; total_in += x.size;
} }
hdr_u_len = 0;
} }
else { else {
total_in += x.size; total_in += x.size;
} }
hdr_u_len = 0;
++nx; ++nx;
} }
sz_pack2a = fpad4(fo); // MATCH01 sz_pack2a = fpad4(fo); // MATCH01
total_out = up4(total_out);
// Accounting only; ::pack3 will do the compression and output // Accounting only; ::pack3 will do the compression and output
for (k = 0; k < e_phnum; ++k) { for (k = 0; k < e_phnum; ++k) {
@@ -4725,7 +4764,7 @@ void PackLinuxElf64::pack4(OutputFile *fo, Filter &ft)
//fo->seek(xct_off, SEEK_SET); // FIXME //fo->seek(xct_off, SEEK_SET); // FIXME
//fo->rewrite(&linfo, sizeof(linfo)); //fo->rewrite(&linfo, sizeof(linfo));
fo->seek(linfo_off, SEEK_SET); fo->seek(linfo_off, SEEK_SET);
fo->rewrite(&linfo, sizeof(linfo)); // duplicate? fo->rewrite(&linfo, sizeof(linfo)); // new info: l_checksum, l_size
} }
else { else {
if (PT_NOTE64 == get_te64(&elfout.phdr[C_NOTE].p_type)) { if (PT_NOTE64 == get_te64(&elfout.phdr[C_NOTE].p_type)) {
@@ -4869,7 +4908,7 @@ PackLinuxElf64::unRela64(
// 11. UPX runtime de-compressing loader // 11. UPX runtime de-compressing loader
// 12. compressed gaps between PT_LOADs (and EOF) above xct_off // 12. compressed gaps between PT_LOADs (and EOF) above xct_off
// 13. 32-byte pack header // 13. 32-byte pack header
// 14. 4-byte overlay offset // 14. 4-byte overlay_offset
void PackLinuxElf64::un_shlib_1( void PackLinuxElf64::un_shlib_1(
OutputFile *const fo, OutputFile *const fo,
@@ -4881,23 +4920,26 @@ void PackLinuxElf64::un_shlib_1(
unsigned const szb_info unsigned const szb_info
) )
{ {
// xct_off [input side] was set by ::unpack when is_shlib
// yct_off [output side]
unsigned yct_off = xct_off;
// Below xct_off is not compressed (for benefit of rtld.) // Below xct_off is not compressed (for benefit of rtld.)
fi->seek(0, SEEK_SET); fi->seek(0, SEEK_SET);
unsigned const limit_dynhdr = get_te64(&dynhdr->p_offset) + get_te64(&dynhdr->p_filesz); unsigned const limit_dynhdr = get_te64(&dynhdr->p_offset) + get_te64(&dynhdr->p_filesz);
fi->readx(ibuf, limit_dynhdr); fi->readx(ibuf, limit_dynhdr);
//overlay_offset -= sizeof(linfo); //overlay_offset -= sizeof(linfo);
loader_offset = 0; loader_offset = 0;
xct_off = overlay_offset;
e_shoff = get_te64(&ehdri.e_shoff); e_shoff = get_te64(&ehdri.e_shoff);
if (e_shoff && e_shnum if (e_shoff && e_shnum
&& (e_shoff + sizeof(Elf64_Shdr) * e_shnum) <= limit_dynhdr) { // --android-shlib && (e_shoff + sizeof(Elf64_Shdr) * e_shnum) <= limit_dynhdr) { // --android-shlib
is_asl = 1;
ibuf.subref("bad .e_shoff %#lx for %#lx", e_shoff, sizeof(Elf64_Shdr) * e_shnum); ibuf.subref("bad .e_shoff %#lx for %#lx", e_shoff, sizeof(Elf64_Shdr) * e_shnum);
shdri = (Elf64_Shdr /*const*/ *)ibuf.subref( shdri = (Elf64_Shdr /*const*/ *)ibuf.subref(
"bad Shdr table", e_shoff, sizeof(Elf64_Shdr)*e_shnum); "bad Shdr table", e_shoff, sizeof(Elf64_Shdr)*e_shnum);
upx_uint64_t xct_off2 = get_te64(&shdri->sh_offset); yct_off = get_te64(&shdri->sh_offset); // for the output file (de-compresssed)
if (e_shoff == xct_off2) { xct_off = asl_delta + yct_off; // for the input file (compressed)
xct_off = e_shoff;
}
// un-Relocate dynsym (DT_SYMTAB) which is below xct_off // un-Relocate dynsym (DT_SYMTAB) which is below xct_off
dynstr = (char const *)elf_find_dynamic(Elf64_Dyn::DT_STRTAB); dynstr = (char const *)elf_find_dynamic(Elf64_Dyn::DT_STRTAB);
sec_dynsym = elf_find_section_type(Elf64_Shdr::SHT_DYNSYM); sec_dynsym = elf_find_section_type(Elf64_Shdr::SHT_DYNSYM);
@@ -4965,11 +5007,13 @@ void PackLinuxElf64::un_shlib_1(
InputFile u_fi; InputFile u_fi;
// Recover original Elf headers from current output file // Recover original Elf headers from current output file
u_fi.open(fo->getName(), 0); u_fi.open(fo->getName(), 0);
u_fi.readx((void *)o_elfhdrs,o_elfhdrs.getSize()); u_fi.readx((void *)o_elfhdrs, o_elfhdrs.getSize());
u_fi.close(); u_fi.close();
// Re-generate unmodified rtld data below xct_off // Re-generate unmodified rtld data below xct_off
fo->write(&ibuf[ph.u_len], xct_off - ph.u_len); unsigned d = yct_off - ph.u_len;
fo->write(&ibuf[ph.u_len], d);
total_out += d;
} }
Elf64_Phdr const *o_phdr = (Elf64_Phdr const *)(1+ (Elf64_Ehdr const *)(void const *)o_elfhdrs); Elf64_Phdr const *o_phdr = (Elf64_Phdr const *)(1+ (Elf64_Ehdr const *)(void const *)o_elfhdrs);
@@ -4980,13 +5024,15 @@ void PackLinuxElf64::un_shlib_1(
if (PT_LOAD64 != type || Elf64_Phdr::PF_W & flags) { if (PT_LOAD64 != type || Elf64_Phdr::PF_W & flags) {
continue; continue;
} }
unsigned vaddr = get_te64(&o_phdr->p_vaddr); unsigned p_offset = get_te64(&o_phdr->p_offset);
if (xct_off <= vaddr) { // not first PT_LOAD must position its output unsigned p_filesz = get_te64(&o_phdr->p_filesz);
if (fo) { if ((p_filesz + p_offset) <= yct_off) {
unsigned o_offset = get_te64(&o_phdr->p_offset); continue; // already entirely re-generated
fo->seek(o_offset, SEEK_SET);
} }
if (p_offset < yct_off) {
p_offset = yct_off; // not at beginning of PT_LOAD
} }
fo->seek(p_offset, SEEK_SET);
// Peek at b_info to find sizes // Peek at b_info to find sizes
fi->readx(&hdr.b, sizeof(hdr.b)); fi->readx(&hdr.b, sizeof(hdr.b));
fi->seek(-(off_t)sizeof(struct b_info), SEEK_CUR); fi->seek(-(off_t)sizeof(struct b_info), SEEK_CUR);
@@ -5084,18 +5130,18 @@ void PackLinuxElf64::un_DT_INIT(
// So changing the value will get ignored. Do it anyway. // So changing the value will get ignored. Do it anyway.
// FIXME: we must fix the Rela ? // FIXME: we must fix the Rela ?
Elf64_Phdr const *phdr = phdro; Elf64_Phdr const *phdr = phdro;
if (1) for (unsigned j = 0; j < e_phnum; ++j, ++phdr) { for (unsigned j = 0; j < e_phnum; ++j, ++phdr) {
upx_uint64_t vaddr = get_te64(&phdr->p_vaddr); upx_uint64_t vaddr = get_te64(&phdr->p_vaddr);
upx_uint64_t filesz = get_te64(&phdr->p_filesz); upx_uint64_t filesz = get_te64(&phdr->p_filesz);
if ((val - vaddr) < filesz) { unsigned q = val - (is_asl ? asl_delta : 0) - vaddr;
if (q < filesz) {
upx_uint64_t offset = get_te64(&phdr->p_offset); upx_uint64_t offset = get_te64(&phdr->p_offset);
// Rela overwrites the target; assumed default is 0 // Rela overwrites the target; assumed default is 0
upx_uint64_t oldval = 0; //set_te64(&oldval, old_dtinit); upx_uint64_t oldval = 0; //set_te64(&oldval, old_dtinit);
// Counter-act unRel64 if asl_delta // Counter-act unRel64 if asl_delta
// FIXME? the in-memory copy? // FIXME? the in-memory copy?
if (fo) { if (fo) {
unsigned q = (val - vaddr) + offset - (is_asl ? asl_delta : 0); fo->seek(q + offset, SEEK_SET);
fo->seek(q, SEEK_SET);
fo->write(&oldval, sizeof(oldval)); fo->write(&oldval, sizeof(oldval));
} }
break; break;
@@ -5208,6 +5254,7 @@ void PackLinuxElf64::unpack(OutputFile *fo)
|| ph.u_len > orig_file_size) || ph.u_len > orig_file_size)
throwCantUnpack("b_info corrupted"); throwCantUnpack("b_info corrupted");
ph.filter_cto = bhdr.b_cto8; ph.filter_cto = bhdr.b_cto8;
prev_method = bhdr.b_method; // FIXME if multiple de-compressors
MemBuffer u(ph.u_len); MemBuffer u(ph.u_len);
Elf64_Ehdr *const ehdr = (Elf64_Ehdr *)&u[0]; Elf64_Ehdr *const ehdr = (Elf64_Ehdr *)&u[0];
@@ -5221,19 +5268,16 @@ void PackLinuxElf64::unpack(OutputFile *fo)
loader_offset = 0; loader_offset = 0;
MemBuffer o_elfhdrs; MemBuffer o_elfhdrs;
Elf64_Phdr const *const dynhdr = elf_find_ptype(Elf64_Phdr::PT_DYNAMIC, phdri, c_phnum); Elf64_Phdr const *const dynhdr = elf_find_ptype(Elf64_Phdr::PT_DYNAMIC, phdri, c_phnum);
if (dynhdr) { // dynseg was set by PackLinuxElf64help1
upx_uint64_t dyn_offset = get_te64(&dynhdr->p_offset); if (dynhdr && !(Elf64_Dyn::DF_1_PIE & elf_unsigned_dynamic(Elf64_Dyn::DT_FLAGS_1))) {
upx_uint64_t dyn_filesz = get_te64(&dynhdr->p_filesz);
dynseg = (Elf64_Dyn const *)ibuf.subref("bad DYNAMIC", dyn_offset, dyn_filesz);
// Packed shlib? (ET_DYN without -fPIE) // Packed shlib? (ET_DYN without -fPIE)
if (!(Elf64_Dyn::DF_1_PIE & elf_unsigned_dynamic(Elf64_Dyn::DT_FLAGS_1))) {
is_shlib = 1; is_shlib = 1;
xct_off = overlay_offset - sizeof(l_info);
u_phnum = get_te16(&ehdri.e_phnum); u_phnum = get_te16(&ehdri.e_phnum);
o_elfhdrs.alloc(sz_elf_hdrs); o_elfhdrs.alloc(sz_elf_hdrs);
un_shlib_1(fo, o_elfhdrs, c_adler, u_adler, dynhdr, orig_file_size, szb_info); un_shlib_1(fo, o_elfhdrs, c_adler, u_adler, dynhdr, orig_file_size, szb_info);
*ehdr = ehdri; *ehdr = ehdri;
} }
}
else { // main executable else { // main executable
// Uncompress Ehdr and Phdrs: info for control of unpacking // Uncompress Ehdr and Phdrs: info for control of unpacking
if (ibuf.getSize() < ph.c_len) if (ibuf.getSize() < ph.c_len)
@@ -5285,14 +5329,24 @@ void PackLinuxElf64::unpack(OutputFile *fo)
} }
} }
upx_uint64_t const e_entry = get_te64(&ehdri.e_entry);
unsigned off_entry = 0;
phdr = phdri; phdr = phdri;
load_va = 0; load_va = 0;
for (unsigned j=0; j < c_phnum; ++j) { for (unsigned j=0; j < c_phnum; ++j, ++phdr) {
if (PT_LOAD64==get_te32(&phdr->p_type)) { if (PT_LOAD64==get_te32(&phdr->p_type)) {
load_va = get_te64(&phdr->p_vaddr); upx_uint64_t offset = get_te64(&phdr->p_offset);
upx_uint64_t vaddr = get_te64(&phdr->p_vaddr);
upx_uint64_t filesz = get_te64(&phdr->p_filesz);
if (!load_va) {
load_va = vaddr;
}
if ((e_entry - vaddr) < filesz) {
off_entry = (e_entry - vaddr) + offset;
break; break;
} }
} }
}
unsigned d_info[6]; unsigned d_info[6];
unsigned sz_d_info = sizeof(d_info); unsigned sz_d_info = sizeof(d_info);
if (!is_shlib) { if (!is_shlib) {
@@ -5309,7 +5363,7 @@ void PackLinuxElf64::unpack(OutputFile *fo)
case Elf64_Ehdr::EM_X86_64: sz_d_info = 2 * sizeof(unsigned); break; case Elf64_Ehdr::EM_X86_64: sz_d_info = 2 * sizeof(unsigned); break;
} }
} }
loader_offset = get_te64(&ehdri.e_entry) - load_va - sz_d_info; loader_offset = off_entry - sz_d_info;
} }
if (0x1000==get_te64(&phdri[0].p_filesz) // detect C_BASE style if (0x1000==get_te64(&phdri[0].p_filesz) // detect C_BASE style
@@ -5319,8 +5373,7 @@ void PackLinuxElf64::unpack(OutputFile *fo)
fi->seek(up4(get_te64(&phdr[1].p_memsz)), SEEK_SET); // past the loader fi->seek(up4(get_te64(&phdr[1].p_memsz)), SEEK_SET); // past the loader
} }
else if (is_shlib else if (is_shlib
|| ((unsigned)(get_te64(&ehdri.e_entry) - load_va) + up4(lsize) + || (off_entry + up4(lsize) + ph.getPackHeaderSize() + sizeof(overlay_offset))
ph.getPackHeaderSize() + sizeof(overlay_offset))
< up4(file_size)) { < up4(file_size)) {
// Loader is not at end; skip past it. // Loader is not at end; skip past it.
if (loader_offset) { if (loader_offset) {
@@ -5352,6 +5405,33 @@ void PackLinuxElf64::unpack(OutputFile *fo)
get_te64(&phdr[j].p_filesz); get_te64(&phdr[j].p_filesz);
if (fo) if (fo)
fo->seek(where, SEEK_SET); fo->seek(where, SEEK_SET);
{ // Recover from some piracy [also serves as error tolerance :-) ]
b_info b_peek;
fi->readx(&b_peek, sizeof(b_peek));
upx_off_t pos = fi->seek(-sizeof(b_peek), SEEK_CUR);
if (b_peek.sz_unc != size
|| b_peek.b_method != prev_method) {
opt->info_mode++;
infoWarning("bad b_info at %#zx", (size_t)pos);
unsigned const N_PEEK(64), H_PEEK(N_PEEK >> 1);
unsigned char peek_arr[N_PEEK];
fi->seek(pos - H_PEEK, SEEK_SET);
fi->readx(peek_arr, sizeof(peek_arr));
fi->seek(pos + H_PEEK - sizeof(peek_arr), SEEK_SET);
int boff = find_le32(peek_arr, sizeof(peek_arr), size);
unsigned word3 = *(int *)&peek_arr[2*sizeof(int) + boff];
if (0 <= boff // found
&& (0 == word3 // uncompressible literal
|| prev_method == word3) // same method, no filter
) {
pos -= H_PEEK;
pos += boff;
infoWarning("... recovery at %#zx", (size_t)pos);
fi->seek(pos, SEEK_SET);
}
opt->info_mode--;
}
}
unpackExtent(size, fo, unpackExtent(size, fo,
c_adler, u_adler, false, szb_info, c_adler, u_adler, false, szb_info,
is_shlib && ((phdr[j].p_offset != hi_offset))); is_shlib && ((phdr[j].p_offset != hi_offset)));
+2 -1
View File
@@ -101,6 +101,7 @@ protected:
unsigned char ei_class; unsigned char ei_class;
unsigned char ei_data; unsigned char ei_data;
unsigned char ei_osabi; unsigned char ei_osabi;
unsigned char prev_method;
char const *osabi_note; char const *osabi_note;
unsigned upx_dt_init; // DT_INIT, DT_PREINIT_ARRAY, DT_INIT_ARRAY unsigned upx_dt_init; // DT_INIT, DT_PREINIT_ARRAY, DT_INIT_ARRAY
static unsigned const DT_NUM = 34; // elf.h static unsigned const DT_NUM = 34; // elf.h
@@ -272,7 +273,7 @@ protected:
virtual int canUnpack() override; // bool, except -1: format known, but not packed virtual int canUnpack() override; // bool, except -1: format known, but not packed
virtual void pack1(OutputFile *, Filter &) override; // generate executable header virtual void pack1(OutputFile *, Filter &) override; // generate executable header
virtual void asl_pack2_Shdrs(OutputFile *); // AndroidSharedLibrary processes Shdrs virtual void asl_pack2_Shdrs(OutputFile *, unsigned pre_xct_top); // AndroidSharedLibrary processes Shdrs
virtual int pack2(OutputFile *, Filter &) override; // append compressed data virtual int pack2(OutputFile *, Filter &) override; // append compressed data
virtual off_t pack3(OutputFile *, Filter &) override; // append loader virtual off_t pack3(OutputFile *, Filter &) override; // append loader
virtual void pack4(OutputFile *, Filter &) override; // append pack header virtual void pack4(OutputFile *, Filter &) override; // append pack header
+4 -1
View File
@@ -410,6 +410,7 @@ void PackUnix::packExtent(
tmp.b_method = (unsigned char) forced_method(ph.method); tmp.b_method = (unsigned char) forced_method(ph.method);
tmp.b_extra = b_extra; tmp.b_extra = b_extra;
fo->write(&tmp, sizeof(tmp)); fo->write(&tmp, sizeof(tmp));
total_out += sizeof(tmp);
b_len += sizeof(b_info); b_len += sizeof(b_info);
fo->write(hdr_obuf, hdr_c_len); fo->write(hdr_obuf, hdr_c_len);
total_out += hdr_c_len; total_out += hdr_c_len;
@@ -428,6 +429,7 @@ void PackUnix::packExtent(
} }
tmp.b_extra = b_extra; tmp.b_extra = b_extra;
fo->write(&tmp, sizeof(tmp)); fo->write(&tmp, sizeof(tmp));
total_out += sizeof(tmp);
b_len += sizeof(b_info); b_len += sizeof(b_info);
if (ft) { if (ft) {
@@ -436,15 +438,16 @@ void PackUnix::packExtent(
// write compressed data // write compressed data
if (ph.c_len < ph.u_len) { if (ph.c_len < ph.u_len) {
fo->write(obuf, ph.c_len); fo->write(obuf, ph.c_len);
total_out += ph.c_len;
// Checks ph.u_adler after decompression, after unfiltering // Checks ph.u_adler after decompression, after unfiltering
verifyOverlappingDecompression(ft); verifyOverlappingDecompression(ft);
} }
else { else {
fo->write(ibuf, ph.u_len); fo->write(ibuf, ph.u_len);
total_out += ph.u_len;
} }
total_in += ph.u_len; total_in += ph.u_len;
total_out += ph.c_len;
} }
} }