EreTIk's Box » Cтатьи, исходники » Verifier и динамический импорт в ядре


При разработке драйверов, я стараюсь не собирать различные исполняемые файлы для разных версий Windows. В большинстве случаев проблемы, связанные с разным набором API, решаются тем, что адреса функций, которые присутствуют только на более старших версиях ядра ОС, можно получить через динамический импорт.


Для получения адреса функции по имени можно использовать функцию MmGetSystemRoutineAddress. К сожалению эта функция имеет ограничение, о чем написано в MSDN:


Drivers can use this routine to determine if a routine is available on a specific version of Windows. It can only be used for routines exported by the kernel or HAL, not for any driver-defined routine.

Поэтому если есть необходимость обрабатывать динамический импорт не из ядра и не из HAL'а, то придется эту функцию писать руками. Второй интересный момент, относящийся к MmGetSystemRoutineAddress, связан с входным параметром. Почему эта функция принимает UNICODE-строку для меня остается загадкой. Особенно расстраивает то, что внутри вызова этой функции строка все же преобразуется к ANSI-строке, что приводит к лишним манипуляциям с пулом. Из этого я делаю вывод, что не только я пользуюсь самописной функцией получения адреса экспортируемой функции по адресу загруженного модуля и ANSI-строке :) Назовем ее GetProcAddress. Реализация такой функции достаточно тривиальная (90% процентов кода это разбор директории экспортов PE-файла), поэтому я не буду приводить ее здесь.


Не для кого не секрет, что включенный verifier заменяет в целевом исполняемом модуле статически импортируемые из модуля ядра функции на свои, которые начинаются с префикса Verifier. Мне стало интересно как же обстоят дела с динамическим импортом, поэтому я предлагаю посмотреть на результат получения адреса функции различными способами. Для этого будем использовать следующий тестовый код:


    static UNICODE_STRING usExAcquireResourceSharedLite = 
        RTL_CONSTANT_STRING(L"ExAcquireResourceSharedLite");
    PVOID pFuncStatic = &::ExAcquireResourceSharedLite;
    PVOID pFuncDynMm = MmGetSystemRoutineAddress(&usExAcquireResourceSharedLite);
    PVOID pFuncDynamic = 
        GetProcAddress(pNtoskrnlBase, "ExAcquireResourceSharedLite");
    __debugbreak();
                

Переменная pNtoskrnlBase в вышеприведенном примере содержит адрес загрузки модуля ядра.


Для начала запустим (естественно, с включенным verifier'ом) на Windows Server 2003 R2 SP2 (3790):


0: kd> ln @@C++(pFuncStatic) ... Exact matches: nt!VerifierExAcquireResourceExclusiveLite = <no type information> 0: kd> ln @@C++(pFuncDynMm) ... Exact matches: nt!ExAcquireResourceExclusiveLite = <no type information> 0: kd> ln @@C++(pFuncDynamic) ... Exact matches: nt!ExAcquireResourceExclusiveLite = <no type information>

Затем запустим этот же код под verifier'ом на Windows 7 SP1 (7601). Результат отличается:


kd> ln @@C++(pFuncStatic) ... Exact matches: nt!VerifierExAcquireResourceExclusiveLite = <no type information> kd> ln @@C++(pFuncDynMm) ... Exact matches: nt!VerifierExAcquireResourceExclusiveLite = <no type information> kd> ln @@C++(pFuncDynamic) ... Exact matches: nt!ExAcquireResourceExclusiveLite = <no type information>

Как видно из вышеприведенных данных (результат выполнения "ln @@C++(pFuncDynMm)"), на Win7 функция MmGetSystemRoutineAddress обрабатывает ситуацию, когда на вызывающий драйвер включен verifier и возвращает более правильный адрес запрашиваемой функции.


На самом деле разница на старшей версии ОС в том, что появилась функция nt!VerifierMmGetSystemRoutineAddress (она появилась в Windows Vista (6000)). Так как MmGetSystemRoutineAddress была использована статическим вызовом, вместо нее реально была вызвана nt!VerifierMmGetSystemRoutineAddress, которая является достаточно простой proxy-оберткой:


    push        ebp
    mov         ebp,esp
    push        esi
    mov         esi,[ebp][8]
    push        esi
    call        MmGetSystemRoutineAddress
    test        eax,eax
    jz         .000744511
    push        eax
    call        VfThunkAdjustExportAddressIfHooked
; .00744511:
    pop         esi
    pop         ebp
    retn        4
                

Из вышесказанного можно лишь еще раз подтвердить старую истину, что использование велосипедов вредно для вашего кода.


ΞρεΤΙκ