radare2/libr/core/cmd_zign.c
2017-03-17 00:43:02 +01:00

591 lines
14 KiB
C

/* radare - LGPL - Copyright 2009-2017 - pancake, nibble */
#include <r_core.h>
#include <r_anal.h>
#include <r_sign.h>
#include <r_list.h>
#include <r_cons.h>
static bool zignAddFcn(RCore *core, RAnalFunction *fcn, int type, int minzlen, int maxzlen) {
int fcnlen = 0, len = 0;
ut8 *buf = NULL, *mask = NULL;
bool retval = true;
fcnlen = r_anal_fcn_realsize (fcn);
if (fcnlen < minzlen) {
eprintf ("warn: omitting %s zignature is too small. Length is %d. Check zign.min.\n",
fcn->name, fcnlen);
retval = false;
goto exit_function;
}
len = R_MIN (fcnlen, maxzlen);
buf = malloc (len);
if (r_io_read_at (core->io, fcn->addr, buf, len) != len) {
eprintf ("error: cannot read at 0x%08"PFMT64x"\n", fcn->addr);
retval = false;
goto exit_function;
}
switch (type) {
case R_SIGN_EXACT:
mask = malloc (len);
memset (mask, 0xff, len);
retval = r_sign_add (core->anal, R_SIGN_EXACT, fcn->name, len, buf, mask);
break;
case R_SIGN_ANAL:
retval = r_sign_add_anal (core->anal, fcn->name, len, buf);
break;
}
exit_function:
free (buf);
free (mask);
return retval;
}
static bool zignAddHex(RCore *core, const char *name, int type, const char *hexbytes) {
ut8 *mask = NULL, *bytes = NULL;
int size = 0, blen = 0;
bool retval = true;
blen = strlen (hexbytes) + 4;
bytes = malloc (blen);
mask = malloc (blen);
size = r_hex_str2binmask (hexbytes, bytes, mask);
if (size <= 0) {
retval = false;
goto exit_function;
}
switch (type) {
case R_SIGN_EXACT:
retval = r_sign_add (core->anal, type, name, size, bytes, mask);
break;
case R_SIGN_ANAL:
retval = r_sign_add_anal (core->anal, name, size, bytes);
break;
}
exit_function:
free (bytes);
free (mask);
return retval;
}
static int zignAddBytes(void *data, const char *input, int type) {
RCore *core = (RCore *) data;
switch (*input) {
case ' ':
{
const char *name = NULL, *hexbytes = NULL;
char *args = NULL;
int n = 0;
bool retval = true;
args = r_str_new (input + 1);
n = r_str_word_set0(args);
if (n != 2) {
eprintf ("usage: za%s name bytes\n", type == R_SIGN_ANAL? "a": "e");
retval = false;
goto exit_case;
}
name = r_str_word_get0(args, 0);
hexbytes = r_str_word_get0(args, 1);
if (!zignAddHex (core, name, type, hexbytes)) {
eprintf ("error: cannot add zignature\n");
retval = false;
goto exit_case;
}
exit_case:
free (args);
return retval;
}
break;
case 'f':
{
RAnalFunction *fcni = NULL;
RListIter *iter = NULL;
const char *name = NULL;
int minzlen = r_config_get_i (core->config, "zign.min");
int maxzlen = r_config_get_i (core->config, "zign.max");
if (input[1] != ' ') {
eprintf ("usage: za%sf name\n", type == R_SIGN_ANAL? "a": "e");
return false;
}
name = input + 2;
r_cons_break_push (NULL, NULL);
r_list_foreach (core->anal->fcns, iter, fcni) {
if (r_cons_is_breaked ()) {
break;
}
if (r_str_cmp (name, fcni->name, strlen (name))) {
if (!zignAddFcn (core, fcni, type, minzlen, maxzlen)) {
eprintf ("error: could not add zignature for fcn %s\n", fcni->name);
}
break;
}
}
r_cons_break_pop ();
}
break;
case 'F':
{
RAnalFunction *fcni = NULL;
RListIter *iter = NULL;
int minzlen = r_config_get_i (core->config, "zign.min");
int maxzlen = r_config_get_i (core->config, "zign.max");
r_cons_break_push (NULL, NULL);
r_list_foreach (core->anal->fcns, iter, fcni) {
if (r_cons_is_breaked ()) {
break;
}
if (!zignAddFcn (core, fcni, type, minzlen, maxzlen)) {
eprintf ("error: could not add zignature for fcn %s\n", fcni->name);
}
}
r_cons_break_pop ();
}
break;
case '?':
{
if (type == R_SIGN_ANAL) {
const char *help_msg[] = {
"Usage:", "zaa[fF] [args] ", "# Create anal zignature",
"zaa ", "name bytes", "create anal zignature",
"zaaf ", "[name]", "create anal zignature for function (use function name if name is not given)",
"zaaF ", "", "generate anal zignatures for all functions",
NULL};
r_core_cmd_help (core, help_msg);
} else {
const char *help_msg[] = {
"Usage:", "zae[fF] [args] ", "# Create anal zignature",
"zae ", "name bytes", "create anal zignature",
"zaef ", "[name]", "create anal zignature for function (use function name if name is not given)",
"zaeF ", "", "generate anal zignatures for all functions",
NULL};
r_core_cmd_help (core, help_msg);
}
}
break;
default:
eprintf ("usage: za%s[f] [args]\n", type == R_SIGN_ANAL? "a": "e");
return false;
}
return true;
}
static int zignAdd(void *data, const char *input) {
RCore *core = (RCore *) data;
switch (*input) {
case 'a':
return zignAddBytes (data, input + 1, R_SIGN_ANAL);
case 'e':
return zignAddBytes (data, input + 1, R_SIGN_EXACT);
case '?':
{
const char *help_msg[] = {
"Usage:", "za[aemg] [args] ", "# Add zignature",
"zaa", "[?]", "add anal zignature",
"zae", "[?]", "add exact-match zignature",
"zam ", "name params", "add metric zignature (e.g. zm foo bbs=10 calls=printf,exit)",
NULL};
r_core_cmd_help (core, help_msg);
}
break;
default:
eprintf ("usage: za[aemg] [args]\n");
return false;
}
return true;
}
static int zignFile(void *data, const char *input) {
RCore *core = (RCore *) data;
switch (*input) {
case ' ':
{
const char *filename;
if (input[1] != '\x00') {
filename = input + 1;
return r_sign_load (core->anal, filename);
} else {
eprintf ("Usage: zo filename\n");
return false;
}
}
break;
case 's':
{
const char *filename;
if (input[1] == ' ' && input[2] != '\x00') {
filename = input + 2;
return r_sign_save (core->anal, filename);
} else {
eprintf ("Usage: zos filename\n");
return false;
}
}
break;
case '?':
{
const char *help_msg[] = {
"Usage:", "zo[s] filename ", "# Manage zignature files",
"zo ", "filename", "load zinatures from sdb file",
"zos ", "filename", "save zignatures to sdb file",
NULL};
r_core_cmd_help (core, help_msg);
}
break;
default:
eprintf ("usage: zo[s] filename\n");
return false;
}
return true;
}
static int zignSpace(void *data, const char *input) {
RCore *core = (RCore *) data;
RSpaces *zs = &core->anal->zign_spaces;
switch (*input) {
case '+':
if (input[1] != '\x00') {
r_space_push (zs, input + 1);
} else {
eprintf ("Usage: zs+zignspace\n");
return false;
}
break;
case 'r':
if (input[1] == ' ' && input[2] != '\x00') {
r_space_rename (zs, NULL, input + 2);
} else {
eprintf ("Usage: zsr newname\n");
return false;
}
break;
case '-':
if (input[1] == '\x00') {
r_space_pop (zs);
} else if (input[1] == '*') {
r_space_unset (zs, NULL);
} else {
r_space_unset (zs, input+1);
}
break;
case 'j':
case '*':
case '\0':
r_space_list (zs, input[0]);
break;
case ' ':
if (input[1] != '\x00') {
r_space_set (zs, input + 1);
} else {
eprintf ("Usage: zs zignspace\n");
return false;
}
break;
case '?':
{
const char *help_msg[] = {
"Usage:", "zs[+-*] [namespace] ", "# Manage zignspaces",
"zs", "", "display zignspaces",
"zs ", "zignspace", "select zignspace",
"zs ", "*", "select all zignspaces",
"zs-", "zignspace", "delete zignspace",
"zs-", "*", "delete all zignspaces",
"zs+", "zignspace", "push previous zignspace and set",
"zs-", "", "pop to the previous zignspace",
"zsr ", "newname", "rename selected zignspace",
NULL};
r_core_cmd_help (core, help_msg);
}
break;
default:
{
int i, count = 0;
for (i = 0; i < R_FLAG_SPACES_MAX; i++) {
if (!zs->spaces[i]) {
continue;
}
r_cons_printf ("%02d %c %s\n", count,
(i == zs->space_idx)? '*': ' ',
zs->spaces[i]);
count++;
}
}
}
return true;
}
static int zignFlirt(void *data, const char *input) {
RCore *core = (RCore *) data;
switch (*input) {
case 'd':
if (input[1] != ' ') {
eprintf ("usage: zfd filename\n");
return false;
}
r_sign_flirt_dump (core->anal, input + 2);
break;
case 's':
if(input[1] != ' ') {
eprintf ("usage: zfs filename\n");
return false;
}
r_sign_flirt_scan (core->anal, input + 2);
break;
case 'z':
eprintf ("TODO\n");
break;
case '?':
{
const char *help_msg[] = {
"Usage:", "zf[dsz] filename ", "# Manage FLIRT signatures",
"zfd ", "filename", "open FLIRT file and dump",
"zfs ", "filename", "open FLIRT file and scan",
"zfz ", "filename", "open FLIRT file and get sig commands (zfz flirt_file > zignatures.sig)",
NULL};
r_core_cmd_help (core, help_msg);
}
break;
default:
eprintf ("usage: zf[dsz] filename\n");
return false;
}
return true;
}
struct ctxSearchCB {
RCore *core;
bool rad;
};
static int zignSearchHitCB(RSearchKeyword *kw, RSignItem *it, ut64 addr, void *user) {
struct ctxSearchCB *ctx = (struct ctxSearchCB *) user;
RConfig *cfg = ctx->core->config;
RAnal *a = ctx->core->anal;
const char *zign_prefix = r_config_get (cfg, "zign.prefix");
char *name;
if (it->space == -1) {
name = r_str_newf ("%s.%s_%d", zign_prefix, it->name, kw->count);
} else {
name = r_str_newf ("%s.%s.%s_%d", zign_prefix,
a->zign_spaces.spaces[it->space], it->name, kw->count);
}
if (ctx->rad) {
r_cons_printf ("f %s %d @ 0x%08"PFMT64x"\n", name, kw->keyword_length, addr);
} else {
r_flag_set(ctx->core->flags, name, addr, kw->keyword_length);
}
free(name);
return 1;
}
static bool zignSearchRange(RCore *core, ut64 from, ut64 to, bool rad) {
RSignSearch *ss;
ut8 *buf = malloc (core->blocksize);
ut64 at;
int rlen;
bool retval = true;
struct ctxSearchCB ctx = { core, rad };
ss = r_sign_search_new ();
ss->search->align = r_config_get_i (core->config, "search.align");
r_sign_search_init (core->anal, ss, zignSearchHitCB, &ctx);
r_cons_break_push (NULL, NULL);
for (at = from; at < to; at += core->blocksize) {
if (r_cons_is_breaked ()) {
retval = false;
break;
}
rlen = R_MIN (core->blocksize, to - at);
if (!r_io_read_at (core->io, at, buf, rlen)) {
retval = false;
break;
}
if (r_sign_search_update (core->anal, ss, &at, buf, rlen) == -1) {
eprintf ("search: update read error at 0x%08"PFMT64x"\n", at);
retval = false;
break;
}
}
r_cons_break_pop ();
free (buf);
r_sign_search_free (ss);
return retval;
}
static bool zignDoSearch(RCore *core, bool rad) {
RList *list;
RListIter *iter;
RIOMap *map;
bool retval = true;
const char *zign_prefix = r_config_get (core->config, "zign.prefix");
const char *mode = r_config_get (core->config, "search.in");
ut64 sin_from = UT64_MAX, sin_to = UT64_MAX;
if (rad) {
r_cons_printf ("fs+%s\n", zign_prefix);
} else {
if (!r_flag_space_push (core->flags, zign_prefix)) {
eprintf ("error: cannot create flagspace\n");
return false;
}
}
list = r_core_get_boundaries_prot (core, R_IO_EXEC | R_IO_WRITE | R_IO_READ, mode, &sin_from, &sin_to);
if (list) {
r_list_foreach (list, iter, map) {
eprintf ("[+] searching 0x%08"PFMT64x" - 0x%08"PFMT64x"\n", map->from, map->to);
retval &= zignSearchRange (core, map->from, map->to, rad);
}
r_list_free (list);
} else {
eprintf ("[+] searching 0x%08"PFMT64x" - 0x%08"PFMT64x"\n", sin_from, sin_to);
retval = zignSearchRange (core, sin_from, sin_to, rad);
}
if (rad) {
r_cons_printf ("fs-\n");
} else {
if (!r_flag_space_pop (core->flags)) {
eprintf ("error: cannot restore flagspace\n");
return false;
}
}
return retval;
}
static int zignSearch(void *data, const char *input) {
RCore *core = (RCore *) data;
switch (*input) {
case '\x00':
case '*':
return zignDoSearch (core, input[0] == '*');
case '?':
{
const char *help_msg[] = {
"Usage:", "z/[*] ", "# Search signatures (see 'e?search' for options)",
"z/ ", "", "search zignatures on range and flag matches",
"z/* ", "", "search zignatures on range and output radare commands",
NULL};
r_core_cmd_help (core, help_msg);
}
break;
default:
eprintf ("usage: z/[*]\n");
return false;
}
return true;
}
static int zignCheck(void *data, const char *input) {
RCore *core = (RCore *) data;
RSignSearch *ss;
ut64 at = core->offset;
bool retval = true;
struct ctxSearchCB ctx = { core, input[0] == '*' };
ss = r_sign_search_new ();
r_sign_search_init (core->anal, ss, zignSearchHitCB, &ctx);
if (r_sign_search_update (core->anal, ss, &at, core->block, core->blocksize) == -1) {
eprintf ("search: update read error at 0x%08"PFMT64x"\n", at);
retval = false;
}
r_sign_search_free (ss);
return retval;
}
static int cmd_zign(void *data, const char *input) {
RCore *core = (RCore *) data;
switch (*input) {
case '\0':
case '*':
case 'j':
r_sign_list (core->anal, input[0]);
break;
case '-':
r_sign_delete (core->anal, input + 1);
break;
case 'o':
return zignFile (data, input + 1);
case 'a':
return zignAdd (data, input + 1);
case 'f':
return zignFlirt (data, input + 1);
case '/':
return zignSearch (data, input + 1);
case 'c':
return zignCheck (data, input + 1);
case 's':
return zignSpace (data, input + 1);
case '?':
{
const char* help_msg[] = {
"Usage:", "z[*j-aof/cs] [args] ", "# Manage zignatures",
"z", "", "show zignagures",
"z*", "", "show zignatures in radare format",
"zj", "", "show zignatures in json format",
"z-", "zignature", "delete zignature",
"z-", "*", "delete all zignatures",
"za", "[?]", "add zignature",
"zo", "[?]", "Manage zignature files",
"zf", "[?]", "manage FLIRT signatures",
"z/", "[?]", "search zignatures",
"zc", "", "check zignatures at address",
"zs", "[?]", "manage zignspaces",
"NOTE:", "", "bytes can contain '..' (dots) to specify a binary mask",
NULL
};
r_core_cmd_help (core, help_msg);
}
break;
default:
eprintf ("usage: z[*j-aof/cs] [args]\n");
return false;
}
return true;
}