/* radare - LGPL - Copyright 2008-2015 - nibble, pancake */ // TODO: rename to r_anal_meta_get() ?? #if 0 TODO ==== - handle sync to synchronize all the data on disk. - actually listing only works in memory - array_add doesnt needs index, right? - remove unused arguments from r_meta_find (where ?) - implement r_meta_find #endif #if 0 SDB SPECS DatabaseName: 'anal.meta' Keys: 'meta..count=' number of added metas where 'type' is a single char 'meta..=' splitted array, each block contains K elements 'meta..=' string representing extra information of the meta type at given address 'range.=' store valid addresses in a base range array #endif #include #include #define META_RANGE_BASE(x) ((x)>>12) #define META_RANGE_SIZE 0xfff #undef DB #define DB a->sdb_meta #if 0 // Defined but not used. Shall we remove it? static char *meta_inrange_get (RAnal *a, ut64 addr, int size) { char key[64]; ut64 base, base2; base = META_RANGE_BASE (addr); base2 = META_RANGE_BASE (addr+size); // return string array of all the offsets where there are stuff for (; basemeta_spaces.space_idx; meta_type_add (a, type, addr); snprintf (key, sizeof (key)-1, "meta.%c.0x%"PFMT64x, type, addr); size = sdb_array_get_num (DB, key, 0, 0); if (!size) { size = strlen (s); meta_inrange_add (a, addr, size); ret = true; } else ret = false; e_str = sdb_encode ((const void*)s, -1); snprintf (val, sizeof (val)-1, "%d,%d,%s", (int)size, space_idx, e_str); sdb_set (DB, key, val, 0); free ((void*)e_str); return ret; } R_API int r_meta_set_var_comment (RAnal *a, int type, ut64 idx, ut64 addr, const char *s) { char key[100], val[2048], *e_str; int ret; ut64 size; int space_idx = a->meta_spaces.space_idx; meta_type_add (a, type, addr); snprintf (key, sizeof (key)-1, "meta.%c.0x%"PFMT64x".0x%"PFMT64x, type, addr, idx); size = sdb_array_get_num (DB, key, 0, 0); if (!size) { size = strlen (s); meta_inrange_add (a, addr, size); ret = true; } else ret = false; e_str = sdb_encode ((const void*)s, -1); snprintf (val, sizeof (val)-1, "%d,%d,%s", (int)size, space_idx, e_str); sdb_set (DB, key, val, 0); free ((void*)e_str); return ret; } R_API char *r_meta_get_string(RAnal *a, int type, ut64 addr) { char key[100]; const char *k, *p, *p2; snprintf (key, sizeof (key)-1, "meta.%c.0x%"PFMT64x, type, addr); k = sdb_const_get (DB, key, NULL); if (!k) return NULL; p = strchr (k, SDB_RS); if (!p) return NULL; k = p+1; p2 = strchr (k, SDB_RS); if (!p2) { return (char *)sdb_decode (k, NULL); } return (char *)sdb_decode (p2+1, NULL); } R_API char *r_meta_get_var_comment (RAnal *a, int type, ut64 idx, ut64 addr) { char key[100]; const char *k, *p, *p2; snprintf (key, sizeof (key)-1, "meta.%c.0x%"PFMT64x".0x%"PFMT64x, type, addr, idx); k = sdb_const_get (DB, key, NULL); if (!k) return NULL; p = strchr (k, SDB_RS); if (!p) return NULL; k = p+1; p2 = strchr (k, SDB_RS); if (!p2) { return (char *)sdb_decode (k, NULL); } return (char *)sdb_decode (p2+1, NULL); } R_API int r_meta_del(RAnal *a, int type, ut64 addr, ut64 size, const char *str) { char key[100], *dtr, *s, *p, *next; #if 0 char key2[100]; #endif const char *ptr; int i; if (size == UT64_MAX) { // FULL CLEANUP // XXX: this thing ignores the type if (type == R_META_TYPE_ANY) { sdb_reset (DB); } else { snprintf (key, sizeof (key)-1, "meta.%c.count", type); int last = (ut64)sdb_num_get (DB, key, NULL)/K; for (i=0; i %s\n", key); sdb_unset (DB, key, 0); if (!next) break; } free (dtr); } } return false; } meta_inrange_del (a, addr, size); snprintf (key, sizeof (key)-1, type == R_META_TYPE_COMMENT ? "meta.C.0x%"PFMT64x : "meta.0x%"PFMT64x, addr); ptr = sdb_const_get (DB, key, 0); if (ptr) { sdb_unset (DB, key, 0); #if 0 // This code is wrong, but i guess it's necessary in case type is ANY for (i=0; ptr[i]; i++) { if (ptr[i] != SDB_RS) { snprintf (key2, sizeof (key2)-1, "meta.%c.0x%"PFMT64x, ptr[i], addr); printf ("UNSET (%s)\n", key2); sdb_unset (DB, key2, 0); } } #endif } sdb_unset (DB, key, 0); return false; } R_API int r_meta_var_comment_del(RAnal *a, int type, ut64 idx, ut64 addr) { char *key; key = r_str_newf ("meta.%c.0x%"PFMT64x"0x%"PFMT64x, type, addr, idx); sdb_unset (DB, key, 0); return 0; } R_API int r_meta_cleanup(RAnal *a, ut64 from, ut64 to) { return r_meta_del (a, R_META_TYPE_ANY, from, (to-from), NULL); } R_API void r_meta_item_free(void *_item) { RAnalMetaItem *item = _item; free (item); } R_API RAnalMetaItem *r_meta_item_new(int type) { RAnalMetaItem *mi = R_NEW0 (RAnalMetaItem); if (mi) mi->type = type; return mi; } R_API int r_meta_add(RAnal *a, int type, ut64 from, ut64 to, const char *str) { int space_idx = a->meta_spaces.space_idx; char *e_str, key[100], val[2048]; int exists; if (from > to) return false; if (from == to) to = from + 1; if (type == 100 && (to-from)<1) { return false; } /* set entry */ e_str = sdb_encode ((const void*)str, -1); snprintf (key, sizeof (key)-1, "meta.%c.0x%"PFMT64x, type, from); snprintf (val, sizeof (val)-1, "%d,%d,%s", (int)(to-from), space_idx, e_str); exists = sdb_exists (DB, key); sdb_set (DB, key, val, 0); free (e_str); // XXX: This is totally inefficient, using array_add withuot // checking return value is wrong practice, also it may lead // to inconsistent DB, and pretty bad performance. We should // store this list in a different storage that doesnt have // those limits and it's O(1) instead of O(n) snprintf (key, sizeof (key)-1, "meta.0x%"PFMT64x, from); if (exists) { const char *value = sdb_const_get (DB, key, 0); int idx = sdb_array_indexof (DB, key, value, 0); sdb_array_delete (DB, key, idx, 0); } snprintf (val, sizeof (val)-1, "%c", type); sdb_array_add (DB, key, val, 0); return true; } R_API RAnalMetaItem *r_meta_find(RAnal *a, ut64 at, int type, int where) { const char *infos, *metas; char key[100]; Sdb *s = a->sdb_meta; static RAnalMetaItem mi = {0}; // XXX: return allocated item? wtf if (where != R_META_WHERE_HERE) { eprintf ("THIS WAS NOT SUPOSED TO HAPPEN\n"); return NULL; } snprintf (key, sizeof (key)-1, "meta.0x%"PFMT64x, at); infos = sdb_const_get (s, key, 0); if (!infos) return NULL; for (; *infos; infos++) { /* XXX wtf, must use anal.meta.deserialize() */ char *p, *q; if (*infos==',') continue; snprintf (key, sizeof (key) - 1, "meta.%c.0x%"PFMT64x, *infos, at); metas = sdb_const_get (s, key, 0); mi.size = sdb_array_get_num (s, key, 0, 0); mi.type = *infos; mi.from = at; mi.to = at + mi.size; if (type != R_META_TYPE_ANY && type != mi.type) { continue; } if (metas) { p = strchr (metas, ','); if (!p) { continue; } mi.space = atoi (p + 1); q = strchr (p + 1, ','); if (!q) { continue; } free (mi.str); mi.str = (char*)sdb_decode (q + 1, 0); return &mi; } else mi.str = NULL; } return NULL; } R_API const char *r_meta_type_to_string(int type) { // XXX: use type as '%c' switch (type) { case R_META_TYPE_HIDE: return "Ch"; case R_META_TYPE_CODE: return "Cc"; case R_META_TYPE_DATA: return "Cd"; case R_META_TYPE_STRING: return "Cs"; case R_META_TYPE_FORMAT: return "Cf"; case R_META_TYPE_MAGIC: return "Cm"; case R_META_TYPE_COMMENT: return "CCu"; } return "(...)"; } static void printmetaitem(RAnal *a, RAnalMetaItem *d, int rad) { char *pstr, *str; //eprintf ("%d %d\n", d->space, a->meta_spaces.space_idx); if (a->meta_spaces.space_idx != -1) { if (a->meta_spaces.space_idx != d->space) { return; } } str = r_str_escape (d->str); if (str || d->type == 'd') { if (d->type=='s' && !*str) { free (str); return; } if (!str) { pstr = ""; } else if (d->type != 'C') { r_name_filter (str, 0); pstr = str; } else pstr = d->str; // r_str_sanitize (str); switch (rad) { case 'j': a->cb_printf ("{\"offset\":%"PFMT64d", \"type\":\"%s\", \"name\":\"%s\"}", d->from, r_meta_type_to_string (d->type), str); break; case 0: case 1: case '*': default: switch (d->type) { case 'a': //var and arg comments case 'v': case 'e': //XXX I think they do not belong to here break; case 'C': { const char *type = r_meta_type_to_string (d->type); char *s = sdb_encode ((const ut8*)pstr, -1); if (!s) s = strdup (pstr); if (rad) { if (!strcmp (type, "CCu")) { a->cb_printf ("%s base64:%s @ 0x%08"PFMT64x"\n", type, s, d->from); } else { a->cb_printf ("%s %s @ 0x%08"PFMT64x"\n", type, pstr, d->from); } } else { if (!strcmp (type, "CCu")) { char *mys = r_str_escape (pstr); a->cb_printf ("0x%08"PFMT64x" %s \"%s\"\n", d->from, type, mys); free (mys); } else { a->cb_printf ("0x%08"PFMT64x" %s \"%s\"\n", d->from, type, pstr); } } free (s); } break; case 'h': /* hidden */ case 's': /* string */ if (rad) { a->cb_printf ("%s %d @ 0x%08"PFMT64x" # %s\n", r_meta_type_to_string (d->type), (int)d->size, d->from, pstr); } else { // TODO: use b64 here a->cb_printf ("0x%08"PFMT64x" string[%d] \"%s\"\n", d->from, (int)d->size, pstr); } break; case 'd': /* data */ if (rad) { a->cb_printf ("%s %d @ 0x%08"PFMT64x"\n", r_meta_type_to_string (d->type), (int)d->size, d->from); } else { a->cb_printf ("0x%08"PFMT64x" data %s %d\n", d->from, r_meta_type_to_string (d->type), (int)d->size); } break; case 'm': /* magic */ case 'f': /* formatted */ if (rad) { a->cb_printf ("%s %d %s @ 0x%08"PFMT64x"\n", r_meta_type_to_string (d->type), (int)d->size, pstr, d->from); } else { const char *dtype = d->type=='m'?"magic":"format"; a->cb_printf ("0x%08"PFMT64x" %s %d %s\n", d->from, dtype, (int)d->size, pstr); } break; default: if (rad) { a->cb_printf ("%s %d 0x%08"PFMT64x" # %s\n", r_meta_type_to_string (d->type), (int)d->size, d->from, pstr); } else { // TODO: use b64 here a->cb_printf ("0x%08"PFMT64x" array[%d] %s %s\n", d->from, (int)d->size, r_meta_type_to_string (d->type), pstr); } break; } break; } if (str) free (str); } } static int meta_print_item(void *user, const char *k, const char *v) { // const char *v; // size const char *v2; // space_idx RAnalMetaUserItem *ui = user; RAnalMetaItem it; if (strlen (k)<8) return 1; if (memcmp (k+6, ".0x", 3)) return 1; it.type = k[5]; it.size = sdb_atoi (v); it.from = sdb_atoi (k+7); v2 = strchr (v, ','); if (!v2) goto beach; it.space = atoi (v2+1); it.to = it.from + it.size; it.str = strchr (v2+1, ','); if (it.str) it.str = (char *)sdb_decode ((const char*)it.str+1, 0); else { it.str = strdup (it.str? it.str: ""); // don't break in free if (!it.str) goto beach; } printmetaitem (ui->anal, &it, ui->rad); free (it.str); beach: return 1; } R_API int r_meta_list_cb(RAnal *a, int type, int rad, SdbForeachCallback cb, void *user) { RAnalMetaUserItem ui = { a, type, rad, cb, user, 0 }; if (rad=='j') a->cb_printf ("["); if (cb) { sdb_foreach (DB, cb, &ui); } else { sdb_foreach (DB, meta_print_item, &ui); } if (rad=='j') a->cb_printf ("]\n"); return ui.count; } R_API int r_meta_list(RAnal *a, int type, int rad) { return r_meta_list_cb (a, type, rad, NULL, NULL); } static int meta_enumerate_cb(void *user, const char *k, const char *v) { const char *v2; RAnalMetaUserItem *ui = user; RList *list = ui->user; //RAnal *a = ui->anal; RAnalMetaItem *it; if (strlen (k)<8) return 1; if (memcmp (k+6, ".0x", 3)) return 1; it = R_NEW0 (RAnalMetaItem); if (!it) return 0; it->type = k[5]; it->size = sdb_atoi (v); it->from = sdb_atoi (k+7); it->to = it->from + it->size; v2 = strchr (v, ','); if (!v2) { free (it); goto beach; } it->space = atoi (v2+1); it->str = strchr (v2+1, ','); if (it->str) { it->str = (char *)sdb_decode ((const char*)it->str+1, 0); } else { free(it); goto beach; } //printmetaitem (ui->anal, &it, ui->rad); r_list_append (list, it); beach: return 1; } R_API RList *r_meta_enumerate(RAnal *a, int type) { RList *list = r_list_new (); r_meta_list_cb (a, type, 0, meta_enumerate_cb, list); return list; } static int deserialize(RAnalMetaItem *it, const char *k, const char *v) { const char *v2; if (strlen (k)<8) return 1; if (memcmp (k+6, ".0x", 3)) return 1; it->type = k[5]; it->size = sdb_atoi (v); it->from = sdb_atoi (k+7); it->to = it->from + it->size; v2 = strchr (v, ','); if (!v2) goto beach; it->space = atoi (v2+1); it->str = strchr (v2+1, ','); //printmetaitem (ui->anal, &it, ui->rad); beach: return 1; } static void serialize(RAnalMetaItem *it, char *k, char *v) { sprintf (k, "meta.%c.0x%"PFMT64x, it->type, it->from); snprintf (v, 4095, "%d,%d,%s", (int)it->size, it->space, it->str); } static int meta_unset_cb(void *user, const char *k, const char *v) { char nk[128], nv[4096]; RAnalMetaUserItem *ui = user; RAnal *a = ui->anal; RAnalMetaItem it = {0}; if (!strstr(k, ".0x")) return 1; deserialize (&it, k, v); if (it.space != -1) { it.space = -1; serialize (&it, nk, nv); sdb_set (DB, nk, nv, 0); } return 1; } R_API void r_meta_space_unset_for(RAnal *a, int type) { r_meta_list_cb (a, type, 0, meta_unset_cb, NULL); } typedef struct { int count; int index; int ctx; } myMetaUser; static int meta_count_cb(void *user, const char *k, const char *v) { RAnalMetaUserItem *ui = user; myMetaUser *mu = ui->user; RAnalMetaItem it = {0}; if (!strstr(k, ".0x")) return 1; deserialize (&it, k, v); if (mu) { if (it.space == mu->ctx) { mu->count++; } } return 1; } R_API int r_meta_space_count_for(RAnal *a, int ctx) { myMetaUser mu = {0}; mu.ctx = ctx; int type = a->meta_spaces.space_idx; r_meta_list_cb (a, type, 0, meta_count_cb, &mu); return mu.count; }