EreTIk's Box » Cтатьи, исходники » Нотификация проверки цифровой подписи файла драйвера


Сохнут волосы, метёт метла

В кобуре мороза пистолет тепла

Гражданская Оборона - "Тошнота"


Одним из новшеств в 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
  1. SepImageVerificationCallbackPreProcess
  2. зарегистрированные обработчики нотификации

Я думаю, что это сделано сознательно, что бы избежать взаимо-блокировок, так как нотификация вызывается при загрузке файлов драйверов, сразу после работы успешного вызова функции CI!CiValidateImageHeader.


Хотя надо отметить, что в инсталляции "из коробки" Windows 8.1 имеет зарегистрированный нотификатор WdFilter!MpImageVerificationCallback. При изучении содержимого WdFilter.sys стоит учитывать, что адреса функций SeRegisterImageVerificationCallback(...) и SeUnregisterImageVerificationCallback(...) получаются вызовом MmGetSystemRoutineAddress(...), (функция WdFilter!MpSetImageVerificationCallback).


Для теста я тоже набросал драйвер, получающий нотификацию проверки цифровой подписи и печатающий входные данные. Получилось что-то такое:


... Driver `\Windows\System32\Drivers\cdrom.sys' ->Classification=UnknownImage ->ImageFlags=0x0 ->RegistryPath=`(null)' ->CertificatePublisher=`Microsoft Windows' ->CertificateIssuer=`Microsoft Windows Production PCA 2011' ->ImageHashAlgorithm=0x8004 02fa4f92d6f50df4a80dfe9e5c4668f5907d56ed ->ThumbprintHashAlgorithm=0x800c 4383c9a796dc607ddaae1849d8e5d2e7ea211aad2c599fe1e251285ec87dd150 Driver `\Windows\System32\Drivers\null.sys' ->Classification=UnknownImage ->ImageFlags=0x0 ->RegistryPath=`(null)' ->CertificatePublisher=`Microsoft Windows' ->CertificateIssuer=`Microsoft Windows Production PCA 2011' ->ImageHashAlgorithm=0x8004 ba802cc8b72cd02dc28d72361f142283cf3c3231 ->ThumbprintHashAlgorithm=0x800c 4383c9a796dc607ddaae1849d8e5d2e7ea211aad2c599fe1e251285ec87dd150 Driver `\Windows\System32\Drivers\beep.sys' ->Classification=UnknownImage ->ImageFlags=0x0 ->RegistryPath=`(null)' ->CertificatePublisher=`Microsoft Windows' ->CertificateIssuer=`Microsoft Windows Production PCA 2011' ->ImageHashAlgorithm=0x8004 af44cd0e1ebbb7b06290bf05237ce864aa888313 ->ThumbprintHashAlgorithm=0x800c 4383c9a796dc607ddaae1849d8e5d2e7ea211aad2c599fe1e251285ec87dd150 ...

ΞρεΤΙκ