VS2013 WDK8.1驱动开发3(手动加载NT驱动程序)

本系列博客为学习《Windows驱动开发技术详解》一书的学习笔记。

前言

上两文介绍了一个简单的NT驱动程序和一个简单的WDM驱动程序,在第一文中我们使用DriverMonitor工具对NT驱动程序进行加载测试。本文我们学习如何手动加载NT驱动程序。手动加载NT驱动程序有两种方法,一种是在注册表中创建一个服务,一种是编写程序。其实它们的原理是一致的,都是通过服务的方式加载NT驱动程序

注册表创建服务加载NT驱动程序

  1. 在Win7 64位虚拟机的注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services下添加新项目,例如HelloNTDriver,这个名称就是服务的名称。
  2. 在这个项目下面添加如下子键:
    • ImagePath为驱动程序的路径,必须以\??\开头,在这里我们使用第一文介绍过的NT驱动程序。
    • Start为服务的启动模式,3表示为按需要启动服务。
    • Type为1,表明此服务在内核模式下加载,是一个驱动程序。
    • ErrorControl为1,表明加载出错时弹出错误信息。
    • DisplayName,可以自定义
  3. 重启虚拟机后,打开管理员权限的命令行工具,输入net start HelloNTDriver即可加载驱动程序,输入net stop HelloNTDriver即可卸载驱动程序。如果我们提前开启DebugView工具,那么就可以在DebugView中看到调试信息。

编写程序加载NT驱动程序

NT驱动程序的动态加载主要由服务控制管理程序(Service Control Manager, SCM)系统组件完成。加载和卸载NT驱动分为4个步骤:

  1. 为NT驱动程序创建服务。
  2. 开启服务。
  3. 关闭服务。
  4. 删除创建的服务。

加载NT驱动的代码

    /// @brief 加载NT驱动程序
    /// 需要管理员权限, 否则会加载失败
    /// 32位系统需要加载32位驱动, 64位系统需要加载64位驱动
    /// @param[in] pDriverName 驱动名称
    /// @param[in] pDriverPath 驱动文件路径
    /// @return 成功返回true, 失败返回false
    bool LoadNTDriver(IN const char* pDriverName, IN const char* pDriverPath)
    {
        bool bRet = false;
        DWORD dwRet = FALSE;
        char* pFullPathBuffer = NULL;
        DWORD bufferSize = 256;
        DWORD fullPathSize = 0;

        SC_HANDLE hServiceManager = NULL; // 服务控制管理器句柄
        SC_HANDLE hDriverService = NULL; // 驱动服务句柄

        // 获取驱动程序文件全路径
        pFullPathBuffer = new char[bufferSize];
        ZeroMemory(pFullPathBuffer, bufferSize);
        fullPathSize = GetFullPathNameA(pDriverPath, bufferSize, pFullPathBuffer, NULL);
        if (fullPathSize > bufferSize)
        {
            delete[] pFullPathBuffer;
            pFullPathBuffer = NULL;
            pFullPathBuffer = new char[fullPathSize];
            ZeroMemory(pFullPathBuffer, fullPathSize);
            GetFullPathNameA(pDriverPath, bufferSize, pFullPathBuffer, NULL);
        }

        // 打开SCM管理器
        // 需要管理员权限
        hServiceManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
        if (NULL == hServiceManager)
        {
            bRet = false;
            printf("OpenSCManager Fail: %d\n", GetLastError());
            goto SAFE_EXIT;
        }

        // 创建服务
        hDriverService = CreateServiceA(
            hServiceManager,
            pDriverName,
            pDriverName,
            SERVICE_ALL_ACCESS,
            SERVICE_KERNEL_DRIVER,
            SERVICE_DEMAND_START,
            SERVICE_ERROR_IGNORE,
            pFullPathBuffer,
            NULL,
            NULL,
            NULL,
            NULL,
            NULL);

        if (NULL == hDriverService)
        {
             dwRet = GetLastError();
            if (dwRet != ERROR_IO_PENDING && dwRet != ERROR_SERVICE_EXISTS)
            {
                printf("CreateService Fail: %d\n", dwRet);
                bRet = false;
                goto SAFE_EXIT;
            }
            else
            {
                printf("Service Is Exist\n");
            }

            // 如果服务已经存在则打开服务
            hDriverService = OpenServiceA(hServiceManager, pDriverName, SERVICE_ALL_ACCESS);
            if (NULL == hDriverService)
            {
                printf("Open Service Fail: %d\n", GetLastError());
                bRet = false;
                goto SAFE_EXIT;
            }
        }

        // 启动服务
        dwRet = StartServiceA(hDriverService, NULL, NULL);
        if (FALSE == dwRet)
        {
            dwRet = GetLastError();
            if (dwRet != ERROR_SERVICE_ALREADY_RUNNING)
            {
                printf("Start Service Fail: %d\n", dwRet);
                bRet = false;
                goto SAFE_EXIT;
            }
            printf("Service Had Been Started\n");
        }

        bRet = true;

    SAFE_EXIT:

        if (NULL != hDriverService)
        {
            CloseServiceHandle(hDriverService);
            hDriverService = NULL;
        }

        if (NULL != hServiceManager)
        {
            CloseServiceHandle(hServiceManager);
            hServiceManager = NULL;
        }

        if (pFullPathBuffer != NULL)
        {
            delete[] pFullPathBuffer;
            pFullPathBuffer = NULL;
        }

        return bRet;
    };
  • 创建服务时需要填写驱动程序文件的全路径,所以我们在一开始先获取驱动程序文件的全路径。
  • 创建服务时填写的参数SERVICE_DEMAND_START,意思为需要时启动我们的服务,也就是需要我们自己去启动,也可以设置为开机启动。
  • 创建完成后我们就可以使用StartServiceA启动服务。

卸载NT驱动的代码

    /// @brief 卸载NT驱动程序
    /// 需要管理员权限, 否则会卸载失败
    /// @param[in] pDriverName 驱动程序名称
    /// @return 成功返回true, 失败返回false
    bool UnLoadNTDriver(const char* pDriverName)
    {
        bool bRet = true;
        DWORD dwRet = FALSE;

        SC_HANDLE hServiceManager = NULL; // 服务控制管理器句柄
        SC_HANDLE hDriverService = NULL; // 驱动服务句柄
        SERVICE_STATUS serviceStatus;

        // 打开SCM管理器
        // 需要管理员权限
        hServiceManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
        if (NULL == hServiceManager)
        {
            bRet = false;
            printf("OpenSCManager Fail: %d\n", GetLastError());
            goto SAFE_EXIT;
        }

        // 打开服务
        hDriverService = OpenServiceA(hServiceManager, pDriverName, SERVICE_ALL_ACCESS);
        if (NULL == hDriverService)
        {
            printf("Open Service Fail: %d\n", GetLastError());
            bRet = false;
            goto SAFE_EXIT;
        }

        // 停止服务
        dwRet = ControlService(hDriverService, SERVICE_CONTROL_STOP, &serviceStatus);
        if (dwRet == FALSE)
        {
            printf("Control Service Stop Fail: %d\n", GetLastError());
        }

        // 删除服务
        dwRet = DeleteService(hDriverService);
        if (dwRet == FALSE)
        {
            printf("Delete Service Fail: %d\n", GetLastError());
        }

    SAFE_EXIT:

        if (NULL != hDriverService)
        {
            CloseServiceHandle(hDriverService);
            hDriverService = NULL;
        }

        if (NULL != hServiceManager)
        {
            CloseServiceHandle(hServiceManager);
            hServiceManager = NULL;
        }

        return bRet;
    }

测试驱动程序代码

    /// @brief 测试驱动程序
    void TestNTDriver()
    {
        HANDLE hDevice = CreateFileA(
            "\\\\.\\HelloNTDriver",
            GENERIC_READ | GENERIC_WRITE,
            0,
            NULL,
            OPEN_EXISTING,
            0,
            NULL);

        if (hDevice == INVALID_HANDLE_VALUE)
            printf("Open Device Fail\n");
        else
            printf("Open Device Success\n");

        CloseHandle(hDevice);
    }
  • 将NT驱动程序代码中创建的链接符号作为参数传递给CreateFile就可以打开设备对象,原来的链接符号格式为"\\??\\HelloNTDriver",这里我们需要修改成"\\\\.\\HelloNTDriver"。
  • Win32程序和驱动程序的沟通都是通过这样的方式,在前面的文章中我们已经对IRP_MJ_CREATE和IRP_MJ_CLOSE设置了派遣函数,CreateFile函数会触发IRP_MJ_CREATE的排遣函数,CloseHandle函数会触发IRP_MJ_CLOSE的排遣函数。

主程序代码

    int main(int argc, char** argv)
    {
        if (argc == 3)
        {
            bool bRet = false;

            bRet = LoadNTDriver(argv[1], argv[2]);
            if (bRet)
                printf("Load NT Driver Success\n");
            else
                printf("Load NT Driver Fail\n");

            system("pause");

            TestNTDriver();

            system("pause");

            bRet = UnLoadNTDriver(argv[1]);
            if (bRet)
                printf("UnLoad NT Driver Success\n");
            else
                printf("UnLoad NT Driver Fail\n");
        }

        system("pause");
        return 0;
    }
  • 主程序接受两个命令行参数,第一个为驱动程序的名称(也用做服务名),第二个为驱动程序的路径。
  • 主程序先加载驱动,之后测试驱动,最后卸载驱动。

运行程序

编译我们程序,将得到的可执行文件(LoadNTDriver.exe)和需要加载的NT驱动程序(chapter01-1.sys)放在虚拟机的相同目录下,打开管理员权限的命令行工具,运行如下命令:

之后我们会在DebugView中看到如下的调试信息:

如果在卸载驱动前我们可以在注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services目录下看到系统自动创建的HelloNTDriver项:

后话

其他章节链接

VS2013 WDK8.1驱动开发1(最简单的NT驱动)

VS2013 WDK8.1驱动开发2(最简单的WDM驱动)

VS2013 WDK8.1驱动开发3(手动加载NT驱动程序)

VS2013 WDK8.1驱动开发4(NT式驱动基本结构)

VS2013 WDK8.1驱动开发5(WDM驱动基本结构)

VS2013 WDK8.1驱动开发6(内存管理)

VS2013 WDK8.1驱动开发7(派遣函数)

VS2013 WDK8.1驱动开发8(设备读写操作)

VS2013 WDK8.1驱动开发9(IO设备控制操作)


分享给朋友阅读吧


您还未登录,登录微博账号发表精彩评论

 微博登录


最新评论

    还没有人评论...

 

 

刘杰

28岁, 现居苏州

微博:

微信:

BurnellLIU

邮箱:

burnell_liu@outlook.com

Github:

https://github.com/BurnellLiu

简介:

努力做一个快乐的程序员, good good study, day day up!

苏ICP备16059872号

Copyright © 2016. http://www.burnelltek.com. All rights reserved.