finish device io call to scan

This commit is contained in:
nganhkhoa 2020-02-27 03:27:54 +07:00
parent 0ca87a871c
commit d08852af55
5 changed files with 402 additions and 19 deletions

View File

@ -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"] }

246
src/driver_state.rs Normal file
View File

@ -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<u64> = match pool_size {
0xf00 => vec![0x40],
0xd80 => vec![0x40, 0x70, 0x80],
0xe00 => vec![0x60, 0x70, 0x80, 0x90],
_ => vec![]
};
// let eprocess_offset: Vec<u64> = 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<T>(&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<T>(&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]);
}
}

95
src/ioctl_protocol.rs Normal file
View File

@ -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,
}

View File

@ -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());
}

View File

@ -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<T, E>(&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
}
}