radare2/libr/bin/p/bin_mach0.c
Álvaro Felipe Melchor b7cc6c04d7 LDID parse signature in mach0 binaries
* added iC command to show signature info

in the case of mach it shows entitlements

* added rabin2 -OC to get signature info from binary

* change swap to r_read_bleX new API

* change eprintf to r_cons_printf

* add const char in plg->signature
2016-05-09 23:42:28 +02:00

620 lines
15 KiB
C

/* radare - LGPL - Copyright 2009-2016 - pancake */
#include <r_types.h>
#include <r_util.h>
#include <r_lib.h>
#include <r_bin.h>
#include "mach0/mach0.h"
#include "objc/mach0_classes.h"
extern RBinWrite r_bin_write_mach0;
static int check(RBinFile *arch);
static int check_bytes(const ut8 *buf, ut64 length);
static RBinInfo* info(RBinFile *arch);
static Sdb* get_sdb (RBinObject *o) {
if (!o) return NULL;
struct MACH0_(obj_t) *bin = (struct MACH0_(obj_t) *) o->bin_obj;
if (bin && bin->kv) return bin->kv;
return NULL;
}
static char *entitlements(RBinFile *arch) {
struct MACH0_(obj_t) *bin;
if (!arch || !arch->o)
return NULL;
bin = arch->o->bin_obj;
return (char *)bin->signature;
}
static void * load_bytes(RBinFile *arch, const ut8 *buf, ut64 sz, ut64 loadaddr, Sdb *sdb){
struct MACH0_(obj_t) *res = NULL;
RBuffer *tbuf = NULL;
if (!buf || sz == 0 || sz == UT64_MAX) return NULL;
tbuf = r_buf_new();
r_buf_set_bytes (tbuf, buf, sz);
res = MACH0_(new_buf) (tbuf);
if (res) {
sdb_ns_set (sdb, "info", res->kv);
}
r_buf_free (tbuf);
return res;
}
static int load(RBinFile *arch) {
void *res;
const ut8 *bytes = arch ? r_buf_buffer (arch->buf) : NULL;
ut64 sz = arch ? r_buf_size (arch->buf): 0;
if (!arch || !arch->o) return false;
res = load_bytes (arch, bytes, sz, arch->o->loadaddr, arch->sdb);
if (!arch->o || !res) {
MACH0_(mach0_free) (res);
return false;
}
arch->o->bin_obj = res;
struct MACH0_(obj_t) *mo = arch->o->bin_obj;
arch->o->kv = mo->kv; // NOP
sdb_ns_set (arch->sdb, "info", mo->kv);
return true;
}
static int destroy(RBinFile *arch) {
MACH0_(mach0_free) (arch->o->bin_obj);
return true;
}
static ut64 baddr(RBinFile *arch) {
struct MACH0_(obj_t) *bin;
if (!arch || !arch->o || !arch->o->bin_obj)
return 0LL;
bin = arch->o->bin_obj;
return MACH0_(get_baddr)(bin);
}
static RList* entries(RBinFile *arch) {
RList *ret;
RBinAddr *ptr = NULL;
RBinObject *obj = arch ? arch->o : NULL;
struct addr_t *entry = NULL;
if (!obj || !obj->bin_obj || !(ret = r_list_new ()))
return NULL;
ret->free = free;
if (!(entry = MACH0_(get_entrypoint) (obj->bin_obj)))
return ret;
if ((ptr = R_NEW0 (RBinAddr))) {
ptr->paddr = entry->offset + obj->boffset;
ptr->vaddr = entry->addr; //
r_list_append (ret, ptr);
}
free (entry);
return ret;
}
static RList* sections(RBinFile *arch) {
RList *ret = NULL;
RBinSection *ptr = NULL;
struct section_t *sections = NULL;
RBinObject *obj = arch ? arch->o : NULL;
int i;
if (!obj || !obj->bin_obj || !(ret = r_list_new ()))
return NULL;
ret->free = free;
if (!(sections = MACH0_(get_sections) (obj->bin_obj)))
return ret;
for (i = 0; !sections[i].last; i++) {
if (!(ptr = R_NEW0 (RBinSection)))
break;
strncpy (ptr->name, (char*)sections[i].name, R_BIN_SIZEOF_STRINGS);
if (strstr (ptr->name, "la_symbol_ptr")) {
#ifndef R_BIN_MACH064
const int sz = 4;
#else
const int sz = 8;
#endif
int len = sections[i].size / sz;
ptr->format = r_str_newf ("Cd %d[%d]", sz, len);
}
ptr->name[R_BIN_SIZEOF_STRINGS] = 0;
ptr->size = sections[i].size;
ptr->vsize = sections[i].size;
ptr->paddr = sections[i].offset + obj->boffset;
ptr->vaddr = sections[i].addr;
ptr->add = true;
if (ptr->vaddr == 0)
ptr->vaddr = ptr->paddr;
ptr->srwx = sections[i].srwx | R_BIN_SCN_MAP;
r_list_append (ret, ptr);
}
free (sections);
return ret;
}
static RList* symbols(RBinFile *arch) {
struct MACH0_(obj_t) *bin;
int i;
struct symbol_t *symbols = NULL;
RBinSymbol *ptr = NULL;
RBinObject *obj = arch ? arch->o : NULL;
RList *ret = r_list_newf (free);
const char *lang = "c";
int wordsize = 16;
if (!ret)
return NULL;
if (!obj || !obj->bin_obj) {
free (ret);
return NULL;
}
wordsize = MACH0_(get_bits) (obj->bin_obj);
if (!(symbols = MACH0_(get_symbols) (obj->bin_obj))) {
return ret;
}
bin = (struct MACH0_(obj_t) *) obj->bin_obj;
for (i = 0; !symbols[i].last; i++) {
if (!symbols[i].name[0] || symbols[i].addr<100) continue;
if (!(ptr = R_NEW0 (RBinSymbol)))
break;
ptr->name = strdup ((char*)symbols[i].name);
ptr->forwarder = r_str_const ("NONE");
ptr->bind = r_str_const ((symbols[i].type == R_BIN_MACH0_SYMBOL_TYPE_LOCAL)?
"LOCAL":"GLOBAL");
ptr->type = r_str_const ("FUNC");
ptr->vaddr = symbols[i].addr;
ptr->paddr = symbols[i].offset+obj->boffset;
ptr->size = symbols[i].size;
if (wordsize == 16) {
// if thumb, hint non-thumb symbols
if (!(ptr->paddr & 1)) {
ptr->bits = 32;
}
}
ptr->ordinal = i;
bin->dbg_info = strncmp (ptr->name, "radr://", 7)? 0: 1;
if (!strncmp (ptr->name, "type.", 5)) {
lang = "go";
}
r_list_append (ret, ptr);
}
//functions from LC_FUNCTION_STARTS
if (bin->func_start) {
ut64 value = 0, address = 0;
const ut8* temp = bin->func_start;
const ut8* temp_end = bin->func_start + bin->func_size;
while (temp+3 < temp_end && *temp) {
temp = r_uleb128_decode (temp, NULL, &value);
address += value;
ptr = R_NEW0 (RBinSymbol);
if (!ptr) break;
ptr->vaddr = bin->baddr + address;
ptr->paddr = address;
ptr->size = 0;
ptr->name = r_str_newf ("func.%08"PFMT64x, ptr->vaddr);
ptr->type = r_str_const ("FUNC");
ptr->forwarder = r_str_const ("NONE");
ptr->bind = r_str_const ("LOCAL");
ptr->ordinal = i++;
r_list_append (ret, ptr);
}
}
bin->lang = lang;
free (symbols);
return ret;
}
static RList* imports(RBinFile *arch) {
const char *_objc_class = "_OBJC_CLASS_$";
const int _objc_class_len = strlen (_objc_class);
const char *_objc_metaclass = "_OBJC_METACLASS_$";
const int _objc_metaclass_len = strlen (_objc_metaclass);
struct MACH0_(obj_t) *bin = arch ? arch->o->bin_obj : NULL;
struct import_t *imports = NULL;
const char *name, *type;
RBinImport *ptr = NULL;
RList *ret = NULL;
int i;
RBinObject *obj = arch ? arch->o : NULL;
if (!obj || !bin || !obj->bin_obj || !(ret = r_list_newf (free)))
return NULL;
if (!(imports = MACH0_(get_imports) (arch->o->bin_obj)))
return ret;
bin->has_canary = false;
for (i = 0; !imports[i].last; i++) {
if (!(ptr = R_NEW0 (RBinImport)))
break;
name = imports[i].name;
type = "FUNC";
if (!strncmp (name, _objc_class, _objc_class_len)) {
name += _objc_class_len;
type = "OBJC_CLASS";
} else if (!strncmp (name, _objc_metaclass, _objc_metaclass_len)) {
name += _objc_metaclass_len;
type = "OBJC_METACLASS";
}
// Remove the extra underscore that every import seems to have in Mach-O.
if (*name == '_')
name++;
ptr->name = strdup (name);
ptr->bind = r_str_const ("NONE");
ptr->type = r_str_const (type);
ptr->ordinal = imports[i].ord;
if (bin->imports_by_ord && ptr->ordinal < bin->imports_by_ord_size)
bin->imports_by_ord[ptr->ordinal] = ptr;
if (!strcmp (name, "__stack_chk_fail") ) {
bin->has_canary = true;
}
r_list_append (ret, ptr);
}
free (imports);
return ret;
}
static RList* relocs(RBinFile *arch) {
RList *ret = NULL;
RBinReloc *ptr = NULL;
struct reloc_t *relocs = NULL;
struct MACH0_(obj_t) *bin = NULL;
int i;
RBinObject *obj = arch ? arch->o : NULL;
if (arch && arch->o) {
bin = arch->o->bin_obj;
}
if (!obj || !obj->bin_obj || !(ret = r_list_newf (free)))
return NULL;
ret->free = free;
if (!(relocs = MACH0_(get_relocs) (arch->o->bin_obj)))
return ret;
for (i = 0; !relocs[i].last; i++) {
// TODO(eddyb) filter these out earlier.
if (!relocs[i].addr)
continue;
if (!(ptr = R_NEW0 (RBinReloc)))
break;
ptr->type = relocs[i].type;
ptr->additive = 0;
if (bin->imports_by_ord && relocs[i].ord < bin->imports_by_ord_size)
ptr->import = bin->imports_by_ord[relocs[i].ord];
else ptr->import = NULL;
ptr->addend = relocs[i].addend;
ptr->vaddr = relocs[i].addr;
ptr->paddr = relocs[i].offset;
r_list_append (ret, ptr);
}
free (relocs);
return ret;
}
static RList* libs(RBinFile *arch) {
int i;
char *ptr = NULL;
struct lib_t *libs;
RList *ret = NULL;
RBinObject *obj = arch ? arch->o : NULL;
if (!obj || !obj->bin_obj || !(ret = r_list_newf (free)))
return NULL;
if ((libs = MACH0_(get_libs) (obj->bin_obj))) {
for (i = 0; !libs[i].last; i++) {
ptr = strdup (libs[i].name);
r_list_append (ret, ptr);
}
free (libs);
}
return ret;
}
static RBinInfo* info(RBinFile *arch) {
struct MACH0_(obj_t) *bin = NULL;
char *str;
RBinInfo *ret;
if (!arch || !arch->o)
return NULL;
ret = R_NEW0 (RBinInfo);
if (!ret)
return NULL;
bin = arch->o->bin_obj;
if (arch->file)
ret->file = strdup (arch->file);
if ((str = MACH0_(get_class) (arch->o->bin_obj))) {
ret->bclass = str;
}
if (bin) {
ret->has_canary = bin->has_canary;
ret->dbg_info = bin->dbg_info;
ret->lang = bin->lang;
}
ret->intrp = r_str_dup (NULL, MACH0_(get_intrp)(arch->o->bin_obj));
ret->rclass = strdup ("mach0");
ret->os = strdup (MACH0_(get_os)(arch->o->bin_obj));
ret->subsystem = strdup ("darwin");
ret->arch = MACH0_(get_cputype) (arch->o->bin_obj);
ret->machine = MACH0_(get_cpusubtype) (arch->o->bin_obj);
ret->type = MACH0_(get_filetype) (arch->o->bin_obj);
ret->bits = 32;
ret->big_endian = 0;
if (arch && arch->o && arch->o->bin_obj) {
ret->has_crypto = ((struct MACH0_(obj_t)*)
arch->o->bin_obj)->has_crypto;
ret->bits = MACH0_(get_bits) (arch->o->bin_obj);
ret->big_endian = MACH0_(is_big_endian) (arch->o->bin_obj);
}
ret->has_va = true;
ret->has_pi = MACH0_(is_pie) (arch->o->bin_obj);
return ret;
}
#if !R_BIN_MACH064
static int check(RBinFile *arch) {
const ut8 *bytes = arch ? r_buf_buffer (arch->buf) : NULL;
ut64 sz = arch ? r_buf_size (arch->buf): 0;
return check_bytes (bytes, sz);
}
static int check_bytes(const ut8 *buf, ut64 length) {
if (buf && length >= 4) {
if (!memcmp (buf, "\xce\xfa\xed\xfe", 4) ||
!memcmp (buf, "\xfe\xed\xfa\xce", 4))
return true;
}
return false;
}
#if 0
typedef struct r_bin_create_t {
int arch;
ut8 *code;
int clen;
ut8 *data;
int dlen;
} RBinCreate;
#endif
static RBuffer* create(RBin* bin, const ut8 *code, int clen, const ut8 *data, int dlen) {
ut32 filesize, codeva, datava;
ut32 ncmds, cmdsize, magiclen;
ut32 p_codefsz = 0, p_codeva = 0, p_codesz = 0, p_codepa = 0;
ut32 p_datafsz = 0, p_datava = 0, p_datasz = 0, p_datapa = 0;
ut32 p_cmdsize = 0, p_entry = 0, p_tmp = 0;
ut32 baddr = 0x1000;
int is_arm = !strcmp (bin->cur->o->info->arch, "arm");
RBuffer *buf = r_buf_new ();
#ifndef R_BIN_MACH064
if (bin->cur->o->info->bits == 64) {
eprintf ("TODO: Please use mach064 instead of mach0\n");
free (buf);
return NULL;
}
#endif
#define B(x,y) r_buf_append_bytes(buf,(const ut8*)x,y)
#define D(x) r_buf_append_ut32(buf,x)
#define Z(x) r_buf_append_nbytes(buf,x)
#define W(x,y,z) r_buf_write_at(buf,x,(const ut8*)y,z)
#define WZ(x,y) p_tmp=buf->length;Z(x);W(p_tmp,y,strlen(y))
/* MACH0 HEADER */
B ("\xce\xfa\xed\xfe", 4); // header
if (is_arm) {
D (12); // cpu type (arm)
D (3); // subtype (all?)
} else {
/* x86-32 */
D (7); // cpu type (x86)
D (3); // subtype (i386-all)
}
D (2); // filetype (executable)
if (data && dlen>0) {
ncmds = 3;
cmdsize = 0;
} else {
ncmds = 2;
cmdsize = 0;
}
/* COMMANDS */
D (ncmds); // ncmds
p_cmdsize = buf->length;
D (-1); // cmdsize
D (0); // flags
magiclen = buf->length;
/* TEXT SEGMENT */
D (1); // cmd.LC_SEGMENT
D (124); // sizeof (cmd)
WZ (16, "__TEXT");
D (baddr); // vmaddr
D (0x1000); // vmsize XXX
D (0); // fileoff
p_codefsz = buf->length;
D (-1); // filesize
D (7); // maxprot
D (5); // initprot
D (1); // nsects
D (0); // flags
WZ (16, "__text");
WZ (16, "__TEXT");
p_codeva = buf->length; // virtual address
D (-1);
p_codesz = buf->length; // size of code (end-start)
D (-1);
p_codepa = buf->length; // code - baddr
D (-1); //_start-0x1000);
D (2); // align
D (0); // reloff
D (0); // nrelocs
D (0); // flags
D (0); // reserved
D (0);
if (data && dlen>0) {
/* DATA SEGMENT */
D (1); // cmd.LC_SEGMENT
D (124); // sizeof (cmd)
p_tmp = buf->length;
Z (16);
W (p_tmp, "__TEXT", 6); // segment name
D (0x2000); // vmaddr
D (0x1000); // vmsize
D (0); // fileoff
p_datafsz = buf->length;
D (-1); // filesize
D (6); // maxprot
D (6); // initprot
D (1); // nsects
D (0); // flags
WZ (16, "__data");
WZ (16, "__DATA");
p_datava = buf->length;
D (-1);
p_datasz = buf->length;
D (-1);
p_datapa = buf->length;
D (-1); //_start-0x1000);
D (2); // align
D (0); // reloff
D (0); // nrelocs
D (0); // flags
D (0); // reserved
D (0);
}
/* THREAD STATE */
D (5); // LC_UNIXTHREAD
D (80); // sizeof (cmd)
if (is_arm) {
/* arm */
D (1); // i386-thread-state
D (17); // thread-state-count
p_entry = buf->length + (16*sizeof (ut32));
Z (17 * sizeof (ut32));
// mach0-arm has one byte more
} else {
/* x86-32 */
D (1); // i386-thread-state
D (16); // thread-state-count
p_entry = buf->length + (10*sizeof (ut32));
Z (16 * sizeof (ut32));
}
cmdsize = buf->length - magiclen;
codeva = buf->length + baddr;
datava = buf->length + clen + baddr;
W (p_entry, &codeva, 4); // set PC
/* fill header variables */
W (p_cmdsize, &cmdsize, 4);
filesize = magiclen + cmdsize + clen + dlen;
// TEXT SEGMENT //
W (p_codefsz, &filesize, 4);
W (p_codeva, &codeva, 4);
W (p_codesz, &clen, 4);
p_tmp = codeva - baddr;
W (p_codepa, &p_tmp, 4);
B (code, clen);
if (data && dlen>0) {
/* append data */
W (p_datafsz, &filesize, 4);
W (p_datava, &datava, 4);
W (p_datasz, &dlen, 4);
p_tmp = datava - baddr;
W (p_datapa, &p_tmp, 4);
B (data, dlen);
}
return buf;
}
static RBinAddr* binsym(RBinFile *arch, int sym) {
ut64 addr;
RBinAddr *ret = NULL;
switch (sym) {
case R_BIN_SYM_MAIN:
addr = MACH0_(get_main) (arch->o->bin_obj);
if (!addr || !(ret = R_NEW0 (RBinAddr)))
return NULL;
//if (arch->o->info && arch->o->info->bits == 16) {
// align for thumb
ret->vaddr = ((addr >>1)<<1);
//}
ret->paddr = ret->vaddr;
break;
}
return ret;
}
static int size(RBinFile *arch) {
ut64 off = 0;
ut64 len = 0;
if (!arch->o->sections) {
RListIter *iter;
RBinSection *section;
arch->o->sections = sections (arch);
r_list_foreach (arch->o->sections, iter, section) {
if (section->paddr > off) {
off = section->paddr;
len = section->size;
}
}
}
return off+len;
}
RBinPlugin r_bin_plugin_mach0 = {
.name = "mach0",
.desc = "mach0 bin plugin",
.license = "LGPL3",
.get_sdb = &get_sdb,
.load = &load,
.load_bytes = &load_bytes,
.destroy = &destroy,
.check = &check,
.check_bytes = &check_bytes,
.baddr = &baddr,
.binsym = &binsym,
.entries = &entries,
.signature = &entitlements,
.sections = &sections,
.symbols = &symbols,
.imports = &imports,
.size = &size,
.info = &info,
.libs = &libs,
.relocs = &relocs,
.create = &create,
.classes = &MACH0_(parse_classes),
.write = &r_bin_write_mach0,
};
#ifndef CORELIB
struct r_lib_struct_t radare_plugin = {
.type = R_LIB_TYPE_BIN,
.data = &r_bin_plugin_mach0,
.version = R2_VERSION
};
#endif
#endif