jython script works?

This commit is contained in:
Alexander Kiselev
2026-01-25 01:54:02 -08:00
parent 4023ca79bb
commit 370e578cae
3 changed files with 343 additions and 0 deletions
+7
View File
@@ -5,6 +5,7 @@ pub fn get_list_functions_script() -> &'static str {
r#"
# List all functions in the program
# @category Analysis
# @runtime Jython
import json
@@ -62,6 +63,7 @@ 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
@@ -156,6 +158,7 @@ pub fn get_list_imports_script() -> &'static str {
r#"
# List all imports in the program
# @category Analysis
# @runtime Jython
import json
@@ -185,6 +188,7 @@ pub fn get_list_exports_script() -> &'static str {
r#"
# List all exports in the program
# @category Analysis
# @runtime Jython
import json
@@ -209,6 +213,7 @@ pub fn get_memory_map_script() -> &'static str {
r#"
# Get memory map
# @category Analysis
# @runtime Jython
import json
@@ -248,6 +253,7 @@ pub fn get_program_info_script() -> &'static str {
r#"
# Get program information
# @category Analysis
# @runtime Jython
import json
@@ -283,6 +289,7 @@ pub fn get_xrefs_to_script() -> &'static str {
r#"
# Get cross-references to an address
# @category Analysis
# @runtime Jython
import json
+174
View File
@@ -0,0 +1,174 @@
//! End-to-end tests for ghidra-cli
//!
//! These tests require a working Ghidra installation and test the full CLI workflow.
use assert_cmd::Command;
use predicates::prelude::*;
use std::path::PathBuf;
/// Get the path to the test fixture binary
fn fixture_binary() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("fixtures")
.join("sample_binary")
}
/// Get a unique project name for each test to avoid conflicts
fn test_project_name(test_name: &str) -> String {
format!("e2e-{}-{}", test_name, std::process::id())
}
mod e2e_tests {
use super::*;
/// Test that doctor command works
#[test]
fn test_doctor() {
let mut cmd = Command::cargo_bin("ghidra").unwrap();
cmd.arg("doctor")
.assert()
.success()
.stdout(predicate::str::contains("Ghidra CLI Doctor"));
}
/// Test version command
#[test]
fn test_version() {
let mut cmd = Command::cargo_bin("ghidra").unwrap();
cmd.arg("version")
.assert()
.success()
.stdout(predicate::str::contains("ghidra-cli"));
}
/// Test config list command
#[test]
fn test_config_list() {
let mut cmd = Command::cargo_bin("ghidra").unwrap();
cmd.arg("config")
.arg("list")
.assert()
.success()
.stdout(predicate::str::contains("ghidra_install_dir"));
}
/// Test import command with sample binary
/// This test requires Ghidra to be installed
#[test]
#[ignore] // Run with: cargo test -- --ignored
fn test_import_binary() {
let binary = fixture_binary();
if !binary.exists() {
panic!("Test fixture not found. Run: rustc --edition 2021 -o tests/fixtures/sample_binary tests/fixtures/sample_binary.rs");
}
let project = test_project_name("import");
let mut cmd = Command::cargo_bin("ghidra").unwrap();
cmd.arg("import")
.arg(binary.to_str().unwrap())
.arg("--project")
.arg(&project)
.arg("--program")
.arg("sample_binary")
.timeout(std::time::Duration::from_secs(120))
.assert()
.success()
.stdout(predicate::str::contains("Successfully imported"));
}
/// Test function list command on pre-analyzed binary
/// Requires the e2e-test project to exist with sample_binary
#[test]
#[ignore]
fn test_function_list() {
let mut cmd = Command::cargo_bin("ghidra").unwrap();
cmd.arg("function")
.arg("list")
.arg("--project")
.arg("e2e-test")
.arg("--program")
.arg("sample_binary")
.arg("--limit")
.arg("100")
.timeout(std::time::Duration::from_secs(120))
.assert()
.success()
// Check for our known exported functions
.stdout(predicate::str::contains("main"))
.stdout(predicate::str::contains("fibonacci").or(predicate::str::contains("factorial")));
}
/// Test decompile command
#[test]
#[ignore]
fn test_decompile() {
let mut cmd = Command::cargo_bin("ghidra").unwrap();
cmd.arg("decompile")
.arg("main") // Decompile main function
.arg("--project")
.arg("e2e-test")
.arg("--program")
.arg("sample_binary")
.timeout(std::time::Duration::from_secs(120))
.assert()
.success()
// Should contain decompiled C code
.stdout(predicate::str::contains("void").or(predicate::str::contains("int")));
}
/// Test strings command
#[test]
#[ignore]
fn test_strings() {
let mut cmd = Command::cargo_bin("ghidra").unwrap();
cmd.arg("strings")
.arg("list")
.arg("--project")
.arg("e2e-test")
.arg("--program")
.arg("sample_binary")
.arg("--limit")
.arg("50")
.timeout(std::time::Duration::from_secs(120))
.assert()
.success()
// Check for our known strings
.stdout(predicate::str::contains("Hello").or(predicate::str::contains("Ghidra")));
}
/// Test memory map command
#[test]
#[ignore]
fn test_memory_map() {
let mut cmd = Command::cargo_bin("ghidra").unwrap();
cmd.arg("memory")
.arg("map")
.arg("--project")
.arg("e2e-test")
.arg("--program")
.arg("sample_binary")
.timeout(std::time::Duration::from_secs(120))
.assert()
.success()
// Should show memory sections
.stdout(predicate::str::contains(".text").or(predicate::str::contains("r")));
}
/// Test summary command
#[test]
#[ignore]
fn test_summary() {
let mut cmd = Command::cargo_bin("ghidra").unwrap();
cmd.arg("summary")
.arg("--project")
.arg("e2e-test")
.arg("--program")
.arg("sample_binary")
.timeout(std::time::Duration::from_secs(120))
.assert()
.success()
.stdout(predicate::str::contains("Program Summary"));
}
}
+162
View File
@@ -0,0 +1,162 @@
//! Sample binary for E2E testing of ghidra-cli.
//!
//! This binary contains various functions and data structures
//! that can be analyzed by Ghidra to test the CLI functionality.
use std::collections::HashMap;
/// A simple structure for testing
#[repr(C)]
struct TestStruct {
value: i32,
name: [u8; 32],
}
/// Global constant string for testing string detection
static HELLO_WORLD: &str = "Hello, Ghidra CLI!";
static VERSION_STRING: &str = "test_binary v1.0.0";
static SECRET_KEY: &str = "super_secret_key_12345";
/// Simple arithmetic function
#[no_mangle]
pub extern "C" fn add_numbers(a: i32, b: i32) -> i32 {
a + b
}
/// Multiply function
#[no_mangle]
pub extern "C" fn multiply(x: i32, y: i32) -> i32 {
x * y
}
/// Calculate factorial (recursive)
#[no_mangle]
pub extern "C" fn factorial(n: u64) -> u64 {
if n <= 1 {
1
} else {
n * factorial(n - 1)
}
}
/// Fibonacci (iterative)
#[no_mangle]
pub extern "C" fn fibonacci(n: u32) -> u64 {
if n == 0 {
return 0;
}
if n == 1 {
return 1;
}
let mut a = 0u64;
let mut b = 1u64;
for _ in 2..=n {
let temp = a + b;
a = b;
b = temp;
}
b
}
/// String processing function
#[no_mangle]
pub extern "C" fn process_string(input: *const u8, len: usize) -> i32 {
if input.is_null() || len == 0 {
return -1;
}
let slice = unsafe { std::slice::from_raw_parts(input, len) };
let mut sum: i32 = 0;
for &byte in slice {
sum += byte as i32;
}
sum
}
/// XOR encryption (simple cipher for testing)
#[no_mangle]
pub extern "C" fn xor_encrypt(data: *mut u8, len: usize, key: u8) {
if data.is_null() || len == 0 {
return;
}
let slice = unsafe { std::slice::from_raw_parts_mut(data, len) };
for byte in slice.iter_mut() {
*byte ^= key;
}
}
/// Hash function (simple for testing)
#[no_mangle]
pub extern "C" fn simple_hash(data: *const u8, len: usize) -> u32 {
if data.is_null() || len == 0 {
return 0;
}
let slice = unsafe { std::slice::from_raw_parts(data, len) };
let mut hash: u32 = 5381;
for &byte in slice {
hash = hash.wrapping_mul(33).wrapping_add(byte as u32);
}
hash
}
/// Initialize a TestStruct
#[no_mangle]
pub extern "C" fn init_struct(ts: *mut TestStruct, value: i32) {
if ts.is_null() {
return;
}
unsafe {
(*ts).value = value;
(*ts).name = [0; 32];
}
}
/// Internal helper (not exported)
fn internal_helper(x: i32) -> i32 {
x * 2 + 1
}
/// Main function that uses the other functions
fn main() {
println!("{}", HELLO_WORLD);
println!("{}", VERSION_STRING);
let sum = add_numbers(10, 20);
println!("10 + 20 = {}", sum);
let product = multiply(5, 6);
println!("5 * 6 = {}", product);
let fact = factorial(10);
println!("10! = {}", fact);
let fib = fibonacci(20);
println!("fib(20) = {}", fib);
let hash = simple_hash(SECRET_KEY.as_ptr(), SECRET_KEY.len());
println!("hash = {:x}", hash);
let helper_result = internal_helper(42);
println!("internal: {}", helper_result);
// Create a simple lookup table
let mut lookup: HashMap<&str, i32> = HashMap::new();
lookup.insert("one", 1);
lookup.insert("two", 2);
lookup.insert("three", 3);
for (key, value) in &lookup {
println!("{} = {}", key, value);
}
}