Major fix 64-bit un_shlib_1() and unpack(), but not --android-shlib

Also canUnpack() is really a 'bool' which sets overlay_offset
when 'true'; confusing!
        https://github.com/upx/upx/issues/599 (partial)
	modified:   p_lx_elf.cpp
	modified:   p_lx_elf.h
	modified:   p_unix.h
This commit is contained in:
John Reiser 2022-10-14 15:53:55 -07:00 committed by Markus F.X.J. Oberhumer
parent f57393c0e8
commit 82ed25bb44
3 changed files with 148 additions and 135 deletions

View File

@ -1978,7 +1978,7 @@ bool PackLinuxElf32::calls_crt1(Elf32_Rel const *rel, int sz)
#include "p_elf_enum.h"
#undef WANT_REL_ENUM
int PackLinuxElf32::canUnpack()
int PackLinuxElf32::canUnpack() // really 'bool'
{
if (checkEhdr(&ehdri)) {
return false;
@ -2409,7 +2409,7 @@ proceed: ;
return true;
}
int PackLinuxElf64::canUnpack()
int PackLinuxElf64::canUnpack() // really 'bool'
{
if (checkEhdr(&ehdri)) {
return false;
@ -2428,7 +2428,7 @@ int PackLinuxElf64::canUnpack()
fi->seek(filesz+offset, SEEK_SET);
MemBuffer buf(32 + sizeof(overlay_offset));
fi->readx(buf, buf.getSize());
unsigned x = PackUnix::find_overlay_offset(buf);
bool x = PackUnix::find_overlay_offset(buf);
if (x) {
return x;
}
@ -3406,7 +3406,11 @@ void PackLinuxElf32::pack1(OutputFile *fo, Filter & /*ft*/)
unsigned sh_size = get_te32(&shdr->sh_size);
unsigned sh_offset = get_te32(&shdr->sh_offset);
unsigned sh_entsize = get_te32(&shdr->sh_entsize);
if (xct_off <= sh_offset) {
unsigned sh_flags = get_te32(&shdr->sh_flags);
if (xct_off <= sh_offset
// Omit .comment (0==.sh_addr; !SHF_ALLOC) etc.
&& (shdr->sh_addr || Elf32_Shdr::SHF_ALLOC & sh_flags)
) {
//set_te32(&shdr->sh_offset, asl_delta + sh_offset); // FIXME ??
upx_uint32_t addr = get_te32(&shdr->sh_addr);
set_te32(&shdr->sh_addr, asl_delta + addr);
@ -4891,52 +4895,56 @@ void PackLinuxElf64::un_shlib_1(
unpackExtent(ph.u_len, fo,
c_adler, u_adler, false, szb_info);
// Recover original Elf headers from current output file
InputFile u_fi;
// FIXME: what if no output file? test mode ("-t") or list mode ("-l")
if (fo) {
InputFile u_fi;
// Recover original Elf headers from current output file
u_fi.open(fo->getName(), 0);
u_fi.readx((void *)o_elfhdrs,o_elfhdrs.getSize());
u_fi.close();
}
Elf64_Phdr const *t_phdr = (Elf64_Phdr const *)(1+ (Elf64_Ehdr const *)(void const *)o_elfhdrs);
unsigned t_flags = get_te32(&t_phdr->p_flags);
// New style: 1st PT_LOAD has no eXecutable code
if (!(Elf64_Phdr::PF_X & t_flags)) {
if (fo) {
// Re-generate unmodified rtld data below xct_off
fo->write(&ibuf[ph.u_len], xct_off - ph.u_len);
}
// Now handle compressed PT_LOADs.
// Phdr are parallel from input to output, except perhaps PT_NULL.
Elf64_Phdr const *i_phdr = phdri;
Elf64_Phdr const *o_phdr = (Elf64_Phdr const *)(1+ (Elf64_Ehdr const *)(void const *)o_elfhdrs);
int once = 0;
for (unsigned k = 0; k < e_phnum; ++k, ++i_phdr, ++o_phdr) {
unsigned type = get_te32(&o_phdr->p_type); // output side avoids PT_NULL
// Handle compressed PT_LOADs (must not have PF_W)
for (unsigned j = 0; j < e_phnum; ++j, ++o_phdr) {
unsigned type = get_te32(&o_phdr->p_type);
unsigned flags = get_te32(&o_phdr->p_flags);
if (PT_LOAD64 != type || Elf64_Phdr::PF_W & flags) {
continue;
}
unsigned vaddr = get_te64(&o_phdr->p_vaddr);
unsigned filesz = get_te64(&o_phdr->p_filesz);
unsigned o_offset = get_te64(&o_phdr->p_offset);
unsigned i_offset = get_te64(&i_phdr->p_offset);
if (xct_off <= vaddr) { // beyond rtld control info
if (PT_LOAD64==type
&& xct_off < (filesz + vaddr)) {
if (!(Elf64_Phdr::PF_W & flags)) { // Read-only, so was compressed
fi->readx(&hdr.b, sizeof(hdr.b));
fi->seek(-(off_t)sizeof(struct b_info), SEEK_CUR);
if (xct_off <= vaddr) { // not first PT_LOAD must position its output
if (fo) {
unsigned o_offset = get_te64(&o_phdr->p_offset);
fo->seek(o_offset, SEEK_SET);
}
}
// Peek at b_info to find sizes
fi->readx(&hdr.b, sizeof(hdr.b));
fi->seek(-(off_t)sizeof(struct b_info), SEEK_CUR);
ph.c_len = get_te32(&hdr.b.sz_cpr);
ph.u_len = get_te32(&hdr.b.sz_unc);
unpackExtent(ph.u_len, fo, c_adler, u_adler, false, szb_info);
}
else { // Writeable, so might written by rtld, so not compressed.
if (!once++) {
funpad4(fi);
loader_offset = fi->tell();
// Handle PT_LOAD with PF_W: writeable, so not compressed. "Slide"
o_phdr = (Elf64_Phdr const *)(1+ (Elf64_Ehdr const *)(void const *)o_elfhdrs);
Elf64_Phdr const *i_phdr = phdri;
for (unsigned j = 0; j < e_phnum; ++j, ++o_phdr, ++i_phdr) {
unsigned type = get_te32(&o_phdr->p_type);
unsigned flags = get_te32(&o_phdr->p_flags);
if (PT_LOAD64 != type || !(Elf64_Phdr::PF_W & flags)) {
continue;
}
unsigned filesz = get_te64(&o_phdr->p_filesz);
unsigned o_offset = get_te64(&o_phdr->p_offset);
unsigned i_offset = get_te64(&i_phdr->p_offset);
fi->seek(i_offset, SEEK_SET);
fi->readx(ibuf, filesz);
total_in += filesz;
@ -4946,16 +4954,8 @@ void PackLinuxElf64::un_shlib_1(
}
total_out = filesz + o_offset; // high-water mark
}
}
}
}
}
else { // Old style
if (fo) {
Elf64_Phdr const *o_phdr = (Elf64_Phdr const *)(1+ (Elf64_Ehdr const *)(void const *)o_elfhdrs);
fi->seek(o_phdr->p_offset, SEEK_CUR); // DEBUG no-op
}
}
// Gaps between PT_LOAD will be handled by ::unpack()
// position fi at loader offset
fi->seek(loader_offset, SEEK_SET);
@ -5012,14 +5012,27 @@ void PackLinuxElf64::un_DT_INIT(
}
else if (Elf64_Dyn::DT_INIT_ARRAY == tag
|| Elf64_Dyn::DT_PREINIT_ARRAY == tag) {
if (val < load_va || (long unsigned)file_size < (long unsigned)val) {
char msg[50]; snprintf(msg, sizeof(msg),
"Bad Dynamic tag %#lx %#lx",
(long unsigned)tag, (long unsigned)val);
throwCantUnpack(msg);
// The slot must have a R_*_RELATIVE relocation (is_shlib,
// after all), but ElfXX_Rela ignores the initial contents!
// So changing the value will get ignored. Do it anyway.
// FIXME: we must fix the Rela ?
Elf64_Phdr const *phdr = phdro;
for (unsigned j = 0; j < e_phnum; ++j, ++phdr) {
upx_uint64_t vaddr = get_te64(&phdr->p_vaddr);
upx_uint64_t filesz = get_te64(&phdr->p_filesz);
if ((val - vaddr) < filesz) {
upx_uint64_t offset = get_te64(&phdr->p_offset);
upx_uint64_t oldval;
// Counter-act unRel64 if asl_delta
set_te64(&oldval, old_dtinit + (is_asl ? asl_delta : 0));
// FIXME? the in-memory copy?
if (fo) {
fo->seek((val - vaddr) + offset, SEEK_SET);
fo->write(&oldval, sizeof(oldval));
}
break;
}
}
set_te64(&ibuf[val - load_va], old_dtinit
+ (is_asl ? asl_delta : 0)); // counter-act unRel64
}
}
}
@ -5286,7 +5299,7 @@ PackLinuxElf32x86::~PackLinuxElf32x86()
{
}
int PackLinuxElf32x86::canUnpack()
int PackLinuxElf32x86::canUnpack() // really 'bool'
{
if (super::canUnpack()) {
return true;

View File

@ -42,7 +42,7 @@ public:
/*virtual void buildLoader(const Filter *);*/
virtual int getVersion() const override { return 14; } // upx-3.96 cannot upack, for instance
virtual bool canUnpackVersion(int version) const override { return (version >= 11); }
virtual int canUnpack() override { return super::canUnpack(); }
virtual int canUnpack() override { return super::canUnpack(); } // really 'bool'
protected:
virtual const int *getCompressionMethods(int method, int level) const override;
@ -123,7 +123,7 @@ protected:
virtual void PackLinuxElf32help1(InputFile *f);
virtual int checkEhdr(Elf32_Ehdr const *ehdr) const;
virtual bool canPack() override;
virtual int canUnpack() override;
virtual int canUnpack() override; // really 'bool'
// These ARM routines are essentially common to big/little endian,
// but the class hierarchy splits after this class.
@ -266,7 +266,7 @@ protected:
virtual void PackLinuxElf64help1(InputFile *f);
virtual int checkEhdr(Elf64_Ehdr const *ehdr) const;
virtual bool canPack() override;
virtual int canUnpack() override;
virtual int canUnpack() override; // really 'bool'
virtual void pack1(OutputFile *, Filter &) override; // generate executable header
virtual int pack2(OutputFile *, Filter &) override; // append compressed data
@ -558,7 +558,7 @@ public:
virtual const char *getName() const override { return "linux/i386"; }
virtual const char *getFullName(const options_t *) const override { return "i386-linux.elf"; }
virtual const int *getFilters() const override;
virtual int canUnpack() override;
virtual int canUnpack() override; // reallly 'bool'
protected:
virtual void pack1(OutputFile *, Filter &) override; // generate executable header

View File

@ -49,7 +49,7 @@ public:
virtual void unpack(OutputFile *fo) override;
virtual bool canPack() override;
virtual int canUnpack() override;
virtual int canUnpack() override; // really 'bool'
int find_overlay_offset(MemBuffer const &buf);
protected: