文件系统过滤驱动开发(一)—Win32底层开发小组
??? 聲明:本文無太多新意,只是介紹下學習經驗,大神級人物(如總監大人)請略過,謝謝合作>_<
??? 吐槽一下:學驅動算起來也是從上學期9月份開始吧,之前在家買了<Windows驅動開發技術詳解>這本書,搭了個環境之后,其實也沒碰很多,編了個經典的Hello,World!之后就無太多后續動作,暑假嘛,你們懂的,學習無壓力.上學期斷斷續續算是把基礎啃完了(其實也只是啃完-__-),就到了萬惡的期末考試復習月.寒假開始著手過濾驅動這一塊的學習,然后學期一開始就忙著這個SIG的項目.到現在的感覺就是,思路是有,但是有一些地方找不到切入點,光查資料就花很多時間.現在分享一下我的學習心得,不對之處,敬請指出.
??? 我們小組主要想完成的東西是一個類似SandBoxIE的軟件,就是沙盒,現在360和微軟的瀏覽器當中都加入了沙盒的功能.當然我也沒試用過,因為搜狗有教育網加速- -,我個人對沙盒的理解是對一些操作的重定向,如文件寫入操作,注冊表寫入操作,當然更高級的話還要加入內存保護.既然有了對寫入操作的重定向,那么對于讀取操作也應該進行重定向,因為需要讀取修改過的文件或者注冊表值.簡單來說,這個沙盒像是一個容器,而我們可以在這個容器里面運行其他程序,這個程序對我們操作系統做的修改會被重定向,結果是這些修改操作不會對系統有任何的影響,徐志摩同學很好的描述了這一過程:悄悄的我走了,正如我悄悄的來,我揮一揮衣袖,不帶走一片云彩...再比如在瀏覽器當中加入沙盒功能,假設我們系統訪問了一個被掛馬的網站中了木馬,那么當瀏覽器關閉的時候,木馬執行的破壞操作都是無效的(當然還是有泄漏帳號密碼的危險,這是另一個話題).
??? 由于驅動開發涉及底層,所以各個版本的Windows都會有所不同,我們針對的操作系統是WindowsXP,對于Windows2K而言,下面有些說的實現方法可能無法實現(底層的支持問題).對于文件操作的攔截,可以通過文件系統過濾來實現.現在一般殺毒軟件的核心都需要一個文件過濾驅動,這樣才能實時監控用戶的文件是否安全.關于磁盤過濾驅動和文件系統過濾驅動,磁盤過濾驅動比文件系統過濾驅動更為底層一點,直接過濾磁盤可以實現對硬盤操作的還原,這樣也可以實現我們想要的功能,但是相對來說粒度太大,我們需要的針對特定程序的監控,區分文件目錄而非無差別的任意磁盤讀寫操作.這一類的軟件有影子系統,雨過天晴多點還原系統.
??? 接下來進入正題,如何開發文件系統過濾驅動,可能你需要對驅動對象,設備對象和IRP派遣函數有所了解.對于文件系統過濾驅動的框架而言有兩種選擇,一種是利用Minifilter,這個是文件系統微過濾驅動,微軟為Windows內核開發者開發了一個新的驅動,叫做過濾管理器,提供一些接口供開發用戶使用,這樣就屏蔽了底層細節,提高了用戶開發的效率,同時也利于在不同系統間的移植和解決兼容性問題.在減少代碼依賴性的同時,由于開發者不需要了解太多的底層細節,只能通過提供的接口進行開發,可能在實現某些特定功能的時候會實現不了.另一種就是傳統型的文件過濾驅動,微軟提供了Sfilter的例子,細節需要開發者自己實現.出于學習底層知識的目的,我們選用的是Sfilter的框架,雖然比較繁瑣,但是能學的多一點.
??? 過濾驅動的一般思路是綁定相關的設備對象,之后就能得到該設備的IRP,進行先手和后手的處理.在文件系統過濾驅動當中,我們會生成三種設備:1,過濾驅動自身的控制設備;2,文件系統控制設備的過濾設備;3,文件系統卷設備的過濾設備.對于過濾驅動自身的控制設備主要是用來與應用層進行交互,剩下兩個是用來綁定文件系統的設備,像FAT32,NTFS這樣的文件系統主要生成兩類設備,一種是控制設備(CDO),一種是卷設備(VDO),我們需要綁定這兩種設備.綁定文件系統的控制設備我們就可以得到類似卷掛載/解掛載等操作的通知從而進行相應的處理,而綁定卷設備就能過濾相關的文件操作.這里的卷設備是指文件系統的卷設備,卷設備有兩類,一類是由卷管理器生成的,這類設備有名字,我們常見的C:,D:是這類卷設備的符號鏈接,其設備名是\Device\harddiskVolume1\,\Device\harddiskVolume2\,這些是由卷設備管理器生成的,綁定這些設備無法得到文件操作的.我們關心的文件系統卷設備是沒有名字的,需要通過其他方式獲得.
??? 如何獲取控制設備并綁定它,可以通過注冊文件系統變動回調來實現,原型如下:
1: NTSTATUS 2: IoRegisterFsRegistrationChange( 3: IN PDRIVER_OBJECT DriverObject, 4: IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine 5: );??? 回調函數的原型如下:
1: VOID 2: (*PDRIVER_FS_NOTIFICATION) ( 3: IN struct _DEVICE_OBJECT *DeviceObject, 4: IN BOOLEAN FsActive 5: );??? 通過注冊回調函數,可以得到系統已經安裝了的文件系統控制設備對象也就是*DeviceObject,FsActive是表示文件系統是激活還是卸載,當文件系統激活時我們綁定他,卸載則解綁定.文件系統的回調函數是用于通知文件系統的變更,在XP下,當注冊文件系統回調之后系統會調用回調函數用以通知已經激活的文件系統,而在較早版本下面如Win2K+SP4之前的版本是不會,所以只能將文件過濾驅動做成靜態加載的形式在系統啟動早期加載.在回調函數中,我們根據FsActive判斷是綁定還是解綁定:
1: if (FsActive) 2: { 3: LzsbfAttachToFileSystemDevice(pDeviceObject, &DeviceName); 4: } 5: else 6: { 7: LzsbfDetachFromFileSystemDevice(pDeviceObject); 8: }??? 需要注意的是,這個回調函數并不通知Raw文件系統,如果需要關注此類文件系統需要自己判斷,我們只關心FAT32和NTFS文件系統,故無視之.
??? 在回調函數當中,我們還需要加入對文件系統識別器的判斷.Windows在加載文件系統驅動的時候并非一開始就加載完整驅動,IO管理器是利用識別器來識別新加入物理介質的文件系統,當識別成功則卸載掉識別器,加載真正的驅動.我們要綁定的是真正的文件系統驅動設備,對于部分的文件系統識別器是由驅動"\FileSystem\Fs_Rec"生成的,注意只是部分,我們對于這部分的設備通過判斷其驅動對象(識別器是一個設備對象)的名字,如果是"\FileSystem\Fs_Rec"跳過不進行綁定,對于不是由Fs_Rec產生的識別器,我們在IRP的派遣函數MarjorFunctions[IRP_MJ_FILE_SYSTEM_CONTROL]當中進行處理,我們能夠得到識別器卸載要求加載真正文件系統驅動的通知.
??? 通過文件系統回調函數提供的文件系統控制設備我們就可以綁定文件系統的控制設備,之后根據控制設備對象得到文件系統驅動對象,進而可以枚舉其相關的卷設備,然后進行一一綁定.具體的實現代碼如下:
1: NTSTATUS 2: LzsbfEnumerateFileSystemVolumes( 3: IN PDEVICE_OBJECT pFSDeviceObject, 4: IN PUNICODE_STRING pFSName 5: ) 6: { 7: PDEVICE_OBJECT pNewDeviceObject; 8: PLZSBFILTER_DEVICE_EXTENSION pNewDeviceExtension; 9: PDEVICE_OBJECT *DeviceList; 10: PDEVICE_OBJECT pStorageStackDeviceObject; 11: NTSTATUS status; 12: ULONG NumberOfDevice; 13: ULONG i; 14: BOOLEAN IsShadowCopyVolume; 15:? 16: PAGED_CODE(); 17:? 18: status = IoEnumerateDeviceObjectList(pFSDeviceObject->DriverObject, 19: NULL, 20: 0, 21: &NumberOfDevice 22: ); 23: if (!NT_SUCCESS(status)) 24: { 25: ASSERT(STATUS_BUFFER_TOO_SMALL == status); 26: NumberOfDevice += 8; 27: DeviceList = ExAllocatePoolWithTag(NonPagedPool, 28: (NumberOfDevice * sizeof(PDEVICE_OBJECT)), 29: LZSB_POOL_TAG 30: ); 31: if (NULL == DeviceList) 32: { 33: KdPrint(("Fail to allocate DeviceList\n")); 34: return STATUS_INSUFFICIENT_RESOURCES; 35: } 36:? 37: status = IoEnumerateDeviceObjectList(pFSDeviceObject->DriverObject, 38: DeviceList, 39: (NumberOfDevice * sizeof(PDEVICE_OBJECT)), 40: &NumberOfDevice 41: ); 42: if (!NT_SUCCESS(status)) 43: { 44: KdPrint(("Fail to call IoEnumerateDeviceObjectList()\n")); 45: ExFreePool(DeviceList); 46: return status; 47: } 48: 49: for (i = 0; i < NumberOfDevice; i++) 50: { 51: pStorageStackDeviceObject = NULL; 52: __try 53: { 54: if ((DeviceList[i] == pFSDeviceObject) || 55: (DeviceList[i]->DeviceType != pFSDeviceObject->DeviceType) || 56: LzsbfIsAttachedToDevice(DeviceList[i], NULL)) 57: { 58: KdPrint(("Not to attach this!\n")); 59: __leave; 60: } 61:? 62: LzsbfGetBaseDeviceObjectName(DeviceList[i], pFSName); 63: if (pFSName->Length > 0) 64: { 65: KdPrint(("It has a name, do not attach!\n")); 66: __leave; 67: } 68: 69: status = IoGetDiskDeviceObject(DeviceList[i], &pStorageStackDeviceObject); 70: if (!NT_SUCCESS(status)) 71: { 72: KdPrint(("Fail to get disk device object!\n")); 73: __leave; 74: } 75:? 76: status = LzsbfIsShadowCopyVolume(pStorageStackDeviceObject, &IsShadowCopyVolume); 77: if (NT_SUCCESS(status) && IsShadowCopyVolume) 78: { 79: KdPrint(("It is shadow copy volume, do not attach!\n")); 80: __leave; 81: } 82:? 83: status = IoCreateDevice(gLZSBFilterDriverObject, 84: sizeof(LZSBFILTER_DEVICE_EXTENSION), 85: NULL, 86: DeviceList[i]->DeviceType, 87: 0, 88: FALSE, 89: &pNewDeviceObject 90: ); 91: if (!NT_SUCCESS(status)) 92: { 93: KdPrint(("Fail to create new device!\n")); 94: __leave; 95: } 96:? 97: pNewDeviceExtension = pNewDeviceObject->DeviceExtension; 98: pNewDeviceExtension->LZSBDeviceType = FILESYSTEM_VOLUME_DEVICE; 99: pNewDeviceExtension->pStorageStackDeviceObject = pStorageStackDeviceObject; 100: RtlInitEmptyUnicodeString(&pNewDeviceExtension->DeviceName, 101: pNewDeviceExtension->DeviceNameBuffer, 102: sizeof(pNewDeviceExtension->DeviceNameBuffer) 103: ); 104: LzsbfGetObjectName(pStorageStackDeviceObject, 105: &pNewDeviceExtension->DeviceName); 106:? 107: ExAcquireFastMutex(&gLZSBFilterAttachLock); 108: if (!LzsbfIsAttachedToDevice(DeviceList[i], NULL)) 109: { 110: status = LzsbfAttachToMountedDevice(DeviceList[i], pNewDeviceObject); 111: if (!NT_SUCCESS(status)) 112: { 113: KdPrint(("Fail to attach volume device\n")); 114: LzsbfCleanupMountedDevice(pNewDeviceObject); 115: IoDeleteDevice(pNewDeviceObject); 116: } 117: } 118: else 119: { 120: KdPrint(("Is attached already\n")); 121: LzsbfCleanupMountedDevice(pNewDeviceObject); 122: IoDeleteDevice(pNewDeviceObject); 123: } 124: ExReleaseFastMutex(&gLZSBFilterAttachLock); 125: 126: } 127: __finally 128: { 129: if (pStorageStackDeviceObject != NULL) 130: { 131: ObDereferenceObject(pStorageStackDeviceObject); 132: } 133: ObDereferenceObject(DeviceList[i]); 134: } 135: } 136: ExFreePool(DeviceList); 137: } 138:? 139: KdPrint(("LZSBFilter!lzsbfEnumerateFileSystemVolumes() success!\n")); 140: return STATUS_SUCCESS; 141: }??? 完成這一過程最主要的調用是IoEnumerateDeviceObjectList,原型如下,同樣也是Win2K+SP4和WinXP之后才有的調用.這個函數能夠枚舉指定驅動的設備鏈并返回到一個設備對象指針數組當中,需要注意的是,此時返回的對象包括文件系統的控制設備(我們枚舉的是文件系統驅動的設備對象),所以在綁定時需要進行判斷是否是控制設備.
1: NTSTATUS 2: IoEnumerateDeviceObjectList( 3: IN PDRIVER_OBJECT DriverObject, 4: IN PDEVICE_OBJECT *DeviceObjectList, 5: IN ULONG DeviceObjectListSize, 6: OUT PULONG ActualNumberDeviceObjects 7: );??? 在枚舉設備對象時,除了判斷是否為控制設備,還需要判斷是否為卷影,卷影是用于磁盤數據恢復的一種特殊設備,對于卷影我們也是跳過不進行綁定.
??? 稍微總結一下思路:1.注冊文件系統變動回調函數;2.在回調函數中綁定文件系統的控制設備,綁定控制設備時需要注意判斷是否是標準文件識別器;3.綁定控制設備之后,枚舉文件系統驅動(通過控制設備對象得到文件系統驅動對象指針)的設備鏈,排除卷影和控制設備本身,綁定其余的卷設備.
??? 至此,大概的綁定設備思路就有了,剩下的就是一些實現細節,下次再分享..
??? 最后,秉承有圖有碼有真相的原則.附上LOG截圖一張- -
??? to be continue....
轉載于:https://www.cnblogs.com/SCUTMSTechClub/archive/2011/03/18/1988386.html
總結
以上是生活随笔為你收集整理的文件系统过滤驱动开发(一)—Win32底层开发小组的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 淘宝店铺图片数据迁移核心代码
- 下一篇: 关于框架