Сохнут волосы, метёт метла
В кобуре мороза пистолет тепла
Гражданская Оборона - "Тошнота"
Одним из новшеств в Windows 8.1 стала нотификация (объект обратного вызова, callback object) проверки цифровой подписи исполняемых файлов драйверов: SeRegisterImageVerificationCallback(...) и SeUnregisterImageVerificationCallback(...). Эти функции документированы в заголовочных файлах WDK 8.1:
// // Types of images. // typedef enum _SE_IMAGE_TYPE { SeImageTypeElamDriver = 0, SeImageTypeDriver, SeImageTypeMax } SE_IMAGE_TYPE, *PSE_IMAGE_TYPE; typedef struct _BDCB_IMAGE_INFORMATION *PBDCB_IMAGE_INFORMATION; typedef _IRQL_requires_same_ _Function_class_(SE_IMAGE_VERIFICATION_CALLBACK_FUNCTION) VOID SE_IMAGE_VERIFICATION_CALLBACK_FUNCTION ( _In_opt_ PVOID CallbackContext, _In_ SE_IMAGE_TYPE ImageType, _Inout_ PBDCB_IMAGE_INFORMATION ImageInformation ); typedef SE_IMAGE_VERIFICATION_CALLBACK_FUNCTION *PSE_IMAGE_VERIFICATION_CALLBACK_FUNCTION; typedef enum _SE_IMAGE_VERIFICATION_CALLBACK_TYPE { SeImageVerificationCallbackInformational = 0 } SE_IMAGE_VERIFICATION_CALLBACK_TYPE, *PSE_IMAGE_VERIFICATION_CALLBACK_TYPE; typedef PVOID SE_IMAGE_VERIFICATION_CALLBACK_TOKEN, *PSE_IMAGE_VERIFICATION_CALLBACK_TOKEN; #if (NTDDI_VERSION >= NTDDI_WINBLUE) _IRQL_requires_max_(PASSIVE_LEVEL) NTKERNELAPI NTSTATUS SeRegisterImageVerificationCallback( _In_ SE_IMAGE_TYPE ImageType, _In_ SE_IMAGE_VERIFICATION_CALLBACK_TYPE CallbackType, _In_ PSE_IMAGE_VERIFICATION_CALLBACK_FUNCTION CallbackFunction, _In_opt_ PVOID CallbackContext, _Reserved_ SE_IMAGE_VERIFICATION_CALLBACK_TOKEN Token, _Out_ PVOID* CallbackHandle ); _IRQL_requires_max_(PASSIVE_LEVEL) NTKERNELAPI VOID SeUnregisterImageVerificationCallback ( _In_ PVOID CallbackHandle ); #endif
Структура BDCB_IMAGE_INFORMATION
Несмотря на такое детальное описание в заголовочном файле wdm.h, статьи описания работы функций SeRegisterImageVerificationCallback(...) и SeUnregisterImageVerificationCallback(...) просто нет. Хочу немного восполнить этот пробел.
Хотя функция регистрации SeRegisterImageVerificationCallback получает достаточно много параметров, большинство из них просто проверяются на валидность и не используются:
- ImageType == SeImageTypeDriver, иначе функция вернет STATUS_INVALID_PARAMETER_1
- CallbackType == SeImageVerificationCallbackInformational, иначе функция вернет STATUS_INVALID_PARAMETER_2
- Token == NULL, иначе функция вернет STATUS_INVALID_PARAMETER_5
То есть сейчас, фактически, функция принимает адрес функции обратного вызова, контекст функции обратного вызова и возвращает описатель зарегистрированного обратного вызова или статус ошибки.
Хочется отметить, что сам объект нотификации имеет имя в дереве объектов: "\Callback\SeImageVerificationDriverInfo". Так же есть не экспортируемый символ объекта обратного вызова - nt!ExCbSeImageVerificationDriverInfo. Но регистрировать нотификацию нужно именно Se-функциями: в ядре присутствует счетчик, имя которого не включено в отладочную информацию. Но этот счетчик атомарно инкрементируется при регистрации, декрементируется при де-регистрации Se-функциями. Но самое важное - он проверяется на 0 перед вызовом самой нотификации. Это, видимо, своеобразная оптимизация: если нет подписчиков нотификации, то нет необходимости копировать структуру BDCB_IMAGE_INFORMATION и ставить WORK_ITEM для вызова нотификации.
Вторая особенность этой нотификации заключается в том, что вызов обработчиков всегда происходит из WORK_ITEM'а, то есть не в контексте нити проверки подписи. Оригинальная операция никак не блокируется, все данные копируются через пул. Последовательность вызовов:
- MiValidateSectionCreate
- SeValidateImageHeader (тут происходит проверка счетчика)
- SepScheduleImageVerificationCallbacks
- ExQueueWorkItem
- ...миграция в другую нить...
- SepImageVerificationCallbackWorker
- ExNotifyWithProcessing
- SepImageVerificationCallbackPreProcess
- зарегистрированные обработчики нотификации
Я думаю, что это сделано сознательно, что бы избежать взаимо-блокировок, так как нотификация вызывается при загрузке файлов драйверов, сразу после работы успешного вызова функции CI!CiValidateImageHeader.
Хотя надо отметить, что в инсталляции "из коробки" Windows 8.1 имеет зарегистрированный нотификатор WdFilter!MpImageVerificationCallback. При изучении содержимого WdFilter.sys стоит учитывать, что адреса функций SeRegisterImageVerificationCallback(...) и SeUnregisterImageVerificationCallback(...) получаются вызовом MmGetSystemRoutineAddress(...), (функция WdFilter!MpSetImageVerificationCallback).
Для теста я тоже набросал драйвер, получающий нотификацию проверки цифровой подписи и печатающий входные данные. Получилось что-то такое:
ΞρεΤΙκ