├── client ├── .gitignore ├── Cargo.toml └── src │ ├── main.rs │ ├── lib.rs │ ├── tests.rs │ └── driver.rs ├── driver ├── .gitignore ├── delete.bat ├── remote_map.bat ├── remote_client.bat ├── Cargo.toml ├── .cargo │ └── config ├── Makefile.toml ├── src │ ├── shared.rs │ ├── dispatch.rs │ └── lib.rs └── build.rs ├── Cargo.toml ├── README.md └── .gitignore /client/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | cmake-build-debug -------------------------------------------------------------------------------- /driver/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | remote_map.bat 3 | remote_client.bat 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "client", 5 | "driver", 6 | ] 7 | -------------------------------------------------------------------------------- /driver/delete.bat: -------------------------------------------------------------------------------- 1 | rm "F:\git\kernel-rs\target\x86_64-pc-windows-msvc\debug\deps\driver.dll" -------------------------------------------------------------------------------- /driver/remote_map.bat: -------------------------------------------------------------------------------- 1 | ssh -i ~/.ssh/id_rsa Administrator@192.168.58.128 "powershell C:\Users\VM\Desktop\load.ps1" 2 | exit -------------------------------------------------------------------------------- /driver/remote_client.bat: -------------------------------------------------------------------------------- 1 | ssh -i ~/.ssh/id_rsa Administrator@192.168.58.128 "powershell C:\Users\VM\Desktop\load2.ps1" 2 | exit -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kernel-rs 2 | A Windows kernel framework written in Rust 3 | 4 | You should be able to build the project and manual map it into kernel space using the provided [kdmapper](https://github.com/TheCruZ/kdmapper-1803-20H2) using `cargo make map` 5 | 6 | Original work from [not-matthias](https://github.com/not-matthias/kernel-driver-with-rust) 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | .idea 13 | target 14 | -------------------------------------------------------------------------------- /driver/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "driver" 3 | version = "0.1.0" 4 | authors = ["not-matthias", "rmccrystal"] 5 | edition = "2018" 6 | build = "build.rs" 7 | 8 | [lib] 9 | path = "src/lib.rs" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | log = { version = "0.4.11", features = ["release_max_level_off"] } 14 | # TODO: Make this a git dep 15 | winkernel = { path = "../../winkernel-rs" } 16 | cstr_core = "0.2.4" 17 | 18 | [build-dependencies] 19 | winreg = "0.7.0" 20 | anyhow = "1.0.45" -------------------------------------------------------------------------------- /client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kernel-client" 3 | version = "0.1.0" 4 | authors = ["Ryan McCrystal "] 5 | edition = "2018" 6 | 7 | [features] 8 | # If this is enabled the driver will remap every time it loads 9 | remap = [] 10 | debug = [] 11 | 12 | [profile.release] 13 | panic = "abort" 14 | 15 | [dependencies] 16 | winreg = "0.10.1" 17 | anyhow = "1.0.38" 18 | log = "0.4.13" 19 | env_logger = "0.8.2" 20 | rand = "0.8.4" 21 | kdmapper = { git = "https://github.com/rmccrystal/kdmapper-rs", features = ["disable-output"] } 22 | memflow = { git = "https://github.com/memflow/memflow", branch = "next", features = ["plugins"] } 23 | memflow-win32 = { git = "https://github.com/memflow/memflow-win32" } 24 | memlib = { path = "../../memlib-rs"} 25 | pelite = "0.9.0" 26 | -------------------------------------------------------------------------------- /driver/.cargo/config: -------------------------------------------------------------------------------- 1 | [unstable] 2 | # So we can get the log crate working with it complaining about std 3 | # https://github.com/rust-lang/cargo/issues/7915 4 | features = ["host_dep"] 5 | 6 | [build] 7 | target = "x86_64-pc-windows-msvc" 8 | 9 | rustflags = [ 10 | "-C", "panic=abort", 11 | 12 | # Pre Link Args 13 | "-Z", "pre-link-arg=/NOLOGO", 14 | "-Z", "pre-link-arg=/NXCOMPAT", 15 | "-Z", "pre-link-arg=/NODEFAULTLIB", 16 | "-Z", "pre-link-arg=/SUBSYSTEM:NATIVE", 17 | "-Z", "pre-link-arg=/DRIVER", 18 | "-Z", "pre-link-arg=/DYNAMICBASE", 19 | "-Z", "pre-link-arg=/MANIFEST:NO", 20 | "-Z", "pre-link-arg=/PDBALTPATH:none", 21 | 22 | # Post Link Args 23 | "-C", "link-arg=/OPT:REF,ICF", 24 | "-C", "link-arg=/ENTRY:driver_entry", 25 | "-C", "link-arg=/MERGE:.edata=.rdata", 26 | "-C", "link-arg=/MERGE:.rustc=.data", 27 | "-C", "link-arg=/INTEGRITYCHECK" 28 | ] -------------------------------------------------------------------------------- /driver/Makefile.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | DRIVER_NAME = "kernel-rs" 3 | CARGO_NAME = "driver" 4 | CARGO_MAKE_CRATE_CURRENT_WORKSPACE_MEMBER = "driver" 5 | 6 | [env.development] 7 | TARGET_PATH = "target/x86_64-pc-windows-msvc/debug" 8 | 9 | [env.production] 10 | TARGET_PATH = "target/x86_64-pc-windows-msvc/release" 11 | BUILD_FLAGS = "--release" 12 | 13 | [tasks.build-driver] 14 | script = [ 15 | "cargo b %BUILD_FLAGS%" 16 | ] 17 | 18 | [tasks.rename] 19 | #dependencies = ["build-driver"] 20 | ignore_errors = true 21 | script = [ 22 | "cd %TARGET_PATH%", 23 | "cp -f %CARGO_NAME%.dll %CARGO_NAME%.sys", 24 | ] 25 | 26 | [tasks.map] 27 | dependencies = ["rename"] 28 | script = [ 29 | "kdmapper.exe %TARGET_PATH%/%CARGO_NAME%.sys" 30 | ] 31 | 32 | [tasks.load] 33 | dependencies = ["rename"] 34 | script = [ 35 | "sc stop %DRIVER_NAME%", 36 | "sc delete %DRIVER_NAME%", 37 | "sc create %DRIVER_NAME% binPath= %CARGO_MAKE_WORKING_DIRECTORY%/%TARGET_PATH%/%CARGO_NAME% type= kernel", 38 | "sc start %DRIVER_NAME%" 39 | ] -------------------------------------------------------------------------------- /driver/src/shared.rs: -------------------------------------------------------------------------------- 1 | pub const KEY: &str = "TestKey1"; 2 | 3 | #[derive(Clone, Eq, PartialEq)] 4 | #[cfg_attr(debug_assertions, derive(Debug))] 5 | pub struct Dispatch { 6 | pub handled: bool, 7 | pub data: Data, 8 | } 9 | 10 | #[derive(Clone, Eq, PartialEq)] 11 | #[cfg_attr(debug_assertions, derive(Debug))] 12 | pub enum Data { 13 | Request(Request), 14 | Response(Result), 15 | } 16 | 17 | #[derive(Clone, Eq, PartialEq)] 18 | #[cfg_attr(debug_assertions, derive(Debug))] 19 | pub enum Request { 20 | Ping, 21 | Unregister, 22 | ReadPhysical { 23 | address: u64, 24 | buf: *mut u8, 25 | len: usize, 26 | }, 27 | WritePhysical { 28 | address: u64, 29 | buf: *const u8, 30 | len: usize, 31 | }, 32 | } 33 | 34 | #[derive(Clone, Eq, PartialEq)] 35 | #[cfg_attr(debug_assertions, derive(Debug))] 36 | pub enum Response { 37 | Ping, 38 | Unregister, 39 | ReadPhysical, 40 | WritePhysical, 41 | } 42 | 43 | #[derive(Clone, Eq, PartialEq, Debug)] 44 | pub enum KernelError { 45 | NtStatus(i32), 46 | MmMapIoSpace { address: u64, len: usize }, 47 | PartialCopy { address: u64, len: usize, read: usize }, 48 | InvalidRequest, 49 | } 50 | -------------------------------------------------------------------------------- /client/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(core_intrinsics)] 2 | use std::time::Duration; 3 | use log::LevelFilter; 4 | use kernel_client::driver::DriverHandle; 5 | use kernel_client::KernelHandle; 6 | use kernel_client::shared::Request; 7 | 8 | fn main() { 9 | unsafe { 10 | env_logger::init(); 11 | // let driver = kernel_client::driver::Driver::new().unwrap(); 12 | // let mut buf = vec![0u8; 0x100_000]; 13 | // driver.read_physical_chunked(0, &mut buf[0..0x10_000]); 14 | // dbg!(&buf); 15 | // let mut buf = vec![0u8; 0x1000000]; 16 | // driver.read_physical_chunked(0, &mut buf); 17 | // dbg!(&buf); 18 | // let mut buf = vec!(0u8; 0x1000); 19 | // for i in (0..0x1000000).step_by(0x1000) { 20 | // let result = driver.read_physical(i, &mut buf); 21 | // if let Err(e) = result { 22 | // log::error!("Could not read {:#X}-{:#X}: {:?}", i, i + 0x1000, e); 23 | // } 24 | // log::info!("Read {:#X}-{:#X}", i, i + 0x1000); 25 | // } 26 | // dbg!(buf); 27 | } 28 | 29 | let h = KernelHandle::new().unwrap(); 30 | 31 | // unsafe { 32 | // let dr = Driver::new().unwrap(); 33 | // let mut buf = [0u8; 0x10]; 34 | // let req = Request::ReadPhysical { buf: buf.as_mut_ptr(), len: buf.len(), address: 1761280 }; 35 | // dbg!(&req); 36 | // let res = dr.send_request(req); 37 | // dbg!(&res); 38 | // // dbg!(buf); 39 | // } 40 | // return; 41 | 42 | 43 | // unsafe { 44 | // let driver = kernel_client::driver::Driver::new().unwrap(); 45 | // driver.ping(); 46 | // } 47 | // println!("success"); 48 | } -------------------------------------------------------------------------------- /driver/src/dispatch.rs: -------------------------------------------------------------------------------- 1 | use winkernel::basedef::ntstatus; 2 | use winkernel::kernel::{is_valid_ptr, PhysicalMap, read_physical_memory}; 3 | 4 | use crate::CONTEXT; 5 | use crate::shared::*; 6 | 7 | /// Returns true if the request was handled 8 | pub unsafe fn dispatch(req: *mut Dispatch) -> bool { 9 | if !is_valid_ptr(req) { 10 | log::error!("Invalid pointer given to dispatch"); 11 | return false; 12 | } 13 | let req = match req.as_mut() { 14 | Some(n) => n, 15 | None => { 16 | log::error!("req.as_mut() failed"); 17 | return false; 18 | } 19 | }; 20 | if req.handled { 21 | return false; 22 | } 23 | 24 | #[cfg(debug_assertions)] 25 | log::trace!("Got dispatch: {:#X?}", req); 26 | 27 | if let Data::Request(request) = &req.data { 28 | let resp = handler(request); 29 | req.data = Data::Response(resp); 30 | } else { 31 | req.data = Data::Response(Err(KernelError::InvalidRequest)) 32 | } 33 | 34 | req.handled = true; 35 | true 36 | } 37 | 38 | pub unsafe fn handler(request: &Request) -> Result { 39 | match *request { 40 | Request::Ping => Ok(Response::Ping), 41 | Request::ReadPhysical { address, buf, len } => { 42 | let buf = buf.as_mut().ok_or(KernelError::InvalidRequest)?; 43 | // let map = PhysicalMap::new(address as _, buf.len()).ok_or(KernelError::MmMapIoSpace { address, len: buf.len() })?; 44 | // buf.copy_from_slice(&map); 45 | let buf = core::slice::from_raw_parts_mut(buf, len); 46 | read_physical_memory(address, buf).map_err(|e| match e { 47 | (ntstatus::STATUS_PARTIAL_COPY, n) => KernelError::PartialCopy { address, len: buf.len(), read: n }, 48 | (e, _) => KernelError::NtStatus(e) 49 | })?; 50 | Ok(Response::ReadPhysical) 51 | } 52 | Request::WritePhysical { address, buf, len } => { 53 | let mut map = PhysicalMap::new(address as _, len).ok_or(KernelError::MmMapIoSpace { address, len })?; 54 | map.copy_from_slice(core::slice::from_raw_parts(buf, len)); 55 | Ok(Response::WritePhysical) 56 | } 57 | Request::Unregister => { 58 | // TODO: Remove this, it crashes your pc 59 | log::info!("Unregistered callback"); 60 | CONTEXT.callback.unregister().map_err(KernelError::NtStatus)?; 61 | Ok(Response::Unregister) 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /client/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(test, feature(test))] 2 | 3 | use std::cell::{RefCell, RefMut}; 4 | use anyhow::*; 5 | use memflow::os::Process; 6 | use memflow::prelude::*; 7 | use memflow::prelude::cache::TimedCacheValidator; 8 | use memflow_win32::prelude::*; 9 | use memlib::{Module, ProcessAttach}; 10 | use crate::driver::DriverHandle; 11 | 12 | pub mod driver; 13 | #[path = "../../driver/src/shared.rs"] 14 | pub mod shared; 15 | 16 | #[cfg(test)] 17 | mod tests; 18 | 19 | pub use memflow; 20 | 21 | type MemflowKernel = Win32Kernel, CachedVirtualTranslate>; 22 | type MemflowProcess = Win32Process, CachedVirtualTranslate, Win32VirtualTranslate>>; 23 | 24 | pub struct KernelHandle(MemflowKernel); 25 | 26 | impl KernelHandle { 27 | pub fn new() -> anyhow::Result { 28 | let driver = unsafe { DriverHandle::new() }?; 29 | 30 | let kernel = Win32KernelBuilder::new(driver) 31 | .build_default_caches() 32 | .build()?; 33 | 34 | Ok(Self(kernel)) 35 | } 36 | 37 | pub fn attach_pid(&self, pid: u64) -> anyhow::Result { 38 | Ok(KernelProcess::new(self.0.clone().into_process_by_pid(pid as _)?)) 39 | } 40 | } 41 | 42 | impl Default for KernelHandle { 43 | fn default() -> Self { 44 | Self::new().unwrap() 45 | } 46 | } 47 | 48 | impl memlib::ProcessAttach for KernelHandle { 49 | type ProcessType = KernelProcess; 50 | 51 | fn attach(&self, process_name: &str) -> anyhow::Result { 52 | Ok(KernelProcess::new(self.0.clone().into_process_by_name(process_name)?)) 53 | } 54 | } 55 | 56 | pub struct KernelProcess(RefCell); 57 | 58 | impl KernelProcess { 59 | pub fn new(proc: MemflowProcess) -> Self { 60 | Self(RefCell::new(proc)) 61 | } 62 | 63 | pub fn memflow(&self) -> RefMut<'_, MemflowProcess> { 64 | self.0.borrow_mut() 65 | } 66 | } 67 | 68 | impl KernelProcess { 69 | pub fn get_main_module(&self) -> Module { 70 | self.memflow() 71 | .primary_module() 72 | .map(|m| memlib::Module { name: m.name.to_string(), base: m.base.to_umem(), size: m.size }) 73 | .unwrap() 74 | } 75 | } 76 | 77 | impl memlib::MemoryRead for KernelProcess { 78 | fn try_read_bytes_into(&self, address: u64, buffer: &mut [u8]) -> Option<()> { 79 | let result = self.memflow().virt_mem 80 | .read_raw_into(Address::from(address), buffer); 81 | 82 | if let Err(e) = &result { 83 | log::error!("Could not read bytes: {:?}", e); 84 | } 85 | 86 | result.ok() 87 | // Some(()) 88 | } 89 | } 90 | 91 | impl memlib::MemoryWrite for KernelProcess { 92 | fn try_write_bytes(&self, address: u64, buffer: &[u8]) -> Option<()> { 93 | let result = self.memflow() 94 | .virt_mem 95 | .write_raw(Address::from(address), buffer); 96 | 97 | if let Err(e) = &result { 98 | log::error!("Could not write bytes: {:?}", e); 99 | } 100 | 101 | result.ok() 102 | } 103 | } 104 | 105 | impl memlib::ModuleList for KernelProcess { 106 | fn get_module_list(&self) -> Vec { 107 | self.memflow() 108 | .module_list().unwrap() 109 | .into_iter() 110 | .map(|m| memlib::Module { name: m.name.to_string(), base: m.base.to_umem(), size: m.size }) 111 | .collect() 112 | } 113 | } 114 | 115 | impl memlib::ProcessInfo for KernelProcess { 116 | fn process_name(&self) -> String { 117 | self.memflow().primary_module().unwrap().name.to_string() 118 | } 119 | 120 | fn peb_base_address(&self) -> u64 { 121 | self.memflow().proc_info.peb().unwrap().to_umem() 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /driver/build.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env::var, 3 | path::{Path, PathBuf}, 4 | }; 5 | use anyhow::*; 6 | use winreg::{enums::*, RegKey}; 7 | 8 | /// Returns the path to the `Windows Kits` directory. It's by default at 9 | /// `C:\Program Files (x86)\Windows Kits\10`. 10 | fn get_windows_kits_dir() -> Result { 11 | let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 12 | let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots"; 13 | let dir: String = hklm.open_subkey(key)?.get_value("KitsRoot10")?; 14 | 15 | Ok(dir.into()) 16 | } 17 | 18 | /// Returns the path to the kernel mode libraries. The path may look like this: 19 | /// `C:\Program Files (x86)\Windows Kits\10\lib\10.0.18362.0\km`. 20 | fn get_km_dir(windows_kits_dir: &PathBuf) -> Result { 21 | let readdir = Path::new(windows_kits_dir).join("lib").read_dir()?; 22 | 23 | let max_libdir = readdir 24 | .filter_map(|dir| dir.ok()) 25 | .map(|dir| dir.path()) 26 | .filter(|dir| { 27 | dir.components() 28 | .last() 29 | .and_then(|c| c.as_os_str().to_str()) 30 | .map(|c| c.starts_with("10.") && dir.join("km").is_dir()) 31 | .unwrap_or(false) 32 | }) 33 | .max() 34 | .ok_or_else(|| format_err!("Can not find a valid km dir in `{:?}`", windows_kits_dir))?; 35 | 36 | Ok(max_libdir.join("km")) 37 | } 38 | 39 | /// Returns the path to the user mode libraries. The path may look like this: 40 | /// `C:\Program Files (x86)\Windows Kits\10\lib\10.0.18362.0\um`. 41 | fn get_um_dir(windows_kits_dir: &PathBuf) -> Result { 42 | let readdir = Path::new(windows_kits_dir).join("lib").read_dir()?; 43 | 44 | let max_libdir = readdir 45 | .filter_map(|dir| dir.ok()) 46 | .map(|dir| dir.path()) 47 | .filter(|dir| { 48 | dir.components() 49 | .last() 50 | .and_then(|c| c.as_os_str().to_str()) 51 | .map(|c| c.starts_with("10.") && dir.join("um").is_dir()) 52 | .unwrap_or(false) 53 | }) 54 | .max() 55 | .ok_or_else(|| format_err!("Can not find a valid um dir in `{:?}`", windows_kits_dir))?; 56 | 57 | Ok(max_libdir.join("um")) 58 | } 59 | 60 | fn get_km_include_dir(windows_kits_dir: &PathBuf) -> Result { 61 | let readdir = Path::new(windows_kits_dir).join("include").read_dir()?; 62 | let max_libdir = readdir 63 | .filter_map(|dir| dir.ok()) 64 | .map(|dir| dir.path()) 65 | .filter(|dir| { 66 | dir.components() 67 | .last() 68 | .and_then(|c| c.as_os_str().to_str()) 69 | .map(|c| c.starts_with("10.") && dir.join("km").is_dir()) 70 | .unwrap_or(false) 71 | }) 72 | .max() 73 | .ok_or_else(|| format_err!("Can not find a valid km dir in `{:?}`", windows_kits_dir))?; 74 | Ok(max_libdir.join("km")) 75 | } 76 | 77 | fn internal_link_search() { 78 | let windows_kits_dir = get_windows_kits_dir().unwrap(); 79 | let km_dir = get_km_dir(&windows_kits_dir).unwrap(); 80 | let um_dir = get_um_dir(&windows_kits_dir).unwrap(); 81 | let target = var("TARGET").unwrap(); 82 | 83 | let arch = if target.contains("x86_64") { 84 | "x64" 85 | } else if target.contains("i686") { 86 | "x86" 87 | } else { 88 | panic!("Only support x86_64 and i686!"); 89 | }; 90 | 91 | let km_lib_dir = km_dir.join(arch); 92 | println!("cargo:rustc-link-search=native={}", km_lib_dir.to_str().unwrap()); 93 | let um_lib_dir = um_dir.join(arch); 94 | println!("cargo:rustc-link-search=native={}", um_lib_dir.to_str().unwrap()); 95 | } 96 | 97 | fn extra_link_search() {} 98 | 99 | fn main() { 100 | if var(format!("CARGO_FEATURE_{}", "extra_link_search".to_uppercase())).is_ok() { 101 | extra_link_search() 102 | } else { 103 | internal_link_search() 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /client/src/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::KernelHandle; 2 | use log::*; 3 | use std::{process, thread}; 4 | use std::process::Command; 5 | use std::time::Duration; 6 | use memlib::*; 7 | 8 | extern crate test; 9 | 10 | use test::Bencher; 11 | 12 | fn init() { 13 | let _ = env_logger::builder().is_test(true).filter_level(LevelFilter::Debug).try_init(); 14 | } 15 | 16 | // Runs init() and gets a handle (or panics) 17 | fn get_handle() -> KernelHandle { 18 | init(); 19 | 20 | KernelHandle::new().unwrap() 21 | } 22 | 23 | struct Process { 24 | proc: process::Child, 25 | name: String, 26 | } 27 | 28 | impl Process { 29 | pub fn new(process_name: &str) -> Self { 30 | let mut proc = Command::new(&process_name).spawn().unwrap(); 31 | thread::sleep(Duration::from_millis(50)); 32 | 33 | Self { proc, name: process_name.to_owned() } 34 | } 35 | 36 | pub fn notepad() -> Self { 37 | Self::new("notepad.exe") 38 | } 39 | 40 | fn name(&self) -> &str { 41 | &self.name 42 | } 43 | 44 | pub fn pid(&self) -> u64 { 45 | self.proc.id() as _ 46 | } 47 | } 48 | 49 | impl Drop for Process { 50 | fn drop(&mut self) { 51 | self.proc.kill().unwrap(); 52 | } 53 | } 54 | 55 | #[test] 56 | fn test_create_handle() { 57 | init(); 58 | 59 | KernelHandle::new().unwrap(); 60 | } 61 | 62 | #[test] 63 | fn test_modules() { 64 | let handle = get_handle(); 65 | let notepad = Process::notepad(); 66 | let proc = handle.attach_pid(notepad.pid()).unwrap(); 67 | 68 | let modules = proc.get_module_list(); 69 | debug!("Found {} modules", modules.len()); 70 | assert!(!modules.is_empty()); 71 | } 72 | 73 | #[test] 74 | fn test_peb_base() { 75 | let handle = get_handle(); 76 | let notepad = Process::notepad(); 77 | let proc = handle.attach_pid(notepad.pid()).unwrap(); 78 | 79 | let peb = proc.peb_base_address(); 80 | dbg!(peb); 81 | assert_ne!(peb, 0); 82 | } 83 | 84 | #[test] 85 | fn test_read_memory() { 86 | let handle = get_handle(); 87 | let notepad = Process::notepad(); 88 | let proc = handle.attach_pid(notepad.pid()).unwrap(); 89 | 90 | let base = proc.get_main_module().base; 91 | 92 | // Read the first 64 bytes from base 93 | let mut buf = vec![0u8; 64]; 94 | proc.try_read_bytes_into(base, &mut buf).unwrap(); 95 | dbg!(buf); 96 | } 97 | 98 | #[test] 99 | fn test_dump_module() { 100 | let handle = get_handle(); 101 | let notepad = Process::notepad(); 102 | let proc = handle.attach_pid(notepad.pid()).unwrap(); 103 | 104 | let main_mod = proc.get_main_module(); 105 | 106 | let mut buf = vec![0u8; main_mod.size as _]; 107 | proc.try_read_bytes_into(main_mod.base, &mut buf).unwrap(); 108 | assert_eq!(buf[0..2], [0x4Du8, 0x5A]); 109 | } 110 | 111 | #[test] 112 | fn test_write_memory() { 113 | let handle = get_handle(); 114 | let notepad = Process::notepad(); 115 | let proc = handle.attach_pid(notepad.pid()).unwrap(); 116 | 117 | let base = proc.get_main_module().base; 118 | 119 | let test_data = [1u8, 2, 3, 4, 5, 6]; 120 | 121 | let mut orig_data = vec![0u8; test_data.len()]; 122 | proc.try_read_bytes_into(base, &mut orig_data).unwrap(); 123 | 124 | proc.try_write_bytes(base, &test_data).unwrap(); 125 | 126 | let mut actual_data = vec![0u8; test_data.len()]; 127 | proc.try_read_bytes_into(base, &mut actual_data).unwrap(); 128 | 129 | proc.try_write_bytes(base, &orig_data).unwrap(); 130 | 131 | assert_eq!(&test_data[..], &actual_data); 132 | } 133 | 134 | #[bench] 135 | fn bench_read(b: &mut Bencher) { 136 | let handle = get_handle(); 137 | let notepad = Process::notepad(); 138 | let proc = handle.attach_pid(notepad.pid()).unwrap(); 139 | let base = proc.get_main_module().base; 140 | 141 | let mut buf = [0u8; 0x100]; 142 | b.iter(|| { 143 | proc.try_read_bytes_into(base, &mut buf); 144 | }); 145 | } 146 | 147 | #[bench] 148 | fn bench_create_handle(b: &mut Bencher) { 149 | init(); 150 | 151 | b.iter(|| { 152 | KernelHandle::new().unwrap(); 153 | }); 154 | } -------------------------------------------------------------------------------- /driver/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(alloc_error_handler)] 3 | #![allow(clippy::missing_safety_doc)] 4 | #![allow(incomplete_features)] 5 | #![feature(core_intrinsics)] 6 | #![feature(option_result_contains)] 7 | 8 | extern crate alloc; 9 | 10 | use alloc::string::String; 11 | use core::convert::TryInto; 12 | use core::mem; 13 | 14 | use log::*; 15 | use winkernel::allocator::KernelAlloc; 16 | use winkernel::basedef::{ntstatus, NTSTATUS, PVOID}; 17 | use winkernel::kernel::{create_registry_callback, get_kernel_modules, get_object_name, get_process_list, is_address_valid, is_valid_ptr, query_performance_counter, RegistryCallback, RegistryCallbackFunc, RegNotifyClass, RegSetValueKeyInformation, safe_copy}; 18 | use winkernel::log::KernelLogger; 19 | 20 | pub mod dispatch; 21 | pub mod shared; 22 | 23 | /// When using the alloc crate it seems like it does some unwinding. Adding this 24 | /// export satisfies the compiler but may introduce undefined behaviour when a 25 | /// panic occurs. 26 | #[no_mangle] 27 | pub extern "system" fn __CxxFrameHandler3(_: *mut u8, _: *mut u8, _: *mut u8, _: *mut u8) -> i32 { unimplemented!() } 28 | 29 | #[global_allocator] 30 | static GLOBAL: KernelAlloc = KernelAlloc; 31 | 32 | /// Explanation can be found here: https://github.com/Trantect/win_driver_example/issues/4 33 | #[used] 34 | #[export_name = "_fltused"] 35 | static _FLTUSED: i32 = 0; 36 | 37 | #[panic_handler] 38 | #[cfg(not(test))] 39 | fn panic(info: &core::panic::PanicInfo) -> ! { 40 | error!("panic: {:?}", info); 41 | #[allow(unused_unsafe)] 42 | loop {} 43 | } 44 | 45 | static LOG_LEVEL: LevelFilter = LevelFilter::Debug; 46 | 47 | pub struct Context { 48 | pub count: u64, 49 | pub callback: RegistryCallback, 50 | } 51 | 52 | static mut CONTEXT: Context = Context { count: 0, callback: RegistryCallback(0) }; 53 | 54 | unsafe extern "C" fn handler(ctx: &mut Context, class: RegNotifyClass, operation: *mut PVOID) -> NTSTATUS { 55 | let status = ntstatus::STATUS_SUCCESS; 56 | 57 | if class != RegNotifyClass::RegNtPreSetValueKey { 58 | return status; 59 | } 60 | 61 | let set_value = match (operation as *mut RegSetValueKeyInformation).as_ref() { 62 | Some(n) => n, 63 | None => return status 64 | }; 65 | 66 | if !set_value.value_name.try_to_string().contains(®ISTRY_KEY.as_deref().unwrap_or("TestKey1")) || !set_value.data().len() == 8 { 67 | return status; 68 | } 69 | 70 | let ptr = u64::from_ne_bytes(set_value.data().try_into().unwrap()); 71 | trace!("Received registry buf: {:#X}", ptr); 72 | 73 | let result = dispatch::dispatch(ptr as _); 74 | 75 | if result { 76 | ntstatus::STATUS_ACCESS_DENIED 77 | } else { 78 | status 79 | } 80 | } 81 | 82 | static mut REGISTRY_KEY: Option = None; 83 | 84 | /* 85 | unsafe fn init_registry_key(param1: usize) -> bool { 86 | if param1 == 0 { 87 | error!("param1 was 0"); 88 | return false; 89 | } 90 | 91 | if !is_address_valid(param1) { 92 | error!("{:#X} is not a valid read address for REGISTRY_KEY", param1); 93 | return false; 94 | } 95 | 96 | if let Ok(key) = CStr::from_ptr(param1 as _).to_str() { 97 | REGISTRY_KEY = Some(key.to_string()); 98 | info!("Found registry key {}", key.to_string()); 99 | true 100 | } else { 101 | error!("Could not parse ptr for REGISTRY_KEY"); 102 | false 103 | } 104 | } 105 | */ 106 | 107 | unsafe fn main(param1: usize, param2: usize) -> Result { 108 | info!("kernel-rs loaded"); 109 | // RegistryCallback(132803733244794080).unregister(); 110 | // return Ok(0); 111 | 112 | // if !init_registry_key(param1) { 113 | // return Err(-1); 114 | // } 115 | 116 | let vigembus = get_kernel_modules()?.into_iter().find(|n| n.full_path().ends_with("ViGEmBus.sys")).ok_or(1)?; 117 | 118 | const OFFSET: usize = 0x10D2A; 119 | let codecave = vigembus.image_base + OFFSET; 120 | info!("codecave 1: {:X?}", core::ptr::read(codecave as *const [u8; 30])); 121 | 122 | let dest = handler as *const () as usize; 123 | let add_offset = query_performance_counter() as u16; 124 | let rax_start = dest.wrapping_sub(add_offset as _); 125 | 126 | // rax_start + add_offset = dest 127 | // FIXME: Do we need to store rax? 128 | let mut bytecode = [ 129 | 0x48_u8, 0xB8, // mov rax, rax_start 130 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 131 | 0x48, 0x05, // add rax, add_offset 132 | 0x00, 0x00, 0x00, 0x00, 133 | 0xFF, 0xE0 // jmp rax 134 | ]; 135 | bytecode[2..10].clone_from_slice(&rax_start.to_le_bytes()); 136 | bytecode[12..14].clone_from_slice(&add_offset.to_le_bytes()); 137 | // let bytecode = [ 0xC3_u8 ]; 138 | info!("bytecode: {:X?}", bytecode); 139 | 140 | safe_copy(bytecode.as_ptr(), codecave as _, bytecode.len())?; 141 | info!("codecave 2: {:X?}", core::ptr::read(codecave as *const [u8; 30])); 142 | 143 | let func: RegistryCallbackFunc<_> = mem::transmute(codecave); 144 | let callback = create_registry_callback(func, &mut CONTEXT)?; 145 | CONTEXT.callback = callback; 146 | 147 | // let status = func(&mut CONTEXT, RegNotifyClass::RegNtPreDeleteKey, null_mut()); 148 | info!("callback: {}", callback.0); 149 | 150 | Ok(0) 151 | } 152 | 153 | #[no_mangle] 154 | pub extern "system" fn driver_entry(param1: usize, param2: usize) -> u32 { 155 | if let Err(e) = KernelLogger::init(LOG_LEVEL, "kernel-rs") { 156 | error!("Error setting logger: {:?}", e); 157 | } 158 | 159 | match unsafe { main(param1, param2) } { 160 | Ok(code) => code, 161 | Err(err) => { 162 | error!("{:#X}", err as u32); 163 | err as _ 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /client/src/driver.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::missing_safety_doc)] 2 | 3 | use std::ffi::CString; 4 | use std::os::raw::c_void; 5 | use std::rc::Rc; 6 | use std::thread; 7 | use std::time::Duration; 8 | use rand::Rng; 9 | use winreg::enums::{HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS}; 10 | use winreg::RegKey; 11 | use crate::shared::*; 12 | 13 | /// Wrapper for RegKey. Deletes the key when dropped 14 | struct TempRegKey(RegKey, String); 15 | 16 | impl Drop for TempRegKey { 17 | fn drop(&mut self) { 18 | let _ = self.0.delete_value(&self.1); 19 | } 20 | } 21 | 22 | impl Clone for TempRegKey { 23 | fn clone(&self) -> Self { 24 | unsafe { 25 | let mut reg_folder = std::mem::zeroed(); 26 | std::ptr::copy(&self.0, &mut reg_folder, 1); 27 | Self(reg_folder, self.1.clone()) 28 | } 29 | } 30 | } 31 | 32 | #[derive(Clone)] 33 | pub struct DriverHandle { 34 | key: Rc, 35 | } 36 | 37 | unsafe impl Send for DriverHandle {} 38 | 39 | #[cfg(all(debug_assertions, feature = "debug"))] 40 | const DRIVER_BYTES: &[u8] = include_bytes!("../../target/x86_64-pc-windows-msvc/debug/driver.dll").as_slice(); 41 | #[cfg(not(all(debug_assertions, feature = "debug")))] 42 | const DRIVER_BYTES: &[u8] = include_bytes!("../../target/x86_64-pc-windows-msvc/release/driver.dll").as_slice(); 43 | 44 | impl DriverHandle { 45 | pub unsafe fn new() -> anyhow::Result { 46 | // let key: String = rand::thread_rng() 47 | // .sample_iter(&Alphanumeric) 48 | // .take(10) 49 | // .map(char::from) 50 | // .collect(); 51 | // let key_cstr = CString::new(KEY).unwrap(); 52 | 53 | let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 54 | let reg_folder = hklm.open_subkey_with_flags("SOFTWARE\\Microsoft\\Windows\\Dwm", winreg::enums::KEY_ALL_ACCESS)?; 55 | 56 | let driver = Self { 57 | key: Rc::new(TempRegKey(reg_folder, KEY.to_string())) 58 | }; 59 | 60 | // driver.unregister(); 61 | // log::info!("Unregistered driver"); 62 | // std::thread::sleep(Duration::from_millis(1000)); 63 | // log::info!("sleep end"); 64 | 65 | if !driver.ping() || cfg!(feature = "remap") { 66 | log::info!("Mapping driver"); 67 | kdmapper::kdmapper(DRIVER_BYTES, false, true, false, 0, 0).ok_or_else(|| anyhow::anyhow!("Could not map driver"))?; 68 | if !driver.ping() { 69 | anyhow::bail!("Could not ping driver after loading"); 70 | } 71 | } 72 | 73 | log::info!("Driver initialized"); 74 | Ok(driver) 75 | } 76 | 77 | pub unsafe fn ping(&self) -> bool { 78 | matches!(self.send_request(Request::Ping), Ok(Ok(Response::Ping))) 79 | } 80 | 81 | pub unsafe fn read_physical(&self, address: u64, buf: &mut [u8]) -> core::result::Result<(), KernelError> { 82 | // println!("Reading {:#X} into {:p}", address, buf); 83 | // std::thread::sleep(Duration::from_secs_f32(0.5)); 84 | self.send_request(Request::ReadPhysical { address, buf: buf.as_mut_ptr(), len: buf.len() }).unwrap().map(|_| ()) 85 | } 86 | 87 | const CHUNK_SIZE: usize = 0x1000; 88 | 89 | pub unsafe fn read_physical_chunked(&self, address: u64, buf: &mut [u8]) { 90 | if buf.len() <= Self::CHUNK_SIZE { 91 | let result = self.read_physical(address, buf); 92 | if let Err(e) = result { 93 | log::debug!("Error reading {:#X} - {:#X}: {:#X?}", address, address + buf.len() as u64, e); 94 | buf.fill(0); 95 | } 96 | 97 | return; 98 | } 99 | // let mut intermediate_buf = vec![0u8; CHUNK_SIZE]; 100 | for (n, chunk) in buf.chunks_mut(Self::CHUNK_SIZE).enumerate() { 101 | let start = address + (n * Self::CHUNK_SIZE) as u64; 102 | log::trace!("Reading {:#X} - {:#X}", start, start + chunk.len() as u64); 103 | 104 | // let result = self.read_physical(start, &mut intermediate_buf[0..chunk.len()]); 105 | // chunk.copy_from_slice(&intermediate_buf[0..chunk.len()]); 106 | let result = self.read_physical(start, chunk); 107 | 108 | if let Err(e) = result { 109 | log::debug!("Error reading {:#X} - {:#X}: {:#X?}", start, start + chunk.len() as u64, e); 110 | chunk.fill(0); 111 | } 112 | } 113 | } 114 | 115 | pub unsafe fn write_physical(&self, address: u64, buf: &[u8]) -> core::result::Result<(), KernelError> { 116 | self.send_request(Request::WritePhysical { address, buf: buf.as_ptr(), len: buf.len() }).unwrap().map(|_| ()) 117 | } 118 | 119 | pub unsafe fn call_hook(&self, ptr: *mut c_void) -> anyhow::Result<()> { 120 | let result = self.key.0.set_value(&self.key.1, &(ptr as u64)); 121 | match result { 122 | Ok(_) => Err(anyhow::anyhow!("Success returned when writing reg key. Is the hook installed?")), 123 | Err(e) if e.raw_os_error() != Some(5) => Err(anyhow::anyhow!("Did not get permission denied error, instead got {:?}", e)), 124 | Err(_) => Ok(()) 125 | } 126 | } 127 | 128 | pub unsafe fn send_request(&self, req: Request) -> anyhow::Result> { 129 | let mut dispatch = Dispatch { handled: false, data: Data::Request(req) }; 130 | self.call_hook(&mut dispatch as *mut _ as _)?; 131 | match dispatch.data { 132 | Data::Request(_) => { 133 | Err(anyhow::anyhow!("Could not send request to kernel")) 134 | } 135 | Data::Response(r) => Ok(r) 136 | } 137 | } 138 | } 139 | 140 | use memflow::prelude::*; 141 | 142 | impl PhysicalMemory for DriverHandle { 143 | fn phys_read_raw_iter<'a>(&mut self, data: CIterator>, out_fail: &mut ReadFailCallback<'_, 'a>) -> memflow::prelude::Result<()> { 144 | unsafe { 145 | for MemData(addr, mut out) in data { 146 | self.read_physical_chunked(addr.address().to_umem(), out.as_bytes_mut()); 147 | } 148 | Ok(()) 149 | } 150 | } 151 | 152 | fn phys_write_raw_iter<'a>(&mut self, data: CIterator>, out_fail: &mut WriteFailCallback<'_, 'a>) -> memflow::prelude::Result<()> { 153 | unsafe { 154 | for MemData(addr, out) in data { 155 | self.write_physical(addr.address().to_umem(), out.as_bytes()).unwrap(); 156 | } 157 | Ok(()) 158 | } 159 | } 160 | 161 | fn metadata(&self) -> PhysicalMemoryMetadata { 162 | PhysicalMemoryMetadata { readonly: false, ideal_batch_size: 0x1000, real_size: 0xFFFFFFFFFFFFFFFF, max_address: 0xFFFFFFFFFFFFFFFF_u64.into() } 163 | } 164 | 165 | fn set_mem_map(&mut self, mem_map: &[PhysicalMemoryMapping]) {} 166 | } --------------------------------------------------------------------------------