Сохнут волосы, метёт метла
В кобуре мороза пистолет тепла
Гражданская Оборона - "Тошнота"
Одним из новшеств в 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).
Для теста я тоже набросал драйвер, получающий нотификацию проверки цифровой подписи и печатающий входные данные. Получилось что-то такое:
ΞρεΤΙκ