mirror of
https://github.com/radareorg/radare2.git
synced 2024-11-27 15:10:53 +00:00
3349 lines
79 KiB
C
3349 lines
79 KiB
C
/* radare - LGPL - Copyright 2009-2024 - pancake, nibble */
|
|
|
|
#include <r_core.h>
|
|
#include <r_vec.h>
|
|
#include <r_util/r_json.h>
|
|
|
|
R_LIB_VERSION (r_sign);
|
|
|
|
#define SIGN_DIFF_MATCH_BYTES_THRESHOLD 1.0
|
|
#define SIGN_DIFF_MATCH_GRAPH_THRESHOLD 1.0
|
|
|
|
R_VEC_TYPE (RVecAnalRef, RAnalRef);
|
|
|
|
static inline const char *get_xrefname(RCore *core, ut64 addr) {
|
|
RAnalFunction *f = r_anal_get_fcn_in (core->anal, addr, 0);
|
|
return f? f->name: NULL;
|
|
}
|
|
|
|
static const char *get_refname(RCore *core, ut64 addr) {
|
|
RFlagItem *item;
|
|
RListIter *iter;
|
|
|
|
const RList *list = r_flag_get_list (core->flags, addr);
|
|
if (!list) {
|
|
return NULL;
|
|
}
|
|
|
|
r_list_foreach (list, iter, item) {
|
|
if (!item->name || !r_str_startswith (item->name, "sym.")) {
|
|
continue;
|
|
}
|
|
return item->name;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int list_str_cmp(const void *a, const void *b) {
|
|
// prevent silent failure if RListComparator changes
|
|
return strcmp ((const char *)a, (const char *)b);
|
|
}
|
|
|
|
static ut64 valstr(const void *_a) {
|
|
const char *a = _a;
|
|
return r_str_hash64 (a);
|
|
}
|
|
|
|
R_API RList *r_sign_fcn_xrefs(RAnal *a, RAnalFunction *fcn) {
|
|
R_RETURN_VAL_IF_FAIL (a && fcn, NULL);
|
|
|
|
RCore *core = a->coreb.core;
|
|
|
|
if (!core) {
|
|
return NULL;
|
|
}
|
|
|
|
RList *ret = r_list_newf ((RListFree) free);
|
|
RVecAnalRef *xrefs = r_anal_xrefs_get (a, fcn->addr);
|
|
if (!xrefs) {
|
|
return ret;
|
|
}
|
|
|
|
RAnalRef *refi;
|
|
R_VEC_FOREACH (xrefs, refi) {
|
|
// RAnalRefType rt = R_ANAL_REF_TYPE_MASK (refi->type);
|
|
// if (rt == R_ANAL_REF_TYPE_CODE || rt == R_ANAL_REF_TYPE_CALL) {
|
|
const char *flag = get_xrefname (core, refi->addr);
|
|
if (flag) {
|
|
r_list_append (ret, strdup (flag));
|
|
}
|
|
// }
|
|
}
|
|
|
|
r_list_uniq (ret, valstr);
|
|
RVecAnalRef_free (xrefs);
|
|
return ret;
|
|
}
|
|
|
|
R_API RList *r_sign_fcn_refs(RAnal *a, RAnalFunction *fcn) {
|
|
R_RETURN_VAL_IF_FAIL (a && fcn, NULL);
|
|
RAnalRef *refi = NULL;
|
|
RCore *core = a->coreb.core;
|
|
if (!core) {
|
|
return NULL;
|
|
}
|
|
|
|
RList *ret = r_list_newf ((RListFree) free);
|
|
RVecAnalRef *refs = r_anal_function_get_refs (fcn);
|
|
if (!refs) {
|
|
return ret;
|
|
}
|
|
|
|
R_VEC_FOREACH (refs, refi) {
|
|
// RAnalRefType rt = R_ANAL_REF_TYPE_MASK (refi->type);
|
|
// if (rt == R_ANAL_REF_TYPE_CODE || rt == R_ANAL_REF_TYPE_CALL) {
|
|
const char *flag = get_refname (core, refi->addr);
|
|
if (flag) {
|
|
r_list_append (ret, strdup (flag));
|
|
}
|
|
// }
|
|
}
|
|
RVecAnalRef_free (refs);
|
|
return ret;
|
|
}
|
|
|
|
static RSignBytes *des_bytes_norm(const char *in) {
|
|
// "444444:ffffff" or "44444444"
|
|
RSignBytes *b = R_NEW0 (RSignBytes);
|
|
if (b && (b->size = r_hex_str2bin_until_new (in, &b->bytes)) > 0) {
|
|
in += 2 * b->size;
|
|
if (!*in && (b->mask = malloc (b->size))) {
|
|
// no mask, set it to f's
|
|
memset (b->mask, 0xff, b->size);
|
|
return b;
|
|
}
|
|
if (*in++ == ':') {
|
|
// get mask
|
|
int size = r_hex_str2bin_until_new (in, &b->mask);
|
|
in += 2 * size;
|
|
if (size == b->size && !*in) {
|
|
return b;
|
|
}
|
|
}
|
|
}
|
|
r_sign_bytes_free (b);
|
|
return NULL;
|
|
}
|
|
|
|
static RSignBytes *deserialize_bytes(const char *in) {
|
|
RSignBytes *b = des_bytes_norm (in);
|
|
if (b) {
|
|
return b;
|
|
}
|
|
|
|
// "44..44.."
|
|
b = R_NEW0 (RSignBytes);
|
|
size_t len = strlen (in) + 3;
|
|
b->bytes = malloc (len);
|
|
b->mask = malloc (len);
|
|
if (b && b->bytes && b->mask) {
|
|
b->size = r_hex_str2binmask (in, b->bytes, b->mask);
|
|
if (b->size > 0) {
|
|
return b;
|
|
}
|
|
}
|
|
r_sign_bytes_free (b);
|
|
return NULL;
|
|
}
|
|
|
|
static RSignBytes *deserialize_anal(RAnal *a, const char *in) {
|
|
RSignBytes *b = R_NEW0 (RSignBytes);
|
|
if (b && (b->size = r_hex_str2bin_until_new (in, &b->bytes)) > 0) {
|
|
in += 2 * b->size;
|
|
if (!*in && (b->mask = r_anal_mask (a, b->size, b->bytes, 0))) {
|
|
return b;
|
|
}
|
|
}
|
|
r_sign_bytes_free (b);
|
|
return NULL;
|
|
}
|
|
|
|
static inline RList *sign_vars(RAnalFunction *fcn) {
|
|
RList *l = r_anal_var_get_prots (fcn);
|
|
if (l && r_list_empty (l)) {
|
|
r_list_free (l);
|
|
l = NULL;
|
|
}
|
|
return l;
|
|
}
|
|
|
|
// TODO: use primitives from r_types
|
|
#define ALPH(x) (x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z')
|
|
#define VALID_TOKEN_CHR(x) (ALPH (x) || IS_DIGIT (x) || x == '_' || x == '*' || x == ' ' || x == '.')
|
|
static bool types_sig_valid(const char *types) {
|
|
// quick state machine parser to validate types being sent to tcc_compile
|
|
int state = 0; // before, inside, or after ()
|
|
char ch;
|
|
const char *t = types;
|
|
int paren_cnt = 0;
|
|
while ((ch = *(t++)) && state >= 0) {
|
|
switch (state) {
|
|
case 0:
|
|
if (ch == '(') {
|
|
state++;
|
|
} else if (!VALID_TOKEN_CHR (ch) && ch != '.') {
|
|
state = -1;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (ch == '(') {
|
|
paren_cnt++;
|
|
} else if (ch == ')') {
|
|
if (paren_cnt == 0) {
|
|
state++;
|
|
} else if (paren_cnt > 0) {
|
|
paren_cnt--;
|
|
} else {
|
|
state = -1;
|
|
}
|
|
} else if (!VALID_TOKEN_CHR (ch) && ch != ',') {
|
|
state = -1;
|
|
}
|
|
break;
|
|
case 2:
|
|
state = -1;
|
|
break;
|
|
}
|
|
}
|
|
return state == 2? true: false;
|
|
}
|
|
#undef ALPH
|
|
#undef VALID_TOKEN_CHR
|
|
|
|
#define DBL_VAL_FAIL(x,y) \
|
|
if (x) { \
|
|
R_LOG_WARN ("Skipping signature with multiple %c signatures (%s)", 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 = strdup (k);
|
|
char *v2 = strdup (v);
|
|
if (!k2 || !v2) {
|
|
success = false;
|
|
goto out;
|
|
}
|
|
|
|
// Deserialize key: zign|space|name
|
|
int n = r_str_split (k2, '|');
|
|
if (n != 3) {
|
|
R_LOG_WARN ("Skipping signature with invalid key (%s)", k);
|
|
success = false;
|
|
goto out;
|
|
}
|
|
if (strcmp (r_str_word_get0 (k2, 0), "zign")) {
|
|
R_LOG_WARN ("Skipping signature with invalid value (%s)", k);
|
|
success = false;
|
|
goto out;
|
|
}
|
|
|
|
it->space = r_spaces_add (&a->zign_spaces, r_str_word_get0 (k2, 1));
|
|
it->name = R_STR_DUP (r_str_word_get0 (k2, 2));
|
|
|
|
// remove newline at end
|
|
char *save_ptr = NULL;
|
|
r_str_tok_r (v2, "\n", &save_ptr);
|
|
// Deserialize value: |k:v|k:v|k:v|...
|
|
n = r_str_split (v2, '|');
|
|
const char *token = NULL;
|
|
int w;
|
|
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] != ':') {
|
|
R_LOG_WARN ("Skipping signature with corrupted serialization (%s:%s)", 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_NEXT:
|
|
DBL_VAL_FAIL (it->next, R_SIGN_NEXT);
|
|
it->next = strdup (token);
|
|
break;
|
|
case R_SIGN_TYPES:
|
|
DBL_VAL_FAIL (it->types, R_SIGN_TYPES);
|
|
if (!types_sig_valid (token)) {
|
|
R_LOG_ERROR ("Invalid types: ```%s``` in signatuer for %s", token, k);
|
|
success = false;
|
|
goto out;
|
|
}
|
|
it->types = 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 = r_str_split_duplist (token, ",", true))) {
|
|
success = false;
|
|
goto out;
|
|
}
|
|
break;
|
|
case R_SIGN_XREFS:
|
|
DBL_VAL_FAIL (it->xrefs, R_SIGN_XREFS);
|
|
if (!(it->xrefs = r_str_split_duplist (token, ",", true))) {
|
|
success = false;
|
|
goto out;
|
|
}
|
|
break;
|
|
case R_SIGN_VARS:
|
|
DBL_VAL_FAIL (it->vars, R_SIGN_VARS);
|
|
if (!(it->vars = r_anal_var_deserialize (token))) {
|
|
success = false;
|
|
goto out;
|
|
}
|
|
break;
|
|
case R_SIGN_COLLISIONS:
|
|
DBL_VAL_FAIL (it->collisions, R_SIGN_COLLISIONS);
|
|
if (!(it->collisions = r_str_split_duplist (token, ",", true))) {
|
|
success = false;
|
|
goto out;
|
|
}
|
|
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 = strdup (token);
|
|
}
|
|
}
|
|
break;
|
|
case R_SIGN_BYTES:
|
|
DBL_VAL_FAIL (it->bytes, R_SIGN_BYTES);
|
|
if (!(it->bytes = des_bytes_norm (token))) {
|
|
success = false;
|
|
goto out;
|
|
}
|
|
break;
|
|
default:
|
|
R_LOG_ERROR ("Unsupported (%c)", 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) {
|
|
if (!sp || !name) {
|
|
return NULL;
|
|
}
|
|
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 (strchr (e, ',')) {
|
|
return false;
|
|
}
|
|
if (strchr (e, ',') || !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->realname) {
|
|
reserve += 0x10;
|
|
}
|
|
if (it->next) {
|
|
reserve += 0x20;
|
|
}
|
|
if (it->types) {
|
|
reserve += 0x20;
|
|
}
|
|
if (it->hash && it->hash->bbhash) {
|
|
reserve += 64;
|
|
}
|
|
int mul = 10;
|
|
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);
|
|
}
|
|
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);
|
|
|
|
RStrBuf *sb = r_strbuf_new ("");
|
|
r_strbuf_reserve (sb, serial_val_reserv (it));
|
|
|
|
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:%s:%s", R_SIGN_BYTES,
|
|
hexbytes, 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->collisions, sb, R_SIGN_COLLISIONS), sb);
|
|
|
|
if (it->vars && !r_list_empty (it->vars)) {
|
|
char *vrs = r_anal_var_prot_serialize (it->vars, false);
|
|
bool vars_good = false;
|
|
if (vrs) {
|
|
vars_good = r_strbuf_appendf (sb, "|%c:%s", R_SIGN_VARS, vrs);
|
|
free (vrs);
|
|
}
|
|
FreeRet_on_fail (vars_good, sb);
|
|
}
|
|
|
|
if (it->comment && !strchr (it->comment, '|')) {
|
|
FreeRet_on_fail (r_strbuf_appendf (sb, "|%c:%s", R_SIGN_COMMENT, it->comment), sb);
|
|
}
|
|
|
|
if (it->realname && !strchr (it->realname, '|')) {
|
|
FreeRet_on_fail (r_strbuf_appendf (sb, "|%c:%s", R_SIGN_NAME, it->realname), sb);
|
|
}
|
|
|
|
if (it->types && !strchr (it->types, '|')) {
|
|
FreeRet_on_fail (r_strbuf_appendf (sb, "|%c:%s", R_SIGN_TYPES, it->types), sb);
|
|
}
|
|
|
|
if (it->next && !strchr (it->next, '|')) {
|
|
FreeRet_on_fail (r_strbuf_appendf (sb, "|%c:%s", R_SIGN_NEXT, it->next), 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;
|
|
}
|
|
|
|
#define quick_merge(x, freefunc) \
|
|
if (src->x) { \
|
|
freefunc (dst->x); \
|
|
dst->x = src->x; \
|
|
src->x = NULL; \
|
|
}
|
|
|
|
// clobbers src for speed but also garentee success
|
|
static inline void merge_item_clobber(RSignItem *dst, RSignItem *src) {
|
|
dst->space = src->space;
|
|
if (src->addr != UT64_MAX) {
|
|
dst->addr = src->addr;
|
|
}
|
|
|
|
// uniquee free for each
|
|
quick_merge (bytes, r_sign_bytes_free);
|
|
quick_merge (graph, r_sign_graph_free);
|
|
quick_merge (hash, r_sign_hash_free);
|
|
|
|
// strings
|
|
quick_merge (comment, free);
|
|
quick_merge (realname, free);
|
|
quick_merge (next, free);
|
|
quick_merge (types, free);
|
|
|
|
// lists
|
|
quick_merge (refs, r_list_free);
|
|
quick_merge (xrefs, r_list_free);
|
|
quick_merge (vars, r_list_free);
|
|
quick_merge (collisions, r_list_free);
|
|
}
|
|
#undef quick_merge
|
|
|
|
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) {
|
|
R_RETURN_VAL_IF_FAIL (a && name, NULL);
|
|
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;
|
|
}
|
|
|
|
static bool validate_item(RSignItem *it) {
|
|
R_RETURN_VAL_IF_FAIL (it, false);
|
|
// TODO: validate more
|
|
if (!r_name_check (it->name)) {
|
|
R_LOG_ERROR ("Bad name in signature: %s", it->name);
|
|
return false;
|
|
}
|
|
|
|
if (it->space && it->space->name && !r_name_check (it->space->name)) {
|
|
R_LOG_ERROR ("Bad space name in signature: %s", it->space->name);
|
|
return false;
|
|
}
|
|
|
|
if (it->bytes) {
|
|
RSignBytes *b = it->bytes;
|
|
if (!b->mask || !b->bytes || b->size <= 0) {
|
|
R_LOG_ERROR ("Signature '%s' has empty byte field", it->name);
|
|
return false;
|
|
}
|
|
if (b->mask[0] == '\0') {
|
|
R_LOG_ERROR ("Signature '%s' mask starts empty", it->name);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
R_API bool r_sign_add_item(RAnal *a, RSignItem *it) {
|
|
R_RETURN_VAL_IF_FAIL (a && it, NULL);
|
|
r_name_filter (it->name, -1);
|
|
if (!validate_item (it)) {
|
|
return false;
|
|
}
|
|
char *key = item_serialize_key (it);
|
|
if (!key) {
|
|
return false;
|
|
}
|
|
RSignItem *current = sign_get_sdb_item (a, key);
|
|
|
|
bool retval = false;
|
|
if (current) {
|
|
merge_item_clobber (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 RSignItem *item_new_named(RAnal *a, const char *n) {
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (it && (it->name = strdup (n))) {
|
|
it->space = r_spaces_current (&a->zign_spaces);
|
|
return it;
|
|
}
|
|
r_sign_item_free (it);
|
|
return NULL;
|
|
}
|
|
|
|
static bool addHash(RAnal *a, const char *name, int type, const char *val) {
|
|
RSignItem *it = item_new_named (a, name);
|
|
if (!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 = item_new_named (a, name);
|
|
if (it && r_sign_addto_item (a, it, fcn, R_SIGN_BBHASH)) {
|
|
retval = r_sign_add_item (a, it);
|
|
}
|
|
r_sign_item_free (it);
|
|
return retval;
|
|
}
|
|
|
|
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) {
|
|
R_LOG_ERROR ("hash type unknown");
|
|
return false;
|
|
}
|
|
int digestsize = r_hash_size (R_ZIGN_HASH) * 2;
|
|
if (len != digestsize) {
|
|
R_LOG_ERROR ("invalid hash size: %d (%s digest size is %d)", 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, const char *val) {
|
|
R_RETURN_VAL_IF_FAIL (a && name && val, false);
|
|
bool ret = false;
|
|
RSignItem *it = item_new_named (a, name);
|
|
if (it && (it->bytes = deserialize_bytes (val))) {
|
|
ret = r_sign_add_item (a, it);
|
|
}
|
|
r_sign_item_free (it);
|
|
return ret;
|
|
}
|
|
|
|
R_API bool r_sign_add_anal(RAnal *a, const char *name, const char *val) {
|
|
R_RETURN_VAL_IF_FAIL (a && name && val, false);
|
|
bool ret = false;
|
|
RSignItem *it = item_new_named (a, name);
|
|
if (it && (it->bytes = deserialize_anal (a, val))) {
|
|
ret = r_sign_add_item (a, it);
|
|
}
|
|
r_sign_item_free (it);
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static RSignItem *item_from_func(RAnal *a, RAnalFunction *fcn, const char *name) {
|
|
if (r_list_empty (fcn->bbs)) {
|
|
R_LOG_WARN ("Function with no basic blocks at 0x%08"PFMT64x, fcn->addr);
|
|
return false;
|
|
}
|
|
RSignItem *it = item_new_named (a, name? name: fcn->name);
|
|
if (it) {
|
|
if (name && strcmp (name, fcn->name)) {
|
|
it->realname = strdup (name);
|
|
}
|
|
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);
|
|
}
|
|
return it;
|
|
}
|
|
|
|
static int fcn_sort(const void *va, const void *vb) {
|
|
ut64 a = ((const RAnalFunction *)va)->addr;
|
|
ut64 b = ((const RAnalFunction *)vb)->addr;
|
|
if (a < b) {
|
|
return -1;
|
|
} else if (a > b) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool name_exists(Sdb *sdb, const char *n, const RSpace *sp) {
|
|
char *key = space_serialize_key (sp, n);
|
|
bool exist = false;
|
|
if (key) {
|
|
exist = sdb_exists (sdb, key);
|
|
free (key);
|
|
}
|
|
return exist;
|
|
}
|
|
|
|
static char *get_unique_name(Sdb *sdb, const char *name, const RSpace *sp) {
|
|
ut32 i;
|
|
for (i = 2; i < UT32_MAX; i++) {
|
|
char *unique = r_str_newf ("%s_%d", name, i);
|
|
if (!name_exists (sdb, unique, sp)) {
|
|
return unique;
|
|
}
|
|
free (unique);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static char *real_function_name(RAnal *a, RAnalFunction *fcn) {
|
|
RCore *core = a->coreb.core;
|
|
#if 0
|
|
if (fcn->realname) {
|
|
// return strdup (fcn->realname); // r_bin_name_tostring2 (fcn->name, 'o'));
|
|
}
|
|
return strdup (fcn->name); // r_bin_name_tostring2 (fcn->name, 'o'));
|
|
#endif
|
|
#if 1
|
|
ut64 addr = fcn->addr;
|
|
const char *name = fcn->name;
|
|
// resolve the manged name
|
|
char *res = a->coreb.cmdstrf (core, "is,vaddr/eq/0x%"PFMT64x",demangled/cols,a/head/1,:quiet", addr);
|
|
if (res) {
|
|
r_str_trim (res);
|
|
if (*res) {
|
|
return res;
|
|
}
|
|
}
|
|
return strdup (name);
|
|
#endif
|
|
}
|
|
|
|
R_API int r_sign_all_functions(RAnal *a, bool merge) {
|
|
R_RETURN_VAL_IF_FAIL (a, 0);
|
|
RAnalFunction *fcn = NULL;
|
|
RListIter *iter = NULL;
|
|
int count = 0;
|
|
r_list_sort (a->fcns, fcn_sort);
|
|
const RSpace *sp = r_spaces_current (&a->zign_spaces);
|
|
char *prev_name = NULL;
|
|
r_cons_break_push (NULL, NULL);
|
|
RCoreBind cb = a->coreb;
|
|
RCore *core = cb.core;
|
|
bool do_mangled = cb.cfggeti (core, "zign.mangled");
|
|
#if R2_USE_NEW_ABI
|
|
bool zign_dups = a->opt.zigndups;
|
|
#else
|
|
bool zign_dups = cb.cfggeti (core, "zign.dups");
|
|
#endif
|
|
r_list_foreach_prev (a->fcns, iter, fcn) {
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
char *realname = do_mangled? strdup (fcn->name): real_function_name (a, fcn);
|
|
RSignItem *it = NULL;
|
|
if (merge || !name_exists (a->sdb_zigns, realname, sp)) {
|
|
it = item_from_func (a, fcn, realname);
|
|
} else if (zign_dups) {
|
|
char *name = get_unique_name (a->sdb_zigns, fcn->name, sp);
|
|
if (name) {
|
|
it = item_from_func (a, fcn, name);
|
|
}
|
|
free (name);
|
|
}
|
|
free (realname);
|
|
if (it) {
|
|
if (prev_name) {
|
|
it->next = prev_name;
|
|
}
|
|
prev_name = strdup (it->name);
|
|
r_sign_add_item (a, it);
|
|
r_sign_item_free (it);
|
|
count++;
|
|
} else {
|
|
free (prev_name);
|
|
prev_name = NULL;
|
|
}
|
|
}
|
|
r_cons_break_pop ();
|
|
free (prev_name);
|
|
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 = item_from_func (a, fcn, name);
|
|
if (it) {
|
|
r_sign_add_item (a, it);
|
|
r_sign_item_free (it);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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 = sign_vars (fcn));
|
|
case R_SIGN_TYPES:
|
|
if (!it->types) {
|
|
it->types = r_anal_function_get_signature (fcn);
|
|
if (it->types) {
|
|
size_t l = strlen (it->types) - 1;
|
|
if (l > 0 && it->types[l] == ';') {
|
|
it->types[l] = '\0';
|
|
return true;
|
|
} else {
|
|
free (it->types);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
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:
|
|
R_LOG_ERROR ("%s Can not handle type %c", __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 = false;
|
|
RSignItem *it = item_new_named (a, name);
|
|
if (it && (it->graph = R_NEW0 (RSignGraph))) {
|
|
*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);
|
|
|
|
bool retval = false;
|
|
RSignItem *it = item_new_named (a, name);
|
|
if (it && (it->comment = strdup (comment))) {
|
|
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);
|
|
|
|
if (strchr (realname, ' ')) {
|
|
R_LOG_ERROR ("Realname sig can't contain spaces");
|
|
return false;
|
|
}
|
|
|
|
bool retval = false;
|
|
RSignItem *it = item_new_named (a, name);
|
|
if (it && (it->realname = strdup (realname))) {
|
|
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, const char *types) {
|
|
R_RETURN_VAL_IF_FAIL (a && name && types, false);
|
|
if (!types_sig_valid (types)) {
|
|
R_LOG_ERROR ("Invalid types signature: %s", types);
|
|
return false;
|
|
}
|
|
|
|
bool retval = false;
|
|
RSignItem *it = item_new_named (a, name);
|
|
if (it) {
|
|
it->types = strdup (types);
|
|
retval = r_sign_add_item (a, it);
|
|
r_sign_item_free (it);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
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);
|
|
|
|
bool retval = false;
|
|
RSignItem *it = item_new_named (a, name);
|
|
if (it) {
|
|
it->addr = addr;
|
|
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, const char *vars) {
|
|
R_RETURN_VAL_IF_FAIL (a && name && vars, false);
|
|
|
|
bool retval = false;
|
|
RSignItem *it = item_new_named (a, name);
|
|
if (it && (it->vars = r_anal_var_deserialize (vars))) {
|
|
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);
|
|
|
|
bool retval = false;
|
|
RSignItem *it = item_new_named (a, name);
|
|
if (it && (it->refs = r_list_newf ((RListFree)free))) {
|
|
RListIter *iter;
|
|
char *ref;
|
|
r_list_foreach (refs, iter, ref) {
|
|
r_list_append (it->refs, strdup (ref));
|
|
}
|
|
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);
|
|
|
|
bool retval = false;
|
|
RSignItem *it = item_new_named (a, name);
|
|
if (it && (it->xrefs = r_list_newf ((RListFree)free))) {
|
|
RListIter *iter;
|
|
char *xref;
|
|
r_list_foreach (xrefs, iter, xref) {
|
|
r_list_append (it->xrefs, strdup (xref));
|
|
}
|
|
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_last (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) {
|
|
free (data.bytes_combined);
|
|
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 = strdup (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;
|
|
}
|
|
|
|
R_LOG_INFO ("Diff %d %d", (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;
|
|
}
|
|
|
|
R_LOG_INFO ("Diff by name %d %d (%s)", (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;
|
|
ut64 addr;
|
|
};
|
|
|
|
static void list_sanitise_warn(char *s, const char *name, const char *field) {
|
|
if (s) { // NULL value accepted and sane
|
|
bool sanitized = false;
|
|
for (; *s; s++) {
|
|
switch (*name) {
|
|
case '`':
|
|
case '$':
|
|
case '{':
|
|
case '}':
|
|
case '~':
|
|
case '|':
|
|
case '#':
|
|
case '@':
|
|
case '&':
|
|
case '<':
|
|
case '>':
|
|
case ',':
|
|
*s = '_';
|
|
sanitized = true;
|
|
continue;
|
|
}
|
|
}
|
|
if (sanitized) {
|
|
R_LOG_INFO ("%s->%s needs to be sanitized", name, field);
|
|
r_warn_if_reached ();
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
list_sanitise_warn (strbytes, it->name, "bytes");
|
|
list_sanitise_warn (strmask, it->name, "mask");
|
|
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 liststring(RAnal *a, RSignType t, char *value, PJ *pj, int format, char *name) {
|
|
if (value) {
|
|
if (format == 'j') {
|
|
pj_ks (pj, r_sign_type_to_name (t), value);
|
|
} else {
|
|
const char *type = r_sign_type_to_name (t);
|
|
list_sanitise_warn (value, name, type);
|
|
if (format == 'q') {
|
|
a->cb_printf ("\n ; %s\n", value);
|
|
} else if (format == '*') {
|
|
// comment injection via CCu..
|
|
a->cb_printf ("za %s %c %s\n", name, t, value);
|
|
} else {
|
|
a->cb_printf (" %s: %s\n", type, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 inline list_vars_abs(RAnal *a, RSignItem *it, bool rad) {
|
|
if (it->vars && !r_list_empty (it->vars)) {
|
|
char *ser = r_anal_var_prot_serialize (it->vars, true);
|
|
if (ser) {
|
|
// sholdn't do anyting, but just in case
|
|
list_sanitise_warn (ser, it->name, "var");
|
|
if (rad) {
|
|
a->cb_printf ("za %s %c %s\n", it->name, R_SIGN_VARS, ser);
|
|
} else {
|
|
a->cb_printf (" vars: %s\n", ser);
|
|
}
|
|
}
|
|
free (ser);
|
|
}
|
|
}
|
|
|
|
static void inline list_vars_json(RSignItem *it, PJ *pj) {
|
|
pj_ka (pj, "vars");
|
|
if (it->vars) {
|
|
RAnalVarProt *v;
|
|
RListIter *iter;
|
|
r_list_foreach (it->vars, iter, v) {
|
|
char kind[] = { v->kind, '\0' };
|
|
pj_o (pj);
|
|
pj_ks (pj, "name", v->name);
|
|
pj_ks (pj, "type", v->type);
|
|
pj_ks (pj, "kind", kind);
|
|
pj_kN (pj, "delta", v->delta);
|
|
pj_kb (pj, "isarg", v->isarg);
|
|
pj_end (pj);
|
|
}
|
|
}
|
|
pj_end (pj);
|
|
}
|
|
|
|
static void inline list_vars(RAnal *a, RSignItem *it, PJ *pj, int fmt) {
|
|
switch (fmt) {
|
|
case '*':
|
|
list_vars_abs (a, it, true);
|
|
break;
|
|
case 'q':
|
|
if (it->vars) {
|
|
a->cb_printf (" vars[%d]", r_list_length (it->vars));
|
|
}
|
|
break;
|
|
case 'j':
|
|
list_vars_json (it, pj);
|
|
break;
|
|
default:
|
|
list_vars_abs (a, it, false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
list_sanitise_warn (it->hash->bbhash, it->name, "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) {
|
|
list_sanitise_warn (it->hash->bbhash, it->name, "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;
|
|
if (!validate_item (it)) {
|
|
return true;
|
|
}
|
|
|
|
if (ctx->addr != UT64_MAX) {
|
|
if (it->addr != ctx->addr) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Start item
|
|
if (ctx->format == 'j') {
|
|
pj_o (ctx->pj);
|
|
}
|
|
|
|
// Zignspace and name (except for radare format)
|
|
switch (ctx->format) {
|
|
case '*':
|
|
if (it->space) {
|
|
a->cb_printf ("zs %s\n", it->space->name);
|
|
} else {
|
|
a->cb_printf ("zs *\n");
|
|
}
|
|
break;
|
|
case '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);
|
|
break;
|
|
case 'j':
|
|
if (it->space) {
|
|
pj_ks (ctx->pj, "zignspace", it->space->name);
|
|
}
|
|
pj_ks (ctx->pj, "name", it->name);
|
|
break;
|
|
default:
|
|
if (!r_spaces_current (&a->zign_spaces) && it->space) {
|
|
a->cb_printf ("(%s) ", it->space->name);
|
|
}
|
|
a->cb_printf ("%s:\n", it->name);
|
|
break;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
liststring (a, R_SIGN_NAME, it->realname, ctx->pj, ctx->format, it->name);
|
|
liststring (a, R_SIGN_COMMENT, it->comment, ctx->pj, ctx->format, it->name);
|
|
liststring (a, R_SIGN_NEXT, it->next, ctx->pj, ctx->format, it->name);
|
|
liststring (a, R_SIGN_TYPES, it->types, ctx->pj, ctx->format, it->name);
|
|
|
|
// 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);
|
|
}
|
|
// 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);
|
|
}
|
|
|
|
// Vars
|
|
list_vars (a, it, ctx->pj, ctx->format);
|
|
|
|
// 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);
|
|
}
|
|
|
|
{ // R2_600 - we need to pass addr as argument
|
|
RCore *core = a->coreb.core;
|
|
ut64 addr = core? core->offset: UT64_MAX;
|
|
struct ctxListCB ctx = { a, 0, format, pj, addr};
|
|
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 true;
|
|
}
|
|
|
|
R_API const char *r_sign_type_to_name(int type) {
|
|
switch (type) {
|
|
case R_SIGN_BYTES:
|
|
return "bytes";
|
|
case R_SIGN_COMMENT:
|
|
return "comment";
|
|
case R_SIGN_GRAPH:
|
|
return "graph";
|
|
case R_SIGN_OFFSET:
|
|
return "addr";
|
|
case R_SIGN_NAME:
|
|
return "realname";
|
|
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_NEXT:
|
|
return "next";
|
|
case R_SIGN_BBHASH:
|
|
return "bbhash";
|
|
default:
|
|
r_warn_if_reached ();
|
|
return "UnknownType";
|
|
}
|
|
}
|
|
|
|
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 {
|
|
R_LOG_ERROR ("cannot deserialize zign");
|
|
}
|
|
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) {
|
|
R_LOG_ERROR ("Cannot find bytes for this signature: %s", 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;
|
|
if (!sig || !func) {
|
|
return 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 sig_var_diff(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 RAnalVarProt *a = r_list_get_n (la, i);
|
|
const RAnalVarProt *b = r_list_get_n (lb, i);
|
|
|
|
// shouldn't happen, but try to keep it together if it does
|
|
R_RETURN_VAL_IF_FAIL (a, -1);
|
|
R_RETURN_VAL_IF_FAIL (b, 1);
|
|
|
|
dif = a->delta - b->delta;
|
|
if (dif) {
|
|
return dif;
|
|
}
|
|
if (a->isarg != b->isarg) {
|
|
return a->isarg? 1: -1;
|
|
}
|
|
dif = a->kind - b->kind;
|
|
if (dif) {
|
|
return dif;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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. Returns NULL no
|
|
// error, otherwise an RList of collisions. RList will be empty when there are
|
|
// no collisions
|
|
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;
|
|
char *suggest; // holds suggestion for next function match, must be freed
|
|
};
|
|
|
|
static bool match_metrics(RSignItem *it, struct metric_ctx *ctx) {
|
|
R_RETURN_VAL_IF_FAIL (it && ctx, false);
|
|
RSignSearchMetrics *sm = ctx->sm;
|
|
RSignItem *fit = ctx->it;
|
|
RSignType types[R_SIGN_TYPEMAX];
|
|
int count = 0;
|
|
|
|
if (it->bytes && (it->bytes->size >= sm->minsz || ctx->suggest) && !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 && it->hash && !sig_hash_cmp (it->hash, fit->hash)) {
|
|
types[count++] = R_SIGN_BBHASH;
|
|
}
|
|
if (fit->refs && it->refs && !str_list_cmp (it->refs, fit->refs)) {
|
|
types[count++] = R_SIGN_REFS;
|
|
}
|
|
if (fit->vars && it->vars && !sig_var_diff (it->vars, fit->vars)) {
|
|
types[count++] = R_SIGN_VARS;
|
|
}
|
|
if (fit->types && it->types && !strcmp (it->types, fit->types)) {
|
|
types[count++] = R_SIGN_TYPES;
|
|
}
|
|
|
|
bool keep_searching = true;
|
|
if (count) {
|
|
RList *col = NULL;
|
|
if (ctx->suggest) {
|
|
// Collisions are not possible here, assuming collisions are being
|
|
// used. This is b/c we would not recieve a suggestion unless the
|
|
// previous match lacked a collision.
|
|
types[count++] = R_SIGN_NEXT;
|
|
free (ctx->suggest);
|
|
ctx->suggest = NULL;
|
|
col = r_list_new ();
|
|
types[count] = R_SIGN_END;
|
|
} else {
|
|
types[count] = R_SIGN_END;
|
|
col = check_collisions (it->collisions, types);
|
|
}
|
|
|
|
ctx->matched += count;
|
|
types[count] = R_SIGN_END;
|
|
sm->cb (it, ctx->fcn, types, sm->user, col);
|
|
|
|
// is match unique?
|
|
if (col && r_list_length (col) == 0) {
|
|
keep_searching = false;
|
|
// suggest next signature from this match
|
|
ctx->suggest = it->next;
|
|
it->next = NULL;
|
|
}
|
|
r_list_free (col);
|
|
} else {
|
|
free (ctx->suggest);
|
|
ctx->suggest = NULL;
|
|
}
|
|
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:
|
|
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 *);
|
|
|
|
static 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;
|
|
}
|
|
|
|
static inline RSignItem *metric_build_item(RSignSearchMetrics *sm, RAnalFunction *fcn) {
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (it) {
|
|
RSignType *t = sm->stypes;
|
|
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;
|
|
}
|
|
}
|
|
return it;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// returns true if you should keep searching
|
|
static inline bool suggest_check(RAnal *a, struct metric_ctx *ctx) {
|
|
int ret = true;
|
|
if (ctx && ctx->suggest) {
|
|
RSignItem *it = r_sign_get_item (a, ctx->suggest);
|
|
if (it) {
|
|
ret = match_metrics (it, ctx);
|
|
r_sign_item_free (it);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
R_API int r_sign_metric_search(RAnal *a, RSignSearchMetrics *sm) {
|
|
R_RETURN_VAL_IF_FAIL (a && sm, -1);
|
|
RListIter *iter;
|
|
r_list_sort (a->fcns, fcn_sort);
|
|
r_cons_break_push (NULL, NULL);
|
|
struct metric_ctx ctx = { 0, NULL, sm, NULL, NULL };
|
|
r_list_foreach (a->fcns, iter, ctx.fcn) {
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
ctx.it = metric_build_item (sm, ctx.fcn);
|
|
if (ctx.it && suggest_check (sm->anal, &ctx)) {
|
|
r_sign_foreach (sm->anal, (RSignForeachCallback)match_metrics, (void *)&ctx);
|
|
}
|
|
r_sign_item_free (ctx.it);
|
|
}
|
|
r_cons_break_pop ();
|
|
free (ctx.suggest);
|
|
return ctx.matched;
|
|
}
|
|
|
|
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);
|
|
struct metric_ctx ctx = { 0, metric_build_item (sm, fcn), sm, fcn, NULL };
|
|
if (ctx.it) {
|
|
r_sign_foreach (sm->anal, (RSignForeachCallback)match_metrics, (void *)&ctx);
|
|
r_sign_item_free (ctx.it);
|
|
free (ctx.suggest);
|
|
}
|
|
return ctx.matched;
|
|
}
|
|
|
|
R_API RSignItem *r_sign_item_new(void) {
|
|
RSignItem *ret = R_NEW0 (RSignItem);
|
|
if (ret) {
|
|
ret->addr = UT64_MAX;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
R_API void r_sign_item_free(RSignItem *item) {
|
|
if (item) {
|
|
free (item->name);
|
|
free (item->next);
|
|
r_sign_bytes_free (item->bytes);
|
|
r_sign_hash_free (item->hash);
|
|
r_sign_graph_free (item->graph);
|
|
free (item->comment);
|
|
free (item->realname);
|
|
free (item->types);
|
|
r_list_free (item->refs);
|
|
r_list_free (item->vars);
|
|
r_list_free (item->xrefs);
|
|
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);
|
|
}
|
|
}
|
|
|
|
struct load_sign_data {
|
|
RAnal *anal;
|
|
bool merge;
|
|
};
|
|
|
|
static bool loadCB(void *user, const char *k, const char *v) {
|
|
struct load_sign_data *u = (struct load_sign_data *)user;
|
|
RAnal *a = u->anal;
|
|
RSignItem *it = r_sign_item_new ();
|
|
if (it && r_sign_deserialize (a, it, k, v)) {
|
|
if (u->merge || !name_exists (a->sdb_zigns, it->name, it->space)) {
|
|
sdb_set (a->sdb_zigns, k, v, 0);
|
|
} else {
|
|
char *name = get_unique_name (a->sdb_zigns, it->name, it->space);
|
|
if (name) {
|
|
if (!it->realname) {
|
|
it->realname = it->name;
|
|
} else {
|
|
free (it->name);
|
|
}
|
|
it->name = name;
|
|
r_sign_set_item (a->sdb_zigns, it, NULL);
|
|
}
|
|
}
|
|
} else {
|
|
R_LOG_ERROR ("cannot deserialize zign");
|
|
}
|
|
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_xdg_datadir ("zigns");
|
|
abs = r_file_new (home, file, NULL);
|
|
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);
|
|
abs = r_file_new (r_sys_prefix (NULL), R2_ZIGNS, file, NULL);
|
|
if (r_file_is_regular (abs)) {
|
|
return abs;
|
|
}
|
|
free (abs);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
enum {
|
|
SIGNDB_TYPE_SDB,
|
|
SIGNDB_TYPE_KV,
|
|
SIGNDB_TYPE_JSON,
|
|
SIGNDB_TYPE_R2,
|
|
SIGNDB_TYPE_INVALID = -1,
|
|
};
|
|
|
|
static int signdb_type(const char *file) {
|
|
if (r_str_endswith (file, ".sdb")) {
|
|
return SIGNDB_TYPE_SDB;
|
|
}
|
|
if (r_str_endswith (file, ".sdb.txt")) {
|
|
return SIGNDB_TYPE_KV;
|
|
}
|
|
if (r_str_endswith (file, ".json")) {
|
|
return SIGNDB_TYPE_JSON;
|
|
}
|
|
if (r_str_endswith (file, ".r2")) {
|
|
return SIGNDB_TYPE_R2;
|
|
}
|
|
int i, sz = 0;
|
|
char *data = r_file_slurp_range (file, 0, 0x200, &sz);
|
|
if (!data) {
|
|
return SIGNDB_TYPE_INVALID;
|
|
}
|
|
if (sz < 1) {
|
|
free (data);
|
|
return SIGNDB_TYPE_INVALID;
|
|
}
|
|
data[sz - 1] = 0;
|
|
sz = R_MIN (sz, 0x200);
|
|
int is_sdb = 16;
|
|
int is_kv = 4;
|
|
int is_r2 = 2;
|
|
int t = SIGNDB_TYPE_INVALID;
|
|
if (r_str_startswith (data, "[{\"name\":")) {
|
|
t = SIGNDB_TYPE_JSON;
|
|
} else {
|
|
for (i = 0; i < sz; i++) {
|
|
if (!strncmp (data + i, "\nza ", 4)) {
|
|
is_r2--;
|
|
i += 3;
|
|
continue;
|
|
}
|
|
if ((i & 3) == 3 && data[i] == 0) {
|
|
is_sdb--;
|
|
continue;
|
|
}
|
|
if (data[i] == '=' || data[i] == '\n') {
|
|
is_kv--;
|
|
continue;
|
|
}
|
|
}
|
|
if (is_sdb < 0) {
|
|
t = SIGNDB_TYPE_SDB;
|
|
} else if (is_r2 < 0) {
|
|
t = SIGNDB_TYPE_R2;
|
|
} else if (is_kv < 0) {
|
|
t = SIGNDB_TYPE_KV;
|
|
}
|
|
}
|
|
// XXX looks like a false positive bug in gcc 9.4 (debian CI)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wfree-nonheap-object"
|
|
free (data);
|
|
#pragma GCC diagnostic pop
|
|
return t;
|
|
}
|
|
|
|
static bool sign_load_r2(RAnal *a, const char *path) {
|
|
char *cmd = r_str_newf ("'. %s", path);
|
|
a->coreb.cmd (a->coreb.core, cmd);
|
|
free (cmd);
|
|
return true;
|
|
}
|
|
|
|
static bool load_json_signature(RAnal *a, const RJson *child) {
|
|
const char *n = r_json_type (child);
|
|
if (strcmp (n, "object")) {
|
|
return false;
|
|
}
|
|
RSignItem *it = r_sign_item_new ();
|
|
|
|
#if 1
|
|
const RJson *name = r_json_get (child, "name");
|
|
if (name && name->type == R_JSON_STRING) {
|
|
it->name = strdup (name->str_value);
|
|
}
|
|
const RJson *rname = r_json_get (child, "rawname");
|
|
if (!rname) {
|
|
rname = r_json_get (child, "realname");
|
|
}
|
|
#else
|
|
const RJson *name = r_json_get (child, "flagname");
|
|
if (!name) {
|
|
name = r_json_get (child, "name");
|
|
}
|
|
if (name && name->type == R_JSON_STRING) {
|
|
it->name = strdup (name->str_value);
|
|
}
|
|
const RJson *rname = r_json_get (child, "rawname");
|
|
if (!rname) {
|
|
rname = r_json_get (child, "realname");
|
|
}
|
|
#endif
|
|
if (rname && rname->type == R_JSON_STRING) {
|
|
it->realname = strdup (rname->str_value);
|
|
}
|
|
const RJson *bytes = r_json_get (child, "bytes");
|
|
const RJson *mask = r_json_get (child, "mask");
|
|
if (bytes && mask) {
|
|
it->bytes = R_NEW0 (RSignBytes);
|
|
it->bytes->bytes = (ut8*)strdup (bytes->str_value);
|
|
it->bytes->size = r_hex_str2bin (bytes->str_value, it->bytes->bytes);
|
|
it->bytes->mask = (ut8*)strdup (mask->str_value);
|
|
(void)r_hex_str2bin (mask->str_value, it->bytes->mask);
|
|
}
|
|
|
|
const RJson *types = r_json_get (child, "types");
|
|
if (types && types->type == R_JSON_STRING) {
|
|
it->types = strdup (types->str_value);
|
|
}
|
|
|
|
const RJson *next = r_json_get (child, "next");
|
|
if (next && next->type == R_JSON_STRING) {
|
|
it->next = strdup (next->str_value);
|
|
}
|
|
const RJson *addr = r_json_get (child, "addr");
|
|
if (addr) {
|
|
it->addr = addr->num.u_value;
|
|
}
|
|
const RJson *graph = r_json_get (child, "graph");
|
|
if (graph && graph ->type == R_JSON_OBJECT) {
|
|
const RJson *bcc = r_json_get (graph, "cc");
|
|
const RJson *nbb = r_json_get (graph, "nbbs");
|
|
const RJson *edg = r_json_get (graph, "edges");
|
|
const RJson *ebb = r_json_get (graph, "ebbs");
|
|
const RJson *bbs = r_json_get (graph, "bbsum");
|
|
it->graph = R_NEW0 (RSignGraph);
|
|
it->graph->cc = bcc? bcc->num.u_value: 0;
|
|
it->graph->nbbs = nbb? nbb->num.u_value: 0;
|
|
it->graph->edges = edg? edg->num.u_value: 0;
|
|
it->graph->ebbs = ebb? ebb->num.u_value: 0;
|
|
it->graph->bbsum = bbs? bbs->num.u_value: 0;
|
|
}
|
|
const RJson *refs = r_json_get (child, "refs");
|
|
if (refs) {
|
|
it->refs = r_list_new ();
|
|
RJson *p = refs->children.first;
|
|
while (p) {
|
|
if (p->type == R_JSON_STRING) {
|
|
r_list_append (it->refs, strdup (p->str_value));
|
|
}
|
|
p = p->next;
|
|
}
|
|
}
|
|
const RJson *xrefs = r_json_get (child, "xrefs");
|
|
if (xrefs) {
|
|
it->xrefs = r_list_new ();
|
|
RJson *p = xrefs->children.first;
|
|
while (p) {
|
|
if (p->type == R_JSON_STRING) {
|
|
r_list_append (it->xrefs, strdup (p->str_value));
|
|
}
|
|
p = p->next;
|
|
}
|
|
}
|
|
const RJson *hash = r_json_get (child, "hash");
|
|
if (hash && hash->type == R_JSON_OBJECT) {
|
|
const RJson *bbhash = r_json_get (hash, "bbhash");
|
|
it->hash = R_NEW0 (RSignHash);
|
|
it->hash->bbhash = strdup (bbhash->str_value);
|
|
}
|
|
|
|
r_sign_set_item (a->sdb_zigns, it, NULL);
|
|
r_sign_item_free (it);
|
|
return true;
|
|
}
|
|
|
|
static bool sign_load_json(RAnal *a, const char *path) {
|
|
size_t sz;
|
|
char *text = r_file_slurp (path, &sz);
|
|
if (!text) {
|
|
return false;
|
|
}
|
|
bool res = false;
|
|
RJson *rj = r_json_parse (text);
|
|
if (rj->type != R_JSON_ARRAY) {
|
|
R_LOG_ERROR ("Invalid json");
|
|
} else {
|
|
res = true;
|
|
// walk the array
|
|
int i = 0;
|
|
for (i = 0;; i++) {
|
|
const RJson *child = r_json_item (rj, i);
|
|
if (!child) {
|
|
break;
|
|
}
|
|
if (!load_json_signature (a, child)) {
|
|
R_LOG_WARN ("invalid json");
|
|
res = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
r_json_free (rj);
|
|
return res;
|
|
}
|
|
|
|
static bool sign_load_sdb(RAnal *a, const char *path, bool merge) {
|
|
Sdb *db = sdb_new (NULL, path, 0);
|
|
if (db) {
|
|
struct load_sign_data u = {
|
|
.anal = a,
|
|
.merge = merge
|
|
};
|
|
sdb_foreach (db, loadCB, &u);
|
|
sdb_close (db);
|
|
sdb_free (db);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
R_API bool r_sign_load(RAnal *a, const char *file, bool merge) {
|
|
R_RETURN_VAL_IF_FAIL (a && file, false);
|
|
char *path = r_sign_path (a, file);
|
|
if (!path) {
|
|
R_LOG_ERROR ("file %s not found in sign path", file);
|
|
return false;
|
|
}
|
|
if (!r_file_exists (path)) {
|
|
R_LOG_ERROR ("file %s does not exist", file);
|
|
free (path);
|
|
return false;
|
|
}
|
|
bool res = false;
|
|
const int type = signdb_type (path);
|
|
switch (type) {
|
|
case SIGNDB_TYPE_R2:
|
|
res = sign_load_r2 (a, path);
|
|
break;
|
|
case SIGNDB_TYPE_JSON:
|
|
res = sign_load_json (a, path);
|
|
break;
|
|
case SIGNDB_TYPE_KV:
|
|
case SIGNDB_TYPE_SDB:
|
|
res = sign_load_sdb (a, path, merge);
|
|
break;
|
|
default:
|
|
R_LOG_ERROR ("Unsupported signature file format");
|
|
break;
|
|
}
|
|
free (path);
|
|
return res;
|
|
}
|
|
|
|
R_API bool r_sign_load_gz(RAnal *a, const char *filename, bool merge) {
|
|
R_RETURN_VAL_IF_FAIL (a && filename, false);
|
|
ut8 *buf = NULL;
|
|
int size = 0;
|
|
char *tmpfile = NULL;
|
|
bool retval = true;
|
|
char *path = r_sign_path (a, filename);
|
|
if (!path) {
|
|
R_LOG_ERROR ("file %s not found in sign path", filename);
|
|
return false;
|
|
}
|
|
if (!r_file_exists (path)) {
|
|
R_LOG_ERROR ("file %s does not exist", filename);
|
|
retval = false;
|
|
goto out;
|
|
}
|
|
if (!(buf = r_file_gzslurp (path, &size, 0))) {
|
|
R_LOG_ERROR ("cannot decompress file");
|
|
retval = false;
|
|
goto out;
|
|
}
|
|
if (!(tmpfile = r_file_temp ("r2zign"))) {
|
|
R_LOG_ERROR ("cannot create temp file");
|
|
retval = false;
|
|
goto out;
|
|
}
|
|
if (!r_file_dump (tmpfile, buf, size, 0)) {
|
|
R_LOG_ERROR ("cannot dump file");
|
|
retval = false;
|
|
goto out;
|
|
}
|
|
if (!r_sign_load (a, tmpfile, merge)) {
|
|
R_LOG_ERROR ("cannot load file");
|
|
retval = false;
|
|
goto out;
|
|
}
|
|
if (!r_file_rm (tmpfile)) {
|
|
R_LOG_ERROR ("cannot delete temp file");
|
|
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)) {
|
|
R_LOG_WARN ("no zignatures to save");
|
|
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) {
|
|
R_RETURN_VAL_IF_FAIL (bytes_thresh && graph_thresh, NULL);
|
|
RSignOptions *options = R_NEW0 (RSignOptions);
|
|
if (!options) {
|
|
return NULL;
|
|
}
|
|
|
|
options->bytes_diff_threshold = r_num_get_double (NULL, bytes_thresh);
|
|
options->graph_diff_threshold = r_num_get_double (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) {
|
|
free (options);
|
|
}
|