diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2a62b92..533655b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,9 +36,9 @@ jobs: key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: ${{ runner.os }}-cargo- - name: Build - run: cargo build --verbose + run: cargo build - name: Run tests - frida - run: cargo test --verbose + run: cargo test --all-features # - name: Run tests - frida # if: runner.os == 'windows-latest' diff --git a/build.rs b/build.rs index 3b46aaa..f33e40b 100644 --- a/build.rs +++ b/build.rs @@ -37,17 +37,18 @@ fn main() { .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", "")) + // 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", "")) }, - // #[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); @@ -60,20 +61,35 @@ fn main() { println!("cargo:rustc-link-arg=/export:{}={}-orig.{}", e, lib_name, e); } - #[cfg(not(target_os = "windows"))] + #[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"); - writeln!(symbols, "#[allow(dead_code)]").unwrap(); - writeln!(symbols, "#[link(name = \"{}\")]", lib_name.replace("lib", "")).unwrap(); - writeln!(symbols, "extern {{").unwrap(); + 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, "\tpub fn {}();", e).unwrap(); + // writeln!(symbols, "\t#[no_mangle]").unwrap(); + writeln!(symbols, "\t\tpub fn {e}();").unwrap(); } + writeln!(symbols, "\t}}").unwrap(); writeln!(symbols, "}}").unwrap(); } diff --git a/src/cs.rs b/src/cs.rs deleted file mode 100644 index f3afd57..0000000 --- a/src/cs.rs +++ /dev/null @@ -1,32 +0,0 @@ -use cs_eval::{EvalContext, EvalError}; - -fn cs_inject() { - // Define your C# code to be compiled - let csharp_code = r#" - using System; - - public class Program - { - public static void Main() - { - Console.WriteLine("Hello, C#!"); - } - } - "#; - - // Create an evaluation context - let mut context = EvalContext::new(); - - // Compile and execute the C# code - match context.eval::<()>(csharp_code) { - Ok(_) => { - println!("C# code executed successfully"); - } - Err(EvalError::CompilationError(err)) => { - println!("Compilation error: {:?}", err); - } - Err(EvalError::ExecutionError(err)) => { - println!("Execution error: {:?}", err); - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 8ddf2f2..9370e81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,20 +1,20 @@ pub mod injector; #[cfg(feature = "frida")] pub mod frida_handler; -// #[cfg(feature = "dotnet")] -// pub mod cs; -// #[cfg(not(windows))] -// pub mod symbols; -// #[cfg(not(windows))] -// pub use symbols::*; +#[cfg(symbols)] +pub mod symbols; +#[cfg(symbols)] +pub use symbols::*; pub use injector::attach_self; -#[cfg(all(unix, not(test), not(feature = "dotnet")))] +#[cfg(all(unix, not(test)))] use ctor::ctor; -#[cfg(all(unix, not(test), not(feature = "dotnet")))] +// 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"); @@ -23,15 +23,15 @@ fn _start() { // For some reason ctor doesn't work on Windows - it hangs the process // during DeviceManager::obtain. DllMain works fine though. -#[cfg(all(any(windows, feature = "dotenv"), not(test)))] +#[cfg(all(windows, not(test)))] use std::ffi::c_void; -#[cfg(all(any(windows, feature = "dotenv"), not(test)))] +#[cfg(all(windows, not(test)))] use winapi::um::winnt::DLL_PROCESS_ATTACH; -#[cfg(all(any(windows, feature = "dotenv"), not(test)))] +#[cfg(all(windows, not(test)))] use winapi::um::libloaderapi::LoadLibraryA; -#[cfg(all(any(windows, feature = "dotenv"), not(test)))] +#[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 { @@ -57,11 +57,20 @@ pub extern "system" fn DllMain(dll_module: *mut c_void, call_reason: u32, _: *mu mod tests { use pretty_assertions::assert_eq; use std::process::Command; - #[cfg(all(windows, feature = "frida"))] use std::fs; + fn get_lib_name(name: &str) -> String { + #[cfg(target_os = "windows")] + return format!("{}.dll", name); + + #[cfg(target_os = "linux")] + return format!("lib{}.so", name); + + #[cfg(target_os = "darwin")] + return format!("lib{}.dylib", name); + } + #[test] - // #[cfg(all(unix, feature = "frida"))] fn test_frida_on_load() { let lib_status = Command::new("cargo") .arg("build") @@ -88,55 +97,59 @@ mod tests { .arg("target/test_frida_on_load") .env("RUSTFLAGS", "-C link-arg=-Wl,--no-as-needed -C link-arg=-lfrida_deepfreeze_rs") .status() - .expect("Failed to build mybin"); + .unwrap(); assert_eq!(bin_status.code().unwrap(), 40, "Failed to replace foo()"); } - // #[test] - // #[cfg(all(windows, feature = "frida"))] - // fn test_frida_on_load() { - // let mylib_status = Command::new("cargo") - // .arg("build") - // .arg("--lib") - // .arg("--manifest-path") - // .arg("tests/mylib/Cargo.toml") - // .arg("--target-dir") - // .arg("target/test_frida_on_load") - // .status() - // .expect("Failed to build mylib"); - // assert!(mylib_status.success(), "Failed to build mylib"); + #[test] + fn test_frida_dll_proxy() { + let mylib_name = get_lib_name("mylib"); + fs::remove_file(format!("target/test_frida_dll_proxy/debug/deps/{}", mylib_name)).unwrap_or_else(|_| ()); - // let lib_status = Command::new("cargo") - // .arg("build") - // .arg("--lib") - // .arg("--target-dir") - // .arg("target/test_frida_on_load") - // .env("DLL_PROXY", "target/test_frida_on_load/debug/deps/mylib.dll") - // .env("FRIDA_CODE", r#" - // const foo = Module.getExportByName(null, "mylib_foo"); - // Interceptor.replace(foo, new NativeCallback(function () { - // console.log("replaced foo() called"); - // return 40; - // }, "uint8", [])); - // "#) - // .status() - // .expect("Failed to build dynamic library"); + let mylib_status = Command::new("cargo") + .arg("build") + .arg("--lib") + .arg("--manifest-path") + .arg("tests/mylib/Cargo.toml") + .arg("--target-dir") + .arg("target/test_frida_dll_proxy") + .status() + .unwrap(); + assert!(mylib_status.success(), "Failed to build mylib"); - // assert!(lib_status.success(), "Failed to build dynamic library"); + 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") + .env("FRIDA_CODE", r#" + const foo = Module.getExportByName(null, "mylib_foo"); + Interceptor.replace(foo, new NativeCallback(function () { + console.log("replaced foo() called"); + return 40; + }, "uint8", [])); + "#) + .status() + .unwrap(); - // fs::rename("target/test_frida_on_load/debug/deps/mylib.dll", "target/test_frida_on_load/debug/mylib-orig.dll").expect("Failed to rename original DLL"); - // fs::rename("target/test_frida_on_load/debug/frida_deepfreeze_rs.dll", "target/test_frida_on_load/debug/mylib.dll").expect("Failed to rename deepfreeze DLL"); + assert!(lib_status.success(), "Failed to build dynamic library"); - // let bin_status = Command::new("cargo") - // .arg("run") - // .arg("--manifest-path") - // .arg("tests/mybin/Cargo.toml") - // .arg("--target-dir") - // .arg("target/test_frida_on_load") - // .status() - // .expect("Failed to build mybin"); + 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"); - // assert_eq!(bin_status.code().unwrap(), 40, "Failed to replace foo()"); - // } + 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") + .status() + .unwrap(); + + assert_eq!(bin_status.code().unwrap(), 40, "Failed to replace foo()"); + } }