Add Reflective DLL support and xtask utility

Introduced a new `xtask` for building Reflective DLLs and integrated [pe_to_shellcode] for enhanced compatibility. Updated `dll/src/lib.rs` with ReflectiveLoader logic, added necessary FFI bindings, and extended documentation to include the build process and usage.
This commit is contained in:
Steve S.
2025-05-10 08:41:40 -04:00
parent 8437a77933
commit f9063cfbc6
5 changed files with 162 additions and 11 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
[workspace]
members = ["dll", "exe"]
members = ["dll", "exe", "xtask"]
resolver = "3"
[profile.release]
+108 -2
View File
@@ -24,12 +24,118 @@ unsafe extern "system" {
fn GetProcAddress(hmodule: HANDLE, lpProcName: LPVOID) -> LPVOID;
}
#[allow(non_snake_case)]
#[link(name = "user32")]
unsafe extern "system" {
fn MessageBoxA(
hWnd: HANDLE,
lpText: LPVOID,
lpCaption: LPVOID,
uType: DWORD
);
}
#[repr(C)]
struct ImageDosHeader {
e_magic: u16,
e_cblp: u16,
e_cp: u16,
e_crlc: u16,
e_cparhdr: u16,
e_minalloc: u16,
e_maxalloc: u16,
e_ss: u16,
e_sp: u16,
e_csum: u16,
e_ip: u16,
e_cs: u16,
e_lfarlc: u16,
e_ovno: u16,
e_res: [u16; 4],
e_oemid: u16,
e_oeminfo: u16,
e_res2: [u16; 10],
e_lfanew: i32,
}
#[repr(C)]
struct ImageNtHeaders {
signature: u32,
}
const IMAGE_DOS_SIGNATURE: u16 = 0x5A4D; // 'MZ'
const IMAGE_NT_SIGNATURE: u32 = 0x00004550; // 'PE\0\0'
#[cfg(target_arch = "x86_64")]
unsafe fn get_ip() -> usize {
let rip: usize;
unsafe { std::arch::asm!("lea {}, [rip]", out(reg) rip) };
rip
}
#[cfg(target_arch = "x86")]
unsafe fn get_ip() -> usize {
let eip: usize;
unsafe{
std::arch::asm!(
"call 1f",
"1: pop {}",
out(reg) eip,
);
}
eip
}
pub fn find_mz_pe_signature() -> Option<*const u8> {
unsafe {
let rip = get_ip();
let mut ptr = rip as *const u8;
loop {
if ptr < 2 as *const u8 {
break;
}
let dos_header = ptr.offset(-2) as *const ImageDosHeader;
if std::ptr::read_unaligned(&(*dos_header).e_magic) == IMAGE_DOS_SIGNATURE {
let e_lfanew = std::ptr::read_unaligned(&(*dos_header).e_lfanew) as isize;
if e_lfanew >= std::mem::size_of::<ImageDosHeader>() as isize && e_lfanew < 1024 {
let nt_header_ptr = (dos_header as *const u8).offset(e_lfanew) as *const ImageNtHeaders;
if std::ptr::read_unaligned(&(*nt_header_ptr).signature) == IMAGE_NT_SIGNATURE {
return Some(dos_header as *const u8);
}
}
}
ptr = ptr.offset(-1);
}
None
}
}
/// ReflectiveLoader for compatability with legacy Reflective DLL loaders
#[unsafe(no_mangle)]
pub unsafe extern "system" fn ReflectiveLoader(){
let module_base = find_mz_pe_signature();
if module_base.is_some() {
let module_base = module_base.unwrap();
unsafe { std::arch::asm!("call {0}", in(reg) module_base) };
}
}
/// For maximum compatability with this template, all functionality should be called from `dll_main`
#[unsafe(no_mangle)]
#[allow(named_asm_labels)]
#[allow(non_snake_case, unused_variables)]
pub fn dll_main() {
let cmd = b"calc.exe\0";
unsafe { WinExec(cmd.as_ptr() as LPVOID, 0); }
let msg = b"Hello from Rust Reflective DLL!\0";
unsafe { MessageBoxA(std::ptr::null_mut(), msg.as_ptr() as LPVOID, msg.as_ptr() as LPVOID, 0 ); }
}
+21 -8
View File
@@ -1,10 +1,10 @@
# dll-rs
# rdll-rs
A Rust project that demonstrates Windows DLL functionality and WinAPI interaction.
A Rust DLL template project that integrates [pe2shc](https://github.com/hasherezade/pe_to_shellcode) to facilitate the development of [Reflective DLLs](https://github.com/stephenfewer/ReflectiveDLLInjection). The template presently only supports 64-bit DLL development in most contexts, though with a few tweaks it should support 32-bit.
## Overview
dll-rs is a Rust template that can be compiled as both a dynamic-link library (DLL) and a regular executable. It provides an example of how to create Windows DLLs using Rust, including proper exports and Windows API integration.
rdll-rs is a Rust template that can be compiled as both a dynamic-link library (DLL), a regular executable, or a Reflective DLL. It provides an example of how to create Windows DLLs using Rust, including proper exports and Windows API integration.
## Features
@@ -15,8 +15,9 @@ dll-rs is a Rust template that can be compiled as both a dynamic-link library (D
## Project Structure
- `src/main.rs` - Executable entry point
- `src/lib.rs` - Library implementation with DLL exports
- `dll/src/main.rs` - Executable entry point
- `exe/src/lib.rs` - Library implementation with DLL exports
- `build-deps/pe_to_shellcode` - Post-build stomp reflective loader
- Supporting Rust source files
## Building
@@ -29,10 +30,14 @@ Or to build in release:
```bash
cargo build --release
```
Or to build a Reflective DLL:
```bash
cargo run --bin xtask --release
```
## Usage
The project can be used in two ways:
The project can be used in thee ways:
1. As a DLL (**dll-rs.dll**):
- Build in release mode to generate the DLL
@@ -40,12 +45,16 @@ The project can be used in two ways:
2. As an executable (**debug-executable.exe**):
- Run in debug mode to test DLL functionality without DLL debugging gymnastics
- Running in release mode will display a warning message
3. As a Reflective DLL using [@hasherezade's](https://github.com/hasherezade) [pe_to_shellcode](https://github.com/hasherezade/pe_to_shellcode)
- **NOTE: For maximum compatability with this template, all functionality should be called from `dll_main` in `dll/src/lib.rs`**
3. As a Reflective DLL (**dll_rs.shc.dll**) using [@hasherezade's](https://github.com/hasherezade) [pe_to_shellcode](https://github.com/hasherezade/pe_to_shellcode)
- Resolve submodules with `git submodule update --init --recursive`
- `cd .\build-deps\pe_to_shellcode\`
- `cmake .`
- `cmake --build . --config Release`
- `cd ..\..`
- `cargo run --bin xtask --release`
- Use your Reflective DLL in `target/release/dll_rs.shc.dll`
- **NOTE: If the build process above is too complicated/broken for your taste, simply placing the [`pe2shc.exe`](https://github.com/hasherezade/pe_to_shellcode/releases/download/v1.2/pe2shc.exe) executable in the proper folder structure (`build-deps/pe_to_shellcode/pe2shc/Release/pe2shc.exe`) will work**
## Technical Details
@@ -53,12 +62,16 @@ The project can be used in two ways:
- Implements Windows API bindings
- Provides internal FFI declarations for Windows types
- Includes DLL entry point handling
- Remember: maximum compatability with this template, all functionality should be called from `dll_main` in `dll/src/lib.rs`
- Exports `ReflectiveLoader` and handles calling the real reflective loader to support legacy loader checks
- Supports the command line ergonomics of both`shinject` and `dllinject` commands of your [favorite C2 Framework](https://www.cobaltstrike.com/).
## Requirements
- Rust 2024 edition
- Windows operating system
- Cargo build system
- Cmake > 3.0
## Licensing
+6
View File
@@ -0,0 +1,6 @@
[package]
name = "xtask"
version = "0.1.0"
edition = "2024"
[dependencies]
+26
View File
@@ -0,0 +1,26 @@
use std::process::{Command, exit};
fn main() {
let path = std::env::current_dir().unwrap();
println!("The current directory is {}", path.display());
let status = Command::new("cargo")
.args(&["build", "--release", "--manifest-path", "./Cargo.toml"])
.current_dir("./dll")
.status()
.expect("Failed to build");
if !status.success() {
exit(1);
}
let status = Command::new("build-deps/pe_to_shellcode/pe2shc/Release/pe2shc.exe")
.args(&["dll_rs.dll"])
.current_dir("./target/release")
.status()
.expect("Failed to run pe2shc.exe");
if !status.success() {
exit(1);
}
println!("Done");
}