本次實驗的測試環境為Windows Server 2008 R2 X64下。
為了解決例如系統關鍵目錄或者業務敏感目錄被放入惡意的可執行程序或者網頁文件等,一些安全軟件會使用文件過濾驅動的技術結合一定的檢測規則來達到保護系統和業務安全的一些目的。
簡單地來看下Minifilter技術的介紹:
Filter管理器隨Windows一起被安裝,但它只在一個minifilter驅動被加載時才會起作用。Filter管理器綁定到目標卷的文件系統棧上。Minifilter驅動為它要過濾的I/O操作而通過向filter管理器注冊來間接綁定到文件系統棧上。Filter管理器隨Windows一起被安裝,但它只在一個minifilter驅動被加載時才會起作用。Filter管理器綁定到目標卷的文件系統棧上。Minifilter驅動為它要過濾的I/O操作而通過向filter管理器注冊來間接綁定到文件系統棧上。
微軟的WDK實例中存在通過使用REPARSE重定向來完成跨盤的重定向,以及通過Minifilter使用IoCreateFileSpecifyDeviceObjectHint來完成的非跨盤重定向。
所以,我們的目標是先通過Windows部分內核代碼來看看為何Reparse能夠完成文件重定向的功能,再通過實現一個能夠簡易配置監視路徑和重定向后路徑的驅動程序和應用控制程序。
通過源碼查看文件創建源碼的關鍵部分
雖然是Windows Server 2008 x64系統,但是為了避免過多的逆向分析,直接以WRK為目標(由于無法過多引用,所以盡量以文字描述),看看是否可以得到關于Reparse重定向的結論。
創建文件的入口為IoCreateFile時,其主要工作由IopCreateFile的ObOpenObjectByName來完成,其為一個對象管理的內核函數。
ObOpenObjectByName函數中主要完成的2個工作是調用ObpLookupObjectName函數即對應我們真正的文件內核對象,并通過ObpCreateHandle完成內核對象到指定句柄表的插入。
其實最后的答案就在這個ObpLookupObjectName函數內,由于函數存在可選輸入參數RootDirectoryHandle作為搜索的目錄對象,但是類似地我們以不提供該參數的流程分析其中工作。
第一步,它會使用ObpRootDirectoryObject為搜索的根目錄對象,處理了以“??”開頭的對象名,以當前進程的ProcessMap為父目錄的查找。接著進入循環依次解析對象名中的每個段,在訪問前非KernelMode的訪問必須經過安全性檢查,進入ObpLookupDirectoryEntry中其對當前層對象名計算其Hash值后與Hash數組大小37取余,其存在后繼鏈表當獲取到的對象的OBJECT_HEADER_NAME_INFO和提供的名稱一致后就返回這個這個對象的地址。直接調用其對象類型的ParseProcedure(進入IopParseFile->IopParseDevice從而向對應的卷對象發送請求)。
閱讀后發現返回了STATUS_REPARSE后會轉入ParseFromRoot處,完成從前面描述的DeviceMap處處理的重新解析。
這說明了STATUS_REPARSE確實可以幫助我們完成一個文件位置到另一個文件位置的重定向(比如某類目錄總是STATUS_REPARSE,而且STATUS_REPARSE在ObpLookupObjectName有次數限制),第二由于交還給對象管理器時會完完整整地重新解析,所以這確實可以完成跨盤的操作。
實現可配置文件的文件系統跨盤重定向
我們目標是實現一個應用程序將監控配置發送給驅動,驅動將需要重定向文件的結果報告給應用程序。其中驅動完成將在監控文件夾內的新建文件全部轉移到目標文件夾下的操作。
首先我們主要關心如何截獲特定路徑下的文件創建操作。我們先完成對卷實例的綁定工作。這個回調例程在新的卷被掛載后以及注冊后已經掛載卷后被調用。在這個回調中主要通過FltGetVolumeName和完成卷的設備名稱和卷標名稱的獲取,并通過FltAllocateContext從內存池或者lookaside表種得到獲取保存上下文的內存,FltSetInstanceContext來完成設置到實例中,為了滿足程序能夠跨盤重定向后以DOS風格名稱發送給應用程序故加入雙鏈表中以便獲取其他盤的信息。
對于綁定后InstanceQueryTeardownCallback,InstanceTeardownStartCallback,InstanceTeardownCompleteCallback3類回調的處理,值得注意的是InstanceQueryTeardownCallback,因為其對應手工的解綁定,我們簡單地直接返回STATUS_FLT_DO_NOT_DETACH以拒絕。
現在我們來看對應主功能號為IRP_MJ_CREATE的處理,而主功能號(Minifilter中)為IRP_MJ_NETWORK_QUERY_OPEN的請求,我們也請求處理到了這里,由于其是一個Fast I/O操作,所以返回FLT_PREOP_DISALLOW_FASTIO來拒絕。我們使用了FltGetFileNameInformation和FltParseFileNameInformation來得到其文件名信息,后者是為了方便獲得卷設備名以轉換成DOS風格名。
這個過程中,我們根據實例上下文鏈表及配置生成2個名稱,一個是DOS風格文件名及完整文件名,前者以發送給應用程序,后者設置到FileObject中(使用IoReplaceFileObjectName),并在I/O請求包中返回STATUS_REPARSE以完成第一部分分析讓這個創建操作再重新根據新名稱重新從對象管理處開始處理。對于回調函數返回FLT_PREOP_COMPLETE來完成這個I/O請求,并不往下發送請求。
IRP_MJ_CREATE的操作IRP_MJ_SET_INFORMATION的FileInformationClass為FileRenameInformation的處理,因為對于非跨盤的移動會通過這個請求來完成操作,所以使用同IRP_MJ_CREATE一樣的操作。
最后提一下Minifilter中由應用程序向驅動的信息發送和驅動主動向應用程序發送的處理。應用程序以通過FilterConnectCommunicationPort向命名的通信端口請求連接。驅動內部通過PFLT_MESSAGE_NOTIFY MessageNotifyCallback回調接受收監控的目錄和重定向后的目錄。最后,完成重定向后驅動使用FltSendMessage發送消息,應用程序使用FilterGetMessage獲取。