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 ;
69use std:: path:: Path ;
710use std:: path:: PathBuf ;
811use std:: thread:: sleep;
912use std:: time:: Duration ;
10- use std:: collections:: HashSet ;
13+ use std:: io:: Read ;
14+ use std:: process:: exit;
1115use std:: string:: ToString ;
1216
1317use dll_syringe:: Syringe ;
14- use dll_syringe:: process:: { BorrowedProcessModule , Process } ;
18+ use dll_syringe:: process:: BorrowedProcessModule ;
19+ use dll_syringe:: process:: Process ;
1520use dll_syringe:: process:: OwnedProcess ;
1621
22+ use serde:: Deserialize ;
23+
1724use ferrisetw:: EventRecord ;
1825use ferrisetw:: native:: TraceHandle ;
1926use ferrisetw:: native:: EvntraceNativeError ;
2027use ferrisetw:: parser:: Parser ;
2128use ferrisetw:: provider:: Provider ;
2229use 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 ;
2532use ferrisetw:: trace:: TraceError ;
33+ use ferrisetw:: trace:: TraceTrait ;
2634use ferrisetw:: trace:: UserTrace ;
2735
2836use windows:: Win32 :: System :: Diagnostics :: Debug :: DebugActiveProcess ;
2937use windows:: Win32 :: System :: Diagnostics :: Debug :: DebugActiveProcessStop ;
3038
3139const 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
4657fn 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-
133142fn 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