Skip to main content
The RegMover class builds ROP chains that move data from one register to another. It uses graph-based path finding to chain register moves when direct moves aren’t available.

Overview

Accessed through the ROP instance as rop.move_regs(), RegMover automatically:
  • Finds direct move gadgets like mov rax, rdx; ret
  • Chains multiple moves when needed
  • Uses push/pop sequences as fallback
  • Optimizes move paths for efficiency

Class Definition

Located in angrop/chain_builder/reg_mover.py

Public Methods

run

Builds a ROP chain that moves register values.
set | None
default:"None"
Set of register names that must not be modified.
Keyword arguments mapping destination register to source register name (as string). Example: rax='rdx' means move rdx to rax.
Returns: A RopChain that performs the register moves. Raises: RopException if moves cannot be performed.

verify

Verifies that a chain correctly moves registers.
RopChain
required
The chain to verify.
set
required
Registers that should not be modified.
dict
required
Target register moves.
Returns: True if verification passes, False otherwise.

ROP Instance Method

When you call rop.move_regs(), it invokes RegMover.run() internally:

Implementation Details

Move Graph

RegMover builds a directed graph where:
  • Nodes = Registers
  • Edges = Move gadgets with metadata (bits, blocks)
From source code (reg_mover.py:345-361):

Path Finding

Uses NetworkX to find shortest paths between registers:

Gadget Types

RegMover recognizes several move gadget patterns:
  1. Direct moves: mov rax, rdx; ret
  2. Exchange: xchg rax, rbx; ret
  3. Conditional moves: cmov rax, rdx; ret (if enabled)
  4. Arithmetic: lea rax, [rdx]; ret

PushPopMover Component

A specialized component that constructs moves via push/pop chains.

How It Works

From source code (reg_mover.py:64-173):

Usage Examples

Basic Register Move

Output:

Moving Multiple Registers

Using Return Values

Preserving Registers During Moves

Chained Moves

If no direct move exists, RegMover chains multiple moves:

Complex Move Sequences

From kernel ROP example:

Optimization

RegMover includes optimization to normalize complex gadgets:

Normalizing Non-Self-Contained Gadgets

From source code (reg_mover.py:211-258):

Move Graph Metadata

Each edge in the move graph contains:
  • block: List of gadgets sorted by stack_change
  • bits: Maximum bits moved (8, 16, 32, or 64)
Example:

Architecture-Specific Behavior

x86_64

Full support for all GPRs and common move patterns.

ARM

Supports ARM register moves:

Partial Register Moves

RegMover tracks bit width of moves:

Error Handling

”Couldn’t move registers :(”

Raised when no valid move chain can be found. Solutions:
  1. Run find_gadgets(optimize=True) to enable push/pop moves
  2. Use intermediate register: rax='src' then dst='rax'
  3. Check if registers are valid for your architecture

”There is no chain can move X to Y”

Raised when no path exists in the move graph. Solutions:
  1. Try an intermediate register as a bridge
  2. Use set_regs() instead if the value is known
  3. Check if both registers are available on your architecture

Performance Considerations

  • Move graph is built once during bootstrap
  • Only shortest paths are considered to limit complexity
  • Edge blocks are sorted by stack_change for efficiency
  • Multiprocessing used during optimization

Verification

RegMover verifies that moves are correct: From source code (reg_mover.py:363-395):

Best Practices

  1. Prefer direct moves - Let RegMover find the shortest path
  2. Chain efficiently - Combine multiple moves in one call
  3. Use preserve_regs - Protect values you need to keep
  4. Verify chains - Use chain.pp() to inspect generated gadgets

See Also