diff --git a/Cargo.toml b/Cargo.toml index 75453c8..6edb2a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,5 +10,5 @@ edition = "2018" hex = "0.4.2" pdb = "0.5.0" widestring = "0.4.0" -winapi = { version = "0.3.8", features = ["libloaderapi", "processthreadsapi", "winbase", "securitybaseapi", "handleapi", "winnt", "winreg", "fileapi", "ioapiset"] } +winapi = { version = "0.3.8", features = ["libloaderapi", "processthreadsapi", "winbase", "securitybaseapi", "handleapi", "winnt", "winreg", "fileapi", "ioapiset", "winioctl", "errhandlingapi"] } reqwest = { version = "0.10.1", features = ["blocking"] } diff --git a/src/driver_state.rs b/src/driver_state.rs new file mode 100644 index 0000000..68caec0 --- /dev/null +++ b/src/driver_state.rs @@ -0,0 +1,246 @@ +use std::ffi::c_void; +use std::mem::{size_of_val}; + +use winapi::shared::ntdef::{NTSTATUS}; +use winapi::shared::minwindef::{DWORD}; +use winapi::um::winioctl::{ + CTL_CODE, FILE_ANY_ACCESS, + METHOD_IN_DIRECT, METHOD_OUT_DIRECT, METHOD_BUFFERED, METHOD_NEITHER +}; + +use crate::pdb_store::{PdbStore}; +use crate::windows::{WindowsFFI, WindowsVersion}; +use crate::ioctl_protocol::{ + InputData, OffsetData, DerefAddr, ScanRange, + OutputData, Nothing +}; + +const SIOCTL_TYPE: DWORD = 40000; + +#[allow(dead_code)] +#[derive(Debug)] +pub enum DriverAction { + SetupOffset, + GetKernelBase, + ScanPsActiveHead, + ScanPool, + ScanPoolRemote, + DereferenceAddress +} + +impl DriverAction { + pub fn get_code(&self) -> DWORD { + match self { + DriverAction::SetupOffset => CTL_CODE(SIOCTL_TYPE, 0x900, METHOD_IN_DIRECT, FILE_ANY_ACCESS), + DriverAction::GetKernelBase => CTL_CODE(SIOCTL_TYPE, 0x901, METHOD_OUT_DIRECT, FILE_ANY_ACCESS), + DriverAction::ScanPsActiveHead => CTL_CODE(SIOCTL_TYPE, 0x902, METHOD_NEITHER, FILE_ANY_ACCESS), + DriverAction::ScanPool => CTL_CODE(SIOCTL_TYPE, 0x903, METHOD_IN_DIRECT, FILE_ANY_ACCESS), + DriverAction::ScanPoolRemote => CTL_CODE(SIOCTL_TYPE, 0x904, METHOD_IN_DIRECT, FILE_ANY_ACCESS), + DriverAction::DereferenceAddress => CTL_CODE(SIOCTL_TYPE, 0xA00, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + } + } +} + +#[allow(dead_code)] +pub struct DriverState { + pdb_store: PdbStore, + windows_ffi: WindowsFFI, + ntosbase: u64, + nonpaged_range: [u64; 2], +} + +impl DriverState { + pub fn new(pdb_store: PdbStore, windows_ffi: WindowsFFI) -> Self { + pdb_store.print_default_information(); + windows_ffi.print_version(); + Self { + pdb_store, + windows_ffi, + ntosbase: 0u64, + nonpaged_range: [0, 0] + } + } + + pub fn startup(&mut self) -> NTSTATUS { + self.windows_ffi.load_driver() + } + + pub fn shutdown(&self) -> NTSTATUS { + self.windows_ffi.unload_driver() + } + + // TODO: Function output and input data???? + pub fn interact(&mut self, action: DriverAction) { + let code = action.get_code(); + println!("Driver action: {:?}", action); + match action { + DriverAction::SetupOffset => { + let mut input = InputData { + offset_value: OffsetData::new(&self.pdb_store, self.windows_ffi.short_version) + }; + self.windows_ffi.device_io(code, &mut input, &mut Nothing); + }, + DriverAction::GetKernelBase => { + self.windows_ffi.device_io(code, &mut Nothing, &mut self.ntosbase); + println!("ntosbase: 0x{:x}", self.ntosbase); + }, + DriverAction::ScanPsActiveHead => { + self.interact(DriverAction::GetKernelBase); + let ps_active_head = self.ntosbase + self.pdb_store.get_offset("PsActiveProcessHead").unwrap_or(0u64); + let flink_offset = self.pdb_store.get_offset("_LIST_ENTRY.Flink").unwrap_or(0u64); + let eprocess_link_offset = self.pdb_store.get_offset("_EPROCESS.ActiveProcessLinks").unwrap_or(0u64); + let eprocess_name_offset = self.pdb_store.get_offset("_EPROCESS.ImageFileName").unwrap_or(0u64); + + let mut ptr = ps_active_head; + self.deref_addr(ptr + flink_offset, &mut ptr); + + println!("========================"); + println!("Scan PsActiveProcessHead"); + while ptr != ps_active_head { + let mut image_name = [0u8; 15]; + let eprocess = ptr - eprocess_link_offset; + self.deref_addr(eprocess + eprocess_name_offset, &mut image_name); + match std::str::from_utf8(&image_name) { + Ok(n) => { + // TODO: save to somewhere + println!("_EPROCESS at 0x{:x} of {}", eprocess, n); + }, + _ => {} + }; + self.deref_addr(ptr + flink_offset, &mut ptr); + } + println!("========================"); + + // test call to check result + self.windows_ffi.device_io(code, &mut Nothing, &mut Nothing); + }, + DriverAction::ScanPool => { + self.get_nonpaged_range(); + let mut input = InputData { + scan_range: ScanRange::new(&self.nonpaged_range) + }; + self.windows_ffi.device_io(code, &mut input, &mut Nothing); + }, + DriverAction::ScanPoolRemote => { + self.get_nonpaged_range(); + let start_address = self.nonpaged_range[0]; + let end_address = self.nonpaged_range[1]; + + let pool_header_size = self.pdb_store.get_offset("_POOL_HEADER.struct_size").unwrap_or(0u64); + let eprocess_name_offset = self.pdb_store.get_offset("_EPROCESS.ImageFileName").unwrap_or(0u64); + + let mut ptr = start_address; + while ptr < end_address { + let mut input = InputData { + scan_range: ScanRange::new(&[ptr, end_address]) + }; + self.windows_ffi.device_io(code, &mut input, &mut ptr); + if ptr >= end_address { + break; + } + let pool_addr = ptr; + ptr += pool_header_size; + + let mut pool = vec![0u8; pool_header_size as usize]; + self.deref_addr_ptr(pool_addr, pool.as_mut_ptr(), pool_header_size); + // TODO: Use pdb to parse, bit mangling and stuff + println!("========================="); + println!("Pool at 0x{:x}", pool_addr); + println!("Previos Size: 0x{:x}", pool[0]); + println!("Pool index : {:x}", pool[1]); + println!("Block size : 0x{:x}", (pool[2] as u64) * 16u64); // CHUNK_SIZE = 16 + println!("Pool type : {}", pool[3]); + println!("Pool tag : {}", std::str::from_utf8(&pool[4..8]).unwrap()); + + let pool_size = (pool[2] as u64) * 16u64; + // dump pool here + let eprocess_offset: Vec = match pool_size { + 0xf00 => vec![0x40], + 0xd80 => vec![0x40, 0x70, 0x80], + 0xe00 => vec![0x60, 0x70, 0x80, 0x90], + _ => vec![] + }; + // let eprocess_offset: Vec = vec![0x40, 0x70, 0x80]; + let mut found_valid = false; + for &offset in &eprocess_offset { + let eprocess = pool_addr + offset; + let mut image_name = [0u8; 15]; + self.deref_addr(eprocess + eprocess_name_offset, &mut image_name); + match std::str::from_utf8(&image_name) { + Ok(n) => { + // TODO: save to somewhere + // TODO: check if a name is not valid, values outside ascii, + // remember that if a string is vaid, the rest of the name is 0 + found_valid = true; + println!("_EPROCESS at 0x{:x} of {}", eprocess, n); + }, + _ => {} + }; + } + if !found_valid { + println!("Not an eprocess maybe"); + } + } + }, + _ => {} + }; + } + + fn deref_addr(&self, addr: u64, outbuf: &mut T) { + let code = DriverAction::DereferenceAddress.get_code(); + let size: usize = size_of_val(outbuf); + let mut input = InputData { + deref_addr: DerefAddr { + addr, + size: size as u64 + } + }; + // unsafe { println!("Dereference {} bytes at 0x{:x}", input.deref_addr.size, input.deref_addr.addr) }; + self.windows_ffi.device_io(code, &mut input, outbuf); + } + + fn deref_addr_ptr(&self, addr: u64, outptr: *mut T, output_len: u64) { + let code = DriverAction::DereferenceAddress.get_code(); + let mut input = InputData { + deref_addr: DerefAddr { + addr, + size: output_len + } + }; + self.windows_ffi.device_io_raw(code, + &mut input as *mut _ as *mut c_void, size_of_val(&input) as DWORD, + outptr as *mut c_void, output_len as DWORD); + } + + #[allow(dead_code)] + fn get_nonpaged_range(&mut self) { + // TODO: Add support for other Windows version here + match self.windows_ffi.short_version { + WindowsVersion::Windows10FastRing => { + let mistate = self.ntosbase + self.pdb_store.get_offset("MiState").unwrap_or(0u64); + let system_node_ptr = self.pdb_store.addr_decompose( + mistate, "_MI_SYSTEM_INFORMATION.Hardware.SystemNodeNonPagedPool") + .unwrap_or(0u64); + let mut system_node_addr = 0u64; + self.deref_addr(system_node_ptr, &mut system_node_addr); + + let mut first_va = 0u64; + let mut last_va = 0u64; + self.deref_addr( + system_node_addr + self.pdb_store.get_offset( + "_MI_SYSTEM_NODE_NONPAGED_POOL.NonPagedPoolFirstVa").unwrap_or(0u64), + &mut first_va); + + self.deref_addr( + system_node_addr + self.pdb_store.get_offset( + "_MI_SYSTEM_NODE_NONPAGED_POOL.NonPagedPoolLastVa").unwrap_or(0u64), + &mut last_va); + + self.nonpaged_range[0] = first_va; + self.nonpaged_range[1] = last_va; + } + _ => {} + }; + println!("Nonpaged pool range: 0x{:x} - 0x{:x}", self.nonpaged_range[0], self.nonpaged_range[1]); + } +} diff --git a/src/ioctl_protocol.rs b/src/ioctl_protocol.rs new file mode 100644 index 0000000..82f5ac7 --- /dev/null +++ b/src/ioctl_protocol.rs @@ -0,0 +1,95 @@ +use crate::pdb_store::PdbStore; +use crate::windows::WindowsVersion; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct OffsetData { + eprocess_name_offset: u64, + eprocess_link_offset: u64, + list_blink_offset: u64, + process_head_offset: u64, + mistate_offset: u64, + hardware_offset: u64, + system_node_offset: u64, + first_va_offset: u64, + last_va_offset: u64, + large_page_table_offset: u64, + large_page_size_offset: u64, + pool_chunk_size: u64, +} + +// TODO: Move to WindowsScanStrategy and return the corresponding struct base on Windows version +impl OffsetData { + pub fn new(pdb_store: &PdbStore, windows_version: WindowsVersion) -> Self { + match windows_version { + WindowsVersion::Windows10FastRing => Self { + eprocess_name_offset: pdb_store.get_offset("_EPROCESS.ImageFileName").unwrap_or(0u64), + eprocess_link_offset: pdb_store.get_offset("_EPROCESS.ActiveProcessLinks").unwrap_or(0u64), + list_blink_offset: pdb_store.get_offset("_LIST_ENTRY.Blink").unwrap_or(0u64), + process_head_offset: pdb_store.get_offset("PsActiveProcessHead").unwrap_or(0u64), + mistate_offset: pdb_store.get_offset("MiState").unwrap_or(0u64), + hardware_offset: pdb_store.get_offset("_MI_SYSTEM_INFORMATION.Hardware").unwrap_or(0u64), + system_node_offset: pdb_store.get_offset("_MI_HARDWARE_STATE.SystemNodeNonPagedPool").unwrap_or(0u64), + first_va_offset: pdb_store.get_offset("_MI_SYSTEM_NODE_NONPAGED_POOL.NonPagedPoolFirstVa").unwrap_or(0u64), + last_va_offset: pdb_store.get_offset("_MI_SYSTEM_NODE_NONPAGED_POOL.NonPagedPoolLastVa").unwrap_or(0u64), + large_page_table_offset: pdb_store.get_offset("PoolBigPageTable").unwrap_or(0u64), + large_page_size_offset: pdb_store.get_offset("PoolBigPageTableSize").unwrap_or(0u64), + pool_chunk_size: pdb_store.get_offset("_POOL_HEADER.struct_size").unwrap_or(0u64), + }, + // TODO: Add other version of Windows here + _ => Self { + eprocess_name_offset: 0u64, + eprocess_link_offset: 0u64, + list_blink_offset: 0u64, + process_head_offset: 0u64, + mistate_offset: 0u64, + hardware_offset: 0u64, + system_node_offset: 0u64, + first_va_offset: 0u64, + last_va_offset: 0u64, + large_page_table_offset: 0u64, + large_page_size_offset: 0u64, + pool_chunk_size: 0u64, + } + } + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DerefAddr { + pub addr: u64, + pub size: u64 +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ScanRange { + pub start: u64, + pub end: u64 +} + +impl ScanRange { + pub fn new(arr: &[u64; 2]) -> Self { + Self { + start: arr[0], + end: arr[1] + } + } +} + +#[repr(C)] +pub union InputData { + pub offset_value: OffsetData, + pub deref_addr: DerefAddr, + pub scan_range: ScanRange, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Nothing; // for empty data + +#[repr(C)] +pub union OutputData { + pub nothing: Nothing, +} diff --git a/src/main.rs b/src/main.rs index 01f5a15..3b7b573 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,25 @@ mod pdb_store; mod windows; +mod ioctl_protocol; +mod driver_state; + +use pdb_store::parse_pdb; +use windows::WindowsFFI; +use driver_state::{DriverState, DriverAction}; fn main() { - let store = pdb_store::parse_pdb(); - store.print_default_information(); - // for windows admin require // https://github.com/nabijaczleweli/rust-embed-resource - let mut windows_ffi = windows::WindowsFFI::new(); - windows_ffi.print_version(); - println!("NtLoadDriver() -> 0x{:x}", windows_ffi.load_driver()); + let mut driver = DriverState::new(parse_pdb(), WindowsFFI::new()); - windows_ffi.device_io(0x900); + println!("NtLoadDriver() -> 0x{:x}", driver.startup()); - println!("NtUnloadDriver() -> 0x{:x}", windows_ffi.unload_driver()); + driver.interact(DriverAction::SetupOffset); + driver.interact(DriverAction::GetKernelBase); + // driver.interact(DriverAction::ScanPsActiveHead); + // driver.interact(DriverAction::ScanPool); + driver.interact(DriverAction::ScanPoolRemote); + + println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown()); } diff --git a/src/windows.rs b/src/windows.rs index 30496f6..0300b27 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -1,5 +1,5 @@ -use std::ffi::CString; -use std::mem::transmute; +use std::ffi::{c_void, CString}; +use std::mem::{transmute, size_of_val}; use std::ptr::null_mut; use widestring::U16CString; @@ -12,7 +12,8 @@ use winapi::um::winnt::{ FILE_ATTRIBUTE_NORMAL, GENERIC_READ, GENERIC_WRITE }; -use winapi::um::ioapiset::DeviceIoControl; +use winapi::um::ioapiset::{DeviceIoControl}; +use winapi::um::errhandlingapi::{GetLastError}; use winapi::um::fileapi::{CreateFileA, CREATE_ALWAYS}; use winapi::um::handleapi::{INVALID_HANDLE_VALUE, CloseHandle}; use winapi::um::libloaderapi::{LoadLibraryA, GetProcAddress}; @@ -24,7 +25,7 @@ use winapi::um::winreg::{RegCreateKeyExA, RegSetValueExA, RegCloseKey, HKEY_LOCA const STR_DRIVER_REGISTRY_PATH: &str = "\\Registry\\Machine\\System\\CurrentControlSet\\Services\\nganhkhoa"; #[allow(dead_code)] -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub enum WindowsVersion { Windows10_2015, Windows10_2016, @@ -37,6 +38,7 @@ pub enum WindowsVersion { } #[allow(dead_code)] +#[derive(Copy, Clone)] pub struct WindowsFFI { pub version_info: OSVERSIONINFOW, pub short_version: WindowsVersion, @@ -148,7 +150,7 @@ impl WindowsFFI { Self { version_info, short_version, - driver_handle: null_mut(), + driver_handle: INVALID_HANDLE_VALUE, ntdll, nt_load_driver, nt_unload_driver, @@ -157,7 +159,13 @@ impl WindowsFFI { } } + pub fn driver_loaded(self) -> bool { + self.driver_handle != INVALID_HANDLE_VALUE + } + pub fn load_driver(&mut self) -> NTSTATUS { + // TODO: Move this to new() + // If we move this function to new(), self.driver_handle will be init, and thus no mut here let str_driver_reg = U16CString::from_str(STR_DRIVER_REGISTRY_PATH).unwrap(); let mut str_driver_reg_unicode = UNICODE_STRING::default(); (self.rtl_init_unicode_str)(&mut str_driver_reg_unicode, str_driver_reg.as_ptr() as *const u16); @@ -180,7 +188,7 @@ impl WindowsFFI { status } - pub fn unload_driver(&mut self) -> NTSTATUS { + pub fn unload_driver(&self) -> NTSTATUS { let str_driver_reg = U16CString::from_str(STR_DRIVER_REGISTRY_PATH).unwrap(); let mut str_driver_reg_unicode = UNICODE_STRING::default(); (self.rtl_init_unicode_str)(&mut str_driver_reg_unicode, str_driver_reg.as_ptr()); @@ -202,10 +210,37 @@ impl WindowsFFI { ); } - #[allow(dead_code)] - pub fn device_io(&self, _code: DWORD) { + pub fn device_io(&self, code: DWORD, inbuf: &mut T, outbuf: &mut E) -> DWORD { + // input_ptr: *mut c_void, input_len: DWORD, output_ptr: *mut c_void, output_len: DWORD) { + // println!("driver loaded: {}; device_io_code: {}", self.driver_loaded(), code); + // TODO: call device_io_raw + let mut bytes_returned: DWORD = 0; unsafe { - DeviceIoControl(self.driver_handle, 0x900, null_mut(), 0, null_mut(), 0, null_mut(), null_mut()); - } + let status = DeviceIoControl(self.driver_handle, code, + inbuf as *mut _ as *mut c_void, size_of_val(inbuf) as DWORD, + outbuf as *mut _ as *mut c_void, size_of_val(outbuf) as DWORD, + &mut bytes_returned, null_mut()); + if status == 0 { + println!("device io failed: last error {}", GetLastError()); + } + }; + bytes_returned + } + + pub fn device_io_raw(&self, code: DWORD, + input_ptr: *mut c_void, input_len: DWORD, + output_ptr: *mut c_void, output_len: DWORD) -> DWORD { + // println!("driver loaded: {}; device_io_code: {}", self.driver_loaded(), code); + let mut bytes_returned: DWORD = 0; + unsafe { + let status = DeviceIoControl(self.driver_handle, code, + input_ptr, input_len, + output_ptr, output_len, + &mut bytes_returned, null_mut()); + if status == 0 { + println!("device io failed: last error {}", GetLastError()); + } + }; + bytes_returned } }