use crate::{Device, Error, Result};
use std::ffi::CString;
use std::marker::PhantomData;
use std::path::Path;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
use frida_sys::{_FridaInjector, g_bytes_new, g_bytes_unref};
pub struct Injector<'a> {
injector_ptr: *mut _FridaInjector,
phantom: PhantomData<&'a _FridaInjector>,
}
impl<'a> Injector<'a> {
pub(crate) fn from_raw(injector_ptr: *mut _FridaInjector) -> Injector<'a> {
Injector {
injector_ptr,
phantom: PhantomData,
}
}
pub fn new() -> Self {
Self::from_raw(unsafe { frida_sys::frida_injector_new() })
}
pub fn in_process() -> Self {
Self::from_raw(unsafe { frida_sys::frida_injector_new_inprocess() })
}
}
impl Default for Injector<'_> {
fn default() -> Self {
Self::new()
}
}
impl Drop for Injector<'_> {
fn drop(&mut self) {
unsafe {
frida_sys::frida_injector_close_sync(
self.injector_ptr,
std::ptr::null_mut(),
std::ptr::null_mut(),
);
frida_sys::frida_unref(self.injector_ptr as _)
}
}
}
pub trait Inject {
fn inject_library_file_sync<D, E, P>(
&mut self,
pid: u32,
path: P,
entrypoint: E,
data: D,
) -> Result<u32>
where
D: Into<Vec<u8>>,
P: AsRef<Path>,
E: AsRef<str>;
fn inject_library_blob_sync<D, E>(
&mut self,
pid: u32,
blob: &[u8],
entrypoint: E,
data: D,
) -> Result<u32>
where
D: Into<Vec<u8>>,
E: AsRef<str>;
}
impl Inject for Injector<'_> {
fn inject_library_file_sync<D, E, P>(
&mut self,
pid: u32,
path: P,
entrypoint: E,
data: D,
) -> Result<u32>
where
D: Into<Vec<u8>>,
P: AsRef<Path>,
E: AsRef<str>,
{
#[cfg(unix)]
let path =
CString::new(path.as_ref().as_os_str().as_bytes()).map_err(|_| Error::CStringFailed)?;
#[cfg(windows)]
let path = CString::new(
path.as_ref()
.as_os_str()
.to_str()
.ok_or(Error::CStringFailed)?,
)
.map_err(|_| Error::CStringFailed)?;
let entrypoint = CString::new(entrypoint.as_ref()).unwrap();
let data = CString::new(data).unwrap();
let mut error: *mut frida_sys::GError = std::ptr::null_mut();
let id = unsafe {
frida_sys::frida_injector_inject_library_file_sync(
self.injector_ptr,
pid as frida_sys::guint,
path.as_ptr() as *const frida_sys::gchar,
entrypoint.as_ptr() as *const frida_sys::gchar,
data.as_ptr() as *const frida_sys::gchar,
std::ptr::null_mut(),
&mut error,
)
};
if !error.is_null() {
let message = unsafe { CString::from_raw((*error).message) }
.into_string()
.map_err(|_| Error::CStringFailed)?;
let code = unsafe { (*error).code };
return Err(Error::InjectFailed { code, message });
}
Ok(id)
}
fn inject_library_blob_sync<D, E>(
&mut self,
pid: u32,
blob: &[u8],
entrypoint: E,
data: D,
) -> Result<u32>
where
D: Into<Vec<u8>>,
E: AsRef<str>,
{
let entrypoint = CString::new(entrypoint.as_ref()).unwrap();
let data = CString::new(data).unwrap();
let mut error: *mut frida_sys::GError = std::ptr::null_mut();
let id = unsafe {
let g_blob = g_bytes_new(blob.as_ptr() as _, blob.len() as _);
let id = frida_sys::frida_injector_inject_library_blob_sync(
self.injector_ptr,
pid,
g_blob,
entrypoint.as_ptr() as *const frida_sys::gchar,
data.as_ptr() as *const frida_sys::gchar,
std::ptr::null_mut(),
&mut error,
);
g_bytes_unref(g_blob);
id
};
if !error.is_null() {
let message = unsafe { CString::from_raw((*error).message) }
.into_string()
.map_err(|_| Error::CStringFailed)?;
let code = unsafe { (*error).code };
return Err(Error::InjectFailed { code, message });
}
Ok(id)
}
}
impl Inject for Device<'_> {
fn inject_library_file_sync<D, E, P>(
&mut self,
pid: u32,
path: P,
entrypoint: E,
data: D,
) -> Result<u32>
where
D: Into<Vec<u8>>,
P: AsRef<Path>,
E: AsRef<str>,
{
#[cfg(unix)]
let path =
CString::new(path.as_ref().as_os_str().as_bytes()).map_err(|_| Error::CStringFailed)?;
#[cfg(windows)]
let path = CString::new(
path.as_ref()
.as_os_str()
.to_str()
.ok_or(Error::CStringFailed)?,
)
.map_err(|_| Error::CStringFailed)?;
let entrypoint = CString::new(entrypoint.as_ref()).unwrap();
let data = CString::new(data).unwrap();
let mut error: *mut frida_sys::GError = std::ptr::null_mut();
let id = unsafe {
frida_sys::frida_device_inject_library_file_sync(
self.device_ptr,
pid as frida_sys::guint,
path.as_ptr() as *const frida_sys::gchar,
entrypoint.as_ptr() as *const frida_sys::gchar,
data.as_ptr() as *const frida_sys::gchar,
std::ptr::null_mut(),
&mut error,
)
};
if !error.is_null() {
let message = unsafe { CString::from_raw((*error).message) }
.into_string()
.map_err(|_| Error::CStringFailed)?;
let code = unsafe { (*error).code };
return Err(Error::InjectFailed { code, message });
}
Ok(id)
}
fn inject_library_blob_sync<D, E>(
&mut self,
pid: u32,
blob: &[u8],
entrypoint: E,
data: D,
) -> Result<u32>
where
D: Into<Vec<u8>>,
E: AsRef<str>,
{
let entrypoint = CString::new(entrypoint.as_ref()).unwrap();
let data = CString::new(data).unwrap();
let mut error: *mut frida_sys::GError = std::ptr::null_mut();
let id = unsafe {
let g_blob = g_bytes_new(blob.as_ptr() as _, blob.len() as _);
let id = frida_sys::frida_device_inject_library_blob_sync(
self.device_ptr,
pid,
g_blob,
entrypoint.as_ptr() as *const frida_sys::gchar,
data.as_ptr() as *const frida_sys::gchar,
std::ptr::null_mut(),
&mut error,
);
g_bytes_unref(g_blob);
id
};
if !error.is_null() {
let message = unsafe { CString::from_raw((*error).message) }
.into_string()
.map_err(|_| Error::CStringFailed)?;
let code = unsafe { (*error).code };
return Err(Error::InjectFailed { code, message });
}
Ok(id)
}
}