inject_lib/
error.rs

1///This module contains all error Types.
2use core::fmt::{Display, Formatter};
3#[derive(Debug)]
4///This is the error type for this crate
5pub enum Error {
6    ///This represents an error from the regular windows api
7    ///String is the method the error occurred in
8    ///u32 is the Error, that occurred
9    Winapi(&'static str, u32),
10    ///Gets returned from NTDLL calls.
11    ///This maps, if NTStatus is considered a Warning or Error.
12    ///(Because typically NTDLL calls don't succeed, even if the return type is just a Warning)
13    Ntdll(i32),
14    ///Gets returned, if a Wide String cannot be converted into a regular string.
15    WTFConvert(widestring::error::DecodeUtf16Error), //Windows u16 string stuff
16    #[cfg(feature = "std")]
17    ///Passes errors from std::io.
18    Io(std::io::Error),
19    #[cfg(target_family = "windows")]
20    ///Contains errors from the pelite crate.
21    ///
22    ///See [pelite::Error]
23    Pelite(pelite::Error),
24    ///This is mapped, if a certain thing could potentially be supported with more work, but just has not been implemented yet.
25    Unsupported(Option<&'static str>),
26    ///This represents Error Codes generated by this crate.
27    InjectLib(CustomError),
28}
29
30#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
31//It is fine for additional Error variants to appear in a patch release.
32//Disappearing Error variants is always a breaking change.
33#[non_exhaustive]
34///This Represents Error Codes generated by this Crate
35pub enum CustomError {
36    ///This is thrown, if all available injectors cannot be used in a certain situation.
37    ///This should only happen, if the feature x86tox64 is disabled, but the library is compiled into a x86 binary, which tries injecting into a x64 process.
38    NoViableInjector,
39    ///Special Return codes returned only by WaitForSingleObject
40    WaitForSingleObject(u32),
41    ///This occurs, if some predicate is supplied, that doesn't select an element in a module list
42    ModuleListLoop,
43    ///If for some reason a function call returns zero bytes, but succeeded, this error will get used
44    ZeroBytes,
45    ///If the LDR is unpopulated during a get_module_in_proc call
46    LDRUninit,
47    ///This errror Indicates, that there is possibly a problem with a structure in this crate
48    InvalidStructure,
49    ///Dll file path name does not end in a file
50    DllPathNoFile,
51    ///The Memory Page was not allocated in the expected process.
52    MempageInvalidProcess,
53    ///The input parameters were invalid.
54    InvalidInput,
55    ///The requested operation would have resulted in a Permission error.
56    PermissionDenied,
57    ///This occures when it was expexted, that a Library is Loaded, but not found.
58    LibraryNotFound(alloc::string::String),
59}
60
61impl From<CustomError> for Error {
62    fn from(x: CustomError) -> Self {
63        Error::InjectLib(x)
64    }
65}
66#[cfg(feature = "std")]
67impl From<std::io::Error> for Error {
68    ///Converts a std::Io::Error into a Error for use in this crate
69    fn from(e: std::io::Error) -> Self {
70        Error::Io(e)
71    }
72}
73impl Error {
74    ///Gets the contents of Error::Winapi, if self holds data of that type
75    fn get_winapi(&self) -> Option<(&&'static str, &u32)> {
76        match self {
77            Error::Winapi(x, y) => Some((x, y)),
78            _ => None,
79        }
80    }
81    ///Gets the contents of Error::NTDLL, if self holds data of that type
82    fn get_ntdll(&self) -> Option<&i32> {
83        match self {
84            Error::Ntdll(x) => Some(x),
85            _ => None,
86        }
87    }
88    ///Gets the contents of Error::WTFConvert, if self holds data of that type
89    fn get_wtfconvert(&self) -> Option<&widestring::error::DecodeUtf16Error> {
90        match self {
91            Error::WTFConvert(x) => Some(x),
92            _ => None,
93        }
94    }
95    #[cfg(feature = "std")]
96    ///Gets the contents of Error::Io, if self holds data of that type
97    fn get_io(&self) -> Option<&std::io::Error> {
98        match self {
99            Error::Io(x) => Some(x),
100            _ => None,
101        }
102    }
103    ///Gets the contents of Error::Unsupported, if self holds data of that type
104    fn get_unsupported(&self) -> Option<&Option<&'static str>> {
105        match self {
106            Error::Unsupported(x) => Some(x),
107            _ => None,
108        }
109    }
110
111    #[cfg(target_family = "windows")]
112    ///Gets the contents of Error::Pelite, if self holds data of that type
113    fn get_pelite(&self) -> Option<&pelite::Error> {
114        match self {
115            Error::Pelite(x) => Some(x),
116            _ => None,
117        }
118    }
119    ///Gets the contents of Error::InjectLib, if self holds data of that type
120    fn get_injectlib(&self) -> Option<&CustomError> {
121        match self {
122            Error::InjectLib(x) => Some(x),
123            _ => None,
124        }
125    }
126}
127//due to equality testing for Error::Io, we cannot impl Eq for Error{}
128impl PartialEq<Self> for Error {
129    fn eq(&self, other: &Self) -> bool {
130        fn helper(me: &Error, other: &Error) -> Option<bool> {
131            Some(match me {
132                Error::Winapi(x, y) => other.get_winapi()?.eq(&(x, y)),
133                Error::Ntdll(x) => other.get_ntdll()?.eq(x),
134                Error::WTFConvert(x) => other.get_wtfconvert()?.eq(x),
135                #[cfg(feature = "std")]
136                Error::Io(x) => other.get_io()?.kind() == x.kind(), //todo:improve equality testing for Error::Io
137                #[cfg(target_family = "windows")]
138                Error::Pelite(x) => other.get_pelite()?.eq(x),
139                Error::InjectLib(x) => other.get_injectlib()?.eq(x),
140                Error::Unsupported(x) => other.get_unsupported()?.eq(x),
141            })
142        }
143        *helper(self, other).get_or_insert(false)
144    }
145}
146
147#[cfg(target_family = "windows")]
148mod windows {
149    use crate::error::Error;
150    use winapi::um::errhandlingapi::GetLastError;
151
152    impl From<pelite::Error> for Error {
153        fn from(e: pelite::Error) -> Self {
154            Error::Pelite(e)
155        }
156    }
157    impl From<&'static str> for Error {
158        fn from(s: &'static str) -> Self {
159            Error::Winapi(s, unsafe { GetLastError() })
160        }
161    }
162}
163
164impl Display for Error {
165    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
166        match self {
167            Error::Winapi(s, n) => write!(f, "Winapi error:{} failed with code {}", s, n),
168            Error::Ntdll(n) => write!(f, "Ntdll({:#x})", n),
169            Error::WTFConvert(_) => write!(f, "WTFConvert: Buffer contained non UTF-8 characters."), //todo: should I print osstring here?
170            Error::Unsupported(r) => write!(
171                f,
172                "Unsupported({})",
173                if let Some(s) = r { s } else { "None" }
174            ),
175            #[cfg(feature = "std")]
176            Error::Io(e) => write!(f, "io:{}", e),
177            Error::InjectLib(e) => write!(f, "Inject-Lib({})", e),
178            #[cfg(target_family = "windows")]
179            Error::Pelite(e) => write!(f, "pelite({})", e),
180        }
181    }
182}
183
184impl Display for CustomError {
185    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
186        match self {
187            CustomError::NoViableInjector=>write!(f,"Could not find a viable injection method for the circumstances"),
188            CustomError::WaitForSingleObject(x)=>write!(f,"WaitForSingleObject return code {:#x}",x),
189            CustomError::ModuleListLoop=>write!(f,"We looped through the whole InLoadOrderModuleList, but still have no match. Aborting, because this would end in an endless loop."),
190            CustomError::ZeroBytes=>write!(f,"Zero bytes read, but the requested type is not Zero sized."),
191            CustomError::LDRUninit=>write!(f,"LDR is not initialised"),
192            CustomError::InvalidStructure=>write!(f,"a structure in inject-lib is invalid."),
193            CustomError::DllPathNoFile=>write!(f,"The Dll File Path provided did not point to a file."),
194            CustomError::MempageInvalidProcess=>write!(f,"The Mempage did not belong to the expected Process."),
195            CustomError::InvalidInput=>write!(f,"The Provided Input was invalid."),
196            CustomError::PermissionDenied=>write!(f,"The requested Action would result in a Permission Error."),
197            CustomError::LibraryNotFound(lib)=>write!(f,"It was expected, that the Library:'{}' would be loaded when it wasn't",lib),
198        }
199    }
200}
201
202#[derive(Debug, Clone)]
203///Abstracts a NTStatus return type.
204pub enum Ntdll {
205    ///Maps, if Ntdll considers the NTStatus a Success
206    Success(i32),
207    ///Maps, if Ntdll considers the NTStatus a Information
208    Information(i32),
209    ///Maps, if Ntdll considers the NTStatus a Warning
210    Warning(i32),
211    ///Maps, if Ntdll considers the NTStatus an Error
212    Error(i32),
213    ///Maps, if nothing else maps. Ideally this should go unused
214    Other(i32),
215}
216
217impl Ntdll {
218    ///Get the contained Variable in the Ntdll enum
219    pub fn get_status(&self) -> &i32 {
220        match self {
221            Ntdll::Error(v) => v,
222            Ntdll::Warning(v) => v,
223            Ntdll::Other(v) => v,
224            Ntdll::Success(v) => v,
225            Ntdll::Information(v) => v,
226        }
227    }
228}
229impl Display for Ntdll {
230    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
231        match self {
232            Ntdll::Error(v) => write!(f, "Ntdll::Error({:#x})", v),
233            Ntdll::Warning(v) => write!(f, "Ntdll::Warning({:#x})", v),
234            Ntdll::Other(v) => write!(f, "Ntdll::Other({:#x})", v),
235            Ntdll::Success(v) => write!(f, "Ntdll::Success({:#x})", v),
236            Ntdll::Information(v) => write!(f, "Ntdll::Information({:#x})", v),
237        }
238    }
239}
240
241#[cfg(windows)]
242mod ntdll {
243    use crate::error::{Error, Ntdll};
244    use log::error;
245    use winapi::shared::ntdef::{NTSTATUS, NT_ERROR, NT_INFORMATION, NT_SUCCESS, NT_WARNING};
246
247    impl crate::error::Ntdll {
248        ///Create a new Ntdll enum from a NTSTATUS
249        pub fn new(n: NTSTATUS) -> Self {
250            if NT_ERROR(n) {
251                Ntdll::Error(n)
252            } else if NT_WARNING(n) {
253                Ntdll::Warning(n)
254            } else if NT_INFORMATION(n) {
255                Ntdll::Information(n)
256            } else if NT_SUCCESS(n) {
257                Ntdll::Success(n)
258            } else {
259                error!("");
260                Ntdll::Other(n)
261            }
262        }
263        ///Returns true, if the enum contains Error discriminant
264        pub fn is_error(&self) -> bool {
265            match self {
266                Ntdll::Error(_) => true,
267                _ => false,
268            }
269        }
270        ///Returns true, if the enum contains Warning discriminant
271        pub fn is_warning(&self) -> bool {
272            match self {
273                Ntdll::Warning(_) => true,
274                _ => false,
275            }
276        }
277        ///Returns true, if the enum contains Information discriminant
278        pub fn is_info(&self) -> bool {
279            match self {
280                Ntdll::Information(_) => true,
281                _ => false,
282            }
283        }
284        ///Returns true, if the enum contains Success discriminant
285        pub fn is_success(&self) -> bool {
286            match self {
287                Ntdll::Success(_) => true,
288                _ => false,
289            }
290        }
291        ///Returns true, if the enum contains Other discriminant
292        pub fn is_other(&self) -> bool {
293            match self {
294                Ntdll::Other(_) => true,
295                _ => false,
296            }
297        }
298    }
299    impl Into<Error> for Ntdll {
300        ///Transform Ntdll enum into Error
301        fn into(self) -> Error {
302            match self {
303                Ntdll::Error(v) => Error::Ntdll(v),
304                Ntdll::Warning(v) => Error::Ntdll(v),
305                Ntdll::Information(v) => Error::Ntdll(v),
306                Ntdll::Success(v) => Error::Ntdll(v),
307                Ntdll::Other(v) => Error::Ntdll(v),
308            }
309        }
310    }
311}