From ecc476c60441c31623eaeae31b46ca9b7603898c Mon Sep 17 00:00:00 2001 From: nganhkhoa Date: Fri, 22 May 2020 14:44:47 +0700 Subject: [PATCH] Update scan frontend Reject invalid block size Unicode string handle for empty ptr, empty size Add _FILE_OBJECT scan Add FileImage dump of _EPROCESS scan --- src/bin/eprocess_scan.rs | 12 ++++++-- src/bin/file_object_scan.rs | 48 ++++++++++++++++++++++++++++++ src/driver_state.rs | 59 ++++++++++++++++++++++++------------- 3 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 src/bin/file_object_scan.rs diff --git a/src/bin/eprocess_scan.rs b/src/bin/eprocess_scan.rs index 8ff1c9f..7843d38 100644 --- a/src/bin/eprocess_scan.rs +++ b/src/bin/eprocess_scan.rs @@ -34,6 +34,8 @@ fn main() -> Result<(), Box> { 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 fob_filename_offset = driver.pdb_store.get_offset_r("_FILE_OBJECT.FileName")?; + let eprocess_image_file_ptr_offset = driver.pdb_store.get_offset_r("_EPROCESS.ImageFilePointer")?; // let eprocess_exit_time_offset = driver.pdb_store.get_offset_r("_EPROCESS.ExitTime")?; let eprocess_valid_start = data_addr; @@ -56,16 +58,22 @@ fn main() -> Result<(), Box> { } let mut image_name = [0u8; 15]; + let mut file_object_ptr = 0u64; + driver.deref_addr(try_eprocess_ptr + eprocess_name_offset, &mut image_name); + driver.deref_addr(try_eprocess_ptr + eprocess_image_file_ptr_offset, &mut file_object_ptr); + let filename = if file_object_ptr != 0 { driver.get_unicode_string(file_object_ptr + fob_filename_offset)? } + else { "".to_string() }; + if let Ok(name) = from_utf8(&image_name) { let eprocess_name = name .to_string() .trim_end_matches(char::from(0)) .to_string(); - println!("pool: 0x{:x} | eprocess: 0x{:x} | {}", pool_addr, try_eprocess_ptr, eprocess_name); + println!("pool: 0x{:x} | eprocess: 0x{:x} | {} | {}", pool_addr, try_eprocess_ptr, filename, eprocess_name); } else { - println!("pool: 0x{:x} | eprocess: 0x{:x} | {:?}", pool_addr, try_eprocess_ptr, image_name); + println!("pool: 0x{:x} | eprocess: 0x{:x} | {} | {:?}", pool_addr, try_eprocess_ptr, filename, image_name); } Ok(true) // eprocess_list.push(EprocessPoolChunk { diff --git a/src/bin/file_object_scan.rs b/src/bin/file_object_scan.rs new file mode 100644 index 0000000..8526241 --- /dev/null +++ b/src/bin/file_object_scan.rs @@ -0,0 +1,48 @@ +use std::error::Error; + +use lpus::{ + driver_state::{DriverState} +}; + +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| { + 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_filename_offset = driver.pdb_store.get_offset_r("_FILE_OBJECT.FileName")?; + + let valid_end = (pool_addr + 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); + if (size as u64) == fob_size && ftype == 5u16 { + break; + } + try_ptr += 0x4; // search exhaustively + } + if try_ptr > valid_end { + return Ok(false); + } + let fob_addr = try_ptr; + // println!("pool: 0x{:x} | file object: 0x{:x} | offsetby: {}", pool_addr, fob_addr, fob_addr - pool_addr); + if let Ok(filename) = driver.get_unicode_string(fob_addr + fob_filename_offset) { + println!("pool: 0x{:x} | file object: 0x{:x} | offsetby: {} | {}", + pool_addr, fob_addr, fob_addr - pool_addr, filename); + return Ok(true); + } + Ok(false) + })?; + + println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown()); + Ok(()) +} + + diff --git a/src/driver_state.rs b/src/driver_state.rs index 87b87e3..2422a49 100644 --- a/src/driver_state.rs +++ b/src/driver_state.rs @@ -108,7 +108,8 @@ impl DriverState { ntosbase } - pub fn scan_active_head(&self, ntosbase: u64) -> BoxResult> { + 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")?; @@ -148,8 +149,8 @@ impl DriverState { // TODO: Pool Header as a real struct { let ntosbase = self.get_kernel_base(); - // TODO: check valid tag 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 code = DriverAction::ScanPoolRemote.get_code(); let range = self.get_nonpaged_range(ntosbase)?; let start_address = range[0]; @@ -165,6 +166,9 @@ impl DriverState { } let pool_addr = ptr; + // println!("chunk: 0x{:x}", pool_addr); + // ptr += 0x4; + // continue; let mut header = vec![0u8; pool_header_size as usize]; self.deref_addr_ptr(pool_addr, header.as_mut_ptr(), pool_header_size); let chunk_size = (header[2] as u64) * 16u64; @@ -174,23 +178,14 @@ impl DriverState { break; } - // TODO: move to another function, with tables mapping fromm tag -> struct // automatically reject bad chunk - // Proc -> _EPROCESS - // Thre -> _KTHREAD - 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; - if chunk_size < minimum_data_size { - ptr += 0x4; - continue; - } - } - else { - println!("Tag unknown"); - break; + if chunk_size < minimum_block_size { + ptr += 0x4; + continue; } + // ptr += 0x4; + // continue; let success = handler(pool_addr, &header, pool_addr + pool_header_size)?; if success { ptr += chunk_size; /* pass this chunk */ @@ -202,6 +197,25 @@ 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"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 deref_addr(&self, addr: u64, outbuf: &mut T) { // println!("deref addr: 0x{:x}", addr); let code = DriverAction::DereferenceAddress.get_code(); @@ -231,18 +245,23 @@ impl DriverState { pub fn get_unicode_string(&self, unicode_str_addr: u64) -> BoxResult { let mut strlen = 0u16; + let mut capacity = 0u16; let mut bufaddr = 0u64; let buffer_ptr = unicode_str_addr + self.pdb_store.get_offset_r("_UNICODE_STRING.Buffer")?; + let capacity_addr = unicode_str_addr + self.pdb_store.get_offset_r("_UNICODE_STRING.MaximumLength")?; self.deref_addr(unicode_str_addr, &mut strlen); + self.deref_addr(capacity_addr, &mut capacity); self.deref_addr(buffer_ptr, &mut bufaddr); - let mut buf = vec![0u8; strlen as usize]; + if bufaddr == 0 || strlen > capacity || strlen == 0 { + return Err("Unicode string is empty".into()); + } + + let mut buf = vec![0u16; (strlen / 2) as usize]; self.deref_addr_ptr(bufaddr, buf.as_mut_ptr(), strlen as u64); - println!("unicode string {:?}", buf); - - Ok(std::str::from_utf8(&buf)?.to_string()) + Ok(String::from_utf16(&buf)?) } pub fn get_nonpaged_range(&self, ntosbase: u64) -> BoxResult<[u64; 2]> {