2011年5月29日 星期日

檔案過濾驅動學習筆記(還原軟體的基礎)

最近幾天學習了下檔案過濾驅動這方面的資料確實很少

這篇文章是我這幾天學習的總結特此貼出來供大家一起學習,

畢竟是初學檔案過濾驅動錯誤之處難免還請多多見諒.





下面進入正題

1.DriverEntry
常式

(1)
創建過濾驅動的控制設備以後我們的IO控制碼就是發到這個設備上面



//
這裡的設備名與普通設備有所不同.

//
當然最簡單的可以直接寫成 L"\\Device\\Filemontor";(FileMon中是這麼寫的調試過可行)

RtlInitUnicodeString( &nameString, L"\\FileSystem\\Filters\\FileMonitor" );

status = IoCreateDevice( DriverObject,

             0,                     //has no device extension  

                        //
這是與其他Attach到別的設備上的設備的不同之處 

             &nameString,

             FILE_DEVICE_DISK_FILE_SYSTEM,

             FILE_DEVICE_SECURE_OPEN,

             FALSE,

             &gSFilterControlDeviceObject );



if (status == STATUS_OBJECT_PATH_NOT_FOUND) {



  RtlInitUnicodeString( &nameString, L"\\FileSystem\\FileMonitor" );

  status = IoCreateDevice( DriverObject,

               0,                      

               &nameString,

               FILE_DEVICE_DISK_FILE_SYSTEM,

               FILE_DEVICE_SECURE_OPEN,

               FALSE,

               &gSFilterControlDeviceObject );





//
創建符號連結

RtlInitUnicodeString(&syblnkString, L"\\DosDevices\\FileMonitor");

status = IoCreateSymbolicLink( &syblnkString, &nameString );



if (!NT_SUCCESS(status)) {



  IoDeleteSymbolicLink( &syblnkString );

  status = IoCreateSymbolicLink( &syblnkString, &nameString );

  

  if (!NT_SUCCESS(status)) {



    KdPrint(("
創建符號連結失敗~\n"));

    IoDeleteDevice(gSFilterControlDeviceObject);

    return status;

  }

}



(2)
設置常式

for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {



  DriverObject->MajorFunction[i] = SfDispatch;

}

DriverObject->MajorFunction[IRP_MJ_CREATE] = SfCreate;



DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = SfFsControl;

DriverObject->MajorFunction[IRP_MJ_CLOSE] = SfCleanupClose;



(3)
調用IoRegisterFsRegistrationChange函數來通知我們檔案系統的載入和卷的mount.





2.SfCreate 
常式

(1)sfilter
的原版中是這麼寫的

if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {



  Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;

  Irp->IoStatus.Information = 0;

  IoCompleteRequest( Irp, IO_NO_INCREMENT );

  return STATUS_INVALID_DEVICE_REQUEST;

}



這樣寫的後果是我們用CreateFile函數在R3下打開此控制設備符號連結的時候失敗

我剛開始學習檔過濾驅動的時候對此不是很瞭解, CreateFile老是失敗

起初還以為是符號連結名寫錯了後來參看了FileMon的代碼才反應過來



於是修改如下:

if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {

    

  Irp->IoStatus.Status = STATUS_SUCCESS;

  Irp->IoStatus.Information = FILE_OPENED;

  IoCompleteRequest( Irp, IO_NO_INCREMENT );

  return STATUS_SUCCESS;

}



(2)
根據不同軟體的需要編寫此函數的接下來部分

[1]
比如我們要阻止病毒在Windows目錄下創建檔那麼我們就要在此檔還沒創建的時候得到此檔的全路徑.

要在這個時候得到檔的路徑楚狂人也說了有點麻煩下面提供一個函數給大家用於在檔創建前得到路徑.

BOOLEAN MzfGetFileFullPathPreCreate(PFILE_OBJECT pFile, PUNICODE_STRING path )

{

   NTSTATUS status;

   POBJECT_NAME_INFORMATION pObjName = NULL;

   WCHAR buf[256] = {0};

   void *obj_ptr = NULL;

   ULONG ulRet = 0;

   BOOLEAN bSplit = FALSE;



   if (pFile == NULL) return FALSE;

   if (pFile->FileName.Buffer == NULL) return FALSE;



   pObjName = (POBJECT_NAME_INFORMATION)buf;



   if (pFile->RelatedFileObject != NULL)

      obj_ptr = (void *)pFile->RelatedFileObject;

   else

      obj_ptr = (void *)pFile->DeviceObject;



   status = ObQueryNameString(obj_ptr, pObjName, 256*sizeof(WCHAR), &ulRet);

   if (status == STATUS_INFO_LENGTH_MISMATCH)

   {

      pObjName = (POBJECT_NAME_INFORMATION)ExAllocatePool(NonPagedPool, ulRet);



      if (pObjName == NULL)  return FALSE;



      RtlZeroMemory(pObjName, ulRet);



      status = ObQueryNameString(obj_ptr, pObjName, ulRet, &ulRet);

      if (!NT_SUCCESS(status)) return FALSE;

   }



 //
拼接的時候判斷是否需要加 '\\'

 if (pFile->FileName.Length > 2 && 

  pFile->FileName.Buffer[0] != L'\\' &&

  pObjName->Name.Buffer[pObjName->Name.Length/sizeof(WCHAR) -1] != L'\\')

    bSplit = TRUE;

 

 ulRet = pObjName->Name.Length + pFile->FileName.Length;



 if (path->MaximumLength < ulRet)  return FALSE;



 RtlCopyUnicodeString(path, &pObjName->Name);

 if (bSplit)

   RtlAppendUnicodeToString(path, L"\\");



 RtlAppendUnicodeStringToString(path, &pFile->FileName);



 if ((void*)pObjName != (void*)buf)

    ExFreePool(pObjName);



 return TRUE;

}



至此得到了檔的路徑以後我們就可以做出判斷了如果要阻止檔創建

那麼直接用IoCompleteRequest函數結束此IRP即可否則下發

IoSkipCurrentIrpStackLocation( Irp );

return IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->AttachedToDeviceObject, Irp );



[2]
要是像FileMon那樣只是記錄系統中創建了哪些檔的話我們可以設置此函數的完成常式.

然後等檔創建完成了之後只要調用 IoQueryFileDosDeviceName 函數即可知道檔的全路徑了.



設置完成常式如下:

{

  KEVENT waitEvent;

  KeInitializeEvent( &waitEvent, NotificationEvent, FALSE );



  IoCopyCurrentIrpStackLocationToNext( Irp );

  IoSetCompletionRoutine(Irp, SfCreateCompletion, &waitEvent, TRUE, TRUE, TRUE );

  status = IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->AttachedToDeviceObject, Irp );



  if (STATUS_PENDING == status) {



    NTSTATUS localStatus = KeWaitForSingleObject(&waitEvent, Executive, KernelMode, FALSE, NULL);

    ASSERT(STATUS_SUCCESS == localStatus);

  }

  //
此處檔已經創建完成了我們可以調用IoQueryFileDosDeviceName函數得到檔的全路徑



  //
最後結束此IRP

  status = Irp->IoStatus.Status;

  IoCompleteRequest( Irp, IO_NO_INCREMENT );

  return status;

}



3.SfDispatch
常式

在此常式中要判斷是不是我們的控制設備如果使我們的控制設備則要處理相應的IO控制碼.

否則下發此IRP

NTSTATUS SfDispatch ( IN PDEVICE_OBJECT DeviceObject,  IN PIRP Irp )

{

  NTSTATUS status = STATUS_SUCCESS;

    PIO_STACK_LOCATION irpStack;

    

    if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {



        Irp->IoStatus.Information = 0;

        irpStack = IoGetCurrentIrpStackLocation( Irp );

    

        switch (irpStack->MajorFunction) {

      

    case IRP_MJ_DEVICE_CONTROL:

      // 
此函數用來執行相應的控制碼

      status = SpyCommonDeviceIoControl( Irp->AssociatedIrp.SystemBuffer,

                                                   irpStack->Parameters.DeviceIoControl.InputBufferLength,

                                                   Irp->AssociatedIrp.SystemBuffer,

                                                   irpStack->Parameters.DeviceIoControl.OutputBufferLength,

                                                   irpStack->Parameters.DeviceIoControl.IoControlCode,

                                                   &Irp->IoStatus );

      break;

      

    case IRP_MJ_CLEANUP

      status = STATUS_SUCCESS;

      break;

      

    default:

      status = STATUS_INVALID_DEVICE_REQUEST;

        }

    

        Irp->IoStatus.Status = status;

        IoCompleteRequest( Irp, IO_NO_INCREMENT );

        return status;

    }

  

  //
不是我們的控制設備則下發此IRP

    return SfPassThrough( DeviceObject, Irp );

}





IO
控制碼也可以在FASTIO常式的SfFastIoDeviceControl函數中處理,如下:

當然最簡單的還是在FASTIO常式中返回FALSE. 這樣系統便會調用我們上面的SfDispatch函數.

if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {

    

        SpyCommonDeviceIoControl( InputBuffer,

      InputBufferLength,

      OutputBuffer,

      OutputBufferLength,

      IoControlCode,

      IoStatus );

    

        return TRUE;




}




 點我下載原始碼




沒有留言:

張貼留言