RustVEHSyscalls is a Rust-based port of the LayeredSyscall project. This tool performs indirect syscalls while generating legitimate API call stack frames by abusing Vectored Exception Handling (VEH) to bypass user-land EDR hooks in Windows.
RustVEHSyscalls performs indirect syscalls by abusing Vectored Exception Handling (VEH) to generate legitimate API call stack frames. By calling a standard Windows API function and setting a hardware breakpoint within it, the function's call stack is captured. This breakpoint then lets VEH redirect the process to a syscall wrapper in ntdll.dll
, preserving the original API's call stack structure. This approach enables syscalls to appear as if they originate from legitimate Windows API calls.
RustVEHSyscalls provides functions to initialize and clean up the Vectored Exception Handling environment necessary for syscall interception. These functions establish the hooks needed to capture and handle indirect syscalls, ensuring clean operation and teardown.
initialize_hooks()
:- Sets up two vectored exception handlers for adding and managing hardware breakpoints in the system call path. This function allocates memory for the CPU context and retrieves
ntdll.dll
's base and end addresses for tracing purposes.
- Sets up two vectored exception handlers for adding and managing hardware breakpoints in the system call path. This function allocates memory for the CPU context and retrieves
destroy_hooks()
:- Cleans up by removing the added vectored exception handlers.
RustVEHSyscalls provides a syscall!
macro that wraps several key steps:
- Resolving the Syscall Address and SSN: The macro uses the PEB to locate
ntdll.dll
and parses its Exception Directory and Export Address Table to retrieve both the syscall’s address and System Service Number (SSN). - Setting Hardware Breakpoint: Once the syscall address and SSN are resolved, the macro sets a hardware breakpoint, allowing RustVEHSyscalls to intercept the syscall execution.
- Invoking the Syscall: Finally, the macro invokes the syscall with the specified parameters, completing the indirect syscall path.
To initialize syscall interception, call initialize_hooks()
at the start of your main
function and destroy_hooks()
to clean up once you're done. You can also adjust the legitimate call stack by modifying the demofunction()
in the hooks.rs
module.
/// Example function designed to maintain a clean call stack.
/// This function can be modified to call different legitimate Windows APIs.
pub unsafe extern "C" fn demofunction() {
MessageBoxA(null_mut(), null_mut(), null_mut(), 0);
}
The following example demonstrates how to invoke the NtCreateUserProcess
syscall. Full test code is available in lib.rs
.
fn main() {
initialize_hooks(); // Set up necessary hooks
// Initialize all necessary parameters here...
// Call NtCreateUserProcess syscall
let status = syscall!(
"NtCreateUserProcess",
OrgNtCreateUserProcess,
&mut process_handle,
&mut thread_handle,
desired_access,
desired_access,
null_mut(),
null_mut(),
0,
0,
process_parameters,
&mut create_info,
attribute_list
);
destroy_hooks(); // Clean up hooks when done
}
This project is intended for educational and research purposes only. Use it responsibly, as any misuse is solely your responsibility—not mine! Always follow ethical guidelines and legal frameworks when doing security research (and, you know, just in general).
Special thanks to:
- LayeredSyscall by White Knight Labs for their work.
- Resolving System Service Numbers Using The Exception Directory by MDsec for their insights on resolving SSNs.
Contributions are welcome! If you want to help improve RustVEHSyscalls
or report bugs, feel free to open an issue or a pull request in the repository.