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
This commit is contained in:
nganhkhoa 2020-05-22 14:44:47 +07:00
parent ee13c6be58
commit ecc476c604
3 changed files with 97 additions and 22 deletions

View File

@ -34,6 +34,8 @@ fn main() -> Result<(), Box<dyn Error>> {
let eprocess_size = driver.pdb_store.get_offset_r("_EPROCESS.struct_size")?; 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_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_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_exit_time_offset = driver.pdb_store.get_offset_r("_EPROCESS.ExitTime")?;
let eprocess_valid_start = data_addr; let eprocess_valid_start = data_addr;
@ -56,16 +58,22 @@ fn main() -> Result<(), Box<dyn Error>> {
} }
let mut image_name = [0u8; 15]; 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_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) { if let Ok(name) = from_utf8(&image_name) {
let eprocess_name = name let eprocess_name = name
.to_string() .to_string()
.trim_end_matches(char::from(0)) .trim_end_matches(char::from(0))
.to_string(); .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 { 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) Ok(true)
// eprocess_list.push(EprocessPoolChunk { // eprocess_list.push(EprocessPoolChunk {

View File

@ -0,0 +1,48 @@
use std::error::Error;
use lpus::{
driver_state::{DriverState}
};
fn main() -> Result<(), Box<dyn Error>> {
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(())
}

View File

@ -108,7 +108,8 @@ impl DriverState {
ntosbase ntosbase
} }
pub fn scan_active_head(&self, ntosbase: u64) -> BoxResult<Vec<EprocessPoolChunk>> { pub fn scan_active_head(&self) -> BoxResult<Vec<EprocessPoolChunk>> {
let ntosbase = self.get_kernel_base();
let ps_active_head = ntosbase + self.pdb_store.get_offset_r("PsActiveProcessHead")?; 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 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_link_offset = self.pdb_store.get_offset_r("_EPROCESS.ActiveProcessLinks")?;
@ -148,8 +149,8 @@ impl DriverState {
// TODO: Pool Header as a real struct // TODO: Pool Header as a real struct
{ {
let ntosbase = self.get_kernel_base(); 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 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 code = DriverAction::ScanPoolRemote.get_code();
let range = self.get_nonpaged_range(ntosbase)?; let range = self.get_nonpaged_range(ntosbase)?;
let start_address = range[0]; let start_address = range[0];
@ -165,6 +166,9 @@ impl DriverState {
} }
let pool_addr = ptr; let pool_addr = ptr;
// println!("chunk: 0x{:x}", pool_addr);
// ptr += 0x4;
// continue;
let mut header = vec![0u8; pool_header_size as usize]; let mut header = vec![0u8; pool_header_size as usize];
self.deref_addr_ptr(pool_addr, header.as_mut_ptr(), pool_header_size); self.deref_addr_ptr(pool_addr, header.as_mut_ptr(), pool_header_size);
let chunk_size = (header[2] as u64) * 16u64; let chunk_size = (header[2] as u64) * 16u64;
@ -174,23 +178,14 @@ impl DriverState {
break; break;
} }
// TODO: move to another function, with tables mapping fromm tag -> struct
// automatically reject bad chunk // automatically reject bad chunk
// Proc -> _EPROCESS if chunk_size < minimum_block_size {
// Thre -> _KTHREAD ptr += 0x4;
if tag == b"Proc" { continue;
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;
} }
// ptr += 0x4;
// continue;
let success = handler(pool_addr, &header, pool_addr + pool_header_size)?; let success = handler(pool_addr, &header, pool_addr + pool_header_size)?;
if success { if success {
ptr += chunk_size; /* pass this chunk */ ptr += chunk_size; /* pass this chunk */
@ -202,6 +197,25 @@ impl DriverState {
Ok(true) Ok(true)
} }
fn get_minimum_block_size(&self, tag: &[u8; 4]) -> BoxResult<u64> {
// 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<T>(&self, addr: u64, outbuf: &mut T) { pub fn deref_addr<T>(&self, addr: u64, outbuf: &mut T) {
// println!("deref addr: 0x{:x}", addr); // println!("deref addr: 0x{:x}", addr);
let code = DriverAction::DereferenceAddress.get_code(); let code = DriverAction::DereferenceAddress.get_code();
@ -231,18 +245,23 @@ impl DriverState {
pub fn get_unicode_string(&self, unicode_str_addr: u64) -> BoxResult<String> { pub fn get_unicode_string(&self, unicode_str_addr: u64) -> BoxResult<String> {
let mut strlen = 0u16; let mut strlen = 0u16;
let mut capacity = 0u16;
let mut bufaddr = 0u64; let mut bufaddr = 0u64;
let buffer_ptr = unicode_str_addr + self.pdb_store.get_offset_r("_UNICODE_STRING.Buffer")?; 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(unicode_str_addr, &mut strlen);
self.deref_addr(capacity_addr, &mut capacity);
self.deref_addr(buffer_ptr, &mut bufaddr); 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); self.deref_addr_ptr(bufaddr, buf.as_mut_ptr(), strlen as u64);
println!("unicode string {:?}", buf); Ok(String::from_utf16(&buf)?)
Ok(std::str::from_utf8(&buf)?.to_string())
} }
pub fn get_nonpaged_range(&self, ntosbase: u64) -> BoxResult<[u64; 2]> { pub fn get_nonpaged_range(&self, ntosbase: u64) -> BoxResult<[u64; 2]> {