Rust 移植版,源自 Dinvoke。DInvoke_rs 可用于多种用途,例如 PE 解析、动态解析导出函数、在运行时动态加载 PE 插件、规避 API 钩子等。
功能特性:
所有功劳归于本工具原始 C# 实现的创建者:
通过将以下行添加到你的 cargo.toml 中,将此 crate 导入到你的项目:```rust
[dependencies]
dinvoke_rs = "0.2.2"
# 示例
## 解析导出的 API
下面的示例演示了如何使用 DInvoke_rs 动态查找并调用 DLL(本例中为 `ntdll.dll`)的导出函数。
1) 获取 ntdll 的基地址。
2) 使用 `get_function_address()` 按名称查找 `ntdll.dll` 内的导出函数。这是通过遍历并解析该 DLL 的 EAT 实现的。
3) 你还可以通过调用 `get_function_address_by_ordinal()` 按序号查找导出函数。```rust
fn main() {
// Dynamically obtain ntdll.dll's base address.
let ntdll = dinvoke_rs::dinvoke::get_module_base_address("ntdll.dll");
if ntdll != 0
{
println!("ntdll.dll base address is 0x{:X}", ntdll);
// Dynamically obtain the address of a function by name.
let nt_create_thread = dinvoke_rs::dinvoke::get_function_address(ntdll, "NtCreateThread");
if nt_create_thread != 0
{
println!("NtCreateThread is at address 0x{:X}", nt_create_thread);
}
// Dynamically obtain the address of a function by ordinal.
let ordinal_8 = dinvoke_rs::dinvoke::get_function_address_by_ordinal(ntdll, 8);
if ordinal_8 != 0
{
println!("The function with ordinal 8 is located at addresss 0x{:X}", ordinal_8);
}
}
}
在以下示例中,我们使用 DInvoke_rs 动态调用 RtlAdjustPrivilege,以便为当前进程的令牌启用 SeDebugPrivilege。这种执行方式将绕过 Win32 中存在的任何 API 钩子。此外,它不会在最终 PE 的导入地址表(Import Address Table)上创建任何条目,从而使得在不执行该 PE 的情况下更难检测其行为。```rust
fn main() {
// Dynamically obtain ntdll.dll's base address.
let ntdll = dinvoke_rs::dinvoke::get_module_base_address("ntdll.dll");
if ntdll != 0
{
unsafe
{
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 in Rust can be represented as an i32
let privilege: u32 = 20; // This value matches with SeDebugPrivilege
let enable: u8 = 1; // Enable the privilege
let current_thread: u8 = 0; // Enable the privilege for the current process, not only for the current thread
let e = u8::default(); // https://github.com/Kudaes/rust_tips_and_tricks/tree/main#transmute
let enabled: *mut u8 = std::mem::transmute(&e);
dinvoke_rs::dinvoke::dynamic_invoke!(ntdll,"RtlAdjustPrivilege",func_ptr,ret,privilege,enable,current_thread,enabled);
match ret {
Some(x) =>
if x == 0 { println!("NTSTATUS == Success. Privilege enabled."); }
else { println!("[x] NTSTATUS == {:X}", x as u32); },
None => panic!("[x] Error!"),
}
}
}
}
## 执行间接系统调用
在下一个示例中,我们使用 DInvoke_rs 执行与函数 `NtQueryInformationProcess` 对应的系统调用。由于宏 `execute_syscall!()` 会动态分配并执行用于完成所需系统调用的 shellcode,因此 `ntdll.dll` 中存在的所有挂钩都会被绕过。一旦系统调用返回,所分配的内存即被释放,从而避免具有执行权限的内存页永久存在。```rust
use std::mem::size_of;
use windows::Win32::System::Threading::{GetCurrentProcess, PROCESS_BASIC_INFORMATION};
use dinvoke_rs::data::{NtQueryInformationProcess, PVOID};
fn main() {
unsafe
{
let function_type:NtQueryInformationProcess;
let ret: Option<i32>; //NtQueryInformationProcess returns a NTSTATUS, which is a i32.
let handle = GetCurrentProcess();
let p = PROCESS_BASIC_INFORMATION::default();
let process_information: PVOID = std::mem::transmute(&p);
let r = u32::default();
let return_length: *mut u32 = std::mem::transmute(&r);
dinvoke_rs::dinvoke::execute_syscall!(
"NtQueryInformationProcess",
function_type,
ret,
handle,
0,
process_information,
size_of::<PROCESS_BASIC_INFORMATION>() as u32,
return_length
);
let pbi: *mut PROCESS_BASIC_INFORMATION;
match ret {
Some(x) =>
if x == 0 {
pbi = std::mem::transmute(process_information);
let pbi = *pbi;
println!("The Process Environment Block base address is 0x{:X}", pbi.PebBaseAddress as u64);
},
None => println!("[x] Error executing direct syscall for NtQueryInformationProcess."),
}
}
}
在此示例中,使用 DInvoke_rs 手动映射一份没有任何 EDR 钩子的全新 ntdll.dll 副本。然后,该全新的 ntdll.dll 副本可用于执行任何所需函数。
这种手动映射也可以从内存中执行(在这种情况下使用 manually_map_module()),从而可以执行经典的反射式 DLL 注入。```rust
use dinvoke_rs::data::PeMetadata;
fn main() {
unsafe
{
let ntdll: (PeMetadata, usize) = dinvoke_rs::manualmap::read_and_map_module(r"C:\Windows\System32\ntdll.dll", true, false).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
let privilege: u32 = 20; // This value matches with SeDebugPrivilege
let enable: u8 = 1; // Enable the privilege
let current_thread: u8 = 0; // Enable the privilege for the current process, not only for the current thread
let e = u8::default();
let enabled: *mut u8 = std::mem::transmute(&e);
dinvoke_rs::dinvoke::dynamic_invoke!(ntdll.1,"RtlAdjustPrivilege",func_ptr,ret,privilege,enable,current_thread,enabled);
match ret {
Some(x) =>
if x == 0 { println!("NTSTATUS == Success. Privilege enabled."); }
else { println!("[x] NTSTATUS == {:X}", x as u32); },
None => panic!("[x] Error!"),
}
}
}
## 重载内存节
在以下示例中,DInvoke_rs 用于创建一个文件后备的内存节,之后通过手动映射一个 PE 来重载它。
默认情况下,内存节将指向位于 `%WINDIR%\System32\` 中的合法文件,但也可以使用任何其他诱饵模块。
这种重载也可以通过从内存映射一个 PE 来执行(如下例所示),从而无需将载荷写入磁盘即可完成重载。```rust
use dinvoke_rs::data::PeMetadata;
fn main() {
unsafe
{
let payload: Vec<u8> = your_download_function();
// This will map your payload into a legitimate file-backed memory section.
let overload: (PeMetadata, usize) = dinvoke_rs::overload::overload_module(&payload, "").unwrap();
// Then any exported function of the mapped PE can be dynamically called.
// Let's say we want to execute a function with header pub fn random_function(i32, i32) -> i32
let func_ptr: unsafe extern "Rust" fn (i32, i32) -> i32; // Function header
let ret: Option<i32>; // The value that the called function will return
let parameter1: i32 = 10;
let parameter2: i32 = 20;
dinvoke_rs::dinvoke::dynamic_invoke!(overload.1,"random_function",func_ptr,ret,parameter1,parameter2);
match ret {
Some(x) =>
println!("The function returned the value {}", x),
None => panic!("[x] Error!"),
}
}
}
DInvoke_rs 允许在映射的 PE 未使用时将其隐藏,从而使 EDR 内存检查更难检测到进程中存在可疑的 dll。