diff --git a/Cargo.lock b/Cargo.lock index 51d9981..c6561a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -337,6 +337,18 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lpus" +version = "0.1.0" +dependencies = [ + "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pdb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "matches" version = "0.1.8" @@ -482,18 +494,6 @@ dependencies = [ "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "parse_pdb_for_offsets" -version = "0.1.0" -dependencies = [ - "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pdb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "pdb" version = "0.5.0" diff --git a/Cargo.toml b/Cargo.toml index 4fa6068..2fbe7d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,14 @@ [package] -name = "parse_pdb_for_offsets" +name = "lpus" version = "0.1.0" authors = ["nganhkhoa "] +description = "Live pool tag scanning frontend" edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "lpus" +doctest = false [dependencies] hex = "0.4.2" diff --git a/src/bin/eprocess_scan.rs b/src/bin/eprocess_scan.rs new file mode 100644 index 0000000..718aae3 --- /dev/null +++ b/src/bin/eprocess_scan.rs @@ -0,0 +1,73 @@ +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 */} +}; + +#[allow(dead_code)] +fn to_str_time(time_ms: u64) -> String { + if time_ms == 0 { + return "".to_string(); + } + let d = UNIX_EPOCH + Duration::from_millis(time_ms); + let datetime = DateTime::::from(d); + let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S.%f").to_string(); + timestamp_str +} + +fn main() -> Result<(), Box> { + // for windows admin require + // https://github.com/nabijaczleweli/rust-embed-resource + + let mut driver = DriverState::new(); + println!("NtLoadDriver() -> 0x{:x}", driver.startup()); + + // 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 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 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); + // using heuristics to eliminate false positive + if driver.windows_ffi.valid_process_time(create_time) { + break; + } + try_eprocess_ptr += 0x4; // search exhaustively + } + 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(); + // eprocess_list.push(EprocessPoolChunk { + // pool_addr, + // eprocess_addr: try_eprocess_ptr, + // eprocess_name: eprocess_name, + // 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()); + Ok(()) +} + diff --git a/src/bin/print_pdb.rs b/src/bin/print_pdb.rs new file mode 100644 index 0000000..ba4ad91 --- /dev/null +++ b/src/bin/print_pdb.rs @@ -0,0 +1,12 @@ +use std::error::Error; + +use lpus::{ + driver_state::{DriverState} +}; + +fn main() -> Result<(), Box> { + let driver = DriverState::new(); + driver.windows_ffi.print_version(); + driver.pdb_store.print_default_information(); + Ok(()) +} diff --git a/src/driver_state.rs b/src/driver_state.rs index 70a87a6..6c03983 100644 --- a/src/driver_state.rs +++ b/src/driver_state.rs @@ -10,7 +10,7 @@ use winapi::um::winioctl::{ METHOD_IN_DIRECT, METHOD_OUT_DIRECT, /* METHOD_BUFFERED, */ METHOD_NEITHER }; -use crate::pdb_store::{PdbStore}; +use crate::pdb_store::{PdbStore, parse_pdb}; use crate::windows::{WindowsFFI, WindowsVersion}; use crate::ioctl_protocol::{ InputData, OffsetData, DerefAddr, ScanPoolData, /* HideProcess, */ @@ -79,12 +79,10 @@ pub struct DriverState { } impl DriverState { - pub fn new(pdb_store: PdbStore, windows_ffi: WindowsFFI) -> Self { - pdb_store.print_default_information(); - windows_ffi.print_version(); + pub fn new() -> Self { Self { - pdb_store, - windows_ffi + pdb_store: parse_pdb(), + windows_ffi: WindowsFFI::new() } } @@ -102,12 +100,12 @@ impl DriverState { self.windows_ffi.unload_driver() } - pub fn get_kernel_base(&self) -> BoxResult { + pub fn get_kernel_base(&self) -> u64 { let mut ntosbase = 0u64; self.windows_ffi.device_io(DriverAction::GetKernelBase.get_code(), &mut Nothing, &mut ntosbase); // println!("ntosbase: 0x{:x}", self.ntosbase); - Ok(ntosbase) + ntosbase } pub fn scan_active_head(&self, ntosbase: u64) -> BoxResult> { @@ -144,9 +142,14 @@ impl DriverState { Ok(result) } - pub fn scan_pool(&self, ntosbase: u64, tag: [u8; 4], mut handler: F) -> BoxResult - where F: FnMut(&DriverState, u64) -> BoxResult + pub fn scan_pool(&self, tag: &[u8; 4], mut handler: F) -> BoxResult + where F: FnMut(u64, &[u8], u64) -> BoxResult + // F(Pool Address, Pool Header Data, Pool Data Address) + // 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 code = DriverAction::ScanPoolRemote.get_code(); let range = self.get_nonpaged_range(ntosbase)?; let start_address = range[0]; @@ -154,22 +157,23 @@ impl DriverState { let mut ptr = start_address; while ptr < end_address { let mut input = InputData { - scan_range: ScanPoolData::new(&[ptr, end_address], &tag) + scan_range: ScanPoolData::new(&[ptr, end_address], tag) }; self.windows_ffi.device_io(code, &mut input, &mut ptr); if ptr >= end_address { break; } - ptr += match handler(&self, ptr) { - Ok(success) => { - if success { - } - else { - } - }, - Err(e) => println!("Handle error {:?}", e), - // found, ptr += chunk size - // ptr += pool_header_size; + 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 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 */ + } + else { + ptr += 0x4 /* search next */ }; } Ok(true) diff --git a/src/ioctl_protocol.rs b/src/ioctl_protocol.rs index 18cba43..26113a5 100644 --- a/src/ioctl_protocol.rs +++ b/src/ioctl_protocol.rs @@ -67,7 +67,7 @@ pub struct DerefAddr { pub struct ScanPoolData { pub start: u64, pub end: u64, - pub tag: [u8; 4] + pub tag: u32 } impl ScanPoolData{ @@ -75,7 +75,7 @@ impl ScanPoolData{ Self { start: arr[0], end: arr[1], - tag: *tag + tag: u32::from_le_bytes(*tag) } } } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..cda8c7f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,7 @@ +extern crate chrono; + +pub mod pdb_store; +pub mod windows; +pub mod ioctl_protocol; +pub mod driver_state; + diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 634bc5d..0000000 --- a/src/main.rs +++ /dev/null @@ -1,124 +0,0 @@ -extern crate chrono; - -mod pdb_store; -mod windows; -mod ioctl_protocol; -mod driver_state; - -use std::error::Error; -use std::str::{from_utf8}; -// use chrono::prelude::DateTime; -// use chrono::Utc; -// use chrono::{Local, DateTime}; -// use std::time::{SystemTime, UNIX_EPOCH, Duration}; - -use pdb_store::parse_pdb; -use windows::WindowsFFI; -use driver_state::{DriverState, EprocessPoolChunk, to_epoch}; - -fn to_str_time(_time_ms: u64) -> String { - // if time_ms == 0 { - // return "".to_string(); - // } - // let d = UNIX_EPOCH + Duration::from_millis(time_ms); - // let datetime = DateTime::::from(d); - // let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S.%f").to_string(); - // timestamp_str - "".to_string() -} - -fn main() -> Result<(), Box> { - // for windows admin require - // https://github.com/nabijaczleweli/rust-embed-resource - - let mut driver = DriverState::new(parse_pdb(), WindowsFFI::new()); - println!("NtLoadDriver() -> 0x{:x}", driver.startup()); - - let ntosbase = driver.get_kernel_base()?; - let pool_header_size = driver.pdb_store.get_offset_r("_POOL_HEADER.struct_size")?; - - let eprocess_tag: [u8; 4] = [80, 114, 111, 99]; // Proc - 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 eprocess_scan_head = driver.scan_active_head(ntosbase)?; - let mut eprocess_list: Vec = Vec::new(); - driver.scan_pool(ntosbase, eprocess_tag, |dr, pool_addr| { - let mut pool = vec![0u8; pool_header_size as usize]; - dr.deref_addr_ptr(pool_addr, pool.as_mut_ptr(), pool_header_size); - - let chunk_size = (pool[2] as u64) * 16u64; - let eprocess_valid_start = pool_addr + pool_header_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 { - dr.deref_addr(try_eprocess_ptr + eprocess_create_time_offset, &mut create_time); - dr.deref_addr(try_eprocess_ptr + eprocess_exit_time_offset, &mut exit_time); - if dr.windows_ffi.valid_process_time(create_time) { - break; - } - try_eprocess_ptr += 0x4; // search exhaustively - } - let mut image_name = [0u8; 15]; - dr.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(); - eprocess_list.push(EprocessPoolChunk { - pool_addr, - eprocess_addr: try_eprocess_ptr, - eprocess_name: eprocess_name, - create_time: to_epoch(create_time), - exit_time: to_epoch(exit_time) - }); - Ok(try_eprocess_ptr <= eprocess_valid_end) - })?; - - let ethread_tag: [u8; 4] = [84, 104, 114, 101]; // Thre - let ethread_create_time_offset = driver.pdb_store.get_offset_r("_ETHREAD.CreateTime")?; - let ethread_exit_time_offset = driver.pdb_store.get_offset_r("_ETHREAD.ExitTime")?; - let ethread_threadname_offset = driver.pdb_store.get_offset_r("_ETHREAD.TheadName")?; - let ethread_size = driver.pdb_store.get_offset_r("_ETHREAD.struct_size")?; - - // let mut ethread_list: Vec = Vec::new(); - driver.scan_pool(ntosbase, ethread_tag, |dr, pool_addr| { - let mut pool = vec![0u8; pool_header_size as usize]; - dr.deref_addr_ptr(pool_addr, pool.as_mut_ptr(), pool_header_size); - - let chunk_size = (pool[2] as u64) * 16u64; - let ethread_valid_start = pool_addr + pool_header_size; - 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 { - dr.deref_addr(try_ethread_ptr + ethread_create_time_offset, &mut create_time); - dr.deref_addr(try_ethread_ptr + ethread_exit_time_offset, &mut exit_time); - if dr.windows_ffi.valid_process_time(create_time) { - break; - } - try_ethread_ptr += 0x4; // search exhaustively - } - let mut threadname_ptr = 0u64; - dr.deref_addr(try_ethread_ptr + ethread_threadname_offset, &mut threadname_ptr); - let threadname = dr.get_unicode_string(threadname_ptr)?; - println!("threadname: {}", threadname); - Ok(try_ethread_ptr <= ethread_valid_end) - })?; - - // for result in &driver.eprocess_traverse_result { - // println!("- [{}] 0x{:x} {}", - // driver.pool_scan_result.contains(&result), - // result.eprocess_addr, result.eprocess_name); - // } - - println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown()); - Ok(()) -} diff --git a/src/pdb_store.rs b/src/pdb_store.rs index f58a4be..97dc398 100644 --- a/src/pdb_store.rs +++ b/src/pdb_store.rs @@ -299,7 +299,15 @@ pub fn download_pdb() { pub fn parse_pdb() -> PdbStore { // TODO: Detect pdb file and ntoskrnl file version differs - // The guid of ntoskrnl and pdb file are different + // Use a folder at %APPDATA% to save pdb files + // %APPDATA%\lpus + // |--ntoskrnl + // |--|--GUID + // |--|--|--ntkrnlmp.pdb + // |--file + // |--|--GUID + // |--|--|--file.pdb + // TODO: Turn function to Result to handle error if !Path::new(PDBNAME).exists() { download_pdb(); }