SysCaller class builds ROP chains that invoke system calls, automatically handling syscall numbers and arguments according to the platform’s syscall convention.
Overview
Accessed through the ROP instance asrop.do_syscall() and rop.execve(), SysCaller automatically:
- Sets syscall number register (rax on x64, eax on x86, r7 on ARM)
- Sets argument registers according to syscall convention
- Handles both returning and non-returning syscalls
- Finds optimal syscall gadgets
- Supports execve convenience method
Class Definition
FuncCaller and extends it with syscall-specific functionality.
Located in angrop/chain_builder/sys_caller.py
Public Methods
do_syscall
int
required
System call number. Examples:
- Linux x64:
execve=59,read=0,write=1,open=2 - Linux x86:
execve=11,read=3,write=4,open=5
list
required
List of syscall arguments. Number and meaning depend on the specific syscall.
bool
default:"True"
Whether control flow should continue after the syscall. Set to False for syscalls like execve that don’t return.
set | None
default:"None"
Set of register names that should not be modified.
RopChain that invokes the syscall.
Raises: RopException if syscall cannot be invoked.
execve
/bin/sh.
bytes | None
default:"b'/bin/sh\\\\x00'"
Command to execute. Must be null-terminated.
int | None
default:"None"
Address to write the path string. If None, automatically finds writable memory.
RopException if execve cannot be invoked.
verify
RopChain
required
Chain to verify.
dict
required
Target register values.
set
required
Registers that should not be modified.
ROP Instance Methods
Implementation Details
Syscall Convention Detection
SysCaller uses angr’s syscall calling convention: From source code (sys_caller.py:42):Syscall Conventions by Architecture
x86_64 Linux
x86 (32-bit) Linux
ARM Linux
Gadget Filtering
From source code (sys_caller.py:80-84):- Can return (preferred)
- Fewer symbolic memory accesses
- Smaller stack change
- Fewer instructions
Per-Request Gadget Filtering
SysCaller optimizes gadget selection per syscall: From source code (sys_caller.py:140-175):Usage Examples
Basic Syscall
Execve Shell
Read Syscall
Write Syscall
Open Syscall
Non-Returning Syscall
Complete File Read Example
Chaining Syscalls
Preserving Registers Across Syscalls
Execve Implementation
From source code (sys_caller.py:86-131):Syscall Verification
From source code (sys_caller.py:51-78):Common Syscall Numbers
Linux x86_64
Linux x86 (32-bit)
Linux ARM
Error Handling
”target does not contain syscall gadget!”
Raised when no syscall gadgets are found. Solutions:- Binary may not have syscall instructions
- Try using function calls instead:
rop.func_call("syscall", [...]) - Check if binary is statically linked
”Fail to invoke syscall with arguments: !”
Raised when syscall chain cannot be built. Solutions:- Run
find_gadgets(optimize=True) - Try
needs_return=Falsefor simpler chain - Check if arguments are valid for the syscall
- Verify syscall number is correct for the architecture
”Fail to invoke execve!”
Raised when execve cannot be invoked. Solutions:- Provide explicit
path_addrto writable memory - Check badbytes configuration
- Try
rop.do_syscall(execve_num, [...])directly
Platform Support
Operating Systems
From source code (sys_caller.py:45-46):- Linux (all architectures)
- BSD variants
- Other Unix-like systems
- Windows (uses different syscall mechanism)
Best Practices
- Use execve() for shells - Simplest and most reliable
- Set needs_return=False - For syscalls that don’t return (execve, exit)
- Preserve return values - Move rax to another register if needed
- Verify syscall numbers - They differ across architectures
- Handle file descriptors - Save fd from open for read/write
Performance Considerations
- Gadget filtering reduces search space significantly
- Concrete value matching optimizes gadget selection
- Verification adds overhead but ensures correctness
- Non-returning syscalls generate shorter chains
See Also
- FuncCaller - Function calling (parent class)
- SigreturnBuilder - SROP chains
- Syscalls Guide - Usage examples and patterns