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;
}