1129 lines
30 KiB
C
1129 lines
30 KiB
C
// SPDX-FileCopyrightText: 2009-2019 pancake <pancake@nopcode.org>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include "rz_core.h"
|
|
#include "core_private.h"
|
|
|
|
RZ_API int rz_core_setup_debugger(RzCore *r, const char *debugbackend, bool attach) {
|
|
int pid, *p = NULL;
|
|
RzIODesc *fd = r->file ? rz_io_desc_get(r->io, r->file->fd) : NULL;
|
|
char buf[20];
|
|
|
|
p = fd ? fd->data : NULL;
|
|
if (!p) {
|
|
RZ_LOG_ERROR("core: invalid debug io descriptor\n");
|
|
return false;
|
|
}
|
|
|
|
rz_config_set_b(r->config, "cfg.debug", true);
|
|
rz_config_set_b(r->config, "io.ff", true);
|
|
rz_config_set(r->config, "dbg.backend", debugbackend);
|
|
pid = rz_io_desc_get_pid(fd);
|
|
// NOTE: pid could be negative here, but it must be passed to the debug
|
|
// plugin as is, so we don't check it here. The plugin should handle it and
|
|
// return an error if it's invalid.
|
|
r->dbg->main_pid = pid;
|
|
rz_debug_select(r->dbg, pid, r->dbg->tid);
|
|
if (attach) {
|
|
rz_debug_attach(r->dbg, pid);
|
|
rz_debug_select(r->dbg, r->dbg->pid, r->dbg->tid);
|
|
}
|
|
rz_config_set_bool(r->config, "dbg.swstep", (r->dbg->cur && !r->dbg->cur->canstep));
|
|
free(rz_io_system(r->io, rz_strf(buf, "pid %d", r->dbg->pid)));
|
|
|
|
// this makes to attach twice showing warnings in the output
|
|
// we get "resource busy" so it seems isn't an issue
|
|
rz_core_reg_update_flags(r);
|
|
/* honor dbg.bep */
|
|
{
|
|
const char *bep = rz_config_get(r->config, "dbg.bep");
|
|
if (bep) {
|
|
ut64 address = 0;
|
|
if (!strcmp(bep, "loader")) {
|
|
/* do nothing here */
|
|
} else if (!strcmp(bep, "entry")) {
|
|
address = rz_num_math(r->num, "entry0");
|
|
rz_core_debug_continue_until(r, address);
|
|
} else {
|
|
address = rz_num_math(r->num, bep);
|
|
rz_core_debug_continue_until(r, address);
|
|
}
|
|
}
|
|
}
|
|
rz_core_seek_to_register(r, "PC", false);
|
|
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_core_dump(RzCore *core, const char *file, ut64 addr, ut64 size, int append) {
|
|
ut64 i;
|
|
ut8 *buf;
|
|
int bs = core->blocksize;
|
|
FILE *fd;
|
|
if (append) {
|
|
fd = rz_sys_fopen(file, "ab");
|
|
} else {
|
|
rz_sys_truncate(file, 0);
|
|
fd = rz_sys_fopen(file, "wb");
|
|
}
|
|
if (!fd) {
|
|
RZ_LOG_ERROR("core: 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) {
|
|
RZ_LOG_ERROR("core: cannot alloc %d byte(s)\n", bs);
|
|
fclose(fd);
|
|
return false;
|
|
}
|
|
rz_cons_break_push(NULL, NULL);
|
|
for (i = 0; i < size; i += bs) {
|
|
if (rz_cons_is_breaked()) {
|
|
break;
|
|
}
|
|
if ((i + bs) > size) {
|
|
bs = size - i;
|
|
}
|
|
rz_io_read_at_mapped(core->io, addr + i, buf, bs);
|
|
if (fwrite(buf, bs, 1, fd) < 1) {
|
|
RZ_LOG_ERROR("core: cannot write to buffer\n");
|
|
break;
|
|
}
|
|
}
|
|
rz_cons_break_pop();
|
|
fclose(fd);
|
|
free(buf);
|
|
return true;
|
|
}
|
|
|
|
// Get address-specific bits and arch at a certain address.
|
|
// If there are no specific infos (i.e. asm.bits and asm.arch should apply), the bits and arch will be 0 or NULL respectively!
|
|
RZ_API void rz_core_arch_bits_at(RzCore *core, ut64 addr, RZ_OUT RZ_NULLABLE int *bits, RZ_OUT RZ_BORROW RZ_NULLABLE const char **arch) {
|
|
int bitsval = 0;
|
|
const char *archval = NULL;
|
|
RzBinObject *o = rz_bin_cur_object(core->bin);
|
|
RzBinSection *s = o ? rz_bin_get_section_at(o, addr, core->io->va) : NULL;
|
|
if (s) {
|
|
if (!core->fixedarch) {
|
|
archval = s->arch;
|
|
}
|
|
if (!core->fixedbits && s->bits) {
|
|
// only enforce if there's one bits set
|
|
switch (s->bits) {
|
|
case RZ_SYS_BITS_16:
|
|
case RZ_SYS_BITS_32:
|
|
case RZ_SYS_BITS_64:
|
|
bitsval = s->bits * 8;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// if we found bits related with analysis hints pick it up
|
|
if (bits && !bitsval && !core->fixedbits) {
|
|
bitsval = rz_analysis_hint_bits_at(core->analysis, addr, NULL);
|
|
}
|
|
if (arch && !archval && !core->fixedarch) {
|
|
archval = rz_analysis_hint_arch_at(core->analysis, addr, NULL);
|
|
}
|
|
if (bits && bitsval) {
|
|
*bits = bitsval;
|
|
}
|
|
if (arch && archval) {
|
|
*arch = archval;
|
|
}
|
|
}
|
|
|
|
RZ_API void rz_core_seek_arch_bits(RzCore *core, ut64 addr) {
|
|
int bits = 0;
|
|
const char *arch = NULL;
|
|
rz_core_arch_bits_at(core, addr, &bits, &arch);
|
|
if (bits) {
|
|
rz_config_set_i(core->config, "asm.bits", bits);
|
|
}
|
|
if (arch) {
|
|
rz_config_set(core->config, "asm.arch", arch);
|
|
}
|
|
}
|
|
|
|
RZ_API bool rz_core_write_at(RzCore *core, ut64 addr, const ut8 *buf, int size) {
|
|
rz_return_val_if_fail(core && buf && addr != UT64_MAX, false);
|
|
if (size < 1) {
|
|
return false;
|
|
}
|
|
bool ret = rz_io_write_at(core->io, addr, buf, size);
|
|
// whether the written contents affect core->block
|
|
bool start_in_block = addr >= core->offset && addr <= core->offset + core->blocksize - 1;
|
|
bool end_in_block = addr + size > core->offset && addr + size <= core->offset + core->blocksize;
|
|
if (start_in_block || end_in_block) {
|
|
rz_core_block_read(core);
|
|
}
|
|
if (rz_config_get_i(core->config, "cfg.wseek")) {
|
|
rz_core_seek_delta(core, size, true);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* \brief Extend the file at current offset by inserting \p size 0 bytes at \p addr
|
|
*
|
|
* \p addr is an physical/virtual address based on the value of eval "io.va".
|
|
* When virtual it is translated to a physical address according to the IO map
|
|
* at the current offset
|
|
*
|
|
* \param core Reference to RzCore instance
|
|
* \param addr Address where to insert new 0 bytes.
|
|
* \param size Number of 0 bytes to insert
|
|
* \return true if extend operation was successful, false otherwise
|
|
*/
|
|
RZ_API bool rz_core_extend_at(RzCore *core, ut64 addr, ut64 size) {
|
|
rz_return_val_if_fail(core, false);
|
|
|
|
int io_va = rz_config_get_i(core->config, "io.va");
|
|
if (io_va) {
|
|
RzIOMap *map = rz_io_map_get(core->io, core->offset);
|
|
if (map) {
|
|
addr = addr - map->itv.addr + map->delta;
|
|
}
|
|
}
|
|
bool ret = rz_io_extend_at(core->io, addr, size);
|
|
rz_core_block_read(core);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* \brief Shift a block of data from \p addr of size \p b_size left or right based on \p dist.
|
|
*
|
|
* \param core Reference to RzCore instance
|
|
* \param addr Address of the block of data to move
|
|
* \param b_size Size of the block of data to move
|
|
* \param dist Where to shift the data, whether backward or forward and how
|
|
* distant from the original position
|
|
* \return true if the shift operation was succesful, false otherwise
|
|
*/
|
|
RZ_API bool rz_core_shift_block(RzCore *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) {
|
|
rz_io_use_fd(core->io, core->file->fd);
|
|
file_sz = rz_io_size(core->io);
|
|
if (file_sz == UT64_MAX) {
|
|
file_sz = 0;
|
|
}
|
|
bstart = 0;
|
|
fend = file_sz;
|
|
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) {
|
|
RZ_LOG_ERROR("core: cannot allocate %d byte(s)\n", (int)b_size);
|
|
return false;
|
|
}
|
|
|
|
if (addr + dist < fstart) {
|
|
res = false;
|
|
} else if ((addr) + dist > fend) {
|
|
res = false;
|
|
} else {
|
|
rz_io_use_fd(core->io, core->file->fd);
|
|
rz_io_read_at_mapped(core->io, addr, shift_buf, b_size);
|
|
rz_io_write_at(core->io, addr + dist, shift_buf, b_size);
|
|
res = true;
|
|
}
|
|
rz_core_seek(core, addr, true);
|
|
free(shift_buf);
|
|
return res;
|
|
}
|
|
|
|
RZ_API int rz_core_block_read(RzCore *core) {
|
|
if (core && core->block) {
|
|
return rz_io_read_at_mapped(core->io, core->offset, core->block, core->blocksize);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
RZ_API int rz_core_is_valid_offset(RZ_NONNULL RzCore *core, ut64 offset) {
|
|
rz_return_val_if_fail(core, -1);
|
|
return rz_io_is_valid_offset(core->io, offset, 0);
|
|
}
|
|
|
|
/**
|
|
* Writes the hexadecimal string at the given offset
|
|
*
|
|
* Returns the length of the written data.
|
|
*
|
|
* \param core RzCore reference
|
|
* \param addr Address to where to write
|
|
* \param pairs Data as the hexadecimal string
|
|
*/
|
|
RZ_API int rz_core_write_hexpair(RzCore *core, ut64 addr, const char *pairs) {
|
|
rz_return_val_if_fail(core && pairs, 0);
|
|
ut8 *buf = malloc(strlen(pairs) + 1);
|
|
if (!buf) {
|
|
return 0;
|
|
}
|
|
int len = rz_hex_str2bin(pairs, buf);
|
|
if (len < 0) {
|
|
RZ_LOG_ERROR("Could not convert hexpair '%s' to bin data\n", pairs);
|
|
goto err;
|
|
}
|
|
if (!rz_core_write_at(core, addr, buf, len)) {
|
|
RZ_LOG_ERROR("Could not write hexpair '%s' at %" PFMT64x "\n", pairs, addr);
|
|
goto err;
|
|
}
|
|
err:
|
|
free(buf);
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* Writes the bytes \p data at address \p addr cyclically until it fills the whole block
|
|
*
|
|
* It repeats the data \p data with length \p len until it fills an entire block
|
|
* starting at \p addr.
|
|
*
|
|
* \param core RzCore reference
|
|
* \param addr Address to where to write
|
|
* \param data Array of bytes to cyclically write in the block at \p addr
|
|
* \param len Length of \p data
|
|
*/
|
|
RZ_API bool rz_core_write_block(RzCore *core, ut64 addr, ut8 *data, size_t len) {
|
|
rz_return_val_if_fail(core && data, 0);
|
|
|
|
ut8 *buf = RZ_NEWS(ut8, core->blocksize);
|
|
if (!buf) {
|
|
return false;
|
|
}
|
|
|
|
bool res = false;
|
|
rz_mem_copyloop(buf, data, core->blocksize, len);
|
|
if (!rz_core_write_at(core, addr, buf, core->blocksize)) {
|
|
RZ_LOG_ERROR("Could not write cyclic data (%d bytes) at %" PFMT64x "\n", core->blocksize, addr);
|
|
goto err;
|
|
}
|
|
res = true;
|
|
err:
|
|
free(buf);
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* \brief Assembles instructions and writes the resulting data at the given offset.
|
|
*
|
|
* \param core RzCore reference
|
|
* \param addr Address to where to write
|
|
* \param instructions List of instructions to assemble as a string
|
|
* \return Returns the length of the written data or -1 in case of error
|
|
*/
|
|
RZ_API int rz_core_write_assembly(RzCore *core, ut64 addr, RZ_NONNULL const char *instructions) {
|
|
rz_return_val_if_fail(core && instructions, -1);
|
|
|
|
int ret = -1;
|
|
|
|
rz_asm_set_pc(core->rasm, core->offset);
|
|
RzAsmCode *acode = rz_asm_massemble(core->rasm, instructions);
|
|
if (!acode) {
|
|
return -1;
|
|
}
|
|
if (acode->len <= 0) {
|
|
ret = 0;
|
|
goto err;
|
|
}
|
|
|
|
if (!rz_core_write_at(core, core->offset, acode->bytes, acode->len)) {
|
|
RZ_LOG_ERROR("Cannot write %d bytes at 0x%" PFMT64x " address\n", acode->len, core->offset);
|
|
core->num->value = 1;
|
|
goto err;
|
|
}
|
|
ret = acode->len;
|
|
err:
|
|
rz_asm_code_free(acode);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* \brief Assemble instructions and write the resulting data inside the current instruction.
|
|
*
|
|
* Assemble one or more instructions and write the resulting data inside the
|
|
* current instruction, if the new instructions fit. Fill the rest of the bytes
|
|
* of the old instruction with NOP
|
|
*
|
|
* \param core RzCore reference
|
|
* \param addr Address to where to write
|
|
* \param instructions List of instructions to assemble as a string
|
|
* \return Returns the length of the written data or -1 in case of error (e.g. the new instruction does not fit)
|
|
*/
|
|
RZ_API int rz_core_write_assembly_fill(RzCore *core, ut64 addr, RZ_NONNULL const char *instructions) {
|
|
rz_return_val_if_fail(core && instructions, -1);
|
|
|
|
int ret = -1;
|
|
|
|
rz_asm_set_pc(core->rasm, core->offset);
|
|
RzAsmCode *acode = rz_asm_massemble(core->rasm, instructions);
|
|
if (!acode) {
|
|
return -1;
|
|
}
|
|
if (acode->len <= 0) {
|
|
ret = 0;
|
|
goto err;
|
|
}
|
|
|
|
RzAnalysisOp op = { 0 };
|
|
rz_analysis_op_init(&op);
|
|
if (rz_analysis_op(core->analysis, &op, core->offset, core->block, core->blocksize, RZ_ANALYSIS_OP_MASK_BASIC) < 1) {
|
|
RZ_LOG_ERROR("Invalid instruction at %" PFMT64x "\n", core->offset);
|
|
goto err;
|
|
}
|
|
if (op.size < acode->len) {
|
|
RZ_LOG_ERROR("Instructions do not fit at %" PFMT64x "\n", core->offset);
|
|
goto err;
|
|
}
|
|
rz_core_hack(core, "nop");
|
|
|
|
if (!rz_core_write_at(core, core->offset, acode->bytes, acode->len)) {
|
|
RZ_LOG_ERROR("Cannot write %d bytes at 0x%" PFMT64x " address\n", acode->len, core->offset);
|
|
core->num->value = 1;
|
|
goto err;
|
|
}
|
|
ret = acode->len;
|
|
err:
|
|
rz_analysis_op_fini(&op);
|
|
rz_asm_code_free(acode);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* \brief Print an IO plugin according to \p state
|
|
*
|
|
* \param plugin Reference to RzIOPlugin
|
|
* \param state Specify how the plugin shall be printed
|
|
*/
|
|
RZ_API RzCmdStatus rz_core_io_plugin_print(RzIOPlugin *plugin, RzCmdStateOutput *state) {
|
|
char str[4];
|
|
PJ *pj = state->d.pj;
|
|
str[0] = 'r';
|
|
str[1] = plugin->write ? 'w' : '_';
|
|
str[2] = plugin->isdbg ? 'd' : '_';
|
|
str[3] = 0;
|
|
switch (state->mode) {
|
|
case RZ_OUTPUT_MODE_JSON:
|
|
pj_o(pj);
|
|
pj_ks(pj, "permissions", str);
|
|
pj_ks(pj, "name", plugin->name);
|
|
pj_ks(pj, "description", plugin->desc);
|
|
pj_ks(pj, "license", plugin->license);
|
|
|
|
if (plugin->uris) {
|
|
char *uri;
|
|
char *uris = rz_str_dup(plugin->uris);
|
|
RzList *plist = rz_str_split_list(uris, ",", 0);
|
|
RzListIter *piter;
|
|
pj_k(pj, "uris");
|
|
pj_a(pj);
|
|
rz_list_foreach (plist, piter, uri) {
|
|
pj_s(pj, uri);
|
|
}
|
|
pj_end(pj);
|
|
rz_list_free(plist);
|
|
free(uris);
|
|
}
|
|
if (plugin->version) {
|
|
pj_ks(pj, "version", plugin->version);
|
|
}
|
|
if (plugin->author) {
|
|
pj_ks(pj, "author", plugin->author);
|
|
}
|
|
pj_end(pj);
|
|
break;
|
|
case RZ_OUTPUT_MODE_TABLE:
|
|
rz_table_set_columnsf(state->d.t, "sssss", "perm", "license", "name", "uri", "description");
|
|
rz_table_add_rowf(state->d.t, "sssss", str, plugin->license, plugin->name, plugin->uris, plugin->desc);
|
|
break;
|
|
case RZ_OUTPUT_MODE_QUIET:
|
|
rz_cons_printf("%s\n", plugin->name);
|
|
break;
|
|
case RZ_OUTPUT_MODE_STANDARD:
|
|
rz_cons_printf("%s %-8s %s (%s)",
|
|
str, plugin->name,
|
|
plugin->desc, plugin->license);
|
|
if (plugin->uris) {
|
|
rz_cons_printf(" %s", plugin->uris);
|
|
}
|
|
if (plugin->version) {
|
|
rz_cons_printf(" v%s", plugin->version);
|
|
}
|
|
if (plugin->author) {
|
|
rz_cons_printf(" %s", plugin->author);
|
|
}
|
|
rz_cons_printf("\n");
|
|
break;
|
|
default: {
|
|
rz_warn_if_reached();
|
|
return RZ_CMD_STATUS_NONEXISTINGCMD;
|
|
}
|
|
}
|
|
return RZ_CMD_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Print the registered IO plugins according to \p state
|
|
*
|
|
* \param io Reference to RzIO instance
|
|
* \param state Specify how plugins shall be printed
|
|
*/
|
|
RZ_API RzCmdStatus rz_core_io_plugins_print(RZ_NONNULL RZ_BORROW RzIO *io, RzCmdStateOutput *state) {
|
|
rz_return_val_if_fail(io && state, RZ_CMD_STATUS_ERROR);
|
|
|
|
if (!io) {
|
|
return RZ_CMD_STATUS_ERROR;
|
|
}
|
|
rz_cmd_state_output_array_start(state);
|
|
rz_cmd_state_output_set_columnsf(state, "sssss", "perm", "license", "name", "uri", "description");
|
|
|
|
RzIterator *iter = ht_sp_as_iter(io->plugins);
|
|
RzList *plugin_list = rz_list_new_from_iterator(iter);
|
|
if (!plugin_list) {
|
|
rz_iterator_free(iter);
|
|
return RZ_CMD_STATUS_ERROR;
|
|
}
|
|
rz_list_sort(plugin_list, (RzListComparator)rz_io_plugin_cmp, NULL);
|
|
RzListIter *it;
|
|
RzIOPlugin *plugin;
|
|
rz_list_foreach (plugin_list, it, plugin) {
|
|
rz_core_io_plugin_print(plugin, state);
|
|
}
|
|
rz_iterator_free(iter);
|
|
rz_list_free(plugin_list);
|
|
rz_cmd_state_output_array_end(state);
|
|
return RZ_CMD_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Write a given \p value at the specified \p address, using \p sz bytes
|
|
*
|
|
* \param core RzCore reference
|
|
* \param addr Address where to write the value
|
|
* \param value Value to write
|
|
* \param sz Number of bytes to write. Can be 1, 2, 4, 8 or the special value 0
|
|
* if you want the function to choose based on \p value (4 if \p value
|
|
* is <4GB, 8 otherwise)
|
|
*/
|
|
RZ_API bool rz_core_write_value_at(RzCore *core, ut64 addr, ut64 value, int sz) {
|
|
rz_return_val_if_fail(sz == 0 || sz == 1 || sz == 2 || sz == 4 || sz == 8, false);
|
|
ut8 buf[sizeof(ut64)];
|
|
bool be = rz_config_get_b(core->config, "cfg.bigendian");
|
|
|
|
core->num->value = 0;
|
|
if (sz == 0) {
|
|
sz = value & UT64_32U ? 8 : 4;
|
|
}
|
|
|
|
switch (sz) {
|
|
case 1:
|
|
rz_write_ble8(buf, (ut8)(value & UT8_MAX));
|
|
break;
|
|
case 2:
|
|
rz_write_ble16(buf, (ut16)(value & UT16_MAX), be);
|
|
break;
|
|
case 4:
|
|
rz_write_ble32(buf, (ut32)(value & UT32_MAX), be);
|
|
break;
|
|
case 8:
|
|
rz_write_ble64(buf, value, be);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (!rz_core_write_at(core, addr, buf, sz)) {
|
|
RZ_LOG_ERROR("Could not write %d bytes at %" PFMT64x "\n", sz, addr);
|
|
core->num->value = 1;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Write at \p addr the current value + \p value passed as argument
|
|
*
|
|
* The values read/written are considered as integers of \p sz bytes.
|
|
*
|
|
* \param core RzCore reference
|
|
* \param addr Address where to overwrite the value
|
|
* \param value Value to sum to the existing value in \p addr
|
|
* \param sz Size of the values, in bytes, to consider. Can be 1, 2, 4, 8.
|
|
*/
|
|
RZ_API bool rz_core_write_value_inc_at(RzCore *core, ut64 addr, st64 value, int sz) {
|
|
rz_return_val_if_fail(sz == 1 || sz == 2 || sz == 4 || sz == 8, false);
|
|
|
|
ut8 buf[sizeof(ut64)];
|
|
bool be = rz_config_get_b(core->config, "cfg.bigendian");
|
|
|
|
if (!rz_io_read_at_mapped(core->io, addr, buf, sz)) {
|
|
return false;
|
|
}
|
|
|
|
switch (sz) {
|
|
case 1: {
|
|
ut8 cur = rz_read_ble8(buf);
|
|
cur += value;
|
|
rz_write_ble8(buf, cur);
|
|
break;
|
|
}
|
|
case 2: {
|
|
ut16 cur = rz_read_ble16(buf, be);
|
|
cur += value;
|
|
rz_write_ble16(buf, cur, be);
|
|
break;
|
|
}
|
|
case 4: {
|
|
ut32 cur = rz_read_ble32(buf, be);
|
|
cur += value;
|
|
rz_write_ble32(buf, cur, be);
|
|
break;
|
|
}
|
|
case 8: {
|
|
ut64 cur = rz_read_ble64(buf, be);
|
|
cur += value;
|
|
rz_write_ble64(buf, cur, be);
|
|
break;
|
|
}
|
|
default:
|
|
rz_warn_if_reached();
|
|
break;
|
|
}
|
|
|
|
if (!rz_core_write_at(core, addr, buf, sz)) {
|
|
RZ_LOG_ERROR("Could not write %d bytes at %" PFMT64x "\n", sz, addr);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Write a given string \p s at the specified \p addr
|
|
*
|
|
* \param core RzCore reference
|
|
* \param addr Address where to write the string
|
|
* \param s String to write. The string is unescaped, meaning that if there is `\n` it becomes 0x0a
|
|
*/
|
|
RZ_API bool rz_core_write_string_at(RzCore *core, ut64 addr, RZ_NONNULL const char *s) {
|
|
rz_return_val_if_fail(core && s, false);
|
|
|
|
char *str = rz_str_dup(s);
|
|
if (!str) {
|
|
return false;
|
|
}
|
|
|
|
int len = rz_str_unescape(str);
|
|
if (!rz_core_write_at(core, addr, (const ut8 *)str, len)) {
|
|
RZ_LOG_ERROR("Could not write '%s' at %" PFMT64x "\n", s, addr);
|
|
free(str);
|
|
return false;
|
|
}
|
|
free(str);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Write a given string \p s as a wide string at the specified \p addr
|
|
*
|
|
* \param core RzCore reference
|
|
* \param addr Address where to write the string
|
|
* \param s String to write. The string is unescaped, meaning that if there is `\n` it becomes 0x0a
|
|
*/
|
|
RZ_API bool rz_core_write_string_wide_at(RzCore *core, ut64 addr, const char *s) {
|
|
rz_return_val_if_fail(core && s, false);
|
|
|
|
bool res = false;
|
|
char *str = rz_str_dup(s);
|
|
if (!str) {
|
|
return false;
|
|
}
|
|
|
|
int len = rz_str_unescape(str);
|
|
if (len < 1) {
|
|
goto str_err;
|
|
}
|
|
|
|
len++; // Consider for the terminator char
|
|
char *tmp = RZ_NEWS(char, len * 2);
|
|
if (!tmp) {
|
|
goto str_err;
|
|
}
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
tmp[i * 2] = str[i];
|
|
tmp[i * 2 + 1] = 0;
|
|
}
|
|
|
|
if (!rz_core_write_at(core, addr, (const ut8 *)tmp, len * 2)) {
|
|
RZ_LOG_ERROR("Could not write wide string '%s' at %" PFMT64x "\n", s, addr);
|
|
free(str);
|
|
free(tmp);
|
|
return false;
|
|
}
|
|
res = true;
|
|
free(tmp);
|
|
str_err:
|
|
free(str);
|
|
return res;
|
|
}
|
|
/**
|
|
* \brief Write at the specified \p addr the length of the string in one byte,
|
|
* followed by the given string \p s
|
|
*
|
|
* \param core RzCore reference
|
|
* \param addr Address where to write the string
|
|
* \param s String to write. The string is unescaped, meaning that if there is `\n` it becomes 0x0a
|
|
*/
|
|
RZ_API bool rz_core_write_length_string_at(RzCore *core, ut64 addr, const char *s) {
|
|
rz_return_val_if_fail(core && s, false);
|
|
|
|
char *str = rz_str_dup(s);
|
|
if (!str) {
|
|
return false;
|
|
}
|
|
|
|
int len = rz_str_unescape(str);
|
|
ut8 ulen = (ut8)len;
|
|
if (!rz_core_write_at(core, addr, &ulen, sizeof(ulen)) ||
|
|
!rz_core_write_at(core, addr + 1, (const ut8 *)str, len)) {
|
|
RZ_LOG_ERROR("Could not write length+'%s' at %" PFMT64x "\n", s, addr);
|
|
free(str);
|
|
return false;
|
|
}
|
|
free(str);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Write a given string \p s at the specified \p addr encoded as base64.
|
|
*
|
|
* \param core RzCore reference
|
|
* \param addr Address where to write the string
|
|
* \param s String to encode as base64 and then written.
|
|
*/
|
|
RZ_API bool rz_core_write_base64_at(RzCore *core, ut64 addr, RZ_NONNULL const char *s) {
|
|
rz_return_val_if_fail(core && s, false);
|
|
|
|
bool res = false;
|
|
size_t str_len = strlen(s) + 1;
|
|
ut8 *bin_buf = malloc(str_len);
|
|
if (!bin_buf) {
|
|
return false;
|
|
}
|
|
|
|
const int bin_len = rz_hex_str2bin(s, bin_buf);
|
|
if (bin_len <= 0) {
|
|
free(bin_buf);
|
|
return false;
|
|
}
|
|
|
|
ut8 *buf = calloc(str_len + 1, 4);
|
|
if (!buf) {
|
|
free(bin_buf);
|
|
return false;
|
|
}
|
|
|
|
int len = rz_base64_encode((char *)buf, bin_buf, bin_len);
|
|
free(bin_buf);
|
|
if (len == 0) {
|
|
goto err;
|
|
}
|
|
|
|
if (!rz_core_write_at(core, addr, buf, len)) {
|
|
RZ_LOG_ERROR("Could not write base64 encoded string '%s' at %" PFMT64x "\n", s, addr);
|
|
goto err;
|
|
}
|
|
res = true;
|
|
err:
|
|
free(buf);
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* \brief Write a given base64 string \p s at the specified \p addr, decoded
|
|
*
|
|
* \param core RzCore reference
|
|
* \param addr Address where to write the string
|
|
* \param s String to decode from base64 and then written
|
|
*/
|
|
RZ_API bool rz_core_write_base64d_at(RzCore *core, ut64 addr, RZ_NONNULL const char *s) {
|
|
rz_return_val_if_fail(core && s, false);
|
|
|
|
bool res = false;
|
|
size_t str_len = strlen(s) + 1;
|
|
ut8 *buf = malloc(str_len);
|
|
int len = rz_base64_decode(buf, s, -1);
|
|
if (len < 0) {
|
|
goto err;
|
|
}
|
|
|
|
if (!rz_core_write_at(core, addr, buf, len)) {
|
|
RZ_LOG_ERROR("Could not write base64 decoded string '%s' at %" PFMT64x "\n", s, addr);
|
|
goto err;
|
|
}
|
|
res = true;
|
|
err:
|
|
free(buf);
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* \brief Write \p len random bytes at address \p addr
|
|
*
|
|
* \param core RzCore reference
|
|
* \param addr Address where to write the data
|
|
* \param len Length of the random data to write
|
|
*/
|
|
RZ_API bool rz_core_write_random_at(RzCore *core, ut64 addr, size_t len) {
|
|
rz_return_val_if_fail(core, false);
|
|
|
|
bool res = false;
|
|
ut8 *buf = malloc(len);
|
|
if (!buf) {
|
|
return false;
|
|
}
|
|
|
|
rz_num_irand();
|
|
for (int i = 0; i < len; i++) {
|
|
buf[i] = rz_num_rand32(256);
|
|
}
|
|
|
|
if (!rz_core_write_at(core, addr, buf, len)) {
|
|
RZ_LOG_ERROR("Could not write random data of length %zd at %" PFMT64x "\n", len, addr);
|
|
goto err;
|
|
}
|
|
res = true;
|
|
err:
|
|
free(buf);
|
|
return res;
|
|
}
|
|
|
|
RZ_API RzCmdStatus rz_core_io_cache_print(RzCore *core, RzCmdStateOutput *state) {
|
|
rz_return_val_if_fail(core && core->io, RZ_CMD_STATUS_ERROR);
|
|
|
|
size_t i, j = 0;
|
|
void **iter;
|
|
RzIOCache *c;
|
|
|
|
rz_pvector_foreach (&core->io->cache, iter) {
|
|
c = *iter;
|
|
const ut64 dataSize = rz_itv_size(c->itv);
|
|
switch (state->mode) {
|
|
case RZ_OUTPUT_MODE_STANDARD:
|
|
rz_cons_printf("idx=%" PFMTSZu " addr=0x%08" PFMT64x " size=%" PFMT64u " ", j, rz_itv_begin(c->itv), dataSize);
|
|
for (i = 0; i < dataSize; i++) {
|
|
rz_cons_printf("%02x", c->odata[i]);
|
|
}
|
|
rz_cons_printf(" -> ");
|
|
for (i = 0; i < dataSize; i++) {
|
|
rz_cons_printf("%02x", c->data[i]);
|
|
}
|
|
rz_cons_printf(" %s\n", c->written ? "(written)" : "(not written)");
|
|
break;
|
|
case RZ_OUTPUT_MODE_JSON:
|
|
pj_o(state->d.pj);
|
|
pj_kn(state->d.pj, "idx", j);
|
|
pj_kn(state->d.pj, "addr", rz_itv_begin(c->itv));
|
|
pj_kn(state->d.pj, "size", dataSize);
|
|
char *hex = rz_hex_bin2strdup(c->odata, dataSize);
|
|
pj_ks(state->d.pj, "before", hex);
|
|
free(hex);
|
|
hex = rz_hex_bin2strdup(c->data, dataSize);
|
|
pj_ks(state->d.pj, "after", hex);
|
|
free(hex);
|
|
pj_kb(state->d.pj, "written", c->written);
|
|
pj_end(state->d.pj);
|
|
break;
|
|
default:
|
|
rz_warn_if_reached();
|
|
break;
|
|
}
|
|
j++;
|
|
}
|
|
return RZ_CMD_STATUS_OK;
|
|
}
|
|
|
|
RZ_API RzCmdStatus rz_core_io_pcache_print(RzCore *core, RzIODesc *desc, RzCmdStateOutput *state) {
|
|
rz_return_val_if_fail(core && core->io, RZ_CMD_STATUS_ERROR);
|
|
rz_return_val_if_fail(desc, RZ_CMD_STATUS_ERROR);
|
|
|
|
RzList *caches = rz_io_desc_cache_list(desc);
|
|
RzListIter *iter;
|
|
RzIOCache *c;
|
|
|
|
rz_list_foreach (caches, iter, c) {
|
|
const int cacheSize = rz_itv_size(c->itv);
|
|
int i;
|
|
|
|
switch (state->mode) {
|
|
case RZ_OUTPUT_MODE_STANDARD:
|
|
rz_cons_printf("0x%08" PFMT64x ": %02x",
|
|
rz_itv_begin(c->itv), c->odata[0]);
|
|
for (i = 1; i < cacheSize; i++) {
|
|
rz_cons_printf("%02x", c->odata[i]);
|
|
}
|
|
rz_cons_printf(" -> %02x", c->data[0]);
|
|
for (i = 1; i < cacheSize; i++) {
|
|
rz_cons_printf("%02x", c->data[i]);
|
|
}
|
|
rz_cons_printf("\n");
|
|
break;
|
|
default:
|
|
rz_warn_if_reached();
|
|
break;
|
|
}
|
|
}
|
|
rz_list_free(caches);
|
|
return RZ_CMD_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Write a given string \p s, followed by the zero terminator, at the specified \p addr
|
|
*
|
|
* \param core RzCore reference
|
|
* \param addr Address where to write the string
|
|
* \param s String to write. The string is unescaped, meaning that if there is `\n` it becomes 0x0a
|
|
*/
|
|
RZ_API bool rz_core_write_string_zero_at(RzCore *core, ut64 addr, const char *s) {
|
|
rz_return_val_if_fail(core && s, false);
|
|
|
|
char *str = rz_str_dup(s);
|
|
if (!str) {
|
|
return false;
|
|
}
|
|
|
|
int len = rz_str_unescape(str);
|
|
if (!rz_core_write_at(core, addr, (const ut8 *)str, len + 1)) {
|
|
RZ_LOG_ERROR("Could not write '%s' at %" PFMT64x "\n", s, addr);
|
|
free(str);
|
|
return false;
|
|
}
|
|
free(str);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Transform a block of data at \p addr according to the operation \p op and the hexvalue \p hex
|
|
*
|
|
* \param core Reference to RzCore instance
|
|
* \param addr Where the block of data to modify starts
|
|
* \param op Operation to perform on the block of data
|
|
* \param hex Optional hex string that may be required by the specific operation
|
|
* \param hexlen Optional length of the \p hex string. Must be present if \p hex is specified.
|
|
* \param buflen Used to return the length of the returned buffer
|
|
* \return The transformed buffer
|
|
*/
|
|
RZ_API RZ_OWN ut8 *rz_core_transform_op(RzCore *core, ut64 addr, RzCoreWriteOp op, RZ_NULLABLE ut8 *hex, size_t hexlen, size_t *buflen) {
|
|
rz_return_val_if_fail(core, NULL);
|
|
rz_return_val_if_fail(buflen, NULL);
|
|
|
|
switch (op) {
|
|
case RZ_CORE_WRITE_OP_ADD:
|
|
case RZ_CORE_WRITE_OP_SUB:
|
|
case RZ_CORE_WRITE_OP_DIV:
|
|
case RZ_CORE_WRITE_OP_MUL:
|
|
case RZ_CORE_WRITE_OP_AND:
|
|
case RZ_CORE_WRITE_OP_OR:
|
|
case RZ_CORE_WRITE_OP_XOR:
|
|
case RZ_CORE_WRITE_OP_SHIFT_LEFT:
|
|
case RZ_CORE_WRITE_OP_SHIFT_RIGHT:
|
|
rz_return_val_if_fail(hex, NULL);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ut8 *buf = RZ_NEWS(ut8, core->blocksize);
|
|
if (!buf) {
|
|
return NULL;
|
|
}
|
|
|
|
int len = rz_io_nread_at(core->io, addr, buf, core->blocksize);
|
|
if (len < 0) {
|
|
free(buf);
|
|
return NULL;
|
|
}
|
|
|
|
for (int i = 0, j = 0; i < len; i++, j = (j + 1) % (hexlen ? hexlen : 1)) {
|
|
ut16 tmp16;
|
|
ut32 tmp32;
|
|
ut64 tmp64;
|
|
switch (op) {
|
|
case RZ_CORE_WRITE_OP_BYTESWAP2:
|
|
if (i + 1 < len) {
|
|
tmp16 = rz_read_le16(buf + i);
|
|
rz_write_be16(buf + i, tmp16);
|
|
i++;
|
|
}
|
|
break;
|
|
case RZ_CORE_WRITE_OP_BYTESWAP4:
|
|
if (i + 3 < len) {
|
|
tmp32 = rz_read_le32(buf + i);
|
|
rz_write_be32(buf + i, tmp32);
|
|
i += 3;
|
|
}
|
|
break;
|
|
case RZ_CORE_WRITE_OP_BYTESWAP8:
|
|
if (i + 7 < len) {
|
|
tmp64 = rz_read_le64(buf + i);
|
|
rz_write_be64(buf + i, tmp64);
|
|
i += 7;
|
|
}
|
|
break;
|
|
case RZ_CORE_WRITE_OP_ADD:
|
|
buf[i] += hex[j];
|
|
break;
|
|
case RZ_CORE_WRITE_OP_SUB:
|
|
buf[i] -= hex[j];
|
|
break;
|
|
case RZ_CORE_WRITE_OP_DIV:
|
|
buf[i] = hex[j] ? buf[i] / hex[j] : 0;
|
|
break;
|
|
case RZ_CORE_WRITE_OP_MUL:
|
|
buf[i] *= hex[j];
|
|
break;
|
|
case RZ_CORE_WRITE_OP_AND:
|
|
buf[i] &= hex[j];
|
|
break;
|
|
case RZ_CORE_WRITE_OP_OR:
|
|
buf[i] |= hex[j];
|
|
break;
|
|
case RZ_CORE_WRITE_OP_XOR:
|
|
buf[i] ^= hex[j];
|
|
break;
|
|
case RZ_CORE_WRITE_OP_SHIFT_LEFT:
|
|
buf[i] <<= hex[j];
|
|
break;
|
|
case RZ_CORE_WRITE_OP_SHIFT_RIGHT:
|
|
buf[i] >>= hex[j];
|
|
break;
|
|
default:
|
|
rz_warn_if_reached();
|
|
break;
|
|
}
|
|
}
|
|
*buflen = (size_t)len;
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* \brief Write a full block of data according to the operation \p op and the hexvalue \p hex
|
|
*
|
|
* \param core Reference to RzCore instance
|
|
* \param addr Where the block of data to modify starts
|
|
* \param op Operation to perform on the block of data
|
|
* \param hex Optional hex string that may be required by the specific operation
|
|
* \param hexlen Optional length of the \p hex string. Must be present if \p hex is specified.
|
|
* \return true if the write operation succeeds, false otherwise
|
|
*/
|
|
RZ_API bool rz_core_write_block_op_at(RzCore *core, ut64 addr, RzCoreWriteOp op, RZ_NULLABLE ut8 *hex, size_t hexlen) {
|
|
size_t buflen;
|
|
ut8 *buf = rz_core_transform_op(core, addr, op, hex, hexlen, &buflen);
|
|
if (!buf) {
|
|
return false;
|
|
}
|
|
|
|
if (!rz_core_write_at(core, addr, buf, buflen)) {
|
|
RZ_LOG_ERROR("Could not write block operation at %" PFMT64x "\n", addr);
|
|
free(buf);
|
|
return false;
|
|
}
|
|
|
|
free(buf);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Write a full block of data with a sequence
|
|
*
|
|
* Write a full block of data with a sequence of numbers starting from \p from
|
|
* up to \p to, with a step of \p step. The values are written as numbers of
|
|
* \p value_size bytes.
|
|
*
|
|
* \param core Reference to RzCore instance
|
|
* \param addr Where the block of data to modify starts
|
|
* \param from From where to start the sequence of numbers
|
|
* \param to Where to stop in the sequence
|
|
* \param step Difference between two numbers in the sequence
|
|
* \param value_size Size of each number of the sequence, in bytes
|
|
* \return true if the write operation succeeds, false otherwise
|
|
*/
|
|
RZ_API bool rz_core_write_seq_at(RzCore *core, ut64 addr, ut64 from, ut64 to, ut64 step, int value_size) {
|
|
rz_return_val_if_fail(core, false);
|
|
rz_return_val_if_fail(value_size == 1 || value_size == 2 || value_size == 4 || value_size == 8, false);
|
|
ut64 max_val = (1ULL << (8 * value_size));
|
|
rz_return_val_if_fail(from < max_val, false);
|
|
rz_return_val_if_fail(to < max_val, false);
|
|
|
|
ut8 *buf = RZ_NEWS0(ut8, core->blocksize);
|
|
if (!buf) {
|
|
return false;
|
|
}
|
|
|
|
ut64 diff = to <= from ? max_val : to - from + 1;
|
|
ut64 p = from;
|
|
for (size_t i = 0; i < core->blocksize; i += value_size, p = (from + ((p + step - from) % diff)) % max_val) {
|
|
rz_write_ble(buf + i, p, rz_config_get_b(core->config, "cfg.bigendian"), value_size * 8);
|
|
}
|
|
|
|
if (!rz_core_write_at(core, addr, buf, core->blocksize)) {
|
|
RZ_LOG_ERROR("Could not write sequence [%" PFMT64d ", %" PFMT64d "] step=%" PFMT64d ",value_size=%d at %" PFMT64x "\n", from, to, step, value_size, addr);
|
|
free(buf);
|
|
return false;
|
|
}
|
|
|
|
free(buf);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Copy \p len bytes from \p from to \p addr
|
|
*
|
|
* \param core Reference to RzCore instance
|
|
* \param addr Where the data should be copied to
|
|
* \param from Where the data should be read from
|
|
* \param len Number of bytes to copy, expected to not be negative
|
|
* \return true if the write operation succeeds, false otherwise
|
|
*/
|
|
RZ_API bool rz_core_write_duplicate_at(RzCore *core, ut64 addr, ut64 from, int len) {
|
|
rz_return_val_if_fail(core, false);
|
|
rz_return_val_if_fail(len >= 0, false);
|
|
|
|
bool res = false;
|
|
ut8 *data = RZ_NEWS(ut8, len);
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
|
|
int n = rz_io_nread_at(core->io, from, data, len);
|
|
if (n < 0) {
|
|
RZ_LOG_ERROR("Cannot read data from %" PFMT64x ".\n", from);
|
|
goto err;
|
|
}
|
|
if (!rz_core_write_at(core, addr, data, n)) {
|
|
RZ_LOG_ERROR("Cannot write %d bytes to %" PFMT64x ".\n", n, addr);
|
|
goto err;
|
|
}
|
|
res = true;
|
|
err:
|
|
free(data);
|
|
return res;
|
|
}
|