270 lines
7.7 KiB
C
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;
|
|
}
|