/*****************************************************************************/ /* pemagination: a (virtual) tour into portable bits and executable bytes */ /* Copyright (C) 2013--2016 Z. Gilboa */ /* Released under GPLv2 and GPLv3; see COPYING.PEMAGINE. */ /*****************************************************************************/ #include #include #include #include #include "pe_impl.h" static __inline__ int pe_addr_within_bounds(void * addr, void * bottom, void * top) { return (((uintptr_t)addr >= (uintptr_t)bottom) && ((uintptr_t)addr < (uintptr_t)top)); } int pe_get_export_symbol_info( const void * base, const char * name, struct pe_export_sym * sym) { struct pe_raw_export_hdr * exp_hdr; ssize_t len; uint32_t * rva_offset; uintptr_t * addr_offset; uintptr_t addr; uint32_t sec_size; void * sec_addr_cap; uint32_t * fn_addr; uint32_t * fn_names; uint16_t * fn_ordinals; uint32_t * addr_entries; uint32_t * name_entries; char * exp_name; const char * src; const char * dst; ssize_t match; uint32_t lower; uint32_t upper; uint32_t idx; /* init */ sym->ordinal_base = 0; sym->ordinal = 0; sym->addr = 0; sym->forwarder_rva = 0; sym->name = 0; sym->status = 0; if (!(exp_hdr = pe_get_image_export_hdr_addr(base,&sec_size))) return -1; sec_addr_cap = pe_va_from_rva(exp_hdr,sec_size); rva_offset = (uint32_t *)(exp_hdr->eh_export_addr_tbl_rva); fn_addr = (uint32_t *)pe_va_from_rva(base,*rva_offset); rva_offset = (uint32_t *)(exp_hdr->eh_name_ptr_rva); fn_names = (uint32_t *)pe_va_from_rva(base,*rva_offset); rva_offset = (uint32_t *)(exp_hdr->eh_ordinal_tbl_rva); fn_ordinals = (uint16_t *)pe_va_from_rva(base,*rva_offset); addr_entries = (uint32_t *)exp_hdr->eh_addr_tbl_entries; name_entries = (uint32_t *)exp_hdr->eh_num_of_name_ptrs; /* by ordinal? */ if ((intptr_t)name < 0x10000) { sym->ordinal_base = (uint32_t *)exp_hdr->eh_ordinal_base; /* the array is zero-based, but ordinals are normally one-based... */ if (((intptr_t)name - *sym->ordinal_base + 1) > *addr_entries) return -1; addr = ((uintptr_t)name - *sym->ordinal_base) * sizeof(uint32_t); rva_offset = (uint32_t *)pe_va_from_rva(fn_addr,addr); addr_offset = (uintptr_t *)pe_va_from_rva(base,*rva_offset); if (pe_addr_within_bounds(addr_offset,exp_hdr,sec_addr_cap)) { sym->forwarder_rva = 0; sym->addr = addr_offset; } else /* todo: resolve forwarder address */ sym->addr = addr_offset; return 0; } if ((len = pe_impl_strlen_ansi(name)) < 0) return -1; len++; lower = 0; upper = *name_entries; while (lower < upper) { idx = (lower + upper) / 2; rva_offset = (uint32_t *)pe_va_from_rva(fn_names,idx*sizeof(uint32_t)); exp_name = (char *)pe_va_from_rva(base,*rva_offset); src = name; dst = exp_name; for (match=0; (matchordinal_base = (uint32_t *)exp_hdr->eh_ordinal_base; sym->ordinal = (uint16_t *)pe_va_from_rva(fn_ordinals,idx*sizeof(uint16_t)); rva_offset = (uint32_t *)pe_va_from_rva(fn_addr,(*sym->ordinal)*sizeof(uint32_t)); addr_offset = (uintptr_t *)pe_va_from_rva(base,*rva_offset); if (pe_addr_within_bounds(addr_offset,exp_hdr,sec_addr_cap)) { /* todo: resolve forwarder address */ sym->forwarder_rva = 0; sym->addr = 0; } else { sym->forwarder_rva = 0; sym->addr = addr_offset; } return 0; } else { if (*src > *dst) lower = idx + 1; else upper = idx; } } return -1; }