Skip to content
Back to skills

Exploitation

ASecurity

Search for ROP/JOP/COP gadgets in a binary via r2xsql's rop_gadgets and rop_gadget_instructions tables, and look up syscall names/numbers via the syscalls reference table. Use when building an exploit chain, hunting for stack-pivot/syscall/write-what-where primitives, resolving a target's syscall table, or answering 'what gadgets are available here'.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 17, 2026
toolsbashsql

Works with

  • cli

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned October 17, 2026

npx -y skills add NetVar1337/agent-skill-canon --skill exploitation --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Exploitation?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Exploitation
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/netvar1337-exploitation/badge)](https://www.skillsdirectory.com/skills/netvar1337-exploitation)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: exploitation
description: "Search for ROP/JOP/COP gadgets in a binary via r2xsql's rop_gadgets and rop_gadget_instructions tables, and look up syscall names/numbers via the syscalls reference table. Use when building an exploit chain, hunting for stack-pivot/syscall/write-what-where primitives, resolving a target's syscall table, or answering 'what gadgets are available here'."
allowed-tools:
  - Bash
  - Read
---

## When to use

Pick this skill when the task is finding usable code sequences for
control-flow hijacking, not understanding normal program logic:

- return-, call-, or jump-terminated gadget search (`rop_gadgets`)
- reading a specific gadget's instruction sequence (`rop_gadget_instructions`)
- filtering by radare2's own gadget-class vocabulary (`pivot`, `syscall`,
  `www` / write-what-where, …)

For ordinary disassembly/decompilation of a function, use `disassembly` or
`decompiler` instead — this skill is specifically about short, out-of-context
instruction sequences ending in a control-flow transfer.

## Tables

| table | source | columns |
|---|---|---|
| `rop_gadgets` | `/gj` / `/gRj` / `/gCj` / `/gJj` | `addr`, `retaddr`, `size`, `classes`, `ninstrs`; hidden `pattern`, `terminator`, `klass`, `max_results` |
| `rop_gadget_instructions` | same search, flattened | `gadget_addr`, `seq`, `addr`, `size`, `opcode`, `type`; same four hidden inputs as `rop_gadgets` |

No predicate is required on either table — an unfiltered scan is a real,
bounded answer (a few hundred milliseconds on a typical binary). The search
scope defaults to executable memory only (`io.maps.x`), not radare2's own
CLI default (`io.maps`, which also matches non-code data and can report
false-positive "gadgets" built from string tables).

## Common queries

```sql
-- every gadget, cheapest possible query
SELECT COUNT(*) FROM rop_gadgets;

-- return-terminated gadgets mentioning "pop" (classic stack-cleanup gadget)
SELECT printf('0x%x', addr) AS at, classes, ninstrs FROM rop_gadgets
WHERE terminator = 'ret' AND pattern = 'pop';

-- stack pivots (change esp/rsp to attacker-controlled data)
SELECT printf('0x%x', addr) AS at, retaddr, classes FROM rop_gadgets
WHERE klass = 'pivot';

-- syscall gadgets
SELECT printf('0x%x', addr) AS at, classes FROM rop_gadgets
WHERE klass = 'syscall';

-- write-what-where primitives
SELECT printf('0x%x', addr) AS at, classes FROM rop_gadgets
WHERE klass = 'www';

-- shortest gadgets first (fewer side effects to account for)
SELECT printf('0x%x', addr) AS at, size, ninstrs, classes
FROM rop_gadgets ORDER BY ninstrs ASC LIMIT 20;

-- read one gadget's instruction sequence, once you have its address
-- (apply the SAME filters used to find it -- see Caveats)
SELECT seq, printf('0x%x', addr) AS at, opcode, type
FROM rop_gadget_instructions
WHERE gadget_addr = 0x140001284 AND terminator = 'ret'
ORDER BY seq;

-- cap a broad, unfiltered sweep
SELECT printf('0x%x', addr) AS at, classes FROM rop_gadgets
WHERE max_results = 50;
```

## `klass` vocabulary

radare2's own gadget-class names, used verbatim (never invent a parallel
vocabulary): `ret`, `jop`, `cop`, `cond.always`, `cond.controlled`,
`syscall`, `pivot`, `memread`, `memwrite`, `www` (write-what-where),
`rww` (read-what-where), `signal`, `mov`, `ldconst`, `arithm`, `logic`,
`shift`, `cmp`, `nop`, `arithm_ct`. `cond.always`/`cond.controlled` only
appear when the radare2 session has `gadget.esil` enabled (off by default).

## Caveats

- **The unfiltered ("any" terminator) gadget set is not a strict superset of
  a terminator-filtered one, address for address.** radare2's gadget
  construction walks backward from qualifying end instructions and consumes
  start bytes per pass, so a start address one search finds is not
  guaranteed to also start a gadget in a differently-filtered pass. Always
  apply the **same** `pattern`/`terminator`/`klass` to both `rop_gadgets` and
  `rop_gadget_instructions` when relating a row in one to a row in the
  other — an unfiltered lookup on one side and a filtered one on the other
  can silently return nothing even though the gadget clearly exists.
- **Do not join `rop_gadget_instructions` against many `rop_gadgets` rows at
  once.** There is no radare2 primitive that resolves a single gadget by
  address, so a query like `rop_gadgets g JOIN rop_gadget_instructions gi ON
  gi.gadget_addr = g.addr` with no narrow filter re-runs the whole gadget
  search once per outer row. For "every gadget's instructions", query
  `rop_gadget_instructions` directly with the filters you want (it already
  carries `gadget_addr`) instead of joining. For one specific gadget,
  `WHERE gadget_addr = <addr> AND <same filters>` is cheap (one search).
- `retaddr` is the gadget's own *terminating instruction's address* — not a
  resolved branch target, despite the name. A "R" (return-class) gadget's
  terminating instruction can be something other than a literal `ret`
  mnemonic (e.g. `int3`, which radare2's own return-class test still
  counts) — `rop_gadget_instructions.type` reflects that classification.
- `opcode` on `rop_gadget_instructions` is rendered disassembly text (same
  renderer as `instructions.disasm`), not raw bytes.
- `classes` is free (no extra command, no config gate) — it is always safe
  to select even on a broad scan.

## Syscall reference

`syscalls` is a **static** per-`(arch,bits,os)` name/number lookup table
(backed by `asj` — **not** `aslj`, which does not exist). It is a reference
dictionary keyed by the current target's architecture/OS, **not** derived
from the binary's own bytes — the same table exists whether or not the
binary makes any syscalls at all.

| table | source | columns |
|---|---|---|
| `syscalls` | `asj` | `name`, `num`, `arch`, `os` (`arch`/`os` echo `asm.arch`/`asm.os`) |

```sql
-- what does syscall number 60 do on this target?
SELECT name FROM syscalls WHERE num = 60;

-- what's the syscall number for a name you already know?
SELECT num FROM syscalls WHERE name = 'execve';

-- full table (small -- hundreds of rows, one command)
SELECT name, num FROM syscalls ORDER BY num;
```

**Where this fits with the gadget search above:** `rop_gadgets WHERE klass =
'syscall'` finds *gadgets that end in a syscall instruction*; `syscalls`
tells you what a given number/name *means* once you have one. They answer
different questions and are not joined to each other (there is no shared
key — a gadget's `classes` field doesn't carry a resolved syscall number).

**Where syscall *instructions* are in the binary is a different question,
already answered without this table**: `SELECT addr FROM instructions WHERE
op_type = 'swi'` finds every syscall/software-interrupt instruction site —
no `syscalls` lookup needed for that.

**What this table does NOT do:** resolve which syscall number a specific
`swi` instruction actually makes. radare2's own static emulation cannot
reliably determine that on realistic compiler-generated code — the number
is typically set in a caller and reloaded across a function-call boundary
into a syscall wrapper (e.g. glibc's `syscall()`), which single-function
emulation does not follow. This project deliberately does not ship a
best-effort/guessed resolution column for that — a value that is sometimes
right and silently wrong is worse than none. Don't try to derive it by
joining `syscalls` against `instructions`/`esil_ops`; there is nothing
reliable to join on.

Files in this skill

  • SKILL.md7.3 KB
  • agents/openai.yaml43 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.