multiple binary and code refactor
This commit is contained in:
parent
3214e79d63
commit
dae10a5312
24
Cargo.lock
generated
24
Cargo.lock
generated
@ -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"
|
||||
|
@ -1,10 +1,14 @@
|
||||
[package]
|
||||
name = "parse_pdb_for_offsets"
|
||||
name = "lpus"
|
||||
version = "0.1.0"
|
||||
authors = ["nganhkhoa <mail.nganhkhoa@gmail.com>"]
|
||||
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"
|
||||
|
73
src/bin/eprocess_scan.rs
Normal file
73
src/bin/eprocess_scan.rs
Normal file
@ -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::<Utc>::from(d);
|
||||
let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S.%f").to_string();
|
||||
timestamp_str
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
// 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<EprocessPoolChunk> = 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(())
|
||||
}
|
||||
|
12
src/bin/print_pdb.rs
Normal file
12
src/bin/print_pdb.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use std::error::Error;
|
||||
|
||||
use lpus::{
|
||||
driver_state::{DriverState}
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let driver = DriverState::new();
|
||||
driver.windows_ffi.print_version();
|
||||
driver.pdb_store.print_default_information();
|
||||
Ok(())
|
||||
}
|
@ -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<u64> {
|
||||
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<Vec<EprocessPoolChunk>> {
|
||||
@ -144,9 +142,14 @@ impl DriverState {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn scan_pool<F>(&self, ntosbase: u64, tag: [u8; 4], mut handler: F) -> BoxResult<bool>
|
||||
where F: FnMut(&DriverState, u64) -> BoxResult<bool>
|
||||
pub fn scan_pool<F>(&self, tag: &[u8; 4], mut handler: F) -> BoxResult<bool>
|
||||
where F: FnMut(u64, &[u8], u64) -> 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: 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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
7
src/lib.rs
Normal file
7
src/lib.rs
Normal file
@ -0,0 +1,7 @@
|
||||
extern crate chrono;
|
||||
|
||||
pub mod pdb_store;
|
||||
pub mod windows;
|
||||
pub mod ioctl_protocol;
|
||||
pub mod driver_state;
|
||||
|
124
src/main.rs
124
src/main.rs
@ -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::<Utc>::from(d);
|
||||
// let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S.%f").to_string();
|
||||
// timestamp_str
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
// 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<EprocessPoolChunk> = 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<EprocessPoolChunk> = 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(())
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user