Reverse Engineering Platforms: IDA, Ghidra, and Cutter
- Reverse Engineering Platforms: IDA, Ghidra, and Cutter
- What an RE Platform Does
- The Three Platforms at a Glance
- Installation and Licensing
- The Concepts You Need First
- Getting Started with IDA
- Getting Started with Ghidra
- Getting Started with Cutter
- Side-by-Side: Common Operations
- A Suggested First-Binary Workflow
- Scripting and Automation
- Choosing a Tool
- Troubleshooting
- Further Reading
This page introduces the three reverse engineering (RE) platforms used in this course: IDA Classroom (Hex-Rays), Ghidra (NSA), and Cutter (the Rizin-based GUI). It assumes you are already comfortable reading x86 assembly and have worked through basic malware triage — extracting strings, checking imports, examining PE headers, and running file/hash/entropy checks. This page picks up where triage ends: you have an interesting binary, and now you need to understand what it actually does.
All three tools attempt to solve the same core problem — turn a binary back into something a human can reason about — and they share most of the same vocabulary. Once you know how to navigate one, the other two feel familiar. The differences show up in cost, licensing, decompiler quality, debugger integration, and scripting ecosystems.
What an RE Platform Does
A reverse engineering platform is a workbench for static binary analysis. Every one of these tools does the following things, just with different names and hotkeys:
- Loader — parses the executable format (PE, ELF, Mach-O, raw binary, firmware image) and maps sections into a virtual address space the same way the OS loader would.
- Disassembler — converts machine code into assembly, identifies function boundaries, and recovers stack frames and parameter layouts.
- Cross-referencer — builds a bidirectional index of “who calls this function,” “who reads this string,” and “where does this jump come from.” This is the single most important feature for analysis work; you will use it constantly.
- Decompiler — translates assembly into C-like pseudocode. All three tools now have a decompiler, though their quality and availability differ.
- Database — stores your annotations (renames, comments, type information, structure definitions) so your analysis survives closing the program.
- Scripting / plugin interface — lets you automate repetitive tasks and build on top of the platform.
The workflow is the same everywhere: load → auto-analyze → navigate from known starting points (imports, strings, entry point) → rename and comment as you understand code → iterate until the behavior is clear.
The Three Platforms at a Glance
| Feature | IDA Classroom | Ghidra | Cutter |
|---|---|---|---|
| License / cost | Free for educational use (per-student license file, non-transferable) | Free and open source (Apache 2.0) | Free and open source (GPLv3) |
| Developer | Hex-Rays | US National Security Agency | Rizin Organization |
| First released | 1991 (IDA); Classroom edition recent | 2019 (publicly released) | 2018 |
| Implementation | C++ native binary | Java (requires JDK 21) | Qt/C++ frontend over Rizin (C) |
| Primary view | Graph view (toggle with Space) | Listing window + Decompiler side-by-side | Disassembly + Graph + Decompiler, freely arranged |
| Decompiler | Cloud-based (Classroom: x86, x86-64, ARM-32, ARM-64) | Native, full-featured, recovers C++ classes | Ghidra decompiler via rz-ghidra + jsdec |
| Debugger | Yes (local and remote; gdb, WinDbg, Bochs, etc.) | Yes (added in recent versions; less mature) | Yes (via Rizin; gdb/lldb/windbg/native) |
| Architecture support | Classroom: x86/x64/ARM32/ARM64 | ~30 architectures including PPC, MIPS, SH-4, 6502, PIC | Very broad (Rizin inherits from radare2’s esil engine) |
| Scripting | IDC, IDAPython | Java plugins, Jython/PyGhidra | Rizin commands, Python plugins, C++ plugins |
| Project model | Single .idb / .i64 database per binary |
Project contains many binaries; supports shared multi-user repos | Rizin project files (.rzdb) |
The one-sentence summary: IDA is the industry reference with the polished UI and the best signature database; Ghidra has the best decompiler and scales to multi-binary projects; Cutter is the lightweight Rizin-backed option that lets you drop to a command-line console whenever the GUI gets in the way.
Installation and Licensing
IDA Classroom
See the IDA Classroom installation page. Each student receives a pre-activated license registered to their PSU email; download the .hexlic file from the My Hex-Rays portal and place it in the install directory or home folder.
Ghidra
Download a pre-built release from the Ghidra GitHub releases page. Requires JDK 21 (64-bit). Extract the archive and launch:
# Linux / macOS
./ghidraRun
# Windows
ghidraRun.bat
On REMnux, Ghidra is pre-installed; just type ghidra at the shell.
Cutter
Download from the Cutter releases page. Platform packages:
- macOS:
.dmg - Windows:
.zip(portable) or.msi - Linux:
.AppImage
chmod +x Cutter-v*-x86_64.AppImage
./Cutter-v*-x86_64.AppImage
Cutter bundles Rizin; no separate installation required. REMnux ships with Cutter as well.
The Concepts You Need First
Before walking through each tool, here are the concepts that show up in all three. Learn them once and the platform-specific terminology follows.
Loaders and Base Addresses
When a platform loads a binary, it parses the container format and lays the sections out in virtual memory exactly as the OS loader would. This is why the addresses you see (0x00401000, 0x10001757) match what you’d see at runtime in a debugger. Malware frequently appends shellcode or encrypted blobs after the normal PE sections; the loader won’t include these by default. All three platforms offer a “raw binary” or “manual load” option to force every byte into the analysis view.
Auto-Analysis
After loading, each tool runs an analysis pass that starts at the entry point, follows control flow through call/jmp/jcc instructions, marks function boundaries, recovers stack frames, and identifies strings and data. This takes anywhere from a few seconds (small EXEs) to several minutes (statically linked binaries). Let auto-analysis finish before you start working — navigating into unanalyzed regions will confuse you.
Disassembly Views: Linear vs. Graph
- Linear (text) view — instructions shown sequentially by address, the way they sit in the file. Good for seeing data interspersed with code, inspecting jump tables, or scanning long basic blocks.
- Graph view — each function rendered as a flowchart of basic blocks connected by control-flow edges. Good for understanding conditionals, loops, and switch structures at a glance.
In all three tools, edge color conveys branch semantics (taken/not-taken/unconditional). Upward-pointing edges are loops.
Decompilers
A decompiler lifts assembly into C-like pseudocode. This is not the original source — variable names are synthesized, control-flow may be restructured, and types are guessed from usage — but it is dramatically easier to read than assembly for most functions. The three tools handle decompilation differently:
- IDA Classroom ships cloud-based decompilation for x86, x86-64, ARM-32, and ARM-64.
- Ghidra has a native decompiler widely considered the strongest free option; it recovers C++ classes and handles unusual calling conventions well.
- Cutter can use jsdec (built in, approximate) or delegate to the Ghidra decompiler via the
rz-ghidraplugin (same engine as Ghidra proper).
Workflow tip: read the decompiler output first to form a hypothesis, then drop to assembly to verify the details. Don’t trust the decompiler blindly — optimized or obfuscated code produces misleading pseudocode.
Cross-References (XRefs)
Every tool tracks two categories:
- Code xrefs — who calls this function, who jumps here
- Data xrefs — who reads this string, who writes this global
Cross-references are how you move through a binary productively. You find an interesting API call in Imports → follow its xrefs to see every caller → rename the caller based on what it’s doing → follow its xrefs, and so on.
Renaming and Commenting
Auto-generated names like sub_401000, FUN_00401000, fcn.00401000, var_8, and arg_4 tell you nothing. As soon as you understand what a function or variable does, rename it. All three tools propagate the new name to every reference. Add comments for the why (non-obvious constraints, observed behavior, references to external documentation) rather than the what (which the code already says).
The Database / Project
Your analysis is saved in a per-binary database:
- IDA:
.idb(32-bit) or.i64(64-bit) - Ghidra: inside the project directory, a
.gprfile plus storage - Cutter:
.rzdbRizin project file
Save often. None of these tools have a crash recovery model as reliable as modern IDEs, and large malware samples can provoke interesting bugs.
Getting Started with IDA
Loading a File
Drag the binary onto IDA or use File → Open. IDA’s loader dialog asks you to confirm the file format, processor, and loading options. Accepting defaults works for almost all PE/ELF malware; the two options worth knowing are:
- Binary File — forces IDA to treat the input as raw bytes (no PE parsing). Use this when analyzing shellcode dumps or firmware.
- Manual Load — lets you load each PE section including the PE header itself, which malware sometimes abuses to hide code.
Let auto-analysis complete before doing anything else (the progress bar at the bottom).
The Interface
The main window is the disassembly view, which opens in graph mode by default. Press Space to toggle between graph and text (linear) modes.
Subviews you will use constantly, found under View → Open Subviews:
- Functions — all recognized functions; L flag marks library functions identified by FLIRT (skip these)
- Imports — every API the binary calls; usually the first thing to check
- Exports — only relevant for DLLs
- Strings — ASCII and Unicode strings longer than 5 characters by default
- Names — every named location (functions, labels, globals)
- Structures — define or import struct layouts to improve readability
At the bottom of the toolbar is the navigation band, a color-coded map of the binary’s address space. Dark blue is user-written code — focus your analysis there.
Navigation
| Action | Shortcut |
|---|---|
| Jump to address or name | G |
| Jump to file offset | Jump → Jump to File Offset |
| Toggle graph / text mode | Space |
| Open pseudocode (decompile current function) | F5 (opens in new tab) or Tab (toggles) |
| Back / forward in history | Esc / Ctrl+Enter |
| Follow link (jump target, string, etc.) | double-click or Enter |
| Xrefs to the item under cursor | X |
| Xrefs from the current address | Ctrl+J |
Annotation
| Action | Shortcut |
|---|---|
| Rename | N |
| Local comment | : |
| Repeatable comment (shows at xrefs) | ; |
| Define as code | C |
| Define as data | D |
| Define as string | A |
| Undefine | U |
| Create function | P |
| Standard symbolic constant | M |
The M shortcut is especially useful when you see push 80000000h — pressing M lets you replace the literal with GENERIC_READ, instantly revealing that the nearby call is CreateFile.
Decompiler
Press F5 on any function to open the Pseudocode view in a new tab. Renames and type edits in the pseudocode propagate to the disassembly view and vice versa. Right-click a variable → Set lvar type to apply a C type; this often cleans up casts and reveals structure access.
Getting Started with Ghidra
Creating a Project
Ghidra organizes work into projects, which hold one or more binaries plus your annotations. On first launch, create a project via File → New Project → Non-Shared Project, pick a directory, and name it. Projects persist across sessions; you can import many binaries into the same project and cross-reference between them (useful for malware families with multiple components).
Importing and Analyzing
Drag a binary into the project window, or use File → Import File. Ghidra auto-detects the format and opens an import dialog; the defaults are almost always correct. Once imported, double-click the binary to open it in the CodeBrowser, then say Yes to the auto-analyze prompt. The default analysis options are reasonable; for malware, leave Decompiler Parameter ID and Stack enabled.
The CodeBrowser
The CodeBrowser is Ghidra’s main analysis environment. The default layout shows:
- Listing (center) — the disassembly, similar to IDA’s text mode. Ghidra does not have a separate “graph mode” by default; graphs open in a separate window.
- Decompiler (right) — live C-like pseudocode for the current function. Click anywhere in the decompiler and the listing cursor follows; click in the listing and the decompiler highlights the matching line. This bidirectional linking is Ghidra’s signature feature.
- Symbol Tree (left) — organized view of Imports, Exports, Functions, Labels, Classes, and Namespaces. Expand Imports first when analyzing malware.
- Data Type Manager (lower left) — all known types; drag a struct onto a variable to apply it.
- Program Tree (upper left, tab) — view by section (
.text,.data, etc.) - Console (bottom) — script output and messages.
Opening the Function Graph (Window → Function Graph, or the icon in the toolbar) gives you a graph view equivalent to IDA’s graph mode.
Navigation
| Action | Shortcut |
|---|---|
| Go to address or symbol | G |
| Back / forward in history | Alt+Left / Alt+Right |
| Show references to cursor | Ctrl+Shift+F (or right-click → References → Show References To) |
| Show references from cursor | Ctrl+Shift+E |
| Open Function Graph | Window → Function Graph |
| Open Decompiler if closed | Window → Decompiler |
| Find next function | Ctrl+Shift+F in Functions window |
Annotation
| Action | Shortcut |
|---|---|
| Rename label / function | L |
| Add comment | ; (opens comment dialog with all five types) |
| Cycle byte size (byte → word → dword → qword) | B |
| Cycle string type | “ |
| Cycle float type | F |
| Define function at cursor | F (in listing when on undefined bytes) |
| Disassemble | D |
| Undo | Ctrl+Z |
Ghidra’s comment system offers five distinct types (EOL, Plate, Pre, Post, Repeatable), all reachable from the single ; dialog. Use plate comments to mark section headers (“— Decryption routine —”) and EOL comments for inline notes.
Decompiler Workflow
The decompiler is where you spend most of your time in Ghidra. Key operations:
- Rename variable — right-click in decompiler → Rename Variable (or press L). Propagates everywhere.
- Retype variable — right-click → Retype Variable. Change
undefined4toint, or apply a struct pointer. - Edit function signature — right-click the function name at the top → Edit Function Signature. Fix parameter counts and types here first; correct types propagate through all callers.
- Highlight — click a variable; every use in the current function highlights. Click an if-condition; the corresponding compare-and-branch highlights in the listing.
Multi-Binary Projects
Ghidra’s killer feature for malware work is multi-binary projects. Import a dropper, its second-stage payload, and any dropped DLLs into the same project. You can cross-reference symbols across binaries and use Version Tracking (Tools → Version Tracking) to compare variants of the same family.
Getting Started with Cutter
Loading a File
Launch Cutter and pick Open File. The load dialog shows detected architecture, bits, OS, and base address — usually correct. For shellcode or firmware, toggle Load in write mode, Load bin info, and Use virtual addressing as needed. Click Ok and Cutter runs Rizin’s auto-analysis (aaa under the hood).
The Interface
Cutter’s UI is fully dockable: every panel (widget) can be dragged, detached, or closed. The default layout includes:
- Disassembly — main code view; press Space to toggle between linear and graph.
- Decompiler — select between jsdec (Rizin’s built-in, lightweight) and Ghidra (via
rz-ghidra) in the view selector. Use Ghidra for anything non-trivial. - Functions — searchable function list with offsets, sizes, and xref counts.
- Imports / Exports / Strings / Symbols — standard triage pivots.
- Graph Overview — miniature control-flow graph of the current function.
- Hexdump — byte-level view with computed hashes (MD5, SHA1, SHA256, CRC32) and format conversion to C, Python, JSON.
- Console — direct Rizin command line; anything the GUI can’t do, Rizin commands can.
Close unused panels via Windows → [panel name] and save your preferred layout with Windows → Save Layout.
Navigation
| Action | Shortcut |
|---|---|
| Go to address / symbol | G |
| Toggle disassembly / graph | Space |
| Back / forward | Alt+Left / Alt+Right |
| Follow reference at cursor | Enter |
| Show xrefs | X |
| Seek to entry point | . (period) |
| Toggle decompiler sync with cursor | sync button in decompiler toolbar |
Annotation
| Action | Shortcut |
|---|---|
| Rename | N |
| Add comment | ; |
| Define function | P |
| Undefine function | U |
| Edit function signature | right-click → Edit function |
| Add flag (label) | Shift+N |
The Rizin Console
The console at the bottom exposes the full Rizin command surface. A few commands worth knowing:
| Command | Effect |
|---|---|
aaa |
Re-run full analysis |
afl |
List all functions |
pdf @ main |
Print disassembly of main |
izz |
List all strings (not just those in data sections) |
axt <addr> |
Show xrefs to address |
/ <string> |
Search for string |
? |
Help |
Rizin uses an operation-on-what @ where syntax. pdf @ 0x401000 prints function disassembly at 0x401000. This is Cutter’s escape hatch when the GUI doesn’t expose what you need.
Decompiler Selection
Switch decompiler engines via the dropdown in the Decompiler panel header:
- jsdec — fast, JavaScript-based, approximate. Fine for small functions.
- rz-ghidra (Ghidra) — the real Ghidra decompiler. Slower on first run (loads language definitions) but produces output comparable to Ghidra itself.
For serious analysis, always use the Ghidra backend.
Side-by-Side: Common Operations
| Task | IDA | Ghidra | Cutter |
|---|---|---|---|
| Go to address | G | G | G |
| Toggle graph/text | Space | Window → Function Graph | Space |
| Decompile current function | F5 | always visible (right pane) | always visible (Decompiler panel) |
| Rename | N | L | N |
| Add comment | : or ; | ; | ; |
| Xrefs to | X | Ctrl+Shift+F | X |
| Back / forward | Esc / Ctrl+Enter | Alt+Left / Alt+Right | Alt+Left / Alt+Right |
| Undefine | U | C (clear) | U |
| Define as function | P | F | P |
| Define as string | A | “ cycle | Ay (via console) |
| Standard constant | M | right-click → Set Equate | right-click → Set as |
| Apply struct type | T | drag from Data Type Manager | right-click → Structure offset |
| Scripting console | IDAPython (File → Script File) | Window → Python | Console (Rizin commands) or Python plugin |
A Suggested First-Binary Workflow
This workflow applies to all three tools. The specifics of how you do each step are covered above.
- Triage recap: before loading anything, you should already know the file’s hash, architecture, import table highlights, and any obvious strings. If you don’t, go back to triage.
- Load and let auto-analysis finish. Don’t touch anything until the progress bar clears.
- Scan Imports. Flag network APIs (
WSAStartup,connect,HttpSendRequest), process manipulation (CreateProcess,WriteProcessMemory,CreateRemoteThread), persistence (RegSetValue,CreateService), crypto (CryptEncrypt,BCryptEncrypt), and file I/O (CreateFile,WriteFile). - Scan Strings. Look for URLs, file paths, registry keys, mutex names, user-agent strings, ransom notes, and error messages. Double-click interesting strings to jump to the code that uses them.
- Find
main(orWinMain, orDllMain). Start from the entry point and follow calls. - Decompile each called function. Rename as you identify behavior:
sub_401200becomesresolve_c2_domain;FUN_00402000becomesbuild_rc4_key. - Follow xrefs from imports you flagged in step 3. If
CreateProcessAis only called in one place, you want to read that function first. - Save frequently.
- When stuck, toggle views. If the decompiler output is confusing, read the assembly. If the assembly is too tedious, check the decompiler. If both are confusing, look at the graph — the shape of the control flow sometimes reveals the construct (loop, switch, error handling) faster than either text view.
Scripting and Automation
All three platforms expose their analysis database through scripting APIs. You don’t need these for basic analysis, but they become essential once you start dealing with obfuscated or packed samples.
| Platform | Languages | Typical use case |
|---|---|---|
| IDA | IDC (built-in), IDAPython | Automated string decryption, batch renaming, custom analyzers |
| Ghidra | Java, Jython, PyGhidra (CPython3) | Headless batch analysis, custom decompiler transformations, Version Tracking |
| Cutter | Python plugins, C++ plugins, Rizin commands | Quick one-liners via the console, Python scripts for custom widgets |
For classroom exercises, IDAPython scripts are usually the most approachable. Ghidra’s Script Manager (Window → Script Manager) ships with hundreds of example scripts that demonstrate the API.
Choosing a Tool
You will use all three in this course. In practice, working analysts tend to settle on a primary tool and use others as needed:
- Start with IDA Classroom for in-class exercises; its graph view and documentation are the most polished, and the Hex-Rays decompiler is strong for x86/x64.
- Reach for Ghidra when you need a better decompiler, when analyzing a multi-binary family, when you want multi-user collaboration via Ghidra Server, or when working on a non-x86 architecture the Classroom license doesn’t cover.
- Reach for Cutter when you want the Rizin command line alongside a GUI, when working on ARM/MIPS/RISC-V firmware, or when you want a lightweight tool that starts in under a second.
You’ll develop strong opinions about which one feels right; they’re all correct opinions. What matters is that you can navigate any binary handed to you in any of the three.
Troubleshooting
| Problem | Cause / fix |
|---|---|
| IDA hangs on load | Very large binary or statically-linked runtime; wait, or disable some analysis passes in Options → General → Analysis |
Ghidra decompiler shows UNRECOVERED_JUMPTABLE |
Indirect jump (virtual call or computed jump); right-click the jump → Create Jump Table, or fix parameter types upstream |
| Cutter decompiler is empty or wrong | Switch backend from jsdec to Ghidra in the decompiler toolbar |
| Functions tab is empty | Auto-analysis didn’t run or was skipped; trigger it manually (IDA: Options → Analysis; Ghidra: Analysis → Auto Analyze; Cutter: aaa in console) |
| Can’t see PE header / resource section | Load manually (IDA: Manual Load checkbox; Ghidra: Import format options; Cutter: toggle Load bin info) |
| Variable names don’t propagate | You renamed in the decompiler but the disassembly didn’t update, or vice versa — save the project and refresh the view |
Further Reading
Official documentation
- Hex-Rays IDA documentation — Basic Usage, Graph View, Cross-References, Subviews
- IDA Pro Basic Usage
- IDA Graph View reference
- Ghidra on GitHub — official source, releases, and security advisories
- Introduction to Ghidra Student Guide — NSA’s own class material, best free Ghidra tutorial
- Cutter documentation — official user and developer docs
- Rizin documentation — the command reference for Cutter’s console
- Igor’s Tip of the Week — Cross-references (Hex-Rays blog)
Books
- Practical Malware Analysis (Sikorski & Honig, No Starch Press, 2012) — chapter 5 covers IDA in depth; see Advanced Static Analysis summary.
- The IDA Pro Book, 2nd Edition (Chris Eagle, No Starch Press, 2011) — the definitive IDA reference.
- The Ghidra Book (Chris Eagle & Kara Nance, No Starch Press, 2020) — the Ghidra counterpart.
Community writeups
- Intro to Cutter — GoggleHeadedHacker
- How to Use Ghidra to Reverse Engineer Malware — Varonis
- Ghidra vs. IDA Pro — hackmag
- Comparison of Reverse-Engineering Tools — reHex Ninja
- Get started with the Ghidra reverse-engineering framework — TechTarget
Source repositories
- NationalSecurityAgency/ghidra
- rizinorg/cutter
- rizinorg/rizin
- rizinorg/rz-ghidra — the Ghidra decompiler integration used by Cutter