
استدعاء ديناميكي لأي كود غير مُدار
نسخة Rust من Dinvoke. يمكن استخدام DInvoke_rs لأغراض عديدة، مثل تحليل PE، وحل الدوال المُصدَّرة ديناميكيًا، وتحميل إضافات PE ديناميكيًا أثناء التشغيل، وتجنب خطافات API، وغير ذلك.
الميزات:
يعود الفضل كله إلى مبتكري النسخة الأصلية بلغة C# لهذه الأداة:
استورد هذا الـ crate إلى مشروعك بإضافة السطر التالي إلى ملف cargo.toml الخاص بك:```rust
[dependencies]
dinvoke_rs = "0.2.2"
# أمثلة
## حل واجهات برمجة التطبيقات المُصدَّرة
يوضح المثال أدناه كيفية استخدام DInvoke_rs للعثور ديناميكيًا على وظائف DLL المُصدَّرة واستدعائها (في هذه الحالة `ntdll.dll`).
1) احصل على العنوان الأساسي لـ ntdll.
2) استخدم `get_function_address()` للعثور على تصدير داخل `ntdll.dll` بالاسم. يتم ذلك عن طريق اجتياز وتحليل EAT الخاص بالدالة.
3) يمكنك أيضًا العثور على تصدير بواسطة ordinal عن طريق استدعاء `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 لتوكن العملية الحالية. هذا النوع من التنفيذ سيتجاوز أي خطافات API موجودة في Win32. كما أنه لن يُنشئ أي إدخال في جدول عناوين الاستيراد الخاص بالملف التنفيذي 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!()` يخصّص وينفّذ الشيلكود المطلوب لإجراء استدعاء النظام المطلوب ديناميكيًا، فإن جميع الخطافات الموجودة في `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 لتعيين نسخة جديدة من ntdll.dll يدويًا، دون أي خطافات EDR. ثم يمكن استخدام تلك النسخة الجديدة من 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!"),
}