radare2/libr/core/cio.c
Álvaro Felipe Melchor 23d01f869c Improvements arm/thumb analysis ##anal
* anal: arm: emulate correctly BX PC in arm32/thumb ##anal
* anal: arm: handle BX PC in analysis to propagate bits

PC is well known without ESIL, that way it is possible to propagate the
bits to correctly create the hints

* anal: arm: modify r_anal_build_range_on_hints ##anal

This function now accepts a second parameter that specify when to
dispose hints when overlapping.

The rationale is that if this is performed in a continuos basis, old
ranges are lost when in a latter stage in the analysis is inserted a new
hint.

For example, if we have something like

0x80000 -> 16 bits
0x82000 -> 16 bits

With the previous logic this would have been become

0x8000 -> 16 bits

However, during analysis a new hint like this might happen

0x8100 -> 32 bits

Therefore, 0x8200 which was 16 bits is lost.  With the second parameter
update, we postpone this until the user print the disassembly - we wait
until the end to clean up hints to speed up the looks up. However,
during analysis we mantain all the hints.

* anal: arm: handle better anal hints to increase performance ##anal

new API r_anal_hint_get_bits_at

This saves time for example on r_anal_build_range_on_hints without the
need to use heap for RAnalHints speeding up the analysis

Added cb when calling r_anal_hint_set_bits and rbtree for anal ranges
which improves lookups

fix __anal_range_tree_find_bits_at

fix conflicts and coding style

* arm: set anal hint when BL instruction
2018-11-22 21:31:54 +01:00

431 lines
10 KiB
C

/* radare2 - LGPL - Copyright 2009-2017 - pancake */
#include "r_core.h"
R_API int r_core_setup_debugger (RCore *r, const char *debugbackend, bool attach) {
int pid, *p = NULL;
bool is_gdb = !strcmp (debugbackend, "gdb");
RIODesc * fd = r->file ? r_io_desc_get (r->io, r->file->fd) : NULL;
const char *prompt = NULL;
p = fd ? fd->data : NULL;
r_config_set_i (r->config, "cfg.debug", 1);
if (!p) {
eprintf ("Invalid debug io\n");
return false;
}
r_config_set (r->config, "io.ff", "true");
r_core_cmdf (r, "dL %s", debugbackend);
if (!is_gdb) {
pid = r_io_desc_get_pid (fd);
r_core_cmdf (r, "dp=%d", pid);
if (attach) {
r_core_cmdf (r, "dpa %d", pid);
}
}
//this makes to attach twice showing warnings in the output
//we get "resource busy" so it seems isn't an issue
r_core_cmd (r, ".dr*", 0);
/* honor dbg.bep */
{
const char *bep = r_config_get (r->config, "dbg.bep");
if (bep) {
if (!strcmp (bep, "loader")) {
/* do nothing here */
} else if (!strcmp (bep, "entry")) {
r_core_cmd (r, "dcu entry0", 0);
} else {
r_core_cmdf (r, "dcu %s", bep);
}
}
}
r_core_cmd (r, "sr PC", 0);
/* set the prompt if it's not been set already by the callbacks */
prompt = r_config_get (r->config, "cmd.prompt");
if (prompt && !strcmp (prompt, "")) {
if (r_config_get_i (r->config, "dbg.status")) {
r_config_set (r->config, "cmd.prompt", ".dr*;drd;sr PC;pi 1;s-");
} else {
r_config_set (r->config, "cmd.prompt", ".dr*");
}
}
r_config_set (r->config, "cmd.vprompt", ".dr*");
return true;
}
R_API int r_core_seek_base (RCore *core, const char *hex) {
ut64 addr = r_num_tail (core->num, core->offset, hex);
return r_core_seek (core, addr, 1);
}
R_API bool r_core_dump(RCore *core, const char *file, ut64 addr, ut64 size, int append) {
ut64 i;
ut8 *buf;
int bs = core->blocksize;
FILE *fd;
if (append) {
fd = r_sandbox_fopen (file, "ab");
} else {
r_sys_truncate (file, 0);
fd = r_sandbox_fopen (file, "wb");
}
if (!fd) {
eprintf ("Cannot open '%s' for writing\n", file);
return false;
}
/* some io backends seems to be buggy in those cases */
if (bs > 4096) {
bs = 4096;
}
buf = malloc (bs);
if (!buf) {
eprintf ("Cannot alloc %d byte(s)\n", bs);
fclose (fd);
return false;
}
r_cons_break_push (NULL, NULL);
for (i = 0; i < size; i += bs) {
if (r_cons_is_breaked ()) {
break;
}
if ((i + bs) > size) {
bs = size - i;
}
r_io_read_at (core->io, addr + i, buf, bs);
if (fwrite (buf, bs, 1, fd) < 1) {
eprintf ("write error\n");
break;
}
}
r_cons_break_pop ();
fclose (fd);
free (buf);
return true;
}
R_API int r_core_write_op(RCore *core, const char *arg, char op) {
int i, j, len, ret = false;
char *str = NULL;
ut8 *buf;
// XXX we can work with config.block instead of dupping it
buf = (ut8 *)malloc (core->blocksize);
if (!buf) {
goto beach;
}
memcpy (buf, core->block, core->blocksize);
if (op!='e') {
// fill key buffer either from arg or from clipboard
if (arg) { // parse arg for key
// r_hex_str2bin() is guaranteed to output maximum half the
// input size, or 1 byte if there is just a single nibble.
str = (char *)malloc (strlen (arg) / 2 + 1);
if (!str) {
goto beach;
}
len = r_hex_str2bin (arg, (ut8 *)str);
// Output is invalid if there was just a single nibble,
// but in that case, len is negative (-1).
if (len <= 0) {
eprintf ("Invalid hexpair string\n");
goto beach;
}
} else { // use clipboard as key
len = core->yank_buf->length;
if (len <= 0) {
eprintf ("Clipboard is empty and no value argument(s) given\n");
goto beach;
}
str = r_mem_dup (core->yank_buf->buf, len);
if (!str) {
goto beach;
}
}
} else {
len = 0;
}
// execute the operand
if (op=='e') {
int wordsize = 1;
char *os, *p, *s = strdup (arg);
int n, from = 0, to = 0, dif = 0, step = 1;
n = from = to;
os = s;
to = UT8_MAX;
//
p = strchr (s, ' ');
if (p) {
*p = 0;
from = r_num_math (core->num, s);
s = p + 1;
}
p = strchr (s, ' ');
if (p) {
*p = 0;
to = r_num_math (core->num, s);
s = p + 1;
}
p = strchr (s, ' ');
if (p) {
*p = 0;
step = r_num_math (core->num, s);
s = p + 1;
wordsize = r_num_math (core->num, s);
} else {
step = r_num_math (core->num, s);
}
free (os);
eprintf ("from %d to %d step %d size %d\n", from, to, step, wordsize);
dif = (to <= from)? UT8_MAX: to - from + 1;
if (wordsize == 1) {
if (to < 1 || to > UT8_MAX) {
to = UT8_MAX;
}
from %= (UT8_MAX + 1);
}
if (dif < 1) {
dif = UT8_MAX + 1;
}
if (step < 1) {
step = 1;
}
if (wordsize < 1) {
wordsize = 1;
}
if (wordsize == 1) {
for (i = n = 0; i < core->blocksize; i++, n += step) {
buf[i] = (ut8)(n % dif) + from;
}
} else if (wordsize == 2) {
ut16 num16 = from;
for (i = 0; i < core->blocksize; i += wordsize, num16 += step) {
r_write_le16 (buf + i, num16);
}
} else if (wordsize == 4) {
ut32 num32 = from;
for (i = 0; i < core->blocksize; i += wordsize, num32 += step) {
r_write_le32 (buf + i, num32);
}
} else if (wordsize == 8) {
ut64 num64 = from;
for (i = 0; i < core->blocksize; i += wordsize, num64 += step) {
r_write_le64 (buf + i, num64);
}
} else {
eprintf ("Invalid word size. Use 1, 2, 4 or 8\n");
}
} else if (op=='2' || op=='4') {
op -= '0';
// if i < core->blocksize would pass the test but buf[i+3] goes beyond the buffer
if (core->blocksize > 3) {
for (i=0; i<core->blocksize-3; i+=op) {
/* endian swap */
ut8 tmp = buf[i];
buf[i] = buf[i+3];
buf[i+3] = tmp;
if (op == 4) {
tmp = buf[i + 1];
buf[i + 1] = buf[i + 2];
buf[i + 2] = tmp;
}
}
}
} else {
for (i=j=0; i<core->blocksize; i++) {
switch (op) {
case 'x': buf[i] ^= str[j]; break;
case 'a': buf[i] += str[j]; break;
case 's': buf[i] -= str[j]; break;
case 'm': buf[i] *= str[j]; break;
case 'w': buf[i] = str[j]; break;
case 'd': buf[i] = (str[j])? buf[i] / str[j]: 0; break;
case 'r': buf[i] >>= str[j]; break;
case 'l': buf[i] <<= str[j]; break;
case 'o': buf[i] |= str[j]; break;
case 'A': buf[i] &= str[j]; break;
}
j++;
if (j >= len) {
j = 0; /* cyclic key */
}
}
}
ret = r_core_write_at (core, core->offset, buf, core->blocksize);
beach:
free (buf);
free (str);
return ret;
}
static void __choose_bits_anal_hints(RCore *core, ut64 addr, int *bits) {
if (core->anal) {
int ret = r_anal_range_tree_find_bits_at (core->anal->rb_hints_ranges,
addr);
if (ret) {
*bits = ret;
}
}
}
R_API void r_core_seek_archbits(RCore *core, ut64 addr) {
int bits = 0;
const char *arch = r_io_section_get_archbits (core->io, addr, &bits);
if (!bits && !core->fixedbits) {
//if we found bits related with anal hints pick it up
__choose_bits_anal_hints (core, addr, &bits);
}
if (bits && !core->fixedbits) {
r_config_set_i (core->config, "asm.bits", bits);
}
if (arch && !core->fixedarch) {
r_config_set (core->config, "asm.arch", arch);
}
}
R_API bool r_core_seek(RCore *core, ut64 addr, bool rb) {
core->offset = r_io_seek (core->io, addr, R_IO_SEEK_SET);
if (rb) {
r_core_block_read (core);
}
return core->offset == addr;
}
R_API int r_core_seek_delta(RCore *core, st64 addr) {
ut64 tmp = core->offset;
int ret;
if (addr == 0) {
return true;
}
if (addr > 0LL) {
/* TODO: check end of file */
addr += tmp;
} else {
/* check < 0 */
if (-addr > tmp) {
addr = 0;
} else {
addr += tmp;
}
}
core->offset = addr;
ret = r_core_seek (core, addr, 1);
//ret = r_core_block_read (core);
//if (ret == -1)
// memset (core->block, 0xff, core->blocksize);
// core->offset = tmp;
return ret;
}
R_API bool r_core_write_at(RCore *core, ut64 addr, const ut8 *buf, int size) {
bool ret;
if (!core) {
return false;
}
ret = r_io_write_at (core->io, addr, buf, size);
if (addr >= core->offset && addr <= core->offset + core->blocksize - 1) {
r_core_block_read (core);
}
return ret;
}
R_API int r_core_extend_at(RCore *core, ut64 addr, int size) {
int ret;
if (!core->io || !core->file || size < 1) {
return false;
}
ret = r_io_use_fd (core->io, core->file->fd);
if (ret != -1) {
ret = r_io_extend_at (core->io, addr, size);
if (addr >= core->offset && addr <= core->offset+core->blocksize) {
r_core_block_read (core);
}
}
return (ret==-1)? false: true;
}
R_API int r_core_shift_block(RCore *core, ut64 addr, ut64 b_size, st64 dist) {
// bstart - block start, fstart file start
ut64 fend = 0, fstart = 0, bstart = 0, file_sz = 0;
ut8 * shift_buf = NULL;
int res = false;
if (!core->io || !core->file) {
return false;
}
if (b_size == 0 || b_size == (ut64) -1) {
res = r_io_use_fd (core->io, core->file->fd);
file_sz = r_io_size (core->io);
if (file_sz == UT64_MAX) {
file_sz = 0;
}
#if 0
bstart = r_io_seek (core->io, addr, R_IO_SEEK_SET);
fend = r_io_seek (core->io, 0, R_IO_SEEK_END);
if (fend < 1) {
fend = 0;
}
#else
bstart = 0;
fend = file_sz;
#endif
fstart = file_sz - fend;
b_size = fend > bstart ? fend - bstart: 0;
}
if ((st64)b_size < 1) {
return false;
}
shift_buf = calloc (b_size, 1);
if (!shift_buf) {
eprintf ("Cannot allocated %d byte(s)\n", (int)b_size);
return false;
}
// cases
// addr + b_size + dist > file_end
//if ( (addr+b_size) + dist > file_end ) {
// res = false;
//}
// addr + b_size + dist < file_start (should work since dist is signed)
//else if ( (addr+b_size) + dist < 0 ) {
// res = false;
//}
// addr + dist < file_start
if (addr + dist < fstart) {
res = false;
// addr + dist > file_end
} else if ( (addr) + dist > fend) {
res = false;
} else {
r_io_use_fd (core->io, core->file->fd);
r_io_read_at (core->io, addr, shift_buf, b_size);
r_io_write_at (core->io, addr + dist, shift_buf, b_size);
res = true;
}
r_core_seek (core, addr, 1);
free (shift_buf);
return res;
}
R_API int r_core_block_read(RCore *core) {
if (core && core->block) {
return r_io_read_at (core->io, core->offset, core->block, core->blocksize);
}
return -1;
}
R_API int r_core_is_valid_offset (RCore *core, ut64 offset) {
if (!core) {
eprintf ("r_core_is_valid_offset: core is NULL\n");
r_sys_backtrace ();
return R_FAIL;
}
return r_io_is_valid_offset (core->io, offset, 0);
}