• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            elva

            Windows 文件過濾驅(qū)動(dòng)經(jīng)驗(yàn)總結(jié)

            Windows 文件過濾驅(qū)動(dòng)經(jīng)驗(yàn)總結(jié)

            看了 ChuKuangRen 的第二版《文件過濾驅(qū)動(dòng)開發(fā)教程》后,頗有感觸。我想,交流都是
            建立在平等的基礎(chǔ)上,在抱怨氛圍和環(huán)境不好的同時(shí)應(yīng)該先想一想自己究竟付出了多少?
            只知索取不愿付出的人也就不用抱怨了,要怪也只能怪自己。發(fā)自己心得的人無非是兩種
            目的,一是引發(fā)一些討論,好糾正自己錯(cuò)誤的認(rèn)識(shí),以便從中獲取更多的知識(shí)使自己進(jìn)步
            的更快。二是做一份備忘,當(dāng)自己遺忘的時(shí)候能夠馬上找到相關(guān)資料。我這里也總結(jié)了近
            幾年做文件過濾驅(qū)動(dòng)時(shí)所積累下來的一些小小經(jīng)驗(yàn),這分筆記也是看了 ChuKuangRen 的
            教程后,臨時(shí)想到的一小部分而已,是想到哪寫到哪,不是很全,如果以后再回想起什么
            也會(huì)不斷補(bǔ)充。因其工作原因,近段時(shí)間在 SOLARIS 驅(qū)動(dòng)與 Linux 內(nèi)核方面投入的精
            力比較多,Windows 下的文件過濾驅(qū)動(dòng)一直也沒有怎么去碰,所以最后還是那句老話
            FIXME。



            1、獲得文件全路徑以及判斷時(shí)機(jī)

            除在所有 IRP_MJ_XXX 之前自己從頭創(chuàng)建 IRP 發(fā)送到下層設(shè)備查詢?nèi)窂酵猓?br>不要嘗試在 IRP_MJ_CREATE 以外的地方獲得全路徑,因?yàn)橹挥性?IRP_MJ_CREATE
            中才會(huì)使用 ObCreateObject() 來建立一個(gè)有效的 FILE_OBJECT。而在 IRP_READ
            IRP_WRITE 中它們是直接操作 FCB (File Control Block)的。


            2、從頭建立 IRP 發(fā)送關(guān)注點(diǎn)

            無論你建立什么樣的 IRP,是 IRP_MJ_CREATE 也好還是 IRP_MJ_DIRECTORY_CONTROL
            也罷,最要提醒的就是一些標(biāo)志。不同的標(biāo)志會(huì)代來不同的結(jié)果,有些結(jié)果是直接
            返回失敗。這里指的標(biāo)志不光是 IRP->Flags,還要考慮 IO_STACK_LOCATION->Flags
            還有其它等等。尤其是你要達(dá)到一些特殊目的,這時(shí)候更需要注意,如 IRP_MN_QUERY_DIRECTORY,
            不同的標(biāo)志結(jié)果有很大的不同。


            3、從頭建立 IRP 獲取全路徑注意點(diǎn)

            自己從頭建立一個(gè) IRP_MJ_QUERY_INFORMATION 的 IRP 獲取全路徑時(shí)需要注意,
            不僅在 IRP_MJ_CREATE 要做區(qū)別處理,在 IRP_MJ_CLOSE 也要做同樣的處理,
            否則如果目標(biāo)是 NTFS 文件系統(tǒng)的話可能產(chǎn)生 deadlock。如果是 NTFS 那么在
            IRP_MJ_CLEANUP 的時(shí)候也需要對 FO_STREAM_FILE 類型的文件做同樣處理。



            4、獲得本地/遠(yuǎn)程訪問用戶名(域名/SID)

            方法只有在 IRP_MJ_CREATE 中才可用,那是因?yàn)?IO_SECURITY_CONTEXT 只有
            在 IO_STACK_LOCATION->Parameters.Create.SecurityContext 才會(huì)有效。
            這樣你才有可能從 IO_SECURITY_CONTEXT->SecurityContext->AccessState->
            SubjectSecurityContext.XXXToken 中獲得訪問 TOKEN,從而進(jìn)一步得到用戶名
            或 SID。記得 IFS 中有一個(gè)庫,它的 LIB 導(dǎo)出一個(gè)函數(shù)可以讓你在獲得以上
            信息后得到用戶名與域名。但如果你想兼容 NT4 的話,只能自己分析來得出本
            地和遠(yuǎn)程的 SID。



            5、文件與目錄的判斷

            正確的方法在楚狂人的文檔里已經(jīng)說過了,再補(bǔ)充一句。如果你的文件過濾驅(qū)動(dòng)
            要兼容所有文件系統(tǒng),那么不要十分相信從 FileObject->FsContext 里取得的數(shù)據(jù)。
            正確的方法還是在你傳遞下去 IRP_MJ_CREATE 后從最下層文件系統(tǒng)延設(shè)備棧返回到
            你這里后再獲得。



            6、加/解密中判斷點(diǎn)

            只判斷 IRP_PAGING_IO,IRP_SYNCHRONOUS_PAGING_IO,IRP_NOCACHE 是
            沒錯(cuò)的。如果有問題,相信是自己的問題。關(guān)于有人提到在 FILE_OBJECT->Flags
            中的 FO_NO_INTERMEDIATE_BUFFERING 是否需要判斷,對此問題的回答是只要
            你判斷了 IRP_NOCACHE 就不用再判斷 FILE_OBJECT 中的,因?yàn)樗罱K會(huì)
            設(shè)置 IRP->Flags 為 IRP_NOCACHE。關(guān)于你看到的諸如 IRP_DEFER_IO_COMPLETION
            等 IRP 不要去管它,因?yàn)樗皇且粋€(gè)過程。最終讀寫還是如上所介紹。至于
            以上這些 IRP 哪個(gè)是由 CC MGR 發(fā)送的,哪些是由 I/O MGR 發(fā)送和在什么
            時(shí)候發(fā)送的,這個(gè)已經(jīng)有很多討論了,相信可以找到。



            7、舉例說明關(guān)于 IRP 傳遞與完成注意事項(xiàng)

            只看 Walter Oney 的那本 《Programming the Microsoft Windows driver model》
            里介紹的流程,自己沒有實(shí)際的體會(huì)還是不夠的,那里只介紹了基礎(chǔ)概念,讓自己
            有了知識(shí)。知道如何用,在什么情況下用,用哪種方法,能夠用的穩(wěn)定這叫有了技術(shù)。
            我們從另一個(gè)角度出發(fā),把問題分為兩段來看,這樣利于總結(jié)。一個(gè) IRP 在過濾驅(qū)
            動(dòng)中,把它分為需要安裝 CompleteRoutine 的與無需安裝 CompleteRoutine 的。
            那么在不需要安裝 CompleteRoutine 的有以下幾類情況。

            (1) 拿到這個(gè) IRP 后什么都不做,直接調(diào)用 IoCompleteRequest() 來返回。
            (2) 拿到這個(gè) IRP 后什么都不做,直接傳遞到底層設(shè)備,使用
              IoSkipCurrentIrpStackLocation() 后調(diào)用 IoCallDriver() 傳遞。
            (3) 使用 IoBuildSynchronousFsdRequest() 或 IoBuildDeviceIoControlRequest()
              來建立 IRP 的。

            以上幾種根據(jù)需要直接使用即可,除了一些參數(shù)與標(biāo)志需要注意外,沒有什么系統(tǒng)
            機(jī)制相關(guān)的東西需要注意了。那么再來看需要安裝 CompleteRoutine 的情況。我們
            把這種情況再細(xì)分為兩種,一是在 CompleteRoutine 中返回標(biāo)志為
            STATUS_MORE_PROCESSING_REQUIRED 的情況。二是返回處這個(gè)外的標(biāo)志,需要使用函數(shù)
            IoMarkIrpPending() 的情況。在 CompleteRoutine 中絕大多數(shù)就這么兩種情況,
            你需要使用其中的一種情況。那么為什么需要安裝 CompleteRoutine 呢?那是因?yàn)?br>我們對其 IRP 從上層驅(qū)動(dòng),經(jīng)過我們驅(qū)動(dòng),在經(jīng)過底層設(shè)備棧返回到我們這一層驅(qū)
            動(dòng)時(shí)需要得到其中內(nèi)容作為參考依據(jù)的,還有對其中內(nèi)容需要進(jìn)行修改的。再有一種
            情況是沒有經(jīng)過上層驅(qū)動(dòng),而 IRP 的產(chǎn)生是在我們驅(qū)動(dòng)直接下發(fā)到底層驅(qū)動(dòng),而經(jīng)
            過設(shè)備棧后返回到我們這一層,且我們不在希望它繼續(xù)向上返回的,因?yàn)檫@個(gè) IRP
            本身就不是從上層來的。綜上所述,先來看下 IoMarkIrpPending() 的情況。


            (1) 在 CompleteRoutine 中判斷 Irp->PendingReturned 并使用 IoMarkIrpPending()
              然后返回。這種方法在沒有使用 KeSetEvent() 的情況下,且不是自建 IRP 發(fā)送
              到底層驅(qū)動(dòng)返回時(shí)使用。也就是說有可能我所做的工作都是在 CompleteRoutine
              中進(jìn)行的。比如加/解密時(shí),我在這里對下層驅(qū)動(dòng)返回?cái)?shù)據(jù)的判斷并修改。修改
              后因?yàn)闆]有使用 STATUS_MORE_PROCESSING_REQUIRED 標(biāo)志,它會(huì)延設(shè)備堆一直向
              上返回并到用戶得到數(shù)據(jù)為止。這里一定要注意,在這種情況下 CompleteRoutine
              返回后,不要在碰這個(gè) IRP。也就是說如果這個(gè)時(shí)候你使用了 IoCompleteRequest()
              的話會(huì)出現(xiàn)一個(gè) MULTIPLE_IRP_COMPLIETE_REQUEST 的 BSOD 錯(cuò)誤。


            (2) 在 CompleteRoutine 中直接返回 STATUS_MORE_PROCESSING_REQUIRED 標(biāo)志。這種
              情況在使用了 KeSetEvent() 的函數(shù)下出現(xiàn)。這里又有兩個(gè)小小的分之。

              1) 出現(xiàn)于上層發(fā)送到我這里,當(dāng)我這里使用 IoCallDriver() 后,底層返回?cái)?shù)
                據(jù)經(jīng)過我這一層時(shí),我想讓它暫時(shí)停止繼續(xù)向上傳遞,讓這個(gè) IRP 稍微歇息
                一會(huì),等我對這個(gè) IRP 返回的數(shù)據(jù)操作完成后(一般是沒有在 CompleteRoutine
                中對返回?cái)?shù)據(jù)進(jìn)行操作情況下,也就是說等到完成例程返回后再進(jìn)行操作),由
                我來調(diào)用 IoCompleteRequest() 讓它延著設(shè)備棧繼續(xù)返回。這里要注意,我們
                是想讓它返回的,所以調(diào)用了 IoCompleteRequest()。這個(gè)可不同于下面所講的
                自己從頭分配 IRP 時(shí)在 CompleteRoutine 中已經(jīng)調(diào)用 IoFreeIrp() 釋放了當(dāng)前
                IRP 的情況。比如我在做一個(gè)改變文件大小,向文件頭寫入加密標(biāo)志的驅(qū)動(dòng)時(shí),
                在上層發(fā)來了 IRP_MJ_QUERY_INFORMATION 查詢文件,我想在這個(gè)時(shí)候獲得文件
                信息進(jìn)行判斷,然后根據(jù)我的判斷結(jié)果再移動(dòng)文件指針。注意:上面是兩步,第
                一步是先獲得文件大小,那么在這個(gè)時(shí)候我就需要用到上述辦法,先讓這個(gè) IRP
                傳遞下去,得到我想要的東西后在進(jìn)行對比。等待適當(dāng)時(shí)機(jī)完成這個(gè) IRP,讓數(shù)
                據(jù)繼續(xù)傳遞,直到用戶收到為止。第二步我會(huì)結(jié)合下面小節(jié)來講。

              2) 出現(xiàn)于自己從頭建立 IRP,當(dāng)使用 IoAllocate() 或 IoBuildAsynchronousFsdRequest()
                創(chuàng)建 IRP 調(diào)用 IoCallDriver() 后,底層返回?cái)?shù)據(jù)到我這一層時(shí),我不想讓這
                個(gè) IRP 繼續(xù)向上延設(shè)備棧傳遞。因?yàn)檫@個(gè) IRP 就是在我這層次建立的,上層本
                就不知道有這么一個(gè) IRP。那么到這里我就要在 CompleteRoutine 中使用 IoFreeIrp()
                來釋放掉這個(gè) IRP,并不讓它繼續(xù)傳遞。這里一定要注意,在 CompleteRoutine
                函數(shù)返回后,這個(gè) IRP 已經(jīng)釋放了,如果這個(gè)時(shí)候在有任何關(guān)于這個(gè) IRP 的操作
                那么后果是災(zāi)難性的,必定導(dǎo)致 BSOD 錯(cuò)誤。前面 1) 小節(jié)給出的例子只完成了第
                一步這里繼續(xù)講第二步,第一步我重用這個(gè) IRP 得到了文件大小,那么這個(gè)時(shí)候雖
                然知道大小,但我還是無法知道這個(gè)文件是否被我加過密。這時(shí),我就需要在這里
                自己從頭建立一個(gè) IRP_MJ_READ 的 IRP 來讀取文件來判斷是否我加密過了的文件,
                如果是,則要減少相應(yīng)的大小,然后繼續(xù)返回。注意:這里的返回是指讓第一步的
                IRP 返回。而不是我們自己創(chuàng)建的。我們創(chuàng)建的都已經(jīng)在 CompleteRoutine 中銷
                毀了。



            8、關(guān)于完成 IRP 的動(dòng)作簡介

            當(dāng)一個(gè)底層驅(qū)動(dòng)調(diào)用了 IoCompleteRequest() 函數(shù)時(shí),基本上所有設(shè)備棧相關(guān) IRP 處理工
            作都是在它那里完成的。包括 IRP->Flags 的一些標(biāo)志的判斷,對 APC 的處理,拋出
            MULTIPLE_IRP_COMPLETE_REQUESTS 錯(cuò)誤等。當(dāng)它延設(shè)備棧一直調(diào)用驅(qū)動(dòng)所安裝的 CompleteRoutine
            時(shí),如果發(fā)現(xiàn) STATUS_MORE_PROCESSING_REQUIRED 這個(gè)標(biāo)志,則會(huì)停止向上繼續(xù)回滾。這也是
            為什么在 CompleteRoutine 中使用這個(gè)標(biāo)志即可暫停 IRP 的原因。



            9、關(guān)于 ObQueryNameString 的使用

            這個(gè)函數(shù)的使用,在有些環(huán)境下會(huì)有問題。它的上層函數(shù)是 ZwQueryObject()。在某些
            情況下會(huì)導(dǎo)致系統(tǒng)掛起,或者直接 BSOD。它是從 對象管理器中的 ObpRootDirectoryObject
            開始遍歷,通過 OBJECT_HEADER_TO_NAME_INFO 獲得對象名稱。今天問了下 PolyMeta
            好象是在處理 PIPE 時(shí)會(huì)掛啟,這個(gè)問題出現(xiàn)在 2000 系統(tǒng)。在 XP 上好象補(bǔ)丁了。


            10、關(guān)于重入問題

            其實(shí)這個(gè)問題在很久前的 IFS FAQ 里已經(jīng)介紹的很清楚,包括處理方法以及每種方法
            可能帶來的問題。IFS FAQ 里的 Q34 一共介紹了四種方法,包括自己從頭建立 IRP
            發(fā)送,使用 ShadowDevice,使用特征字符串,根據(jù)線程 ID,在 XP 下使用IoCreateFileSpecifyDeviceObjectHint() 函數(shù)。并且把以上幾種在不同環(huán)境
            下使用要處理的問題也做了簡單的介紹。且在 Q33 里介紹了在 CIFS 碰到的 FILE_COMPLETE_IF_OPLOCKED 問題的解決方法。

            posted on 2007-05-07 23:48 葉子 閱讀(2910) 評論(0)  編輯 收藏 引用 所屬分類: 驅(qū)動(dòng)開發(fā)

            久久人人爽人人爽人人爽| 无码超乳爆乳中文字幕久久| 精品一二三区久久aaa片| 久久99亚洲综合精品首页| 精品国产91久久久久久久| 久久久一本精品99久久精品66| 国产精品久久婷婷六月丁香| 久久国产成人精品国产成人亚洲| 国产精品久久久久久福利漫画 | 久久久久久久波多野结衣高潮| 久久综合久久综合久久| 久久精品成人国产午夜| 国产成人久久精品区一区二区| 精品无码久久久久久午夜| 777米奇久久最新地址| 国产日产久久高清欧美一区| 四虎国产精品免费久久5151| 亚洲狠狠综合久久| 狠狠色综合网站久久久久久久| 久久久久久久国产免费看| 四虎影视久久久免费| 久久婷婷色香五月综合激情 | 久久久久久午夜精品| 精品久久久久久久国产潘金莲| 久久天天躁狠狠躁夜夜2020一| 久久精品国产亚洲av麻豆小说| 精品国产福利久久久| 狠狠色伊人久久精品综合网| 久久综合九色综合欧美就去吻| 久久精品国产亚洲AV影院 | 一本久久综合亚洲鲁鲁五月天亚洲欧美一区二区 | 狠狠色丁香婷婷综合久久来| 国产成人无码精品久久久免费 | 亚洲精品99久久久久中文字幕| 亚洲精品白浆高清久久久久久| 好属妞这里只有精品久久| 久久精品国产亚洲Aⅴ蜜臀色欲| 亚洲午夜无码AV毛片久久| 久久亚洲中文字幕精品有坂深雪| 99久久人人爽亚洲精品美女| 久久久久亚洲AV片无码下载蜜桃|