windows驅動開發基礎(一)使用者層與驅動層通訊------ioctl
阿新 • • 發佈:2021-02-05
技術標籤:windows核心
程式碼片段來自wdk7,工程路徑C:\WinDDK\7600.16385.1\src\general\ioctl\wdm,該工程描述了IOCTLs (METHOD_IN_DIRECT, METHOD_OUT_DIRECT, METHOD_NEITHER, and METHOD_BUFFERED)的使用方法。以下擷取關鍵部分,詳情請參考wdk.
4種ioctl 的特點
method_buffered:輸入和輸出緩衝區一樣,並且是由I/O manager 申請的,也就是說不是直接接觸使用者層傳入的緩衝區。
method_neither: 這種型別是直接傳入使用者層的輸入和輸出兩個緩衝區,在驅動中直接操作使用者層記憶體是比較危險的,為了安全處理,程式碼增加不是檢查。
method_in_direct: 從application 傳資料到驅動中。
method_out_direct:從driver傳資料到application。
NTSTATUS SioctlDeviceControl( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /*++ Routine Description: This routine is called by the I/O system to perform a device I/O control function. Arguments: DeviceObject - a pointer to the object that represents the device that I/O is to be done on. Irp - a pointer to the I/O Request Packet for this request. Return Value: NT status code --*/ { PIO_STACK_LOCATION irpSp;// Pointer to current stack location NTSTATUS ntStatus = STATUS_SUCCESS;// Assume success ULONG inBufLength; // Input buffer length ULONG outBufLength; // Output buffer length PCHAR inBuf, outBuf; // pointer to Input and output buffer PCHAR data = "This String is from Device Driver !!!"; size_t datalen = strlen(data)+1;//Length of data including null PMDL mdl = NULL; PCHAR buffer = NULL; UNREFERENCED_PARAMETER(DeviceObject); PAGED_CODE(); irpSp = IoGetCurrentIrpStackLocation( Irp ); inBufLength = irpSp->Parameters.DeviceIoControl.InputBufferLength; outBufLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; if (!inBufLength || !outBufLength) { ntStatus = STATUS_INVALID_PARAMETER; goto End; } // // Determine which I/O control code was specified. // switch ( irpSp->Parameters.DeviceIoControl.IoControlCode ) { case IOCTL_SIOCTL_METHOD_BUFFERED: // // In this method the I/O manager allocates a buffer large enough to // to accommodate larger of the user input buffer and output buffer, // assigns the address to Irp->AssociatedIrp.SystemBuffer, and // copies the content of the user input buffer into this SystemBuffer // SIOCTL_KDPRINT(("Called IOCTL_SIOCTL_METHOD_BUFFERED\n")); PrintIrpInfo(Irp); // // Input buffer and output buffer is same in this case, read the // content of the buffer before writing to it // inBuf = Irp->AssociatedIrp.SystemBuffer; outBuf = Irp->AssociatedIrp.SystemBuffer; // // Read the data from the buffer // SIOCTL_KDPRINT(("\tData from User :")); // // We are using the following function to print characters instead // DebugPrint with %s format because we string we get may or // may not be null terminated. // PrintChars(inBuf, inBufLength); // // Write to the buffer over-writes the input buffer content // RtlCopyBytes(outBuf, data, outBufLength); SIOCTL_KDPRINT(("\tData to User : ")); PrintChars(outBuf, datalen ); // // Assign the length of the data copied to IoStatus.Information // of the Irp and complete the Irp. // Irp->IoStatus.Information = (outBufLength<datalen?outBufLength:datalen); // // When the Irp is completed the content of the SystemBuffer // is copied to the User output buffer and the SystemBuffer is // is freed. // break; case IOCTL_SIOCTL_METHOD_NEITHER: // // In this type of transfer the I/O manager assigns the user input // to Type3InputBuffer and the output buffer to UserBuffer of the Irp. // The I/O manager doesn't copy or map the buffers to the kernel // buffers. Nor does it perform any validation of user buffer's address // range. // SIOCTL_KDPRINT(("Called IOCTL_SIOCTL_METHOD_NEITHER\n")); PrintIrpInfo(Irp); // // A driver may access these buffers directly if it is a highest level // driver whose Dispatch routine runs in the context // of the thread that made this request. The driver should always // check the validity of the user buffer's address range and check whether // the appropriate read or write access is permitted on the buffer. // It must also wrap its accesses to the buffer's address range within // an exception handler in case another user thread deallocates the buffer // or attempts to change the access rights for the buffer while the driver // is accessing memory. // inBuf = irpSp->Parameters.DeviceIoControl.Type3InputBuffer; outBuf = Irp->UserBuffer; // // Access the buffers directly if only if you are running in the // context of the calling process. Only top level drivers are // guaranteed to have the context of process that made the request. // try { // // Before accessing user buffer, you must probe for read/write // to make sure the buffer is indeed an userbuffer with proper access // rights and length. ProbeForRead/Write will raise an exception if it's otherwise. // ProbeForRead( inBuf, inBufLength, sizeof( UCHAR ) ); // // Since the buffer access rights can be changed or buffer can be freed // anytime by another thread of the same process, you must always access // it within an exception handler. // SIOCTL_KDPRINT(("\tData from User :")); PrintChars(inBuf, inBufLength); } except(EXCEPTION_EXECUTE_HANDLER) { ntStatus = GetExceptionCode(); SIOCTL_KDPRINT(( "Exception while accessing inBuf 0X%08X in METHOD_NEITHER\n", ntStatus)); break; } // // If you are accessing these buffers in an arbitrary thread context, // say in your DPC or ISR, if you are using it for DMA, or passing these buffers to the // next level driver, you should map them in the system process address space. // First allocate an MDL large enough to describe the buffer // and initilize it. Please note that on a x86 system, the maximum size of a buffer // that an MDL can describe is 65508 KB. // mdl = IoAllocateMdl(inBuf, inBufLength, FALSE, TRUE, NULL); if (!mdl) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; break; } try { // // Probe and lock the pages of this buffer in physical memory. // You can specify IoReadAccess, IoWriteAccess or IoModifyAccess // Always perform this operation in a try except block. // MmProbeAndLockPages will raise an exception if it fails. // MmProbeAndLockPages(mdl, UserMode, IoReadAccess); } except(EXCEPTION_EXECUTE_HANDLER) { ntStatus = GetExceptionCode(); SIOCTL_KDPRINT(( "Exception while locking inBuf 0X%08X in METHOD_NEITHER\n", ntStatus)); IoFreeMdl(mdl); break; } // // Map the physical pages described by the MDL into system space. // Note: double mapping the buffer this way causes lot of // system overhead for large size buffers. // buffer = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority ); if (!buffer) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; MmUnlockPages(mdl); IoFreeMdl(mdl); break; } // // Now you can safely read the data from the buffer. // SIOCTL_KDPRINT(("\tData from User (SystemAddress) : ")); PrintChars(buffer, inBufLength); // // Once the read is over unmap and unlock the pages. // MmUnlockPages(mdl); IoFreeMdl(mdl); // // The same steps can be followed to access the output buffer. // mdl = IoAllocateMdl(outBuf, outBufLength, FALSE, TRUE, NULL); if (!mdl) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; break; } try { // // Probe and lock the pages of this buffer in physical memory. // You can specify IoReadAccess, IoWriteAccess or IoModifyAccess. // MmProbeAndLockPages(mdl, UserMode, IoWriteAccess); } except(EXCEPTION_EXECUTE_HANDLER) { ntStatus = GetExceptionCode(); SIOCTL_KDPRINT(( "Exception while locking outBuf 0X%08X in METHOD_NEITHER\n", ntStatus)); IoFreeMdl(mdl); break; } buffer = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority ); if (!buffer) { MmUnlockPages(mdl); IoFreeMdl(mdl); ntStatus = STATUS_INSUFFICIENT_RESOURCES; break; } // // Write to the buffer // RtlCopyBytes(buffer, data, outBufLength); SIOCTL_KDPRINT(("\tData to User : %s\n", buffer)); PrintChars(buffer, datalen); MmUnlockPages(mdl); // // Free the allocated MDL // IoFreeMdl(mdl); // // Assign the length of the data copied to IoStatus.Information // of the Irp and complete the Irp. // Irp->IoStatus.Information = (outBufLength<datalen?outBufLength:datalen); break; case IOCTL_SIOCTL_METHOD_IN_DIRECT: // // In this type of transfer, the I/O manager allocates a system buffer // large enough to accommodatethe User input buffer, sets the buffer address // in Irp->AssociatedIrp.SystemBuffer and copies the content of user input buffer // into the SystemBuffer. For the user output buffer, the I/O manager // probes to see whether the virtual address is readable in the callers // access mode, locks the pages in memory and passes the pointer to // MDL describing the buffer in Irp->MdlAddress. // SIOCTL_KDPRINT(("Called IOCTL_SIOCTL_METHOD_IN_DIRECT\n")); PrintIrpInfo(Irp); inBuf = Irp->AssociatedIrp.SystemBuffer; SIOCTL_KDPRINT(("\tData from User in InputBuffer: ")); PrintChars(inBuf, inBufLength); // // To access the output buffer, just get the system address // for the buffer. For this method, this buffer is intended for transfering data // from the application to the driver. // buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); if (!buffer) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; break; } SIOCTL_KDPRINT(("\tData from User in OutputBuffer: ")); PrintChars(buffer, outBufLength); // // Return total bytes read from the output buffer. // Note OutBufLength = MmGetMdlByteCount(Irp->MdlAddress) // Irp->IoStatus.Information = MmGetMdlByteCount(Irp->MdlAddress); // // NOTE: Changes made to the SystemBuffer are not copied // to the user input buffer by the I/O manager // break; case IOCTL_SIOCTL_METHOD_OUT_DIRECT: // // In this type of transfer, the I/O manager allocates a system buffer // large enough to accommodate the User input buffer, sets the buffer address // in Irp->AssociatedIrp.SystemBuffer and copies the content of user input buffer // into the SystemBuffer. For the output buffer, the I/O manager // probes to see whether the virtual address is writable in the callers // access mode, locks the pages in memory and passes the pointer to MDL // describing the buffer in Irp->MdlAddress. // SIOCTL_KDPRINT(("Called IOCTL_SIOCTL_METHOD_OUT_DIRECT\n")); PrintIrpInfo(Irp); inBuf = Irp->AssociatedIrp.SystemBuffer; SIOCTL_KDPRINT(("\tData from User : ")); PrintChars(inBuf, inBufLength); // // To access the output buffer, just get the system address // for the buffer. For this method, this buffer is intended for transfering data // from the driver to the application. // buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); if (!buffer) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; break; } // // Write data to be sent to the user in this buffer // RtlCopyBytes(buffer, data, outBufLength); SIOCTL_KDPRINT(("\tData to User : ")); PrintChars(buffer, datalen); Irp->IoStatus.Information = (outBufLength<datalen?outBufLength:datalen); // // NOTE: Changes made to the SystemBuffer are not copied // to the user input buffer by the I/O manager // break; default: // // The specified I/O control code is unrecognized by this driver. // ntStatus = STATUS_INVALID_DEVICE_REQUEST; SIOCTL_KDPRINT(("ERROR: unrecognized IOCTL %x\n", irpSp->Parameters.DeviceIoControl.IoControlCode)); break; } End: // // Finish the I/O operation by simply completing the packet and returning // the same status as in the packet itself. // Irp->IoStatus.Status = ntStatus; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return ntStatus; }
使用者層請求,可以看到在使用者層的請求資料傳輸都是由DeviceIoControl加不同引數完成
// // Printing Input & Output buffer pointers and size // printf("InputBuffer Pointer = %p, BufLength = %d\n", InputBuffer, sizeof(InputBuffer)); printf("OutputBuffer Pointer = %p BufLength = %d\n", OutputBuffer, sizeof(OutputBuffer)); // // Performing METHOD_BUFFERED // StringCbCopy(InputBuffer, sizeof(InputBuffer), "This String is from User Application; using METHOD_BUFFERED"); printf("\nCalling DeviceIoControl METHOD_BUFFERED:\n"); memset(OutputBuffer, 0, sizeof(OutputBuffer)); bRc = DeviceIoControl ( hDevice, (DWORD) IOCTL_SIOCTL_METHOD_BUFFERED, &InputBuffer, (DWORD) strlen ( InputBuffer )+1, &OutputBuffer, sizeof( OutputBuffer), &bytesReturned, NULL ); if ( !bRc ) { printf ( "Error in DeviceIoControl : %d", GetLastError()); return; } printf(" OutBuffer (%d): %s\n", bytesReturned, OutputBuffer); // // Performing METHOD_NIETHER // printf("\nCalling DeviceIoControl METHOD_NEITHER\n"); StringCbCopy(InputBuffer, sizeof(InputBuffer), "This String is from User Application; using METHOD_NEITHER"); memset(OutputBuffer, 0, sizeof(OutputBuffer)); bRc = DeviceIoControl ( hDevice, (DWORD) IOCTL_SIOCTL_METHOD_NEITHER, &InputBuffer, (DWORD) strlen ( InputBuffer )+1, &OutputBuffer, sizeof( OutputBuffer), &bytesReturned, NULL ); if ( !bRc ) { printf ( "Error in DeviceIoControl : %d\n", GetLastError()); return; } printf(" OutBuffer (%d): %s\n", bytesReturned, OutputBuffer); // // Performing METHOD_IN_DIRECT // printf("\nCalling DeviceIoControl METHOD_IN_DIRECT\n"); StringCbCopy(InputBuffer, sizeof(InputBuffer), "This String is from User Application; using METHOD_IN_DIRECT"); StringCbCopy(OutputBuffer, sizeof(OutputBuffer), "This String is from User Application in OutBuffer; using METHOD_IN_DIRECT"); bRc = DeviceIoControl ( hDevice, (DWORD) IOCTL_SIOCTL_METHOD_IN_DIRECT, &InputBuffer, (DWORD) strlen ( InputBuffer )+1, &OutputBuffer, sizeof( OutputBuffer), &bytesReturned, NULL ); if ( !bRc ) { printf ( "Error in DeviceIoControl : : %d", GetLastError()); return; } printf(" Number of bytes transfered from OutBuffer: %d\n", bytesReturned); // // Performing METHOD_OUT_DIRECT // printf("\nCalling DeviceIoControl METHOD_OUT_DIRECT\n"); StringCbCopy(InputBuffer, sizeof(InputBuffer), "This String is from User Application; using METHOD_OUT_DIRECT"); memset(OutputBuffer, 0, sizeof(OutputBuffer)); bRc = DeviceIoControl ( hDevice, (DWORD) IOCTL_SIOCTL_METHOD_OUT_DIRECT, &InputBuffer, (DWORD) strlen ( InputBuffer )+1, &OutputBuffer, sizeof( OutputBuffer), &bytesReturned, NULL ); if ( !bRc ) { printf ( "Error in DeviceIoControl : : %d", GetLastError()); return; } printf(" OutBuffer (%d): %s\n", bytesReturned, OutputBuffer); CloseHandle ( hDevice );