From ff53a1a31c04d728d21712fb7289fcbd2850a10a Mon Sep 17 00:00:00 2001 From: nganhkhoa Date: Wed, 20 May 2020 00:42:24 +0700 Subject: [PATCH] Fix runtime BOSD Chunk size and tag is check before handle. Check if heuristics search is not correct, and the try_ptr goes of the bound, making dereference an invalid address. --- src/bin/eprocess_scan.rs | 30 ++++++++++++++++++++---------- src/driver_state.rs | 36 ++++++++++++++++++++++++++++++------ src/pdb_store.rs | 5 ++++- 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/bin/eprocess_scan.rs b/src/bin/eprocess_scan.rs index 718aae3..ffe89db 100644 --- a/src/bin/eprocess_scan.rs +++ b/src/bin/eprocess_scan.rs @@ -29,33 +29,45 @@ 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| { + 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_exit_time_offset = driver.pdb_store.get_offset_r("_EPROCESS.ExitTime")?; - let eprocess_size = driver.pdb_store.get_offset_r("_EPROCESS.struct_size")?; - let chunk_size = (header[2] as u64) * 16u64; let eprocess_valid_start = data_addr; - let eprocess_valid_end = pool_addr + chunk_size - eprocess_size; + let eprocess_valid_end = (pool_addr + chunk_size) - eprocess_size; let mut try_eprocess_ptr = eprocess_valid_start; 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); + // driver.deref_addr(try_eprocess_ptr + eprocess_exit_time_offset, &mut exit_time); // using heuristics to eliminate false positive if driver.windows_ffi.valid_process_time(create_time) { break; } try_eprocess_ptr += 0x4; // search exhaustively } + if (try_eprocess_ptr > eprocess_valid_end) { + return Ok(false); + } + let mut image_name = [0u8; 15]; driver.deref_addr(try_eprocess_ptr + eprocess_name_offset, &mut image_name); - let eprocess_name = from_utf8(&image_name)? - .to_string() - .trim_end_matches(char::from(0)) - .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); + } + else { + println!("pool: 0x{:x} | eprocess: 0x{:x} | {:?}", pool_addr, try_eprocess_ptr, image_name); + } + Ok(true) // eprocess_list.push(EprocessPoolChunk { // pool_addr, // eprocess_addr: try_eprocess_ptr, @@ -63,8 +75,6 @@ fn main() -> Result<(), Box> { // create_time: to_epoch(create_time), // exit_time: to_epoch(exit_time) // }); - println!("pool: {} | eprocess: {}: {}", pool_addr, try_eprocess_ptr, eprocess_name); - Ok(try_eprocess_ptr <= eprocess_valid_end) })?; println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown()); diff --git a/src/driver_state.rs b/src/driver_state.rs index 6c03983..bd6f68f 100644 --- a/src/driver_state.rs +++ b/src/driver_state.rs @@ -163,23 +163,47 @@ impl DriverState { 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 chunk_size = (header[2] as u64) * 16u64; - let success = handler(ptr, &header, pool_addr + pool_header_size)?; - if success { - let chunk_size = (header[2] as u64) * 16u64; - ptr += chunk_size /* pass this chunk */ + if pool_addr + chunk_size > end_address { + // the chunk found is not a valid chunk for sure + 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 { - ptr += 0x4 /* search next */ - }; + println!("Tag unknown"); + break; + } + + let success = handler(pool_addr, &header, pool_addr + pool_header_size)?; + if success { + ptr += chunk_size; /* pass this chunk */ + } + else { + ptr += 0x4; /* search next */ + } } Ok(true) } 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 { diff --git a/src/pdb_store.rs b/src/pdb_store.rs index 97dc398..dc5663e 100644 --- a/src/pdb_store.rs +++ b/src/pdb_store.rs @@ -99,7 +99,10 @@ impl PdbStore { ]; let mut need_structs = HashMap::new(); - need_structs.insert("_POOL_HEADER", vec![]); + need_structs.insert("_POOL_HEADER", vec![ + "struct_size", + "PoolType", "BlockSize", "PoolTag" + ]); need_structs.insert("_PEB", vec![]); need_structs.insert("_LIST_ENTRY", vec![ "Flink", "Blink"