use core::fmt;
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::string::String;
#[cfg(feature = "std")]
use std::string::String;
#[derive(Debug)]
pub enum WraithError {
UnsupportedWindowsVersion { major: u32, minor: u32, build: u32 },
UnsupportedArchitecture,
InvalidPebAccess,
InvalidTebAccess,
CorruptedStructure {
name: &'static str,
reason: &'static str,
},
NullPointer { context: &'static str },
ModuleNotFound { name: String },
AddressNotInModule { address: u64 },
ThreadNotFound { tid: u32 },
UnlinkFailed { module: String, reason: String },
RelinkFailed { module: String, reason: String },
ListCorrupted { list_type: ModuleListType },
InvalidPeFormat { reason: String },
AllocationFailed { size: usize, protection: u32 },
MappingFailed { section: String, reason: String },
RelocationFailed { rva: u32, reason: String },
ImportResolutionFailed { dll: String, function: String },
ForwardedExport { forwarder: String },
TlsCallbackFailed { index: usize },
EntryPointFailed { status: i32 },
SyscallEnumerationFailed { reason: String },
SyscallNotFound { name: String },
SyscallFailed { name: String, status: i32 },
NtdllNotFound,
HookDetectionFailed { function: String, reason: String },
UnhookFailed { function: String, reason: String },
IntegrityCheckFailed { function: String },
CleanCopyUnavailable,
HookInstallFailed { target: u64, reason: String },
HookRestoreFailed { target: u64, reason: String },
InstructionDecodeFailed { address: u64, reason: String },
TrampolineAllocationFailed { near: u64, size: usize },
HookConflict { target: u64, existing_type: String },
InsufficientHookSpace { target: u64, available: usize, required: usize },
ReadFailed { address: u64, size: usize },
WriteFailed { address: u64, size: usize },
ProtectionChangeFailed { address: u64, size: usize },
PatternParseFailed { reason: String },
ProcessOpenFailed { pid: u32, reason: String },
ProcessNotFound { pid: u32 },
RemoteThreadFailed { reason: String },
InjectionFailed { method: String, reason: String },
RemoteModuleEnumFailed { reason: String },
HandleDuplicateFailed { reason: String },
SectionMappingFailed { reason: String },
ApcQueueFailed { reason: String },
ThreadContextFailed { reason: String },
ThreadSuspendResumeFailed { reason: String },
GadgetNotFound { gadget_type: &'static str },
SpoofTrampolineFailed { reason: String },
StackSynthesisFailed { reason: String },
Win32Error { code: u32, context: &'static str },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModuleListType {
InLoadOrder,
InMemoryOrder,
InInitializationOrder,
}
impl fmt::Display for ModuleListType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InLoadOrder => write!(f, "InLoadOrderModuleList"),
Self::InMemoryOrder => write!(f, "InMemoryOrderModuleList"),
Self::InInitializationOrder => write!(f, "InInitializationOrderModuleList"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for WraithError {}
impl fmt::Display for WraithError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnsupportedWindowsVersion { major, minor, build } => {
write!(f, "unsupported Windows version: {major}.{minor}.{build}")
}
Self::UnsupportedArchitecture => {
write!(f, "unsupported architecture (only x86/x64 supported)")
}
Self::InvalidPebAccess => {
write!(f, "failed to access PEB")
}
Self::InvalidTebAccess => {
write!(f, "failed to access TEB")
}
Self::CorruptedStructure { name, reason } => {
write!(f, "corrupted structure {name}: {reason}")
}
Self::NullPointer { context } => {
write!(f, "unexpected null pointer in {context}")
}
Self::ModuleNotFound { name } => {
write!(f, "module not found: {name}")
}
Self::AddressNotInModule { address } => {
write!(f, "address {address:#x} not in any loaded module")
}
Self::ThreadNotFound { tid } => {
write!(f, "thread {tid} not found")
}
Self::UnlinkFailed { module, reason } => {
write!(f, "failed to unlink {module}: {reason}")
}
Self::RelinkFailed { module, reason } => {
write!(f, "failed to relink {module}: {reason}")
}
Self::ListCorrupted { list_type } => {
write!(f, "PEB {list_type} appears corrupted")
}
Self::InvalidPeFormat { reason } => {
write!(f, "invalid PE format: {reason}")
}
Self::AllocationFailed { size, protection } => {
write!(
f,
"failed to allocate {size} bytes with protection {protection:#x}"
)
}
Self::MappingFailed { section, reason } => {
write!(f, "failed to map section {section}: {reason}")
}
Self::RelocationFailed { rva, reason } => {
write!(f, "relocation failed at RVA {rva:#x}: {reason}")
}
Self::ImportResolutionFailed { dll, function } => {
write!(f, "failed to resolve {dll}!{function}")
}
Self::ForwardedExport { forwarder } => {
write!(f, "export forwarded to {forwarder}")
}
Self::TlsCallbackFailed { index } => {
write!(f, "TLS callback {index} failed")
}
Self::EntryPointFailed { status } => {
write!(f, "entry point returned {status}")
}
Self::SyscallEnumerationFailed { reason } => {
write!(f, "syscall enumeration failed: {reason}")
}
Self::SyscallNotFound { name } => {
write!(f, "syscall not found: {name}")
}
Self::SyscallFailed { name, status } => {
write!(f, "syscall {name} failed with status {status:#x}")
}
Self::NtdllNotFound => {
write!(f, "ntdll.dll not found in loaded modules")
}
Self::HookDetectionFailed { function, reason } => {
write!(f, "hook detection failed for {function}: {reason}")
}
Self::UnhookFailed { function, reason } => {
write!(f, "unhook failed for {function}: {reason}")
}
Self::IntegrityCheckFailed { function } => {
write!(f, "integrity check failed for {function}")
}
Self::CleanCopyUnavailable => {
write!(f, "clean copy of module not available for comparison")
}
Self::HookInstallFailed { target, reason } => {
write!(f, "failed to install hook at {target:#x}: {reason}")
}
Self::HookRestoreFailed { target, reason } => {
write!(f, "failed to restore hook at {target:#x}: {reason}")
}
Self::InstructionDecodeFailed { address, reason } => {
write!(f, "instruction decode failed at {address:#x}: {reason}")
}
Self::TrampolineAllocationFailed { near, size } => {
write!(f, "failed to allocate {size} bytes trampoline near {near:#x}")
}
Self::HookConflict { target, existing_type } => {
write!(f, "hook conflict at {target:#x}: already hooked ({existing_type})")
}
Self::InsufficientHookSpace { target, available, required } => {
write!(
f,
"insufficient hook space at {target:#x}: need {required} bytes, have {available}"
)
}
Self::ReadFailed { address, size } => {
write!(f, "failed to read {size} bytes at {address:#x}")
}
Self::WriteFailed { address, size } => {
write!(f, "failed to write {size} bytes at {address:#x}")
}
Self::ProtectionChangeFailed { address, size } => {
write!(
f,
"failed to change protection for {size} bytes at {address:#x}"
)
}
Self::PatternParseFailed { reason } => {
write!(f, "failed to parse pattern: {reason}")
}
Self::ProcessOpenFailed { pid, reason } => {
write!(f, "failed to open process {pid}: {reason}")
}
Self::ProcessNotFound { pid } => {
write!(f, "process {pid} not found")
}
Self::RemoteThreadFailed { reason } => {
write!(f, "remote thread creation failed: {reason}")
}
Self::InjectionFailed { method, reason } => {
write!(f, "injection via {method} failed: {reason}")
}
Self::RemoteModuleEnumFailed { reason } => {
write!(f, "remote module enumeration failed: {reason}")
}
Self::HandleDuplicateFailed { reason } => {
write!(f, "handle duplication failed: {reason}")
}
Self::SectionMappingFailed { reason } => {
write!(f, "section mapping failed: {reason}")
}
Self::ApcQueueFailed { reason } => {
write!(f, "APC queue operation failed: {reason}")
}
Self::ThreadContextFailed { reason } => {
write!(f, "thread context operation failed: {reason}")
}
Self::ThreadSuspendResumeFailed { reason } => {
write!(f, "thread suspend/resume failed: {reason}")
}
Self::GadgetNotFound { gadget_type } => {
write!(f, "no suitable {gadget_type} gadget found")
}
Self::SpoofTrampolineFailed { reason } => {
write!(f, "spoof trampoline failed: {reason}")
}
Self::StackSynthesisFailed { reason } => {
write!(f, "stack frame synthesis failed: {reason}")
}
Self::Win32Error { code, context } => {
write!(f, "Win32 error {code:#x} in {context}")
}
}
}
}
pub type Result<T> = core::result::Result<T, WraithError>;
impl WraithError {
pub fn from_last_error(context: &'static str) -> Self {
let code = unsafe { GetLastError() };
Self::Win32Error { code, context }
}
}
#[link(name = "kernel32")]
extern "system" {
fn GetLastError() -> u32;
}