Windows driver framework

After configuring the corresponding development environment really want, we can develop the driver. Note: The following mainly NT drive, for example, relates in part to the WDM driver of the difference will be specified.

Under Console console, we have an entry function main; in the Windows graphical interface platform, there is another entry function Winmain. As long as we call other related functions in this function inside the entrance, the program will run up according to our will. When we use the IDE development, maybe you will not find how to configure these nuances are out, in general, we do not care, because at the time of new construction, has helped us to IDE compiler (Compiler) and the connection device-related parameters (Linker) is set in a formal program, we just follow the provisions of the framework program on the line.

Similarly, the driver also has an entry function DriverEntry, this is not certain, but this is the Microsoft default, recommended. When we configure the development environment, we have the opportunity to specify the entry function, which is the linker parameter / entry: "DriverEntry".

Declaration of entry function

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegistryPath)


DriverEntry mainly on the driver initializes, it creates a system process (System), the system starts when the system processes System was created.


Driver is loaded when the system process will create a new thread, and then call the executable components in the Object Manager, create a drive object (DRIVER_OBJECT). In addition, the system calls the process had to perform configuration management program in the assembly, the driver queries the corresponding entry in the registry. The system will process the two values ​​passed pDriverObject and pRegistryPath when calling the driver's DriverEntry.


Next, we introduce several data structures appear at the top:

typedef LONG NTSTATUS

Drivers in the development, we should be used to return information NTSTATUS, NTSTATUS individual bits have different meanings, we can and should use to determine whether to return a macro NT_SUCCESS success.

#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)

NTSTAUS code meaning :


among them

Ser is Serviity the acronym that stands for severity.

00: 01 Success: Information 10: 11 Warning: Error

C is an acronym for Customer, representing bit custom.

Facility: Bit Device

Code: status code device.

According to this custom coding, as well as complement the concept, so long as it is wrong, the most significant bit is 1, NTSTATUS value is negative, it can be judged greater than zero, but in any case I hope the reader with NT_SUCCESS macro to determine success because it might be modified in the future, even though so many years have been in use with.

Similarly, Microsoft also defines several other judge our macro:

#define NT_INFORMATION(Status) ((((ULONG)(Status)) >> 30) == 1)
#define NT_WARNING(Status) ((((ULONG)(Status)) >> 30) == 2)
#define NT_ERROR(Status) ((((ULONG)(Status)) >> 30) == 3)

With the previous introduction, three did not say I believe we can get the idea. But the most common is NT_SUCCESS.

We went on two other data structures, first said it PUNICODE_STRING, P representative of this type is a pointer pointing to a UNICODE_STRING structure.


Wide string structure (the UNICODE_STRING)
typedef struct _UNICODE_STRING {
  USHORT  Length;
  USHORT  MaximumLength;
  PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

among them,

Ø Length: Unicode string length of the current character. Note that not the number of bytes for each Unicode character occupies two bytes.

Ø MaximumLength: receiving the maximum length of the Unicode string.

Ø Buffer: Buffer address Unicode strings.

 

UNICODE_STRING Windows Driver Development is a structure which is frequently used, labeled with the length of the string Length without reuse \ 0 to indicate the end. RtlInitUnicodeString can be used to initialize it, but pRegistryPath here is passed directly by the driver to create a thread that came parameter, if the next still need to use this value, it is best to use a function of its value addition RtlCopyUnicodeString saved, because this string is not a long-standing, when the DriverEntry function returns could have been destroyed.

 

PDRIVER_OBJECT, P represent this type is a pointer pointing to a drive object (the DRIVER_OBJECT), each driver has a driven object. This is the data structure of a translucent, Microsoft did not disclose its fully defined, but several members have mentioned, but we can still see that it is defined by WinDbg, just different systems may have different structures. But I Also in the WDK headers WDM.h found inside its definition:

 

Driven (the DRIVER_OBJECT)

typedef struct _DRIVER_OBJECT {
    CSHORT Type;
    CSHORT Size;
    PDEVICE_OBJECT DeviceObject;
    ULONG Flags;
    PVOID DriverStart;
    ULONG DriverSize;
    PVOID DriverSection;
    PDRIVER_EXTENSION DriverExtension;
    UNICODE_STRING DriverName;
    PUNICODE_STRING HardwareDatabase;
    PFAST_IO_DISPATCH FastIoDispatch;
    PDRIVER_INITIALIZE DriverInit;
    PDRIVER_STARTIO DriverStartIo;
    PDRIVER_UNLOAD DriverUnload;
    PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT;

Here to mention a few of the more important under the field,

Ø DeviceObject: pointing device objects created thereby driving. Each driver has a device object or more. Wherein each device object will have a pointer to the next device object, and then went on to say that in time we introduce the device object.

Ø DriverName: name of the driver, the string usually \ Driver \ [driver name].

Ø HardwareDatabase: recording hardware key database name of the device. The string is generally "\ REGISTRY \ MACHINE \ SYSTEM \ ControlSet001 \ Services \ [service name]."

Ø FastIoDispatch: point Fast I / O function entry, function files driver distraction is used.

Ø DriverStartIo: StartIo routine recording function address for serial operation.

Ø DriverUnload: specify a callback function address is used when the driver uninstall.

Ø MajorFunction: This is an array of function pointers, each pointer points to a function that is the treatment of the corresponding distraction function IRP, and the index value corresponds IRP_MJ_XXX array.

 

We already know that the data structure DriverEntry function of the head, but not enough, in DriverEntry, we mainly on driver initialization, which involves a number of other data structures, here we continue to introduce one by one.

 

Device Object (DEVICE_OBJECT)

typedef struct _DEVICE_OBJECT {
  CSHORT                      Type;
  USHORT                      Size;
  LONG                        ReferenceCount;
  struct _DRIVER_OBJECT  *DriverObject;
  struct _DEVICE_OBJECT  *NextDevice;
  struct _DEVICE_OBJECT  *AttachedDevice;
  struct _IRP  *CurrentIrp;
  PIO_TIMER                   Timer;
  ULONG                       Flags;
  ULONG                       Characteristics;
  __volatile PVPB             Vpb;
  PVOID                       DeviceExtension;
  DEVICE_TYPE                 DeviceType;
  CCHAR                       StackSize;
  union {
    LIST_ENTRY         ListEntry;
    WAIT_CONTEXT_BLOCK Wcb;
  } Queue;
  ULONG                       AlignmentRequirement;
  KDEVICE_QUEUE               DeviceQueue;
  KDPC                        Dpc;
  ULONG                       ActiveThreadCount;
  PSECURITY_DESCRIPTOR        SecurityDescriptor;
  KEVENT                      DeviceLock;
  USHORT                      SectorSize;
  USHORT                      Spare1;
  struct _DEVOBJ_EXTENSION  *  DeviceObjectExtension;
  PVOID                       Reserved;
} DEVICE_OBJECT, *PDEVICE_OBJECT;

Here only a few of the more important fields Description:

Ø DriverObject: pointing device driver object created this object. Belong to one object pointing device driver is driving the same object.

Ø NextObject: points to the next object created with a device driver. Objects can be created with a plurality of driving device objects, each device object together into a linked list according NextDevice, NextDevice last field device object to NULL.

Ø AttachedDevice: towards the nearest device object above the device attached to this object. We should understand the concept of layered drivers.

Ø DeviceExtension: extension object pointing devices. Each device is assigned a device extension object, the data structure itself is defined by the driver developer can be used to record some information related to the number of devices, while the use of global variables should be avoided, the expansion in the data stored in the device, having great flexibility.

Ø CurrentIrp: When using StartIO routine, the members of the IRP points to the current structure.

Ø Flags: Mark the specified device object. Listed below are a few commonly used markers:

flag value

meaning

DO_BUFFERED_IO

Mode read and write operations using a buffer (buffer system copy) mode, access user data

DO_EXCLUSIVE

Allow only one thread to open the device handle

DO_DIRECT_IO

Using direct mode read and write operations (memory descriptor table) user access mode data

DO_DEVICE_INITIALIZING

Device object is initialized

DO_POWER_PAGABLE

IRP_MJ_PNP requests must be processed at PASSIVE_LEVEL

 

Ø DeviceType: Specifies the type of device. Generally in the development of virtual appliance, select FILE_DEVICE_UNKNOW. Please refer to the other self-WDK documentation.

Ø StackSize: In the case of driving a multi-layer, similar to a stack structure formed between the drive and the drive, the device is called a stack. IRP will in turn pass from the top to the bottom. StackSize description is the number of layers. The bottom layers of the device 1.

Ø AlignmentRequirement: performing large-capacity transmission, often require memory alignment to ensure that the transmission speed. Please use similar FILE_ XXX _ALIGNMENT manner assignment.



Here to show you what the basic framework of DriverEntry :

#ifdef __cplusplus
extern "C"
{
#endif

#include <NTDDK.h>

#ifdef __cplusplus
};
#endif

#define PAGECODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_seg("INIT")

#define PAGEDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")


typedef struct _DEVICE_EXTENSION
{
	PDEVICE_OBJECT pDevice;
	UNICODE_STRING ustrDeviceName;
	UNICODE_STRING ustrSymLinkName;
} DEVICE_EXTENSION , *PDEVICE_EXTENSION;

//函数声明
NTSTATUS DispatchRoutine(__in struct _DEVICE_OBJECT  *DeviceObject, __in struct _IRP  *Irp);
VOID UnloadRoutine(__in struct _DRIVER_OBJECT  *DriverObject);

///////////////////////
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,
								PUNICODE_STRING pRegistryPath)
{
	NTSTATUS status;
	PDEVICE_EXTENSION pDevExt;
	PDEVICE_OBJECT pDevObj;
	KdPrint(("\n--------------------------------------!\n"));
	KdPrint(("Enter DriverEntry!\n"));
	//注册相关例程
	pDriverObject->DriverUnload = UnloadRoutine;
	pDriverObject->MajorFunction[IRP_MJ_CREATE]	=	DispatchRoutine;
	pDriverObject->MajorFunction[IRP_MJ_READ]	=	DispatchRoutine;
	pDriverObject->MajorFunction[IRP_MJ_WRITE]	=	DispatchRoutine;
	pDriverObject->MajorFunction[IRP_MJ_CLOSE]	=	DispatchRoutine;

	//初始化相关字符串
	UNICODE_STRING ustrDeviceName;	//设备名
	UNICODE_STRING ustrSymLinkName; //符号链接名

	RtlInitUnicodeString(&ustrDeviceName,L"\\Device\\MyDDKDevice1");
	RtlInitUnicodeString(&ustrSymLinkName,L"\\??\\MyDDKDriver1");

	//创建设备对象
	
	status = IoCreateDevice(pDriverObject,sizeof(DEVICE_EXTENSION),&ustrDeviceName,FILE_DEVICE_UNKNOWN,0,TRUE,&pDevObj);
	if (!NT_SUCCESS(status))
	{
		KdPrint(("Create Device Failure!\n"));
		return status;	
	}
	pDevObj->Flags |= DO_BUFFERED_IO;
	pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
	pDevExt->ustrDeviceName = ustrDeviceName;
	pDevExt->pDevice = pDevObj;

	//创建符号链接
	pDevExt->ustrSymLinkName = ustrSymLinkName;
	status = IoCreateSymbolicLink(&ustrSymLinkName,&ustrDeviceName);
	if (!NT_SUCCESS(status))
	{
		IoDeleteDevice(pDevObj);
		return status;
	}
	KdPrint(("Leave DriverEntry! stauts=%d",status));
	return status;
}

It is written in C ++, it is necessary to place added extern "C", otherwise it will cause some errors because C ++ and C carrying the name of crushing when handling out differently, C ++ This improvement is mainly to achieve some advanced features, such as polymorphism. While adding extern "C" will be a bit of trouble, but those can be used for C ++ functions, have also been personally feel that value. If you use C, simply ignore the above extern "C".

 

NTDDK.h NT drive is required to load the header file, if it is WDM drive, then the load is WDM.h

 

#define INITCODE code_seg ( "INIT") define a macro, after # prama INITCODE reduction is #pramacode_seg ( "INIT"), represents the next INIT code is loaded into memory area, after successfully loaded, you can exit the memory. For DriverEntry This one-time function, which is the most suitable choice, you can save memory. You need to explicitly switch back after the end of the function, such as: #prama LOCKEDCODE.

Similarly, PAGECODE represents paged memory, the role of this part of the code is paged into operation, the switching may be in exchange for the paging file in the context of the process inside the code. LOCKEDCODE indicates the default memory, in which non-paged memory, permanent memory inside the code. IRQL in DISPATCH_LEVEL or above grade, must be in non-paged memory inside.

Similarly, the data segments have the same mechanism, so with PAGEDATA, LOCKEDDATA, INITDATA.

 

KdPrint is a macro in the debug version of which (with DBG macro definition), there is

#define KdPrint(_x_) DbgPrint _x_

In the official version of which, KdPrint is defined as empty. It can be used as a debugging output. But note Kdprint followed by two brackets, printf usage and C language runtime almost.


pDriverObject-> DriverUnload = UnloadRoutine; the uninstall routine function tells the drive object, the object in front of the drive has been defined, do not do this in-depth discussion.


pDriverObject-> MajorFunction [IRP_MJ_CREATE] = DispatchRoutine; distraction registration routine. Windows is a message-driven, while the driver is driving the IRP, I / O manager sends to the drive "message" encapsulated inside the IRP, the driver can tell the results IRP. Similar windows message mechanism for the different "news" Drivers need to register different processing routines to be treated differently, of course, also be placed inside the same routine, and then use the switch statement to be treated differently, but when the process is treated a long time, would be more messy.

IRP_MJ_CREATE RING3 application when establishing communication channel with the driver using the CreateFile function activated. IRP_MJ_READ is ReadFile, IRP_MJ_WRITE is WriteFile, and CloseHandle to close the file handle IRP_MJ_CLOSE is generated when.

Tips: For WDM drive, still need to register AddDevice routine, pDriverObject-> DriverExtension-> = WDMAddDeviceRoutine AddDevice , initialize the device object will instead DriverEntry in AddDevice inside. Also you need to register IRP_MJ_PNP distraction function.

UNICODE_STRING mentioned in front of the structure, this can be a good understanding of these two structures initialized, and forget the look back to the previous, listed here only RtlInitUnicodeString function definition.

VOID RtlInitUnicodeString(PUNICODE_STRING  DestinationString,PCWSTR  SourceString);

IoCreateDevice registered device object, a driver must correspond to the one or more device objects.

NTSTATUS 
  IoCreateDevice(
    IN PDRIVER_OBJECT  DriverObject,
    IN ULONG  DeviceExtensionSize,
    IN PUNICODE_STRING  DeviceName  OPTIONAL,
    IN DEVICE_TYPE  DeviceType,
    IN ULONG  DeviceCharacteristics,
    IN BOOLEAN  Exclusive,
    OUT PDEVICE_OBJECT  *DeviceObject
    );

Ø DriverObject: hand drive object here is passed in the function entry driven. Each drive has a plurality of device objects, each device object is only a function of the drive.

Ø DeviceExtensionSize: custom device extension size.

Ø DeviceName: device object name in front of it a useful RtlInitUnicodeString been initialized. Device object is exposed on the kernel level name for RING3 layer desktop program is not visible. Format must be a: \ Device \ [device name]. If no device name, I / O manager automatically assigns a device number as the name, such as: \ Device \ 00000001

Ø DeviceType: device type, here FILE_DEVICE_UNKNOWN.

Ø DeviceCharacteristics: features of the device object.

Ø Exclusive: Set whether the object is a dedicated device. That is, whether to allow a second drive, to access the device object.

Ø DeviceObject: I / O Manager will be responsible for creating the device object, you can use this parameter to receive the address of an object.

Creating a device object, near the end of the program on the occasion of IoDeleteDevice need to remove the device object.

 

pDevObj-> Flags | = DO_BUFFERED_IO; access flag is set to buffer mode.

 

pDevExt = (PDEVICE_EXTENSION) pDevObj-> DeviceExtension; acquisition device expansion.

 

pDevExt-> ustrDeviceName = ustrDeviceName; the information stored in the device in an extension.

 

Device object name only exposed to the kernel level, you need to want to create a symbolic link in RING3 driver user access layer. A symbolic link is exposed to the user level. Functions registered a symbolic link is IoCreateSymbolicLink.

NTSTATUS 
  IoCreateSymbolicLink(
    IN PUNICODE_STRING  SymbolicLinkName,
    IN PUNICODE_STRING  DeviceName
    );

Ø SymbolicLinkName: string symbolic link with its previous initialization. Symbolic link names need to \ ?? \ beginning, or \ DosDevice \ (unconfirmed). The need to \\. \ Beginning to find the corresponding symbolic link in user mode.

Ø DeviceName: device name strings.

Note: Creating a symbolic link, you need to delete a symbolic link IoDeleteSymbolicLink at the end of the program drawing near. And delete symbolic links in to remove the device object.

 

In the above DriverEntry function inside, we have completed the basic initialization, then, we look at the unload callback routine.

 

Statement callback routine is:

VOID UnloadRoutine(__in struct _DRIVER_OBJECT  *DriverObject);

Instead of a function name, but do not change other parameters.

 

Inside the callback function, we mainly do some clean-up work.

#pragma PAGECODE
VOID UnloadRoutine(__in struct _DRIVER_OBJECT  *pDriverObject)
{
	PDEVICE_OBJECT pNextObj;
	PDEVICE_EXTENSION pDevExt;
	pNextObj = pDriverObject->DeviceObject;
	KdPrint(("Enter unload routine!\n"));
	while(pNextObj != NULL)
	{
		pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;

		//删除符号链接
		IoDeleteSymbolicLink(&pDevExt->ustrSymLinkName);
		pNextObj = pNextObj->NextDevice;
		//删除设备
		IoDeleteDevice(pDevExt->pDevice);
	}
	KdPrint(("Leave unload routine!\n"));
	KdPrint(("--------------------------------------!\n"));
}

Basic above it has been described. Here also emphasize that a driver can have one or more device objects, you need to delete the corresponding symbolic link, device object before the driver completely uninstall. So here uses a while loop to do the job, I do not understand go back to the familiar structure under the device object above.

 

The driver does not do any work, so the distraction function which we just simply set the state for the success of the operation is 0 bytes, is set to complete the IRP state, on the return.


#pragma PAGECODE
NTSTATUS DispatchRoutine(__in struct _DEVICE_OBJECT  *pDeviceObject,
						 __in struct _IRP  *pIrp)
{
	KdPrint(("Enter dispatch routine!\n"));
	NTSTATUS status = STATUS_SUCCESS;
	//完成IRP
	pIrp->IoStatus.Status = status;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest(pIrp,IO_NO_INCREMENT);
	KdPrint(("Leave dispatch routine!\n"));
	return status;
}

pIrp-> IoStatus.Status = status; IO setting state.

pIrp-> IoStatus.Information = 0; Set number of bytes actually operated. User layer function ReadFile, WriteFile fourth parameter lpNumberOfBytesRead actual number of bytes received for operation, the result is produced.

IoCompleteRequest set to complete the IRP process, otherwise it will continue to pass down layer.

 

A basic introduction to the whole framework are finished, paste the following code to complete it.

#ifdef __cplusplus
extern "C"
{
#endif

#include <NTDDK.h>

#ifdef __cplusplus
};
#endif

#define PAGECODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_seg("INIT")

#define PAGEDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")

#define arrarysize(arr) (sizeof(arr)/sizeof(arr)[0])

typedef struct _DEVICE_EXTENSION
{
	PDEVICE_OBJECT pDevice;
	UNICODE_STRING ustrDeviceName;
	UNICODE_STRING ustrSymLinkName;
} DEVICE_EXTENSION , *PDEVICE_EXTENSION;


//函数声明
NTSTATUS DispatchRoutine(__in struct _DEVICE_OBJECT  *DeviceObject, __in struct _IRP  *Irp);
VOID UnloadRoutine(__in struct _DRIVER_OBJECT  *DriverObject);
	  

///////////////////////
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,
								PUNICODE_STRING pRegistryPath)
{
	NTSTATUS status;
	PDEVICE_EXTENSION pDevExt;
	PDEVICE_OBJECT pDevObj;
	KdPrint(("\n--------------------------------------!\n"));
	KdPrint(("pRegistryPath value:%ws",pRegistryPath));
	KdPrint(("Enter DriverEntry!\n"));
	//注册相关例程
	pDriverObject->DriverUnload = UnloadRoutine;
	pDriverObject->MajorFunction[IRP_MJ_CREATE]	=	DispatchRoutine;
	pDriverObject->MajorFunction[IRP_MJ_READ]	=	DispatchRoutine;
	pDriverObject->MajorFunction[IRP_MJ_WRITE]	=	DispatchRoutine;
	pDriverObject->MajorFunction[IRP_MJ_CLOSE]	=	DispatchRoutine;

	//初始化相关字符串
	UNICODE_STRING ustrDeviceName;	//设备名
	UNICODE_STRING ustrSymLinkName; //符号链接名

	RtlInitUnicodeString(&ustrDeviceName,L"\\Device\\MyDDKDevice1");
	RtlInitUnicodeString(&ustrSymLinkName,L"\\??\\MyDDKDriver1");

	//创建设备对象
	
	status = IoCreateDevice(pDriverObject,sizeof(DEVICE_EXTENSION),&ustrDeviceName,FILE_DEVICE_UNKNOWN,0,TRUE,&pDevObj);
	if (!NT_SUCCESS(status))
	{
		KdPrint(("Create Device Failure!\n"));
		return status;	
	}
	pDevObj->Flags |= DO_BUFFERED_IO;
	pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
	pDevExt->ustrDeviceName = ustrDeviceName;
	pDevExt->pDevice = pDevObj;

	//创建符号链接
	pDevExt->ustrSymLinkName = ustrSymLinkName;
	status = IoCreateSymbolicLink(&ustrSymLinkName,&ustrDeviceName);
	if (!NT_SUCCESS(status))
	{
		IoDeleteDevice(pDevObj);
		return status;
	}
	KdPrint(("Leave DriverEntry! stauts=%d",status));
	return status;
}
#pragma PAGECODE
NTSTATUS DispatchRoutine(__in struct _DEVICE_OBJECT  *pDeviceObject,
						 __in struct _IRP  *pIrp)
{
	KdPrint(("Enter dispatch routine!\n"));
	NTSTATUS status = STATUS_SUCCESS;
	//完成IRP
	pIrp->IoStatus.Status = status;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest(pIrp,IO_NO_INCREMENT);
	KdPrint(("Leave dispatch routine!\n"));
	return status;
	
}

#pragma PAGECODE
VOID UnloadRoutine(__in struct _DRIVER_OBJECT  *pDriverObject)
{
	PDEVICE_OBJECT pNextObj;
	PDEVICE_EXTENSION pDevExt;
	pNextObj = pDriverObject->DeviceObject;
	KdPrint(("Enter unload routine!\n"));
	while(pNextObj != NULL)
	{
		pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;

		//删除符号链接
		IoDeleteSymbolicLink(&pDevExt->ustrSymLinkName);
		pNextObj = pNextObj->NextDevice;
		//删除设备
		IoDeleteDevice(pDevExt->pDevice);
	}
	KdPrint(("Leave unload routine!\n"));
	KdPrint(("--------------------------------------!\n"));
}

Reproduced in: https: //my.oschina.net/iwuyang/blog/198615

Guess you like

Origin blog.csdn.net/weixin_33994444/article/details/91897352