Описание проекта VC6 для создания драйвера-посредника, обеспечивающего доступ любому VxD драйверу к сервисам драйвера WDM. Содержит описание DDK функций, использованных для этой цели, а также комментарии к коду самого драйвера. Проект находится в файле vxd.zip. В том же архиве находится файл whoiswho, содержащий собственно статью.

Взаимодействие между драйверами.

По поводу WDM часто задают вопросы о согласовании драйверов этой модели с VxD-драйверами. Драйвер виртуального устройства NTKERN в W98 предоставляет интерфейс, который другие VxD драйвера могут использовать для этой цели. Для исполняемого под W98 WDM-драйвера также возможно (но очень неудобно!) обращаться к VxD.
Сервисы NTKERN.VXD для операций ввода-вывода.

* NTKERN_Get_Version: Возвращает номер версии NTKERN.VXD
* _NtKernCreateFile: Открывает handle для WDM устройства
* _NtKernClose: Закрывает WDM handle
* _NtKernReadFile: Читает с WDM устройства
* _NtKernWriteFile: Записывает на WDM устройство
* _NtKernDeviceIoControl: Выполняет контрольную операцию с WDM устройством


Эти сервисы NTKERN пригодятся разработчику VxD, если тот захочет "пообщаться" с WDM- драйвером. NTKERN экспортирует также дополнительные VxD- сервисы, здесь не рассматриваемые. Сервисы, которые я собираюсь описать, используются подобно соответствующим функциям ZwXxx режима ядра. Например, чтобы прочитать данные с устройства, вы первым вызываете _NtKernCreateFile чтобы открыть хэндл, затем вызываете _NtKernReadFile один или сколько нужно раз, затем, при помощи _NtKernClose, закрываете хэндл. Чтобы понять, как можно использовать упомянутые ф-и, внимательно изучите документацию на ф-и ZwCreateFile, ZwReadFile, ZwClose в W98 DDK.

Необходимо принять во внимание три особенности бета-версии W98 DDK. Первое, в некоторых ранних версиях отсутствует библиотека VXDWRAPS.CLB, необходимая для определения "обертки" Майкрософт для вызова NTKERN. ("Обертка" wrappers - специальная библиотека). Поэтому, я определяю собственные "обертки" для некоторых из этих функций в начале С-файла моего VxD драйвера. Второе, бета-DDK включает поддиректорию, incmemphis, с новым Н- файлом NTKERN.H, которым заменяется несколько стандартных H файлов DDK. Если вы пытаетесь построить свой драйвер с бета-версией DDK, вы можете поэкспериментировать с настройками среды, чтобы работать с правильными Н-файлами и библиотеками. Наконец, сервисы NTKERN полностью не представлены в документации, поэтому я постарался показать, как их использовать применительно к различным ситуациям.

Кому интересно, то следующий драйвер является переделкой примера MYVXD.VXD из MSDN (MSDNJULY2001MSDNperiod97.chm::/html/dnnfig.htm#fig10). Поскольку мне не удалось напрямую использовать сервисы NTKERN в своем VXD из-за так и непонятого конфликта в заголовочных файлах, я сделал из MYVXD.VXD промежуточный драйвер, предоставляющий моему VXD функции для обращения к WDM. Ниже рассматривается исполнительный код драйвера, а также поднимается вопрос как устроить экспорт его функций.
Исполнительный код: myvxd.c
Код:
#include "stdvxd.h"
#pragma hdrstop
#include "myvxd.h"

/*Прототипы функций драйвера*/

DWORD OpenHandle(PUNICODE_STRING name,
ACCESS_MASK DesiredAccess,
ULONG FileAttributes,
ULONG ShareAccess,
ULONG CreateDisposition,
ULONG CreateOptions,
PHANDLE FileHandle);
DWORD _stdcall Ioctl(DWORD hDevice,
ULONG IoControlCode,
PVOID pInbuf,
ULONG cbInbuf,
PVOID pOutbuf,
ULONG cbOutbuf);
DWORD CloseHandle(DWORD hDevice);
DWORD _stdcall Read_file(DWORD hDevice,
PVOID pReadBuf,
ULONG ulReadBuf);
DWORD _stdcall Write_file(DWORD hDevice,
PVOID pWriteBuf,
ULONG ulWriteBuf);

DWORD MapStatusToError(NTSTATUS status);

/////////////////////////////////////////////////////////////////////////////////////////
/*Отладочная функция, для функционирования драйвера не нужна*/
WORD _stdcall testing(void)
{
return 15;
}
//////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Temporary wrappers for NtKern calls. These are only necessary with beta
// releases of the DDK, which define the wrappers in VXDWRAPS.H but don't
// include the VXDWRAPS.CLB file that resolves them!
///////////////////////////////////////////////////////////////////////////////
/* Те самые обертки, коих не хватает в бета-версии*/

#pragma warning(disable:4035)

#undef _NtKernCreateFile
#undef _NtKernClose
#undef _NtKernDeviceIoControl

NTSTATUS __declspec(naked) __stdcall _NtKernCreateFile(PHANDLE FileHandle,ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PIO_STATUS_BLOCK IoStatusBlock,
PLARGE_INTEGER AllocationSize,
ULONG FileAttributes,
ULONG ShareAccess,
ULONG CreateDisposition,
ULONG CreateOptions,
PVOID EaBuffer,
ULONG EaLength)
{
VxDJmp(_NtKernCreateFile)
}

NTSTATUS __declspec(naked) __stdcall _NtKernClose(HANDLE FileHandle)
{
VxDJmp(_NtKernClose)
}

NTSTATUS __declspec(naked) __stdcall _NtKernDeviceIoControl(HANDLE FileHandle,
HANDLE Event, //NULL
PIO_APC_ROUTINE ApcRoutine, //NULL
PVOID ApcContext, //NULL
PIO_STATUS_BLOCK IoStatusBlock,
ULONG IoControlCode,
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID OutputBuffer,
ULONG OutputBufferLength)
{
VxDJmp(_NtKernDeviceIoControl)
}

#pragma warning(default:4035)


///////////////////////////////////////////////////////////////////////////////
// System control message handlers
///////////////////////////////////////////////////////////////////////////////

#pragma VxD_INIT_CODE_SEG

SYSCTL BOOL OnSysDynamicDeviceInit(void)
{ // OnSysDynamicDeviceInit
if (NTKERN_Get_Version() < 0x40A)
return FALSE; // don't have Memphis level of NtKern
return TRUE;
} // OnSysDynamicDeviceInit

///////////////////////////////////////////////////////////////////////////////

#pragma VxD_PAGEABLE_CODE_SEG

SYSCTL BOOL OnSysDynamicDeviceExit(void)
{ // OnSysDynamicDeviceExit
return TRUE;
} // OnSysDynamicDeviceExit

///////////////////////////////////////////////////////////////////////////////

#pragma VxD_PAGEABLE_CODE_SEG

/*Обработчик DeviceIoControl для данного драйвера. В нем, в принципе, реализованы обращения к WDM драйверу
через соответствующие DeviceIoControl- коды. Нас это мало интересует, так как мы все равно устроим экспорт и будем обращаться к соответствующим функциям (OpenHandle, Ioctl, Read_file и пр.) напрямую*/

SYSCTL DWORD OnW32DeviceIoControl(PDIOCPARAMETERS p)
{ // OnW32DeviceIoControl
switch (p->dwIoControlCode)
{ // process control code

case DIOC_OPEN:
case DIOC_CLOSEHANDLE:
return 0;

case MYVXD_GETVERSION: // function 1
if (p->cbOutBuffer < sizeof(DWORD) || !p->lpvOutBuffer)
return ERROR_INVALID_PARAMETER;
*(PDWORD) p->lpvOutBuffer = 0x040A;
if (p->lpcbBytesReturned) // can be NULL!
*(PDWORD) p->lpcbBytesReturned = sizeof(DWORD);
return 0;

case MYVXD_OPENHANDLE: // function 2
{ // MYVXD_OPENHANDLE
OPENHANDLE_PARMS* parms = (OPENHANDLE_PARMS*) p->lpvInBuffer;
if (!parms || p->cbInBuffer < sizeof(OPENHANDLE_PARMS))
return ERROR_INVALID_PARAMETER;
return OpenHandle(&parms->name, parms->access, parms->attributes,
parms->share, parms->howcreate, parms->options,
(PHANDLE) &parms->hDevice);
} // MYVXD_OPENHANDLE

case MYVXD_IOCTL: // function 3
{ // MYVXD_IOCTL
IOCTL_PARMS* parms = (IOCTL_PARMS*) p->lpvInBuffer;
if (!parms || p->cbInBuffer < sizeof(IOCTL_PARMS))
return ERROR_INVALID_PARAMETER;
return Ioctl(parms->hDevice, parms->code, parms->inbuf,
parms->cbInbuf, parms->outbuf, parms->cbOutbuf);
} // MYVXD_IOCTL

case MYVXD_CLOSEHANDLE: // function 4
{ // MYVXD_CLOSEHANDLE
CLOSEHANDLE_PARMS* parms = (CLOSEHANDLE_PARMS*) p->lpvInBuffer;
if (!parms || p->cbInBuffer < sizeof(CLOSEHANDLE_PARMS))
return ERROR_INVALID_PARAMETER;
return CloseHandle(parms->hDevice);
} // MYVXD_CLOSEHANDLE

default:
ASSERT(FALSE);
return ERROR_INVALID_FUNCTION;
} // process control code
} // OnW32DeviceIoControl

///////////////////////////////////////////////////////////////////////////////
// OpenHandle opens a handle to a device managed by a WDM driver.

#pragma VxD_PAGEABLE_CODE_SEG

/*Реализация функций драйвера. Чтобы уточнить назначение передаваемых параметров, см. соответствующие ф-и в DDK: например
вызываемая в OpenHandle ф-я _NtKernCreateFile эквивалентна ZwCreateFile и т.д.*/


DWORD OpenHandle(PUNICODE_STRING name, ACCESS_MASK DesiredAccess, ULONG FileAttributes,
ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PHANDLE FileHandle)
{ // OpenHandle
OBJECT_ATTRIBUTES ObjectAttributes = {
sizeof(OBJECT_ATTRIBUTES), // Length
NULL, // RootDirectory
name, // ObjectName
0, // Attributes
NULL, // SecurityDescriptor
NULL, // SecurityQualityOfService
};

IO_STATUS_BLOCK IoStatusBlock;

return MapStatusToError(_NtKernCreateFile(FileHandle, DesiredAccess, &ObjectAttributes,
&IoStatusBlock, NULL, FileAttributes, ShareAccess, CreateDisposition,
CreateOptions, NULL, 0));
} // OpenHandle

///////////////////////////////////////////////////////////////////////////////
// Ioctl performs an IOCTL operation on a WDM device handle.

#pragma VxD_LOCKED_CODE_SEG

DWORD _stdcall Ioctl(DWORD hDevice, ULONG IoControlCode, PVOID pInbuf, ULONG cbInbuf, PVOID pOutbuf, ULONG cbOutbuf)
{ // Ioctl
IO_STATUS_BLOCK IoStatusBlock;

return MapStatusToError(_NtKernDeviceIoControl((HANDLE) hDevice, NULL, NULL, NULL,
&IoStatusBlock, IoControlCode, pInbuf, cbInbuf, pOutbuf, cbOutbuf));
} // Ioctl

///////////////////////////////////////////////////////////////////////////////
// Read_File performs an READ operation on a WDM device handle.

#pragma VxD_PAGEABLE_CODE_SEG

DWORD _stdcall Read_file(DWORD hDevice, PVOID pReadBuf, ULONG ulReadBuf)
{ // Read_File
IO_STATUS_BLOCK IoStatusBlock;

return MapStatusToError(_NtKernReadFile((HANDLE) hDevice, NULL, NULL, NULL,
&IoStatusBlock, pReadBuf, ulReadBuf,NULL,NULL));
} // Read_File

///////////////////////////////////////////////////////////////////////////////
// Write_file performs an WRITE operation on a WDM device handle.

#pragma VxD_PAGEABLE_CODE_SEG

DWORD _stdcall Write_file(DWORD hDevice, PVOID pWriteBuf, ULONG ulWriteBuf)
{ // Write_file
IO_STATUS_BLOCK IoStatusBlock;

return MapStatusToError(_NtKernWriteFile((HANDLE) hDevice, NULL, NULL, NULL,
&IoStatusBlock, pWriteBuf, ulWriteBuf,NULL,NULL));
} // Write_file

///////////////////////////////////////////////////////////////////////////////
// CloseHandle closes a WDM device handle.

#pragma VxD_PAGEABLE_CODE_SEG

DWORD CloseHandle(DWORD hDevice)
{ // CloseHandle
return MapStatusToError(_NtKernClose((HANDLE) hDevice));
} // CloseHandle

///////////////////////////////////////////////////////////////////////////////
// MapStatusToError translates an NTSTATUS return from a WDM driver to a standard
// Win32 error code.
/*Есть подозрение, что именно из-за несоответствия таблиц кодов ошибок я и не смог использовать сервисы NTKERN напрямую */

#pragma VxD_PAGEABLE_CODE_SEG

DWORD MapStatusToError(NTSTATUS status)
{ // MapStatusToError

// Status code mapping taken from KB article Q113996:

static struct {NTSTATUS s; DWORD e;} statmap[] = {
{STATUS_DATATYPE_MISALIGNMENT, ERROR_NOACCESS},
{STATUS_ACCESS_VIOLATION, ERROR_NOACCESS},
.......................................
{STATUS_VERIFY_REQUIRED, ERROR_MEDIA_CHANGED}
};
int i;

if (NT_SUCCESS(status))
return 0;

for (i = 0; i < arraysize(statmap); ++i)
if (status == statmap[i].s)
return statmap[i].e;
return ERROR_ACCESS_DENIED; // unknown error
} // MapStatusToError

Примечание. В англоязычных конфах, я встречал большую ругань в адрес ZwXxx функций. Там они обзывались "настоящей волчьей ямой для программиста". Объяснялось это тем, что открытие-закрытие хэндла на уровне ядра - вещь абсолютно непрозрачная, нетривиальная и глюкавая. Не имея возможности проверить это, да и не желая, возится, я попросту все операции открытия-закрытия хэндлов выполняю в приложении пользователя, после чего посылаю значения хэндлов в драйвер при помощи DeviceIoControl.


Экспорт функций. Если посмотреть в файл DevCtrl.asm, то можно увидеть следующий раздел:
Код:
;======================================================================
; The VxD services provided for other VxDs to call
; !!!! -> MUST be declared first off all !!!
;=======================================================================

Create_MYVXD_Service_Table EQU 1

Begin_Service_Table MYVXD
MYVXD_Service MYVXD_Get_Version, LOCAL
MYVXD_Service _Ioctl@24, VxD_LOCKED_CODE
MYVXD_Service _Read_file@12, VxD_LOCKED_CODE
MYVXD_Service _Write_file@12, VxD_LOCKED_CODE
MYVXD_Service _testing@0, VxD_LOCKED_CODE
End_Service_Table MYVXD
;------------------------------------------------------------------------------

Это так называемая таблица сервисов. Если вы хотите, чтобы функции драйвера VXD были доступны из других драйверов, как, например, функции предоставляемые динамической библиотекой доступны для приложений, то вы должны объявить их в таблице сервисов след. образом:
_[имя функции]@[размер передаваемых ей параметров в байтах] [директива]
Если таблица сервисов отсутствует (до вашего вмешательства драйвер ничего не экспортировал или вы пишите драйвер с нуля), объявите ее. Как это сделать, показано выше.

Заголовочный файл с "обертками" (myvxdw.h). Этот файл подключается как заголовочный в начало текста любого VxD драйвера, после чего тот обретает возможность пользоваться функциями Myvxd.vxd, как своими собственными.
Здесь интересны 2 раздела:

Декларации сервисов. Выглядит тривиально, поэтому без комментариев.
Код:
/**************************** VxD services declaration ******************** */

#define MYVXD_Service Declare_Service

Begin_Service_Table (MYVXD, VxD)

MYVXD_Service (MYVXD_Get_Version, VxD_CODE)
MYVXD_Service (Ioctl, VxD_CODE)
MYVXD_Service (Read_file, VxD_CODE)
MYVXD_Service (Write_file, VxD_CODE)
MYVXD_Service (_testing, VxD_CODE)

End_Service_Table (MYVXD, VxD)
/*********************************************************************/

Обертки.
Здесь комментариев тоже немного: чтобы писать собственные обертки, необходимо знать хотя бы азы ассемблера. При написании обертки для ф-и принимающей параметры (для примера - Ioctl), помните о порядке заполнения стека параметрами: от последнего к первому.
Код:
/*********************** VxD services wrappers ************************ */

WORD VXDINLINE MYVXD_Get_Version(VOID)
{
WORD w;
Touch_Register(eax)
VxDCall(MYVXD_Get_Version);
_asm mov [w], ax
return(w);
}

DWORD VXDINLINE Ioctl(DWORD hDevice, ULONG IoControlCode, PVOID pInbuf, ULONG cbInbuf, PVOID pOutbuf, ULONG cbOutbuf)
{
DWORD dw;
Touch_Register(eax)
_asm mov eax,0
_asm push [cbOutbuf]
_asm push [pOutbuf]
_asm push [cbInbuf]
_asm push [pInbuf]
_asm push [IoControlCode]
_asm push [hDevice]
VxDCall(Ioctl);
_asm add esp, 4
_asm mov [dw], eax
return(dw);
}
/******************************************************************/
Information
  • Posted on 31.01.2010 21:55
  • Просмотры: 299