Skip to main content
Angrop provides powerful methods to write data to memory and modify memory contents using arithmetic and logical operations. These operations are essential for setting up data structures, strings, and shellcode.

Writing to Memory: write_to_mem()

The write_to_mem() method writes arbitrary data to a specified memory address.

Method Signature

Parameters:
  • addr: Target memory address (integer or RopValue)
  • data: Bytes to write (must be a bytes object)
  • preserve_regs: Set of register names that should not be modified
  • fill_byte: Single byte used to pad data if needed (default: b"\xff")
Returns: A RopChain that writes the data to memory

Basic Examples

1

Write a string to memory

2

Write binary data

3

Write with register preservation

Angrop automatically handles writing data in chunks based on available gadgets. It will use the most efficient write size (8, 4, 2, or 1 byte) depending on what gadgets are available.

Real-World Example: Building an Execve Chain

From angrop’s Python API documentation:

Writing Files and Paths

Memory Addition: mem_add()

Add a value to data stored at a memory location.

Method Signature

Parameters:
  • addr: Memory address to modify
  • value: Value to add (integer or RopValue)
  • size: Number of bytes to operate on (1, 2, 4, or 8). Default is architecture word size.
Returns: A RopChain that performs the addition

Example

The memory operation methods require gadgets that can perform memory writes with arithmetic operations, such as add dword ptr [rax], ebx; ret.

Memory XOR: mem_xor()

XOR data at a memory location with a value.

Method Signature

Parameters:
  • addr: Memory address to modify
  • value: Value to XOR with
  • size: Number of bytes (1, 2, 4, or 8)

Example

Memory OR: mem_or()

Perform bitwise OR on data at a memory location.

Method Signature

Parameters:
  • addr: Memory address to modify
  • value: Value to OR with
  • size: Number of bytes (1, 2, 4, or 8)

Example

Memory AND: mem_and()

Perform bitwise AND on data at a memory location.

Method Signature

Parameters:
  • addr: Memory address to modify
  • value: Value to AND with
  • size: Number of bytes (1, 2, 4, or 8)

Example

Complete Memory Operations Example

From angrop’s Python API documentation:

Writing with Badbytes

When badbytes are configured, angrop uses sophisticated techniques to avoid them:

How Badbyte Avoidance Works

From the source code (mem_writer.py:606-630), angrop uses multiple strategies:
1

Try chunk transforms

Find operations (XOR, OR, AND, ADD) that can transform safe bytes into target bytes:
2

Try per-byte operations

Handle each byte individually using different operations:
3

Use register arithmetic

Construct values in registers using arithmetic, then write to memory.
The fill_byte parameter should never be a badbyte. Angrop will raise an error if you try to use a badbyte as fill_byte.

Memory Write Gadget Requirements

For memory operations to work, angrop needs specific types of gadgets:

For write_to_mem():

  • Gadgets like: mov [rax], rbx; ret
  • Controllable address register (rax)
  • Controllable data register (rbx)
  • Address and data must be independent

For mem_add(), mem_xor(), etc.:

  • Gadgets like: add [rax], ebx; ret
  • xor [rdi], rsi; ret
  • or [rcx], rdx; ret
  • and [r8], r9; ret
If you get “Fail to write data to memory” or “Fail to perform _mem_change” errors, it means angrop couldn’t find suitable gadgets. Try:
  1. Using fast_mode=False when initializing ROP
  2. Checking if the binary has the necessary gadgets
  3. Using alternative approaches (e.g., setting up registers and using library functions)

Advanced Memory Techniques

Building Data Structures

Writing Pointer Arrays

Modifying Existing Data

Size Specification

All memory operations support explicit size specification:
If you don’t specify size, angrop uses the architecture’s default word size (4 bytes for 32-bit, 8 bytes for 64-bit).

Error Handling

Best Practices

  1. Choose safe addresses: Use addresses that don’t contain badbytes
  2. Minimize writes: Write larger chunks when possible instead of byte-by-byte
  3. Use fill_byte wisely: Choose a fill byte that’s not a badbyte
  4. Verify operations: Use chain.pp() to inspect generated chains
  5. Consider data alignment: Some gadgets may require aligned addresses

Performance Considerations

  • Writing large data may generate long chains
  • Memory operations with badbytes are slower due to additional transformations
  • Using size parameter efficiently can reduce chain length
  • Preservation of registers adds constraints and may increase chain complexity

Next Steps

  • Function Calls - Use memory writes to set up function arguments
  • Syscalls - Combine memory writes with syscalls for exploits
  • Badbytes - Learn more about handling restricted bytes