1. 程式人生 > >回顧過濾器原理

回顧過濾器原理

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