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 Tree → Functions: 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.