208 lines
7.3 KiB
C#
208 lines
7.3 KiB
C#
// Copyright (c) StellaOps. All rights reserved.
|
|
// Licensed under AGPL-3.0-or-later. See LICENSE in the project root.
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
namespace StellaOps.BinaryIndex.Ghidra;
|
|
|
|
/// <summary>
|
|
/// Bridge interface for ghidriff Python tool integration.
|
|
/// ghidriff provides automated binary diff reports using Ghidra.
|
|
/// </summary>
|
|
public interface IGhidriffBridge
|
|
{
|
|
/// <summary>
|
|
/// Run ghidriff to compare two binaries.
|
|
/// </summary>
|
|
/// <param name="oldBinaryPath">Path to the older binary version.</param>
|
|
/// <param name="newBinaryPath">Path to the newer binary version.</param>
|
|
/// <param name="options">ghidriff configuration options.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>Diff result with added, removed, and modified functions.</returns>
|
|
Task<GhidriffResult> DiffAsync(
|
|
string oldBinaryPath,
|
|
string newBinaryPath,
|
|
GhidriffDiffOptions? options = null,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Run ghidriff to compare two binaries from streams.
|
|
/// </summary>
|
|
/// <param name="oldBinary">Stream of the older binary version.</param>
|
|
/// <param name="newBinary">Stream of the newer binary version.</param>
|
|
/// <param name="options">ghidriff configuration options.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>Diff result with added, removed, and modified functions.</returns>
|
|
Task<GhidriffResult> DiffAsync(
|
|
Stream oldBinary,
|
|
Stream newBinary,
|
|
GhidriffDiffOptions? options = null,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Generate a formatted report from ghidriff results.
|
|
/// </summary>
|
|
/// <param name="result">The diff result to format.</param>
|
|
/// <param name="format">Output format.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>Formatted report string.</returns>
|
|
Task<string> GenerateReportAsync(
|
|
GhidriffResult result,
|
|
GhidriffReportFormat format,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Check if ghidriff is available (Python + ghidriff installed).
|
|
/// </summary>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>True if ghidriff is available.</returns>
|
|
Task<bool> IsAvailableAsync(CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Get ghidriff version information.
|
|
/// </summary>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>Version string.</returns>
|
|
Task<string> GetVersionAsync(CancellationToken ct = default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Options for ghidriff diff operation.
|
|
/// </summary>
|
|
public sealed record GhidriffDiffOptions
|
|
{
|
|
/// <summary>
|
|
/// Path to Ghidra installation (auto-detected if not set).
|
|
/// </summary>
|
|
public string? GhidraPath { get; init; }
|
|
|
|
/// <summary>
|
|
/// Path for Ghidra project files (temp dir if not set).
|
|
/// </summary>
|
|
public string? ProjectPath { get; init; }
|
|
|
|
/// <summary>
|
|
/// Whether to include decompiled code in results.
|
|
/// </summary>
|
|
public bool IncludeDecompilation { get; init; } = true;
|
|
|
|
/// <summary>
|
|
/// Whether to include disassembly listing in results.
|
|
/// </summary>
|
|
public bool IncludeDisassembly { get; init; } = true;
|
|
|
|
/// <summary>
|
|
/// Functions to exclude from comparison (by name pattern).
|
|
/// </summary>
|
|
public ImmutableArray<string> ExcludeFunctions { get; init; } = [];
|
|
|
|
/// <summary>
|
|
/// Maximum number of concurrent Ghidra instances.
|
|
/// </summary>
|
|
public int MaxParallelism { get; init; } = 1;
|
|
|
|
/// <summary>
|
|
/// Maximum analysis time in seconds.
|
|
/// </summary>
|
|
public int TimeoutSeconds { get; init; } = 600;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Result of a ghidriff comparison.
|
|
/// </summary>
|
|
/// <param name="OldBinaryHash">SHA256 hash of the old binary.</param>
|
|
/// <param name="NewBinaryHash">SHA256 hash of the new binary.</param>
|
|
/// <param name="OldBinaryName">Name/path of the old binary.</param>
|
|
/// <param name="NewBinaryName">Name/path of the new binary.</param>
|
|
/// <param name="AddedFunctions">Functions added in new binary.</param>
|
|
/// <param name="RemovedFunctions">Functions removed from old binary.</param>
|
|
/// <param name="ModifiedFunctions">Functions modified between versions.</param>
|
|
/// <param name="Statistics">Comparison statistics.</param>
|
|
/// <param name="RawJsonOutput">Raw JSON output from ghidriff.</param>
|
|
public sealed record GhidriffResult(
|
|
string OldBinaryHash,
|
|
string NewBinaryHash,
|
|
string OldBinaryName,
|
|
string NewBinaryName,
|
|
ImmutableArray<GhidriffFunction> AddedFunctions,
|
|
ImmutableArray<GhidriffFunction> RemovedFunctions,
|
|
ImmutableArray<GhidriffDiff> ModifiedFunctions,
|
|
GhidriffStats Statistics,
|
|
string RawJsonOutput);
|
|
|
|
/// <summary>
|
|
/// A function from ghidriff output.
|
|
/// </summary>
|
|
/// <param name="Name">Function name.</param>
|
|
/// <param name="Address">Function address.</param>
|
|
/// <param name="Size">Function size in bytes.</param>
|
|
/// <param name="Signature">Decompiled signature.</param>
|
|
/// <param name="DecompiledCode">Decompiled C code (if requested).</param>
|
|
public sealed record GhidriffFunction(
|
|
string Name,
|
|
ulong Address,
|
|
int Size,
|
|
string? Signature,
|
|
string? DecompiledCode);
|
|
|
|
/// <summary>
|
|
/// A function diff from ghidriff output.
|
|
/// </summary>
|
|
/// <param name="FunctionName">Function name.</param>
|
|
/// <param name="OldAddress">Address in old binary.</param>
|
|
/// <param name="NewAddress">Address in new binary.</param>
|
|
/// <param name="OldSize">Size in old binary.</param>
|
|
/// <param name="NewSize">Size in new binary.</param>
|
|
/// <param name="OldSignature">Signature in old binary.</param>
|
|
/// <param name="NewSignature">Signature in new binary.</param>
|
|
/// <param name="Similarity">Similarity score.</param>
|
|
/// <param name="OldDecompiled">Decompiled code from old binary.</param>
|
|
/// <param name="NewDecompiled">Decompiled code from new binary.</param>
|
|
/// <param name="InstructionChanges">List of instruction-level changes.</param>
|
|
public sealed record GhidriffDiff(
|
|
string FunctionName,
|
|
ulong OldAddress,
|
|
ulong NewAddress,
|
|
int OldSize,
|
|
int NewSize,
|
|
string? OldSignature,
|
|
string? NewSignature,
|
|
decimal Similarity,
|
|
string? OldDecompiled,
|
|
string? NewDecompiled,
|
|
ImmutableArray<string> InstructionChanges);
|
|
|
|
/// <summary>
|
|
/// Statistics from ghidriff comparison.
|
|
/// </summary>
|
|
/// <param name="TotalOldFunctions">Total functions in old binary.</param>
|
|
/// <param name="TotalNewFunctions">Total functions in new binary.</param>
|
|
/// <param name="AddedCount">Number of added functions.</param>
|
|
/// <param name="RemovedCount">Number of removed functions.</param>
|
|
/// <param name="ModifiedCount">Number of modified functions.</param>
|
|
/// <param name="UnchangedCount">Number of unchanged functions.</param>
|
|
/// <param name="AnalysisDuration">Time taken for analysis.</param>
|
|
public sealed record GhidriffStats(
|
|
int TotalOldFunctions,
|
|
int TotalNewFunctions,
|
|
int AddedCount,
|
|
int RemovedCount,
|
|
int ModifiedCount,
|
|
int UnchangedCount,
|
|
TimeSpan AnalysisDuration);
|
|
|
|
/// <summary>
|
|
/// Report output format for ghidriff.
|
|
/// </summary>
|
|
public enum GhidriffReportFormat
|
|
{
|
|
/// <summary>JSON format.</summary>
|
|
Json,
|
|
|
|
/// <summary>Markdown format.</summary>
|
|
Markdown,
|
|
|
|
/// <summary>HTML format.</summary>
|
|
Html
|
|
}
|