Implement "next" signature type ##signatures

* Update tests for added signature
This commit is contained in:
Dennis Goodlett 2021-08-31 19:27:52 -04:00 committed by GitHub
parent c6c498455f
commit c4622b6603
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 215 additions and 197 deletions

View File

@ -271,6 +271,10 @@ R_API bool r_sign_deserialize(RAnal *a, RSignItem *it, const char *k, const char
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_GRAPH:
DBL_VAL_FAIL (it->graph, R_SIGN_GRAPH);
if (strlen (token) == 2 * sizeof (RSignGraph)) {
@ -425,6 +429,12 @@ static inline size_t serial_val_reserv(RSignItem *it) {
if (it->graph) {
reserve += sizeof (RSignGraph) * 2 + 1;
}
if (it->realname) {
reserve += 0x10;
}
if (it->next) {
reserve += 0x10;
}
if (it->hash && it->hash->bbhash) {
reserve += 64;
}
@ -509,6 +519,10 @@ static char *serialize_value(RSignItem *it) {
FreeRet_on_fail (r_strbuf_appendf (sb, "|%c:%s", R_SIGN_NAME, it->realname), sb);
}
if (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);
}
@ -546,100 +560,38 @@ static RList *deserialize_sign_space(RAnal *a, RSpace *space) {
return ret;
}
static inline bool merge_list (RList **dst, RList *src) {
if (!src) {
return true;
#define quick_merge(x, freefunc) \
if (src->x) { \
freefunc (dst->x); \
dst->x = src->x; \
src->x = NULL; \
}
r_list_free (*dst);
if (!(*dst = r_list_newf (free))) {
return false;
}
RListIter *iter;
char *s, *dup;
r_list_foreach (src, iter, s) {
if (!(dup = strdup (s))) {
return false;
}
r_list_append (*dst, dup);
}
return true;
}
static bool mergeItem(RSignItem *dst, RSignItem *src) {
// clobbers src for speed but also garentee success
static inline void merge_item_clobber(RSignItem *dst, RSignItem *src) {
dst->space = src->space;
if (src->bytes) {
// ensure we have a dst->bytes
if (!dst->bytes && !(dst->bytes = R_NEW0 (RSignBytes))) {
return false;
}
// ensure we have space in dst->bytes->[bytes|mask]
if (src->bytes->size > dst->bytes->size) {
free (dst->bytes->bytes);
free (dst->bytes->mask);
dst->bytes->bytes = R_NEWS (ut8, src->bytes->size);
dst->bytes->mask = R_NEWS (ut8, src->bytes->size);
if (!dst->bytes->bytes || !dst->bytes->mask) {
return false;
}
}
// do copy
dst->bytes->size = src->bytes->size;
memcpy (dst->bytes->bytes, src->bytes->bytes, src->bytes->size);
memcpy (dst->bytes->mask, src->bytes->mask, src->bytes->size);
}
if (src->graph) {
if (!dst->graph && !(dst->graph = R_NEW0 (RSignGraph))) {
return false;
}
*dst->graph = *src->graph;
}
if (src->comment) {
free (dst->comment);
if (!(dst->comment = strdup (src->comment))) {
return false;
}
dst->comment = src->comment;
src->comment = NULL;
}
if (src->realname) {
free (dst->realname);
if (!(dst->realname = strdup (src->realname))) {
return false;
}
}
if (src->addr != UT64_MAX) {
dst->addr = src->addr;
}
if (!merge_list (&dst->refs, src->refs)) {
return false;
}
if (!merge_list (&dst->xrefs, src->xrefs)) {
return false;
}
if (!merge_list (&dst->vars, src->vars)) {
return false;
}
if (!merge_list (&dst->types, src->types)) {
return false;
}
if (!merge_list (&dst->collisions, src->collisions)) {
return false;
}
// 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);
if (src->hash && src->hash->bbhash) {
if (!dst->hash && !(dst->hash = R_NEW0 (RSignHash))) {
return false;
}
free (dst->hash->bbhash);
dst->hash->bbhash = strdup (src->hash->bbhash);
}
return true;
// strings
quick_merge (comment, free);
quick_merge (realname, free);
quick_merge (next, free);
// lists
quick_merge (refs, r_list_free);
quick_merge (xrefs, r_list_free);
quick_merge (vars, r_list_free);
quick_merge (types, 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;
@ -685,10 +637,9 @@ R_API bool r_sign_add_item(RAnal *a, RSignItem *it) {
bool retval = false;
if (current) {
if (mergeItem (current, it)) {
retval = r_sign_set_item (a->sdb_zigns, current, key);
r_sign_item_free (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);
}
@ -931,50 +882,80 @@ static RSignHash *r_sign_fcn_bbhash(RAnal *a, RAnalFunction *fcn) {
return hash;
}
static RSignItem *item_from_func(RAnal *a, RAnalFunction *fcn, const char *name) {
RSignItem *it = r_sign_item_new ();
if (it) {
it->space = r_spaces_current (&a->zign_spaces);
it->name = strdup (name? name: fcn->name);
if (!it->name) {
r_sign_item_free (it);
return NULL;
}
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;
}
R_API int r_sign_all_functions(RAnal *a) {
RAnalFunction *fcni = NULL;
RListIter *iter = NULL;
int count = 0;
r_list_foreach (a->fcns, iter, fcni) {
r_list_sort (a->fcns, fcn_sort);
char *prev_name = NULL;
r_cons_break_push (NULL, NULL);
r_list_foreach_prev (a->fcns, iter, fcni) {
if (r_cons_is_breaked ()) {
break;
}
if (r_sign_add_func (a, fcni, NULL)) {
RSignItem *it = item_from_func (a, fcni, fcni->name);
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 = r_sign_item_new ();
if (!it) {
return false;
}
it->space = r_spaces_current (&a->zign_spaces);
it->name = strdup (name? name: fcn->name);
if (!it->name) {
RSignItem *it = item_from_func (a, fcn, name);
if (it) {
r_sign_add_item (a, it);
r_sign_item_free (it);
return false;
return true;
}
r_sign_addto_item (a, it, fcn, R_SIGN_GRAPH);
r_sign_addto_item (a, it, fcn, R_SIGN_BYTES);
r_sign_addto_item (a, it, fcn, R_SIGN_XREFS);
r_sign_addto_item (a, it, fcn, R_SIGN_REFS);
r_sign_addto_item (a, it, fcn, R_SIGN_VARS);
r_sign_addto_item (a, it, fcn, R_SIGN_TYPES);
r_sign_addto_item (a, it, fcn, R_SIGN_BBHASH);
r_sign_addto_item (a, it, fcn, R_SIGN_OFFSET);
r_sign_addto_item (a, it, fcn, R_SIGN_NAME);
// commit the item to anal
r_sign_add_item (a, it);
r_sign_item_free (it);
return true;
return false;
}
R_API bool r_sign_addto_item(RAnal *a, RSignItem *it, RAnalFunction *fcn, RSignType type) {
@ -1653,32 +1634,17 @@ static void listGraph(RAnal *a, RSignItem *it, PJ *pj, int format) {
}
}
static void listComment(RAnal *a, RSignItem *it, PJ *pj, int format) {
if (it->comment) {
static void liststring(RAnal *a, RSignType t, char *value, PJ *pj, int format, char *name) {
if (value) {
if (format == 'q') {
// a->cb_printf (" addr(0x%08"PFMT64x")", it->addr);
a->cb_printf ("\n ; %s\n", it->comment);
a->cb_printf ("\n ; %s\n", value);
} else if (format == '*') {
a->cb_printf ("%s\n", it->comment); // comment injection via CCu..
// comment injection via CCu..
a->cb_printf ("za %s %c %s\n", name, t, value);
} else if (format == 'j') {
pj_ks (pj, "comments", it->comment);
pj_ks (pj, r_sign_type_to_name (t), value);
} else {
a->cb_printf (" comment: 0x%08" PFMT64x "\n", it->addr);
}
}
}
static void listRealname(RAnal *a, RSignItem *it, PJ *pj, int format) {
if (it->realname) {
if (format == 'q') {
// a->cb_printf (" addr(0x%08"PFMT64x")", it->addr);
} else if (format == '*') {
a->cb_printf ("za %s n %s\n", it->name, it->realname);
a->cb_printf ("afn %s @ 0x%08"PFMT64x"\n", it->realname, it->addr);
} else if (format == 'j') {
pj_ks (pj, "realname", it->realname);
} else {
a->cb_printf (" realname: %s\n", it->realname);
a->cb_printf (" %s: %s\n", r_sign_type_to_name (t), value);
}
}
}
@ -1840,9 +1806,6 @@ static bool 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);
@ -1862,14 +1825,11 @@ static bool listCB(RSignItem *it, void *user) {
} else if (ctx->format == 'j') {
pj_kN (ctx->pj, "addr", -1);
}
// Name
if (it->realname) {
listRealname (a, it, ctx->pj, ctx->format);
}
// Comments
if (it->comment) {
listComment (a, it, ctx->pj, ctx->format);
}
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);
// References
if (it->refs) {
list_sign_list (a, it->refs, ctx->pj, ctx->format, R_SIGN_REFS, it->name);
@ -1952,7 +1912,7 @@ R_API void r_sign_list(RAnal *a, int format) {
static bool listGetCB(RSignItem *it, void *user) {
r_list_append ((RList *)user, it);
return 1;
return true;
}
R_API const char *r_sign_type_to_name(int type) {
@ -1970,7 +1930,7 @@ R_API const char *r_sign_type_to_name(int type) {
case R_SIGN_OFFSET:
return "addr";
case R_SIGN_NAME:
return "name";
return "realname";
case R_SIGN_REFS:
return "refs";
case R_SIGN_XREFS:
@ -1981,6 +1941,8 @@ R_API const char *r_sign_type_to_name(int type) {
return "types";
case R_SIGN_COLLISIONS:
return "collisions";
case R_SIGN_NEXT:
return "next";
case R_SIGN_BBHASH:
return "bbhash";
default:
@ -2427,7 +2389,9 @@ static RListIter *collision_skip_unused(RListIter *iter, RSignType *used) {
}
// return NULL one error, otherwise return a, possibly empty, list of
// collisions. Relies on sets being ordered in groups of types
// 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 ();
@ -2519,16 +2483,17 @@ struct metric_ctx {
RSignItem *it;
RSignSearchMetrics *sm;
RAnalFunction *fcn;
char *suggest; // holds suggestion for next function match, must be freed
};
static bool match_metrics(RSignItem *it, void *user) {
struct metric_ctx *ctx = (struct metric_ctx *)user;
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[7];
RSignType types[R_SIGN_TYPEMAX];
int count = 0;
if (it->bytes && it->bytes->size >= sm->minsz && !sig_bytes_diff (it, fit)) {
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)) {
@ -2552,14 +2517,36 @@ static bool match_metrics(RSignItem *it, void *user) {
bool keep_searching = true;
if (count) {
RList *col = check_collisions (it->collisions, types);
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;
}
sm->cb (it, ctx->fcn, types, sm->user, col);
r_list_free (col);
} else {
free (ctx->suggest);
ctx->suggest = NULL;
}
return keep_searching;
}
@ -2709,6 +2696,28 @@ static inline bool sign_collide_by(RPVector *sigs, RSignType type) {
return true;
}
static inline RSignItem *metric_build_item(RSignSearchMetrics *sm, RAnalFunction *fcn) {
RSignItem *it = r_sign_item_new ();
if (it) {
RSignType *t = sm->types;
while (*t != R_SIGN_END) {
if (*t == R_SIGN_BYTES) {
// no need for mask
it->bytes = r_sign_func_empty_mask (sm->anal, fcn);
} else {
r_sign_addto_item (sm->anal, it, fcn, *t);
}
t++;
}
if (it->graph && it->graph->cc < sm->mincc) {
r_sign_graph_free (it->graph);
it->graph = NULL;
}
}
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);
@ -2740,52 +2749,48 @@ R_API bool r_sign_resolve_collisions(RAnal *a) {
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);
int count = 0;
RAnalFunction *fcni;
RListIter *iter;
r_list_sort (a->fcns, fcn_sort);
r_cons_break_push (NULL, NULL);
r_list_foreach (a->fcns, iter, fcni) {
struct metric_ctx ctx = { 0, NULL, sm, NULL, NULL };
r_list_foreach (a->fcns, iter, ctx.fcn) {
if (r_cons_is_breaked ()) {
break;
}
int match = r_sign_fcn_match_metrics (sm, fcni);
if (match < 0) {
count = -1;
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);
}
count++;
r_sign_item_free (ctx.it);
}
r_cons_break_pop ();
return count;
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);
RSignItem *it = r_sign_item_new ();
if (!it) {
return -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);
}
RSignType *t = sm->types;
while (*t != R_SIGN_END) {
if (*t == R_SIGN_BYTES) {
// no need for mask
it->bytes = r_sign_func_empty_mask (sm->anal, fcn);
} else {
r_sign_addto_item (sm->anal, it, fcn, *t);
}
t++;
}
if (it->graph && it->graph->cc < sm->mincc) {
r_sign_graph_free (it->graph);
it->graph = NULL;
}
struct metric_ctx ctx = { 0, it, sm, fcn };
r_sign_foreach (sm->anal, match_metrics, (void *)&ctx);
r_sign_item_free (it);
return ctx.matched;
}
@ -2793,7 +2798,6 @@ R_API RSignItem *r_sign_item_new(void) {
RSignItem *ret = R_NEW0 (RSignItem);
if (ret) {
ret->addr = UT64_MAX;
ret->space = NULL;
}
return ret;
}
@ -2801,6 +2805,7 @@ R_API RSignItem *r_sign_item_new(void) {
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);

View File

@ -510,6 +510,7 @@ struct ctxSearchCB {
int refs_count;
int types_count;
int bbhash_count;
int next_count;
};
static void apply_name(RCore *core, RAnalFunction *fcn, RSignItem *it, bool rad) {
@ -608,6 +609,7 @@ static int fcnMatchCB(RSignItem *it, RAnalFunction *fcn, RSignType *types, void
RSignType t;
bool collides = false;
if (!col || !r_list_empty (col)) {
// NULL col implies collision computation err, so assume collides
collides = true;
ctx->collisions++;
}
@ -639,6 +641,10 @@ static int fcnMatchCB(RSignItem *it, RAnalFunction *fcn, RSignType *types, void
prefix = "bbhash";
ctx->bbhash_count++;
break;
case R_SIGN_NEXT:
prefix = "next";
ctx->next_count++;
break;
default:
r_warn_if_reached ();
break;
@ -764,6 +770,10 @@ static void print_ctx_hits(struct ctxSearchCB *ctx) {
eprintf ("bbhash: %d\n", ctx->bbhash_count);
prints++;
}
if (ctx->next_count) {
eprintf ("next: %d\n", ctx->next_count);
prints++;
}
if (prints > 1) {
eprintf ("total: %d\n", ctx->count);
}

View File

@ -29,9 +29,11 @@ typedef enum {
R_SIGN_VARS = 'v', // variables
R_SIGN_TYPES = 't', // types
R_SIGN_COLLISIONS = 'C', // collisions
R_SIGN_NEXT = 'N', // next
R_SIGN_BBHASH = 'h', // basic block hash
R_SIGN_END = '\x00', // used for sentenal value
} RSignType;
#define R_SIGN_TYPEMAX 16
typedef struct r_sign_graph_t {
int cc;
@ -54,6 +56,7 @@ typedef struct r_sign_hash_t {
typedef struct r_sign_item_t {
char *name;
char *realname;
char *next;
char *comment;
const RSpace *space;
@ -76,7 +79,7 @@ typedef struct r_sign_search_met {
/* 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[8];
RSignType types[R_SIGN_TYPEMAX];
int mincc; // min complexity for graph search
int minsz;
RAnal *anal;

View File

@ -640,7 +640,7 @@ aa
za sym.print_metric g cc=1 nbbs=1 edges=0 ebbs=1
e zign.mincc = 0
z/
?v sign.graph.sym.print_metric_4
?v sign.graph.sym.print_metric_3
EOF
EXPECT=<<EOF
0x400536
@ -657,7 +657,7 @@ e zign.minsz = 0
e zign.mincc = 0
z/
?v sign.bytes.sym.print_0
?v sign.graph.sym.print_5
?v sign.graph.sym.print_4
EOF
EXPECT=<<EOF
0x400536

View File

@ -18,7 +18,7 @@ NAME=rasign2 -r
FILE=-
CMDS=!!rasign2 -r bins/elf/hello_world~main?
EXPECT=<<EOF
7
9
EOF
RUN
@ -26,7 +26,7 @@ NAME=rasign2 -rj
FILE=-
CMDS=!!rasign2 -rj bins/elf/hello_world~main?
EXPECT=<<EOF
8
10
EOF
RUN
@ -34,7 +34,7 @@ NAME=rasign2 -raa
FILE=-
CMDS=!!rasign2 -raa bins/elf/hello_world~main?
EXPECT=<<EOF
7
9
EOF
RUN
@ -42,7 +42,7 @@ NAME=rasign2 -aarj
FILE=-
CMDS=!!rasign2 -aarj bins/elf/hello_world~main?
EXPECT=<<EOF
8
10
EOF
RUN