Skip to content

Commit

Permalink
decompiled elf_parse
Browse files Browse the repository at this point in the history
  • Loading branch information
smx-smx committed May 1, 2024
1 parent 3755aac commit 36053f2
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 2 deletions.
25 changes: 24 additions & 1 deletion xzre.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,20 @@ struct auditstate

typedef struct link_map *lookup_t;

/** reference: https://flapenguin.me/elf-dt-gnu-hash */
typedef struct gnu_hash_table {
uint32_t nbuckets;
uint32_t symoffset;
uint32_t bloom_size;
uint32_t bloom_shift;
uint64_t bloom[];
#if 0
// uint64_t bloom[bloom_size]; /* uint32_t for 32-bit binaries */
// uint32_t buckets[nbuckets];
// uint32_t chain[];
#endif
} gnu_hash_table_t;

struct La_i86_regs;
struct La_i86_retval;
struct La_x86_64_regs;
Expand Down Expand Up @@ -2265,7 +2279,7 @@ extern BOOL find_function(
* @param p_flags the expected segment protection flags (PF_*)
* @return BOOL TRUE if found, FALSE otherwise
*/
extern BOOL elf_contains_vaddr(elf_info_t *elf_info, u64 vaddr, u64 size, u32 p_flags);
extern BOOL elf_contains_vaddr(elf_info_t *elf_info, void *vaddr, u64 size, u32 p_flags);

/**
* @brief checks if given ELF file contains the range [vaddr, vaddr+size)
Expand All @@ -2288,6 +2302,15 @@ extern BOOL elf_contains_vaddr_relro(elf_info_t *elf_info, u64 vaddr, u64 size,
*/
extern BOOL elf_parse(Elf64_Ehdr *ehdr, elf_info_t *elf_info);

/**
* @brief checks if the provided identifiers represent a `PT_GNU_RELRO`
*
* @param p_type program header type
* @param addend constant `0xA0000000`
* @return BOOL TRUE if the supplied pt_type is `PT_GNU_RELRO`, FALSE otherwise
*/
extern BOOL is_gnu_relro(Elf64_Word p_type, u32 addend);

/**
* @brief Parses the main executable from the provided structure.
* As part of the process the arguments and environment is checked.
Expand Down
2 changes: 1 addition & 1 deletion xzre.lds.in
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ SECTIONS_BEGIN()
/* 0000000000001200 */ DEFSYM(fake_lzma_free, .text.stream_decoda)
/* 0000000000001230 */ DEFSYM(elf_contains_vaddr_impl, .text.powerpc_coda) // FIXME: prototype
/* 0000000000001390 */ DEFSYM(elf_contains_vaddr, .text.parse_bcz)
/* 00000000000013A0 */ DEFSYM(is_gnu_relro, .text.lzma_simple_props_sizd) // FIXME: prototype
/* 00000000000013A0 */ DEFSYM(is_gnu_relro, .text.lzma_simple_props_sizd)
/* 00000000000013C0 */ DEFSYM(elf_parse, .text.get_literal_prica)
/* 0000000000001870 */ DEFSYM(elf_symbol_get, .text.crc_inia)
DEFSYM_START(.text.crc64_generia)
Expand Down
1 change: 1 addition & 0 deletions xzre_code/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_library(xzre_code
backdoor_entry.c
chacha_decrypt.c
elf_parse.c
elf_symbol_get_addr.c
get_lzma_allocator.c
is_endbr64_instruction.c
Expand Down
202 changes: 202 additions & 0 deletions xzre_code/elf_parse.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/**
* Copyright (C) 2024 Stefano Moioli <[email protected]>
**/

#include "xzre.h"
#include <elf.h>
#include <openssl/bn.h>

BOOL elf_parse(Elf64_Ehdr *ehdr, elf_info_t *elf_info){
if(!ehdr || !elf_info){
return FALSE;
}

memset(elf_info, 0x00, sizeof(*elf_info));
elf_info->elfbase = ehdr;
elf_info->phdrs = (Elf64_Phdr *)PTRADD(ehdr, ehdr->e_phoff);
int i; Elf64_Phdr *phdr;
u64 first_vaddr = -1;
int dynamic_idx = -1;
for(i=0, phdr = elf_info->phdrs; i<ehdr->e_phnum; i++, phdr++){
if(phdr->p_type == PT_LOAD){
if(phdr->p_vaddr < first_vaddr){
first_vaddr = phdr->p_vaddr;
}
} else if(phdr->p_type == PT_DYNAMIC){
dynamic_idx = i;
} else if(is_gnu_relro(phdr->p_type, 0xA0000000)){
if(elf_info->gnurelro_found){
return FALSE;
}
elf_info->gnurelro_vaddr = phdr->p_vaddr;
elf_info->gnurelro_found = TRUE;
elf_info->gnurelro_memsize = phdr->p_memsz;
}
}
if(first_vaddr == -1 || dynamic_idx == -1){
return FALSE;
}
elf_info->first_vaddr = first_vaddr;

Elf64_Phdr *dyn_phdr = &elf_info->phdrs[dynamic_idx];
Elf64_Dyn *dyn = (Elf64_Dyn *)PTRADD(ehdr, PTRDIFF(dyn_phdr->p_vaddr, first_vaddr));
elf_info->dyn = dyn;
elf_info->dyn_num_entries = dyn_phdr->p_memsz / sizeof(Elf64_Dyn);
if(!elf_contains_vaddr(elf_info, dyn, dyn_phdr->p_memsz, PF_R)){
return FALSE;
}

gnu_hash_table_t *gnu_hash = NULL;

u64 d_pltrelsz = -1;
u64 d_relasz = -1;
u64 d_relrsz = -1;
BOOL have_verdef_num = FALSE;

for(i=0; i<elf_info->dyn_num_entries; i++, dyn++){
if(dyn->d_tag == DT_NULL){
elf_info->dyn_num_entries = i;
break;
}
switch(dyn->d_tag){
case DT_JMPREL:
elf_info->plt_relocs = (Elf64_Rela *)dyn->d_un.d_ptr;
break;
case DT_BIND_NOW:
elf_info->flags |= X_ELF_NOW;
break;
case DT_FLAGS:
if((dyn->d_un.d_val & DF_BIND_NOW) != 0){
elf_info->flags |= X_ELF_NOW;
}
break;
case DT_RELRSZ:
d_relrsz = dyn->d_un.d_val;
break;
case DT_RELR:
elf_info->relr_relocs = (Elf64_Relr *)dyn->d_un.d_ptr;
break;
case DT_PLTRELSZ:
d_pltrelsz = dyn->d_un.d_val;
break;
case DT_STRTAB:
elf_info->strtab = (char *)dyn->d_un.d_ptr;
break;
case DT_SYMTAB:
elf_info->symtab = (Elf64_Sym *)dyn->d_un.d_ptr;
break;
case DT_RELA:
elf_info->rela_relocs = (Elf64_Rela *)dyn->d_un.d_ptr;
break;
case DT_RELASZ:
d_relasz = dyn->d_un.d_val;
break;
case DT_FLAGS_1:
if((dyn->d_un.d_val & DF_1_NOW) != 0){
elf_info->flags |= X_ELF_NOW;
}
break;
case DT_VERDEFNUM:
elf_info->verdef_num = dyn->d_un.d_val;
break;
case DT_HIPROC:
return FALSE;
case DT_VERDEF:
have_verdef_num = TRUE;
elf_info->verdef = (Elf64_Verdef *)dyn->d_un.d_ptr;
break;
case DT_VERSYM:
elf_info->flags |= X_ELF_VERSYM;
elf_info->versym = (Elf64_Versym *)dyn->d_un.d_ptr;
break;
case DT_GNU_HASH:
gnu_hash = (gnu_hash_table_t *)dyn->d_un.d_ptr;
break;
default:
if(dyn->d_tag > DT_CONFIG){
return FALSE;
}
break;
}
}

if(elf_info->plt_relocs){
if(d_pltrelsz == -1){
return FALSE;
}
elf_info->flags |= X_ELF_PLTREL;
elf_info->plt_relocs_num = d_pltrelsz / sizeof(Elf64_Rela);
}
if(elf_info->rela_relocs){
if(d_relasz == -1){
return FALSE;
}
elf_info->flags |= X_ELF_RELA;
elf_info->rela_relocs_num = d_relasz / sizeof(Elf64_Rela);
}
if(elf_info->relr_relocs){
if(d_relrsz == -1){
return FALSE;
}
elf_info->flags |= X_ELF_RELR;
elf_info->relr_relocs_num = d_relrsz / sizeof(Elf64_Relr);
}
if(elf_info->verdef){
if(have_verdef_num){
elf_info->flags |= X_ELF_VERDEF;
} else {
elf_info->verdef = NULL;
}
}
if(!elf_info->strtab || !gnu_hash || !elf_info->symtab){
return FALSE;
}

// in case strtab is an offset, in case of MIPS/RISCV (see https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2718)
if(UPTR(elf_info->strtab) <= UPTR(ehdr)){
elf_info->strtab = (char *)PTRADD(ehdr, elf_info->strtab);
if(elf_info->plt_relocs){
elf_info->plt_relocs = (Elf64_Rela *)PTRADD(ehdr, elf_info->plt_relocs);
}
if(elf_info->rela_relocs){
elf_info->rela_relocs = (Elf64_Rela *)PTRADD(ehdr, elf_info->rela_relocs);
}
if(elf_info->relr_relocs){
elf_info->relr_relocs = (Elf64_Relr *)PTRADD(ehdr, elf_info->relr_relocs);
}
if(elf_info->versym){
elf_info->versym = (Elf64_Versym *)PTRADD(ehdr, elf_info->versym);
}
gnu_hash = (gnu_hash_table_t *)PTRADD(ehdr, gnu_hash);
}

// check if verdef is relative, and convert
if(elf_info->verdef && UPTR(elf_info->verdef) < UPTR(ehdr)){
elf_info->verdef = (Elf64_Verdef *)PTRADD(ehdr, elf_info->verdef);
}

if(elf_info->plt_relocs && !elf_contains_vaddr(elf_info, elf_info->plt_relocs, d_pltrelsz, PF_R)){
return FALSE;
}
if(elf_info->rela_relocs && !elf_contains_vaddr(elf_info, elf_info->rela_relocs, d_relasz, PF_R)){
return FALSE;
}
if(elf_info->relr_relocs && !elf_contains_vaddr(elf_info, elf_info->relr_relocs, d_relrsz, PF_R)){
return FALSE;
}
if(elf_info->verdef && !elf_contains_vaddr(elf_info, elf_info->verdef, sizeof(Elf64_Verdef) * elf_info->verdef_num, PF_R)){
return FALSE;
}

u64 *hash_bloom = &gnu_hash->bloom[0];
u32 *hash_buckets = (u32 *)&hash_bloom[gnu_hash->bloom_size];
u32 *hash_chain = &hash_buckets[gnu_hash->nbuckets - gnu_hash->symoffset];

elf_info->gnu_hash_nbuckets = gnu_hash->nbuckets;
elf_info->gnu_hash_last_bloom = gnu_hash->bloom_size - 1;
elf_info->gnu_hash_bloom = hash_bloom;
elf_info->gnu_hash_bloom_shift = gnu_hash->bloom_shift;
elf_info->gnu_hash_buckets = hash_buckets;
elf_info->gnu_hash_chain = hash_chain;
return TRUE;
}

0 comments on commit 36053f2

Please sign in to comment.