The debugger in radare is implemented as an IO plugin. It handles two different URIs for creating or attaching to a process: dbg:// and pid://.
There are different backends for multiple architectures and operating systems like GNU/Linux, Windows, MacOSX, (Net,Free,Open)BSD and Solaris.
The process memory is interpreted by radare as a plain file. So all the mapped pages like the program and the libraries can be readed and interpreted as code, structures, etc..
The rest of the communication between radare and the debugger layer is the wrapped system() call that receives a string as argument and executes the given command. The result of the operation is buffered in the output console and this contents can be handled by a scripting language.
This is the reason why radare can handle single and double exclamation marks for calling system().
[0x00000000]> !step
[0x00000000]> !!ls
The double exclamation mark tells radare to skip the plugin list to find an IO plugin handling this command to launch it directly to the shell. A single one will walk thru the io plugin list.
The debugger commands are mostly portable between architectures and operating systems. But radare tries to implement them on all the artchitectures and OSs injecting shellcodes, or handling exceptions in a special way. For example in mips there's no stepping feature by hardware, so radare has an own implementation using a mix of code analysis and software breakpoints to bypass this limitation.
To get the basic help of the debugger you can just type '!help':
[0x4A13B8C0]> !help
Information
info show debugger and process status
msg show last debugger status message
pid [tid] [action] show pid of the debug process, current tid and childs, or set tid.
status show the contents of /proc/pid/status
signal show signals handler
maps[*] flags the current memory maps (.!rsc maps)
syms flags all syms of the debugged program (TODO: get syms from libs too)
fd[?][-#] [arg] file descriptors (fd? for help)
th[?] threads control command
Stack analysis
bt backtrace stack frames (use :!bt for user code only)
st analyze stack trace (experimental)
Memory allocation
alloc [N] allocates N bytes (no args = list regions)
mmap [F] [off] mmaps a file into a memory region
free free allocated memory regions
imap map input stream to a memory region (DEPRECATED)
Loader
run [args] load and start execution
(un)load load or unload a program to debug
kill [-S] [pid] sends a signal to a process
{a,de}ttach [pid] attach or detach target pid
Flow Control
jmp [addr] set program counter
call [addr] enters a subroutine
ret emulates a return from subroutine
skip [N] skip (N=1) instruction(s)
step{o,u,bp,ret} step, step over, step until user code, step until ret
cont{u,uh,sc,fork} continue until user code, here, syscall or fork
Tracing
trace [N] trace until bp or eof at N debug level
tt [size] touch trace using a swapable bps area
wtrace watchpoint trace (see !wp command)
wp[m|?] [expr] put a watchpoint (!wp? for help) (wpm for monitor)
Breakpoints
bp [addr] put a breakpoint at addr (no arg = list)
mp [rwx] [a] [s] change memory protections at [a]ddress with [s]ize
ie [-][event] ignore debug events
Registers
[o|d|fp]regs[*] show registers (o=old, d=diff, fp=fpu, *=radare)
reg[s|*] [reg[=v] show get and set registers
oregs[*] show old registers information (* for radare)
dr[rwx-] DR registers control (dr? for help) (x86 only)
Other
dump/restore [N] dump/restore pages (and registers) to/from disk
dall dump from current seek to cfg.limit all available bytes (no !maps required)
core dump core of the process
hack [N] Make a hack.
inject [bin] inject code inside child process (UNSTABLE)
Usage: !<cmd>[?] <args> @ <offset> ; see eval dbg. fmi