diff --git a/driver/Cargo.toml b/driver/Cargo.toml index 4ac51b9..d717525 100644 --- a/driver/Cargo.toml +++ b/driver/Cargo.toml @@ -34,7 +34,8 @@ x86_64 = { version = "0.14.7", features = ["nightly", "const_fn", "abi_x86_inter x86 = { version = "0.44.0" } winapi = { git = "https://github.com/Trantect/winapi-rs.git", branch = "feature/km", features = ["wdm", "ntstatus"] } once_cell = { version = "1.9.0", default-features = false, features = ["unstable", "alloc"] } -iced-x86 = { version = "1.15.0", default-features = false, features = ["no_std", "decoder", "block_encoder", "instr_info", "no_d3now", "no_evex", "no_vex", "no_xop"] } +#iced-x86 = { version = "1.15.0", default-features = false, features = ["no_std", "decoder", "block_encoder", "encoder", "instr_info", "no_d3now", "no_evex", "no_vex", "no_xop"] } +iced-x86 = { path = "C:/Users/not-matthias/Documents/iced/src/rust/iced-x86", default-features = false, features = ["no_std", "decoder", "block_encoder", "instr_info", "no_d3now", "no_evex", "no_vex", "no_xop"] } [profile.release] lto = "thin" # IMPORTANT: "fat" doesn't work and will result in an access violation @@ -42,4 +43,7 @@ lto = "thin" # IMPORTANT: "fat" doesn't work and will result in an access vio debug = true panic = "abort" overflow-checks = false -debug-assertions = false \ No newline at end of file +debug-assertions = false + +[patch.crates-io] +ahash = { git = "https://github.com/tkaitchuck/aHash", branch = "simplification" } \ No newline at end of file diff --git a/driver/src/hook/handlers.rs b/driver/src/hook/handlers.rs index 761b7c0..18b0351 100644 --- a/driver/src/hook/handlers.rs +++ b/driver/src/hook/handlers.rs @@ -1,13 +1,15 @@ use crate::nt::inline_hook::InlineHook; use crate::nt::ptr::Pointer; -use nt::include::{ZwQuerySystemInformation, SYSTEM_INFORMATION_CLASS}; +use nt::include::MmIsAddressValid; +use winapi::km::wdm::POOL_TYPE::NonPagedPool; use crate::dbg_break; +use crate::nt::include::{ExAllocatePoolWithTag, ExFreePool}; use winapi::shared::ntdef::NTSTATUS; pub static mut ZWQSI_ORIGINAL: Option> = None; -pub fn zw_query_system_information( +pub extern "system" fn zw_query_system_information( system_information_class: u32, system_information: u64, system_information_length: u32, @@ -84,16 +86,16 @@ pub fn test_hooks() { // Test zw_query_system_information // - log::info!("Testing zw_query_system_information."); - let status = unsafe { - ZwQuerySystemInformation( - SYSTEM_INFORMATION_CLASS::SystemProcessInformation, - 0x0 as _, - 0x0, - 0x0 as _, - ) - }; - log::info!("zw_query_system_information returned {:x}.", status); + // log::info!("Testing zw_query_system_information."); + // let status = unsafe { + // ZwQuerySystemInformation( + // SYSTEM_INFORMATION_CLASS::SystemProcessInformation, + // 0x0 as _, + // 0x0, + // 0x0 as _, + // ) + // }; + // log::info!("zw_query_system_information returned {:x}.", status); // Test ex_allocate_pool_with_tag // @@ -107,9 +109,13 @@ pub fn test_hooks() { // // unsafe { ExFreePool(ptr as _) }; - // Test MmIsAddressValid + // // Test MmIsAddressValid + // // + // dbg_break!(); // - // unsafe { MmIsAddressValid(0 as _) }; + // log::info!("Is address valid: {:?}", unsafe { + // MmIsAddressValid(0 as _) + // }); dbg_break!(); } diff --git a/driver/src/hook/mod.rs b/driver/src/hook/mod.rs index e78a293..26a3123 100644 --- a/driver/src/hook/mod.rs +++ b/driver/src/hook/mod.rs @@ -115,7 +115,7 @@ impl Hook { // Install inline hook on the **copied** page (not the original one). // - let inline_hook = InlineHook::new(hook_va, handler)?; + let inline_hook = InlineHook::new(address, hook_va, handler)?; Some(Self { original_va: address, diff --git a/driver/src/lib.rs b/driver/src/lib.rs index 8deaf5d..befbb8a 100644 --- a/driver/src/lib.rs +++ b/driver/src/lib.rs @@ -11,6 +11,7 @@ #![feature(const_ptr_as_ref)] #![feature(const_trait_impl)] #![allow(clippy::new_ret_no_self)] +#![feature(int_abs_diff)] extern crate alloc; @@ -47,35 +48,34 @@ static LOGGER: KernelLogger = KernelLogger; static mut PROCESSORS: Option = None; fn init_hooks() -> Option> { - // ZwQuerySystemInformation + // // ZwQuerySystemInformation + // // + // let zwqsi_hook = Hook::hook_function( + // "ZwQuerySystemInformation", + // handlers::zw_query_system_information as *const (), + // )?; + // unsafe { + // handlers::ZWQSI_ORIGINAL = match zwqsi_hook.hook_type { + // HookType::Function { ref inline_hook } => Pointer::new(inline_hook.as_ptr()), + // HookType::Page => None, + // }; + // } + + // ExAllocatePoolWithTag // - let zwqsi_hook = Hook::hook_function( - "ZwQuerySystemInformation", - handlers::zw_query_system_information as *const (), + let eapwt_hook = Hook::hook_function( + "ExAllocatePoolWithTag", + handlers::ex_allocate_pool_with_tag as *const (), )?; unsafe { - handlers::ZWQSI_ORIGINAL = match zwqsi_hook.hook_type { + handlers::EAPWT_ORIGINAL = match eapwt_hook.hook_type { HookType::Function { ref inline_hook } => Pointer::new(inline_hook.as_ptr()), - HookType::Page => None, + HookType::Page => unreachable!(), }; } - // ExAllocatePool + // MmIsAddressValid // - // let eapwt_hook = Hook::hook_function( - // "ExAllocatePoolWithTag", - // handlers::ex_allocate_pool_with_tag as *const (), - // )?; - // unsafe { - // handlers::EAPWT_ORIGINAL = match eapwt_hook.hook_type { - // HookType::Function { ref inline_hook } => Pointer::new(inline_hook.as_ptr()), - // HookType::Page => unreachable!(), - // }; - // } - - // Currently not supported because of the minimalistic InlineHook implementation. - // // MmIsAddressValid - // // // let mmiav_hook = Hook::hook_function( // "MmIsAddressValid", // handlers::mm_is_address_valid as *const (), @@ -87,7 +87,7 @@ fn init_hooks() -> Option> { // }; // } - Some(vec![zwqsi_hook]) + Some(vec![eapwt_hook]) } fn virtualize_system() -> Option<()> { diff --git a/driver/src/nt/inline_hook.rs b/driver/src/nt/inline_hook.rs index d02f316..a6058dd 100644 --- a/driver/src/nt/inline_hook.rs +++ b/driver/src/nt/inline_hook.rs @@ -1,14 +1,14 @@ -use crate::dbg_break; use crate::nt::include::RtlCopyMemory; use crate::nt::memory::AllocatedMemory; use alloc::vec::Vec; +use core::arch::x86_64::_mm_clflush; use iced_x86::{ BlockEncoder, BlockEncoderOptions, Code, Decoder, DecoderOptions, FlowControl, Instruction, InstructionBlock, Register, }; use snafu::prelude::*; -pub const JMP_SHELLCODE_LEN: usize = 12; +pub const JMP_SHELLCODE_LEN: usize = 14; #[derive(Debug, Snafu)] pub enum InlineHookError { @@ -38,9 +38,20 @@ pub struct InlineHook { } impl InlineHook { - // Note: We have to allocate a new instance here, so that it's valid after the virtualization. Otherwise, - // all the addresses would be 0x0. - pub fn new(address: u64, handler: *const ()) -> Option> { + /// Creates a new inline hook (not yet enabled) for the specified function. + /// + /// + /// ## Note + /// + /// Note: We have to allocate a new instance here, so that it's valid after the virtualization. Otherwise, + /// all the addresses would be 0x0. + /// + /// TODO: Can we somehow get rid of the original address? + pub fn new( + original_address: u64, + address: u64, + handler: *const (), + ) -> Option> { log::info!( "Creating a new inline hook. Address: {:x}, handler: {:x}", address, @@ -48,7 +59,7 @@ impl InlineHook { ); let mut hook = AllocatedMemory::::alloc(core::mem::size_of::())?; - hook.trampoline = match Self::trampoline_shellcode(address as u64) { + hook.trampoline = match Self::trampoline_shellcode(original_address, address as u64) { Ok(trampoline) => trampoline, Err(e) => { log::error!("Failed to create trampoline: {:?}", e); @@ -88,35 +99,61 @@ impl InlineHook { self.trampoline.as_ptr() as _ } - fn jmp_shellcode(target_address: u64) -> Vec { + /// Creates the jmp shellcode. + /// + /// ## How it works. + /// + /// We are using the following assembly shellcode: + /// ```asm + /// jmp [rip+00h] + /// 0xDEADBEEF + /// ``` + /// + /// Or in a different format: + /// + /// ```asm + /// jmp qword ptr cs:jmp_add + /// jmp_addr: dq 0xDEADBEEF + /// ``` + /// + /// The core premise behind it is, that we jump to the address that is right after the current + /// instruction. + /// + /// ## Why use this instead of `mov rax, jmp rax`? + /// + /// This shellcode has one very important feature: **It doesn't require any registers to store the + /// jmp address**. And because of that, we don't have to fear overwriting some register values. + /// + fn jmp_shellcode(target_address: u64) -> [u8; 14] { log::info!( "Creating the jmp shellcode for address: {:#x}", target_address ); - // Create the instructions: + // Create the shellcode. See function documentation for more information. // - // nop - // mov rax, target_address - // jmp rax - // - let instructions = [ - Instruction::with(Code::Nopq), - Instruction::with2(Code::Mov_r64_imm64, Register::RAX, target_address).unwrap(), - Instruction::with1(Code::Jmp_rm64, Register::RAX).unwrap(), + let mut shellcode = [ + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, ]; - - // Encode the instructions. It's a absolute jump, so we don't have to care about the rip. - // - let block = InstructionBlock::new(&instructions, 0x0); - let shellcode = BlockEncoder::encode(64, block, BlockEncoderOptions::NONE) - .map(|b| b.code_buffer) - .unwrap(); + unsafe { + (shellcode.as_mut_ptr().add(6) as *mut u64).write_volatile(target_address as u64) + }; shellcode } - fn trampoline_shellcode(function_address: u64) -> Result, InlineHookError> { + /// Creates a trampoline shellcode that jumps to the original function. + /// + /// ## Parameters + /// + /// - `original_address`: The address of the original function. We need this so that we can relocate potential jumps that have been overwritten by the hook. + /// - `function_address`: The address of same function in the copied page. + /// + /// TODO: Replace one of these parameters + fn trampoline_shellcode( + original_address: u64, + function_address: u64, + ) -> Result, InlineHookError> { log::info!( "Creating the trampoline shellcode for function: {:#x}", function_address @@ -128,7 +165,7 @@ impl InlineHook { let bytes = unsafe { core::slice::from_raw_parts(function_address as *mut u8, JMP_SHELLCODE_LEN * 2) }; - let mut decoder = Decoder::with_ip(64, &bytes, function_address, DecoderOptions::NONE); + let mut decoder = Decoder::with_ip(64, &bytes, original_address, DecoderOptions::NONE); let mut total_bytes = 0; let mut trampoline = Vec::new(); @@ -139,28 +176,45 @@ impl InlineHook { // Create the new trampoline instruction // - let instr = match instr.flow_control() { - FlowControl::Next => instr, + match instr.flow_control() { + FlowControl::Next | FlowControl::Return => { + total_bytes += instr.len(); + trampoline.push(instr); + } FlowControl::Call => { - // TODO: Relocate call - instr + if instr.is_call_near() { + total_bytes += instr.len(); + + let branch_target = instr.near_branch_target(); + + // TODO: Just relocate the relative jump + + // mov rax, branch_target + // jmp rax + // + // let mov_rax = + // Instruction::with2(Code::Mov_r64_imm64, Register::RAX, branch_target) + // .unwrap(); + // let jmp_rax = Instruction::with1(Code::Jmp_rm64, Register::RAX).unwrap(); + // + // trampoline.push(mov_rax); + // trampoline.push(jmp_rax); + trampoline.push( + Instruction::with_branch(Code::Jmp_rel32_64, branch_target).unwrap(), + ); + } else { + log::warn!("Found call far"); + } } - FlowControl::Return => instr, FlowControl::IndirectBranch | FlowControl::ConditionalBranch | FlowControl::UnconditionalBranch | FlowControl::IndirectCall | FlowControl::Interrupt | FlowControl::XbeginXabortXend - | FlowControl::Exception => { - log::warn!("Using unsupported instruction: {:?}", instr); - instr - } + | FlowControl::Exception => log::warn!("Unsupported instruction"), }; - total_bytes += instr.len(); - trampoline.push(instr); - if total_bytes >= JMP_SHELLCODE_LEN { break; } @@ -180,6 +234,10 @@ impl InlineHook { let last_instr = trampoline.last().unwrap(); let jmp_back_address = last_instr.next_ip(); if last_instr.flow_control() != FlowControl::Return { + log::info!( + "Creating jmp back to original instructions at {:#x}", + jmp_back_address + ); trampoline .push(Instruction::with_branch(Code::Jmp_rel32_64, jmp_back_address).unwrap()); } @@ -190,17 +248,21 @@ impl InlineHook { .ok_or_else(|| InlineHookError::AllocationFailed)?; log::info!("Allocated trampoline memory at {:p}", memory.as_ptr()); + log::info!( + "Offset between original and trampoline: {:#x}", + function_address.abs_diff(memory.as_ptr() as u64) + ); let block = InstructionBlock::new(&trampoline, memory.as_ptr() as _); let encoded = BlockEncoder::encode(decoder.bitness(), block, BlockEncoderOptions::NONE) .map(|b| b.code_buffer) .map_err(|_| InlineHookError::EncodingFailed)?; + log::info!("Encoded trampoline: {:x?}", encoded); + // Copy the encoded bytes and return the allocated memory. // - unsafe { RtlCopyMemory(memory.as_ptr() as _, encoded.as_ptr() as _, encoded.len()) }; - - dbg_break!(); + unsafe { core::ptr::copy_nonoverlapping(encoded.as_ptr(), memory.as_ptr(), encoded.len()) }; Ok(memory) }