|
 |
|
«Сырая» загрузка драйвера |
|
Загрузка драйвера с использованием native-функции NtLoadDriver(…) |
|
|
Всем прекрасно известно, что ключи, описывающие драйверы режима ядра ОС располагаются в реестре по пути «\REGISTRY\MACHINE\System\CurrentControlSet\Services\<Driver_Name>». Это железный путь, которым пользуется SCM, в который мы сами можем прописать свой драйвер в системе, главное указать правильный тип и параметры старта. Но я недавно столкнулся с невозможностью записи в данный ключ реестра, а очень хотелось.… И посмотрев на загрузку драйвера стороны ядра, я столкнулся с интересной функцией NtLoadDriver.
Со стороны ядра эта функция выглядит следующим образом (она, естественно, не документирована, но я возьму на себя смелость ее описать):
NTSTATUS
NtLoadDriver(
__in PUNICODE_STRING DriverServiceName
);
Функция загрузки драйвера режима ядра ОС.
Параметры:
DriverServiceName – UNICODE-строка, содержащая путь до ключа драйвера.
Возвращаемое значение:
STATUS_SUCCESS – в случае успешной загрузки, иначе статус, детализирующий ошибку загрузки драйвера.
В сборнике статей по программированию в Kernel Mode (http://rootkits.ru/library/ShowLib.aspx?id_l=9) сказано, что параметр DriverServiceName должен представлять собой путь «\REGISTRY\MACHINE\System\CurrentControlSet\Services\...», где «…» является символическим именем драйвера…. Ложь и провокация:) Можно подсовывать почти любой путь в реестре. Далее я буду ссылаться на свой же код, который был написан для загрузки драйвера, в случае невозможности править этот обязательный путь.
Рассмотрим конкретную задачу, когда необходимо единовременно загрузить драйвер режима ядра в систему, не засоряя «…\CurrentControlSet\Services\…». В начале нам необходимо произвести сканирование реестра на наличие подходящего ключа-жертвы, который станет платформой для старта нашего sys-модуля. Рекурсивный обход реестра я описывать не буду, так как это тривиальный алгоритм. Хочется отметить два момента.
Первый заключаться в критерии отбора ключа жертвы. На мой взгляд, достаточно успешно открыть ключ с маской доступа STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_CREATE_SUB_KEY | KEY_SET_VALUE. Поиграв немного с функцией NtLoadDriver, я пришел к выводу, что лучше всего, если целевой ключ, указанный в DriverServiceName, будет иметь то же имя, что и файл модуля драйвера ядра. Для этого в маске присутствует KEY_CREATE_SUB_KEY.
Второй момент заключаться в том, что, используя нативную функцию, мы обязаны указывать путь реестра в формате дерева объектов. По этому я рекомендую использовать два базовых предопределенных ключа (HKEY_LOCAL_MACHINE и HKEY_USERS) в качестве стартовых ключей для поиска. Они соответствуют путям дерева объектов «\REGISTRY\MACHINE» и «\REGISTRY\USER» соответственно. Выбор на эти ключи также пал и потому, что из пользовательского режима загружать внешние ульи реестра функцией RegLoadKey, можно только в них. Это обусловлено тем, что они являются верхними уровнями иерархии ключей реестра Windows.
После успешного поиска ключа-жертвы, необходимо создать в нем новый подключ. В ново созданный подключ необходимо вписать REG_SZ значение ImagePath с путем до sys-файла загружаемого драйвера и REG_DWORD значение Type, содержащего 1 (SERVICE_KERNEL_DRIVER). Не забывайте, что имя файла должно быть понятно для ядра. Если необходимо стартовать драйвер из нестандартных директорий, то полный UserMode-путь должен начинаться с префикса «\??\» (например «\??\c:\driver.sys»).
Теперь все готово для загрузки драйвера. Получаем адрес функции NtLoadDriver, и производим загрузку. Например, так:
typedef
NTSTATUS
(__stdcall *PFNtLoadDriver)(
IN PUNICODE_STRING pusKeyPath
);
...
HMODULE hLibrary;
PFNtLoadDriver pFunction;
NTSTATUS nStatus;
UNICODE_STRING usRegPath;
...
hLibrary = LoadLibrary(L“ntdll.dll”);
if(NULL == hLibrary)
return GetLastError();
pFunction = (PFNtLoadDriver)
GetProcAddress(hLibrary,“NtLoadDriver”);
if(NULL == pFunction)
{
FreeLibrary(hLibrary);
return GetLastError();
}
nStatus = pFunction(&usRegPath);
...
После того, как сделаны все чёрные дела очень не плохо удалить за собой и ОС ключи реестра. Так же важно, что перед вызовом функции загрузки драйвера необходимо разрешить привилегию загрузки драйверов (SE_LOAD_DRIVER_NAME), так как описанный метод, к сожалению, не обходит проверку подсистемой безопасности привилегий. Но ведь всегда есть к чему стремиться;)….
P.S.: Написанное выше было протестировано на ОС Windows XP SP2 и Windows Vista SP1.
P.S.S.: Если возникли замечания, предложения или вопросы - пишите на форум в http://rootkits.ru/viewforum.php?id=10
@author: EreTIk |
|
|
EreTIk |
|
|
|