Cross-References, Renaming, and Annotation

25 minIn Progress

Cross-References, Renaming, and Annotation

Why Annotation Is Essential

Raw Ghidra output is filled with auto-generated names like FUN_00401030 and DAT_00403000. These names tell you nothing about what the code does. The act of renaming functions, variables, and data labels as you understand them is what transforms an impenetrable wall of assembly into a readable analysis. Annotation is not optional cleanup -- it is the core analytical process.

The Transformation

Before annotation:

void FUN_00401030(void) {
    FUN_00401080(DAT_00403000, 0x50);
    local_10c = FUN_00401130(0, DAT_00403000, 0x1bb, 0, 0, 3, 0, 0);
    FUN_004010c0(local_10c, DAT_00403200, 0x100);
}

After annotation:

void establish_c2_session(void) {
    xor_decrypt(g_encrypted_c2_domain, 0x50);
    hConnection = InternetConnectA(0, g_encrypted_c2_domain, 443, 0, 0, INTERNET_SERVICE_HTTP, 0, 0);
    send_beacon(hConnection, g_system_fingerprint, 256);
}

The second version tells the complete story: the function decrypts a C2 domain, connects to it on port 443, and sends a system fingerprint beacon. This is the goal of annotation.


Cross-References (XRefs) -- The Analyst's Best Tool

Cross-references answer the fundamental question: "Where is this function, variable, or data used?" They are the single most important navigation mechanism in Ghidra.

Viewing Cross-References

  1. Click on any function name, data label, or symbol
  2. Press X (or right-click > References > Show References To)
  3. A table appears listing every location that references the selected item

XRef Types Explained

TypeSymbolMeaning
Call (c)(c)A CALL instruction targets this function
Data Read (r)(r)This data is read at the referencing location
Data Write (w)(w)This data is written at the referencing location
Unconditional Jump(j)A JMP instruction targets this address
Conditional Jump(j)A conditional branch (Jcc) targets this address

XRef Analysis Strategies

Strategy 1: API-driven analysis (most effective for malware)

  1. Open the Symbol Tree > Imports
  2. Select a suspicious API (e.g., CreateFileA, RegSetValueExA, InternetOpenA)
  3. Press X to see every function that calls this API
  4. Navigate to each caller to understand how the API is used
  5. Rename the calling function based on its purpose

Strategy 2: String-driven analysis

  1. Open Window > Defined Strings to see all strings in the binary
  2. Look for suspicious strings: URLs, file paths, registry keys, commands
  3. Double-click a string to navigate to its location in the data section
  4. Press X on the string to find which function references it
  5. The referencing function reveals how the string is used

Strategy 3: Function popularity analysis

  • Functions with many callers (many incoming XRefs) are utility functions: decryption routines, string builders, logging
  • Functions with many callees (many outgoing calls) are orchestrators: main(), initialization, command dispatchers
  • Functions called from only one location are often specific-purpose: a single persistence mechanism, a single exfiltration method

The Function Call Tree

Ghidra provides Window > Function Call Trees which shows both:

  • Incoming Calls -- every function that calls the current function
  • Outgoing Calls -- every function the current function calls

This is invaluable for understanding a function's role in the overall program architecture without manually tracing each XRef.


Renaming: Building the Narrative

Renaming Functions

  1. Click on the function name (in Listing or Decompiler)
  2. Press L (Label) to open the rename dialog
  3. Type a descriptive name following a consistent convention

Naming conventions for malware analysis:

Function PurposeNaming PatternExample
Network communicationverb_network_objectsend_beacon_http, receive_c2_command
File operationsverb_file_objectdrop_payload_to_temp, read_config_file
Persistenceinstall_persistence_typeinstall_run_key, create_scheduled_task
Crypto/encodingalgorithm_operationxor_decrypt_buffer, base64_decode
Anti-analysischeck_conditioncheck_debugger_present, detect_vm_artifacts
Data collectioncollect_data_typecollect_system_info, harvest_browser_creds

Renaming Variables

  1. In the Decompiler view, right-click a variable
  2. Select Rename Variable (or press L)
  3. Use names that describe the data, not the type

Good variable names: c2_domain, xor_key, file_handle, beacon_interval, encoded_payload Bad variable names: var1, temp, x, buf (too generic to be useful)

Renaming Data Labels

  1. Click on a DAT_XXXXXXXX reference in the Listing or Decompiler
  2. Press L to rename
  3. Use the g_ prefix for global data: g_encrypted_c2_config, g_mutex_name, g_xor_key

Retyping Variables and Parameters

Ghidra's auto-detected types are often too generic. Correcting types dramatically improves decompiler output readability.

How to Retype

  1. In the Decompiler view, right-click a variable or parameter
  2. Select Retype Variable (or press Ctrl+L)
  3. Enter the correct type

Common Retypes

Ghidra Auto-TypeContext ClueCorrect Type
undefined4Return value of CreateFileAHANDLE
undefined4Used in arithmeticint or DWORD
undefined4Passed to RegSetValueEx as typeDWORD (REG_SZ, REG_BINARY, etc.)
undefined *Passed where a string is expectedchar * or LPCSTR
undefined *Passed to VirtualAllocLPVOID
undefined864-bit handle or pointerHANDLE, size_t, LPVOID

Applying Data Type Archives

Ghidra ships with Windows API data type archives that define parameter names and types for thousands of Windows APIs:

  1. Open File > Parse C Source or apply a .gdt archive
  2. For 32-bit: use windows_vs12_32.gdt or winapi_32.gdt
  3. For 64-bit: use windows_vs12_64.gdt or winapi_64.gdt
  4. After applying, Ghidra automatically labels API parameters with their correct names

Analyst Tip: After loading a data type archive, re-run Analysis > One Shot > WindowsPE x86 Propagate External Parameters. This propagates the named parameters to all call sites, turning param_1, param_2, param_3 into lpFileName, dwDesiredAccess, dwShareMode.


Comments: Documenting Your Analysis

End-of-Line (EOL) Comments

  • Click at the end of a disassembly line in the Listing view
  • Press ; (semicolon)
  • Add a short inline explanation

Use for: explaining magic numbers, noting what a register holds, clarifying a non-obvious instruction.

Pre-Comments

  • Right-click in the Listing > Comments > Set Pre-Comment
  • Appears above the instruction as a block comment
  • Use for: section headers, block descriptions, analysis notes

Plate Comments

  • Right-click a function name > Set Plate Comment
  • Appears as a decorative header above the entire function
  • Ideal for function summaries:
/***********************************************************
 * xor_decrypt_buffer
 * Decrypts a buffer using single-byte XOR
 * Params: buffer (char*), key (byte), length (int)
 * Called from: establish_c2_session, load_config
 * Note: Key 0x50 used for C2 config, 0x37 for strings
 ***********************************************************/

Bookmarks: Marking Key Locations

For long analysis sessions (hours or days), bookmarks help you return to important locations:

  1. Click on an address in the Listing view
  2. Press Ctrl+D to add a bookmark
  3. Add a descriptive note: "C2 decryption routine", "persistence install", "command dispatcher"
  4. View all bookmarks via Window > Bookmarks
  5. Double-click any bookmark to navigate directly to it

Suggested Bookmark Categories

CategoryWhat to Bookmark
C2C2 initialization, beacon function, command handler
PersistenceRegistry write, service creation, scheduled task
CryptoDecryption routines, key derivation, encoding functions
IOCHardcoded IPs, domains, file paths, mutex names
TODOFunctions you need to revisit or investigate further

Practical Workflow: Annotating a Function

  1. Select a function from the Symbol Tree or navigate via XRef
  2. Read the Decompiler output to get a high-level understanding
  3. Identify API calls and rename the function based on its purpose
  4. Rename parameters using Windows API documentation as a guide
  5. Retype variables where Ghidra's auto-detection is wrong
  6. Add a Plate Comment summarizing the function
  7. Add EOL comments for non-obvious logic or magic numbers
  8. Bookmark the function if it is important for your analysis report
  9. Press Ctrl+S to save your work
Try it in the shell
Practise this lesson's tooling on its sample in an emulated analyst shell. Output is pre-recorded — nothing executes.

Suggested triage steps

  1. 1

    Pick an anchor worth xref-ing

    This shell cannot show cross-references — Ghidra does that. What it can give you is the anchors: each command string this backdoor dispatches on is referenced from the handler you would rename.

  2. 2

    Pick a second anchor, an API this time

    recv is where attacker input enters. In Ghidra its xrefs are the shortest route to every function that matters here.

  3. 3

    Know what the renamed functions should add up to

    The summary your annotated listing ought to describe once the handlers have names.

analyst@lab:~emulated · nothing executes

MAA analyst shell — emulated. Nothing executes.

Type 'help', or click a step on the left.

$
Cross-References, Renaming, and Annotation | Malware Analysis Academy