
रस्ट के लिए कॉल स्टैक स्पूफिंग
Unwinder, SilentMoonWalk तकनीक का पूर्ण शस्त्रीकरण प्रदान करता है, जो Rust में पूर्ण और स्थिर कॉल स्टैक स्पूफिंग प्राप्त करने की अनुमति देता है।
इस तकनीक में निम्नलिखित विशेषताएँ हैं:
SilentMoonWalk तकनीक के निर्माताओं को धन्यवाद:
और निश्चित रूप से namazso को उस Twitter थ्रेड के लिए एक बड़ा धन्यवाद, जिसने इस पूरे प्रोजेक्ट को प्रेरित किया।
इस क्रेट को अपने प्रोजेक्ट में आयात करने के लिए अपने cargo.toml में निम्नलिखित पंक्ति जोड़ें और release मोड पर कंपाइल करें:
[dependencies]
unwinder = "=0.1.4"
इस क्रेट की मुख्य कार्यक्षमता को दो मैक्रोज़ में लपेटा गया है:
call_function!() मैक्रो एक साफ कॉल स्टैक के साथ किसी भी मनमाने फ़ंक्शन को चलाने की अनुमति देता है।indirect_syscall!() मैक्रो एक साफ कॉल स्टैक के साथ निर्दिष्ट (अप्रत्यक्ष) syscall निष्पादित करता है।इनमें से किसी भी मैक्रो का उपयोग करने के लिए std::ffi::c_void डेटा प्रकार को आयात करना आवश्यक है।
दोनों मैक्रोज़ एक *mut c_void लौटाते हैं जिसका उपयोग निष्पादित फ़ंक्शन द्वारा लौटाए गए मान को प्राप्त करने के लिए किया जा सकता है। अधिक विस्तृत जानकारी उदाहरण अनुभाग में दी गई है।
इस मैक्रो का उपयोग एक साफ कॉल स्टैक के साथ किसी भी वांछित फ़ंक्शन को कॉल करने के लिए किया जाता है। मैक्रो निम्नलिखित पैरामीटर की अपेक्षा करता है:
usize, isize या पॉइंटर के रूप में पास किया जाना चाहिए।इस मैक्रो का उपयोग एक साफ कॉल स्टैक के साथ किसी भी वांछित अप्रत्यक्ष syscall को करने के लिए किया जाता है। मैक्रो निम्नलिखित पैरामीटर की अपेक्षा करता है:
इन दोनों मैक्रोज़ में विभिन्न प्रकार के तर्क पास करने के लिए, निम्नलिखित बातों को ध्यान में रखा जाना चाहिए:
usize (u8-u64, i8-i64, bool, आदि) में परिवर्तित किया जा सकता है, सीधे मैक्रोज़ में पास किया जा सकता है।&str और String) को पॉइंटर के रूप में पास किया जाना चाहिए।ptr::null(), ptr::null_mut(), आदि) को 0 के रूप में पास किया जाता है (चाहे वह u8, u16, i32 या कोई अन्य हो)।let k32 = dinvoke_rs::dinvoke::get_module_base_address("kernel32.dll");
let sleep = dinvoke_rs::dinvoke::get_function_address(k32, "Sleep"); // Memory address of kernel32.dll!Sleep()
let miliseconds = 1000i32;
unwinder::call_function!(sleep, false, miliseconds);
let k32 = dinvoke_rs::dinvoke::get_module_base_address("kernel32.dll");
let open_process: isize = dinvoke_rs::dinvoke::get_function_address(k32, "Openprocess");
let desired_access: u32 = 0x1000;
let inherit = 0i32;
let pid = 20628i32;
let handle = unwinder::call_function!(open_process, false, desired_access, inherit, pid); // returns *mut c_void
let handle: HANDLE = std::mem::transmute(handle);
println!("Handle id: {:x}", handle.0);
ध्यान दें कि मैक्रो एक *mut c_void लौटाता है जिसे सीधे HANDLE में परिवर्तित किया जा सकता है, क्योंकि दोनों डेटा प्रकारों का आकार समान है। यह OpenProcess द्वारा लौटाए गए मान तक पहुँचने की अनुमति देता है, जो टारगेट प्रोसेस के लिए नया हैंडल है।
let large = 0x8000000000000000 as u64; // Sleep indefinitely
let large: *mut i64 = std::mem::transmute(&large);
let alertable = false;
let ntstatus = unwinder::indirect_syscall!("NtDelayExecution", false, alertable, large); // returns *mut c_void
println!("ntstatus: {:x}", ntstatus as i32);
ध्यान दें कि मैक्रो एक *mut c_void लौटाता है जिसका उपयोग NtDelayExecution द्वारा लौटाए गए NTSTATUS को प्राप्त करने के लिए किया जा सकता है।
स्पूफिंग प्रक्रिया को कॉल स्टैक आकार में असामान्य वृद्धि के बिना कितनी भी बार जोड़ा जा सकता है। निष्पादन प्रवाह भी संरक्षित रहेगा। निम्नलिखित कोड इसका एक उदाहरण है:
fn main()
{
function_a();
}
fn function_a()
{
unsafe
{
let func_b = function_b as usize;
call_function!(func_b, false);
println!("function_a done.");
}
}
fn function_b()
{
unsafe
{
let func_c = function_c as usize;
call_function!(func_c, false);
println!("function_b done.")
}
}
fn function_c()
{
unsafe
{
let large = 0x0000000000000000 as u64; // Don't sleep so we return to function_b, allowing to check the execution flow preservation.
let large: *mut i64 = std::mem::transmute(&large);
let alertable = false;
let ntstatus = unwinder::indirect_syscall!("NtDelayExecution", false, alertable, large);
println!("ntstatus: {:x}", (ntstatus as usize) as i32); //NTSTATUS is a i32, although that second casting is not really required in this case.
}
}
यदि आप दूसरे पैरामीटर को true पर सेट करते हैं (दोनों मैक्रोज़ में), तो स्पूफिंग प्रक्रिया वैधता बढ़ाने के लिए कॉल स्टैक में थ्रेड स्टार्ट एड्रेस के फ्रेम को रखने का प्रयास करेगी।
