mirror of
https://github.com/radareorg/radare2.git
synced 2024-12-28 00:16:55 +00:00
330 lines
8.4 KiB
C
330 lines
8.4 KiB
C
/* radare - LGPL - Copyright 2016-2017 - Oscar Salvador */
|
|
|
|
#include <r_types.h>
|
|
#include <r_util.h>
|
|
#include <r_lib.h>
|
|
#include <r_bin.h>
|
|
#include <r_io.h>
|
|
#include "bflt/bflt.h"
|
|
|
|
static void *load_bytes(RBinFile *arch, const ut8 *buf, ut64 sz, ut64 loaddr, Sdb *sdb) {
|
|
if (!buf || !sz || sz == UT64_MAX) {
|
|
return NULL;
|
|
}
|
|
RBuffer *tbuf = r_buf_new ();
|
|
if (!tbuf) {
|
|
return NULL;
|
|
}
|
|
r_buf_set_bytes (tbuf, buf, sz);
|
|
struct r_bin_bflt_obj *res = r_bin_bflt_new_buf (tbuf);
|
|
r_buf_free (tbuf);
|
|
return res? res: NULL;
|
|
}
|
|
|
|
static bool load(RBinFile *arch) {
|
|
const ut8 *bytes = r_buf_buffer (arch->buf);
|
|
ut64 sz = r_buf_size (arch->buf);
|
|
arch->o->bin_obj = load_bytes (arch, bytes, sz, arch->o->loadaddr, arch->sdb);
|
|
return arch->o->bin_obj? true: false;
|
|
}
|
|
|
|
static RList *entries(RBinFile *arch) {
|
|
struct r_bin_bflt_obj *obj = (struct r_bin_bflt_obj *) arch->o->bin_obj;
|
|
RList *ret;
|
|
RBinAddr *ptr;
|
|
|
|
if (!(ret = r_list_newf (free))) {
|
|
return NULL;
|
|
}
|
|
ptr = r_bflt_get_entry (obj);
|
|
if (!ptr) {
|
|
return NULL;
|
|
}
|
|
r_list_append (ret, ptr);
|
|
return ret;
|
|
}
|
|
|
|
static void __patch_reloc(RBuffer *buf, ut32 addr_to_patch, ut32 data_offset) {
|
|
ut8 val[4] = {
|
|
0
|
|
};
|
|
r_write_le32 (val, data_offset);
|
|
r_buf_write_at (buf, addr_to_patch, (void *) val, sizeof (val));
|
|
}
|
|
|
|
static int search_old_relocation(struct reloc_struct_t *reloc_table,
|
|
ut32 addr_to_patch, int n_reloc) {
|
|
int i;
|
|
for (i = 0; i < n_reloc; i++) {
|
|
if (addr_to_patch == reloc_table[i].data_offset) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static RList *patch_relocs(RBin *b) {
|
|
struct r_bin_bflt_obj *bin = NULL;
|
|
RList *list = NULL;
|
|
RBinObject *obj;
|
|
int i = 0;
|
|
if (!b || !b->iob.io || !b->iob.io->desc) {
|
|
return NULL;
|
|
}
|
|
if (!b->iob.io->cached) {
|
|
eprintf (
|
|
"Warning: please run r2 with -e io.cache=true to patch "
|
|
"relocations\n");
|
|
return list;
|
|
}
|
|
|
|
obj = r_bin_cur_object (b);
|
|
if (!obj) {
|
|
return NULL;
|
|
}
|
|
bin = obj->bin_obj;
|
|
list = r_list_newf ((RListFree) free);
|
|
if (!list) {
|
|
return NULL;
|
|
}
|
|
if (bin->got_table) {
|
|
struct reloc_struct_t *got_table = bin->got_table;
|
|
for (i = 0; i < bin->n_got; i++) {
|
|
__patch_reloc (bin->b, got_table[i].addr_to_patch,
|
|
got_table[i].data_offset);
|
|
RBinReloc *reloc = R_NEW0 (RBinReloc);
|
|
if (reloc) {
|
|
reloc->type = R_BIN_RELOC_32;
|
|
reloc->paddr = got_table[i].addr_to_patch;
|
|
reloc->vaddr = reloc->paddr;
|
|
r_list_append (list, reloc);
|
|
}
|
|
}
|
|
R_FREE (bin->got_table);
|
|
}
|
|
|
|
if (bin->reloc_table) {
|
|
struct reloc_struct_t *reloc_table = bin->reloc_table;
|
|
for (i = 0; i < bin->hdr->reloc_count; i++) {
|
|
int found = search_old_relocation (reloc_table,
|
|
reloc_table[i].addr_to_patch,
|
|
bin->hdr->reloc_count);
|
|
if (found != -1) {
|
|
__patch_reloc (bin->b, reloc_table[found].addr_to_patch,
|
|
reloc_table[i].data_offset);
|
|
} else {
|
|
__patch_reloc (bin->b, reloc_table[i].addr_to_patch,
|
|
reloc_table[i].data_offset);
|
|
}
|
|
RBinReloc *reloc = R_NEW0 (RBinReloc);
|
|
if (reloc) {
|
|
reloc->type = R_BIN_RELOC_32;
|
|
reloc->paddr = reloc_table[i].addr_to_patch;
|
|
reloc->vaddr = reloc->paddr;
|
|
r_list_append (list, reloc);
|
|
}
|
|
}
|
|
R_FREE (bin->reloc_table);
|
|
}
|
|
b->iob.write_at (b->iob.io, bin->b->base, bin->b->buf, bin->b->length);
|
|
return list;
|
|
}
|
|
|
|
static int get_ngot_entries(struct r_bin_bflt_obj *obj) {
|
|
ut32 data_size = obj->hdr->data_end - obj->hdr->data_start;
|
|
int i = 0, n_got = 0;
|
|
if (data_size > obj->size) {
|
|
return 0;
|
|
}
|
|
for (i = 0, n_got = 0; i < data_size; i += 4, n_got++) {
|
|
ut32 entry, offset = obj->hdr->data_start;
|
|
if (offset + i + sizeof (ut32) > obj->size ||
|
|
offset + i + sizeof (ut32) < offset) {
|
|
return 0;
|
|
}
|
|
int len = r_buf_read_at (obj->b, offset + i, (ut8 *) &entry,
|
|
sizeof (ut32));
|
|
if (len != sizeof (ut32)) {
|
|
return 0;
|
|
}
|
|
if (!VALID_GOT_ENTRY (entry)) {
|
|
break;
|
|
}
|
|
}
|
|
return n_got;
|
|
}
|
|
|
|
static RList *relocs(RBinFile *arch) {
|
|
struct r_bin_bflt_obj *obj = (struct r_bin_bflt_obj *) arch->o->bin_obj;
|
|
RList *list = r_list_newf ((RListFree) free);
|
|
int i, len, n_got, amount;
|
|
if (!list || !obj) {
|
|
r_list_free (list);
|
|
return NULL;
|
|
}
|
|
if (obj->hdr->flags & FLAT_FLAG_GOTPIC) {
|
|
n_got = get_ngot_entries (obj);
|
|
if (n_got) {
|
|
amount = n_got * sizeof (ut32);
|
|
if (amount < n_got || amount > UT32_MAX) {
|
|
goto out_error;
|
|
}
|
|
struct reloc_struct_t *got_table = calloc (
|
|
1, n_got * sizeof (struct reloc_struct_t));
|
|
if (got_table) {
|
|
ut32 offset = 0;
|
|
for (i = 0; i < n_got; offset += 4, i++) {
|
|
ut32 got_entry;
|
|
if (obj->hdr->data_start + offset + 4 > obj->size ||
|
|
obj->hdr->data_start + offset + 4 < offset) {
|
|
break;
|
|
}
|
|
len = r_buf_read_at (obj->b, obj->hdr->data_start + offset,
|
|
(ut8 *) &got_entry, sizeof (ut32));
|
|
if (!VALID_GOT_ENTRY (got_entry) || len != sizeof (ut32)) {
|
|
break;
|
|
}
|
|
got_table[i].addr_to_patch = got_entry;
|
|
got_table[i].data_offset = got_entry + BFLT_HDR_SIZE;
|
|
}
|
|
obj->n_got = n_got;
|
|
obj->got_table = got_table;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj->hdr->reloc_count > 0) {
|
|
int n_reloc = obj->hdr->reloc_count;
|
|
|
|
amount = n_reloc * sizeof (struct reloc_struct_t);
|
|
if (amount < n_reloc || amount > UT32_MAX) {
|
|
goto out_error;
|
|
}
|
|
struct reloc_struct_t *reloc_table = calloc (1, amount + 1);
|
|
if (!reloc_table) {
|
|
goto out_error;
|
|
}
|
|
amount = n_reloc * sizeof (ut32);
|
|
if (amount < n_reloc || amount > UT32_MAX) {
|
|
free (reloc_table);
|
|
goto out_error;
|
|
}
|
|
ut32 *reloc_pointer_table = calloc (1, amount + 1);
|
|
if (!reloc_pointer_table) {
|
|
free (reloc_table);
|
|
goto out_error;
|
|
}
|
|
if (obj->hdr->reloc_start + amount > obj->size ||
|
|
obj->hdr->reloc_start + amount < amount) {
|
|
free (reloc_table);
|
|
free (reloc_pointer_table);
|
|
goto out_error;
|
|
}
|
|
len = r_buf_read_at (obj->b, obj->hdr->reloc_start,
|
|
(ut8 *) reloc_pointer_table, amount);
|
|
if (len != amount) {
|
|
free (reloc_table);
|
|
free (reloc_pointer_table);
|
|
goto out_error;
|
|
}
|
|
for (i = 0; i < n_reloc; i++) {
|
|
// XXX it doesn't take endian as consideration when swapping
|
|
ut32 reloc_offset =
|
|
r_swap_ut32 (reloc_pointer_table[i]) +
|
|
BFLT_HDR_SIZE;
|
|
|
|
if (reloc_offset < obj->hdr->bss_end && reloc_offset < obj->size) {
|
|
ut32 reloc_fixed, reloc_data_offset;
|
|
if (reloc_offset + sizeof (ut32) > obj->size ||
|
|
reloc_offset + sizeof (ut32) < reloc_offset) {
|
|
free (reloc_table);
|
|
free (reloc_pointer_table);
|
|
goto out_error;
|
|
}
|
|
len = r_buf_read_at (obj->b, reloc_offset,
|
|
(ut8 *) &reloc_fixed,
|
|
sizeof (ut32));
|
|
if (len != sizeof (ut32)) {
|
|
eprintf ("problem while reading relocation entries\n");
|
|
free (reloc_table);
|
|
free (reloc_pointer_table);
|
|
goto out_error;
|
|
}
|
|
reloc_data_offset = r_swap_ut32 (reloc_fixed) + BFLT_HDR_SIZE;
|
|
reloc_table[i].addr_to_patch = reloc_offset;
|
|
reloc_table[i].data_offset = reloc_data_offset;
|
|
|
|
RBinReloc *reloc = R_NEW0 (RBinReloc);
|
|
if (reloc) {
|
|
reloc->type = R_BIN_RELOC_32;
|
|
reloc->paddr = reloc_table[i].addr_to_patch;
|
|
reloc->vaddr = reloc->paddr;
|
|
r_list_append (list, reloc);
|
|
}
|
|
}
|
|
}
|
|
free (reloc_pointer_table);
|
|
obj->reloc_table = reloc_table;
|
|
}
|
|
return list;
|
|
out_error:
|
|
r_list_free (list);
|
|
return NULL;
|
|
}
|
|
|
|
static RBinInfo *info(RBinFile *arch) {
|
|
struct r_bin_bflt_obj *obj = NULL;
|
|
RBinInfo *info = NULL;
|
|
if (!arch || !arch->o || !arch->o->bin_obj) {
|
|
return NULL;
|
|
}
|
|
obj = (struct r_bin_bflt_obj *) arch->o->bin_obj;
|
|
if (!(info = R_NEW0 (RBinInfo))) {
|
|
return NULL;
|
|
}
|
|
info->file = arch->file? strdup (arch->file): NULL;
|
|
info->rclass = strdup ("bflt");
|
|
info->bclass = strdup ("bflt" );
|
|
info->type = strdup ("bFLT (Executable file)");
|
|
info->os = strdup ("Linux");
|
|
info->subsystem = strdup ("Linux");
|
|
info->arch = strdup ("arm");
|
|
info->big_endian = obj->endian;
|
|
info->bits = 32;
|
|
info->has_va = false;
|
|
info->dbg_info = 0;
|
|
info->machine = strdup ("unknown");
|
|
return info;
|
|
}
|
|
|
|
static bool check_bytes(const ut8 *buf, ut64 length) {
|
|
return length > 4 && !memcmp (buf, "bFLT", 4);
|
|
}
|
|
|
|
static int destroy(RBinFile *arch) {
|
|
r_bin_bflt_free ((struct r_bin_bflt_obj *) arch->o->bin_obj);
|
|
return true;
|
|
}
|
|
|
|
RBinPlugin r_bin_plugin_bflt = {
|
|
.name = "bflt",
|
|
.desc = "bFLT format r_bin plugin",
|
|
.license = "LGPL3",
|
|
.load = &load,
|
|
.load_bytes = &load_bytes,
|
|
.destroy = &destroy,
|
|
.check_bytes = &check_bytes,
|
|
.entries = &entries,
|
|
.info = &info,
|
|
.relocs = &relocs,
|
|
.patch_relocs = &patch_relocs,
|
|
};
|
|
|
|
#ifndef CORELIB
|
|
RLibStruct radare_plugin = {
|
|
.type = R_LIB_TYPE_BIN,
|
|
.data = &r_bin_plugin_bflt,
|
|
.version = R2_VERSION
|
|
};
|
|
#endif
|