Skip to content

Commit 2221a1f

Browse files
committed
Changed from hardcoded values to configurable ones.
Processes to hook, the process trace name, and the name of the dll to inject are now configurable. Ctrl-C now stops the trace. Fixed version information in build.rs and bumped to 0.4.0.
1 parent 434bda0 commit 2221a1f

File tree

6 files changed

+154
-76
lines changed

6 files changed

+154
-76
lines changed

build.ps1

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ if ($LASTEXITCODE -eq 0) { cargo.exe build --target=i686-pc-windows-msvc --$rele
3030
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
3131
$x86_path = ".\target\i686-pc-windows-msvc\$release_type"
3232
$x64_path = ".\target\x86_64-pc-windows-msvc\$release_type"
33-
Copy-Item $x86_path\noblock_input_hook.dll $output_dir\noblock_input_hook_x86.dll
34-
Copy-Item $x86_path\noblock_input_hook_injector.exe $output_dir\noblock_input_hook_injector_x86.exe
35-
Copy-Item $x64_path\noblock_input_hook.dll $output_dir\noblock_input_hook.dll
36-
Copy-Item $x64_path\noblock_input_hook_injector.exe $output_dir\noblock_input_hook_injector.exe
33+
Copy-Item $x86_path\noblock_input_hook.dll $output_dir\noblock_input32.dll
34+
Copy-Item $x86_path\noblock_input_hook_injector.exe $output_dir\noblock_input32.exe
35+
Copy-Item $x64_path\noblock_input_hook.dll $output_dir\noblock_input.dll
36+
Copy-Item $x64_path\noblock_input_hook_injector.exe $output_dir\noblock_input.exe
37+
Copy-Item .\injector\configuration\injector.toml $output_dir\injector.toml

hook/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "noblock_input_hook"
3-
version = "0.3.1"
3+
version = "0.4.0"
44
edition = "2021"
55

66
[lib]

injector/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
[package]
22
name = "noblock_input_hook_injector"
3-
version = "0.3.1"
3+
version = "0.4.0"
44
edition = "2021"
55

66
[dependencies]
77
dll-syringe = "0.15"
88
ferrisetw = "1.1"
99
ctrlc = "3.4"
10+
toml = "0.8.8"
11+
12+
[dependencies.serde]
13+
version = "1.0.192"
14+
features = ["derive"]
1015

1116
[dependencies.windows]
1217
version = "0.51"

injector/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ fn main()
1212
.max_version_tested(MaxVersionTested::Windows11Version22H2)
1313
.name("NoBlockInput Injector")
1414
.requested_execution_level(ExecutionLevel::RequireAdministrator)
15-
.version(0, 3, 0, 0);
15+
.version(0, 4, 0, 0);
1616
embed_manifest(manifest_builder).expect("Couldn't embed manifest.");
1717
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# The hook_dll_name fields are optional. If not specified, the injector will look for a dll with the same base name as itself with a .dll extension
2+
# e.g., an injector named injsvc.exe will look for injsvc.dll.
3+
# The trace name can be anything you like that is not already in use by the system.
4+
# The processes are the names of the exact processes that call BlockInput(). The defaults are known to be culprits, but you can add your own for other remote desktop software. Don't get the bitness wrong.
5+
6+
# Important: do not use the same name for both x64 and x86 for any of these fields. You WILL run into to issues running both injectors if you do.
7+
8+
[x64]
9+
# hook_dll_name = "noblock_input_hook.dll"
10+
trace_name = "NoBlockInput"
11+
processes = ["ScreenConnect.WindowsClient.exe"]
12+
13+
[x86]
14+
# hook_dll_name = "noblock_input_hook_x86.dll"
15+
trace_name = "NoBlockInput_x86"
16+
processes = ["rfusclient.exe", "Supremo.exe"]

injector/src/main.rs

Lines changed: 125 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -3,66 +3,107 @@
33

44
#![cfg_attr(all(target_os = "windows", not(debug_assertions)), windows_subsystem = "windows")]
55

6+
use std::collections::HashSet;
7+
use std::env;
8+
use std::fs::File;
69
use std::path::Path;
710
use std::path::PathBuf;
811
use std::thread::sleep;
912
use std::time::Duration;
10-
use std::collections::HashSet;
13+
use std::io::Read;
14+
use std::process::exit;
1115
use std::string::ToString;
1216

1317
use dll_syringe::Syringe;
14-
use dll_syringe::process::{BorrowedProcessModule, Process};
18+
use dll_syringe::process::BorrowedProcessModule;
19+
use dll_syringe::process::Process;
1520
use dll_syringe::process::OwnedProcess;
1621

22+
use serde::Deserialize;
23+
1724
use ferrisetw::EventRecord;
1825
use ferrisetw::native::TraceHandle;
1926
use ferrisetw::native::EvntraceNativeError;
2027
use ferrisetw::parser::Parser;
2128
use ferrisetw::provider::Provider;
2229
use ferrisetw::schema_locator::SchemaLocator;
23-
use ferrisetw::trace::{RealTimeTraceTrait, stop_trace_by_name};
24-
use ferrisetw::trace::TraceTrait;
30+
use ferrisetw::trace::RealTimeTraceTrait;
31+
use ferrisetw::trace::stop_trace_by_name;
2532
use ferrisetw::trace::TraceError;
33+
use ferrisetw::trace::TraceTrait;
2634
use ferrisetw::trace::UserTrace;
2735

2836
use windows::Win32::System::Diagnostics::Debug::DebugActiveProcess;
2937
use windows::Win32::System::Diagnostics::Debug::DebugActiveProcessStop;
3038

3139
const MICROSOFT_WINDOWS_KERNEL_PROCESS_PROVIDER: &str = "22fb2cd6-0e7b-422b-a0c7-2fad1fd0e716";
3240

33-
#[cfg(target_arch = "x86_64")]
34-
const HOOK_DLL_NAME: &str = "noblock_input_hook.dll";
35-
#[cfg(target_arch = "x86_64")]
36-
const TRACE_NAME: &str = "NoBlockInput";
37-
#[cfg(target_arch = "x86_64")]
38-
const HOOK_TARGETS: [&str; 1] = ["ScreenConnect.WindowsClient.exe"];
39-
#[cfg(target_arch = "x86")]
40-
const HOOK_DLL_NAME: &str = "noblock_input_hook_x86.dll";
41-
#[cfg(target_arch = "x86")]
42-
const TRACE_NAME: &str = "NoBlockInput_x86";
43-
#[cfg(target_arch = "x86")]
44-
const HOOK_TARGETS: [&str; 2] = ["rfusclient.exe", "Supremo.exe"];
41+
#[derive(Debug, Deserialize)]
42+
#[allow(dead_code)]
43+
struct FullConfig
44+
{
45+
x64: InjectorConfig,
46+
x86: InjectorConfig
47+
}
48+
49+
#[derive(Debug, Deserialize)]
50+
struct InjectorConfig
51+
{
52+
hook_dll_name: Option<String>,
53+
trace_name: String,
54+
processes: Vec<String>
55+
}
4556

4657
fn main()
4758
{
59+
let toml_file = File::open("injector.toml");
60+
let mut toml_file = match toml_file
61+
{
62+
Ok(file) => file,
63+
Err(err) => { println!("Couldn't open the injector configuration file: {err}"); return; }
64+
};
65+
let mut config_toml = String::new();
66+
let toml_read_result = toml_file.read_to_string(&mut config_toml);
67+
match toml_read_result
68+
{
69+
Ok(_) => { },
70+
Err(err) => { println!("Couldn't read the injector configuration file: {err}"); return; }
71+
};
72+
let toml_str = config_toml.as_str();
73+
let injector_config: Result<FullConfig, toml::de::Error> = toml::from_str(toml_str);
74+
let injector_config = match injector_config
75+
{
76+
Ok(config) => config,
77+
Err(err) => { println!("Couldn't parse the injector configuration file: {err}"); return; }
78+
};
79+
80+
#[cfg(target_arch = "x86_64")]
81+
let configuration = injector_config.x64;
82+
#[cfg(target_arch = "x86")]
83+
let configuration = injector_config.x86;
84+
85+
let hook_dll_name = configuration.hook_dll_name;
4886
// Changed the signature to set up for future work
49-
let hook_dll_path = get_hook_dll_path(Some(HOOK_DLL_NAME.to_string()));
87+
let hook_dll_path = get_hook_dll_path(&hook_dll_name);
88+
env::set_var("NOBLOCKINPUT_DLL_PATH", &hook_dll_path);
89+
env::set_var("NOBLOCKINPUT_TRACE_NAME", configuration.trace_name);
5090

5191
let mut all_client_processes: HashSet<OwnedProcess> = HashSet::new();
52-
for proc_name in HOOK_TARGETS
92+
for proc_name in &configuration.processes
5393
{
5494
let client_processes = OwnedProcess::find_all_by_name(proc_name);
5595
for client_process in client_processes
5696
{
5797
all_client_processes.insert(client_process);
5898
}
5999
}
100+
env::set_var("NOBLOCKINPUT_PROCESSES", configuration.processes.join(";"));
60101

61102
let all_client_processes = all_client_processes;
62103
for client_process in all_client_processes
63104
{
64105
let syringe: Syringe = Syringe::for_process(client_process);
65-
let _payload: BorrowedProcessModule = syringe.inject(&hook_dll_path).unwrap();
106+
let _payload: BorrowedProcessModule = syringe.inject(hook_dll_path.as_str()).unwrap();
66107
let pid_result = syringe.process().pid();
67108
let name_result = syringe.process().base_name();
68109
println!("{:?} {:?} [hooked existing]", pid_result.unwrap(), name_result.unwrap());
@@ -74,65 +115,34 @@ fn main()
74115
let name = user_trace.trace_name();
75116
println!("Trace name: {:?}", name);
76117

77-
std::thread::spawn(move || {
118+
std::thread::spawn(move ||
119+
{
78120
let status = UserTrace::process_from_handle(handle);
79121
// This code will be executed when the trace stops. Examples:
80122
// * when it is dropped
81123
// * when it is manually stopped (either by user_trace.stop, or by the `logman stop -ets MyTrace` command)
82124
println!("Trace ended with status {:?}", status);
83125
});
84126

85-
ctrlc::set_handler(move || { std::process::exit(0); }).expect("Error setting Ctrl-C handler");
127+
ctrlc::set_handler(move ||
128+
{
129+
let trace_name = env::var("NOBLOCKINPUT_TRACE_NAME").expect("TRACE_NAME wasn't previously set.");
130+
let stopped_trace_result = stop_trace_by_name(trace_name.as_str());
131+
let err_code = match stopped_trace_result
132+
{
133+
Ok(()) => 0,
134+
Err(_) => -1
135+
};
136+
exit(err_code);
137+
}).expect("Error setting Ctrl-C handler");
86138

87139
loop { sleep(Duration::from_millis(10)); }
88140
}
89141

90-
91-
fn process_callback(record: &EventRecord, schema_locator: &SchemaLocator)
92-
{
93-
match schema_locator.event_schema(record)
94-
{
95-
Ok(schema) =>
96-
{
97-
let event_id = record.event_id();
98-
if event_id == 1
99-
{
100-
let parser = Parser::create(record, &schema);
101-
let process_id: u32 = parser.try_parse("ProcessID").unwrap();
102-
let image_name: String = parser.try_parse("ImageName").unwrap();
103-
let file_name = Path::new(&image_name).file_name().unwrap();
104-
let file_name = file_name.to_str().unwrap().to_string();
105-
println!("{} {}", process_id, file_name);
106-
if !HOOK_TARGETS.contains(&file_name.as_str()) { return; }
107-
unsafe
108-
{
109-
std::thread::spawn(move ||
110-
{
111-
let attachment_result = DebugActiveProcess(process_id);
112-
// Sometimes the process is crashy and dies very quickly and it's better to just ignore it and move on
113-
if attachment_result.is_err() { return; }
114-
sleep(Duration::from_millis(1000));
115-
DebugActiveProcessStop(process_id).unwrap();
116-
});
117-
}
118-
// Same here: if the process goes bye-bye when we want to own it or inject into it, we march on
119-
let owned_sc_client_result = OwnedProcess::from_pid(process_id);
120-
if owned_sc_client_result.is_err() { return; }
121-
let owned_sc_client = owned_sc_client_result.unwrap();
122-
let syringe: Syringe = Syringe::for_process(owned_sc_client);
123-
let hook_dll_path = get_hook_dll_path(Some(HOOK_DLL_NAME.to_string()));
124-
let payload_result = syringe.inject(hook_dll_path);
125-
if payload_result.is_err() { println!("{:?}", payload_result.err().unwrap()); return; }
126-
println!("{} {} [hooked]", process_id, file_name);
127-
}
128-
},
129-
Err(err) => println!("Error {:?}", err)
130-
}
131-
}
132-
133142
fn get_result(provider: Provider) -> (UserTrace, TraceHandle)
134143
{
135-
let th_result = UserTrace::new().named(String::from(TRACE_NAME)).enable(provider).start();
144+
let trace_name = env::var("NOBLOCKINPUT_TRACE_NAME").expect("TRACE_NAME wasn't previously set.");
145+
let th_result = UserTrace::new().named(String::from(&trace_name)).enable(provider).start();
136146
match th_result
137147
{
138148
Ok((ref _user_trace, _trace_handle)) => { return th_result.unwrap(); },
@@ -141,18 +151,64 @@ fn get_result(provider: Provider) -> (UserTrace, TraceHandle)
141151
EvntraceNativeError::AlreadyExist =>
142152
{
143153
let provider = Provider::by_guid(MICROSOFT_WINDOWS_KERNEL_PROCESS_PROVIDER).add_callback(process_callback).build();
144-
stop_trace_by_name(TRACE_NAME).expect("Couldn't stop existing trace.");
145-
return UserTrace::new().named(String::from(TRACE_NAME)).enable(provider).start().unwrap();
154+
stop_trace_by_name(&trace_name).expect("Couldn't stop existing trace.");
155+
return UserTrace::new().named(String::from(&trace_name)).enable(provider).start().unwrap();
146156
},
147157
_ => panic!("Couldn't start a trace: {:?}", err)
148158
},
149159
Err(err) => panic!("Couldn't start a trace: {:?}", err)
150160
};
151161
}
152162

153-
fn get_hook_dll_path(hook_dll_name: Option<String>) -> String
163+
fn process_callback(record: &EventRecord, schema_locator: &SchemaLocator)
164+
{
165+
let hook_targets = env::var("NOBLOCKINPUT_PROCESSES").expect("HOOK_TARGETS was not previously set");
166+
let hook_targets: Vec<&str> = hook_targets.split(';').collect();
167+
match schema_locator.event_schema(record)
168+
{
169+
Ok(schema) =>
170+
{
171+
let event_id = record.event_id();
172+
if event_id == 1
173+
{
174+
let parser = Parser::create(record, &schema);
175+
let process_id: u32 = parser.try_parse("ProcessID").unwrap();
176+
let image_name: String = parser.try_parse("ImageName").unwrap();
177+
let file_name = Path::new(&image_name).file_name().unwrap();
178+
let file_name = file_name.to_str().unwrap().to_string();
179+
if !hook_targets.contains(&file_name.as_str()) { return; }
180+
unsafe
181+
{
182+
std::thread::spawn(move ||
183+
{
184+
let attachment_result = DebugActiveProcess(process_id);
185+
// Sometimes the process is crashy and dies very quickly and it's better to just ignore it and move on
186+
if attachment_result.is_err() { return; }
187+
sleep(Duration::from_millis(1000));
188+
DebugActiveProcessStop(process_id).unwrap();
189+
});
190+
}
191+
// Same here: if the process goes bye-bye when we want to own it or inject into it, we march on
192+
let owned_sc_client_result = OwnedProcess::from_pid(process_id);
193+
if owned_sc_client_result.is_err() { return; }
194+
let owned_sc_client = owned_sc_client_result.unwrap();
195+
let syringe: Syringe = Syringe::for_process(owned_sc_client);
196+
let dll_path = env::var("NOBLOCKINPUT_DLL_PATH").expect("DLL_PATH was not previously set");
197+
let payload_result = syringe.inject(dll_path);
198+
if payload_result.is_err() {
199+
println!("{:?}", payload_result.err().unwrap());
200+
return;
201+
}
202+
println!("{} {} [hooked]", process_id, file_name);
203+
}
204+
},
205+
Err(err) => println!("Error {:?}", err)
206+
}
207+
}
208+
209+
fn get_hook_dll_path(hook_dll_name: &Option<String>) -> String
154210
{
155-
let exe_path: PathBuf = std::env::current_exe().unwrap();
211+
let exe_path: PathBuf = env::current_exe().unwrap();
156212
let cwd = Path::new(&exe_path).parent().unwrap();
157213
return match hook_dll_name
158214
{

0 commit comments

Comments
 (0)