#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(not(feature = "alloc"))]
compile_error!("inject_lib doesn't yet support no alloc environments");
extern crate core;
use core::fmt::{Display, Formatter};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Injector<'a> {
pub dll: Data<'a>,
pub pid: u32,
}
pub(crate) type Result<T, V = error::Error> = core::result::Result<T, V>;
pub(crate) use log::{debug, error, info, trace, warn};
pub mod error;
mod platforms;
pub trait Inject {
fn inject(&self) -> Result<()>;
fn eject(&self) -> Result<()>;
fn find_pid(name: Data) -> Result<alloc::vec::Vec<u32>>;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Data<'a> {
Str(&'a str),
#[cfg(feature = "std")]
Path(&'a std::path::Path),
}
impl<'a> Display for Data<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Data::Str(s) => write!(f, "{}", s),
#[cfg(feature = "std")]
Data::Path(p) => write!(f, "{}", p.to_string_lossy()),
}
}
}
impl<'a> Data<'a> {
#[allow(unused)]
pub fn get_str(&self) -> Option<&'a str> {
match self {
Data::Str(a) => Some(a),
_ => None,
}
}
#[cfg(feature = "std")]
#[allow(unused)]
fn get_path(&self) -> Option<&'a std::path::Path> {
match self {
Data::Path(a) => Some(a),
_ => None,
}
}
}
impl<'a> Injector<'a> {
pub fn new(dll: Data<'a>, pid: u32) -> Self {
Injector { dll, pid }
}
pub fn set_dll(&mut self, dll: Data<'a>) {
self.dll = dll;
}
pub fn set_pid(&mut self, pid: u32) {
self.pid = pid;
}
#[cfg(target_family = "windows")]
pub fn inject(&self, wait: bool) -> impl Inject + '_ {
platforms::windows::InjectWin { inj: self, wait }
}
#[cfg(target_family = "windows")]
pub fn find_pid(name: Data) -> Result<alloc::vec::Vec<u32>> {
platforms::windows::InjectWin::find_pid(name)
}
}
impl<'a> Default for Injector<'a> {
fn default() -> Self {
Self::new(crate::Data::Str(""), 0)
}
}
pub fn trim_wide_str<const FAST: bool>(v: &[u16]) -> &[u16] {
let i = {
if FAST {
v.partition_point(|x| *x != 0)
} else {
let mut len = v.len();
while v[len - 1] == 0 {
len -= 1;
}
len
}
};
let (out, _) = v.split_at(i);
return out;
}
pub fn str_from_wide_str(v: &[u16]) -> Result<alloc::string::String> {
let tmp: alloc::vec::Vec<Result<char, widestring::error::DecodeUtf16Error>> =
widestring::decode_utf16(v.iter().map(|x| *x)).collect();
let mut o = alloc::string::String::with_capacity(v.len());
for i in tmp {
match i {
Err(e) => return Err(crate::error::Error::WTFConvert(e)),
Ok(v) => o.push(v),
}
}
o.shrink_to_fit();
Ok(o)
}
fn cmp<'a>(name: crate::Data<'a>) -> impl Fn(crate::Data<'_>) -> bool + 'a {
move |s| {
return match name {
crate::Data::Str(s2) => match s {
crate::Data::Str(s) => s2.ends_with(s) || s.ends_with(s2),
#[cfg(feature = "std")]
crate::Data::Path(p) => {
let p1 = std::path::Path::new(s2);
p1.ends_with(p) || p.ends_with(p1)
}
},
#[cfg(feature = "std")]
crate::Data::Path(p2) => match s {
crate::Data::Str(s) => {
let p1 = std::path::Path::new(s);
p1.ends_with(p2) || p2.ends_with(p1)
}
#[cfg(feature = "std")]
crate::Data::Path(p) => p.ends_with(p2) || p2.ends_with(p),
},
};
}
}
#[cfg(test)]
mod test {
use alloc::vec::Vec;
pub const STR:&str = "This is just any string, since we are not testing anything else, other than setting the dll.!'\r\n\t%$§\"{\\[()]}=?´`öäü^°,.-;:_#+*~<>|³²@";
use crate::Result;
#[test]
fn trim_vec() {
let buf: Vec<u16> = (1..u16::MAX).collect();
let mut buf2 = buf.clone();
buf2.append(&mut [0u16; 100].to_vec());
assert_eq!(super::trim_wide_str::<true>(buf2.as_slice()), buf);
assert_eq!(super::trim_wide_str::<false>(buf2.as_slice()), buf);
}
#[test]
fn set_dll() {
let mut inj = super::Injector::default();
let dll = crate::Data::Str(STR);
inj.set_dll(dll);
assert_eq!(inj.dll, dll, "Setter did not correctly set the dll string");
}
#[test]
fn set_pid() {
let mut inj = super::Injector::default();
const PID: u32 = 0;
inj.set_pid(PID);
assert_eq!(inj.pid, PID, "Setter did not correctly set the PID");
}
#[test]
#[cfg(target_os = "windows")]
fn cmp() {
simple_logger::SimpleLogger::new().init().ok();
{
let f = super::cmp(crate::Data::Str("test"));
assert!(f(crate::Data::Str("test")));
assert!(f(crate::Data::Str("something test")));
assert!(!f(crate::Data::Str("something 1351")));
let f = super::cmp(crate::Data::Str("KERNEL32.DLL"));
assert!(f(crate::Data::Str("C:\\Windows\\System32\\KERNEL32.DLL")));
let f = super::cmp(crate::Data::Str("ntdll.dll"));
assert!(f(crate::Data::Str("C:\\Windows\\SYSTEM32\\ntdll.dll")));
}
#[cfg(feature = "std")]
{
let f = std::vec![
super::cmp(crate::Data::Path(std::path::Path::new(
r"C:\this\is\a\test\path\with\a\dir\at\the\end\"
))),
super::cmp(crate::Data::Path(std::path::Path::new(
r"C:\this\is\a\test\path\with\a\dir\at\the\end"
))),
super::cmp(crate::Data::Path(std::path::Path::new(
"C:/this/is/a/test/path/with/a/dir/at/the/end/"
))),
super::cmp(crate::Data::Path(std::path::Path::new(
"C:/this/is/a/test/path/with/a/dir/at/the/end"
))),
];
for f in f {
assert!(f(crate::Data::Str("end")));
assert!(f(crate::Data::Str("the\\end")));
assert!(f(crate::Data::Str("the/end")));
assert!(f(crate::Data::Str("at/the\\end")));
assert!(f(crate::Data::Str("at\\the/end")));
assert!(f(crate::Data::Path(std::path::Path::new("end"))));
assert!(f(crate::Data::Path(std::path::Path::new("the\\end"))));
assert!(f(crate::Data::Path(std::path::Path::new("the/end"))));
assert!(f(crate::Data::Path(std::path::Path::new("at/the\\end"))));
assert!(f(crate::Data::Path(std::path::Path::new("at\\the/end"))));
}
}
{
let f = super::cmp(crate::Data::Str(
r"C:\this\is\a\test\path\with\a\dir\at\the\end\",
));
assert!(!f(crate::Data::Str("end")));
assert!(!f(crate::Data::Str(r"the\end")));
assert!(f(crate::Data::Str(r"end\")));
let f = super::cmp(crate::Data::Str(
r"C:\this\is\a\test\path\with\a\dir\at\the\end",
));
assert!(f(crate::Data::Str("end")));
assert!(f(crate::Data::Str(r"the\end")));
assert!(!f(crate::Data::Str(r"end\")));
assert!(!f(crate::Data::Str(r"the\end\")));
let f = super::cmp(crate::Data::Str(
"C:/this/is/a/test/path/with/a/dir/at/the/end/",
));
assert!(f(crate::Data::Str("end/")));
assert!(f(crate::Data::Str("the/end/")));
assert!(!f(crate::Data::Str("end")));
assert!(!f(crate::Data::Str("the/end")));
let f = super::cmp(crate::Data::Str(
"C:/this/is/a/test/path/with/a/dir/at/the/end",
));
assert!(f(crate::Data::Str("end")));
assert!(!f(crate::Data::Str("end/")));
assert!(!f(crate::Data::Str("the/end/")));
}
}
}