Introducing Address type

Use address type to represent address
Decompose address with ease using DriverState.decompose
This commit is contained in:
nganhkhoa 2020-06-11 01:27:26 +07:00
parent 72a947ccd7
commit 060f222c0a
8 changed files with 453 additions and 199 deletions

177
src/address.rs Normal file
View File

@ -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<F>(&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<Rc<Address>>,
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<F>(&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<F>(&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<u64> 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<u64> for Address {
fn add_assign(&mut self, other: u64) {
*self = Self {
base: self.base,
pointer: self.pointer.clone(),
offset: self.offset + other,
}
}
}
impl Sub<u64> 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<u64> 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<Ordering> {
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
}
}
}

View File

@ -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<dyn Error>> {
// let eprocess_scan_head = driver.scan_active_head(ntosbase)?;
// let mut eprocess_list: Vec<EprocessPoolChunk> = 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<dyn Error>> {
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<u8> = 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,

View File

@ -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<dyn Error>> {
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<EprocessPoolChunk> = 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<dyn Error>> {
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(())
}

View File

@ -8,40 +8,34 @@ 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| {
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 {

View File

@ -1,7 +1,8 @@
use std::error::Error;
use lpus::{
driver_state::{DriverState}
driver_state::{DriverState},
address::Address
};
fn main() -> Result<(), Box<dyn Error>> {

View File

@ -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<Vec<EprocessPoolChunk>> {
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<Vec<EprocessPoolChunk>> {
// 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<EprocessPoolChunk> = 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<EprocessPoolChunk> = 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<F>(&self, tag: &[u8; 4], mut handler: F) -> BoxResult<bool>
where F: FnMut(u64, &[u8], u64) -> BoxResult<bool>
pub fn scan_pool<F>(&self, tag: &[u8; 4], expected_struct: &str, mut handler: F) -> BoxResult<bool>
where F: FnMut(Address, &[u8], Address) -> BoxResult<bool>
// 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<u8> = 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<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"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<u64> {
let resolver = |p| { self.deref_addr_new(p) };
let r = self.pdb_store.decompose(&addr, &name)?;
Ok(r.get(&resolver))
}
pub fn decompose<T: Default>(&self, addr: &Address, name: &str) -> BoxResult<T> {
// 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<T: Default + Clone>(&self, addr: &Address, name: &str, len: u64) -> BoxResult<Vec<T>> {
// interface to pdb_store.decompose for array
let r: Vec<T> = self.deref_array(&self.pdb_store.decompose(&addr, &name)?, len);
Ok(r)
}
pub fn deref_addr_new<T: Default>(&self, addr: u64) -> T {
let mut r: T = Default::default();
if addr != 0 {
self.deref_addr(addr, &mut r);
}
r
}
pub fn deref_array<T: Default + Clone>(&self, addr: &Address, len: u64) -> Vec<T> {
let resolver = |p| { self.deref_addr_new(p) };
let mut r: Vec<T> = 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<T>")]
pub fn deref_addr<T>(&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<T>")]
pub fn deref_addr_ptr<T>(&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<u8> and turn to Vec<u16>
// let buf: Vec<u16> = 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])
},
_ => {

View File

@ -5,4 +5,5 @@ pub mod pdb_store;
pub mod windows;
pub mod ioctl_protocol;
pub mod driver_state;
pub mod address;

View File

@ -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<Address> {
// 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 = [