Skip to main content
Angrop provides the func_call() method to automatically generate ROP chains that invoke functions with proper calling conventions, argument setup, and optional return handling.

Basic Function Calls: func_call()

The func_call() method handles all the complexity of setting up function arguments according to the target architecture’s calling convention.

Method Signature

Parameters:
  • address: Function address (integer) or name (string)
  • args: List or tuple of arguments to pass to the function
  • preserve_regs: Set of registers that should not be modified
  • needs_return: Whether execution should continue after the function (default: True)
Returns: A RopChain that calls the function with the specified arguments

Basic Examples

1

Call a function by name

2

Call a function by address

3

Call without returning

Angrop automatically detects the calling convention based on the architecture and platform. On x86_64 Linux, arguments go in rdi, rsi, rdx, rcx, r8, r9, then stack. On x86, arguments go on the stack.

Real-World Example: File Operations

From angrop’s Python API documentation:
When needs_return=True, angrop ensures the ROP chain continues executing after the function returns. Use needs_return=False for the final function call to generate shorter chains.

Calling Convention Details

x86_64 (System V AMD64 ABI)

Arguments are passed in registers:
  1. rdi - First argument
  2. rsi - Second argument
  3. rdx - Third argument
  4. rcx - Fourth argument
  5. r8 - Fifth argument
  6. r9 - Sixth argument
  7. Stack - Additional arguments
Return value in rax.

x86 (32-bit cdecl)

All arguments passed on the stack in reverse order:
Return value in eax.

ARM

Arguments in r0, r1, r2, r3, then stack:

Preserving Registers Across Calls

Use preserve_regs to maintain register values across function calls:

Real-World Example: Kernel Function Calls

From angrop’s kernel test suite (solve.py:38-44):
This example shows:
  • Calling functions that return values (in rax)
  • Moving return values to argument registers
  • Preserving multiple registers across calls
  • Building complex multi-stage exploits
When calling functions that return values, the result is typically in rax (x64) or eax (x86). Use move_regs() to move return values to other registers before the next call.

Using Function Return Values

Calling Functions from PLT/GOT

Angrop automatically resolves function names from the PLT (Procedure Linkage Table):

How PLT/GOT Resolution Works

From the source code (func_caller.py:48-72), angrop:
  1. Checks if the name exists in PLT
  2. Looks for the symbol in the binary
  3. Finds function pointers in GOT if available
  4. Searches for pointers to the function in readable segments
When calling library functions like system, execve, or open, use the string name rather than looking up addresses manually. Angrop handles all the resolution for you.

Handling Stack Arguments

For functions with more than 6 arguments on x86_64 (or any arguments on x86):
Angrop automatically:
  • Sets up register arguments
  • Pushes stack arguments
  • Aligns the stack properly
  • Handles cleanup if needs_return=True

Advanced: Direct Register Control

From angrop’s Python API documentation (pythonapi.md:101-111), you can use registers directly as arguments:
Output:
By using preserve_regs={'rdi'}, angrop skips setting rdi and uses whatever value is already there (e.g., from a previous function’s return value).

Calling Functions Without Returning

When you don’t need to return from a function, set needs_return=False:
Setting needs_return=False generates shorter chains because angrop doesn’t need to set up stack frames for returning to the ROP chain. Use this for the last function call in your exploit.

Common Function Call Patterns

Pattern 1: Open-Read-Write

Pattern 2: Allocate-Write-Execute

Pattern 3: Chain Library Calls

Troubleshooting

”Symbol does not exist” Error

”Fail to invoke function” Error

This occurs when angrop can’t find suitable gadgets:
Solutions:
  1. Ensure find_gadgets(optimize=True) was called
  2. Try needs_return=False if you don’t need to return
  3. Check if required registers can be set
  4. Verify the function address is correct
  5. Consider using do_syscall() instead of library functions

Return Value Not Preserved

If you need to use a function’s return value:

Best Practices

  1. Use function names when possible instead of hardcoded addresses
  2. Set needs_return=False for the final call to reduce chain size
  3. Preserve return values by moving them before subsequent operations
  4. Use preserve_regs when you need to maintain state across calls
  5. Test incrementally - build and test each function call before chaining
  6. Check calling conventions for your target architecture

Performance Tips

  • Function calls without returns are faster to generate
  • Preserving many registers increases constraint complexity
  • Using stack arguments adds overhead
  • PLT/GOT calls may be more efficient than direct addresses

Next Steps