回顧過濾器原理
阿新 • • 發佈:2018-11-11
win驅動下過濾器原理:
簡單說就是電腦一般真實裝置只有一個,像鍵盤,但有的說一個電腦有多個鍵盤。多個鍵盤分別是多個不同的真實物體。
而真實裝置對應多個裝置物件。多個裝置共同組成一個裝置棧。不能的裝置對應不同的裝置物件。如鍵盤與滑鼠是不同的裝置物件。裝置物件上層是驅動物件。每個裝置物件只有唯一的驅動物件。驅動物件有多個裝置物件。如一個驅動物件有鍵盤,滑鼠裝置物件。一般通訊是使用者層呼叫api去訪問相應的裝置。而呼叫api底層是呼叫驅動。也就是發請求給驅動。這個請求一般叫做irp包,然後這個包給裝置物件。因為一個真實裝置有多個裝置物件。因此這個包從裝置物件棧第一個開始一直髮。直到發給真實裝置。在裝置棧中的裝置物件可以不是同一個驅動物件。比如過濾器驅動建立的裝置物件。如果這個裝置物件把這個包不往下發也就是不給真實裝置。就達到了過濾的作用。
那這些裝置物件怎麼與真實裝置物件關聯的呢,就是通過api把一個驅動物件建立的裝置物件附加到真實裝置物件上。
結構圖
簡單程式碼引用獨釣寒江書的原始碼串列埠過濾
/// /// @file comcap.c /// @author crazy_chu /// @date 2008-6-18 /// #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; }