Reverse engineering is at the heart of malware analysis, vulnerability research, and CTF challenges. Radare2 (r2) is a free, open-source framework that handles binary analysis, disassembly, debugging, and patching across a wide range of architectures. It has a steep learning curve, but once you internalize its command language, it becomes one of the fastest tools available for deep binary work.
Installation
# Linux — recommended: install from source for latest version
git clone https://github.com/radareorg/radare2
cd radare2
sys/install.sh
# macOS
brew install radare2
# Kali Linux
sudo apt install radare2
# Verify
r2 -version
Opening a Binary
r2 /path/to/binary
By default, r2 opens the binary in read-only mode. To open for writing (patching):
r2 -w /path/to/binary
To open with auto-analysis:
r2 -A /path/to/binary
The -A flag runs aaa (analyze all) automatically — essential for understanding function boundaries and cross-references.
Navigation Basics
The r2 command language uses short prefixes that group related commands. The most important:
| Command | Action |
|---|
s <addr> | Seek (navigate) to address |
s main | Seek to main function |
s sym.functionname | Seek to named function |
pdf | Print disassembly of function |
px 64 | Print 64 bytes as hex dump |
pxw 32 | Print 32 bytes as hex words |
ps | Print string at current address |
? | Help for any command prefix |
After analysis, list all functions:
[0x00401000]> afl
This shows every detected function with its address, size, and name. Grep through them:
[0x00401000]> afl~check
The ~ character is r2’s internal grep.
Analyzing Functions
Navigate to a function and disassemble it:
[0x00401000]> s main
[0x00401234]> pdf
pdf (print disassembly function) shows the full function with comments, labels, and cross-references. To see the function in a graph view:
[0x00401234]> VV
This opens an interactive graph where you can navigate basic blocks with arrow keys. Press q to exit.
To rename a function (helps when reversing stripped binaries):
[0x00401234]> afn my_decrypt_routine
To add a comment:
[0x00401234]> CCa 0x401240 "This checks the license key"
Cross-References (xrefs)
Understanding where functions are called from and what data they reference is critical:
# List all calls to the current function
[0x00401234]> axt
# List all data xrefs from a function
[0x00401234]> axf
Strings and Imports
# Find all strings in the binary
iz
# Find strings with "password"
iz~password
# List imports
ii
# List exports
iE
# File info (architecture, bits, OS, etc.)
iI
Debugging with r2
r2 includes a built-in debugger:
r2 -d /path/to/binary arg1 arg2
Common debug commands:
dc # continue execution
db 0x401234 # set breakpoint at address
db sym.main # set breakpoint at main
dbt # print backtrace
dr # print all registers
dr rax # print specific register
dm # show memory maps
dso # step over
dsi # step into
Radare2 vs. Ghidra
Both tools are free and powerful, but they serve different workflows:
| Feature | Radare2 | Ghidra |
|---|
| Interface | Terminal (+ Cutter GUI) | Full Java GUI |
| Decompiler | r2ghidra plugin | Built-in |
| Speed | Faster on large binaries | Slower startup |
| Scripting | r2pipe (Python, JS, Go) | Python/Java API |
| Collaboration | File-based | Project sharing |
| Learning curve | Steep | Moderate |
| Architecture support | Extensive | Extensive |
Many reverse engineers use both: Ghidra for the decompiler view and r2 for fast CLI work and scripting. The r2ghidra plugin brings Ghidra’s decompiler into r2:
r2pm -ci r2ghidra
# Then use:
pdg # decompile current function
r2pipe: Python Scripting
r2pipe lets you control a radare2 session from Python, enabling automation of repetitive analysis tasks.
pip install r2pipe
Example: Extract all strings and function names
import r2pipe
import json
r2 = r2pipe.open("/path/to/binary")
r2.cmd("aaa") # analyze
# Get all functions as JSON
functions = json.loads(r2.cmd("aflj"))
for f in functions:
print(f"Function: {f['name']} at {hex(f['offset'])} ({f['size']} bytes)")
# Get all strings
strings = json.loads(r2.cmd("izj"))
for s in strings:
print(f"String at {hex(s['vaddr'])}: {s['string']}")
r2.quit()
Example: Automated vulnerability pattern detection
import r2pipe
import json
r2 = r2pipe.open("/path/to/binary")
r2.cmd("aaa")
# Find calls to dangerous functions
dangerous = ["strcpy", "sprintf", "gets", "strcat"]
for func in dangerous:
result = r2.cmd(f"axt sym.imp.{func}")
if result.strip():
print(f"[!] Calls to {func} found:")
print(result)
r2.quit()
Practical CTF Workflow
- Open with analysis:
r2 -A ./challenge
- Check file type:
iI
- List strings for hints:
iz~flag
- Find
main: s main; pdf
- Identify key decision points in graph view:
VV
- Set breakpoints around comparisons, debug interactively
- Rename functions as you understand them:
afn
- Use r2pipe to automate any repetitive analysis
Radare2 rewards investment. The first hour is frustrating; the tenth hour reveals why so many security researchers rely on it daily.