Having studied kernel programming for several months, we now have a more in-depth understanding of Windows Driver development, especially the layered processing logic of the IRP.
Sum up just a few words:
When the IRP comes down, you have to deal with it according to the actual situation.
1> no treatment, continue to pass down
2> after processing, go down
3> after processing, go to upload
4> do not do the processing, directly discarded
Specifically how to understand, through a serial port to drive filtering can be deeply understood.
First, the concept of serial port filtering
Serial Port filtering: Usually we see the host on the USB, network cable port, etc. belong to the serial port, then imagine an environment, I went to internet cafes, is through a treasure payment, and then the host behind the USB plug in a serial monitor, my data are acquired, and then my money brushes brush no ... What a sad thing, so, a serial port filtering, can not affect the entire serial port communication function, intercept the user layer sent out of the information, this for serial data monitoring and security has a great role.
Second, serial port filtering ideas
The first is the principle of the drive filter:
Filter is an extremely important concept. Filtering involves adding a new layer to the Windows system kernel without affecting the upper and lower interfaces, so that new functionality is added without modifying the upper-level software or the underlying real-world drivers.
It can also be said that a virtual device attach on a real physical device, as long as the message packet sent to the direction of the real physical device, the priority to enter the virtual device, and the virtual device we are bound to the real device, so the priority to get the message packet, and the content of the message packet parsing, filtering, The filter here can be said to be a tens of thousands of IRP for the if selection, to get the necessary corresponding device IRP, thus, for analysis and processing.
To deepen our understanding, I have made a brief summary of the logical thinking:
1> first registers the dispatch function of the IRP (DISPATHC Funtion), where the function content does the IRP filtering operation, extracting the buffer, the next IRP, etc.
2> Open the real serial device to get an object pointer to the serial device, for the next real virtual binding
3> Create a virtual serial filtering device (IoCreateDevice) based on the actual serial device.
4> the virtual serial device attach on the physical device
5> Perfect Dispatch function: To filter the Irp_mj_write, complete the filtering operation, the various IRP to do the relevant distribution processing
In short: opencom-----iocreatefilterdevice-----Attachto-----irp_mj_fuction
Third, the Code test
First, the kernel API
1. Binding Device API
Ntstatusioattachdevicetodevicestacksafe (in Pdevice_object sourcedevice, //filtering device in Pdevice_object TargetDevice, //devices in the device stack to be bound in the out Pdevice_object *attachedtodeviceobject//return the device that is eventually bound);
Sourcedevice is the virtual device that the caller generates to filter, and the TargetDevice is the target device to be bound. Note that the targetdevice here is not a pdevice_object (Device_object is the data structure of the device object, whose pointer begins with P), but rather a string (in the driver development, the string is represented by the unicode_string). In fact, this string is the name of the device to be bound. Many device objects in Windows have names, but not all device objects have names. Must be a name.
Device to bind with this kernel API. In Windows, the serial device has a fixed name. Here's a question: Suppose this function binds a device to a name, what if the device is already bound by another device? If a device is bound by another device, the set of devices that they are together, called the device stack, is referred to as the stack, and is due to the way the request was delivered.
in fact, Ioattachdevice always binds to the topmost device on the device stack.
2. Create a virtual device
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);
This function looks complicated, but there is no need to know too much when it is used. DriverObject is the driver of this drive. This pointer is provided by the system, passed in from the DriverEntry, and explained in the final complete example. Deviceextensionsize is a device extension, readers should simply pass in 0. DeviceName is the device name. One rule is that filtering devices do not normally require a name, so passing in null. The DeviceType is a device type that remains the same as the type of device being bound. DeviceCharacteristics is a device feature, in the generation of device objects, I always fill in the experience directly 0, and then see whether the exclusion, choose False.
It is important to note that before binding a device,
multiple subdomains of the device object should be set sing Woo the target object to be bound, including flags and features . The following is an example of a function that can generate a device and then bind to another device.
3. Binding Device API
First, get pointers to real device objects
NTSTATUS Iogetdeviceobjectpointer (in punicode_string objectname,in access_mask desiredaccess,out PFILE_ OBJECT *fileobject,out pdevice_object *deviceobject);
then bind the serial port and return the device after the binding. For example, Topdev actually refers to the whole serial port after the binding, can say a new device object, through the operation of this device object, you can complete the operation of the filter device
Topdev = IoAttachDeviceToDeviceStack (*fltobj, Olddev);
4. Processing IRP, getting write buffer contents
Here IRPSP called the stack space of the IRP, iogetcurrentirpstacklocation get the current stack space//stack space is very important data structure pio_stack_location IRPSP = Iogetcurrentirpstacklocation (IRP); if (irpsp->majorfunction = = Irp_mj_write) {//If it is write ...} else if (irpsp->majorfunction = = Irp_mj_read) {//If it is read ...
Get to the current stack, which is the stack space of the IRP, and then through, irp->majorfuction = = Irp_mj_write to filter out the write action, also can filter to read action, because this IRP is from the current IRP, And this IRP in the physical layer is the serial data transmission, and our driver is just bound to the real physical serial port device, so, can intercept this kind of IRP, that is, although many IRP, but we through the binding device to complete the targeted IRP interception, so as to achieve the filtering effect.
5. Request complete
The final outcome is 3 kinds:
(1) The request was allowed to pass. Filter not to do anything, or simply to get some information for the request. But the request itself is undisturbed so that the system behavior does not change.
(2) The request was rejected directly. Filtering prevents this request from being passed, the request is returned incorrectly, and the underlying driver does not receive the request at all. This changes the system behavior, The result is often see the upper application pop-up error box prompt permission error or read file failure information.
(3) filtering has completed this request. Sometimes there is a need, like a read request, that we want to record what we read. If the read request has not yet been completed, how do you know what to read at the end? Only make this request complete before you record it. Filtering to complete this request does not have to be done intact, the parameters of the request can be modified (such as the data are encrypted).
Once the request is complete, here's what the IRP is dealing with: There are four kinds, namely
1> no treatment, continue to pass down
2> after processing, go down
3> after processing, go to upload
4> do not do the processing, directly discarded
Such treatment, without any processing, goes straight down:
POSTARTNEXTPOWERIRP (IRP); Ioskipcurrentirpstacklocation (IRP); return Pocalldriver (S_NEXTOBJ[I],IRP);
after the IRP is complete, direct the
Ioskipcurrentirpstacklocation (IRP); return IoCallDriver (S_NEXTOBJ[I],IRP);
Iv. Module Code
Drive entry:
#include "Driver.h" #include <ntstrsafe.h>/////////////////////////////////////////////////// Function name: driverentry//: Drive Entry// geons// time: April 14, 2016 22:02:54///////////////////////////////////////// extern "C" NTSTATUS driverentry (in Pdriver_object driverobj,in punicode_string registrypath) {size_t i;// Register the Dispatch function for (i = 0;i<irp_mj_maximum_function;i++) {Driverobj->majorfunction[i] = Ccpdispatch;} Unload driver Driverobj->driverunload = ccpunload;//Bind physical device port ccpattachallocoms (driverobj); return status_success;}
Dispatch function:
Distribution function Ntstatus Ccpdispatch (pdevice_object device, pirp IRP) {pio_stack_location IRPSP = iogetcurrentirpstacklocation ( IRP); NTSTATUS status; ULONG I, j;for (i = 0; i< ccp_max_com_id;i++) {if (s_fltobj[i] = = device) {if (irpsp->majorfunction = = Irp_mj_power) { POSTARTNEXTPOWERIRP (IRP); Ioskipcurrentirpstacklocation (IRP); return IoCallDriver (S_nextobj[i], IRP);} if (irpsp->majorfunction = = Irp_mj_write) {//write request distribution processing//Get write Request text length ulong Len = irpsp->parameters.write.length;// Open Write buffer Puchar buf = null;if (irp->mdladdress! = NULL) {buf = (Puchar) mmgetsystemaddressformdlsafe (irp->mdladdress, normalpagepriority);} Else{buf = (Puchar) Irp->userbuffer;} if (buf = = NULL) {buf = (Puchar) Irp->associatedirp.systembuffer;} for (j = 0; j<len;j++) {Kdprint (("Comcap:send Data:%2x\r\n", buf[j]));}} Issued Irpioskipcurrentirpstacklocation (IRP); return IoCallDriver (S_nextobj[i], IRP);}} Irp->iostatus.information = 0;irp->iostatus.status = Status_invalid_parameter;iocompleterequest (IRP, IO_NO_ INCREMENT);// Return IRP success status return STATUS_SUCCESS;}
Drive Offload:
Drive unload void Ccpunload (Pdriver_object drv) {ULONG i; Large_integer interval;for (i = 0; i<ccp_max_com_id;i++) {if (S_nextobj[i]! = NULL) {iodeletedevice (s_nextobj[i]);}} Interval. QuadPart = (5*1000*delay_one_millisecond); Kedelayexecutionthread (KernelMode, FALSE, &interval); for (int i =0; i<ccp_max_com_id; i++) {if (s_nextobj[i]! = NULL) {Iodeletedevice (s_fltobj[i]);}}}
Creating a filtering device
Create a filter device, and the parameters of the virtual device and the real device remain consistent ntstatus Ccpattachdevice (Pdriver_object driverobj, Pdevice_object olddev, Pdevice_object * Fltobj, Pdevice_object *next) {NTSTATUS status; Pdevice_object Topdev = Null;status = IoCreateDevice (Driverobj, 0,null,olddev->devicetype,0,false,fltobj); if ( Status! = Status_success) {return status;} if (Olddev->flags & Do_buffered_io) {(*fltobj)->flags |= do_buffered_io;} else if (Olddev->flags & Do_direct_io) {(*fltobj)->flags |= do_direct_io;} else if (Olddev->characteristics & File_device_secure_open) {(*fltobj)->characteristics |= FILE_DEVICE_ Secure_open;} else{} (*fltobj)->flags |= Do_power_pagable;topdev = IoAttachDeviceToDeviceStack (*fltobj, OldDev); if (TopDev = = NULL {Iodeletedevice (*fltobj); *fltobj = Null;status = Status_success;return status;} *next = topdev;//Setup device has started (*fltobj)->flags = (*fltobj)->flags & ~do_device_initializing;return STATUS_ SUCCESS;}
Open serial port, get the serial object pointer
Open serial port Pdevice_object ccpopencom (ULONG ID, NTSTATUS *status) {//external input is serial port idunicode_string name_str;static WCHAR NAME[32] = {0}; Pfile_object fileobj = NULL; Pdevice_object devobj = null;memset (name, 0, sizeof (WCHAR) *32); Rtlinitunicodestring (&name_str, L "\\Device\\Serial0"); Kdprint (("The test serial is%w", name_str));//Open Device Object *status = Iogetdeviceobjectpointer (&name_str, file_all_access , &fileobj, &devobj);//After opening the file successfully, de-reference the File object if (*status = = status_success) {obdereferenceobject (fileobj);} return devobj;}
Binding serial port
Bind existing serial port void Ccpattachallocoms (Pdriver_object driverobj) {ULONG i; Pdevice_object Com_obj; NTSTATUS status;for (i =0; i<dpfltr_tcpip_id;i++) {com_obj = ccpopencom (i, &status); if (com_obj = = NULL) {continue;} Ccpattachdevice (Driverobj, Com_obj, &s_fltobj[i], &s_nextobj[i]);}}
Test:
Windows kernel Development serial filtering