execve() wrapper for spawning shells. This guide covers syscall invocation and common exploitation patterns.
Making System Calls: do_syscall()
The do_syscall() method generates ROP chains that invoke Linux system calls with proper register setup.
Method Signature
syscall_num: The syscall number to invoke (integer)args: List or tuple of arguments for the syscallneeds_return: Whether execution should continue after the syscall (default:True)preserve_regs: Set of registers that should not be modified
RopChain that invokes the system call
Basic Examples
1
Invoke a simple syscall
2
Invoke without returning
3
Invoke execve
Syscall numbers vary by architecture:
- x86_64: execve=59, read=0, write=1, open=2, exit=60
- x86 (32-bit): execve=11, read=3, write=4, open=5, exit=1
- ARM: execve=11, read=3, write=4, open=5, exit=1
/usr/include/asm/unistd_64.h or similar headers for your architecture.Syscall Calling Convention
x86_64 Linux
Arguments are passed in registers:rax- Syscall numberrdi- First argumentrsi- Second argumentrdx- Third argumentr10- Fourth argument (note: not rcx!)r8- Fifth argumentr9- Sixth argument
syscall
x86 (32-bit) Linux
Arguments are passed in registers:eax- Syscall numberebx- First argumentecx- Second argumentedx- Third argumentesi- Fourth argumentedi- Fifth argumentebp- Sixth argument
int 0x80
Spawning a Shell: execve()
The execve() method is a convenience wrapper that writes “/bin/sh” to memory and invokes the execve syscall.
Method Signature
path: Custom path to execute (default:b"/bin/sh\x00")path_addr: Memory address to write the path (default: auto-selected writable region)
RopChain that spawns a shell
Basic Example
- Finds a writable memory region
- Writes
/bin/sh\x00to memory - Invokes
execve("/bin/sh", NULL, NULL)
Custom Shell Path
Specify Memory Location
Real-World Syscall Examples
Example 1: Read-Write Chain
Example 2: Open-Read-Write File
Example 3: Execve with Arguments
When using syscalls directly, you have more control than library functions but need to handle setup manually. The execve example above shows building a proper argv array.
Syscall Gadget Requirements
For syscalls to work, angrop needs gadgets ending in:syscall; ret(x86_64)int 0x80; ret(x86)svc #0; ret(ARM)
Handling Syscall Return Values
Like function calls, syscall return values go inrax (x64) or eax (x86):
Common Syscall Numbers
x86_64 Linux
x86 (32-bit) Linux
Advanced: Kernel Mode Syscalls
Angrop supports kernel-mode ROP for Linux kernel exploitation:Badbytes and Syscalls
When badbytes are configured, angrop avoids them in syscall numbers and arguments:If your syscall arguments contain badbytes (like NULL pointers), angrop will use arithmetic operations to construct them, similar to how it handles badbytes in
set_regs().Error Handling
No Syscall Gadgets
Cannot Set Syscall Number
From the source code (sys_caller.py:134-165), if angrop can’t set the syscall number register:
Complete Exploit Example
Here’s a full exploit using syscalls:Best Practices
- Check for syscall gadgets before relying on
do_syscall() - Use
execve()for shells instead of manually building the syscall - Set
needs_return=Falsefor syscalls that don’t return (execve, exit) - Save return values immediately after syscalls that return important data
- Use symbolic constants for syscall numbers instead of magic numbers
- Test syscall availability - not all binaries have syscall instructions
Syscalls vs Library Functions
Next Steps
- Kernel ROP - Kernel-mode exploitation techniques
- Function Calls - Alternative to syscalls using library functions
- Badbytes - Handling restricted bytes in syscalls