radare2/libr/core/cmd_meta.c

841 lines
21 KiB
C

/* radare2 - LGPL - Copyright 2009-2016 - pancake */
#include "r_anal.h"
#include "r_bin.h"
#include "r_cons.h"
#include "r_core.h"
#include "r_print.h"
#include "r_types.h"
#include "sdb/sdb.h"
static int remove_meta_offset(RCore *core, ut64 offset) {
char aoffset[64];
char *aoffsetptr = sdb_itoa (offset, aoffset, 16);
if (!aoffsetptr) {
eprintf ("Failed to convert %"PFMT64x" to a key", offset);
return -1;
}
return sdb_unset (core->bin->cur->sdb_addrinfo, aoffsetptr, 0);
}
static void print_meta_offset(RCore *core, ut64 offset) {
int ret, line, line_old, i;
char file[1024];
ret = r_bin_addr2line (core->bin, offset, file, sizeof (file)-1, &line);
if (ret) {
r_cons_printf ("file %s\nline %d\n", file, line);
line_old = line;
if (line >= 2)
line -= 2;
if (r_file_exists (file)) {
for (i = 0; i<5; i++) {
char *row = r_file_slurp_line (file, line+i, 0);
if (row) {
r_cons_printf ("%c %.3x %s\n", line+i == line_old ? '>' : ' ', line+i, row);
free (row);
}
}
} else {
eprintf ("Cannot open '%s'\n", file);
}
} else {
eprintf ("Cannot find meta information at 0x%08"
PFMT64x"\n", offset);
}
}
static int remove_meta_fileline(RCore *core, const char *file_line) {
return sdb_unset (core->bin->cur->sdb_addrinfo, file_line, 0);
}
static int print_meta_fileline(RCore *core, const char *file_line) {
char *meta_info = sdb_get (core->bin->cur->sdb_addrinfo, file_line, 0);
if (meta_info) {
r_cons_printf ("Meta info %s\n", meta_info);
} else {
r_cons_printf ("No meta info for %s found\n", file_line);
}
return 0;
}
static int print_addrinfo (void *user, const char *k, const char *v) {
ut64 offset;
char *colonpos, *subst;
offset = sdb_atoi (v);
if (!offset)
return true;
subst = strdup (k);
colonpos = strchr (subst, '|');
if (colonpos)
*colonpos = ':';
r_cons_printf ("CL %s %s\n", subst, v);
free (subst);
return true;
}
static int cmd_meta_add_fileline(Sdb *s, char *fileline, ut64 offset) {
char aoffset[64], *aoffsetptr;
aoffsetptr = sdb_itoa (offset, aoffset, 16);
if (!aoffsetptr)
return -1;
if (!sdb_add (s, aoffsetptr, fileline, 0)) {
sdb_set (s, aoffsetptr, fileline, 0);
}
if (!sdb_add (s, fileline, aoffsetptr, 0)) {
sdb_set (s, fileline, aoffsetptr, 0);
}
return 0;
}
static int cmd_meta_lineinfo(RCore *core, const char *input) {
int ret;
ut64 offset = UT64_MAX; // use this as error value
int remove = false;
int all = false;
const char *p = input;
char *colon, *space, *file_line = 0;
if (*p == '?') {
eprintf ("Usage: CL[-][*] [file:line] [addr]");
return 0;
}
if (*p == '-') {
p++;
remove = true;
}
if (*p == '*') {
p++;
all = true;
}
if (all) {
if (remove) {
sdb_reset (core->bin->cur->sdb_addrinfo);
} else {
sdb_foreach (core->bin->cur->sdb_addrinfo, print_addrinfo, NULL);
}
return 0;
}
while (*p == ' ') {
p++;
}
if (*p) {
offset = r_num_math (core->num, p);
if (!offset)
offset = core->offset;
} else offset = core->offset;
colon = strchr (p, ':');
if (colon) {
space = strchr (p, ' ');
if (!space) {
file_line = strdup (p);
} else if (space > colon) {
file_line = r_str_ndup (p, space - p);
} else {
goto error;
}
colon = strchr (file_line, ':');
if (!colon)
goto error;
*colon = '|';
while (*p != ' ')
p++;
while (*p == ' ')
p++;
if (*p != '\0') {
ret = sscanf (p, "0x%"PFMT64x, &offset);
if (ret != 1) {
eprintf ("Failed to parse addr at %s\n", p);
goto error;
}
ret = cmd_meta_add_fileline (core->bin->cur->sdb_addrinfo,
file_line, offset);
goto error;
}
if (!file_line)
return -1;
if (remove) {
remove_meta_fileline (core, file_line);
} else {
print_meta_fileline (core, file_line);
}
free (file_line);
return 0;
}
offset = core->offset;
if (offset != UT64_MAX) {
if (remove) {
remove_meta_offset (core, offset);
} else {
print_meta_offset (core, offset);
}
} else {
goto error;
}
return 0;
error:
free (file_line);
return -1;
}
static int cmd_meta_comment(RCore *core, const char *input) {
ut64 addr = core->offset;
switch (input[1]) {
case '?': {
const char* help_msg[] = {
"Usage:", "CC[-+!*au] [base64:..|str] @ addr", "",
"CC", "", "list all comments in human friendly form",
"CC*", "", "list all comments in r2 commands",
"CC.", "", "show comment at current offset",
"CC,", " [file]", "show or set comment file",
"CC", " or maybe not", "append comment at current address",
"CC+", " same as above", "append comment at current address",
"CC!", "", "edit comment using cfg.editor (vim, ..)",
"CC-", " @ cmt_addr", "remove comment at given address",
"CCu", " good boy @ addr", "add good boy comment at given address",
"CCu", " base64:AA== @ addr", "add comment in base64",
NULL};
r_core_cmd_help (core, help_msg);
} break;
case ',': // "CC,"
if (input[2]=='?') {
eprintf ("Usage: CC, [file]\n");
} else if (input[2]==' ') {
const char *fn = input+2;
char *comment = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
while (*fn== ' ')fn++;
if (comment && *comment) {
// append filename in current comment
char *nc = r_str_newf ("%s ,(%s)", comment, fn);
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, nc);
free (nc);
} else {
char *comment = r_str_newf (",(%s)", fn);
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, comment);
free (comment);
}
} else {
char *comment = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
if (comment && *comment) {
char *cmtfile = r_str_between (comment, ",(", ")");
if (cmtfile && *cmtfile) {
char *getcommapath(RCore *core);
char *cwd = getcommapath (core);
r_cons_printf ("%s"R_SYS_DIR"%s\n", cwd, cmtfile);
free (cwd);
}
free (cmtfile);
}
free (comment);
}
break;
case '.':
{
char *comment = r_meta_get_string (
core->anal, R_META_TYPE_COMMENT, addr);
if (comment) {
r_cons_println (comment);
free (comment);
}
}
break;
case 0:
r_meta_list (core->anal, R_META_TYPE_COMMENT, 0);
break;
case 'j':
r_meta_list (core->anal, R_META_TYPE_COMMENT, 'j');
break;
case '!':
{
char *out, *comment = r_meta_get_string (
core->anal, R_META_TYPE_COMMENT, addr);
out = r_core_editor (core, NULL, comment);
if (out) {
//r_meta_add (core->anal->meta, R_META_TYPE_COMMENT, addr, 0, out);
r_core_cmdf (core, "CC-@0x%08"PFMT64x, addr);
//r_meta_del (core->anal->meta, input[0], addr, addr+1, NULL);
r_meta_set_string (core->anal,
R_META_TYPE_COMMENT, addr, out);
free (out);
}
free (comment);
}
break;
case '+':
case ' ':
{
const char* newcomment = r_str_chop_ro (input + 2);
char *text, *comment = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
char *nc = strdup (newcomment);
r_str_unescape (nc);
if (comment) {
text = malloc (strlen (comment)+strlen (newcomment)+2);
if (text) {
strcpy (text, comment);
strcat (text, "\n");
strcat (text, nc);
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, text);
free (text);
} else {
r_sys_perror ("malloc");
}
} else {
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, nc);
}
free (nc);
}
break;
case '*':
r_meta_list (core->anal, R_META_TYPE_COMMENT, 1);
break;
case '-': // "CC-"
r_meta_del (core->anal, R_META_TYPE_COMMENT, core->offset, 1, NULL);
break;
case 'u':
//
{
char *newcomment;
const char *arg = input + 2;
while (*arg && *arg == ' ') arg++;
if (!strncmp (arg, "base64:", 7)) {
char *s = (char *)sdb_decode (arg+7, NULL);
if (s) {
newcomment = s;
} else {
newcomment = NULL;
}
} else {
newcomment = strdup (arg);
}
if (newcomment) {
char *comment = r_meta_get_string (
core->anal, R_META_TYPE_COMMENT, addr);
if (!comment || (comment && !strstr (comment, newcomment))) {
r_meta_set_string (core->anal, R_META_TYPE_COMMENT,
addr, newcomment);
}
free (comment);
free (newcomment);
}
}
break;
case 'a':
{
char *s, *p;
s = strchr (input, ' ');
if (s) {
s = strdup (s+1);
} else {
eprintf ("Usage\n");
return false;
}
p = strchr (s, ' ');
if (p) *p++ = 0;
ut64 addr;
if (input[2]=='-') {
if (input[3]) {
addr = r_num_math (core->num, input+3);
r_meta_del (core->anal,
R_META_TYPE_COMMENT,
addr, 1, NULL);
} else eprintf ("Usage: CCa-[address]\n");
free (s);
return true;
}
addr = r_num_math (core->num, s);
// Comment at
if (p) {
if (input[2]=='+') {
char *comment = r_meta_get_string (
core->anal, R_META_TYPE_COMMENT,
addr);
if (comment) {
char* text = r_str_newf ("%s\n%s", comment, p);
r_meta_add (core->anal,
R_META_TYPE_COMMENT,
addr, addr+1, text);
free (text);
} else {
r_meta_add (core->anal,
R_META_TYPE_COMMENT,
addr, addr+1, p);
}
} else {
r_meta_add (core->anal,
R_META_TYPE_COMMENT,
addr, addr+1, p);
}
} else eprintf ("Usage: CCa [address] [comment]\n");
free (s);
return true;
}
}
return true;
}
static int cmd_meta_hsdmf(RCore *core, const char *input) {
int n, type = input[0];
char *t = 0, *p, name[256];
int repeat = 1;
ut64 addr_end = 0LL, addr = core->offset;
switch (input[1]) {
case '?':
eprintf ("See C?\n");
break;
case '-':
switch (input[2]) {
case '*':
core->num->value = r_meta_del (core->anal,
input[0], 0, UT64_MAX, NULL);
break;
case ' ':
addr = r_num_math (core->num, input+3);
/* fallthrough */
default:
core->num->value = r_meta_del (core->anal,
input[0], addr, 1, NULL);
break;
}
break;
case '*':
r_meta_list (core->anal, input[0], 1);
break;
case '!':
{
char *out, *comment = r_meta_get_string (
core->anal, R_META_TYPE_COMMENT, addr);
out = r_core_editor (core, NULL, comment);
if (out) {
//r_meta_add (core->anal->meta, R_META_TYPE_COMMENT, addr, 0, out);
r_core_cmdf (core, "CC-@0x%08"PFMT64x, addr);
//r_meta_del (core->anal->meta, input[0], addr, addr+1, NULL);
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, out);
free (out);
}
free (comment);
}
break;
case ' ':
case '\0':
if (type!='z' && input[1] == '*') {
r_meta_list (core->anal, type, 0);
break;
}
if (type == 'z') {
type = 's';
}
if (strlen (input) > 2) {
char *rep = strchr (input + 2, '[');
if (!rep) rep = strchr (input + 2, ' ');
if (rep) {
repeat = r_num_get (core->num, rep + 1);
}
}
int repcnt = 0;
if (repeat < 1) repeat = 1;
while (repcnt < repeat) {
t = strdup (r_str_chop_ro (input + 1));
p = NULL;
n = 0;
strncpy (name, t, sizeof (name) - 1);
if (type != 'C') {
n = r_num_math (core->num, t);
if (type == 'f') {
p = strchr (t, ' ');
if (p) {
n = r_print_format (core->print, addr, core->block,
core->blocksize, p + 1, 0, NULL, NULL);
}
}
if (type == 's') {
strncpy (name, t, sizeof (name) - 1);
(void)r_core_read_at (core, addr, (ut8*)name, sizeof (name) - 1);
name[sizeof (name) - 1] = '\0';
int name_len = strlen (name);
if (n == 0) {
n = name_len + 1;
} else {
if (n > 0 && n < name_len) {
name[n] = 0;
}
}
}
if (!*t || n > 0) {
RFlagItem *fi;
p = strchr (t, ' ');
if (p) {
*p = '\0';
strncpy (name, p + 1, sizeof (name)-1);
} else {
if (type != 's') {
fi = r_flag_get_i (core->flags, addr);
if (fi) strncpy (name, fi->name, sizeof (name)-1);
}
}
} else if (n < 1) {
eprintf ("Invalid length %d\n", n);
return false;
}
}
if (!n) n++;
addr_end = addr + n;
r_meta_add (core->anal, type, addr, addr_end, name);
free (t);
repcnt ++;
addr = addr_end;
}
//r_meta_cleanup (core->anal->meta, 0LL, UT64_MAX);
break;
default:
eprintf ("Missing space after CC\n");
break;
}
return true;
}
void r_comment_var_help(RCore *core, char type) {
const char *help_bp[] = {
"Usage:", "Cvb", "[name] [comment]",
"Cvb?", "", "show this help",
"Cvb", "", "list all base pointer args/vars comments in human friendly format",
"Cvb*", "", "list all base pointer args/vars comments in r2 format",
"Cvb-", "[name]", "delete comments for var/arg at current offset for base pointer",
"Cvb", " [name]", "Show comments for var/arg at current offset for base pointer",
"Cvb", " [name] [comment]", "add/append comment for the variable with the current name",
"Cvb!", "[name]", "edit comment using cfg editor",
NULL
};
const char *help_sp[] = {
"Usage:", "Cvs", "[name] [comment]",
"Cvs?", "", "show this help",
"Cvs", "", "list all stack based args/vars comments in human friendly format",
"Cvs*", "", "list all stack based args/vars comments in r2 format",
"Cvs-", "[name]", "delete comments for stack pointer var/arg with that name",
"Cvs", "[name]", "Show comments for stack pointer var/arg with that name",
"Cvs", "[name] [comment]", "add/append comment for the variable",
"Cvs!", "[name]", "edit comment using cfg editor",
NULL
};
const char *help_reg[] = {
"Usage:", "Cvr", "[name] [comment]",
"Cvr?", "", "show this help",
"Cvr", "", "list all register based args comments in human friendly format",
"Cvr*", "", "list all register based args comments in r2 format",
"Cvr-", "[name]", "delete comments for register based arg for that name",
"Cvr", "[name]", "Show comments for register based arg for that name",
"Cvr", "[name] [comment]", "add/append comment for the variable",
"Cvr!", "[name]", "edit comment using cfg editor",
NULL
};
switch (type) {
case 'b':
r_core_cmd_help (core, help_bp);
break;
case 's':
r_core_cmd_help (core, help_sp);
break;
case 'r':
r_core_cmd_help (core, help_reg);
break;
default:
r_cons_printf("See Cvb, Cvs and Cvr\n");
}
}
void r_comment_vars(RCore *core, const char *input) {
//TODO enable base64 and make it the default for C*
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
int idx;
char *oname = NULL, *name = NULL;
char *oldcomment = NULL;
char *heap_comment = NULL;
RAnalVar *var;
if (input[1] == '?' || (input[0] != 'b' && input[0] != 'r' && input[0] != 's') ) {
r_comment_var_help (core, input[0]);
return;
}
if (!fcn) {
eprintf ("Cant find function here\n");
return;
}
oname = name = strdup (input + 2);
while (*name == ' ') {
name++;
}
switch (input[1]) {
case '*':
case '\0': {
RList *var_list;
RListIter *iter;
var_list = r_anal_var_list (core->anal, fcn, input[0]);
r_list_foreach (var_list, iter, var) {
oldcomment = r_meta_get_var_comment (core->anal, input[0], var->delta, fcn->addr);
if (!oldcomment) {
continue;
}
if (!input[1]) {
r_cons_printf ("%s : %s\n", var->name, oldcomment);
} else {
r_cons_printf ("\"Cv%c %s base64:%s @ 0x%08"PFMT64x"\"\n", input[0], var->name,
sdb_encode ((const ut8 *) oldcomment, strlen(oldcomment)), fcn->addr);
}
}
}
break;
case ' ': {
// TODO check that idx exist
char *comment = strstr (name, " ");
if (comment) { // new comment given
if (*comment) {
*comment++ = 0;
}
if (!strncmp (comment, "base64:", 7)) {
heap_comment = (char *)sdb_decode (comment + 7, NULL);
comment = heap_comment;
}
}
var = r_anal_var_get_byname (core->anal, fcn, name);
if (var) {
idx = var->delta;
} else if (!strncmp (name, "0x", 2)) {
idx = (int) r_num_get (NULL, name);
} else if (!strncmp (name, "-0x", 3)) {
idx = -(int) r_num_get (NULL, name+1);
} else {
eprintf ("cant find variable named `%s`\n",name);
free (heap_comment);
break;
}
r_anal_var_free (var);
if (!r_anal_var_get (core->anal, fcn->addr, input[0], 1, idx)) {
eprintf ("cant find variable at given offset\n");
} else {
oldcomment = r_meta_get_var_comment (core->anal, input[0], idx, fcn->addr);
if (oldcomment) {
if (comment && *comment) {
char *text = r_str_newf ("%s\n%s", oldcomment, comment);
r_meta_set_var_comment (core->anal, input[0], idx, fcn->addr, text);
free (text);
} else {
r_cons_println (oldcomment);
}
} else {
r_meta_set_var_comment (core->anal, input[0], idx, fcn->addr, comment);
}
}
free (heap_comment);
}
break;
case '-':
var = r_anal_var_get_byname (core->anal,fcn, name);
if (var) {
idx = var->delta;
} else if (!strncmp (name, "0x", 2)) {
idx = (int) r_num_get (NULL, name);
} else if (!strncmp (name, "-0x", 3)) {
idx = -(int) r_num_get (NULL, name+1);
}else {
eprintf ("cant find variable named `%s`\n",name);
break;
}
r_anal_var_free (var);
//XXX TODO here we leak a var
if (!r_anal_var_get (core->anal, fcn->addr, input[0],1,idx)) {
eprintf ("cant find variable at given offset\n");
break;
}
r_meta_var_comment_del (core->anal, input[0], idx, fcn->addr);
break;
case '!': {
char *comment;
var = r_anal_var_get_byname (core->anal,fcn, name);
if (!var) {
eprintf ("cant find variable named `%s`\n",name);
break;
}
oldcomment = r_meta_get_var_comment (core->anal, input[0], var->delta, fcn->addr);
comment = r_core_editor (core, NULL, oldcomment);
if (comment) {
r_meta_var_comment_del (core->anal, input[0], var->delta, fcn->addr);
r_meta_set_var_comment (core->anal, input[0], var->delta, fcn->addr, comment);
free (comment);
}
r_anal_var_free (var);
}
break;
}
free (oname);
}
static int cmd_meta(void *data, const char *input) {
RCore *core = (RCore*)data;
RAnalFunction *f;
RSpaces *ms;
int i;
switch (*input) {
case 'v': // Cr
r_comment_vars (core, input + 1);
break;
case 'j':
case '*':
r_meta_list (core->anal, R_META_TYPE_ANY, *input);
break;
case 'L':
cmd_meta_lineinfo (core, input + 1);
break;
case 'C': // "CC"
cmd_meta_comment (core, input);
break;
case 'h': /* comment */
case 's': /* string */
case 'z': /* zero-terminated string */
case 'd': /* data */
case 'm': /* magic */
case 'f': /* formatted */
cmd_meta_hsdmf (core, input);
break;
case '-':
if (input[1]!='*') {
i = r_num_math (core->num, input+((input[1]==' ')?2:1));
r_meta_del (core->anal, R_META_TYPE_ANY, core->offset, i, "");
} else r_meta_cleanup (core->anal, 0LL, UT64_MAX);
break;
case '\0':
case '?':{
const char* help_msg[] = {
"Usage:", "C[-LCvsdfm*?][*?] [...]", " # Metadata management",
"C*", "", "list meta info in r2 commands",
"C-", " [len] [[@]addr]", "delete metadata at given address range",
"CL", "[-][*] [file:line] [addr]", "show or add 'code line' information (bininfo)",
"CS", "[-][space]", "manage meta-spaces to filter comments, etc..",
"CC", "[-] [comment-text] [@addr]", "add/remove comment",
"CC!", " [@addr]", "edit comment with $EDITOR",
"CCa", "[-at]|[at] [text] [@addr]", "add/remove comment at given address",
"CCu", " [comment-text] [@addr]", "add unique comment",
"Ca", "[?]", "add comments to base pointer bases args/vars",
"Ce", "[?]", "add comments to stack pointer based args/vars",
"Cv", "[?]", "add comments to register based args",
"Cs", "[-] [size] [@addr]", "add string",
"Cz", "[@addr]", "add zero-terminated string",
"Ch", "[-] [size] [@addr]", "hide data",
"Cd", "[-] [size] [repeat] [@addr]", "hexdump data array (Cd 4 10 == dword [10])",
"Cf", "[-] [sz] [fmt..] [@addr]", "format memory (see pf?)",
"Cm", "[-] [sz] [fmt..] [@addr]", "magic parse (see pm?)",
NULL};
r_core_cmd_help (core, help_msg);
}
break;
case 'F':
f = r_anal_get_fcn_in (core->anal, core->offset,
R_ANAL_FCN_TYPE_FCN|R_ANAL_FCN_TYPE_SYM);
if (f) r_anal_str_to_fcn (core->anal, f, input+2);
else eprintf ("Cannot find function here\n");
break;
case 'S':
ms = &core->anal->meta_spaces;
/** copypasta from `fs`.. this must be refactorized to be shared */
switch (input[1]) {
case '?':
{
const char *help_msg[] = {
"Usage: CS","[*] [+-][metaspace|addr]", " # Manage metaspaces",
"CS","","display metaspaces",
"CS"," *","select all metaspaces",
"CS"," metaspace","select metaspace or create if it doesn't exist",
"CS","-metaspace","remove metaspace",
"CS","-*","remove all metaspaces",
"CS","+foo","push previous metaspace and set",
"CS","-","pop to the previous metaspace",
// "CSm"," [addr]","move metas at given address to the current metaspace",
"CSr"," newname","rename selected metaspace",
NULL};
r_core_cmd_help (core, help_msg);
}
break;
case '+':
r_space_push (ms, input+2);
break;
case 'r':
if (input[2]==' ')
r_space_rename (ms, NULL, input+2);
else eprintf ("Usage: CSr [newname]\n");
break;
case '-':
if (input[2]) {
if (input[2]=='*') {
r_space_unset (ms, NULL);
} else {
r_space_unset (ms, input+2);
}
} else {
r_space_pop (ms);
}
break;
case 'j':
case '\0':
case '*':
r_space_list (ms, input[1]);
break;
case ' ':
r_space_set (ms, input + 2);
break;
#if 0
case 'm':
{ RFlagItem *f;
ut64 off = core->offset;
if (input[2] == ' ')
off = r_num_math (core->num, input+2);
f = r_flag_get_i (core->flags, off);
if (f) {
f->space = core->flags->space_idx;
} else eprintf ("Cannot find any flag at 0x%"PFMT64x".\n", off);
}
break;
#endif
default: {
int i, j = 0;
for (i = 0; i < R_FLAG_SPACES_MAX; i++) {
if (!ms->spaces[i]) continue;
r_cons_printf ("%02d %c %s\n", j++,
(i == ms->space_idx)?'*':' ',
ms->spaces[i]);
}
} break;
}
break;
}
return true;
}