RopBot / Angrop

#angrop#rop

https://www.ndss-symposium.org/wp-content/uploads/2026-f845-paper.pdf

  • There are several other ROP Gadget finders but this is definitely a “State of the Art”. Angrop utilises symbolic execution to find gadgets efficiently, unlike other tools which use a Galileo algorithm (backtrace from a “ret” instruction).

  • I am mainly convinced by this tool for KPWN because of the following claims (cited from the paper).

Due to the Retpoline protection [29] adopted in the Linux kernel, all functions end with jmp indirect_thunk, which in turn executes ret. In Listing 2, this is jmp 0xffffffff822a7e60; ret, where 0xffffffff822a7e60 is the indirect_thunk. As a result, many existing gadget finders [21], [22] will misclassify these gadgets as gadgets ending with jmp, while in reality, they are equivalent to ending with ret. This demonstrates the importance of gadget effect analysis used in ropbot instead of instruction-based analysis and the need to analyze gadgets spanning multiple basic blocks

screenshot

  • Moreover, this tool also makes sure to try chaining ret gadgets with non-ret-ending gadgets and check whether the combination can result in a ret gadget. Thus, it utilises all resources from the binary.
  • Read the paper for implementation details

Cons:

  • Angrop is way slower than other tools because of symbolic execution. While it takes almost 30 minutes to find all possible gadgets in larger binaries like the Linux kernel, it provides a caching API to save the gadgets to disk. Therefore, later analyses will be quicker.

    Example Script

  • More Documentation : https://angr-angrop.mintlify.app/guides/register-operations
# Example script to find a gadget that moves rdi value to rax
import angr
import angrop  # noqa

BINARY = "kernel.elf"
CACHE = "gadgets.cache"


proj = angr.Project(BINARY, auto_load_libs=False)
rop = proj.analyses.ROP()

#this takes a lot of time
rop.find_gadgets(show_progress=True)

rop.save_gadgets(CACHE)

found_gadgets = []
for g in rop.rop_gadgets:
    for mv in g.reg_moves:
        if mv.from_reg == 'rdi' and mv.to_reg == 'rax' and mv.bits == 64:
            found_gadgets.append(g)
            break
            
            
######### ANGROP FOUND GADGET TO MOVE RDI to RAX. Needed for commit_creds(prepare_kernel_creds(0))

ffffffff82e44de1  nop        
ffffffff82e44de2  sar     dh, cl  
ffffffff82e44de4  push    rdi  
ffffffff82e44de5  nop        
ffffffff82e44de6  sar     dh, cl  
ffffffff82e44de8  pop     rax  
ffffffff82e44de9  nop        
ffffffff82e44dea  sar     dh, cl  
ffffffff82e44dec  ret        
ffffffff82e44ded  nop        
ffffffff82e44dee  sar     dh, cl
####### No way to find it manually ###########################