內核與應用通訊的幾種方式-轉載

轉載於: https://www.cnblogs.com/endenvor/p/9057856.html 感謝作者的詳細記錄.
內容如下做個備份:

應用程序與驅動程序據我所知,細分可以分6種,ReadFile,WirteFile方式的緩衝區設備讀寫,直接方式讀寫,和其他方式讀寫。Io設備控制操作(即DeviceControl)的緩衝內存模式IOCTL,直接內存方式的IOCTL,其他內存方式的IOCTL!當然還有一種就是創建文件,然後文件讀寫也應該算是一種通信吧,這裏不討論這個!

1,緩衝區方式設備讀寫:

在創建Device後,須要指定方式爲Device的Flags有DO_BUFFERED_IO!通過應用層Api函數ReadFile,WriteFile,等函數,ntoskrnl.exe創建Irp後,ReadFile和WriteFile參數的緩衝區就在irp->AssociatedIrp.Systembuffer,同時要求讀寫的偏移量,和長度都在PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp) 數據類型中的stack->Parameters.Read.Length,stack->Parameters.Read.ByteOffset(該類型爲Large_Interge類型),

2,直接方式讀寫

在創建Device後,須要指定方式爲Device的Flags有DO_DIRECT_IO!通過應用層APi函數ReadFile,WriteFile等函數,ntoskrnl.exe創建的Irp後,ReadFile和WriteFile參數的緩衝區將被鎖住,然後操作系統將這段緩衝區在內核模式地址再次映射一遍,這樣應用層的緩衝區和內存層的就指向同一個物理內存!而內核模式用MDL數據結構記錄這段內存,這個虛擬內存大小在MmGetByteCount(pIrp->MdlAddress),首地址在MmGetMdlVirtualAddress(pIrp->MdlAddress);偏移量爲MmGetMdlByteOffset(pIrp->MdlAddress)(這裏的偏移量不是文件讀寫的偏移量,而是在MDL中的偏移量)然後文件的長度還是stack->Parameters.Read.Length,這個值和MmGetByteCount(pIrp->MdlAddress)是一樣的,要不然就出錯了,而真正的讀寫偏移量還是在stack->Parameters.Read.ByteOffset!這裏無論是讀還是寫,都要得到MDL在內核模式下的映射,因此還要用MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority)將其轉化爲內核模式,然後可以讀寫該地址,就會轉化到應用層相應的內存!!

3其他方式讀寫

這種方式很少用到,在創建Device後,Flags既不標誌DO_BUFFERED_IO也不標誌DO_DIRECT_IO,ReadFile和WriteFile提供的緩衝區內存地址,可以再IRP的pIrp->UserBuffer字段得到,而長度和偏移量還是在stack->Paameters.Read中,但是用這種方法須要注意的是ReadFile可能把空指針地址或者非法地址傳遞給驅動程序,因此驅動程序使用用戶模式地址錢須要檢查是否可讀或者可寫,可以用ProbeForWrite或者ProbeForWrite函數和try模塊。

這裏有個問題困擾着我,就是應用層用GetFileSize函數時用第一種方法處理該Irp時結果是正確的,就是衝pIrp->AssociatedIrp.SystemBuffer;得到PFILE_STANDARD_INFORMATION結構指針,然後講該結構的EndOfFile設置爲你自己文件的長度,然後結束該IRP,GetFileSize就能得到正確的文件長度,但是用方法三就是的時候用GetFileSize,我同樣用pIrp->UserBufer得到PFILE_STANDARD_INFORMATION結構指針,然後設置後,在GetFileSize裏面去得不到正確的長度!!這個是爲什麼?還有就是該Irp的CurrentStackLocation-stack,該字段裏面的stack->Parameters.QueryFile.Length指的是什麼,顯示的都是24,無論文長度爲多少,還是用哪種方法都市24,很鬱悶!!!

下面是方式都是用IO設備控制操作的方法基本上與上面3中相對應!

對於IO設備控制操作,不知道爲什麼可以用設置DO_DIRECT_IO後就能對三種方式都適用,而且stack->Parameters.Read.Length/Write.Length 都是對應應用層的DeviceIoControl函數中的第6個參數也就是輸出緩衝區!DeviceIoControl函數的輸入輸出緩衝區長度都再stack->Parameters.DeviceioControl.InputBufferLength/OutputBufferLength中

4緩衝內存IOCTL,在DeviceIoControl函數第二個參數的時候,使用CTL_CODE來產生該常數,其中Method字段設置爲METHOD_BUFFERED,在內核模式中輸入緩衝區很輸出緩衝區都爲pIrp->AssociatedIrp.SystemBuffer,

5直接方式IOCTL,在DeviceIoControl函數第二個參數的時候,使用CTL_CODE來產生該常數,其中Method字段設置爲METHOD_IN_DIRECT/METHOD_OUT_DIRECT(最好使用第一個,因爲第一個對所有的打開設備方式都通用)!對於第4中的區別是輸入緩衝區還在pIrp->AssocatedIrp.Systembuffer中,但是輸出緩衝區卻是pIrp->MdlAddress,因此在內核對輸出的寫應該寫在MmGetSystemAddressForMdlSafe(pIrp->MdlAddress)中,

6其他方式IOCTL,在DeviceIoControl函數第二個參數的時候,使用CTL_CODE來產生該常數,其中Method字段設置爲MEHTOD_NEITHER,輸入緩衝區爲stack->Parameters.DeviceIoControl.Tyep3InputBuffer;輸出緩衝區爲pIrp->Userbuffer

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章