Add zac command and collisions type to ##signatures (#18692)

This commit ads the R_SIGN_COLLISIONS ('C') signature type, which will later be
used to resolve what a function should be named when multiple signatures match.
This commit is contained in:
Dennis Goodlett 2021-05-14 18:49:53 -04:00 committed by GitHub
parent d1c31c00dd
commit 2942cf1433
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 359 additions and 62 deletions

View File

@ -313,6 +313,10 @@ R_API bool r_sign_deserialize(RAnal *a, RSignItem *it, const char *k, const char
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) {
@ -499,6 +503,7 @@ static char *serialize_value(RSignItem *it) {
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);
@ -599,6 +604,8 @@ static bool mergeItem(RSignItem *dst, RSignItem *src) {
if (!(dst->comment = strdup (src->comment))) {
return false;
}
dst->comment = src->comment;
src->comment = NULL;
}
if (src->realname) {
@ -624,6 +631,9 @@ static bool mergeItem(RSignItem *dst, RSignItem *src) {
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))) {
@ -1642,38 +1652,6 @@ static void listOffset(RAnal *a, RSignItem *it, PJ *pj, int format) {
}
}
static inline const char *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_BBHASH:
return "bbhash";
default:
r_warn_if_reached ();
return "UnkownType";
}
}
static void print_function_args_json(PJ *pj, char *arg_type) {
char *arg_name = strchr (arg_type, ',');
@ -1714,7 +1692,7 @@ static void list_types_json(RSignItem *it, PJ *pj) {
}
static void list_sign_list(RAnal *a, RList *l, PJ *pj, int fmt, int type, const char *name) {
const char *tname = sign_type_to_name (type);
const char *tname = r_sign_type_to_name (type);
switch (fmt) {
case '*':
a->cb_printf ("za %s %c ", name, type);
@ -1819,6 +1797,9 @@ static int listCB(RSignItem *it, void *user) {
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);
@ -1878,6 +1859,15 @@ static int listCB(RSignItem *it, void *user) {
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);
@ -1922,6 +1912,40 @@ static int listGetCB(RSignItem *it, void *user) {
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);
@ -2176,26 +2200,87 @@ static int matchCount(int a, int b) {
return R_ABS (c) < m;
}
static bool graph_match(RSignGraph *a, RSignGraph *b) {
static int sig_graph_match(RSignItem *ia, RSignItem *ib) {
RSignGraph *a = ia->graph;
RSignGraph *b = ib->graph;
if (!a || !b) {
return false;
return 1;
}
if (a->cc != -1 && a->cc != b->cc) {
return false;
return 1;
}
if (a->nbbs != -1 && a->nbbs != b->nbbs) {
return false;
return 1;
}
if (a->edges != -1 && a->edges != b->edges) {
return false;
return 1;
}
if (a->ebbs != -1 && a->ebbs != b->ebbs) {
return false;
return 1;
}
if (a->bbsum > 0 && matchCount (a->bbsum, b->bbsum)) {
return false;
return 1;
}
return true;
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;
}
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) {
@ -2254,7 +2339,7 @@ static int match_metrics(RSignItem *it, void *user) {
int count = 0;
// only matching alg that is not a cmp, but true/false
if (graph_match (it->graph, fit->graph)) {
if (!sig_graph_match (it, fit)) {
types[count++] = R_SIGN_GRAPH;
}
if (fit->addr != UT64_MAX && !sig_addr_cmp (it->addr, fit->addr)) {
@ -2281,6 +2366,181 @@ static int match_metrics(RSignItem *it, void *user) {
return 0;
}
static int _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)? 1: 0;
}
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, (RListComparator)strcmp)) {
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 ("%s:%s", r_sign_type_to_name (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_match;
}
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 NULL;
}
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_sign_add_item (a, it);
}
}
r_pvector_free (sigs);
return true;
}
R_API int r_sign_fcn_match_metrics(RSignSearchMetrics *sm) {
r_return_val_if_fail (sm && sm->mincc >= 0 && sm->anal && sm->fcn, -1);
RSignItem *it = r_sign_item_new ();
@ -2313,23 +2573,20 @@ R_API RSignItem *r_sign_item_new(void) {
}
R_API void r_sign_item_free(RSignItem *item) {
if (!item) {
return;
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);
}
free (item->name);
r_sign_bytes_free (item->bytes);
if (item->hash) {
free (item->hash->bbhash);
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);
free (item);
}
R_API void r_sign_graph_free(RSignGraph *graph) {
@ -2344,6 +2601,13 @@ R_API void r_sign_bytes_free(RSignBytes *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 ();

View File

@ -412,6 +412,11 @@ out_case_fcn:
return retval;
}
break;
case 'c': // "zac"
r_cons_break_push (NULL, NULL);
r_sign_resolve_collisions (core->anal);
r_cons_break_pop ();
break;
case 'F':
{
RAnalFunction *fcni = NULL;
@ -735,9 +740,9 @@ static int fcnMatchCB(RSignItem *it, RAnalFunction *fcn, RSignType *types, void
}
}
// TODO: check collisions signature before upating
apply_name (ctx->core, fcn, it, ctx->rad);
apply_types (ctx->core, fcn, it);
// TODO(nibble): use one counter per metric zign instead of ctx->count
return 1;
}

View File

@ -27,6 +27,7 @@ typedef enum {
R_SIGN_XREFS = 'x', // xrefs
R_SIGN_VARS = 'v', // variables
R_SIGN_TYPES = 't', // types
R_SIGN_COLLISIONS = 'C', // collisions
R_SIGN_BBHASH = 'h', // basic block hash
R_SIGN_END = '\x00', // used for sentenal value
} RSignType;
@ -62,6 +63,7 @@ typedef struct r_sign_item_t {
RList *xrefs;
RList *vars;
RList *types;
RList *collisions;
RSignHash *hash;
} RSignItem;
@ -70,10 +72,10 @@ typedef int (*RSignSearchCallback) (RSignItem *it, RSearchKeyword *kw, ut64 addr
typedef int (*RSignMatchCallback) (RSignItem *it, RAnalFunction *fcn, RSignType *types, void *user);
typedef struct r_sign_search_met {
/* types is an 0 terminated array of RSignTypes that are going to be
/* types is an R_SIGN_END terminated array of RSignTypes that are going to be
* searched for. Valid types are: graph, offset, refs, bbhash, types, vars
*/
RSignType types[7];
RSignType types[8];
int mincc; // min complexity for graph search
RAnal *anal;
void *user; // user data for callback function
@ -123,11 +125,13 @@ R_API RSignItem *r_sign_get_item(RAnal *a, const char *name);
R_API bool r_sign_add_item(RAnal *a, RSignItem *it);
R_API bool r_sign_foreach(RAnal *a, RSignForeachCallback cb, void *user);
R_API const char *r_sign_type_to_name(int type);
R_API RSignSearch *r_sign_search_new(void);
R_API void r_sign_search_free(RSignSearch *ss);
R_API void r_sign_search_init(RAnal *a, RSignSearch *ss, int minsz, RSignSearchCallback cb, void *user);
R_API int r_sign_search_update(RAnal *a, RSignSearch *ss, ut64 *at, const ut8 *buf, int len);
R_API bool r_sign_resolve_collisions(RAnal *a);
R_API int r_sign_fcn_match_metrics(RSignSearchMetrics *sm);
R_API bool r_sign_load(RAnal *a, const char *file);
@ -139,6 +143,7 @@ R_API RSignItem *r_sign_item_new(void);
R_API void r_sign_item_free(RSignItem *item);
R_API void r_sign_graph_free(RSignGraph *graph);
R_API void r_sign_bytes_free(RSignBytes *bytes);
R_API void r_sign_hash_free(RSignHash *hash);
R_API RList *r_sign_fcn_refs(RAnal *a, RAnalFunction *fcn);
R_API RList *r_sign_fcn_xrefs(RAnal *a, RAnalFunction *fcn);

View File

@ -1214,7 +1214,7 @@ zaf fcn.00484d40 fcn.00484d40
zj
EOF
EXPECT=<<EOF
[{"name":"fcn.00484d40","bytes":"55534883ec08488b1f4885db741b4889fd4889dfe857b7f7ff4801d8803800751748c74500000000004883c4084889d85b5dc30f1f440000c600004883c001488945004883c4084889d85b5dc3","mask":"ffffffffffffffffffffffffff00ffffffffffffff00000000ffffffffffffff00ffffffffffffffffffffffffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffff","graph":{"cc":4,"nbbs":5,"edges":5,"ebbs":2,"bbsum":72},"addr":4738368,"refs":[],"xrefs":[],"vars":["r82"],"types":[{"name":"arg1","type":"int64_t"}],"hash":{"bbhash":"8ff8a5c7f84179483b764fbb18dc4c44f39da6527a0a16485c7ae519f00e687f"}}]
[{"name":"fcn.00484d40","bytes":"55534883ec08488b1f4885db741b4889fd4889dfe857b7f7ff4801d8803800751748c74500000000004883c4084889d85b5dc30f1f440000c600004883c001488945004883c4084889d85b5dc3","mask":"ffffffffffffffffffffffffff00ffffffffffffff00000000ffffffffffffff00ffffffffffffffffffffffffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffff","graph":{"cc":4,"nbbs":5,"edges":5,"ebbs":2,"bbsum":72},"addr":4738368,"refs":[],"xrefs":[],"vars":["r82"],"types":[{"name":"arg1","type":"int64_t"}],"collisions":[],"hash":{"bbhash":"8ff8a5c7f84179483b764fbb18dc4c44f39da6527a0a16485c7ae519f00e687f"}}]
EOF
RUN
@ -1865,3 +1865,26 @@ main:
bbhash: 2222222222222222222222222222222222222222222222222222222222222222
EOF
RUN
NAME=zac creates collision signatures
FILE=-
CMDS=<<EOF
za sym.collide1 g cc=1 nbbs=2 edges=3 ebbs=4 bbsum=5
za sym.collide1 b 4433444444444444:ff00ffffffffffff
za sym.collide2 g cc=1 nbbs=2 edges=3 ebbs=4 bbsum=5
za sym.collide3 g cc=1 nbbs=2 edges=3 ebbs=4 bbsum=5
za sym.collide2 b 4422444444444444:ff00ffffffffffff
za sym.collide4 g cc=1 nbbs=2 edges=3 ebbs=4 bbsum=5
za sym.collide5 g cc=1 nbbs=2 edges=3 ebbs=4 bbsum=5
za sym.other1 g cc=0 nbbs=2 edges=3 ebbs=4 bbsum=5
za sym.other1 b 4422444444444445:ff00ffffffffffff
za sym.other2 g cc=2 nbbs=2 edges=3 ebbs=4 bbsum=5
zac
z*~sym.collide1 C
z*~sym.collide2 C
EOF
EXPECT=<<EOF
za sym.collide1 C bytes:sym.collide2 graph:sym.collide5 graph:sym.collide3 graph:sym.collide4 graph:sym.collide2
za sym.collide2 C bytes:sym.collide1 graph:sym.collide5 graph:sym.collide3 graph:sym.collide1 graph:sym.collide4
EOF
RUN