zoukankan      html  css  js  c++  java
  • Win64 驱动内核编程-2.基本框架(安装.通讯.HelloWorld)

    驱动安装,通讯,Hello World

    开发驱动的简单流程是这样,开发驱动安装程序,开发驱动程序,然后安装程序(或者其他程序)通过通讯给驱动传命令,驱动接到之后进行解析并且执行,然后把执行结果返回。

    驱动程序Hello World

    之前总结了驱动环境的搭建,这里就直接继续之前搭建好的环境创建项目,打开vs2015创建一个驱动项目:



    写代码之前先配置下编译选项:


    然后添加一个项目文件main.c(注意后缀是.c,前面名字无所谓可以不叫main),里面的内容如下(下面模板代码来源于网络,作者:胡文亮,我是在看他的资料学习,感谢这位前辈。)

    /*
    WIN64驱动开发模板
    作者:Tesla.Angela
    */
     
    //【0】包含的头文件,可以加入系统或自己定义的头文件
    #include <ntddk.h>
    #include <windef.h>
    #include <stdlib.h>
     
    //【1】定义符号链接,一般来说修改为驱动的名字即可
    #define	DEVICE_NAME	L"\Device\KrnlHW64"
    #define LINK_NAME	L"\DosDevices\KrnlHW64"
    #define LINK_GLOBAL_NAME	L"\DosDevices\Global\KrnlHW64"
     
    //【2】定义驱动功能号和名字,提供接口给应用程序调用
    #define IOCTL_IO_TEST	CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
    #define IOCTL_SAY_HELLO	CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
     
    //【3】驱动卸载的处理例程
    VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
    {
    UNICODE_STRING strLink;
    DbgPrint("[KrnlHW64]DriverUnload
    ");
    //删除符号连接和设备
    RtlInitUnicodeString(&strLink, LINK_NAME);
    IoDeleteSymbolicLink(&strLink);
    IoDeleteDevice(pDriverObj->DeviceObject);
    }
     
    //【4】IRP_MJ_CREATE对应的处理例程,一般不用管它
    NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
    {
    DbgPrint("[KrnlHW64]DispatchCreate
    ");
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
    }
     
    //【5】IRP_MJ_CLOSE对应的处理例程,一般不用管它
    NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
    {
    DbgPrint("[KrnlHW64]DispatchClose
    ");
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
    }
     
    //【6】IRP_MJ_DEVICE_CONTROL对应的处理例程,驱动最重要的函数之一,一般走正常途径调用驱动功能的程序,都会经过这个函数
    NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
    {
    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
    PIO_STACK_LOCATION pIrpStack;
    ULONG uIoControlCode;
    PVOID pIoBuffer;
    ULONG uInSize;
    ULONG uOutSize;
    DbgPrint("[KrnlHW64]DispatchIoctl
    ");
    //获得IRP里的关键数据
    pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
    //这个就是传说中的控制码
    uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
    //输入和输出的缓冲区(DeviceIoControl的InBuffer和OutBuffer都是它)
    pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
    //EXE发送传入数据的BUFFER长度(DeviceIoControl的nInBufferSize)
    uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
    //EXE接收传出数据的BUFFER长度(DeviceIoControl的nOutBufferSize)
    uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
    switch(uIoControlCode)
    {
    //在这里加入接口
    case IOCTL_IO_TEST:
    {
    DWORD dw=0;
    //输入
    memcpy(&dw,pIoBuffer,sizeof(DWORD));
    //使用
    dw++;
    //输出
    memcpy(pIoBuffer,&dw,sizeof(DWORD));
    //返回通信状态
    status = STATUS_SUCCESS;
    break;
    }
    case IOCTL_SAY_HELLO:
    {
    DbgPrint("[KrnlHW64]IOCTL_SAY_HELLO
    ");
    status = STATUS_SUCCESS;
    break;
    }
    }
    //这里设定DeviceIoControl的*lpBytesReturned的值(如果通信失败则返回0长度)
    if(status == STATUS_SUCCESS)
    pIrp->IoStatus.Information = uOutSize;
    else
    pIrp->IoStatus.Information = 0;
    //这里设定DeviceIoControl的返回值是成功还是失败
    pIrp->IoStatus.Status = status;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return status;
    }
     
    //【7】驱动加载的处理例程,里面进行了驱动的初始化工作
    NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
    {
    NTSTATUS status = STATUS_SUCCESS;
    UNICODE_STRING ustrLinkName;
    UNICODE_STRING ustrDevName;  
    PDEVICE_OBJECT pDevObj;
    //设置分发函数和卸载例程
    pDriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
    pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
    pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;
    pDriverObj->DriverUnload = DriverUnload;
    //创建一个设备
    RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);
    status = IoCreateDevice(pDriverObj, 0, &ustrDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
    if(!NT_SUCCESS(status))	return status;
    //判断支持的WDM版本,其实这个已经不需要了,纯属WIN9X和WINNT并存时代的残留物
    if(IoIsWdmVersionAvailable(1, 0x10))
    RtlInitUnicodeString(&ustrLinkName, LINK_GLOBAL_NAME);
    else
    RtlInitUnicodeString(&ustrLinkName, LINK_NAME);
    //创建符号连接
    status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);  
    if(!NT_SUCCESS(status))
    {
    IoDeleteDevice(pDevObj); 
    return status;
    }
    DbgPrint("[KrnlHW64]DriverEntry
    ");
    //返回加载驱动的状态(如果返回失败,驱动讲被清除出内核空间)
    return STATUS_SUCCESS;
    }
     

    然后右键编译工程,会出现这个错误提示:


    然后把这个文件删除:


    继续编译,还是不会过:继续改另一个配置文件选项:


    然后就可以了:


    然后是驱动安装程序:

    目前如果没有仔细看上面的那个基本模板代码,需要回去仔细看下。大体了解细节,尤其是里面的注释。

    看完代码了,接下来是驱动安装。

    驱动安装和服务安装,如果之前写过安装服务的代码看驱动安装代码会很熟悉,都是采用SCM安装。具体流程是:


    然后这个函数要仔细看定义:


    更详细的细节之前看MSDN吧。

    然后还是把资料上的模板代码直接拿过来,注意目前先用这个模板,因为这个安装模板是和上面的那个Hello World对应的,等看懂之后了,在自己定义相关驱动安装和驱动程序的代码,也可以自己写个模板,这里先不自己随便定义,可能会导致链接名字,驱动名字,还有驱动通讯的那个地方的细节自己弄乱了。这个博客看完了再回头定义自己的就行。

    创建C++工程,然后直接添加安装代码和安装测试代码:

    功能代码:

    /*============================
    Drvier Control Class (SCM way)
    ============================*/
     
    #pragma comment(lib,"advapi32.lib")
     
    class cDrvCtrl
    {
    public:
    cDrvCtrl()
    {
    m_pSysPath = NULL;
    m_pServiceName = NULL;
    m_pDisplayName = NULL;
    m_hSCManager = NULL;
    m_hService = NULL;
    m_hDriver = INVALID_HANDLE_VALUE;
    }
    ~cDrvCtrl()
    {
    CloseServiceHandle(m_hService);
    CloseServiceHandle(m_hSCManager);
    CloseHandle(m_hDriver);
    }
    public:
    DWORD m_dwLastError;
    PCHAR m_pSysPath;
    PCHAR m_pServiceName;
    PCHAR m_pDisplayName;
    HANDLE m_hDriver;
    SC_HANDLE m_hSCManager;
    SC_HANDLE m_hService;
    public:
    BOOL Install(PCHAR pSysPath,PCHAR pServiceName,PCHAR pDisplayName);
    BOOL Start();
    BOOL Stop();
    BOOL Remove();
    BOOL Open(PCHAR pLinkName);
    BOOL IoControl(DWORD dwIoCode, PVOID InBuff, DWORD InBuffLen, PVOID OutBuff, DWORD OutBuffLen, DWORD *RealRetBytes);
    private:
    BOOL GetSvcHandle(PCHAR pServiceName);
    DWORD CTL_CODE_GEN(DWORD lngFunction);
    protected:
    //null
    };
     
    BOOL cDrvCtrl::GetSvcHandle(PCHAR pServiceName)
    {
    m_pServiceName = pServiceName;
    m_hSCManager = OpenSCManagerA(NULL,NULL,SC_MANAGER_ALL_ACCESS);
    if (NULL == m_hSCManager)
    {
    m_dwLastError = GetLastError();
    return FALSE;
    }
    m_hService = OpenServiceA(m_hSCManager,m_pServiceName,SERVICE_ALL_ACCESS);
    if (NULL == m_hService)
    {
    CloseServiceHandle(m_hSCManager);
    return FALSE;
    }
    else
    {
    return TRUE;
    }
    }
     
    BOOL cDrvCtrl::Install(PCHAR pSysPath,PCHAR pServiceName,PCHAR pDisplayName)
    {
    m_pSysPath = pSysPath;
    m_pServiceName = pServiceName;
    m_pDisplayName = pDisplayName;
    m_hSCManager = OpenSCManagerA(NULL,NULL,SC_MANAGER_ALL_ACCESS);
    if (NULL == m_hSCManager)
    {
    m_dwLastError = GetLastError();
    return FALSE;
    }
    m_hService = CreateServiceA(m_hSCManager,m_pServiceName,m_pDisplayName,
                                SERVICE_ALL_ACCESS,SERVICE_KERNEL_DRIVER,SERVICE_DEMAND_START,SERVICE_ERROR_NORMAL,
                                m_pSysPath,NULL,NULL,NULL,NULL,NULL);
    if (NULL == m_hService)
    {
    m_dwLastError = GetLastError();
    if (ERROR_SERVICE_EXISTS == m_dwLastError)
    {
    m_hService = OpenServiceA(m_hSCManager,m_pServiceName,SERVICE_ALL_ACCESS);
    if (NULL == m_hService)
    {
    CloseServiceHandle(m_hSCManager);
    return FALSE;
    }
    }
    else
    {
    CloseServiceHandle(m_hSCManager);
    return FALSE;
    }
    }
    return TRUE;
    }
     
    BOOL cDrvCtrl::Start()
    {
    if (!StartServiceA(m_hService,NULL,NULL))
    {
    m_dwLastError = GetLastError();
    return FALSE;
    }
    return TRUE;
    }
     
    BOOL cDrvCtrl::Stop()
    {
    SERVICE_STATUS ss;
    GetSvcHandle(m_pServiceName);
    if (!ControlService(m_hService,SERVICE_CONTROL_STOP,&ss))
    {
    m_dwLastError = GetLastError();
    return FALSE;
    }
    return TRUE;
     
    }
     
    BOOL cDrvCtrl::Remove()
    {
    GetSvcHandle(m_pServiceName);
    if (!DeleteService(m_hService))
    {
    m_dwLastError = GetLastError();
    return FALSE;
    }
    return TRUE;
    }
     
    BOOL cDrvCtrl::Open(PCHAR pLinkName)//example: \\.\xxoo
    {
    if (m_hDriver != INVALID_HANDLE_VALUE)
    return TRUE;
    m_hDriver = CreateFileA(pLinkName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if(m_hDriver != INVALID_HANDLE_VALUE)
    return TRUE;
    else
    return FALSE;
    }
     
    BOOL cDrvCtrl::IoControl(DWORD dwIoCode, PVOID InBuff, DWORD InBuffLen, PVOID OutBuff, DWORD OutBuffLen, DWORD *RealRetBytes)
    {
    DWORD dw;
    BOOL b=DeviceIoControl(m_hDriver,CTL_CODE_GEN(dwIoCode),InBuff,InBuffLen,OutBuff,OutBuffLen,&dw,NULL);
    if(RealRetBytes)
    *RealRetBytes=dw;
    return b;
    }
     
    DWORD cDrvCtrl::CTL_CODE_GEN(DWORD lngFunction)
    {
    return (FILE_DEVICE_UNKNOWN * 65536) | (FILE_ANY_ACCESS * 16384) | (lngFunction * 4) | METHOD_BUFFERED;
    }
     

    测试代码:

    // DriverInstall.cpp : 定义控制台应用程序的入口点。
    //
     
    #include "stdafx.h"
    #include <string>
    #include <windows.h>
    #include "ScmDrvCtrl.h"
     
    #pragma warning(disable:4996)
     
    #pragma comment(lib,"user32.lib")
     
    using namespace std;
     
     
    void GetAppPath(char *szCurFile) //最后带斜杠
    {
    GetModuleFileNameA(0, szCurFile, MAX_PATH);
    for (SIZE_T i = strlen(szCurFile) - 1; i >= 0; i--)
    {
    if (szCurFile[i] == '\')
    {
    szCurFile[i + 1] = '';
    break;
    }
    }
    }
     
    int main()
    {
    BOOL b;
    cDrvCtrl dc;
    //设置驱动名称
    char szSysFile[MAX_PATH] = { 0 };
    char szSvcLnkName[] = "KrnlHW64";;
    GetAppPath(szSysFile);
    strcat(szSysFile, "KrnlHW64.sys");
    //安装并启动驱动
    b = dc.Install(szSysFile, szSvcLnkName, szSvcLnkName);
    b = dc.Start();
    printf("LoadDriver=%d
    ", b);
    //“打开”驱动的符号链接
    dc.Open("\\.\KrnlHW64");
    //使用控制码控制驱动(0x800:传入一个数字并返回一个数字)
    DWORD x = 100, y = 0, z = 0;
    dc.IoControl(0x800, &x, sizeof(x), &y, sizeof(y), &z);
    printf("INPUT=%ld
    OUTPUT=%ld
    ReturnBytesLength=%ld
    ", x, y, z);
    //使用控制码控制驱动(0x801:在DBGVIEW里显示HELLOWORLD)
    dc.IoControl(0x801, 0, 0, 0, 0, 0);
    //关闭符号链接句柄
    CloseHandle(dc.m_hDriver);
    //停止并卸载驱动
    b = dc.Stop();
    b = dc.Remove();
    printf("UnloadDriver=%d
    ", b);
    getchar();
    return 0;
    }

    然后就直接本地尝试调试安装一次,结果先是这个:


    不用管它,点击全部允许,但是还是会发现

     

    还是装不上,其实是肯定装不上的。原因是64位机器需要的强制签名加载驱动:


    注意最后面那句,各种系统的防护已经被攻克,他没有说win10,不过目前已经亲测win10也已经攻克,之后有机会再说这些东西,这里只讨论正常安装。想在win64上安装无签名驱动也可以,如果是win7,直接cmd 输入  bcdedit /set testsigning on ,然后重启电脑(win7是这样,win10貌似是前面要多输入一条命令,如果是要win10的话自己搜下吧,同时还有其他改设置关掉无签名驱动提示等的设置,加载测试驱动程序,需要的也可以找找,网上很多)。设置后上面的配置之后,重启电脑:

     

    有的时候会有测试模式水印,有的时候没有,可能提示不是正版的话会没这个水印,不过这不重要,只要相面的那个cmd命令执行成功,重启电脑就可以加载无签名64位驱动了。

    接下来测试下我们上面的那一套代码(所有程序都右键管理员启动):

    先打开dbgview全都选上:


    然后启动安装程序:


    OK驱动加载成功,并且通讯测试成功。这里基本模板就算完事了,之后就是一些常用的驱动开发,后续再整理。

  • 相关阅读:
    关于XML文档
    Why sql is called structured query language?1
    UML学习---交互
    C#为什么不采用多继承:
    url中
    array
    hard
    构造函数返回值
    布局容器layout Container
    k8s的概念
  • 原文地址:https://www.cnblogs.com/csnd/p/12062052.html
Copyright © 2011-2022 走看看