mirror of
https://github.com/radareorg/radare2.git
synced 2025-01-15 10:08:11 +00:00
296 lines
8.6 KiB
C
296 lines
8.6 KiB
C
/* radare - LGPL - Copyright 2015-2016 nodepad, pancake */
|
|
|
|
#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) {
|
|
#if 0
|
|
ut16 cs = r_read_ble16 (buf + 0x16, false);
|
|
ut16 ip = r_read_ble16 (buf + 0x14, false);
|
|
ut16 pa = ((r_read_ble16 (buf + 8 , false) + cs) << 4) + ip;
|
|
#endif
|
|
/* Value of CS in DOS header may be negative */
|
|
const short cs = bin->dos_header->cs;
|
|
ut32 pa = bin->dos_header->header_paragraphs + cs;
|
|
const ut32 paddr = (pa<<4) + bin->dos_header->ip;
|
|
if (paddr < bin->dos_file_size) {
|
|
return paddr;
|
|
}
|
|
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 || !mb) {
|
|
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 && msegs && *msegs) {
|
|
**msegs = *mseg;
|
|
*msegs = *msegs + 1;
|
|
}
|
|
}
|
|
|
|
struct r_bin_mz_segment_t * r_bin_mz_get_segments(const struct r_bin_mz_obj_t *bin) {
|
|
#if 0
|
|
int i;
|
|
struct r_bin_mz_segment_t *ret;
|
|
|
|
const MZ_image_relocation_entry * const relocs = bin->relocation_entries;
|
|
const int num_relocs = bin->dos_header->num_relocs;
|
|
|
|
eprintf ("cs 0x%x\n", bin->dos_header->cs);
|
|
eprintf ("ss 0x%x\n", bin->dos_header->ss);
|
|
for (i = 0; i < num_relocs; i++) {
|
|
eprintf ("0x%08x segment 0x%08lx\n", relocs[i].offset, relocs[i].segment);
|
|
// ut65 paddr = r_bin_mz_seg_to_paddr (bin, relocs[i].segment) + relocs[i].offset;
|
|
// eprintf ("pa 0x%08llx\n", paddr);
|
|
}
|
|
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);
|
|
}
|
|
return NULL;
|
|
#endif
|
|
#if 1
|
|
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) {
|
|
btree_cleartree (tree, NULL);
|
|
return NULL;
|
|
}
|
|
segments = calloc (1 + num_relocs, sizeof (*segments));
|
|
if (!segments) {
|
|
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) {
|
|
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;
|
|
#endif
|
|
}
|
|
|
|
struct r_bin_mz_reloc_t *r_bin_mz_get_relocs(const struct r_bin_mz_obj_t *bin) {
|
|
int i, j;
|
|
struct r_bin_mz_reloc_t *relocs;
|
|
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) {
|
|
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 = R_NEW0 (MZ_image_dos_header))) {
|
|
r_sys_perror ("malloc (MZ_image_dos_header)");
|
|
return false;
|
|
}
|
|
// TODO: read field by field to avoid endian and alignment issues
|
|
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);
|
|
if ((bin->dos_header->reloc_table_offset + relocations_size) > bin->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");
|
|
R_FREE (bin->relocation_entries);
|
|
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);
|
|
}
|
|
return r_bin_mz_init (bin) ? bin : r_bin_mz_free (bin);
|
|
}
|