Создание антивирусного программного обеспечения с применением языка программирования низкого уровня: Методические указания по выполнению лабораторных работ по курсу «Информационная безопасность», страница 4

«UNDEFINED_INIT_ORDER» – порядок инициализации драйвера относительно других драйверов. Для динамически подгружаемых драйверов, естественно, значения не имеет.

После описательной части драйвера разумно разместить сегмент данных. Он может выглядеть приблизительно так:

VxD_LOCKED_DATA_SEG

данные...

Txt1      db   ‘Hello!’, 0

данные...

VxD_LOCKED_DATA_ENDS

Макросы «VxD_LOCKED_DATA_SEG» и «VxD_LOCKED_DATA_ENDS» означают начало и конец сегмента данных. Некоторые из Вас могут задать вопрос, почему в названии макроопределений присутствует слово «Locked»? Ответ прост. Внутри Windows существует два типа оперативной памяти – обычная и заблокированная (Locked). В случае нехватки физической памяти, Windows может произвести выгрузку некоторых страниц памяти (Memory pages) на жесткий диск в так называемый своп-файл (Swap file). Такая операция может быть произведена только со страницами обычной памяти. Страницы заблокированной памяти никогда не выгружаются на диск.

Для пояснения этого приведу следующий пример. Сам VMM, который отвечает за управление памятью, находится в драйвере (файл VMM32.vxd в частности). Что получится, если вдруг при каком-либо запросе приложения на дополнительную память VMM выгрузит себя на диск (т.е. страницу памяти, в которой он находится)? Правильно – зависание в лучшем случае. Такая проблема возникает из того, что некоторые функции (точнее – сервисы) VMM и драйверов могут вызываться асинхронно, т.е. когда система к этому не готова. Это возникает, например, когда приходит аппаратное прерывание от устройства (не следует путать с программным прерыванием, которое программист вызывает самостоятельно с помощью соответствующей команды процессора, т.к. к такому прерыванию система готова). Если при этом страница, в которой находится код обработчика этого прерывания вдруг окажется выгруженной на диск, может произойти зависание или другие непредвиденные действия (если обслуживаемое прерывание как-нибудь связано с файловой системой или менеджером памяти).

Это же относится и к файлам драйверов – поэтому, страница памяти, в которую попадет сегмент данных, отмеченный как Locked, никогда не будет выгружена на диск.

Следовательно, не следует в таких сегментах использовать чрезмерно большой объем памяти (например, 16 Мб), т.к. это снизит производительность компьютера или приведет к его неработоспособности вообще.

После сегмента данных следует разместить сегмент кода. При написании драйвера начало и конец сегмента кода обозначаются следующими макроопределениями: «VxD_LOCKED_CODE_SEG» (начало) и «VxD_LOCKED_CODE_ENDS» (конец сегмента кода). Пример:

VxD_LOCKED_CODE_SEG

...

Ваш код драйвера

...

VxD_LOCKED_CODE_ENDS

End

«End» после сегмента обозначает конец всей программы.

Теперь, когда стала понятна общая структура исходного файла драйвера, рассмотрим принцип передачи управления драйверу.

Какому либо куску кода внутри драйвера передается управление (т.е. он начинает выполняться) только когда драйверу приходит какое-либо сообщение (message). На каждое сообщение внутри кода драйвера существует своя процедура, которая предназначена для его обработки и возврата управления обратно ОС.

Все обрабатываемые драйвером сообщения и адреса соответствующих процедур собираются в таблицу, которая называется таблицей обработки сообщений (Table for dispatching messages). К нашему счастью, физическое составление этой таблицы берет на себя макроопределение из DDK, а нам необходимо лишь указать сообщение и имя процедуры его обработки. Это делается с помощью следующих строк исходного кода:

VxD_LOCKED_CODE_SEG

...

Begin_control_dispatch    AV

Control_Dispatch W32_DeviceIoControl, OnDeviceIoControl

Control_Dispatch SYS_DYNAMIC_DEVICE_INIT, OnSysInit

Control_Dispatch SYS_DYNAMIC_DEVICE_EXIT, OnSysExit

End_control_dispatch      AV 

...

VxD_LOCKED_CODE_ENDS

Как видно из примера, таблица обработки сообщений должна находиться внутри сегмента кода. Она начинается макросом «Begin_control_dispatch» (AV – это то самое имя драйвера, которое было задано ранее и обозначено нами как «имя»), а заканчивается макросом «End_control_dispatch».