标签:des style blog http color os 使用 io strong
一、过滤的概念:
过滤是在不影响上层和下层接口的情况下,在Windows系统内核中加入新的层,从而不需要修改上层的软件或者下层的真是驱动程序,就加入了新的功能。
1.1 设备绑定的内核API
进行过滤的最主要的方法是对一个设备对象(Device Object)进行绑定。通过编程可以生成一个虚拟设备对象,并“绑定”(Attach)在一个真实的设备上。一旦绑定,则本来操作系统发送给真实设备的请求,就会首先发送到这个虚拟设备。
在WDK中,有多个内核API能实现绑定的功能。以下三个绑定API是从WDK帮助文档中获得的,需要进一步了解可以查看帮助文档。
(1)IoAttachDevice
The IoAttachDevice routine attaches the caller‘s device object to a named target device object, so that I/O requests bound for the target device are routed first to the caller.
NTSTATUS IoAttachDevice( IN PDEVICE_OBJECT SourceDevice, IN PUNICODE_STRING TargetDevice, OUT PDEVICE_OBJECT *AttachedDevice );
Parameters
PDEVICE_OBJECT
IoAttachDeviceToDeviceStack(
IN PDEVICE_OBJECT SourceDevice,
IN PDEVICE_OBJECT TargetDevice
);
Parameters
IoAttachDeviceToDeviceStack returns a pointer to the device object to which the SourceDevice was attached. The returned device object pointer can differ from TargetDevice if TargetDevice had additional drivers layered on top of it.
IoAttachDeviceToDeviceStack returns NULL if it could not attach the device object because, for example, the target device was being unloaded.
(3)IoAttachDeviceToDeviceStackSafe
The IoAttachDeviceToDeviceStackSafe routine attaches the caller‘s device object to the topmost device object in a driver stack.
NTSTATUS IoAttachDeviceToDeviceStackSafe( IN PDEVICE_OBJECT SourceDevice, IN PDEVICE_OBJECT TargetDevice, IN OUT PDEVICE_OBJECT *AttachedToDeviceObject );
IoAttachDeviceToDeviceStackSafe returns STATUS_SUCCESS if SourceDevice is successfully attached above the TargetDevice; otherwise it returns STATUS_NO_SUCH_DEVICE.
1.2 生成过滤设备并绑定
在绑定一个设备之前,先要知道如何生成一个用于过滤的过滤设备。函数IoCreateDevice被用于生成设备。函数的参数可以占时根据源码中的进行设置,需要进一步了解可以查看WDK帮助文档。此外还需要注意的就是,在绑定一个设备之前,应该把这个设备对象的多个子域设置成和要绑定的目标对象一致,包括标志和特征。实现代码参见下文源码中的ccpAttachDevice函数。
IoCreateDevice 定义,来自WDK帮助文档:
Call IoCreateDevice to create a filter device object to attach to a volume or file system stack. In the FileSpy sample, this is done as follows:
status = IoCreateDevice(
gFileSpyDriverObject, //DriverObject
sizeof(FILESPY_DEVICE_EXTENSION), //DeviceExtensionSize
NULL, //DeviceName
DeviceObject->DeviceType, //DeviceType
0, //DeviceCharacteristics
FALSE, //Exclusive
&newDeviceObject); //DeviceObject
In the above code snippet, DeviceObject is a pointer to the target device object to which the filter device object will be attached; newDeviceObject is a pointer to the filter device object itself.
Setting the DeviceExtensionSize parameter to sizeof(FILESPY_DEVICE_EXTENSION) causes a FILESPY_DEVICE_EXTENSION structure to be allocated for the filter device object. The newly created filter device object‘s DeviceExtension member is set to point to this structure. File system filter drivers usually define and allocate memory for a device extension for each filter device object. The structure and contents of the device extension are driver-specific. However, on Microsoft Windows XP and later, filter drivers should define a DEVICE_EXTENSION structure for filter driver objects that contains at least the following member:
PDEVICE_OBJECT AttachedToDeviceObject;
In the above call to IoCreateDevice, setting the DeviceName parameter to NULL specifies that the filter device object will not be named. Filter device objects are never named. Because a filter device object is attached to a file system or volume driver stack, assigning a name to the filter device object would create a system security hole.
The DeviceType parameter must always be set to the same device type as that of the target (file system or filter) device object to which the filter device object is being attached. It is important to propagate the device type in this way, because it is used by the I/O Manager and can be reported back to applications.
Note File systems and file system filter drivers should never set the DeviceType parameter to FILE_DEVICE_FILE_SYSTEM. This is not a valid value for this parameter. (The FILE_DEVICE_FILE_SYSTEM constant is intended only for use in defining FSCTL codes.)
Another reason why the DeviceType parameter is important is that many filters attach only to certain types of file systems. For example, a particular filter may attach to all local disk file systems, but not to CD-ROM file systems or remote file systems. Such filters determine the type of file system by examining the device type of the topmost device object in the file system or volume driver stack. In most cases, the topmost device object in the stack is a filter device object. Thus it is essential that all attached filter device objects have the same device type as that of the underlying file system or volume device object.
1.3 从名字获得设备对象
在知道一个设备名字的情况下,使用函数IoGetDeviceObjectPointer可以获得这个设备对象的指针。必须注意的是:在使用这个函数之后必须把这个文件对象“解除引用”,否则会引起内存泄露(请注意实现的代码)。运用此函数实现下文源码中的ccpOpenCom函数用于打开一个端口设备。
IoGetDeviceObjectPointer:
The IoGetDeviceObjectPointer routine returns a pointer to the top object in the named device object‘s stack and a pointer to the corresponding file object, if the requested access to the objects can be granted.
NTSTATUS
IoGetDeviceObjectPointer(
IN PUNICODE_STRING ObjectName,
IN ACCESS_MASK DesiredAccess,
OUT PFILE_OBJECT *FileObject,
OUT PDEVICE_OBJECT *DeviceObject
);
#include <ntddk.h> #define NTSTRSAFE_LIB #include <ntstrsafe.h> #ifndef SetFlag #define SetFlag(_F,_SF) ((_F) |= (_SF)) #endif #ifndef ClearFlag #define ClearFlag(_F,_SF) ((_F) &= ~(_SF)) #endif #define CCP_MAX_COM_ID 32 // 过滤设备和真实设备 static PDEVICE_OBJECT s_fltobj[CCP_MAX_COM_ID] = { 0 }; static PDEVICE_OBJECT s_nextobj[CCP_MAX_COM_ID] = { 0 }; // 打开一个端口设备 PDEVICE_OBJECT ccpOpenCom(ULONG id,NTSTATUS *status) { UNICODE_STRING name_str; static WCHAR name[32] = { 0 }; PFILE_OBJECT fileobj = NULL; PDEVICE_OBJECT devobj = NULL; // 输入字符串。 memset(name,0,sizeof(WCHAR)*32); RtlStringCchPrintfW( name,32, L"\\Device\\Serial%d",id); RtlInitUnicodeString(&name_str,name); // 打开设备对象 *status = IoGetDeviceObjectPointer(&name_str, FILE_ALL_ACCESS, &fileobj, &devobj); if (*status == STATUS_SUCCESS) ObDereferenceObject(fileobj); return devobj; } //生成并绑定一个设备 NTSTATUS ccpAttachDevice( PDRIVER_OBJECT driver, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT *fltobj, PDEVICE_OBJECT *next) { NTSTATUS status; PDEVICE_OBJECT topdev = NULL; // 生成设备,然后绑定之。 status = IoCreateDevice(driver, 0, NULL, oldobj->DeviceType, 0, FALSE, fltobj); if (status != STATUS_SUCCESS) return status; // 拷贝重要标志位。 if(oldobj->Flags & DO_BUFFERED_IO) (*fltobj)->Flags |= DO_BUFFERED_IO; if(oldobj->Flags & DO_DIRECT_IO) (*fltobj)->Flags |= DO_DIRECT_IO; if(oldobj->Flags & DO_BUFFERED_IO) (*fltobj)->Flags |= DO_BUFFERED_IO; if(oldobj->Characteristics & FILE_DEVICE_SECURE_OPEN) (*fltobj)->Characteristics |= FILE_DEVICE_SECURE_OPEN; (*fltobj)->Flags |= DO_POWER_PAGABLE; // 绑定一个设备到另一个设备上 topdev = IoAttachDeviceToDeviceStack(*fltobj,oldobj); if (topdev == NULL) { // 如果绑定失败了,销毁设备,重新来过。 IoDeleteDevice(*fltobj); *fltobj = NULL; status = STATUS_UNSUCCESSFUL; return status; } *next = topdev; // 设置这个设备已经启动。 (*fltobj)->Flags = (*fltobj)->Flags & ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; } // 这个函数绑定所有的串口。 void ccpAttachAllComs(PDRIVER_OBJECT driver) { ULONG i; PDEVICE_OBJECT com_ob; NTSTATUS status; for(i = 0;i<CCP_MAX_COM_ID;i++) { // 获得object引用。 com_ob = ccpOpenCom(i,&status); if(com_ob == NULL) continue; // 在这里绑定。并不管绑定是否成功。 ccpAttachDevice(driver,com_ob,&s_fltobj[i],&s_nextobj[i]); // 取消object引用。 } } #define DELAY_ONE_MICROSECOND (-10) #define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000) #define DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000) //动态卸载函数 void ccpUnload(PDRIVER_OBJECT drv) { ULONG i; LARGE_INTEGER interval; // 首先解除绑定 for(i=0;i<CCP_MAX_COM_ID;i++) { if(s_nextobj[i] != NULL) IoDetachDevice(s_nextobj[i]); } // 睡眠5秒。等待所有irp处理结束 interval.QuadPart = (5*1000 * DELAY_ONE_MILLISECOND); KeDelayExecutionThread(KernelMode,FALSE,&interval); // 删除这些设备 for(i=0;i<CCP_MAX_COM_ID;i++) { if(s_fltobj[i] != NULL) IoDeleteDevice(s_fltobj[i]); } } //分发函数 NTSTATUS ccpDispatch(PDEVICE_OBJECT device,PIRP irp) { PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp); NTSTATUS status; ULONG i,j; // 首先得知道发送给了哪个设备。设备一共最多CCP_MAX_COM_ID // 个,是前面的代码保存好的,都在s_fltobj中。 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 PoCallDriver(s_nextobj[i],irp); } // 此外我们只过滤写请求。写请求的话,获得缓冲区以及其长度。 // 然后打印一下。 if(irpsp->MajorFunction == IRP_MJ_WRITE) { // 如果是写,先获得长度 ULONG len = irpsp->Parameters.Write.Length; // 然后获得缓冲区 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) { DbgPrint("comcap: Send Data: %2x\r\n", buf[j]); } } // 这些请求直接下发执行即可。我们并不禁止或者改变它。 IoSkipCurrentIrpStackLocation(irp); return IoCallDriver(s_nextobj[i],irp); } } // 如果根本就不在被绑定的设备中,那是有问题的,直接返回参数错误。 irp->IoStatus.Information = 0; irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(irp,IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) { size_t i; // 所有的分发函数都设置成一样的。 for(i=0;i<IRP_MJ_MAXIMUM_FUNCTION;i++) { driver->MajorFunction[i] = ccpDispatch; } // 支持动态卸载。 driver->DriverUnload = ccpUnload; // 绑定所有的串口。 ccpAttachAllComs(driver); // 直接返回成功即可。 return STATUS_SUCCESS; }
!IF 0 Copyright (C) Microsoft Corporation, 1997 - 1999 Module Name: sources. !ENDIF TARGETNAME=comcap TARGETPATH=obj TARGETTYPE=DRIVER SOURCES =comcap.c TARGETLIBS= $(DDK_LIB_PATH)\ntstrsafe.lib
标签:des style blog http color os 使用 io strong
原文地址:http://www.cnblogs.com/fanling999/p/3943926.html