diff --git a/src/address.rs b/src/address.rs new file mode 100644 index 0000000..205b3d3 --- /dev/null +++ b/src/address.rs @@ -0,0 +1,177 @@ +use std::rc::Rc; +use std::ops::{Add, AddAssign, Sub, SubAssign}; +use std::cmp::Ordering; +use std::fmt; + +// pub struct Object { +// name: String, +// address: Address +// } +// +// impl Object { +// pub fn get(&self, resolver: &F) -> u64 +// where F: Fn(u64) -> u64 { +// // this function returns address of Object +// self.address.get(resolver) +// } +// } + +pub struct Address { + base: u64, + pointer: Option>, + offset: u64, + // TODO: resolver + // It would be nice to have an address resolver + // Then implement Deref trait to call get() + // resolver uses DriverState address decompose + // lifetime issue occur +} + +impl Address { + pub fn from_base(base: u64) -> Self { + Address { + base: base, + pointer: None, + offset: 0, + } + } + pub fn from_ptr(pointer: Address) -> Self { + Address { + base: 0, + pointer: Some(Rc::new(pointer)), + offset: 0, + } + } + fn deref(&self, resolver: &F) -> Address + where F: Fn(u64) -> u64 { + match &self.pointer { + Some(p) => { + let addr = p.deref(resolver); + // println!("deref: {} -> {}; resolve: 0x{:x}", self, addr, addr.base + addr.offset); + let base = + if addr.base != 0 { + resolver(addr.base + addr.offset) + } else { + 0 + }; + Address { + base: base, + pointer: None, + offset: self.offset, + } + }, + None => { + Address { + base: self.base, + pointer: None, + offset: self.offset, + } + } + } + } + pub fn get(&self, resolver: &F) -> u64 + where F: Fn(u64) -> u64 { + if self.pointer.is_some() { + self.deref(resolver).get(resolver) + } + else if self.base == 0 { + 0 + } + else { + self.base + self.offset + } + } + pub fn address(&self) -> u64 { + self.base + self.offset + } + // pub fn to(&self, name: &str) -> Object { + // Object { + // name: name.to_string(), + // address: self.clone() + // } + // } +} + +impl Add for Address { + type Output = Self; + fn add(self, other: u64) -> Self { + Self { + base: self.base, + pointer: self.pointer.map(|p| Rc::clone(&p)), + offset: self.offset + other, + } + } +} + +impl AddAssign for Address { + fn add_assign(&mut self, other: u64) { + *self = Self { + base: self.base, + pointer: self.pointer.clone(), + offset: self.offset + other, + } + } +} + +impl Sub for Address { + type Output = Self; + fn sub(self, other: u64) -> Self { + Self { + base: self.base, + pointer: self.pointer.map(|p| Rc::clone(&p)), + offset: self.offset - other, + } + } +} + +impl SubAssign for Address { + fn sub_assign(&mut self, other: u64) { + *self = Self { + base: self.base, + pointer: self.pointer.clone(), + offset: self.offset - other, + } + } +} + +impl PartialEq for Address { + fn eq(&self, other: &Self) -> bool { + self.pointer.is_none() && other.pointer.is_none() + && self.base == other.base + && self.offset == other.offset + } +} + +impl PartialOrd for Address { + fn partial_cmp(&self, other: &Address) -> Option { + if self.pointer.is_some() || other.pointer.is_some() { + None + } + else { + let this = self.base + self.offset; + let that = other.base + other.offset; + Some(this.cmp(&that)) + } + } +} + +impl fmt::Display for Address { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(p) = &self.pointer { + write!(f, "*({}) + 0x{:x}", *p, self.offset) + } + else { + write!(f, "0x{:x} + 0x{:x}", self.base, self.offset) + } + } +} + +impl Clone for Address { + fn clone(&self) -> Self { + Address { + base: self.base, + pointer: self.pointer.clone(), + offset: self.offset + } + } +} diff --git a/src/bin/eprocess_scan.rs b/src/bin/eprocess_scan.rs index 93fb822..469bbc8 100644 --- a/src/bin/eprocess_scan.rs +++ b/src/bin/eprocess_scan.rs @@ -5,7 +5,8 @@ use chrono::{DateTime}; use std::time::{UNIX_EPOCH, Duration}; use lpus::{ - driver_state::{DriverState /* , EprocessPoolChunk */} + driver_state::{DriverState /* , EprocessPoolChunk */}, + address::Address }; #[allow(dead_code)] @@ -28,28 +29,17 @@ fn main() -> Result<(), Box> { // let eprocess_scan_head = driver.scan_active_head(ntosbase)?; // let mut eprocess_list: Vec = Vec::new(); - driver.scan_pool(b"Proc", |pool_addr, header, data_addr| { + driver.scan_pool(b"Proc", "_EPROCESS", |pool_addr, header, data_addr| { let chunk_size = (header[2] as u64) * 16u64; let eprocess_size = driver.pdb_store.get_offset_r("_EPROCESS.struct_size")?; - let eprocess_name_offset = driver.pdb_store.get_offset_r("_EPROCESS.ImageFileName")?; - let eprocess_create_time_offset = driver.pdb_store.get_offset_r("_EPROCESS.CreateTime")?; - let eprocess_image_file_offset = driver.pdb_store.get_offset_r("_EPROCESS.ImageFilePointer")?; - let eprocess_pid_offset = driver.pdb_store.get_offset_r("_EPROCESS.UniqueProcessId")?; - let eprocess_ppid_offset = driver.pdb_store.get_offset_r("_EPROCESS.InheritedFromUniqueProcessId")?; - // let eprocess_exit_time_offset = driver.pdb_store.get_offset_r("_EPROCESS.ExitTime")?; - let fob_filename_offset = driver.pdb_store.get_offset_r("_FILE_OBJECT.FileName")?; - let eprocess_valid_start = data_addr; - let eprocess_valid_end = (pool_addr + chunk_size) - eprocess_size; - let mut try_eprocess_ptr = eprocess_valid_start; + let eprocess_valid_start = &data_addr; + let eprocess_valid_end = Address::from_base((pool_addr.address() + chunk_size) - eprocess_size); + let mut try_eprocess_ptr = Address::from_base(eprocess_valid_start.address()); - let mut create_time = 0u64; - // let mut exit_time = 0u64; while try_eprocess_ptr <= eprocess_valid_end { - driver.deref_addr(try_eprocess_ptr + eprocess_create_time_offset, &mut create_time); - // driver.deref_addr(try_eprocess_ptr + eprocess_exit_time_offset, &mut exit_time); - // using heuristics to eliminate false positive + let create_time: u64 = driver.decompose(&try_eprocess_ptr, "_EPROCESS.CreateTime")?; if driver.windows_ffi.valid_process_time(create_time) { break; } @@ -59,33 +49,27 @@ fn main() -> Result<(), Box> { return Ok(false); } - let eprocess_ptr = try_eprocess_ptr; + let eprocess_ptr = &try_eprocess_ptr; - let mut image_name = [0u8; 15]; - let mut image_file_ptr = 0u64; - let mut ppid = 0u64; - let mut pid = 0u64; + let pid: u64 = driver.decompose(eprocess_ptr, "_EPROCESS.UniqueProcessId")?; + let ppid: u64 = driver.decompose(eprocess_ptr, "_EPROCESS.InheritedFromUniqueProcessId")?; + let image_name: Vec = driver.decompose_array(eprocess_ptr, "_EPROCESS.ImageFileName", 15)?; + let unicode_str_ptr = driver.address_of(eprocess_ptr, "_EPROCESS.ImageFilePointer.FileName")?; - driver.deref_addr(eprocess_ptr + eprocess_ppid_offset, &mut ppid); - driver.deref_addr(eprocess_ptr + eprocess_pid_offset, &mut pid); - - driver.deref_addr(eprocess_ptr + eprocess_name_offset, &mut image_name); - driver.deref_addr(eprocess_ptr + eprocess_image_file_offset, &mut image_file_ptr); - - let binary_path = - if image_file_ptr != 0 { - driver.get_unicode_string(image_file_ptr + fob_filename_offset, true)? - } else { - "".to_string() - }; let eprocess_name = if let Ok(name) = from_utf8(&image_name) { name.to_string().trim_end_matches(char::from(0)).to_string() } else { "".to_string() }; + let binary_path = + if unicode_str_ptr != 0 { + driver.get_unicode_string(unicode_str_ptr, true)? + } else { + "".to_string() + }; - println!("pool: 0x{:x} | eprocess: 0x{:x} | pid: {} | ppid: {} | name: {} | path: {}", + println!("pool: {} | eprocess: {} | pid: {} | ppid: {} | name: {} | path: {}", pool_addr, eprocess_ptr, pid, ppid, eprocess_name, binary_path); // eprocess_list.push(EprocessPoolChunk { // pool_addr, diff --git a/src/bin/ethread_scan.rs b/src/bin/ethread_scan.rs index e662598..c4859a7 100644 --- a/src/bin/ethread_scan.rs +++ b/src/bin/ethread_scan.rs @@ -1,11 +1,10 @@ use std::error::Error; -use std::str::{from_utf8}; use chrono::Utc; use chrono::{DateTime}; use std::time::{UNIX_EPOCH, Duration}; use lpus::{ - driver_state::{DriverState /* , EprocessPoolChunk */} + driver_state::{DriverState} }; #[allow(dead_code)] @@ -26,26 +25,16 @@ fn main() -> Result<(), Box> { let mut driver = DriverState::new(); println!("NtLoadDriver() -> 0x{:x}", driver.startup()); - // let ethread_scan_head = driver.scan_active_head(ntosbase)?; - // let mut ethread_list: Vec = Vec::new(); - driver.scan_pool(b"Thre", |pool_addr, header, data_addr| { + driver.scan_pool(b"Thre", "_ETHREAD", |pool_addr, header, data_addr| { let chunk_size = (header[2] as u64) * 16u64; let ethread_size = driver.pdb_store.get_offset_r("_ETHREAD.struct_size")?; - let ethread_create_time_offset = driver.pdb_store.get_offset_r("_ETHREAD.CreateTime")?; - let ethread_name_offset = driver.pdb_store.get_offset_r("_ETHREAD.ThreadName")?; - // let ethread_exit_time_offset = driver.pdb_store.get_offset_r("_ETHREAD.ExitTime")?; + let ethread_valid_start = &data_addr; + let ethread_valid_end = (pool_addr.clone() + chunk_size) - ethread_size; + let mut try_ethread_ptr = ethread_valid_start.clone(); - let ethread_valid_start = data_addr; - let ethread_valid_end = (pool_addr + chunk_size) - ethread_size; - let mut try_ethread_ptr = ethread_valid_start; - - let mut create_time = 0u64; - // let mut exit_time = 0u64; while try_ethread_ptr <= ethread_valid_end { - driver.deref_addr(try_ethread_ptr + ethread_create_time_offset, &mut create_time); - // driver.deref_addr(try_ethread_ptr + ethread_exit_time_offset, &mut exit_time); - // using heuristics to eliminate false positive + let create_time: u64 = driver.decompose(&try_ethread_ptr, "_ETHREAD.CreateTime")?; if driver.windows_ffi.valid_process_time(create_time) { break; } @@ -55,22 +44,82 @@ fn main() -> Result<(), Box> { return Ok(false); } - let mut thread_name_ptr = 0u64; - driver.deref_addr(try_ethread_ptr + ethread_name_offset, &mut thread_name_ptr); - let thread_name = if thread_name_ptr != 0 { driver.get_unicode_string(thread_name_ptr, true)? } - else { "".to_string() }; + let ethread_ptr = &try_ethread_ptr; - println!("pool: 0x{:x} | ethread: 0x{:x} | {}", pool_addr, try_ethread_ptr, thread_name); + let pid: u64 = driver.decompose(ethread_ptr, "_ETHREAD.Cid.UniqueProcess")?; + let tid: u64 = driver.decompose(ethread_ptr, "_ETHREAD.Cid.UniqueThread")?; + let unicode_str_ptr: u64 = driver.address_of(ethread_ptr, "_ETHREAD.ThreadName")?; + + let thread_name = + if unicode_str_ptr == 0 { + "".to_string() + } + else if let Ok(name) = driver.get_unicode_string(unicode_str_ptr, true) { + name + } + else { + "".to_string() + }; + + println!("pool: {} | ethread: {} | pid: {} | tid: {} | {}", + pool_addr, ethread_ptr, pid, tid, thread_name); Ok(true) - // ethread_list.push(EprocessPoolChunk { - // pool_addr, - // ethread_addr: try_ethread_ptr, - // ethread_name: ethread_name, - // create_time: to_epoch(create_time), - // exit_time: to_epoch(exit_time) - // }); })?; + println!("Scan _KMUTANT"); + + // scan for mutants, also reveals Threads + // driver.scan_pool(b"Muta", "_KMUTANT", |pool_addr, header, data_addr| { + // let chunk_size = (header[2] as u64) * 16u64; + // + // println!("Mutant pool size {}", chunk_size); + // return Ok(false); + // + // let kmutant_size = driver.pdb_store.get_offset_r("_KMUTANT.struct_size")?; + // let kmutant_ownerthread_offset = driver.pdb_store.get_offset_r("_KMUTANT.OwnerThread")?; + // let ethread_name_offset = driver.pdb_store.get_offset_r("_ETHREAD.ThreadName")?; + // + // let kmutant_valid_start = data_addr; + // let kmutant_valid_end = (pool_addr + chunk_size) - kmutant_size; + // let mut try_kmutant_ptr = kmutant_valid_start; + // + // while try_kmutant_ptr <= kmutant_valid_end { + // // TODO: Create check + // try_kmutant_ptr += 0x4; // search exhaustively + // } + // if try_kmutant_ptr > kmutant_valid_end { + // return Ok(false); + // } + // + // let kmutant_ptr = try_kmutant_ptr; + // let mut ethread_ptr = 0u64; + // let mut thread_name_ptr = 0u64; + // let mut pid = 0u64; + // let mut tid = 0u64; + // + // driver.deref_addr(kmutant_ptr + kmutant_ownerthread_offset, &mut ethread_ptr); + // let pid_ptr = driver.pdb_store.addr_decompose(ethread_ptr, "_ETHREAD.Cid.UniqueProcess")?; + // let tid_ptr = driver.pdb_store.addr_decompose(ethread_ptr, "_ETHREAD.Cid.UniqueThread")?; + // + // driver.deref_addr(pid_ptr, &mut pid); + // driver.deref_addr(tid_ptr, &mut tid); + // driver.deref_addr(ethread_ptr + ethread_name_offset, &mut thread_name_ptr); + // + // let thread_name = + // if thread_name_ptr != 0 { driver.get_unicode_string(thread_name_ptr, true)? } + // else { "".to_string() }; + // + // println!("pool: 0x{:x} | kmutant: 0x{:x} | pid: {} | tid: {} | {}", + // pool_addr, kmutant_ptr, pid, tid, thread_name); + // Ok(true) + // // kmutant_list.push(EprocessPoolChunk { + // // pool_addr, + // // kmutant_addr: try_kmutant_ptr, + // // kmutant_name: kmutant_name, + // // create_time: to_epoch(create_time), + // // exit_time: to_epoch(exit_time) + // // }); + // })?; println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown()); Ok(()) } diff --git a/src/bin/file_object_scan.rs b/src/bin/file_object_scan.rs index 11519e4..8ed340d 100644 --- a/src/bin/file_object_scan.rs +++ b/src/bin/file_object_scan.rs @@ -8,40 +8,34 @@ fn main() -> Result<(), Box> { let mut driver = DriverState::new(); println!("NtLoadDriver() -> 0x{:x}", driver.startup()); - driver.scan_pool(b"File", |pool_addr, header, data_addr| { + driver.scan_pool(b"File", "_FILE_OBJECT", |pool_addr, header, data_addr| { let chunk_size = (header[2] as u64) * 16u64; let fob_size = driver.pdb_store.get_offset_r("_FILE_OBJECT.struct_size")?; - let fob_size_offset = driver.pdb_store.get_offset_r("_FILE_OBJECT.Size")?; - let fob_read_access_offset = driver.pdb_store.get_offset_r("_FILE_OBJECT.ReadAccess")?; - let fob_filename_offset = driver.pdb_store.get_offset_r("_FILE_OBJECT.FileName")?; - - let valid_end = (pool_addr + chunk_size) - fob_size; + let valid_end = (pool_addr.clone() + chunk_size) - fob_size; let mut try_ptr = data_addr; - let mut ftype = 0u16; - let mut size = 0u16; while try_ptr <= valid_end { - driver.deref_addr(try_ptr, &mut ftype); - driver.deref_addr(try_ptr + fob_size_offset, &mut size); + let ftype: u16 = driver.decompose(&try_ptr, "_FILE_OBJECT.Type")?; + let size: u16 = driver.decompose(&try_ptr, "_FILE_OBJECT.Size")?; if (size as u64) == fob_size && ftype == 5u16 { break; } try_ptr += 0x4; // search exhaustively } if try_ptr > valid_end { - println!("pool: 0x{:x} cannot detect file object", pool_addr); return Ok(false); } - let fob_addr = try_ptr; - let mut read_ok = 0u8; - driver.deref_addr(fob_addr + fob_read_access_offset, &mut read_ok); - println!("pool: 0x{:x} | file object: 0x{:x} | offsetby: 0x{:x}", pool_addr, fob_addr, fob_addr - pool_addr); + let fob_addr = &try_ptr; + let read_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.ReadAccess")?; + let unicode_str_ptr = driver.address_of(fob_addr, "_FILE_OBJECT.FileName")?; + + println!("pool: {} | file object: {}", pool_addr, fob_addr); if read_ok == 0 { println!(" [NOT READABLE]"); } - else if let Ok(filename) = driver.get_unicode_string(fob_addr + fob_filename_offset, true) { + else if let Ok(filename) = driver.get_unicode_string(unicode_str_ptr, true) { println!(" {}", filename); } else { diff --git a/src/bin/print_pdb.rs b/src/bin/print_pdb.rs index ba4ad91..c282ab3 100644 --- a/src/bin/print_pdb.rs +++ b/src/bin/print_pdb.rs @@ -1,7 +1,8 @@ use std::error::Error; use lpus::{ - driver_state::{DriverState} + driver_state::{DriverState}, + address::Address }; fn main() -> Result<(), Box> { diff --git a/src/driver_state.rs b/src/driver_state.rs index 0b16d97..996e824 100644 --- a/src/driver_state.rs +++ b/src/driver_state.rs @@ -1,3 +1,5 @@ +use std::default::Default; +use std::clone::Clone; use std::error::Error; // use std::io::{Error, ErrorKind}; use std::ffi::c_void; @@ -10,6 +12,7 @@ use winapi::um::winioctl::{ METHOD_IN_DIRECT, METHOD_OUT_DIRECT, /* METHOD_BUFFERED, */ METHOD_NEITHER }; +use crate::address::Address; use crate::pdb_store::{PdbStore, parse_pdb}; use crate::windows::{WindowsFFI, WindowsVersion}; use crate::ioctl_protocol::{ @@ -100,79 +103,84 @@ impl DriverState { self.windows_ffi.unload_driver() } - pub fn get_kernel_base(&self) -> u64 { + pub fn get_kernel_base(&self) -> Address { let mut ntosbase = 0u64; self.windows_ffi.device_io(DriverAction::GetKernelBase.get_code(), &mut Nothing, &mut ntosbase); - // println!("ntosbase: 0x{:x}", self.ntosbase); - ntosbase + Address::from_base(ntosbase) } - pub fn scan_active_head(&self) -> BoxResult> { - let ntosbase = self.get_kernel_base(); - let ps_active_head = ntosbase + self.pdb_store.get_offset_r("PsActiveProcessHead")?; - let flink_offset = self.pdb_store.get_offset_r("_LIST_ENTRY.Flink")?; - let eprocess_link_offset = self.pdb_store.get_offset_r("_EPROCESS.ActiveProcessLinks")?; - let eprocess_name_offset = self.pdb_store.get_offset_r("_EPROCESS.ImageFileName")?; + // pub fn scan_active_head(&self) -> BoxResult> { + // let ntosbase = self.get_kernel_base(); + // let ps_active_head = ntosbase + self.pdb_store.get_offset_r("PsActiveProcessHead")?; + // let flink_offset = self.pdb_store.get_offset_r("_LIST_ENTRY.Flink")?; + // let eprocess_link_offset = self.pdb_store.get_offset_r("_EPROCESS.ActiveProcessLinks")?; + // let eprocess_name_offset = self.pdb_store.get_offset_r("_EPROCESS.ImageFileName")?; + // + // let mut ptr = ps_active_head; + // self.deref_addr((ptr + flink_offset).get(), &mut ptr); + // + // let mut result: Vec = Vec::new(); + // 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) => { + // result.push(EprocessPoolChunk { + // pool_addr: 0, + // eprocess_addr: eprocess, + // eprocess_name: n.to_string() + // .trim_end_matches(char::from(0)) + // .to_string(), + // create_time: 0, + // exit_time: 0 + // + // }); + // }, + // _ => {} + // }; + // self.deref_addr(ptr + flink_offset, &mut ptr); + // } + // Ok(result) + // } - let mut ptr = ps_active_head; - self.deref_addr(ptr + flink_offset, &mut ptr); - - let mut result: Vec = Vec::new(); - 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) => { - result.push(EprocessPoolChunk { - pool_addr: 0, - eprocess_addr: eprocess, - eprocess_name: n.to_string() - .trim_end_matches(char::from(0)) - .to_string(), - create_time: 0, - exit_time: 0 - - }); - }, - _ => {} - }; - self.deref_addr(ptr + flink_offset, &mut ptr); - } - Ok(result) - } - - pub fn scan_pool(&self, tag: &[u8; 4], mut handler: F) -> BoxResult - where F: FnMut(u64, &[u8], u64) -> BoxResult + pub fn scan_pool(&self, tag: &[u8; 4], expected_struct: &str, mut handler: F) -> BoxResult + where F: FnMut(Address, &[u8], Address) -> BoxResult // F(Pool Address, Pool Header Data, Pool Data Address) // TODO: Pool Header as a real struct { - let ntosbase = self.get_kernel_base(); + // TODO: make generator, in hold: https://github.com/rust-lang/rust/issues/43122 + // Making this function a generator will turn the call to a for loop + // https://docs.rs/gen-iter/0.2.0/gen_iter/ + // >> More flexibility in code let pool_header_size = self.pdb_store.get_offset_r("_POOL_HEADER.struct_size")?; - let minimum_block_size = self.get_minimum_block_size(tag)?; + let minimum_block_size = self.pdb_store.get_offset_r(&format!("{}.struct_size", expected_struct))? + + pool_header_size; let code = DriverAction::ScanPoolRemote.get_code(); - let range = self.get_nonpaged_range(ntosbase)?; - let start_address = range[0]; - let end_address = range[1]; + let ntosbase = self.get_kernel_base(); + let [start_address, end_address] = self.get_nonpaged_range(&ntosbase)?; + + println!("kernel base: {}; non-paged pool (start, end): ({}, {})", ntosbase, start_address, end_address); + let mut ptr = start_address; while ptr < end_address { + let mut next_found = 0u64; let mut input = InputData { - scan_range: ScanPoolData::new(&[ptr, end_address], tag) + scan_range: ScanPoolData::new(&[ptr.address(), end_address.address()], tag) }; - self.windows_ffi.device_io(code, &mut input, &mut ptr); - // println!("found: 0x{:x}", ptr); + self.windows_ffi.device_io(code, &mut input, &mut next_found); + ptr = Address::from_base(next_found); if ptr >= end_address { break; } - let pool_addr = ptr; - let mut header = vec![0u8; pool_header_size as usize]; - self.deref_addr_ptr(pool_addr, header.as_mut_ptr(), pool_header_size); + let pool_addr = Address::from_base(ptr.address()); + let header: Vec = self.deref_array(&pool_addr, pool_header_size); let chunk_size = (header[2] as u64) * 16u64; - if pool_addr + chunk_size > end_address { - // the chunk found is not a valid chunk for sure + if pool_addr.address() + chunk_size > end_address.address() { + // the chunk surpasses the non page pool range break; } @@ -182,10 +190,11 @@ impl DriverState { continue; } - let success = handler(pool_addr, &header, pool_addr + pool_header_size)?; + let data_addr = Address::from_base(pool_addr.address() + pool_header_size); + + let success = handler(pool_addr, &header, data_addr)?; if success { - ptr += chunk_size; /* pass this chunk */ - // ptr += 0x4; + ptr += chunk_size; /* skip this chunk */ } else { ptr += 0x4; /* search next */ @@ -194,32 +203,42 @@ impl DriverState { Ok(true) } - fn get_minimum_block_size(&self, tag: &[u8; 4]) -> BoxResult { - // Proc -> _EPROCESS - // Thre -> _KTHREAD - let pool_header_size = self.pdb_store.get_offset_r("_POOL_HEADER.struct_size")?; - if tag == b"Proc" { - let eprocess_size = self.pdb_store.get_offset_r("_EPROCESS.struct_size")?; - let minimum_data_size = eprocess_size + pool_header_size; - Ok(minimum_data_size) - } - else if tag == b"Thre" { - let ethread_size = self.pdb_store.get_offset_r("_EPROCESS.struct_size")?; - let minimum_data_size = ethread_size + pool_header_size; - Ok(minimum_data_size) - } - else if tag == b"File" { - let file_object_size = self.pdb_store.get_offset_r("_FILE_OBJECT.struct_size")?; - let minimum_data_size = file_object_size + pool_header_size; - Ok(minimum_data_size) - } - else { - Err("Tag unknown".into()) - } + pub fn address_of(&self, addr: &Address, name: &str) -> BoxResult { + let resolver = |p| { self.deref_addr_new(p) }; + let r = self.pdb_store.decompose(&addr, &name)?; + Ok(r.get(&resolver)) } + pub fn decompose(&self, addr: &Address, name: &str) -> BoxResult { + // interface to pdb_store.decompose + let resolver = |p| { self.deref_addr_new(p) }; + let r: T = self.deref_addr_new(self.pdb_store.decompose(&addr, &name)?.get(&resolver)); + Ok(r) + } + + pub fn decompose_array(&self, addr: &Address, name: &str, len: u64) -> BoxResult> { + // interface to pdb_store.decompose for array + let r: Vec = self.deref_array(&self.pdb_store.decompose(&addr, &name)?, len); + Ok(r) + } + + pub fn deref_addr_new(&self, addr: u64) -> T { + let mut r: T = Default::default(); + if addr != 0 { + self.deref_addr(addr, &mut r); + } + r + } + + pub fn deref_array(&self, addr: &Address, len: u64) -> Vec { + let resolver = |p| { self.deref_addr_new(p) }; + let mut r: Vec = vec![Default::default(); len as usize]; + self.deref_addr_ptr(addr.get(&resolver), r.as_mut_ptr(), len); + r + } + + // #[deprecated(note="use deref_addr_new")] pub fn deref_addr(&self, addr: u64, outbuf: &mut T) { - // println!("deref addr: 0x{:x}", addr); let code = DriverAction::DereferenceAddress.get_code(); let size: usize = size_of_val(outbuf); let mut input = InputData { @@ -228,10 +247,10 @@ impl DriverState { 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); } + // #[deprecated(note="use deref_array")] pub fn deref_addr_ptr(&self, addr: u64, outptr: *mut T, output_len: u64) { let code = DriverAction::DereferenceAddress.get_code(); let mut input = InputData { @@ -256,7 +275,6 @@ impl DriverState { self.deref_addr(capacity_addr, &mut capacity); self.deref_addr(buffer_ptr, &mut bufaddr); - // println!("unicode str: 0x{:x} size: 0x{:x} capacity: 0x{:x}", bufaddr, strlen, capacity); if bufaddr == 0 || strlen > capacity || strlen == 0 || strlen % 2 != 0 { return Err("Unicode string is empty".into()); } @@ -267,54 +285,53 @@ impl DriverState { let mut buf = vec![0u16; (strlen / 2) as usize]; self.deref_addr_ptr(bufaddr, buf.as_mut_ptr(), strlen as u64); + // TODO: BUG with deref_array, len is wrong, + // >> the size of vector is strlen / 2 + // >> the size to dereference is strlen + // XXX: use Vec and turn to Vec + // let buf: Vec = self.deref_array(&Address::from_base(bufaddr), (strlen / 2) as u64); Ok(String::from_utf16(&buf)?) } - pub fn get_nonpaged_range(&self, ntosbase: u64) -> BoxResult<[u64; 2]> { + pub fn get_nonpaged_range(&self, ntosbase: &Address) -> BoxResult<[Address; 2]> { // TODO: Add support for other Windows version here match self.windows_ffi.short_version { WindowsVersion::Windows10FastRing => { - let mistate = ntosbase + self.pdb_store.get_offset_r("MiState")?; - let system_node_ptr = self.pdb_store.addr_decompose( - mistate, "_MI_SYSTEM_INFORMATION.Hardware.SystemNodeNonPagedPool")?; - 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_r("_MI_SYSTEM_NODE_NONPAGED_POOL.NonPagedPoolFirstVa")?, - &mut first_va); - - self.deref_addr( - system_node_addr - + self.pdb_store.get_offset_r("_MI_SYSTEM_NODE_NONPAGED_POOL.NonPagedPoolLastVa")?, - &mut last_va); - + let mistate = ntosbase.clone() + self.pdb_store.get_offset_r("MiState")?; + let path_first_va: String = vec![ + "_MI_SYSTEM_INFORMATION", + "Hardware", + "SystemNodeNonPagedPool", + "NonPagedPoolFirstVa" + ].join("."); + let path_last_va: String = vec![ + "_MI_SYSTEM_INFORMATION", + "Hardware", + "SystemNodeNonPagedPool", + "NonPagedPoolLastVa" + ].join("."); + let first_va = Address::from_base(self.decompose(&mistate, &path_first_va)?); + let last_va = Address::from_base(self.decompose(&mistate, &path_last_va)?); Ok([first_va, last_va]) }, WindowsVersion::Windows10_2019 | WindowsVersion::Windows10_2018 => { - let mistate = ntosbase + self.pdb_store.get_offset_r("MiState")?; - let system_node_ptr = self.pdb_store.addr_decompose( - mistate, "_MI_SYSTEM_INFORMATION.Hardware.SystemNodeInformation")?; - 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_r("_MI_SYSTEM_NODE_INFORMATION.NonPagedPoolFirstVa")?, - &mut first_va); - - self.deref_addr( - system_node_addr - + self.pdb_store.get_offset_r("_MI_SYSTEM_NODE_INFORMATION.NonPagedPoolLastVa")?, - &mut last_va); - + let mistate = ntosbase.clone() + self.pdb_store.get_offset_r("MiState")?; + let path_first_va: String = vec![ + "_MI_SYSTEM_INFORMATION", + "Hardware", + "SystemNodeInformation", + "NonPagedPoolFirstVa" + ].join("."); + let path_last_va: String = vec![ + "_MI_SYSTEM_INFORMATION", + "Hardware", + "SystemNodeInformation", + "NonPagedPoolLastVa" + ].join("."); + let first_va = Address::from_base(self.decompose(&mistate, &path_first_va)?); + let last_va = Address::from_base(self.decompose(&mistate, &path_last_va)?); Ok([first_va, last_va]) }, _ => { diff --git a/src/lib.rs b/src/lib.rs index 4621008..6d851b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,4 +5,5 @@ pub mod pdb_store; pub mod windows; pub mod ioctl_protocol; pub mod driver_state; +pub mod address; diff --git a/src/pdb_store.rs b/src/pdb_store.rs index c61e434..f451450 100644 --- a/src/pdb_store.rs +++ b/src/pdb_store.rs @@ -9,9 +9,10 @@ use pdb::{ PDB, SymbolData, TypeData, ClassType, ModifierType, Rva, FallibleIterator, TypeFinder, TypeIndex }; - use app_dirs::{AppInfo, AppDataType, app_dir}; +use crate::address::Address; + const APP_INFO: AppInfo = AppInfo { name: "lpus", author: "nganhkhoa" }; const KERNEL_PDB_NAME: &str = "ntkrnlmp.pdb"; @@ -85,6 +86,36 @@ impl PdbStore { } } + pub fn decompose(&self, source: &Address, full_name: &str) -> BoxResult
{ + // println!("decompose {}", full_name); + if !full_name.contains(".") { + return Err("Not decomposable".into()); + } + + let mut name_part: Vec<&str> = full_name.split_terminator('.').collect(); + let mut next: Vec<_> = name_part.drain(2..).collect(); + let member_info = self.structs.get(name_part[0]) + .ok_or(format!("No struct {}", name_part[0]))?; + let (memtype, offset) = member_info.get(name_part[1]) + .ok_or(format!("No member {} in {}", name_part[1], name_part[0]))?; + + if next.len() == 0 { + return Ok(source.clone() + *offset); + } + if memtype.contains("*") { + let mut t = memtype.clone(); // remove * + t.pop(); + next.insert(0, &t); + let p = Address::from_ptr(source.clone() + *offset); + self.decompose(&p, &next.join(".")) + + } + else { + next.insert(0, memtype); + self.decompose(&(source.clone() + *offset), &next.join(".")) + } + } + #[allow(dead_code)] pub fn print_default_information(&self) { let need_symbols = [