Preamble

The topic plan called for a reverse-engineering pass. This month is deliberately small: a trivial C++ binary—maybe with a hard-coded string or obvious strcmp—imported into NSA GHIDRA, free and scriptable. The goal is literacy, not a CTF trophy.


GHIDRA step by step (first session)

These steps assume a small ELF or PE you built yourself or are allowed to analyze.

  1. Create a project
    File → New Project → pick a folder. GHIDRA stores analysis metadata here, not a copy of every binary (you still point at the file on disk).

  2. Import the binary
    File → Import File → select the executable. Accept sensible defaults for format and loader options unless you know you need something special (packed malware, odd segments). Double-click it in the project window to open the CodeBrowser.

  3. Run analysis
    When prompted, run auto-analysis with the default analyzers. Wait until the status bar quiets down. Skipping this leaves missing functions, weak xref graphs, and a decompiler that invents less structure than it could.

  4. Orient in the UI

    • Listing: linear disassembly.
    • Decompile: C-like output for the function under the cursor (Window → Decompile if it is hidden).
    • Symbol TreeFunctions: browse recovered symbols; main or _start / entry is a usual anchor.
    • Defined Strings: literal pool for C++ ("usage:", error messages) and RTTI-ish noise.
  5. Navigate
    Double-click a call target or jump to enter a function. G (Go To) jumps to an address or symbol. X on an instruction or data item shows cross-references (who calls this, who reads this string).

  6. Annotate as you read
    Rename functions (L), variables, and data (Label from context menu). Drop comments (;) on blocks you understand. The decompiler picks up renames; your future self will too.

  7. Optional exports
    When scripting or diffing, you may export listings or use the Script Manager (Python/Java). That is optional for the literacy pass.

The old shorthand still holds: import → let analysis finish → find main (or entry) → follow xrefs from strings → rename and comment as you go.


Tracing an if / else from a C++ binary

Compilers lower conditionals to compare + conditional jump (sometimes cmov or SETcc for small branches, but jumps are the teaching case). Your job is to connect decompiler if to Listing instructions and not confuse C++ structure with what is actually on disk.

Steps

  1. Start in the decompiler
    In main (or the function you care about), find an if () block. Note the predicate (what is being compared) and whether there is an else.

  2. Sync to disassembly
    Click inside the if line or the opening brace; GHIDRA usually moves the cursor in the Listing to the corresponding address. If it does not, click in the Listing on the same function and step through until the graph/decompile highlight aligns.

  3. Identify the compare
    Look for CMP, TEST (often test reg, reg for null/zero), or SUB used as a compare. For C++ objects you may see calls (strcmp, operator==, virtual dispatch) followed by a test of the return value and a jump.

  4. Read the branch
    After the compare, find the conditional jump: JZ/JNZ, JE/JNE, JL/JGE, etc. (Intel mnemonics vary; ARM uses CMP + B.cond.) One successor is the fall-through path; the other is the taken branch. Map mentally: fall-through = then or else?—it depends on how the compiler inverted the condition. Trust labels and the decompiler, then verify by reading both sides.

  5. Follow both edges
    Step into the not-taken path (fall-through) and the taken path (jump target). Each should match one arm of if / else. If the optimizer merged arms or duplicated tails, the decompiler may show goto or shared merge blocks; the graph view (Show Function Graph) makes that obvious.

  6. Find the merge point
    Both arms often reconverge at a label. That is where the if statement ends in the high-level sense. Loops add back-edges; an if inside a loop shows as a conditional that does not dominate the function exit.

What to look out for

  • Signed vs unsigned compares use different jump families (JL vs JB). Misreading that inverts your mental model of edge cases.
  • Short-circuit && / || is multiple compares and jumps, not one big if.
  • Inlined code can make “the if” disappear into a sea of straight-line instructions until you spot the CMP/TEST + Jcc pattern again.
  • Exceptions and STL wrap logic in outlined helpers; you may land in std::-ish symbols. Follow one level, rename, return to the caller.
  • Decompiler lies: odd types, fused compares, or hand-written assembly can produce plausible-looking C that is wrong. When something feels off, anchor on the Listing and treat the decompiler as a hypothesis.
  • No symbols: if still exists as jumps; use strings, constant pools, and call patterns to orient.

GHIDRA vs IDA Pro vs another decompiler (Binary Ninja)

All three are professional-grade; the differences are mostly workflow, cost, and how much you live in the decompiler pane.

Aspect GHIDRA IDA Pro (+ Hex-Rays) Binary Ninja
Cost / license Free, open source Paid; decompiler is an add-on (Hex-Rays) Paid; personal licenses exist
Decompiler Built-in (P-code based) Hex-Rays is the industry reference for readability Built-in HLIL; strong IL story
Project model Project folder + imported files Database (.idb / .i64) per binary Database with good undo
Scripting Java + Python IDC + Python (IDA 7+); huge third-party corpus Python API; clean and consistent
Learning curve Busy UI, but one download gets everything Familiar to many analysts; power features are deep Smaller surface area; graph and IL are first-class

IDA Pro (with Hex-Rays) often wins on maturity of signatures, team habits, and “I need this exact plugin” ecosystems. The decompiler output is what many people mentally compare others against. GHIDRA gives a full pipeline at zero cost: disassembly, decompiler, decent type editor, scripting—excellent for learning and many production workflows. Binary Ninja sits in the middle for many users: polished UX, HLIL that is pleasant to read, and an intermediate language that rewards people who like lifting and programmatic analysis.

For tracing an if/else, the procedure is the same idea everywhere: compare → conditional branch → two subgraphs → merge. GHIDRA’s Listing ↔ Decompile sync is strong; IDA’s F5 decompiler and graph are the classic pairing; Binary Ninja’s HLIL and graph side-by-side are similarly effective. Pick based on budget, team standard, and whether you care more about plugin ecosystem (IDA), all-in-one free stack (GHIDRA), or API/IL ergonomics (Binary Ninja).


Ethics and scope

This is reconnaissance on binaries you are allowed to analyze—your own builds, CTF artifacts, or licensed samples. It is not a license to poke production systems.


Conclusion

Comfort with disassembly pays dividends when debugging native crashes, JNI boundaries, or supply-chain questions. sqlmap Basics with a Defensive Mindset moves to sqlmap with a defensive lens.