Merge pull request #2 from nganhkhoa/device_io_call
This commit is contained in:
commit
5bddf90501
53
Cargo.lock
generated
53
Cargo.lock
generated
@ -48,6 +48,16 @@ name = "cfg-if"
|
|||||||
version = "0.1.10"
|
version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
@ -327,6 +337,18 @@ dependencies = [
|
|||||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "matches"
|
name = "matches"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
@ -416,6 +438,23 @@ dependencies = [
|
|||||||
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_cpus"
|
name = "num_cpus"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
@ -455,17 +494,6 @@ dependencies = [
|
|||||||
"vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parse_pdb_for_offsets"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"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]]
|
[[package]]
|
||||||
name = "pdb"
|
name = "pdb"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@ -1081,6 +1109,7 @@ dependencies = [
|
|||||||
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
|
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
|
||||||
"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
|
"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
|
||||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01"
|
||||||
"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
|
"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
|
||||||
"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
|
"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
|
||||||
"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
|
"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
|
||||||
@ -1125,6 +1154,8 @@ dependencies = [
|
|||||||
"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e"
|
"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e"
|
||||||
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
|
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
|
||||||
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||||
|
"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
|
||||||
|
"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
|
||||||
"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
|
"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
|
||||||
"checksum openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)" = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52"
|
"checksum openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)" = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52"
|
||||||
"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||||
|
11
Cargo.toml
11
Cargo.toml
@ -1,14 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "parse_pdb_for_offsets"
|
name = "lpus"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["nganhkhoa <mail.nganhkhoa@gmail.com>"]
|
authors = ["nganhkhoa <mail.nganhkhoa@gmail.com>"]
|
||||||
|
description = "Live pool tag scanning frontend"
|
||||||
edition = "2018"
|
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]
|
[dependencies]
|
||||||
hex = "0.4.2"
|
hex = "0.4.2"
|
||||||
pdb = "0.5.0"
|
pdb = "0.5.0"
|
||||||
|
chrono = "0.4"
|
||||||
widestring = "0.4.0"
|
widestring = "0.4.0"
|
||||||
winapi = { version = "0.3.8", features = ["libloaderapi", "processthreadsapi", "winbase", "securitybaseapi", "handleapi", "winnt", "winreg"] }
|
winapi = { version = "0.3.8", features = ["libloaderapi", "processthreadsapi", "winbase", "securitybaseapi", "handleapi", "winnt", "winreg", "fileapi", "ioapiset", "winioctl", "errhandlingapi", "sysinfoapi"] }
|
||||||
reqwest = { version = "0.10.1", features = ["blocking"] }
|
reqwest = { version = "0.10.1", features = ["blocking"] }
|
||||||
|
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(())
|
||||||
|
}
|
254
src/driver_state.rs
Normal file
254
src/driver_state.rs
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
// use std::io::{Error, ErrorKind};
|
||||||
|
use std::ffi::c_void;
|
||||||
|
use std::mem::{size_of_val};
|
||||||
|
|
||||||
|
use winapi::shared::ntdef::{NTSTATUS};
|
||||||
|
use winapi::shared::minwindef::{DWORD};
|
||||||
|
use winapi::um::winioctl::{
|
||||||
|
CTL_CODE, FILE_ANY_ACCESS,
|
||||||
|
METHOD_IN_DIRECT, METHOD_OUT_DIRECT, /* METHOD_BUFFERED, */ METHOD_NEITHER
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::pdb_store::{PdbStore, parse_pdb};
|
||||||
|
use crate::windows::{WindowsFFI, WindowsVersion};
|
||||||
|
use crate::ioctl_protocol::{
|
||||||
|
InputData, OffsetData, DerefAddr, ScanPoolData, /* HideProcess, */
|
||||||
|
/* OutputData, */ Nothing
|
||||||
|
};
|
||||||
|
|
||||||
|
type BoxResult<T> = Result<T, Box<dyn Error>>;
|
||||||
|
|
||||||
|
const SIOCTL_TYPE: DWORD = 40000;
|
||||||
|
|
||||||
|
pub fn to_epoch(filetime: u64) -> u64 {
|
||||||
|
let windows_epoch_diff: u64 = 11644473600000 * 10000;
|
||||||
|
if filetime < windows_epoch_diff {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let process_time_epoch: u64 = (filetime - windows_epoch_diff) / 10000;
|
||||||
|
process_time_epoch
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DriverAction {
|
||||||
|
SetupOffset,
|
||||||
|
GetKernelBase,
|
||||||
|
ScanPsActiveHead,
|
||||||
|
ScanPool,
|
||||||
|
ScanPoolRemote,
|
||||||
|
DereferenceAddress,
|
||||||
|
HideProcess
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DriverAction {
|
||||||
|
pub fn get_code(&self) -> DWORD {
|
||||||
|
match self {
|
||||||
|
DriverAction::SetupOffset => CTL_CODE(SIOCTL_TYPE, 0x900, METHOD_IN_DIRECT, FILE_ANY_ACCESS),
|
||||||
|
DriverAction::GetKernelBase => CTL_CODE(SIOCTL_TYPE, 0x901, METHOD_OUT_DIRECT, FILE_ANY_ACCESS),
|
||||||
|
DriverAction::ScanPsActiveHead => CTL_CODE(SIOCTL_TYPE, 0x902, METHOD_NEITHER, FILE_ANY_ACCESS),
|
||||||
|
DriverAction::ScanPool => CTL_CODE(SIOCTL_TYPE, 0x903, METHOD_IN_DIRECT, FILE_ANY_ACCESS),
|
||||||
|
DriverAction::ScanPoolRemote => CTL_CODE(SIOCTL_TYPE, 0x904, METHOD_IN_DIRECT, FILE_ANY_ACCESS),
|
||||||
|
DriverAction::DereferenceAddress => CTL_CODE(SIOCTL_TYPE, 0xA00, METHOD_OUT_DIRECT, FILE_ANY_ACCESS),
|
||||||
|
DriverAction::HideProcess => CTL_CODE(SIOCTL_TYPE, 0xA01, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct EprocessPoolChunk {
|
||||||
|
pub pool_addr: u64,
|
||||||
|
pub eprocess_addr: u64,
|
||||||
|
pub eprocess_name: String,
|
||||||
|
pub create_time: u64,
|
||||||
|
pub exit_time: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for EprocessPoolChunk {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.eprocess_addr == other.eprocess_addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct DriverState {
|
||||||
|
// TODO: Make private, only call methods of DriverState
|
||||||
|
pub pdb_store: PdbStore,
|
||||||
|
pub windows_ffi: WindowsFFI,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DriverState {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
pdb_store: parse_pdb(),
|
||||||
|
windows_ffi: WindowsFFI::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn startup(&mut self) -> NTSTATUS {
|
||||||
|
let s = self.windows_ffi.load_driver();
|
||||||
|
let mut input = InputData {
|
||||||
|
offset_value: OffsetData::new(&self.pdb_store, self.windows_ffi.short_version)
|
||||||
|
};
|
||||||
|
self.windows_ffi.device_io(DriverAction::SetupOffset.get_code(),
|
||||||
|
&mut input, &mut Nothing);
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shutdown(&self) -> NTSTATUS {
|
||||||
|
self.windows_ffi.unload_driver()
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
ntosbase
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scan_active_head(&self, ntosbase: u64) -> BoxResult<Vec<EprocessPoolChunk>> {
|
||||||
|
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, &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>
|
||||||
|
// 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];
|
||||||
|
let end_address = range[1];
|
||||||
|
let mut ptr = start_address;
|
||||||
|
while ptr < end_address {
|
||||||
|
let mut input = InputData {
|
||||||
|
scan_range: ScanPoolData::new(&[ptr, end_address], tag)
|
||||||
|
};
|
||||||
|
self.windows_ffi.device_io(code, &mut input, &mut ptr);
|
||||||
|
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 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deref_addr<T>(&self, addr: u64, outbuf: &mut T) {
|
||||||
|
let code = DriverAction::DereferenceAddress.get_code();
|
||||||
|
let size: usize = size_of_val(outbuf);
|
||||||
|
let mut input = InputData {
|
||||||
|
deref_addr: DerefAddr {
|
||||||
|
addr,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
deref_addr: DerefAddr {
|
||||||
|
addr,
|
||||||
|
size: output_len
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.windows_ffi.device_io_raw(code,
|
||||||
|
&mut input as *mut _ as *mut c_void, size_of_val(&input) as DWORD,
|
||||||
|
outptr as *mut c_void, output_len as DWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_unicode_string(&self, unicode_str_addr: u64) -> BoxResult<String> {
|
||||||
|
let mut strlen = 0u16;
|
||||||
|
let mut bufaddr = 0u64;
|
||||||
|
let buffer_ptr = unicode_str_addr + self.pdb_store.get_offset_r("_UNICODE_STRING.Buffer")?;
|
||||||
|
|
||||||
|
self.deref_addr(unicode_str_addr, &mut strlen);
|
||||||
|
self.deref_addr(buffer_ptr, &mut bufaddr);
|
||||||
|
|
||||||
|
let mut buf = vec![0u8; strlen as usize];
|
||||||
|
self.deref_addr_ptr(bufaddr, buf.as_mut_ptr(), strlen as u64);
|
||||||
|
|
||||||
|
println!("unicode string {:?}", buf);
|
||||||
|
|
||||||
|
Ok(std::str::from_utf8(&buf)?.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_nonpaged_range(&self, ntosbase: u64) -> BoxResult<[u64; 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);
|
||||||
|
|
||||||
|
Ok([first_va, last_va])
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
Err("Windows version for nonpaged pool algorithm is not implemented".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
105
src/ioctl_protocol.rs
Normal file
105
src/ioctl_protocol.rs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
use crate::pdb_store::PdbStore;
|
||||||
|
use crate::windows::WindowsVersion;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct OffsetData {
|
||||||
|
eprocess_name_offset: u64,
|
||||||
|
eprocess_link_offset: u64,
|
||||||
|
list_blink_offset: u64,
|
||||||
|
process_head_offset: u64,
|
||||||
|
mistate_offset: u64,
|
||||||
|
hardware_offset: u64,
|
||||||
|
system_node_offset: u64,
|
||||||
|
first_va_offset: u64,
|
||||||
|
last_va_offset: u64,
|
||||||
|
large_page_table_offset: u64,
|
||||||
|
large_page_size_offset: u64,
|
||||||
|
pool_chunk_size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move to WindowsScanStrategy and return the corresponding struct base on Windows version
|
||||||
|
impl OffsetData {
|
||||||
|
pub fn new(pdb_store: &PdbStore, windows_version: WindowsVersion) -> Self {
|
||||||
|
match windows_version {
|
||||||
|
WindowsVersion::Windows10FastRing => Self {
|
||||||
|
eprocess_name_offset: pdb_store.get_offset("_EPROCESS.ImageFileName").unwrap_or(0u64),
|
||||||
|
eprocess_link_offset: pdb_store.get_offset("_EPROCESS.ActiveProcessLinks").unwrap_or(0u64),
|
||||||
|
list_blink_offset: pdb_store.get_offset("_LIST_ENTRY.Blink").unwrap_or(0u64),
|
||||||
|
process_head_offset: pdb_store.get_offset("PsActiveProcessHead").unwrap_or(0u64),
|
||||||
|
mistate_offset: pdb_store.get_offset("MiState").unwrap_or(0u64),
|
||||||
|
hardware_offset: pdb_store.get_offset("_MI_SYSTEM_INFORMATION.Hardware").unwrap_or(0u64),
|
||||||
|
system_node_offset: pdb_store.get_offset("_MI_HARDWARE_STATE.SystemNodeNonPagedPool").unwrap_or(0u64),
|
||||||
|
first_va_offset: pdb_store.get_offset("_MI_SYSTEM_NODE_NONPAGED_POOL.NonPagedPoolFirstVa").unwrap_or(0u64),
|
||||||
|
last_va_offset: pdb_store.get_offset("_MI_SYSTEM_NODE_NONPAGED_POOL.NonPagedPoolLastVa").unwrap_or(0u64),
|
||||||
|
large_page_table_offset: pdb_store.get_offset("PoolBigPageTable").unwrap_or(0u64),
|
||||||
|
large_page_size_offset: pdb_store.get_offset("PoolBigPageTableSize").unwrap_or(0u64),
|
||||||
|
pool_chunk_size: pdb_store.get_offset("_POOL_HEADER.struct_size").unwrap_or(0u64),
|
||||||
|
},
|
||||||
|
// TODO: Add other version of Windows here
|
||||||
|
_ => Self {
|
||||||
|
eprocess_name_offset: 0u64,
|
||||||
|
eprocess_link_offset: 0u64,
|
||||||
|
list_blink_offset: 0u64,
|
||||||
|
process_head_offset: 0u64,
|
||||||
|
mistate_offset: 0u64,
|
||||||
|
hardware_offset: 0u64,
|
||||||
|
system_node_offset: 0u64,
|
||||||
|
first_va_offset: 0u64,
|
||||||
|
last_va_offset: 0u64,
|
||||||
|
large_page_table_offset: 0u64,
|
||||||
|
large_page_size_offset: 0u64,
|
||||||
|
pool_chunk_size: 0u64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct DerefAddr {
|
||||||
|
pub addr: u64,
|
||||||
|
pub size: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ScanPoolData {
|
||||||
|
pub start: u64,
|
||||||
|
pub end: u64,
|
||||||
|
pub tag: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScanPoolData{
|
||||||
|
pub fn new(arr: &[u64; 2], tag: &[u8; 4]) -> Self {
|
||||||
|
Self {
|
||||||
|
start: arr[0],
|
||||||
|
end: arr[1],
|
||||||
|
tag: u32::from_le_bytes(*tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct HideProcess {
|
||||||
|
pub name: [u8; 15],
|
||||||
|
pub size: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union InputData {
|
||||||
|
pub offset_value: OffsetData,
|
||||||
|
pub deref_addr: DerefAddr,
|
||||||
|
pub scan_range: ScanPoolData,
|
||||||
|
pub hide_process: HideProcess,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Nothing; // for empty data
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union OutputData {
|
||||||
|
pub nothing: Nothing,
|
||||||
|
}
|
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;
|
||||||
|
|
13
src/main.rs
13
src/main.rs
@ -1,13 +0,0 @@
|
|||||||
mod pdb_store;
|
|
||||||
mod windows;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let store = pdb_store::parse_pdb();
|
|
||||||
store.print_default_information();
|
|
||||||
|
|
||||||
let mut windows_ffi = windows::WindowsFFI::new();
|
|
||||||
windows_ffi.print_version();
|
|
||||||
|
|
||||||
println!("NtLoadDriver() -> 0x{:x}", windows_ffi.load_driver());
|
|
||||||
println!("NtUnloadDriver() -> 0x{:x}", windows_ffi.unload_driver());
|
|
||||||
}
|
|
@ -1,3 +1,4 @@
|
|||||||
|
use std::error::Error;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{Read};
|
use std::io::{Read};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@ -29,6 +30,10 @@ pub struct PdbStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PdbStore {
|
impl PdbStore {
|
||||||
|
pub fn get_offset_r(&self, name: &str) -> Result<u64, Box<dyn Error>> {
|
||||||
|
self.get_offset(name)
|
||||||
|
.ok_or(format!("{} is not found in PDB", name).into())
|
||||||
|
}
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn get_offset(&self, name: &str) -> Option<u64> {
|
pub fn get_offset(&self, name: &str) -> Option<u64> {
|
||||||
if name.contains(".") {
|
if name.contains(".") {
|
||||||
@ -52,9 +57,9 @@ impl PdbStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn addr_decompose(&self, addr: u64, full_name: &str) -> Result<u64, String>{
|
pub fn addr_decompose(&self, addr: u64, full_name: &str) -> Result<u64, Box<dyn Error>>{
|
||||||
if !full_name.contains(".") {
|
if !full_name.contains(".") {
|
||||||
return Err("Not decomposable".to_string());
|
return Err("Not decomposable".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut name_part: Vec<&str> = full_name.split_terminator('.').collect();
|
let mut name_part: Vec<&str> = full_name.split_terminator('.').collect();
|
||||||
@ -65,7 +70,7 @@ impl PdbStore {
|
|||||||
Some((memtype, offset)) => {
|
Some((memtype, offset)) => {
|
||||||
if next.len() != 0 {
|
if next.len() != 0 {
|
||||||
if memtype.contains("*") {
|
if memtype.contains("*") {
|
||||||
return Err(format!("Cannot dereference pointer at {} {}", memtype, name_part[1]));
|
return Err(format!("Cannot dereference pointer at {} {}", memtype, name_part[1]).into());
|
||||||
}
|
}
|
||||||
next.insert(0, memtype);
|
next.insert(0, memtype);
|
||||||
self.addr_decompose(addr + *offset, &next.join("."))
|
self.addr_decompose(addr + *offset, &next.join("."))
|
||||||
@ -74,10 +79,10 @@ impl PdbStore {
|
|||||||
Ok(addr + *offset)
|
Ok(addr + *offset)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => Err(format!("Not found member {}", name_part[1]))
|
None => Err(format!("Not found member {}", name_part[1]).into())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => Err(format!("Struct {} not found", name_part[0]))
|
None => Err(format!("Struct {} not found", name_part[0]).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +299,15 @@ pub fn download_pdb() {
|
|||||||
|
|
||||||
pub fn parse_pdb() -> PdbStore {
|
pub fn parse_pdb() -> PdbStore {
|
||||||
// TODO: Detect pdb file and ntoskrnl file version differs
|
// 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() {
|
if !Path::new(PDBNAME).exists() {
|
||||||
download_pdb();
|
download_pdb();
|
||||||
}
|
}
|
||||||
|
176
src/windows.rs
176
src/windows.rs
@ -1,23 +1,33 @@
|
|||||||
use std::ffi::CString;
|
use std::ffi::{c_void, CString};
|
||||||
use widestring::{U16CString};
|
use std::mem::{transmute, size_of_val};
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
use widestring::U16CString;
|
||||||
|
|
||||||
use winapi::shared::ntdef::*;
|
use winapi::shared::ntdef::*;
|
||||||
use winapi::shared::minwindef::{DWORD, HKEY, HMODULE};
|
use winapi::shared::minwindef::{DWORD, HKEY, HMODULE};
|
||||||
use winapi::um::winnt::{
|
use winapi::um::winnt::{
|
||||||
SE_PRIVILEGE_ENABLED, TOKEN_PRIVILEGES, TOKEN_ADJUST_PRIVILEGES, LUID_AND_ATTRIBUTES,
|
SE_PRIVILEGE_ENABLED, TOKEN_PRIVILEGES, TOKEN_ADJUST_PRIVILEGES, LUID_AND_ATTRIBUTES,
|
||||||
REG_DWORD, REG_SZ, REG_OPTION_NON_VOLATILE, KEY_WRITE,
|
REG_DWORD, REG_SZ, REG_OPTION_NON_VOLATILE, KEY_WRITE,
|
||||||
PRTL_OSVERSIONINFOW, OSVERSIONINFOW
|
PRTL_OSVERSIONINFOW, OSVERSIONINFOW,
|
||||||
|
FILE_ATTRIBUTE_NORMAL, GENERIC_READ, GENERIC_WRITE
|
||||||
};
|
};
|
||||||
|
|
||||||
use winapi::um::handleapi::*;
|
use winapi::um::ioapiset::{DeviceIoControl};
|
||||||
use winapi::um::libloaderapi::*;
|
use winapi::um::errhandlingapi::{GetLastError};
|
||||||
use winapi::um::processthreadsapi::*;
|
use winapi::um::fileapi::{CreateFileA, CREATE_ALWAYS};
|
||||||
use winapi::um::securitybaseapi::*;
|
use winapi::um::handleapi::{INVALID_HANDLE_VALUE, CloseHandle};
|
||||||
use winapi::um::winbase::*;
|
use winapi::um::libloaderapi::{LoadLibraryA, GetProcAddress};
|
||||||
use winapi::um::winreg::*;
|
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
|
||||||
|
use winapi::um::sysinfoapi::{GetTickCount64};
|
||||||
|
use winapi::um::securitybaseapi::{AdjustTokenPrivileges};
|
||||||
|
use winapi::um::winbase::{LookupPrivilegeValueA};
|
||||||
|
use winapi::um::winreg::{RegCreateKeyExA, RegSetValueExA, RegCloseKey, HKEY_LOCAL_MACHINE};
|
||||||
|
|
||||||
|
const STR_DRIVER_REGISTRY_PATH: &str = "\\Registry\\Machine\\System\\CurrentControlSet\\Services\\nganhkhoa";
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum WindowsVersion {
|
pub enum WindowsVersion {
|
||||||
Windows10_2015,
|
Windows10_2015,
|
||||||
Windows10_2016,
|
Windows10_2016,
|
||||||
@ -30,36 +40,34 @@ pub enum WindowsVersion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct WindowsFFI {
|
pub struct WindowsFFI {
|
||||||
pub version_info: OSVERSIONINFOW,
|
pub version_info: OSVERSIONINFOW,
|
||||||
pub short_version: WindowsVersion,
|
pub short_version: WindowsVersion,
|
||||||
driver_registry_string: UNICODE_STRING,
|
driver_handle: HANDLE,
|
||||||
ntdll: HMODULE,
|
ntdll: HMODULE,
|
||||||
nt_load_driver: extern "stdcall" fn(PUNICODE_STRING) -> NTSTATUS,
|
nt_load_driver: extern "system" fn(PUNICODE_STRING) -> NTSTATUS,
|
||||||
nt_unload_driver: extern "stdcall" fn(PUNICODE_STRING) -> NTSTATUS,
|
nt_unload_driver: extern "system" fn(PUNICODE_STRING) -> NTSTATUS,
|
||||||
rtl_init_unicode_str: extern "stdcall" fn(PUNICODE_STRING, PCWSTR),
|
rtl_init_unicode_str: extern "system" fn(PUNICODE_STRING, PCWSTR),
|
||||||
rtl_get_version: extern "system" fn(PRTL_OSVERSIONINFOW) -> NTSTATUS,
|
rtl_get_version: extern "system" fn(PRTL_OSVERSIONINFOW) -> NTSTATUS,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowsFFI {
|
impl WindowsFFI {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let str_ntdll = CString::new("ntdll").expect("");
|
let str_ntdll = CString::new("ntdll").unwrap();
|
||||||
let str_nt_load_driver = CString::new("NtLoadDriver").expect("");
|
let str_nt_load_driver = CString::new("NtLoadDriver").unwrap();
|
||||||
let str_nt_unload_driver = CString::new("NtUnloadDriver").expect("");
|
let str_nt_unload_driver = CString::new("NtUnloadDriver").unwrap();
|
||||||
let str_rtl_init_unicode_str = CString::new("RtlInitUnicodeString").expect("");
|
let str_rtl_init_unicode_str = CString::new("RtlInitUnicodeString").unwrap();
|
||||||
let str_rtl_get_version = CString::new("RtlGetVersion").expect("");
|
let str_rtl_get_version = CString::new("RtlGetVersion").unwrap();
|
||||||
let str_se_load_driver_privilege = CString::new("SeLoadDriverPrivilege").expect("");
|
let str_se_load_driver_privilege = CString::new("SeLoadDriverPrivilege").unwrap();
|
||||||
|
|
||||||
let str_driver_path = CString::new("\\SystemRoot\\System32\\DRIVERS\\nganhkhoa.sys").expect("");
|
let str_driver_path = CString::new("\\SystemRoot\\System32\\DRIVERS\\nganhkhoa.sys").unwrap();
|
||||||
let str_registry_path = CString::new("System\\CurrentControlSet\\Services\\nganhkhoa").expect("");
|
let str_registry_path = CString::new("System\\CurrentControlSet\\Services\\nganhkhoa").unwrap();
|
||||||
let str_driver_reg =
|
let str_type = CString::new("Type").unwrap();
|
||||||
U16CString::from_str("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\nganhkhoa").expect("");
|
let str_error_control = CString::new("ErrorControl").unwrap();
|
||||||
let str_type = CString::new("Type").expect("");
|
let str_start = CString::new("Start").unwrap();
|
||||||
let str_error_control = CString::new("ErrorControl").expect("");
|
let str_image_path = CString::new("ImagePath").unwrap();
|
||||||
let str_start = CString::new("Start").expect("");
|
|
||||||
let str_image_path = CString::new("ImagePath").expect("");
|
|
||||||
|
|
||||||
let mut str_driver_reg_unicode = UNICODE_STRING::default();
|
|
||||||
let mut version_info = OSVERSIONINFOW {
|
let mut version_info = OSVERSIONINFOW {
|
||||||
dwOSVersionInfoSize: 0u32,
|
dwOSVersionInfoSize: 0u32,
|
||||||
dwMajorVersion: 0u32,
|
dwMajorVersion: 0u32,
|
||||||
@ -70,9 +78,9 @@ impl WindowsFFI {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let ntdll: HMODULE;
|
let ntdll: HMODULE;
|
||||||
let nt_load_driver: extern "stdcall" fn(PUNICODE_STRING) -> NTSTATUS;
|
let nt_load_driver: extern "system" fn(PUNICODE_STRING) -> NTSTATUS;
|
||||||
let nt_unload_driver: extern "stdcall" fn(PUNICODE_STRING) -> NTSTATUS;
|
let nt_unload_driver: extern "system" fn(PUNICODE_STRING) -> NTSTATUS;
|
||||||
let rtl_init_unicode_str: extern "stdcall" fn(PUNICODE_STRING, PCWSTR);
|
let rtl_init_unicode_str: extern "system" fn(PUNICODE_STRING, PCWSTR);
|
||||||
let rtl_get_version: extern "system" fn(PRTL_OSVERSIONINFOW) -> NTSTATUS;
|
let rtl_get_version: extern "system" fn(PRTL_OSVERSIONINFOW) -> NTSTATUS;
|
||||||
|
|
||||||
// some pointer unsafe C code
|
// some pointer unsafe C code
|
||||||
@ -83,18 +91,18 @@ impl WindowsFFI {
|
|||||||
let rtl_init_unicode_str_ = GetProcAddress(ntdll, str_rtl_init_unicode_str.as_ptr());
|
let rtl_init_unicode_str_ = GetProcAddress(ntdll, str_rtl_init_unicode_str.as_ptr());
|
||||||
let rtl_get_version_ = GetProcAddress(ntdll, str_rtl_get_version.as_ptr());
|
let rtl_get_version_ = GetProcAddress(ntdll, str_rtl_get_version.as_ptr());
|
||||||
|
|
||||||
nt_load_driver = std::mem::transmute(nt_load_driver_);
|
nt_load_driver = transmute(nt_load_driver_);
|
||||||
nt_unload_driver = std::mem::transmute(nt_unload_driver_);
|
nt_unload_driver = transmute(nt_unload_driver_);
|
||||||
rtl_init_unicode_str = std::mem::transmute(rtl_init_unicode_str_);
|
rtl_init_unicode_str = transmute(rtl_init_unicode_str_);
|
||||||
rtl_get_version = std::mem::transmute(rtl_get_version_);
|
rtl_get_version = transmute(rtl_get_version_);
|
||||||
|
|
||||||
// setup registry
|
// setup registry
|
||||||
let mut registry_key: HKEY = std::ptr::null_mut();
|
let mut registry_key: HKEY = null_mut();
|
||||||
RegCreateKeyExA(
|
RegCreateKeyExA(
|
||||||
HKEY_LOCAL_MACHINE, str_registry_path.as_ptr(),
|
HKEY_LOCAL_MACHINE, str_registry_path.as_ptr(),
|
||||||
0, std::ptr::null_mut(),
|
0, null_mut(),
|
||||||
REG_OPTION_NON_VOLATILE, KEY_WRITE,
|
REG_OPTION_NON_VOLATILE, KEY_WRITE,
|
||||||
std::ptr::null_mut(), &mut registry_key, std::ptr::null_mut()
|
null_mut(), &mut registry_key, null_mut()
|
||||||
);
|
);
|
||||||
let type_value: [u8; 4] = 1u32.to_le_bytes();
|
let type_value: [u8; 4] = 1u32.to_le_bytes();
|
||||||
let error_control_value: [u8; 4] = 1u32.to_le_bytes();
|
let error_control_value: [u8; 4] = 1u32.to_le_bytes();
|
||||||
@ -115,10 +123,10 @@ impl WindowsFFI {
|
|||||||
RegCloseKey(registry_key);
|
RegCloseKey(registry_key);
|
||||||
|
|
||||||
// Setup privilege SeLoadDriverPrivilege
|
// Setup privilege SeLoadDriverPrivilege
|
||||||
let mut token_handle: HANDLE = std::ptr::null_mut();
|
let mut token_handle: HANDLE = null_mut();
|
||||||
let mut luid = LUID::default();
|
let mut luid = LUID::default();
|
||||||
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &mut token_handle);
|
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &mut token_handle);
|
||||||
LookupPrivilegeValueA(std::ptr::null_mut(), str_se_load_driver_privilege.as_ptr(), &mut luid);
|
LookupPrivilegeValueA(null_mut(), str_se_load_driver_privilege.as_ptr(), &mut luid);
|
||||||
let mut new_token_state = TOKEN_PRIVILEGES {
|
let mut new_token_state = TOKEN_PRIVILEGES {
|
||||||
PrivilegeCount: 1,
|
PrivilegeCount: 1,
|
||||||
Privileges: [LUID_AND_ATTRIBUTES {
|
Privileges: [LUID_AND_ATTRIBUTES {
|
||||||
@ -127,11 +135,8 @@ impl WindowsFFI {
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
AdjustTokenPrivileges(
|
AdjustTokenPrivileges(
|
||||||
token_handle, 0, &mut new_token_state, 16, std::ptr::null_mut(), std::ptr::null_mut());
|
token_handle, 0, &mut new_token_state, 16, null_mut(), null_mut());
|
||||||
CloseHandle(token_handle);
|
CloseHandle(token_handle);
|
||||||
|
|
||||||
// init string for load and unload driver routine
|
|
||||||
rtl_init_unicode_str(&mut str_driver_reg_unicode, str_driver_reg.as_ptr() as *const u16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rtl_get_version(&mut version_info);
|
rtl_get_version(&mut version_info);
|
||||||
@ -147,7 +152,7 @@ impl WindowsFFI {
|
|||||||
Self {
|
Self {
|
||||||
version_info,
|
version_info,
|
||||||
short_version,
|
short_version,
|
||||||
driver_registry_string: str_driver_reg_unicode,
|
driver_handle: INVALID_HANDLE_VALUE,
|
||||||
ntdll,
|
ntdll,
|
||||||
nt_load_driver,
|
nt_load_driver,
|
||||||
nt_unload_driver,
|
nt_unload_driver,
|
||||||
@ -156,12 +161,40 @@ impl WindowsFFI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_driver(&mut self) -> NTSTATUS {
|
pub fn driver_loaded(self) -> bool {
|
||||||
(self.nt_load_driver)(&mut self.driver_registry_string)
|
self.driver_handle != INVALID_HANDLE_VALUE
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unload_driver(&mut self) -> NTSTATUS {
|
pub fn load_driver(&mut self) -> NTSTATUS {
|
||||||
(self.nt_unload_driver)(&mut self.driver_registry_string)
|
// TODO: Move this to new()
|
||||||
|
// If we move this function to new(), self.driver_handle will be init, and thus no mut here
|
||||||
|
let str_driver_reg = U16CString::from_str(STR_DRIVER_REGISTRY_PATH).unwrap();
|
||||||
|
let mut str_driver_reg_unicode = UNICODE_STRING::default();
|
||||||
|
(self.rtl_init_unicode_str)(&mut str_driver_reg_unicode, str_driver_reg.as_ptr() as *const u16);
|
||||||
|
let status = (self.nt_load_driver)(&mut str_driver_reg_unicode);
|
||||||
|
|
||||||
|
let filename = CString::new("\\\\.\\poolscanner").unwrap();
|
||||||
|
let driver_file_handle: HANDLE = unsafe {
|
||||||
|
CreateFileA(filename.as_ptr(),
|
||||||
|
GENERIC_READ | GENERIC_WRITE,
|
||||||
|
0, null_mut(), CREATE_ALWAYS,
|
||||||
|
FILE_ATTRIBUTE_NORMAL, null_mut())
|
||||||
|
};
|
||||||
|
|
||||||
|
if driver_file_handle == INVALID_HANDLE_VALUE {
|
||||||
|
println!("Driver CreateFileA failed");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.driver_handle = driver_file_handle;
|
||||||
|
}
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unload_driver(&self) -> NTSTATUS {
|
||||||
|
let str_driver_reg = U16CString::from_str(STR_DRIVER_REGISTRY_PATH).unwrap();
|
||||||
|
let mut str_driver_reg_unicode = UNICODE_STRING::default();
|
||||||
|
(self.rtl_init_unicode_str)(&mut str_driver_reg_unicode, str_driver_reg.as_ptr());
|
||||||
|
(self.nt_unload_driver)(&mut str_driver_reg_unicode)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -178,4 +211,47 @@ impl WindowsFFI {
|
|||||||
self.short_version
|
self.short_version
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn valid_process_time(&self, filetime: u64) -> bool {
|
||||||
|
// https://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/
|
||||||
|
let windows_epoch_diff = 11644473600000 * 10000;
|
||||||
|
if filetime < windows_epoch_diff {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let system_up_time_ms = unsafe { GetTickCount64() };
|
||||||
|
let process_time_epoch = (filetime - windows_epoch_diff) / 10000;
|
||||||
|
let now_ms = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_millis() as u64;
|
||||||
|
let system_start_up_time_ms = now_ms - system_up_time_ms;
|
||||||
|
|
||||||
|
if process_time_epoch < system_start_up_time_ms {
|
||||||
|
false
|
||||||
|
} else if process_time_epoch > now_ms {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn device_io<T, E>(&self, code: DWORD, inbuf: &mut T, outbuf: &mut E) -> DWORD {
|
||||||
|
self.device_io_raw(code,
|
||||||
|
inbuf as *mut _ as *mut c_void, size_of_val(inbuf) as DWORD,
|
||||||
|
outbuf as *mut _ as *mut c_void, size_of_val(outbuf) as DWORD)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn device_io_raw(&self, code: DWORD,
|
||||||
|
input_ptr: *mut c_void, input_len: DWORD,
|
||||||
|
output_ptr: *mut c_void, output_len: DWORD) -> DWORD {
|
||||||
|
// println!("driver loaded: {}; device_io_code: {}", self.driver_loaded(), code);
|
||||||
|
let mut bytes_returned: DWORD = 0;
|
||||||
|
unsafe {
|
||||||
|
let status = DeviceIoControl(self.driver_handle, code,
|
||||||
|
input_ptr, input_len,
|
||||||
|
output_ptr, output_len,
|
||||||
|
&mut bytes_returned, null_mut());
|
||||||
|
if status == 0 {
|
||||||
|
println!("device io failed: last error {}", GetLastError());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
bytes_returned
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user