scanmem alternative with concurrent memory scanning
1use anyhow::Result;
2use rustyline::Editor;
3use rustyline::error::ReadlineError;
4
5use crate::process::Process;
6use crate::scanner::{MemoryScanner, ScanValueType};
7
8pub fn run_interactive_mode(process_name: &str) -> Result<()> {
9 println!(
10 "# Scanning for process \"{}\" like how pgrep -f works",
11 process_name
12 );
13
14 let process = Process::find_by_name(process_name)?;
15 let mut scanner = MemoryScanner::new(process)?;
16
17 println!("Please enter current value, or \"help\" for other commands.");
18
19 let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new()?;
20 let mut first_scan = true;
21
22 loop {
23 let prompt = if first_scan {
24 "> ".to_string()
25 } else {
26 format!("{}> ", scanner.get_matches_count())
27 };
28
29 let readline = rl.readline(&prompt);
30 match readline {
31 Ok(line) => {
32 let _ = rl.add_history_entry(&line);
33 let line = line.trim();
34
35 if line.is_empty() {
36 continue;
37 }
38
39 if line == "help" {
40 print_help();
41 continue;
42 }
43
44 if line == "exit" || line == "quit" {
45 break;
46 }
47
48 if line.starts_with("set ") {
49 handle_set_command(&mut scanner, line)?;
50 continue;
51 }
52 match parse_value(line) {
53 Ok(value) => {
54 if first_scan {
55 match scanner.scan_for_value(value) {
56 Ok(_) => first_scan = false,
57 Err(e) => println!("Error scanning: {}", e),
58 }
59 } else {
60 match scanner.filter_matches(value) {
61 Ok(_) => {}
62 Err(e) => println!("Error filtering: {}", e),
63 }
64 }
65 }
66 Err(e) => println!("Invalid value: {}", e),
67 }
68 }
69 Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {
70 println!("Exiting...");
71 break;
72 }
73 Err(err) => {
74 println!("Error: {}", err);
75 break;
76 }
77 }
78 }
79
80 Ok(())
81}
82
83fn print_help() {
84 println!("Available commands:");
85 println!(" <value> - Search for value or narrow down existing results");
86 println!(" set <idx> <value> - Set value at the memory address at index <idx>");
87 println!(" help - Show this help message");
88 println!(" exit/quit - Exit the program");
89}
90
91fn handle_set_command(scanner: &mut MemoryScanner, command: &str) -> Result<()> {
92 let parts: Vec<&str> = command.split_whitespace().collect();
93 if parts.len() != 3 {
94 anyhow::bail!("Invalid set command format. Use: set <idx> <value>");
95 }
96
97 let idx = match parts[1].parse::<usize>() {
98 Ok(idx) => idx,
99 Err(_) => anyhow::bail!("Invalid index: {}", parts[1]),
100 };
101
102 let value = match parse_value(parts[2])? {
103 ScanValueType::I32(val) => val,
104 _ => anyhow::bail!("Only i32 values are currently supported for setting"),
105 };
106
107 scanner.set_value(idx, value)
108}
109
110fn parse_value(value_str: &str) -> Result<ScanValueType> {
111 if let Ok(val) = value_str.parse::<i32>() {
112 return Ok(ScanValueType::I32(val));
113 }
114
115 // TODO missing implementation for all the other types
116
117 anyhow::bail!("Could not parse '{}' as a valid number", value_str)
118}