Создание VxD на Visual C++ без ассемблерных модулей
Общая
схема драйвера VxDМинимальный
виртуальный драйвер должен содержать секцию резидентного кода, в которой
расположены блок описателя устройства, диспетчер системных сообщений и
обработчики сервисных функций.Обработчик
системных сообщений анализирует код сообщения, переданный в EAX, выделяет
интересующие его сообщения, обрабатывает их и возвращает сброшенный флаг
CF в случае успеха, и установленный — в случае неудачи. Для всех необрабатываемых
сообщений должен возвращаться сброшенный флаг CF.Обработчики
сервисных функций вызываются по таблице, так что каждой функции соответствует
собственный обработчик. Способ передачи параметров и возврата результатов
определяется разработчиком.При
необходимости драйвер может содержать обработчики запросов V86 и PM API.
Для доступа к данным виртуальных машин DOS, указатели на которые могут
передаваться в регистрах при запросе, достаточно преобразовать их в линейные
32-разрядные адреса, ибо первый мегабайт адресного пространства текущей
виртуальной машины непосредственно «виден» из VxD. Для доступа к данным
приложений Win16 потребуется выполнить отображение адресов посредством
функции VMM _SelectorMapFlat.Программирование
VxDСредства
разработки, включаемые файлы и библиотекиМинимально
необходимый набор включаемых файлов и библиотек содержится в Windows 95
DDK (подкаталоги Inc32 и Lib). Обычно требуется включение
хотя бы файлов BASEDEF.H и VMM.H.Файлы
VXDWRAPS.H, CONFIGMG.H и некоторые другие оформлены в стиле
обычного языка C, поэтому при включении их в файлы типа CPP директивы
#include необходимо помещать внутрь квалификатора extern "C":extern
"C" {
#include <vxdwraps.h>
} Файлы
из DDK можно включать и в тексты модулей обычных приложений, определив
перед этим символическое имя Not_VxD. При этом определяются только
полезные константы и типы, а определение специфических для VxD конструкций
отключается.Структуры,
обычно используемые в VxDVxD_Desc_Block
- блок описателя устройстваОписывает
структуру DDB. Заполняется статически, чтобы к моменту загрузки драйвера
все поля имели нужные значения.ULONG DDB_Next;
USHORT DDB_SDK_Version;
USHORT DDB_Req_Device_Number;
UCHAR DDB_Dev_Major_Version;
UCHAR DDB_Dev_Minor_Version;
USHORT DDB_Flags;
UCHAR DDB_Name [8];
ULONG DDB_Init_Order;
ULONG DDB_Control_Proc;
ULONG DDB_V86_API_Proc;
ULONG DDB_PM_API_Proc;
ULONG DDB_V86_API_CSIP;
ULONG DDB_PM_API_CSIP;
ULONG DDB_Reference_Data;
ULONG DDB_Service_Table_Ptr;
ULONG DDB_Service_Table_Size;
ULONG DDB_Win32_Service_Table;
ULONG DDB_Prev;
ULONG DDB_Size;
ULONG DDB_Reserved1;
ULONG DDB_Reserved2;
ULONG DDB_Reserved3;
- DDB_Next — поле для адреса следующего DDB в списке VMM. Инициализируется нулем.
- DDB_SDK_Version — версия DDK, с которой построен драйвер. Инициализируется константой DDK_VERSION.
- DDB_Req_Device_Number — идентификатор устройства. При отсутствии назначенного идентификатора задается нулевое значение.
- DDB_Dev_Major_Version — старшая часть номера версии драйвера.
- DDB_Dev_Minor_Version — младшая часть номера версии драйвера.
- DDB_Flags — для служебных флагов VMM. Инициализируется нулем.
- DDB_Name — имя устройства, дополненное пробелами до восьми символов.
- DDB_Init_Order — позиция драйвера в списке загрузки. Если порядок загрузки не важен, используется константа UNDEFINED_INIT_ORDER (нуль).
- DDB_Control_Proc — адрес функции диспетчера системных сообщений.
- DDB_V86_API_Proc — адрес функции диспетчера V86 API.
- DDB_PM_API_Proc — адрес функции диспетчера PM API.
- DDB_V86_API_CSIP — служебное поле, инициализируется нулем.
- DDB_PM_API_CSIP — служебное поле, инициализируется нулем.
- DDB_Reference_Data — служебное поле, инициализируется нулем.
- DDB_Service_Table_Ptr — указатель таблицы адресов процедур—обработчиков сервисных функций.
- DDB_Service_Table_Size — количество сервисных функций, реализованных в драйвере.
- DDB_Win32_Service_Table — служебный указатель таблицы функций Win32, инициализируется нулем.
- DDB_Prev — поле для адреса предыдущего DDB в списке VMM, инициализируется константой 'Prev'.
- DDB_Size — размер структуры описателя.
- DDB_Reserved1 — служебное поле. Инициализируется константой 'Rsv1'.
- DDB_Reserved2 — служебное поле. Инициализируется константой 'Rsv2'.
- DDB_Reserved3 — служебное поле. Инициализируется константой 'Rsv3'.
ULONG Client_ESI;
ULONG Client_EBP;
ULONG Client_res0;
ULONG Client_EBX;
ULONG Client_EDX;
ULONG Client_ECX;
ULONG Client_EAX;
ULONG Client_Error;
ULONG Client_EIP;
USHORT Client_CS;
USHORT Client_res1;
ULONG Client_EFlags;
ULONG Client_ESP;
USHORT Client_SS;
USHORT Client_res2;
USHORT Client_ES;
USHORT Client_res3;
USHORT Client_DS;
USHORT Client_res4;
USHORT Client_FS;
USHORT Client_res5;
USHORT Client_GS;
USHORT Client_res6;
ULONG Client_Alt_EIP;
USHORT Client_Alt_CS;
USHORT Client_res7;
ULONG Client_Alt_EFlags;
ULONG Client_Alt_ESP;
USHORT Client_Alt_SS;
USHORT Client_res8;
USHORT Client_Alt_ES;
USHORT Client_res9;
USHORT Client_Alt_DS;
USHORT Client_res10;
USHORT Client_Alt_FS;
USHORT Client_res11;
USHORT Client_Alt_GS;
USHORT Client_res12;Поля с именами Client_xxx содержат значения соответствующих регистров на момент обращения виртуальной машины или приложения к системной функции. В поле Client_Error может быть занесен код ошибки.Для структуры введен синоним типа (typedef) с именем CRS. В файле VMM.H определены также вспомогательные структуры Client_Word_Reg_Struc и Client_Byte_Reg_Struc и объединение всех трех структур CLIENT_STRUCT.DIOCParams - параметры запроса DeviceIoControlDWORD Internal1;
DWORD VMHandle;
DWORD Internal2;
DWORD dwIoControlCode;
DWORD lpvInBuffer;
DWORD cbInBuffer;
DWORD lpvOutBuffer;
DWORD cbOutBuffer;
DWORD lpcbBytesReturned;
DWORD lpoOverlapped;
DWORD hDevice;
DWORD tagProcess;
- VMHandle — идентификатор виртуальной машины, сделавшей запрос.
- dwIoControlCode — код функции. Константы для определенных в системе кодов функций имеют префикс DIOC_, остальные функции определяются разработчиком.
GETVERSION (0) | Открывание и опрос интерфейса. Если драйвер не поддерживает Win32 API, он должен вернуть в EAX ненулевое значение. В противном случае в EAX возвращается нуль, а если задан буфер результата, то в него заносится номер версии драйвера. |
CLOSEHANDLE (-1) | Закрывание интерфейса. Драйвер должен прервать обработку всех асинхронных запросов по этому устройству и освободить относящиеся к нему ресурсы. |
- lpvInBuffer — указатель исходного буфера.
- cbInBuffer — размер исходного буфера в байтах.
- lpvOutBuffer — указатель буфера результата.
- cbOutBuffer — размер буфера результата в байтах.
- lpcbBytesReturned — поле для объема в байтах данных, занесенных драйвером в буфер результата.
- lpoOverlapped — указатель структуры типа OVERLAPPED (описатель адреса внутри файла и/или данных для асинхронной операции).
- hDevice — идентификатор устройства.
- tagProcess — идентификатор запроса. Вместе с полем hDevice образует уникальный внутри системы идентификатор запроса, по которому запрос может быть найден и аварийно прерван при получении запроса CLOSEHANDLE.
- EBX — идентификатор системной виртуальной машины.
- ESI — адрес командной строки VMM в его PSP. Первый байт строки определяет ее длину.
- EBX — идентификатор создаваемой виртуальной машины.
- EBX — идентификатор виртуальной машины.
- EBX — идентификатор завершаемой машины.
- EBX — идентификатор уничтожаемой машины.
- EDI — идентификатор создаваемой задачи.
- EDI — идентификатор завершаемой задачи.
- EDI — идентификатор уничтожаемой задачи.
- EBX — идентификатор системной виртуальной машины.
- EBX — идентификатор текущей виртуальной машины.
- ESI — адрес блока параметров DIOCParams.
- Сообщение всегда посылается в контексте вызвавшей задачи Win32, так что драйверу напрямую доступно адресное пространство приложения. Драйвер обрабатывает запрос, извлекая из блока параметров и исходного буфера данные запроса, и возвращает в EAX код завершения:
DWORD VMHandle,
DWORD Sel,
DWORD Reserved
);
- VMHandle — идентификатор виртуальной машины, которой принадлежит селектор. Если селектор относится к GDT, идентификатор игнорируется.
- Sel — селектор для отображения.
- Flags — резервный параметр, должен быть нулевым.
- String — указатель строки, выводимой в отладочный поток. Для перехода на новую строку используется символ '\n".
_declspec (naked)
void *SelectorMapFlat (DWORD VM, DWORD Sel, DWORD Rsv = 0) {
VMMJmp (_SelectorMapFlat) // Передача управления VMM
(void)(VM, Sel, Rsv); // Имитация использования параметров
}
#pragma warning (default: 4035) // Восстановление предупреждений о невозврате Квалификатор _declspec (naked) подавляет генерацию пролога/эпилога, так что от функции остается лишь конструкция VMMJmp. При вызове этой функции-обертки параметры VM, Sel и Rsv помещаются в стек, затем туда же помещается адрес возврата, управление передается в функцию-обертку, при этом VMMJmp передает управление сервисной функции VMM _SelectorMapFlat, не запоминая нового адреса возврата в стеке. VMM использует значения параметров из стека, затем выполняет возврат по адресу, находящемуся на верхушке стека, при этом управление сразу возвращается в точку за вызовом функции-обертки, где находится код, удаляющий из стека параметры и использующий значение, возвращенное VMM в EAX.Такая схема типична для построения функций-оберток, так как в полной мере использует особенности создания стекового кадра в языках C, а также возможности компилятора Visual C++ по оформлению функций. Без использования VMMJmp пришлось бы делать возврат дважды, а без использования квалификатора naked — дважды помещать в стек список параметров.Пример простого VxDВ качестве примера приводится простейший VxD, отслеживающий события создания и уничтожения задач (threads). Единственная цель проекта — иллюстрация построения VxD целиком на C++. Драйвер намеренно сделан максимально простым, чтобы продемонстрировать прозрачность и относительную несложность базового VxD.Для полного понимания всех рассмотренных вопросов, а также для разработки собственных VxD вам понадобится Windows 9x DDK — набор включаемых файлов и библиотек Microsoft. Примерно до весны 1999 года, когда появился DDK для Windows 98, DDK для Windows 95 распространялся Microsoft свободно и бесплатно; сейчас свободно можно получить только DDK для Windows 98, который гораздо менее удобен для небольших проектов. Однако DDK для Windows 95 до сих пор можно найти в Интернете при помощи поисковых систем — например, http://www.filesearch.ru/ — по ключевым словам Windows, 95, DDK. Объем архива — около 17 Мбайт (полного комплекта DDK для Windows 98 — в несколько раз больше).При желании можно обойтись без «официальной», полной установки DDK, которая создает среду для разработки, принятую в Microsoft. Достаточно лишь распаковать подкаталоги INC32 и LIB и внести пути к ним в список путей поиска включаемых и библиотечных файлов компилятора (Tools -> Options -> Directories). После этого можно строить практически любые VxD (для некоторых — например, мультимедийных — может понадобиться добавление каталогов INC16, MMEDIA\INC и других специализированных).В комплект файлов примера включены необходимые для построения файлы из DDK для Windows 95 (basedef.h, vmm.h, vxdwraps.h, vxdwraps.clb). Созданный VxD будет работоспособен под Windows 95, 98 и Me.Для загрузки и тестирования драйвера можно воспользоваться утилитой Monitor, включенной в комплект примера. Для запуска утилиты достаточно распаковать архив и запустить файл Monitor.exe; в случае возникновения проблем нужно сделать изменения в реестре, запустив файл dbgmsg.reg.
Оставить комментарий
Комментарии
Меня интересует такой вопрос:
Мне нужно срочно достать пакет DDK.
И желательно где.
Благодарен за любую помощь!