1use std::{
2 fmt::{self, Display},
3 io,
4};
5
6use num_enum::{IntoPrimitive, TryFromPrimitive, TryFromPrimitiveError};
7use thiserror::Error;
8use winapi::um::{
9 minwinbase::{
10 EXCEPTION_ACCESS_VIOLATION, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_BREAKPOINT,
11 EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_FLT_DENORMAL_OPERAND,
12 EXCEPTION_FLT_DIVIDE_BY_ZERO, EXCEPTION_FLT_INEXACT_RESULT,
13 EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW, EXCEPTION_FLT_STACK_CHECK,
14 EXCEPTION_FLT_UNDERFLOW, EXCEPTION_GUARD_PAGE, EXCEPTION_ILLEGAL_INSTRUCTION,
15 EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, EXCEPTION_INVALID_DISPOSITION,
16 EXCEPTION_INVALID_HANDLE, EXCEPTION_IN_PAGE_ERROR, EXCEPTION_NONCONTINUABLE_EXCEPTION,
17 EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_SINGLE_STEP, EXCEPTION_STACK_OVERFLOW,
18 },
19 winnt::STATUS_UNWIND_CONSOLIDATE,
20};
21
22#[cfg(feature = "syringe")]
23use winapi::shared::winerror::ERROR_PARTIAL_COPY;
24
25#[derive(Debug, Error)]
26pub enum IoOrNulError {
28 #[error("interior nul found")]
30 Nul(#[from] widestring::error::ContainsNul<u16>),
31 #[error("io error: {}", _0)]
33 Io(#[from] io::Error),
34}
35
36#[derive(Debug, Error)]
40pub enum GetLocalProcedureAddressError {
41 #[error("interior nul found")]
43 Nul(#[from] std::ffi::NulError),
44 #[error("io error: {}", _0)]
46 Io(#[from] io::Error),
47 #[error("unsupported remote target process")]
49 UnsupportedRemoteTarget,
50}
51
52#[derive(
53 Debug, TryFromPrimitive, IntoPrimitive, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash,
54)]
55#[repr(u32)]
56pub enum ExceptionCode {
58 AccessViolation = EXCEPTION_ACCESS_VIOLATION,
60 ArrayBoundsExceeded = EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
62 Breakpoint = EXCEPTION_BREAKPOINT,
64 DatatypeMisalignment = EXCEPTION_DATATYPE_MISALIGNMENT,
67 FltDenormalOperand = EXCEPTION_FLT_DENORMAL_OPERAND,
70 FltDivideByZero = EXCEPTION_FLT_DIVIDE_BY_ZERO,
72 FltInexactResult = EXCEPTION_FLT_INEXACT_RESULT,
74 FltInvalidOperation = EXCEPTION_FLT_INVALID_OPERATION,
76 FltOverflow = EXCEPTION_FLT_OVERFLOW,
78 FltStackCheck = EXCEPTION_FLT_STACK_CHECK,
80 FltUnderflow = EXCEPTION_FLT_UNDERFLOW,
82 GuardPage = EXCEPTION_GUARD_PAGE,
84 IllegalInstruction = EXCEPTION_ILLEGAL_INSTRUCTION,
86 InPageError = EXCEPTION_IN_PAGE_ERROR,
89 IntegerDivideByZero = EXCEPTION_INT_DIVIDE_BY_ZERO,
91 IntegerOverflow = EXCEPTION_INT_OVERFLOW,
95 InvalidDisposition = EXCEPTION_INVALID_DISPOSITION,
98 InvalidHandle = EXCEPTION_INVALID_HANDLE,
100 NoncontinuableException = EXCEPTION_NONCONTINUABLE_EXCEPTION,
102 PrivilegedInstruction = EXCEPTION_PRIV_INSTRUCTION,
104 SingleStep = EXCEPTION_SINGLE_STEP,
106 StackOverflow = EXCEPTION_STACK_OVERFLOW,
108 UnwindConsolidate = STATUS_UNWIND_CONSOLIDATE,
110}
111
112impl ExceptionCode {
113 pub fn try_from_code(code: u32) -> Result<Self, TryFromPrimitiveError<Self>> {
115 Self::try_from_primitive(code)
116 }
117 #[must_use]
119 pub fn code(self) -> u32 {
120 self.into()
121 }
122}
123
124impl Display for ExceptionCode {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 match *self {
131 Self::AccessViolation => write!(f, "Invalid access to memory location."),
132 Self::ArrayBoundsExceeded => write!(f, "Array bounds exceeded."),
133 Self::Breakpoint => write!(f, "A breakpoint has been reached."),
134 Self::DatatypeMisalignment => write!(f, "A datatype misalignment was detected in a load or store instruction."),
135 Self::FltDenormalOperand => write!(f, "Floating-point denormal operand."),
136 Self::FltDivideByZero => write!(f, "Floating-point division by zero."),
137 Self::FltInexactResult => write!(f, "Floating-point inexact result."),
138 Self::FltInvalidOperation => write!(f, "Floating-point invalid operation."),
139 Self::FltOverflow => write!(f, "Floating-point overflow."),
140 Self::FltStackCheck => write!(f, "Floating-point stack check."),
141 Self::FltUnderflow => write!(f, "Floating-point underflow."),
142 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."),
143 Self::IllegalInstruction => write!(f, "An attempt was made to execute an illegal instruction."),
144 Self::InPageError => write!(f, "Error performing inpage operation."),
145 Self::IntegerDivideByZero => write!(f, "Integer division by zero."),
146 Self::IntegerOverflow => write!(f, "Integer overflow."),
147 Self::InvalidDisposition => write!(f, "An invalid exception disposition was returned by an exception handler."),
148 Self::InvalidHandle => write!(f, "The handle is invalid."),
149 Self::NoncontinuableException => write!(f, "Windows cannot continue from this exception."),
150 Self::PrivilegedInstruction => write!(f, "Privileged instruction."),
151 Self::SingleStep => write!(f, "A single step or trace operation has just been completed."),
152 Self::StackOverflow => write!(f, "Recursion too deep; the stack overflowed."),
153 Self::UnwindConsolidate => write!(f, "A frame consolidation has been executed."),
154 }
155 }
156}
157
158#[derive(Debug, Error)]
159pub enum ExceptionOrIoError {
161 #[error("remote io error: {}", _0)]
163 Io(io::Error),
164 #[error("remote exception: {}", _0)]
166 Exception(ExceptionCode),
167}
168
169#[derive(Debug, Error)]
171#[cfg(feature = "syringe")]
172#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "syringe")))]
173pub(crate) enum LoadInjectHelpDataError {
174 #[error("io error: {}", _0)]
176 Io(io::Error),
177 #[error("unsupported target process")]
179 UnsupportedTarget,
180 #[error("inaccessible target process")]
183 ProcessInaccessible,
184 #[cfg(target_arch = "x86_64")]
186 #[cfg(feature = "into-x86-from-x64")]
187 #[error("failed to load pe file: {}", _0)]
188 Goblin(#[from] goblin::error::Error),
189}
190
191#[cfg(feature = "syringe")]
192impl From<io::Error> for LoadInjectHelpDataError {
193 fn from(err: io::Error) -> Self {
194 if err.raw_os_error() == Some(ERROR_PARTIAL_COPY as _)
195 || err.kind() == io::ErrorKind::PermissionDenied
196 {
197 Self::ProcessInaccessible
198 } else {
199 Self::Io(err)
200 }
201 }
202}
203
204#[derive(Debug, Error)]
206#[cfg(feature = "syringe")]
207#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "syringe")))]
208pub enum InjectError {
209 #[error("module path contains illegal interior nul")]
211 IllegalPath(#[from] widestring::error::ContainsNul<u16>),
212 #[error("io error: {}", _0)]
214 Io(io::Error),
215 #[error("unsupported target process")]
217 UnsupportedTarget,
218 #[error("remote io error: {}", _0)]
220 RemoteIo(io::Error),
221 #[error("remote exception: {}", _0)]
223 RemoteException(ExceptionCode),
224 #[error("inaccessible target process")]
227 ProcessInaccessible,
228 #[error("mismatch between target and payload architecture")]
230 ArchitectureMismatch,
231 #[cfg(target_arch = "x86_64")]
233 #[cfg(feature = "into-x86-from-x64")]
234 #[error("failed to load pe file: {}", _0)]
235 Goblin(#[from] goblin::error::Error),
236}
237
238#[cfg(feature = "syringe")]
239impl From<io::Error> for InjectError {
240 fn from(err: io::Error) -> Self {
241 if err.raw_os_error() == Some(ERROR_PARTIAL_COPY as _)
242 || err.kind() == io::ErrorKind::PermissionDenied
243 {
244 Self::ProcessInaccessible
245 } else {
246 Self::Io(err)
247 }
248 }
249}
250
251#[cfg(feature = "syringe")]
252impl From<ExceptionCode> for InjectError {
253 fn from(err: ExceptionCode) -> Self {
254 Self::RemoteException(err)
255 }
256}
257
258#[cfg(feature = "syringe")]
259impl From<IoOrNulError> for InjectError {
260 fn from(err: IoOrNulError) -> Self {
261 match err {
262 IoOrNulError::Nul(e) => e.into(),
263 IoOrNulError::Io(e) => e.into(),
264 }
265 }
266}
267
268#[cfg(feature = "syringe")]
269impl From<ExceptionOrIoError> for InjectError {
270 fn from(err: ExceptionOrIoError) -> Self {
271 match err {
272 ExceptionOrIoError::Io(e) => Self::RemoteIo(e),
273 ExceptionOrIoError::Exception(e) => Self::RemoteException(e),
274 }
275 }
276}
277
278#[cfg(feature = "syringe")]
279impl From<LoadInjectHelpDataError> for InjectError {
280 fn from(err: LoadInjectHelpDataError) -> Self {
281 match err {
282 LoadInjectHelpDataError::Io(e) => Self::Io(e),
283 LoadInjectHelpDataError::UnsupportedTarget => Self::UnsupportedTarget,
284 LoadInjectHelpDataError::ProcessInaccessible => Self::ProcessInaccessible,
285 #[cfg(target_arch = "x86_64")]
286 #[cfg(feature = "into-x86-from-x64")]
287 LoadInjectHelpDataError::Goblin(e) => Self::Goblin(e),
288 }
289 }
290}
291
292#[derive(Debug, Error)]
294#[cfg(feature = "syringe")]
295#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "syringe")))]
296pub enum EjectError {
297 #[error("io error: {}", _0)]
299 Io(io::Error),
300 #[error("unsupported target process")]
302 UnsupportedTarget,
303 #[error("remote io error: {}", _0)]
305 RemoteIo(io::Error),
306 #[error("remote exception: {}", _0)]
308 RemoteException(ExceptionCode),
309 #[error("inaccessible target process")]
312 ProcessInaccessible,
313 #[error("inaccessible target module")]
316 ModuleInaccessible,
317 #[cfg(target_arch = "x86_64")]
319 #[cfg(feature = "into-x86-from-x64")]
320 #[error("failed to load pe file: {}", _0)]
321 Goblin(#[from] goblin::error::Error),
322}
323
324#[cfg(feature = "syringe")]
325impl From<LoadInjectHelpDataError> for EjectError {
326 fn from(err: LoadInjectHelpDataError) -> Self {
327 match err {
328 LoadInjectHelpDataError::Io(e) => Self::Io(e),
329 LoadInjectHelpDataError::UnsupportedTarget => Self::UnsupportedTarget,
330 LoadInjectHelpDataError::ProcessInaccessible => Self::ProcessInaccessible,
331 #[cfg(target_arch = "x86_64")]
332 #[cfg(feature = "into-x86-from-x64")]
333 LoadInjectHelpDataError::Goblin(e) => Self::Goblin(e),
334 }
335 }
336}
337
338#[cfg(feature = "syringe")]
339impl From<io::Error> for EjectError {
340 fn from(err: io::Error) -> Self {
341 if err.raw_os_error() == Some(ERROR_PARTIAL_COPY as _)
342 || err.kind() == io::ErrorKind::PermissionDenied
343 {
344 Self::ProcessInaccessible
345 } else {
346 Self::Io(err)
347 }
348 }
349}
350
351#[cfg(feature = "syringe")]
352impl From<ExceptionCode> for EjectError {
353 fn from(err: ExceptionCode) -> Self {
354 Self::RemoteException(err)
355 }
356}
357
358#[cfg(feature = "syringe")]
359impl From<ExceptionOrIoError> for EjectError {
360 fn from(err: ExceptionOrIoError) -> Self {
361 match err {
362 ExceptionOrIoError::Io(e) => Self::RemoteIo(e),
363 ExceptionOrIoError::Exception(e) => Self::RemoteException(e),
364 }
365 }
366}
367
368#[derive(Debug, Error)]
370#[cfg(feature = "syringe")]
371#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "syringe")))]
372pub enum LoadProcedureError {
373 #[error("io error: {}", _0)]
375 Io(io::Error),
376 #[error("unsupported target process")]
378 UnsupportedTarget,
379 #[error("remote io error: {}", _0)]
381 RemoteIo(io::Error),
382 #[error("remote exception: {}", _0)]
384 RemoteException(ExceptionCode),
385 #[error("inaccessible target process")]
388 ProcessInaccessible,
389 #[error("inaccessible target module")]
392 ModuleInaccessible,
393 #[cfg(target_arch = "x86_64")]
395 #[cfg(feature = "into-x86-from-x64")]
396 #[error("failed to load pe file: {}", _0)]
397 Goblin(#[from] goblin::error::Error),
398}
399
400#[cfg(feature = "syringe")]
401impl From<LoadInjectHelpDataError> for LoadProcedureError {
402 fn from(err: LoadInjectHelpDataError) -> Self {
403 match err {
404 LoadInjectHelpDataError::Io(e) => Self::Io(e),
405 LoadInjectHelpDataError::UnsupportedTarget => Self::UnsupportedTarget,
406 LoadInjectHelpDataError::ProcessInaccessible => Self::ProcessInaccessible,
407 #[cfg(target_arch = "x86_64")]
408 #[cfg(feature = "into-x86-from-x64")]
409 LoadInjectHelpDataError::Goblin(e) => Self::Goblin(e),
410 }
411 }
412}
413
414#[cfg(feature = "syringe")]
415impl From<io::Error> for LoadProcedureError {
416 fn from(err: io::Error) -> Self {
417 if err.raw_os_error() == Some(ERROR_PARTIAL_COPY as _)
418 || err.kind() == io::ErrorKind::PermissionDenied
419 {
420 Self::ProcessInaccessible
421 } else {
422 Self::Io(err)
423 }
424 }
425}
426
427#[cfg(feature = "syringe")]
428impl From<ExceptionCode> for LoadProcedureError {
429 fn from(err: ExceptionCode) -> Self {
430 Self::RemoteException(err)
431 }
432}
433
434#[cfg(feature = "syringe")]
435impl From<ExceptionOrIoError> for LoadProcedureError {
436 fn from(err: ExceptionOrIoError) -> Self {
437 match err {
438 ExceptionOrIoError::Io(e) => Self::RemoteIo(e),
439 ExceptionOrIoError::Exception(e) => Self::RemoteException(e),
440 }
441 }
442}
443
444#[derive(Debug, Error)]
446#[cfg(feature = "syringe")]
447#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "syringe")))]
448pub enum SyringeError {
449 #[error("module path contains illegal interior nul")]
451 IllegalPath(#[from] widestring::error::ContainsNul<u16>),
452 #[error("io error: {}", _0)]
454 Io(io::Error),
455 #[error("unsupported target process")]
457 UnsupportedTarget,
458 #[error("remote io error: {}", _0)]
460 RemoteIo(io::Error),
461 #[error("remote exception: {}", _0)]
463 RemoteException(ExceptionCode),
464 #[error("inaccessible target process")]
467 ProcessInaccessible,
468 #[error("mismatch between target and payload architecture")]
470 ArchitectureMismatch,
471 #[error("inaccessible target module")]
474 ModuleInaccessible,
475 #[cfg(feature = "rpc-payload")]
477 #[error("serde error: {}", _0)]
478 Serde(crate::rpc::SerdeError),
479 #[cfg(feature = "rpc-payload")]
481 #[error("remote payload error: {}", _0)]
482 RemotePayloadProcedure(String),
483 #[cfg(target_arch = "x86_64")]
485 #[cfg(feature = "into-x86-from-x64")]
486 #[error("failed to load pe file: {}", _0)]
487 Goblin(#[from] goblin::error::Error),
488}
489
490#[cfg(feature = "syringe")]
491impl From<io::Error> for SyringeError {
492 fn from(err: io::Error) -> Self {
493 if err.raw_os_error() == Some(ERROR_PARTIAL_COPY as _)
494 || err.kind() == io::ErrorKind::PermissionDenied
495 {
496 Self::ProcessInaccessible
497 } else {
498 Self::Io(err)
499 }
500 }
501}
502
503#[cfg(feature = "syringe")]
504impl From<ExceptionCode> for SyringeError {
505 fn from(err: ExceptionCode) -> Self {
506 Self::RemoteException(err)
507 }
508}
509
510#[cfg(feature = "syringe")]
511impl From<IoOrNulError> for SyringeError {
512 fn from(err: IoOrNulError) -> Self {
513 match err {
514 IoOrNulError::Nul(e) => e.into(),
515 IoOrNulError::Io(e) => e.into(),
516 }
517 }
518}
519
520#[cfg(feature = "syringe")]
521impl From<ExceptionOrIoError> for SyringeError {
522 fn from(err: ExceptionOrIoError) -> Self {
523 match err {
524 ExceptionOrIoError::Io(e) => Self::RemoteIo(e),
525 ExceptionOrIoError::Exception(e) => Self::RemoteException(e),
526 }
527 }
528}
529
530#[cfg(feature = "syringe")]
531impl From<InjectError> for SyringeError {
532 fn from(err: InjectError) -> Self {
533 match err {
534 InjectError::IllegalPath(e) => Self::IllegalPath(e),
535 InjectError::Io(e) => Self::Io(e),
536 InjectError::UnsupportedTarget => Self::UnsupportedTarget,
537 InjectError::RemoteIo(e) => Self::RemoteIo(e),
538 InjectError::RemoteException(e) => Self::RemoteException(e),
539 InjectError::ProcessInaccessible => Self::ProcessInaccessible,
540 InjectError::ArchitectureMismatch => Self::ArchitectureMismatch,
541 #[cfg(target_arch = "x86_64")]
542 #[cfg(feature = "into-x86-from-x64")]
543 InjectError::Goblin(e) => Self::Goblin(e),
544 }
545 }
546}
547
548#[cfg(feature = "syringe")]
549impl From<EjectError> for SyringeError {
550 fn from(err: EjectError) -> Self {
551 match err {
552 EjectError::Io(e) => Self::Io(e),
553 EjectError::UnsupportedTarget => Self::UnsupportedTarget,
554 EjectError::RemoteIo(e) => Self::RemoteIo(e),
555 EjectError::RemoteException(e) => Self::RemoteException(e),
556 EjectError::ProcessInaccessible => Self::ProcessInaccessible,
557 EjectError::ModuleInaccessible => Self::ModuleInaccessible,
558 #[cfg(target_arch = "x86_64")]
559 #[cfg(feature = "into-x86-from-x64")]
560 EjectError::Goblin(e) => Self::Goblin(e),
561 }
562 }
563}
564
565#[cfg(feature = "rpc-core")]
566impl From<LoadProcedureError> for SyringeError {
567 fn from(err: LoadProcedureError) -> Self {
568 match err {
569 LoadProcedureError::Io(e) => Self::Io(e),
570 LoadProcedureError::UnsupportedTarget => Self::UnsupportedTarget,
571 LoadProcedureError::RemoteIo(e) => Self::RemoteIo(e),
572 LoadProcedureError::RemoteException(e) => Self::RemoteException(e),
573 LoadProcedureError::ProcessInaccessible => Self::ProcessInaccessible,
574 LoadProcedureError::ModuleInaccessible => Self::ModuleInaccessible,
575 #[cfg(target_arch = "x86_64")]
576 #[cfg(feature = "into-x86-from-x64")]
577 LoadProcedureError::Goblin(e) => Self::Goblin(e),
578 }
579 }
580}
581
582#[cfg(feature = "rpc-core")]
583#[cfg_attr(all(feature = "rpc-core", not(feature = "rpc-raw")), doc(hidden))]
584impl From<crate::rpc::RawRpcError> for SyringeError {
585 fn from(err: crate::rpc::RawRpcError) -> Self {
586 match err {
587 crate::rpc::RawRpcError::Io(err) => Self::Io(err),
588 crate::rpc::RawRpcError::RemoteException(code) => Self::RemoteException(code),
589 crate::rpc::RawRpcError::ProcessInaccessible => Self::ProcessInaccessible,
590 crate::rpc::RawRpcError::ModuleInaccessible => Self::ModuleInaccessible,
591 }
592 }
593}
594
595#[cfg(feature = "rpc-payload")]
596#[cfg_attr(all(feature = "rpc-core", not(feature = "rpc-raw")), doc(hidden))]
597impl From<crate::rpc::PayloadRpcError> for SyringeError {
598 fn from(err: crate::rpc::PayloadRpcError) -> Self {
599 match err {
600 crate::rpc::PayloadRpcError::Io(e) => Self::Io(e),
601 crate::rpc::PayloadRpcError::RemoteException(e) => Self::RemoteException(e),
602 crate::rpc::PayloadRpcError::ProcessInaccessible => Self::ProcessInaccessible,
603 crate::rpc::PayloadRpcError::ModuleInaccessible => Self::ModuleInaccessible,
604 crate::rpc::PayloadRpcError::RemoteProcedure(e) => Self::RemotePayloadProcedure(e),
605 crate::rpc::PayloadRpcError::Serde(e) => Self::Serde(e),
606 }
607 }
608}
609
610#[derive(Debug, Error)]
612#[cfg(feature = "syringe")]
613#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "syringe")))]
614pub enum SyringeOperationError {
615 #[error("inject error: {}", _0)]
617 Inject(#[from] InjectError),
618 #[error("eject error: {}", _0)]
620 Eject(#[from] EjectError),
621 #[cfg(feature = "rpc-payload")]
623 #[error("payload rpc error: {}", _0)]
624 PayloadProcedureCall(#[from] crate::rpc::PayloadRpcError),
625 #[cfg(feature = "rpc-raw")]
627 #[error("raw rpc error: {}", _0)]
628 RawProcedureCall(#[from] crate::rpc::RawRpcError),
629 #[cfg(feature = "rpc-core")]
631 #[error("procedure load error: {}", _0)]
632 ProcedureLoad(#[from] LoadProcedureError),
633}