激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C/C++ - 詳解應用程序與驅動程序通信DeviceIoControl

詳解應用程序與驅動程序通信DeviceIoControl

2021-11-15 14:50沉疴 C/C++

這種通信方式,就是驅動程序和應用程序自定義一種IO控制碼,然后調用DeviceIoControl函數,IO管理器會產生一個MajorFunction為IRP_MJ_DEVICE_CONTROL,MinorFunction為自己定義的控制碼的IRP,系統就調用相應的處理IRP_MJ_DEVICE_CONTROL的派遣函數

一、定義IO控制碼 

其實可以看作是一種通信協議

看看CTL_CODE原型:

#define CTL_CODE( DeviceType, Function, Method, Access ) ( 
  ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) 
  )

可以看到,這個宏四個參數,自然是一個32位分成了4部分,高16位存儲設備類型,14~15位訪問權限,2~13位操作功能,最后0,1兩位就是確定緩沖區是如何與I/O和文件系統數據緩沖區進行數據傳遞方式,最常見的就是METHOD_BUFFERED。

自定義CTL_CODE:

#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)

IOCTL_Device_Function:生成的IRP的MinorFunction

DeviceType:設備對象的類型。設備類型可參考:http://blog.csdn.net/liyun123gx/article/details/38058965

Function :自定義的IO控制碼。自己定義時取0x800到0xFFF,因為0x0到0x7FF是微軟保留的。

Method :數據的操作模式。

METHOD_BUFFERED:緩沖區模式

METHOD_IN_DIRECT:直接寫模式

METHOD_OUT_DIRECT:直接讀模式

METHOD_NEITHER :Neither模式

Access:訪問權限,可取值有:

FILE_ANY_ACCESS:表明用戶擁有所有的權限

FILE_READ_DATA:表明權限為只讀

FILE_WRITE_DATA:表明權限為可寫

也可以 FILE_WRITE_DATA | FILE_READ_DATA:表明權限為可讀可寫,但還沒達到FILE_ANY_ACCESS的權限。

繼續介紹這個緩沖區數據傳遞方式Method:

Method表示Ring3/Ring0的通信中的內存訪問方式,有四種方式:

#defineMETHOD_BUFFERED0

#defineMETHOD_IN_DIRECT1

#defineMETHOD_OUT_DIRECT2

#defineMETHOD_NEITHER3

(1)如果使用METHOD_BUFFERED,表示系統將用戶的輸入輸出都經過pIrp->AssociatedIrp.SystemBuffer來緩沖,因此這種方式的通信比較安全。

METHOD_BUFFERED方式相當于對Ring3的輸入輸出都進行了緩沖。

METHOD_BUFFERED方式:

詳解應用程序與驅動程序通信DeviceIoControl

(2)如果使用METHOD_IN_DIRECT或METHOD_OUT_DIRECT方式,表示系統會將輸入緩沖在pIrp->AssociatedIrp.SystemBuffer中,并將輸出緩沖區鎖定,然后在內核模式下重新映射一段地址,這樣也是比較安全的。

METHOD_IN_DIRECT和METHOD_OUT_DIRECT可稱為"直接方式",是指系統依然對Ring3的輸入緩沖區進行緩沖,但是對Ring3的輸出緩沖區并沒有緩沖,而是在內核中進行了鎖定。這樣Ring3輸出緩沖區在驅動程序完成I/O請求之前,都是無法訪問的,從一定程度上保障了安全性。

這兩種方式,對于Ring3的輸入緩沖區和METHOD_BUFFERED方式是一致的。對于Ring3的輸出緩沖區,首先由系統鎖定,并使用pIrp->MdlAddress來描述這段內存,驅動程序需要使用MmGetSystemAddressForMdlSafe函數將這段內存映射到內核內存地址(OutputBuffer),然后可以直接寫入OutputBuffer地址,最終在驅動派遣例程返回后,由系統解除這段內存的鎖定。

METHOD_IN_DIRECT和METHOD_OUT_DIRECT方式的內存訪問

METHOD_IN_DIRECT和METHOD_OUT_DIRECT方式的區別,僅在于打開設備的權限上,當以只讀權限打開設備時,METHOD_IN_DIRECT方式的IoControl將會成功,而METHOD_OUT_DIRECT方式將會失敗。如果以讀寫權限打開設備,兩種方式都會成功。

METHOD_IN_DIRECT和METHOD_OUT_DIRECT方式:

詳解應用程序與驅動程序通信DeviceIoControl

(3)如果使用METHOD_NEITHER方式,"其他方式",雖然通信的效率提高了,但是不夠安全。驅動的派遣函數中輸入緩沖區可以通過I/O堆棧(IO_STACK_LOCATION)的stack->Parameters.DeviceIo Control.Type3InputBuffer得到。輸出緩沖區可以通過pIrp->UserBuffer得到。由于驅動中的派遣函數不能保證傳遞進來的用戶輸入和輸出地址,因此最好不要直接去讀寫這些地址的緩沖區。應該在讀寫前使用ProbeForRead和ProbeForWrite函數探測地址是否可讀和可寫。

METHOD_ NEITHER方式是不進行緩沖的,在驅動中可以直接使用Ring3的輸入輸出內存地址,

驅動程序可以通過pIrpStack->Parameters.DeviceIoControl.Type3InputBuffer得到Ring3的輸入緩沖區地址(其中pIrpStack是IoGetCurrentIrpStackLocation(pIrp)的返回);通過pIrp-> UserBuffer得到Ring3的輸出緩沖區地址。

由于METHOD_NEITHER方式并不安全,因此最好對Type3InputBuffer讀取之前使用ProbeForRead函數進行探測,對UserBuffer寫入之前使用ProbeForWrite函數進行探測,當沒有發生異常時,再進行讀取和寫入操作。

METHOD_NEITHER方式:

詳解應用程序與驅動程序通信DeviceIoControl

二、定義驅動設備名,符號鏈接名

定義好了IO控制碼CTL_CODE,第二步驅動程序還要準備驅動設備名和符號鏈接名。

關于在Ring0層中要設置驅動設備名的同時還要設置符號鏈接名的原因,是因為只有符號鏈接名才可以被用戶模式下的應用程序識別。

windows下的設備是以"Device[設備名]”形式命名的。

例如磁盤分區的c盤,d盤的設備名稱就是"DeviceHarddiskVolume1”,"DeviceHarddiskVolume2”, 當然也可以不指定設備名稱。

如果IoCreateDevice中沒有指定設備名稱,那么I/O管理器會自動分配一個數字作為設備的名稱。

例如"Device0000001"。Device[設備名],不容易記憶,通常符號鏈接可以理解為設備的別名,更重要的是設備名,只能被內核模式下的其他驅動所識別,而別名可以被用戶模式下的應用程序識別,例如c盤,就是名為"c:"的符號鏈接,其真正的設備對象是"DeviceHarddiskVolume1”,所以在寫驅動時候,一般我們創建符號鏈接,即使驅動中沒有用到,這也算是一個好的習慣吧。

驅動中符號鏈接名是這樣寫的

L"??HelloDDK" --->??HelloDDK

或者

L"DosDevicesHelloDDK"--->DosDevicesHelloDDK

在應用程序中,符號鏈接名:

L"\.HelloDDK"-->.HelloDDK

DosDevices的符號鏈接名就是??, 所以"DosDevicesXXXX"其實就是??XXXX

#define DEVICE_OBJECT_NAME  L"DeviceBufferedIODeviceObjectName"
//設備與設備之間通信
#define DEVICE_LINK_NAME    L"DosDevicesBufferedIODevcieLinkName"
//設備與Ring3之間通信

三、將符號鏈接名與設備對象名稱關聯 ,等待IO控制碼

驅動程序要做的最后一步,先用IoCreateDevice函數創建設備對象,再用IoCreateSymbolicLink將符號鏈接名與設備對象名稱關聯,大功告成,等待IO控制碼。

 //創建設備對象名稱
RtlInitUnicodeString(&DeviceObjectName,DEVICE_OBJECT_NAME);
//創建設備對象
Status = IoCreateDevice(DriverObject,NULL,
    &DeviceObjectName,
    FILE_DEVICE_UNKNOWN,
    0, FALSE,
    &DeviceObject);
if (!NT_SUCCESS(Status))
{
    return Status;
}
 
//創建設備連接名稱
RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
//將設備連接名稱與設備名稱關聯
Status = IoCreateSymbolicLink(&DeviceLinkName,&DeviceObjectName);
 
if (!NT_SUCCESS(Status))
{
    IoDeleteDevice(DeviceObject);
    return Status;
}       

四、應用程序獲取設備句柄,發送IO控制碼

驅動程序鋪墊打理好之后,應用程序就可以由符號鏈接名通過CreateFile函數獲取到設備句柄DeviceHandle,再用本場的主角,DeviceIoControl通過這個DeviceHandle發送控制碼了。

先看看這兩個函數:

BOOL WINAPI DeviceIoControl(
  _In_         HANDLE hDevice,       //CreateFile函數打開的設備句柄
  _In_         DWORD dwIoControlCode,//自定義的控制碼
  _In_opt_     LPVOID lpInBuffer,    //輸入緩沖區
  _In_         DWORD nInBufferSize,  //輸入緩沖區的大小
  _Out_opt_    LPVOID lpOutBuffer,   //輸出緩沖區
  _In_         DWORD nOutBufferSize, //輸出緩沖區的大小
  _Out_opt_    LPDWORD lpBytesReturned, //實際返回的字節數,對應驅動程序中pIrp->IoStatus.Information。
  _Inout_opt_  LPOVERLAPPED lpOverlapped //重疊操作結構指針。同步設為NULL,DeviceIoControl將進行阻塞調用;否則,應在編程時按異步操作設計
);
HANDLE CreateFile(
  LPCTSTR lpFileName,                         //打開的文件名
  DWORD dwDesiredAccess,                    //訪問權限
  DWORD dwShareMode,                      //共享模式
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,   //安全屬性
  DWORD dwCreationDisposition,               //文件存在與不存在時的文件創建模式
  DWORD dwFlagsAndAttributes,                //文件屬性設定(隱藏、只讀、壓縮、指定為系統文件等)
  HANDLE hTemplateFile                       //文件副本句柄
);

五、總結DeviceIoControl的通信流程

1.驅動程序和應用程序自定義好IO控制碼 (CTL_CODE宏 四個參數,32位,4部分,存儲設備類型,訪問權限,操作功能,緩沖區數據傳遞方式(四種))

2.驅動程序定義驅動設備名,符號鏈接名, 將符號鏈接名與設備對象名稱關聯 ,等待IO控制碼(IoCreateDevice,IoCreateSymbolicLink)

3.應用程序由符號鏈接名通過CreateFile函數獲取到設備句柄DeviceHandle,再用本場的主角,DeviceIoControl通過這個設備句柄發送控制碼給派遣函數。

六、源代碼

BufferedIO.h

#pragma once
#include <ntifs.h>

#define CTL_SYS 
    CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)

#define DEVICE_OBJECT_NAME  L"DeviceBufferedIODeviceObjectName"
//設備與設備之間通信
#define DEVICE_LINK_NAME    L"DosDevicesBufferedIODevcieLinkName"
//設備與Ring3之間通信
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
NTSTATUS PassThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp);
NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp);

BufferedIO.c

#include "BufferedIO.h"

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
    NTSTATUS Status = STATUS_SUCCESS;
    PDEVICE_OBJECT  DeviceObject = NULL;
    UNICODE_STRING  DeviceObjectName;
    UNICODE_STRING  DeviceLinkName;
    ULONG           i;
    //   棧
    //   堆
    //   全局(global Static Const)
    DriverObject->DriverUnload = DriverUnload;
 
    //創建設備對象名稱
    RtlInitUnicodeString(&DeviceObjectName,DEVICE_OBJECT_NAME);
 
    //創建設備對象
    Status = IoCreateDevice(DriverObject,NULL,
        &DeviceObjectName,
        FILE_DEVICE_UNKNOWN,
        0, FALSE,
        &DeviceObject);
    if (!NT_SUCCESS(Status))
    {
        return Status;
    }
    //創建設備連接名稱
    RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
 
    //將設備連接名稱與設備名稱關聯
    Status = IoCreateSymbolicLink(&DeviceLinkName,&DeviceObjectName);
 
    if (!NT_SUCCESS(Status))
    {
        IoDeleteDevice(DeviceObject);
        return Status;
    }
    //設計符合我們代碼的派遣歷程
    for (i=0;i<IRP_MJ_MAXIMUM_FUNCTION;i++)
    {
        DriverObject->MajorFunction[i] = PassThroughDispatch;   //函數指針
    }
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ControlThroughDispatch;
    return Status;
}
//派遣歷程
NTSTATUS PassThroughDispatch(PDEVICE_OBJECT  DeviceObject,PIRP Irp)
{
    Irp->IoStatus.Status = STATUS_SUCCESS;     //LastError()
    Irp->IoStatus.Information = 0;             //ReturnLength
    IoCompleteRequest(Irp, IO_NO_INCREMENT);   //將Irp返回給Io管理器
    return STATUS_SUCCESS;
}
NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp)
{
    NTSTATUS Status;
    ULONG_PTR Informaiton = 0;
    PVOID InputData = NULL;
    ULONG InputDataLength = 0;
    PVOID OutputData = NULL;
    ULONG OutputDataLength = 0;
    ULONG IoControlCode = 0;
    PIO_STACK_LOCATION  IoStackLocation = IoGetCurrentIrpStackLocation(Irp);  //Irp堆棧  
    IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;
    InputData  = Irp->AssociatedIrp.SystemBuffer;
    OutputData = Irp->AssociatedIrp.SystemBuffer;
    InputDataLength  = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
    OutputDataLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
    switch (IoControlCode)
    {
    case CTL_SYS:
    {
        if (InputData != NULL&&InputDataLength > 0)
        {
            DbgPrint("%s
", InputData);
        }
        if (OutputData != NULL&&OutputDataLength >= strlen("Ring0->Ring3") + 1)
        {
            memcpy(OutputData, "Ring0->Ring3", strlen("Ring0->Ring3") + 1);
            Status = STATUS_SUCCESS;
            Informaiton = strlen("Ring0->Ring3") + 1;
        }
        else
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;   //內存不夠
            Informaiton = 0;
        }
        break;
    }
    default:
        break;
    }
    Irp->IoStatus.Status = Status;             //Ring3 GetLastError();
    Irp->IoStatus.Information = Informaiton;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);  //將Irp返回給Io管理器
    return Status;                            //Ring3 DeviceIoControl()返回值
}
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
    UNICODE_STRING  DeviceLinkName;
    PDEVICE_OBJECT  v1 = NULL;
    PDEVICE_OBJECT  DeleteDeviceObject = NULL;
     
    RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
    IoDeleteSymbolicLink(&DeviceLinkName);
 
    DeleteDeviceObject = DriverObject->DeviceObject;
    while (DeleteDeviceObject != NULL)
    {
        v1 = DeleteDeviceObject->NextDevice;
        IoDeleteDevice(DeleteDeviceObject);
        DeleteDeviceObject = v1;
    }
}

IO.cpp

// 緩沖區IO.cpp : 定義控制臺應用程序的入口點。
//
 
#include "stdafx.h"
#include <windows.h>
#define DEVICE_LINK_NAME    L"\.BufferedIODevcieLinkName"
 
#define CTL_SYS 
    CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)
int main()
{
    HANDLE DeviceHandle = CreateFile(DEVICE_LINK_NAME,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if (DeviceHandle==INVALID_HANDLE_VALUE)
    {
        return 0;
    }
    char BufferData = NULL;
    DWORD ReturnLength = 0;
    BOOL IsOk = DeviceIoControl(DeviceHandle, CTL_SYS,
        "Ring3->Ring0",
        strlen("Ring3->Ring0")+1,
        (LPVOID)BufferData,
        0,
        &ReturnLength,
        NULL);
    if (IsOk == FALSE)
    {
        int LastError = GetLastError();
 
        if (LastError == ERROR_NO_SYSTEM_RESOURCES)
        {
            char BufferData[MAX_PATH] = { 0 };
            IsOk = DeviceIoControl(DeviceHandle, CTL_SYS,
                "Ring3->Ring0",
                strlen("Ring3->Ring0") + 1,
                (LPVOID)BufferData,
                MAX_PATH,
                &ReturnLength,
                NULL);
 
            if (IsOk == TRUE)
            {
                printf("%s
", BufferData);
            }
        }
    }
    if (DeviceHandle != NULL)
    {
        CloseHandle(DeviceHandle);
        DeviceHandle = NULL;
    }
    printf("Input AnyKey To Exit
");
 
    getchar();
    return 0;
}

以上就是詳解應用程序與驅動程序通信DeviceIoControl的詳細內容,更多關于應用程序 驅動程序通信 DeviceIoControl的資料請關注服務器之家其它相關文章!

原文鏈接:https://www.cnblogs.com/lsh123/p/7354573.html

延伸 · 閱讀

精彩推薦
  • C/C++學習C++編程的必備軟件

    學習C++編程的必備軟件

    本文給大家分享的是作者在學習使用C++進行編程的時候所用到的一些常用的軟件,這里推薦給大家...

    謝恩銘10102021-05-08
  • C/C++c++ 單線程實現同時監聽多個端口

    c++ 單線程實現同時監聽多個端口

    這篇文章主要介紹了c++ 單線程實現同時監聽多個端口的方法,幫助大家更好的理解和學習使用c++,感興趣的朋友可以了解下...

    源之緣11542021-10-27
  • C/C++詳解c語言中的 strcpy和strncpy字符串函數使用

    詳解c語言中的 strcpy和strncpy字符串函數使用

    strcpy 和strcnpy函數是字符串復制函數。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數使用,感興趣的朋友跟隨小編要求看看吧...

    spring-go5642021-07-02
  • C/C++C語言中炫酷的文件操作實例詳解

    C語言中炫酷的文件操作實例詳解

    內存中的數據都是暫時的,當程序結束時,它們都將丟失,為了永久性的保存大量的數據,C語言提供了對文件的操作,這篇文章主要給大家介紹了關于C語言中文件...

    針眼_6702022-01-24
  • C/C++C語言實現電腦關機程序

    C語言實現電腦關機程序

    這篇文章主要為大家詳細介紹了C語言實現電腦關機程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    xiaocaidayong8482021-08-20
  • C/C++深入理解goto語句的替代實現方式分析

    深入理解goto語句的替代實現方式分析

    本篇文章是對goto語句的替代實現方式進行了詳細的分析介紹,需要的朋友參考下...

    C語言教程網7342020-12-03
  • C/C++C++之重載 重定義與重寫用法詳解

    C++之重載 重定義與重寫用法詳解

    這篇文章主要介紹了C++之重載 重定義與重寫用法詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下...

    青山的青6062022-01-04
  • C/C++C/C++經典實例之模擬計算器示例代碼

    C/C++經典實例之模擬計算器示例代碼

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關于C/C++經典實例之模擬計算器的相關資料,文中通過示...

    jia150610152021-06-07
主站蜘蛛池模板: 日韩欧美电影一区二区三区 | 欧美视频国产精品 | 91精品动漫在线观看 | 91亚洲精品一区二区福利 | 亚洲成人第一区 | 黄色av一区二区三区 | 久草热久| 久久精品一区二区三区国产主播 | 蜜桃免费在线 | 中文字幕亚洲一区二区三区 | 91高清完整版在线观看 | a免费毛片 | 中文字幕免费播放 | 欧美18一12sex性处hd | 久久tv免费国产高清 | 九九热精品免费视频 | 亚洲福利在线观看视频 | 日本欧美在线播放 | 我爱我色成人网 | 国产亚洲精品久久久久久网站 | 日本黄色大片免费观看 | 欧美淫视频 | 久久草在线视频 | chinesehdxxxx实拍 日韩电影视频 | 国产精品久久久久久238 | 中文字幕一区二区三区久久 | 成人资源在线 | 久久亚洲精选 | 视频一区二区中文字幕 | 毛片在哪看 | 在线成人看片 | 日本黄色免费播放 | 久久色在线 | 久久久久久久久久综合 | 日韩在线视频观看免费 | 香蕉视频18 | 欧美不卡视频在线观看 | 国产欧美日韩二区 | 日韩黄网站 | 一级毛片免费高清 | 精品国产一区二区三区免费 |