Generalize the API for common scan and return json
This commit is contained in:
parent
060f222c0a
commit
1707b301ff
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -218,11 +218,6 @@ dependencies = [
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.0"
|
||||
@ -354,10 +349,10 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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)",
|
||||
"serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"widestring 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -764,7 +759,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.48"
|
||||
version = "1.0.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -988,7 +983,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -1080,7 +1075,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.4.0"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -1166,7 +1161,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum h2 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9433d71e471c1736fd5a61b671fc0b148d7a2992f666c958d03cd8feb3b88d1"
|
||||
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
||||
"checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772"
|
||||
"checksum hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
|
||||
"checksum http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b"
|
||||
"checksum http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b"
|
||||
"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
|
||||
@ -1226,7 +1220,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
||||
"checksum serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25"
|
||||
"checksum serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226"
|
||||
"checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
|
||||
"checksum shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c"
|
||||
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||
@ -1263,7 +1257,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum wasm-bindgen-webidl 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "ef012a0d93fc0432df126a8eaf547b2dce25a8ce9212e1d3cbeef5c11157975d"
|
||||
"checksum web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "aaf97caf6aa8c2b1dac90faf0db529d9d63c93846cca4911856f78a83cebf53b"
|
||||
"checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164"
|
||||
"checksum widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6"
|
||||
"checksum widestring 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a763e303c0e0f23b0da40888724762e802a8ffefbc22de4127ef42493c2ea68c"
|
||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
|
@ -12,9 +12,9 @@ doctest = false
|
||||
|
||||
[dependencies]
|
||||
app_dirs = "1.2.1"
|
||||
hex = "0.4.2"
|
||||
pdb = "0.5.0"
|
||||
chrono = "0.4"
|
||||
widestring = "0.4.0"
|
||||
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"] }
|
||||
serde_json = "1.0.55"
|
||||
|
21
src/bin/driver_scan.rs
Normal file
21
src/bin/driver_scan.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use std::error::Error;
|
||||
|
||||
use lpus::{
|
||||
driver_state::{DriverState},
|
||||
scan_driver
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let mut driver = DriverState::new();
|
||||
println!("NtLoadDriver() -> 0x{:x}", driver.startup());
|
||||
|
||||
let result = scan_driver(&driver).unwrap_or(Vec::new());
|
||||
|
||||
for r in result.iter() {
|
||||
println!("{:#}", r.to_string());
|
||||
}
|
||||
|
||||
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,85 +1,19 @@
|
||||
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 */},
|
||||
address::Address
|
||||
driver_state::{DriverState},
|
||||
scan_eprocess
|
||||
};
|
||||
|
||||
#[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", "_EPROCESS", |pool_addr, header, data_addr| {
|
||||
let chunk_size = (header[2] as u64) * 16u64;
|
||||
let result = scan_eprocess(&driver).unwrap_or(Vec::new());
|
||||
|
||||
let eprocess_size = driver.pdb_store.get_offset_r("_EPROCESS.struct_size")?;
|
||||
|
||||
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());
|
||||
|
||||
while try_eprocess_ptr <= eprocess_valid_end {
|
||||
let create_time: u64 = driver.decompose(&try_eprocess_ptr, "_EPROCESS.CreateTime")?;
|
||||
if driver.windows_ffi.valid_process_time(create_time) {
|
||||
break;
|
||||
for r in result.iter() {
|
||||
println!("{:#}", r.to_string());
|
||||
}
|
||||
try_eprocess_ptr += 0x4; // search exhaustively
|
||||
}
|
||||
if try_eprocess_ptr > eprocess_valid_end {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let eprocess_ptr = &try_eprocess_ptr;
|
||||
|
||||
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")?;
|
||||
|
||||
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: {} | eprocess: {} | pid: {} | ppid: {} | name: {} | path: {}",
|
||||
pool_addr, eprocess_ptr, pid, ppid, eprocess_name, binary_path);
|
||||
// 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(true)
|
||||
})?;
|
||||
|
||||
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
|
||||
Ok(())
|
||||
|
@ -1,127 +0,0 @@
|
||||
use std::error::Error;
|
||||
use chrono::Utc;
|
||||
use chrono::{DateTime};
|
||||
use std::time::{UNIX_EPOCH, Duration};
|
||||
|
||||
use lpus::{
|
||||
driver_state::{DriverState}
|
||||
};
|
||||
|
||||
#[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());
|
||||
|
||||
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_valid_start = &data_addr;
|
||||
let ethread_valid_end = (pool_addr.clone() + chunk_size) - ethread_size;
|
||||
let mut try_ethread_ptr = ethread_valid_start.clone();
|
||||
|
||||
while try_ethread_ptr <= ethread_valid_end {
|
||||
let create_time: u64 = driver.decompose(&try_ethread_ptr, "_ETHREAD.CreateTime")?;
|
||||
if driver.windows_ffi.valid_process_time(create_time) {
|
||||
break;
|
||||
}
|
||||
try_ethread_ptr += 0x4; // search exhaustively
|
||||
}
|
||||
if try_ethread_ptr > ethread_valid_end {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let ethread_ptr = &try_ethread_ptr;
|
||||
|
||||
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)
|
||||
})?;
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
|
@ -1,48 +1,19 @@
|
||||
use std::error::Error;
|
||||
|
||||
use lpus::{
|
||||
driver_state::{DriverState}
|
||||
driver_state::{DriverState},
|
||||
scan_file
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let mut driver = DriverState::new();
|
||||
println!("NtLoadDriver() -> 0x{:x}", driver.startup());
|
||||
|
||||
driver.scan_pool(b"File", "_FILE_OBJECT", |pool_addr, header, data_addr| {
|
||||
let chunk_size = (header[2] as u64) * 16u64;
|
||||
let result = scan_file(&driver).unwrap_or(Vec::new());
|
||||
|
||||
let fob_size = driver.pdb_store.get_offset_r("_FILE_OBJECT.struct_size")?;
|
||||
let valid_end = (pool_addr.clone() + chunk_size) - fob_size;
|
||||
let mut try_ptr = data_addr;
|
||||
|
||||
while try_ptr <= valid_end {
|
||||
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;
|
||||
for r in result.iter() {
|
||||
println!("{:#}", r.to_string());
|
||||
}
|
||||
try_ptr += 0x4; // search exhaustively
|
||||
}
|
||||
if try_ptr > valid_end {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
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(unicode_str_ptr, true) {
|
||||
println!(" {}", filename);
|
||||
}
|
||||
else {
|
||||
println!(" [NOT A VALID _UNICODE_STRING]");
|
||||
}
|
||||
Ok(true)
|
||||
})?;
|
||||
|
||||
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
|
||||
Ok(())
|
||||
|
@ -2,7 +2,6 @@ use std::error::Error;
|
||||
|
||||
use lpus::{
|
||||
driver_state::{DriverState},
|
||||
address::Address
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
26
src/bin/thread_scan.rs
Normal file
26
src/bin/thread_scan.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use std::error::Error;
|
||||
|
||||
use lpus::{
|
||||
driver_state::{DriverState},
|
||||
scan_ethread, /* scan_mutant */
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let mut driver = DriverState::new();
|
||||
println!("NtLoadDriver() -> 0x{:x}", driver.startup());
|
||||
|
||||
let threads = scan_ethread(&driver).unwrap_or(Vec::new());
|
||||
// let mutants = scan_mutant(&driver).unwrap_or(Vec::new());
|
||||
|
||||
for r in threads.iter() {
|
||||
println!("{:#}", r.to_string());
|
||||
}
|
||||
// for r in mutants.iter() {
|
||||
// println!("{:#}", r.to_string());
|
||||
// }
|
||||
|
||||
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -265,6 +265,10 @@ impl DriverState {
|
||||
}
|
||||
|
||||
pub fn get_unicode_string(&self, unicode_str_addr: u64, deref: bool) -> BoxResult<String> {
|
||||
if unicode_str_addr == 0 {
|
||||
return Err("Not a valid address".into());
|
||||
}
|
||||
|
||||
let mut strlen = 0u16;
|
||||
let mut capacity = 0u16;
|
||||
let mut bufaddr = 0u64;
|
||||
|
279
src/lib.rs
279
src/lib.rs
@ -7,3 +7,282 @@ pub mod ioctl_protocol;
|
||||
pub mod driver_state;
|
||||
pub mod address;
|
||||
|
||||
use std::error::Error;
|
||||
use std::str::{from_utf8};
|
||||
use serde_json::{json, Value};
|
||||
use driver_state::DriverState;
|
||||
use address::Address;
|
||||
|
||||
type BoxResult<T> = Result<T, Box<dyn Error>>;
|
||||
|
||||
pub fn scan_eprocess(driver: &DriverState) -> BoxResult<Vec<Value>> {
|
||||
let mut result: Vec<Value> = Vec::new();
|
||||
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_valid_start = &data_addr;
|
||||
let eprocess_valid_end = (pool_addr.clone() + chunk_size) - eprocess_size;
|
||||
let mut try_eprocess_ptr = eprocess_valid_start.clone();
|
||||
|
||||
while try_eprocess_ptr <= eprocess_valid_end {
|
||||
let create_time: u64 = driver.decompose(&try_eprocess_ptr, "_EPROCESS.CreateTime")?;
|
||||
if driver.windows_ffi.valid_process_time(create_time) {
|
||||
break;
|
||||
}
|
||||
try_eprocess_ptr += 0x4; // search exhaustively
|
||||
}
|
||||
if try_eprocess_ptr > eprocess_valid_end {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let eprocess_ptr = &try_eprocess_ptr;
|
||||
|
||||
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")?;
|
||||
|
||||
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 = driver.get_unicode_string(unicode_str_ptr, true)
|
||||
.unwrap_or("".to_string());
|
||||
|
||||
result.push(json!({
|
||||
"pool": format!("0x{:x}", pool_addr.address()),
|
||||
"address": format!("0x{:x}", eprocess_ptr.address()),
|
||||
"type": "_EPROCESS",
|
||||
"pid": pid,
|
||||
"ppid": ppid,
|
||||
"name": eprocess_name,
|
||||
"path": binary_path
|
||||
}));
|
||||
Ok(true)
|
||||
})?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn scan_file(driver: &DriverState) -> BoxResult<Vec<Value>> {
|
||||
let mut result: Vec<Value> = Vec::new();
|
||||
|
||||
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 valid_end = (pool_addr.clone() + chunk_size) - fob_size;
|
||||
let mut try_ptr = data_addr;
|
||||
|
||||
while try_ptr <= valid_end {
|
||||
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 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let fob_addr = &try_ptr;
|
||||
let read_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.ReadAccess")?;
|
||||
let write_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.WriteAccess")?;
|
||||
let delete_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.DeleteAccess")?;
|
||||
let share_read_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.SharedRead")?;
|
||||
let share_write_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.SharedWrite")?;
|
||||
let share_delete_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.SharedDelete")?;
|
||||
let filename_ptr = driver.address_of(fob_addr, "_FILE_OBJECT.FileName")?;
|
||||
let devicename_ptr: u64 = driver.address_of(fob_addr, "_FILE_OBJECT.DeviceObject.DriverObject.DriverName")?;
|
||||
let hardware_ptr: u64 = driver.decompose(fob_addr, "_FILE_OBJECT.DeviceObject.DriverObject.HardwareDatabase")?;
|
||||
|
||||
let filename =
|
||||
if read_ok == 0 {
|
||||
"[NOT READABLE]".to_string()
|
||||
}
|
||||
else if let Ok(n) = driver.get_unicode_string(filename_ptr, true) {
|
||||
n
|
||||
}
|
||||
else {
|
||||
"[NOT A VALID _UNICODE_STRING]".to_string()
|
||||
};
|
||||
let devicename = driver.get_unicode_string(devicename_ptr, true)
|
||||
.unwrap_or("".to_string());
|
||||
let hardware = driver.get_unicode_string(hardware_ptr, true)
|
||||
.unwrap_or("".to_string());
|
||||
result.push(json!({
|
||||
"pool": format!("0x{:x}", pool_addr.address()),
|
||||
"address": format!("0x{:x}", fob_addr.address()),
|
||||
"type": "_FILE_OBJECT",
|
||||
"path": filename,
|
||||
"device": devicename,
|
||||
"hardware": hardware,
|
||||
"access": {
|
||||
"r": read_ok == 1,
|
||||
"w": write_ok == 1,
|
||||
"d": delete_ok == 1,
|
||||
"R": share_read_ok == 1,
|
||||
"W": share_write_ok == 1,
|
||||
"D": share_delete_ok == 1
|
||||
}
|
||||
}));
|
||||
Ok(true)
|
||||
})?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn scan_ethread(driver: &DriverState) -> BoxResult<Vec<Value>> {
|
||||
let mut result: Vec<Value> = Vec::new();
|
||||
|
||||
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_valid_start = &data_addr;
|
||||
let ethread_valid_end = (pool_addr.clone() + chunk_size) - ethread_size;
|
||||
let mut try_ethread_ptr = ethread_valid_start.clone();
|
||||
|
||||
while try_ethread_ptr <= ethread_valid_end {
|
||||
let create_time: u64 = driver.decompose(&try_ethread_ptr, "_ETHREAD.CreateTime")?;
|
||||
if driver.windows_ffi.valid_process_time(create_time) {
|
||||
break;
|
||||
}
|
||||
try_ethread_ptr += 0x4; // search exhaustively
|
||||
}
|
||||
if try_ethread_ptr > ethread_valid_end {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let ethread_ptr = &try_ethread_ptr;
|
||||
|
||||
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 let Ok(name) = driver.get_unicode_string(unicode_str_ptr, true) {
|
||||
name
|
||||
}
|
||||
else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
result.push(json!({
|
||||
"pool": format!("0x{:x}", pool_addr.address()),
|
||||
"address": format!("0x{:x}", ethread_ptr.address()),
|
||||
"type": "_ETHREAD",
|
||||
"pid": pid,
|
||||
"tid": tid,
|
||||
"name": thread_name
|
||||
}));
|
||||
Ok(true)
|
||||
})?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// Unstable, do not use
|
||||
pub fn scan_mutant(driver: &DriverState) -> BoxResult<Vec<Value>> {
|
||||
let mut result: Vec<Value> = Vec::new();
|
||||
|
||||
let ntosbase = driver.get_kernel_base();
|
||||
let [start, end] = driver.get_nonpaged_range(&ntosbase)?;
|
||||
|
||||
driver.scan_pool(b"Muta", "_KMUTANT", |pool_addr, header, data_addr| {
|
||||
let chunk_size = (header[2] as u64) * 16u64;
|
||||
|
||||
let kmutant_size = driver.pdb_store.get_offset_r("_KMUTANT.struct_size")?;
|
||||
|
||||
let kmutant_valid_start = data_addr;
|
||||
let kmutant_valid_end = (pool_addr.clone() + chunk_size) - kmutant_size;
|
||||
let mut try_kmutant_ptr = kmutant_valid_start.clone();
|
||||
|
||||
while try_kmutant_ptr <= kmutant_valid_end {
|
||||
// TODO: Stronger constrain
|
||||
let kthread_ptr = driver.address_of(&try_kmutant_ptr, "_KMUTANT.OwnerThread")?;
|
||||
if kthread_ptr > start.address() && kthread_ptr < end.address() {
|
||||
break;
|
||||
}
|
||||
try_kmutant_ptr += 0x4; // search exhaustively
|
||||
}
|
||||
if try_kmutant_ptr > kmutant_valid_end {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let kmutant_ptr = try_kmutant_ptr;
|
||||
let ethread_ptr = Address::from_base(driver.address_of(&kmutant_ptr, "_KMUTANT.OwnerThread")?);
|
||||
|
||||
let pid: u64 = driver.decompose(ðread_ptr, "_ETHREAD.Cid.UniqueProcess")?;
|
||||
let tid: u64 = driver.decompose(ðread_ptr, "_ETHREAD.Cid.UniqueThread")?;
|
||||
let unicode_str_ptr: u64 = driver.address_of(ðread_ptr, "_ETHREAD.ThreadName")?;
|
||||
|
||||
let thread_name =
|
||||
if let Ok(name) = driver.get_unicode_string(unicode_str_ptr, true) {
|
||||
name
|
||||
}
|
||||
else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
result.push(json!({
|
||||
"pool": format!("0x{:x}", pool_addr.address()),
|
||||
"address": format!("0x{:x}", ethread_ptr.address()),
|
||||
"type": "_KMUTANT",
|
||||
"pid": pid,
|
||||
"tid": tid,
|
||||
"name": thread_name
|
||||
}));
|
||||
Ok(true)
|
||||
})?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn scan_driver(driver: &DriverState) -> BoxResult<Vec<Value>> {
|
||||
let mut result: Vec<Value> = Vec::new();
|
||||
|
||||
driver.scan_pool(b"Driv", "_DRIVER_OBJECT", |pool_addr, header, data_addr| {
|
||||
let chunk_size = (header[2] as u64) * 16u64;
|
||||
|
||||
let dob_size = driver.pdb_store.get_offset_r("_DRIVER_OBJECT.struct_size")?;
|
||||
let valid_end = (pool_addr.clone() + chunk_size) - dob_size;
|
||||
let mut try_ptr = data_addr;
|
||||
|
||||
while try_ptr <= valid_end {
|
||||
// No documentation on type constrain
|
||||
// let ftype: u16 = driver.decompose(&try_ptr, "_DRIVER_OBJECT.Type")?;
|
||||
let size: u16 = driver.decompose(&try_ptr, "_DRIVER_OBJECT.Size")?;
|
||||
if (size as u64) == dob_size /* && ftype == 5u16 */ {
|
||||
break;
|
||||
}
|
||||
try_ptr += 0x4; // search exhaustively
|
||||
}
|
||||
if try_ptr > valid_end {
|
||||
return Ok(false);
|
||||
}
|
||||
let dob_addr = &try_ptr;
|
||||
|
||||
let devicename_ptr = driver.address_of(dob_addr, "_DRIVER_OBJECT.DriverName")?;
|
||||
let hardware_ptr: u64 = driver.decompose(dob_addr, "_DRIVER_OBJECT.HardwareDatabase")?;
|
||||
|
||||
let devicename = driver.get_unicode_string(devicename_ptr, true)
|
||||
.unwrap_or("".to_string());
|
||||
let hardware = driver.get_unicode_string(hardware_ptr, true)
|
||||
.unwrap_or("".to_string());
|
||||
result.push(json!({
|
||||
"pool": format!("0x{:x}", pool_addr.address()),
|
||||
"address": format!("0x{:x}", dob_addr.address()),
|
||||
"type": "_DRIVER_OBJECT",
|
||||
"device": devicename,
|
||||
"hardware": hardware
|
||||
}));
|
||||
Ok(true)
|
||||
})?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user