use jni::{JValue, jni_sig, jni_str, objects::JObject};
use crate::{Error, Result, runtime::GhidraRuntime};
use super::{
lifecycle::attach_bridge,
support::{JavaHandle, find_bridge_class, java_optional_string, java_string, jni_error},
};
pub(crate) fn list_functions(runtime: &GhidraRuntime, program: JavaHandle) -> Result<String> {
string_operation(
runtime,
program,
jni_str!("listFunctions"),
"failed to list Ghidra functions",
)
}
pub(crate) fn parse_address(
runtime: &GhidraRuntime,
program: JavaHandle,
address: &str,
) -> Result<String> {
string_string_operation(
runtime,
program,
address,
jni_str!("parseAddress"),
"failed to parse Ghidra address",
)
}
pub(crate) fn function_at(
runtime: &GhidraRuntime,
program: JavaHandle,
address: &str,
) -> Result<Option<String>> {
optional_string_string_operation(
runtime,
program,
address,
jni_str!("functionAt"),
"failed to find Ghidra function at address",
)
}
pub(crate) fn function_containing(
runtime: &GhidraRuntime,
program: JavaHandle,
address: &str,
) -> Result<Option<String>> {
optional_string_string_operation(
runtime,
program,
address,
jni_str!("functionContaining"),
"failed to find Ghidra function containing address",
)
}
pub(crate) fn symbols(runtime: &GhidraRuntime, program: JavaHandle) -> Result<String> {
string_operation(
runtime,
program,
jni_str!("symbols"),
"failed to list Ghidra symbols",
)
}
pub(crate) fn symbols_at(
runtime: &GhidraRuntime,
program: JavaHandle,
address: &str,
) -> Result<String> {
string_string_operation(
runtime,
program,
address,
jni_str!("symbolsAt"),
"failed to list Ghidra symbols at address",
)
}
pub(crate) fn find_symbols(
runtime: &GhidraRuntime,
program: JavaHandle,
name: &str,
case_sensitive: bool,
) -> Result<String> {
attach_bridge(runtime)?;
runtime.with_attached(|env| -> Result<String> {
let name = env.new_string(name)?;
let class = find_bridge_class(env)?;
let value = match env.call_static_method(
class,
jni_str!("findSymbols"),
jni_sig!("(JLjava/lang/String;Z)Ljava/lang/String;"),
&[
JValue::Long(program),
JValue::Object(&JObject::from(name)),
JValue::Bool(case_sensitive),
],
) {
Ok(value) => value,
Err(error) => return Err(jni_error(env, "failed to find Ghidra symbols", error)),
};
java_string(env, value.l()?)
})
}
pub(crate) fn references_from(
runtime: &GhidraRuntime,
program: JavaHandle,
address: &str,
) -> Result<String> {
string_string_operation(
runtime,
program,
address,
jni_str!("referencesFrom"),
"failed to list Ghidra references from address",
)
}
pub(crate) fn references_to(
runtime: &GhidraRuntime,
program: JavaHandle,
address: &str,
) -> Result<String> {
string_string_operation(
runtime,
program,
address,
jni_str!("referencesTo"),
"failed to list Ghidra references to address",
)
}
pub(crate) fn memory_blocks(runtime: &GhidraRuntime, program: JavaHandle) -> Result<String> {
string_operation(
runtime,
program,
jni_str!("memoryBlocks"),
"failed to list Ghidra memory blocks",
)
}
pub(crate) fn control_flow_graph(
runtime: &GhidraRuntime,
program: JavaHandle,
function_address: &str,
) -> Result<String> {
string_string_operation(
runtime,
program,
function_address,
jni_str!("controlFlowGraph"),
"failed to extract Ghidra control-flow graph",
)
}
pub(crate) fn control_flow_graphs(
runtime: &GhidraRuntime,
program: JavaHandle,
function_addresses: &[&str],
) -> Result<String> {
string_array_string_operation(
runtime,
program,
function_addresses,
jni_str!("controlFlowGraphs"),
"failed to extract Ghidra control-flow graphs",
"control-flow graph batch is too large",
)
}
pub(crate) fn call_graph(runtime: &GhidraRuntime, program: JavaHandle) -> Result<String> {
string_operation(
runtime,
program,
jni_str!("callGraph"),
"failed to extract Ghidra call graph",
)
}
pub(crate) fn metadata(runtime: &GhidraRuntime, program: JavaHandle) -> Result<String> {
string_operation(
runtime,
program,
jni_str!("metadata"),
"failed to extract Ghidra program metadata",
)
}
pub(crate) fn function_summary(
runtime: &GhidraRuntime,
program: JavaHandle,
function_address: &str,
) -> Result<String> {
string_string_operation(
runtime,
program,
function_address,
jni_str!("functionSummary"),
"failed to summarize Ghidra function",
)
}
pub(crate) fn function_summaries(
runtime: &GhidraRuntime,
program: JavaHandle,
function_addresses: &[&str],
) -> Result<String> {
string_array_string_operation(
runtime,
program,
function_addresses,
jni_str!("functionSummaries"),
"failed to summarize Ghidra functions",
"function summary batch is too large",
)
}
fn string_array_string_operation(
runtime: &GhidraRuntime,
program: JavaHandle,
strings: &[&str],
method: &'static jni::strings::JNIStr,
context: &'static str,
too_large_message: &'static str,
) -> Result<String> {
attach_bridge(runtime)?;
let length =
i32::try_from(strings.len()).map_err(|_| Error::invalid_input(too_large_message))?;
runtime.with_attached(|env| -> Result<String> {
let values = env.new_object_array(length, jni_str!("java/lang/String"), JObject::null())?;
for (index, value) in strings.iter().enumerate() {
let value = JObject::from(env.new_string(*value)?);
values.set_element(env, index, &value)?;
}
let class = find_bridge_class(env)?;
let value = match env.call_static_method(
class,
method,
jni_sig!("(J[Ljava/lang/String;)Ljava/lang/String;"),
&[JValue::Long(program), JValue::Object(values.as_ref())],
) {
Ok(value) => value,
Err(error) => return Err(jni_error(env, context, error)),
};
java_string(env, value.l()?)
})
}
pub(crate) fn basic_block_count(
runtime: &GhidraRuntime,
program: JavaHandle,
function_address: &str,
) -> Result<u64> {
long_string_operation(
runtime,
program,
function_address,
jni_str!("basicBlockCount"),
"failed to count Ghidra function basic blocks",
)
}
pub(crate) fn instruction_count(
runtime: &GhidraRuntime,
program: JavaHandle,
function_address: &str,
) -> Result<u64> {
long_string_operation(
runtime,
program,
function_address,
jni_str!("instructionCount"),
"failed to count Ghidra function instructions",
)
}
pub(crate) fn callers(
runtime: &GhidraRuntime,
program: JavaHandle,
function_address: &str,
) -> Result<String> {
string_string_operation(
runtime,
program,
function_address,
jni_str!("callers"),
"failed to list Ghidra function callers",
)
}
pub(crate) fn callees(
runtime: &GhidraRuntime,
program: JavaHandle,
function_address: &str,
) -> Result<String> {
string_string_operation(
runtime,
program,
function_address,
jni_str!("callees"),
"failed to list Ghidra function callees",
)
}
pub(crate) fn strings(
runtime: &GhidraRuntime,
program: JavaHandle,
function_address: &str,
) -> Result<String> {
string_string_operation(
runtime,
program,
function_address,
jni_str!("strings"),
"failed to list Ghidra function strings",
)
}
pub(crate) fn constants(
runtime: &GhidraRuntime,
program: JavaHandle,
function_address: &str,
) -> Result<String> {
string_string_operation(
runtime,
program,
function_address,
jni_str!("constants"),
"failed to list Ghidra function constants",
)
}
pub(crate) fn data_refs(
runtime: &GhidraRuntime,
program: JavaHandle,
function_address: &str,
) -> Result<String> {
string_string_operation(
runtime,
program,
function_address,
jni_str!("dataRefs"),
"failed to list Ghidra function data references",
)
}
pub(crate) fn imports(
runtime: &GhidraRuntime,
program: JavaHandle,
function_address: &str,
) -> Result<String> {
string_string_operation(
runtime,
program,
function_address,
jni_str!("imports"),
"failed to list Ghidra function imports",
)
}
pub(crate) fn exports(
runtime: &GhidraRuntime,
program: JavaHandle,
function_address: &str,
) -> Result<String> {
string_string_operation(
runtime,
program,
function_address,
jni_str!("exports"),
"failed to list Ghidra function exports",
)
}
pub(crate) fn source_map(
runtime: &GhidraRuntime,
program: JavaHandle,
function_address: &str,
) -> Result<Option<String>> {
optional_string_string_operation(
runtime,
program,
function_address,
jni_str!("sourceMap"),
"failed to read Ghidra function source map",
)
}
pub(crate) fn start_transaction(
runtime: &GhidraRuntime,
program: JavaHandle,
description: &str,
) -> Result<JavaHandle> {
attach_bridge(runtime)?;
runtime.with_attached(|env| -> Result<JavaHandle> {
let description = env.new_string(description)?;
let class = find_bridge_class(env)?;
let value = match env.call_static_method(
class,
jni_str!("startTransaction"),
jni_sig!("(JLjava/lang/String;)J"),
&[
JValue::Long(program),
JValue::Object(&JObject::from(description)),
],
) {
Ok(value) => value,
Err(error) => return Err(jni_error(env, "failed to start Ghidra transaction", error)),
};
Ok(value.j()?)
})
}
pub(crate) fn end_transaction(
runtime: &GhidraRuntime,
transaction: JavaHandle,
commit: bool,
) -> Result<()> {
attach_bridge(runtime)?;
runtime.with_attached(|env| -> Result<()> {
let class = find_bridge_class(env)?;
match env.call_static_method(
class,
jni_str!("endTransaction"),
jni_sig!("(JZ)V"),
&[JValue::Long(transaction), JValue::Bool(commit)],
) {
Ok(_) => Ok(()),
Err(error) => Err(jni_error(env, "failed to end Ghidra transaction", error)),
}
})
}
pub(crate) fn set_function_plate_comment(
runtime: &GhidraRuntime,
transaction: JavaHandle,
function_address: &str,
comment: &str,
) -> Result<()> {
attach_bridge(runtime)?;
runtime.with_attached(|env| -> Result<()> {
let function_address = env.new_string(function_address)?;
let comment = env.new_string(comment)?;
let class = find_bridge_class(env)?;
match env.call_static_method(
class,
jni_str!("setFunctionPlateComment"),
jni_sig!("(JLjava/lang/String;Ljava/lang/String;)V"),
&[
JValue::Long(transaction),
JValue::Object(&JObject::from(function_address)),
JValue::Object(&JObject::from(comment)),
],
) {
Ok(_) => Ok(()),
Err(error) => Err(jni_error(
env,
"failed to set Ghidra function plate comment",
error,
)),
}
})
}
pub(crate) fn function_plate_comment(
runtime: &GhidraRuntime,
program: JavaHandle,
function_address: &str,
) -> Result<Option<String>> {
optional_string_string_operation(
runtime,
program,
function_address,
jni_str!("functionPlateComment"),
"failed to read Ghidra function plate comment",
)
}
fn string_operation(
runtime: &GhidraRuntime,
program: JavaHandle,
method: &'static jni::strings::JNIStr,
context: &'static str,
) -> Result<String> {
attach_bridge(runtime)?;
runtime.with_attached(|env| -> Result<String> {
let class = find_bridge_class(env)?;
let value = match env.call_static_method(
class,
method,
jni_sig!("(J)Ljava/lang/String;"),
&[JValue::Long(program)],
) {
Ok(value) => value,
Err(error) => return Err(jni_error(env, context, error)),
};
java_string(env, value.l()?)
})
}
fn string_string_operation(
runtime: &GhidraRuntime,
program: JavaHandle,
argument: &str,
method: &'static jni::strings::JNIStr,
context: &'static str,
) -> Result<String> {
attach_bridge(runtime)?;
runtime.with_attached(|env| -> Result<String> {
let argument = env.new_string(argument)?;
let class = find_bridge_class(env)?;
let value = match env.call_static_method(
class,
method,
jni_sig!("(JLjava/lang/String;)Ljava/lang/String;"),
&[
JValue::Long(program),
JValue::Object(&JObject::from(argument)),
],
) {
Ok(value) => value,
Err(error) => return Err(jni_error(env, context, error)),
};
java_string(env, value.l()?)
})
}
fn optional_string_string_operation(
runtime: &GhidraRuntime,
program: JavaHandle,
argument: &str,
method: &'static jni::strings::JNIStr,
context: &'static str,
) -> Result<Option<String>> {
attach_bridge(runtime)?;
runtime.with_attached(|env| -> Result<Option<String>> {
let argument = env.new_string(argument)?;
let class = find_bridge_class(env)?;
let value = match env.call_static_method(
class,
method,
jni_sig!("(JLjava/lang/String;)Ljava/lang/String;"),
&[
JValue::Long(program),
JValue::Object(&JObject::from(argument)),
],
) {
Ok(value) => value,
Err(error) => return Err(jni_error(env, context, error)),
};
java_optional_string(env, value.l()?)
})
}
fn long_string_operation(
runtime: &GhidraRuntime,
program: JavaHandle,
argument: &str,
method: &'static jni::strings::JNIStr,
context: &'static str,
) -> Result<u64> {
attach_bridge(runtime)?;
runtime.with_attached(|env| -> Result<u64> {
let argument = env.new_string(argument)?;
let class = find_bridge_class(env)?;
let value = match env.call_static_method(
class,
method,
jni_sig!("(JLjava/lang/String;)J"),
&[
JValue::Long(program),
JValue::Object(&JObject::from(argument)),
],
) {
Ok(value) => value,
Err(error) => return Err(jni_error(env, context, error)),
};
u64::try_from(value.j()?)
.map_err(|_| Error::invalid_input("Java long result must not be negative"))
})
}