radare2/libr/main/rasign2.c

340 lines
7.7 KiB
C

/* radare - LGPL - Copyright 2009-2022 - pancake, DennisGoodlett */
#define R_LOG_ORIGIN "rasign2"
#include <r_main.h>
#include <r_core.h>
struct rasignconf {
const char *ofile, *space;
size_t a_cnt;
bool merge, sdb, ar, rad, quiet, json, flirt, collision;
};
static void rasign_show_help(void) {
printf ("Usage: rasign2 [options] [file]\n"
" -a [-a] add extra 'a' to analysis command\n"
" -A make signatures from all .o files in the provided .a file\n"
" -f interpret the file as a FLIRT .sig file and dump signatures\n"
" -h help menu\n"
" -j show signatures in json\n"
" -o sigs.sdb add signatures to file, create if it does not exist\n"
" -q quiet mode\n"
" -r show output in radare commands\n"
" -S perform operation on sdb signature file ('-o -' to save to same file)\n"
" -s signspace save all signatures under this signspace\n"
" -c add collision signatures before writing file\n"
" -v show version information\n"
" -m merge/overwrite signatures with same name\n"
"Examples:\n"
" rasign2 -o libc.sdb libc.so.6\n");
}
static RCore *opencore(const char *fname) {
RIODesc * rfile = NULL;
RCore *c = r_core_new ();
if (!c) {
R_LOG_ERROR ("Count not get core");
return NULL;
}
r_core_loadlibs (c, R_CORE_LOADLIBS_ALL, NULL);
r_config_set_b (c->config, "scr.interactive", false);
if (fname) {
#if __WINDOWS__
char *winf = r_acp_to_utf8 (fname);
rfile = r_core_file_open (c, winf, 0, 0);
free (winf);
#else
rfile = r_core_file_open (c, fname, 0, 0);
#endif
if (!rfile) {
R_LOG_ERROR ("Could not open file %s", fname);
r_core_free (c);
return NULL;
}
(void)r_core_bin_load (c, NULL, UT64_MAX);
(void)r_core_bin_update_arch_bits (c);
r_cons_flush ();
}
return c;
}
static void find_functions(RCore *core, size_t count) {
const char *cmd = NULL;
switch (count) {
case 0: cmd = "aa"; break;
case 1: cmd = "aaa"; break;
case 2: cmd = "aaaa"; break;
}
r_core_cmd0 (core, cmd);
}
static int inline output(RAnal *anal, struct rasignconf *conf) {
if (conf->collision) {
r_sign_resolve_collisions (anal);
}
if (conf->rad) {
r_sign_list (anal, '*');
}
if (conf->json) {
r_sign_list (anal, 'j');
}
// write sigs to file
if (conf->ofile && !r_sign_save (anal, conf->ofile)) {
R_LOG_ERROR ("Failed to write file");
return -1;
}
r_cons_flush ();
return 0;
}
static int handle_sdb(const char *fname, struct rasignconf *conf) {
int ret = -1;
// can't use RAnal here because JSON output requires core, in a sneaky way
RCore *core = r_core_new ();
if (!core) {
return -1;
}
r_config_set_b (core->config, "scr.interactive", false);
if (conf->ofile && r_file_exists (conf->ofile)) {
r_sign_load (core->anal, conf->ofile, true);
}
if (r_sign_load (core->anal, fname, conf->merge)) {
if (conf->collision) {
r_sign_resolve_collisions (core->anal);
}
ret = output (core->anal, conf);
}
r_core_free (core);
return ret;
}
static int signs_from_file(const char *fname, struct rasignconf *conf) {
RCore *core = opencore (fname);
if (!core) {
R_LOG_ERROR ("Could not get core");
return -1;
}
if (conf->quiet) {
r_config_set_b (core->config, "scr.prompt", false);
r_config_set_i (core->config, "scr.color", COLOR_MODE_DISABLED);
}
if (conf->space) {
r_spaces_set (&core->anal->zign_spaces, conf->space);
}
if (conf->ofile && r_file_exists (conf->ofile)) {
r_sign_load (core->anal, conf->ofile, true);
}
// run analysis to find functions
find_functions (core, conf->a_cnt);
// create zignatures
r_sign_all_functions (core->anal, conf->merge);
int ret = output (core->anal, conf);
r_core_free (core);
return ret;
}
static RList *get_ar_file_uris(const char *fname) {
r_return_val_if_fail (fname, NULL);
RIO *io = r_io_new ();
// core is only used to to list uri's in archive, then it's free'd
if (!io) {
R_LOG_ERROR ("Failed to alloc io");
return NULL;
}
char *allfiles = r_str_newf ("arall://%s", fname);
if (!allfiles) {
R_LOG_ERROR ("Failed to alloc");
r_io_free (io);
return NULL;
}
RList *uris = r_list_newf (free);
RList *list_fds = r_io_open_many (io, allfiles, 0, 0);
free (allfiles);
bool fail = false;
if (list_fds && uris) {
RIODesc *fd;
RListIter *iter;
r_list_foreach (list_fds, iter, fd) {
char *u = strdup (fd->uri);
if (!u) {
fail = true;
break;
}
r_list_append (uris, u);
}
}
r_list_free (list_fds);
r_io_free (io);
if (fail) {
r_list_free (uris);
uris = NULL;
}
return uris;
}
static int dump_flirt(const char *ifile) {
RCore *core = opencore (NULL);
r_sign_flirt_dump (core->anal, ifile);
r_cons_flush ();
r_core_free (core);
return 0;
}
static int handle_archive_files(const char *fname, struct rasignconf *conf) {
RList *uris = get_ar_file_uris (fname);
if (!uris) {
return -1;
}
bool collision = false;
if (conf->collision && conf->ofile) {
collision = true;
}
conf->collision = false;
RListIter *iter;
char *u;
int ret = 0;
r_list_foreach (uris, iter, u) {
if (r_str_endswith (u, ".o")) {
eprintf ("\nProcessing %s...\n", u);
int err = signs_from_file (u, conf);
if (err) {
ret = err;
}
} else {
R_LOG_WARN ("[!!] skipping %s because it is not a .o file", u);
}
}
r_list_free (uris);
if (collision) {
R_LOG_INFO ("Computing collisions on sdb file");
RAnal *anal = r_anal_new ();
if (anal) {
r_sign_load (anal, conf->ofile, true);
r_sign_resolve_collisions (anal);
int tmpret = r_sign_save (anal, conf->ofile);
r_anal_free (anal);
if (!ret && tmpret) {
ret = tmpret;
}
}
}
return ret;
}
R_API int r_main_rasign2(int argc, const char **argv) {
int c;
RGetopt opt;
struct rasignconf conf = {0};
r_getopt_init (&opt, argc, argv, "Aafhjmo:qrSs:cv");
while ((c = r_getopt_next (&opt)) != -1) {
switch (c) {
case 'A':
conf.ar = true;
break;
case 'a':
conf.a_cnt++;
break;
case 'o':
conf.ofile = opt.arg;
break;
case 'c':
conf.collision = true;
break;
case 'S':
conf.sdb = true;
break;
case 's':
conf.space = opt.arg;
break;
case 'r':
conf.rad = true;
break;
case 'j':
conf.json = true;
break;
case 'm':
conf.merge = true;
break;
case 'q':
conf.quiet = true;
break;
case 'f':
conf.flirt = true;
break;
case 'v':
return r_main_version_print ("rasign2");
case 'h':
rasign_show_help ();
return 0;
default:
rasign_show_help ();
return -1;
}
}
if (conf.a_cnt > 2) {
R_LOG_ERROR ("Invalid analysis (too many -a's?)");
rasign_show_help ();
return -1;
}
if (opt.ind >= argc) {
R_LOG_ERROR ("You must provide a file");
rasign_show_help ();
return -1;
}
const char *ifile = argv[opt.ind];
if (conf.flirt) {
if (conf.rad || conf.ofile || conf.json) {
R_LOG_ERROR ("Only FLIRT output is supported for FLIRT files");
return -1;
}
if (conf.sdb) {
R_LOG_ERROR ("Can't use -S with -f");
return -1;
}
return dump_flirt (ifile);
} else if (conf.ar) {
if (conf.json) {
R_LOG_ERROR ("JSON does not work with .a files currently");
return -1;
}
if (conf.collision && conf.rad) {
R_LOG_ERROR ("Rasign2 can not currently handle .a files with -c and -r");
return -1;
}
if (conf.sdb) {
R_LOG_ERROR ("Can't use -S with -A");
return -1;
}
return handle_archive_files (ifile, &conf);
} else if (conf.sdb) {
if (conf.a_cnt > 0) {
R_LOG_ERROR ("Option -a invalid with -S");
return -1;
}
if (conf.ofile && !strcmp (conf.ofile, "-")) {
if (!conf.collision) {
R_LOG_ERROR ("Option '-So -' is only useful with '-c'");
return -1;
}
conf.ofile = ifile;
}
return handle_sdb (ifile, &conf);
} else {
return signs_from_file (ifile, &conf);
}
}