diff --git a/.gitignore b/.gitignore index 18356ab..b0b9238 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ -/target +target __handlers__ +/examples/cs/*/obj +/examples/cs/*/bin diff --git a/Cargo.lock b/Cargo.lock index 17dd16a..59aedf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,6 +102,16 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "ctor" version = "0.2.0" @@ -112,6 +122,12 @@ dependencies = [ "syn 2.0.16", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "either" version = "1.8.1" @@ -202,7 +218,7 @@ dependencies = [ [[package]] name = "frida" version = "0.4.0" -source = "git+https://github.com/dzervas/frida-rust#1367b5d58bf50738bbeb49272b3eb99b98bbf891" +source = "git+https://github.com/frida/frida-rust#6be323811b87d204595ded8e4f18e090a954ca81" dependencies = [ "frida-sys", "thiserror", @@ -211,7 +227,7 @@ dependencies = [ [[package]] name = "frida-build" version = "0.2.1" -source = "git+https://github.com/dzervas/frida-rust#1367b5d58bf50738bbeb49272b3eb99b98bbf891" +source = "git+https://github.com/frida/frida-rust#6be323811b87d204595ded8e4f18e090a954ca81" dependencies = [ "reqwest", "tar", @@ -222,19 +238,23 @@ dependencies = [ name = "frida-deepfreeze-rs" version = "0.1.0" dependencies = [ - "ctor", + "cc", + "ctor 0.2.0", "frida", "goblin", "lazy_static", + "mylib", + "pretty_assertions", "serde", "serde_json", "winapi", + "windows-sys 0.48.0", ] [[package]] name = "frida-sys" version = "0.4.0" -source = "git+https://github.com/dzervas/frida-rust#1367b5d58bf50738bbeb49272b3eb99b98bbf891" +source = "git+https://github.com/frida/frida-rust#6be323811b87d204595ded8e4f18e090a954ca81" dependencies = [ "bindgen", "frida-build", @@ -561,6 +581,10 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "mylib" +version = "0.1.0" + [[package]] name = "native-tls" version = "0.2.11" @@ -649,6 +673,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "output_vt100" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" +dependencies = [ + "winapi", +] + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -685,6 +718,18 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "pretty_assertions" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +dependencies = [ + "ctor 0.1.26", + "diff", + "output_vt100", + "yansi", +] + [[package]] name = "proc-macro2" version = "1.0.58" @@ -1417,3 +1462,9 @@ checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" dependencies = [ "lzma-sys", ] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/Cargo.toml b/Cargo.toml index 6a24fe3..53fafda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,18 +10,32 @@ crate-type = ["cdylib"] name = "standalone" path = "src/main.rs" +[features] +default = ["frida"] +dll_proxy = [] +managed_lib = ["dep:windows-sys"] +# unmanaged_lib = [], +frida = ["dep:frida", "dep:lazy_static", "dep:serde", "dep:serde_json"] + [dependencies] # frida = { version = "0.4.0", features = ["auto-download"] } -frida = { git = "https://github.com/frida/frida-rust", features = ["auto-download"] } -lazy_static = "1.4.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" +frida = { git = "https://github.com/frida/frida-rust", features = ["auto-download"], optional = true } +lazy_static = { version = "1.4.0", optional = true } +serde = { version = "1.0", features = ["derive"], optional = true } +serde_json = { version = "1.0", optional = true } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.9", features = ["winnt", "libloaderapi"] } +windows-sys = { version = "0.48.0", features = ["Win32_System_ClrHosting"], optional = true } [target.'cfg(unix)'.dependencies] ctor = "0.2.0" [build-dependencies] goblin = "0.6.1" +# Can't find a way to have a test-only dependency +cc = "1.0.67" + +[dev-dependencies] +pretty_assertions = "1.1.0" +mylib = { path = "tests/mylib" } diff --git a/build.rs b/build.rs index 8d9a5b8..16d540a 100644 --- a/build.rs +++ b/build.rs @@ -35,4 +35,15 @@ fn main() { println!("cargo:warning=Expected library name: {}-orig.dll", lib_name); println!("cargo:rustc-env=LIB_NAME={}-orig.dll", lib_name); } + + // if env::var("PROFILE").unwrap() == "test" { + // cc::Build::new() + // .shared_flag(true) + // .static_flag(false) + // .cargo_metadata(false) + // .file("tests/mylib.c") + // .compile("mylib"); + // println!("cargo:rustc-link-search=native={}/tests", env::var("OUT_DIR").unwrap()); + // println!("cargo:rustc-link-lib=dylib=mylib"); + // } } diff --git a/src/cs.rs b/src/cs.rs new file mode 100644 index 0000000..f3afd57 --- /dev/null +++ b/src/cs.rs @@ -0,0 +1,32 @@ +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/frida_handler.rs b/src/frida_handler.rs new file mode 100644 index 0000000..973b71f --- /dev/null +++ b/src/frida_handler.rs @@ -0,0 +1,116 @@ +#![cfg(feature = "frida")] +use frida::{DeviceManager, Frida, ScriptHandler, ScriptOption, ScriptRuntime}; +use lazy_static::lazy_static; +use serde::Deserialize; + +lazy_static! { + pub static ref FRIDA: Frida = unsafe { Frida::obtain() }; +} + +pub fn attach_pid(frida_code: String, pid: u32) { + println!("[+] Injecting into PID: {}", pid); + + let device_manager = DeviceManager::obtain(&FRIDA); + println!("[*] Device Manager obtained"); + + if let Some(device) = device_manager.enumerate_all_devices().first() { + println!("[*] First device: {}", device.get_name()); + + let session = device.attach(pid).unwrap(); + + if !session.is_detached() { + 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(); + + script.handle_message(&mut Handler).unwrap(); + + script.load().unwrap(); + println!("[*] Script loaded"); + } + } else { + eprintln!("[!] No device found!"); + }; +} + +#[derive(Debug, Deserialize)] +struct LogEntry { + #[serde(rename = "type")] + log_type: LogType, + level: LogLevel, + payload: String, +} + +#[derive(Debug, Deserialize)] +enum LogType { + #[serde(rename = "log")] + Log, +} + +#[derive(Debug, Deserialize)] +enum LogLevel { + #[serde(rename = "debug")] + Debug, + #[serde(rename = "info")] + Info, + #[serde(rename = "warning")] + Warning, + #[serde(rename = "error")] + Error, +} + +struct Handler; + +impl ScriptHandler for Handler { + fn on_message(&mut self, message: &str) { + if let Ok(log_entry) = serde_json::from_str::(message) { + match log_entry.log_type { + LogType::Log => { + match log_entry.level { + LogLevel::Debug => eprint!("[-] "), + LogLevel::Info => eprint!("[i] "), + LogLevel::Warning => eprint!("[!] "), + LogLevel::Error => eprint!("[X] "), + } + } + } + + eprintln!("{}", log_entry.payload); + return; + } + + eprintln!("{message}"); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[link(name = "mylib", kind = "dylib")] + extern { + fn mylib_foo() -> u8; + } + + #[test] + fn test_attach_pid() { + assert_eq!(10, unsafe { mylib_foo() }); + + let frida_script = r#" + const foo = Module.getExportByName(null, "mylib_foo"); + Interceptor.replace(foo, new NativeCallback(function () { + console.log("replaced foo() called"); + return 20; + }, "uint8", [])); + "#; + + attach_pid(frida_script.to_string(), 0); + assert_eq!(20, unsafe { mylib_foo() }); + } +} diff --git a/src/injector.rs b/src/injector.rs index 26f754f..8fd114e 100644 --- a/src/injector.rs +++ b/src/injector.rs @@ -1,99 +1,27 @@ -use frida::{DeviceManager, Frida, ScriptHandler, ScriptOption, ScriptRuntime}; -use serde::Deserialize; -use lazy_static::lazy_static; -lazy_static! { - static ref FRIDA: Frida = unsafe { Frida::obtain() }; +#[cfg(all(unix, not(feature = "frida")))] +compile_error!("Only Frida injection is supported for Unix targets"); + +#[cfg(all(not(feature = "managed_lib"), not(feature = "frida")))] +compile_error!("No injection method is selected - please enable either managed_lib (windows-only) and/or frida feature"); + +#[cfg(all(not(windows), feature = "managed_lib"))] +compile_error!("Managed library injection is only supported for Windows target"); + +#[cfg(feature = "frida")] +use crate::frida_handler::attach_pid as frida_attach_pid; + +#[no_mangle] +pub extern "C" fn attach(pid: u32) { + #[cfg(feature = "frida")] + { + let frida_code = env!("FRIDA_CODE").to_string(); + std::thread::spawn(move || frida_attach_pid(frida_code, pid)); + } } #[no_mangle] -pub fn attach(pid: u32) { - let frida_code = env!("FRIDA_CODE").to_string(); - println!("[+] Injecting into PID: {}", pid); - - std::thread::spawn(move || { - let device_manager = DeviceManager::obtain(&FRIDA); - println!("[*] Device Manager obtained"); - - if let Some(device) = device_manager.enumerate_all_devices().first() { - println!("[*] First device: {}", device.get_name()); - - let session = device.attach(pid).unwrap(); - - if !session.is_detached() { - println!("[*] Attached"); - - let mut script_option = ScriptOption::new() - // .set_name("frida-deepfreeze-rs") - .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.load().unwrap(); - println!("[*] Script loaded"); - } - } else { - eprintln!("[!] No device found!"); - }; - }); -} - -#[no_mangle] -pub fn attach_self() { +pub extern "C" fn attach_self() { println!("[*] Attaching to self"); attach(0); } - -#[derive(Debug, Deserialize)] -struct LogEntry { - #[serde(rename = "type")] - log_type: LogType, - level: LogLevel, - payload: String, -} - -#[derive(Debug, Deserialize)] -enum LogType { - #[serde(rename = "log")] - Log, -} - -#[derive(Debug, Deserialize)] -enum LogLevel { - #[serde(rename = "debug")] - Debug, - #[serde(rename = "info")] - Info, - #[serde(rename = "warning")] - Warning, - #[serde(rename = "error")] - Error, -} - -struct Handler; - -impl ScriptHandler for Handler { - fn on_message(&mut self, message: &str) { - if let Ok(log_entry) = serde_json::from_str::(message) { - match log_entry.log_type { - LogType::Log => { - match log_entry.level { - LogLevel::Debug => eprint!("[-] "), - LogLevel::Info => eprint!("[i] "), - LogLevel::Warning => eprint!("[!] "), - LogLevel::Error => eprint!("[X] "), - } - } - } - - eprintln!("{}", log_entry.payload); - return; - } - - eprintln!("{message}"); - } -} diff --git a/src/lib.rs b/src/lib.rs index 248743e..bbe9f17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,13 @@ pub mod injector; +#[cfg(feature = "frida")] +pub mod frida_handler; pub use injector::{attach, attach_self}; -#[cfg(unix)] +#[cfg(all(unix, not(test)))] use ctor::ctor; -#[cfg(unix)] +#[cfg(all(unix, not(test)))] #[ctor] fn _start() { println!("[+] frida-deepfreeze-rs library injected"); @@ -16,21 +18,26 @@ fn _start() { // during DeviceManager::obtain. DllMain works fine though. #[cfg(windows)] use std::ffi::c_void; - #[cfg(windows)] use winapi::um::winnt::DLL_PROCESS_ATTACH; -#[cfg(windows)] + +#[cfg(all(windows, feature = "dll_proxy"))] use winapi::um::libloaderapi::LoadLibraryA; -#[cfg(windows)] +#[cfg(all(windows, not(test)))] #[no_mangle] #[allow(non_snake_case, unused_variables)] 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"); - unsafe { LoadLibraryA(env!("LIB_NAME").as_ptr() as *const i8); } - println!("[+] Original DLL {} loaded", env!("LIB_NAME")); + + #[cfg(feature = "dll_proxy")] + { + unsafe { LoadLibraryA(env!("LIB_NAME").as_ptr() as *const i8); } + println!("[+] Original DLL {} loaded", env!("LIB_NAME")); + } + attach_self(); } // Maybe we should detach? Is it useful? diff --git a/src/main.rs b/src/main.rs index e2b6245..8f4f80c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,7 @@ pub mod injector; +#[cfg(feature = "frida")] +pub mod frida_handler; + pub use injector::attach; fn main() { @@ -12,3 +15,6 @@ fn main() { let pid: u32 = args[1].parse().unwrap(); attach(pid); } + +// #[cfg(test)] +// mod integration_tests; diff --git a/tests/mylib/Cargo.lock b/tests/mylib/Cargo.lock new file mode 100644 index 0000000..d97f29a --- /dev/null +++ b/tests/mylib/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "mylib" +version = "0.1.0" diff --git a/tests/mylib/Cargo.toml b/tests/mylib/Cargo.toml new file mode 100644 index 0000000..f587b06 --- /dev/null +++ b/tests/mylib/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "mylib" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] diff --git a/tests/mylib/src/lib.rs b/tests/mylib/src/lib.rs new file mode 100644 index 0000000..037ed7e --- /dev/null +++ b/tests/mylib/src/lib.rs @@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn mylib_foo() -> u8 { + 10 +}