mirror of
https://github.com/encounter/ghidra-cli.git
synced 2026-07-10 03:18:56 -07:00
346 lines
9.1 KiB
Rust
346 lines
9.1 KiB
Rust
/// Built-in Ghidra scripts for data extraction
|
|
/// These are Python scripts that will be written to disk and executed by Ghidra headless
|
|
|
|
pub fn get_list_functions_script() -> &'static str {
|
|
r#"
|
|
# List all functions in the program
|
|
# @category Analysis
|
|
# @runtime Jython
|
|
|
|
import json
|
|
|
|
functions = []
|
|
function_manager = currentProgram.getFunctionManager()
|
|
|
|
for func in function_manager.getFunctions(True):
|
|
entry = func.getEntryPoint()
|
|
body = func.getBody()
|
|
|
|
func_data = {
|
|
"name": func.getName(),
|
|
"address": entry.toString(),
|
|
"size": body.getNumAddresses(),
|
|
"entry_point": entry.toString(),
|
|
"signature": func.getPrototypeString(False, False) if func.getSignature() else None,
|
|
"calling_convention": func.getCallingConventionName(),
|
|
"comment": func.getComment()
|
|
}
|
|
|
|
# Get called functions
|
|
called = []
|
|
refs = func.getBody().getAddresses(True)
|
|
for addr in refs:
|
|
for ref in currentProgram.getReferenceManager().getReferencesFrom(addr):
|
|
if ref.getReferenceType().isCall():
|
|
to_addr = ref.getToAddress()
|
|
to_func = function_manager.getFunctionAt(to_addr)
|
|
if to_func:
|
|
called.append(to_func.getName())
|
|
|
|
func_data["calls"] = list(set(called))
|
|
|
|
# Get callers
|
|
callers = []
|
|
refs_to = currentProgram.getReferenceManager().getReferencesTo(entry)
|
|
for ref in refs_to:
|
|
if ref.getReferenceType().isCall():
|
|
from_addr = ref.getFromAddress()
|
|
from_func = function_manager.getFunctionContaining(from_addr)
|
|
if from_func:
|
|
callers.append(from_func.getName())
|
|
|
|
func_data["called_by"] = list(set(callers))
|
|
|
|
functions.append(func_data)
|
|
|
|
print("---GHIDRA_CLI_START---")
|
|
print(json.dumps(functions, indent=2))
|
|
print("---GHIDRA_CLI_END---")
|
|
"#
|
|
}
|
|
|
|
pub fn get_decompile_function_script() -> &'static str {
|
|
r#"
|
|
# Decompile a specific function
|
|
# @category Analysis
|
|
# @runtime Jython
|
|
|
|
import json
|
|
from ghidra.app.decompiler import DecompInterface
|
|
from ghidra.util.task import ConsoleTaskMonitor
|
|
|
|
# Get function address from args
|
|
if len(args) < 1:
|
|
print(json.dumps({"error": "No address provided"}))
|
|
exit(1)
|
|
|
|
addr_str = args[0]
|
|
addr = currentProgram.getAddressFactory().getAddress(addr_str)
|
|
|
|
function_manager = currentProgram.getFunctionManager()
|
|
func = function_manager.getFunctionContaining(addr)
|
|
|
|
if not func:
|
|
print(json.dumps({"error": "No function at address " + addr_str}))
|
|
exit(1)
|
|
|
|
# Decompile
|
|
decompiler = DecompInterface()
|
|
decompiler.openProgram(currentProgram)
|
|
|
|
monitor = ConsoleTaskMonitor()
|
|
results = decompiler.decompileFunction(func, 30, monitor)
|
|
|
|
if results.decompileCompleted():
|
|
code = results.getDecompiledFunction().getC()
|
|
|
|
result = {
|
|
"name": func.getName(),
|
|
"address": func.getEntryPoint().toString(),
|
|
"signature": func.getPrototypeString(False, False),
|
|
"code": code
|
|
}
|
|
|
|
print("---GHIDRA_CLI_START---")
|
|
print(json.dumps(result, indent=2))
|
|
print("---GHIDRA_CLI_END---")
|
|
else:
|
|
print("---GHIDRA_CLI_START---")
|
|
print(json.dumps({"error": "Decompilation failed"}))
|
|
print("---GHIDRA_CLI_END---")
|
|
"#
|
|
}
|
|
|
|
pub fn get_list_strings_script() -> &'static str {
|
|
r#"
|
|
# List all strings in the program
|
|
# @category Analysis
|
|
# @runtime Jython
|
|
|
|
import json
|
|
|
|
strings = []
|
|
listing = currentProgram.getListing()
|
|
data_iterator = listing.getDefinedData(True)
|
|
|
|
while data_iterator.hasNext():
|
|
data = data_iterator.next()
|
|
if data.hasStringValue():
|
|
try:
|
|
# Get string value, handle Unicode properly
|
|
string_val = unicode(data.getValue())
|
|
string_data = {
|
|
"address": str(data.getAddress()),
|
|
"value": string_val,
|
|
"length": len(string_val),
|
|
"encoding": "unicode"
|
|
}
|
|
|
|
# Get references to this string
|
|
refs = []
|
|
refs_to = currentProgram.getReferenceManager().getReferencesTo(data.getAddress())
|
|
for ref in refs_to:
|
|
refs.append(str(ref.getFromAddress()))
|
|
|
|
string_data["references"] = refs
|
|
strings.append(string_data)
|
|
except Exception as e:
|
|
# Skip strings that cause encoding issues
|
|
pass
|
|
|
|
print("---GHIDRA_CLI_START---")
|
|
print(json.dumps(strings, indent=2))
|
|
print("---GHIDRA_CLI_END---")
|
|
"#
|
|
}
|
|
|
|
pub fn get_list_imports_script() -> &'static str {
|
|
r#"
|
|
# List all imports in the program
|
|
# @category Analysis
|
|
# @runtime Jython
|
|
|
|
import json
|
|
|
|
imports = []
|
|
symbol_table = currentProgram.getSymbolTable()
|
|
external_manager = currentProgram.getExternalManager()
|
|
|
|
for symbol in symbol_table.getExternalSymbols():
|
|
external_location = external_manager.getExternalLocation(symbol)
|
|
|
|
if external_location:
|
|
import_data = {
|
|
"name": symbol.getName(),
|
|
"address": symbol.getAddress().toString(),
|
|
"library": external_location.getLibraryName(),
|
|
"is_external": True
|
|
}
|
|
imports.append(import_data)
|
|
|
|
print("---GHIDRA_CLI_START---")
|
|
print(json.dumps(imports, indent=2))
|
|
print("---GHIDRA_CLI_END---")
|
|
"#
|
|
}
|
|
|
|
pub fn get_list_exports_script() -> &'static str {
|
|
r#"
|
|
# List all exports in the program
|
|
# @category Analysis
|
|
# @runtime Jython
|
|
|
|
import json
|
|
|
|
exports = []
|
|
symbol_table = currentProgram.getSymbolTable()
|
|
|
|
for symbol in symbol_table.getSymbolIterator():
|
|
if symbol.isExternalEntryPoint():
|
|
export_data = {
|
|
"name": symbol.getName(),
|
|
"address": symbol.getAddress().toString()
|
|
}
|
|
exports.append(export_data)
|
|
|
|
print("---GHIDRA_CLI_START---")
|
|
print(json.dumps(exports, indent=2))
|
|
print("---GHIDRA_CLI_END---")
|
|
"#
|
|
}
|
|
|
|
pub fn get_memory_map_script() -> &'static str {
|
|
r#"
|
|
# Get memory map
|
|
# @category Analysis
|
|
# @runtime Jython
|
|
|
|
import json
|
|
|
|
blocks = []
|
|
memory = currentProgram.getMemory()
|
|
|
|
for block in memory.getBlocks():
|
|
block_data = {
|
|
"name": block.getName(),
|
|
"start": block.getStart().toString(),
|
|
"end": block.getEnd().toString(),
|
|
"size": block.getSize(),
|
|
"permissions": "",
|
|
"is_initialized": block.isInitialized(),
|
|
"is_loaded": block.isLoaded()
|
|
}
|
|
|
|
# Build permissions string
|
|
perms = ""
|
|
if block.isRead():
|
|
perms += "r"
|
|
if block.isWrite():
|
|
perms += "w"
|
|
if block.isExecute():
|
|
perms += "x"
|
|
|
|
block_data["permissions"] = perms
|
|
blocks.append(block_data)
|
|
|
|
print("---GHIDRA_CLI_START---")
|
|
print(json.dumps(blocks, indent=2))
|
|
print("---GHIDRA_CLI_END---")
|
|
"#
|
|
}
|
|
|
|
pub fn get_program_info_script() -> &'static str {
|
|
r#"
|
|
# Get program information
|
|
# @category Analysis
|
|
# @runtime Jython
|
|
|
|
import json
|
|
|
|
info = {
|
|
"name": currentProgram.getName(),
|
|
"executable_path": currentProgram.getExecutablePath(),
|
|
"executable_format": currentProgram.getExecutableFormat(),
|
|
"compiler": currentProgram.getCompiler() if currentProgram.getCompiler() else None,
|
|
"language": currentProgram.getLanguage().toString(),
|
|
"image_base": currentProgram.getImageBase().toString(),
|
|
"min_address": currentProgram.getMinAddress().toString(),
|
|
"max_address": currentProgram.getMaxAddress().toString()
|
|
}
|
|
|
|
# Count functions and instructions
|
|
function_manager = currentProgram.getFunctionManager()
|
|
info["function_count"] = function_manager.getFunctionCount()
|
|
|
|
instruction_count = 0
|
|
listing = currentProgram.getListing()
|
|
for instruction in listing.getInstructions(True):
|
|
instruction_count += 1
|
|
|
|
info["instruction_count"] = instruction_count
|
|
|
|
print("---GHIDRA_CLI_START---")
|
|
print(json.dumps(info, indent=2))
|
|
print("---GHIDRA_CLI_END---")
|
|
"#
|
|
}
|
|
|
|
pub fn get_xrefs_to_script() -> &'static str {
|
|
r#"
|
|
# Get cross-references to an address
|
|
# @category Analysis
|
|
# @runtime Jython
|
|
|
|
import json
|
|
|
|
if len(args) < 1:
|
|
print(json.dumps({"error": "No address provided"}))
|
|
exit(1)
|
|
|
|
addr_str = args[0]
|
|
addr = currentProgram.getAddressFactory().getAddress(addr_str)
|
|
|
|
xrefs = []
|
|
refs = currentProgram.getReferenceManager().getReferencesTo(addr)
|
|
function_manager = currentProgram.getFunctionManager()
|
|
|
|
for ref in refs:
|
|
from_addr = ref.getFromAddress()
|
|
from_func = function_manager.getFunctionContaining(from_addr)
|
|
to_func = function_manager.getFunctionContaining(addr)
|
|
|
|
xref_data = {
|
|
"from": from_addr.toString(),
|
|
"to": addr.toString(),
|
|
"ref_type": ref.getReferenceType().toString(),
|
|
"from_function": from_func.getName() if from_func else None,
|
|
"to_function": to_func.getName() if to_func else None
|
|
}
|
|
xrefs.append(xref_data)
|
|
|
|
print("---GHIDRA_CLI_START---")
|
|
print(json.dumps(xrefs, indent=2))
|
|
print("---GHIDRA_CLI_END---")
|
|
"#
|
|
}
|
|
|
|
/// Save a script to disk
|
|
pub fn save_script(name: &str, content: &str, scripts_dir: &std::path::Path) -> crate::error::Result<std::path::PathBuf> {
|
|
// All scripts are Python now with PyGhidra support
|
|
let script_path = scripts_dir.join(format!("{}.py", name));
|
|
std::fs::write(&script_path, content)?;
|
|
Ok(script_path)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_scripts_not_empty() {
|
|
assert!(!get_list_functions_script().is_empty());
|
|
assert!(!get_decompile_function_script().is_empty());
|
|
assert!(!get_list_strings_script().is_empty());
|
|
}
|
|
}
|