Allows IDA connections to lumen servers lumen.abda.nl/

initial commit

Naim A. a4daab1b

+2
.gitignore
··· 1 + target/ 2 + .zed/
+311
Cargo.lock
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "bitflags" 7 + version = "1.3.2" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 10 + 11 + [[package]] 12 + name = "cc" 13 + version = "1.2.46" 14 + source = "registry+https://github.com/rust-lang/crates.io-index" 15 + checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" 16 + dependencies = [ 17 + "find-msvc-tools", 18 + "shlex", 19 + ] 20 + 21 + [[package]] 22 + name = "cfg-if" 23 + version = "1.0.4" 24 + source = "registry+https://github.com/rust-lang/crates.io-index" 25 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 26 + 27 + [[package]] 28 + name = "find-msvc-tools" 29 + version = "0.1.5" 30 + source = "registry+https://github.com/rust-lang/crates.io-index" 31 + checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" 32 + 33 + [[package]] 34 + name = "generic-array" 35 + version = "0.14.9" 36 + source = "registry+https://github.com/rust-lang/crates.io-index" 37 + checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" 38 + dependencies = [ 39 + "typenum", 40 + "version_check", 41 + ] 42 + 43 + [[package]] 44 + name = "libc" 45 + version = "0.2.177" 46 + source = "registry+https://github.com/rust-lang/crates.io-index" 47 + checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" 48 + 49 + [[package]] 50 + name = "libudis86-sys" 51 + version = "0.2.1" 52 + source = "registry+https://github.com/rust-lang/crates.io-index" 53 + checksum = "139bbf9ddb1bfc90c1ac64dd2923d9c957cd433cee7315c018125d72ab08a6b0" 54 + dependencies = [ 55 + "cc", 56 + "libc", 57 + ] 58 + 59 + [[package]] 60 + name = "log" 61 + version = "0.4.20" 62 + source = "registry+https://github.com/rust-lang/crates.io-index" 63 + checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 64 + 65 + [[package]] 66 + name = "lumen-plugin" 67 + version = "0.1.0" 68 + dependencies = [ 69 + "cc", 70 + "log", 71 + "retour", 72 + "windows", 73 + ] 74 + 75 + [[package]] 76 + name = "mach2" 77 + version = "0.4.3" 78 + source = "registry+https://github.com/rust-lang/crates.io-index" 79 + checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" 80 + dependencies = [ 81 + "libc", 82 + ] 83 + 84 + [[package]] 85 + name = "mmap-fixed-fixed" 86 + version = "0.1.3" 87 + source = "registry+https://github.com/rust-lang/crates.io-index" 88 + checksum = "0681853891801e4763dc252e843672faf32bcfee27a0aa3b19733902af450acc" 89 + dependencies = [ 90 + "libc", 91 + "winapi", 92 + ] 93 + 94 + [[package]] 95 + name = "once_cell" 96 + version = "1.21.3" 97 + source = "registry+https://github.com/rust-lang/crates.io-index" 98 + checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 99 + 100 + [[package]] 101 + name = "region" 102 + version = "3.0.2" 103 + source = "registry+https://github.com/rust-lang/crates.io-index" 104 + checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7" 105 + dependencies = [ 106 + "bitflags", 107 + "libc", 108 + "mach2", 109 + "windows-sys", 110 + ] 111 + 112 + [[package]] 113 + name = "retour" 114 + version = "0.3.1" 115 + source = "registry+https://github.com/rust-lang/crates.io-index" 116 + checksum = "a9af44d40e2400b44d491bfaf8eae111b09f23ac4de6e92728e79d93e699c527" 117 + dependencies = [ 118 + "cfg-if", 119 + "generic-array", 120 + "libc", 121 + "libudis86-sys", 122 + "mmap-fixed-fixed", 123 + "once_cell", 124 + "region", 125 + "slice-pool2", 126 + ] 127 + 128 + [[package]] 129 + name = "shlex" 130 + version = "1.3.0" 131 + source = "registry+https://github.com/rust-lang/crates.io-index" 132 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 133 + 134 + [[package]] 135 + name = "slice-pool2" 136 + version = "0.4.3" 137 + source = "registry+https://github.com/rust-lang/crates.io-index" 138 + checksum = "7a3d689654af89bdfeba29a914ab6ac0236d382eb3b764f7454dde052f2821f8" 139 + 140 + [[package]] 141 + name = "typenum" 142 + version = "1.19.0" 143 + source = "registry+https://github.com/rust-lang/crates.io-index" 144 + checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" 145 + 146 + [[package]] 147 + name = "version_check" 148 + version = "0.9.5" 149 + source = "registry+https://github.com/rust-lang/crates.io-index" 150 + checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 151 + 152 + [[package]] 153 + name = "winapi" 154 + version = "0.3.9" 155 + source = "registry+https://github.com/rust-lang/crates.io-index" 156 + checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 157 + dependencies = [ 158 + "winapi-i686-pc-windows-gnu", 159 + "winapi-x86_64-pc-windows-gnu", 160 + ] 161 + 162 + [[package]] 163 + name = "winapi-i686-pc-windows-gnu" 164 + version = "0.4.0" 165 + source = "registry+https://github.com/rust-lang/crates.io-index" 166 + checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 167 + 168 + [[package]] 169 + name = "winapi-x86_64-pc-windows-gnu" 170 + version = "0.4.0" 171 + source = "registry+https://github.com/rust-lang/crates.io-index" 172 + checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 173 + 174 + [[package]] 175 + name = "windows" 176 + version = "0.44.0" 177 + source = "registry+https://github.com/rust-lang/crates.io-index" 178 + checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" 179 + dependencies = [ 180 + "windows-targets 0.42.2", 181 + ] 182 + 183 + [[package]] 184 + name = "windows-sys" 185 + version = "0.52.0" 186 + source = "registry+https://github.com/rust-lang/crates.io-index" 187 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 188 + dependencies = [ 189 + "windows-targets 0.52.6", 190 + ] 191 + 192 + [[package]] 193 + name = "windows-targets" 194 + version = "0.42.2" 195 + source = "registry+https://github.com/rust-lang/crates.io-index" 196 + checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 197 + dependencies = [ 198 + "windows_aarch64_gnullvm 0.42.2", 199 + "windows_aarch64_msvc 0.42.2", 200 + "windows_i686_gnu 0.42.2", 201 + "windows_i686_msvc 0.42.2", 202 + "windows_x86_64_gnu 0.42.2", 203 + "windows_x86_64_gnullvm 0.42.2", 204 + "windows_x86_64_msvc 0.42.2", 205 + ] 206 + 207 + [[package]] 208 + name = "windows-targets" 209 + version = "0.52.6" 210 + source = "registry+https://github.com/rust-lang/crates.io-index" 211 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 212 + dependencies = [ 213 + "windows_aarch64_gnullvm 0.52.6", 214 + "windows_aarch64_msvc 0.52.6", 215 + "windows_i686_gnu 0.52.6", 216 + "windows_i686_gnullvm", 217 + "windows_i686_msvc 0.52.6", 218 + "windows_x86_64_gnu 0.52.6", 219 + "windows_x86_64_gnullvm 0.52.6", 220 + "windows_x86_64_msvc 0.52.6", 221 + ] 222 + 223 + [[package]] 224 + name = "windows_aarch64_gnullvm" 225 + version = "0.42.2" 226 + source = "registry+https://github.com/rust-lang/crates.io-index" 227 + checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 228 + 229 + [[package]] 230 + name = "windows_aarch64_gnullvm" 231 + version = "0.52.6" 232 + source = "registry+https://github.com/rust-lang/crates.io-index" 233 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 234 + 235 + [[package]] 236 + name = "windows_aarch64_msvc" 237 + version = "0.42.2" 238 + source = "registry+https://github.com/rust-lang/crates.io-index" 239 + checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 240 + 241 + [[package]] 242 + name = "windows_aarch64_msvc" 243 + version = "0.52.6" 244 + source = "registry+https://github.com/rust-lang/crates.io-index" 245 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 246 + 247 + [[package]] 248 + name = "windows_i686_gnu" 249 + version = "0.42.2" 250 + source = "registry+https://github.com/rust-lang/crates.io-index" 251 + checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 252 + 253 + [[package]] 254 + name = "windows_i686_gnu" 255 + version = "0.52.6" 256 + source = "registry+https://github.com/rust-lang/crates.io-index" 257 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 258 + 259 + [[package]] 260 + name = "windows_i686_gnullvm" 261 + version = "0.52.6" 262 + source = "registry+https://github.com/rust-lang/crates.io-index" 263 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 264 + 265 + [[package]] 266 + name = "windows_i686_msvc" 267 + version = "0.42.2" 268 + source = "registry+https://github.com/rust-lang/crates.io-index" 269 + checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 270 + 271 + [[package]] 272 + name = "windows_i686_msvc" 273 + version = "0.52.6" 274 + source = "registry+https://github.com/rust-lang/crates.io-index" 275 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 276 + 277 + [[package]] 278 + name = "windows_x86_64_gnu" 279 + version = "0.42.2" 280 + source = "registry+https://github.com/rust-lang/crates.io-index" 281 + checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 282 + 283 + [[package]] 284 + name = "windows_x86_64_gnu" 285 + version = "0.52.6" 286 + source = "registry+https://github.com/rust-lang/crates.io-index" 287 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 288 + 289 + [[package]] 290 + name = "windows_x86_64_gnullvm" 291 + version = "0.42.2" 292 + source = "registry+https://github.com/rust-lang/crates.io-index" 293 + checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 294 + 295 + [[package]] 296 + name = "windows_x86_64_gnullvm" 297 + version = "0.52.6" 298 + source = "registry+https://github.com/rust-lang/crates.io-index" 299 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 300 + 301 + [[package]] 302 + name = "windows_x86_64_msvc" 303 + version = "0.42.2" 304 + source = "registry+https://github.com/rust-lang/crates.io-index" 305 + checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 306 + 307 + [[package]] 308 + name = "windows_x86_64_msvc" 309 + version = "0.52.6" 310 + source = "registry+https://github.com/rust-lang/crates.io-index" 311 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+27
Cargo.toml
··· 1 + 2 + [package] 3 + name = "lumen-plugin" 4 + version = "0.1.0" 5 + edition = "2021" 6 + 7 + build = "build.rs" 8 + 9 + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 + 11 + [lib] 12 + crate-type = ["cdylib"] 13 + 14 + [dependencies] 15 + log = "0.4" 16 + retour = { version = "0.3.1" } 17 + 18 + [profile.release] 19 + panic = 'unwind' 20 + strip = 'debuginfo' 21 + lto = 'thin' 22 + 23 + [target.'cfg(windows)'.dependencies] 24 + windows = { version = "0.44", features = ["Win32_System_Diagnostics_ToolHelp", "Win32_System_Diagnostics_Debug", "Win32_System_SystemInformation", "Win32_UI_WindowsAndMessaging", "Win32_System_Memory", "Win32_Foundation"] } 25 + 26 + [build-dependencies] 27 + cc = "1.0.83"
+5
build.rs
··· 1 + fn main() { 2 + cc::Build::default().file("helper.c").compile("helper"); 3 + println!("cargo:rustc-link-lib=static=helper"); 4 + println!("cargo:rerun-if-changed=helper.c"); 5 + }
+19
helper.c
··· 1 + #include <stdarg.h> 2 + #include <stddef.h> 3 + #include <stdint.h> 4 + 5 + extern size_t get_callui(); 6 + #define callui(...) ((int(*)(int,...))get_callui())( __VA_ARGS__ ) 7 + 8 + 9 + static int vmsg(const char *fmt, va_list ca) { 10 + return callui(23, fmt, ca); 11 + } 12 + 13 + int msg(const char *fmt, ...) { 14 + va_list va; 15 + va_start(va, fmt); 16 + int r = vmsg(fmt, va); 17 + va_end(va); 18 + return r; 19 + }
+3
rust-toolchain.toml
··· 1 + [toolchain] 2 + channel = "nightly-2025-11-20" 3 + targets = ["x86_64-pc-windows-gnu"]
+45
src/ida/bindings.rs
··· 1 + /// Provides low-level interface to IDA 2 + use std::ffi::{c_char, CStr}; 3 + 4 + #[cfg_attr(target_os = "windows", link(name = "ida", kind = "raw-dylib"))] 5 + extern "C" { 6 + pub static callui: extern "C" fn(i32, ...) -> usize; 7 + fn verror(fmt: *const c_char, va: std::ffi::VaList) -> !; 8 + } 9 + 10 + #[link(name = "helper", kind = "static")] 11 + extern "C" { 12 + fn msg(fmt: *const c_char, ...); 13 + } 14 + 15 + #[no_mangle] 16 + unsafe extern "C" fn get_callui() -> usize { 17 + callui as _ 18 + } 19 + 20 + pub fn info<T: AsRef<CStr>>(s: T) { 21 + let s = s.as_ref().as_ptr(); 22 + 23 + unsafe { 24 + msg(c"%s\n".as_ptr(), s); 25 + } 26 + } 27 + 28 + unsafe extern "C" fn _fatal(fmt: *const c_char, mut va: ...) -> ! { 29 + verror(fmt, va.as_va_list()); 30 + } 31 + 32 + /// Display fatal error message to UI and quit. 33 + pub fn fatal<T: AsRef<CStr>>(msg: T) -> ! { 34 + unsafe { 35 + _fatal(c"%s".as_ptr(), msg.as_ref().as_ptr()); 36 + } 37 + } 38 + 39 + pub fn get_kernel_version() -> String { 40 + let mut buf = [0u8; 1024]; 41 + let res = unsafe { callui(52, buf.as_mut_ptr(), buf.len()) as isize }; 42 + assert!(res > 0, "failed to get kernel version"); 43 + let r = (buf[..res as usize]).to_owned(); 44 + String::from_utf8(r).unwrap() 45 + }
+29
src/ida/logging.rs
··· 1 + use std::ffi::CStr; 2 + 3 + use super::bindings::info; 4 + use log::Log; 5 + 6 + pub struct IdaLogger; 7 + impl Log for IdaLogger { 8 + fn enabled(&self, _metadata: &log::Metadata) -> bool { 9 + true 10 + } 11 + 12 + fn log(&self, record: &log::Record) { 13 + let level = record.level().as_str().chars().next().unwrap_or_default(); 14 + let msg = format!("lumen: {level} {}\x00", record.args()); 15 + if let Ok(cs) = CStr::from_bytes_until_nul(msg.as_bytes()) { 16 + info(cs); 17 + } 18 + } 19 + 20 + fn flush(&self) {} 21 + } 22 + impl IdaLogger { 23 + pub fn init() { 24 + unsafe { 25 + let _ = log::set_logger_racy(&IdaLogger); 26 + log::set_max_level(log::LevelFilter::Trace); 27 + } 28 + } 29 + }
+3
src/ida/mod.rs
··· 1 + mod bindings; 2 + mod logging; 3 + mod plugin_wrapper;
+93
src/ida/plugin_wrapper.rs
··· 1 + use std::{ 2 + ffi::{c_void, CString}, 3 + os::raw::c_char, 4 + panic::PanicHookInfo, 5 + ptr::null_mut, 6 + sync::{Mutex, Once}, 7 + }; 8 + 9 + use log::{error, info, trace}; 10 + 11 + use super::bindings::fatal; 12 + use super::logging::IdaLogger; 13 + 14 + #[no_mangle] 15 + #[used] 16 + static PLUGIN: Plugin = Plugin::new(); 17 + 18 + static PLUGIN_DATA: Mutex<Option<crate::Plugin>> = Mutex::new(None); 19 + 20 + fn handle_panic(pi: &PanicHookInfo) { 21 + let msg = pi.to_string(); 22 + let msg = CString::new(msg).unwrap_or_default(); 23 + fatal(msg); 24 + } 25 + 26 + fn init_globals() { 27 + static ONCE: Once = Once::new(); 28 + ONCE.call_once(|| { 29 + IdaLogger::init(); 30 + std::panic::set_hook(Box::new(handle_panic)); 31 + }); 32 + } 33 + 34 + #[repr(C)] 35 + struct Plugin { 36 + version: i32, 37 + flags: i32, 38 + init: extern "C" fn() -> *mut c_void, 39 + term: Option<extern "C" fn()>, 40 + run: Option<extern "C" fn(usize) -> bool>, 41 + comment: *const c_char, 42 + help: *const c_char, 43 + wanted_name: *const c_char, 44 + wanted_hotkey: *const c_char, 45 + } 46 + unsafe impl Sync for Plugin {} 47 + impl Plugin { 48 + const fn new() -> Self { 49 + Self { 50 + version: 900, 51 + flags: 0, //0x08, // 0x08 /* PLUGIN_UNL */, 52 + init: Self::init, 53 + run: Some(Self::run), 54 + term: Some(Self::term), 55 + comment: c"CMT".as_ptr(), 56 + help: c"Assists in switching between default and custom lumina servers".as_ptr(), 57 + wanted_name: c"Lumina switcher".as_ptr(), 58 + wanted_hotkey: c"ALT+L".as_ptr(), 59 + } 60 + } 61 + 62 + extern "C" fn init() -> *mut c_void { 63 + init_globals(); 64 + trace!("init() called"); 65 + 66 + let mut lock = PLUGIN_DATA.lock().unwrap(); 67 + if lock.is_none() { 68 + if let Some(x) = crate::Plugin::new() { 69 + *lock = Some(x); 70 + } else { 71 + error!("failed to init plugin"); 72 + return null_mut(); 73 + } 74 + } 75 + 76 + 2 as _ 77 + } 78 + 79 + extern "C" fn run(arg: usize) -> bool { 80 + trace!("run({arg}) called"); 81 + let mut lock = PLUGIN_DATA.lock().unwrap(); 82 + if let Some(plugin) = lock.as_mut() { 83 + return plugin.run(arg); 84 + } 85 + true 86 + } 87 + 88 + extern "C" fn term() { 89 + info!("term() called"); 90 + let mut lock = PLUGIN_DATA.lock().unwrap(); 91 + lock.take(); 92 + } 93 + }
+114
src/lib.rs
··· 1 + #![feature(c_variadic)] 2 + 3 + use std::ffi::c_void; 4 + 5 + use log::{error, info, trace, warn}; 6 + use platform::Module; 7 + 8 + mod ida; 9 + mod platform; 10 + 11 + const ANCHOR_STR: &[u8] = b"2.5.4.3"; 12 + 13 + fn get_verify_function_addr() -> Option<usize> { 14 + let module = match Module::get_module_by_name(&["ida64.dll", "ida.dll"]) { 15 + Ok(None) => { 16 + error!("IDA64 module not found!"); 17 + return None; 18 + } 19 + Ok(Some(v)) => v, 20 + Err(err) => { 21 + error!("Failed to enumerate modules! err={err}"); 22 + return None; 23 + } 24 + }; 25 + trace!("ida @ 0x{:06x}", module.get_base()); 26 + let cert_field_str_addr = match module.find_in_image(ANCHOR_STR, 8) { 27 + None => { 28 + error!("anchor not found!"); 29 + return None; 30 + } 31 + Some(v) => v, 32 + }; 33 + trace!("anchor string found @ 0x{cert_field_str_addr:06x}, finding xrefs"); 34 + let mut xrefs = module.xrefs_to(cert_field_str_addr); 35 + let xref = xrefs.next()?; 36 + if xrefs.next().is_some() { 37 + warn!("more than 1 xrefs found!"); 38 + } 39 + trace!("xref @ {xref:08x} ({:08x})", xref - module.get_base()); 40 + 41 + let is_cert_valid = module.find_prologue(xref)?; 42 + trace!("cert_valid_offset @ {is_cert_valid:08x}"); 43 + 44 + Some(is_cert_valid) 45 + } 46 + 47 + /// IDA 9.1: Size 0xE0 48 + #[repr(C)] 49 + struct IdaTlsConnection { 50 + vtable: *const c_void, 51 + } 52 + 53 + struct Plugin { 54 + verify_cert_detour: retour::GenericDetour<extern "C" fn(thus: *mut IdaTlsConnection) -> bool>, 55 + } 56 + impl Plugin { 57 + extern "C" fn patch_is_cert_valid(this: *mut IdaTlsConnection) -> bool { 58 + trace!("patch_is_cert_valid(this={this:p})"); 59 + 60 + true 61 + } 62 + 63 + pub fn new() -> Option<Self> { 64 + let cert_offset = get_verify_function_addr()?; 65 + 66 + let verify_cert_detour = unsafe { 67 + retour::GenericDetour::new::<extern "C" fn(thus: *mut IdaTlsConnection) -> bool>( 68 + std::mem::transmute(cert_offset), 69 + Self::patch_is_cert_valid, 70 + ) 71 + } 72 + .inspect_err(|err| { 73 + error!("failed to init detour: {err}"); 74 + }) 75 + .ok()?; 76 + unsafe { 77 + if let Err(err) = verify_cert_detour.enable() { 78 + error!("failed to enable detour: {err}"); 79 + return None; 80 + } 81 + } 82 + 83 + Some(Self { verify_cert_detour }) 84 + } 85 + 86 + pub fn run(&mut self, _: usize) -> bool { 87 + let is_enabled = self.verify_cert_detour.is_enabled(); 88 + let res = unsafe { 89 + if is_enabled { 90 + self.verify_cert_detour.disable() 91 + } else { 92 + self.verify_cert_detour.enable() 93 + } 94 + }; 95 + if let Err(err) = res { 96 + error!("failed to toggle detour: {err}"); 97 + } else { 98 + if is_enabled { 99 + info!("detour disabled") 100 + } else { 101 + info!("detour enabled"); 102 + } 103 + } 104 + true 105 + } 106 + } 107 + impl Drop for Plugin { 108 + fn drop(&mut self) { 109 + trace!("cleaning up..."); 110 + unsafe { 111 + _ = self.verify_cert_detour.disable(); 112 + } 113 + } 114 + }
+7
src/platform/mod.rs
··· 1 + #[cfg(target_os = "windows")] 2 + mod windows; 3 + 4 + #[cfg(target_os = "windows")] 5 + use self::windows as plat; 6 + 7 + pub use plat::Module;
+221
src/platform/windows.rs
··· 1 + use std::marker::PhantomData; 2 + use std::ptr::addr_of; 3 + use windows::Win32::{ 4 + Foundation::CloseHandle, 5 + System::Diagnostics::{ 6 + Debug::{ImageNtHeader, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ, IMAGE_SECTION_HEADER}, 7 + ToolHelp::{ 8 + CreateToolhelp32Snapshot, Module32FirstW, Module32NextW, MODULEENTRY32W, 9 + TH32CS_SNAPMODULE, 10 + }, 11 + }, 12 + }; 13 + 14 + unsafe fn get_name(ptr: *const u16) -> String { 15 + let mut len = 0; 16 + while unsafe { *ptr.add(len) } != 0 { 17 + len += 1; 18 + } 19 + 20 + let data = unsafe { core::slice::from_raw_parts(ptr, len) }; 21 + String::from_utf16_lossy(data) 22 + } 23 + 24 + #[repr(transparent)] 25 + struct Handle(windows::Win32::Foundation::HANDLE); 26 + impl From<&Handle> for windows::Win32::Foundation::HANDLE { 27 + fn from(val: &Handle) -> Self { 28 + val.0 29 + } 30 + } 31 + impl Drop for Handle { 32 + fn drop(&mut self) { 33 + unsafe { 34 + CloseHandle(self.0); 35 + } 36 + } 37 + } 38 + 39 + pub struct Module { 40 + base: usize, 41 + } 42 + 43 + struct SectionIter<'a> { 44 + base: usize, 45 + idx: usize, 46 + _phantom: std::marker::PhantomData<&'a ()>, 47 + } 48 + impl<'a> Iterator for SectionIter<'a> { 49 + type Item = Section<'a>; 50 + 51 + fn next(&mut self) -> Option<Self::Item> { 52 + unsafe { 53 + let nt_image = ImageNtHeader(self.base as _); 54 + let section_addr_start = addr_of!((*nt_image).OptionalHeader) as usize 55 + + (*nt_image).FileHeader.SizeOfOptionalHeader as usize; 56 + let sections = core::slice::from_raw_parts( 57 + section_addr_start as *const IMAGE_SECTION_HEADER, 58 + (*nt_image).FileHeader.NumberOfSections as usize, 59 + ); 60 + 61 + if self.idx >= sections.len() { 62 + return None; 63 + } 64 + 65 + loop { 66 + let r = &sections[self.idx]; 67 + self.idx += 1; 68 + 69 + let is_read = (r.Characteristics.0 & (IMAGE_SCN_MEM_READ.0)) != 0; 70 + 71 + if !is_read { 72 + continue; 73 + } 74 + 75 + let is_execute = (r.Characteristics.0 & (IMAGE_SCN_MEM_EXECUTE.0)) != 0; 76 + 77 + let start = self.base + r.VirtualAddress as usize; 78 + let len = r.Misc.VirtualSize as usize; 79 + 80 + return Some(Section { 81 + base: std::slice::from_raw_parts(start as *const u8, len), 82 + is_execute, 83 + }); 84 + } 85 + } 86 + } 87 + } 88 + 89 + struct Section<'a> { 90 + base: &'a [u8], 91 + is_execute: bool, 92 + } 93 + 94 + impl Module { 95 + fn walk_sections<'a>(&self) -> impl Iterator<Item = Section<'a>> { 96 + SectionIter { 97 + base: self.base, 98 + idx: 0, 99 + _phantom: PhantomData, 100 + } 101 + } 102 + 103 + pub fn find_in_image(&self, needle: &[u8], alignment: usize) -> Option<usize> { 104 + for section_data in self 105 + .walk_sections() 106 + .filter(|s| !s.is_execute) 107 + .map(|v| v.base) 108 + { 109 + for idx in (0..section_data.len() - needle.len()).step_by(alignment) { 110 + let scope = &section_data[idx..][..needle.len()]; 111 + if scope == needle { 112 + let cert_data_addr = addr_of!(section_data[idx]); 113 + return Some(cert_data_addr as usize); 114 + } 115 + } 116 + } 117 + 118 + None 119 + } 120 + 121 + pub fn get_base(&self) -> usize { 122 + self.base 123 + } 124 + 125 + pub fn get_module_by_name(names: &[&str]) -> Result<Option<Module>, windows::core::Error> { 126 + let handle = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0)? }; 127 + let handle = &Handle(handle); 128 + 129 + let mut entry = MODULEENTRY32W { 130 + dwSize: std::mem::size_of::<MODULEENTRY32W>() as _, 131 + ..Default::default() 132 + }; 133 + 134 + if !unsafe { Module32FirstW(handle, &mut entry).as_bool() } { 135 + return Err(windows::core::Error::from_win32()); 136 + } 137 + 138 + loop { 139 + let module_base = entry.modBaseAddr as usize; 140 + let module_name = unsafe { get_name(entry.szModule.as_ptr()) }; 141 + 142 + for name in names { 143 + if module_name == *name { 144 + return Ok(Some(Module { base: module_base })); 145 + } 146 + } 147 + 148 + if unsafe { !Module32NextW(handle, &mut entry).as_bool() } { 149 + break; 150 + } 151 + } 152 + 153 + Ok(None) 154 + } 155 + 156 + pub fn xrefs_to(&self, ptr: usize) -> impl Iterator<Item = usize> { 157 + self.walk_sections() 158 + .filter(|sec| sec.is_execute) 159 + .map(move |section| { 160 + let ptr = ptr; 161 + section 162 + .base 163 + .windows(7) 164 + .filter(move |&b| { 165 + if !(b[0] == 0x4c && b[1] == 0x8d) { 166 + return false; 167 + } 168 + let modrm = b[2]; 169 + let mod_bits = (modrm & 0b1100_0000) >> 6; 170 + let rm_bits = modrm & 0b0111; 171 + if mod_bits != 0 || rm_bits != 5 { 172 + return false; 173 + } 174 + 175 + let displacement = &b[3..]; 176 + 177 + let base = b.as_ptr() as usize + b.len(); 178 + let displacement = { 179 + let mut v = [0u8; 4]; 180 + v.copy_from_slice(displacement); 181 + i32::from_ne_bytes(v) 182 + }; 183 + 184 + base.wrapping_add_signed(displacement as isize) as usize == ptr 185 + }) 186 + .map(|v| v.as_ptr() as usize) 187 + }) 188 + .flatten() 189 + } 190 + 191 + pub fn find_prologue(&self, addr: usize) -> Option<usize> { 192 + for section in self.walk_sections().filter(|v| v.is_execute) { 193 + let start = section.base.as_ptr() as usize; 194 + let end = start + section.base.len(); 195 + if !(start <= addr && addr < end) { 196 + continue; 197 + } 198 + let scan_len = addr - start; 199 + let search_space = &section.base[..scan_len]; 200 + for idx in (2..search_space.len() - 1).filter(|v| v % 0x10 == 0).rev() { 201 + let data = &search_space[idx..]; 202 + if data.len() < 4 { 203 + continue; 204 + } 205 + 206 + // alignment data... 207 + if !(search_space[idx - 2] == 0xcc && search_space[idx - 1] == 0xcc) { 208 + continue; 209 + } 210 + 211 + // REX prefix 212 + if !(0x40..=0x4f).contains(&data[0]) { 213 + continue; 214 + } 215 + 216 + return Some(idx + search_space.as_ptr() as usize); 217 + } 218 + } 219 + return None; 220 + } 221 + }