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