Blame src/logic/pe_get_image_meta.c

e2e2c2
/***************************************************************/
e2e2c2
/*  perk: PE Resource Kit                                      */
ced38a
/*  Copyright (C) 2015--2017  Z. Gilboa                        */
e2e2c2
/*  Released under GPLv2 and GPLv3; see COPYING.PERK.          */
e2e2c2
/***************************************************************/
e2e2c2
c0fbae
#include <stdint.h>
c0fbae
#include <stdlib.h>
c0fbae
#include <stdio.h>
c0fbae
#include <string.h>
c0fbae
c0fbae
#include <perk/perk.h>
413f56
#include "perk_reader_impl.h"
0e3fd6
#include "perk_errinfo_impl.h"
c0fbae
0e3fd6
static int pe_free_image_meta_impl(struct pe_image_meta * meta, int ret)
c0fbae
{
c3f597
	int i;
4e9e98
fa22b4
	if (meta) {
58ced9
		for (i=0; i<meta->m_stats.nimplibs; i++)
58ced9
			free(meta->m_idata[i].ih_items);
c0fbae
58ced9
		free(meta->m_idata);
58ced9
		free(meta->m_sectbl);
fa22b4
		free(meta);
fa22b4
	}
da8e21
0e3fd6
	return ret;
c0fbae
}
c0fbae
2b7f36
void pe_free_image_meta(struct pe_image_meta * meta)
c0fbae
{
fa22b4
	pe_free_image_meta_impl(meta,0);
c0fbae
}
c0fbae
2b7f36
int pe_get_named_section_index(const struct pe_image_meta * m, const char * name)
c0fbae
{
58ced9
	int i; for (i=0; i<m->m_coff.cfh_num_of_sections; i++)
58ced9
		if (!(strcmp(name,m->m_sectbl[i].sh_name)))
c0fbae
			return i;
c0fbae
c0fbae
	return -1;
c0fbae
}
c0fbae
2b7f36
int pe_get_block_section_index(const struct pe_image_meta * m, const struct pe_block * block)
c0fbae
{
c0fbae
	int i;
c0fbae
	uint32_t low,high;
c0fbae
f3d26e
	if (m->r_obj)
862cb6
		return -1;
862cb6
58ced9
	for (i=0; i<m->m_coff.cfh_num_of_sections; i++) {
58ced9
		low  = m->m_sectbl[i].sh_virtual_addr;
58ced9
		high = low + m->m_sectbl[i].sh_virtual_size;
c0fbae
a402c4
		if ((block->dh_rva >= low) && (block->dh_rva + block->dh_size <= high))
c0fbae
			return i;
c0fbae
	}
c0fbae
c0fbae
	return -1;
c0fbae
}
c0fbae
62b9db
int pe_get_roffset_from_rva(const struct pe_image_meta * m, uint32_t rva, uint32_t * roffset)
62b9db
{
62b9db
	int i;
62b9db
	uint32_t low,high;
62b9db
58ced9
	for (i=0; i<m->m_coff.cfh_num_of_sections; i++) {
58ced9
		low  = m->m_sectbl[i].sh_virtual_addr;
58ced9
		high = low + m->m_sectbl[i].sh_virtual_size;
62b9db
62b9db
		if ((rva >= low) && (rva < high)) {
58ced9
			*roffset = (rva - low) + m->m_sectbl[i].sh_ptr_to_raw_data;
62b9db
			return 0;
62b9db
		}
62b9db
	}
62b9db
62b9db
	return -1;
62b9db
}
62b9db
62b9db
int pe_get_rva_from_roffset(const struct pe_image_meta * m, uint32_t roffset, uint32_t * rva)
62b9db
{
62b9db
	int i;
62b9db
	uint32_t low,high,ref;
62b9db
58ced9
	for (i=0, ref=~0; i<m->m_coff.cfh_num_of_sections; i++) {
58ced9
		low  = m->m_sectbl[i].sh_ptr_to_raw_data;
58ced9
		high = low + m->m_sectbl[i].sh_virtual_size;
62b9db
62b9db
		if ((roffset >= low) && (roffset < high)) {
58ced9
			*rva = (roffset - low) + m->m_sectbl[i].sh_virtual_addr;
62b9db
			return 0;
62b9db
		} else if (ref > low) {
62b9db
			ref = low;
62b9db
		}
62b9db
	}
62b9db
62b9db
	if (roffset < ref) {
62b9db
		*rva = roffset;
62b9db
		return 0;
62b9db
	}
62b9db
62b9db
	return -1;
62b9db
}
62b9db
cf4adc
int pe_get_expsym_by_name(
c7ca52
	const struct pe_image_meta *	m,
cf4adc
	const char *			name,
cf4adc
	struct pe_expsym *		expsym)
c7ca52
{
c7ca52
	uint32_t 	offset;
c7ca52
	uint32_t *	symrva;
c7ca52
	const char *	sym;
c7ca52
	unsigned	i;
c7ca52
f3d26e
	if (m->r_obj || !m->h_edata)
862cb6
		return -1;
862cb6
25e3c0
	offset	= m->h_edata->sh_virtual_addr - m->h_edata->sh_ptr_to_raw_data;
f3d26e
	symrva	= (uint32_t *)((uintptr_t)m->r_image.addr + (m->m_edata.eh_name_ptr_rva - offset));
c7ca52
58ced9
	for (i=0; i<m->m_edata.eh_num_of_name_ptrs; i++) {
f3d26e
		sym = (const char *)m->r_image.addr + symrva[i] - offset;
c7ca52
cf4adc
		if (!(strcmp(sym,name))) {
cf4adc
			if (expsym) {
cf4adc
				expsym->name    = sym;
cf4adc
				expsym->eaddr   = 0;
cf4adc
				expsym->maddr   = 0;
cf4adc
				expsym->roffset = 0;
cf4adc
			}
cf4adc
cf4adc
			return 0;
cf4adc
		}
c7ca52
	}
c7ca52
cf4adc
	return -1;
c7ca52
}
c7ca52
cf4adc
int pe_get_expsym_by_index(
c7ca52
	const struct pe_image_meta *	m,
cf4adc
	unsigned			index,
cf4adc
	struct pe_expsym *		expsym)
c7ca52
{
c7ca52
	uint32_t 	offset;
c7ca52
	uint32_t *	symrva;
cf4adc
	uintptr_t	symaddr;
c7ca52
f3d26e
	if (m->r_obj)
862cb6
		return -1;
862cb6
58ced9
	if (index >= m->m_edata.eh_num_of_name_ptrs)
cf4adc
		return -1;
c7ca52
cf4adc
	if (expsym) {
25e3c0
		offset  = m->h_edata->sh_virtual_addr - m->h_edata->sh_ptr_to_raw_data;
f3d26e
		symrva  = (uint32_t *)((uintptr_t)m->r_image.addr + (m->m_edata.eh_name_ptr_rva - offset));
f3d26e
		symaddr = (uintptr_t)m->r_image.addr + symrva[index] - offset;
c7ca52
cf4adc
		expsym->name    = (const char *)symaddr;
cf4adc
		expsym->eaddr   = 0;
cf4adc
		expsym->maddr   = 0;
cf4adc
		expsym->roffset = 0;
cf4adc
	}
cf4adc
cf4adc
	return 0;
c7ca52
}
c7ca52
0e3fd6
int pe_get_image_meta(
0e3fd6
	const struct pe_driver_ctx *	dctx,
0e3fd6
	const struct pe_raw_image *	image,
0e3fd6
	struct pe_image_meta ** meta)
c0fbae
{
85fd0d
	int			ret;
85fd0d
	int 			i,s;
85fd0d
	long			l;
85fd0d
	unsigned		j;
4a8ae6
	const unsigned char *	mark;
85fd0d
	struct pe_image_meta *	m;
85fd0d
	char *			base;
85fd0d
85fd0d
	base = image->addr;
c0fbae
c0fbae
	if (!(m = calloc(1,sizeof(*m))))
0e3fd6
		return PERK_SYSTEM_ERROR(dctx);
c0fbae
f3d26e
	m->r_obj = (struct pe_raw_coff_object_hdr *)base;
c0fbae
f3d26e
	if (pe_read_object_header(m->r_obj,&m->m_coff)) {
f3d26e
		m->r_obj = 0;
f3d26e
		m->r_dos = (struct pe_raw_image_dos_hdr *)base;
c0fbae
f3d26e
		if ((ret = (pe_read_dos_header(m->r_dos,&m->m_dos))))
862cb6
			return pe_free_image_meta_impl(
862cb6
				m,PERK_CUSTOM_ERROR(dctx,ret));
c0fbae
f3d26e
		m->r_coff = (struct pe_raw_coff_image_hdr *)(base + m->m_dos.dos_lfanew);
862cb6
f3d26e
		if ((ret = (pe_read_coff_header(m->r_coff,&m->m_coff))))
862cb6
			return pe_free_image_meta_impl(
862cb6
				m,PERK_CUSTOM_ERROR(dctx,ret));
862cb6
	}
c0fbae
58ced9
	mark  = (const unsigned char *)base + m->m_coff.cfh_ptr_to_sym_tbl;
58ced9
	mark += m->m_coff.cfh_num_of_syms * sizeof(struct pe_raw_coff_symbol);
67b1e9
58ced9
	if (m->m_coff.cfh_ptr_to_sym_tbl) {
58ced9
		m->m_coff.cfh_ptr_to_str_tbl  = m->m_coff.cfh_ptr_to_sym_tbl;
58ced9
		m->m_coff.cfh_ptr_to_str_tbl += m->m_coff.cfh_num_of_syms * sizeof(struct pe_raw_coff_symbol);
58ced9
		m->m_coff.cfh_size_of_str_tbl = pe_read_long(mark);
b60586
	}
67b1e9
f3d26e
	if (m->r_dos) {
f3d26e
		mark    = &m->r_coff->cfh_signature[0];
f3d26e
		m->r_opt = (union pe_raw_opt_hdr *)(mark + sizeof(*m->r_coff));
c0fbae
f3d26e
		if ((ret = (pe_read_optional_header(m->r_opt,&m->m_opt))))
862cb6
			return pe_free_image_meta_impl(
862cb6
				m,PERK_CUSTOM_ERROR(dctx,ret));
c0fbae
f3d26e
		mark       = &m->r_opt->opt_hdr_32.coh_magic[0];
f3d26e
		m->r_sectbl = (struct pe_raw_sec_hdr *)(mark + m->m_coff.cfh_size_of_opt_hdr);
862cb6
	} else {
f3d26e
		mark       = &m->r_obj->cfh_machine[0];
f3d26e
		m->r_sectbl = (struct pe_raw_sec_hdr *)(mark + sizeof(*m->r_obj));
862cb6
	}
c0fbae
58ced9
	if (!(m->m_sectbl = calloc(m->m_coff.cfh_num_of_sections,sizeof(*(m->m_sectbl)))))
85fd0d
		return pe_free_image_meta_impl(
85fd0d
			m,PERK_SYSTEM_ERROR(dctx));
c0fbae
58ced9
	for (i=0; i<m->m_coff.cfh_num_of_sections; i++) {
f3d26e
		pe_read_section_header(&m->r_sectbl[i],&m->m_sectbl[i]);
c0fbae
58ced9
		if (m->m_sectbl[i].sh_name[0] == '/')
58ced9
			if ((l = strtol(&m->m_sectbl[i].sh_name[1],0,10)) > 0)
58ced9
				if (l < m->m_coff.cfh_size_of_str_tbl)
58ced9
					m->m_sectbl[i].sh_long_name = base + m->m_coff.cfh_ptr_to_str_tbl + l;
67b1e9
	}
67b1e9
c0fbae
	/* .edata */
c0fbae
	i = pe_get_named_section_index(m,".edata");
58ced9
	s = pe_get_block_section_index(m,&m->m_opt.oh_dirs.coh_export_tbl);
c0fbae
c0fbae
	if ((i >= 0) && (i != s))
85fd0d
		return pe_free_image_meta_impl(
85fd0d
			m,PERK_CUSTOM_ERROR(dctx,PERK_ERR_IMAGE_MALFORMED));
c0fbae
ed9e7f
	if (s >= 0) {
25e3c0
		m->h_edata = &m->m_sectbl[s];
f3d26e
		m->r_edata = (struct pe_raw_export_hdr *)(base + m->m_sectbl[s].sh_ptr_to_raw_data
58ced9
				+ m->m_opt.oh_dirs.coh_export_tbl.dh_rva - m->m_sectbl[s].sh_virtual_addr);
ed9e7f
	} else if (i >= 0) {
25e3c0
		m->h_edata = &m->m_sectbl[i];
f3d26e
		m->r_edata = (struct pe_raw_export_hdr *)(base + m->m_sectbl[i].sh_ptr_to_raw_data);
c0fbae
	}
c0fbae
f3d26e
	if (m->r_edata) {
f3d26e
		pe_read_export_header(m->r_edata,&m->m_edata);
58ced9
		m->m_stats.nexpsyms = m->m_edata.eh_num_of_name_ptrs;
ad7d42
	}
c0fbae
c0fbae
	/* .idata */
34ee9e
	struct pe_raw_import_hdr * 	pidata;
34ee9e
	union  pe_raw_import_lookup *	pitem;
c0fbae
c0fbae
	i = pe_get_named_section_index(m,".idata");
58ced9
	s = pe_get_block_section_index(m,&m->m_opt.oh_dirs.coh_import_tbl);
c0fbae
c0fbae
	if ((i >= 0) && (i != s))
85fd0d
		return pe_free_image_meta_impl(
85fd0d
			m,PERK_CUSTOM_ERROR(dctx,PERK_ERR_IMAGE_MALFORMED));
c0fbae
ed9e7f
	if (s >= 0) {
25e3c0
		m->h_idata = &m->m_sectbl[s];
f3d26e
		m->r_idata = (struct pe_raw_import_hdr *)(base + m->m_sectbl[s].sh_ptr_to_raw_data
58ced9
				+ m->m_opt.oh_dirs.coh_import_tbl.dh_rva - m->m_sectbl[s].sh_virtual_addr);
ed9e7f
	} else if (i >= 0) {
25e3c0
		m->h_idata = &m->m_sectbl[i];
f3d26e
		m->r_idata = (struct pe_raw_import_hdr *)(base + m->m_sectbl[i].sh_ptr_to_raw_data);
c0fbae
	}
c0fbae
f3d26e
	if (m->r_idata) {
c0fbae
		/* num of implibs */
f3d26e
		for (pidata=m->r_idata; pe_read_long(pidata->ih_name_rva); pidata++)
58ced9
			m->m_stats.nimplibs++;
c0fbae
c0fbae
		/* import headers */
58ced9
		if (!(m->m_idata = calloc(m->m_stats.nimplibs,sizeof(*m->m_idata))))
85fd0d
			return pe_free_image_meta_impl(
85fd0d
				m,PERK_SYSTEM_ERROR(dctx));
c0fbae
58ced9
		for (i=0; i<m->m_stats.nimplibs; i++) {
f3d26e
			pe_read_import_header(&m->r_idata[i],&m->m_idata[i]);
c0fbae
25e3c0
			m->m_idata[i].ih_name = base + m->h_idata->sh_ptr_to_raw_data
58ced9
						   + m->m_idata[i].ih_name_rva
25e3c0
						   - m->h_idata->sh_virtual_addr;
c0fbae
58ced9
			if (m->m_idata[i].ih_import_lookup_tbl_rva)
25e3c0
				m->m_idata[i].ih_aitems = (union pe_raw_import_lookup *)(base + m->h_idata->sh_ptr_to_raw_data
58ced9
							+ m->m_idata[i].ih_import_lookup_tbl_rva
25e3c0
							- m->h_idata->sh_virtual_addr);
c0fbae
c0fbae
			/* items */
7d801c
			uint32_t * hint;
58ced9
			m->m_idata[i].ih_count = 0;
7d801c
58ced9
			if (m->m_idata[i].ih_import_lookup_tbl_rva) {
58ced9
				pitem = m->m_idata[i].ih_aitems;
f845bd
				hint  = (uint32_t *)pitem->ii_hint_name_tbl_rva;
7d801c
f845bd
				for (; *hint; hint=(uint32_t *)((++pitem)->ii_hint_name_tbl_rva))
58ced9
					m->m_idata[i].ih_count++;
c0fbae
58ced9
				if (!(m->m_idata[i].ih_items = calloc(m->m_idata[i].ih_count,sizeof(*(m->m_idata[i].ih_items)))))
85fd0d
					return pe_free_image_meta_impl(
85fd0d
						m,PERK_SYSTEM_ERROR(dctx));
82a0a1
			}
c0fbae
58ced9
			for (j=0; j<m->m_idata[i].ih_count; j++) {
9770d4
				if ((ret = pe_read_import_lookup(
58ced9
						&(m->m_idata[i].ih_aitems[j]),
58ced9
						&(m->m_idata[i].ih_items[j]),
58ced9
						m->m_opt.oh_std.coh_magic)))
85fd0d
					return pe_free_image_meta_impl(
85fd0d
						m,PERK_CUSTOM_ERROR(dctx,ret));
c0fbae
58ced9
				switch (m->m_opt.oh_std.coh_magic) {
c0fbae
					case PE_MAGIC_PE32:
58ced9
						m->m_idata[i].ih_items[j].ii_flags = m->m_idata[i].ih_items[j].u.ii_import_lookup_entry_32;
c0fbae
						break;
c0fbae
c0fbae
					case PE_MAGIC_PE32_PLUS:
58ced9
						m->m_idata[i].ih_items[j].ii_flags = (m->m_idata[i].ih_items[j].u.ii_import_lookup_entry_64 >> 32);
c0fbae
						break;
c0fbae
				}
c0fbae
58ced9
				if (!m->m_idata[i].ih_items[j].ii_flags) {
34ee9e
					struct pe_raw_hint_name_entry * pentry =
25e3c0
						(struct pe_raw_hint_name_entry *)(base + m->h_idata->sh_ptr_to_raw_data
25e3c0
							+ m->m_idata[i].ih_items[j].u.ii_hint_name_tbl_rva - m->h_idata->sh_virtual_addr);
c0fbae
58ced9
					m->m_idata[i].ih_items[j].ii_name = (char *)pentry->ii_name;
c0fbae
				}
c0fbae
			}
c0fbae
		}
c0fbae
	}
c0fbae
c0fbae
	/* image */
f3d26e
	m->r_image.addr = image->addr;
f3d26e
	m->r_image.size = image->size;
c0fbae
c0fbae
	/* all done */
c0fbae
	*meta = m;
c0fbae
	return 0;
c0fbae
}