diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 27ae1ab..2a62b92 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,4 +42,4 @@ jobs: # - name: Run tests - frida # if: runner.os == 'windows-latest' - # run: cargo test --verbose --no-default-features --features managed_lib + # run: cargo test --verbose --no-default-features --features dotnet diff --git a/.gitignore b/.gitignore index 847c890..ccaa143 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,8 @@ __handlers__ /examples/cs/*/obj /examples/cs/*/bin *.png~ +*.exe +*.dll +/examples/cs/bin +/examples/cs/obj +/dotnet diff --git a/.vscode/settings.json b/.vscode/settings.json index 0c2abb7..13323bf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "rust-analyzer.showUnlinkedFileNotification": false + "rust-analyzer.cargo.features": "all", + "rust-analyzer.check.features": "all" } diff --git a/Cargo.lock b/Cargo.lock index fdf6820..3f66cbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,7 +72,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 2.0.51", "which", ] @@ -148,6 +148,16 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "csbindgen" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c848726dae35e119d20f0aae60950095dc00cc399d7c56620019c47ae1d7b385" +dependencies = [ + "regex", + "syn 1.0.109", +] + [[package]] name = "ctor" version = "0.2.7" @@ -155,7 +165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad291aa74992b9b7a7e88c38acbbf6ad7e107f1d90ee8775b7bc1fc3394f485c" dependencies = [ "quote", - "syn", + "syn 2.0.51", ] [[package]] @@ -268,6 +278,7 @@ dependencies = [ name = "frida-deepfreeze-rs" version = "0.1.0" dependencies = [ + "csbindgen", "ctor", "frida", "goblin", @@ -691,7 +702,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.51", ] [[package]] @@ -759,7 +770,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.51", ] [[package]] @@ -924,7 +935,7 @@ checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.51", ] [[package]] @@ -967,7 +978,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.51", ] [[package]] @@ -1018,6 +1029,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.51" @@ -1096,7 +1118,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.51", ] [[package]] @@ -1259,7 +1281,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.51", "wasm-bindgen-shared", ] @@ -1293,7 +1315,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.51", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index c8feb9c..7a2dd69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,9 +12,7 @@ path = "src/main.rs" [features] default = ["frida"] -# dll_proxy = [] -managed_lib = ["dep:windows-sys"] -# unmanaged_lib = [], +dotnet = ["dep:csbindgen"] frida = ["dep:frida", "dep:lazy_static", "dep:serde", "dep:serde_json"] [dependencies] @@ -32,6 +30,7 @@ ctor = "0.2.6" [build-dependencies] goblin = "0.8.0" +csbindgen = { version = "1.9.0", optional = true} [dev-dependencies] pretty_assertions = "1.4.0" diff --git a/build.rs b/build.rs index 45641db..5552fa2 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,6 @@ use std::env; +#[cfg(feature = "dotnet")] +use csbindgen; fn main() { println!("cargo:rerun-if-env-changed=FRIDA_CODE"); @@ -34,4 +36,15 @@ fn main() { println!("cargo:warning=Expected library name: {}-orig.dll", lib_name); println!("cargo:rustc-env=LIB_NAME={}-orig.dll", lib_name); } + + #[cfg(feature = "dotnet")] + { + let lib_path = concat!(env!("CARGO_MANIFEST_DIR"), "/src/lib.rs"); + let csharp_file = concat!(env!("CARGO_MANIFEST_DIR"), "/dotnet/NativeMethods.g.cs"); + csbindgen::Builder::default() + .input_extern_file(lib_path) + .csharp_dll_name("deepfreeze") + .generate_csharp_file(csharp_file) + .unwrap(); + } } diff --git a/examples/cs/Example.sln b/examples/cs/Example.sln new file mode 100644 index 0000000..daaae02 --- /dev/null +++ b/examples/cs/Example.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyClass", "My\MyClass.csproj", "{F480EEA9-B211-4BDF-A364-8B8D1B6F4FCE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OtherClass", "Other\OtherClass.csproj", "{EAA4714F-3F2E-4801-81B7-9BC502347B53}" +EndProject +Global + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F480EEA9-B211-4BDF-A364-8B8D1B6F4FCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F480EEA9-B211-4BDF-A364-8B8D1B6F4FCE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F480EEA9-B211-4BDF-A364-8B8D1B6F4FCE}.Debug|x64.ActiveCfg = Debug|Any CPU + {F480EEA9-B211-4BDF-A364-8B8D1B6F4FCE}.Debug|x64.Build.0 = Debug|Any CPU + {F480EEA9-B211-4BDF-A364-8B8D1B6F4FCE}.Debug|x86.ActiveCfg = Debug|Any CPU + {F480EEA9-B211-4BDF-A364-8B8D1B6F4FCE}.Debug|x86.Build.0 = Debug|Any CPU + {F480EEA9-B211-4BDF-A364-8B8D1B6F4FCE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F480EEA9-B211-4BDF-A364-8B8D1B6F4FCE}.Release|Any CPU.Build.0 = Release|Any CPU + {F480EEA9-B211-4BDF-A364-8B8D1B6F4FCE}.Release|x64.ActiveCfg = Release|Any CPU + {F480EEA9-B211-4BDF-A364-8B8D1B6F4FCE}.Release|x64.Build.0 = Release|Any CPU + {F480EEA9-B211-4BDF-A364-8B8D1B6F4FCE}.Release|x86.ActiveCfg = Release|Any CPU + {F480EEA9-B211-4BDF-A364-8B8D1B6F4FCE}.Release|x86.Build.0 = Release|Any CPU + {EAA4714F-3F2E-4801-81B7-9BC502347B53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EAA4714F-3F2E-4801-81B7-9BC502347B53}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EAA4714F-3F2E-4801-81B7-9BC502347B53}.Debug|x64.ActiveCfg = Debug|Any CPU + {EAA4714F-3F2E-4801-81B7-9BC502347B53}.Debug|x64.Build.0 = Debug|Any CPU + {EAA4714F-3F2E-4801-81B7-9BC502347B53}.Debug|x86.ActiveCfg = Debug|Any CPU + {EAA4714F-3F2E-4801-81B7-9BC502347B53}.Debug|x86.Build.0 = Debug|Any CPU + {EAA4714F-3F2E-4801-81B7-9BC502347B53}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EAA4714F-3F2E-4801-81B7-9BC502347B53}.Release|Any CPU.Build.0 = Release|Any CPU + {EAA4714F-3F2E-4801-81B7-9BC502347B53}.Release|x64.ActiveCfg = Release|Any CPU + {EAA4714F-3F2E-4801-81B7-9BC502347B53}.Release|x64.Build.0 = Release|Any CPU + {EAA4714F-3F2E-4801-81B7-9BC502347B53}.Release|x86.ActiveCfg = Release|Any CPU + {EAA4714F-3F2E-4801-81B7-9BC502347B53}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/examples/cs/My/MyClass.cs b/examples/cs/My/MyClass.cs new file mode 100644 index 0000000..0ba4934 --- /dev/null +++ b/examples/cs/My/MyClass.cs @@ -0,0 +1,19 @@ +using System; +using OtherNamespace; + +namespace MyNamespace { + public class MyClass { + // This method will be called by native code inside the target process… + public static int MyMethod(String pwzArgument) { + System.Console.WriteLine("Hello World from C# {0}", pwzArgument); + return 0; + } + + public static void Main() { + int my = MyMethod("from Main()"); + System.Console.WriteLine("MyMethod returned {0}", my); + int other = OtherNamespace.OtherClass.OtherMethod(); + System.Console.WriteLine("OtherMethod returned {0}", other); + } + } +} diff --git a/examples/cs/My/MyClass.csproj b/examples/cs/My/MyClass.csproj new file mode 100644 index 0000000..587dbaf --- /dev/null +++ b/examples/cs/My/MyClass.csproj @@ -0,0 +1,10 @@ + + + net8.0 + Exe + + + + + + diff --git a/examples/cs/Other/OtherClass.cs b/examples/cs/Other/OtherClass.cs new file mode 100644 index 0000000..2ccd8a6 --- /dev/null +++ b/examples/cs/Other/OtherClass.cs @@ -0,0 +1,11 @@ +using System; + +namespace OtherNamespace { + public class OtherClass { + // This method will be called by native code inside the target process… + public static int OtherMethod() { + System.Console.WriteLine("Goodbye World from C#"); + return 1; + } + } +} diff --git a/examples/cs/Other/OtherClass.csproj b/examples/cs/Other/OtherClass.csproj new file mode 100644 index 0000000..4893ead --- /dev/null +++ b/examples/cs/Other/OtherClass.csproj @@ -0,0 +1,7 @@ + + + net8.0 + Library + OtherNamespace + + diff --git a/examples/cs/README.md b/examples/cs/README.md new file mode 100644 index 0000000..329bddf --- /dev/null +++ b/examples/cs/README.md @@ -0,0 +1,17 @@ +# C# Example + +To compile: + +```sh +sudo pacman -S dotnet-host dotnet-runtime dotnet-sdk +dotnet build +``` + +To run: + +```sh +./My/bin/Debug/net8.0/MyClass +# or +sudo pacman -S mono +mono My/bin/Debug/net8.0/MyClass.dll +``` diff --git a/src/frida_handler.rs b/src/frida_handler.rs index 973b71f..561c361 100644 --- a/src/frida_handler.rs +++ b/src/frida_handler.rs @@ -1,5 +1,5 @@ #![cfg(feature = "frida")] -use frida::{DeviceManager, Frida, ScriptHandler, ScriptOption, ScriptRuntime}; +use frida::{DeviceManager, DeviceType, Frida, ScriptHandler, ScriptOption, ScriptRuntime}; use lazy_static::lazy_static; use serde::Deserialize; @@ -7,13 +7,13 @@ lazy_static! { pub static ref FRIDA: Frida = unsafe { Frida::obtain() }; } -pub fn attach_pid(frida_code: String, pid: u32) { +pub fn attach_pid(frida_code: &str, 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() { + if let Ok(device) = device_manager.get_device_by_type(DeviceType::Local) { println!("[*] First device: {}", device.get_name()); let session = device.attach(pid).unwrap(); @@ -25,7 +25,7 @@ pub fn attach_pid(frida_code: String, pid: u32) { .set_runtime(ScriptRuntime::QJS); println!("[*] Script {}", frida_code); let script = session - .create_script(&frida_code, &mut script_option) + .create_script(frida_code, &mut script_option) .unwrap(); script.handle_message(&mut Handler).unwrap(); @@ -110,7 +110,7 @@ mod tests { }, "uint8", [])); "#; - attach_pid(frida_script.to_string(), 0); + attach_pid(frida_script, 0); assert_eq!(20, unsafe { mylib_foo() }); } } diff --git a/src/injector.rs b/src/injector.rs index 49e4866..042fa0f 100644 --- a/src/injector.rs +++ b/src/injector.rs @@ -2,11 +2,11 @@ #[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(feature = "dotnet"), not(feature = "frida")))] +compile_error!("No injection method is selected - please enable either dotnet (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(all(not(windows), feature = "dotnet"))] +// compile_error!("Managed library injection is only supported for Windows target"); #[cfg(feature = "frida")] use crate::frida_handler::attach_pid as frida_attach_pid; @@ -15,7 +15,7 @@ use crate::frida_handler::attach_pid as frida_attach_pid; pub extern "C" fn attach(pid: u32) { #[cfg(feature = "frida")] { - let frida_code = env!("FRIDA_CODE").to_string(); + let frida_code = env!("FRIDA_CODE"); #[cfg(windows)] std::thread::spawn(move || frida_attach_pid(frida_code, pid)); #[cfg(not(windows))] diff --git a/src/lib.rs b/src/lib.rs index cd1c1d5..aa3fe7b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,15 @@ pub mod injector; #[cfg(feature = "frida")] pub mod frida_handler; +// #[cfg(feature = "dotnet")] +// pub mod cs; -pub use injector::{attach, attach_self}; +pub use injector::attach_self; -#[cfg(all(unix, not(test)))] +#[cfg(all(unix, not(test), not(feature = "dotnet")))] use ctor::ctor; -#[cfg(all(unix, not(test)))] +#[cfg(all(unix, not(test), not(feature = "dotnet")))] #[ctor] fn _start() { println!("[+] frida-deepfreeze-rs library injected"); @@ -16,27 +18,24 @@ fn _start() { // 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)))] +#[cfg(all(any(windows, feature = "dotenv"), not(test)))] use std::ffi::c_void; -#[cfg(all(windows, not(test)))] +#[cfg(all(any(windows, feature = "dotenv"), not(test)))] use winapi::um::winnt::DLL_PROCESS_ATTACH; -#[cfg(all(windows, feature = "dll_proxy", not(test)))] +#[cfg(all(any(windows, feature = "dotenv"), not(test)))] use winapi::um::libloaderapi::LoadLibraryA; -#[cfg(all(windows, not(test)))] +#[cfg(all(any(windows, feature = "dotenv"), not(test)))] #[no_mangle] #[allow(non_snake_case, unused_variables)] -extern "system" fn DllMain(dll_module: *mut c_void, call_reason: u32, _: *mut ()) -> bool { +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"); - #[cfg(feature = "dll_proxy")] - { - unsafe { LoadLibraryA(env!("LIB_NAME").as_ptr() as *const i8); } - println!("[+] Original DLL {} loaded", env!("LIB_NAME")); - } + unsafe { LoadLibraryA(env!("LIB_NAME").as_ptr() as *const i8); } + println!("[+] Original DLL {} loaded", env!("LIB_NAME")); attach_self(); } diff --git a/src/win_daemon.rs b/src/win_daemon.rs new file mode 100644 index 0000000..7175dd7 --- /dev/null +++ b/src/win_daemon.rs @@ -0,0 +1,99 @@ +#![cfg(windows)] + +use std::ffi::c_void; +use winapi::shared::minwindef::DWORD; +use winapi::um::evntprov::*; +use winapi::um::evntcons::*; +use winapi::um::evntprov::*; +use winapi::um::winnt::{EVENT_TRACE_CONTROL_STOP, EVENT_TRACE_FLAG_PROCESS}; + +pub fn start_daemon() { + // Create an event trace session + let session_name = "frida-deepfreeze-rs"; + let session_handle = create_event_trace_session(session_name); + if session_handle.is_null() { + eprintln!("Failed to create event trace session"); + return; + } + + // Enable process creation events + enable_process_creation_events(session_handle); + + // Process events until a termination event is received + process_events(session_handle); + + // Stop the event trace session + stop_event_trace_session(session_handle); +} + +fn create_event_trace_session(session_name: &str) -> TRACEHANDLE { + let session_name = widestring::WideCString::from_str(session_name).expect("Failed to convert session name"); + + let mut session_handle: TRACEHANDLE = 0; + let status = unsafe { + StartTraceW( + &mut session_handle, + session_name.as_ptr(), + ptr::null_mut(), + ) + }; + + if status != ERROR_SUCCESS { + println!("Failed to start event trace session: {}", status); + } + + session_handle +} + +fn enable_process_creation_events(session_handle: TRACEHANDLE) { + let status = unsafe { + EnableTraceEx2( + session_handle, + &EVENT_TRACE_GUID_PROCESS, + EVENT_CONTROL_CODE_ENABLE_PROVIDER, + TRACE_LEVEL_INFORMATION, + EVENT_TRACE_FLAG_PROCESS, + 0, + 0, + 0, + NULL, + ) + }; + + if status != ERROR_SUCCESS { + println!("Failed to enable process creation events: {}", status); + } +} + +fn process_events(session_handle: TRACEHANDLE) { + let mut buffer_size: DWORD = 64 * 1024; + let mut buffer = vec![0u8; buffer_size as usize]; + + let status = unsafe { + ProcessTrace( + &mut session_handle, + 1, + NULL, + NULL, + ) + }; + + if status != ERROR_SUCCESS && status != ERROR_CANCELLED { + println!("Failed to process events: {}", status); + } +} + +fn stop_event_trace_session(session_handle: TRACEHANDLE) { + let status = unsafe { + ControlTraceW( + session_handle, + NULL, + NULL, + EVENT_TRACE_CONTROL_STOP, + ) + }; + + if status != ERROR_SUCCESS { + println!("Failed to stop event trace session: {}", status); + } +}