RopBot / Angrop
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

- 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 ###########################