Skip to content

Instantly share code, notes, and snippets.

@bkerler
Last active August 8, 2026 17:28
Show Gist options
  • Select an option

  • Save bkerler/301c33b363ad3ff02160b3a11ddc247e to your computer and use it in GitHub Desktop.

Select an option

Save bkerler/301c33b363ad3ff02160b3a11ddc247e to your computer and use it in GitHub Desktop.
Reverse engineering master skill
name RE-SKILLS.md
description Use whenever the user says "reverse engineer", "ida", or "ghidra"; also use for generic reverse-engineering workflows involving IDA Pro, Ghidra, emulation, Frida, USB/libusb, ADB, reporting, and durable note-taking. Keep guidance target-agnostic and avoid vendor-, platform-, subsystem-, or device-family-specific assumptions.

RE-SKILLS.md

Generic reverse-engineering workflow guidance for future sessions. This skill is target-agnostic: do not encode vendor-, platform-, subsystem-, or device-family-specific assumptions here.

Mandatory Tool Routing

  • For all IDA Pro work, use ida-rpc. Do not rely on manual UI-only analysis when an RPC query or database edit is needed.
  • For all Ghidra work, use ghidra-rpc. Use it for decompiler inspection, symbol work, comments, memory maps, and function metadata when available.
  • For all emulation tasks, use emu-rpc. Use emulation to test specific hypotheses, not as a substitute for static analysis.
  • For USB/libusb and ADB device access, run commands outside the sandbox when required and serialize access to each device.
  • Write all durable notes in structured form under /home/$USER/basic-memory/. Prefer topic directories and stable titles over ad hoc local text files.
  • For all android and ios app reverse engineering tasks, prefer using frida.

Core Rules

  • Start by recovering state: read existing notes, reports, scripts, loader/import settings, and prior command logs before making new changes.
  • Separate verified facts from hypotheses. Mark hypotheses clearly and update them when evidence changes.
  • Prefer primary evidence: disassembly, decompiler output, traces, protocol captures, source code, hardware logs, and reproducible test output.
  • Keep experiments narrow. Change one variable at a time and record what changed.
  • Do not repeat a failed path unless a concrete new variable or new evidence changes the expected outcome.
  • Preserve enough detail for replay: tool version, input file hash/path, base address, architecture, endian mode, command, options, device mode, and output.
  • Write findings promptly in /home/$USER/basic-memory/ and, when the task calls for it, in an RE/*.md report.
  • After each testing or verification task completes, use basic memory to take notes what was successful and what failed with the aim to prevent any mistakes in the future.

Structured Notes

  • Store durable notes under /home/$USER/basic-memory/, grouped by domain or project, for example reverse/<topic>/ or reverse/meta/.
  • Include these fields when practical: goal, target artifact, environment, tools, exact commands, observations, conclusion, failed paths, and next action.
  • Record both successes and dead ends. A failed experiment is useful only if the exact setup and reason for failure are captured.
  • Keep secrets, credentials, private tokens, and unrelated personal metadata out of notes.

IDA Pro via ida-rpc

  • Use ida-rpc to inspect decompiler output, disassembly, functions, symbols, xrefs, strings, memory segments, and type information.
  • Confirm loader settings before analysis: architecture, bitness, endian mode, image base, segment permissions, entrypoints, and processor mode.
  • Create meaningful segments for code, data, register windows, stacks, heaps, shared memory, firmware tables, and manually mapped blobs when known.
  • Do not trust the first auto-analysis result. Check function boundaries, mode switches, literal pools, jump tables, veneers, and exception vectors manually.
  • Rename functions only when behavior is understood. Prefer action-oriented names such as parse_command, copy_from_fifo, or dispatch_request.
  • Rename variables and arguments to describe role, not guessed type. Correct pointer, array, struct, and integer widths when the decompiler is misleading.
  • Add comments at decision points: command dispatch, trust boundaries, hardware access, crypto calls, memory copies, validation checks, and error handling.
  • Use cross-references aggressively. A single string, constant, or callsite is weak evidence; corroborate with multiple references.
  • When matching source to binary, compare call structure, constants, register access order, error strings, and side effects, not just symbol names.
  • Keep IDA loader and helper scripts deterministic and rerunnable. Validate with a small known sample.

Ghidra via ghidra-rpc

  • Use ghidra-rpc for decompiler inspection, program metadata, symbol updates, comments, function boundaries, and memory-map work.
  • Verify language/compiler selection and image base before relying on decompiler output.
  • Create memory blocks with explicit permissions and names. Map peripheral/register areas as separate blocks when they are referenced by code.
  • Re-run analysis selectively after correcting memory maps, function starts, or processor mode; do not rely on stale decompilation.
  • Use data types early for repeated structures: command blocks, descriptors, headers, tables, contexts, queues, and register layouts.
  • Rename functions and variables in the database as conclusions mature. Keep comments concise and evidence-backed.
  • Use function graph, references, and symbol trees to validate dispatch paths and initialization order.
  • When importing raw firmware, preserve the original file offset to runtime address relationship in /home/$USER/basic-memory/ notes.

Emulation via emu-rpc

  • Use emu-rpc for emulation setup, execution, memory/register inspection, tracing, and hooks.
  • Emulate to test a specific hypothesis, not to replace static analysis.
  • Define the minimum environment needed: architecture, mode, base address, mapped ranges, stack, input buffers, hooks, and expected stop condition.
  • Stub hardware reads/writes explicitly. Log unmapped accesses and decide whether each one is a required peripheral, a mapping bug, or unreachable code.
  • Keep emulation deterministic: fixed inputs, fixed memory snapshots, bounded instruction counts, and reproducible traces.
  • Compare emulator behavior against static control flow and real-device logs when available.
  • Do not brute force secrets or opaque values. Use emulation to understand data flow, validation logic, and state transitions.
  • Save short traces around important branches, indirect calls, and memory/register accesses.

Frida and Dynamic Instrumentation

  • Confirm process state first: spawn vs attach, architecture, package/process name, library load timing, and whether the runtime is available.
  • Separate hook installation from hook effectiveness. A hook install log is not proof; require an observed event or changed behavior.
  • For Java targets, wait for the Java runtime before Java hooks. For native targets, wait for the library/module before attaching exports or offsets.
  • Use small probes before large hook bundles. Validate one known method/function, then expand.
  • Log structured events with enough context: thread, timestamp, function, arguments, return value, exception, and relevant buffers.
  • Guard hooks against null pointers, invalid Java objects, unavailable classes, overloaded signatures, and repeated installation.
  • Keep generated scripts syntax-checkable and split reusable helpers from target-specific hooks.
  • When hooks fail, distinguish runtime unavailable, symbol not found, wrong overload, wrong process, timing issue, and event-delivery failure.

USB, libusb, and ADB

  • Serialize USB/libusb access. Do not run parallel tools against the same device.
  • Run USB/libusb and ADB device access outside the sandbox when required by the environment.
  • Before a hardware command, record device identity and state: bus/address, VID:PID, product strings, interfaces, endpoints, mode, permissions, and power-cycle state.
  • Claim only the needed interface and release/close handles on exit. Handle disconnects and re-enumeration as normal events.
  • Treat timeouts, stalls, short reads, and disconnects as distinct observations. Do not collapse them into a generic failure.
  • Use conservative timeouts first, then tune based on observed device behavior.
  • For write-capable commands, state read/write direction, target, expected size, and failure impact before running the command.
  • With ADB, check server/device state first: adb devices, authorization, transport ID, root state, SELinux state when relevant, and whether another server instance owns the device.
  • If ADB or libusb reports permission or daemon errors, fix the host access path before changing target logic.
  • Power cycling is a state change. Record it, wait for stable re-enumeration, and re-identify the device before continuing.

Reporting

  • Every report should include scope, inputs, environment, method, findings, failed paths, remaining unknowns, and exact reproduction steps.
  • Include file hashes or stable paths for binaries and dumps when practical.
  • Use short evidence excerpts instead of long raw logs. Keep full logs in files when they matter.
  • End with next actions that are falsifiable: what to test, why, what output would confirm it, and what output would reject it.

Git repositories

  • For each new session, if a .git folder exists in the current directory, create a new branch using git with an appropriate name and commit after each successful step so that any future failures can be reverted to a working state. NEVER push, keep the commits local.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment