Const24.GhidraSharp.Server
0.7.0
dotnet add package Const24.GhidraSharp.Server --version 0.7.0
NuGet\Install-Package Const24.GhidraSharp.Server -Version 0.7.0
<PackageReference Include="Const24.GhidraSharp.Server" Version="0.7.0" />
<PackageVersion Include="Const24.GhidraSharp.Server" Version="0.7.0" />
<PackageReference Include="Const24.GhidraSharp.Server" />
paket add Const24.GhidraSharp.Server --version 0.7.0
#r "nuget: Const24.GhidraSharp.Server, 0.7.0"
#:package Const24.GhidraSharp.Server@0.7.0
#addin nuget:?package=Const24.GhidraSharp.Server&version=0.7.0
#tool nuget:?package=Const24.GhidraSharp.Server&version=0.7.0
GhidraSharp
Drive headless Ghidra from C#: import a binary, analyse it, decompile, walk cross-references, and write findings back. A typed .NET client over a small gRPC bridge, with no Python in the chain.
There is no first-party C# binding to Ghidra. The community route is the Python ghidra_bridge, and
Ghidra's own cross-language protocol, TraceRmi, is scoped to debugging rather than static analysis.
This fills that gap with a small typed surface over the core reverse-engineering operations.
┌─────────────────────┐ gRPC (protobuf) ┌─────────────────────────────┐
│ Const24.GhidraSharp │ ──────────────> │ GhidraSharpServer (Java) │
│ C# client (net8/10) │ <────────────── │ Ghidra-as-library, headless │
└─────────────────────┘ └─────────────────────────────┘
Requirements
- .NET 8 or later. The client targets
net8.0andnet10.0; the MCP tool isnet10.0. - JDK 21 or later, to run the server.
- Ghidra 12.1 or later, with
GHIDRA_INSTALL_DIRpointing at it.
The JDK and Ghidra are not shipped with the packages.
Install
Client and server, with the server bundled into your app's output so dotnet publish carries it and
StartAsync finds it:
dotnet add package Const24.GhidraSharp.Server
Client only, if you connect to a server you run yourself:
dotnet add package Const24.GhidraSharp
Quickstart
From a raw firmware dump to its decompiled functions. No server to start, no project to manage.
using Const24.GhidraSharp;
// StartAsync finds the bundled server, runs it, and stops it on dispose.
await using var server = await GhidraServer.StartAsync();
var ghidra = server.Client;
// Import and analyse. languageId is the target's processor — here a Renesas SH-2A.
await ghidra.OpenProgramAsync(@"C:\firmware\ecu.bin", languageId: "SuperH:BE:32:SH-2A");
var functions = await ghidra.ListFunctionsAsync();
foreach (var fn in functions)
Console.WriteLine($" {fn.EntryPoint} {fn.Name}");
var dec = await ghidra.DecompileAtAsync(functions[0].EntryPoint);
Console.WriteLine(dec.CCode);
languageId is the one Ghidra-specific input: SuperH:BE:32:SH-2A, ARM:LE:32:v7,
x86:LE:64:default. ListLanguagesAsync("SuperH") enumerates them if you are not sure.
That example is transient — nothing touches disk. CreateProjectAsync writes a Ghidra project you
reopen in seconds and can save renames into. For a headerless image it also takes baseAddress and
entryPoint, which matter as much as the processor: at the loader's default of 0, the image's own
pointers resolve to nothing and analysis finds almost no code.
What is bridged
The surface grows one RPC at a time, as consumers need it. Every client method is the name below plus
Async.
Program
Ping— liveness, Ghidra version, server versionOpenProgram— open an analysed project, or import a binary transientlyCreateProject— import into a persistent project (.gpr/.rep), analysed and saved; takesbaseAddress/entryPointfor a headerless imageSaveProgram/CloseProgram— persist edits; release the on-disk lockListLanguages— the processor languages Ghidra supports, i.e. where alanguageIdcomes fromListMemoryBlocks— the program's sections: name, range, size, permissions
Code
DecompileFunction/DecompileFunctions— function → C; the second streams a whole programListFunctions— every function and its callees, built to query client-side with LINQGetFunction— one function in full: typed signature, parameters, locals, callersGetInstructions— the disassembly listing: mnemonic, operands, bytesGetInstructionDetail— one instruction's structured operands and raw PCodeReadBytes— raw program memory, for a pure-C# byte/table layer
Symbols, references, data
ListSymbols/GetSymbolsAt— symbols by name or addressGetReferencesTo/GetReferencesFrom— cross-referencesGetFunctionReferences— every reference out of a function's body, its table data-refs includedListImports/ListExports— the import table grouped by library, and the exports; each import carries the EXTERNAL-space address thatGetReferencesToresolves to its call sitesFindStrings— defined strings whose decoded text matches, each with its xrefs — the concept → code loopGetDataAt/ListDataTypes/ApplyDataType— defined data and data typesGetDataType— a named struct/union/enum's full member layout
Writing findings back — needs writable: true, in memory until saved, and each returns the value
it replaced
RenameSymbol— name a function, label or globalSetComment/GetComments— comments, all five Ghidra typesSetBookmark/GetBookmarks— bookmarks
The escape hatch
RunScript— run any GhidraScript and capture its output
Architecture-agnostic by construction: it forwards a Ghidra language id, so the same code drives any
processor Ghidra supports. Every result is a hand-written, documented record — the generated gRPC
types stay internal, and the XML docs explain each Ghidra concept to a .NET developer who has never
opened Ghidra. A failed call throws GhidraException carrying Ghidra's own diagnosis; a cancelled one
throws OperationCanceledException, as a .NET caller expects.
Not bridged, on purpose: the live Ghidra object graph, the decompiler's internals beyond C text and
raw PCode (no HighFunction), and in-process scripting semantics.
Going further
Links are absolute so they work from nuget.org as well as from the repository.
| A whole corpus, or your own long-running server | docs/bridge/using.md |
| Driving Ghidra from an LLM agent | docs/mcp/using.md |
| What changed, and what to fix when upgrading | CHANGELOG.md |
| How it is built, and why | docs/ |
For agents
A stdio MCP server that exposes the same surface as 30 tools an agent calls directly. It keeps several
programs open at once, caches analysed programs by content hash, and journals every rename and comment
outside that cache so the analysis survives a cache wipe. run_script is off by default and not even
listed when off.
dotnet tool install -g Const24.GhidraSharp.Mcp
{ "mcpServers": { "ghidra": {
"command": "ghidrasharp-mcp",
"args": ["--server-dir", "C:\\path\\to\\ghidrasharp-server"]
} } }
The rest — flags, headerless images, what each tool is for — is in docs/mcp/using.md.
Correctness
GhidraSharp and pyghidra drive the same Ghidra with the same calls, so for any operation the two must
agree byte for byte; anything else is a bridge bug.
bench/ is that comparison, over eight
capabilities and six instruction sets, on targets it builds from tools you already have — no ROMs, no
private data, one command:
python bench/verify.py
The latest run is in
bench/REPORT.md.
Security
The server is unauthenticated and exposes RunScript — arbitrary GhidraScript — plus file and memory
access. It binds to loopback only. It is a local tool driven by a client on the same host. Never
expose its port. The rest of the posture, and how to report something, is in
SECURITY.md.
Build and test
dotnet build GhidraSharp.slnx
dotnet test tests/GhidraSharp.Tests -- --filter-not-trait "Category=Integration"
cd server && ./gradlew test
The integration tier needs a real Ghidra and is gated; it skips silently without one.
CLAUDE.md is the maintainer's guide and
covers that and the rest of the build.
License
Apache-2.0, the same as Ghidra, which the server links against.
Learn more about Target Frameworks and .NET Standard.
-
net8.0
- Const24.GhidraSharp (>= 0.7.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.