设备绑定的内核API
进行过滤的最主要方法是对一个设备对象(Device Object)进行绑定
通过编程可以生成一个虚拟的设备对象,并绑定在一个真实的设备上,一旦绑定,则本来操作系统发送给真实设备的请求,会首先发送到这个虚拟设备上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| NTSTATUS IoAttachDevice( _In_ PDEVICE_OBJECT SourceDevice, _In_ PUNICODE_STRING TargetDevice, _Out_ PDEVICE_OBJECT *AttachedDevice );
NTSTATUS IoAttachDeviceToDeviceStackSafe( _In_ PDEVICE_OBJECT SourceDevice, _In_ PDEVICE_OBJECT TargetDevice, _Out_ PDEVICE_OBJECT *AttachedToDeviceObject );
PDEVICE_OBJECT IoAttachDeviceToDeviceStack( _In_ PDEVICE_OBJECT SourceDevice, _In_ PDEVICE_OBJECT TargetDevice );
|
进行设备绑定时,总是会绑定位于设备栈顶层的设备
生成过滤设备并绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| NTSTATUS IoCreateDevice( _In_ PDRIVER_OBJECT DriverObject, _In_ ULONG DeviceExtensionSize, _In_opt_ PUNICODE_STRING DeviceName, _In_ DEVICE_TYPE DeviceType, _In_ ULONG DeviceCharacteristics, _In_ BOOLEAN Exclusive, _Out_ PDEVICE_OBJECT *DeviceObject );
NTSTATUS ToAttachDevice( PDRIVER_OBJECT driver, PDRIVER_OBJECT oldobj, PDRIVER_OBJECT *fltobj, PDRIVER_OBJECT *next) { NTSTATUS status; PDRIVER_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; } if (topdev == NULL) { IoDeleteDevice(*fltobj); *fltobj = NULL; status = STATUS_UNSUCCESSFUL; return status; } *next = topdev;
(*fltobj)->Flags = (*fltobj)->Flags & ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; }
|
从名字获得设备对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| NTSTATUS IoGetDeviceObjectPointer( _In_ PUNICODE_STRING ObjectName, _In_ ACCESS_MASK DesiredAccess, _Out_ PFILE_OBJECT *FileObject, _Out_ PDEVICE_OBJECT *DeviceObject );
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; }
|
成功打开设备对象后必须调用ObDereferenceObject解除引用
绑定所有串口
串口不会出现插拔的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #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 };
void ccpAttachAllComs(PDRIVER_OBJECT driver) { ULONG i; PDEVICE_OBJECT com_ob; NTSTATUS status; for(i = 0;i<CCP_MAX_COM_ID;i++) { com_ob = ccpOpenCom(i,&status); if(com_ob == NULL) continue; ccpAttachDevice(driver,com_ob,&s_fltobj[i],&s_nextobj[i]); } }
|
请求的区分
请求中包含有要发送的数据,请求的回答中则含有要接收的数据
- 每个驱动程序只有一个驱动对象
- 每个驱动程序可以生成若干个设备对象,这些对象从属于一个驱动对象
- 若干设备(可以从属于不同的驱动)一次绑定形成一个设备栈,最顶端设备最先收到请求
请求有多种类型,在串口过滤中只需要考虑读请求(接收)和写请求(发出),请求类型通过IRP的主功能号进行区分,IRP的主功能号保存在IRP栈空间(irpsp)中的一个字节
1 2 3 4
| PIO_STACK_LOCATION IoGetCurrentIrpStackLocation( _In_ PIRP Irp );
|
过滤设备对象被绑定在设备栈顶端,所以首先接收到IRP请求的时过滤设备对象
1 2 3 4 5 6 7 8 9 10
| VOID IoSkipCurrentIrpStackLocation( [in, out] PIRP Irp );
NTSTATUS IoCallDriver( _In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp );
|
写请求数据
IRP数据结构中有三个域描述缓冲区
- irp->MdlAddress 缓冲方式
- irp->UserBuffer 其他方式(最追求效率)
- irp->AssociatesIrp.SystemBuffer 直接方式(用于不追请效率)
把一块内存映射到内核空间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| PVOID MmGetSystemAddressForMdlSafe( _in_ PMDL Mdl, _in_ MM_PAGE_PRIORITY Priority );
PBYTE buffer = NULL; if(irp->MdlAddress != NULL) { buffer = (PBYTE)MmGetSystemAddressForMdlSafe (irp->MdlAddress, NormalPagePriority); } else { buffer = (PBYTE)irp->UserBuffer; } if(buffer == NULL) { buffer = (PBYTE)irp->AssociatesIrp.SystemBuffer; }
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 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; }
|
动态卸载
在卸载函数中完成解除绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| VOID IoDetachDevice( _Inout_ PDEVICE_OBJECT TargetDevice );
VOID IoDeleteDevice( _In_ PDEVICE_OBJECT DeviceObject );
NTSTATUS KeDelayExecutionThread( _In_ KPROCESSOR_MODE WaitMode, _In_ BOOLEAN Alertable, _In_ PLARGE_INTEGER Interval );
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]); }
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]); } }
|