rizin/librz/core/cmp.c
Giovanni 2f97762b09
librz/core: Remove rizin command output (#5540)
* Remove RIZIN command output for the old projects

* Fix cmeta.c

* Fix ik command

* Revert back pc*

* fix formatting cmd_descs_generate.py

* Fix rz_cons_pal_list_as_css

* Fill all info about colors for ec commands.

* Fix all tests
2025-11-28 23:41:47 +08:00

473 lines
13 KiB
C

// SPDX-FileCopyrightText: 2021 Dhruv Maroo <dhruvmaru007@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_cmp.h>
#include <rz_asm.h>
#include <rz_list.h>
/**
* \brief Compare memory at \p addr1 with the memory at \p addr2
*
* \param core Current RzCore instance
* \param addr1 address to read data from
* \param addr2 address to read data from
* \param len Number of bytes to compare
* \return RzCompareData* A pointer to RzCompareData comparison
*/
RZ_API RZ_OWN RzCompareData *rz_core_cmp_mem_mem(RzCore *core, ut64 addr1, ut64 addr2, ut32 len) {
rz_return_val_if_fail(core, NULL);
ut8 *buf1 = malloc(len * sizeof(ut8));
ut8 *buf2 = malloc(len * sizeof(ut8));
if (!buf1 || !buf2) {
goto error_goto;
}
if (!(rz_io_nread_at(core->io, addr1, buf1, len) == len && rz_io_nread_at(core->io, addr2, buf2, len) == len)) {
RZ_LOG_ERROR("Cannot read %u bytes at provided addresses: 0x%" PFMT64x " 0x%" PFMT64x "\n", len, addr1, addr2);
goto error_goto;
}
RzCompareData *cmp = RZ_NEW0(RzCompareData);
if (!cmp) {
goto error_goto;
}
cmp->len = len;
cmp->data1 = buf1;
cmp->addr1 = addr1;
cmp->data2 = buf2;
cmp->addr2 = addr2;
cmp->same = rz_mem_eq(cmp->data1, cmp->data2, len);
return cmp;
error_goto:
free(buf1);
free(buf2);
return NULL;
}
/**
* \brief Compare mem at \p addr with data \p data
*
* \param core Current RzCore instance
* \param addr address to read data from
* \param data Data to be compared
* \param len Number of bytes to compare
* \return RzCompareData* A pointer to RzCompareData comparison (the data1 corresponds to the data at addr (and addr1 = addr) and data2 is the str data (and addr2 = UT32_MAX))
*/
RZ_API RZ_OWN RzCompareData *rz_core_cmp_mem_data(RzCore *core, ut64 addr, RZ_NONNULL const ut8 *data, ut32 len) {
rz_return_val_if_fail(core && data, NULL);
ut8 *buf1 = malloc(len * sizeof(ut8));
if (!buf1) {
RZ_LOG_ERROR("Cannot read at address: 0x%" PFMT64x "\n", addr);
goto error_goto;
}
if (rz_io_nread_at(core->io, addr, buf1, len) != len) {
RZ_LOG_ERROR("Cannot read %u bytes at provided address: 0x%" PFMT64x "\n", len, addr);
goto error_goto;
}
RzCompareData *cmp = RZ_NEW0(RzCompareData);
if (!cmp) {
goto error_goto;
}
cmp->len = len;
cmp->data1 = buf1;
cmp->addr1 = addr;
cmp->data2 = rz_mem_dup(data, len);
cmp->addr2 = UT64_MAX;
cmp->same = rz_mem_eq(cmp->data1, cmp->data2, len);
return cmp;
error_goto:
free(buf1);
return NULL;
}
/**
* \brief Print a comparison \p cmp according to the print mode \p mode
*
* \param core Current RzCore instance
* \param cmp RzCompareData instance to be printed
* \param state RzCmdStateOutput to be used (options: standard, json) (NULL means standard)
* \return int Number of lines/diffs printed (-1 if failed)
*/
RZ_API int rz_core_cmp_print(RzCore *core, RZ_NONNULL const RzCompareData *cmp, RzCmdStateOutput *state) {
rz_return_val_if_fail(core && cmp, -1);
int i, eq = 0;
bool data_str = cmp->addr2 == UT64_MAX;
if (cmp->len == UT8_MAX) {
return -1;
}
RzOutputMode mode = RZ_OUTPUT_MODE_STANDARD;
PJ *pj = NULL;
if (state) {
mode = state->mode;
pj = state->d.pj;
}
if (mode == RZ_OUTPUT_MODE_JSON) {
pj_o(pj);
pj_k(pj, "diff_bytes");
pj_a(pj);
}
for (i = 0; i < cmp->len; i++) {
if (cmp->data1[i] == cmp->data2[i]) {
eq++;
continue;
}
switch (mode) {
case RZ_OUTPUT_MODE_STANDARD:
rz_cons_printf("0x%08" PFMT64x, cmp->addr1 + i);
if (!data_str) {
rz_cons_printf(" -> 0x%08" PFMT64x, cmp->addr2 + i);
}
rz_cons_printf(" (byte=%.2d) %02x '%c' -> %02x '%c'\n", i + 1,
cmp->data1[i], (IS_PRINTABLE(cmp->data1[i])) ? cmp->data1[i] : ' ',
cmp->data2[i], (IS_PRINTABLE(cmp->data2[i])) ? cmp->data2[i] : ' ');
break;
case RZ_OUTPUT_MODE_JSON:
pj_o(pj);
pj_kn(pj, "offset1", cmp->addr1 + i);
pj_kn(pj, "offset2", data_str ? i : cmp->addr2 + i);
pj_ki(pj, "rel_offset", i);
pj_ki(pj, "value1", (int)cmp->data1[i]);
pj_ki(pj, "value2", (int)cmp->data2[i]);
pj_end(pj);
break;
default:
rz_warn_if_reached();
}
}
if (mode == RZ_OUTPUT_MODE_STANDARD) {
rz_cons_printf("Compare %d/%d equal bytes (%d%%)\n", eq, cmp->len, (int)(100.0 * eq / cmp->len));
} else if (mode == RZ_OUTPUT_MODE_JSON) {
pj_end(pj);
pj_ki(pj, "equal_bytes", eq);
pj_ki(pj, "total_bytes", cmp->len);
pj_end(pj); // End array
pj_end(pj); // End object
}
return cmp->len - eq;
}
/**
* \brief Compare the instructions at \p addr1 and \p addr2
*
* \param core Current RzCore instance
* \param addr1 address to read instructions from
* \param addr2 address to read instructions from
* \param len number of bytes to read instructions from
* \return RzList<RzCompareData>* List of comparison data
*/
RZ_API RZ_OWN RzList /*<RzCompareData *>*/ *rz_core_cmp_disasm(RzCore *core, ut64 addr1, ut64 addr2, ut32 len) {
rz_return_val_if_fail(core, NULL);
RzList *cmp_list = rz_list_new();
if (!cmp_list) {
goto error_goto;
}
cmp_list->free = (RzListFree)&rz_core_cmp_free;
RzAsmOp op = { 0 }, op2 = { 0 };
int i, j;
ut8 *buf = calloc(len + 32, 1);
if (!buf) {
goto error_goto;
}
rz_io_nread_at(core->io, addr2, buf, len + 32);
RzCompareData *comp;
for (i = j = 0; i < len && j < len;) {
comp = RZ_NEW0(RzCompareData);
if (!comp) {
continue;
}
// dis A
rz_asm_set_pc(core->rasm, addr1 + i);
(void)rz_asm_disassemble(core->rasm, &op,
core->block + i, len - i);
// dis B
rz_asm_set_pc(core->rasm, addr2 + i);
(void)rz_asm_disassemble(core->rasm, &op2,
buf + j, len - j);
comp->len = UT8_MAX;
comp->data1 = (ut8 *)rz_str_dup(rz_strbuf_get(&op.buf_asm));
comp->addr1 = addr1 + i;
comp->data2 = (ut8 *)rz_str_dup(rz_strbuf_get(&op2.buf_asm));
comp->addr2 = addr2 + j;
comp->same = !strcmp((char *)comp->data1, (char *)comp->data2); // we can assume that instructions can be represented as plain chars
rz_list_append(cmp_list, comp);
if (op.size < 1) {
op.size = 1;
}
i += op.size;
if (op2.size < 1) {
op2.size = 1;
}
j += op2.size;
rz_asm_op_fini(&op);
rz_asm_op_fini(&op2);
}
free(buf);
return cmp_list;
error_goto:
rz_list_free(cmp_list);
return NULL;
}
/**
* \brief Free RzCompareData object
*
* \param cmp RzCompareData object to be freed
* \return void
*/
RZ_API void rz_core_cmp_free(RzCompareData *cmp) {
if (!cmp) {
return;
}
RZ_FREE(cmp->data1);
RZ_FREE(cmp->data2);
free(cmp);
}
/**
* \brief Print the instruction comparison data \p compare
*
* \param core Current RzCore instance
* \param compare list of RzCompareData of instructions
* \param unified print in unified form
* \return bool true if successfull
*/
RZ_API bool rz_core_cmp_disasm_print(RzCore *core, const RzList /*<RzCompareData *>*/ *compare, bool unified) {
rz_return_val_if_fail(core && compare, false);
char colpad[80];
int hascolor = rz_config_get_i(core->config, "scr.color");
int cols = rz_config_get_i(core->config, "hex.cols") * 2;
RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal;
RzListIter *it;
RzCompareData *cmp;
if (unified) {
rz_list_foreach (compare, it, cmp) {
if (cmp->same) {
rz_cons_printf(" 0x%08" PFMT64x " %s\n",
cmp->addr1, cmp->data1);
} else {
if (hascolor) {
rz_cons_print(pal->graph_false);
}
rz_cons_printf("-0x%08" PFMT64x " %s\n",
cmp->addr1, cmp->data1);
if (hascolor) {
rz_cons_print(pal->graph_true);
}
rz_cons_printf("+0x%08" PFMT64x " %s\n",
cmp->addr2, cmp->data2);
if (hascolor) {
rz_cons_print(Color_RESET);
}
}
}
} else {
rz_list_foreach (compare, it, cmp) {
memset(colpad, ' ', sizeof(colpad));
int pos = strlen((char *)cmp->data1);
pos = (pos > cols) ? 0 : cols - pos;
colpad[pos] = 0;
if (hascolor) {
rz_cons_print(cmp->same ? pal->graph_true : pal->graph_false);
}
rz_cons_printf(" 0x%08" PFMT64x " %s %s",
cmp->addr1, cmp->data1, colpad);
rz_cons_printf("%c 0x%08" PFMT64x " %s\n",
cmp->same ? '=' : '!', cmp->addr2, cmp->data2);
if (hascolor) {
rz_cons_print(Color_RESET);
}
}
}
return true;
}
/**
* \defgroup cmpwatch Memory watcher functions API
* API for memory watcher functions
* \{
*/
RZ_API void rz_core_cmpwatch_free(RzCoreCmpWatcher *w) {
if (!w) {
return;
}
free(w->ndata);
free(w->odata);
free(w);
}
/**
* \brief Get the memory watcher at address \p addr
*
* \param core Current RzCore instance
* \param addr Expected address for the memory watcher to be found
* \return RzCoreCmpWatcher* Pointer to the found memory watcher; NULL if not found
*/
RZ_API RzCoreCmpWatcher *rz_core_cmpwatch_get(RzCore *core, ut64 addr) {
rz_return_val_if_fail(core, NULL);
RzListIter *iter;
RzCoreCmpWatcher *w;
rz_list_foreach (core->watchers, iter, w) {
if (addr == w->addr) {
return w;
}
}
return NULL;
}
/**
* \brief Add a memory watcher at address \p addr od size \p size and command \p cmd
*
* \param core Current RzCore instance
* \param addr Address where to add the new memory watcher
* \param size Size of the memory watcher to be addded
* \param cmd Command to be associated with the memory watcher
* \return bool true if successful, false otherwise
*/
RZ_API bool rz_core_cmpwatch_add(RzCore *core, ut64 addr, int size, const char *cmd) {
rz_return_val_if_fail(core, false);
RzCoreCmpWatcher *cmpw;
bool to_add = false;
if (size < 1) {
return false;
}
cmpw = rz_core_cmpwatch_get(core, addr);
if (!cmpw) {
to_add = true;
cmpw = RZ_NEW(RzCoreCmpWatcher);
if (!cmpw) {
return false;
}
cmpw->addr = addr;
}
cmpw->size = size;
snprintf(cmpw->cmd, sizeof(cmpw->cmd), "%s", cmd);
cmpw->odata = NULL;
cmpw->ndata = malloc(size);
if (!cmpw->ndata) {
free(cmpw);
return false;
}
rz_io_nread_at(core->io, addr, cmpw->ndata, size);
if (to_add) {
rz_list_append(core->watchers, cmpw);
}
return true;
}
/**
* \brief Delete a memory watcher at address \p addr
*
* \param core Current RzCore instance
* \param addr Address of the memory watcher to be deleted (if UT64_MAX, then all memory watchers will be deleted)
* \return bool true if found and deleted; false otherwise
*/
RZ_API bool rz_core_cmpwatch_del(RzCore *core, ut64 addr) {
rz_return_val_if_fail(core, false);
int ret = false;
RzCoreCmpWatcher *w;
RzListIter *iter, *iter2;
rz_list_foreach_safe (core->watchers, iter, iter2, w) {
if (w->addr == addr || addr == UT64_MAX) {
rz_list_delete(core->watchers, iter);
ret = true;
}
}
return ret;
}
/**
* \brief Show/print the memory watcher present at address \p addr
*
* \param core Current RzCore instance
* \param addr Address of the memory watcher to be printed (if UT64_MAX, then all memory watchers will be printed)
* \param mode Output mode
* \return void Print nothing if no memory watcher found at \p addr
*/
RZ_API void rz_core_cmpwatch_show(RzCore *core, ut64 addr, RzOutputMode mode) {
rz_return_if_fail(core);
char cmd[128];
RzListIter *iter;
RzCoreCmpWatcher *w;
rz_list_foreach (core->watchers, iter, w) {
if (addr != UT64_MAX && w->addr != addr) {
continue;
}
int is_diff = w->odata ? memcmp(w->odata, w->ndata, w->size) : 0;
switch (mode) {
case RZ_OUTPUT_MODE_STANDARD:
rz_cons_printf("0x%08" PFMT64x "%s\n", w->addr, is_diff ? " modified" : "");
snprintf(cmd, sizeof(cmd), "%s @ %" PFMT64d " @!%d", w->cmd, w->addr, w->size);
rz_core_cmd0(core, cmd);
break;
default:
rz_warn_if_reached();
}
}
}
/**
* \brief Update the memory watcher at address \p addr
*
* \param core Current RzCore instance
* \param addr Address of the memory watcher to be updated (if UT64_MAX, then all memory watchers will be updated)
* \return bool true if any memory watcher was updated; false otherwise
*/
RZ_API bool rz_core_cmpwatch_update(RzCore *core, ut64 addr) {
rz_return_val_if_fail(core, false);
RzCoreCmpWatcher *w;
RzListIter *iter;
bool ret = false;
rz_list_foreach (core->watchers, iter, w) {
if (addr != UT64_MAX && w->addr != addr) {
continue;
}
free(w->odata);
w->odata = w->ndata;
w->ndata = malloc(w->size);
if (!w->ndata) {
return false;
}
rz_io_nread_at(core->io, w->addr, w->ndata, w->size);
ret = true;
}
return ret;
}
/**
* \brief Revert/reset a memory watcher at address \p addr
*
* \param core Current RzCore instance
* \param addr Address of the memory watcher to be reset (if UT64_MAX, then all memory watchers will be reset)
* \return bool true if the memory watcher was resetted; false otherwise
*/
RZ_API bool rz_core_cmpwatch_revert(RzCore *core, ut64 addr) {
rz_return_val_if_fail(core, false);
RzCoreCmpWatcher *w;
int ret = false;
RzListIter *iter;
rz_list_foreach (core->watchers, iter, w) {
if (w->addr == addr || addr == UT64_MAX) {
if (w->odata) {
free(w->ndata);
w->ndata = w->odata;
w->odata = NULL;
ret = true;
}
}
}
return ret;
}
/** \} */