mirror of
https://github.com/radareorg/radare2.git
synced 2025-02-03 20:22:38 +00:00
Update crypto key search to find keys between blocks ##search (#17323)
This commit is contained in:
parent
a023fe210c
commit
2d53f7083b
@ -11,6 +11,9 @@ static int cmd_search(void *data, const char *input);
|
||||
|
||||
#define USE_EMULATION 0
|
||||
|
||||
#define AES_SEARCH_LENGTH 40
|
||||
#define PRIVATE_KEY_SEARCH_LENGTH 11
|
||||
|
||||
static const char *help_msg_search_esil[] = {
|
||||
"/E", " [esil-expr]", "search offsets matching a specific esil expression",
|
||||
"/Ej", " [esil-expr]", "same as above but using the given magic file",
|
||||
@ -40,7 +43,7 @@ static const char *help_msg_slash[] = {
|
||||
"//", "", "repeat last search",
|
||||
"/a", "[?][1aoditfmsltf] jmp eax", "assemble opcode and search its bytes",
|
||||
"/b", "", "search backwards, command modifier, followed by other command",
|
||||
"/c", "[ar]", "search for crypto materials",
|
||||
"/c", "[?][adr]", "search for crypto materials",
|
||||
"/d", " 101112", "search for a deltified sequence of bytes",
|
||||
"/e", " /E.F/i", "match regular expression",
|
||||
"/E", " esil-expr", "offset matching given esil expressions $$ = here",
|
||||
@ -160,7 +163,6 @@ struct search_parameters {
|
||||
const char *cmd_hit;
|
||||
int outmode; // 0 or R_MODE_RADARE or R_MODE_JSON
|
||||
bool inverse;
|
||||
bool crypto_search;
|
||||
bool aes_search;
|
||||
bool privkey_search;
|
||||
};
|
||||
@ -2355,7 +2357,7 @@ static void do_string_search(RCore *core, RInterval search_itv, struct search_pa
|
||||
if (param->inverse) {
|
||||
core->search->maxhits = 1;
|
||||
}
|
||||
if (core->search->n_kws > 0 || param->crypto_search) {
|
||||
if (core->search->n_kws > 0) {
|
||||
/* set callback */
|
||||
/* TODO: handle last block of data */
|
||||
/* TODO: handle ^C */
|
||||
@ -2399,10 +2401,10 @@ static void do_string_search(RCore *core, RInterval search_itv, struct search_pa
|
||||
}
|
||||
|
||||
const ut64 from = itv.addr, to = r_itv_end (itv),
|
||||
from1 = search->bckwrds ? to : from,
|
||||
to1 = search->bckwrds ? from : to;
|
||||
from1 = search->bckwrds? to: from,
|
||||
to1 = search->bckwrds? from: to;
|
||||
ut64 len;
|
||||
for (at = from1; at != to1; at = search->bckwrds ? at - len : at + len) {
|
||||
for (at = from1; at != to1; at = search->bckwrds? at - len: at + len) {
|
||||
print_search_progress (at, to1, search->nhits, param);
|
||||
if (r_cons_is_breaked ()) {
|
||||
eprintf ("\n\n");
|
||||
@ -2422,23 +2424,21 @@ static void do_string_search(RCore *core, RInterval search_itv, struct search_pa
|
||||
}
|
||||
(void)r_io_read_at (core->io, at, buf, len);
|
||||
}
|
||||
if (param->crypto_search) {
|
||||
// TODO support backward search
|
||||
int t = 0;
|
||||
if (param->aes_search) {
|
||||
t = r_search_aes_update (core->search, at, buf, len);
|
||||
} else if (param->privkey_search) {
|
||||
t = r_search_privkey_update (core->search, at, buf, len);
|
||||
r_search_update (core->search, at, buf, len);
|
||||
if (param->aes_search) {
|
||||
// Adjust length to search between blocks.
|
||||
if (len == core->blocksize) {
|
||||
len -= AES_SEARCH_LENGTH - 1;
|
||||
}
|
||||
if (!t || t > 1) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
(void)r_search_update (core->search, at, buf, len);
|
||||
if (core->search->maxhits > 0 && core->search->nhits >= core->search->maxhits) {
|
||||
goto done;
|
||||
} else if (param->privkey_search) {
|
||||
// Adjust length to search between blocks.
|
||||
if (len == core->blocksize) {
|
||||
len -= PRIVATE_KEY_SEARCH_LENGTH - 1;
|
||||
}
|
||||
}
|
||||
if (core->search->maxhits > 0 && core->search->nhits >= core->search->maxhits) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
print_search_progress (at, to1, search->nhits, param);
|
||||
r_cons_clear_line (1);
|
||||
@ -2447,7 +2447,7 @@ static void do_string_search(RCore *core, RInterval search_itv, struct search_pa
|
||||
eprintf ("hits: %" PFMT64d "\n", search->nhits - saved_nhits);
|
||||
}
|
||||
}
|
||||
done:
|
||||
done:
|
||||
r_cons_break_pop ();
|
||||
free (buf);
|
||||
} else {
|
||||
@ -2929,7 +2929,6 @@ static int cmd_search(void *data, const char *input) {
|
||||
.cmd_hit = r_config_get (core->config, "cmd.hit"),
|
||||
.outmode = 0,
|
||||
.inverse = false,
|
||||
.crypto_search = false,
|
||||
.aes_search = false,
|
||||
.privkey_search = false,
|
||||
};
|
||||
@ -3271,7 +3270,6 @@ reread:
|
||||
break;
|
||||
case 'c': { // "/c"
|
||||
dosearch = true;
|
||||
param.crypto_search = true;
|
||||
switch (input[1]) {
|
||||
case 'c': // "/cc"
|
||||
{
|
||||
@ -3326,7 +3324,6 @@ reread:
|
||||
break;
|
||||
case 'd': // "cd"
|
||||
{
|
||||
param.crypto_search = false;
|
||||
RSearchKeyword *kw;
|
||||
kw = r_search_keyword_new_hex ("308200003082", "ffff0000ffff", NULL);
|
||||
if (kw) {
|
||||
@ -3343,6 +3340,9 @@ reread:
|
||||
{
|
||||
RSearchKeyword *kw;
|
||||
kw = r_search_keyword_new_hexmask ("00", NULL);
|
||||
// AES search is done over 40 bytes
|
||||
kw->keyword_length = AES_SEARCH_LENGTH;
|
||||
r_search_reset (core->search, R_SEARCH_AES);
|
||||
r_search_kw_add (search, kw);
|
||||
r_search_begin (core->search);
|
||||
param.aes_search = true;
|
||||
@ -3352,6 +3352,9 @@ reread:
|
||||
{
|
||||
RSearchKeyword *kw;
|
||||
kw = r_search_keyword_new_hexmask ("00", NULL);
|
||||
// Private key search is at least 11 bytes
|
||||
kw->keyword_length = PRIVATE_KEY_SEARCH_LENGTH;
|
||||
r_search_reset (core->search, R_SEARCH_PRIV_KEY);
|
||||
r_search_kw_add (search, kw);
|
||||
r_search_begin (core->search);
|
||||
param.privkey_search = true;
|
||||
@ -3359,7 +3362,6 @@ reread:
|
||||
}
|
||||
default: {
|
||||
dosearch = false;
|
||||
param.crypto_search = false;
|
||||
r_core_cmd_help (core, help_msg_slash_c);
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ enum {
|
||||
R_SEARCH_STRING,
|
||||
R_SEARCH_XREFS,
|
||||
R_SEARCH_AES,
|
||||
R_SEARCH_PRIV_KEY,
|
||||
R_SEARCH_DELTAKEY,
|
||||
R_SEARCH_MAGIC,
|
||||
R_SEARCH_LAST
|
||||
|
@ -12,6 +12,13 @@
|
||||
#include <r_search.h>
|
||||
#include <r_crypto/r_aes.h>
|
||||
|
||||
#define AES128_SEARCH_LENGTH 24
|
||||
#define AES192_SEARCH_LENGTH 32
|
||||
#define AES256_SEARCH_LENGTH 40
|
||||
#define AES128_KEY_LENGTH 16
|
||||
#define AES192_KEY_LENGTH 24
|
||||
#define AES256_KEY_LENGTH 32
|
||||
|
||||
static bool aes256_key_test(const unsigned char *buf) {
|
||||
bool word1 = buf[32] == (buf[0] ^ Sbox[buf[29]] ^ 1) \
|
||||
&& buf[33] == (buf[1] ^ Sbox[buf[30]]) \
|
||||
@ -49,31 +56,49 @@ static bool aes128_key_test(const unsigned char *buf) {
|
||||
}
|
||||
|
||||
R_API int r_search_aes_update(RSearch *s, ut64 from, const ut8 *buf, int len) {
|
||||
int i, last = len - 20;
|
||||
int i, t, last = len - AES128_SEARCH_LENGTH;
|
||||
RListIter *iter;
|
||||
RSearchKeyword *kw;
|
||||
const int old_nhits = s->nhits;
|
||||
|
||||
r_list_foreach (s->kws, iter, kw) {
|
||||
if (last > 0) {
|
||||
if (last >= 0) {
|
||||
for (i = 0; i < last; i++) {
|
||||
if (aes128_key_test (buf + i)) {
|
||||
kw->keyword_length = 16;
|
||||
return r_search_hit_new (s, kw, from + i);
|
||||
}
|
||||
if (len - i - 28 > 0) {
|
||||
if (aes192_key_test (buf + i)) {
|
||||
kw->keyword_length = 24;
|
||||
return r_search_hit_new (s, kw, from + i);
|
||||
kw->keyword_length = AES128_KEY_LENGTH;
|
||||
t = r_search_hit_new (s, kw, from + i);
|
||||
if (!t) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (len - i - 36 > 0) {
|
||||
if (aes256_key_test (buf + i)) {
|
||||
kw->keyword_length = 32;
|
||||
return r_search_hit_new (s, kw, from + i);
|
||||
if (t > 1) {
|
||||
return s->nhits - old_nhits;
|
||||
}
|
||||
i += AES128_SEARCH_LENGTH;
|
||||
}
|
||||
if (len - i - AES192_SEARCH_LENGTH >= 0 && aes192_key_test (buf + i)) {
|
||||
kw->keyword_length = AES192_KEY_LENGTH;
|
||||
t = r_search_hit_new (s, kw, from + i);
|
||||
if (!t) {
|
||||
return -1;
|
||||
}
|
||||
if (t > 1) {
|
||||
return s->nhits - old_nhits;
|
||||
}
|
||||
i = i + AES192_SEARCH_LENGTH;
|
||||
}
|
||||
if (len - i - AES256_SEARCH_LENGTH >= 0 && aes256_key_test (buf + i)) {
|
||||
kw->keyword_length = AES256_KEY_LENGTH;
|
||||
t = r_search_hit_new (s, kw, from + i);
|
||||
if (!t) {
|
||||
return -1;
|
||||
}
|
||||
if (t > 1) {
|
||||
return s->nhits - old_nhits;
|
||||
}
|
||||
i = i + AES256_SEARCH_LENGTH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
@ -5,6 +5,11 @@
|
||||
|
||||
#include <r_search.h>
|
||||
|
||||
/* The minimal length to perform a search is the sizes of
|
||||
the sequence tag, the minimal length of the sequence,
|
||||
the version marker and the minimal key length. */
|
||||
#define PRIVKEY_SEARCH_MIN_LENGTH (1 + 1 + 4 + 1)
|
||||
|
||||
/*Baby BER parser, just good enough for private keys.
|
||||
|
||||
This is not robust to errors in the memory image, but if we added
|
||||
@ -60,21 +65,24 @@ static int check_fields(const ut8 *start) {
|
||||
// As defined in RFC 3447 for RSA, as defined in RFC 5915 for
|
||||
// elliptic curves and as defined in 7 of RFC 8410 for SafeCurves
|
||||
R_API int r_search_privkey_update(RSearch *s, ut64 from, const ut8 *buf, int len) {
|
||||
int i, k, max, index;
|
||||
int i, k, max, index, t;
|
||||
RListIter *iter;
|
||||
RSearchKeyword *kw;
|
||||
const size_t old_nhits = s->nhits;
|
||||
const ut8 rsa_versionmarker[] = { 0x02, 0x01, 0x00, 0x02 };
|
||||
const ut8 ecc_versionmarker[] = { 0x02, 0x01, 0x01, 0x04 };
|
||||
const ut8 safecurves_versionmarker[] = { 0x02, 0x01, 0x00, 0x30 };
|
||||
|
||||
if (len < sizeof (rsa_versionmarker)) {
|
||||
if (len < PRIVKEY_SEARCH_MIN_LENGTH) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
r_list_foreach (s->kws, iter, kw) {
|
||||
for (i = 2; i < len - sizeof (rsa_versionmarker); i++) {
|
||||
if (memcmp (&buf[i], rsa_versionmarker, sizeof (rsa_versionmarker)) &&
|
||||
memcmp (&buf[i], ecc_versionmarker, sizeof (ecc_versionmarker)) &&
|
||||
memcmp (&buf[i], safecurves_versionmarker, sizeof (safecurves_versionmarker))) {
|
||||
// Iteration until the remaining length is too small to contain a key.
|
||||
for (i = 2; i < len - PRIVKEY_SEARCH_MIN_LENGTH; i++) {
|
||||
if (memcmp (buf + i, rsa_versionmarker, sizeof (rsa_versionmarker)) &&
|
||||
memcmp (buf + i, ecc_versionmarker, sizeof (ecc_versionmarker)) &&
|
||||
memcmp (buf + i, safecurves_versionmarker, sizeof (safecurves_versionmarker))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -98,7 +106,11 @@ R_API int r_search_privkey_update(RSearch *s, ut64 from, const ut8 *buf, int len
|
||||
|
||||
if (check_fields (buf + index)) {
|
||||
parse_next_field(buf + index, &kw->keyword_length);
|
||||
return r_search_hit_new (s, kw, from + index);
|
||||
t = r_search_hit_new (s, kw, from + index);
|
||||
if (t > 1) {
|
||||
return s->nhits - old_nhits;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ R_API int r_search_set_mode(RSearch *s, int mode) {
|
||||
case R_SEARCH_KEYWORD: s->update = r_search_mybinparse_update; break;
|
||||
case R_SEARCH_REGEXP: s->update = r_search_regexp_update; break;
|
||||
case R_SEARCH_AES: s->update = r_search_aes_update; break;
|
||||
case R_SEARCH_PRIV_KEY: s->update = r_search_privkey_update; break;
|
||||
case R_SEARCH_STRING: s->update = r_search_strings_update; break;
|
||||
case R_SEARCH_DELTAKEY: s->update = r_search_deltakey_update; break;
|
||||
case R_SEARCH_MAGIC: s->update = r_search_magic_update; break;
|
||||
@ -110,13 +111,14 @@ R_API int r_search_hit_new(RSearch *s, RSearchKeyword *kw, ut64 addr) {
|
||||
if (!s->contiguous) {
|
||||
if (kw->last && addr == kw->last) {
|
||||
kw->count--;
|
||||
kw->last = s->bckwrds ? addr : addr + kw->keyword_length;
|
||||
kw->last = s->bckwrds? addr: addr + kw->keyword_length;
|
||||
eprintf ("0x%08"PFMT64x" Sequential hit ignored.\n", addr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// kw->last is used by string search, the right endpoint of last matcch (forward search), to honor search.overlap
|
||||
// kw->last is used by string search, the right endpoint of last match (forward search), to honor search.overlap
|
||||
kw->last = s->bckwrds ? addr : addr + kw->keyword_length;
|
||||
|
||||
if (s->callback) {
|
||||
int ret = s->callback (kw, s->user, addr);
|
||||
kw->count++;
|
||||
@ -474,9 +476,6 @@ R_API int r_search_update(RSearch *s, ut64 from, const ut8 *buf, long len) {
|
||||
return 0;
|
||||
}
|
||||
ret = s->update (s, from, buf, len);
|
||||
if (s->mode == R_SEARCH_AES) {
|
||||
ret = R_MIN (R_SEARCH_AES_BOX_SIZE, len);
|
||||
}
|
||||
} else {
|
||||
eprintf ("r_search_update: No search method defined\n");
|
||||
}
|
||||
|
@ -152,6 +152,14 @@ EXPECT=<<EOF
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=cmd.hit for /ca
|
||||
FILE=bins/other/aes_192.dump
|
||||
CMDS=/ca
|
||||
EXPECT=<<EOF
|
||||
0x000000fa hit0_0 000102030405060708090a0b0c0d0e0f1011121314151617
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=cmd.hit for /cr
|
||||
FILE=bins/other/rsa-private-4096.key
|
||||
CMDS=/cr
|
||||
|
Loading…
x
Reference in New Issue
Block a user