一個檔案系統過濾驅動的demo

Ox9A82發表於2016-05-24

因為沒寫過FSD過濾驅動,所以拿來練練手,沒有什麼技術含量。參考自Win核心安全與驅動開發。

先梳理一下大概的流程,就是怎麼去繫結裝置棧、怎麼去過濾各種請求的。

首先肯定是要繫結裝置棧的,來看下怎麼繫結的裝置棧。

先確定繫結的物件是什麼,檔案系統的裝置是分兩個部分的,分別是卷裝置和控制裝置。其中,卷裝置是每有一個卷就會對應有一個卷裝置,這麼說可能不太清楚我畫了一張圖。

注意,這裡的卷裝置並不是磁碟的卷裝置。而是檔案系統生成的,用來對應每一個卷的卷裝置。一定要區分開這兩者。繫結這些個卷裝置也不能夠直接進行,因為這些由檔案系統生成的卷裝置是無名的裝置,根本沒法直接繫結。這裡使用的辦法是先繫結FSD的控制裝置,當控制裝置接收到新建卷裝置的請求這時就是可以繫結到卷裝置上了。但是,FSD的控制裝置也不是直接就可以繫結上的,所以也要想辦法去獲取下,這裡用的是註冊檔案系統變動回撥函式的方法。IoRegisterFsRegistrationChange()函式就是用來註冊FSD過濾驅動的,這個回撥在檔案系統發生變動時會被呼叫,比如說檔案系統被掛載時。回撥函式和其他的系統事件通知類似都是有固定格式的,函式原型如下。

1 VOID NotifyFunction(PDEVICE_OBJECT DeviceObject,BOOLEAN Type)
2 {
3    //其中DeviceObject就可以是FSD載入時的控制裝置指標,Type參數列示是解除安裝還是載入     
4 }

註冊了這個回撥函式後,在裡面對DeviceObject進行一下判斷看一下是不是自己想要的檔案系統,因為一個電腦上有很多的檔案系統。

小結一下,目前為止我們知道了要進行這麼一個流程:

分三步才能實現最終繫結。而我們的目的只是為了去繫結FSD卷裝置,因為這個卷裝置才是真正去接受和處理讀寫請求的裝置。

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegPath)
{
    UNICODE_STRING DeviceName;
    DEVICE_OBJECT  DeviceObject;
    NTSTATUS        Status;
    FilterDriver=DriverObject;//全域性變數
    RtlInitUnicodeString(&DeviceName,L"\\FileSystem\\Filters\\DemoDriver");
    Status=IoCreateDevice(DriverObject,
                    0,
                    &DeviceName,
                    FILE_DEVICE_DISK_FILE_SYSTEM,
                    FILE_DEVICE_SECURE_OPEN,
                    FALSE,
                    &DeviceObject);
    if(!NT_STATUS(Status))
    {
        //裝置建立失敗
        return Status;
    }
    for(i=0;i<IRP_MJ_MAXIMUM_FUNCTION)
    {
        DriverObject->MajorFunction[i]=CallNextIrpLocation;
    }
    //DriverObject->MajorFunction[]
    //當前裝置繫結到檔案系統的控制裝置(CDO)上
    //註冊一個檔案系統變動回撥函式
    Status=IoRegisterFsRegistrationChange(DriverObject,FsChangeCallBack);
    if(!NT_STATUS(Status))
    {
        IoDeleteDevice(DeviceObject);
        return Status;
    }
    
}

 我這裡把MajorFunctionp[]的分發函式一些給省略了,後面會寫出來。再來看一下FsChangeCallBack也就檔案系統變動回撥函式的內容,這個函式的目的是要繫結FSD控制裝置。

 1 VOID FsChangeCallBack(PDEVICE_OBJECT DeviceObject,BOOLEAN Create)
 2 {
 3     NTSTATUS Status;
 4     UNICODE_STRING CompareName;
 5     UNICODE_STRING DriverName;
 6     DEVICE_OBJECT  FilterDevice;
 7     RtlInitUnicodeString(&CompareName,L"\\FileSystem\\Fs_Rec");
 8     if(Create)
 9     {
10         //附加當前過濾驅動到CDO上去
11         //附加前要注意兩點
12         //1.是不是自己想要的CDO型別
13         //2.是不是檔案識別器
14         if(IS_WANTED_DEVICE_TYPE(DeviceObject->DriverObject->type))
15         {
16             //
17             GetDriverNameFromDeviceObject(&DriverName,DeviceObject);
18             if(!RtlCompareUnicodeString(&DriverName,&CompareName))
19             {
20                 Status=IoCreateDevice(FilterDriver,
21                                 sizeof(DEVICE_EXTENSION),
22                                 NULL,
23                                 DeviceObject->DeviceType,
24                                 0,
25                                 FALSE,
26                                 &FilterDevice);
27                 if(!NT_SUCCESS(Status))
28                 {
29                     return Status;
30                 }
31                 FilterDevice->Flags=DeviceObject->Flags;
32                 Status=(PVOID)IoAttachDeviceToDeviceStack(FilterDevice,DeviceObject);
33                 if(!Status)
34                 {
35                     IoDeleteDevice(FilterDevice);
36                     return Status;
37                 }
38                 DeviceObject->DeviceExtension->NextDevice=(PDRIVER_OBJECT)Status;
39                 
40             }
41             
42         }
43     }
44     else
45     {
46         return 0;
47     }
48     return 0;
49 }

做了簡單的判斷:

  1. 判斷是掛載檔案系統的通知
  2. 判斷是想要的檔案系統型別
  3. 判斷不是檔案識別器

這裡FSD的控制裝置就被繫結上了,然後再去過濾FSD控制裝置識別器接受的卷掛載請求。過濾請求這裡有一點要注意的,因為當請求發下來時卷裝置還沒真正建立,所以不能去繫結,要等Irp返回後才能去繫結。這一點跟處理過濾驅動過濾讀請求時的處理是很像的,因為讀請求下發下來時,還沒有正在的讀動作,只有等到Irp返回後才能進行過濾。具體的做法就是用完成函式,但是畢竟特殊的一點是書上是用完成函式去啟用事件然後在分發例程中等待事件來進行的處理,等待事件後進行繫結。書中的解釋是完成函式處於DPC的IRQL,其實我覺得放在完成函式裡應該不會有問題吧,又使用了完成函式又使用了等待事件感覺好重複。這種寫法以前好像沒寫過。

 1 NTSTATUS FilterAddToMount(PDEVICE_OBJECT DeviceObject,PIRP Irp)
 2 {
 3     //從DeviceObject中獲取VPB,再從VPB中獲取卷的資訊
 4     PIO_STACK_LOCATION irpSP;
 5     PDEVICE_OBJECT RealDevice;
 6     PDEVICE_OBJECT FilterDevice;
 7     KEVENT NotifyEvent;
 8     NTSTATUS Status;
 9     PVPB vpb;
10     irpSP=IoGetCurrentIrpStackLocation(Irp);
11     RealDevice=irpSP->Parameters.MountVolume.Vpb->RealDevice;
12     IoCreateDevice(FilterDriver,
13                     sizeof(DEVICE_EXTENSION),
14                     NULL,
15                     DeviceObject->DeviceType,
16                     0,
17                     FALSE,
18                     &FilterDevice);
19     KeInitializeEvent(&NotifyEvent,
20                         NotificationEvent,
21                         FALSE);
22     IoCopyCurrentIrpStackLocationToNext(Irp);
23     IoSetCompletionRoutine(Irp,
24                             SetEventRoutine,
25                             &NotifyEvent,
26                             TRUE,
27                             TRUE,
28                             TRUE);
29     IoCallDriver(Device,Irp);
30     
31     KeWaitForSingleObject(&NotifyEvent,
32                             Executive,
33                             KernelMode,
34                             FALSE,
35                             NULL);
36     if(NT_SUCCESS(irpSP->IoStatus.Status))
37     {
38         vpb=RealDevice->Vpb;
39         Status=IoAttachDeviceToDeviceStack(vpb->DeviceObject,FilterDevice);
40     }
41     IoCompleteRequest(Irp,IO_NO_INCREMENT);
42     return STATUS_SUCCESS;
43                             
44                     
45 }
46 
47 NTSTATUS SetEventRoutine(PDEVICE_OBJECT DeviceObject,
48                             PIRP Irp,
49                             PVOID Context)
50 {
51     KeSetEvent((PKEVENT)Context,IO_NO_INCREMENT,FALSE);
52     return STATUS_MORE_PROCESSING_REQUIRED;
53 
54 }

 到這裡已經繫結了卷裝置,這時只要設定好分發函式就可以完成過濾了。

接下來要說的就是具體的針對每種資訊的處理了,要分類來說了。

 

0x0 過濾檔案的開啟

檔案開啟請求的主功能號是IRP_MJ_CREATE

首先,在Irp棧中可以獲得檔案物件指標。FileObject=irpsp->FileObject;目錄和檔案都是用檔案物件表示的,直接是看不出來兩者的分別的。irpsp->Parameters.Create的結構如下

struct{
PIO_SECURITY_CONTEXT SecurityContext;
ULONG Options;
USHORT FileAttributes;
USHORT ShareAccess;
ULONG EaLength;    
}

注意,開啟請求是一種嘗試性動作,就是說開啟只有當返回成功時才有過濾的意義,獲取結果要用完成函式才行。

 

0x1 過濾檔案的刪除

檔案刪除是分三步請求的,所以不是過濾一次就行的。刪除的三步如圖

開啟檔案的請求上面已經說過了。設定刪除標誌是主功能號為IRP_MJ_SET_INFORMATION的設定請求中的一種,其中irpsp->Parameters.SetFile.FileInformationClass為FileDispositionInformation。然後irp->AssociatedIrp.SystemBuffer指向如下結構

typdef struct _FILE_DISPOSITION_INFORMATION{
BOOLEAN DeleteFile;
} FILE_DISPOSITION_INFORMATION;

 

0x2 過濾檔案的路徑

我們使用檔案系統過濾驅動很多時候都是為了根據路徑來進行操作,要不就使用磁碟過濾驅動就可以了完全沒必要使用FSD過濾驅動。

(未完待續)

相關文章