rizin/libr/bin/format/mz/mz.c
2015-09-14 02:08:31 +02:00

270 lines
7.7 KiB
C

/* radare - LGPL - Copyright 2015 nodepad */
#include "mz.h"
#include <btree.h>
static ut64 r_bin_mz_seg_to_paddr (const struct r_bin_mz_obj_t *bin,
const ut16 segment)
{
return (bin->dos_header->header_paragraphs + segment) << 4;
}
int r_bin_mz_get_entrypoint (const struct r_bin_mz_obj_t *bin)
{
/* Value of CS in DOS header may be negative */
const short cs = (const short)bin->dos_header->cs;
const int paddr = ((bin->dos_header->header_paragraphs + cs) << 4) + \
bin->dos_header->ip;
if (paddr >= 0 && paddr < bin->dos_file_size) return paddr;
else return -1;
}
int cmp_segs (const void *a, const void *b) {
const ut16 * const ma = (const ut16 * const)a;
const ut16 * const mb = (const ut16 * const)b;
if (ma == NULL || mb == NULL) return 0;
return (int)(*ma-*mb);
}
void trv_segs (const void *seg, const void *segs)
{
const ut16 * const mseg = (const ut16 * const)seg;
ut16 ** const msegs = (ut16 **)segs;
if (mseg != NULL && msegs != NULL && *msegs != NULL) {
**msegs = *mseg;
*msegs = *msegs + 1;
}
}
struct r_bin_mz_segment_t * r_bin_mz_get_segments (
const struct r_bin_mz_obj_t *bin) {
struct btree_node *tree;
struct r_bin_mz_segment_t *ret;
ut16 *segments, *curr_seg;
int i, num_segs;
ut64 paddr;
const ut16 first_segment = 0;
const ut16 stack_segment = bin->dos_header->ss;
const MZ_image_relocation_entry * const relocs = bin->relocation_entries;
const int num_relocs = bin->dos_header->num_relocs;
const ut64 last_parag = ((bin->dos_file_size + 0xF) >> 4) - \
bin->dos_header->header_paragraphs;
btree_init (&tree);
for (i = 0; i < num_relocs; i++) {
paddr = r_bin_mz_seg_to_paddr (bin, relocs[i].segment) + \
relocs[i].offset;
if ((paddr + 2) < bin->dos_file_size) {
curr_seg = (ut16 *) (bin->b->buf + paddr);
/* Add segment only if it's located inside dos executable data */
if (*curr_seg <= last_parag)
btree_add (&tree, curr_seg, cmp_segs);
}
}
/* Add segment address of first segment to make sure that it will be
added. If relocations empty or there isn't first segment in relocations.)
*/
btree_add (&tree, (void *)&first_segment, cmp_segs);
/* Add segment address of stack segment if it's resides inside dos
executable.
*/
if (r_bin_mz_seg_to_paddr (bin, stack_segment) < bin->dos_file_size) {
btree_add (&tree, (void *)&stack_segment, cmp_segs);
}
if (num_relocs == 0) {
btree_cleartree (tree, NULL);
return NULL;
}
segments = calloc (num_relocs, sizeof(*segments));
if (segments == NULL) {
eprintf ("Error: calloc (segments)\n");
btree_cleartree (tree, NULL);
return NULL;
}
curr_seg = segments;
btree_traverse (tree, 0, &curr_seg, trv_segs);
num_segs = curr_seg - segments;
ret = calloc (num_segs + 1, sizeof(struct r_bin_mz_segment_t));
if (ret == NULL) {
free (segments);
btree_cleartree (tree, NULL);
eprintf ("Error: calloc (struct r_bin_mz_segment_t)\n");
return NULL;
}
btree_cleartree (tree, NULL);
ret[0].paddr = r_bin_mz_seg_to_paddr (bin, segments[0]);
for (i = 1; i < num_segs; i++) {
ret[i].paddr = r_bin_mz_seg_to_paddr (bin, segments[i]);
ret[i - 1].size = ret[i].paddr - ret[i - 1].paddr;
}
ret[i - 1].size = bin->dos_file_size - ret[i - 1].paddr;
ret[i].last = 1;
free (segments);
return ret;
}
struct r_bin_mz_reloc_t *r_bin_mz_get_relocs (const struct r_bin_mz_obj_t *bin)
{
struct r_bin_mz_reloc_t *relocs;
int i, j;
const int num_relocs = bin->dos_header->num_relocs;
const MZ_image_relocation_entry * const rel_entry = \
bin->relocation_entries;
relocs = calloc (num_relocs + 1, sizeof(*relocs));
if (relocs == NULL) {
eprintf ("Error: calloc (struct r_bin_mz_reloc_t)\n");
return NULL;
}
for (i = 0, j = 0; i < num_relocs; i++) {
relocs[j].paddr = r_bin_mz_seg_to_paddr (bin,
rel_entry[i].segment) + rel_entry[i].offset;
/* Add only relocations which resides inside dos executable */
if (relocs[j].paddr < bin->dos_file_size) j++;
}
relocs[j].last = 1;
return relocs;
}
void *r_bin_mz_free (struct r_bin_mz_obj_t* bin) {
if (!bin) return NULL;
free ((void *)bin->dos_header);
free ((void *)bin->dos_extended_header);
free ((void *)bin->relocation_entries);
r_buf_free (bin->b);
bin->b = NULL;
free (bin);
return NULL;
}
static int r_bin_mz_init_hdr (struct r_bin_mz_obj_t* bin) {
int relocations_size, dos_file_size;
if (!(bin->dos_header = malloc (sizeof(MZ_image_dos_header)))) {
r_sys_perror ("malloc (MZ_image_dos_header)");
return false;
}
if (r_buf_read_at (bin->b, 0, (ut8*)bin->dos_header,
sizeof(*bin->dos_header)) == -1) {
eprintf ("Error: read (MZ_image_dos_header)\n");
return false;
}
if (bin->dos_header->blocks_in_file < 1) return false;
dos_file_size = ((bin->dos_header->blocks_in_file - 1) << 9) + \
bin->dos_header->bytes_in_last_block;
bin->dos_file_size = dos_file_size;
if (dos_file_size > bin->size) return false;
relocations_size = bin->dos_header->num_relocs * \
sizeof(MZ_image_relocation_entry);
/* Check if relocation table doesn't exceed dos binary size */
if ((bin->dos_header->reloc_table_offset + relocations_size) > \
dos_file_size)
return false;
sdb_num_set (bin->kv, "mz.initial.cs", bin->dos_header->cs, 0);
sdb_num_set (bin->kv, "mz.initial.ip", bin->dos_header->ip, 0);
sdb_num_set (bin->kv, "mz.initial.ss", bin->dos_header->ss, 0);
sdb_num_set (bin->kv, "mz.initial.sp", bin->dos_header->sp, 0);
sdb_num_set (bin->kv, "mz.overlay_number",
bin->dos_header->overlay_number, 0);
sdb_num_set (bin->kv, "mz.dos_header.offset", 0, 0);
sdb_set (bin->kv, "mz.dos_header.format", "[2]zwwwwwwwwwwwww"
" signature bytes_in_last_block blocks_in_file num_relocs "
" header_paragraphs min_extra_paragraphs max_extra_paragraphs "
" ss sp checksum ip cs reloc_table_offset overlay_number ", 0);
bin->dos_extended_header_size = bin->dos_header->reloc_table_offset - \
sizeof(MZ_image_dos_header);
if (bin->dos_extended_header_size > 0)
{
if (!(bin->dos_extended_header = \
malloc (bin->dos_extended_header_size))) {
r_sys_perror ("malloc (dos extended header)");
return false;
}
if (r_buf_read_at (bin->b, sizeof(MZ_image_dos_header),
(ut8*)bin->dos_extended_header,
bin->dos_extended_header_size) == -1) {
eprintf ("Error: read (dos extended header)\n");
return false;
}
}
if (relocations_size > 0)
{
if (!(bin->relocation_entries = malloc (relocations_size))) {
r_sys_perror ("malloc (dos relocation entries)");
return false;
}
if (r_buf_read_at (bin->b, bin->dos_header->reloc_table_offset,
(ut8*)bin->relocation_entries, relocations_size) == -1) {
eprintf ("Error: read (dos relocation entries)\n");
return false;
}
}
return true;
}
static int r_bin_mz_init (struct r_bin_mz_obj_t* bin) {
bin->dos_header = NULL;
bin->dos_extended_header = NULL;
bin->relocation_entries = NULL;
bin->kv = sdb_new0 ();
if (!r_bin_mz_init_hdr (bin)) {
eprintf ("Warning: File is not MZ\n");
return false;
}
return true;
}
struct r_bin_mz_obj_t* r_bin_mz_new (const char* file)
{
const ut8 *buf;
struct r_bin_mz_obj_t *bin = R_NEW0 (struct r_bin_mz_obj_t);
if (!bin) return NULL;
bin->file = file;
if (!(buf = (ut8*)r_file_slurp (file, &bin->size)))
return r_bin_mz_free (bin);
bin->b = r_buf_new ();
if (!r_buf_set_bytes (bin->b, buf, bin->size)) {
free ((void *)buf);
return r_bin_mz_free (bin);
}
free ((void *)buf);
if (!r_bin_mz_init (bin)) return r_bin_mz_free (bin);
return bin;
}
struct r_bin_mz_obj_t* r_bin_mz_new_buf(const struct r_buf_t *buf)
{
struct r_bin_mz_obj_t *bin = R_NEW0 (struct r_bin_mz_obj_t);
if (!bin) return NULL;
bin->b = r_buf_new ();
bin->size = buf->length;
if (!r_buf_set_bytes (bin->b, buf->buf, bin->size)){
return r_bin_mz_free (bin);
}
if (!r_bin_mz_init (bin)) return r_bin_mz_free (bin);
return bin;
}