Add ability to spawn or attach by process name

Signed-off-by: Dimitris Zervas <dzervas@dzervas.gr>
This commit is contained in:
Dimitris Zervas 2024-04-15 21:49:55 +03:00
parent b6a48d5155
commit ff5a7f152f
No known key found for this signature in database
5 changed files with 174 additions and 49 deletions

View File

@ -16,8 +16,8 @@ pub struct Config {
pub dll_proxy: Option<String>,
pub frida_code: Option<String>,
pub frida_code_file: Option<String>,
pub target_exec: Option<String>,
pub target_process: Option<String>,
pub target_spawn: Option<String>,
pub hooks: Vec<Hook>,
}
@ -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<Hook>) {
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");

View File

@ -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() });
}
}

View File

@ -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()));
}
}

View File

@ -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();

View File

@ -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<String> = std::env::args().collect();
if args.len() < 2 {
eprintln!("Usage: {} <PID>", 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: {} <PID|Process Name>", args[0]);
}
// #[cfg(test)]
// mod integration_tests;