1pub mod cli;
13pub mod cmp;
14pub mod connections;
15pub mod drcore;
16pub mod drwrap;
17pub mod ffi;
18pub mod fuzzer;
19pub mod instrument;
20pub mod network;
21pub mod pipe;
22pub mod socket;
23pub mod trajectory;
24pub mod utils;
25pub mod wrappers;
26
27use drcore::{get_proc_address, log, utf8_name_of_module};
28use enum_iterator::all;
29use ffi::{app_pc, module_data_t};
30use navigator::pipe_event::PipeEvent;
31use std::ffi::CStr;
32use std::os::raw::{c_char, c_void};
33use std::panic;
34use std::sync::{LazyLock, Mutex};
35use utils::Boolean;
36
37static MOD_BASE_ADDR: LazyLock<Mutex<Option<usize>>> = LazyLock::new(|| Mutex::new(None));
38static MOD_SIZE: LazyLock<Mutex<Option<usize>>> = LazyLock::new(|| Mutex::new(None));
39
40unsafe extern "C" {
41 pub static tls_idx: ::std::os::raw::c_int;
42}
43
44#[unsafe(no_mangle)]
45pub extern "C" fn libinject_init(id: ffi::client_id_t) {
46 panic::set_hook(Box::new(|panic_info| {
48 log(&format!("panic occurred: {panic_info}"));
49 }));
50
51 let args = unsafe { cli::parse(id) };
53 log(&format!("CLI args parsed as:\n {:?}", args));
54
55 if let Some(trajectory) = args.trajectory {
58 trajectory::init(trajectory);
59 } else if let Some(trajectory) = args.trajectory_file {
60 trajectory::init(trajectory);
61 }
62 pipe::init(args.pipe);
64}
65
66#[unsafe(no_mangle)]
67pub extern "C" fn libinject_exit() {
68 network::dump(network::DumpFormat::CSV)
69 .expect("Failed to dump traffic captured on the spoofed network");
70}
71
72#[unsafe(no_mangle)]
73pub extern "C" fn libinject_warn(msg: *const c_char) {
74 unsafe {
75 if msg.is_null() {
76 pipe::send(&PipeEvent::Warning("Warning msg ptr is null!".to_string()));
77 } else {
78 if let Ok(s) = CStr::from_ptr(msg).to_str() {
79 pipe::send(&PipeEvent::Warning(s.to_string()));
80 } else {
81 pipe::send(&PipeEvent::Warning(
82 "Warning msg is an invalid UTF-8 string!".to_string(),
83 ));
84 }
85 }
86 }
87}
88
89#[unsafe(no_mangle)]
93pub extern "C" fn module_load_event(
94 _drctx: *mut c_void,
95 module_ptr: *const module_data_t,
96 _loaded: bool,
97) {
98 let module;
99 unsafe {
100 module = *module_ptr;
101 }
102 let mod_name = match utf8_name_of_module(module) {
103 Ok(name) => name,
104 Err(utf8_name_error) => {
105 log(&format!(
106 "[module load] failed to read the module name: {:?}",
107 utf8_name_error
108 ));
109 return;
110 }
111 };
112 log(&format!("[module load] loading {mod_name}"));
113
114 let target_mod_name = cli::get_args()
115 .expect(
116 "Args parsed in libinject_init, which will have been called before module_load_event",
117 )
118 .target_module
119 .to_lowercase();
120
121 if &mod_name.to_lowercase() == &target_mod_name {
122 let (mod_base_addr, mod_end, internal_size);
123 unsafe {
124 mod_base_addr = module.__bindgen_anon_1.start as app_pc;
125 mod_end = module.end as app_pc;
126 internal_size = module.module_internal_size as usize;
127 }
128 let computed_size =
129 internal_size.max((mod_end as usize).saturating_sub(mod_base_addr as usize));
130 log(&format!(
131 "[module load] TARGET_MODULE ({}) base=0x{:X} end=0x{:X} size=0x{:X}",
132 target_mod_name, mod_base_addr as usize, mod_end as usize, computed_size
133 ));
134 {
135 let mut base_lock = MOD_BASE_ADDR
136 .lock()
137 .expect("can't get lock on MOD_BASE_ADDR");
138 *base_lock = Some(mod_base_addr as usize);
139 }
140 {
141 let mut size_lock = MOD_SIZE.lock().expect("can't get lock on MOD_SIZE");
142 *size_lock = Some(computed_size);
143 }
144 }
145
146 if module.entry_point.is_null() {
147 log(
148 "[module load] module entry point is a null pointer, will not search for proc_addresses.",
149 );
150 return;
151 };
152
153 if &mod_name.to_lowercase() == "ws2_32.dll" {
156 let mod_base_addr = module.entry_point as app_pc;
157 unsafe {
158 wrap_network_symbols(mod_base_addr, "ws2_32.dll");
159 }
160 };
161 return;
162}
163
164unsafe fn wrap_network_symbols(mod_base_addr: app_pc, mod_name: &str) {
167 unsafe {
168 all::<wrappers::Symbols>().for_each(|symbol| {
169 if let Some(addr) = get_proc_address(mod_base_addr, symbol.to_str()) {
170 match ffi::drwrap_wrap(addr as *mut u8, Some(symbol.pre_fn()), symbol.post_fn())
171 .as_bool()
172 {
173 true => {
174 log(&format!(
175 "[module load] wrapped {} in module {mod_name} @ 0x{:?}",
176 symbol.to_str(),
177 addr
178 ));
179 }
180 false => log(&format!(
181 "[module load] failed to wrap {} in module {mod_name} @ 0x{:?}",
182 symbol.to_str(),
183 addr
184 )),
185 }
186 };
187 });
188 }
189}
190
191#[unsafe(no_mangle)]
192pub unsafe extern "C" fn wrap_network_symbols_extern(
193 mod_base_addr: app_pc,
194 module_name: *const c_char,
195) {
196 unsafe {
197 if module_name.is_null() {
198 log("[wrap_network_symbols_extern] module_name is a null pointer!");
199 return;
200 }
201
202 let module_name_str = match CStr::from_ptr(module_name).to_str() {
203 Ok(s) => s,
204 Err(_) => return,
205 };
206 wrap_network_symbols(mod_base_addr, module_name_str);
207 }
208}
209
210#[unsafe(no_mangle)]
211pub unsafe extern "C" fn event_thread_init(drcontext: *mut c_void) {
212 log("[event_thread_init] new thread!");
213 let stack = Box::new(instrument::RawCallStack::new());
214
215 unsafe {
216 ffi::drmgr_set_tls_field(drcontext, tls_idx, Box::into_raw(stack) as *mut c_void);
218 }
219}
220
221#[unsafe(no_mangle)]
222pub unsafe extern "C" fn event_thread_exit(drcontext: *mut c_void) {
223 unsafe {
224 let ptr = ffi::drmgr_get_tls_field(drcontext, tls_idx) as *mut instrument::RawCallStack;
225 drop(Box::from_raw(ptr)); }
227}