use std::{marker::PhantomData, path::Path};
use crate::{
Address, AddressRange, AnalysisOptions, AnalysisReport, ControlFlowGraph, DataInfo,
DecompileOptions, DecompileResult, Error, ExportInfo, FunctionControlFlowGraph,
FunctionDescriptor, FunctionSummary, GhidraRuntime, ImportInfo, InstructionInfo, LaunchOptions,
MemoryBlockInfo, ProgramCallGraph, ProgramLoadInfo, ProgramLoadOptions, ProgramMetadata,
ProgramOpenOptions, ReferenceInfo, Result, SourceMapInfo, SymbolInfo,
bridge::{self, JavaHandle},
};
#[derive(Clone)]
pub struct Ghidra {
runtime: GhidraRuntime,
}
pub struct Project {
runtime: GhidraRuntime,
handle: Option<JavaHandle>,
}
pub struct Program<'project> {
runtime: GhidraRuntime,
project: JavaHandle,
handle: Option<JavaHandle>,
_project: PhantomData<&'project Project>,
}
pub struct LoadedProgram<'project> {
pub program: Program<'project>,
pub info: ProgramLoadInfo,
}
pub struct ProgramTransaction<'program> {
runtime: GhidraRuntime,
handle: Option<JavaHandle>,
_program: PhantomData<&'program ()>,
}
pub struct Decompiler<'program> {
runtime: GhidraRuntime,
handle: Option<JavaHandle>,
_program: PhantomData<&'program ()>,
}
impl Ghidra {
pub fn start(options: LaunchOptions) -> Result<Self> {
Ok(Self {
runtime: bridge::start(options)?,
})
}
pub fn with_runtime(runtime: GhidraRuntime) -> Result<Self> {
bridge::attach_bridge(&runtime)?;
Ok(Self { runtime })
}
pub fn runtime(&self) -> &GhidraRuntime {
&self.runtime
}
pub fn open_or_create_project(
&self,
project_location: impl AsRef<Path>,
project_name: &str,
) -> Result<Project> {
let handle =
bridge::open_or_create_project(&self.runtime, project_location.as_ref(), project_name)?;
Ok(Project {
runtime: self.runtime.clone(),
handle: Some(handle),
})
}
}
impl Project {
pub fn open_or_import_program(&self, binary: impl AsRef<Path>) -> Result<Program<'_>> {
Ok(self
.open_or_import_program_with_options(ProgramLoadOptions::new(binary.as_ref())?)?
.program)
}
pub fn open_program(&self, options: ProgramOpenOptions) -> Result<Program<'_>> {
let project = self.handle()?;
let handle = bridge::open_program(&self.runtime, project, &options.path)?;
Ok(Program {
runtime: self.runtime.clone(),
project,
handle: Some(handle),
_project: PhantomData,
})
}
pub fn open_or_import_program_with_options(
&self,
options: ProgramLoadOptions,
) -> Result<LoadedProgram<'_>> {
let project = self.handle()?;
let loaded = bridge::open_or_import_program(&self.runtime, project, &options)?;
Ok(LoadedProgram {
program: Program {
runtime: self.runtime.clone(),
project,
handle: Some(loaded.handle),
_project: PhantomData,
},
info: loaded.info,
})
}
pub fn close(mut self) -> Result<()> {
self.close_inner()
}
fn handle(&self) -> Result<JavaHandle> {
self.handle
.ok_or_else(|| Error::closed_handle("Ghidra project"))
}
fn close_inner(&mut self) -> Result<()> {
if let Some(handle) = self.handle.take() {
bridge::close_project(&self.runtime, handle)?;
}
Ok(())
}
}
impl Drop for Project {
fn drop(&mut self) {
let _ = self.close_inner();
}
}
impl Program<'_> {
pub fn name(&self) -> Result<String> {
bridge::program_name(&self.runtime, self.handle()?)
}
pub fn analyze(&self) -> Result<()> {
self.analyze_with_options(AnalysisOptions::default())?;
Ok(())
}
pub fn analyze_with_options(&self, options: AnalysisOptions) -> Result<AnalysisReport> {
bridge::analyze_program(&self.runtime, self.project, self.handle()?, options)
}
pub fn save(&self) -> Result<()> {
bridge::save_program(&self.runtime, self.project, self.handle()?)
}
pub fn functions(&self) -> Result<Vec<FunctionDescriptor>> {
let json = bridge::list_functions(&self.runtime, self.handle()?)?;
parse_json(&json, "failed to parse Ghidra function descriptors")
}
pub fn parse_address(&self, address: &str) -> Result<Address> {
let json = bridge::parse_address(&self.runtime, self.handle()?, address)?;
parse_json(&json, format!("failed to parse Ghidra address {address}"))
}
pub fn function_at(&self, address: &Address) -> Result<Option<FunctionDescriptor>> {
let Some(json) = bridge::function_at(&self.runtime, self.handle()?, address.as_str())?
else {
return Ok(None);
};
parse_json(
&json,
format!("failed to parse Ghidra function descriptor at {address}"),
)
}
pub fn function_containing(&self, address: &Address) -> Result<Option<FunctionDescriptor>> {
let Some(json) =
bridge::function_containing(&self.runtime, self.handle()?, address.as_str())?
else {
return Ok(None);
};
parse_json(
&json,
format!("failed to parse Ghidra function descriptor containing {address}"),
)
}
pub fn symbols(&self) -> Result<Vec<SymbolInfo>> {
let json = bridge::symbols(&self.runtime, self.handle()?)?;
parse_json(&json, "failed to parse Ghidra symbols")
}
pub fn symbols_at(&self, address: &Address) -> Result<Vec<SymbolInfo>> {
let json = bridge::symbols_at(&self.runtime, self.handle()?, address.as_str())?;
parse_json(
&json,
format!("failed to parse Ghidra symbols at {address}"),
)
}
pub fn find_symbols(&self, name: &str, case_sensitive: bool) -> Result<Vec<SymbolInfo>> {
let json = bridge::find_symbols(&self.runtime, self.handle()?, name, case_sensitive)?;
parse_json(
&json,
format!("failed to parse Ghidra symbols named {name}"),
)
}
pub fn references_from(&self, address: &Address) -> Result<Vec<ReferenceInfo>> {
let json = bridge::references_from(&self.runtime, self.handle()?, address.as_str())?;
parse_json(
&json,
format!("failed to parse Ghidra references from {address}"),
)
}
pub fn references_to(&self, address: &Address) -> Result<Vec<ReferenceInfo>> {
let json = bridge::references_to(&self.runtime, self.handle()?, address.as_str())?;
parse_json(
&json,
format!("failed to parse Ghidra references to {address}"),
)
}
pub fn memory_blocks(&self) -> Result<Vec<MemoryBlockInfo>> {
let json = bridge::memory_blocks(&self.runtime, self.handle()?)?;
parse_json(&json, "failed to parse Ghidra memory blocks")
}
pub fn control_flow_graph(&self, function: &FunctionDescriptor) -> Result<ControlFlowGraph> {
let json =
bridge::control_flow_graph(&self.runtime, self.handle()?, function.entry.as_str())?;
parse_json(
&json,
format!(
"failed to parse Ghidra control-flow graph for {} at {}",
function.name, function.entry
),
)
}
pub fn control_flow_graphs(
&self,
functions: &[FunctionDescriptor],
) -> Result<Vec<FunctionControlFlowGraph>> {
if functions.is_empty() {
return Ok(Vec::new());
}
let addresses = functions
.iter()
.map(|function| function.entry.as_str())
.collect::<Vec<_>>();
let json = bridge::control_flow_graphs(&self.runtime, self.handle()?, &addresses)?;
parse_json(&json, "failed to parse Ghidra control-flow graphs")
}
pub fn call_graph(&self) -> Result<ProgramCallGraph> {
let json = bridge::call_graph(&self.runtime, self.handle()?)?;
parse_json(&json, "failed to parse Ghidra call graph")
}
pub fn metadata(&self) -> Result<ProgramMetadata> {
let json = bridge::metadata(&self.runtime, self.handle()?)?;
parse_json(&json, "failed to parse Ghidra program metadata")
}
pub fn function_summary(&self, function: &FunctionDescriptor) -> Result<FunctionSummary> {
let json =
bridge::function_summary(&self.runtime, self.handle()?, function.entry.as_str())?;
parse_json(
&json,
format!(
"failed to parse Ghidra function summary for {} at {}",
function.name, function.entry
),
)
}
pub fn function_summaries(
&self,
functions: &[FunctionDescriptor],
) -> Result<Vec<FunctionSummary>> {
if functions.is_empty() {
return Ok(Vec::new());
}
let addresses = functions
.iter()
.map(|function| function.entry.as_str())
.collect::<Vec<_>>();
let json = bridge::function_summaries(&self.runtime, self.handle()?, &addresses)?;
parse_json(&json, "failed to parse Ghidra function summaries")
}
pub fn open_decompiler(&self) -> Result<Decompiler<'_>> {
let handle = bridge::open_decompiler(&self.runtime, self.handle()?)?;
Ok(Decompiler {
runtime: self.runtime.clone(),
handle: Some(handle),
_program: PhantomData,
})
}
pub fn decompile_function(
&self,
function: &FunctionDescriptor,
timeout: u64,
) -> Result<DecompileResult> {
let decompiler = self.open_decompiler()?;
decompiler.decompile(function, timeout)
}
pub fn decompile_function_with_options(
&self,
function: &FunctionDescriptor,
options: DecompileOptions,
) -> Result<DecompileResult> {
let decompiler = self.open_decompiler()?;
decompiler.decompile_with_options(function, options)
}
pub fn instructions_for_function(
&self,
function: &FunctionDescriptor,
) -> Result<Vec<InstructionInfo>> {
let json = bridge::instructions_for_function(
&self.runtime,
self.handle()?,
function.entry.as_str(),
)?;
parse_json(
&json,
format!("failed to parse Ghidra instructions for {}", function.name),
)
}
pub fn instructions_in_range(&self, range: &AddressRange) -> Result<Vec<InstructionInfo>> {
let json = bridge::instructions_in_range(
&self.runtime,
self.handle()?,
range.start.as_str(),
range.end.as_str(),
)?;
parse_json(
&json,
format!(
"failed to parse Ghidra instructions in range {}..{}",
range.start, range.end
),
)
}
pub fn instruction_at(&self, address: &Address) -> Result<Option<InstructionInfo>> {
let Some(json) = bridge::instruction_at(&self.runtime, self.handle()?, address.as_str())?
else {
return Ok(None);
};
parse_json(
&json,
format!("failed to parse Ghidra instruction at {address}"),
)
}
pub fn data_at(&self, address: &Address) -> Result<Option<DataInfo>> {
let Some(json) = bridge::data_at(&self.runtime, self.handle()?, address.as_str())? else {
return Ok(None);
};
parse_json(&json, format!("failed to parse Ghidra data at {address}"))
}
pub fn data_containing(&self, address: &Address) -> Result<Option<DataInfo>> {
let Some(json) = bridge::data_containing(&self.runtime, self.handle()?, address.as_str())?
else {
return Ok(None);
};
parse_json(
&json,
format!("failed to parse Ghidra data containing {address}"),
)
}
pub fn basic_block_count(&self, function: &FunctionDescriptor) -> Result<u64> {
bridge::basic_block_count(&self.runtime, self.handle()?, function.entry.as_str())
}
pub fn instruction_count(&self, function: &FunctionDescriptor) -> Result<u64> {
bridge::instruction_count(&self.runtime, self.handle()?, function.entry.as_str())
}
pub fn callers(&self, function: &FunctionDescriptor) -> Result<Vec<Address>> {
let json = bridge::callers(&self.runtime, self.handle()?, function.entry.as_str())?;
parse_json(
&json,
format!("failed to parse Ghidra callers for {}", function.name),
)
}
pub fn callees(&self, function: &FunctionDescriptor) -> Result<Vec<Address>> {
let json = bridge::callees(&self.runtime, self.handle()?, function.entry.as_str())?;
parse_json(
&json,
format!("failed to parse Ghidra callees for {}", function.name),
)
}
pub fn strings(&self, function: &FunctionDescriptor) -> Result<Vec<String>> {
let json = bridge::strings(&self.runtime, self.handle()?, function.entry.as_str())?;
parse_json(
&json,
format!("failed to parse Ghidra strings for {}", function.name),
)
}
pub fn constants(&self, function: &FunctionDescriptor) -> Result<Vec<String>> {
let json = bridge::constants(&self.runtime, self.handle()?, function.entry.as_str())?;
parse_json(
&json,
format!("failed to parse Ghidra constants for {}", function.name),
)
}
pub fn data_refs(&self, function: &FunctionDescriptor) -> Result<Vec<Address>> {
let json = bridge::data_refs(&self.runtime, self.handle()?, function.entry.as_str())?;
parse_json(
&json,
format!(
"failed to parse Ghidra data references for {}",
function.name
),
)
}
pub fn imports(&self, function: &FunctionDescriptor) -> Result<Vec<ImportInfo>> {
let json = bridge::imports(&self.runtime, self.handle()?, function.entry.as_str())?;
parse_json(
&json,
format!("failed to parse Ghidra imports for {}", function.name),
)
}
pub fn exports(&self, function: &FunctionDescriptor) -> Result<Vec<ExportInfo>> {
let json = bridge::exports(&self.runtime, self.handle()?, function.entry.as_str())?;
parse_json(
&json,
format!("failed to parse Ghidra exports for {}", function.name),
)
}
pub fn source_map(&self, function: &FunctionDescriptor) -> Result<Option<SourceMapInfo>> {
let Some(json) =
bridge::source_map(&self.runtime, self.handle()?, function.entry.as_str())?
else {
return Ok(None);
};
parse_json(
&json,
format!("failed to parse Ghidra source map for {}", function.name),
)
}
pub fn start_transaction(&self, description: &str) -> Result<ProgramTransaction<'_>> {
let handle = bridge::start_transaction(&self.runtime, self.handle()?, description)?;
Ok(ProgramTransaction {
runtime: self.runtime.clone(),
handle: Some(handle),
_program: PhantomData,
})
}
pub fn function_plate_comment(&self, function: &FunctionDescriptor) -> Result<Option<String>> {
bridge::function_plate_comment(&self.runtime, self.handle()?, function.entry.as_str())
}
pub fn close(mut self) -> Result<()> {
self.close_inner()
}
fn handle(&self) -> Result<JavaHandle> {
self.handle
.ok_or_else(|| Error::closed_handle("Ghidra program"))
}
fn close_inner(&mut self) -> Result<()> {
if let Some(handle) = self.handle.take() {
bridge::close_program(&self.runtime, self.project, handle)?;
}
Ok(())
}
}
impl Drop for Program<'_> {
fn drop(&mut self) {
let _ = self.close_inner();
}
}
impl ProgramTransaction<'_> {
pub fn set_function_plate_comment(
&self,
function: &FunctionDescriptor,
comment: &str,
) -> Result<()> {
bridge::set_function_plate_comment(
&self.runtime,
self.handle()?,
function.entry.as_str(),
comment,
)
}
pub fn commit(mut self) -> Result<()> {
self.end(true)
}
pub fn rollback(mut self) -> Result<()> {
self.end(false)
}
fn handle(&self) -> Result<JavaHandle> {
self.handle
.ok_or_else(|| Error::closed_handle("Ghidra transaction"))
}
fn end(&mut self, commit: bool) -> Result<()> {
if let Some(handle) = self.handle {
bridge::end_transaction(&self.runtime, handle, commit)?;
self.handle = None;
}
Ok(())
}
}
impl Drop for ProgramTransaction<'_> {
fn drop(&mut self) {
let _ = self.end(false);
}
}
impl Decompiler<'_> {
pub fn program_metadata(&self) -> Result<ProgramMetadata> {
let json = bridge::decompiler_metadata(&self.runtime, self.handle()?)?;
parse_json(&json, "failed to parse Ghidra decompiler program metadata")
}
pub fn decompile(
&self,
function: &FunctionDescriptor,
timeout: u64,
) -> Result<DecompileResult> {
let json = bridge::decompile_function(
&self.runtime,
self.handle()?,
function.entry.as_str(),
DecompileOptions::new(timeout),
)?;
parse_json(
&json,
format!(
"failed to parse Ghidra decompile result for {} at {}",
function.name, function.entry
),
)
}
pub fn decompile_with_options(
&self,
function: &FunctionDescriptor,
options: DecompileOptions,
) -> Result<DecompileResult> {
let json = bridge::decompile_function(
&self.runtime,
self.handle()?,
function.entry.as_str(),
options,
)?;
parse_json(
&json,
format!(
"failed to parse Ghidra decompile result for {} at {}",
function.name, function.entry
),
)
}
pub fn close(mut self) -> Result<()> {
self.close_inner()
}
fn handle(&self) -> Result<JavaHandle> {
self.handle
.ok_or_else(|| Error::closed_handle("Ghidra decompiler"))
}
fn close_inner(&mut self) -> Result<()> {
if let Some(handle) = self.handle.take() {
bridge::close_decompiler(&self.runtime, handle)?;
}
Ok(())
}
}
impl Drop for Decompiler<'_> {
fn drop(&mut self) {
let _ = self.close_inner();
}
}
fn parse_json<T, C>(json: &str, context: C) -> Result<T>
where
T: serde::de::DeserializeOwned,
C: JsonContext,
{
serde_json::from_str(json).map_err(|source| context.error(source))
}
trait JsonContext {
fn error(self, source: serde_json::Error) -> Error;
}
impl JsonContext for &'static str {
fn error(self, source: serde_json::Error) -> Error {
Error::json(self, source)
}
}
impl JsonContext for String {
fn error(self, source: serde_json::Error) -> Error {
Error::json(self, source)
}
}
impl<F> JsonContext for F
where
F: FnOnce(serde_json::Error) -> Error,
{
fn error(self, source: serde_json::Error) -> Error {
self(source)
}
}