
ड्राइवर रिवर्स और एक्सप्लॉइटेशन
⚠️ चेतावनी : यह प्रोजेक्ट पूर्णतः शैक्षिक और प्रदर्शनात्मक है। इसका दुर्भावनापूर्ण संदर्भ में उपयोग करने का कोई इरादा नहीं है। उद्देश्य reverse engineering की पद्धति और Windows ड्राइवर के शोषण के चरणों को सीखना है।
मैं यहाँ उस प्रक्रिया को समझा रहा हूँ जो मैंने d1rk(SaadAhla) https://github.com/SaadAhla द्वारा प्रस्तावित अभ्यास को हल करने के लिए अपनाई थी, जिसमें एक वैध, हस्ताक्षरित ड्राइवर पर reverse engineering और शोषण करना शामिल है जो blocklists (HVCI, LOLBIN...) में नहीं है। एक C प्रोग्राम उपलब्ध है जो इस Kernel-mode ड्राइवर के माध्यम से सिस्टम पर किसी भी सक्रिय प्रोसेस को समाप्त कर सकता है; मैं इसके संचालन का विवरण थोड़ा नीचे दे रहा हूँ।

📃 उपयोग : DriverKiller.exe <प्रोसेस_नाम.exe> [-d]
विकल्प -d : शोषण के बाद सिस्टम से सेवा और ड्राइवर को हटाने की अनुमति देता है।
लक्ष्य मशीन पर testsigning मोड सक्षम होना चाहिए क्योंकि ड्राइवर का प्रमाणपत्र समाप्त हो चुका है।
भाग 1 - रिवर्स इंजीनियरिंग :
अभ्यास में एक .sys फ़ाइल दी गई है, जिसका नाम उसके SHA-256 हैश के साथ रखा गया है।
पहला चरण इस फ़ाइल को IDA के साथ खोलना है।
IDA निःशुल्क उपलब्ध है। लाइसेंस उत्पन्न करने और सॉफ़्टवेयर डाउनलोड करने के लिए बस Hex-Rays की वेबसाइट पर जाना होगा।
हम ड्राइवर की IAT (Import Address Table) को सूचीबद्ध करके शुरू करते हैं और उस API कॉल की खोज करते हैं जिसमें हमारी रुचि है : ZwTerminateProcess।
ZwTerminateProcess पर डबल-क्लिक करने पर, IDA हमें इस फ़ंक्शन के संकलित कोड पर पुनर्निर्देशित करता है। एंट्री का चयन करके और cross-references प्रदर्शित करके, हमें ड्राइवर के उन फ़ंक्शनों की सूची मिलती है जो इसे कॉल करते हैं।

हम देखते हैं कि sub_12EF4 फ़ंक्शन, ऑफ़सेट 1CE पर, ZwTerminateProcess का उपयोग करता है। डबल-क्लिक के बाद, IDA इसका संकलित कोड प्रदर्शित करता है।

डीकंपाइल किया गया कोड ZwOpenProcess (जो लक्ष्य प्रोसेस के लिए एक हैंडल खोलता है) और ZwTerminateProcess (जो इस हैंडल के माध्यम से प्रोसेस को समाप्त करता है) के कॉल प्रकट करता है।
ZwOpenProcess के दस्तावेज़ (https://learn.microsoft.com/fr-fr/windows-hardware/drivers/ddi/ntddk/nf-ntddk-zwopenprocess) को देखने पर, हम पाते हैं कि ClientID पैरामीटर एक पॉइंटर है जो लक्षित प्रोसेस के PID को इंगित करता है।
ऊपर वाली पंक्ति में, ClientId.UniqueProcess को v22 चर के साथ प्रारंभ किया गया है। इसे ठीक ऊपर परिभाषित किया गया है :
v22 = (void )((_QWORD *)i + 10);
इस असाइनमेंट को समझने के लिए, हमें i चर और +10 फ़ील्ड की पहचान करनी होगी।

इस फ़ंक्शन में थोड़ा ऊपर, हम SYSTEM_PROCESS_INFORMATION पैरामीटर के साथ ZwQuerySystemInformation का कॉल देखते हैं। हम यह भी समझते हैं कि i, v6 चर के साथ इस संरचना की प्रविष्टियों पर पुनरावृत्ति करता है।
ZwQuerySystemInformation के दस्तावेज़ के अनुसार : (https://learn.microsoft.com/en-us/windows/win32/sysinfo/zwquerysysteminformation), यह फ़ंक्शन एक सरणी लौटाता है जिसमें सिस्टम पर प्रत्येक सक्रिय प्रोसेस के लिए एक प्रविष्टि होती है।
SYSTEM_PROCESS_INFORMATION संरचना का वर्णन यहाँ किया गया है : https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation
typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset;
ULONG NumberOfThreads;
BYTE Reserved1[48];
UNICODE_STRING ImageName;
KPRIORITY BasePriority;
HANDLE UniqueProcessId;
PVOID Reserved2;
ULONG HandleCount;
ULONG SessionId;
PVOID Reserved3;
SIZE_T PeakVirtualSize;
SIZE_T VirtualSize;
ULONG Reserved4;
SIZE_T PeakWorkingSetSize;
SIZE_T WorkingSetSize;
PVOID Reserved5;
SIZE_T QuotaPagedPoolUsage;
PVOID Reserved6;
SIZE_T QuotaNonPagedPoolUsage;
SIZE_T PagefileUsage;
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER Reserved7[6];
} SYSTEM_PROCESS_INFORMATION;
स्मरण : Windows x64 पर कुछ प्रकारों के आकार
typedef struct _UNICODE_STRING {
USHORT Length; -> 2
USHORT MaximumLength; -> + 2 = 4
PWSTR Buffer; -> + 8 = 12 (12 n'est pas un multiple de 8 donc padding de 4 ajouté en amont de Buffer) = 16
} UNICODE_STRING;UniqueProcessId के ऑफ़सेट की गणना :
ULONG NextEntryOffset; -> 4
ULONG NumberOfThreads; -> + 4 = 8
BYTE Reserved1[48]; -> + 48 = 56
UNICODE_STRING ImageName; -> + 16 = 72
KPRIORITY BasePriority; -> + 4 = 76 (76 n'est pas un multiple de 8 donc padding de 4 ajouté) = 80
HANDLE UniqueProcessId; -> + 8 = 88
UniqueProcessId सदस्य इस प्रकार ऑफ़सेट 0x50 (80 दशमलव) पर है।
हमारे चर v22 के असाइनमेंट को देखते हुए, हम पाते हैं कि i को QWORD (8 बाइट्स) पॉइंटर में कास्ट किया गया है
v22 = (void )((_QWORD *)i + 10);
v22iयह जानने के लिए कि ZwTerminateProcess को कौन सा PID दिया जाएगा, हमें इस असाइनमेंट के आसपास की शर्त का विश्लेषण करना होगा।

हम देखते हैं कि पहले प्रोसेस की इमेज का नाम प्राप्त किया जाता है :
v9 = (wchar_t )((_QWORD *)i + 8);
v9iImageNameBufferनीचे दिए गए संचालनों और लूपों को देखते हुए, हम यह परिकल्पना कर सकते हैं कि तर्क के रूप में पारित प्रोसेस के नाम (a2) और सिस्टम पर सक्रिय प्रोसेसेस v9/String के बीच तुलना की जाती है।
sub_1C078(String, v9, (int)v13); v17 = strupr(a2); v18 = strupr(String);
इस प्रकार a2 पैरामीटर ही वह है जिसमें ZwTerminateProcess के माध्यम से समाप्त किए जाने वाले प्रोसेस का नाम होना चाहिए।
हम ध्यान देते हैं कि a2 फ़ंक्शन sub_12EF4 का एक पैरामीटर है। आगे बढ़ने के लिए, हमें इस फ़ंक्शन के संदर्भों की जाँच करनी होगी (बेहतर पठनीयता के लिए मैंने इसे ZwTerminateProcessCaller नाम दिया है)।

हम देखते हैं कि ZwTerminateProcessCaller को फ़ंक्शन sub_13624 द्वारा ऑफ़सेट 61A पर कॉल किया जाता है।

इस डीकंपाइल किए गए कोड का विश्लेषण करने से पहले, मैं फ़ंक्शन sub_13624 (जिसे ZwTerminateProcessCallerCaller नाम दिया गया है) के संदर्भ खोजूँगा ताकि यह सुनिश्चित हो सके कि इस कोड का उपयोग वास्तव में UserMode से DeviceIoControl के लिए API कॉल के बाद किया जाता है।

हम देखते हैं कि ZwTerminateProcessCallerCaller को फ़ंक्शन sub_14130 द्वारा कॉल किया जाता है (जिसे ZwTerminateProcessCallerCallerCaller नाम दिया गया है... सौभाग्य से हमारे लिए, यह प्रवेश बिंदु से पहले का अंतिम फ़ंक्शन है 😅)।

हम देखते हैं कि ZwTerminateProcessCallerCallerCaller को फ़ंक्शन sub_1A4A8 द्वारा ऑफ़सेट 306 पर कॉल किया जाता है।

हमें फ़ंक्शन ZwTerminateProcessCallerCallerCaller का असाइनमेंट मिलता है :
memset64(DriverObject->MajorFunction, (unsigned __int64)ZwTerminateProcessCallerCallerCaller, 0x1Cu);

फ़ंक्शन sub_13624 (उर्फ ZwTerminateProcessCallerCaller) पर लौटने से पहले, हम Symbolic Name और Device Name (यहाँ समान) प्राप्त करते हैं : Viragtlt।

ZwTerminateProcessCallerCaller पर लौटते हुए, हम ध्यान देते हैं कि इसका दूसरा पैरामीटर (अर्थात a2) MasterIrp->AssociatedIrp.SystemBuffer के अनुरूप है।

ZwTerminateProcessCaller कॉल के ठीक ऊपर हमें IOCTL कोड मिलता है : -2106392528 (हेक्साडेसिमल में : 0x82730030)।
इन जानकारियों की सहायता से, हम यह निष्कर्ष निकाल सकते हैं कि इस ड्राइवर का शोषण करने के लिए, ड्राइवर को DeviceIoControl API कॉल भेजना होगा जिसमें SystemBuffer में समाप्त किए जाने वाले प्रोसेस का नाम हो।
🔷 रिवर्स इंजीनियरिंग के माध्यम से प्राप्त जानकारी :
0x82730030ViragtltViragtltभाग 2 - शोषण
इस ड्राइवर का शोषण करने के लिए (यदि यह लक्ष्य मशीन पर स्थापित और सक्रिय है), इसके लिए एक हैंडल खोलना आवश्यक है, फिर एक Buffer के साथ DeviceIoControl API कॉल करना होगा जिसमें उस प्रोसेस का नाम हो जिसे आप समाप्त करना चाहते हैं।
इस अभ्यास के लिए, मैंने एक C प्रोजेक्ट विकसित किया है जो :
मैंने एक -d विकल्प भी जोड़ा है जो शोषण के बाद सिस्टम से सेवा और ड्राइवर को हटाने की अनुमति देता है।
अपने पूर्ण निष्पादन चक्र में C प्रोग्राम का व्यवहार यहाँ दिया गया है :

AV/EDR एवेज़न
इस मामले में, DriverKiller.exe को Microsoft Defender द्वारा न तो स्टैटिक रूप से और न ही डायनामिक रूप से पहचाना जाता है। यहाँ एवेज़न का वास्तव में कोई मतलब नहीं है क्योंकि शोषित ड्राइवर के पास एक समाप्त प्रमाणपत्र है, इसलिए वास्तविक परिस्थितियों में इसका उपयोग शायद ही संभव है। लेकिन बेहतर स्टील्थ के लिए, हम इन्हें लागू कर सकते थे :
29/08/2025 को ड्राइवर पर पहचान (परिणाम पहले से मौजूद है, स्पष्ट कारणों से मैंने VirusTotal पर कुछ भी सबमिट नहीं किया) :

⚠️ यह प्रोजेक्ट एक सीखने के उद्देश्य से बनाया गया है। इसमें अशुद्धियाँ या त्रुटियाँ हो सकती हैं। कोई भी सुझाव, सुधार या चर्चा का स्वागत है! 😃 d1rk(SaadAhla) को धन्यवाद : https://github.com/SaadAhla !