diff --git a/build.rs b/build.rs index de4e839..393f1d1 100644 --- a/build.rs +++ b/build.rs @@ -16,8 +16,8 @@ pub struct Config { pub dll_proxy: Option, pub frida_code: Option, pub frida_code_file: Option, - pub target_exec: Option, pub target_process: Option, + pub target_spawn: Option, pub hooks: Vec, } @@ -34,8 +34,8 @@ impl Default for Config { dll_proxy : env::var("DLL_PROXY").ok(), frida_code : env::var("FRIDA_CODE").ok(), frida_code_file: env::var("FRIDA_CODE_FILE").ok(), - target_exec : env::var("TARGET_SPAWN").ok(), target_process : env::var("TARGET_PROCESS").ok(), + target_spawn : env::var("TARGET_SPAWN").ok(), hooks: Vec::new(), } @@ -58,6 +58,18 @@ impl Config { code.replace("\n", "\\n") } + + pub fn populate_env(&self) { + if let Some(target_process) = &self.target_process { + println!("cargo:rustc-env=TARGET_PROCESS={target_process}"); + } + + if let Some(target_spawn) = &self.target_spawn { + println!("cargo:rustc-env=TARGET_SPAWN={target_spawn}"); + } + + println!(r#"cargo:rustc-env=FRIDA_CODE={}"#, self.get_frida_code()); + } } fn main() { @@ -70,17 +82,13 @@ fn main() { Config::default() }; - println!(r#"cargo:rustc-env=FRIDA_CODE={}"#, config.get_frida_code()); + config.populate_env(); let Some(lib_path) = config.dll_proxy else { 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."); - } - dll_proxy_linker_flags(&lib_path, config.hooks); } @@ -95,16 +103,65 @@ fn dll_proxy_linker_flags(lib_path: &str, _hooks: Vec) { 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 Object::PE(pe) = object else { - panic!("Only PE (.dll) files are supported in this mode."); + let (exports, lib_name): (Vec<&str>, String) = match object { + 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", "")) + } + 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", "")) + }, + // 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 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", ""); + if build_target::target_os() == Ok(build_target::Os::Windows) { + for e in exports.iter() { + println!("cargo:warning=Exported function: {e} => {lib_name}-orig.{e}"); + println!("cargo:rustc-link-arg=/export:{e}={lib_name}-orig.{e}"); + } + // } else { + // let link_lib_name = if lib_name.starts_with("lib") { + // lib_name.replacen("lib", "", 1) + // } else { + // lib_name.clone() + // }; - for e in exports.iter() { - println!("cargo:warning=Exported function: {e} => {lib_name}-orig.{e}"); - println!("cargo:rustc-link-arg=/export:{e}={lib_name}-orig.{e}"); + // let lib_dir = path.parent().unwrap().to_str().unwrap(); + + // let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + // std::fs::copy("test.x", out.join("test.x")).unwrap(); + // println!("cargo:rustc-link-search={}", out.display()); + + // println!("cargo:rustc-link-lib={link_lib_name}"); + // println!("cargo:rustc-link-search={lib_dir}"); + // println!("cargo:rustc-link-arg=-l{link_lib_name}"); + // println!("cargo:rustc-link-arg=-Wl,--no-as-needed"); + // println!("cargo:rustc-link-arg=-Wl,--just-symbols={lib_path}"); + + // for e in exports.iter() { + // println!("cargo:warning=Re-exporting function: {e} => {lib_name}-orig.{e}"); + // println!("cargo:rustc-link-arg=-Wl,--wrap={e}"); + // } } println!("cargo:warning=Expected library name: {lib_name}-orig.dll"); diff --git a/src/frida_handler.rs b/src/frida_handler.rs index 10343bf..d9ebf04 100644 --- a/src/frida_handler.rs +++ b/src/frida_handler.rs @@ -1,6 +1,6 @@ #![cfg(feature = "frida")] -use frida::{DeviceManager, DeviceType, Frida, ScriptHandler, ScriptOption, ScriptRuntime}; +use frida::{DeviceManager, DeviceType, Frida, ScriptHandler, ScriptOption, ScriptRuntime, SpawnOptions}; use lazy_static::lazy_static; use serde::Deserialize; @@ -8,31 +8,52 @@ lazy_static! { pub static ref FRIDA: Frida = unsafe { Frida::obtain() }; } -pub fn attach_pid(frida_code: &str, pid: u32) { - println!("[+] Injecting into PID: {}", pid); +#[derive(Debug)] +pub enum AttachMode { + Pid(u32), + Name(String), + Spawn(String), +} + +pub fn attach_with(frida_code: &str, mode: AttachMode) { + println!("[+] Injecting into: {mode:?}"); let device_manager = DeviceManager::obtain(&FRIDA); println!("[*] Device Manager obtained"); - if let Ok(device) = device_manager.get_device_by_type(DeviceType::Local) { + if let Ok(mut device) = device_manager.get_device_by_type(DeviceType::Local) { println!("[*] First device: {}", device.get_name()); + let pid = match mode { + AttachMode::Pid(pid) => pid, + AttachMode::Name(ref name) => { + device.enumerate_processes().iter() + .find(|p| p.get_name() == name) + .expect("Process not found") + .get_pid() + }, + AttachMode::Spawn(ref name) => device.spawn(name, &SpawnOptions::new()).unwrap(), + }; + let session = device.attach(pid).unwrap(); - if !session.is_detached() { - println!("[*] Attached"); + println!("[*] Attached"); - let mut script_option = ScriptOption::new() - .set_runtime(ScriptRuntime::QJS); - println!("[*] Script {}", frida_code); - let script = session - .create_script(frida_code, &mut script_option) - .unwrap(); + let mut script_option = ScriptOption::new() + .set_runtime(ScriptRuntime::QJS); + println!("[*] Script {}", frida_code); + let script = session + .create_script(frida_code, &mut script_option) + .unwrap(); - script.handle_message(&mut Handler).unwrap(); + script.handle_message(&mut Handler).unwrap(); - script.load().unwrap(); - println!("[*] Script loaded"); + script.load().unwrap(); + println!("[*] Script loaded"); + + if let AttachMode::Spawn(_) = mode { + device.resume(pid).unwrap(); + println!("[*] Resuming spawned process") } } else { eprintln!("[!] No device found!"); @@ -111,7 +132,7 @@ mod tests { }, "uint8", [])); "#; - attach_pid(frida_script, 0); + attach_with(frida_script, AttachMode::Pid(0)); assert_eq!(20, unsafe { mylib_foo() }); } } diff --git a/src/injector.rs b/src/injector.rs index 2bccc6a..e424514 100644 --- a/src/injector.rs +++ b/src/injector.rs @@ -3,7 +3,8 @@ compile_error!("No injection method is selected - please enable either dotnet (windows-only) and/or frida feature"); #[cfg(feature = "frida")] -use crate::frida_handler::attach_pid as frida_attach_pid; +use crate::frida_handler::attach_with as frida_attach_with; +use crate::frida_handler::AttachMode; #[no_mangle] pub extern "C" fn attach(pid: u32) { @@ -11,9 +12,26 @@ pub extern "C" fn attach(pid: u32) { { let frida_code = env!("FRIDA_CODE").replace("\\n", "\n"); #[cfg(windows)] - std::thread::spawn(move || frida_attach_pid(&frida_code, pid)); + std::thread::spawn(move || frida_attach_pid(&frida_code, AttachMode::Pid(pid))); #[cfg(not(windows))] - frida_attach_pid(&frida_code, pid); + frida_attach_with(&frida_code, AttachMode::Pid(pid)); + } +} + +#[no_mangle] +pub extern "C" fn attach_name(name: *const u8, len: usize) { + let name_str = unsafe { + let buf = std::slice::from_raw_parts(name, len); + std::str::from_utf8(buf).expect("Invalid UTF-8 in process name") + }; + + #[cfg(feature = "frida")] + { + let frida_code = env!("FRIDA_CODE").replace("\\n", "\n"); + #[cfg(windows)] + std::thread::spawn(move || frida_attach_with(&frida_code, AttachMode::Name(name_str.to_string()))); + #[cfg(not(windows))] + frida_attach_with(&frida_code, AttachMode::Name(name_str.to_string())); } } @@ -22,3 +40,22 @@ pub extern "C" fn attach_self() { println!("[*] Attaching to self"); attach(0); } + +#[no_mangle] +pub extern "C" fn spawn(name: *const u8, len: usize) { + let name_str = unsafe { + let buf = std::slice::from_raw_parts(name, len); + std::str::from_utf8(buf).expect("Invalid UTF-8 in spawn name") + }; + + println!("[*] Spawning: {name_str}"); + + #[cfg(feature = "frida")] + { + let frida_code = env!("FRIDA_CODE").replace("\\n", "\n"); + #[cfg(windows)] + std::thread::spawn(move || frida_attach_with(&frida_code, AttachMode::Spawn(name_str.to_string()))); + #[cfg(not(windows))] + frida_attach_with(&frida_code, AttachMode::Spawn(name_str.to_string())); + } +} diff --git a/src/lib.rs b/src/lib.rs index 43a1473..75b86f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,8 @@ Interceptor.replace(foo, new NativeCallback(function () { .arg("tests/mybin/Cargo.toml") .arg("--target-dir") .arg("target/test_frida_on_load") + // We're compiling in the same target dir so that frida_deepfreeze_rs is found + // We'd have to copy it to the target dir otherwise .env("RUSTFLAGS", "-C link-arg=-Wl,--no-as-needed -C link-arg=-lfrida_deepfreeze_rs") .status() .unwrap(); @@ -77,18 +79,18 @@ Interceptor.replace(foo, new NativeCallback(function () { .arg("--manifest-path") .arg("tests/mylib/Cargo.toml") .arg("--target-dir") - .arg("target/test_frida_dll_proxy") + .arg("target/test_frida_dll_proxy/mylib") .status() .unwrap(); assert!(mylib_status.success(), "Failed to build mylib"); + // fs::copy(format!("target/test_frida_dll_proxy/mylib/debug/{}", get_lib_name("mylib")), format!("target/test_frida_dll_proxy/lib/debug/deps/{}", get_lib_name("mylib-orig"))).expect("Failed to rename original DLL"); let lib_status = Command::new("cargo") .arg("build") .arg("--lib") .arg("--target-dir") - .arg("target/test_frida_dll_proxy") - .env("DLL_PROXY", format!("target/test_frida_dll_proxy/debug/deps/{}", mylib_name)) - .env("RUSTFLAGS", "-C link-arg=-Wl,--no-as-needed -C link-arg=-lmylib") + .arg("target/test_frida_dll_proxy/lib") + .env("DLL_PROXY", format!("target/test_frida_dll_proxy/mylib/debug/{mylib_name}")) .env("FRIDA_CODE", r#" const foo = Module.getExportByName(null, "mylib_foo"); Interceptor.replace(foo, new NativeCallback(function () { @@ -101,16 +103,17 @@ Interceptor.replace(foo, new NativeCallback(function () { assert!(lib_status.success(), "Failed to build dynamic library"); - let target_dir = "target/test_frida_dll_proxy/debug/deps/"; - fs::rename(format!("{}{}", target_dir, get_lib_name("mylib")), format!("{}{}", target_dir, get_lib_name("mylib-orig"))).expect("Failed to rename original DLL"); - fs::rename(format!("{}{}", target_dir, get_lib_name("frida_deepfreeze_rs")), format!("{}{}", target_dir, get_lib_name("mylib"))).expect("Failed to rename deepfreeze DLL"); + let target_dir = "target/test_frida_dll_proxy/mybin/debug/deps/"; + fs::copy(format!("target/test_frida_dll_proxy/mylib/debug/{}", get_lib_name("mylib")), format!("{target_dir}{}", get_lib_name("mylib-orig"))).expect("Failed to rename original DLL"); + fs::copy(format!("target/test_frida_dll_proxy/lib/debug/{}", get_lib_name("frida_deepfreeze_rs")), format!("{target_dir}{}", get_lib_name("mylib"))).expect("Failed to rename deepfreeze DLL"); let bin_status = Command::new("cargo") .arg("run") .arg("--manifest-path") .arg("tests/mybin/Cargo.toml") .arg("--target-dir") - .arg("target/test_frida_dll_proxy") + .arg("target/test_frida_dll_proxy/mybin") + // .env("RUSTFLAGS", "-C link-arg=-Wl,--no-as-needed -C link-arg=-lmylib") .status() .unwrap(); diff --git a/src/main.rs b/src/main.rs index 8f4f80c..2acddd8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,19 +2,26 @@ pub mod injector; #[cfg(feature = "frida")] pub mod frida_handler; -pub use injector::attach; +pub use injector::*; fn main() { let args: Vec = std::env::args().collect(); - if args.len() < 2 { - eprintln!("Usage: {} ", args[0]); + if args.len() >= 2 { + if let Ok(pid) = args[1].parse() { + attach(pid); + return; + } else { + attach_name(args[1].as_ptr(), args[1].len()); + return; + } + } else if let Some(spawn_path) = option_env!("TARGET_SPAWN") { + spawn(spawn_path.as_ptr(), spawn_path.len()); + return; + } else if let Some(process_name) = option_env!("TARGET_PROCESS") { + attach_name(process_name.as_ptr(), process_name.len()); return; } - let pid: u32 = args[1].parse().unwrap(); - attach(pid); + eprintln!("Usage: {} ", args[0]); } - -// #[cfg(test)] -// mod integration_tests;