Windows內核-7-(IRP)I/O請求包
- 2021 年 8 月 22 日
- 筆記
- Windows內核安全與驅動開發
Windows內核-7-(IRP)I/O請求包
IRP(I/O Request Packet)就是一個進行I/O操作的請求包。
IRP是一個結構體,誰分配誰就得釋放,通常由執行體里的管理器,獲取內核驅動程式來分配。
IRP不是單獨的,只要創建了IRP就會跟著創建IRP的I/O棧,有一個棧是給內核驅動用的:

驅動需要調用IoGetCurrentIrpStackLocation函數來獲取內驅驅動對應的I/O棧。
I/O傳遞
I/O系統是以設備對象為中心,而不是以驅動對象為中心的。IRP可以在設備對象中傳來傳去:

有點類似於電腦網路的體系結構。但是不可否認的是I/O可以在設備直接進行各種傳遞,所以通常用來作為讀取文本數據。
具體的傳遞過程有點類似於棧。
IRP和I/O棧

這張圖是對前面第一張圖的細化。
分發常式(dispatch routine)
IRP最主要的一個作用就是拿來做分發常式,其實也就是分發的實例函數,就是對文件數據的傳遞。在我的理解里,就是對資訊的分發,讓大家一起用到數據。
所有的分發常式都有一個原型:
typedef NTSTATUS DRIVER_DISPATCH (
_In_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp);
分發常式必須保存在驅動對象的MajorFunction裡面,且需要對應和驅動的連接狀態:
| 狀態 | 描述 |
|---|---|
| IRP_MJ_CREATE | 獲取文件句柄,對應User下的CreateFile |
| IRP_MJ_CLOSE | 關閉文件句柄,對應User下的CloseHandle |
| IRP_MJ_READ | 對應ReadFile |
| IRP_MJ_WRITE | 對應WriteFile |
| IRP_MJ_DEVICE_CONTROL | 對應DeviceIoControl,即可讀又可寫 |
| IRP_MJ_INTERNAL_DEVICE_CONTROL | 和IRP_MJ_DEVICE_CONTROL差不多,但是只有Kernel可用 |
完成請求
一旦決定要處理IRP(意味著不會把該IRP傳遞給下一個設備對象),就必須要完成它。
比如:
NTSTATUS MyDispatchRoutine(PDEVICE_OBJECT, PIRP Irp) {
//...
Irp->IoStatus.Status = STATUS_XXX;
Irp->IoStatus.Information = NumberOfBytesTransfered; // depends on request type
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_XXX;
}
//這裡的IoCompleteRequest就是必須的
安全IRP操作
由於執行緒原因,很有可能一個去讀一個去寫,導致兩個只有一個拿到了想要的數據。
還有頁面記憶體的原因,修改的時候有可能一起修改了。
內核提供了兩種比較安全的處理方式:
1 緩衝I/O
2 直接I/O
緩衝I/O
要使用緩衝I/O只需要在設備對象的Flag欄位裡面添加一個DO_BUFFERED_IO
DeviceObject->Flags |= DO_BUFFERED_IO
緩衝I/O的原理就是在系統記憶體中開闢一段緩衝區,作為一個中間人。
比如說要寫,就先把內容寫到系統記憶體緩衝區里,然後系統記憶體緩衝區再寫入/讀取到緩衝區裡面。





直接I/O
直接緩衝採用物理記憶體映射來處理,將User和Kernel的緩衝區都指向物理記憶體裡面,並且鎖定在記憶體里,然後讀寫都採用它。這樣的好處是減少了調用的開銷。但是佔了部分記憶體。






IRP_MJ_DEVICE_CONTROL 調度
前面的緩衝和直接I/O是讀或寫操作,這裡的IRP_MJ_DEVICE_CONTROL操作是基於控制程式碼來選擇讀或寫兩個都可以的。
User態的IRP_MJ_DEVICE_CONTROL 和Kernel態的調用函數原型差不多:
BOOL DeviceIoControl(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
);
dwIoControlCode:控制程式碼,由CTL_CODE宏的四個參數來處理。
Zero驅動程式
Client+Kernel的一個練手項目:
對於寫請求,會把緩衝區消耗掉
源程式碼

