mirror of
https://github.com/encounter/ghidra-cli.git
synced 2026-07-10 03:18:56 -07:00
jython script works?
This commit is contained in:
@@ -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
@@ -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"));
|
||||
}
|
||||
}
|
||||
Vendored
+162
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user