Skip to main content
Angrop provides powerful methods to invoke Linux system calls directly, including a convenient 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

Parameters:
  • syscall_num: The syscall number to invoke (integer)
  • args: List or tuple of arguments for the syscall
  • needs_return: Whether execution should continue after the syscall (default: True)
  • preserve_regs: Set of registers that should not be modified
Returns: A 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
Check /usr/include/asm/unistd_64.h or similar headers for your architecture.

Syscall Calling Convention

x86_64 Linux

Arguments are passed in registers:
  1. rax - Syscall number
  2. rdi - First argument
  3. rsi - Second argument
  4. rdx - Third argument
  5. r10 - Fourth argument (note: not rcx!)
  6. r8 - Fifth argument
  7. r9 - Sixth argument
The syscall instruction is: syscall

x86 (32-bit) Linux

Arguments are passed in registers:
  1. eax - Syscall number
  2. ebx - First argument
  3. ecx - Second argument
  4. edx - Third argument
  5. esi - Fourth argument
  6. edi - Fifth argument
  7. ebp - Sixth argument
The syscall instruction is: 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

Parameters:
  • path: Custom path to execute (default: b"/bin/sh\x00")
  • path_addr: Memory address to write the path (default: auto-selected writable region)
Returns: A RopChain that spawns a shell

Basic Example

This automatically:
  1. Finds a writable memory region
  2. Writes /bin/sh\x00 to memory
  3. Invokes execve("/bin/sh", NULL, NULL)

Custom Shell Path

Specify Memory Location

If you don’t specify path_addr, angrop automatically searches for a writable region in the binary’s memory. This is convenient but you can also specify an address if you know a good 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)
You can check if syscall gadgets were found:
If no syscall gadgets are found, you may need to:
  1. Use fast_mode=False when initializing ROP
  2. Check if the binary actually contains syscall instructions
  3. Fall back to using func_call() with library functions instead

Handling Syscall Return Values

Like function calls, syscall return values go in rax (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:
See the Kernel ROP guide for details.

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

  1. Check for syscall gadgets before relying on do_syscall()
  2. Use execve() for shells instead of manually building the syscall
  3. Set needs_return=False for syscalls that don’t return (execve, exit)
  4. Save return values immediately after syscalls that return important data
  5. Use symbolic constants for syscall numbers instead of magic numbers
  6. Test syscall availability - not all binaries have syscall instructions

Syscalls vs Library Functions

Prefer func_call() for standard library functions when available. Use do_syscall() when:
  • The binary doesn’t have the required library functions
  • You need precise control over syscall arguments
  • You’re doing kernel-level exploitation
  • Library functions have security mitigations

Next Steps

  • Kernel ROP - Kernel-mode exploitation techniques
  • Function Calls - Alternative to syscalls using library functions
  • Badbytes - Handling restricted bytes in syscalls