diff --git a/librz/include/rz_io.h b/librz/include/rz_io.h index 512885851bd..1d7fd597528 100644 --- a/librz/include/rz_io.h +++ b/librz/include/rz_io.h @@ -138,6 +138,7 @@ typedef struct rz_io_plugin_t { int (*create)(RzIO *io, const char *file, int mode, int type); bool (*check)(RzIO *io, const char *, bool many); ut8 *(*get_buf)(RzIODesc *desc, ut64 *size); + bool (*download_file)(RzIO *io, RzIODesc *fd, const char *remote, const char *local); } RzIOPlugin; typedef struct rz_io_map_t { @@ -384,6 +385,7 @@ RZ_API bool rz_io_desc_is_blockdevice(RzIODesc *desc); RZ_API bool rz_io_desc_is_chardevice(RzIODesc *desc); RZ_API bool rz_io_desc_exchange(RzIO *io, int fd, int fdx); // this should get 2 descs RZ_API bool rz_io_desc_is_dbg(RzIODesc *desc); +RZ_API bool rz_io_desc_download_file(RzIODesc *desc, const char *remote, const char *local); RZ_API int rz_io_desc_get_pid(RzIODesc *desc); RZ_API int rz_io_desc_get_tid(RzIODesc *desc); RZ_API bool rz_io_desc_get_base(RzIODesc *desc, ut64 *base); diff --git a/librz/io/io_desc.c b/librz/io/io_desc.c index 4ebe324832c..31edee4b054 100644 --- a/librz/io/io_desc.c +++ b/librz/io/io_desc.c @@ -307,6 +307,13 @@ RZ_API bool rz_io_desc_is_dbg(RzIODesc *desc) { return false; } +RZ_API bool rz_io_desc_download_file(RzIODesc *desc, const char *remote, const char *local) { + if (!desc || !desc->plugin || !desc->plugin->download_file || RZ_STR_ISEMPTY(remote) || RZ_STR_ISEMPTY(local)) { + return false; + } + return desc->plugin->download_file(desc->io, desc, remote, local); +} + RZ_API int rz_io_desc_get_pid(RzIODesc *desc) { //-1 and -2 are reserved if (!desc) { diff --git a/librz/io/p/io_gdb.c b/librz/io/p/io_gdb.c index 3a4039aef8b..f10d9fb3a3c 100644 --- a/librz/io/p/io_gdb.c +++ b/librz/io/p/io_gdb.c @@ -15,6 +15,9 @@ #define RZ_GDB_MAGIC rz_str_djb2_hash("gdb") +#define MIN_DOWNLOAD_CHUNK 64 +#define MAX_DOWNLOAD_CHUNK 65536 + static int __close(RzIODesc *fd); static bool __plugin_open(RzIO *io, const char *file, bool many) { @@ -207,6 +210,56 @@ static int __gettid(RzIODesc *fd) { extern int send_msg(libgdbr_t *g, const char *command); extern int read_packet(libgdbr_t *instance, bool vcont); +static bool gdbr_download_file(libgdbr_t *g, const char *remote, const char *local) { + rz_return_val_if_fail(g && remote && local, false); + // empty the destination file by writing nothing in non-append mode + if (!rz_file_dump(local, NULL, 0, false)) { + return false; + } + // open the remote file on the stub side + if (gdbr_open_file(g, remote, O_RDONLY, 0) < 0) { + return false; + } + + uint32_t off = 0; + bool ok = true; + // clamp the download size between a reasonable MIN floor and MAX ceiling + ut32 chunk = RZ_MAX(MIN_DOWNLOAD_CHUNK, g->stub_features.pkt_sz / 2); + chunk = RZ_MIN(chunk, MAX_DOWNLOAD_CHUNK); + ut8 *buf = malloc(chunk); + if (!buf) { + gdbr_close_file(g); + return false; + } + + int ret; + do { + ret = gdbr_pread_file(g, buf, chunk, off); + if (ret < 0) { + ok = false; + break; + } + if (ret == 0) { + break; + } + if (!rz_file_dump(local, buf, ret, true)) { + ok = false; + break; + } + off += ret; + } while ((ut32)ret == chunk); + free(buf); + gdbr_close_file(g); + return ok; +} + +static bool io_gdb_download_file(RzIO *io, RzIODesc *fd, const char *remote, const char *local) { + if (!fd || !fd->data) { + return false; + } + return gdbr_download_file(fd->data, remote, local); +} + static char *__system(RzIO *io, RzIODesc *fd, const char *cmd) { if (!fd || !fd->data) { return NULL; @@ -228,10 +281,22 @@ static char *__system(RzIO *io, RzIODesc *fd, const char *cmd) { " R! pktsz - get max packet size used\n" " R! pktsz bytes - set max. packet size as 'bytes' bytes\n" " R! exec_file [pid] - get file which was executed for" - " current/specified pid\n"); + " current/specified pid\n" + " R! download_file remote local - download remote file to local path\n"); return NULL; } libgdbr_t *desc = fd->data; + if (rz_str_startswith(cmd, "download_file")) { + int argc = 0; + char **argv = rz_str_argv(cmd, &argc); + if (!argv || argc != 3) { + rz_str_argv_free(argv); + return rz_str_dup("0"); + } + bool ok = gdbr_download_file(desc, argv[1], argv[2]); + rz_str_argv_free(argv); + return rz_str_dup(ok ? "1" : "0"); + } if (rz_str_startswith(cmd, "pktsz")) { const char *ptr = rz_str_trim_head_ro(cmd + 5); if (!isdigit((ut8)*ptr)) { @@ -430,6 +495,7 @@ RzIOPlugin rz_io_plugin_gdb = { .system = __system, .getpid = __getpid, .gettid = __gettid, + .download_file = io_gdb_download_file, .isdbg = true }; diff --git a/librz/main/rizin.c b/librz/main/rizin.c index 380ea640e10..4d3b61a4767 100644 --- a/librz/main/rizin.c +++ b/librz/main/rizin.c @@ -20,6 +20,24 @@ static bool is_valid_dmp_file(RzCoreFile *fh) { return d && strncmp(d->name, "dmp://", 6); } +static char *download_gdb_remote_file(RzIODesc *iod, const char *remote_path) { + if (!iod || RZ_STR_ISEMPTY(remote_path)) { + return NULL; + } + char *local_path = NULL; + int fd = rz_file_mkstemp("gdbexe", &local_path); + if (fd == -1 || !local_path) { + free(local_path); + return NULL; + } + close(fd); + if (!rz_io_desc_download_file(iod, remote_path, local_path)) { + rz_file_rm(local_path); + RZ_FREE(local_path); + } + return local_path; +} + static char *get_file_in_cur_dir(const char *filepath) { filepath = rz_file_basename(filepath); if (rz_file_exists(filepath) && !rz_file_is_directory(filepath)) { @@ -428,6 +446,7 @@ RZ_API int rz_main_rizin(int argc, const char **argv) { bool do_list_io_plugins = false; char *file = NULL; char *pfile = NULL; + char *gdb_downloaded_exe = NULL; const char *asmarch = NULL; const char *asmos = NULL; const char *forcebin = NULL; @@ -1073,12 +1092,33 @@ RZ_API int rz_main_rizin(int argc, const char **argv) { } } else if (is_valid_gdb_file(fh) || is_valid_dmp_file(fh)) { filepath = iod->name; - if (RZ_STR_ISNOTEMPTY(filepath) && rz_file_exists(filepath) && !rz_file_is_directory(filepath)) { + bool is_gdb_remote = rz_str_startswith(pfile, "gdb://"); + bool is_localhost_gdb_remote = rz_str_startswith(pfile, "gdb://localhost:"); + bool local_file_exists = RZ_STR_ISNOTEMPTY(filepath) && rz_file_exists(filepath) && !rz_file_is_directory(filepath); + bool should_download = is_gdb_remote && RZ_STR_ISNOTEMPTY(filepath) && (!is_localhost_gdb_remote || !local_file_exists); + if (should_download) { + char *downloaded = NULL; + if (addr == UINT64_MAX) { + addr = rz_debug_get_baddr(r->dbg, filepath); + } + if (rz_cons_yesno('n', "Download remote executable '%s' to a temporary file? (y/N) ", filepath)) { + downloaded = download_gdb_remote_file(iod, filepath); + if (!downloaded) { + RZ_LOG_ERROR("Failed to download remote executable '%s'.\n", filepath); + } + } + if (downloaded) { + gdb_downloaded_exe = downloaded; + free(iod->name); + iod->name = rz_str_dup(downloaded); + rz_core_bin_load(r, NULL, addr); + } + } else if (local_file_exists) { if (addr == UINT64_MAX) { addr = rz_debug_get_baddr(r->dbg, filepath); } rz_core_bin_load(r, filepath, addr); - } else if ((filepath = get_file_in_cur_dir(filepath))) { + } else if (!is_gdb_remote && (filepath = get_file_in_cur_dir(filepath))) { // Present in local directory if (iod) { free(iod->name); @@ -1574,6 +1614,10 @@ RZ_API int rz_main_rizin(int argc, const char **argv) { // execution. // rz_core_file_close (r, fh); rz_core_free(r); + if (gdb_downloaded_exe) { + rz_file_rm(gdb_downloaded_exe); + free(gdb_downloaded_exe); + } rz_cons_set_raw(0); rz_cons_free(); LISTS_FREE(); diff --git a/subprojects/rzgdb/include/gdbclient/commands.h b/subprojects/rzgdb/include/gdbclient/commands.h index 49e2c9fa718..4fa409248fc 100644 --- a/subprojects/rzgdb/include/gdbclient/commands.h +++ b/subprojects/rzgdb/include/gdbclient/commands.h @@ -126,6 +126,7 @@ int gdbr_remove_hwa(libgdbr_t *g, ut64 address, int sizebp); * File read from remote target (only one file open at a time for now) */ int gdbr_open_file(libgdbr_t *g, const char *filename, int flags, int mode); +int gdbr_pread_file(libgdbr_t *g, ut8 *buf, ut64 max_len, uint32_t start_offset); int gdbr_read_file(libgdbr_t *g, ut8 *buf, ut64 max_len); int gdbr_close_file(libgdbr_t *g); diff --git a/subprojects/rzgdb/src/gdbclient/core.c b/subprojects/rzgdb/src/gdbclient/core.c index 7a30aad11e1..296041f47c5 100644 --- a/subprojects/rzgdb/src/gdbclient/core.c +++ b/subprojects/rzgdb/src/gdbclient/core.c @@ -1507,7 +1507,7 @@ int gdbr_open_file(libgdbr_t *g, const char *filename, int flags, int mode) { return ret; } -int gdbr_read_file(libgdbr_t *g, ut8 *buf, ut64 max_len) { +int gdbr_pread_file(libgdbr_t *g, ut8 *buf, ut64 max_len, uint32_t start_offset) { int ret, ret1; char command[64]; ut64 data_sz; @@ -1534,7 +1534,7 @@ int gdbr_read_file(libgdbr_t *g, ut8 *buf, ut64 max_len) { if (snprintf(command, sizeof(command) - 1, "vFile:pread:%x,%" PFMT64x ",%" PFMT64x, (int)g->remote_file_fd, (ut64)RZ_MIN(data_sz, max_len - ret), - (ut64)ret) < 0) { + (ut64)start_offset + ret) < 0) { ret = -1; goto end; } @@ -1561,6 +1561,10 @@ int gdbr_read_file(libgdbr_t *g, ut8 *buf, ut64 max_len) { return ret; } +int gdbr_read_file(libgdbr_t *g, ut8 *buf, ut64 max_len) { + return gdbr_pread_file(g, buf, max_len, 0); +} + int gdbr_close_file(libgdbr_t *g) { int ret = -1; char buf[64]; diff --git a/test/db/archos/linux-x64/dbg_gdbserver b/test/db/archos/linux-x64/dbg_gdbserver index 2114a89662b..3036ae532b0 100644 --- a/test/db/archos/linux-x64/dbg_gdbserver +++ b/test/db/archos/linux-x64/dbg_gdbserver @@ -26,3 +26,19 @@ EXPECT=<