Navigating Disassembly & the Decompiler
The CodeBrowser: Your Analysis Workspace
The Ghidra CodeBrowser is where all reverse engineering happens. It presents multiple synchronized views of the binary, letting you examine the same code from different perspectives simultaneously. Understanding how to navigate these views efficiently is the difference between productive analysis and frustration.
The Listing View (Disassembly)
The Listing window occupies the center of the CodeBrowser and displays the raw disassembled instructions. This is the ground truth of what the binary does.
Anatomy of a Listing Line
Address Bytes Mnemonic Operands Comment
---------- -------------- ---------- ------------------ ------------------
004010a0 55 PUSH EBP ; save frame pointer
004010a1 8b ec MOV EBP,ESP ; set up stack frame
004010a3 83 ec 20 SUB ESP,0x20 ; allocate 32 bytes
004010a6 6a 00 PUSH 0x0 ; uType = MB_OK
004010a8 68 00 30 40 00 PUSH 0x403000 ; "Hello"
004010ad e8 4e ff ff ff CALL MessageBoxA
| Column | Meaning |
|---|---|
| Address | Virtual memory address of the instruction |
| Bytes | Raw machine-code opcodes (toggle with Edit > Tool Options > Listing Fields) |
| Mnemonic | The instruction name (PUSH, MOV, CALL, SUB, etc.) |
| Operands | The instruction's parameters -- registers, immediates, memory references |
| Comment | Analyst-added or auto-generated annotations |
Reading x86 Instruction Format
x86 assembly uses the format: MNEMONIC DESTINATION, SOURCE
This means data flows right to left:
MOV EAX, 5-- move the value 5 into EAXADD ECX, EAX-- add EAX to ECX, storing the result in ECXCMP EAX, 0-- compare EAX with 0 (sets processor flags)
Essential Instruction Categories
| Category | Instructions | What They Do |
|---|---|---|
| Data Movement | MOV, LEA, PUSH, POP, XCHG | Move data between registers, memory, and the stack |
| Arithmetic | ADD, SUB, MUL, DIV, INC, DEC | Mathematical operations |
| Logic | AND, OR, XOR, NOT, SHL, SHR | Bitwise operations (XOR reg, reg is a common zeroing idiom) |
| Comparison | CMP, TEST | Compare values and set flags (do not store a result) |
| Control Flow | JMP, Jcc, CALL, RET | Change execution flow -- branches, function calls, returns |
| String/Rep | REP MOVSB, REP STOSB | Memory copy/fill operations |
Key Insight:
XOR EAX, EAXdoes not mean "encrypt." It is the standard compiler idiom for setting a register to zero, equivalent toEAX = 0. You will see this pattern constantly.
The Decompiler View
The Decompile window (right side) reconstructs C-like pseudocode from the assembly. It is your primary tool for understanding what a function does at a high level.
Decompiler Output Naming Conventions
| Name Pattern | Meaning | What to Do |
|---|---|---|
FUN_004010a0 | Auto-named function at address 0x004010a0 | Rename after you understand its purpose |
DAT_00403000 | Auto-named global data at address 0x00403000 | Rename to describe the data (e.g., g_c2_url) |
local_XX | Local variable (stack-allocated, offset XX from frame) | Rename to describe usage |
param_X | Function parameter (X = ordinal position) | Rename to match API documentation |
iVar1, uVar2 | Typed local variables (i = int, u = unsigned) | Rename for clarity |
Example: Before and After Annotation
Before:
void FUN_00401080(void) {
undefined4 local_10c;
undefined4 local_8;
FUN_00401000(DAT_00403100, 0x50);
local_10c = FUN_00401130(0, DAT_00403100, 0x1bb, 0, 0, 3, 0, 0);
// ...
}
After analyst annotation:
void establish_c2_connection(void) {
HINTERNET hConnect;
HINTERNET hInternet;
xor_decrypt(g_encrypted_c2_url, 0x50);
hConnect = InternetConnectA(0, g_encrypted_c2_url, 443, 0, 0, INTERNET_SERVICE_HTTP, 0, 0);
// ...
}
Decompiler Limitations
- Auto-generated names are meaningless -- you must rename as you analyze
- Type inference can be wrong -- an
intmight actually be aHANDLEorDWORDflag - Optimized code may decompile into confusing expressions
- Obfuscated or anti-analysis code produces unreliable output
- Struct access may appear as pointer arithmetic until you define the struct type
Synchronized Navigation
The Listing and Decompiler views are synchronized: clicking a line in one view highlights the corresponding code in the other. This dual-view approach is the core of effective Ghidra analysis.
The Recommended Workflow
- Start in the Decompiler -- read pseudocode to understand the high-level logic and control flow
- Switch to the Listing when something looks wrong, unclear, or when the decompiler oversimplifies
- Click a variable in the Decompiler to highlight all locations where it is used
- Click an address in the Listing to see the corresponding pseudocode line
- Double-click a CALL target to navigate into the called function
Navigation Keyboard Shortcuts
| Action | Shortcut | Description |
|---|---|---|
| Go to address | G | Jump to a specific memory address |
| Go back | Alt+Left | Return to previous location (like browser back) |
| Go forward | Alt+Right | Move forward in navigation history |
| Search strings in memory | S then search | Find string patterns in the binary |
| Find references | Ctrl+Shift+F | Search for references to a value |
| Next function | Ctrl+Down | Jump to the start of the next function |
| Previous function | Ctrl+Up | Jump to the start of the previous function |
Analyst Tip: Use
Alt+LeftandAlt+Rightconstantly. Ghidra maintains a navigation history like a web browser. When you dive into a function and want to return,Alt+Lefttakes you back instantly.
The Program Trees and Symbol Tree
Program Trees (Section Layout)
The Program Trees panel shows the PE section layout of the loaded binary:
| Section | Typical Contents |
|---|---|
.text | Executable code -- this is where the disassembly lives |
.data | Initialized global variables (read/write) |
.rdata | Read-only data: strings, constants, import tables |
.rsrc | Resources: icons, version info, embedded files |
.reloc | Relocation table (for ASLR support) |
Symbol Tree (Organized Navigation)
The Symbol Tree is your primary navigation panel:
| Category | Contents | Analysis Value |
|---|---|---|
| Imports | DLLs and their imported functions | Reveals what external APIs the binary uses |
| Exports | Functions the binary exposes | Critical for DLL analysis |
| Functions | All discovered functions | Browse and search all code |
| Labels | Named addresses | Includes data labels, string references |
| Namespaces | Grouped symbols (DLL names, classes) | Organized hierarchy of symbols |
Filtering Imports for Malware Analysis
The Imports section is one of the first places to check. Look for DLLs that reveal malware capabilities:
| DLL | Capability Indicator |
|---|---|
WININET.DLL | HTTP/FTP network communication |
WS2_32.DLL | Raw socket networking |
ADVAPI32.DLL | Registry, services, crypto, security |
CRYPT32.DLL | Encryption and certificate operations |
URLMON.DLL | URL download operations |
SHELL32.DLL | Shell execution, file operations |
The Graph View
For complex functions with many branches, the Graph View provides a visual representation of control flow:
- Open via Window > Function Graph or the graph icon in the toolbar
- Each block represents a sequence of instructions ending in a branch
- Green arrows indicate the "true" branch of a conditional jump
- Red arrows indicate the "false" branch
- Blue arrows indicate unconditional jumps
The Graph View is especially useful for understanding switch statements, nested if-else chains, and loop structures at a glance.
Practical Exercise
- Open an imported binary in Ghidra's CodeBrowser
- Navigate to the
entrypoint via the Symbol Tree - In the Listing view, identify the function prologue (
PUSH EBP/MOV EBP,ESP) - Switch to the Decompiler view and read the pseudocode for the entry function
- Double-click a CALL instruction to follow it into a subroutine
- Use
Alt+Leftto navigate back - Open the Imports in the Symbol Tree and browse the imported DLLs
- Select any imported function and press
Xto see where it is referenced
