• <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 文件過(guò)濾驅(qū)動(dòng)經(jīng)驗(yàn)總結(jié)

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

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


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

            無(wú)論你建立什么樣的 IRP,是 IRP_MJ_CREATE 也好還是 IRP_MJ_DIRECTORY_CONTROL
            也罷,最要提醒的就是一些標(biāo)志。不同的標(biāo)志會(huì)代來(lái)不同的結(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í)候也需要對(duì) FO_STREAM_FILE 類(lèi)型的文件做同樣處理。



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

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



            5、文件與目錄的判斷

            正確的方法在楚狂人的文檔里已經(jīng)說(shuō)過(guò)了,再補(bǔ)充一句。如果你的文件過(guò)濾驅(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 是
            沒(méi)錯(cuò)的。如果有問(wèn)題,相信是自己的問(wèn)題。關(guān)于有人提到在 FILE_OBJECT->Flags
            中的 FO_NO_INTERMEDIATE_BUFFERING 是否需要判斷,對(duì)此問(wèn)題的回答是只要
            你判斷了 IRP_NOCACHE 就不用再判斷 FILE_OBJECT 中的,因?yàn)樗罱K會(huì)
            設(shè)置 IRP->Flags 為 IRP_NOCACHE。關(guān)于你看到的諸如 IRP_DEFER_IO_COMPLETION
            等 IRP 不要去管它,因?yàn)樗皇且粋€(gè)過(guò)程。最終讀寫(xiě)還是如上所介紹。至于
            以上這些 IRP 哪個(gè)是由 CC MGR 發(fā)送的,哪些是由 I/O MGR 發(fā)送和在什么
            時(shí)候發(fā)送的,這個(gè)已經(jīng)有很多討論了,相信可以找到。



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

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

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

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


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

              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()
                來(lái)釋放掉這個(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í)候雖
                然知道大小,但我還是無(wú)法知道這個(gè)文件是否被我加過(guò)密。這時(shí),我就需要在這里
                自己從頭建立一個(gè) IRP_MJ_READ 的 IRP 來(lái)讀取文件來(lái)判斷是否我加密過(guò)了的文件,
                如果是,則要減少相應(yīng)的大小,然后繼續(xù)返回。注意:這里的返回是指讓第一步的
                IRP 返回。而不是我們自己創(chuàng)建的。我們創(chuàng)建的都已經(jīng)在 CompleteRoutine 中銷(xiāo)
                毀了。



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

            當(dāng)一個(gè)底層驅(qū)動(dòng)調(diào)用了 IoCompleteRequest() 函數(shù)時(shí),基本上所有設(shè)備棧相關(guān) IRP 處理工
            作都是在它那里完成的。包括 IRP->Flags 的一些標(biāo)志的判斷,對(duì) 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ì)有問(wèn)題。它的上層函數(shù)是 ZwQueryObject()。在某些
            情況下會(huì)導(dǎo)致系統(tǒng)掛起,或者直接 BSOD。它是從 對(duì)象管理器中的 ObpRootDirectoryObject
            開(kāi)始遍歷,通過(guò) OBJECT_HEADER_TO_NAME_INFO 獲得對(duì)象名稱。今天問(wèn)了下 PolyMeta
            好象是在處理 PIPE 時(shí)會(huì)掛啟,這個(gè)問(wèn)題出現(xiàn)在 2000 系統(tǒng)。在 XP 上好象補(bǔ)丁了。


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

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

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

            亚洲国产一成久久精品国产成人综合 | 久久噜噜电影你懂的| 69SEX久久精品国产麻豆| 亚洲综合婷婷久久| 久久久久无码专区亚洲av| 久久黄视频| 久久精品国产99久久无毒不卡| 国产精品久久久久久久| 婷婷久久综合九色综合绿巨人| 亚洲精品乱码久久久久久蜜桃图片| 精品国产乱码久久久久久郑州公司| 久久夜色撩人精品国产| 久久99国产综合精品免费| 久久有码中文字幕| 99久久99这里只有免费费精品| 亚洲国产精品一区二区三区久久 | 久久精品国产精品亚洲| 亚洲欧美日韩久久精品第一区| 久久国产三级无码一区二区 | 狠狠色丁香婷综合久久| 久久强奷乱码老熟女网站| 国产亚洲色婷婷久久99精品| 久久综合九色综合网站| 一本色道久久88加勒比—综合| 精品久久8x国产免费观看| 国产亚洲精久久久久久无码77777| 久久www免费人成看国产片| 久久精品国产69国产精品亚洲| 999久久久无码国产精品| 亚洲av伊人久久综合密臀性色| 久久夜色精品国产亚洲av| 久久亚洲天堂| 久久久久亚洲国产| 狠狠色丁香婷婷久久综合| 欧美一级久久久久久久大片| 久久久久一本毛久久久| 久久这里只有精品视频99| 亚洲人成无码网站久久99热国产| 久久久久这里只有精品| 久久久久亚洲AV无码专区首JN| 亚洲精品久久久www|