After reading the book "Hanjiang standalone fishing-Windows Kernel security programming", I wrote
# Include <ntddk. h>
# Include <ntstrsafe. h>
# Define ntstrsafe_lib
# Define ccp_max_com_id 32 // set the maximum number of COM ports
// Save all filters
Static pdevice_object s_fltobj [ccp_max_com_id] = {0 };
Static pdevice_object s_nextobj [ccp_max_com_id] = {0 };
// Parameter definitions related to device detachment
# Define delay_one_microsecond (-10)
# Define delay_one_millisecond (delay_one_microsecond * 1000)
# Define delay_one_second (delay_one_mililisecond * 1000)
// Generate a virtual device and bind it with the real device
Ntstatus ccpattachdevice (pdriver_object driver, pdevice_object oldobj, pdevice_object * fltobj, pdevice_object * Next)
{
Ntstatus status;
Pdevice_object topdev = NULL;
// Fltobj is a newly generated device and then bound
Status = iocreatedevice (driver, 0, null, oldobj-> devicetype, 0, false, fltobj );
If (status! = STATUS_SUCCESS)
{
Return status;
}
// Copy the important flag. This step is very important, that is, the generated virtual device flag is the same as the parameter option of the bound real device.
If (oldobj-> flags & do_buffered_io)
{
(* Fltobj)-> flags | = do_buffered_io;
}
If (oldobj-> flags & do_direct_io)
{
(* Fltobj)-> flags | = do_direct_io;
}
If (oldobj-> Characteristics & file_device_secure_open)
{
(* Fltobj)-> characteristics | = file_device_secure_open;
}
(* Fltobj)-> flags | = do_power_pagable;
// Bind the virtual device fltobj generated above to the oldobj on another device
Topdev = ioattachdevicetodevicestack (* fltobj, oldobj );
If (topdev = NULL)
{
// If the binding fails and the device is destroyed, an error is returned.
Iodeletedevice (* fltobj );
* Fltobj = NULL;
Status = status_unsuccessful;
Return status;
}
* Next = topdev;
// Set the device to be started
(* Fltobj)-> flags = (* fltobj)-> flags &~ Do_device_initializing;
Return STATUS_SUCCESS;
}
// Open a port device
Pdevice_object ccpopencom (ulong ID, ntstatus * Status)
{
// Enter the serial port ID, which will be changed to the string format.
Unicode_string name_str;
Static wchar name [32] = {0 };
Pfile_object fileobj = NULL;
Pdevice_object required bj = NULL;
// Convert string names by ID
Memset (name, 0, sizeof (wchar) * 32 );
Rtlstringcchprintfw (name, 32, l "\ device \ serial % d", ID );
Rtlinitunicodestring (& name_str, name );
// Open the device object
* Status = iogetdeviceobjectpointer (& name_str, file_all_access, & fileobj, & devobj );
// If the file is successfully opened, remember to unreference the file object.
If (* status = STATUS_SUCCESS)
{
Obdereferenceobject (fileobj );
}
// Return the device object
Return response BJ;
}
// This function is bound to all serial ports.
Void ccpattachallcoms (pdriver_object driver)
{
Ulong I = 0;
Pdevice_object com_ob = NULL;
Ntstatus status;
For (I = 0; I <ccp_max_com_id; I ++)
{
// Get Object Reference
Com_ob = ccpopencom (I, & status );
If (com_ob = NULL)
{
Continue;
}
// Bind it here, regardless of whether the binding is successful
Ccpattachdevice (driver, com_ob, & s_fltobj [I], & s_nextobj [I]);
}
}
Ntstatus ccpdispatch (pdevice_object device, pirp)
{
Pio_stack_location irpsp = iogetcurrentirpstacklocation (IRP );
Ntstatus status;
Ulong I = 0;
Ulong J = 0;
// First, you need to know the device to which the message is sent. A maximum of ccp_max_com_id devices are sent to. All the above codes are saved in s_fltobj.
For (I = 0; I <ccp_max_com_id; I ++)
{
If (s_fltobj [I] = device)
{
// All power supply operations are performed directly through
If (irpsp-> majorfunction = irp_mj_power)
{
// Send the message directly, and then return the message indicating that the message has been processed.
Postartnextpowerirp (IRP );
Ioskipcurrentirpstacklocation (IRP );
Return pocalldriver (s_nextobj [I], IRP );
}
// In addition, we only filter write requests, write requests, obtain the buffer zone and its length, and then print
If (irpsp-> majorfunction = irp_mj_write)
{
// If it is a write operation, obtain the length first.
Ulong Len = irpsp-> parameters. Write. length;
// Then obtain the 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;
}
// Print the content
For (j = 0; j <Len; j ++)
{
Dbuplint ("comcap: send data: % 02x \ r \ n", Buf [J]);
}
}
// These requests can be directly issued for execution. We do not prohibit or change them.
Ioskipcurrentirpstacklocation (IRP );
Return iocalldriver (s_nextobj [I], IRP );
}
}
// If it is not in the bound device at all, it is a problem and a parameter error is returned directly.
IRP-> iostatus. Information = 0;
IRP-> iostatus. Status = status_invalid_parameter;
Iocompleterequest (IRP, io_no_increment );
Return STATUS_SUCCESS;
}
Void ccpunload (pdriver_object DRV)
{
Ulong I = 0;
Large_integer interval;
// Unbind
For (I = 0; I <ccp_max_com_id; I ++)
{
If (s_nextobj [I]! = NULL)
{
Iodetachdevice (s_nextobj [I]);
}
// Sleep for 5 seconds and wait until all IRP processing ends
Interval. quadpart = (5*1000 * delay_one_millisecond );
Kedelayexecutionthread (kernelmode, false, & interval );
// Delete these devices
For (I = 0; I <ccp_max_com_id; I ++)
{
If (s_fltobj [I]! = NULL)
{
Iodeletedevice (s_fltobj [I]);
}
}
}
}
Ntstatus DriverEntry (pdriver_object driver, punicode_string reg_path)
{
Size_t I = 0;
// All the distribution functions are set to the same
For (I = 0; I <irp_mj_maximum_function; I ++)
{
Driver-> majorfunction [I] = ccpdispatch;
}
// Supports dynamic uninstallation
Driver-> driverunload = ccpunload;
// Bind all serial ports
Ccpattachallcoms (driver );
// Directly return success
Return STATUS_SUCCESS;
}