مرحبا يا هبر!
عندما واجهتني مهمة كتابة برنامج التشغيل الخاص بي ، والذي يراقب العمليات في السجل ، بالطبع ، دخلت في البحث على الإنترنت للحصول على بعض المعلومات حول هذا الأمر على الأقل. لكن الشيء الوحيد الذي خرج بناءً على طلب "Driver-filter of Registry" هو مجموعة من المقالات حول كتابة مرشح برنامج التشغيل (يا هلا) ، لكن كل هذه المقالات كانت تتعلق فقط بمرشح
نظام الملفات (الحزن).
لسوء الحظ ، كان الشيء الوحيد الذي تم العثور عليه هو مقالة عام 2003 ، وهي الكود الذي لن تجمع منه أبدًا في VS19 الجديد.
لحسن الحظ ، هناك مثال رائع من Microsoft على GitHub (سألقي
رابطًا فورًا ) ، حيث سيتم بناء معظم هذا التحليل.
ربما يكون ارتباط مثال ما يكفيًا للمبرمجين الفائقين لمعرفة كل شيء في غضون 5 دقائق. ولكن هناك أيضًا مبتدئين ، طلاب ، مثلي ، بالنسبة لهم ، على الأرجح ، ستكون هذه المقالة. آمل أن يكون هذا حقا يساعد شخص ما.
حسنا. مطاردة. نفتح مثالا. تحذير! نحن لسنا خائفين من عدد كبير من الملفات ، 80 ٪ نحن لسنا بحاجة.
نرى مجلدين في المشروع: exe و sys. يحتوي البرنامج الأول على برنامج يقوم بتشغيل برنامج التشغيل ، ويقوم بتسجيله في النظام ، وعند إتمام العمل مع برنامج التشغيل ، يقوم بإزالته. سنبدأ معها.
افتح
regctrl.cهنا هو تقريبا كل رمز البرنامج الذي نحتاجه.
انتقل على الفور إلى وظيفة wmain. ماذا نرى هناك؟ قم بتنزيل برنامج التشغيل باستخدام وظيفة UtilLoadDriver (util.c) ، ثم قم بتعليمات بعض الإعدادات:
printf("\treg add \"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Debug Print Filter\" /v IHVDRIVER /t REG_DWORD /d 0x8\n\n");
نعم ، تحتاج إلى إدخال المعلمة في السجل في المجلد المحدد (يمكنك استخدام cmd ، أو يمكنك استخدام الأقلام). هذا ضروري حتى نتمكن من رؤية المزيد من الرسائل من السائق
بالمناسبة ، لا تنس تنزيل تطبيق يسمح لك بمشاهدة معلومات تصحيح الأخطاء ، فقد استخدمت DbgView.علاوة على ذلك ، نرى دالتين مهمتين: DoKernelModeSamples و DoUserModeSamples - هناك حاجة لإظهار تشغيل برنامج التشغيل. هنا هو الأول ، على سبيل المثال ، يرسل طلبًا إلى برنامج تشغيل IOCL مع وظيفة DeviceIoControl ، وسيقوم برنامج التشغيل بدوره بتشغيل الوظائف الضرورية باستخدام المعلمة الثانية IOCTL_DO_KERNELMODE_SAMPLES.
من وصف وظيفة DeviceIoControl ، نرى أنه يمكن تمرير مخزن مؤقت إلى برنامج التشغيل وقبوله أيضًا. سنحتاج هذا في المستقبل. في غضون ذلك ، لا يوجد شيء مثير للاهتمام بالنسبة لنا في هذا الملف.
دعنا
نذهب إلى مجلد sys ، ملف
driver.cلنبدأ بوظيفة DriverEntry. هناك ، يعرض برنامج التشغيل نوعًا من معلومات تصحيح الأخطاء ، ثم تقوم وظيفة IoCreateDeviceSecure بإنشاء كائن جهاز مسمى وتطبق معايير الأمان المحددة ، وينتظرنا بعض الشيء المثير للاهتمام:
DriverObject->MajorFunction[IRP_MJ_CREATE] = DeviceCreate; DriverObject->MajorFunction[IRP_MJ_CLOSE] = DeviceClose; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DeviceCleanup; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceControl; DriverObject->DriverUnload = DeviceUnload;
بين قوسين هي رموز الوظيفة الرئيسية ل IRP. وهذا هو ، هذه هي أنواع الحزم التي ستحظى باهتمام سائقنا. بعد علامة "=" ، تتم الإشارة إلى الوظيفة التي ستقوم بمعالجة الحزمة الواردة. ثم مرة أخرى ، مثيرة للاهتمام قليلا. BUT. هنا سوف تحتاج إلى إضافة ميزة واحدة مثيرة للاهتمام.
تذكر هذا المكان ، سنعود هنالذلك ، إذا كان كل شيء واضحًا مع DeviceCreate و DeviceClose و DeviceCleanup و DeviceUnload ، فما الذي يحدث في DeviceControl؟ وهناك سوف يطير طلب برنامجنا ، الذي أرسلناه مع وظيفة DeviceIoControl. نلتقط الطلب من المكدس ونسترجع (في هذا المثال) المعلمة الثانية التي تحدثت عنها:
IrpStack = IoGetCurrentIrpStackLocation(Irp); Ioctl = IrpStack->Parameters.DeviceIoControl.IoControlCode;
بناءً على IoControlCode ، سينتقل برنامج التشغيل لأداء وظيفة معينة. أنصحك أن تفهم ، على سبيل المثال ، النظر في ملف pre.c ومعرفة ما يحدث هناك.
وسننتهي من النظر في المثال مع آخر نقطة مثيرة للاهتمام - بالطبع ، وظيفة
رد الاتصال .
هذا هو المكان الذي تطير فيه إعلامات العمليات التي تحدث في السجل. تذكر المكان الذي طلبت أن أتذكره؟ إنه أعلى قليلاً. هنا سنترك CmRegisterCallbackEx. سيعلنون عن وظيفة Callback باعتبارها "حقيبة" تطير فيها حزم IRP للمعالجة. سيحدد CallbackCtx-> Altitude مستوى برنامج التشغيل الخاص بنا (نحن لسنا الوحيدين الذين يراقبون السجل) ، بمعنى أي ارتفاع سوف يعترض سائقنا الحزم ويفعل شيئًا معهم (مرة أخرى ، قبل ذلك ، من الواضح تمامًا ما يحدث وكيف يحدث ذلك : نقوم بتسجيل الوظيفة ، والقيام بشيء ما في السجل ، يتم إصلاح كل شيء ، يتم عرض المعلومات من قبل برنامج التشغيل ، ثم نقوم بالإجراء المعاكس - CmUnRegisterCallback - حتى لا يصل أي شيء آخر إلينا).
اوه نعم لا تقلق عندما تجد في DbgView مجموعة لا نهائية من الرسائل من برنامج التشغيل - هناك دائمًا بعض جلسات Hangout في السجل.
في الواقع ، من وسيطات وظيفة CallBack ، يمكنك استخراج جميع المعلومات الضرورية - كل من العملية التي يتم تنفيذها على بعض المفتاح (هذا فقط في الكود - NotifyClass) ، واسم المفتاح
الآن دعنا نبتعد عن هذا المثال. النظر في ما يمكن القيام به بشكل مثير للاهتمام.
مثل هذه المهمة: دعنا نحصل على أسماء البرامج ومفاتيح التسجيل في أحد الملفات ، ونحدد أيضًا حقوق الوصول إلى البرنامج لمفتاح معين (نقصر أنفسنا على البساطة: له / لا يملك حق الوصول).
سيقوم برنامجنا (البرنامج الموجود في مجلد exe) بقراءة التكوين وإرساله إلى برنامج التشغيل باستخدام طلب IOCL. وهذا هو ، في وظيفة DeviceIoControl كالوسيطة الثالثة ، سنقوم بتمرير المخزن المؤقت. يمكنك نقل وترتيب التكوين كما يحلو لك.
يحصل السائق على هذه الحقوق ويحفظ نفسه في بعض المخازن المؤقتة العالمية. يمكن الحصول على مجموعة الإدخال بهذه الطريقة:
in_buf = Irp->AssociatedIrp.SystemBuffer;
حاول الآن رفض وصول بعض البرامج إلى المفتاح
انتقل إلى وظيفة رد الاتصال.
دعنا نشير إلى اسم برنامجنا والمفتاح الذي لا يمكنه الوصول إليه ، على التوالي MyProg و MyKey.نحتاج إلى معرفة البرنامج الذي يحاول حاليًا الوصول إلى المفتاح ومقارنة اسمه بأسماء المسجلة في تكويننا. يمكن الحصول على اسم العملية بهذه الطريقة:
PUNICODE_STRING processName = NULL; GetProcessImageName(PsGetCurrentProcess(), &processName); if (wcsstr(processName->Buffer, MyProg) != NULL) { <>}
وظيفة GetProcessImageName ليست مكتبة (ولكن الإنترنت) ، يمكن العثور على أشكالها المختلفة في العديد من المنتديات. سأتركها هنا:
typedef NTSTATUS(*QUERY_INFO_PROCESS) ( __in HANDLE ProcessHandle, __in PROCESSINFOCLASS ProcessInformationClass, __out_bcount(ProcessInformationLength) PVOID ProcessInformation, __in ULONG ProcessInformationLength, __out_opt PULONG ReturnLength ); QUERY_INFO_PROCESS ZwQueryInformationProcess; NTSTATUS GetProcessImageName( PEPROCESS eProcess, PUNICODE_STRING* ProcessImageName ) { NTSTATUS status = STATUS_UNSUCCESSFUL; ULONG returnedLength; HANDLE hProcess = NULL; PAGED_CODE(); // this eliminates the possibility of the IDLE Thread/Process if (eProcess == NULL) { return STATUS_INVALID_PARAMETER_1; } status = ObOpenObjectByPointer(eProcess, 0, NULL, 0, 0, KernelMode, &hProcess); if (!NT_SUCCESS(status)) { DbgPrint("ObOpenObjectByPointer Failed: %08x\n", status); return status; } if (ZwQueryInformationProcess == NULL) { UNICODE_STRING routineName = RTL_CONSTANT_STRING(L"ZwQueryInformationProcess"); ZwQueryInformationProcess = (QUERY_INFO_PROCESS)MmGetSystemRoutineAddress(&routineName); if (ZwQueryInformationProcess == NULL) { DbgPrint("Cannot resolve ZwQueryInformationProcess\n"); status = STATUS_UNSUCCESSFUL; goto cleanUp; } } /* Query the actual size of the process path */ status = ZwQueryInformationProcess(hProcess, ProcessImageFileName, NULL, // buffer 0, // buffer size &returnedLength); if (STATUS_INFO_LENGTH_MISMATCH != status) { DbgPrint("ZwQueryInformationProcess status = %x\n", status); goto cleanUp; } *ProcessImageName = ExAllocatePoolWithTag(NonPagedPoolNx, returnedLength, '2gat'); if (ProcessImageName == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto cleanUp; } /* Retrieve the process path from the handle to the process */ status = ZwQueryInformationProcess(hProcess, ProcessImageFileName, *ProcessImageName, returnedLength, &returnedLength); if (!NT_SUCCESS(status)) ExFreePoolWithTag(*ProcessImageName, '2gat'); cleanUp: ZwClose(hProcess); return status; }
لقد وجدنا أن MyProg يقوم الآن بالوصول إلى السجل. الآن تحتاج إلى معرفة أي مفتاح.
من الوسيطة الثانية ، نقوم باستخراج معلومات حول المفتاح الذي يتم الوصول إليه
REG_PRE_OPEN_KEY_INFORMATION* pRegPreCreateKey = (REG_PRE_OPEN_KEY_INFORMATION*)Argument2; if (pRegPreCreateKey != NULL) { if (wcscmp(pRegPreCreateKey->CompleteName->Buffer, MyKey) == 0) { if (){// return STATUS_SUCCESS; } else {// return STATUS_ACCESS_DENIED; } } }
ما عليك سوى إرجاع قيمة تشير إلى الحظر. وهذا كل شيء.
لا تهدف هذه المقالة إلى التأكد من أن كل من يقرأ بعد ذلك سيشاهد برامج تشغيل فائقة.هذا ، إذاً ، هو مقدمة لمسار الأمور :) لأنه عادة ما يكون في الحقيقة لا يكفي عندما تبدأ في الفهم.