use std::{
fmt::{self, Display},
io,
};
use num_enum::{IntoPrimitive, TryFromPrimitive, TryFromPrimitiveError};
use thiserror::Error;
use winapi::um::{
minwinbase::{
EXCEPTION_ACCESS_VIOLATION, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_BREAKPOINT,
EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_FLT_DENORMAL_OPERAND,
EXCEPTION_FLT_DIVIDE_BY_ZERO, EXCEPTION_FLT_INEXACT_RESULT,
EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW, EXCEPTION_FLT_STACK_CHECK,
EXCEPTION_FLT_UNDERFLOW, EXCEPTION_GUARD_PAGE, EXCEPTION_ILLEGAL_INSTRUCTION,
EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, EXCEPTION_INVALID_DISPOSITION,
EXCEPTION_INVALID_HANDLE, EXCEPTION_IN_PAGE_ERROR, EXCEPTION_NONCONTINUABLE_EXCEPTION,
EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_SINGLE_STEP, EXCEPTION_STACK_OVERFLOW,
},
winnt::STATUS_UNWIND_CONSOLIDATE,
};
#[cfg(feature = "syringe")]
use winapi::shared::winerror::ERROR_PARTIAL_COPY;
#[derive(Debug, Error)]
pub enum IoOrNulError {
#[error("interior nul found")]
Nul(#[from] widestring::error::ContainsNul<u16>),
#[error("io error: {}", _0)]
Io(#[from] io::Error),
}
#[derive(Debug, Error)]
pub enum GetLocalProcedureAddressError {
#[error("interior nul found")]
Nul(#[from] std::ffi::NulError),
#[error("io error: {}", _0)]
Io(#[from] io::Error),
#[error("unsupported remote target process")]
UnsupportedRemoteTarget,
}
#[derive(
Debug, TryFromPrimitive, IntoPrimitive, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash,
)]
#[repr(u32)]
pub enum ExceptionCode {
AccessViolation = EXCEPTION_ACCESS_VIOLATION,
ArrayBoundsExceeded = EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
Breakpoint = EXCEPTION_BREAKPOINT,
DatatypeMisalignment = EXCEPTION_DATATYPE_MISALIGNMENT,
FltDenormalOperand = EXCEPTION_FLT_DENORMAL_OPERAND,
FltDivideByZero = EXCEPTION_FLT_DIVIDE_BY_ZERO,
FltInexactResult = EXCEPTION_FLT_INEXACT_RESULT,
FltInvalidOperation = EXCEPTION_FLT_INVALID_OPERATION,
FltOverflow = EXCEPTION_FLT_OVERFLOW,
FltStackCheck = EXCEPTION_FLT_STACK_CHECK,
FltUnderflow = EXCEPTION_FLT_UNDERFLOW,
GuardPage = EXCEPTION_GUARD_PAGE,
IllegalInstruction = EXCEPTION_ILLEGAL_INSTRUCTION,
InPageError = EXCEPTION_IN_PAGE_ERROR,
IntegerDivideByZero = EXCEPTION_INT_DIVIDE_BY_ZERO,
IntegerOverflow = EXCEPTION_INT_OVERFLOW,
InvalidDisposition = EXCEPTION_INVALID_DISPOSITION,
InvalidHandle = EXCEPTION_INVALID_HANDLE,
NoncontinuableException = EXCEPTION_NONCONTINUABLE_EXCEPTION,
PrivilegedInstruction = EXCEPTION_PRIV_INSTRUCTION,
SingleStep = EXCEPTION_SINGLE_STEP,
StackOverflow = EXCEPTION_STACK_OVERFLOW,
UnwindConsolidate = STATUS_UNWIND_CONSOLIDATE,
}
impl ExceptionCode {
pub fn try_from_code(code: u32) -> Result<Self, TryFromPrimitiveError<Self>> {
Self::try_from_primitive(code)
}
#[must_use]
pub fn code(self) -> u32 {
self.into()
}
}
impl Display for ExceptionCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::AccessViolation => write!(f, "Invalid access to memory location."),
Self::ArrayBoundsExceeded => write!(f, "Array bounds exceeded."),
Self::Breakpoint => write!(f, "A breakpoint has been reached."),
Self::DatatypeMisalignment => write!(f, "A datatype misalignment was detected in a load or store instruction."),
Self::FltDenormalOperand => write!(f, "Floating-point denormal operand."),
Self::FltDivideByZero => write!(f, "Floating-point division by zero."),
Self::FltInexactResult => write!(f, "Floating-point inexact result."),
Self::FltInvalidOperation => write!(f, "Floating-point invalid operation."),
Self::FltOverflow => write!(f, "Floating-point overflow."),
Self::FltStackCheck => write!(f, "Floating-point stack check."),
Self::FltUnderflow => write!(f, "Floating-point underflow."),
Self::GuardPage => write!(f, "A page of memory that marks the end of a data structure, such as a stack or an array, has been accessed."),
Self::IllegalInstruction => write!(f, "An attempt was made to execute an illegal instruction."),
Self::InPageError => write!(f, "Error performing inpage operation."),
Self::IntegerDivideByZero => write!(f, "Integer division by zero."),
Self::IntegerOverflow => write!(f, "Integer overflow."),
Self::InvalidDisposition => write!(f, "An invalid exception disposition was returned by an exception handler."),
Self::InvalidHandle => write!(f, "The handle is invalid."),
Self::NoncontinuableException => write!(f, "Windows cannot continue from this exception."),
Self::PrivilegedInstruction => write!(f, "Privileged instruction."),
Self::SingleStep => write!(f, "A single step or trace operation has just been completed."),
Self::StackOverflow => write!(f, "Recursion too deep; the stack overflowed."),
Self::UnwindConsolidate => write!(f, "A frame consolidation has been executed."),
}
}
}
#[derive(Debug, Error)]
pub enum ExceptionOrIoError {
#[error("remote io error: {}", _0)]
Io(io::Error),
#[error("remote exception: {}", _0)]
Exception(ExceptionCode),
}
#[derive(Debug, Error)]
#[cfg(feature = "syringe")]
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "syringe")))]
pub(crate) enum LoadInjectHelpDataError {
#[error("io error: {}", _0)]
Io(io::Error),
#[error("unsupported target process")]
UnsupportedTarget,
#[error("inaccessible target process")]
ProcessInaccessible,
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "into-x86-from-x64")]
#[error("failed to load pe file: {}", _0)]
Goblin(#[from] goblin::error::Error),
}
#[cfg(feature = "syringe")]
impl From<io::Error> for LoadInjectHelpDataError {
fn from(err: io::Error) -> Self {
if err.raw_os_error() == Some(ERROR_PARTIAL_COPY as _)
|| err.kind() == io::ErrorKind::PermissionDenied
{
Self::ProcessInaccessible
} else {
Self::Io(err)
}
}
}
#[derive(Debug, Error)]
#[cfg(feature = "syringe")]
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "syringe")))]
pub enum InjectError {
#[error("module path contains illegal interior nul")]
IllegalPath(#[from] widestring::error::ContainsNul<u16>),
#[error("io error: {}", _0)]
Io(io::Error),
#[error("unsupported target process")]
UnsupportedTarget,
#[error("remote io error: {}", _0)]
RemoteIo(io::Error),
#[error("remote exception: {}", _0)]
RemoteException(ExceptionCode),
#[error("inaccessible target process")]
ProcessInaccessible,
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "into-x86-from-x64")]
#[error("failed to load pe file: {}", _0)]
Goblin(#[from] goblin::error::Error),
}
#[cfg(feature = "syringe")]
impl From<io::Error> for InjectError {
fn from(err: io::Error) -> Self {
if err.raw_os_error() == Some(ERROR_PARTIAL_COPY as _)
|| err.kind() == io::ErrorKind::PermissionDenied
{
Self::ProcessInaccessible
} else {
Self::Io(err)
}
}
}
#[cfg(feature = "syringe")]
impl From<ExceptionCode> for InjectError {
fn from(err: ExceptionCode) -> Self {
Self::RemoteException(err)
}
}
#[cfg(feature = "syringe")]
impl From<IoOrNulError> for InjectError {
fn from(err: IoOrNulError) -> Self {
match err {
IoOrNulError::Nul(e) => e.into(),
IoOrNulError::Io(e) => e.into(),
}
}
}
#[cfg(feature = "syringe")]
impl From<ExceptionOrIoError> for InjectError {
fn from(err: ExceptionOrIoError) -> Self {
match err {
ExceptionOrIoError::Io(e) => Self::RemoteIo(e),
ExceptionOrIoError::Exception(e) => Self::RemoteException(e),
}
}
}
#[cfg(feature = "syringe")]
impl From<LoadInjectHelpDataError> for InjectError {
fn from(err: LoadInjectHelpDataError) -> Self {
match err {
LoadInjectHelpDataError::Io(e) => Self::Io(e),
LoadInjectHelpDataError::UnsupportedTarget => Self::UnsupportedTarget,
LoadInjectHelpDataError::ProcessInaccessible => Self::ProcessInaccessible,
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "into-x86-from-x64")]
LoadInjectHelpDataError::Goblin(e) => Self::Goblin(e),
}
}
}
#[derive(Debug, Error)]
#[cfg(feature = "syringe")]
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "syringe")))]
pub enum EjectError {
#[error("io error: {}", _0)]
Io(io::Error),
#[error("unsupported target process")]
UnsupportedTarget,
#[error("remote io error: {}", _0)]
RemoteIo(io::Error),
#[error("remote exception: {}", _0)]
RemoteException(ExceptionCode),
#[error("inaccessible target process")]
ProcessInaccessible,
#[error("inaccessible target module")]
ModuleInaccessible,
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "into-x86-from-x64")]
#[error("failed to load pe file: {}", _0)]
Goblin(#[from] goblin::error::Error),
}
#[cfg(feature = "syringe")]
impl From<LoadInjectHelpDataError> for EjectError {
fn from(err: LoadInjectHelpDataError) -> Self {
match err {
LoadInjectHelpDataError::Io(e) => Self::Io(e),
LoadInjectHelpDataError::UnsupportedTarget => Self::UnsupportedTarget,
LoadInjectHelpDataError::ProcessInaccessible => Self::ProcessInaccessible,
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "into-x86-from-x64")]
LoadInjectHelpDataError::Goblin(e) => Self::Goblin(e),
}
}
}
#[cfg(feature = "syringe")]
impl From<io::Error> for EjectError {
fn from(err: io::Error) -> Self {
if err.raw_os_error() == Some(ERROR_PARTIAL_COPY as _)
|| err.kind() == io::ErrorKind::PermissionDenied
{
Self::ProcessInaccessible
} else {
Self::Io(err)
}
}
}
#[cfg(feature = "syringe")]
impl From<ExceptionCode> for EjectError {
fn from(err: ExceptionCode) -> Self {
Self::RemoteException(err)
}
}
#[cfg(feature = "syringe")]
impl From<ExceptionOrIoError> for EjectError {
fn from(err: ExceptionOrIoError) -> Self {
match err {
ExceptionOrIoError::Io(e) => Self::RemoteIo(e),
ExceptionOrIoError::Exception(e) => Self::RemoteException(e),
}
}
}
#[derive(Debug, Error)]
#[cfg(feature = "syringe")]
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "syringe")))]
pub enum LoadProcedureError {
#[error("io error: {}", _0)]
Io(io::Error),
#[error("unsupported target process")]
UnsupportedTarget,
#[error("remote io error: {}", _0)]
RemoteIo(io::Error),
#[error("remote exception: {}", _0)]
RemoteException(ExceptionCode),
#[error("inaccessible target process")]
ProcessInaccessible,
#[error("inaccessible target module")]
ModuleInaccessible,
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "into-x86-from-x64")]
#[error("failed to load pe file: {}", _0)]
Goblin(#[from] goblin::error::Error),
}
#[cfg(feature = "syringe")]
impl From<LoadInjectHelpDataError> for LoadProcedureError {
fn from(err: LoadInjectHelpDataError) -> Self {
match err {
LoadInjectHelpDataError::Io(e) => Self::Io(e),
LoadInjectHelpDataError::UnsupportedTarget => Self::UnsupportedTarget,
LoadInjectHelpDataError::ProcessInaccessible => Self::ProcessInaccessible,
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "into-x86-from-x64")]
LoadInjectHelpDataError::Goblin(e) => Self::Goblin(e),
}
}
}
#[cfg(feature = "syringe")]
impl From<io::Error> for LoadProcedureError {
fn from(err: io::Error) -> Self {
if err.raw_os_error() == Some(ERROR_PARTIAL_COPY as _)
|| err.kind() == io::ErrorKind::PermissionDenied
{
Self::ProcessInaccessible
} else {
Self::Io(err)
}
}
}
#[cfg(feature = "syringe")]
impl From<ExceptionCode> for LoadProcedureError {
fn from(err: ExceptionCode) -> Self {
Self::RemoteException(err)
}
}
#[cfg(feature = "syringe")]
impl From<ExceptionOrIoError> for LoadProcedureError {
fn from(err: ExceptionOrIoError) -> Self {
match err {
ExceptionOrIoError::Io(e) => Self::RemoteIo(e),
ExceptionOrIoError::Exception(e) => Self::RemoteException(e),
}
}
}
#[derive(Debug, Error)]
#[cfg(feature = "syringe")]
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "syringe")))]
pub enum SyringeError {
#[error("module path contains illegal interior nul")]
IllegalPath(#[from] widestring::error::ContainsNul<u16>),
#[error("io error: {}", _0)]
Io(io::Error),
#[error("unsupported target process")]
UnsupportedTarget,
#[error("remote io error: {}", _0)]
RemoteIo(io::Error),
#[error("remote exception: {}", _0)]
RemoteException(ExceptionCode),
#[error("inaccessible target process")]
ProcessInaccessible,
#[error("inaccessible target module")]
ModuleInaccessible,
#[cfg(feature = "rpc-payload")]
#[error("serde error: {}", _0)]
Serde(Box<bincode::ErrorKind>),
#[cfg(feature = "rpc-payload")]
#[error("remote payload error: {}", _0)]
RemotePayloadProcedure(String),
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "into-x86-from-x64")]
#[error("failed to load pe file: {}", _0)]
Goblin(#[from] goblin::error::Error),
}
#[cfg(feature = "syringe")]
impl From<io::Error> for SyringeError {
fn from(err: io::Error) -> Self {
if err.raw_os_error() == Some(ERROR_PARTIAL_COPY as _)
|| err.kind() == io::ErrorKind::PermissionDenied
{
Self::ProcessInaccessible
} else {
Self::Io(err)
}
}
}
#[cfg(feature = "syringe")]
impl From<ExceptionCode> for SyringeError {
fn from(err: ExceptionCode) -> Self {
Self::RemoteException(err)
}
}
#[cfg(feature = "syringe")]
impl From<IoOrNulError> for SyringeError {
fn from(err: IoOrNulError) -> Self {
match err {
IoOrNulError::Nul(e) => e.into(),
IoOrNulError::Io(e) => e.into(),
}
}
}
#[cfg(feature = "syringe")]
impl From<ExceptionOrIoError> for SyringeError {
fn from(err: ExceptionOrIoError) -> Self {
match err {
ExceptionOrIoError::Io(e) => Self::RemoteIo(e),
ExceptionOrIoError::Exception(e) => Self::RemoteException(e),
}
}
}
#[cfg(feature = "syringe")]
impl From<InjectError> for SyringeError {
fn from(err: InjectError) -> Self {
match err {
InjectError::IllegalPath(e) => Self::IllegalPath(e),
InjectError::Io(e) => Self::Io(e),
InjectError::UnsupportedTarget => Self::UnsupportedTarget,
InjectError::RemoteIo(e) => Self::RemoteIo(e),
InjectError::RemoteException(e) => Self::RemoteException(e),
InjectError::ProcessInaccessible => Self::ProcessInaccessible,
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "into-x86-from-x64")]
InjectError::Goblin(e) => Self::Goblin(e),
}
}
}
#[cfg(feature = "syringe")]
impl From<EjectError> for SyringeError {
fn from(err: EjectError) -> Self {
match err {
EjectError::Io(e) => Self::Io(e),
EjectError::UnsupportedTarget => Self::UnsupportedTarget,
EjectError::RemoteIo(e) => Self::RemoteIo(e),
EjectError::RemoteException(e) => Self::RemoteException(e),
EjectError::ProcessInaccessible => Self::ProcessInaccessible,
EjectError::ModuleInaccessible => Self::ModuleInaccessible,
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "into-x86-from-x64")]
EjectError::Goblin(e) => Self::Goblin(e),
}
}
}
#[cfg(feature = "rpc-core")]
impl From<LoadProcedureError> for SyringeError {
fn from(err: LoadProcedureError) -> Self {
match err {
LoadProcedureError::Io(e) => Self::Io(e),
LoadProcedureError::UnsupportedTarget => Self::UnsupportedTarget,
LoadProcedureError::RemoteIo(e) => Self::RemoteIo(e),
LoadProcedureError::RemoteException(e) => Self::RemoteException(e),
LoadProcedureError::ProcessInaccessible => Self::ProcessInaccessible,
LoadProcedureError::ModuleInaccessible => Self::ModuleInaccessible,
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "into-x86-from-x64")]
LoadProcedureError::Goblin(e) => Self::Goblin(e),
}
}
}
#[cfg(feature = "rpc-core")]
#[cfg_attr(all(feature = "rpc-core", not(feature = "rpc-raw")), doc(hidden))]
impl From<crate::rpc::RawRpcError> for SyringeError {
fn from(err: crate::rpc::RawRpcError) -> Self {
match err {
crate::rpc::RawRpcError::Io(err) => Self::Io(err),
crate::rpc::RawRpcError::RemoteException(code) => Self::RemoteException(code),
crate::rpc::RawRpcError::ProcessInaccessible => Self::ProcessInaccessible,
crate::rpc::RawRpcError::ModuleInaccessible => Self::ModuleInaccessible,
}
}
}
#[cfg(feature = "rpc-payload")]
#[cfg_attr(all(feature = "rpc-core", not(feature = "rpc-raw")), doc(hidden))]
impl From<crate::rpc::PayloadRpcError> for SyringeError {
fn from(err: crate::rpc::PayloadRpcError) -> Self {
match err {
crate::rpc::PayloadRpcError::Io(e) => Self::Io(e),
crate::rpc::PayloadRpcError::RemoteException(e) => Self::RemoteException(e),
crate::rpc::PayloadRpcError::ProcessInaccessible => Self::ProcessInaccessible,
crate::rpc::PayloadRpcError::ModuleInaccessible => Self::ModuleInaccessible,
crate::rpc::PayloadRpcError::RemoteProcedure(e) => Self::RemotePayloadProcedure(e),
crate::rpc::PayloadRpcError::Serde(e) => Self::Serde(e),
}
}
}
#[derive(Debug, Error)]
#[cfg(feature = "syringe")]
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "syringe")))]
pub enum SyringeOperationError {
#[error("inject error: {}", _0)]
Inject(#[from] InjectError),
#[error("eject error: {}", _0)]
Eject(#[from] EjectError),
#[cfg(feature = "rpc-payload")]
#[error("payload rpc error: {}", _0)]
PayloadProcedureCall(#[from] crate::rpc::PayloadRpcError),
#[cfg(feature = "rpc-raw")]
#[error("raw rpc error: {}", _0)]
RawProcedureCall(#[from] crate::rpc::RawRpcError),
#[cfg(feature = "rpc-core")]
#[error("procedure load error: {}", _0)]
ProcedureLoad(#[from] LoadProcedureError),
}