wraith/
error.rs

1//! Unified error types for wraith-rs
2
3use core::fmt;
4
5#[cfg(all(not(feature = "std"), feature = "alloc"))]
6use alloc::string::String;
7
8#[cfg(feature = "std")]
9use std::string::String;
10
11/// all errors that can occur in wraith-rs
12#[derive(Debug)]
13pub enum WraithError {
14    // === version/architecture ===
15    /// windows version not supported by this library
16    UnsupportedWindowsVersion { major: u32, minor: u32, build: u32 },
17
18    /// architecture not supported (e.g., ARM)
19    UnsupportedArchitecture,
20
21    // === structure access ===
22    /// failed to access PEB
23    InvalidPebAccess,
24
25    /// failed to access TEB
26    InvalidTebAccess,
27
28    /// structure data appears corrupted
29    CorruptedStructure {
30        name: &'static str,
31        reason: &'static str,
32    },
33
34    /// null pointer where non-null expected
35    NullPointer { context: &'static str },
36
37    // === navigation ===
38    /// module with given name not found
39    ModuleNotFound { name: String },
40
41    /// address does not belong to any loaded module
42    AddressNotInModule { address: u64 },
43
44    /// thread with given ID not found
45    ThreadNotFound { tid: u32 },
46
47    // === manipulation ===
48    /// failed to unlink module from PEB lists
49    UnlinkFailed { module: String, reason: String },
50
51    /// failed to restore module links
52    RelinkFailed { module: String, reason: String },
53
54    /// PEB module list appears corrupted
55    ListCorrupted { list_type: ModuleListType },
56
57    // === manual mapping ===
58    /// PE file format invalid or unsupported
59    InvalidPeFormat { reason: String },
60
61    /// memory allocation failed
62    AllocationFailed { size: usize, protection: u32 },
63
64    /// failed to map PE section
65    MappingFailed { section: String, reason: String },
66
67    /// failed to process relocation entry
68    RelocationFailed { rva: u32, reason: String },
69
70    /// failed to resolve import
71    ImportResolutionFailed { dll: String, function: String },
72
73    /// export is forwarded to another module
74    ForwardedExport { forwarder: String },
75
76    /// TLS callback execution failed
77    TlsCallbackFailed { index: usize },
78
79    /// DllMain returned FALSE
80    EntryPointFailed { status: i32 },
81
82    // === syscalls ===
83    /// failed to enumerate syscalls from ntdll
84    SyscallEnumerationFailed { reason: String },
85
86    /// syscall with given name/hash not found
87    SyscallNotFound { name: String },
88
89    /// syscall returned error status
90    SyscallFailed { name: String, status: i32 },
91
92    /// ntdll.dll not found in loaded modules
93    NtdllNotFound,
94
95    // === hooks ===
96    /// failed to detect hooks in function
97    HookDetectionFailed { function: String, reason: String },
98
99    /// failed to remove hook
100    UnhookFailed { function: String, reason: String },
101
102    /// function integrity check failed
103    IntegrityCheckFailed { function: String },
104
105    /// clean copy of module not available
106    CleanCopyUnavailable,
107
108    // === inline hooks ===
109    /// failed to install inline hook
110    HookInstallFailed { target: u64, reason: String },
111
112    /// failed to restore hook
113    HookRestoreFailed { target: u64, reason: String },
114
115    /// instruction decoding failed
116    InstructionDecodeFailed { address: u64, reason: String },
117
118    /// trampoline allocation failed
119    TrampolineAllocationFailed { near: u64, size: usize },
120
121    /// hook conflict detected (target already hooked)
122    HookConflict { target: u64, existing_type: String },
123
124    /// insufficient space for hook at target
125    InsufficientHookSpace { target: u64, available: usize, required: usize },
126
127    // === memory ===
128    /// memory read operation failed
129    ReadFailed { address: u64, size: usize },
130
131    /// memory write operation failed
132    WriteFailed { address: u64, size: usize },
133
134    /// failed to change memory protection
135    ProtectionChangeFailed { address: u64, size: usize },
136
137    // === pattern scanning ===
138    /// failed to parse pattern string
139    PatternParseFailed { reason: String },
140
141    // === remote process ===
142    /// failed to open remote process
143    ProcessOpenFailed { pid: u32, reason: String },
144
145    /// process with given ID not found
146    ProcessNotFound { pid: u32 },
147
148    /// failed to create remote thread
149    RemoteThreadFailed { reason: String },
150
151    /// failed to inject into remote process
152    InjectionFailed { method: String, reason: String },
153
154    /// failed to enumerate remote modules
155    RemoteModuleEnumFailed { reason: String },
156
157    /// failed to duplicate handle
158    HandleDuplicateFailed { reason: String },
159
160    /// section mapping failed
161    SectionMappingFailed { reason: String },
162
163    /// APC queue operation failed
164    ApcQueueFailed { reason: String },
165
166    /// thread context operation failed
167    ThreadContextFailed { reason: String },
168
169    /// thread suspend/resume failed
170    ThreadSuspendResumeFailed { reason: String },
171
172    // === spoofing ===
173    /// no suitable gadget found for spoofing
174    GadgetNotFound { gadget_type: &'static str },
175
176    /// failed to build spoof trampoline
177    SpoofTrampolineFailed { reason: String },
178
179    /// stack frame synthesis failed
180    StackSynthesisFailed { reason: String },
181
182    // === win32 ===
183    /// underlying Win32 API returned error
184    Win32Error { code: u32, context: &'static str },
185}
186
187/// which PEB module list had an issue
188#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189pub enum ModuleListType {
190    InLoadOrder,
191    InMemoryOrder,
192    InInitializationOrder,
193}
194
195impl fmt::Display for ModuleListType {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        match self {
198            Self::InLoadOrder => write!(f, "InLoadOrderModuleList"),
199            Self::InMemoryOrder => write!(f, "InMemoryOrderModuleList"),
200            Self::InInitializationOrder => write!(f, "InInitializationOrderModuleList"),
201        }
202    }
203}
204
205#[cfg(feature = "std")]
206impl std::error::Error for WraithError {}
207
208impl fmt::Display for WraithError {
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210        match self {
211            Self::UnsupportedWindowsVersion { major, minor, build } => {
212                write!(f, "unsupported Windows version: {major}.{minor}.{build}")
213            }
214            Self::UnsupportedArchitecture => {
215                write!(f, "unsupported architecture (only x86/x64 supported)")
216            }
217            Self::InvalidPebAccess => {
218                write!(f, "failed to access PEB")
219            }
220            Self::InvalidTebAccess => {
221                write!(f, "failed to access TEB")
222            }
223            Self::CorruptedStructure { name, reason } => {
224                write!(f, "corrupted structure {name}: {reason}")
225            }
226            Self::NullPointer { context } => {
227                write!(f, "unexpected null pointer in {context}")
228            }
229            Self::ModuleNotFound { name } => {
230                write!(f, "module not found: {name}")
231            }
232            Self::AddressNotInModule { address } => {
233                write!(f, "address {address:#x} not in any loaded module")
234            }
235            Self::ThreadNotFound { tid } => {
236                write!(f, "thread {tid} not found")
237            }
238            Self::UnlinkFailed { module, reason } => {
239                write!(f, "failed to unlink {module}: {reason}")
240            }
241            Self::RelinkFailed { module, reason } => {
242                write!(f, "failed to relink {module}: {reason}")
243            }
244            Self::ListCorrupted { list_type } => {
245                write!(f, "PEB {list_type} appears corrupted")
246            }
247            Self::InvalidPeFormat { reason } => {
248                write!(f, "invalid PE format: {reason}")
249            }
250            Self::AllocationFailed { size, protection } => {
251                write!(
252                    f,
253                    "failed to allocate {size} bytes with protection {protection:#x}"
254                )
255            }
256            Self::MappingFailed { section, reason } => {
257                write!(f, "failed to map section {section}: {reason}")
258            }
259            Self::RelocationFailed { rva, reason } => {
260                write!(f, "relocation failed at RVA {rva:#x}: {reason}")
261            }
262            Self::ImportResolutionFailed { dll, function } => {
263                write!(f, "failed to resolve {dll}!{function}")
264            }
265            Self::ForwardedExport { forwarder } => {
266                write!(f, "export forwarded to {forwarder}")
267            }
268            Self::TlsCallbackFailed { index } => {
269                write!(f, "TLS callback {index} failed")
270            }
271            Self::EntryPointFailed { status } => {
272                write!(f, "entry point returned {status}")
273            }
274            Self::SyscallEnumerationFailed { reason } => {
275                write!(f, "syscall enumeration failed: {reason}")
276            }
277            Self::SyscallNotFound { name } => {
278                write!(f, "syscall not found: {name}")
279            }
280            Self::SyscallFailed { name, status } => {
281                write!(f, "syscall {name} failed with status {status:#x}")
282            }
283            Self::NtdllNotFound => {
284                write!(f, "ntdll.dll not found in loaded modules")
285            }
286            Self::HookDetectionFailed { function, reason } => {
287                write!(f, "hook detection failed for {function}: {reason}")
288            }
289            Self::UnhookFailed { function, reason } => {
290                write!(f, "unhook failed for {function}: {reason}")
291            }
292            Self::IntegrityCheckFailed { function } => {
293                write!(f, "integrity check failed for {function}")
294            }
295            Self::CleanCopyUnavailable => {
296                write!(f, "clean copy of module not available for comparison")
297            }
298            Self::HookInstallFailed { target, reason } => {
299                write!(f, "failed to install hook at {target:#x}: {reason}")
300            }
301            Self::HookRestoreFailed { target, reason } => {
302                write!(f, "failed to restore hook at {target:#x}: {reason}")
303            }
304            Self::InstructionDecodeFailed { address, reason } => {
305                write!(f, "instruction decode failed at {address:#x}: {reason}")
306            }
307            Self::TrampolineAllocationFailed { near, size } => {
308                write!(f, "failed to allocate {size} bytes trampoline near {near:#x}")
309            }
310            Self::HookConflict { target, existing_type } => {
311                write!(f, "hook conflict at {target:#x}: already hooked ({existing_type})")
312            }
313            Self::InsufficientHookSpace { target, available, required } => {
314                write!(
315                    f,
316                    "insufficient hook space at {target:#x}: need {required} bytes, have {available}"
317                )
318            }
319            Self::ReadFailed { address, size } => {
320                write!(f, "failed to read {size} bytes at {address:#x}")
321            }
322            Self::WriteFailed { address, size } => {
323                write!(f, "failed to write {size} bytes at {address:#x}")
324            }
325            Self::ProtectionChangeFailed { address, size } => {
326                write!(
327                    f,
328                    "failed to change protection for {size} bytes at {address:#x}"
329                )
330            }
331            Self::PatternParseFailed { reason } => {
332                write!(f, "failed to parse pattern: {reason}")
333            }
334            Self::ProcessOpenFailed { pid, reason } => {
335                write!(f, "failed to open process {pid}: {reason}")
336            }
337            Self::ProcessNotFound { pid } => {
338                write!(f, "process {pid} not found")
339            }
340            Self::RemoteThreadFailed { reason } => {
341                write!(f, "remote thread creation failed: {reason}")
342            }
343            Self::InjectionFailed { method, reason } => {
344                write!(f, "injection via {method} failed: {reason}")
345            }
346            Self::RemoteModuleEnumFailed { reason } => {
347                write!(f, "remote module enumeration failed: {reason}")
348            }
349            Self::HandleDuplicateFailed { reason } => {
350                write!(f, "handle duplication failed: {reason}")
351            }
352            Self::SectionMappingFailed { reason } => {
353                write!(f, "section mapping failed: {reason}")
354            }
355            Self::ApcQueueFailed { reason } => {
356                write!(f, "APC queue operation failed: {reason}")
357            }
358            Self::ThreadContextFailed { reason } => {
359                write!(f, "thread context operation failed: {reason}")
360            }
361            Self::ThreadSuspendResumeFailed { reason } => {
362                write!(f, "thread suspend/resume failed: {reason}")
363            }
364            Self::GadgetNotFound { gadget_type } => {
365                write!(f, "no suitable {gadget_type} gadget found")
366            }
367            Self::SpoofTrampolineFailed { reason } => {
368                write!(f, "spoof trampoline failed: {reason}")
369            }
370            Self::StackSynthesisFailed { reason } => {
371                write!(f, "stack frame synthesis failed: {reason}")
372            }
373            Self::Win32Error { code, context } => {
374                write!(f, "Win32 error {code:#x} in {context}")
375            }
376        }
377    }
378}
379
380/// result type alias using WraithError
381pub type Result<T> = core::result::Result<T, WraithError>;
382
383impl WraithError {
384    /// create Win32Error from GetLastError
385    pub fn from_last_error(context: &'static str) -> Self {
386        // SAFETY: GetLastError is always safe to call
387        let code = unsafe { GetLastError() };
388        Self::Win32Error { code, context }
389    }
390}
391
392#[link(name = "kernel32")]
393extern "system" {
394    fn GetLastError() -> u32;
395}