From f872b8e14a71e51f5661e0060e038bb6d4569cc8 Mon Sep 17 00:00:00 2001 From: nganhkhoa Date: Mon, 24 Feb 2020 00:10:00 +0700 Subject: [PATCH] moved functions to modules --- src/main.rs | 501 ++++------------------------------------------- src/pdb_store.rs | 353 +++++++++++++++++++++++++++++++++ src/windows.rs | 177 +++++++++++++++++ 3 files changed, 565 insertions(+), 466 deletions(-) create mode 100644 src/pdb_store.rs create mode 100644 src/windows.rs diff --git a/src/main.rs b/src/main.rs index 8a4a13a..183bfaf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,478 +1,47 @@ extern crate reqwest; - -use std::io; -use std::io::Read; -use std::fs::File; +use std::io::{Write}; use std::path::Path; -use std::collections::HashMap; +use std::net::TcpListener; +use std::thread; -use pdb::PDB; -use pdb::SymbolData; -use pdb::TypeData; -use pdb::ClassType; -use pdb::ModifierType; -use pdb::Rva; - -use pdb::FallibleIterator; -use pdb::TypeFinder; -use pdb::TypeIndex; - -use std::ffi::CString; -use widestring::{U16CString}; -use winapi::shared::minwindef::{HKEY}; -use winapi::shared::ntdef::*; -use winapi::um::winnt::{ - SE_PRIVILEGE_ENABLED, TOKEN_PRIVILEGES, TOKEN_ADJUST_PRIVILEGES, LUID_AND_ATTRIBUTES, - REG_DWORD, REG_SZ, REG_OPTION_NON_VOLATILE, KEY_WRITE -}; -use winapi::um::winreg::*; -use winapi::um::handleapi::*; -use winapi::um::winbase::*; -use winapi::um::processthreadsapi::*; -use winapi::um::libloaderapi::*; -use winapi::um::securitybaseapi::*; - -const PDBNAME: &str = "ntkrnlmp.pdb"; -const NTOSKRNL_PATH: &str = "C:\\Windows\\System32\\ntoskrnl.exe"; -const PDB_SERVER_PATH: &str = "http://msdl.microsoft.com/download/symbols"; - -fn get_type_as_str(type_finder: &TypeFinder, typ: &TypeIndex) -> String { - match type_finder.find(*typ).unwrap().parse().unwrap() { - TypeData::Class(ct) => { - format!("{}", ct.name.to_string()) - }, - TypeData::Primitive(pt) => { - format!("{:?}", pt.kind) - }, - TypeData::Pointer(pt) => { - format!("{}*", get_type_as_str(type_finder, &pt.underlying_type)) - }, - TypeData::StaticMember(st) => { - format!("static {}", get_type_as_str(type_finder, &st.field_type)) - }, - TypeData::Array(at) => { - format!("{}{:?}", - get_type_as_str(type_finder, &at.element_type), /* get_type_as_str(type_finder, &at.indexing_type), */ at.dimensions) - }, - // TypeData::Enumeration(et) => { - // format!("enumeration") - // }, - // TypeData::Enumerate(et) => { - // format!("enumerate") - // }, - // TypeData::MemberFunction(mft) => { - // format!("member function") - // }, - // TypeData::OverloadedMethod(ovmt) => { - // format!("overloaded method") - // }, - // TypeData::Nested(nt) => { - // format!("nested") - // }, - // TypeData::BaseClass(bct) => { - // format!("base class") - // }, - // TypeData::VirtualBaseClass(vbct) => { - // format!("virtual base class") - // }, - // TypeData::VirtualFunctionTablePointer(vftpt) => { - // format!("virtual function table pointer") - // }, - TypeData::Procedure(pt) => { - let rettype = match pt.return_type { - Some(rt) => get_type_as_str(type_finder, &rt), - _ => "UNKNOWN".to_string() - }; - format!("{}({})", rettype, get_type_as_str(type_finder, &pt.argument_list)) - }, - TypeData::Modifier(mt) => { - match mt { - ModifierType { constant: true, volatile: true, unaligned: true, .. } => - format!("const volatile unaligned {}", get_type_as_str(type_finder, &mt.underlying_type)), - ModifierType { constant: true, volatile: true, unaligned: false, .. } => - format!("const volatile {}", get_type_as_str(type_finder, &mt.underlying_type)), - ModifierType { constant: true, volatile: false, unaligned: true, .. } => - format!("const unaligned {}", get_type_as_str(type_finder, &mt.underlying_type)), - ModifierType { constant: false, volatile: true, unaligned: true, .. } => - format!("volatile unaligned {}", get_type_as_str(type_finder, &mt.underlying_type)), - ModifierType { constant: true, volatile: false, unaligned: false, .. } => - format!("const {}", get_type_as_str(type_finder, &mt.underlying_type)), - ModifierType { constant: false, volatile: true, unaligned: false, .. } => - format!("volatile {}", get_type_as_str(type_finder, &mt.underlying_type)), - ModifierType { constant: false, volatile: false, unaligned: true, .. } => - format!("unaligned {}", get_type_as_str(type_finder, &mt.underlying_type)), - _ => format!("modifier {}", get_type_as_str(type_finder, &mt.underlying_type)) - } - }, - // TypeData::Union(ut) => { - // format!("union") - // }, - // TypeData::Bitfield(bft) => { - // format!("bitfield") - // }, - TypeData::FieldList(_flt) => { - format!("fieldlist") - }, - // TypeData::ArgumentList(alt) => { - // format!("arglist") - // }, - // TypeData::MethodList(mlt) => { - // format!("methodlist") - // }, - unk => { - match unk.name() { - Some(s) => format!("{}", s.to_string()), - _ => "UNNOWN".to_string() - } - } - } -} - -type SymbolStore = HashMap; -type StructStore = HashMap>; - -struct PdbStore { - pub symbols: SymbolStore, - pub structs: StructStore -} - -impl PdbStore { - fn get_offset(&self, name: &str) -> Option { - if name.contains(".") { - let v: Vec<&str> = name.split_terminator('.').collect(); - match self.structs.get(v[0]) { - Some(member_info) => { - match member_info.get(v[1]) { - Some((_memtype, offset)) => Some(*offset), - None => None - } - }, - None => None - } - } - else { - match self.symbols.get(name) { - Some(offset) => Some(*offset), - None => None - } - } - } - - fn addr_decompose(&self, addr: u64, full_name: &str) -> Result{ - if !full_name.contains(".") { - return Err("Not decomposable".to_string()); - } - - let mut name_part: Vec<&str> = full_name.split_terminator('.').collect(); - let mut next: Vec<_> = name_part.drain(2..).collect(); - match self.structs.get(name_part[0]) { - Some(member_info) => { - match member_info.get(name_part[1]) { - Some((memtype, offset)) => { - if next.len() != 0 { - if memtype.contains("*") { - return Err(format!("Cannot dereference pointer at {} {}", memtype, name_part[1])); - } - next.insert(0, memtype); - self.addr_decompose(addr + *offset, &next.join(".")) - } - else { - Ok(addr + *offset) - } - }, - None => Err(format!("Not found member {}", name_part[1])) - } - }, - None => Err(format!("Struct {} not found", name_part[0])) - } - } - - fn default_information(&self) { - let need_symbols = [ - "PsLoadedModuleList", "PsActiveProcessHead", "KeNumberNodes", - "PoolBigPageTable", "PoolBigPageTableSize", - // "PoolVector", "ExpNumberOfNonPagedPools", - "KdDebuggerDataBlock", "MmNonPagedPoolStart", "MmNonPagedPoolEnd", // Windows XP - "MiNonPagedPoolStartAligned", "MiNonPagedPoolEnd", "MiNonPagedPoolBitMap", // Windows 7, 8 - "MiNonPagedPoolBitMap", "MiNonPagedPoolVaBitMap", - "MiState" // Windows 10 - ]; - - let mut need_structs = HashMap::new(); - need_structs.insert("_POOL_HEADER", vec![]); - need_structs.insert("_PEB", vec![]); - need_structs.insert("_LIST_ENTRY", vec![ - "Flink", "Blink" - ]); - need_structs.insert("_FILE_OBJECT", vec![ - "FileName" - ]); - need_structs.insert("_EPROCESS", vec![ - "struct_size", - "UniqueProcessId", "ActiveProcessLinks", "CreateTime", - "Peb", "ImageFilePointer", "ImageFileName", "ThreadListHead" - ]); - need_structs.insert("_KDDEBUGGER_DATA64", vec![ - "MmNonPagedPoolStart", "MmNonPagedPoolEnd", // Windows XP - ]); - need_structs.insert("_POOL_TRACKER_BIG_PAGES", vec![]); - - // these struct supports finding NonPagedPool{First,Last}Va in windows 10 - need_structs.insert("_MI_SYSTEM_INFORMATION", vec![ - "Hardware", // windows 10 2016+ - "SystemNodeInformation" // windows 10 2015 - ]); - need_structs.insert("_MI_HARDWARE_STATE", vec![ - "SystemNodeInformation", // till windows 10 1900 - "SystemNodeNonPagedPool" // windows insider, 2020 - ]); - need_structs.insert("_MI_SYSTEM_NODE_INFORMATION", vec![ // till windows 10 1900 - "NonPagedPoolFirstVa", "NonPagedPoolLastVa", - "NonPagedBitMap", // missing on windows 10 1900+ - "DynamicBitMapNonPagedPool" // some weird field - ]); - need_structs.insert("_MI_SYSTEM_NODE_NONPAGED_POOL", vec![ // windows insider, 2020 - "NonPagedPoolFirstVa", "NonPagedPoolLastVa", - "DynamicBitMapNonPagedPool" // some weird field - ]); - need_structs.insert("_MI_DYNAMIC_BITMAP", vec![]); - need_structs.insert("_RTL_BITMAP", vec![]); // windows 10 until 2020 - need_structs.insert("_RTL_BITMAP_EX", vec![]); // windows insider, 2020 - - for &symbol in &need_symbols { - match self.symbols.get(symbol) { - Some(offset) => println!("0x{:x} {}", offset, symbol), - None => {} - } - } - - for (&struct_name, members) in &need_structs { - match self.structs.get(struct_name) { - Some(member_info) => { - for &member in members { - match member_info.get(member) { - Some((memtype, offset)) => - println!("0x{:x} {} {}.{}", offset, memtype, struct_name, member), - None => {} - } - } - }, - None => {} - } - } - } -} - -fn parse_pdb() -> PdbStore { - let f = File::open("ntkrnlmp.pdb").expect("No such file ./ntkrnlmp.pdb"); - let mut pdb = PDB::open(f).expect("Cannot open as a PDB file"); - - let info = pdb.pdb_information().expect("Cannot get pdb information"); - let dbi = pdb.debug_information().expect("cannot get debug information"); - println!("PDB for {}, guid: {}, age: {}\n", dbi.machine_type().unwrap(), info.guid, dbi.age().unwrap_or(0)); - - let type_information = pdb.type_information().expect("Cannot get type information"); - let mut type_finder = type_information.type_finder(); - let mut iter = type_information.iter(); - while let Some(_typ) = iter.next().unwrap() { - type_finder.update(&iter); - } - - let mut symbol_extracted: SymbolStore = HashMap::new(); - - // find global symbols offset - let addr_map = pdb.address_map().expect("Cannot get address map"); - let glosym = pdb.global_symbols().expect("Cannot get global symbols"); - let mut symbols = glosym.iter(); - while let Some(symbol) = symbols.next().unwrap() { - match symbol.parse() { - Ok(SymbolData::PublicSymbol(data)) => { - let name = symbol.name().unwrap().to_string(); - let Rva(rva) = data.offset.to_rva(&addr_map).unwrap_or_default(); - symbol_extracted.insert(format!("{}", name), rva as u64); - }, - _ => { - // println!("Something else"); - } - } - } - - // println!("{:?}", symbol_extracted); - - let mut struct_extracted: StructStore = HashMap::new(); - - iter = type_information.iter(); - while let Some(typ) = iter.next().unwrap() { - // type_finder.update(&iter); - match typ.parse() { - Ok(TypeData::Class(ClassType {name, fields: Some(fields), size, ..})) => { - let mut struct_fields = HashMap::new(); - struct_fields.insert("struct_size".to_string(), ("u32".to_string(), size as u64)); - match type_finder.find(fields).unwrap().parse().unwrap() { - TypeData::FieldList(list) => { - // `fields` is a Vec - for field in list.fields { - if let TypeData::Member(member) = field { - let mem_typ = get_type_as_str(&type_finder, &member.field_type); - // println!("\t0x{:x} {} {}", member.offset, mem_typ, member.name); - struct_fields.insert( - format!("{}", member.name), (mem_typ, member.offset as u64)); - } else { - } - } - } - _ => {} - } - struct_extracted.insert(format!("{}", name), struct_fields); - // println!("endstruct\n"); - }, - _ => {} - } - } - // println!("{:?}", struct_extracted); - PdbStore { - symbols: symbol_extracted, - structs: struct_extracted - } -} - -fn download_pdb() { - let mut ntoskrnl = File::open(NTOSKRNL_PATH).expect("Cannot open ntoskrnl.exe"); - - let mut buffer = Vec::new(); - ntoskrnl.read_to_end(&mut buffer).expect("Cannot read file ntoskrnl.exe"); - - let mut buffiter = buffer.chunks(4); - while buffiter.next().unwrap() != [0x52, 0x53, 0x44, 0x53] { - // signature == RSDS - } - - // next 16 bytes is guid in raw bytes - let raw_guid: Vec = vec![ - buffiter.next().unwrap(), - buffiter.next().unwrap(), - buffiter.next().unwrap(), - buffiter.next().unwrap(), - ].concat(); - - // guid to hex string - let guid = (vec![ - raw_guid[3], raw_guid[2], raw_guid[1], raw_guid[0], - raw_guid[5], raw_guid[4], - raw_guid[7], raw_guid[6], - raw_guid[8], raw_guid[9], raw_guid[10], raw_guid[11], - raw_guid[12], raw_guid[13], raw_guid[14], raw_guid[15], - ].iter().map(|b| format!("{:02X}", b)).collect::>()).join(""); - - // next 4 bytes is age, in little endian - let raw_age = buffiter.next().unwrap(); - let age = u32::from_le_bytes([ - raw_age[0], raw_age[1], raw_age[2], raw_age[3] - ]); - - let downloadurl = format!("{}/{}/{}{:X}/{}", PDB_SERVER_PATH, PDBNAME, guid, age, PDBNAME); - println!("{}", downloadurl); - - let mut resp = reqwest::blocking::get(&downloadurl).expect("request failed"); - let mut out = File::create(PDBNAME).expect("failed to create file"); - io::copy(&mut resp, &mut out).expect("failed to copy content"); -} +mod pdb_store; +mod windows; fn main() { - if !Path::new(PDBNAME).exists() { - download_pdb(); + if !Path::new(pdb_store::PDBNAME).exists() { + pdb_store::download_pdb(); } - let store = parse_pdb(); - store.default_information(); + let store = pdb_store::parse_pdb(); + store.print_default_information(); - match store.get_offset("MiState") { - Some(offset) => println!("0x{:x} MiState", offset), - None => {} - }; - match store.get_offset("_MI_HARDWARE_STATE.SystemNodeNonPagedPool") { - Some(offset) => println!("0x{:x} _MI_HARDWARE_STATE.SystemNodeNonPagedPool", offset), - None => {} - }; - match store.addr_decompose(0xfffff8005d44f200, "_MI_SYSTEM_INFORMATION.Hardware.SystemNodeNonPagedPool") { - Ok(offset) => - println!("0x{:x} == ((_MI_SYSTEM_INFORMATION)0xfffff8005d44f200).Hardware.SystemNodeNonPagedPool", offset), - Err(msg) => println!("{}", msg) - }; + // match store.get_offset("MiState") { + // Some(offset) => println!("0x{:x} MiState", offset), + // None => {} + // }; + // match store.get_offset("_MI_HARDWARE_STATE.SystemNodeNonPagedPool") { + // Some(offset) => println!("0x{:x} _MI_HARDWARE_STATE.SystemNodeNonPagedPool", offset), + // None => {} + // }; + // match store.addr_decompose(0xfffff8005d44f200, "_MI_SYSTEM_INFORMATION.Hardware.SystemNodeNonPagedPool") { + // Ok(offset) => + // println!("0x{:x} == ((_MI_SYSTEM_INFORMATION)0xfffff8005d44f200).Hardware.SystemNodeNonPagedPool", offset), + // Err(msg) => println!("{}", msg) + // }; - let str_ntdll = CString::new("ntdll").expect(""); - let str_nt_load_driver = CString::new("NtLoadDriver").expect(""); - let str_nt_unload_driver = CString::new("NtUnloadDriver").expect(""); - let str_rtl_init_unicode_str = CString::new("RtlInitUnicodeString").expect(""); - let str_se_load_driver_privilege = CString::new("SeLoadDriverPrivilege").expect(""); + let mut windows_ffi = windows::WindowsFFI::new(); + windows_ffi.print_version(); - let str_driver_path = CString::new("\\SystemRoot\\System32\\DRIVERS\\nganhkhoa.sys").expect(""); - let str_registry_path = CString::new("System\\CurrentControlSet\\Services\\nganhkhoa").expect(""); - let str_driver_reg = - U16CString::from_str("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\nganhkhoa").expect(""); - let str_type = CString::new("Type").expect(""); - let str_error_control = CString::new("ErrorControl").expect(""); - let str_start = CString::new("Start").expect(""); - let str_image_path = CString::new("ImagePath").expect(""); + println!("NtLoadDriver() -> 0x{:x}", windows_ffi.load_driver()); + println!("NtUnloadDriver() -> 0x{:x}", windows_ffi.unload_driver()); - let mut str_driver_reg_unicode = UNICODE_STRING::default(); - let nt_load_driver: extern "stdcall" fn(PUNICODE_STRING) -> NTSTATUS; - let nt_unload_driver: extern "stdcall" fn(PUNICODE_STRING) -> NTSTATUS; - let rtl_init_unicode_str: extern "stdcall" fn(PUNICODE_STRING, PCWSTR); - - unsafe { - let ntdll = LoadLibraryA(str_ntdll.as_ptr()); - let nt_load_driver_ = GetProcAddress(ntdll, str_nt_load_driver.as_ptr()); - let nt_unload_driver_ = GetProcAddress(ntdll, str_nt_unload_driver.as_ptr()); - let rtl_init_unicode_str_ = GetProcAddress(ntdll, str_rtl_init_unicode_str.as_ptr()); - - nt_load_driver = std::mem::transmute(nt_load_driver_); - nt_unload_driver = std::mem::transmute(nt_unload_driver_); - rtl_init_unicode_str = std::mem::transmute(rtl_init_unicode_str_); - - // setup registry - let mut registry_key: HKEY = std::ptr::null_mut(); - RegCreateKeyExA( - HKEY_LOCAL_MACHINE, str_registry_path.as_ptr(), - 0, std::ptr::null_mut(), - REG_OPTION_NON_VOLATILE, KEY_WRITE, - std::ptr::null_mut(), &mut registry_key, std::ptr::null_mut() - ); - let type_value: [u8; 4] = 1u32.to_le_bytes(); - let error_control_value: [u8; 4] = 1u32.to_le_bytes(); - let start_value: [u8; 4] = 3u32.to_le_bytes(); - let registry_values = [ - (str_type.as_ptr(), REG_DWORD, type_value.as_ptr(), 4), - (str_error_control.as_ptr(), REG_DWORD, error_control_value.as_ptr(), 4), - (str_start.as_ptr(), REG_DWORD, start_value.as_ptr(), 4), - (str_image_path.as_ptr(), REG_SZ, str_driver_path.as_ptr() as *const u8, str_driver_path.to_bytes().len() + 1) - ]; - for &(key, keytype, value_ptr, size_in_bytes) in ®istry_values { - RegSetValueExA( - registry_key, key, 0, - keytype, value_ptr, size_in_bytes as u32 - ); - } - RegCloseKey(registry_key); - - // Setup privilege SeLoadDriverPrivilege - let mut token_handle: HANDLE = std::ptr::null_mut(); - let mut luid = LUID::default(); - OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &mut token_handle); - LookupPrivilegeValueA(std::ptr::null_mut(), str_se_load_driver_privilege.as_ptr(), &mut luid); - let mut new_token_state = TOKEN_PRIVILEGES { - PrivilegeCount: 1, - Privileges: [LUID_AND_ATTRIBUTES { - Luid: luid, - Attributes: SE_PRIVILEGE_ENABLED - }] - }; - AdjustTokenPrivileges(token_handle, 0, &mut new_token_state, 16, std::ptr::null_mut(), std::ptr::null_mut()); - CloseHandle(token_handle); - - rtl_init_unicode_str(&mut str_driver_reg_unicode, str_driver_reg.as_ptr() as *const u16); - } - - println!("NtLoadDriver() -> 0x{:x}", nt_load_driver(&mut str_driver_reg_unicode)); - println!("NtUnloadDriver() -> 0x{:x}", nt_unload_driver(&mut str_driver_reg_unicode)); + // let listener = TcpListener::bind("127.0.0.1:8989").expect("Cannot bind to port 8989"); + // println!("listening started, ready to accept"); + // for stream in listener.incoming() { + // thread::spawn(|| { + // println!("Connection received"); + // let mut stream = stream.unwrap(); + // stream.write(b"Hello World\r\n").unwrap(); + // }); + // } } diff --git a/src/pdb_store.rs b/src/pdb_store.rs new file mode 100644 index 0000000..ae8b309 --- /dev/null +++ b/src/pdb_store.rs @@ -0,0 +1,353 @@ +use std::io; +use std::io::{Read}; +use std::fs::File; +use std::collections::HashMap; + +use pdb::PDB; +use pdb::SymbolData; +use pdb::TypeData; +use pdb::ClassType; +use pdb::ModifierType; +use pdb::Rva; + +use pdb::FallibleIterator; +use pdb::TypeFinder; +use pdb::TypeIndex; + + +pub const PDBNAME: &str = "ntkrnlmp.pdb"; +pub const NTOSKRNL_PATH: &str = "C:\\Windows\\System32\\ntoskrnl.exe"; +pub const PDB_SERVER_PATH: &str = "http://msdl.microsoft.com/download/symbols"; + +type SymbolStore = HashMap; +type StructStore = HashMap>; + +pub struct PdbStore { + pub symbols: SymbolStore, + pub structs: StructStore +} + +impl PdbStore { + pub fn get_offset(&self, name: &str) -> Option { + if name.contains(".") { + let v: Vec<&str> = name.split_terminator('.').collect(); + match self.structs.get(v[0]) { + Some(member_info) => { + match member_info.get(v[1]) { + Some((_memtype, offset)) => Some(*offset), + None => None + } + }, + None => None + } + } + else { + match self.symbols.get(name) { + Some(offset) => Some(*offset), + None => None + } + } + } + + pub fn addr_decompose(&self, addr: u64, full_name: &str) -> Result{ + if !full_name.contains(".") { + return Err("Not decomposable".to_string()); + } + + let mut name_part: Vec<&str> = full_name.split_terminator('.').collect(); + let mut next: Vec<_> = name_part.drain(2..).collect(); + match self.structs.get(name_part[0]) { + Some(member_info) => { + match member_info.get(name_part[1]) { + Some((memtype, offset)) => { + if next.len() != 0 { + if memtype.contains("*") { + return Err(format!("Cannot dereference pointer at {} {}", memtype, name_part[1])); + } + next.insert(0, memtype); + self.addr_decompose(addr + *offset, &next.join(".")) + } + else { + Ok(addr + *offset) + } + }, + None => Err(format!("Not found member {}", name_part[1])) + } + }, + None => Err(format!("Struct {} not found", name_part[0])) + } + } + + pub fn print_default_information(&self) { + let need_symbols = [ + "PsLoadedModuleList", "PsActiveProcessHead", "KeNumberNodes", + "PoolBigPageTable", "PoolBigPageTableSize", + // "PoolVector", "ExpNumberOfNonPagedPools", + "KdDebuggerDataBlock", "MmNonPagedPoolStart", "MmNonPagedPoolEnd", // Windows XP + "MiNonPagedPoolStartAligned", "MiNonPagedPoolEnd", "MiNonPagedPoolBitMap", // Windows 7, 8 + "MiNonPagedPoolBitMap", "MiNonPagedPoolVaBitMap", + "MiState" // Windows 10 + ]; + + let mut need_structs = HashMap::new(); + need_structs.insert("_POOL_HEADER", vec![]); + need_structs.insert("_PEB", vec![]); + need_structs.insert("_LIST_ENTRY", vec![ + "Flink", "Blink" + ]); + need_structs.insert("_FILE_OBJECT", vec![ + "FileName" + ]); + need_structs.insert("_EPROCESS", vec![ + "struct_size", + "UniqueProcessId", "ActiveProcessLinks", "CreateTime", + "Peb", "ImageFilePointer", "ImageFileName", "ThreadListHead" + ]); + need_structs.insert("_KDDEBUGGER_DATA64", vec![ + "MmNonPagedPoolStart", "MmNonPagedPoolEnd", // Windows XP + ]); + need_structs.insert("_POOL_TRACKER_BIG_PAGES", vec![]); + + // these struct supports finding NonPagedPool{First,Last}Va in windows 10 + need_structs.insert("_MI_SYSTEM_INFORMATION", vec![ + "Hardware", // windows 10 2016+ + "SystemNodeInformation" // windows 10 2015 + ]); + need_structs.insert("_MI_HARDWARE_STATE", vec![ + "SystemNodeInformation", // till windows 10 1900 + "SystemNodeNonPagedPool" // windows insider, 2020 + ]); + need_structs.insert("_MI_SYSTEM_NODE_INFORMATION", vec![ // till windows 10 1900 + "NonPagedPoolFirstVa", "NonPagedPoolLastVa", + "NonPagedBitMap", // missing on windows 10 1900+ + "DynamicBitMapNonPagedPool" // some weird field + ]); + need_structs.insert("_MI_SYSTEM_NODE_NONPAGED_POOL", vec![ // windows insider, 2020 + "NonPagedPoolFirstVa", "NonPagedPoolLastVa", + "DynamicBitMapNonPagedPool" // some weird field + ]); + need_structs.insert("_MI_DYNAMIC_BITMAP", vec![]); + need_structs.insert("_RTL_BITMAP", vec![]); // windows 10 until 2020 + need_structs.insert("_RTL_BITMAP_EX", vec![]); // windows insider, 2020 + + for &symbol in &need_symbols { + match self.symbols.get(symbol) { + Some(offset) => println!("0x{:x} {}", offset, symbol), + None => {} + } + } + + for (&struct_name, members) in &need_structs { + match self.structs.get(struct_name) { + Some(member_info) => { + for &member in members { + match member_info.get(member) { + Some((memtype, offset)) => + println!("0x{:x} {} {}.{}", offset, memtype, struct_name, member), + None => {} + } + } + }, + None => {} + } + } + } +} + +fn get_type_as_str(type_finder: &TypeFinder, typ: &TypeIndex) -> String { + match type_finder.find(*typ).unwrap().parse().unwrap() { + TypeData::Class(ct) => { + format!("{}", ct.name.to_string()) + }, + TypeData::Primitive(pt) => { + format!("{:?}", pt.kind) + }, + TypeData::Pointer(pt) => { + format!("{}*", get_type_as_str(type_finder, &pt.underlying_type)) + }, + TypeData::StaticMember(st) => { + format!("static {}", get_type_as_str(type_finder, &st.field_type)) + }, + TypeData::Array(at) => { + format!("{}{:?}", + get_type_as_str(type_finder, &at.element_type), /* get_type_as_str(type_finder, &at.indexing_type), */ at.dimensions) + }, + // TypeData::Enumeration(et) => { + // format!("enumeration") + // }, + // TypeData::Enumerate(et) => { + // format!("enumerate") + // }, + // TypeData::MemberFunction(mft) => { + // format!("member function") + // }, + // TypeData::OverloadedMethod(ovmt) => { + // format!("overloaded method") + // }, + // TypeData::Nested(nt) => { + // format!("nested") + // }, + // TypeData::BaseClass(bct) => { + // format!("base class") + // }, + // TypeData::VirtualBaseClass(vbct) => { + // format!("virtual base class") + // }, + // TypeData::VirtualFunctionTablePointer(vftpt) => { + // format!("virtual function table pointer") + // }, + TypeData::Procedure(pt) => { + let rettype = match pt.return_type { + Some(rt) => get_type_as_str(type_finder, &rt), + _ => "UNKNOWN".to_string() + }; + format!("{}({})", rettype, get_type_as_str(type_finder, &pt.argument_list)) + }, + TypeData::Modifier(mt) => { + match mt { + ModifierType { constant: true, volatile: true, unaligned: true, .. } => + format!("const volatile unaligned {}", get_type_as_str(type_finder, &mt.underlying_type)), + ModifierType { constant: true, volatile: true, unaligned: false, .. } => + format!("const volatile {}", get_type_as_str(type_finder, &mt.underlying_type)), + ModifierType { constant: true, volatile: false, unaligned: true, .. } => + format!("const unaligned {}", get_type_as_str(type_finder, &mt.underlying_type)), + ModifierType { constant: false, volatile: true, unaligned: true, .. } => + format!("volatile unaligned {}", get_type_as_str(type_finder, &mt.underlying_type)), + ModifierType { constant: true, volatile: false, unaligned: false, .. } => + format!("const {}", get_type_as_str(type_finder, &mt.underlying_type)), + ModifierType { constant: false, volatile: true, unaligned: false, .. } => + format!("volatile {}", get_type_as_str(type_finder, &mt.underlying_type)), + ModifierType { constant: false, volatile: false, unaligned: true, .. } => + format!("unaligned {}", get_type_as_str(type_finder, &mt.underlying_type)), + _ => format!("modifier {}", get_type_as_str(type_finder, &mt.underlying_type)) + } + }, + // TypeData::Union(ut) => { + // format!("union") + // }, + // TypeData::Bitfield(bft) => { + // format!("bitfield") + // }, + TypeData::FieldList(_flt) => { + format!("fieldlist") + }, + // TypeData::ArgumentList(alt) => { + // format!("arglist") + // }, + // TypeData::MethodList(mlt) => { + // format!("methodlist") + // }, + unk => { + match unk.name() { + Some(s) => format!("{}", s.to_string()), + _ => "UNNOWN".to_string() + } + } + } +} + +pub fn parse_pdb() -> PdbStore { + let f = File::open("ntkrnlmp.pdb").expect("No such file ./ntkrnlmp.pdb"); + let mut pdb = PDB::open(f).expect("Cannot open as a PDB file"); + + let info = pdb.pdb_information().expect("Cannot get pdb information"); + let dbi = pdb.debug_information().expect("cannot get debug information"); + println!("PDB for {}, guid: {}, age: {}\n", + dbi.machine_type().unwrap(), info.guid, dbi.age().unwrap_or(0)); + + let type_information = pdb.type_information().expect("Cannot get type information"); + let mut type_finder = type_information.type_finder(); + let mut iter = type_information.iter(); + while let Some(_typ) = iter.next().unwrap() { + type_finder.update(&iter); + } + + let mut symbol_extracted: SymbolStore = HashMap::new(); + let addr_map = pdb.address_map().expect("Cannot get address map"); + let glosym = pdb.global_symbols().expect("Cannot get global symbols"); + let mut symbols = glosym.iter(); + while let Some(symbol) = symbols.next().unwrap() { + match symbol.parse() { + Ok(SymbolData::PublicSymbol(data)) => { + let name = symbol.name().unwrap().to_string(); + let Rva(rva) = data.offset.to_rva(&addr_map).unwrap_or_default(); + symbol_extracted.insert(format!("{}", name), rva as u64); + }, + _ => { + } + } + } + + let mut struct_extracted: StructStore = HashMap::new(); + iter = type_information.iter(); + while let Some(typ) = iter.next().unwrap() { + match typ.parse() { + Ok(TypeData::Class(ClassType {name, fields: Some(fields), size, ..})) => { + let mut struct_fields = HashMap::new(); + struct_fields.insert("struct_size".to_string(), ("u32".to_string(), size as u64)); + match type_finder.find(fields).unwrap().parse().unwrap() { + TypeData::FieldList(list) => { + for field in list.fields { + if let TypeData::Member(member) = field { + let mem_typ = get_type_as_str(&type_finder, &member.field_type); + struct_fields.insert( + format!("{}", member.name), (mem_typ, member.offset as u64)); + } + } + } + _ => {} + } + struct_extracted.insert(format!("{}", name), struct_fields); + }, + _ => {} + } + } + + PdbStore { + symbols: symbol_extracted, + structs: struct_extracted + } +} + +pub fn download_pdb() { + let mut ntoskrnl = File::open(NTOSKRNL_PATH).expect("Cannot open ntoskrnl.exe"); + + let mut buffer = Vec::new(); + ntoskrnl.read_to_end(&mut buffer).expect("Cannot read file ntoskrnl.exe"); + + let mut buffiter = buffer.chunks(4); + while buffiter.next().unwrap() != [0x52, 0x53, 0x44, 0x53] { + // signature == RSDS + } + + // next 16 bytes is guid in raw bytes + let raw_guid: Vec = vec![ + buffiter.next().unwrap(), + buffiter.next().unwrap(), + buffiter.next().unwrap(), + buffiter.next().unwrap(), + ].concat(); + + // guid to hex string + let guid = (vec![ + raw_guid[3], raw_guid[2], raw_guid[1], raw_guid[0], + raw_guid[5], raw_guid[4], + raw_guid[7], raw_guid[6], + raw_guid[8], raw_guid[9], raw_guid[10], raw_guid[11], + raw_guid[12], raw_guid[13], raw_guid[14], raw_guid[15], + ].iter().map(|b| format!("{:02X}", b)).collect::>()).join(""); + + // next 4 bytes is age, in little endian + let raw_age = buffiter.next().unwrap(); + let age = u32::from_le_bytes([ + raw_age[0], raw_age[1], raw_age[2], raw_age[3] + ]); + + let downloadurl = format!("{}/{}/{}{:X}/{}", PDB_SERVER_PATH, PDBNAME, guid, age, PDBNAME); + println!("{}", downloadurl); + + let mut resp = reqwest::blocking::get(&downloadurl).expect("request failed"); + let mut out = File::create(PDBNAME).expect("failed to create file"); + io::copy(&mut resp, &mut out).expect("failed to copy content"); +} + diff --git a/src/windows.rs b/src/windows.rs new file mode 100644 index 0000000..0775e8d --- /dev/null +++ b/src/windows.rs @@ -0,0 +1,177 @@ +use std::ffi::CString; +use widestring::{U16CString}; + +use winapi::shared::ntdef::*; +use winapi::shared::minwindef::{DWORD, HKEY, HMODULE}; +use winapi::um::winnt::{ + SE_PRIVILEGE_ENABLED, TOKEN_PRIVILEGES, TOKEN_ADJUST_PRIVILEGES, LUID_AND_ATTRIBUTES, + REG_DWORD, REG_SZ, REG_OPTION_NON_VOLATILE, KEY_WRITE, + PRTL_OSVERSIONINFOW, OSVERSIONINFOW +}; + +use winapi::um::handleapi::*; +use winapi::um::libloaderapi::*; +use winapi::um::processthreadsapi::*; +use winapi::um::securitybaseapi::*; +use winapi::um::winbase::*; +use winapi::um::winreg::*; + +#[derive(Debug)] +pub enum WindowsVersion { + Windows10_2015, + Windows10_2016, + Windows10_2017, + Windows10_2018, + Windows10_2019, + Windows10_2020, + Windows10FastRing, + Windows10VersionUnknown +} + +pub struct WindowsFFI { + pub version_info: OSVERSIONINFOW, + pub short_version: WindowsVersion, + driver_registry_string: UNICODE_STRING, + ntdll: HMODULE, + nt_load_driver: extern "stdcall" fn(PUNICODE_STRING) -> NTSTATUS, + nt_unload_driver: extern "stdcall" fn(PUNICODE_STRING) -> NTSTATUS, + rtl_init_unicode_str: extern "stdcall" fn(PUNICODE_STRING, PCWSTR), + rtl_get_version: extern "system" fn(PRTL_OSVERSIONINFOW) -> NTSTATUS, +} + +impl WindowsFFI { + pub fn new() -> Self { + let str_ntdll = CString::new("ntdll").expect(""); + let str_nt_load_driver = CString::new("NtLoadDriver").expect(""); + let str_nt_unload_driver = CString::new("NtUnloadDriver").expect(""); + let str_rtl_init_unicode_str = CString::new("RtlInitUnicodeString").expect(""); + let str_rtl_get_version = CString::new("RtlGetVersion").expect(""); + let str_se_load_driver_privilege = CString::new("SeLoadDriverPrivilege").expect(""); + + let str_driver_path = CString::new("\\SystemRoot\\System32\\DRIVERS\\nganhkhoa.sys").expect(""); + let str_registry_path = CString::new("System\\CurrentControlSet\\Services\\nganhkhoa").expect(""); + let str_driver_reg = + U16CString::from_str("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\nganhkhoa").expect(""); + let str_type = CString::new("Type").expect(""); + let str_error_control = CString::new("ErrorControl").expect(""); + 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 { + dwOSVersionInfoSize: 0u32, + dwMajorVersion: 0u32, + dwMinorVersion: 0u32, + dwBuildNumber: 0u32, + dwPlatformId: 0u32, + szCSDVersion: [0u16; 128], + }; + + let ntdll: HMODULE; + let nt_load_driver: extern "stdcall" fn(PUNICODE_STRING) -> NTSTATUS; + let nt_unload_driver: extern "stdcall" fn(PUNICODE_STRING) -> NTSTATUS; + let rtl_init_unicode_str: extern "stdcall" fn(PUNICODE_STRING, PCWSTR); + let rtl_get_version: extern "system" fn(PRTL_OSVERSIONINFOW) -> NTSTATUS; + + // some pointer unsafe C code + unsafe { + ntdll = LoadLibraryA(str_ntdll.as_ptr()); + let nt_load_driver_ = GetProcAddress(ntdll, str_nt_load_driver.as_ptr()); + let nt_unload_driver_ = GetProcAddress(ntdll, str_nt_unload_driver.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()); + + nt_load_driver = std::mem::transmute(nt_load_driver_); + nt_unload_driver = std::mem::transmute(nt_unload_driver_); + rtl_init_unicode_str = std::mem::transmute(rtl_init_unicode_str_); + rtl_get_version = std::mem::transmute(rtl_get_version_); + + // setup registry + let mut registry_key: HKEY = std::ptr::null_mut(); + RegCreateKeyExA( + HKEY_LOCAL_MACHINE, str_registry_path.as_ptr(), + 0, std::ptr::null_mut(), + REG_OPTION_NON_VOLATILE, KEY_WRITE, + std::ptr::null_mut(), &mut registry_key, std::ptr::null_mut() + ); + let type_value: [u8; 4] = 1u32.to_le_bytes(); + let error_control_value: [u8; 4] = 1u32.to_le_bytes(); + let start_value: [u8; 4] = 3u32.to_le_bytes(); + let registry_values = [ + (str_type.as_ptr(), REG_DWORD, type_value.as_ptr(), 4), + (str_error_control.as_ptr(), REG_DWORD, error_control_value.as_ptr(), 4), + (str_start.as_ptr(), REG_DWORD, start_value.as_ptr(), 4), + (str_image_path.as_ptr(), REG_SZ, + str_driver_path.as_ptr() as *const u8, str_driver_path.to_bytes().len() + 1) + ]; + for &(key, keytype, value_ptr, size_in_bytes) in ®istry_values { + RegSetValueExA( + registry_key, key, 0, + keytype, value_ptr, size_in_bytes as u32 + ); + } + RegCloseKey(registry_key); + + // Setup privilege SeLoadDriverPrivilege + let mut token_handle: HANDLE = std::ptr::null_mut(); + let mut luid = LUID::default(); + OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &mut token_handle); + LookupPrivilegeValueA(std::ptr::null_mut(), str_se_load_driver_privilege.as_ptr(), &mut luid); + let mut new_token_state = TOKEN_PRIVILEGES { + PrivilegeCount: 1, + Privileges: [LUID_AND_ATTRIBUTES { + Luid: luid, + Attributes: SE_PRIVILEGE_ENABLED + }] + }; + AdjustTokenPrivileges( + token_handle, 0, &mut new_token_state, 16, std::ptr::null_mut(), std::ptr::null_mut()); + 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); + + let short_version = match version_info.dwBuildNumber { + 17134 | 17763 => WindowsVersion::Windows10_2018, + 18362 | 18363 => WindowsVersion::Windows10_2019, + 19041 => WindowsVersion::Windows10_2020, + _ if version_info.dwBuildNumber >= 19536 => WindowsVersion::Windows10FastRing, + _ => WindowsVersion::Windows10VersionUnknown + }; + + Self { + version_info, + short_version, + driver_registry_string: str_driver_reg_unicode, + ntdll, + nt_load_driver, + nt_unload_driver, + rtl_init_unicode_str, + rtl_get_version + } + } + + pub fn load_driver(&mut self) -> NTSTATUS { + (self.nt_load_driver)(&mut self.driver_registry_string) + } + + pub fn unload_driver(&mut self) -> NTSTATUS { + (self.nt_unload_driver)(&mut self.driver_registry_string) + } + + pub fn get_build_number(&self) -> DWORD { + self.version_info.dwBuildNumber + } + + pub fn print_version(&self) { + println!("Windows version: {}.{}.{} {:?}", + self.version_info.dwMajorVersion, + self.version_info.dwMinorVersion, + self.version_info.dwBuildNumber, + self.short_version + ); + } +}