Check DT_HASH and DT_GNU_HASH.

https://github.com/upx/upx/issues/235  POC2,POC4
Also cleanup checking of symbol number and .st_name
in POC1,POC3,POC5,POC7,POC9,POC11
	modified:   p_lx_elf.cpp
	modified:   p_lx_elf.h
This commit is contained in:
John Reiser
2018-12-15 19:16:14 -08:00
parent 606ad08dd1
commit 780a4347b8
2 changed files with 132 additions and 30 deletions
+130 -30
View File
@@ -1598,6 +1598,50 @@ PackLinuxElf32::invert_pt_dynamic(Elf32_Dyn const *dynp)
symnum_end = (v_str - v_sym) / sz_sym;
}
}
// DT_HASH often ends at DT_SYMTAB
unsigned const v_hsh = elf_unsigned_dynamic(Elf64_Dyn::DT_HASH);
if (v_hsh && file_image) {
hashtab = (unsigned const *)elf_find_dynamic(Elf64_Dyn::DT_HASH);
unsigned const nbucket = get_te32(&hashtab[0]);
unsigned const *const buckets = &hashtab[2];
unsigned const *const chains = &buckets[nbucket];
unsigned const v_sym = get_te32(&dynp0[-1+ x_sym].d_val);
if (v_hsh < v_sym
&& (v_sym - v_hsh) < (sizeof(unsigned)*2 // headers
+ sizeof(*buckets)*nbucket // buckets
+ sizeof(*chains) *nbucket // chains
)) {
char msg[90]; snprintf(msg, sizeof(msg),
"bad DT_HASH nbucket=%#x len=%#x",
nbucket, (v_sym - v_hsh));
throwCantPack(msg);
}
}
// DT_GNU_HASH often ends at DT_SYMTAB
unsigned const v_gsh = elf_unsigned_dynamic(Elf32_Dyn::DT_GNU_HASH);
if (v_gsh && file_image) {
gashtab = (unsigned const *)elf_find_dynamic(Elf32_Dyn::DT_GNU_HASH);
unsigned const n_bucket = get_te32(&gashtab[0]);
unsigned const n_bitmask = get_te32(&gashtab[2]);
unsigned const *const bitmask = (unsigned const *)(void const *)&gashtab[4];
unsigned const *const buckets = (unsigned const *)&bitmask[n_bitmask];
unsigned const *const hasharr = &buckets[n_bucket];
//unsigned const *const gashend = &hasharr[n_bucket]; // minimum
unsigned const v_sym = get_te32(&dynp0[-1+ x_sym].d_val);
if (v_gsh < v_sym
&& (v_sym - v_gsh) < (sizeof(unsigned)*4 // headers
+ sizeof(*bitmask)*n_bitmask // bitmask
+ sizeof(*buckets)*n_bucket // buckets
+ sizeof(*hasharr)*n_bucket // hasharr
)) {
char msg[90]; snprintf(msg, sizeof(msg),
"bad DT_GNU_HASH n_bucket=%#x n_bitmask=%#x len=%#x",
n_bucket, n_bitmask, v_sym - v_gsh);
throwCantPack(msg);
}
}
}
Elf32_Phdr const *
@@ -1691,22 +1735,26 @@ Elf64_Shdr const *PackLinuxElf64::elf_find_section_type(
return 0;
}
char const *PackLinuxElf64::get_dynsym_name(unsigned symnum, unsigned relnum) const
char const *PackLinuxElf64::get_str_name(upx_uint64_t st_name, unsigned symnum) const
{
if (symnum_end <= symnum) {
char msg[50]; snprintf(msg, sizeof(msg),
"bad symnum %#x in Elf64_Rel[%d]\n", symnum, relnum);
throwCantPack(msg);
}
unsigned st_name = get_te64(&dynsym[symnum].st_name);
if (strtab_end <= st_name) {
char msg[50]; snprintf(msg, sizeof(msg),
"bad .st_name %#x in DT_STRTAB[%d]\n", st_name, symnum);
char msg[70]; snprintf(msg, sizeof(msg),
"bad .st_name %#lx in DT_SYMTAB[%d]\n", (unsigned long)st_name, symnum);
throwCantPack(msg);
}
return &dynstr[st_name];
}
char const *PackLinuxElf64::get_dynsym_name(unsigned symnum, unsigned relnum) const
{
if (symnum_end <= symnum) {
char msg[70]; snprintf(msg, sizeof(msg),
"bad symnum %#x in Elf64_Rel[%d]\n", symnum, relnum);
throwCantPack(msg);
}
return get_str_name(get_te64(&dynsym[symnum].st_name), symnum);
}
bool PackLinuxElf64::calls_crt1(Elf64_Rela const *rela, int sz)
{
if (!dynsym || !dynstr) {
@@ -1724,22 +1772,26 @@ bool PackLinuxElf64::calls_crt1(Elf64_Rela const *rela, int sz)
return false;
}
char const *PackLinuxElf32::get_dynsym_name(unsigned symnum, unsigned relnum) const
char const *PackLinuxElf32::get_str_name(unsigned st_name, unsigned symnum) const
{
if (symnum_end <= symnum) {
char msg[50]; snprintf(msg, sizeof(msg),
"bad symnum %#x in Elf32_Rel[%d]\n", symnum, relnum);
throwCantPack(msg);
}
unsigned st_name = get_te32(&dynsym[symnum].st_name);
if (strtab_end <= st_name) {
char msg[50]; snprintf(msg, sizeof(msg),
"bad .st_name %#x in DT_STRTAB[%d]\n", st_name, symnum);
char msg[70]; snprintf(msg, sizeof(msg),
"bad .st_name %#x in DT_SYMTAB[%d]\n", st_name, symnum);
throwCantPack(msg);
}
return &dynstr[st_name];
}
char const *PackLinuxElf32::get_dynsym_name(unsigned symnum, unsigned relnum) const
{
if (symnum_end <= symnum) {
char msg[70]; snprintf(msg, sizeof(msg),
"bad symnum %#x in Elf32_Rel[%d]\n", symnum, relnum);
throwCantPack(msg);
}
return get_str_name(get_te32(&dynsym[symnum].st_name), symnum);
}
bool PackLinuxElf32::calls_crt1(Elf32_Rel const *rel, int sz)
{
if (!dynsym || !dynstr) {
@@ -2738,7 +2790,8 @@ int
PackLinuxElf32::adjABS(Elf32_Sym *sym, unsigned delta)
{
for (int j = 0; abs_symbol_names[j][0]; ++j) {
if (!strcmp(abs_symbol_names[j], &dynstr[sym->st_name])) {
unsigned st_name = get_te32(&sym->st_name);
if (!strcmp(abs_symbol_names[j], get_str_name(st_name, -1))) {
sym->st_value += delta;
return 1;
}
@@ -2750,7 +2803,8 @@ int
PackLinuxElf64::adjABS(Elf64_Sym *sym, unsigned delta)
{
for (int j = 0; abs_symbol_names[j][0]; ++j) {
if (!strcmp(abs_symbol_names[j], &dynstr[sym->st_name])) {
upx_uint64_t st_name = get_te64(&sym->st_name);
if (!strcmp(abs_symbol_names[j], get_str_name(st_name, -1))) {
sym->st_value += delta;
return 1;
}
@@ -4657,6 +4711,8 @@ PackLinuxElf64::invert_pt_dynamic(Elf64_Dyn const *dynp)
throwCantPack(msg);
}
}
// DT_SYMTAB has no designated length.
// End it when next area else starts; often DT_STRTAB. (FIXME)
unsigned const x_sym = dt_table[Elf64_Dyn::DT_SYMTAB];
unsigned const x_str = dt_table[Elf64_Dyn::DT_STRTAB];
if (x_sym && x_str) {
@@ -4669,6 +4725,50 @@ PackLinuxElf64::invert_pt_dynamic(Elf64_Dyn const *dynp)
symnum_end = (v_str - v_sym) / sz_sym;
}
}
// DT_HASH often ends at DT_SYMTAB
unsigned const v_hsh = elf_unsigned_dynamic(Elf64_Dyn::DT_HASH);
if (v_hsh && file_image) {
hashtab = (unsigned const *)elf_find_dynamic(Elf64_Dyn::DT_HASH);
unsigned const nbucket = get_te32(&hashtab[0]);
unsigned const *const buckets = &hashtab[2];
unsigned const *const chains = &buckets[nbucket];
unsigned const v_sym = get_te32(&dynp0[-1+ x_sym].d_val);
if (v_hsh < v_sym
&& (v_sym - v_hsh) < (sizeof(unsigned)*2 // headers
+ sizeof(*buckets)*nbucket // buckets
+ sizeof(*chains) *nbucket // chains
)) {
char msg[90]; snprintf(msg, sizeof(msg),
"bad DT_HASH nbucket=%#x len=%#x",
nbucket, (v_sym - v_hsh));
throwCantPack(msg);
}
}
// DT_GNU_HASH often ends at DT_SYMTAB
unsigned const v_gsh = elf_unsigned_dynamic(Elf64_Dyn::DT_GNU_HASH);
if (v_gsh && file_image) {
gashtab = (unsigned const *)elf_find_dynamic(Elf64_Dyn::DT_GNU_HASH);
unsigned const n_bucket = get_te32(&gashtab[0]);
unsigned const n_bitmask = get_te32(&gashtab[2]);
upx_uint64_t const *const bitmask = (upx_uint64_t const *)(void const *)&gashtab[4];
unsigned const *const buckets = (unsigned const *)&bitmask[n_bitmask];
unsigned const *const hasharr = &buckets[n_bucket];
//unsigned const *const gashend = &hasharr[n_bucket]; // minimum
upx_uint64_t const v_sym = get_te64(&dynp0[-1+ x_sym].d_val);
if (v_gsh < v_sym
&& (v_sym - v_gsh) < (sizeof(unsigned)*4 // headers
+ sizeof(*bitmask)*n_bitmask // bitmask
+ sizeof(*buckets)*n_bucket // buckets
+ sizeof(*hasharr)*n_bucket // hasharr
)) {
char msg[90]; snprintf(msg, sizeof(msg),
"bad DT_GNU_HASH n_bucket=%#x n_bitmask=%#x len=%#lx",
n_bucket, n_bitmask, (long unsigned)(v_sym - v_gsh));
throwCantPack(msg);
}
}
}
void const *
@@ -4733,7 +4833,7 @@ Elf32_Sym const *PackLinuxElf32::elf_lookup(char const *name) const
unsigned const m = elf_hash(name) % nbucket;
unsigned si;
for (si= get_te32(&buckets[m]); 0!=si; si= get_te32(&chains[si])) {
char const *const p= get_dynsym_name(si, 0);
char const *const p= get_dynsym_name(si, -1);
if (0==strcmp(name, p)) {
return &dynsym[si];
}
@@ -4746,6 +4846,7 @@ Elf32_Sym const *PackLinuxElf32::elf_lookup(char const *name) const
unsigned const gnu_shift = get_te32(&gashtab[3]);
unsigned const *const bitmask = &gashtab[4];
unsigned const *const buckets = &bitmask[n_bitmask];
unsigned const *const hasharr = &buckets[n_bucket];
unsigned const h = gnu_hash(name);
unsigned const hbit1 = 037& h;
@@ -4755,13 +4856,12 @@ Elf32_Sym const *PackLinuxElf32::elf_lookup(char const *name) const
if (1& (w>>hbit1) & (w>>hbit2)) {
unsigned bucket = get_te32(&buckets[h % n_bucket]);
if (0!=bucket) {
Elf32_Sym const *dsp = dynsym;
unsigned const *const hasharr = &buckets[n_bucket];
Elf32_Sym const *dsp = &dynsym[bucket];
unsigned const *hp = &hasharr[bucket - symbias];
dsp += bucket;
do if (0==((h ^ get_te32(hp))>>1)) {
char const *const p = get_te32(&dsp->st_name) + dynstr;
unsigned st_name = get_te32(&dsp->st_name);
char const *const p = get_str_name(st_name, -1);
if (0==strcmp(name, p)) {
return dsp;
}
@@ -4782,7 +4882,7 @@ Elf64_Sym const *PackLinuxElf64::elf_lookup(char const *name) const
unsigned const m = elf_hash(name) % nbucket;
unsigned si;
for (si= get_te32(&buckets[m]); 0!=si; si= get_te32(&chains[si])) {
char const *const p= get_dynsym_name(si, 0);
char const *const p= get_dynsym_name(si, -1);
if (0==strcmp(name, p)) {
return &dynsym[si];
}
@@ -4795,6 +4895,7 @@ Elf64_Sym const *PackLinuxElf64::elf_lookup(char const *name) const
unsigned const gnu_shift = get_te32(&gashtab[3]);
upx_uint64_t const *const bitmask = (upx_uint64_t const *)(void const *)&gashtab[4];
unsigned const *const buckets = (unsigned const *)&bitmask[n_bitmask];
unsigned const *const hasharr = &buckets[n_bucket];
unsigned const h = gnu_hash(name);
unsigned const hbit1 = 077& h;
@@ -4804,13 +4905,12 @@ Elf64_Sym const *PackLinuxElf64::elf_lookup(char const *name) const
if (1& (w>>hbit1) & (w>>hbit2)) {
unsigned bucket = get_te32(&buckets[h % n_bucket]);
if (0!=bucket) {
Elf64_Sym const *dsp = dynsym;
unsigned const *const hasharr = &buckets[n_bucket];
Elf64_Sym const *dsp = &dynsym[bucket];
unsigned const *hp = &hasharr[bucket - symbias];
dsp += bucket;
do if (0==((h ^ get_te32(hp))>>1)) {
char const *const p = get_te64(&dsp->st_name) + dynstr;
upx_uint64_t st_name = get_te64(&dsp->st_name);
char const *const p = get_str_name(st_name, -1);
if (0==strcmp(name, p)) {
return dsp;
}
+2
View File
@@ -163,6 +163,7 @@ protected:
virtual upx_uint64_t elf_unsigned_dynamic(unsigned) const;
virtual int adjABS(Elf32_Sym *sym, unsigned delta);
char const *get_str_name(unsigned st_name, unsigned symnum) const;
char const *get_dynsym_name(unsigned symnum, unsigned relnum) const;
protected:
Elf32_Ehdr ehdri; // from input file
@@ -298,6 +299,7 @@ protected:
virtual upx_uint64_t elf_unsigned_dynamic(unsigned) const;
virtual int adjABS(Elf64_Sym *sym, unsigned delta);
char const *get_str_name(upx_uint64_t st_name, unsigned symnum) const;
char const *get_dynsym_name(unsigned symnum, unsigned relnum) const;
protected:
Elf64_Ehdr ehdri; // from input file