de-compression of arm64 --android-shlib

modified:   src/p_lx_elf.cpp
	modified:   src/p_lx_elf.h
	modified:   src/stub/src/arm64-linux.shlib-init.S
This commit is contained in:
John Reiser
2018-01-24 15:39:46 -08:00
parent 5d13c6cd01
commit 85f53b210d
5 changed files with 365 additions and 264 deletions
+105 -11
View File
@@ -328,7 +328,8 @@ off_t PackLinuxElf::pack3(OutputFile *fo, Filter &ft) // return length of output
: elf_unsigned_dynamic(Elf32_Dyn::DT_INIT) );
set_te32(&disp, firstpc_va - load_va);
if (opt->o_unix.android_shlib) { // the extra page
set_te32(&disp, asl_delta + firstpc_va - load_va);
// Hacked in *-linux.shlib-init.S
//set_te32(&disp, asl_delta + firstpc_va - load_va);
}
fo->write(&disp, sizeof(disp)); // DT_INIT.d_val
len += sizeof(disp);
@@ -2953,8 +2954,13 @@ void PackLinuxElf64::pack1(OutputFile *fo, Filter & /*ft*/)
// .p_filesz,.p_memsz are updated in ::pack3
}
Elf64_Ehdr *const ehdr = (Elf64_Ehdr *)&lowmem[0];
upx_uint64_t e_entry = get_te64(&ehdr->e_entry);
if (xct_off < e_entry) {
set_te64(&ehdr->e_entry, asl_delta + e_entry);
}
// Relocate Shdr; and Rela, Rel (below xct_off)
set_te64(&((Elf64_Ehdr *)&lowmem[0])->e_shoff, xct_off);
set_te64(&ehdr->e_shoff, xct_off);
memcpy(&lowmem[xct_off], shdri, e_shnum * sizeof(Elf64_Ehdr));
Elf64_Shdr *const shdro = (Elf64_Shdr *)&lowmem[xct_off];
Elf64_Shdr *shdr = shdro;
@@ -3574,6 +3580,49 @@ void PackLinuxElf64::pack4(OutputFile *fo, Filter &ft)
}
}
void
PackLinuxElf64::unRela64(
upx_uint64_t dt_rela,
Elf64_Rela *rela0,
unsigned relasz,
MemBuffer &ptload1,
upx_uint64_t const load_off,
OutputFile *fo
)
{
Elf64_Rela *rela = rela0;
for (int k = relasz / sizeof(Elf64_Rela); --k >= 0; ++rela) {
upx_uint64_t r_addend = get_te64(&rela->r_addend);
upx_uint64_t r_offset = get_te64(&rela->r_offset);
upx_uint64_t r_info = get_te64(&rela->r_info);
unsigned r_type = ELF64_R_TYPE(r_info);
if (xct_off <= r_offset) {
set_te64(&rela->r_offset, r_offset - asl_delta);
}
if (Elf64_Ehdr::EM_AARCH64 == e_machine) {
if (R_AARCH64_RELATIVE == r_type) {
if (xct_off <= r_addend) {
set_te64(&rela->r_addend, r_addend - asl_delta);
}
}
if (R_AARCH64_JUMP_SLOT == r_type) {
++n_jmp_slot;
// .rela.plt contains offset of the "first time" target
upx_uint64_t d = r_offset - load_off - asl_delta;
if (plt_off > d) {
plt_off = d;
}
upx_uint64_t w = get_te64(&ptload1[d]);
if (xct_off <= w) {
set_te64(&ptload1[d], w - asl_delta);
}
}
}
}
fo->seek(dt_rela, SEEK_SET);
fo->rewrite(rela0, relasz);
}
void PackLinuxElf64::unpack(OutputFile *fo)
{
if (e_phoff != sizeof(Elf64_Ehdr)) {// Phdrs not contiguous with Ehdr
@@ -3613,7 +3662,6 @@ void PackLinuxElf64::unpack(OutputFile *fo)
throwCantUnpack("b_info corrupted");
ph.filter_cto = bhdr.b_cto8;
#define MAX_ELF_HDR 1024
MemBuffer u(ph.u_len);
Elf64_Ehdr *const ehdr = (Elf64_Ehdr *)&u[0];
Elf64_Phdr const *phdr = 0;
@@ -3641,9 +3689,11 @@ void PackLinuxElf64::unpack(OutputFile *fo)
unsigned total_out = 0;
unsigned c_adler = upx_adler32(NULL, 0);
unsigned u_adler = upx_adler32(NULL, 0);
#define MAX_ELF_HDR 1024
if ((MAX_ELF_HDR - sizeof(Elf64_Ehdr))/sizeof(Elf64_Phdr) < u_phnum) {
throwCantUnpack("bad compressed e_phnum");
}
#undef MAX_ELF_HDR
// Packed ET_EXE has no PT_DYNAMIC.
// Packed ET_DYN has original PT_DYNAMIC for info needed by rtld.
@@ -3781,6 +3831,7 @@ void PackLinuxElf64::unpack(OutputFile *fo)
for (unsigned j= 0; j < u_phnum; ++j, ++phdr) {
if (PT_LOAD64==get_te32(&phdr->p_type) && 0!=n_ptload++) {
load_off = get_te64(&phdr->p_offset);
load_va = get_te64(&phdr->p_vaddr);
fi->seek(old_data_off, SEEK_SET);
fi->readx(ibuf, old_data_len);
total_in += old_data_len;
@@ -3793,24 +3844,68 @@ void PackLinuxElf64::unpack(OutputFile *fo)
}
// Restore DT_INIT.d_val
phdr = (Elf64_Phdr *)&u[sizeof(*ehdr)];
if (fo)
for (unsigned j= 0; j < u_phnum; ++j, ++phdr) {
if (phdr->PT_DYNAMIC==get_te32(&phdr->p_type)) {
upx_uint64_t dt_pltrelsz(0), dt_jmprel(0);
upx_uint64_t dt_relasz(0), dt_rela(0);
unsigned const dyn_off = get_te64(&phdr->p_offset);
unsigned const dyn_len = get_te64(&phdr->p_filesz);
Elf64_Dyn *dyn = (Elf64_Dyn *)((unsigned char *)ibuf +
(dyn_off - load_off));
for (unsigned j2= 0; j2 < dyn_len; ++dyn, j2 += sizeof(*dyn)) {
if (dyn->DT_INIT==get_te32(&dyn->d_tag)) {
if (fo) {
fo->seek(sizeof(upx_uint64_t) + j2 + dyn_off, SEEK_SET);
fo->rewrite(&old_dtinit, sizeof(old_dtinit));
fo->seek(0, SEEK_END);
}
break;
upx_uint64_t const tag = get_te64(&dyn->d_tag);
upx_uint64_t val = get_te64(&dyn->d_val);
switch (tag) {
case dyn->DT_INIT: {
fo->seek(sizeof(upx_uint64_t) + j2 + dyn_off, SEEK_SET);
fo->rewrite(&old_dtinit, sizeof(old_dtinit));
} break;
case dyn->DT_PREINIT_ARRAY:
case dyn->DT_INIT_ARRAY:
case dyn->DT_FINI_ARRAY:
case dyn->DT_FINI:
case dyn->DT_PLTGOT: {
fo->seek(sizeof(upx_uint64_t) + j2 + dyn_off, SEEK_SET);
val -= asl_delta;
fo->rewrite(&val, sizeof(val));
} break;
case dyn->DT_PLTRELSZ: { // Size in bytes of PLT relocs
dt_pltrelsz = val; (void)dt_pltrelsz;
} break;
case dyn->DT_JMPREL: { // Address of PLT relocs
dt_jmprel = val; (void)dt_jmprel;
} break;
case dyn->DT_RELASZ: { // Total size of Rela relocs
dt_relasz = val;
} break;
case dyn->DT_RELA: { // Address of Rela relocs
dt_rela = val;
} break;
} // end switch()
}
lowmem.alloc(xct_off);
fi->seek(0, SEEK_SET);
fi->read(lowmem, xct_off); // contains relocation tables
if (dt_relasz) {
Elf64_Rela *const rela0 = (Elf64_Rela *)lowmem.subref(
"bad Rela offset", dt_rela, dt_relasz);
unRela64(dt_rela, rela0, dt_relasz, ibuf, load_va, fo);
}
if (dt_pltrelsz) {
Elf64_Rela *const jmp0 = (Elf64_Rela *)lowmem.subref(
"bad Jmprel offset", dt_jmprel, dt_pltrelsz);
plt_off = ~0ull; n_jmp_slot = 0;
unRela64(dt_jmprel, jmp0, dt_pltrelsz, ibuf, load_va, fo);
if (~0ull != plt_off) {
fo->seek(plt_off + load_off, SEEK_SET);
fo->rewrite(&ibuf[plt_off], n_jmp_slot * sizeof(void *));
}
}
}
}
if (fo) fo->seek(0, SEEK_END);
}
// update header with totals
@@ -3824,7 +3919,6 @@ void PackLinuxElf64::unpack(OutputFile *fo)
// finally test the checksums
if (ph.c_adler != c_adler || ph.u_adler != u_adler)
throwChecksumError();
#undef MAX_ELF_HDR
}