mirror of
https://github.com/JayGLXR/Rusty-Stardust
synced 2026-06-06 15:54:28 +00:00
Add files via 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 = "cc"
|
||||
version = "1.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "stardust"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "stardust"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Stardust Team"]
|
||||
description = "A modern and easy to use 32/64-bit shellcode template in Rust"
|
||||
|
||||
[lib]
|
||||
name = "stardust"
|
||||
crate-type = ["staticlib", "cdylib"]
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
debug = []
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
strip = true
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
FROM ubuntu:22.04
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
build-essential \
|
||||
mingw-w64 \
|
||||
nasm \
|
||||
binutils-mingw-w64 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Rust
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
|
||||
# Set up Rust targets for cross-compilation
|
||||
RUN rustup target add x86_64-pc-windows-gnu i686-pc-windows-gnu
|
||||
|
||||
# Create working directory
|
||||
WORKDIR /stardust
|
||||
|
||||
# Set default command
|
||||
CMD ["bash"]
|
||||
@@ -0,0 +1,56 @@
|
||||
NAME := stardust
|
||||
|
||||
# Docker commands
|
||||
DOCKER_BUILD := docker build -t stardust-rust .
|
||||
DOCKER_RUN := docker run --rm -v $(PWD):/stardust stardust-rust
|
||||
|
||||
# Cargo commands
|
||||
CARGO := cargo
|
||||
|
||||
# Build directories
|
||||
BIN_DIR := bin
|
||||
OBJ_DIR := bin/obj
|
||||
|
||||
all: x64 x86
|
||||
debug: x64-debug x86-debug
|
||||
|
||||
# Create necessary directories
|
||||
dirs:
|
||||
@mkdir -p $(BIN_DIR) $(OBJ_DIR)
|
||||
|
||||
# Build Docker image
|
||||
docker-build:
|
||||
$(DOCKER_BUILD)
|
||||
|
||||
# x64 builds
|
||||
x64: dirs docker-build
|
||||
@echo "Compiling x64 project"
|
||||
$(DOCKER_RUN) $(CARGO) build --release --target x86_64-pc-windows-gnu
|
||||
@echo "Extracting shellcode"
|
||||
$(DOCKER_RUN) x86_64-w64-mingw32-objcopy --dump-section .text=$(BIN_DIR)/$(NAME).x64.bin target/x86_64-pc-windows-gnu/release/stardust.dll
|
||||
|
||||
x64-debug: dirs docker-build
|
||||
@echo "Compiling x64 project (debug)"
|
||||
$(DOCKER_RUN) $(CARGO) build --features debug --target x86_64-pc-windows-gnu
|
||||
@echo "Extracting shellcode"
|
||||
$(DOCKER_RUN) x86_64-w64-mingw32-objcopy --dump-section .text=$(BIN_DIR)/$(NAME).x64.bin target/x86_64-pc-windows-gnu/debug/stardust.dll
|
||||
|
||||
# x86 builds
|
||||
x86: dirs docker-build
|
||||
@echo "Compiling x86 project"
|
||||
$(DOCKER_RUN) $(CARGO) build --release --target i686-pc-windows-gnu
|
||||
@echo "Extracting shellcode"
|
||||
$(DOCKER_RUN) i686-w64-mingw32-objcopy --dump-section .text=$(BIN_DIR)/$(NAME).x86.bin target/i686-pc-windows-gnu/release/stardust.dll
|
||||
|
||||
x86-debug: dirs docker-build
|
||||
@echo "Compiling x86 project (debug)"
|
||||
$(DOCKER_RUN) $(CARGO) build --features debug --target i686-pc-windows-gnu
|
||||
@echo "Extracting shellcode"
|
||||
$(DOCKER_RUN) i686-w64-mingw32-objcopy --dump-section .text=$(BIN_DIR)/$(NAME).x86.bin target/i686-pc-windows-gnu/debug/stardust.dll
|
||||
|
||||
clean:
|
||||
@rm -rf target
|
||||
@rm -rf $(BIN_DIR)/*.bin
|
||||
@echo "Cleaned build artifacts"
|
||||
|
||||
.PHONY: all debug x64 x86 x64-debug x86-debug clean dirs docker-build
|
||||
@@ -0,0 +1,170 @@
|
||||
# Rusty-Stardust
|
||||
|
||||
A modern and easy to use 32/64-bit shellcode template, ported to Rust from the original [Stardust](https://github.com/Cracked5pider/Stardust) project by [C5pider](https://twitter.com/C5pider).
|
||||
|
||||
## About
|
||||
|
||||
This project is a Rust implementation of the Stardust design, which provides a modern approach to position-independent code development. The original Stardust project introduced several key innovations in shellcode design:
|
||||
|
||||
- Separation of code and data into distinct sections
|
||||
- Global instance access in position-independent code
|
||||
- Raw string embedding capabilities
|
||||
- Compile-time string hashing for stealth
|
||||
- Exception handling and debug information stripping
|
||||
|
||||
Rusty-Stardust maintains these core features while leveraging Rust's safety guarantees and modern tooling. This implementation serves as both a practical tool and an educational resource for those interested in learning about position-independent code development in Rust.
|
||||
|
||||
## Features
|
||||
|
||||
- Raw string embedding in shellcode
|
||||
- Compile-time string hashing with fnv1a for both function and module resolution
|
||||
- No_std implementation for minimal footprint
|
||||
- Cross-compilation for both x86 and x64 Windows targets
|
||||
- Docker-based build environment for cross-platform development
|
||||
- Position-independent code using RIP-relative addressing
|
||||
- Exception handling and debug information stripping for minimal footprint
|
||||
- Proper MinGW toolchain integration for reliable shellcode extraction
|
||||
|
||||
## Building
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Docker (for cross-compilation)
|
||||
- Rust (for local development)
|
||||
- NASM (assembled automatically in Docker)
|
||||
- MinGW toolchain (provided in Docker environment)
|
||||
|
||||
### Building with Docker
|
||||
|
||||
Build for release:
|
||||
```shell
|
||||
$ make
|
||||
```
|
||||
|
||||
Build for debug mode (with DbgPrint support):
|
||||
```shell
|
||||
$ make debug
|
||||
```
|
||||
|
||||
### Build Outputs
|
||||
|
||||
Output files will be placed in the `bin` directory:
|
||||
- `stardust.x64.bin`: 64-bit shellcode (586 bytes)
|
||||
- `stardust.x86.bin`: 32-bit shellcode (8.2KB)
|
||||
|
||||
The shellcode is extracted using MinGW's objcopy tool and contains:
|
||||
- Stack setup code
|
||||
- DLL loading functionality
|
||||
- Function resolution code
|
||||
- Architecture-specific instructions
|
||||
|
||||
### Build Configuration
|
||||
|
||||
The project uses several key configurations for reliable shellcode generation:
|
||||
|
||||
1. **Linker Script** (`scripts/linker.ld`):
|
||||
- Discards exception handling sections (`.pdata`, `.xdata`)
|
||||
- Removes debug information (`.debug*`)
|
||||
- Strips unnecessary sections (`.eh_frame`, `.note*`, `.comment*`)
|
||||
|
||||
2. **Cargo Config** (`.cargo/config.toml`):
|
||||
- Position Independent Code (PIC) enabled
|
||||
- Structured Exception Handling (SEH) disabled
|
||||
- Windows subsystem configuration
|
||||
- Custom linker settings for both architectures
|
||||
|
||||
3. **Assembly Integration**:
|
||||
- Assembly files are compiled with NASM
|
||||
- RIP-relative addressing for position independence
|
||||
- Proper section alignment and linking
|
||||
|
||||
## Usage Example
|
||||
|
||||
### Module Resolution
|
||||
|
||||
Resolving modules from PEB:
|
||||
```rust
|
||||
// Resolve modules from PEB using hash
|
||||
let ntdll_hash = hash_str!("ntdll.dll");
|
||||
let ntdll_handle = resolve::module(ntdll_hash);
|
||||
|
||||
let kernel32_hash = hash_str!("kernel32.dll");
|
||||
let kernel32_handle = resolve::module(kernel32_hash);
|
||||
```
|
||||
|
||||
### API Resolution
|
||||
|
||||
Resolving function APIs:
|
||||
```rust
|
||||
// Resolve LoadLibraryA from kernel32
|
||||
let load_library_fn: *mut FnLoadLibraryA = resolve::api(
|
||||
kernel32_handle,
|
||||
hash_str!("LoadLibraryA") as usize
|
||||
);
|
||||
|
||||
// Load user32.dll
|
||||
let user32 = unsafe { (*load_library_fn)(symbol("user32.dll".as_ptr())) };
|
||||
|
||||
// Resolve MessageBoxA from user32
|
||||
let msgbox_fn: *mut FnMessageBoxA = resolve::api(
|
||||
user32 as usize,
|
||||
hash_str!("MessageBoxA") as usize
|
||||
);
|
||||
|
||||
// Display message box
|
||||
unsafe {
|
||||
(*msgbox_fn)(
|
||||
core::ptr::null_mut(),
|
||||
symbol("Hello world".as_ptr()),
|
||||
symbol("caption".as_ptr()),
|
||||
MB_OK
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Debugging Output
|
||||
|
||||
When built in debug mode:
|
||||
```rust
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
dbg_printf!(instance, "Shellcode @ %p [%d bytes]\n", base_addr, size);
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
A test program called "stomper" is included to load and execute the shellcode:
|
||||
|
||||
```shell
|
||||
$ cd tests
|
||||
$ cargo build
|
||||
$ ./target/debug/stomper ../bin/stardust.x64.bin
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
This Rust port maintains the same architecture as the original C++ version:
|
||||
- Assembly files are kept intact and linked using build.rs
|
||||
- Memory layouts match the original for PE file parsing
|
||||
- Windows structures are defined in Rust to match their C/C++ counterparts
|
||||
- Function resolution uses the same hash-based approach for stealth
|
||||
- MinGW toolchain integration for reliable shellcode extraction
|
||||
- Exception handling and debug information stripping for minimal footprint
|
||||
|
||||
## Credits
|
||||
|
||||
This project is a Rust implementation of the original Stardust project. Special thanks to:
|
||||
|
||||
- [C5pider](https://twitter.com/C5pider) for the original Stardust design and implementation
|
||||
- [Modexp](https://twitter.com/modexpblog) for his work on Windows PIC
|
||||
- [Austin Hudson](https://twitter.com/ilove2pwn_) for his work on titanldr-ng
|
||||
- [Kyle Avery](https://twitter.com/kyleavery_) for his work on AceLdr
|
||||
- [x86matthew](https://twitter.com/x86matthew) for assembly guidance
|
||||
- [mrexodia](https://twitter.com/mrexodia) for linker script insights
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
The original Stardust project and its design concepts are the work of [C5pider](https://github.com/Cracked5pider/Stardust). This Rust implementation is provided as an educational resource and alternative implementation for those interested in learning about position-independent code development in Rust.
|
||||
@@ -0,0 +1,27 @@
|
||||
[BITS 64]
|
||||
|
||||
DEFAULT REL
|
||||
|
||||
EXTERN entry
|
||||
GLOBAL stardust
|
||||
GLOBAL RipStart
|
||||
|
||||
[SECTION .text$A]
|
||||
stardust:
|
||||
push rsi
|
||||
mov rsi, rsp
|
||||
and rsp, 0FFFFFFFFFFFFFFF0h
|
||||
sub rsp, 020h
|
||||
call entry
|
||||
mov rsp, rsi
|
||||
pop rsi
|
||||
ret
|
||||
|
||||
RipStart:
|
||||
call RipPtr
|
||||
ret
|
||||
|
||||
RipPtr:
|
||||
mov rax, [rsp]
|
||||
sub rax, 0x1b
|
||||
ret
|
||||
@@ -0,0 +1,15 @@
|
||||
[BITS 64]
|
||||
|
||||
DEFAULT REL
|
||||
|
||||
GLOBAL RipData
|
||||
|
||||
[SECTION .text$C]
|
||||
RipData:
|
||||
call RetPtrData
|
||||
ret
|
||||
|
||||
RetPtrData:
|
||||
mov rax, [rsp]
|
||||
sub rax, 0x5
|
||||
ret
|
||||
@@ -0,0 +1,25 @@
|
||||
[BITS 32]
|
||||
|
||||
DEFAULT REL
|
||||
|
||||
EXTERN _entry
|
||||
GLOBAL _stardust
|
||||
GLOBAL _RipStart
|
||||
|
||||
[SECTION .text$A]
|
||||
_stardust:
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
call _entry
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
|
||||
_RipStart:
|
||||
call _RipPtr
|
||||
ret
|
||||
|
||||
_RipPtr:
|
||||
mov eax, [esp]
|
||||
sub eax, 0x11
|
||||
ret
|
||||
@@ -0,0 +1,15 @@
|
||||
[BITS 32]
|
||||
|
||||
DEFAULT REL
|
||||
|
||||
GLOBAL _RipData
|
||||
|
||||
[SECTION .text$C]
|
||||
_RipData:
|
||||
call _RetPtrData
|
||||
ret
|
||||
|
||||
_RetPtrData:
|
||||
mov eax, [esp]
|
||||
sub eax, 0x5
|
||||
ret
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,93 @@
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let target = env::var("TARGET").unwrap();
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
|
||||
// Compile x64 or x86 assembly files based on target
|
||||
if target.contains("x86_64") {
|
||||
// Assemble x64 files
|
||||
let entry_obj = format!("{}/entry.o", out_dir);
|
||||
let utils_obj = format!("{}/utils.o", out_dir);
|
||||
|
||||
let status = Command::new("nasm")
|
||||
.args(&[
|
||||
"-f", "win64",
|
||||
"asm/x64/entry.asm",
|
||||
"-o", &entry_obj
|
||||
])
|
||||
.status()
|
||||
.expect("Failed to assemble entry.x64.asm");
|
||||
|
||||
if !status.success() {
|
||||
panic!("Failed to assemble entry.x64.asm");
|
||||
}
|
||||
|
||||
let status = Command::new("nasm")
|
||||
.args(&[
|
||||
"-f", "win64",
|
||||
"asm/x64/utils.asm",
|
||||
"-o", &utils_obj
|
||||
])
|
||||
.status()
|
||||
.expect("Failed to assemble utils.x64.asm");
|
||||
|
||||
if !status.success() {
|
||||
panic!("Failed to assemble utils.x64.asm");
|
||||
}
|
||||
|
||||
// Use cc to link the object files
|
||||
cc::Build::new()
|
||||
.object(&entry_obj)
|
||||
.object(&utils_obj)
|
||||
.compile("asm");
|
||||
|
||||
} else if target.contains("i686") {
|
||||
// Assemble x86 files
|
||||
let entry_obj = format!("{}/entry.o", out_dir);
|
||||
let utils_obj = format!("{}/utils.o", out_dir);
|
||||
|
||||
let status = Command::new("nasm")
|
||||
.args(&[
|
||||
"-f", "win32",
|
||||
"asm/x86/entry.asm",
|
||||
"-o", &entry_obj
|
||||
])
|
||||
.status()
|
||||
.expect("Failed to assemble entry.x86.asm");
|
||||
|
||||
if !status.success() {
|
||||
panic!("Failed to assemble entry.x86.asm");
|
||||
}
|
||||
|
||||
let status = Command::new("nasm")
|
||||
.args(&[
|
||||
"-f", "win32",
|
||||
"asm/x86/utils.asm",
|
||||
"-o", &utils_obj
|
||||
])
|
||||
.status()
|
||||
.expect("Failed to assemble utils.x86.asm");
|
||||
|
||||
if !status.success() {
|
||||
panic!("Failed to assemble utils.x86.asm");
|
||||
}
|
||||
|
||||
// Use cc to link the object files
|
||||
cc::Build::new()
|
||||
.object(&entry_obj)
|
||||
.object(&utils_obj)
|
||||
.compile("asm");
|
||||
} else {
|
||||
panic!("Unsupported target: {}", target);
|
||||
}
|
||||
|
||||
// Tell cargo to rerun if assembly files change
|
||||
println!("cargo:rerun-if-changed=asm/x64/entry.asm");
|
||||
println!("cargo:rerun-if-changed=asm/x64/utils.asm");
|
||||
println!("cargo:rerun-if-changed=asm/x86/entry.asm");
|
||||
println!("cargo:rerun-if-changed=asm/x86/utils.asm");
|
||||
println!("cargo:rerun-if-changed=scripts/linker.ld");
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# This script can be used for local testing with a native Rust installation
|
||||
# It won't generate the actual cross-compiled Windows shellcode but helps validate code
|
||||
|
||||
echo "Building Rust code with native compiler..."
|
||||
cargo build
|
||||
|
||||
echo "Testing code structure..."
|
||||
cargo check --all-features
|
||||
|
||||
echo "Checking for errors..."
|
||||
cargo clippy
|
||||
|
||||
echo "All tests passed!"
|
||||
echo "Note: This is only a code quality test - for actual Windows shellcode generation,"
|
||||
echo "you need to use the Docker-based build with cross-compilation."
|
||||
@@ -0,0 +1,21 @@
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
*( .text$A );
|
||||
*( .text$B );
|
||||
*( .rdata* );
|
||||
*( .text$C );
|
||||
}
|
||||
|
||||
/* Discard exception handling and debug sections */
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.pdata);
|
||||
*(.xdata);
|
||||
*(.debug*);
|
||||
*(.eh_frame);
|
||||
*(.note*);
|
||||
*(.comment*);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
// FNV1a hash constants
|
||||
pub const FNV1A_PRIME: u32 = 0x01000193;
|
||||
pub const FNV1A_BASIS: u32 = 0x811c9dc5;
|
||||
|
||||
// Constants for shellcode
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub const END_OFFSET: usize = 0x10;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
pub const END_OFFSET: usize = 0x10;
|
||||
|
||||
// Windows constants
|
||||
pub const IMAGE_DOS_SIGNATURE: u16 = 0x5A4D;
|
||||
pub const IMAGE_NT_SIGNATURE: u32 = 0x00004550;
|
||||
pub const IMAGE_DIRECTORY_ENTRY_EXPORT: usize = 0;
|
||||
pub const MB_OK: u32 = 0x00000000;
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
use crate::constants::{FNV1A_BASIS, FNV1A_PRIME};
|
||||
|
||||
// Runtime hash function for strings
|
||||
pub unsafe fn hash_string(string: *const u8) -> u32 {
|
||||
let mut hash = FNV1A_BASIS;
|
||||
let mut ptr = string;
|
||||
|
||||
while *ptr != 0 {
|
||||
let mut byte = *ptr;
|
||||
|
||||
// Convert to uppercase if lowercase
|
||||
if byte >= b'a' {
|
||||
byte -= 0x20;
|
||||
}
|
||||
|
||||
hash ^= byte as u32;
|
||||
hash = hash.wrapping_mul(FNV1A_PRIME);
|
||||
|
||||
ptr = ptr.add(1);
|
||||
}
|
||||
|
||||
hash
|
||||
}
|
||||
|
||||
// Runtime hash function for wide strings
|
||||
pub unsafe fn hash_string_wide(string: *const u16) -> u32 {
|
||||
let mut hash = FNV1A_BASIS;
|
||||
let mut ptr = string;
|
||||
|
||||
while *ptr != 0 {
|
||||
let mut byte = (*ptr & 0xFF) as u8;
|
||||
|
||||
// Convert to uppercase if lowercase
|
||||
if byte >= b'a' {
|
||||
byte -= 0x20;
|
||||
}
|
||||
|
||||
hash ^= byte as u32;
|
||||
hash = hash.wrapping_mul(FNV1A_PRIME);
|
||||
|
||||
ptr = ptr.add(1);
|
||||
}
|
||||
|
||||
hash
|
||||
}
|
||||
|
||||
// Compile-time hash function for const evaluation
|
||||
#[allow(dead_code)]
|
||||
pub const fn hash_const(s: &str) -> u32 {
|
||||
let bytes = s.as_bytes();
|
||||
let mut hash = FNV1A_BASIS;
|
||||
let mut i = 0;
|
||||
|
||||
while i < bytes.len() {
|
||||
let mut byte = bytes[i];
|
||||
|
||||
// Convert to uppercase if lowercase
|
||||
if byte >= b'a' {
|
||||
byte -= 0x20;
|
||||
}
|
||||
|
||||
hash ^= byte as u32;
|
||||
hash = hash.wrapping_mul(FNV1A_PRIME);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
hash
|
||||
}
|
||||
|
||||
// Macro for creating compile-time hashes
|
||||
#[macro_export]
|
||||
macro_rules! hash_str {
|
||||
($s:expr) => {
|
||||
$crate::hash::hash_const($s)
|
||||
};
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
use crate::constants::{END_OFFSET, MB_OK};
|
||||
use crate::hash_str;
|
||||
use crate::memory::symbol;
|
||||
use crate::resolve;
|
||||
use crate::windows::*;
|
||||
use core::ffi::c_void;
|
||||
|
||||
// Type aliases for Windows API functions
|
||||
type FnLoadLibraryA = unsafe extern "system" fn(lpLibFileName: PSTR) -> HMODULE;
|
||||
type FnGetProcAddress = unsafe extern "system" fn(hModule: HMODULE, lpProcName: PSTR) -> PVOID;
|
||||
type FnMessageBoxA = unsafe extern "system" fn(hWnd: PVOID, lpText: PSTR, lpCaption: PSTR, uType: u32) -> i32;
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
type FnDbgPrint = unsafe extern "system" fn(Format: PCH, ...) -> i32;
|
||||
|
||||
// Keep resolve_import macro but mark it as #[allow(unused_macros)] since it might be needed later
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! resolve_import {
|
||||
($instance:expr, $module:ident) => {
|
||||
{
|
||||
let base_addr = $instance.$module.handle;
|
||||
if base_addr != 0 {
|
||||
$(
|
||||
// For each API in the module struct, resolve it using its hash
|
||||
$instance.$module.$api = core::mem::transmute(
|
||||
resolve::_api(base_addr, $instance.$module.$api as usize)
|
||||
);
|
||||
)*
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Debug print macro
|
||||
#[cfg(feature = "debug")]
|
||||
#[macro_export]
|
||||
macro_rules! dbg_printf {
|
||||
($instance:expr, $fmt:expr, $($arg:expr),*) => {
|
||||
{
|
||||
unsafe {
|
||||
if !$instance.ntdll.DbgPrint.is_null() {
|
||||
// Format like the original: [DEBUG::function::line] message
|
||||
let prefix = symbol::<PCH>(concat!("[DEBUG::", file!(), "::", line!(), "] ").as_ptr());
|
||||
let msg = symbol::<PCH>($fmt.as_ptr());
|
||||
($instance.ntdll.DbgPrint)(prefix, msg, $($arg),*);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "debug"))]
|
||||
#[macro_export]
|
||||
macro_rules! dbg_printf {
|
||||
($instance:expr, $fmt:expr, $($arg:expr),*) => {
|
||||
{}
|
||||
};
|
||||
}
|
||||
|
||||
// Main instance structure
|
||||
pub struct Instance {
|
||||
// Base address and size
|
||||
pub base: BaseInfo,
|
||||
|
||||
// Modules
|
||||
pub ntdll: NtdllModule,
|
||||
pub kernel32: Kernel32Module,
|
||||
}
|
||||
|
||||
// Base information
|
||||
pub struct BaseInfo {
|
||||
pub address: usize,
|
||||
pub length: usize,
|
||||
}
|
||||
|
||||
// Ntdll module
|
||||
pub struct NtdllModule {
|
||||
pub handle: usize,
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
pub DbgPrint: *mut FnDbgPrint,
|
||||
}
|
||||
|
||||
// Kernel32 module
|
||||
pub struct Kernel32Module {
|
||||
pub handle: usize,
|
||||
pub LoadLibraryA: *mut FnLoadLibraryA,
|
||||
pub GetProcAddress: *mut FnGetProcAddress,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
// Initialize the instance
|
||||
pub fn new() -> Self {
|
||||
unsafe {
|
||||
// Create instance with empty values
|
||||
let mut instance = Instance {
|
||||
base: BaseInfo {
|
||||
address: 0,
|
||||
length: 0,
|
||||
},
|
||||
|
||||
ntdll: NtdllModule {
|
||||
handle: 0,
|
||||
#[cfg(feature = "debug")]
|
||||
DbgPrint: core::ptr::null_mut(),
|
||||
},
|
||||
|
||||
kernel32: Kernel32Module {
|
||||
handle: 0,
|
||||
LoadLibraryA: core::ptr::null_mut(),
|
||||
GetProcAddress: core::ptr::null_mut(),
|
||||
},
|
||||
};
|
||||
|
||||
// Calculate shellcode base address + size
|
||||
instance.base.address = crate::memory::RipStart();
|
||||
instance.base.length = (crate::memory::RipData() - instance.base.address) + END_OFFSET;
|
||||
|
||||
// Resolve modules from PEB
|
||||
instance.ntdll.handle = resolve::module(hash_str!("ntdll.dll"));
|
||||
if instance.ntdll.handle == 0 {
|
||||
return instance;
|
||||
}
|
||||
|
||||
instance.kernel32.handle = resolve::module(hash_str!("kernel32.dll"));
|
||||
if instance.kernel32.handle == 0 {
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Resolve imports
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
instance.ntdll.DbgPrint = core::mem::transmute(
|
||||
resolve::_api(instance.ntdll.handle, hash_str!("DbgPrint") as usize)
|
||||
);
|
||||
}
|
||||
|
||||
instance.kernel32.LoadLibraryA = core::mem::transmute(
|
||||
resolve::_api(instance.kernel32.handle, hash_str!("LoadLibraryA") as usize)
|
||||
);
|
||||
|
||||
instance.kernel32.GetProcAddress = core::mem::transmute(
|
||||
resolve::_api(instance.kernel32.handle, hash_str!("GetProcAddress") as usize)
|
||||
);
|
||||
|
||||
instance
|
||||
}
|
||||
}
|
||||
|
||||
// Start the shellcode execution
|
||||
pub unsafe fn start(&self, _arg: *mut c_void) {
|
||||
// Load user32.dll
|
||||
let user32 = if !self.kernel32.LoadLibraryA.is_null() {
|
||||
unsafe { (*self.kernel32.LoadLibraryA)(symbol("user32.dll".as_ptr())) }
|
||||
} else {
|
||||
core::ptr::null_mut()
|
||||
};
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
if !user32.is_null() {
|
||||
dbg_printf!(self, "oh wow look we loaded user32.dll -> %p\n", user32);
|
||||
} else {
|
||||
dbg_printf!(self, "okay something went wrong. failed to load user32 :/\n");
|
||||
}
|
||||
|
||||
let peb = NtCurrentPeb();
|
||||
dbg_printf!(self, "running from %ls (Pid: %d)\n",
|
||||
(*(*peb).ProcessParameters).ImagePathName.Buffer,
|
||||
(*NtCurrentTeb()).ClientId.UniqueProcess);
|
||||
|
||||
dbg_printf!(self, "shellcode @ %p [%d bytes]\n", self.base.address, self.base.length);
|
||||
}
|
||||
|
||||
// Resolve MessageBoxA and display message
|
||||
if !user32.is_null() {
|
||||
let msgbox: *mut FnMessageBoxA = core::mem::transmute(
|
||||
resolve::_api(user32 as usize, hash_str!("MessageBoxA") as usize)
|
||||
);
|
||||
|
||||
if !msgbox.is_null() {
|
||||
unsafe {
|
||||
(*msgbox)(
|
||||
core::ptr::null_mut(),
|
||||
symbol("Hello world".as_ptr()),
|
||||
symbol("caption".as_ptr()),
|
||||
MB_OK
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
// Import modules
|
||||
mod constants;
|
||||
mod hash;
|
||||
mod instance;
|
||||
mod memory;
|
||||
mod resolve;
|
||||
mod windows;
|
||||
|
||||
// Re-export key components but exclude memory to avoid shadowing
|
||||
pub use constants::*;
|
||||
pub use hash::*;
|
||||
pub use instance::*;
|
||||
// Don't re-export memory to avoid shadowing the assembly functions
|
||||
pub use resolve::*;
|
||||
pub use windows::*;
|
||||
|
||||
use core::ffi::c_void;
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
// External assembly functions (defined in our assembly files)
|
||||
// These are now properly declared only here
|
||||
#[link(name = "asm")]
|
||||
extern "C" {
|
||||
pub fn RipStart() -> usize;
|
||||
pub fn RipData() -> usize;
|
||||
}
|
||||
|
||||
// Shellcode entry point - matches the assembly entry point
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn entry(args: *mut c_void) {
|
||||
// Create instance and start execution
|
||||
let instance = instance::Instance::new();
|
||||
instance.start(args);
|
||||
}
|
||||
|
||||
// Panic handler that does nothing
|
||||
#[panic_handler]
|
||||
fn panic(_info: &PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
// Exit handler for #![no_std]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __aeabi_unwind_cpp_pr0() {
|
||||
loop {}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
//! Memory manipulation functions
|
||||
|
||||
// Zero memory (Rust's equivalent of RtlSecureZeroMemory)
|
||||
pub unsafe fn zero(memory: *mut u8, length: u32) {
|
||||
for i in 0..length {
|
||||
*memory.offset(i as isize) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Memory copy function
|
||||
pub unsafe fn copy(destination: *mut u8, source: *const u8, length: u32) -> *mut u8 {
|
||||
for i in 0..length {
|
||||
*destination.offset(i as isize) = *source.offset(i as isize);
|
||||
}
|
||||
destination
|
||||
}
|
||||
|
||||
// Memory compare function
|
||||
pub unsafe fn compare(memory1: *const u8, memory2: *const u8, length: usize) -> u32 {
|
||||
let mut a = memory1;
|
||||
let mut b = memory2;
|
||||
let mut len = length;
|
||||
|
||||
while len > 0 {
|
||||
let val1 = *a;
|
||||
let val2 = *b;
|
||||
|
||||
if val1 != val2 {
|
||||
return (val1 as i32 - val2 as i32) as u32;
|
||||
}
|
||||
|
||||
a = a.offset(1);
|
||||
b = b.offset(1);
|
||||
len -= 1;
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
// Get string from RIP-relative addressing
|
||||
pub unsafe fn symbol<T>(s: *const u8) -> T {
|
||||
let rip_data = external_rip_data();
|
||||
let offset = (s as usize).wrapping_sub(rip_data_fn_addr() as usize);
|
||||
let absolute_addr = rip_data.wrapping_sub(offset);
|
||||
|
||||
core::mem::transmute_copy(&absolute_addr)
|
||||
}
|
||||
|
||||
// Use the external RipStart/RipData functions from lib.rs
|
||||
extern "C" {
|
||||
// These are now public functions
|
||||
pub fn RipStart() -> usize;
|
||||
pub fn RipData() -> usize;
|
||||
}
|
||||
|
||||
// Helper functions to get addresses
|
||||
fn rip_data_fn_addr() -> usize {
|
||||
RipData as usize
|
||||
}
|
||||
|
||||
fn external_rip_data() -> usize {
|
||||
unsafe { RipData() }
|
||||
}
|
||||
|
||||
// Macro to define the RangeHeadList loop, improved version
|
||||
#[macro_export]
|
||||
macro_rules! range_head_list {
|
||||
($head_list:expr, $type:ty, |$current:ident| $body:block) => {
|
||||
{
|
||||
let head_ptr = $head_list as *const LIST_ENTRY;
|
||||
let mut $current = (*head_ptr).Flink as $type;
|
||||
|
||||
while $current as *const _ != head_ptr as *const _ {
|
||||
$body
|
||||
$current = (*$current).InLoadOrderLinks.Flink as $type;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
use crate::constants::{IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_DOS_SIGNATURE, IMAGE_NT_SIGNATURE};
|
||||
use crate::hash::{hash_string, hash_string_wide};
|
||||
use crate::range_head_list;
|
||||
use crate::windows::*;
|
||||
|
||||
/// Finds a module by its hash in the PEB's loaded module list
|
||||
pub unsafe fn module(library_hash: u32) -> usize {
|
||||
let peb = NtCurrentPeb();
|
||||
let ldr = (*peb).Ldr;
|
||||
let module_list = &(*ldr).InLoadOrderModuleList;
|
||||
|
||||
let mut result = 0;
|
||||
|
||||
// Use the fixed range_head_list macro instead of manual iteration
|
||||
range_head_list!(module_list, PLDR_DATA_TABLE_ENTRY, |current| {
|
||||
if library_hash == 0 {
|
||||
result = (*current).OriginalBase as usize;
|
||||
break;
|
||||
}
|
||||
|
||||
if hash_string_wide((*current).BaseDllName.Buffer) == library_hash {
|
||||
result = (*current).OriginalBase as usize;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Finds an API function by its hash in the specified module
|
||||
pub unsafe fn _api(module_base: usize, symbol_hash: usize) -> usize {
|
||||
if module_base == 0 || symbol_hash == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut address = 0;
|
||||
|
||||
// Get DOS header
|
||||
let dos_header = module_base as *mut IMAGE_DOS_HEADER;
|
||||
if (*dos_header).e_magic != IMAGE_DOS_SIGNATURE {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get NT headers
|
||||
let nt_headers = (module_base + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS;
|
||||
if (*nt_headers).Signature != IMAGE_NT_SIGNATURE {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get export directory
|
||||
let export_dir_rva = (*nt_headers).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||
let export_dir = (module_base + export_dir_rva as usize) as *mut IMAGE_EXPORT_DIRECTORY;
|
||||
|
||||
// Get export tables
|
||||
let names = (module_base + (*export_dir).AddressOfNames as usize) as *mut u32;
|
||||
let functions = (module_base + (*export_dir).AddressOfFunctions as usize) as *mut u32;
|
||||
let ordinals = (module_base + (*export_dir).AddressOfNameOrdinals as usize) as *mut u16;
|
||||
|
||||
// Find the export by hash
|
||||
for i in 0..(*export_dir).NumberOfNames {
|
||||
let name_rva = *names.offset(i as isize);
|
||||
let name = (module_base + name_rva as usize) as *const u8;
|
||||
|
||||
if hash_string(name) == symbol_hash as u32 {
|
||||
let ordinal = *ordinals.offset(i as isize) as isize;
|
||||
let function_rva = *functions.offset(ordinal);
|
||||
address = module_base + function_rva as usize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
address
|
||||
}
|
||||
|
||||
/// Generic wrapper for _api that returns a typed function pointer
|
||||
pub unsafe fn api<T>(module_base: usize, symbol_hash: usize) -> *mut T {
|
||||
_api(module_base, symbol_hash) as *mut T
|
||||
}
|
||||
|
||||
// Macro to resolve an API by name
|
||||
#[macro_export]
|
||||
macro_rules! resolve_api {
|
||||
($module:expr, $name:ident) => {
|
||||
$crate::resolve::api::<unsafe extern "system" fn()>(
|
||||
$module,
|
||||
$crate::hash_str!(stringify!($name)) as usize
|
||||
) as *const unsafe extern "system" fn()
|
||||
};
|
||||
}
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
#![allow(non_snake_case, non_camel_case_types)]
|
||||
|
||||
use core::ffi::c_void;
|
||||
|
||||
// Windows basic types
|
||||
pub type BYTE = u8;
|
||||
pub type WORD = u16;
|
||||
pub type DWORD = u32;
|
||||
pub type LONG = i32;
|
||||
pub type ULONG = u32;
|
||||
pub type ULONG_PTR = usize;
|
||||
pub type PVOID = *mut c_void;
|
||||
pub type LPVOID = *mut c_void;
|
||||
pub type HANDLE = PVOID;
|
||||
pub type HMODULE = HANDLE;
|
||||
pub type BOOL = i32;
|
||||
pub type PSTR = *mut u8;
|
||||
pub type PWSTR = *mut u16;
|
||||
pub type PCH = *const i8;
|
||||
pub type LPCSTR = *const i8;
|
||||
pub type LPCWSTR = *const u16;
|
||||
pub type UINT = u32;
|
||||
pub type WCHAR = u16;
|
||||
|
||||
// Windows structures
|
||||
#[repr(C)]
|
||||
pub struct LIST_ENTRY {
|
||||
pub Flink: *mut LIST_ENTRY,
|
||||
pub Blink: *mut LIST_ENTRY,
|
||||
}
|
||||
pub type PLIST_ENTRY = *mut LIST_ENTRY;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct UNICODE_STRING {
|
||||
pub Length: u16,
|
||||
pub MaximumLength: u16,
|
||||
pub Buffer: *mut WCHAR,
|
||||
}
|
||||
pub type PUNICODE_STRING = *mut UNICODE_STRING;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct CLIENT_ID {
|
||||
pub UniqueProcess: HANDLE,
|
||||
pub UniqueThread: HANDLE,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct RTL_USER_PROCESS_PARAMETERS {
|
||||
// Simplified - only what we need
|
||||
pub Reserved1: [BYTE; 16],
|
||||
pub Reserved2: [PVOID; 10],
|
||||
pub ImagePathName: UNICODE_STRING,
|
||||
pub CommandLine: UNICODE_STRING,
|
||||
}
|
||||
pub type PRTL_USER_PROCESS_PARAMETERS = *mut RTL_USER_PROCESS_PARAMETERS;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PEB_LDR_DATA {
|
||||
pub Length: ULONG,
|
||||
pub Initialized: BOOL,
|
||||
pub SsHandle: HANDLE,
|
||||
pub InLoadOrderModuleList: LIST_ENTRY,
|
||||
pub InMemoryOrderModuleList: LIST_ENTRY,
|
||||
pub InInitializationOrderModuleList: LIST_ENTRY,
|
||||
}
|
||||
pub type PPEB_LDR_DATA = *mut PEB_LDR_DATA;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct LDR_DATA_TABLE_ENTRY {
|
||||
pub InLoadOrderLinks: LIST_ENTRY,
|
||||
pub InMemoryOrderLinks: LIST_ENTRY,
|
||||
pub InInitializationOrderLinks: LIST_ENTRY,
|
||||
pub OriginalBase: PVOID,
|
||||
pub EntryPoint: PVOID,
|
||||
pub SizeOfImage: ULONG,
|
||||
pub FullDllName: UNICODE_STRING,
|
||||
pub BaseDllName: UNICODE_STRING,
|
||||
pub Flags: ULONG,
|
||||
pub LoadCount: u16,
|
||||
pub TlsIndex: u16,
|
||||
pub HashLinks: LIST_ENTRY,
|
||||
pub TimeDateStamp: ULONG,
|
||||
}
|
||||
pub type PLDR_DATA_TABLE_ENTRY = *mut LDR_DATA_TABLE_ENTRY;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PEB {
|
||||
pub InheritedAddressSpace: BYTE,
|
||||
pub ReadImageFileExecOptions: BYTE,
|
||||
pub BeingDebugged: BYTE,
|
||||
pub BitField: BYTE,
|
||||
pub Mutant: HANDLE,
|
||||
pub ImageBaseAddress: PVOID,
|
||||
pub Ldr: *mut PEB_LDR_DATA,
|
||||
// ... other fields not needed for our purposes
|
||||
}
|
||||
pub type PPEB = *mut PEB;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct TEB {
|
||||
pub Reserved1: [BYTE; 12],
|
||||
pub ProcessEnvironmentBlock: PPEB,
|
||||
pub Reserved2: [BYTE; 399],
|
||||
pub ClientId: CLIENT_ID,
|
||||
// Simplified - truncated remaining fields
|
||||
}
|
||||
pub type PTEB = *mut TEB;
|
||||
|
||||
// PE Format structures
|
||||
#[repr(C)]
|
||||
pub struct IMAGE_DOS_HEADER {
|
||||
pub e_magic: WORD,
|
||||
pub e_cblp: WORD,
|
||||
pub e_cp: WORD,
|
||||
pub e_crlc: WORD,
|
||||
pub e_cparhdr: WORD,
|
||||
pub e_minalloc: WORD,
|
||||
pub e_maxalloc: WORD,
|
||||
pub e_ss: WORD,
|
||||
pub e_sp: WORD,
|
||||
pub e_csum: WORD,
|
||||
pub e_ip: WORD,
|
||||
pub e_cs: WORD,
|
||||
pub e_lfarlc: WORD,
|
||||
pub e_ovno: WORD,
|
||||
pub e_res: [WORD; 4],
|
||||
pub e_oemid: WORD,
|
||||
pub e_oeminfo: WORD,
|
||||
pub e_res2: [WORD; 10],
|
||||
pub e_lfanew: LONG,
|
||||
}
|
||||
pub type PIMAGE_DOS_HEADER = *mut IMAGE_DOS_HEADER;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct IMAGE_DATA_DIRECTORY {
|
||||
pub VirtualAddress: DWORD,
|
||||
pub Size: DWORD,
|
||||
}
|
||||
pub type PIMAGE_DATA_DIRECTORY = *mut IMAGE_DATA_DIRECTORY;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct IMAGE_OPTIONAL_HEADER64 {
|
||||
pub Magic: WORD,
|
||||
pub MajorLinkerVersion: BYTE,
|
||||
pub MinorLinkerVersion: BYTE,
|
||||
pub SizeOfCode: DWORD,
|
||||
pub SizeOfInitializedData: DWORD,
|
||||
pub SizeOfUninitializedData: DWORD,
|
||||
pub AddressOfEntryPoint: DWORD,
|
||||
pub BaseOfCode: DWORD,
|
||||
pub ImageBase: u64,
|
||||
pub SectionAlignment: DWORD,
|
||||
pub FileAlignment: DWORD,
|
||||
pub MajorOperatingSystemVersion: WORD,
|
||||
pub MinorOperatingSystemVersion: WORD,
|
||||
pub MajorImageVersion: WORD,
|
||||
pub MinorImageVersion: WORD,
|
||||
pub MajorSubsystemVersion: WORD,
|
||||
pub MinorSubsystemVersion: WORD,
|
||||
pub Win32VersionValue: DWORD,
|
||||
pub SizeOfImage: DWORD,
|
||||
pub SizeOfHeaders: DWORD,
|
||||
pub CheckSum: DWORD,
|
||||
pub Subsystem: WORD,
|
||||
pub DllCharacteristics: WORD,
|
||||
pub SizeOfStackReserve: u64,
|
||||
pub SizeOfStackCommit: u64,
|
||||
pub SizeOfHeapReserve: u64,
|
||||
pub SizeOfHeapCommit: u64,
|
||||
pub LoaderFlags: DWORD,
|
||||
pub NumberOfRvaAndSizes: DWORD,
|
||||
pub DataDirectory: [IMAGE_DATA_DIRECTORY; 16],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct IMAGE_OPTIONAL_HEADER32 {
|
||||
pub Magic: WORD,
|
||||
pub MajorLinkerVersion: BYTE,
|
||||
pub MinorLinkerVersion: BYTE,
|
||||
pub SizeOfCode: DWORD,
|
||||
pub SizeOfInitializedData: DWORD,
|
||||
pub SizeOfUninitializedData: DWORD,
|
||||
pub AddressOfEntryPoint: DWORD,
|
||||
pub BaseOfCode: DWORD,
|
||||
pub BaseOfData: DWORD,
|
||||
pub ImageBase: DWORD,
|
||||
pub SectionAlignment: DWORD,
|
||||
pub FileAlignment: DWORD,
|
||||
pub MajorOperatingSystemVersion: WORD,
|
||||
pub MinorOperatingSystemVersion: WORD,
|
||||
pub MajorImageVersion: WORD,
|
||||
pub MinorImageVersion: WORD,
|
||||
pub MajorSubsystemVersion: WORD,
|
||||
pub MinorSubsystemVersion: WORD,
|
||||
pub Win32VersionValue: DWORD,
|
||||
pub SizeOfImage: DWORD,
|
||||
pub SizeOfHeaders: DWORD,
|
||||
pub CheckSum: DWORD,
|
||||
pub Subsystem: WORD,
|
||||
pub DllCharacteristics: WORD,
|
||||
pub SizeOfStackReserve: DWORD,
|
||||
pub SizeOfStackCommit: DWORD,
|
||||
pub SizeOfHeapReserve: DWORD,
|
||||
pub SizeOfHeapCommit: DWORD,
|
||||
pub LoaderFlags: DWORD,
|
||||
pub NumberOfRvaAndSizes: DWORD,
|
||||
pub DataDirectory: [IMAGE_DATA_DIRECTORY; 16],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct IMAGE_FILE_HEADER {
|
||||
pub Machine: WORD,
|
||||
pub NumberOfSections: WORD,
|
||||
pub TimeDateStamp: DWORD,
|
||||
pub PointerToSymbolTable: DWORD,
|
||||
pub NumberOfSymbols: DWORD,
|
||||
pub SizeOfOptionalHeader: WORD,
|
||||
pub Characteristics: WORD,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[repr(C)]
|
||||
pub struct IMAGE_NT_HEADERS {
|
||||
pub Signature: DWORD,
|
||||
pub FileHeader: IMAGE_FILE_HEADER,
|
||||
pub OptionalHeader: IMAGE_OPTIONAL_HEADER64,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
#[repr(C)]
|
||||
pub struct IMAGE_NT_HEADERS {
|
||||
pub Signature: DWORD,
|
||||
pub FileHeader: IMAGE_FILE_HEADER,
|
||||
pub OptionalHeader: IMAGE_OPTIONAL_HEADER32,
|
||||
}
|
||||
|
||||
pub type PIMAGE_NT_HEADERS = *mut IMAGE_NT_HEADERS;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct IMAGE_EXPORT_DIRECTORY {
|
||||
pub Characteristics: DWORD,
|
||||
pub TimeDateStamp: DWORD,
|
||||
pub MajorVersion: WORD,
|
||||
pub MinorVersion: WORD,
|
||||
pub Name: DWORD,
|
||||
pub Base: DWORD,
|
||||
pub NumberOfFunctions: DWORD,
|
||||
pub NumberOfNames: DWORD,
|
||||
pub AddressOfFunctions: DWORD,
|
||||
pub AddressOfNames: DWORD,
|
||||
pub AddressOfNameOrdinals: DWORD,
|
||||
}
|
||||
pub type PIMAGE_EXPORT_DIRECTORY = *mut IMAGE_EXPORT_DIRECTORY;
|
||||
|
||||
// Helper functions for accessing TEB/PEB
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn NtCurrentTeb() -> PTEB {
|
||||
let teb: PTEB;
|
||||
core::arch::asm!(
|
||||
"mov {}, gs:[0x30]",
|
||||
out(reg) teb,
|
||||
options(nostack, preserves_flags)
|
||||
);
|
||||
teb
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn NtCurrentTeb() -> PTEB {
|
||||
let teb: PTEB;
|
||||
core::arch::asm!(
|
||||
"mov {}, fs:[0x18]",
|
||||
out(reg) teb,
|
||||
options(nostack, preserves_flags)
|
||||
);
|
||||
teb
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub unsafe fn NtCurrentPeb() -> *mut PEB {
|
||||
let peb: *mut PEB;
|
||||
core::arch::asm!(
|
||||
"mov {}, gs:[0x60]",
|
||||
out(reg) peb,
|
||||
options(nostack, preserves_flags)
|
||||
);
|
||||
peb
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
pub unsafe fn NtCurrentPeb() -> *mut PEB {
|
||||
let peb: *mut PEB;
|
||||
core::arch::asm!(
|
||||
"mov {}, fs:[0x30]",
|
||||
out(reg) peb,
|
||||
options(nostack, preserves_flags)
|
||||
);
|
||||
peb
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "stomper"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "stomper"
|
||||
path = "stomper.rs"
|
||||
|
||||
[dependencies]
|
||||
windows = { version = "0.48", features = ["Win32_System_Memory"] }
|
||||
@@ -0,0 +1,76 @@
|
||||
use std::fs;
|
||||
use std::mem;
|
||||
use std::path::Path;
|
||||
use std::ptr;
|
||||
use std::env;
|
||||
use windows::Win32::System::Memory::{
|
||||
VirtualAlloc, VirtualProtect, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE, PAGE_READWRITE
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Determine architecture
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let bin_path = if args.len() > 1 {
|
||||
args[1].clone()
|
||||
} else {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let bin_name = "stardust.x64.bin";
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
let bin_name = "stardust.x86.bin";
|
||||
|
||||
format!("../bin/{}", bin_name)
|
||||
};
|
||||
|
||||
println!("Loading shellcode from: {}", bin_path);
|
||||
|
||||
// Read shellcode from file
|
||||
let shellcode = fs::read(Path::new(&bin_path))?;
|
||||
let shellcode_len = shellcode.len();
|
||||
|
||||
println!("Shellcode size: {} bytes", shellcode_len);
|
||||
|
||||
unsafe {
|
||||
// Allocate memory for the shellcode
|
||||
let memory = VirtualAlloc(
|
||||
ptr::null(),
|
||||
shellcode_len,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_READWRITE
|
||||
);
|
||||
|
||||
if memory.is_null() {
|
||||
return Err("Failed to allocate memory".into());
|
||||
}
|
||||
|
||||
println!("Memory allocated at: {:p}", memory);
|
||||
|
||||
// Copy shellcode to allocated memory
|
||||
ptr::copy_nonoverlapping(
|
||||
shellcode.as_ptr(),
|
||||
memory.cast::<u8>(),
|
||||
shellcode_len
|
||||
);
|
||||
|
||||
// Make memory executable
|
||||
let mut old_protect = PAGE_READWRITE;
|
||||
VirtualProtect(
|
||||
memory,
|
||||
shellcode_len,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
&mut old_protect
|
||||
)?;
|
||||
|
||||
println!("Memory protection changed to PAGE_EXECUTE_READWRITE");
|
||||
|
||||
// Create function pointer and execute shellcode
|
||||
let shellcode_fn: extern "C" fn(arg: *const u8) = mem::transmute(memory);
|
||||
|
||||
println!("Executing shellcode...");
|
||||
shellcode_fn(ptr::null());
|
||||
|
||||
println!("Shellcode execution completed");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user