How Ghidra's Version Tracking and Program Diff Work: A Deep Dive into Binary Comparison
Ghidra compares binary programs by converting them into address sets and running them through a memory-block comparator that identifies byte-level, code-unit, and symbolic differences, while Version Tracking adds a persistent correlation layer that matches functions and data structures across program versions.
Ghidra's reverse engineering platform provides two powerful capabilities for analyzing binary evolution: Program Diff for immediate comparison and Version Tracking (VT) for long-term correlation management. Both systems rely on a shared architecture that treats program differences as address sets computed by the ProgramDiff engine in Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiff.java. Understanding how these subsystems traverse memory blocks, apply filters, and persist matches reveals how the NSA's open-source framework enables analysts to track code changes across firmware versions and malware variants.
Core Diff Engine Architecture
The diff engine is built around three primary components that transform two binaries into comparable address ranges. The ProgramDiff class orchestrates the process, utilizing ProgramMemoryComparator to validate address-space compatibility and ProgramDiffFilter to determine which difference types to calculate.
Key implementation files include:
ProgramDiff.java: Located atGhidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiff.java, this class builds address sets for each difference type including bytes, code units, comments, references, symbols, and functions.ProgramDiffFilter.java: A bitmask interface atGhidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiffFilter.javathat specifies which primary diff types to compute (e.g.,BYTE_DIFFS,CODE_UNIT_DIFFS).ProgramDiffPlugin.java: The UI layer atGhidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.javaimplements theDiffServiceinterface, allowing other plugins to launch diff sessions programmatically.
The ProgramMemoryComparator validates that both programs share compatible memory maps, determining "in-common" versus "only-in-one" address ranges while handling uninitialized versus initialized memory states.
How ProgramDiff Computes Differences
The diff computation follows a four-phase pipeline that operates transaction-free, ensuring the source binaries remain read-only throughout the comparison.
-
Compatibility Check:
ProgramDiffinstantiates aProgramMemoryComparatorto verify address-space alignment. If memory maps diverge, the engine restricts comparison to symbolic differences only. -
Address Set Creation: For each enabled diff type in the filter,
ProgramDiff.createAddressSetinvokes specialized routines likegetByteDifferences,getCodeUnitDifferences, andgetReferenceDifferences. These methods traverse the address ranges returned bygetAddressesInCommon(), populating anAddressSetwith discrepancy start addresses. -
Filtering and Post-Processing: The
computeDiffsToReturnmethod applies user-defined constraints—ignore sets, restrict ranges, and check addresses—to refine the results. -
Result Delivery: The final
AddressSetViewreturns to the UI, which highlights differing addresses and enables navigation between source and destination locations.
The engine processes memory in small chunks of BYTE_DIFF_GRAB_SIZE = 1024 bytes to maintain UI responsiveness and support cancellation via TaskMonitor.
Version Tracking Infrastructure
Version Tracking extends Program Diff with a match-making and markup persistence layer. While Program Diff identifies raw differences, VT correlates them into meaningful entities like functions and data structures, storing analyst decisions in a VTSessionDB database.
Core VT components include:
VTPlugin.java: The entry point atGhidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTPlugin.javathat registers actions (Create, Open, Add, Save, Auto-VT) and instantiates UI providers.VTControllerImpl.java: The model-side controller atGhidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTControllerImpl.javaimplementing theVTControllerservice interface, managing the active session and forwarding events.VTSessionDB.java: A database-backed session implementation atGhidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTSessionDB.javathat persists matches (VTMatch), markup items (VTMarkupItem), status flags, and tags.AddressCorrelatorManager.java: Executes correlation algorithms (function similarity, data-type similarity, instruction similarity) that propose matches for the match table.
The UI providers—including VTMatchTableProvider, VTMarkupItemsTableProvider, and VTFunctionAssociationProvider—render the correlation results and enable acceptance, rejection, and markup application.
VT Session Lifecycle and Persistence
A Version Tracking session progresses through six distinct stages, integrating closely with Ghidra's transaction system for undo/redo support.
Session Creation: Users launch the Create New Session wizard (CreateVersionTrackingSessionAction), which instantiates VTNewSessionWizardModel to capture source and destination programs. VTControllerImpl constructs a VTSessionDB instance, registering it as a DomainObjectListener on both programs to invalidate caches automatically when changes occur.
Correlation Execution: AddressCorrelatorManager runs function, data, and instruction correlators, generating VTMatch objects that populate the match table with proposed correspondences.
User Interaction: Analysts accept, reject, or apply markup from matches. Applying markup creates VTMarkupItem objects—such as function renames or operand changes—that are persisted within the session.
Synchronization: When saving, VTControllerImpl.checkForSave creates a SaveTask to synchronize the destination program with the session database, ensuring consistency between the .vt domain file and underlying binaries.
Undo/Redo Integration: Each bulk operation starts a transaction via session.startTransaction(taskTitle), enabling standard Ghidra undo/redo across VT sessions.
Background tasks like ApplyMatchTask and AutoVersionTrackingTask execute expensive operations without blocking the UI, utilizing the VTTask hierarchy.
Automating Diff and VT via Service APIs
Both subsystems expose service interfaces that scripts and plugins can invoke to programmatically launch comparisons.
Launching a classic Program Diff:
import ghidra.app.services.DiffService;
import ghidra.framework.model.DomainFile;
DiffService diffService = tool.getService(DiffService.class);
if (diffService != null && diffService.launchDiff(otherFile)) {
println("Diff launched successfully.");
}
The DiffService interface is defined at Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/services/DiffService.java.
Managing Version Tracking sessions:
import ghidra.feature.vt.api.main.VTController;
import ghidra.framework.model.DomainFile;
VTController vtController = tool.getService(VTController.class);
// Open existing session
if (vtController.openVersionTrackingSession(vtFile)) {
println("VT session opened.");
}
// Create new session programmatically
vtController.openVersionTrackingSession(srcFile);
vtController.openVersionTrackingSession(dstFile);
vtController.runVTTask(new AutoVersionTrackingTask(vtController));
The VTController implementation resides in VTControllerImpl.java at Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTControllerImpl.java.
Summary
- ProgramDiff provides a read-only, address-set-based comparison engine that computes byte, code-unit, and symbolic differences through
ProgramMemoryComparatorandProgramDiffFilter. - Version Tracking layers correlation algorithms and persistence on top of Program Diff, storing matches and markup decisions in
VTSessionDBvia theVTControllerImplarchitecture. - Both systems utilize chunked memory processing (
BYTE_DIFF_GRAB_SIZE = 1024) and background tasks to maintain UI responsiveness during large binary comparisons. - Service interfaces (
DiffServiceandVTController) enable automation of diff launches and VT session management from Ghidra scripts and plugins. - The UI components (
ProgramDiffPlugin,VTMatchTableProvider,VTMarkupItemsTableProvider) render results through a filter-driven diff engine that supports transaction-based undo/redo.
Frequently Asked Questions
What is the difference between Program Diff and Version Tracking in Ghidra?
Program Diff is a transient, read-only comparison that highlights immediate differences between two binaries using address sets and memory-block comparators. Version Tracking is a persistent workflow that uses correlation algorithms to match functions and data structures across versions, storing analyst decisions (accept/reject matches, applied markup) in a database-backed VTSessionDB that survives across Ghidra sessions.
How does Ghidra's diff engine handle large binaries without freezing the UI?
The ProgramDiff class processes memory in configurable chunks (BYTE_DIFF_GRAB_SIZE = 1024 bytes) and accepts a TaskMonitor parameter for cancellation support. All difference computation runs transaction-free in the background, with heavy operations delegated to VTTask subclasses like AutoVersionTrackingTask, ensuring the interface remains responsive during comparisons.
Can Version Tracking sessions be automated through Ghidra scripts?
Yes. Both Program Diff and Version Tracking expose service interfaces—DiffService and VTController respectively—that scripts can obtain via tool.getService(). Developers can programmatically launch diffs, create VT sessions, run auto-correlators via AutoVersionTrackingTask, and apply markup without manual UI interaction, as implemented in VTControllerImpl.
Where does Ghidra store Version Tracking match and markup data?
VT persists matches (VTMatch objects), markup items (VTMarkupItem), and session metadata in a .vt domain file managed by VTSessionDB at Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTSessionDB.java. This database registers as a DomainObjectListener on both source and destination programs, automatically invalidating caches when underlying program data changes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →