re_ghidra_agy_hook/
hook.rs1use re_ghidra_mcp_agy::{load_settings, parse_java_major, preflight, Probe};
19use std::path::{Path, PathBuf};
20use std::process::Command;
21
22fn main() {
23 let mut raw = String::new();
24 let _ = std::io::Read::read_to_string(&mut std::io::stdin(), &mut raw);
25
26 let Ok(v) = serde_json::from_str::<serde_json::Value>(&raw) else {
28 return;
29 };
30
31 if let Some(inv_num) = v.get("invocationNum").and_then(|n| n.as_u64()) {
34 if inv_num > 1 {
35 println!("{}", serde_json::json!({}));
36 return;
37 }
38 }
39
40 let project_dir =
41 extract_project_dir(&v).unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
42
43 let mut cfg = load_settings(&project_dir);
44 overlay_env(&mut cfg);
45
46 let probe = Probe {
47 launcher_present: cfg
48 .ghidra_install_dir
49 .as_deref()
50 .is_some_and(|d| launcher_exists(Path::new(d))),
51 java_major: detect_java_major(),
52 };
53
54 let findings = preflight(&cfg, &probe);
55 if findings.is_empty() {
56 println!("{}", serde_json::json!({}));
57 return;
58 }
59
60 let body = format!(
61 "re-ghidra-mcp-agy is not ready to attach to a Ghidra project:\n{}",
62 findings
63 .iter()
64 .map(|f| format!(" - {f}"))
65 .collect::<Vec<_>>()
66 .join("\n")
67 );
68
69 let payload = serde_json::json!({
70 "injectSteps": [
71 {
72 "ephemeralMessage": body
73 }
74 ]
75 });
76
77 println!("{payload}");
78}
79
80fn extract_project_dir(v: &serde_json::Value) -> Option<PathBuf> {
81 if let Some(workspaces) = v.get("workspacePaths").and_then(|w| w.as_array()) {
83 if let Some(first) = workspaces.first().and_then(|f| f.as_str()) {
84 if !first.is_empty() {
85 return Some(PathBuf::from(first));
86 }
87 }
88 }
89 None
90}
91
92fn overlay_env(cfg: &mut ghidra_mcp::config::RawConfig) {
93 let get = |k: &str| std::env::var(k).ok().filter(|s| !s.is_empty());
94 for (slot, key) in [
95 (&mut cfg.ghidra_install_dir, "GHIDRA_INSTALL_DIR"),
96 (&mut cfg.project_dir, "GHIDRA_MCP_PROJECT_DIR"),
97 (&mut cfg.project_name, "GHIDRA_MCP_PROJECT_NAME"),
98 (&mut cfg.bootstrap_program, "GHIDRA_MCP_BOOTSTRAP_PROGRAM"),
99 (
100 &mut cfg.bootstrap_program_path,
101 "GHIDRA_MCP_BOOTSTRAP_PROGRAM_PATH",
102 ),
103 (&mut cfg.max_heap, "GHIDRA_MCP_MAX_HEAP"),
104 ] {
105 if let Some(v) = get(key) {
106 *slot = Some(v);
107 }
108 }
109}
110
111fn launcher_exists(install_dir: &Path) -> bool {
112 let support = install_dir.join("support");
113 support.join("analyzeHeadless.bat").exists() || support.join("analyzeHeadless").exists()
114}
115
116fn detect_java_major() -> Option<u32> {
117 let out = Command::new("java").arg("-version").output().ok()?;
118 parse_java_major(&String::from_utf8_lossy(&out.stderr))
119 .or_else(|| parse_java_major(&String::from_utf8_lossy(&out.stdout)))
120}