mirror of
https://github.com/radareorg/radare2.git
synced 2024-12-01 09:00:46 +00:00
3006 lines
70 KiB
C
3006 lines
70 KiB
C
/* radare - LGPL - Copyright 2009-2021 - pancake, nibble */
|
|
|
|
#include <r_anal.h>
|
|
#include <r_sign.h>
|
|
#include <r_search.h>
|
|
#include <r_core.h>
|
|
|
|
R_LIB_VERSION (r_sign);
|
|
|
|
#define SIGN_DIFF_MATCH_BYTES_THRESHOLD 1.0
|
|
#define SIGN_DIFF_MATCH_GRAPH_THRESHOLD 1.0
|
|
|
|
const char *getRealRef(RCore *core, ut64 off) {
|
|
RFlagItem *item;
|
|
RListIter *iter;
|
|
|
|
const RList *list = r_flag_get_list (core->flags, off);
|
|
if (!list) {
|
|
return NULL;
|
|
}
|
|
|
|
r_list_foreach (list, iter, item) {
|
|
if (!item->name) {
|
|
continue;
|
|
}
|
|
if (strncmp (item->name, "sym.", 4)) {
|
|
continue;
|
|
}
|
|
return item->name;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int list_str_cmp (const void *a, const void *b) {
|
|
// prevent silent failure if RListComparator changes
|
|
return strcmp ((const char *)a, (const char *)b);
|
|
}
|
|
|
|
R_API RList *r_sign_fcn_vars(RAnal *a, RAnalFunction *fcn) {
|
|
r_return_val_if_fail (a && fcn, NULL);
|
|
|
|
RListIter *iter;
|
|
RAnalVar *var;
|
|
RList *ret = r_list_newf ((RListFree) free);
|
|
if (!ret) {
|
|
return NULL;
|
|
}
|
|
RList *reg_vars = r_anal_var_list (a, fcn, R_ANAL_VAR_KIND_REG);
|
|
RList *spv_vars = r_anal_var_list (a, fcn, R_ANAL_VAR_KIND_SPV);
|
|
RList *bpv_vars = r_anal_var_list (a, fcn, R_ANAL_VAR_KIND_BPV);
|
|
r_list_foreach (bpv_vars, iter, var) {
|
|
r_list_append (ret, r_str_newf ("b%d", var->delta));
|
|
}
|
|
r_list_foreach (spv_vars, iter, var) {
|
|
r_list_append (ret, r_str_newf ("s%d", var->delta));
|
|
}
|
|
r_list_foreach (reg_vars, iter, var) {
|
|
r_list_append (ret, r_str_newf ("r%d", var->delta));
|
|
}
|
|
r_list_free (reg_vars);
|
|
r_list_free (bpv_vars);
|
|
r_list_free (spv_vars);
|
|
return ret;
|
|
}
|
|
|
|
R_API RList *r_sign_fcn_types(RAnal *a, RAnalFunction *fcn) {
|
|
|
|
// From anal/types/*:
|
|
// Get key-value types from sdb matching "func.%s", fcn->name
|
|
// Get func.%s.args (number of args)
|
|
// Get type,name pairs
|
|
// Put everything in RList following the next format:
|
|
// types: main.ret=%type%, main.args=%num%, main.arg.0="int,argc", ...
|
|
|
|
r_return_val_if_fail (a && fcn, NULL);
|
|
|
|
RList *ret = r_list_newf ((RListFree) free);
|
|
if (!ret) {
|
|
return NULL;
|
|
}
|
|
|
|
char *scratch = r_str_newf ("func.%s.args", fcn->name);
|
|
if (!scratch) {
|
|
return NULL;
|
|
}
|
|
const char *fcntypes = sdb_const_get (a->sdb_types, scratch, 0);
|
|
free (scratch);
|
|
|
|
scratch = r_str_newf ("func.%s.ret", fcn->name);
|
|
if (!scratch) {
|
|
return NULL;
|
|
}
|
|
const char *ret_type = sdb_const_get (a->sdb_types, scratch, 0);
|
|
free (scratch);
|
|
|
|
if (fcntypes) {
|
|
if (ret_type) {
|
|
r_list_append (ret, r_str_newf ("func.%s.ret=%s", fcn->name, ret_type));
|
|
}
|
|
int argc = atoi (fcntypes);
|
|
r_list_append (ret, r_str_newf ("func.%s.args=%d", fcn->name, argc));
|
|
int i;
|
|
for (i = 0; i < argc; i++) {
|
|
char *k = r_str_newf ("func.%s.arg.%d", fcn->name, i);
|
|
if (k) {
|
|
const char *arg = sdb_const_get (a->sdb_types, k, 0);
|
|
r_list_append (ret, r_str_newf ("func.%s.arg.%d=\"%s\"", fcn->name, i, arg));
|
|
free (k);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
R_API RList *r_sign_fcn_xrefs(RAnal *a, RAnalFunction *fcn) {
|
|
RListIter *iter = NULL;
|
|
RAnalRef *refi = NULL;
|
|
|
|
r_return_val_if_fail (a && fcn, NULL);
|
|
|
|
RCore *core = a->coreb.core;
|
|
|
|
if (!core) {
|
|
return NULL;
|
|
}
|
|
|
|
RList *ret = r_list_newf ((RListFree) free);
|
|
RList *xrefs = r_anal_function_get_xrefs (fcn);
|
|
r_list_foreach (xrefs, iter, refi) {
|
|
if (refi->type == R_ANAL_REF_TYPE_CODE || refi->type == R_ANAL_REF_TYPE_CALL) {
|
|
const char *flag = getRealRef (core, refi->addr);
|
|
if (flag) {
|
|
r_list_append (ret, r_str_new (flag));
|
|
}
|
|
}
|
|
}
|
|
r_list_free (xrefs);
|
|
return ret;
|
|
}
|
|
|
|
R_API RList *r_sign_fcn_refs(RAnal *a, RAnalFunction *fcn) {
|
|
RListIter *iter = NULL;
|
|
RAnalRef *refi = NULL;
|
|
|
|
r_return_val_if_fail (a && fcn, NULL);
|
|
|
|
RCore *core = a->coreb.core;
|
|
|
|
if (!core) {
|
|
return NULL;
|
|
}
|
|
|
|
RList *ret = r_list_newf ((RListFree) free);
|
|
RList *refs = r_anal_function_get_refs (fcn);
|
|
r_list_foreach (refs, iter, refi) {
|
|
if (refi->type == R_ANAL_REF_TYPE_CODE || refi->type == R_ANAL_REF_TYPE_CALL) {
|
|
const char *flag = getRealRef (core, refi->addr);
|
|
if (flag) {
|
|
r_list_append (ret, r_str_new (flag));
|
|
}
|
|
}
|
|
}
|
|
r_list_free (refs);
|
|
return ret;
|
|
}
|
|
|
|
static RList *zign_types_to_list(RAnal *a, const char *types) {
|
|
RList *ret = r_list_newf ((RListFree)free);
|
|
if (!ret) {
|
|
return NULL;
|
|
}
|
|
|
|
unsigned int i = 0, prev = 0, len = strlen (types);
|
|
bool quoted = false;
|
|
char *token = NULL;
|
|
for (i = 0; i <= len; i++) {
|
|
if (types[i] == '"') {
|
|
quoted = !quoted;
|
|
} else if ((types[i] == ',' && !quoted) || types[i] == '\0') {
|
|
token = r_str_ndup (types + prev, i - prev);
|
|
if (token) {
|
|
prev = i + 1;
|
|
r_list_append (ret, token);
|
|
token = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static RList *do_reflike_sig(const char *token) {
|
|
RList *list = NULL;
|
|
char *scratch = r_str_new (token);
|
|
int cnt = r_str_split (scratch, ',');
|
|
if (cnt > 0 && (list = r_list_newf ((RListFree)free))) {
|
|
int i;
|
|
for (i = 0; i < cnt; i++) {
|
|
r_list_append (list, r_str_new (r_str_word_get0 (scratch, i)));
|
|
}
|
|
}
|
|
free (scratch);
|
|
return list;
|
|
}
|
|
|
|
#define DBL_VAL_FAIL(x,y) \
|
|
if (x) { \
|
|
eprintf ("Warning: Skipping signature with multiple %c signatures (%s)\n", y, k); \
|
|
success = false; \
|
|
goto out; \
|
|
}
|
|
R_API bool r_sign_deserialize(RAnal *a, RSignItem *it, const char *k, const char *v) {
|
|
r_return_val_if_fail (a && it && k && v, false);
|
|
|
|
bool success = true;
|
|
char *k2 = r_str_new (k);
|
|
char *v2 = r_str_new (v);
|
|
if (!k2 || !v2) {
|
|
success = false;
|
|
goto out;
|
|
}
|
|
|
|
// Deserialize key: zign|space|name
|
|
int n = r_str_split (k2, '|');
|
|
if (n != 3) {
|
|
eprintf ("Warning: Skipping signature with invalid key (%s)\n", k);
|
|
success = false;
|
|
goto out;
|
|
}
|
|
if (strcmp (r_str_word_get0 (k2, 0), "zign")) {
|
|
eprintf ("Warning: Skipping signature with invalid value (%s)\n", k);
|
|
success = false;
|
|
goto out;
|
|
}
|
|
|
|
it->space = r_spaces_add (&a->zign_spaces, r_str_word_get0 (k2, 1));
|
|
it->name = r_str_new (r_str_word_get0 (k2, 2));
|
|
|
|
// remove newline at end
|
|
strtok (v2, "\n");
|
|
// Deserialize value: |k:v|k:v|k:v|...
|
|
n = r_str_split (v2, '|');
|
|
const char *token = NULL;
|
|
int w, size;
|
|
for (w = 0; w < n; w++) {
|
|
const char *word = r_str_word_get0 (v2, w);
|
|
if (!word) {
|
|
break;
|
|
}
|
|
if (!*word) {
|
|
continue;
|
|
}
|
|
token = word + 2;
|
|
if (!strcmp (word, "*")) {
|
|
continue;
|
|
}
|
|
if (strlen (word) < 3 || word[1] != ':') {
|
|
eprintf ("Warning: Skipping signature with corrupted serialization (%s:%s)\n", k, word);
|
|
success = false;
|
|
goto out;
|
|
}
|
|
RSignType st = (RSignType)*word;
|
|
switch (st) {
|
|
case R_SIGN_NAME:
|
|
DBL_VAL_FAIL (it->realname, R_SIGN_NAME);
|
|
it->realname = strdup (token);
|
|
break;
|
|
case R_SIGN_COMMENT:
|
|
DBL_VAL_FAIL (it->comment, R_SIGN_COMMENT);
|
|
it->comment = strdup (token);
|
|
break;
|
|
case R_SIGN_GRAPH:
|
|
DBL_VAL_FAIL (it->graph, R_SIGN_GRAPH);
|
|
if (strlen (token) == 2 * sizeof (RSignGraph)) {
|
|
it->graph = R_NEW0 (RSignGraph);
|
|
if (it->graph) {
|
|
r_hex_str2bin (token, (ut8 *)it->graph);
|
|
}
|
|
}
|
|
break;
|
|
case R_SIGN_OFFSET:
|
|
DBL_VAL_FAIL ((it->addr != UT64_MAX), R_SIGN_OFFSET);
|
|
it->addr = atoll (token);
|
|
break;
|
|
case R_SIGN_REFS:
|
|
DBL_VAL_FAIL (it->refs, R_SIGN_REFS);
|
|
if (!(it->refs = do_reflike_sig (token))) {
|
|
success = false;
|
|
goto out;
|
|
}
|
|
break;
|
|
case R_SIGN_XREFS:
|
|
DBL_VAL_FAIL (it->xrefs, R_SIGN_XREFS);
|
|
if (!(it->xrefs = do_reflike_sig (token))) {
|
|
success = false;
|
|
goto out;
|
|
}
|
|
break;
|
|
case R_SIGN_VARS:
|
|
DBL_VAL_FAIL (it->vars, R_SIGN_VARS);
|
|
if (!(it->vars = do_reflike_sig (token))) {
|
|
success = false;
|
|
goto out;
|
|
}
|
|
break;
|
|
case R_SIGN_TYPES:
|
|
DBL_VAL_FAIL (it->types, R_SIGN_TYPES);
|
|
it->types = zign_types_to_list (a, token);
|
|
break;
|
|
case R_SIGN_COLLISIONS:
|
|
DBL_VAL_FAIL (it->collisions, R_SIGN_COLLISIONS);
|
|
it->collisions = zign_types_to_list (a, token);
|
|
break;
|
|
case R_SIGN_BBHASH:
|
|
DBL_VAL_FAIL (it->hash, R_SIGN_BBHASH);
|
|
if (token[0] != 0) {
|
|
it->hash = R_NEW0 (RSignHash);
|
|
if (it->hash) {
|
|
it->hash->bbhash = r_str_new (token);
|
|
}
|
|
}
|
|
break;
|
|
case R_SIGN_BYTES:
|
|
// following two errors are not due to double entries
|
|
if (!it->bytes) {
|
|
eprintf ("Warning: Skipping signature with no bytes size (%s)\n", k);
|
|
success = false;
|
|
goto out;
|
|
}
|
|
if (strlen (token) != 2 * it->bytes->size) {
|
|
eprintf ("Warning: Skipping signature with invalid size (%s)\n", k);
|
|
success = false;
|
|
goto out;
|
|
}
|
|
DBL_VAL_FAIL (it->bytes->bytes, R_SIGN_BYTES);
|
|
it->bytes->bytes = malloc (it->bytes->size);
|
|
if (it->bytes->bytes) {
|
|
r_hex_str2bin (token, it->bytes->bytes);
|
|
}
|
|
break;
|
|
case R_SIGN_BYTES_MASK:
|
|
// following two errors are not due to double entries
|
|
if (!it->bytes) {
|
|
eprintf ("Warning: Skipping signature with no mask size (%s)\n", k);
|
|
success = false;
|
|
goto out;
|
|
}
|
|
if (strlen (token) != 2 * it->bytes->size) {
|
|
eprintf ("Warning: Skipping signature invalid mask size (%s)\n", k);
|
|
success = false;
|
|
goto out;
|
|
}
|
|
DBL_VAL_FAIL (it->bytes->mask, R_SIGN_BYTES);
|
|
it->bytes->mask = malloc (it->bytes->size);
|
|
if (!it->bytes->mask) {
|
|
goto out;
|
|
}
|
|
r_hex_str2bin (token, it->bytes->mask);
|
|
break;
|
|
case R_SIGN_BYTES_SIZE:
|
|
// allocate
|
|
size = atoi (token);
|
|
if (size > 0) {
|
|
DBL_VAL_FAIL (it->bytes, R_SIGN_BYTES_SIZE);
|
|
it->bytes = R_NEW0 (RSignBytes);
|
|
if (!it->bytes) {
|
|
goto out;
|
|
}
|
|
it->bytes->size = size;
|
|
}
|
|
break;
|
|
default:
|
|
eprintf ("Unsupported (%c)\n", st);
|
|
break;
|
|
}
|
|
}
|
|
out:
|
|
free (k2);
|
|
free (v2);
|
|
return success;
|
|
}
|
|
#undef DBL_VAL_FAIL
|
|
|
|
static inline char *str_serialize_key(const char *sp, const char *name) {
|
|
return r_str_newf ("zign|%s|%s", sp, name);
|
|
}
|
|
|
|
static inline char *space_serialize_key(const RSpace *space, const char *name) {
|
|
const char *sp = space? space->name: "*";
|
|
return str_serialize_key (sp, name);
|
|
}
|
|
|
|
static inline char *item_serialize_key(RSignItem *it) {
|
|
return space_serialize_key (it->space, it->name);
|
|
}
|
|
|
|
static bool serialize_str_list(RList *l, RStrBuf *sb, RSignType t) {
|
|
if (!l || l->length == 0) {
|
|
return true;
|
|
}
|
|
if (!r_strbuf_appendf (sb, "|%c:", t)) {
|
|
return false;
|
|
}
|
|
char *e, *c = "";
|
|
RListIter *iter;
|
|
r_list_foreach (l, iter, e) {
|
|
if (!r_strbuf_appendf (sb, "%s%s", c, e)) {
|
|
return false;
|
|
}
|
|
if (!*c) {
|
|
c = ",";
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static inline size_t serial_val_reserv(RSignItem *it) {
|
|
// does not have to be exact, just save time on re-alloc+copy
|
|
size_t reserve = 32;
|
|
if (it->bytes) {
|
|
reserve += it->bytes->size * 4 + 10;
|
|
}
|
|
if (it->graph) {
|
|
reserve += sizeof (RSignGraph) * 2 + 1;
|
|
}
|
|
if (it->hash && it->hash->bbhash) {
|
|
reserve += 64;
|
|
}
|
|
int mul = 5;
|
|
if (it->refs) {
|
|
reserve += mul * r_list_length (it->refs);
|
|
}
|
|
if (it->xrefs) {
|
|
reserve += mul * r_list_length (it->xrefs);
|
|
}
|
|
if (it->vars) {
|
|
reserve += mul * r_list_length (it->vars);
|
|
}
|
|
if (it->types) {
|
|
reserve += mul * r_list_length (it->types);
|
|
}
|
|
return reserve;
|
|
}
|
|
|
|
#define FreeRet_on_fail(exp, buf) \
|
|
if (!exp) { \
|
|
r_strbuf_free (buf); \
|
|
return NULL; \
|
|
}
|
|
|
|
static char *serialize_value(RSignItem *it) {
|
|
r_return_val_if_fail (it, false);
|
|
|
|
size_t reserve = 0;
|
|
RStrBuf *sb = r_strbuf_new ("");
|
|
r_strbuf_reserve (sb, serial_val_reserv (it));
|
|
|
|
if (it->bytes) {
|
|
reserve += it->bytes->size * 2;
|
|
}
|
|
|
|
RSignBytes *bytes = it->bytes;
|
|
if (bytes && bytes->bytes && bytes->mask) {
|
|
RSignBytes *bytes = it->bytes;
|
|
size_t len = bytes->size * 2 + 1;
|
|
char *hexbytes = calloc (1, len);
|
|
char *hexmask = calloc (1, len);
|
|
bool success = false;
|
|
if (hexbytes && hexmask) {
|
|
r_hex_bin2str (bytes->bytes, bytes->size, hexbytes);
|
|
r_hex_bin2str (bytes->mask, bytes->size, hexmask);
|
|
success = r_strbuf_appendf (sb, "|%c:%d|%c:%s|%c:%s",
|
|
R_SIGN_BYTES_SIZE, bytes->size, R_SIGN_BYTES, hexbytes,
|
|
R_SIGN_BYTES_MASK, hexmask);
|
|
free (hexbytes);
|
|
free (hexmask);
|
|
}
|
|
FreeRet_on_fail (success, sb);
|
|
}
|
|
|
|
if (it->addr != UT64_MAX) {
|
|
FreeRet_on_fail (r_strbuf_appendf (sb, "|%c:%" PFMT64d, R_SIGN_OFFSET, it->addr), sb);
|
|
}
|
|
|
|
if (it->graph) {
|
|
char *hexgraph = calloc (1, sizeof (RSignGraph) * 2 + 1);
|
|
bool success = false;
|
|
if (hexgraph) {
|
|
r_hex_bin2str ((ut8 *)it->graph, sizeof (RSignGraph), hexgraph);
|
|
success = r_strbuf_appendf (sb, "|%c:%s", R_SIGN_GRAPH, hexgraph);
|
|
free (hexgraph);
|
|
}
|
|
FreeRet_on_fail (success, sb);
|
|
}
|
|
|
|
FreeRet_on_fail (serialize_str_list (it->refs, sb, R_SIGN_REFS), sb);
|
|
FreeRet_on_fail (serialize_str_list (it->xrefs, sb, R_SIGN_XREFS), sb);
|
|
FreeRet_on_fail (serialize_str_list (it->vars, sb, R_SIGN_VARS), sb);
|
|
FreeRet_on_fail (serialize_str_list (it->types, sb, R_SIGN_TYPES), sb);
|
|
FreeRet_on_fail (serialize_str_list (it->collisions, sb, R_SIGN_COLLISIONS), sb);
|
|
|
|
if (it->comment) {
|
|
FreeRet_on_fail (r_strbuf_appendf (sb, "|%c:%s", R_SIGN_COMMENT, it->comment), sb);
|
|
}
|
|
|
|
if (it->realname) {
|
|
FreeRet_on_fail (r_strbuf_appendf (sb, "|%c:%s", R_SIGN_NAME, it->realname), sb);
|
|
}
|
|
|
|
if (it->hash && it->hash->bbhash) {
|
|
FreeRet_on_fail (r_strbuf_appendf (sb, "|%c:%s", R_SIGN_BBHASH, it->hash->bbhash), sb);
|
|
}
|
|
|
|
return r_strbuf_drain (sb);
|
|
}
|
|
|
|
static RList *deserialize_sign_space(RAnal *a, RSpace *space) {
|
|
r_return_val_if_fail (a && space, NULL);
|
|
|
|
char *key = space_serialize_key (space, "");
|
|
if (!key) {
|
|
return NULL;
|
|
}
|
|
SdbList *zigns = sdb_foreach_match (a->sdb_zigns, key, false);
|
|
free (key);
|
|
|
|
SdbListIter *iter;
|
|
SdbKv *kv;
|
|
RList *ret = r_list_newf ((RListFree)r_sign_item_free);
|
|
if (ret) {
|
|
ls_foreach (zigns, iter, kv) {
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (!it) {
|
|
break;
|
|
}
|
|
if (r_sign_deserialize (a, it, kv->base.key, kv->base.value)) {
|
|
r_list_append (ret, it);
|
|
} else {
|
|
r_sign_item_free (it);
|
|
}
|
|
}
|
|
}
|
|
ls_free (zigns);
|
|
return ret;
|
|
}
|
|
|
|
static inline bool merge_list (RList **dst, RList *src) {
|
|
if (!src) {
|
|
return true;
|
|
}
|
|
r_list_free (*dst);
|
|
if (!(*dst = r_list_newf (free))) {
|
|
return false;
|
|
}
|
|
RListIter *iter;
|
|
char *s, *dup;
|
|
r_list_foreach (src, iter, s) {
|
|
if (!(dup = strdup (s))) {
|
|
return false;
|
|
}
|
|
r_list_append (*dst, dup);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool mergeItem(RSignItem *dst, RSignItem *src) {
|
|
dst->space = src->space;
|
|
if (src->bytes) {
|
|
// ensure we have a dst->bytes
|
|
if (!dst->bytes && !(dst->bytes = R_NEW0 (RSignBytes))) {
|
|
return false;
|
|
}
|
|
// ensure we have space in dst->bytes->[bytes|mask]
|
|
if (src->bytes->size > dst->bytes->size) {
|
|
free (dst->bytes->bytes);
|
|
free (dst->bytes->mask);
|
|
dst->bytes->bytes = R_NEWS (ut8, src->bytes->size);
|
|
dst->bytes->mask = R_NEWS (ut8, src->bytes->size);
|
|
if (!dst->bytes->bytes || !dst->bytes->mask) {
|
|
return false;
|
|
}
|
|
}
|
|
// do copy
|
|
dst->bytes->size = src->bytes->size;
|
|
memcpy (dst->bytes->bytes, src->bytes->bytes, src->bytes->size);
|
|
memcpy (dst->bytes->mask, src->bytes->mask, src->bytes->size);
|
|
}
|
|
|
|
if (src->graph) {
|
|
if (!dst->graph && !(dst->graph = R_NEW0 (RSignGraph))) {
|
|
return false;
|
|
}
|
|
*dst->graph = *src->graph;
|
|
}
|
|
|
|
if (src->comment) {
|
|
free (dst->comment);
|
|
if (!(dst->comment = strdup (src->comment))) {
|
|
return false;
|
|
}
|
|
dst->comment = src->comment;
|
|
src->comment = NULL;
|
|
}
|
|
|
|
if (src->realname) {
|
|
free (dst->realname);
|
|
if (!(dst->realname = strdup (src->realname))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (src->addr != UT64_MAX) {
|
|
dst->addr = src->addr;
|
|
}
|
|
|
|
if (!merge_list (&dst->refs, src->refs)) {
|
|
return false;
|
|
}
|
|
if (!merge_list (&dst->xrefs, src->xrefs)) {
|
|
return false;
|
|
}
|
|
if (!merge_list (&dst->vars, src->vars)) {
|
|
return false;
|
|
}
|
|
if (!merge_list (&dst->types, src->types)) {
|
|
return false;
|
|
}
|
|
if (!merge_list (&dst->collisions, src->collisions)) {
|
|
return false;
|
|
}
|
|
|
|
if (src->hash && src->hash->bbhash) {
|
|
if (!dst->hash && !(dst->hash = R_NEW0 (RSignHash))) {
|
|
return false;
|
|
}
|
|
free (dst->hash->bbhash);
|
|
dst->hash->bbhash = strdup (src->hash->bbhash);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static RSignItem *sign_get_sdb_item(RAnal *a, const char *key) {
|
|
RSignItem *it = NULL;
|
|
const char *value = sdb_const_get (a->sdb_zigns, key, 0);
|
|
if (value && (it = r_sign_item_new ())) {
|
|
if (r_sign_deserialize (a, it, key, value)) {
|
|
return it;
|
|
}
|
|
r_sign_item_free (it);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static bool r_sign_set_item(Sdb *sdb, RSignItem *it, char *key_optional) {
|
|
bool retval = false;
|
|
char *key = NULL, *mykey = key_optional;
|
|
if (!mykey) {
|
|
key = mykey = item_serialize_key (it);
|
|
}
|
|
char *value = serialize_value (it);
|
|
if (mykey && value) {
|
|
sdb_set (sdb, mykey, value, 0);
|
|
retval = true;
|
|
}
|
|
free (key);
|
|
free (value);
|
|
return retval;
|
|
}
|
|
|
|
R_API RSignItem *r_sign_get_item(RAnal *a, const char *name) {
|
|
char *k = space_serialize_key (r_spaces_current (&a->zign_spaces), name);
|
|
if (k) {
|
|
RSignItem *it = sign_get_sdb_item (a, k);
|
|
free (k);
|
|
return it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
R_API bool r_sign_add_item(RAnal *a, RSignItem *it) {
|
|
char *key = item_serialize_key (it);
|
|
RSignItem *current = sign_get_sdb_item (a, key);
|
|
|
|
bool retval = false;
|
|
if (current) {
|
|
if (mergeItem (current, it)) {
|
|
retval = r_sign_set_item (a->sdb_zigns, current, key);
|
|
r_sign_item_free (current);
|
|
}
|
|
} else {
|
|
retval = r_sign_set_item (a->sdb_zigns, it, key);
|
|
}
|
|
free (key);
|
|
return retval;
|
|
}
|
|
|
|
static bool addHash(RAnal *a, const char *name, int type, const char *val) {
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (!it) {
|
|
r_sign_item_free (it);
|
|
return false;
|
|
}
|
|
it->name = r_str_new (name);
|
|
if (!it->name) {
|
|
r_sign_item_free (it);
|
|
return false;
|
|
}
|
|
it->hash = R_NEW0 (RSignHash);
|
|
if (!it->hash) {
|
|
r_sign_item_free (it);
|
|
return false;
|
|
}
|
|
it->space = r_spaces_current (&a->zign_spaces);
|
|
|
|
bool retval = false;
|
|
switch (type) {
|
|
case R_SIGN_BBHASH:
|
|
it->hash->bbhash = strdup (val);
|
|
retval = r_sign_add_item (a, it);
|
|
r_sign_item_free (it);
|
|
break;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static bool addBBHash(RAnal *a, RAnalFunction *fcn, const char *name) {
|
|
bool retval = false;
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (!it) {
|
|
goto beach;
|
|
}
|
|
it->name = r_str_new (name);
|
|
if (!it->name) {
|
|
goto beach;
|
|
}
|
|
it->space = r_spaces_current (&a->zign_spaces);
|
|
|
|
if (r_sign_addto_item (a, it, fcn, R_SIGN_BBHASH)) {
|
|
retval = r_sign_add_item (a, it);
|
|
}
|
|
beach:
|
|
r_sign_item_free (it);
|
|
return retval;
|
|
}
|
|
|
|
static bool addBytes(RAnal *a, const char *name, ut64 size, const ut8 *bytes, const ut8 *mask) {
|
|
bool retval = true;
|
|
|
|
if (r_mem_is_zero (mask, size)) {
|
|
eprintf ("error: zero mask\n");
|
|
return false;
|
|
}
|
|
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (!it) {
|
|
return false;
|
|
}
|
|
|
|
it->name = r_str_new (name);
|
|
if (!it->name) {
|
|
free (it);
|
|
return false;
|
|
}
|
|
it->space = r_spaces_current (&a->zign_spaces);
|
|
it->bytes = R_NEW0 (RSignBytes);
|
|
if (!it->bytes) {
|
|
goto fail;
|
|
}
|
|
it->bytes->size = size;
|
|
it->bytes->bytes = malloc (size);
|
|
if (!it->bytes->bytes) {
|
|
goto fail;
|
|
}
|
|
memcpy (it->bytes->bytes, bytes, size);
|
|
it->bytes->mask = malloc (size);
|
|
if (!it->bytes->mask) {
|
|
goto fail;
|
|
}
|
|
memcpy (it->bytes->mask, mask, size);
|
|
retval = r_sign_add_item (a, it);
|
|
r_sign_item_free (it);
|
|
return retval;
|
|
fail:
|
|
if (it) {
|
|
free (it->name);
|
|
r_sign_bytes_free (it->bytes);
|
|
}
|
|
free (it);
|
|
return false;
|
|
}
|
|
|
|
R_API bool r_sign_add_hash(RAnal *a, const char *name, int type, const char *val, int len) {
|
|
r_return_val_if_fail (a && name && type && val && len > 0, false);
|
|
if (type != R_SIGN_BBHASH) {
|
|
eprintf ("error: hash type unknown");
|
|
return false;
|
|
}
|
|
int digestsize = r_hash_size (R_ZIGN_HASH) * 2;
|
|
if (len != digestsize) {
|
|
eprintf ("error: invalid hash size: %d (%s digest size is %d)\n", len, ZIGN_HASH, digestsize);
|
|
return false;
|
|
}
|
|
return addHash (a, name, type, val);
|
|
}
|
|
|
|
R_API bool r_sign_add_bb_hash(RAnal *a, RAnalFunction *fcn, const char *name) {
|
|
r_return_val_if_fail (a && fcn && name, false);
|
|
return addBBHash (a, fcn, name);
|
|
}
|
|
|
|
R_API bool r_sign_add_bytes(RAnal *a, const char *name, ut64 size, const ut8 *bytes, const ut8 *mask) {
|
|
r_return_val_if_fail (a && name && size > 0 && bytes && mask, false);
|
|
return addBytes (a, name, size, bytes, mask);
|
|
}
|
|
|
|
R_API bool r_sign_add_anal(RAnal *a, const char *name, ut64 size, const ut8 *bytes, ut64 at) {
|
|
bool retval = false;
|
|
r_return_val_if_fail (a && name && size > 0 && bytes, false);
|
|
ut8 *mask = r_anal_mask (a, size, bytes, at);
|
|
if (mask) {
|
|
retval = addBytes (a, name, size, bytes, mask);
|
|
free (mask);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static RSignGraph *r_sign_fcn_graph(RAnalFunction *fcn) {
|
|
r_return_val_if_fail (fcn, false);
|
|
RSignGraph *graph = R_NEW0 (RSignGraph);
|
|
if (graph) {
|
|
graph->cc = r_anal_function_complexity (fcn),
|
|
graph->nbbs = r_list_length (fcn->bbs);
|
|
graph->edges = r_anal_function_count_edges (fcn, &graph->ebbs);
|
|
graph->bbsum = r_anal_function_realsize (fcn);
|
|
}
|
|
return graph;
|
|
}
|
|
|
|
static int bb_sort_by_addr(const void *x, const void *y) {
|
|
RAnalBlock *a = (RAnalBlock *)x;
|
|
RAnalBlock *b = (RAnalBlock *)y;
|
|
if (a->addr > b->addr) {
|
|
return 1;
|
|
}
|
|
if (a->addr < b->addr) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static RSignBytes *r_sign_func_empty_mask(RAnal *a, RAnalFunction *fcn) {
|
|
r_return_val_if_fail (a && fcn && fcn->bbs && fcn->bbs->head, false);
|
|
|
|
// get size
|
|
RCore *core = a->coreb.core;
|
|
int maxsz = a->coreb.cfggeti (core, "zign.maxsz");
|
|
r_list_sort (fcn->bbs, &bb_sort_by_addr);
|
|
ut64 ea = fcn->addr;
|
|
RAnalBlock *bb = (RAnalBlock *)fcn->bbs->tail->data;
|
|
int size = R_MIN (bb->addr + bb->size - ea, maxsz);
|
|
|
|
// alloc space for signature
|
|
RSignBytes *sig = R_NEW0 (RSignBytes);
|
|
if (sig) {
|
|
sig->bytes = malloc (size);
|
|
sig->mask = R_NEWS0 (ut8, size);
|
|
sig->size = size;
|
|
if (sig->bytes && sig->mask && a->iob.read_at (a->iob.io, ea, sig->bytes, size)) {
|
|
return sig;
|
|
}
|
|
}
|
|
r_sign_bytes_free (sig);
|
|
return NULL;
|
|
}
|
|
|
|
static RSignBytes *r_sign_fcn_bytes(RAnal *a, RAnalFunction *fcn) {
|
|
r_return_val_if_fail (a && fcn && fcn->bbs && fcn->bbs->head, false);
|
|
RSignBytes *sig = r_sign_func_empty_mask (a, fcn);
|
|
if (!sig) {
|
|
return NULL;
|
|
}
|
|
|
|
ut64 ea = fcn->addr;
|
|
int size = sig->size;
|
|
ut8 *tmpmask = NULL;
|
|
RAnalBlock *bb;
|
|
RListIter *iter;
|
|
r_list_foreach (fcn->bbs, iter, bb) {
|
|
if (bb->addr >= ea) {
|
|
size_t delta = bb->addr - ea;
|
|
size_t rsize = bb->size;
|
|
|
|
// bounds check
|
|
if (delta >= size) {
|
|
break;
|
|
}
|
|
if (size - delta < rsize) {
|
|
rsize = size - delta;
|
|
}
|
|
|
|
// get mask for block
|
|
if (!(tmpmask = r_anal_mask (a, rsize, sig->bytes + delta, ea))) {
|
|
r_sign_bytes_free (sig);
|
|
return NULL;
|
|
}
|
|
if (rsize > 0) {
|
|
memcpy (sig->mask + delta, tmpmask, rsize);
|
|
}
|
|
free (tmpmask);
|
|
}
|
|
}
|
|
return sig;
|
|
}
|
|
|
|
static RSignHash *r_sign_fcn_bbhash(RAnal *a, RAnalFunction *fcn) {
|
|
r_return_val_if_fail (a && fcn, NULL);
|
|
RSignHash *hash = R_NEW0 (RSignHash);
|
|
if (!hash) {
|
|
return NULL;
|
|
}
|
|
|
|
char *digest_hex = r_sign_calc_bbhash (a, fcn);
|
|
if (!digest_hex) {
|
|
free (hash);
|
|
return NULL;
|
|
}
|
|
hash->bbhash = digest_hex;
|
|
return hash;
|
|
}
|
|
|
|
R_API int r_sign_all_functions(RAnal *a) {
|
|
RAnalFunction *fcni = NULL;
|
|
RListIter *iter = NULL;
|
|
int count = 0;
|
|
|
|
r_list_foreach (a->fcns, iter, fcni) {
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
if (r_sign_add_func (a, fcni, NULL)) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
R_API bool r_sign_add_func(RAnal *a, RAnalFunction *fcn, const char *name) {
|
|
r_return_val_if_fail (a && fcn, false);
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (!it) {
|
|
return false;
|
|
}
|
|
it->space = r_spaces_current (&a->zign_spaces);
|
|
it->name = strdup (name? name: fcn->name);
|
|
|
|
if (!it->name) {
|
|
r_sign_item_free (it);
|
|
return false;
|
|
}
|
|
|
|
r_sign_addto_item (a, it, fcn, R_SIGN_GRAPH);
|
|
r_sign_addto_item (a, it, fcn, R_SIGN_BYTES);
|
|
r_sign_addto_item (a, it, fcn, R_SIGN_XREFS);
|
|
r_sign_addto_item (a, it, fcn, R_SIGN_REFS);
|
|
r_sign_addto_item (a, it, fcn, R_SIGN_VARS);
|
|
r_sign_addto_item (a, it, fcn, R_SIGN_TYPES);
|
|
r_sign_addto_item (a, it, fcn, R_SIGN_BBHASH);
|
|
r_sign_addto_item (a, it, fcn, R_SIGN_OFFSET);
|
|
r_sign_addto_item (a, it, fcn, R_SIGN_NAME);
|
|
|
|
// commit the item to anal
|
|
r_sign_add_item (a, it);
|
|
r_sign_item_free (it);
|
|
return true;
|
|
}
|
|
|
|
R_API bool r_sign_addto_item(RAnal *a, RSignItem *it, RAnalFunction *fcn, RSignType type) {
|
|
r_return_val_if_fail (a && it && fcn, false);
|
|
switch (type) {
|
|
case R_SIGN_GRAPH:
|
|
return !it->graph && (it->graph = r_sign_fcn_graph (fcn));
|
|
case R_SIGN_BYTES:
|
|
return !it->bytes && (it->bytes = r_sign_fcn_bytes (a, fcn));
|
|
case R_SIGN_XREFS:
|
|
return !it->xrefs && (it->xrefs = r_sign_fcn_xrefs (a, fcn));
|
|
case R_SIGN_REFS:
|
|
return !it->refs && (it->refs = r_sign_fcn_refs (a, fcn));
|
|
case R_SIGN_VARS:
|
|
return !it->vars && (it->vars = r_sign_fcn_vars (a, fcn));
|
|
case R_SIGN_TYPES:
|
|
return !it->types && (it->types = r_sign_fcn_types (a, fcn));
|
|
case R_SIGN_BBHASH:
|
|
return !it->hash && (it->hash = r_sign_fcn_bbhash (a, fcn));
|
|
case R_SIGN_OFFSET:
|
|
it->addr = fcn->addr;
|
|
return true;
|
|
case R_SIGN_NAME:
|
|
if (!it->realname && it->name) {
|
|
if (strcmp (it->name, fcn->name)) {
|
|
it->realname = strdup (fcn->name);
|
|
}
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
eprintf ("Error: %s Can not handle type %c\n", __FUNCTION__, type);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
R_API bool r_sign_add_graph(RAnal *a, const char *name, RSignGraph graph) {
|
|
r_return_val_if_fail (a && !R_STR_ISEMPTY (name), false);
|
|
bool retval = true;
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (!it) {
|
|
return false;
|
|
}
|
|
it->name = r_str_new (name);
|
|
if (!it->name) {
|
|
free (it);
|
|
return false;
|
|
}
|
|
it->space = r_spaces_current (&a->zign_spaces);
|
|
it->graph = R_NEW0 (RSignGraph);
|
|
if (!it->graph) {
|
|
free (it->name);
|
|
free (it);
|
|
return false;
|
|
}
|
|
*it->graph = graph;
|
|
retval = r_sign_add_item (a, it);
|
|
r_sign_item_free (it);
|
|
|
|
return retval;
|
|
}
|
|
|
|
R_API bool r_sign_add_comment(RAnal *a, const char *name, const char *comment) {
|
|
r_return_val_if_fail (a && name && comment, false);
|
|
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (!it) {
|
|
return false;
|
|
}
|
|
it->name = r_str_new (name);
|
|
it->space = r_spaces_current (&a->zign_spaces);
|
|
it->comment = strdup (comment);
|
|
bool retval = r_sign_add_item (a, it);
|
|
r_sign_item_free (it);
|
|
return retval;
|
|
}
|
|
|
|
R_API bool r_sign_add_name(RAnal *a, const char *name, const char *realname) {
|
|
r_return_val_if_fail (a && name && realname, false);
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (it) {
|
|
it->name = r_str_new (name);
|
|
it->realname = strdup (realname);
|
|
it->space = r_spaces_current (&a->zign_spaces);
|
|
bool retval = r_sign_add_item (a, it);
|
|
r_sign_item_free (it);
|
|
return retval;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
R_API bool r_sign_add_addr(RAnal *a, const char *name, ut64 addr) {
|
|
r_return_val_if_fail (a && name && addr != UT64_MAX, false);
|
|
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (!it) {
|
|
return false;
|
|
}
|
|
it->name = r_str_new (name);
|
|
it->space = r_spaces_current (&a->zign_spaces);
|
|
it->addr = addr;
|
|
|
|
bool retval = r_sign_add_item (a, it);
|
|
|
|
r_sign_item_free (it);
|
|
|
|
return retval;
|
|
}
|
|
|
|
R_API bool r_sign_add_vars(RAnal *a, const char *name, RList *vars) {
|
|
r_return_val_if_fail (a && name && vars, false);
|
|
|
|
RListIter *iter;
|
|
char *var;
|
|
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (!it) {
|
|
return false;
|
|
}
|
|
it->name = r_str_new (name);
|
|
if (!it->name) {
|
|
r_sign_item_free (it);
|
|
return false;
|
|
}
|
|
it->space = r_spaces_current (&a->zign_spaces);
|
|
it->vars = r_list_newf ((RListFree)free);
|
|
r_list_foreach (vars, iter, var) {
|
|
r_list_append (it->vars, strdup (var));
|
|
}
|
|
bool retval = r_sign_add_item (a, it);
|
|
r_sign_item_free (it);
|
|
|
|
return retval;
|
|
}
|
|
|
|
R_API bool r_sign_add_types(RAnal *a, const char *name, RList *types) {
|
|
r_return_val_if_fail (a && name && types, false);
|
|
|
|
RListIter *iter;
|
|
char *type;
|
|
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (!it) {
|
|
return false;
|
|
}
|
|
it->name = r_str_new (name);
|
|
if (!it->name) {
|
|
r_sign_item_free (it);
|
|
return false;
|
|
}
|
|
it->space = r_spaces_current (&a->zign_spaces);
|
|
it->types = r_list_newf ((RListFree) free);
|
|
r_list_foreach (types, iter, type) {
|
|
r_list_append (it->types, strdup (type));
|
|
}
|
|
bool retval = r_sign_add_item (a, it);
|
|
r_sign_item_free (it);
|
|
|
|
return retval;
|
|
}
|
|
|
|
R_API bool r_sign_add_refs(RAnal *a, const char *name, RList *refs) {
|
|
r_return_val_if_fail (a && name && refs, false);
|
|
|
|
char *ref;
|
|
RListIter *iter;
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (!it) {
|
|
return false;
|
|
}
|
|
it->name = r_str_new (name);
|
|
if (!it->name) {
|
|
free (it);
|
|
return false;
|
|
}
|
|
it->space = r_spaces_current (&a->zign_spaces);
|
|
it->refs = r_list_newf ((RListFree) free);
|
|
r_list_foreach (refs, iter, ref) {
|
|
r_list_append (it->refs, strdup (ref));
|
|
}
|
|
bool retval = r_sign_add_item (a, it);
|
|
r_sign_item_free (it);
|
|
|
|
return retval;
|
|
}
|
|
|
|
R_API bool r_sign_add_xrefs(RAnal *a, const char *name, RList *xrefs) {
|
|
r_return_val_if_fail (a && name && xrefs, false);
|
|
|
|
RListIter *iter = NULL;
|
|
char *ref = NULL;
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (!it) {
|
|
return false;
|
|
}
|
|
it->name = r_str_new (name);
|
|
if (!it->name) {
|
|
free (it);
|
|
return false;
|
|
}
|
|
it->space = r_spaces_current (&a->zign_spaces);
|
|
it->xrefs = r_list_newf ((RListFree) free);
|
|
r_list_foreach (xrefs, iter, ref) {
|
|
r_list_append (it->xrefs, strdup (ref));
|
|
}
|
|
bool retval = r_sign_add_item (a, it);
|
|
r_sign_item_free (it);
|
|
|
|
return retval;
|
|
}
|
|
|
|
struct ctxDeleteCB {
|
|
RAnal *anal;
|
|
char *key;
|
|
size_t len;
|
|
};
|
|
|
|
static bool deleteBySpaceCB(void *user, const char *k, const char *v) {
|
|
struct ctxDeleteCB *ctx = (struct ctxDeleteCB *) user;
|
|
if (!strncmp (k, ctx->key, ctx->len)) {
|
|
sdb_remove (ctx->anal->sdb_zigns, k, 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
R_API bool r_sign_delete(RAnal *a, const char *name) {
|
|
r_return_val_if_fail (a && name, false);
|
|
bool retval = false;
|
|
|
|
// Remove all zigns
|
|
if (*name == '*') {
|
|
if (!r_spaces_current (&a->zign_spaces)) {
|
|
sdb_reset (a->sdb_zigns);
|
|
return true;
|
|
}
|
|
struct ctxDeleteCB ctx = { .anal = a };
|
|
ctx.key = space_serialize_key (r_spaces_current (&a->zign_spaces), "");
|
|
if (ctx.key) {
|
|
ctx.len = strlen (ctx.key);
|
|
retval = sdb_foreach (a->sdb_zigns, deleteBySpaceCB, &ctx);
|
|
free (ctx.key);
|
|
}
|
|
} else {
|
|
// Remove specific zign
|
|
char *key = space_serialize_key (r_spaces_current (&a->zign_spaces), name);
|
|
if (key) {
|
|
retval = sdb_remove (a->sdb_zigns, key, 0);
|
|
free (key);
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static ut8 * build_combined_bytes(RSignBytes *bsig) {
|
|
r_return_val_if_fail (bsig && bsig->bytes && bsig->mask, NULL);
|
|
ut8 *buf = (ut8 *)malloc (bsig->size);
|
|
if (buf) {
|
|
size_t i;
|
|
for (i = 0; i < bsig->size; i++) {
|
|
buf[i] = bsig->bytes[i] & bsig->mask[i];
|
|
}
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static double cmp_bytesig_to_buff(RSignBytes *sig, ut8 *buf, int len) {
|
|
r_return_val_if_fail (sig && buf && len >= 0, (double)-1.0);
|
|
ut8 *sigbuf = build_combined_bytes (sig);
|
|
double sim = -1.0;
|
|
if (sigbuf) {
|
|
r_diff_buffers_distance (NULL, sigbuf, sig->size, buf, len, NULL, &sim);
|
|
free (sigbuf);
|
|
}
|
|
return sim;
|
|
}
|
|
|
|
static double matchBytes(RSignItem *a, RSignItem *b) {
|
|
double result = 0.0;
|
|
|
|
if (!a->bytes || !b->bytes) {
|
|
return result;
|
|
}
|
|
|
|
size_t min_size = R_MIN ((size_t)a->bytes->size, (size_t)b->bytes->size);
|
|
if (!min_size) {
|
|
return result;
|
|
}
|
|
|
|
ut8 *combined_mask = NULL;
|
|
if (a->bytes->mask || b->bytes->mask) {
|
|
combined_mask = (ut8*)malloc (min_size);
|
|
if (!combined_mask) {
|
|
return result;
|
|
}
|
|
memcpy (combined_mask, a->bytes->mask, min_size);
|
|
if (b->bytes->mask) {
|
|
int i;
|
|
for (i = 0; i != min_size; i++) {
|
|
combined_mask[i] &= b->bytes->mask[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((combined_mask && !r_mem_cmp_mask (a->bytes->bytes, b->bytes->bytes, combined_mask, min_size)) ||
|
|
(!combined_mask && !memcmp (a->bytes->bytes, b->bytes->bytes, min_size))) {
|
|
result = (double)min_size / (double)R_MAX (a->bytes->size, b->bytes->size);
|
|
}
|
|
|
|
free (combined_mask);
|
|
|
|
return result;
|
|
}
|
|
|
|
#define SIMILARITY(a, b) \
|
|
((a) == (b)? 1.0: (R_MAX ((a), (b)) == 0.0? 0.0: (double)R_MIN ((a), (b)) / (double)R_MAX ((a), (b))))
|
|
|
|
static double matchGraph(RSignItem *a, RSignItem *b) {
|
|
if (!a->graph || !b->graph) {
|
|
return 0.0;
|
|
}
|
|
|
|
double total = 0.0;
|
|
|
|
total += SIMILARITY (a->graph->cc, b->graph->cc);
|
|
total += SIMILARITY (a->graph->nbbs, b->graph->nbbs);
|
|
total += SIMILARITY (a->graph->ebbs, b->graph->ebbs);
|
|
total += SIMILARITY (a->graph->edges, b->graph->edges);
|
|
total += SIMILARITY (a->graph->bbsum, b->graph->bbsum);
|
|
|
|
return total / 5.0;
|
|
}
|
|
|
|
static int score_cmpr(const void *a, const void *b) {
|
|
double sa = ((RSignCloseMatch *)a)->score;
|
|
double sb = ((RSignCloseMatch *)b)->score;
|
|
|
|
if (sa < sb) {
|
|
return 1;
|
|
}
|
|
if (sa > sb) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
typedef struct {
|
|
RSignItem *test;
|
|
RList *output;
|
|
size_t count;
|
|
double score_threshold;
|
|
ut8 *bytes_combined;
|
|
|
|
// greatest lower bound. Thanks lattice theory for helping name variables
|
|
double infimum;
|
|
} ClosestMatchData;
|
|
|
|
static bool closest_match_update(RSignItem *it, ClosestMatchData *data) {
|
|
// quantify how close the signature matches
|
|
int div = 0;
|
|
double score = 0.0;
|
|
double gscore = -1.0;
|
|
if (it->graph && data->test->graph) {
|
|
gscore = matchGraph (it, data->test);
|
|
score += gscore;
|
|
div++;
|
|
}
|
|
double bscore = -1.0;
|
|
bool list_full = (r_list_length (data->output) == data->count);
|
|
|
|
// value to beat to enter the list
|
|
double pivot = data->score_threshold;
|
|
if (list_full) {
|
|
pivot = R_MAX (pivot, data->infimum);
|
|
}
|
|
|
|
if (it->bytes && data->bytes_combined) {
|
|
int sizea = it->bytes->size;
|
|
int sizeb = data->test->bytes->size;
|
|
if (pivot > 0.0) {
|
|
// bytes distance is slow. To avoid it, we can do quick maths to
|
|
// see if the highest possible score would be good enough to change
|
|
// results
|
|
double maxscore = R_MIN (sizea, sizeb) / R_MAX (sizea, sizeb);
|
|
if (div > 0) {
|
|
maxscore = (maxscore + score) / div;
|
|
}
|
|
if (maxscore < pivot) {
|
|
r_sign_item_free (it);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// get true byte score
|
|
bscore = cmp_bytesig_to_buff (it->bytes, data->bytes_combined, sizeb);
|
|
score += bscore;
|
|
div++;
|
|
}
|
|
if (div == 0) {
|
|
r_sign_item_free (it);
|
|
return true;
|
|
}
|
|
score /= div;
|
|
|
|
// score is too low, don't bother doing any more work
|
|
if (score < pivot) {
|
|
r_sign_item_free (it);
|
|
return true;
|
|
}
|
|
|
|
// add new element
|
|
RSignCloseMatch *row = R_NEW (RSignCloseMatch);
|
|
if (!row) {
|
|
r_sign_item_free (it);
|
|
return false;
|
|
}
|
|
row->score = score;
|
|
row->gscore = gscore;
|
|
row->bscore = bscore;
|
|
row->item = it;
|
|
r_list_add_sorted (data->output, (void *)row, &score_cmpr);
|
|
|
|
if (list_full) {
|
|
// remove smallest element
|
|
r_sign_close_match_free (r_list_pop (data->output));
|
|
|
|
// get new infimum
|
|
row = r_list_get_top (data->output);
|
|
data->infimum = row->score;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
R_API void r_sign_close_match_free(RSignCloseMatch *match) {
|
|
if (match) {
|
|
r_sign_item_free (match->item);
|
|
free (match);
|
|
}
|
|
}
|
|
|
|
static bool _closest_match_cb(RSignItem *it, void *user) {
|
|
return closest_match_update (it, (ClosestMatchData *)user);
|
|
}
|
|
|
|
R_API RList *r_sign_find_closest_fcn(RAnal *a, RSignItem *it, int count, double score_threshold) {
|
|
r_return_val_if_fail (a && it && count > 0 && score_threshold >= 0 && score_threshold <= 1, NULL);
|
|
r_return_val_if_fail (it->bytes || it->graph, NULL);
|
|
|
|
RList *output = r_list_newf ((RListFree)r_sign_close_match_free);
|
|
if (!output) {
|
|
return NULL;
|
|
}
|
|
|
|
ClosestMatchData data;
|
|
data.output = output;
|
|
data.count = count;
|
|
data.score_threshold = score_threshold;
|
|
data.infimum = 0.0;
|
|
data.test = it;
|
|
if (it->bytes) {
|
|
data.bytes_combined = build_combined_bytes (it->bytes);
|
|
} else {
|
|
data.bytes_combined = NULL;
|
|
}
|
|
|
|
RAnalFunction *fcn;
|
|
RListIter *iter;
|
|
r_list_foreach (a->fcns, iter, fcn) {
|
|
// turn function into signature item
|
|
RSignItem *fsig = r_sign_item_new ();
|
|
if (!fsig) {
|
|
r_list_free (output);
|
|
return NULL;
|
|
}
|
|
if (data.bytes_combined) {
|
|
r_sign_addto_item (a, fsig, fcn, R_SIGN_BYTES);
|
|
}
|
|
if (it->graph) {
|
|
r_sign_addto_item (a, fsig, fcn, R_SIGN_GRAPH);
|
|
}
|
|
r_sign_addto_item (a, fsig, fcn, R_SIGN_OFFSET);
|
|
fsig->name = r_str_new (fcn->name);
|
|
|
|
// maybe add signature item to output list
|
|
closest_match_update (fsig, &data);
|
|
}
|
|
free (data.bytes_combined);
|
|
return output;
|
|
}
|
|
|
|
R_API bool r_sign_diff(RAnal *a, RSignOptions *options, const char *other_space_name) {
|
|
r_return_val_if_fail (a && other_space_name, false);
|
|
|
|
RSpace *current_space = r_spaces_current (&a->zign_spaces);
|
|
if (!current_space) {
|
|
return false;
|
|
}
|
|
RSpace *other_space = r_spaces_get (&a->zign_spaces, other_space_name);
|
|
if (!other_space) {
|
|
return false;
|
|
}
|
|
|
|
RList *la = deserialize_sign_space (a, current_space);
|
|
if (!la) {
|
|
return false;
|
|
}
|
|
RList *lb = deserialize_sign_space (a, other_space);
|
|
if (!lb) {
|
|
r_list_free (la);
|
|
return false;
|
|
}
|
|
|
|
eprintf ("Diff %d %d\n", (int)ls_length (la), (int)ls_length (lb));
|
|
|
|
RListIter *itr;
|
|
RListIter *itr2;
|
|
RSignItem *si;
|
|
RSignItem *si2;
|
|
|
|
// do the sign diff here
|
|
r_list_foreach (la, itr, si) {
|
|
if (strstr (si->name, "imp.")) {
|
|
continue;
|
|
}
|
|
r_list_foreach (lb, itr2, si2) {
|
|
if (strstr (si2->name, "imp.")) {
|
|
continue;
|
|
}
|
|
double bytesScore = matchBytes (si, si2);
|
|
double graphScore = matchGraph (si, si2);
|
|
bool bytesMatch = bytesScore >= (options ? options->bytes_diff_threshold : SIGN_DIFF_MATCH_BYTES_THRESHOLD);
|
|
bool graphMatch = graphScore >= (options ? options->graph_diff_threshold : SIGN_DIFF_MATCH_GRAPH_THRESHOLD);
|
|
|
|
if (bytesMatch) {
|
|
a->cb_printf ("0x%08" PFMT64x " 0x%08"PFMT64x " %02.5lf B %s\n", si->addr, si2->addr, bytesScore, si->name);
|
|
}
|
|
|
|
if (graphMatch) {
|
|
a->cb_printf ("0x%08" PFMT64x " 0x%08"PFMT64x" %02.5lf G %s\n", si->addr, si2->addr, graphScore, si->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
r_list_free (la);
|
|
r_list_free (lb);
|
|
return true;
|
|
}
|
|
|
|
R_API bool r_sign_diff_by_name(RAnal *a, RSignOptions *options, const char *other_space_name, bool not_matching) {
|
|
r_return_val_if_fail (a && other_space_name, false);
|
|
|
|
RSpace *current_space = r_spaces_current (&a->zign_spaces);
|
|
if (!current_space) {
|
|
return false;
|
|
}
|
|
RSpace *other_space = r_spaces_get (&a->zign_spaces, other_space_name);
|
|
if (!other_space) {
|
|
return false;
|
|
}
|
|
|
|
RList *la = deserialize_sign_space (a, current_space);
|
|
if (!la) {
|
|
return false;
|
|
}
|
|
RList *lb = deserialize_sign_space (a, other_space);
|
|
if (!lb) {
|
|
return false;
|
|
}
|
|
|
|
eprintf ("Diff by name %d %d (%s)\n", (int)ls_length (la), (int)ls_length (lb), not_matching? "not matching" : "matching");
|
|
|
|
RListIter *itr;
|
|
RListIter *itr2;
|
|
RSignItem *si;
|
|
RSignItem *si2;
|
|
size_t current_space_name_len = strlen (current_space->name);
|
|
size_t other_space_name_len = strlen (other_space->name);
|
|
|
|
r_list_foreach (la, itr, si) {
|
|
if (strstr (si->name, "imp.")) {
|
|
continue;
|
|
}
|
|
r_list_foreach (lb, itr2, si2) {
|
|
if (strcmp (si->name + current_space_name_len + 1, si2->name + other_space_name_len + 1)) {
|
|
continue;
|
|
}
|
|
// TODO: add config variable for threshold
|
|
double bytesScore = matchBytes (si, si2);
|
|
double graphScore = matchGraph (si, si2);
|
|
bool bytesMatch = bytesScore >= (options ? options->bytes_diff_threshold : SIGN_DIFF_MATCH_BYTES_THRESHOLD);
|
|
bool graphMatch = graphScore >= (options ? options->graph_diff_threshold : SIGN_DIFF_MATCH_GRAPH_THRESHOLD);
|
|
if ((bytesMatch && !not_matching) || (!bytesMatch && not_matching)) {
|
|
a->cb_printf ("0x%08"PFMT64x" 0x%08"PFMT64x" %02.5f B %s\n", si->addr, si2->addr, bytesScore, si->name);
|
|
}
|
|
if ((graphMatch && !not_matching) || (!graphMatch && not_matching)) {
|
|
a->cb_printf ("0x%08"PFMT64x" 0x%08"PFMT64x" %02.5f G %s\n", si->addr, si2->addr, graphScore, si->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
r_list_free (la);
|
|
r_list_free (lb);
|
|
|
|
return true;
|
|
}
|
|
|
|
struct ctxListCB {
|
|
RAnal *anal;
|
|
int idx;
|
|
int format;
|
|
PJ *pj;
|
|
};
|
|
|
|
static void listBytes(RAnal *a, RSignItem *it, PJ *pj, int format) {
|
|
RSignBytes *bytes = it->bytes;
|
|
|
|
if (!bytes->bytes) {
|
|
return;
|
|
}
|
|
|
|
int masked = 0, i = 0;
|
|
for (i = 0; i < bytes->size; i++) {
|
|
masked += bytes->mask[i] == 0xff;
|
|
}
|
|
|
|
char * strbytes = r_hex_bin2strdup (bytes->bytes, bytes->size);
|
|
if (!strbytes) {
|
|
return;
|
|
}
|
|
char * strmask = r_hex_bin2strdup (bytes->mask, bytes->size);
|
|
if (!strmask) {
|
|
free (strbytes);
|
|
return;
|
|
}
|
|
|
|
if (format == '*') {
|
|
if (masked == bytes->size) {
|
|
a->cb_printf ("za %s b %s\n", it->name, strbytes);
|
|
} else {
|
|
a->cb_printf ("za %s b %s:%s\n", it->name, strbytes, strmask);
|
|
}
|
|
} else if (format == 'q') {
|
|
a->cb_printf (" b(%d/%d)", masked, bytes->size);
|
|
} else if (format == 'j') {
|
|
pj_ks (pj, "bytes", strbytes);
|
|
pj_ks (pj, "mask", strmask);
|
|
} else {
|
|
a->cb_printf (" bytes: %s\n", strbytes);
|
|
a->cb_printf (" mask: %s\n", strmask);
|
|
}
|
|
|
|
free (strbytes);
|
|
free (strmask);
|
|
}
|
|
|
|
static void listGraph(RAnal *a, RSignItem *it, PJ *pj, int format) {
|
|
RSignGraph *graph = it->graph;
|
|
|
|
if (format == 'q') {
|
|
a->cb_printf (" g(cc=%d,nb=%d,e=%d,eb=%d,h=%d)",
|
|
graph->cc, graph->nbbs, graph->edges, graph->ebbs, graph->bbsum);
|
|
} else if (format == '*') {
|
|
a->cb_printf ("za %s g cc=%d nbbs=%d edges=%d ebbs=%d bbsum=%d\n",
|
|
it->name, graph->cc, graph->nbbs, graph->edges, graph->ebbs, graph->bbsum);
|
|
} else if (format == 'j') {
|
|
pj_ko (pj, "graph");
|
|
pj_kN (pj, "cc", graph->cc);
|
|
pj_kN (pj, "nbbs", graph->nbbs);
|
|
pj_kN (pj, "edges", graph->edges);
|
|
pj_kN (pj, "ebbs", graph->ebbs);
|
|
pj_kN (pj, "bbsum", graph->bbsum);
|
|
pj_end (pj);
|
|
} else {
|
|
a->cb_printf (" graph: cc=%d nbbs=%d edges=%d ebbs=%d bbsum=%d\n",
|
|
graph->cc, graph->nbbs, graph->edges, graph->ebbs, graph->bbsum);
|
|
}
|
|
}
|
|
|
|
static void listComment(RAnal *a, RSignItem *it, PJ *pj, int format) {
|
|
if (it->comment) {
|
|
if (format == 'q') {
|
|
// a->cb_printf (" addr(0x%08"PFMT64x")", it->addr);
|
|
a->cb_printf ("\n ; %s\n", it->comment);
|
|
} else if (format == '*') {
|
|
a->cb_printf ("%s\n", it->comment); // comment injection via CCu..
|
|
} else if (format == 'j') {
|
|
pj_ks (pj, "comments", it->comment);
|
|
} else {
|
|
a->cb_printf (" comment: 0x%08" PFMT64x "\n", it->addr);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void listRealname(RAnal *a, RSignItem *it, PJ *pj, int format) {
|
|
if (it->realname) {
|
|
if (format == 'q') {
|
|
// a->cb_printf (" addr(0x%08"PFMT64x")", it->addr);
|
|
} else if (format == '*') {
|
|
a->cb_printf ("za %s n %s\n", it->name, it->realname);
|
|
a->cb_printf ("afn %s @ 0x%08"PFMT64x"\n", it->realname, it->addr);
|
|
} else if (format == 'j') {
|
|
pj_ks (pj, "realname", it->realname);
|
|
} else {
|
|
a->cb_printf (" realname: %s\n", it->realname);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void listOffset(RAnal *a, RSignItem *it, PJ *pj, int format) {
|
|
if (format == 'q') {
|
|
// a->cb_printf (" addr(0x%08"PFMT64x")", it->addr);
|
|
} else if (format == '*') {
|
|
a->cb_printf ("za %s o 0x%08"PFMT64x"\n", it->name, it->addr);
|
|
} else if (format == 'j') {
|
|
pj_kN (pj, "addr", it->addr);
|
|
} else {
|
|
a->cb_printf (" addr: 0x%08"PFMT64x"\n", it->addr);
|
|
}
|
|
}
|
|
|
|
static void print_function_args_json(PJ *pj, char *arg_type) {
|
|
char *arg_name = strchr (arg_type, ',');
|
|
|
|
if (arg_name == NULL) {
|
|
return;
|
|
}
|
|
|
|
*arg_name = '\0';
|
|
++arg_name;
|
|
|
|
size_t len_arg_name = strlen (arg_name);
|
|
arg_name[len_arg_name - 1] = '\0';
|
|
|
|
pj_o (pj);
|
|
pj_ks (pj, "name", arg_name);
|
|
pj_ks (pj, "type", arg_type + 1);
|
|
pj_end (pj);
|
|
}
|
|
|
|
static void list_types_json(RSignItem *it, PJ *pj) {
|
|
pj_ka (pj, "types");
|
|
|
|
int i = 0;
|
|
char *element = NULL;
|
|
RListIter *iter = NULL;
|
|
r_list_foreach (it->types, iter, element) {
|
|
char *t = strdup (element);
|
|
char *sep = NULL;
|
|
if (i > 0 && (sep = strchr (t, '='))) {
|
|
*sep = '\0';
|
|
++sep;
|
|
print_function_args_json (pj, sep);
|
|
}
|
|
free (t);
|
|
i++;
|
|
}
|
|
pj_end (pj);
|
|
}
|
|
|
|
static void list_sign_list(RAnal *a, RList *l, PJ *pj, int fmt, int type, const char *name) {
|
|
const char *tname = r_sign_type_to_name (type);
|
|
switch (fmt) {
|
|
case '*':
|
|
a->cb_printf ("za %s %c ", name, type);
|
|
break;
|
|
case 'q':
|
|
a->cb_printf (" %s(%d)", tname, r_list_length (l));
|
|
return;
|
|
case 'j':
|
|
pj_ka (pj, tname);
|
|
break;
|
|
default:
|
|
if (l && !r_list_empty (l)) {
|
|
a->cb_printf (" %s: ", tname);
|
|
}
|
|
}
|
|
|
|
int i = 0;
|
|
char *ref = NULL;
|
|
RListIter *iter = NULL;
|
|
r_list_foreach (l, iter, ref) {
|
|
if (i > 0) {
|
|
if (fmt == '*') {
|
|
a->cb_printf (" ");
|
|
} else if (fmt != 'j') {
|
|
a->cb_printf (", ");
|
|
}
|
|
}
|
|
if (fmt == 'j') {
|
|
pj_s (pj, ref);
|
|
} else {
|
|
a->cb_printf ("%s", ref);
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (fmt == 'j') {
|
|
pj_end (pj);
|
|
} else {
|
|
a->cb_printf ("\n");
|
|
}
|
|
}
|
|
|
|
static void listHash(RAnal *a, RSignItem *it, PJ *pj, int format) {
|
|
if (!it->hash) {
|
|
return;
|
|
}
|
|
switch (format) {
|
|
case 'q':
|
|
if (it->hash->bbhash) {
|
|
a->cb_printf (" h(%08x)", r_str_hash (it->hash->bbhash));
|
|
}
|
|
break;
|
|
case '*':
|
|
if (it->hash->bbhash) {
|
|
a->cb_printf ("za %s h %s\n", it->name, it->hash->bbhash);
|
|
}
|
|
break;
|
|
case 'j':
|
|
pj_ko (pj, "hash");
|
|
if (it->hash->bbhash) {
|
|
pj_ks (pj, "bbhash", it->hash->bbhash);
|
|
}
|
|
pj_end (pj);
|
|
break;
|
|
default:
|
|
if (it->hash->bbhash) {
|
|
a->cb_printf (" bbhash: %s\n", it->hash->bbhash);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool listCB(RSignItem *it, void *user) {
|
|
struct ctxListCB *ctx = (struct ctxListCB *)user;
|
|
RAnal *a = ctx->anal;
|
|
|
|
// Start item
|
|
if (ctx->format == 'j') {
|
|
pj_o (ctx->pj);
|
|
}
|
|
|
|
// Zignspace and name (except for radare format)
|
|
if (ctx->format == '*') {
|
|
if (it->space) {
|
|
a->cb_printf ("zs %s\n", it->space->name);
|
|
} else {
|
|
a->cb_printf ("zs *\n");
|
|
}
|
|
} else if (ctx->format == 'q') {
|
|
a->cb_printf ("0x%08" PFMT64x " ", it->addr);
|
|
const char *pad = r_str_pad (' ', 30 - strlen (it->name));
|
|
a->cb_printf ("%s:%s", it->name, pad);
|
|
} else if (ctx->format == 'j') {
|
|
if (it->space) {
|
|
pj_ks (ctx->pj, "zignspace", it->space->name);
|
|
}
|
|
pj_ks (ctx->pj, "name", it->name);
|
|
} else {
|
|
if (!r_spaces_current (&a->zign_spaces) && it->space) {
|
|
a->cb_printf ("(%s) ", it->space->name);
|
|
}
|
|
a->cb_printf ("%s:\n", it->name);
|
|
}
|
|
|
|
// TODO: listCollisions, listXRefs, listRefs... all just dump RList's of
|
|
// strings, replace them with something more abstract
|
|
|
|
// Bytes pattern
|
|
if (it->bytes) {
|
|
listBytes (a, it, ctx->pj, ctx->format);
|
|
} else if (ctx->format == 'j') {
|
|
pj_ks (ctx->pj, "bytes", "");
|
|
}
|
|
// Graph metrics
|
|
if (it->graph) {
|
|
listGraph (a, it, ctx->pj, ctx->format);
|
|
} else if (ctx->format == 'j') {
|
|
pj_ko (ctx->pj, "graph");
|
|
pj_end (ctx->pj);
|
|
}
|
|
// Offset
|
|
if (it->addr != UT64_MAX) {
|
|
listOffset (a, it, ctx->pj, ctx->format);
|
|
} else if (ctx->format == 'j') {
|
|
pj_kN (ctx->pj, "addr", -1);
|
|
}
|
|
// Name
|
|
if (it->realname) {
|
|
listRealname (a, it, ctx->pj, ctx->format);
|
|
}
|
|
// Comments
|
|
if (it->comment) {
|
|
listComment (a, it, ctx->pj, ctx->format);
|
|
}
|
|
// References
|
|
if (it->refs) {
|
|
list_sign_list (a, it->refs, ctx->pj, ctx->format, R_SIGN_REFS, it->name);
|
|
} else if (ctx->format == 'j') {
|
|
pj_ka (ctx->pj, "refs");
|
|
pj_end (ctx->pj);
|
|
}
|
|
// XReferences
|
|
if (it->xrefs) {
|
|
list_sign_list (a, it->xrefs, ctx->pj, ctx->format, R_SIGN_XREFS, it->name);
|
|
} else if (ctx->format == 'j') {
|
|
pj_ka (ctx->pj, "xrefs");
|
|
pj_end (ctx->pj);
|
|
}
|
|
// Vars
|
|
if (it->vars) {
|
|
list_sign_list (a, it->vars, ctx->pj, ctx->format, R_SIGN_VARS, it->name);
|
|
} else if (ctx->format == 'j') {
|
|
pj_ka (ctx->pj, "vars");
|
|
pj_end (ctx->pj);
|
|
}
|
|
// Types
|
|
if (it->types) {
|
|
if (ctx->format == 'j') {
|
|
list_types_json (it, ctx->pj);
|
|
} else {
|
|
list_sign_list (a, it->types, ctx->pj, ctx->format, R_SIGN_TYPES, it->name);
|
|
}
|
|
} else if (ctx->format == 'j') {
|
|
pj_ka (ctx->pj, "types");
|
|
pj_end (ctx->pj);
|
|
}
|
|
|
|
// Collisions
|
|
if (it->collisions) {
|
|
list_sign_list (a, it->collisions, ctx->pj, ctx->format, R_SIGN_COLLISIONS, it->name);
|
|
} else if (ctx->format == 'j') {
|
|
pj_ka (ctx->pj, "collisions");
|
|
pj_end (ctx->pj);
|
|
}
|
|
|
|
// Hash
|
|
if (it->hash) {
|
|
listHash (a, it, ctx->pj, ctx->format);
|
|
} else if (ctx->format == 'j') {
|
|
pj_ko (ctx->pj, "hash");
|
|
pj_end (ctx->pj);
|
|
}
|
|
|
|
// End item
|
|
if (ctx->format == 'j') {
|
|
pj_end (ctx->pj);
|
|
}
|
|
if (ctx->format == 'q') {
|
|
a->cb_printf ("\n");
|
|
}
|
|
|
|
ctx->idx++;
|
|
return true;
|
|
}
|
|
|
|
R_API void r_sign_list(RAnal *a, int format) {
|
|
r_return_if_fail (a);
|
|
PJ *pj = NULL;
|
|
|
|
if (format == 'j') {
|
|
pj = a->coreb.pjWithEncoding (a->coreb.core);
|
|
pj_a (pj);
|
|
}
|
|
|
|
struct ctxListCB ctx = { a, 0, format, pj };
|
|
r_sign_foreach (a, listCB, &ctx);
|
|
|
|
if (format == 'j') {
|
|
pj_end (pj);
|
|
a->cb_printf ("%s\n", pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
}
|
|
|
|
static bool listGetCB(RSignItem *it, void *user) {
|
|
r_list_append ((RList *)user, it);
|
|
return 1;
|
|
}
|
|
|
|
R_API const char *r_sign_type_to_name(int type) {
|
|
switch (type) {
|
|
case R_SIGN_BYTES:
|
|
return "bytes";
|
|
case R_SIGN_BYTES_MASK:
|
|
return "mask";
|
|
case R_SIGN_BYTES_SIZE:
|
|
return "size";
|
|
case R_SIGN_COMMENT:
|
|
return "comment";
|
|
case R_SIGN_GRAPH:
|
|
return "graph";
|
|
case R_SIGN_OFFSET:
|
|
return "addr";
|
|
case R_SIGN_NAME:
|
|
return "name";
|
|
case R_SIGN_REFS:
|
|
return "refs";
|
|
case R_SIGN_XREFS:
|
|
return "xrefs";
|
|
case R_SIGN_VARS:
|
|
return "vars";
|
|
case R_SIGN_TYPES:
|
|
return "types";
|
|
case R_SIGN_COLLISIONS:
|
|
return "collisions";
|
|
case R_SIGN_BBHASH:
|
|
return "bbhash";
|
|
default:
|
|
r_warn_if_reached ();
|
|
return "UnkownType";
|
|
}
|
|
}
|
|
|
|
static int cmpaddr(const void *_a, const void *_b) {
|
|
const RAnalBlock *a = _a, *b = _b;
|
|
return (a->addr - b->addr);
|
|
}
|
|
|
|
R_API char *r_sign_calc_bbhash(RAnal *a, RAnalFunction *fcn) {
|
|
RListIter *iter = NULL;
|
|
RAnalBlock *bbi = NULL;
|
|
char *digest_hex = NULL;
|
|
RHash *ctx = r_hash_new (true, R_ZIGN_HASH);
|
|
if (!ctx) {
|
|
goto beach;
|
|
}
|
|
r_list_sort (fcn->bbs, &cmpaddr);
|
|
r_hash_do_begin (ctx, R_ZIGN_HASH);
|
|
r_list_foreach (fcn->bbs, iter, bbi) {
|
|
ut8 *buf = malloc (bbi->size);
|
|
if (!buf) {
|
|
goto beach;
|
|
}
|
|
if (!a->iob.read_at (a->iob.io, bbi->addr, buf, bbi->size)) {
|
|
goto beach;
|
|
}
|
|
if (!r_hash_do_sha256 (ctx, buf, bbi->size)) {
|
|
goto beach;
|
|
}
|
|
free (buf);
|
|
}
|
|
r_hash_do_end (ctx, R_ZIGN_HASH);
|
|
|
|
digest_hex = r_hex_bin2strdup (ctx->digest, r_hash_size (R_ZIGN_HASH));
|
|
beach:
|
|
free (ctx);
|
|
return digest_hex;
|
|
}
|
|
|
|
static bool countForCB(RSignItem *it, void *user) {
|
|
(*(int *)user)++;
|
|
return true;
|
|
}
|
|
|
|
static bool unsetForCB(RSignItem *it, void *user) {
|
|
Sdb *db = (Sdb *)user;
|
|
char *key = item_serialize_key (it);
|
|
if (key) {
|
|
sdb_remove (db, key, 0);
|
|
free (key);
|
|
}
|
|
it->space = NULL;
|
|
r_sign_set_item (db, it, NULL);
|
|
return true;
|
|
}
|
|
|
|
struct ctxRenameForCB {
|
|
RAnal *anal;
|
|
char *oprefix; // old prefix
|
|
const char *newname;
|
|
size_t oldlen;
|
|
};
|
|
|
|
static bool renameForCB(void *user, const char *k, const char *v) {
|
|
struct ctxRenameForCB *ctx = (struct ctxRenameForCB *) user;
|
|
Sdb *db = ctx->anal->sdb_zigns;
|
|
if (!strncmp (k, ctx->oprefix, ctx->oldlen)) {
|
|
char *nk = str_serialize_key (ctx->newname, k + ctx->oldlen);
|
|
char *nv = strdup (v);
|
|
if (nk && nv) {
|
|
// must remove before set, must alloc new nk and nv before hand
|
|
sdb_remove (db, k, 0);
|
|
sdb_set (db, nk, nv, 0);
|
|
}
|
|
free (nv);
|
|
free (nk);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
R_API void r_sign_space_rename_for(RAnal *a, const RSpace *space, const char *oname, const char *nname) {
|
|
r_return_if_fail (a && space && oname && nname);
|
|
struct ctxRenameForCB ctx = { .anal = a, .newname = nname };
|
|
ctx.oprefix = str_serialize_key (oname, "");
|
|
if (ctx.oprefix) {
|
|
ctx.oldlen = strlen (ctx.oprefix);
|
|
sdb_foreach (a->sdb_zigns, renameForCB, &ctx);
|
|
}
|
|
free (ctx.oprefix);
|
|
}
|
|
|
|
struct ctxForeachCB {
|
|
RAnal *anal;
|
|
RSignForeachCallback cb;
|
|
const RSpace *space;
|
|
bool freeit;
|
|
void *user;
|
|
};
|
|
|
|
static bool foreachCB(void *user, const char *k, const char *v) {
|
|
struct ctxForeachCB *ctx = (struct ctxForeachCB *) user;
|
|
r_return_val_if_fail (ctx && ctx->cb, false);
|
|
RSignItem *it = r_sign_item_new ();
|
|
RAnal *a = ctx->anal;
|
|
|
|
bool keep_going = true;
|
|
if (it && r_sign_deserialize (a, it, k, v)) {
|
|
if (!ctx->space || ctx->space == it->space) {
|
|
keep_going = ctx->cb (it, ctx->user);
|
|
}
|
|
} else {
|
|
eprintf ("error: cannot deserialize zign\n");
|
|
}
|
|
if (ctx->freeit) {
|
|
r_sign_item_free (it);
|
|
}
|
|
return keep_going;
|
|
}
|
|
|
|
static inline bool local_foreach_item(RAnal *a, RSignForeachCallback cb, const RSpace *sp, bool freeit, void *user) {
|
|
r_return_val_if_fail (a && cb, false);
|
|
struct ctxForeachCB ctx = { a, cb, sp, freeit, user };
|
|
return sdb_foreach (a->sdb_zigns, foreachCB, &ctx);
|
|
}
|
|
|
|
static bool r_sign_foreach_nofree(RAnal *a, RSignForeachCallback cb, void *user) {
|
|
return local_foreach_item (a, cb, r_spaces_current (&a->zign_spaces), false, user);
|
|
}
|
|
|
|
R_API int r_sign_space_count_for(RAnal *a, const RSpace *space) {
|
|
int count = 0;
|
|
local_foreach_item (a, countForCB, space, true, &count);
|
|
return count;
|
|
}
|
|
|
|
R_API void r_sign_space_unset_for(RAnal *a, const RSpace *space) {
|
|
local_foreach_item (a, unsetForCB, space, true, a->sdb_zigns);
|
|
}
|
|
|
|
R_API bool r_sign_foreach(RAnal *a, RSignForeachCallback cb, void *user) {
|
|
return local_foreach_item (a, cb, r_spaces_current (&a->zign_spaces), true, user);
|
|
}
|
|
|
|
R_API RList *r_sign_get_list(RAnal *a) {
|
|
r_return_val_if_fail (a, NULL);
|
|
RList *l = r_list_newf ((RListFree)r_sign_item_free);
|
|
if (l) {
|
|
local_foreach_item (a, listGetCB, NULL, false, (void *)l);
|
|
}
|
|
return l;
|
|
}
|
|
|
|
R_API RSignSearch *r_sign_search_new(void) {
|
|
RSignSearch *ret = R_NEW0 (RSignSearch);
|
|
if (ret) {
|
|
ret->search = r_search_new (R_SEARCH_KEYWORD);
|
|
ret->items = r_list_newf ((RListFree) r_sign_item_free);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
R_API void r_sign_search_free(RSignSearch *ss) {
|
|
if (!ss) {
|
|
return;
|
|
}
|
|
r_search_free (ss->search);
|
|
r_list_free (ss->items);
|
|
free (ss);
|
|
}
|
|
|
|
R_API RList *r_sign_find_closest_sig(RAnal *a, RSignItem *it, int count, double score_threshold) {
|
|
r_return_val_if_fail (a && it && count > 0 && score_threshold >= 0 && score_threshold <= 1, NULL);
|
|
|
|
// need at least one acceptable signature type
|
|
r_return_val_if_fail (it->bytes || it->graph, NULL);
|
|
|
|
ClosestMatchData data;
|
|
RList *output = r_list_newf ((RListFree)r_sign_close_match_free);
|
|
if (!output) {
|
|
return NULL;
|
|
}
|
|
|
|
data.output = output;
|
|
data.count = count;
|
|
data.score_threshold = score_threshold;
|
|
data.infimum = 0.0;
|
|
data.test = it;
|
|
if (it->bytes) {
|
|
data.bytes_combined = build_combined_bytes (it->bytes);
|
|
} else {
|
|
data.bytes_combined = NULL;
|
|
}
|
|
|
|
if (!r_sign_foreach_nofree (a, _closest_match_cb, &data)) {
|
|
r_list_free (output);
|
|
output = NULL;
|
|
}
|
|
|
|
free (data.bytes_combined);
|
|
return output;
|
|
}
|
|
|
|
static int searchHitCB(RSearchKeyword *kw, void *user, ut64 addr) {
|
|
RSignSearch *ss = (RSignSearch *) user;
|
|
return ss->cb? ss->cb ((RSignItem *) kw->data, kw, addr, ss->user): 1;
|
|
}
|
|
|
|
struct ctxAddSearchKwCB {
|
|
RSignSearch *ss;
|
|
int minsz;
|
|
};
|
|
|
|
static bool addSearchKwCB(RSignItem *it, void *user) {
|
|
struct ctxAddSearchKwCB *ctx = (struct ctxAddSearchKwCB *) user;
|
|
RSignSearch *ss = ctx->ss;
|
|
RSignBytes *bytes = it->bytes;
|
|
|
|
if (!bytes) {
|
|
eprintf ("Cannot find bytes for this signature: %s\n", it->name);
|
|
r_sign_item_free (it);
|
|
return true;
|
|
}
|
|
|
|
if (ctx->minsz && bytes->size < ctx->minsz) {
|
|
r_sign_item_free (it);
|
|
return true;
|
|
}
|
|
r_list_append (ss->items, it);
|
|
// TODO(nibble): change arg data in r_search_keyword_new to void*
|
|
RSearchKeyword *kw = r_search_keyword_new (bytes->bytes, bytes->size, bytes->mask, bytes->size, (const char *)it);
|
|
r_search_kw_add (ss->search, kw);
|
|
return true;
|
|
}
|
|
|
|
R_API void r_sign_search_init(RAnal *a, RSignSearch *ss, int minsz, RSignSearchCallback cb, void *user) {
|
|
struct ctxAddSearchKwCB ctx = { ss, minsz };
|
|
r_return_if_fail (a && ss && cb);
|
|
ss->cb = cb;
|
|
ss->user = user;
|
|
r_list_purge (ss->items);
|
|
r_search_reset (ss->search, R_SEARCH_KEYWORD);
|
|
r_sign_foreach_nofree (a, addSearchKwCB, &ctx);
|
|
r_search_begin (ss->search);
|
|
r_search_set_callback (ss->search, searchHitCB, ss);
|
|
}
|
|
|
|
R_API int r_sign_search_update(RAnal *a, RSignSearch *ss, ut64 *at, const ut8 *buf, int len) {
|
|
r_return_val_if_fail (a && ss && buf && len > 0, 0);
|
|
return r_search_update (ss->search, *at, buf, len);
|
|
}
|
|
|
|
// allow ~10% of margin error
|
|
static int matchCount(int a, int b) {
|
|
int m = R_MAX (a, b);
|
|
if (m > 100) {
|
|
return R_ABS (a - b) < m / 10;
|
|
}
|
|
return a == b;
|
|
}
|
|
|
|
static int sig_graph_diff(RSignItem *ia, RSignItem *ib) {
|
|
RSignGraph *a = ia->graph;
|
|
RSignGraph *b = ib->graph;
|
|
if (!a || !b) {
|
|
return 1;
|
|
}
|
|
if (a->cc != -1 && a->cc != b->cc) {
|
|
return 1;
|
|
}
|
|
if (a->nbbs != -1 && a->nbbs != b->nbbs) {
|
|
return 1;
|
|
}
|
|
if (a->edges != -1 && a->edges != b->edges) {
|
|
return 1;
|
|
}
|
|
if (a->ebbs != -1 && a->ebbs != b->ebbs) {
|
|
return 1;
|
|
}
|
|
if (a->bbsum > 0 && !matchCount (a->bbsum, b->bbsum)) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define SORT_EMPY_LAST(x, y) \
|
|
if (!x) { \
|
|
return !y? 1: 0; \
|
|
} \
|
|
if (!y) { \
|
|
return -1; \
|
|
}
|
|
|
|
static int sig_graph_cmp(RSignItem *ia, RSignItem *ib) {
|
|
RSignGraph *a = ia->graph;
|
|
RSignGraph *b = ib->graph;
|
|
|
|
SORT_EMPY_LAST (a, b);
|
|
|
|
int diff = a->bbsum - b->bbsum;
|
|
if (diff) {
|
|
return diff;
|
|
}
|
|
|
|
diff = a->cc - b->cc;
|
|
if (diff) {
|
|
return diff;
|
|
}
|
|
|
|
diff = a->nbbs - b->nbbs;
|
|
if (diff) {
|
|
return diff;
|
|
}
|
|
|
|
diff = a->edges - b->edges;
|
|
if (diff) {
|
|
return diff;
|
|
}
|
|
|
|
diff = a->ebbs - b->ebbs;
|
|
if (diff) {
|
|
return diff;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// this is to compare a byte signature to a function, that math is slightly
|
|
// different
|
|
static int sig_bytes_diff(RSignItem *isig, RSignItem *ifunc) {
|
|
RSignBytes *sig = isig->bytes;
|
|
RSignBytes *func = ifunc->bytes;
|
|
r_return_val_if_fail (sig && func, 1);
|
|
|
|
if (sig->size != func->size) {
|
|
return 1;
|
|
}
|
|
int i;
|
|
for (i = 0; i < sig->size; i++) {
|
|
char m = sig->mask[i];
|
|
if (m && (sig->bytes[i] & m) != (func->bytes[i] & m)) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sig_bytes_cmp(RSignItem *ia, RSignItem *ib) {
|
|
RSignBytes *a = ia->bytes;
|
|
RSignBytes *b = ib->bytes;
|
|
SORT_EMPY_LAST (a, b);
|
|
int i = a->size - b->size;
|
|
if (i) {
|
|
return i;
|
|
}
|
|
for (i = 0; i < a->size; i++) {
|
|
int cmp = a->bytes[i] & a->mask[i];
|
|
cmp -= b->bytes[i] & b->mask[i];
|
|
if (cmp) {
|
|
return cmp;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sig_addr_cmp(ut64 a, ut64 b) {
|
|
if (a < b) {
|
|
return 1;
|
|
}
|
|
return a > b? -1: 0;
|
|
}
|
|
|
|
static int sig_hash_cmp(RSignHash *a, RSignHash *b) {
|
|
if (!a) {
|
|
return b? -1: 0;
|
|
}
|
|
if (!b) {
|
|
return 1;
|
|
}
|
|
return strcmp (a->bbhash, b->bbhash);
|
|
}
|
|
|
|
static int str_list_cmp(RList *la, RList *lb) {
|
|
if (!la) {
|
|
return lb? -1: 0;
|
|
}
|
|
if (!lb) {
|
|
return 1;
|
|
}
|
|
|
|
int len = r_list_length (la);
|
|
int dif = len - r_list_length (lb);
|
|
if (dif) {
|
|
return dif;
|
|
}
|
|
|
|
size_t i;
|
|
for (i = 0; i < len; i++) {
|
|
const char *a = r_list_get_n (la, i);
|
|
const char *b = r_list_get_n (lb, i);
|
|
if ((dif = strcmp (a, b))) {
|
|
return dif;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool type_in_array(RSignType *arr, RSignType needle) {
|
|
while (*arr != R_SIGN_END) {
|
|
if (*arr == needle) {
|
|
return true;
|
|
}
|
|
arr++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static RListIter *collision_skip_unused(RListIter *iter, RSignType *used) {
|
|
char *n = (char *)r_list_iter_get_data (iter);
|
|
if (!n) {
|
|
return r_list_iter_get_next (iter);
|
|
}
|
|
RSignType skip = n[0];
|
|
if (type_in_array (used, skip)) {
|
|
return iter;
|
|
}
|
|
RListIter *next;
|
|
while ((next = r_list_iter_get_next (iter))) {
|
|
n = r_list_iter_get_data (next);
|
|
if (n) {
|
|
RSignType t = n[0];
|
|
if (skip != t) {
|
|
if (type_in_array (used, t)) {
|
|
return iter;
|
|
}
|
|
}
|
|
}
|
|
iter = next;
|
|
}
|
|
return iter;
|
|
}
|
|
|
|
// return NULL one error, otherwise return a, possibly empty, list of
|
|
// collisions. Relies on sets being ordered in groups of types
|
|
static RList *check_collisions(RList *collisions, RSignType *types) {
|
|
if (!collisions || types[0] == R_SIGN_END) {
|
|
return r_list_new ();
|
|
}
|
|
|
|
RListIter *iter = r_list_iterator (collisions);
|
|
if (!iter) {
|
|
return NULL;
|
|
}
|
|
|
|
// skip over types that were not matched against
|
|
if (!(iter = collision_skip_unused (iter, types))) {
|
|
return r_list_new ();
|
|
}
|
|
|
|
char *col = (char *)r_list_iter_get_data (iter);
|
|
if (!col || col[1] != ':') {
|
|
return NULL;
|
|
}
|
|
|
|
RList *set = r_list_new ();
|
|
if (!set) {
|
|
return NULL;
|
|
}
|
|
RList *holder = NULL;
|
|
|
|
// add the names from first matched type to return set
|
|
RSignType thistype = col[0];
|
|
while (iter) {
|
|
r_list_append (set, col + 2);
|
|
if ((iter = r_list_iter_get_next (iter))) {
|
|
col = (char *)r_list_iter_get_data (iter);
|
|
if (!col || col[1] != ':') {
|
|
goto collerr;
|
|
}
|
|
RSignType t = col[1];
|
|
if (t != thistype) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
holder = r_list_new ();
|
|
if (!holder) {
|
|
goto collerr;
|
|
}
|
|
|
|
// now we interset return set with next relevent type and repeat
|
|
while (iter && (iter = collision_skip_unused (iter, types))) { // loop over new type group
|
|
col = (char *)r_list_iter_get_data (iter);
|
|
if (!col || col[1] != ':') {
|
|
goto collerr;
|
|
}
|
|
|
|
// interset current type
|
|
RSignType t = col[0];
|
|
RSignType nexttype = t;
|
|
while (t == nexttype) {
|
|
char *name = col + 2;
|
|
if (r_list_find (set, name, list_str_cmp)) {
|
|
r_list_append (holder, col + 2);
|
|
}
|
|
nexttype = R_SIGN_END;
|
|
if ((iter = r_list_iter_get_next (iter))) {
|
|
col = (char *)r_list_iter_get_data (iter);
|
|
if (!col || col[1] != ':') {
|
|
goto collerr;
|
|
}
|
|
nexttype = col[1];
|
|
}
|
|
}
|
|
r_list_purge (set);
|
|
RList *tmplist = set;
|
|
set = holder;
|
|
holder = tmplist;
|
|
}
|
|
|
|
r_list_free (holder);
|
|
return set;
|
|
|
|
collerr:
|
|
r_list_free (set);
|
|
r_list_free (holder);
|
|
return NULL;
|
|
}
|
|
|
|
struct metric_ctx {
|
|
int matched;
|
|
RSignItem *it;
|
|
RSignSearchMetrics *sm;
|
|
RAnalFunction *fcn;
|
|
};
|
|
|
|
static bool match_metrics(RSignItem *it, void *user) {
|
|
struct metric_ctx *ctx = (struct metric_ctx *)user;
|
|
RSignSearchMetrics *sm = ctx->sm;
|
|
RSignItem *fit = ctx->it;
|
|
RSignType types[7];
|
|
int count = 0;
|
|
|
|
if (it->bytes && it->bytes->size >= sm->minsz && !sig_bytes_diff (it, fit)) {
|
|
types[count++] = R_SIGN_BYTES;
|
|
}
|
|
if (it->graph && it->graph->cc >= sm->mincc && !sig_graph_diff (it, fit)) {
|
|
types[count++] = R_SIGN_GRAPH;
|
|
}
|
|
if (fit->addr != UT64_MAX && !sig_addr_cmp (it->addr, fit->addr)) {
|
|
types[count++] = R_SIGN_OFFSET;
|
|
}
|
|
if (fit->hash && !sig_hash_cmp (it->hash, fit->hash)) {
|
|
types[count++] = R_SIGN_BBHASH;
|
|
}
|
|
if (fit->refs && !str_list_cmp (it->refs, fit->refs)) {
|
|
types[count++] = R_SIGN_REFS;
|
|
}
|
|
if (fit->vars && !str_list_cmp (it->vars, fit->vars)) {
|
|
types[count++] = R_SIGN_VARS;
|
|
}
|
|
if (fit->types && !str_list_cmp (it->types, fit->types)) {
|
|
types[count++] = R_SIGN_TYPES;
|
|
}
|
|
|
|
bool keep_searching = true;
|
|
if (count) {
|
|
RList *col = check_collisions (it->collisions, types);
|
|
ctx->matched += count;
|
|
types[count] = R_SIGN_END;
|
|
if (col && r_list_length (col) == 0) {
|
|
keep_searching = false;
|
|
}
|
|
sm->cb (it, ctx->fcn, types, sm->user, col);
|
|
r_list_free (col);
|
|
}
|
|
return keep_searching;
|
|
}
|
|
|
|
static bool _sig_to_vec_cb(RSignItem *it, void *user) {
|
|
if (it->collisions) {
|
|
r_list_free (it->collisions);
|
|
it->collisions = NULL;
|
|
}
|
|
return r_pvector_push ((RPVector *)user, it)? true: false;
|
|
}
|
|
|
|
static bool item_addto_collisions(RSignItem *it, const char *add) {
|
|
r_return_val_if_fail (it, false);
|
|
if (!it->collisions) {
|
|
it->collisions = r_list_newf (free);
|
|
if (!it->collisions) {
|
|
return false;
|
|
}
|
|
}
|
|
RList *l = it->collisions;
|
|
if (r_list_find (l, (void *)add, list_str_cmp)) {
|
|
return true;
|
|
}
|
|
char *dup = strdup (add);
|
|
if (!dup) {
|
|
return false;
|
|
}
|
|
return r_list_append (l, dup)? true: false;
|
|
}
|
|
|
|
static bool update_collide(RPVector *sigs, int start, int end, int type) {
|
|
r_return_val_if_fail (start >= 0 && end > 0 && sigs, false);
|
|
int i, ii;
|
|
for (i = start; i <= end; i++) {
|
|
RSignItem *it = (RSignItem *)r_pvector_at (sigs, i);
|
|
if (!it) {
|
|
return false;
|
|
}
|
|
char *fmt = r_str_newf ("%c:%s", type, it->name);
|
|
if (!fmt) {
|
|
return false;
|
|
}
|
|
for (ii = start; ii <= end; ii++) {
|
|
if (i != ii) {
|
|
RSignItem *itt = (RSignItem *)r_pvector_at (sigs, ii);
|
|
if (!item_addto_collisions (itt, fmt)) {
|
|
free (fmt);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
free (fmt);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool item_has_type(RSignItem *it, RSignType t) {
|
|
switch (t) {
|
|
case R_SIGN_BYTES:
|
|
case R_SIGN_BYTES_MASK:
|
|
case R_SIGN_BYTES_SIZE:
|
|
return it->bytes? true: false;
|
|
case R_SIGN_COMMENT:
|
|
return it->comment? true: false;
|
|
case R_SIGN_GRAPH:
|
|
return it->graph? true: false;
|
|
case R_SIGN_OFFSET:
|
|
return it->addr != UT64_MAX? true: false;
|
|
case R_SIGN_NAME:
|
|
return it->realname? true: false;
|
|
case R_SIGN_REFS:
|
|
return it->refs? true: false;
|
|
case R_SIGN_XREFS:
|
|
return it->xrefs? true: false;
|
|
case R_SIGN_VARS:
|
|
return it->vars? true: false;
|
|
case R_SIGN_TYPES:
|
|
return it->types? true: false;
|
|
case R_SIGN_COLLISIONS:
|
|
return it->collisions? true: false;
|
|
case R_SIGN_BBHASH:
|
|
return (it->hash && it->hash->bbhash)? true: false;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
typedef int (*RSignSorter) (RSignItem *, RSignItem *);
|
|
RSignSorter type_to_cmp(int type, bool exact) {
|
|
switch (type) {
|
|
case R_SIGN_GRAPH:
|
|
if (exact) {
|
|
return sig_graph_diff;
|
|
}
|
|
return sig_graph_cmp;
|
|
case R_SIGN_BYTES:
|
|
return sig_bytes_cmp;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static inline bool sign_collide_by(RPVector *sigs, RSignType type) {
|
|
RSignSorter cmp = type_to_cmp (type, false);
|
|
if (!cmp) {
|
|
return false;
|
|
}
|
|
r_pvector_sort (sigs, (RPVectorComparator)cmp);
|
|
// sorting and matching can be slightly different
|
|
cmp = type_to_cmp (type, true);
|
|
|
|
void **p;
|
|
int i, start, end;
|
|
RSignItem *old = NULL;
|
|
i = 0;
|
|
start = end = -1;
|
|
r_pvector_foreach (sigs, p) {
|
|
RSignItem *it = *p;
|
|
if (!item_has_type (it, type)) {
|
|
// sort algs should put NULL at bottom
|
|
break;
|
|
}
|
|
if (old) {
|
|
if (!cmp (old, it)) {
|
|
// signature collisions
|
|
if (start < 0) {
|
|
start = i - 1;
|
|
end = i;
|
|
} else {
|
|
end++;
|
|
}
|
|
} else if (start >= 0) {
|
|
// not a collision, but had a collision before
|
|
if (!update_collide (sigs, start, end, type)) {
|
|
return false;
|
|
}
|
|
start = end = -1;
|
|
}
|
|
}
|
|
old = it;
|
|
i++;
|
|
}
|
|
if (start >= 0 && !update_collide (sigs, start, end, type)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
R_API bool r_sign_resolve_collisions(RAnal *a) {
|
|
r_return_val_if_fail (a, false);
|
|
RPVector *sigs = r_pvector_new ((RPVectorFree)r_sign_item_free);
|
|
if (!sigs) {
|
|
return false;
|
|
}
|
|
|
|
if (!r_sign_foreach_nofree (a, _sig_to_vec_cb, (void *)sigs)) {
|
|
r_pvector_free (sigs);
|
|
return false;
|
|
}
|
|
int i = 0;
|
|
RSignType types[] = { R_SIGN_BYTES, R_SIGN_GRAPH, R_SIGN_END };
|
|
for (i = 0; types[i] != R_SIGN_END; i++) {
|
|
sign_collide_by (sigs, types[i]);
|
|
}
|
|
|
|
// save updated signatures
|
|
void **p;
|
|
r_pvector_foreach (sigs, p) {
|
|
RSignItem *it = *p;
|
|
if (it->collisions) {
|
|
r_list_sort (it->collisions, list_str_cmp);
|
|
r_sign_add_item (a, it);
|
|
}
|
|
}
|
|
|
|
r_pvector_free (sigs);
|
|
return true;
|
|
}
|
|
|
|
R_API int r_sign_metric_search(RAnal *a, RSignSearchMetrics *sm) {
|
|
r_return_val_if_fail (a && sm, -1);
|
|
int count = 0;
|
|
RAnalFunction *fcni;
|
|
RListIter *iter;
|
|
r_cons_break_push (NULL, NULL);
|
|
r_list_foreach (a->fcns, iter, fcni) {
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
int match = r_sign_fcn_match_metrics (sm, fcni);
|
|
if (match < 0) {
|
|
count = -1;
|
|
break;
|
|
}
|
|
count++;
|
|
}
|
|
r_cons_break_pop ();
|
|
return count;
|
|
}
|
|
|
|
R_API int r_sign_fcn_match_metrics(RSignSearchMetrics *sm, RAnalFunction *fcn) {
|
|
r_return_val_if_fail (sm && sm->mincc >= 0 && sm->anal && fcn, -1);
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (!it) {
|
|
return -1;
|
|
}
|
|
|
|
RSignType *t = sm->types;
|
|
while (*t != R_SIGN_END) {
|
|
if (*t == R_SIGN_BYTES) {
|
|
// no need for mask
|
|
it->bytes = r_sign_func_empty_mask (sm->anal, fcn);
|
|
} else {
|
|
r_sign_addto_item (sm->anal, it, fcn, *t);
|
|
}
|
|
t++;
|
|
}
|
|
|
|
if (it->graph && it->graph->cc < sm->mincc) {
|
|
r_sign_graph_free (it->graph);
|
|
it->graph = NULL;
|
|
}
|
|
struct metric_ctx ctx = { 0, it, sm, fcn };
|
|
r_sign_foreach (sm->anal, match_metrics, (void *)&ctx);
|
|
r_sign_item_free (it);
|
|
return ctx.matched;
|
|
}
|
|
|
|
R_API RSignItem *r_sign_item_new(void) {
|
|
RSignItem *ret = R_NEW0 (RSignItem);
|
|
if (ret) {
|
|
ret->addr = UT64_MAX;
|
|
ret->space = NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
R_API void r_sign_item_free(RSignItem *item) {
|
|
if (item) {
|
|
free (item->name);
|
|
r_sign_bytes_free (item->bytes);
|
|
r_sign_hash_free (item->hash);
|
|
r_sign_graph_free (item->graph);
|
|
free (item->comment);
|
|
free (item->realname);
|
|
r_list_free (item->refs);
|
|
r_list_free (item->vars);
|
|
r_list_free (item->xrefs);
|
|
r_list_free (item->types);
|
|
r_list_free (item->collisions);
|
|
free (item);
|
|
}
|
|
}
|
|
|
|
R_API void r_sign_graph_free(RSignGraph *graph) {
|
|
free (graph);
|
|
}
|
|
|
|
R_API void r_sign_bytes_free(RSignBytes *bytes) {
|
|
if (bytes) {
|
|
free (bytes->bytes);
|
|
free (bytes->mask);
|
|
free (bytes);
|
|
}
|
|
}
|
|
|
|
R_API void r_sign_hash_free(RSignHash *hash) {
|
|
if (hash) {
|
|
free (hash->bbhash);
|
|
free (hash);
|
|
}
|
|
}
|
|
|
|
static bool loadCB(void *user, const char *k, const char *v) {
|
|
RAnal *a = (RAnal *)user;
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (it && r_sign_deserialize (a, it, k, v)) {
|
|
r_sign_set_item (a->sdb_zigns, it, NULL);
|
|
} else {
|
|
eprintf ("error: cannot deserialize zign\n");
|
|
}
|
|
r_sign_item_free (it);
|
|
return true;
|
|
}
|
|
|
|
R_API char *r_sign_path(RAnal *a, const char *file) {
|
|
char *abs = r_file_abspath (file);
|
|
if (abs) {
|
|
if (r_file_is_regular (abs)) {
|
|
return abs;
|
|
}
|
|
free (abs);
|
|
}
|
|
|
|
if (a->zign_path) {
|
|
char *path = r_str_newf ("%s%s%s", a->zign_path, R_SYS_DIR, file);
|
|
abs = r_file_abspath (path);
|
|
free (path);
|
|
if (r_file_is_regular (abs)) {
|
|
return abs;
|
|
}
|
|
free (abs);
|
|
} else {
|
|
char *home = r_str_home (R2_HOME_ZIGNS);
|
|
abs = r_str_newf ("%s%s%s", home, R_SYS_DIR, file);
|
|
free (home);
|
|
if (r_file_is_regular (abs)) {
|
|
return abs;
|
|
}
|
|
free (abs);
|
|
}
|
|
|
|
abs = r_str_newf (R_JOIN_3_PATHS ("%s", R2_ZIGNS, "%s"), r_sys_prefix (NULL), file);
|
|
if (r_file_is_regular (abs)) {
|
|
return abs;
|
|
}
|
|
free (abs);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
R_API bool r_sign_load(RAnal *a, const char *file) {
|
|
if (!a || !file) {
|
|
return false;
|
|
}
|
|
char *path = r_sign_path (a, file);
|
|
if (!r_file_exists (path)) {
|
|
eprintf ("error: file %s does not exist\n", file);
|
|
free (path);
|
|
return false;
|
|
}
|
|
Sdb *db = sdb_new (NULL, path, 0);
|
|
if (!db) {
|
|
free (path);
|
|
return false;
|
|
}
|
|
sdb_foreach (db, loadCB, a);
|
|
sdb_close (db);
|
|
sdb_free (db);
|
|
free (path);
|
|
return true;
|
|
}
|
|
|
|
R_API bool r_sign_load_gz(RAnal *a, const char *filename) {
|
|
ut8 *buf = NULL;
|
|
int size = 0;
|
|
char *tmpfile = NULL;
|
|
bool retval = true;
|
|
|
|
char *path = r_sign_path (a, filename);
|
|
if (!r_file_exists (path)) {
|
|
eprintf ("error: file %s does not exist\n", filename);
|
|
retval = false;
|
|
goto out;
|
|
}
|
|
|
|
if (!(buf = r_file_gzslurp (path, &size, 0))) {
|
|
eprintf ("error: cannot decompress file\n");
|
|
retval = false;
|
|
goto out;
|
|
}
|
|
|
|
if (!(tmpfile = r_file_temp ("r2zign"))) {
|
|
eprintf ("error: cannot create temp file\n");
|
|
retval = false;
|
|
goto out;
|
|
}
|
|
|
|
if (!r_file_dump (tmpfile, buf, size, 0)) {
|
|
eprintf ("error: cannot dump file\n");
|
|
retval = false;
|
|
goto out;
|
|
}
|
|
|
|
if (!r_sign_load (a, tmpfile)) {
|
|
eprintf ("error: cannot load file\n");
|
|
retval = false;
|
|
goto out;
|
|
}
|
|
|
|
if (!r_file_rm (tmpfile)) {
|
|
eprintf ("error: cannot delete temp file\n");
|
|
retval = false;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
free (buf);
|
|
free (tmpfile);
|
|
free (path);
|
|
|
|
return retval;
|
|
}
|
|
|
|
R_API bool r_sign_save(RAnal *a, const char *file) {
|
|
r_return_val_if_fail (a && file, false);
|
|
|
|
if (sdb_isempty (a->sdb_zigns)) {
|
|
eprintf ("Warning: no zignatures to save\n");
|
|
return false;
|
|
}
|
|
|
|
Sdb *db = sdb_new (NULL, file, 0);
|
|
if (!db) {
|
|
return false;
|
|
}
|
|
sdb_merge (db, a->sdb_zigns);
|
|
bool retval = sdb_sync (db);
|
|
sdb_close (db);
|
|
sdb_free (db);
|
|
|
|
return retval;
|
|
}
|
|
|
|
R_API RSignOptions *r_sign_options_new(const char *bytes_thresh, const char *graph_thresh) {
|
|
RSignOptions *options = R_NEW0 (RSignOptions);
|
|
if (!options) {
|
|
return NULL;
|
|
}
|
|
|
|
options->bytes_diff_threshold = r_num_get_float (NULL, bytes_thresh);
|
|
options->graph_diff_threshold = r_num_get_float (NULL, graph_thresh);
|
|
|
|
if (options->bytes_diff_threshold > 1.0) {
|
|
options->bytes_diff_threshold = 1.0;
|
|
}
|
|
if (options->bytes_diff_threshold < 0) {
|
|
options->bytes_diff_threshold = 0.0;
|
|
}
|
|
if (options->graph_diff_threshold > 1.0) {
|
|
options->graph_diff_threshold = 1.0;
|
|
}
|
|
if (options->graph_diff_threshold < 0) {
|
|
options->graph_diff_threshold = 0.0;
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
R_API void r_sign_options_free(RSignOptions *options) {
|
|
R_FREE (options);
|
|
}
|