Update base code for windows 7, 8, 8.1

Because the tag is different in lower version of Windows, need to
change the tag in scan algorithm

4b29cf1986/volatility/framework/plugins/windows/poolscanner.py (L229)
This commit is contained in:
nganhkhoa 2020-06-30 04:09:13 +07:00
parent abb7a70b72
commit 8cb553eb11
7 changed files with 136 additions and 17 deletions

View File

@ -1,2 +1,5 @@
[target.x86_64-pc-windows-msvc]
# CRT static to make run on machine without VC++
# https://github.com/rust-lang/rust/pull/66801#issuecomment-558947376
# >> "-Clink-args=/subsystem:console,5.02"
rustflags = ["-Ctarget-feature=+crt-static"]

View File

@ -1,6 +1,16 @@
# LPUS (A live pool-tag scanning solution)
This is the frontend to the live pool tag scanning solution, the backend is a driver (which is now closed source).
This is the frontend to the live pool tag scanning solution, the backend is a
driver (which is now closed source).
Works on Windows 7 and above (Vista not tested, but 7 ok and 10 ok), and on x64
systems only. (I hardcoded the address as u64 so only 64 systems should run this).
> The binary is runable, without crashing. But I still need to add some
manual instructions on referencing the structs and offset on some places.
> Windows 10, versions 2018, 2019 and 2020 is tested and works.
Windows XP is not supported: Windows XP Win32Api is missing here and there.
## How this works
@ -21,7 +31,7 @@ use lpus::{
fn main() -> Result<(), Box<dyn Error>> {
let mut driver = DriverState::new();
println!("NtLoadDriver() -> 0x{:x}", driver.startup());
driver.scan_pool(b"Tag ", |pool_addr, header, data_addr| {
driver.scan_pool(b"Tag ", "_STRUCT_NAME", |pool_addr, header, data_addr| {
})?;
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
}
@ -33,3 +43,67 @@ Parsing the struct data is up to you.
You can use `driver.deref_addr(addr, &value)` to dereference an address in kernel space
and `driver.pdb_store.get_offset_r("offset")?` to get an offset from PDB file.
We also have a set of functions for scanning a specific tag/object.
- `pub fn scan_eprocess(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn scan_file(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn scan_ethread(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn scan_mutant(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn scan_driver(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn scan_kernel_module(driver: &DriverState) -> BoxResult<Vec<Value>>`
And a list traversing the kernel object:
- `pub fn traverse_loadedmodulelist(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn traverse_activehead(driver: &DriverState) -> BoxResult<Vec<Value>>`
- missing symbols `pub fn traverse_afdendpoint(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn traverse_kiprocesslist(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn traverse_handletable(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn traverse_unloadeddrivers(driver: &DriverState) -> BoxResult<Vec<Value>>`
## Things to note
Right now, we only have one symbol file of ntoskrnl.exe. While we may need more
symbols, kernel32.sys, win32k.sys, tcpis.sys... This will be a future update
where symbols are combined into one big `HashMap` but still retain the module. I
haven't tested the debug symbols of others binary, I wonder if the PDB file even
exists.
The pdb file is not restricted in ntoskrnl.exe, I might need to split to a
smaller module or such.
Also the symbols list is parsed directly from the PDB file, but some structs
(like the callback routine members or network structs) are missing. Right now a
simple hardcoded to add in a struct member is used, but it would break if the
OS running have a different layout.
The HashMap of symbols/struct is now using string and u32 to store member
offset and types, this should be changed into something that would be type-safe
and more functional.
I also follow a few Volatility implementation on Rootkit, The art of Memory
forensics Chapter 13. Scanning in Windows 10 yields promising result, though I
haven't tested on any malware to see if we can have the "same" result.
At the pace of development, I seperate the binary to functionalities for
testing, I would add a CLI and a REPL.
One last thing, the backend doesn't have any check on address referencing, so
one may get a blue screen, eventhough I tried to avoid it, I'm not 100% sure it
would not crash the system.
## Future works
- [ ] An interactive repl (1)
- [ ] More kernel modules symbols (2)
- [ ] Implementation of more technique (reference Volatility here)
- [ ] Quick and easy way to add manual struct, symbols (3)
(1) This is quite hard to work out, because we have to make the *types* works.
The currently chosen repl is based on Lisp, because lisp is cool. If the repl
is online, we can combine everything into one binary.
(2) We may need to download it all and combine to one `HashMap`, with their
types as a specific struct. (Try to avoid string).
(3) Have no idea on this.

View File

@ -160,9 +160,12 @@ impl fmt::Display for Address {
if let Some(p) = &self.pointer {
write!(f, "*({}) + 0x{:x}", *p, self.offset)
}
else {
else if self.offset != 0 {
write!(f, "0x{:x} + 0x{:x}", self.base, self.offset)
}
else {
write!(f, "0x{:x}", self.base)
}
}
}

View File

@ -191,13 +191,12 @@ impl DriverState {
}
let data_addr = Address::from_base(pool_addr.address() + pool_header_size);
let success = handler(pool_addr, &header, data_addr)?;
if success {
ptr += chunk_size; /* skip this chunk */
ptr += chunk_size; // skip this chunk
}
else {
ptr += 0x4; /* search next */
ptr += 0x4; // search next
}
}
Ok(true)
@ -298,7 +297,7 @@ impl DriverState {
pub fn get_nonpaged_range(&self, ntosbase: &Address) -> BoxResult<[Address; 2]> {
// TODO: Add support for other Windows version here
match self.windows_ffi.short_version {
WindowsVersion::Windows10FastRing => {
WindowsVersion::WindowsFastRing => {
let mistate = ntosbase.clone() + self.pdb_store.get_offset_r("MiState")?;
let path_first_va: String = vec![
"_MI_SYSTEM_INFORMATION",
@ -335,6 +334,13 @@ impl DriverState {
let last_va = Address::from_base(self.decompose(&mistate, &path_last_va)?);
Ok([first_va, last_va])
},
WindowsVersion::Windows7 => {
let path_first_va = ntosbase.clone() + self.pdb_store.get_offset_r("MmNonPagedPoolStart")?;
let path_last_va = ntosbase.clone() + self.pdb_store.get_offset_r("MiNonPagedPoolEnd")?;
let first_va = Address::from_base(self.deref_addr_new(path_first_va.address()));
let last_va = Address::from_base(self.deref_addr_new(path_last_va.address()));
Ok([first_va, last_va])
},
_ => {
Err("Windows version for nonpaged pool algorithm is not implemented".into())
}

View File

@ -21,8 +21,10 @@ pub struct OffsetData {
// 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 {
// TODO: Fix the backend so that only neccessary fields are used
// This is too much, most of the functionality has been move to the frontend
match windows_version {
WindowsVersion::Windows10FastRing => Self {
WindowsVersion::WindowsFastRing => 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),
@ -51,6 +53,20 @@ impl OffsetData {
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),
},
WindowsVersion::Windows7 => 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.SystemNodeInformation").unwrap_or(0u64),
first_va_offset: pdb_store.get_offset("_MI_SYSTEM_NODE_INFORMATION.NonPagedPoolFirstVa").unwrap_or(0u64),
last_va_offset: pdb_store.get_offset("_MI_SYSTEM_NODE_INFORMATION.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
// TODO: Warn user of unknown windows version, because BSOD will occur
_ => Self {

View File

@ -143,10 +143,13 @@ pub fn scan_eprocess(driver: &DriverState) -> BoxResult<Vec<Value>> {
let eprocess_ptr = &try_eprocess_ptr;
println!("EPROCESS: 0x{:x}", eprocess_ptr.address());
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 unicode_str_ptr = driver.address_of(eprocess_ptr, "_EPROCESS.ImageFilePointer.FileName")
.unwrap_or(0); // ImageFilePointer is Windows 10+
let eprocess_name =
if let Ok(name) = from_utf8(&image_name) {
@ -525,7 +528,8 @@ pub fn traverse_activehead(driver: &DriverState) -> BoxResult<Vec<Value>> {
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 unicode_str_ptr = driver.address_of(&eprocess_ptr, "_EPROCESS.ImageFilePointer.FileName")
.unwrap_or(0);
let eprocess_name =
if let Ok(name) = from_utf8(&image_name) {
@ -608,7 +612,8 @@ pub fn traverse_kiprocesslist(driver: &DriverState) -> BoxResult<Vec<Value>> {
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 unicode_str_ptr = driver.address_of(&eprocess_ptr, "_EPROCESS.ImageFilePointer.FileName")
.unwrap_or(0);
let eprocess_name =
if let Ok(name) = from_utf8(&image_name) {
@ -652,7 +657,8 @@ pub fn traverse_handletable(driver: &DriverState) -> BoxResult<Vec<Value>> {
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 unicode_str_ptr = driver.address_of(&eprocess_ptr, "_EPROCESS.ImageFilePointer.FileName")
.unwrap_or(0);
let eprocess_name =
if let Ok(name) = from_utf8(&image_name) {

View File

@ -29,14 +29,17 @@ const STR_DRIVER_REGISTRY_PATH: &str = "\\Registry\\Machine\\System\\CurrentCont
#[allow(dead_code)]
#[derive(Debug, Copy, Clone)]
pub enum WindowsVersion {
Windows7,
Windows8,
Windows10Legacy,
Windows10_2015,
Windows10_2016,
Windows10_2017,
Windows10_2018,
Windows10_2019,
Windows10_2020,
Windows10FastRing,
Windows10VersionUnknown
WindowsFastRing,
WindowsUnknown
}
#[allow(dead_code)]
@ -142,11 +145,19 @@ impl WindowsFFI {
rtl_get_version(&mut version_info);
let short_version = match version_info.dwBuildNumber {
// 2600 => WindowsVersion::WindowsXP,
// 6000 | 6001 | 6002 => WindowsVersion::WindowsVista,
7600 | 7601 => WindowsVersion::Windows7,
9200 | 9600 => WindowsVersion::Windows8,
10240 => WindowsVersion::Windows10Legacy,
10586 => WindowsVersion::Windows10_2015,
14393 => WindowsVersion::Windows10_2016,
15063 | 16299 => WindowsVersion::Windows10_2017,
17134 | 17763 => WindowsVersion::Windows10_2018,
18362 | 18363 => WindowsVersion::Windows10_2019,
18363 | 18362 => WindowsVersion::Windows10_2019,
19041 => WindowsVersion::Windows10_2020,
_ if version_info.dwBuildNumber >= 19536 => WindowsVersion::Windows10FastRing,
_ => WindowsVersion::Windows10VersionUnknown
x if x >= 19536 => WindowsVersion::WindowsFastRing,
_ => WindowsVersion::WindowsUnknown
};
Self {