diff --git a/Cargo.lock b/Cargo.lock index 5db0aff..edf6abe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,6 +88,12 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +[[package]] +name = "build-target" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832133bbabbbaa9fbdba793456a2827627a7d2b8fb96032fa1e7666d7895832b" + [[package]] name = "bumpalo" version = "3.15.3" @@ -216,6 +222,7 @@ dependencies = [ name = "frida-deepfreeze-rs" version = "0.1.0" dependencies = [ + "build-target", "ctor", "frida", "goblin", diff --git a/Cargo.toml b/Cargo.toml index 2deaff7..4afba94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ ctor = "0.2.7" [build-dependencies] goblin = "0.8.0" +build-target = "0.4" [dev-dependencies] pretty_assertions = "1.4.0" -# mylib = { path = "tests/mylib" } diff --git a/build.rs b/build.rs index f33e40b..0857a48 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,4 @@ use std::env; -use std::io::Write; -use std::fs::File; use std::path::Path; fn main() { @@ -8,91 +6,35 @@ fn main() { println!("cargo:rerun-if-env-changed=DLL_PROXY"); let Ok(lib_path) = env::var("DLL_PROXY") else { - println!("cargo:warning=No DLL_PROXY set, the resulting library has to be manually injected or compiled into the target binary"); + println!("cargo:warning=No DLL_PROXY set, the resulting library has to be manually injected or compiled into the target binary/process"); return; }; + if build_target::target_os() != Ok(build_target::Os::Windows) { + panic!("Dll proxying mode is only supported on Windows."); + } + use goblin::Object; println!("cargo:rerun-if-changed={}", &lib_path); - let path = std::path::Path::new(&lib_path); + let path = Path::new(&lib_path); let lib_filename = path.file_name().unwrap().to_str().unwrap(); let lib_bytes = std::fs::read(path).expect(format!("Failed to open given library file {}", &lib_filename).as_str()); let object = Object::parse(&lib_bytes).expect(format!("Failed to parse given libary file {}", &lib_filename).as_str()); - let (exports, lib_name): (Vec<&str>, String) = match object { - #[cfg(target_os = "windows")] - Object::PE(o) => { - (o.exports - .iter() - .map(|e| e.name.unwrap()) - .collect(), - o.name.expect("Couldn't read the name of the DLL. Is it a .NET DLL? It's not supported").replace(".dll", "")) - } - #[cfg(target_os = "linux")] - Object::Elf(o) => { - (o.dynsyms - .iter() - .filter(|e| e.is_function() && !e.is_import()) - .map(|e| o.dynstrtab.get_at(e.st_name).unwrap()) - .collect(), - // o.soname.expect("Couldn't read the name of the SO.").replace(".so", "")) - lib_filename.replace(".so", "")) - }, - #[cfg(target_os = "darwin")] - Object::Mach(goblin::mach::Mach::Binary(o)) => { - (o.dynsyms - .iter() - .filter(|e| e.is_function() && !e.is_import()) - .map(|e| o.dynstrtab.get_at(e.st_name).unwrap()) - .collect(), - o.name.expect("Couldn't read the name of the DLL. Is it a .NET DLL? It's not supported").replace(".dll", "")) - }, - _ => { - println!("Only PE (.dll) and ELF (.so) files are supported in their respective target platforms."); - std::process::exit(1); - }, + let Object::PE(pe) = object else { + panic!("Only PE (.dll) files are supported in this mode."); }; - #[cfg(target_os = "windows")] + let exports: Vec<&str> = pe.exports.iter().map(|e| e.name.unwrap()).collect(); + let lib_name = pe.name.expect("Couldn't read the name of the DLL. Is it a .NET DLL? It's not supported").replace(".dll", ""); + for e in exports.iter() { println!("cargo:warning=Exported function: {} => {}-orig.{}", e, lib_name, e); println!("cargo:rustc-link-arg=/export:{}={}-orig.{}", e, lib_name, e); } - #[cfg(unix)] - { - let symbols_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("src").join("symbols.rs"); - let mut symbols = File::create(&symbols_path).unwrap(); - println!("cargo:rerun-if-changed={:?}", symbols_path); - println!("cargo:rustc-cfg=symbols"); - - let lib_name = if lib_name.starts_with("lib") { - lib_name.replacen("lib", "", 1) - } else { - lib_name.clone() - }; - - writeln!(symbols, "#![allow(dead_code)]").unwrap(); - - for e in exports.iter() { - writeln!(symbols, "#[no_mangle]").unwrap(); - writeln!(symbols, r#"pub unsafe extern "C" fn {e}() {{ original::{e}() }}"#).unwrap(); - } - - writeln!(symbols, "pub mod original {{").unwrap(); - writeln!(symbols, "\t#[link(name = \"{}\")]", lib_name).unwrap(); - writeln!(symbols, "\textern {{").unwrap(); - for e in exports.iter() { - println!("cargo:warning=Exported function: {}", e); - // writeln!(symbols, "\t#[no_mangle]").unwrap(); - writeln!(symbols, "\t\tpub fn {e}();").unwrap(); - } - writeln!(symbols, "\t}}").unwrap(); - writeln!(symbols, "}}").unwrap(); - } - println!("cargo:warning=Expected library name: {}-orig.dll", lib_name); println!("cargo:rustc-env=LIB_NAME={}-orig.dll", lib_name); } diff --git a/src/lib.rs b/src/lib.rs index 9370e81..fd3e0d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,63 +2,24 @@ pub mod injector; #[cfg(feature = "frida")] pub mod frida_handler; -#[cfg(symbols)] -pub mod symbols; -#[cfg(symbols)] -pub use symbols::*; - pub use injector::attach_self; -#[cfg(all(unix, not(test)))] -use ctor::ctor; - // During testing we compile a debug binary without `test`. // Enabling `ctor` during testing would hook the test runner and break it. #[cfg(all(unix, not(test)))] -#[ctor] -fn _start() { - println!("[+] frida-deepfreeze-rs library injected"); - attach_self(); -} - -// For some reason ctor doesn't work on Windows - it hangs the process -// during DeviceManager::obtain. DllMain works fine though. -#[cfg(all(windows, not(test)))] -use std::ffi::c_void; -#[cfg(all(windows, not(test)))] -use winapi::um::winnt::DLL_PROCESS_ATTACH; +pub mod loader_unix; #[cfg(all(windows, not(test)))] -use winapi::um::libloaderapi::LoadLibraryA; - +pub mod loader_windows; #[cfg(all(windows, not(test)))] -#[no_mangle] -#[allow(non_snake_case, unused_variables)] -pub extern "system" fn DllMain(dll_module: *mut c_void, call_reason: u32, _: *mut ()) -> bool { - match call_reason { - DLL_PROCESS_ATTACH => { - println!("[+] frida-deepfreeze-rs DLL injected"); - - if let Some(lib_name) = option_env!("LIB_NAME") { - unsafe { LoadLibraryA(lib_name.as_ptr() as *const i8); } - println!("[+] Original DLL {} loaded", lib_name); - } - - attach_self(); - } - // Maybe we should detach? Is it useful? - _ => () - } - - true -} +pub use loader_windows::DllMain; #[cfg(test)] mod tests { use pretty_assertions::assert_eq; use std::process::Command; - use std::fs; + #[allow(dead_code)] fn get_lib_name(name: &str) -> String { #[cfg(target_os = "windows")] return format!("{}.dll", name); @@ -103,7 +64,10 @@ mod tests { } #[test] + #[cfg(windows)] fn test_frida_dll_proxy() { + use std::fs; + let mylib_name = get_lib_name("mylib"); fs::remove_file(format!("target/test_frida_dll_proxy/debug/deps/{}", mylib_name)).unwrap_or_else(|_| ()); diff --git a/src/loader_unix.rs b/src/loader_unix.rs new file mode 100644 index 0000000..ad04610 --- /dev/null +++ b/src/loader_unix.rs @@ -0,0 +1,9 @@ +use ctor::ctor; + +use crate::injector::attach_self; + +#[ctor] +fn _start() { + println!("[+] frida-deepfreeze-rs library injected"); + attach_self(); +} diff --git a/src/loader_windows.rs b/src/loader_windows.rs new file mode 100644 index 0000000..3c55b90 --- /dev/null +++ b/src/loader_windows.rs @@ -0,0 +1,27 @@ +use std::ffi::c_void; +use winapi::um::winnt::DLL_PROCESS_ATTACH; +use winapi::um::libloaderapi::LoadLibraryA; + +// For some reason ctor doesn't work on Windows - it hangs the process +// during DeviceManager::obtain. DllMain works fine though. +// Would be nice to have a single entry point for all platforms. +#[no_mangle] +#[allow(non_snake_case, unused_variables)] +pub extern "system" fn DllMain(dll_module: *mut c_void, call_reason: u32, _: *mut ()) -> bool { + match call_reason { + DLL_PROCESS_ATTACH => { + println!("[+] frida-deepfreeze-rs DLL injected"); + + if let Some(lib_name) = option_env!("LIB_NAME") { + unsafe { LoadLibraryA(lib_name.as_ptr() as *const i8); } + println!("[+] Original DLL {} loaded", lib_name); + } + + attach_self(); + } + // Maybe we should detach? Is it useful? + _ => () + } + + true +}