696 lines
18 KiB
C
696 lines
18 KiB
C
// SPDX-FileCopyrightText: 2022 RizinOrg <info@rizin.re>
|
|
// SPDX-FileCopyrightText: 2022 deroad <wargio@libero.it>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
/** \file debug_bochs.c
|
|
* Implements the debugger support using the io_bochs plugin.
|
|
*
|
|
* Website: https://bochs.sourceforge.io/
|
|
*
|
|
* Bochs is a highly portable open source IA-32 (x86) PC emulator written in C++, that runs on most popular
|
|
* platforms. It includes emulation of the Intel x86 CPU, common I/O devices, and a custom BIOS.
|
|
*
|
|
* This plugin uses the spawned process used by io_bochs plugin to set/unset breakpoints, to get and set the
|
|
* registers of the CPU.
|
|
*
|
|
* The plugin uses the following commands to handle the debugger commands from rizin:
|
|
*
|
|
* - `break <addr>` is used to set the breakpoint at a given address.
|
|
*
|
|
* - `step` is used to set execute one instruction and break again.
|
|
*
|
|
* - `continue` is used to continue the execution till `CTRL+C` is pressed by the user.
|
|
*
|
|
* - `info break` is used to fetch all the breakpoint indexes; each time a breakpoint is created, the bochs
|
|
* emulator assigns an index, which is then required as input to remove it. An example of output can be
|
|
* seen below:
|
|
*
|
|
* <bochs:4> info break
|
|
* Num Type Disp Enb Address
|
|
* 1 pbreakpoint keep y 0x000000012345
|
|
*
|
|
* - `delete <breakpoint index>` is used to remove a specific breakpoint using the index provided by
|
|
* the `info break` command.
|
|
*
|
|
* - `regs` is used to fetch the x86 general purpose registers; bochs always uses the 64 bits registers in
|
|
* the output even when in real mode. It has to be mentioned that the `rip` register is never represented
|
|
* as `(16 * CS) + IP` even though the emulator uses this for the internal program counter. The plugin
|
|
* automatically converts this to the proper value and assigns it to the RzReg arena. An example of the
|
|
* output can be seen below:
|
|
*
|
|
* <bochs:4> regs
|
|
* CPU0:
|
|
* rax: 00000000_00000000
|
|
* rbx: 00000000_00000000
|
|
* rcx: 00000000_00000000
|
|
* [...]
|
|
* r15: 00000000_00000000
|
|
* rip: 00000000_0000fff0
|
|
* eflags 0x00000002: id vip vif ac vm rf nt IOPL=0 of df if tf sf zf af pf cf
|
|
*
|
|
* - `sreg` is used to fetch the x86 segment registers. An example of the output can be seen below:
|
|
*
|
|
* <bochs:4> sreg
|
|
* es:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
|
|
* Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
|
|
* cs:0xf000, dh=0xff0093ff, dl=0x0000ffff, valid=7
|
|
* Data segment, base=0xffff0000, limit=0x0000ffff, Read/Write, Accessed
|
|
* ss:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
|
|
* Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
|
|
* ds:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
|
|
* Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
|
|
* fs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
|
|
* Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
|
|
* gs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
|
|
* Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
|
|
* ldtr:0x0000, dh=0x00008200, dl=0x0000ffff, valid=1
|
|
* tr:0x0000, dh=0x00008b00, dl=0x0000ffff, valid=1
|
|
* gdtr:base=0x0000000000000000, limit=0xffff
|
|
* idtr:base=0x0000000000000000, limit=0xffff
|
|
*
|
|
* - `dreg` is used to fetch the x86 debug registers. An example of the output can be seen below:
|
|
*
|
|
* <bochs:4> dreg
|
|
* DR0=0x0000000000000000
|
|
* DR1=0x0000000000000000
|
|
* DR2=0x0000000000000000
|
|
* DR3=0x0000000000000000
|
|
* DR6=0xffff0ff0: bt bs bd b3 b2 b1 b0
|
|
* DR7=0x00000400: DR3=Code-Byte DR2=Code-Byte DR1=Code-Byte DR0=Code-Byte gd | ge le | g3 l3 g2 l2 g1 l1 g0 l0
|
|
*
|
|
* - `set <reg name> = <value>` is used to set the internal registers to any value specified by the user.
|
|
*
|
|
* - `info tab` is used to get the emulator page tables, unfortunately this command when executed at the beginning
|
|
* will always output `paging off`, but when then the tables are set, the plugin can update the tables using the
|
|
* new output which contains virtual address mapping to physical addresses. An example of the output can be seen
|
|
* below:
|
|
*
|
|
* <bochs:4> info tab
|
|
* cr3: 0x000000101000
|
|
* 0x00000000c0000000-0x00000000c1ffffff -> 0x000000000000-0x000001ffffff
|
|
* 0x00000000ffffd000-0x00000000ffffdfff -> 0x0000fec00000-0x0000fec00fff
|
|
* 0x00000000ffffe000-0x00000000ffffefff -> 0x0000fee00000-0x0000fee00fff
|
|
*/
|
|
|
|
#include <rz_debug.h>
|
|
#include <rz_util.h>
|
|
|
|
#define BOCHS_STDIN_SIZE 256
|
|
|
|
static void debug_bochs_wait_till_prompt(RzSubprocess *bochs) {
|
|
RzStrBuf *sb = NULL;
|
|
while ((sb = rz_subprocess_stdout_readline(bochs, 5))) {
|
|
const char *line = rz_strbuf_get(sb);
|
|
if (strstr(line, "<bochs:")) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *debug_bochs_send_command(RzSubprocess *bochs, bool wait_output, const char *format, ...) {
|
|
char command[BOCHS_STDIN_SIZE] = { 0 };
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
vsnprintf(command, BOCHS_STDIN_SIZE, format, args);
|
|
va_end(args);
|
|
|
|
// send command
|
|
rz_subprocess_stdin_write(bochs, (ut8 *)command, strlen(command));
|
|
|
|
if (!wait_output) {
|
|
debug_bochs_wait_till_prompt(bochs);
|
|
return NULL;
|
|
}
|
|
// wait for output
|
|
|
|
RzStrBuf *output = rz_strbuf_new("");
|
|
if (!output) {
|
|
return NULL;
|
|
}
|
|
|
|
RzStrBuf *sb = NULL;
|
|
while ((sb = rz_subprocess_stdout_readline(bochs, 5))) {
|
|
const char *line = rz_strbuf_get(sb);
|
|
if (strstr(line, "<bochs:")) {
|
|
break;
|
|
}
|
|
rz_strbuf_append_n(output, line, sb->len);
|
|
}
|
|
|
|
return rz_strbuf_drain(output);
|
|
}
|
|
|
|
static bool bochs_is_io(RzDebug *dbg) {
|
|
if (!dbg->iob.io || !dbg->iob.io->desc) {
|
|
return false;
|
|
}
|
|
RzIODesc *d = dbg->iob.io->desc;
|
|
return d->plugin && d->plugin->name && !strcmp("bochs", d->plugin->name);
|
|
}
|
|
|
|
static int bochs_find_breakpoint_index(RzSubprocess *bochs, ut64 address) {
|
|
char *output = debug_bochs_send_command(bochs, true, "info break\n");
|
|
if (!output) {
|
|
RZ_LOG_ERROR("io: bochs: Failed to get breakpoints.\n");
|
|
return -1;
|
|
}
|
|
|
|
char hexaddr[128];
|
|
rz_strf(hexaddr, "0x%012" PFMT64x, address);
|
|
char *match = strstr(output, hexaddr);
|
|
if (!match) {
|
|
free(output);
|
|
return -1;
|
|
}
|
|
|
|
char *line = output;
|
|
char *end = NULL;
|
|
while (RZ_STR_ISNOTEMPTY(line)) {
|
|
if ((end = strchr(line, '\n'))) {
|
|
*end = 0;
|
|
end++;
|
|
}
|
|
|
|
if (match < end) {
|
|
break;
|
|
}
|
|
line = end;
|
|
}
|
|
|
|
ut64 index = rz_num_math(NULL, line);
|
|
free(output);
|
|
return index;
|
|
}
|
|
|
|
static int bochs_breakpoint(RzBreakpoint *bp, RzBreakpointItem *b, bool set) {
|
|
RzDebug *dbg = bp->user;
|
|
if (!dbg) {
|
|
return false;
|
|
}
|
|
|
|
RzIODesc *fd = dbg->iob.io->desc;
|
|
RzSubprocess *bochs = fd ? fd->data : NULL;
|
|
if (!bochs) {
|
|
return false;
|
|
}
|
|
|
|
if (set) {
|
|
debug_bochs_send_command(bochs, false, "break 0x%" PFMT64x "\n", b->addr);
|
|
return bochs_find_breakpoint_index(bochs, b->addr) > -1;
|
|
}
|
|
|
|
int index = bochs_find_breakpoint_index(bochs, b->addr);
|
|
if (index < 0) {
|
|
return false;
|
|
}
|
|
debug_bochs_send_command(bochs, false, "delete %d\n", index);
|
|
return true;
|
|
}
|
|
|
|
static bool bochs_sync_profile(RzSubprocess *bochs, RzReg *reg) {
|
|
ut8 regbuf[32] = { 0 };
|
|
/*
|
|
example of output:
|
|
CPU0:
|
|
rax: 00000000_00000000
|
|
rbx: 00000000_00000000
|
|
rcx: 00000000_00000000
|
|
rdx: 00000000_00000000
|
|
rsp: 00000000_00000000
|
|
rbp: 00000000_00000000
|
|
rsi: 00000000_00000000
|
|
rdi: 00000000_00000000
|
|
r8 : 00000000_00000000
|
|
r9 : 00000000_00000000
|
|
r10: 00000000_00000000
|
|
r11: 00000000_00000000
|
|
r12: 00000000_00000000
|
|
r13: 00000000_00000000
|
|
r14: 00000000_00000000
|
|
r15: 00000000_00000000
|
|
rip: 00000000_0000fff0
|
|
eflags 0x00000002: id vip vif ac vm rf nt IOPL=0 of df if tf sf zf af pf cf
|
|
*/
|
|
char *raw = debug_bochs_send_command(bochs, true, "regs\n");
|
|
char *output = raw ? strstr(raw, "CPU0:") : NULL;
|
|
if (!output) {
|
|
free(raw);
|
|
RZ_LOG_ERROR("io: bochs: Failed to get registers.\n");
|
|
return false;
|
|
}
|
|
|
|
ut64 csip_reg = 0;
|
|
|
|
char *line = output + 5;
|
|
while (RZ_STR_ISNOTEMPTY(line)) {
|
|
if (line[3] == ':') {
|
|
char *underscore = strchr(line, '_');
|
|
if (underscore) {
|
|
*underscore = ' ';
|
|
}
|
|
rz_hex_str2bin(line + 4, regbuf);
|
|
ut64 number = rz_read_be64(regbuf);
|
|
|
|
if (line[2] == ' ') {
|
|
line[2] = 0;
|
|
}
|
|
line[3] = 0;
|
|
if (!strcmp(line, "rip")) {
|
|
// it is always cs:ip, not a real rip
|
|
csip_reg = number & UT16_MAX;
|
|
} else {
|
|
RzRegItem *item = rz_reg_get(reg, line, -1);
|
|
if (item) {
|
|
rz_reg_set_value(reg, item, number);
|
|
} else {
|
|
RZ_LOG_ERROR("io: bochs: Failed to find register %s.\n", line);
|
|
}
|
|
}
|
|
line[3] = ':';
|
|
if (!line[2]) {
|
|
line[2] = ' ';
|
|
}
|
|
} else if (!strncmp(line, "eflags ", strlen("eflags "))) {
|
|
rz_hex_str2bin(line + 7, regbuf);
|
|
ut64 number = rz_read_be32(regbuf);
|
|
RzRegItem *item = rz_reg_get(reg, "eflags", -1);
|
|
rz_reg_set_value(reg, item, number);
|
|
}
|
|
if ((line = strchr(line, '\n'))) {
|
|
line++;
|
|
}
|
|
}
|
|
|
|
free(raw);
|
|
|
|
/*
|
|
example of output:
|
|
es:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
|
|
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
|
|
cs:0xf000, dh=0xff0093ff, dl=0x0000ffff, valid=7
|
|
Data segment, base=0xffff0000, limit=0x0000ffff, Read/Write, Accessed
|
|
ss:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
|
|
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
|
|
ds:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
|
|
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
|
|
fs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
|
|
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
|
|
gs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
|
|
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
|
|
ldtr:0x0000, dh=0x00008200, dl=0x0000ffff, valid=1
|
|
tr:0x0000, dh=0x00008b00, dl=0x0000ffff, valid=1
|
|
gdtr:base=0x0000000000000000, limit=0xffff
|
|
idtr:base=0x0000000000000000, limit=0xffff
|
|
*/
|
|
raw = debug_bochs_send_command(bochs, true, "sreg\n");
|
|
if (!raw) {
|
|
RZ_LOG_ERROR("io: bochs: Failed to get SEG registers.\n");
|
|
return false;
|
|
}
|
|
|
|
line = raw;
|
|
while (RZ_STR_ISNOTEMPTY(line)) {
|
|
if (line[2] == ':' && line[9] == ',') {
|
|
|
|
line[9] = 0; // comma
|
|
line[2] = 0; // colon
|
|
RzRegItem *item = rz_reg_get(reg, line, -1);
|
|
if (item) {
|
|
rz_hex_str2bin(line + 3, regbuf);
|
|
ut64 number = rz_read_be16(regbuf);
|
|
rz_reg_set_value(reg, item, number);
|
|
if (!strcmp(line, "cs")) {
|
|
// csip = (16 * CS) + IP;
|
|
csip_reg += (number << 4);
|
|
}
|
|
}
|
|
line[2] = ':';
|
|
line[9] = ',';
|
|
}
|
|
if ((line = strchr(line, '\n'))) {
|
|
line++;
|
|
}
|
|
}
|
|
free(raw);
|
|
|
|
rz_reg_set_value(reg, rz_reg_get(reg, "rip", -1), csip_reg);
|
|
|
|
/*
|
|
example of output:
|
|
DR0=0x0000000000000000
|
|
DR1=0x0000000000000000
|
|
DR2=0x0000000000000000
|
|
DR3=0x0000000000000000
|
|
DR6=0xffff0ff0: bt bs bd b3 b2 b1 b0
|
|
DR7=0x00000400: DR3=Code-Byte DR2=Code-Byte DR1=Code-Byte DR0=Code-Byte gd | ge le | g3 l3 g2 l2 g1 l1 g0 l0
|
|
*/
|
|
raw = debug_bochs_send_command(bochs, true, "dreg\n");
|
|
if (!raw) {
|
|
RZ_LOG_ERROR("io: bochs: Failed to get DRX registers.\n");
|
|
return false;
|
|
}
|
|
|
|
line = raw;
|
|
while (RZ_STR_ISNOTEMPTY(line)) {
|
|
if (line[0] == 'D' && line[1] == 'R' && line[3] == '=') {
|
|
char *colon = strchr(line, ':');
|
|
if (colon) {
|
|
*colon = 0;
|
|
}
|
|
line[0] = 'd';
|
|
line[1] = 'r';
|
|
line[3] = 0; // equal
|
|
RzRegItem *item = rz_reg_get(reg, line, -1);
|
|
if (item) {
|
|
rz_hex_str2bin(line + 3, regbuf);
|
|
ut64 number = rz_read_be16(regbuf);
|
|
rz_reg_set_value(reg, item, number);
|
|
}
|
|
line[3] = '=';
|
|
if (colon) {
|
|
*colon = ':';
|
|
}
|
|
}
|
|
if ((line = strchr(line, '\n'))) {
|
|
line++;
|
|
}
|
|
}
|
|
|
|
free(raw);
|
|
return true;
|
|
}
|
|
|
|
static const char *bochs_updatable_registers[] = {
|
|
"es", "cs", "ss", "ds", "fs", "gs",
|
|
"dr0", "dr1", "dr2", "dr3", "dr6", "dr7",
|
|
"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "rip",
|
|
"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d", "eip"
|
|
};
|
|
|
|
static bool bochs_sync_debugger(RzSubprocess *bochs, RzReg *reg) {
|
|
for (size_t i = 0; i < RZ_ARRAY_SIZE(bochs_updatable_registers); ++i) {
|
|
const char *name = bochs_updatable_registers[i];
|
|
RzRegItem *item = rz_reg_get(reg, name, -1);
|
|
if (!item) {
|
|
continue;
|
|
}
|
|
|
|
ut64 value = rz_reg_get_value(reg, item);
|
|
debug_bochs_send_command(bochs, false, "set %s = 0x%" PFMT64x "\n", name, value);
|
|
}
|
|
|
|
// ensure the output buffer is alywas empty
|
|
free(rz_subprocess_out(bochs, NULL));
|
|
return true;
|
|
}
|
|
|
|
static bool bochs_sync_registers(RzDebug *dbg, RzReg *reg, bool to_debugger) {
|
|
rz_return_val_if_fail(dbg && reg, false);
|
|
if (!bochs_is_io(dbg)) {
|
|
return false;
|
|
}
|
|
|
|
RzIODesc *fd = dbg->iob.io->desc;
|
|
RzSubprocess *bochs = fd ? fd->data : NULL;
|
|
if (!bochs) {
|
|
return false;
|
|
}
|
|
|
|
if (to_debugger) {
|
|
return bochs_sync_debugger(bochs, reg);
|
|
}
|
|
return bochs_sync_profile(bochs, reg);
|
|
}
|
|
|
|
static RzDebugMap *bochs_map_new(ut32 idx, ut64 from, ut64 to) {
|
|
RzDebugMap *map = RZ_NEW0(RzDebugMap);
|
|
if (!map) {
|
|
return NULL;
|
|
}
|
|
map->name = rz_str_newf("page_%u", idx);
|
|
map->addr = from;
|
|
map->addr_end = to;
|
|
map->size = to - from;
|
|
map->perm = RZ_PERM_RWX;
|
|
map->user = 0;
|
|
return map;
|
|
}
|
|
|
|
static RzList /*<RzDebugMap *>*/ *bochs_map_get(RzDebug *dbg) {
|
|
if (!bochs_is_io(dbg)) {
|
|
return NULL;
|
|
}
|
|
|
|
RzIODesc *fd = dbg->iob.io->desc;
|
|
RzSubprocess *bochs = fd ? fd->data : NULL;
|
|
if (!bochs) {
|
|
return NULL;
|
|
}
|
|
|
|
RzList *list = rz_list_newf((RzListFree)rz_debug_map_free);
|
|
if (!list) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
example of output without pages:
|
|
paging off
|
|
|
|
example of output with pages:
|
|
cr3: 0x000000101000
|
|
0x00000000c0000000-0x00000000c1ffffff -> 0x000000000000-0x000001ffffff
|
|
0x00000000ffffd000-0x00000000ffffdfff -> 0x0000fec00000-0x0000fec00fff
|
|
0x00000000ffffe000-0x00000000ffffefff -> 0x0000fee00000-0x0000fee00fff
|
|
|
|
we only map the virtual memory.
|
|
*/
|
|
|
|
char *output = debug_bochs_send_command(bochs, true, "info tab\n");
|
|
if (!output) {
|
|
RZ_LOG_ERROR("io: bochs: Failed to get pages.\n");
|
|
rz_list_free(list);
|
|
return NULL;
|
|
}
|
|
|
|
if (!rz_str_startswith(output, "paging off")) {
|
|
free(output);
|
|
RzDebugMap *map = bochs_map_new(0, 0, UT32_MAX);
|
|
if (!map || !rz_list_append(list, map)) {
|
|
rz_debug_map_free(map);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
ut64 from, to;
|
|
ut32 counter = 0;
|
|
char *line = output;
|
|
while (RZ_STR_ISNOTEMPTY(line)) {
|
|
if (line[0] == '0' && line[1] == 'x') {
|
|
from = to = 0;
|
|
char *dash = strchr(line, '-');
|
|
char *space = strchr(dash + 1, ' ');
|
|
if (dash) {
|
|
*dash = 0;
|
|
from = rz_num_math(NULL, line);
|
|
}
|
|
if (space) {
|
|
*space = 0;
|
|
to = rz_num_math(NULL, dash + 1);
|
|
}
|
|
|
|
RzDebugMap *map = bochs_map_new(counter, from, to);
|
|
if (!map || !rz_list_append(list, map)) {
|
|
rz_debug_map_free(map);
|
|
break;
|
|
}
|
|
counter++;
|
|
}
|
|
if ((line = strchr(line, '\n'))) {
|
|
line++;
|
|
}
|
|
}
|
|
free(output);
|
|
|
|
return list;
|
|
}
|
|
|
|
static bool bochs_step(RzDebug *dbg) {
|
|
if (!bochs_is_io(dbg)) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
RzIODesc *fd = dbg->iob.io->desc;
|
|
RzSubprocess *bochs = fd ? fd->data : NULL;
|
|
if (!bochs) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
|
|
debug_bochs_send_command(bochs, false, "step\n");
|
|
return true;
|
|
}
|
|
|
|
static int bochs_continue(RzDebug *dbg, int pid, int tid, int sig) {
|
|
if (!bochs_is_io(dbg)) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
|
|
RzIODesc *fd = dbg->iob.io->desc;
|
|
RzSubprocess *bochs = fd ? fd->data : NULL;
|
|
if (!bochs) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
|
|
debug_bochs_send_command(bochs, false, "continue\n");
|
|
return true;
|
|
}
|
|
|
|
static RzDebugReasonType bochs_wait(RzDebug *dbg, int pid) {
|
|
if (!bochs_is_io(dbg)) {
|
|
return RZ_DEBUG_REASON_ERROR;
|
|
}
|
|
|
|
return RZ_DEBUG_REASON_NONE;
|
|
}
|
|
|
|
static int bochs_stop(RzDebug *dbg) {
|
|
return true;
|
|
}
|
|
|
|
static int bochs_attach(RzDebug *dbg, int pid) {
|
|
dbg->swstep = true;
|
|
return true;
|
|
}
|
|
|
|
static int bochs_detach(RzDebug *dbg, int pid) {
|
|
dbg->swstep = true;
|
|
return true;
|
|
}
|
|
|
|
static char *bochs_reg_profile(RzDebug *dbg) {
|
|
int bits = dbg->analysis->bits;
|
|
|
|
if (bits == 16 || bits == 32 || bits == 64) {
|
|
return strdup(
|
|
"=PC rip\n"
|
|
"=SP rsp\n"
|
|
"=BP rbp\n"
|
|
"=A0 rax\n"
|
|
"=A1 rbx\n"
|
|
"=A2 rcx\n"
|
|
"=A3 rdi\n"
|
|
|
|
"seg es 2 0x038 0 \n"
|
|
"seg cs 2 0x03A 0 \n"
|
|
"seg ss 2 0x03C 0 \n"
|
|
"seg ds 2 0x03E 0 \n"
|
|
"seg fs 2 0x040 0 \n"
|
|
"seg gs 2 0x042 0 \n"
|
|
|
|
"gpr eflags 4 0x044 0 \n"
|
|
|
|
"drx dr0 8 0x048 0 \n"
|
|
"drx dr1 8 0x050 0 \n"
|
|
"drx dr2 8 0x058 0 \n"
|
|
"drx dr3 8 0x060 0 \n"
|
|
"drx dr6 8 0x068 0 \n"
|
|
"drx dr7 8 0x070 0 \n"
|
|
|
|
"gpr rax 8 0x078 0 \n"
|
|
"gpr eax 4 0x078 0 \n"
|
|
"gpr ax 2 0x078 0 \n"
|
|
"gpr al 1 0x078 0 \n"
|
|
"gpr rcx 8 0x080 0 \n"
|
|
"gpr ecx 4 0x080 0 \n"
|
|
"gpr cx 2 0x080 0 \n"
|
|
"gpr cl 1 0x078 0 \n"
|
|
"gpr rdx 8 0x088 0 \n"
|
|
"gpr edx 4 0x088 0 \n"
|
|
"gpr dx 2 0x088 0 \n"
|
|
"gpr dl 1 0x088 0 \n"
|
|
"gpr rbx 8 0x090 0 \n"
|
|
"gpr ebx 4 0x090 0 \n"
|
|
"gpr bx 2 0x090 0 \n"
|
|
"gpr bl 1 0x090 0 \n"
|
|
"gpr rsp 8 0x098 0 \n"
|
|
"gpr esp 4 0x098 0 \n"
|
|
"gpr sp 2 0x098 0 \n"
|
|
"gpr spl 1 0x098 0 \n"
|
|
"gpr rbp 8 0x0A0 0 \n"
|
|
"gpr ebp 4 0x0A0 0 \n"
|
|
"gpr bp 2 0x0A0 0 \n"
|
|
"gpr bpl 1 0x0A0 0 \n"
|
|
"gpr rsi 8 0x0A8 0 \n"
|
|
"gpr esi 4 0x0A8 0 \n"
|
|
"gpr si 2 0x0A8 0 \n"
|
|
"gpr sil 1 0x0A8 0 \n"
|
|
"gpr rdi 8 0x0B0 0 \n"
|
|
"gpr edi 4 0x0B0 0 \n"
|
|
"gpr di 2 0x0B0 0 \n"
|
|
"gpr dil 1 0x0B0 0 \n"
|
|
"gpr r8 8 0x0B8 0 \n"
|
|
"gpr r8d 4 0x0B8 0 \n"
|
|
"gpr r8w 2 0x0B8 0 \n"
|
|
"gpr r8b 1 0x0B8 0 \n"
|
|
"gpr r9 8 0x0C0 0 \n"
|
|
"gpr r9d 4 0x0C0 0 \n"
|
|
"gpr r9w 2 0x0C0 0 \n"
|
|
"gpr r9b 1 0x0C0 0 \n"
|
|
"gpr r10 8 0x0C8 0 \n"
|
|
"gpr r10d 4 0x0C8 0 \n"
|
|
"gpr r10w 2 0x0C8 0 \n"
|
|
"gpr r10b 1 0x0C8 0 \n"
|
|
"gpr r11 8 0x0D0 0 \n"
|
|
"gpr r11d 4 0x0D0 0 \n"
|
|
"gpr r11w 2 0x0D0 0 \n"
|
|
"gpr r11b 1 0x0D0 0 \n"
|
|
"gpr r12 8 0x0D8 0 \n"
|
|
"gpr r12d 4 0x0D8 0 \n"
|
|
"gpr r12w 2 0x0D8 0 \n"
|
|
"gpr r12b 1 0x0D8 0 \n"
|
|
"gpr r13 8 0x0E0 0 \n"
|
|
"gpr r13d 4 0x0E0 0 \n"
|
|
"gpr r13w 2 0x0E0 0 \n"
|
|
"gpr r13b 1 0x0E0 0 \n"
|
|
"gpr r14 8 0x0E8 0 \n"
|
|
"gpr r14d 4 0x0E8 0 \n"
|
|
"gpr r14w 2 0x0E8 0 \n"
|
|
"gpr r14b 1 0x0E8 0 \n"
|
|
"gpr r15 8 0x0F0 0 \n"
|
|
"gpr r15d 4 0x0F0 0 \n"
|
|
"gpr r15w 2 0x0F0 0 \n"
|
|
"gpr r15b 1 0x0F0 0 \n"
|
|
"gpr rip 8 0x0F8 0 \n"
|
|
"gpr eip 4 0x0F8 0 \n");
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RzDebugPlugin rz_debug_plugin_bochs = {
|
|
.name = "bochs",
|
|
.license = "LGPL3",
|
|
.arch = "x86",
|
|
.bits = RZ_SYS_BITS_16 | RZ_SYS_BITS_32 | RZ_SYS_BITS_64,
|
|
.step = bochs_step,
|
|
.cont = bochs_continue,
|
|
.attach = &bochs_attach,
|
|
.detach = &bochs_detach,
|
|
.canstep = 1,
|
|
.stop = &bochs_stop,
|
|
.wait = &bochs_wait,
|
|
.map_get = bochs_map_get,
|
|
.breakpoint = bochs_breakpoint,
|
|
.sync_registers = &bochs_sync_registers,
|
|
.reg_profile = &bochs_reg_profile,
|
|
};
|
|
|
|
#ifndef RZ_PLUGIN_INCORE
|
|
RZ_API RzLibStruct rizin_plugin = {
|
|
.type = RZ_LIB_TYPE_DBG,
|
|
.data = &rz_debug_plugin_bochs,
|
|
.version = RZ_VERSION
|
|
};
|
|
#endif
|