1 /* radare2 - LGPL - Copyright 2009-2020 - pancake */
2
3 #include "r_anal.h"
4 #include "r_bin.h"
5 #include "r_cons.h"
6 #include "r_core.h"
7 #include "r_util.h"
8 #include "r_types.h"
9 #include <sdb.h>
10
11 char *getcommapath(RCore *core);
12
13 static const char *help_msg_C[] = {
14 "Usage:", "C[-LCvsdfm*?][*?] [...]", " # Metadata management",
15 "C", "", "list meta info in human friendly form",
16 "C*", "", "list meta info in r2 commands",
17 "C*.", "", "list meta info of current offset in r2 commands",
18 "C-", " [len] [[@]addr]", "delete metadata at given address range",
19 "C.", "", "list meta info of current offset in human friendly form",
20 "CC!", " [@addr]", "edit comment with $EDITOR",
21 "CC", "[?] [-] [comment-text] [@addr]", "add/remove comment",
22 "CC.", "[addr]", "show comment in current address",
23 "CCa", "[+-] [addr] [text]", "add/remove comment at given address",
24 "CCu", " [comment-text] [@addr]", "add unique comment",
25 "CF", "[sz] [fcn-sign..] [@addr]", "function signature",
26 "CL", "[-][*] [file:line] [addr]", "show or add 'code line' information (bininfo)",
27 "CS", "[-][space]", "manage meta-spaces to filter comments, etc..",
28 "C[Cthsdmf]", "", "list comments/types/hidden/strings/data/magic/formatted in human friendly form",
29 "C[Cthsdmf]*", "", "list comments/types/hidden/strings/data/magic/formatted in r2 commands",
30 "Cd", "[-] [size] [repeat] [@addr]", "hexdump data array (Cd 4 10 == dword [10])",
31 "Cd.", " [@addr]", "show size of data at current address",
32 "Cf", "[?][-] [sz] [0|cnt][fmt] [a0 a1...] [@addr]", "format memory (see pf?)",
33 "Ch", "[-] [size] [@addr]", "hide data",
34 "Cm", "[-] [sz] [fmt..] [@addr]", "magic parse (see pm?)",
35 "Cs", "[?] [-] [size] [@addr]", "add string",
36 "Ct", "[?] [-] [comment-text] [@addr]", "add/remove type analysis comment",
37 "Ct.", "[@addr]", "show comment at current or specified address",
38 "Cv", "[bsr][?]", "add comments to args",
39 "Cz", "[@addr]", "add string (see Cs?)",
40 NULL
41 };
42
43 static const char *help_msg_CC[] = {
44 "Usage:", "CC[-+!*au] [base64:..|str] @ addr", "",
45 "CC!", "", "edit comment using cfg.editor (vim, ..)",
46 "CC", " [text]", "append comment at current address",
47 "CC", "", "list all comments in human friendly form",
48 "CC*", "", "list all comments in r2 commands",
49 "CC+", " [text]", "append comment at current address",
50 "CC,", " [table-query]", "list comments in table format",
51 "CCF", " [file]", "show or set comment file",
52 "CC-", " @ cmt_addr", "remove comment at given address",
53 "CC.", "", "show comment at current offset",
54 "CCf", "", "list comments in function",
55 "CCf-", "", "delete all comments in current function",
56 "CCu", " base64:AA== @ addr", "add comment in base64",
57 "CCu", " good boy @ addr", "add good boy comment at given address",
58 NULL
59 };
60
61 static const char *help_msg_Ct[] = {
62 "Usage: Ct", "[.|-] [@ addr]", " # Manage comments for variable types",
63 "Ct", "", "list all variable type comments",
64 "Ct", " comment-text [@ addr]", "place comment at current or specified address",
65 "Ct.", " [@ addr]", "show comment at current or specified address",
66 "Ct-", " [@ addr]", "remove comment at current or specified address",
67 NULL
68 };
69
70 static const char *help_msg_CS[] = {
71 "Usage: CS","[*] [+-][metaspace|addr]", " # Manage metaspaces",
72 "CS","","display metaspaces",
73 "CS"," *","select all metaspaces",
74 "CS"," metaspace","select metaspace or create if it doesn't exist",
75 "CS","-metaspace","remove metaspace",
76 "CS","-*","remove all metaspaces",
77 "CS","+foo","push previous metaspace and set",
78 "CS","-","pop to the previous metaspace",
79 // "CSm"," [addr]","move metas at given address to the current metaspace",
80 "CSr"," newname","rename selected metaspace",
81 NULL
82 };
83
84 static const char *help_msg_Cs[] = {
85 "Usage:", "Cs[ga-*.] [size] [@addr]", "",
86 "NOTE:", " size", "1 unit in bytes == width in bytes of smallest possible char in encoding,",
87 "", "", " so ascii/latin1/utf8 = 1, utf16le = 2",
88 " Cz", " [size] [@addr]", "ditto",
89 "Cs", " [size] @addr", "add string (guess latin1/utf16le)",
90 "Cs", "", "list all strings in human friendly form",
91 "Cs*", "", "list all strings in r2 commands",
92 "Cs-", " [@addr]", "remove string",
93 "Cs.", "", "show string at current address",
94 "Cs..", "", "show string + info about it at current address",
95 "Cs.j", "", "show string at current address in JSON",
96 "Cs8", " [size] [@addr]", "add utf8 string",
97 "Csa", " [size] [@addr]", "add ascii/latin1 string",
98 "Csg", " [size] [@addr]", "as above but addr not needed",
99 NULL
100 };
101
102 static const char *help_msg_Cvb[] = {
103 "Usage:", "Cvb", "[name] [comment]",
104 "Cvb?", "", "show this help",
105 "Cvb", "", "list all base pointer args/vars comments in human friendly format",
106 "Cvb*", "", "list all base pointer args/vars comments in r2 format",
107 "Cvb-", "[name]", "delete comments for var/arg at current offset for base pointer",
108 "Cvb", " [name]", "Show comments for var/arg at current offset for base pointer",
109 "Cvb", " [name] [comment]", "add/append comment for the variable with the current name",
110 "Cvb!", "[name]", "edit comment using cfg editor",
111 NULL
112 };
113
114 static const char *help_msg_Cvr[] = {
115 "Usage:", "Cvr", "[name] [comment]",
116 "Cvr?", "", "show this help",
117 "Cvr", "", "list all register based args comments in human friendly format",
118 "Cvr*", "", "list all register based args comments in r2 format",
119 "Cvr-", "[name]", "delete comments for register based arg for that name",
120 "Cvr", "[name]", "Show comments for register based arg for that name",
121 "Cvr", "[name] [comment]", "add/append comment for the variable",
122 "Cvr!", "[name]", "edit comment using cfg editor",
123 NULL
124 };
125
126 static const char *help_msg_Cvs[] = {
127 "Usage:", "Cvs", "[name] [comment]",
128 "Cvs!", "[name]", "edit comment using cfg editor",
129 "Cvs", "", "list all stack based args/vars comments in human friendly format",
130 "Cvs", "[name] [comment]", "add/append comment for the variable",
131 "Cvs", "[name]", "Show comments for stack pointer var/arg with that name",
132 "Cvs*", "", "list all stack based args/vars comments in r2 format",
133 "Cvs-", "[name]", "delete comments for stack pointer var/arg with that name",
134 "Cvs?", "", "show this help",
135 NULL
136 };
137
cmd_meta_init(RCore * core,RCmdDesc * parent)138 static void cmd_meta_init(RCore *core, RCmdDesc *parent) {
139 DEFINE_CMD_DESCRIPTOR (core, C);
140 DEFINE_CMD_DESCRIPTOR (core, CC);
141 DEFINE_CMD_DESCRIPTOR (core, CS);
142 DEFINE_CMD_DESCRIPTOR (core, Cs);
143 DEFINE_CMD_DESCRIPTOR (core, Cvb);
144 DEFINE_CMD_DESCRIPTOR (core, Cvr);
145 DEFINE_CMD_DESCRIPTOR (core, Cvs);
146 }
147
remove_meta_offset(RCore * core,ut64 offset)148 static int remove_meta_offset(RCore *core, ut64 offset) {
149 char aoffset[64];
150 char *aoffsetptr = sdb_itoa (offset, aoffset, 16);
151 if (!aoffsetptr) {
152 eprintf ("Failed to convert %"PFMT64x" to a key", offset);
153 return -1;
154 }
155 return sdb_unset (core->bin->cur->sdb_addrinfo, aoffsetptr, 0);
156 }
157
print_meta_offset(RCore * core,ut64 addr)158 static bool print_meta_offset(RCore *core, ut64 addr) {
159 int line, line_old, i;
160 char file[1024];
161
162 int ret = r_bin_addr2line (core->bin, addr, file, sizeof (file) - 1, &line);
163 if (ret) {
164 r_cons_printf ("file: %s\nline: %d\n", file, line);
165 line_old = line;
166 if (line >= 2) {
167 line -= 2;
168 }
169 if (r_file_exists (file)) {
170 for (i = 0; i < 5; i++) {
171 char *row = r_file_slurp_line (file, line + i, 0);
172 if (row) {
173 r_cons_printf ("%c %.3x %s\n", line+i == line_old ? '>' : ' ', line+i, row);
174 free (row);
175 }
176 }
177 } else {
178 eprintf ("Cannot open '%s'\n", file);
179 }
180 }
181 return ret;
182 }
183
184 #if 0
185 static int remove_meta_fileline(RCore *core, const char *file_line) {
186 return sdb_unset (core->bin->cur->sdb_addrinfo, file_line, 0);
187 }
188
189 static int print_meta_fileline(RCore *core, const char *file_line) {
190 char *meta_info = sdb_get (core->bin->cur->sdb_addrinfo, file_line, 0);
191 if (meta_info) {
192 r_cons_printf ("Meta info %s\n", meta_info);
193 } else {
194 r_cons_printf ("No meta info for %s found\n", file_line);
195 }
196 return 0;
197 }
198 #endif
199
200 static ut64 filter_offset = UT64_MAX;
201 static int filter_format = 0;
202 static size_t filter_count = 0;
203
print_addrinfo(void * user,const char * k,const char * v)204 static bool print_addrinfo (void *user, const char *k, const char *v) {
205 ut64 offset = sdb_atoi (k);
206 if (!offset || offset == UT64_MAX) {
207 return true;
208 }
209 char *subst = strdup (v);
210 char *colonpos = strchr (subst, '|'); // XXX keep only : for simplicity?
211 if (!colonpos) {
212 colonpos = strchr (subst, ':');
213 }
214 if (!colonpos) {
215 r_cons_printf ("%s\n", subst);
216 }
217 if (colonpos && (filter_offset == UT64_MAX || filter_offset == offset)) {
218 if (filter_format) {
219 *colonpos = ':';
220 r_cons_printf ("CL %s %s\n", k, subst);
221 } else {
222 *colonpos = 0;
223 r_cons_printf ("file: %s\nline: %s\n", subst, colonpos + 1);
224 }
225 filter_count++;
226 }
227 free (subst);
228
229 return true;
230 }
231
cmd_meta_add_fileline(Sdb * s,char * fileline,ut64 offset)232 static int cmd_meta_add_fileline(Sdb *s, char *fileline, ut64 offset) {
233 char aoffset[64];
234 char *aoffsetptr = sdb_itoa (offset, aoffset, 16);
235
236 if (!aoffsetptr) {
237 return -1;
238 }
239 if (!sdb_add (s, aoffsetptr, fileline, 0)) {
240 sdb_set (s, aoffsetptr, fileline, 0);
241 }
242 if (!sdb_add (s, fileline, aoffsetptr, 0)) {
243 sdb_set (s, fileline, aoffsetptr, 0);
244 }
245 return 0;
246 }
247
cmd_meta_lineinfo(RCore * core,const char * input)248 static int cmd_meta_lineinfo(RCore *core, const char *input) {
249 int ret;
250 ut64 offset = UT64_MAX; // use this as error value
251 bool remove = false;
252 int all = false;
253 const char *p = input;
254 char *file_line = NULL;
255 char *pheap = NULL;
256
257 if (*p == '?') {
258 eprintf ("Usage: CL[.-*?] [addr] [file:line]\n");
259 eprintf ("or: CL [addr] base64:[string]\n");
260 free (pheap);
261 return 0;
262 }
263 if (*p == '-') {
264 p++;
265 remove = true;
266 }
267 if (*p == '.') {
268 p++;
269 offset = core->offset;
270 }
271 if (*p == ' ') {
272 p = r_str_trim_head_ro (p + 1);
273 char *arg = strchr (p, ' ');
274 if (!arg) {
275 offset = r_num_math (core->num, p);
276 p = "";
277 }
278 } else if (*p == '*') {
279 p++;
280 all = true;
281 filter_format = '*';
282 } else {
283 filter_format = 0;
284 }
285
286 if (all) {
287 if (remove) {
288 sdb_reset (core->bin->cur->sdb_addrinfo);
289 } else {
290 sdb_foreach (core->bin->cur->sdb_addrinfo, print_addrinfo, NULL);
291 }
292 free (pheap);
293 return 0;
294 }
295
296 p = r_str_trim_head_ro (p);
297 char *myp = strdup (p);
298 char *sp = strchr (myp, ' ');
299 if (sp) {
300 *sp = 0;
301 sp++;
302 if (offset == UT64_MAX) {
303 offset = r_num_math (core->num, myp);
304 }
305
306 if (!strncmp (sp, "base64:", 7)) {
307 int len = 0;
308 ut8 *o = sdb_decode (sp + 7, &len);
309 if (!o) {
310 eprintf ("Invalid base64\n");
311 return 0;
312 }
313 sp = pheap = (char *)o;
314 }
315 RBinFile *bf = r_bin_cur (core->bin);
316 ret = 0;
317 if (bf && bf->sdb_addrinfo) {
318 ret = cmd_meta_add_fileline (bf->sdb_addrinfo, sp, offset);
319 } else {
320 eprintf ("TODO: Support global SdbAddrinfo or dummy rbinfile to handlee this case\n");
321 }
322 free (file_line);
323 free (myp);
324 free (pheap);
325 return ret;
326 }
327 free (myp);
328 if (remove) {
329 remove_meta_offset (core, offset);
330 } else {
331 // taken from r2 // TODO: we should move this addrinfo sdb logic into RBin.. use HT
332 filter_offset = offset;
333 filter_count = 0;
334 sdb_foreach (core->bin->cur->sdb_addrinfo, print_addrinfo, NULL);
335 if (filter_count == 0) {
336 print_meta_offset (core, offset);
337 }
338 }
339 free (pheap);
340 return 0;
341 }
342
cmd_meta_comment(RCore * core,const char * input)343 static int cmd_meta_comment(RCore *core, const char *input) {
344 ut64 addr = core->offset;
345 switch (input[1]) {
346 case '?':
347 r_core_cmd_help (core, help_msg_CC);
348 break;
349 case ',': // "CC,"
350 r_meta_print_list_all (core->anal, R_META_TYPE_COMMENT, ',', input + 2);
351 break;
352 case 'F': // "CC,"
353 if (input[2]=='?') {
354 eprintf ("Usage: CCF [file]\n");
355 } else if (input[2] == ' ') {
356 const char *fn = input+2;
357 const char *comment = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
358 while (*fn== ' ')fn++;
359 if (comment && *comment) {
360 // append filename in current comment
361 char *nc = r_str_newf ("%s ,(%s)", comment, fn);
362 r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, nc);
363 free (nc);
364 } else {
365 char *newcomment = r_str_newf (",(%s)", fn);
366 r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, newcomment);
367 free (newcomment);
368 }
369 } else {
370 const char *comment = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
371 if (comment && *comment) {
372 char *cmtfile = r_str_between (comment, ",(", ")");
373 if (cmtfile && *cmtfile) {
374 char *cwd = getcommapath (core);
375 r_cons_printf ("%s"R_SYS_DIR"%s\n", cwd, cmtfile);
376 free (cwd);
377 }
378 free (cmtfile);
379 }
380 }
381 break;
382 case '.':
383 {
384 ut64 at = input[2]? r_num_math (core->num, input + 2): addr;
385 const char *comment = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, at);
386 if (comment) {
387 r_cons_println (comment);
388 }
389 }
390 break;
391 case 0: // "CC"
392 r_meta_print_list_all (core->anal, R_META_TYPE_COMMENT, 0, NULL);
393 break;
394 case 'f': // "CCf"
395 switch (input[2]) {
396 case '-': // "CCf-"
397 {
398 ut64 arg = r_num_math (core->num, input + 2);
399 if (!arg) {
400 arg = core->offset;
401 }
402 RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, arg, 0);
403 if (fcn) {
404 RAnalBlock *bb;
405 RListIter *iter;
406 r_list_foreach (fcn->bbs, iter, bb) {
407 int i;
408 for (i = 0; i < bb->size; i++) {
409 ut64 addr = bb->addr + i;
410 r_meta_del (core->anal, R_META_TYPE_COMMENT, addr, 1);
411 }
412 }
413 }
414 }
415 break;
416 case ',': // "CCf,"
417 r_meta_print_list_in_function (core->anal, R_META_TYPE_COMMENT, ',', core->offset, input + 3);
418 break;
419 case 'j': // "CCfj"
420 r_meta_print_list_in_function (core->anal, R_META_TYPE_COMMENT, 'j', core->offset, NULL);
421 break;
422 case '*': // "CCf*"
423 r_meta_print_list_in_function (core->anal, R_META_TYPE_COMMENT, 1, core->offset, NULL);
424 break;
425 default:
426 r_meta_print_list_in_function (core->anal, R_META_TYPE_COMMENT, 0, core->offset, NULL);
427 break;
428 }
429 break;
430 case 'j': // "CCj"
431 r_meta_print_list_all (core->anal, R_META_TYPE_COMMENT, 'j', input + 2);
432 break;
433 case '!':
434 {
435 char *out;
436 const char *comment = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
437 out = r_core_editor (core, NULL, comment);
438 if (out) {
439 //r_meta_set (core->anal->meta, R_META_TYPE_COMMENT, addr, 0, out);
440 r_core_cmdf (core, "CC-@0x%08"PFMT64x, addr);
441 //r_meta_del (core->anal->meta, input[0], addr, addr+1);
442 r_meta_set_string (core->anal,
443 R_META_TYPE_COMMENT, addr, out);
444 free (out);
445 }
446 }
447 break;
448 case '+':
449 case ' ':
450 {
451 const char *newcomment = r_str_trim_head_ro (input + 2);
452 const char *comment = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
453 char *text;
454 char *nc = strdup (newcomment);
455 r_str_unescape (nc);
456 if (comment) {
457 text = malloc (strlen (comment) + strlen (newcomment) + 2);
458 if (text) {
459 strcpy (text, comment);
460 strcat (text, " ");
461 strcat (text, nc);
462 r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, text);
463 free (text);
464 } else {
465 r_sys_perror ("malloc");
466 }
467 } else {
468 r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, nc);
469 }
470 free (nc);
471 }
472 break;
473 case '*': // "CC*"
474 r_meta_print_list_all (core->anal, R_META_TYPE_COMMENT, 1, NULL);
475 break;
476 case '-': // "CC-"
477 if (input[2] == '*') { // "CC-*"
478 r_meta_del (core->anal, R_META_TYPE_COMMENT, UT64_MAX, UT64_MAX);
479 } else if (input[2]) { // "CC-$$+32"
480 ut64 arg = r_num_math (core->num, input + 2);
481 r_meta_del (core->anal, R_META_TYPE_COMMENT, arg, 1);
482 } else { // "CC-"
483 r_meta_del (core->anal, R_META_TYPE_COMMENT, core->offset, 1);
484 }
485 break;
486 case 'u': // "CCu"
487 //
488 {
489 char *newcomment;
490 const char *arg = input + 2;
491 while (*arg && *arg == ' ') arg++;
492 if (!strncmp (arg, "base64:", 7)) {
493 char *s = (char *)sdb_decode (arg + 7, NULL);
494 if (s) {
495 newcomment = s;
496 } else {
497 newcomment = NULL;
498 }
499 } else {
500 newcomment = strdup (arg);
501 }
502 if (newcomment) {
503 const char *comment = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
504 if (!comment || (comment && !strstr (comment, newcomment))) {
505 r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, newcomment);
506 }
507 free (newcomment);
508 }
509 }
510 break;
511 case 'a': // "CCa"
512 {
513 char *s, *p;
514 s = strchr (input, ' ');
515 if (s) {
516 s = strdup (s + 1);
517 } else {
518 eprintf ("Usage\n");
519 return false;
520 }
521 p = strchr (s, ' ');
522 if (p) {
523 *p++ = 0;
524 }
525 ut64 addr;
526 if (input[2]=='-') {
527 if (input[3]) {
528 addr = r_num_math (core->num, input+3);
529 r_meta_del (core->anal,
530 R_META_TYPE_COMMENT,
531 addr, 1);
532 } else eprintf ("Usage: CCa-[address]\n");
533 free (s);
534 return true;
535 }
536 addr = r_num_math (core->num, s);
537 // Comment at
538 if (p) {
539 if (input[2]=='+') {
540 const char *comment = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
541 if (comment) {
542 char *text = r_str_newf ("%s\n%s", comment, p);
543 r_meta_set (core->anal, R_META_TYPE_COMMENT, addr, 1, text);
544 free (text);
545 } else {
546 r_meta_set (core->anal, R_META_TYPE_COMMENT, addr, 1, p);
547 }
548 } else {
549 r_meta_set (core->anal, R_META_TYPE_COMMENT, addr, 1, p);
550 }
551 } else {
552 eprintf ("Usage: CCa [address] [comment]\n");
553 }
554 free (s);
555 return true;
556 }
557 }
558 return true;
559 }
560
cmd_meta_vartype_comment(RCore * core,const char * input)561 static int cmd_meta_vartype_comment(RCore *core, const char *input) {
562 ut64 addr = core->offset;
563 switch (input[1]) {
564 case '?': // "Ct?"
565 r_core_cmd_help (core, help_msg_Ct);
566 break;
567 case 0: // "Ct"
568 r_meta_print_list_all (core->anal, R_META_TYPE_VARTYPE, 0, NULL);
569 break;
570 case ' ': // "Ct <vartype comment> @ addr"
571 {
572 const char* newcomment = r_str_trim_head_ro (input + 2);
573 const char *comment = r_meta_get_string (core->anal, R_META_TYPE_VARTYPE, addr);
574 char *nc = strdup (newcomment);
575 r_str_unescape (nc);
576 if (comment) {
577 char *text = r_str_newf ("%s %s", comment, nc);
578 if (text) {
579 r_meta_set_string (core->anal, R_META_TYPE_VARTYPE, addr, text);
580 free (text);
581 } else {
582 r_sys_perror ("malloc");
583 }
584 } else {
585 r_meta_set_string (core->anal, R_META_TYPE_VARTYPE, addr, nc);
586 }
587 free (nc);
588 }
589 break;
590 case '.': // "Ct. @ addr"
591 {
592 ut64 at = input[2]? r_num_math (core->num, input + 2): addr;
593 const char *comment = r_meta_get_string (core->anal, R_META_TYPE_VARTYPE, at);
594 if (comment) {
595 r_cons_println (comment);
596 }
597 }
598 break;
599 case '-': // "Ct-"
600 r_meta_del (core->anal, R_META_TYPE_VARTYPE, core->offset, 1);
601 break;
602 default:
603 r_core_cmd_help (core, help_msg_Ct);
604 break;
605 }
606
607 return true;
608 }
609
cmd_meta_others(RCore * core,const char * input)610 static int cmd_meta_others(RCore *core, const char *input) {
611 int n, type = input[0], subtype;
612 char *t = 0, *p, *p2, name[256];
613 int repeat = 1;
614 ut64 addr = core->offset;
615
616 if (!type) {
617 return 0;
618 }
619
620 switch (input[1]) {
621 case '?':
622 switch (input[0]) {
623 case 'f': // "Cf?"
624 r_cons_println(
625 "Usage: Cf[-] [sz] [fmt..] [@addr]\n\n"
626 "'sz' indicates the byte size taken up by struct.\n"
627 "'fmt' is a 'pf?' style format string. It controls only the display format.\n\n"
628 "You may wish to have 'sz' != sizeof(fmt) when you have a large struct\n"
629 "but have only identified specific fields in it. In that case, use 'fmt'\n"
630 "to show the fields you know about (perhaps using 'skip' fields), and 'sz'\n"
631 "to match the total struct size in mem.\n");
632 break;
633 case 's': // "Cs?"
634 r_core_cmd_help (core, help_msg_Cs);
635 break;
636 default:
637 r_cons_println ("See C?");
638 break;
639 }
640 break;
641 case '-': // "Cf-", "Cd-", ...
642 switch (input[2]) {
643 case '*': // "Cf-*", "Cd-*", ...
644 r_meta_del (core->anal, input[0], 0, UT64_MAX);
645 break;
646 case ' ':
647 p2 = strchr (input + 3, ' ');
648 if (p2) {
649 ut64 i;
650 ut64 size = r_num_math (core->num, input + 3);
651 ut64 rep = r_num_math (core->num, p2 + 1);
652 ut64 cur_addr = addr;
653 if (!size) {
654 break;
655 }
656 for (i = 0; i < rep && UT64_MAX - cur_addr > size; i++, cur_addr += size) {
657 r_meta_del (core->anal, input[0], cur_addr, size);
658 }
659 break;
660 } else {
661 addr = r_num_math (core->num, input + 3);
662 /* fallthrough */
663 }
664 default:
665 r_meta_del (core->anal, input[0], addr, 1);
666 break;
667 }
668 break;
669 case '*': // "Cf*", "Cd*", ...
670 r_meta_print_list_all (core->anal, input[0], 1, NULL);
671 break;
672 case 'j': // "Cfj", "Cdj", ...
673 r_meta_print_list_all (core->anal, input[0], 'j', NULL);
674 break;
675 case '!': // "Cf!", "Cd!", ...
676 {
677 char *out;
678 const char *comment = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
679 out = r_core_editor (core, NULL, comment);
680 if (out) {
681 //r_meta_set (core->anal->meta, R_META_TYPE_COMMENT, addr, 0, out);
682 r_core_cmdf (core, "CC-@0x%08"PFMT64x, addr);
683 //r_meta_del (core->anal->meta, input[0], addr, addr+1);
684 r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, out);
685 free (out);
686 }
687 }
688 break;
689 case '.': // "Cf.", "Cd.", ...
690 if (input[2] == '.') { // "Cs.."
691 ut64 size;
692 RAnalMetaItem *mi = r_meta_get_at (core->anal, addr, type, &size);
693 if (mi) {
694 r_meta_print (core->anal, mi, addr, size, input[3], NULL, false);
695 }
696 break;
697 } else if (input[2] == 'j') { // "Cs.j"
698 ut64 size;
699 RAnalMetaItem *mi = r_meta_get_at (core->anal, addr, type, &size);
700 if (mi) {
701 r_meta_print (core->anal, mi, addr, size, input[2], NULL, false);
702 r_cons_newline ();
703 }
704 break;
705 }
706 ut64 size;
707 RAnalMetaItem *mi = r_meta_get_at (core->anal, addr, type, &size);
708 if (!mi) {
709 break;
710 }
711 if (type == 's') {
712 char *esc_str;
713 bool esc_bslash = core->print->esc_bslash;
714 switch (mi->subtype) {
715 case R_STRING_ENC_UTF8:
716 esc_str = r_str_escape_utf8 (mi->str, false, esc_bslash);
717 break;
718 case 0: /* temporary legacy workaround */
719 esc_bslash = false;
720 default:
721 esc_str = r_str_escape_latin1 (mi->str, false, esc_bslash, false);
722 }
723 if (esc_str) {
724 r_cons_printf ("\"%s\"\n", esc_str);
725 free (esc_str);
726 } else {
727 r_cons_println ("<oom>");
728 }
729 } else if (type == 'd') {
730 r_cons_printf ("%"PFMT64u"\n", size);
731 } else {
732 r_cons_println (mi->str);
733 }
734 break;
735 case ' ': // "Cf", "Cd", ...
736 case '\0':
737 case 'g':
738 case 'a':
739 case '8':
740 if (type != 'z' && !input[1] && !core->tmpseek) {
741 r_meta_print_list_all (core->anal, type, 0, NULL);
742 break;
743 }
744 if (type == 'z') {
745 type = 's';
746 }
747 int len = (!input[1] || input[1] == ' ') ? 2 : 3;
748 if (strlen (input) > len) {
749 char *rep = strchr (input + len, '[');
750 if (!rep) {
751 rep = strchr (input + len, ' ');
752 }
753 if (*input == 'd') {
754 if (rep) {
755 repeat = r_num_math (core->num, rep + 1);
756 }
757 }
758 }
759 int repcnt = 0;
760 if (repeat < 1) {
761 repeat = 1;
762 }
763 while (repcnt < repeat) {
764 int off = (!input[1] || input[1] == ' ') ? 1 : 2;
765 t = strdup (r_str_trim_head_ro (input + off));
766 p = NULL;
767 n = 0;
768 strncpy (name, t, sizeof (name) - 1);
769 if (type != 'C') {
770 n = r_num_math (core->num, t);
771 if (type == 'f') { // "Cf"
772 p = strchr (t, ' ');
773 if (p) {
774 p = (char *)r_str_trim_head_ro (p);
775 if (*p == '.') {
776 const char *realformat = r_print_format_byname (core->print, p + 1);
777 if (realformat) {
778 p = (char *)realformat;
779 } else {
780 eprintf ("Cannot resolve format '%s'\n", p + 1);
781 break;
782 }
783 }
784 if (n < 1) {
785 n = r_print_format_struct_size (core->print, p, 0, 0);
786 if (n < 1) {
787 eprintf ("Warning: Cannot resolve struct size for '%s'\n", p);
788 n = 32; //
789 }
790 }
791 //make sure we do not overflow on r_print_format
792 if (n > core->blocksize) {
793 n = core->blocksize;
794 }
795 int r = r_print_format (core->print, addr, core->block,
796 n, p, 0, NULL, NULL);
797 if (r < 0) {
798 n = -1;
799 }
800 } else {
801 eprintf ("Usage: Cf [size] [pf-format-string]\n");
802 break;
803 }
804 } else if (type == 's') { // "Cs"
805 char tmp[256] = R_EMPTY;
806 int i, j, name_len = 0;
807 if (input[1] == 'a' || input[1] == '8') {
808 (void)r_io_read_at (core->io, addr, (ut8*)name, sizeof (name) - 1);
809 name[sizeof (name) - 1] = '\0';
810 name_len = strlen (name);
811 } else {
812 (void)r_io_read_at (core->io, addr, (ut8*)tmp, sizeof (tmp) - 3);
813 name_len = r_str_nlen_w (tmp, sizeof (tmp) - 3);
814 //handle wide strings
815 for (i = 0, j = 0; i < sizeof (name); i++, j++) {
816 name[i] = tmp[j];
817 if (!tmp[j]) {
818 break;
819 }
820 if (!tmp[j + 1]) {
821 if (j + 3 < sizeof (tmp)) {
822 if (tmp[j + 3]) {
823 break;
824 }
825 }
826 j++;
827 }
828 }
829 name[sizeof (name) - 1] = '\0';
830 }
831 if (n == 0) {
832 n = name_len + 1;
833 } else {
834 if (n > 0 && n < name_len) {
835 name[n] = 0;
836 }
837 }
838 }
839 if (n < 1) {
840 /* invalid length, do not insert into db */
841 return false;
842 }
843 if (!*t || n > 0) {
844 RFlagItem *fi;
845 p = strchr (t, ' ');
846 if (p) {
847 *p++ = '\0';
848 p = (char *)r_str_trim_head_ro (p);
849 strncpy (name, p, sizeof (name)-1);
850 } else {
851 if (type != 's') {
852 fi = r_flag_get_i (core->flags, addr);
853 if (fi) {
854 strncpy (name, fi->name, sizeof (name)-1);
855 }
856 }
857 }
858 }
859 }
860 if (!n) {
861 n++;
862 }
863 if (type == 's') {
864 switch (input[1]) {
865 case 'a':
866 case '8':
867 subtype = input[1];
868 break;
869 default:
870 subtype = R_STRING_ENC_GUESS;
871 }
872 r_meta_set_with_subtype (core->anal, type, subtype, addr, n, name);
873 } else {
874 r_meta_set (core->anal, type, addr, n, name);
875 }
876 free (t);
877 repcnt ++;
878 addr += n;
879 }
880 //r_meta_cleanup (core->anal->meta, 0LL, UT64_MAX);
881 break;
882 default:
883 eprintf ("Missing space after CC\n");
884 break;
885 }
886
887 return true;
888 }
889
r_comment_var_help(RCore * core,char type)890 void r_comment_var_help(RCore *core, char type) {
891 switch (type) {
892 case 'b':
893 r_core_cmd_help (core, help_msg_Cvb);
894 break;
895 case 's':
896 r_core_cmd_help (core, help_msg_Cvs);
897 break;
898 case 'r':
899 r_core_cmd_help (core, help_msg_Cvr);
900 break;
901 case '?':
902 r_cons_printf("See Cvb?, Cvs? and Cvr?\n");
903 }
904 }
905
r_comment_vars(RCore * core,const char * input)906 void r_comment_vars(RCore *core, const char *input) {
907 //TODO enable base64 and make it the default for C*
908 RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
909 char *oname = NULL, *name = NULL;
910
911 if (!input[0] || input[1] == '?' || (input[0] != 'b' && input[0] != 'r' && input[0] != 's')) {
912 r_comment_var_help (core, input[0]);
913 return;
914 }
915 if (!fcn) {
916 eprintf ("Can't find function here\n");
917 return;
918 }
919 oname = name = r_str_trim_dup (input + 1);
920 switch (input[1]) {
921 case '*': // "Cv*"
922 case '\0': { // "Cv"
923 void **it;
924 char kind = input[0];
925 r_pvector_foreach (&fcn->vars, it) {
926 RAnalVar *var = *it;
927 if (var->kind != kind || !var->comment) {
928 continue;
929 }
930 if (!input[1]) {
931 r_cons_printf ("%s : %s\n", var->name, var->comment);
932 } else {
933 char *b64 = sdb_encode ((const ut8 *)var->comment, strlen (var->comment));
934 if (!b64) {
935 continue;
936 }
937 r_cons_printf ("\"Cv%c %s base64:%s @ 0x%08"PFMT64x"\"\n", kind, var->name, b64, fcn->addr);
938 }
939 }
940 }
941 break;
942 case ' ': { // "Cv "
943 char *comment = strchr (name, ' ');
944 char *heap_comment = NULL;
945 if (comment) { // new comment given
946 if (*comment) {
947 *comment++ = 0;
948 }
949 if (!strncmp (comment, "base64:", 7)) {
950 heap_comment = (char *)sdb_decode (comment + 7, NULL);
951 comment = heap_comment;
952 }
953 }
954 RAnalVar *var = r_anal_function_get_var_byname (fcn, name);
955 if (!var) {
956 int idx = (int)strtol (name, NULL, 0);
957 var = r_anal_function_get_var (fcn, input[0], idx);
958 }
959 if (!var) {
960 eprintf ("can't find variable at given offset\n");
961 } else {
962 if (var->comment) {
963 if (comment && *comment) {
964 char *text = r_str_newf ("%s\n%s", var->comment, comment);
965 free (var->comment);
966 var->comment = text;
967 } else {
968 r_cons_println (var->comment);
969 }
970 } else {
971 var->comment = strdup (comment);
972 }
973 }
974 free (heap_comment);
975 }
976 break;
977 case '-': { // "Cv-"
978 name++;
979 r_str_trim (name);
980 RAnalVar *var = r_anal_function_get_var_byname (fcn, name);
981 if (!var) {
982 int idx = (int)strtol (name, NULL, 0);
983 var = r_anal_function_get_var (fcn, input[0], idx);
984 }
985 if (!var) {
986 eprintf ("can't find variable at given offset\n");
987 break;
988 }
989 free (var->comment);
990 var->comment = NULL;
991 break;
992 }
993 case '!': { // "Cv!"
994 char *comment;
995 name++;
996 r_str_trim (name);
997 RAnalVar *var = r_anal_function_get_var_byname (fcn, name);
998 if (!var) {
999 eprintf ("can't find variable named `%s`\n", name);
1000 break;
1001 }
1002 comment = r_core_editor (core, NULL, var->comment);
1003 if (comment) {
1004 free (var->comment);
1005 var->comment = comment;
1006 }
1007 }
1008 break;
1009 }
1010 free (oname);
1011 }
1012
cmd_meta(void * data,const char * input)1013 static int cmd_meta(void *data, const char *input) {
1014 RCore *core = (RCore*)data;
1015 RAnalFunction *f;
1016 RSpaces *ms;
1017 int i;
1018
1019 switch (*input) {
1020 case 'v': // "Cv"
1021 r_comment_vars (core, input + 1);
1022 break;
1023 case '\0': // "C"
1024 r_meta_print_list_all (core->anal, R_META_TYPE_ANY, 0, NULL);
1025 break;
1026 case ',': // "C,"
1027 case 'j': // "Cj"
1028 case '*': { // "C*"
1029 if (!input[0] || input[1] == '.') {
1030 r_meta_print_list_at (core->anal, core->offset, *input, input + 2);
1031 } else {
1032 r_meta_print_list_all (core->anal, R_META_TYPE_ANY, *input, input + 2);
1033 }
1034 break;
1035 }
1036 case '.': { // "C."
1037 r_meta_print_list_at (core->anal, core->offset, 0, NULL);
1038 break;
1039 }
1040 case 'L': // "CL"
1041 cmd_meta_lineinfo (core, input + 1);
1042 break;
1043 case 'C': // "CC"
1044 cmd_meta_comment (core, input);
1045 break;
1046 case 't': // "Ct" type analysis commnets
1047 cmd_meta_vartype_comment (core, input);
1048 break;
1049 case 'r': // "Cr" run command
1050 case 'h': // "Ch" comment
1051 case 's': // "Cs" string
1052 case 'z': // "Cz" zero-terminated string
1053 case 'd': // "Cd" data
1054 case 'm': // "Cm" magic
1055 case 'f': // "Cf" formatted
1056 cmd_meta_others (core, input);
1057 break;
1058 case '-': // "C-"
1059 if (input[1] != '*') {
1060 i = input[1] ? r_num_math (core->num, input + (input[1] == ' ' ? 2 : 1)) : 1;
1061 r_meta_del (core->anal, R_META_TYPE_ANY, core->offset, i);
1062 } else {
1063 r_meta_del (core->anal, R_META_TYPE_ANY, 0, UT64_MAX);
1064 }
1065 break;
1066 case '?': // "C?"
1067 r_core_cmd_help (core, help_msg_C);
1068 break;
1069 case 'F': // "CF"
1070 f = r_anal_get_fcn_in (core->anal, core->offset,
1071 R_ANAL_FCN_TYPE_FCN|R_ANAL_FCN_TYPE_SYM);
1072 if (f) {
1073 r_anal_str_to_fcn (core->anal, f, input + 2);
1074 } else {
1075 eprintf ("Cannot find function here\n");
1076 }
1077 break;
1078 case 'S': // "CS"
1079 ms = &core->anal->meta_spaces;
1080 /** copypasta from `fs`.. this must be refactorized to be shared */
1081 switch (input[1]) {
1082 case '?': // "CS?"
1083 r_core_cmd_help (core, help_msg_CS);
1084 break;
1085 case '+': // "CS+"
1086 r_spaces_push (ms, input + 2);
1087 break;
1088 case 'r': // "CSr"
1089 if (input[2] == ' ') {
1090 r_spaces_rename (ms, NULL, input+2);
1091 } else {
1092 eprintf ("Usage: CSr [newname]\n");
1093 }
1094 break;
1095 case '-': // "CS-"
1096 if (input[2]) {
1097 if (input[2]=='*') {
1098 r_spaces_unset (ms, NULL);
1099 } else {
1100 r_spaces_unset (ms, input+2);
1101 }
1102 } else {
1103 r_spaces_pop (ms);
1104 }
1105 break;
1106 case 'j': // "CSj"
1107 case '\0': // "CS"
1108 case '*': // "CS*"
1109 spaces_list (ms, input[1]);
1110 break;
1111 case ' ': // "CS "
1112 r_spaces_set (ms, input + 2);
1113 break;
1114 default:
1115 spaces_list (ms, 0);
1116 break;
1117 }
1118 break;
1119 }
1120 return true;
1121 }
1122