Windows NT Driver >> C/C++ >> IOCTL
IRP
參考資訊:
1. Source Code
Input Output Control(IOCTL)是另一種可以跟驅動程式溝通的方式,本身帶有命令參數,驅動程式可以依據不一樣的命令做不一樣的事情,而除了命令以外,IOCTL更把Buffer切成Input和Output兩塊,單一次命令的運作就可以完成讀寫的動作,Buffer的屬性也是需要做設定才可以使用,分為Buffered、Direct和Neither這三種設定方式,感覺是不是跟File的Buffer設定方式很相似呢?沒錯,基本上是一樣的,雖然司徒講得有點複雜,不過,只要把Buffer的設定方式搞懂後,使用者就可以發現,整個運作原理是相當容易理解的。
當然,使用者可能好奇,IRP_MJ_READ和IRP_MJ_WRITE是否可以取代IRP_MJ_DEVICE_CONTROL的功能呢?答案:肯定是可以的,只要把資料做編碼,功能上是可以取代的,只是這樣的作法,把控制應用以及大量傳輸的概念都綁在一起,失去原有的設計本質,因此,如果以資料為傳輸目的,建議使用IRP_MJ_READ和IRQP_MJ_WRITE,而以控制應用為目的時,則建議使用IRP_MJ_DEVICE_CONTROL的方式。
至於IOCTL命令到底是什麼東西呢?其實就是一個編碼後的32Bit的數值,欄位如下所示:
#define CTL_CODE(DeviceType, Function, Transfer, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Transfer))
表格如下:
Ref. defining-i-o-control-codes
透過Microsoft的Macro(CTL_CODE)定義該數值,說穿了,只要App跟驅動程式都可以互相知道對方的意思(IOCTL命令)就可以,當然有一些是Microsoft定義保留的Global IOCTL,在使用上,需要特別注意一下。
對應的溝通管道:
Win32 API | Kernel IRP |
---|---|
CreateFile() | IRP_MJ_CREATE |
DeviceIoControl() | IRP_MJ_DEVICE_CONTROL |
CloseFile() | IRP_MJ_CLOSE |
取得方式:
IOCTL Code |
---|
(IrpStack) Parameters.DeviceIoControl.IoControlCode |
main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | #include <wdm.h> #define DEV_NAME L"\\Device\\MyDriver" #define SYM_NAME L"\\DosDevices\\MyDriver" #define IOCTL_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) void Unload ( PDRIVER_OBJECT pOurDriver) { UNICODE_STRING usSymboName; RtlInitUnicodeString (&usSymboName, L "\\DosDevices\\MyDriver" ); IoDeleteSymbolicLink (&usSymboName); IoDeleteDevice (pOurDriver->DeviceObject); } NTSTATUS IrpIOCTL ( PDEVICE_OBJECT pOurDevice, PIRP pIrp) { PIO_STACK_LOCATION psk = IoGetCurrentIrpStackLocation (pIrp); switch (psk->Parameters. DeviceIoControl .IoControlCode){ case IOCTL_TEST : DbgPrint ( "IOCTL_TEST" ); break ; } IoCompleteRequest (pIrp, IO_NO_INCREMENT ); return STATUS_SUCCESS ; } NTSTATUS IrpFile ( PDEVICE_OBJECT pOurDevice, PIRP pIrp) { PIO_STACK_LOCATION psk = IoGetCurrentIrpStackLocation (pIrp); switch (psk->MajorFunction){ case IRP_MJ_CREATE : DbgPrint ( "IRP_MJ_CREATE" ); break ; case IRP_MJ_CLOSE : DbgPrint ( "IRP_MJ_CLOSE" ); break ; } IoCompleteRequest (pIrp, IO_NO_INCREMENT ); return STATUS_SUCCESS ; } NTSTATUS DriverEntry ( PDRIVER_OBJECT pOurDriver, PUNICODE_STRING pOurRegistry) { PDEVICE_OBJECT pOurDevice= NULL ; UNICODE_STRING usDeviceName; UNICODE_STRING usSymboName; pOurDriver->MajorFunction[ IRP_MJ_CREATE ] = pOurDriver->MajorFunction[ IRP_MJ_CLOSE ] = IrpFile ; pOurDriver->MajorFunction[ IRP_MJ_DEVICE_CONTROL ] = IrpIOCTL ; pOurDriver-> DriverUnload = Unload ; RtlInitUnicodeString (&usDeviceName, L "\\Device\\MyDriver" ); IoCreateDevice (pOurDriver, 0, &usDeviceName, FILE_DEVICE_UNKNOWN , 0, FALSE , &pOurDevice); RtlInitUnicodeString (&usSymboName, L "\\DosDevices\\MyDriver" ); IoCreateSymbolicLink (&usSymboName, &usDeviceName); pOurDevice->Flags&= ~ DO_DEVICE_INITIALIZING ; pOurDevice->Flags|= DO_BUFFERED_IO ; return STATUS_SUCCESS ; } |
IrpIOCTL()僅列印IOCTL_TEST命令。
app.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #define INITGUID #include <windows.h> #include <winioctl.h> #include <strsafe.h> #include <setupapi.h> #include <stdio.h> #include <stdlib.h> #define IOCTL_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) int __cdecl main ( int argc, char * argv[]) { DWORD dwRet = 0; HANDLE hFile = NULL ; hFile = CreateFile ( "\\\\.\\MyDriver" , GENERIC_READ | GENERIC_WRITE , 0, NULL , OPEN_EXISTING , 0, NULL ); if (hFile == INVALID_HANDLE_VALUE ) { printf ( "failed to open mydriver" ); return 1; } DeviceIoControl (hFile, IOCTL_TEST , NULL , 0, NULL , 0, &dwRet, NULL ); CloseHandle (hFile); return 0; } |
結果