Introducing Address type
Use address type to represent address Decompose address with ease using DriverState.decompose
This commit is contained in:
parent
72a947ccd7
commit
060f222c0a
177
src/address.rs
Normal file
177
src/address.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -1,7 +1,8 @@
|
||||
use std::error::Error;
|
||||
|
||||
use lpus::{
|
||||
driver_state::{DriverState}
|
||||
driver_state::{DriverState},
|
||||
address::Address
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
@ -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])
|
||||
},
|
||||
_ => {
|
||||
|
@ -5,4 +5,5 @@ pub mod pdb_store;
|
||||
pub mod windows;
|
||||
pub mod ioctl_protocol;
|
||||
pub mod driver_state;
|
||||
pub mod address;
|
||||
|
||||
|
@ -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 = [
|
||||
|
Loading…
Reference in New Issue
Block a user