All access to files, network, debugger and all input/output in general is wrapped by an IO abstraction layer that allows radare to treat all data as if it were just a file.
IO plugins are the ones used to wrap the open, read, write and 'system' on virtual file systems. You can make radare understand anything as a plain file. E.g. a socket connection, a remote radare session, a file, a process, a device, a gdb session.
So, when radare reads a block of bytes, it is the task of an IO plugin to get these bytes from any place and put them into internal buffer. An IO plugin is chosen by a file's URI to be opened. Some examples:
- Debugging URIs
$ r2 dbg:///bin/ls<br /> $ r2 pid://1927
- Remote sessions
$ r2 rap://:1234<br /> $ r2 rap://<host>:1234//bin/ls
- Virtual buffers
$ r2 malloc://512<br /> shortcut for $ r2 -
You can get a list of the radare IO plugins by typing `radare2 -L`: ``` $ r2 -L rw_ ar Open ar/lib files [ar|lib]://[file//path] (LGPL3) rw_ bfdbg BrainFuck Debugger (bfdbg://path/to/file) (LGPL3) rwd bochs Attach to a BOCHS debugger (LGPL3) r_d debug Native debugger (dbg:///bin/ls dbg://1388 pidof:// waitfor://) (LGPL3) v0.2.0 pancake rw_ default open local files using def_mmap:// (LGPL3) rwd gdb Attach to gdbserver, 'qemu -s', gdb://localhost:1234 (LGPL3) rw_ gprobe open gprobe connection using gprobe:// (LGPL3) rw_ gzip read/write gzipped files (LGPL3) rw_ http http get (http://rada.re/) (LGPL3) rw_ ihex Intel HEX file (ihex://eeproms.hex) (LGPL) r__ mach mach debug io (unsupported in this platform) (LGPL) rw_ malloc memory allocation (malloc://1024 hex://cd8090) (LGPL3) rw_ mmap open file using mmap:// (LGPL3) rw_ null null-plugin (null://23) (LGPL3) rw_ procpid /proc/pid/mem io (LGPL3) rwd ptrace ptrace and /proc/pid/mem (if available) io (LGPL3) rwd qnx Attach to QNX pdebug instance, qnx://host:1234 (LGPL3) rw_ r2k kernel access API io (r2k://) (LGPL3) rw_ r2pipe r2pipe io plugin (MIT) rw_ r2web r2web io client (r2web://cloud.rada.re/cmd/) (LGPL3) rw_ rap radare network protocol (rap://:port rap://host:port/file) (LGPL3) rw_ rbuf RBuffer IO plugin: rbuf:// (LGPL) rw_ self read memory from myself using 'self://' (LGPL3) rw_ shm shared memory resources (shm://key) (LGPL3) rw_ sparse sparse buffer allocation (sparse://1024 sparse://) (LGPL3) rw_ tcp load files via TCP (listen or connect) (LGPL3) rwd windbg Attach to a KD debugger (windbg://socket) (LGPL3) rwd winedbg Wine-dbg io and debug.io plugin for r2 (MIT) rw_ zip Open zip files [apk|ipa|zip|zipall]://[file//path] (BSD) ```
An example of how to write a plugin is available in this commit: here[1].
To write an IO plugin in radare2, in `radare2/libr/io/p/`, create a file `dap.mk`. Put there:
OBJ_DAP=io_dap.o STATIC_OBJ+=${OBJ_DAP} TARGET_DAP=io_dap.${EXT_SO} ALL_TARGETS+=${TARGET_DAP} ifeq (${WITHPIC},0) LINKFLAGS+=../../util/libr_util.a LINKFLAGS+=../../io/libr_io.a else LINKFLAGS+=-L../../util -lr_util LINKFLAGS+=-L.. -lr_io endif ${TARGET_DAP}: ${OBJ_DAP} ${CC} $(call libname,io_dap) ${OBJ_DAP} ${CFLAGS} \ ${LINKFLAGS} ${LDFLAGS_LIB} $(LDFLAGS)
Edit the file `radare2/libr/io/p/dap.c`:
#include <r_userconf.h> #include <r_io.h> #include <r_lib.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include "io_dap.h" #define URI_PREFIX "foo://" extern RIOPlugin r_io_plugin_dap; // forward declaration static bool __plugin_open(RIO *io, const char *pathname, bool many) { return (strncmp(pathname, URI_PREFIX, strlen(URI_PREFIX)) == 0); } static RIODesc *__open(RIO *io, const char *pathname, int flags, int mode) { RIODesc *ret = NULL; RIOFoo *rio_foo = NULL; printf("%s\n", __func__); if (!__plugin_open(io, pathname, 0)) return ret; return r_io_desc_new (io, &r_io_plugin_dap, pathname, flags, mode, rio_foo); } static int __close(RIODesc *fd) { RIOFoo *rio_foo = NULL; printf("%s\n", __func__); if (!fd || !fd->data) return -1; rio_foo = fd->data; // destroy return true; } static ut64 __lseek(RIO *io, RIODesc *fd, ut64 offset, int whence) { printf("%s, offset: %lx, io->off: %lx\n", __func__, offset, io->off); if (!fd || !fd->data) return -1; switch (whence) { case SEEK_SET: io->off = offset; break; case SEEK_CUR: io->off += (int)offset; break; case SEEK_END: io->off = UT64_MAX; break; } return io->off; } static int __read(RIO *io, RIODesc *fd, ut8 *buf, int len) { RIOFoo *rio_foo = NULL; printf("%s, offset: %lx\n", __func__, io->off); if (!fd || !fd->data) return -1; rio_foo = fd->data; return 0; } static int __write(RIO *io, RIODesc *fd, const ut8 *buf, int len) { printf("%s\n", __func__); return 0; } static int __getpid(RIODesc *fd) { RIOFoo *rio_foo = NULL; printf("%s\n", __func__); if (!fd || !fd->data) return -1; rio_foo = fd->data; return 0; } static int __gettid(RIODesc *fd) { printf("%s\n", __func__); return 0; } static char *__system(RIO *io, RIODesc *fd, const char *command) { printf("%s command: %s\n", __func__, command); // io->cb_printf() return NULL; } RIOPlugin r_io_plugin_dap = { .name = "dap", .desc = "IO Foo plugin", .license = "LGPL", .check = __plugin_open, .open = __open, .close = __close, .seek = __lseek, .read = __read, .write = __write, .getpid = __getpid, .system = __system, .isdbg = true, // # --d flag }; #ifndef R2_PLUGIN_INCORE R_API RLibStruct radare_plugin = { .type = R_LIB_TYPE_IO, .data = &r_io_plugin_dap, .version = R2_VERSION }; #endif
Do not forget to include your definition of `r_io_plugin_dap` in `libr/include/r_io.h`: At this line: here[1], add:
extern RIOPlugin r_io_plugin_dap;
At last but not least, for the Makefile (not for meson), in `dist/plugins-cfg/plugins.def.cfg`, add `io.dap` after `io.debug`.
Edit `radare2/libr/io/meson.build` and in `r_io_sources = [...` add `'p/io_dap.c',`. And in `radare2/libr/meson.build`, add `'dap'` in `io_plugins += [`.
Edit `radare2/dist/plugins-cfg/plugins.def.cfg` and add `io.dap` in the list.
Now, if you need to use some data and structures, edit the file `radare2/libr/io/p/dap.h` for:
#ifndef LIBR_IO_P_IO_DAP_H_ #define LIBR_IO_P_IO_DAP_H_ typedef struct { int x; } RIOFoo; #endif /* LIBR_IO_P_IO_DAP_H_ */
This tutorial is based on this one by Wensel[1].