Defining Stubs for Kernel-Mode Routines

[Previous] [Next]

The stub technique used in WDMSTUB.VXD relies on the same basic trick that Microsoft crafted to port several hundred kernel-mode support functions from Microsoft Windows NT to Windows 98—that is, extending the symbol tables that the run-time loader uses when it resolves import references. To extend the symbol tables, you first define three data tables that will persist in memory:

  • A name table that gives the names of the functions you're defining
  • An address table that gives the addresses of the functions
  • An ordinal table that correlates the name and address tables

Here are some of the table entries from WDMSTUB:

static char* names[] = {   "PoRegisterSystemState",   ...   "ExSystemTimeToLocalTime",   ...   }; static WORD ordinals[] = {   0,   ...,   6,   ...   }; static PFN addresses[] = {   (PFN) PoRegisterSystemState,   ...   (PFN) ExSystemTimeToLocalTime,   ...   };

The purpose of the ordinal table is to provide the index within addresses of the entry for a given names entry. That is, the function named by names[i] is address[ordinals[i]].

If it weren't for a version compatibility problem I'll describe in a moment, you could call _PELDR_AddExportTable as follows:

HPEEXPORTTABLE hExportTable = 0; extern "C" BOOL OnDeviceInit(DWORD dwRefData)   {   _PELDR_AddExportTable(&hExportTable,      "ntoskrnl.exe",     arraysize(addresses), //  don't do it this way!     arraysize(names), 0,      (PVOID*) names,     ordinals, addresses, NULL);   return TRUE;   }

The call to _PELDR_AddExportTable extends the table of symbols that the loader uses when it tries to resolve import references from NTOSKRNL.EXE, which is of course the Windows 2000 kernel. NTKERN.VXD, the main support module for WDM drivers in Windows 98, initializes this table with the addresses of the several hundred functions it supports. WDMSTUB.VXD is a static VxD with an initialization order later than NTKERN and earlier than the Windows 98 Configuration Manager. Consequently, WDMSTUB's export definitions will be in place by the time the system loads any WDM drivers. In effect, then, WDMSTUB is an extension to NTKERN.

Version Compatibility

The version compatibility problem to which I alluded earlier is this: Windows 98 supports a particular subset of the Windows 2000 functions used by WDM drivers. Windows 98, Second Edition, supports a larger subset. The next version of Windows, code-named Millennium, will support a still larger subset (maybe even a superset, given that it will be released after Windows 2000). You would not want your stub VxD to duplicate one of the functions that the OS supports. What WDMSTUB actually does during initialization, therefore, is dynamically construct the tables that it passes to _PELDR_AddExportTable:

HPEEXPORTTABLE hExportTable = 0; extern "C" BOOL OnDeviceInit(DWORD dwRefData)   {   char** stubnames = (char**) _HeapAllocate(sizeof(names), HEAPZEROINIT);   PFN* stubaddresses = (PFN*) _HeapAllocate(sizeof(addresses),     HEAPZEROINIT);   WORD* ordinals = (WORD*) _HeapAllocate(arraysize(names) * sizeof(WORD),     HEAPZEROINIT);   int i, istub;   for (i = 0, istub = 0; i < arraysize(names); ++i)     {     if (_PELDR_GetProcAddress((HPEMODULE) "ntoskrnl.exe", names[i], NULL)       == 0)        {       stubnames[istub] = names[i];       ordinals[istub] = istub;       stubaddresses[istub] = addresses[i];       ++istub;       }     }   _PELDR_AddExportTable(&hExportTable, "ntoskrnl.exe", istub,     istub, 0, (PVOID*) stubnames, ordinals, stubaddresses, NULL);   return TRUE;   }

The line appearing in bold face is the crucial step here—it makes sure that we don't inadvertently replace a function that already exists in NTKERN or another system VxD.

There's one annoying glitch in the version compatibility solution I just outlined. Windows 98, Second Edition, exports just three of the four support functions for managing the IO_REMOVE_LOCK object. The missing function is IoRemoveLockAndWaitEx, if you care. My WDMSTUB.VXD driver compensates for this omission by stubbing either all or none of the remove lock functions based on whether or not this function is missing.

Stub Functions

The main purpose of WDMSTUB.VXD is to resolve symbols that your driver might reference but not actually call. For some functions, such as PoRegisterSystemState , WDMSTUB.VXD simply contains a stub that will return an error indication if it is ever called:

PVOID PoRegisterSystemState(PVOID hstate, ULONG flags)   {   ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);   return NULL;   }

Building WDMSTUB

To get WDMSTUB to build correctly, I needed to incorporate a couple of nonstandard features. Each stub function must use the _ _stdcall calling convention, whereas VxDs normally use _ _cdecl.

I wanted to call KeGetCurrentIrql and maybe other WDM service functions from the stub VxD. A standard way to do this is to include WDM.H or NTDDK.H before all of the VxD header files and link with the WDMVXD.LIB import library. WDMVXD.LIB assumes that the functions you're trying to import are declared with the _ _declspec(dllimport) directive, which is normally true when you include either WDM.H or NTDDK.H. This is because they're all declared using a preprocessor macro named NTKERNALAPI, which normally gets #defined as _ _declspec(dllimport). Unfortunately, if you try to define a function that's marked as dllimport, the compiler assumes you meant to export the function. A VxD's first export must be the device description block (DDB) that defines the driver, though, and not some random exported stub function. I guessed that specifying ordinal number 1 for the DDB in my module definition file would force the DDB to be the first export, but I was mistaken. At the end of this rather sad story, I ended up with a VxD that wouldn't load.

To get past all of these problems with import vs. export declarations, I had to coerce NTDDK.H not to define NTKERNELAPI in the normal way. (See STDVXD.H in the WDMSTUB project.) That leaves the module with unresolved references to symbols like _KeGetCurrentIrql@0 because of the limited vocabulary of WDMVXD.LIB. In the particular case of KeGetCurrentIrql, one can issue a standard VxDCall to a service named ObsoleteKeGetCurrentIrql and reach the right function in the Windows 98 kernel. Alternatively, one could define a function (with a name like MyGetCurrentIrql) that calls KeGetCurrentIrql and place it in a source module that you compile with the normal setting for NTKERNELAPI.

Sometimes, though, you don't need to write a stub that fails the function call—you can actually implement the function, as in this example:

VOID ExLocalTimeToSystemTime(PLARGE_INTEGER localtime,   PLARGE_INTEGER systime)   {   systime->QuadPart = localtime->QuadPart + GetZoneBias();   }

where GetZoneBias is a helper routine that determines the time zone bias—that is, the number of units by which local time differs from Greenwich mean time—by interrogating the ActiveTimeBias value in the TimeZoneInformation registry key.

Table A-1 lists the kernel-mode support functions that WDMSTUB.VXD exports.

Table A-1. Functions exported by WDMSTUB.VXD.

Support Function Remarks
ExLocalTimeToSystemTime Implemented
ExSystemTimeToLocalTime Implemented
IoAcquireRemoveLockEx Implemented
IoAllocateWorkItem Implemented
IoFreeWorkItem Implemented
IoInitializeRemoveLockEx Implemented
IoQueueWorkItem Implemented
IoReleaseRemoveLockEx Implemented
IoReleaseRemoveLockAndWaitEx Implemented
IoCreateNotificationEvent Stub—always fails
IoCreateSynchronizationEvent Stub—always fails
IoReportTargetDeviceChangeAsynchronous Stub—always fails
KdDebuggerEnabled Implemented
KeEnterCriticalRegion Implemented
KeLeaveCriticalRegion Implemented
KeNumberProcessors Always returns 1
KeSetTargetProcessorDpc Implemented
PoCancelDeviceNotify Stub—always fails
PoRegisterDeviceNotify Stub—always fails
PoRegisterSystemState Stub—always fails
PoSetSystemState Stub—always fails
PoUnregisterSystemState Stub—always fails
PsGetVersion Implemented
RtlInt64ToUnicodeString Stub—always fails
RtlUlongByteSwap Implemented
RtlUlonglongByteSwap Implemented
RtlUshortByteSwap Implemented



Programming the Microsoft Windows Driver Model
Programming the Microsoft Windows Driver Model
ISBN: 0735618038
EAN: 2147483647
Year: 1999
Pages: 93
Authors: Walter Oney

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net