DriverObject->MajorFunction[IRP_MJ_CLOSE] = Wdm1Close;

 DriverObject->MajorFunction[IRP_MJ_PNP] = Wdm1Pnp;

 DriverObject->MajorFunction[IRP_MJ_POWER] = Wdm1Power;

 DriverObject->MajorFunction[IRP_MJ_READ] = Wdm1Read;

 DriverObject->MajorFunction[IRP_MJ_WRITE] = Wdm1Write;

 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Wdm1DeviceControl;

 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = Wdm1SystemControl;

 // …

 return status;

}

Version Resource

Wdm1.rc simply defines a version resource block with version and copyright information.

Accessing the Registry

The RegistryPath parameter to DriverEntry contains the registry key of the driver. The Wdm1 code does not currently use its RegistryPath parameter. However, it is common for drivers to use its registry key to store parameters for the whole driver. Therefore, I present the ReadReg routine shown in Listing 4.6. This reads two values from the driver's registry path Parameters subkey. The first value is a ULONG obtained from the value named UlongValue. The second value is a string obtained from the default value for the Parameters key.

Eventually ReadReg needs to call the RtlQueryRegistryValues kernel routine to read both the registry values in one fell swoop. One of the parameters is the absolute registry path, as a NULL-terminated wide string. The driver registry path is supplied in a UNICODE_STRING structure. Although this contains a wide string buffer, it may not necessarily be NULL-terminated. Unfortunately, this means that we have to laboriously make a copy of the string, simply to add on that dratted NULL-terminating character.

The first section of ReadReg does this job. It works out the length of buffer required and uses ExAllocatePool to allocate the memory from the paged pool. The RtlCopyMemory function is used to copy the bulk of the string over and RtlZeroMemory zeroes that all-important last character. You can do the copy and zero by hand if you want, though it should be more efficient to call the kernel functions.

Listing 4.6 ReadReg

void ReadReg(IN PUNICODE_STRING DriverRegistryPath) {

 // Make zero terminated copy of driver registry path

 USHORT FromLen = DriverRegistryPath->Length;

 PUCHAR wstrDriverRegistryPath = (PUCHAR)ExAnocatePool(PagedPool, FromLen+sizeof(WCHAR));

 if( wstrDriverRegistryPath==NULL) return;

 RtlCopyMemory(wstrDriverRegistryPath, DriverRegistryPath->Buffer, FromLen);

 RtlZeroMemory(wstrDriverRegistryPath+FromLen, sizeof(WCHAR));

 // Initialise our ULONG and UNICODE_STRING values

 ULONG UlongValue = –1;

 UNICODE_STRING UnicodeString;

 UnicodeString.Buffer = NULL;

 UnicodeString.MaximumLength = 0;

 UnicodeString.Length = 0;

 // Build up our registry query table

 RTL_QUERY_REGISTRY_TABLE QueryTable[4];

 RtlZeroMemory(QueryTable, sizeof(QueryTable));

 QueryTable[0].Name = L'Parameters';

 QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY;

 QueryTable[0].EntryContext = NULL;

 QueryTable[1].Name = L'UlongValue';

 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;

 OueryTable[1].EntryContext = &UlongValue;

 QueryTable[2].Name = L''; // Default value

 QueryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT;

 QueryTable[2].EntryContext = &UnicodeString;

 // Issue query

 NTSTATUS status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, (PWSTR)wstrDriverRegistryPath, QueryTable, NULL, NULL);

 // Print results

 DebugPrint('ReadReg %x: UlongValue %x UnicodeString %T', status.UlongValue, &UnicodeString);

 // Do not forget to free buffers

 if (UnicodeString.Buffer!=NULL) ExFreePool(UnicodeString.Buffer);

 ExFreePool(wstrDriverRegistryPath);

}

The UNICODE_STRING Structure

This is how the UNICODE_STRING type is defined.

typedef struct _UNICODE_STRING {

 USHORT Length;

 USHORT MaximumLength;

 PWSTR Buffer;

} UNICODE_STRING, *PUNICODE_STRING;

The Buffer field points to a wide 16-bit character buffer. The character string is not usually NULL-terminated. Instead, the Length field gives the current size of the string in bytes. The MaximumLength field gives the maximum size of the string that can fit in the buffer in bytes. This design is used to avoid reallocating string buffers too often. However, it does make manipulating Unicode strings a bit awkward. Table 4.6 shows all the kernel routines that you can use with Unicode strings.

Just in case you were interested, you do not have to use these kernel routines to access Unicode strings. You can fiddle with a string structure however you like, as long as it is in a valid format when passed to the kernel.

Table 4.6 UNICODE_STRING functions

RtlAnsiStringToUnicodeString Converts an ANSI string to a Unicode string, optionally allocating a buffer.
RtlAppendUnicodeStringToString Append one Unicode string to another, up to the length ofthe destination buffer.
RtlAppendUnicodeToString Append a wide string to a Unicode string, up to the length of the destination buffer.
RtlCompareUnicodeString Compares two Unicode strings, optionally case-insensitive.
RtlCopyUnicodeString Copies one Unicode string to another, up to the length of the destination buffer.
Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату