NT式设备驱动程序的动态加载主要是由服务控制管理程序(Service Control Manager,即SCM)系统组件来完成的。

Windwos服务可以在系统启动时加载,用户也可以按需在服务控制平台开启或者关闭服务。程序员可以通过Windows提供的相关服务函数进行加载或者卸载该服务等。服务程序更是可以在用户还没有登录系统的时候,就载入系统并且被执行。

加载NT驱动一般分为4个步骤:

1.    调用OpenSCManager打开SCM管理器;

2.    调用CreateService创建服务;如果存在则调用OpenService打开服务(可根据GetLastError判断);

3.    调用StartService开启服务;

4.    关闭句柄。

卸载NT驱动一般分为5个步骤:

1.    调用OpenSCManager打开SCM管理器;

2.    调用OpenService打开此项服务;

3.    调用ControlService传递SERVICE_CONTROL_STOP来停止服务

4.    调用DeleteService卸载此项服务;

5.    关闭句柄。

注意:DeleteService只是标记一下该项服务需要删除,只有停止了服务并且关闭了打开服务的句柄,改服务才会被正式卸载。

打开SCM管理器函数

SC_HANDLE WINAPI OpenSCManager(
__in LPCTSTR lpMachineName, //计算机名称
__in LPCTSTR lpDatabaseName, //SCM数据库名称
__in DWORD dwDesiredAccess //使用权限
);

说明

函数建立了一个连接到服务控制管理器,并打开指定的数据库。

参数

lpMachineName

指向零终止字符串,命名为目标计算机。如果该指针为NULL ,或者如果它指向一个空字符串,函数连接到服务控制管理器在本地计算机上。

lpDatabaseName

指向零终止字符串,名称的服务控制管理数据库,以开放。此字符串应指定ServicesActive 。如果该指针为NULL ,该ServicesActive数据库默认情况下打开。

dwDesiredAccess

指定服务的访问控制管理。才准予进入的要求,系统会检查访问令牌的调用进程对任意访问控制列表的安全描述符与服务控制管理器对象。访问类型的SC_MANAGER_CONNECT是含蓄地指明调用这个函数。此外,任何或所有下列服务控制管理器对象的访问类型可以指定:

SC_MANAGER_ALL_ACCESS

包括STANDARD_RIGHTS_REQUIRED ,除了所有类型的访问此表中列出。

SC_MANAGER_CONNECT

可以连接到服务控制管理器。

SC_MANAGER_CREATE_SERVICE

使要求的CreateService函数创建一个服务对象,并将其添加到数据库中。

SC_MANAGER_ENUMERATE_SERVICE

使要求的EnumServicesStatus功能清单的服务,这是在数据库中。

SC_MANAGER_LOCK

使要求的LockServiceDatabase功能获得锁定数据库。

SC_MANAGER_QUERY_LOCK_STATUS

使要求的QueryServiceLockStatus检索功能锁定状态信息的数据库。

返回值

如果函数成功,返回值是一个句柄指定的服务控制管理器数据库。如果函数失败,返回值为NULL 。要获得扩展错误信息,请使用GetLastError 获得错误代码。

关闭服务句柄

BOOL WINAPI CloseHandle(
__in HANDLE hObject //要关闭的句柄
);

hObjece

对象句柄,即使用OpenSCManager或者CreateService、OpenService返回的句柄。

创建服务

创建一个服务对象并且把它加入到服务管理数据库中。

SC_HANDLE WINAPI CreateService(
__in SC_HANDLE hSCManager, //SCM管理器的句柄
__in LPCTSTR lpServiceName, //服务名称
__in LPCTSTR lpDisplayName, //服务显示名称
__in DWORD dwDesiredAccess, //访问权限
__in DWORD dwServiceType, //服务类型
__in DWORD dwStartType, //启动类型
__in DWORD dwErrorControl, //关于错误处理的代码
__in LPCTSTR lpBinaryPathName, //二进制文件的代码
__in LPCTSTR lpLoadOrderGroup, //在加载顺序此服务所属的组的名称
__out LPDWORD lpdwTagId, //输出验证标签
__in LPCTSTR lpDependencies, //所依赖的服务名称
__in LPCTSTR lpServiceStartName, //用户账号名称
__in LPCTSTR lpPassword //用户口令
);

参数

hSCManager

服务控制管理器数据库的句柄。 此句柄由OpenSCManager函数返回,并且必须具有SC_MANAGER_CREATE_SERVICE 的访问权限。

lpServiceName

要安装该服务的名称。 最大字符串长度为 256 个字符。 服务控制管理器数据库将保留字符的大小写,但是服务名称比较总是区分大小写。 正斜杠和一个反斜线不是有效的服务名称字符。

lpDisplayName

对被用户界面程序用来识别服务的显示名称。 此字符串具有最大长度为 256 个字符。 服务控制管理器中的情况下保留名称。 显示名称比较总是不区分大小写。

dwDesiredAccess

对服务的访问权限。请求的访问之前,系统将检查调用进程的访问令牌。如果没有特殊要求,一般设置为SERVICE_ALL_ACCESS (0xF01FF)

dwServiceType服务类型。一般选择以下两种:

Value

Meaning

SERVICE_FILE_SYSTEM_DRIVER
0x00000002

文件系统的驱动

SERVICE_KERNEL_DRIVER
0x00000001

普通程序的驱动,一般使用此项。

dwStartType

服务启动选项,亦即打开服务的时间,有以下几种选择:

Value

Meaning

SERVICE_AUTO_START
0x00000002

系统启动时由服务控制管理器自动启动该服务程序。

SERVICE_BOOT_START
0x00000000

用于由系统加载器创建的设备驱动程序。

只能用于驱动服务程序。

SERVICE_DEMAND_START
0x00000003

由服务控制管理器(SCM)启动的服务,即手动启动。

SERVICE_DISABLED
0x00000004

表示该服务不可启动。

SERVICE_SYSTEM_START
0x00000001

用于由IoInitSystem函数创建的设备驱动程序。

dwErrorControl当该启动服务失败时产生错误的严重程度以及采取的保护措施。此参数可以是下列值之一:

Value

Meaning

SERVICE_ERROR_CRITICAL
0x00000003

服务启动程序将把该错误记录到事件日志中

SERVICE_ERROR_IGNORE
0x00000000

服务启动程序将忽略该错误并返回继续执行

SERVICE_ERROR_NORMAL
0x00000001

服务启动程序将把该错误记录到事件日志中并返回继续执行

SERVICE_ERROR_SEVERE
0x00000002

服务启动程序将把该错误记录到事件日志中。

否则将返回继续执行。

lpBinaryPathName

服务所用的二进制文件,也就是编译后的驱动程序。 如果路径中包含空格它必须被引用,以便它正确的解析。

例如"d:\myshare\myservice.exe"应指定为""d:\myshare\myservice.exe""。该路径也可以包含一个自动启动服务的参数。

例如"d:\myshare\myservice.exearg1 arg2"。 这些参数被传递给服务的入口点通常主要作用。

打开服务

此函数针对已经创建过的服务,再次打开此项服务。

SC_HANDLE WINAPI OpenService(
__in SC_HANDLE hSCManager, //SCM管理器的句柄
__in LPCTSTR lpServiceName, //服务名称
__in DWORD dwDesiredAccess //访问权限

hSCManager

SCM管理器的句柄,即OpenSCManager打开的句柄。

lpSeviceName

已经创建的服务名称。

dwDesiredAccess

访问权限。如果没有特殊情况,一般使用SERVICE_ALL_ACCESS(0xF01FF)

控制服务

发送一个控制码去指定的服务,根据不同的控制码操作服务。

BOOL WINAPI ControlService(
__in SC_HANDLE hService, //服务的句柄
__in DWORD dwControl, //发送的控制码
__out LPSERVICE_STATUS lpServiceStatus //接收之前的服务状态信息
);

hService

服务的句柄,即用CreateService创建或者使用OpenService打开的句柄。

dwControl

发送给服务的控制码,常用的有以下几种:

Control code

Meaning

SERVICE_CONTROL_CONTINUE
0x00000003

针对暂停的服务发出继续运行的指令。

SERVICE_CONTROL_PAUSE
0x00000002

暂停正在运行中的服务。

SERVICE_CONTROL_STOP
0x00000001

停止正在运行的服务。

lpServiceStatus

用于接收之前的服务状态信息。

删除服务

标记删除一个指定的服务。

BOOL WINAPI DeleteService(
__in SC_HANDLE hService //服务句柄
);

注意:必须停止服务并且关闭服务句柄后,服务才会被删除。

完整代码

// LoadNtDriver.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <Windows.h>
#include "string.h"
#include "locale.h" BOOL LoadNTDriver(TCHAR * lpszDriverName,TCHAR * lpszDriverPath)
{
TCHAR szDriverPath[256] = {0}; _tprintf(_T("加载驱动...\n")); //获取完整的驱动路径
GetFullPathName(lpszDriverPath,sizeof(szDriverPath)/sizeof(szDriverPath[0]),szDriverPath,NULL);
// _tprintf(szDriverPath);
SC_HANDLE hSCM = NULL;
SC_HANDLE hServie = NULL; hSCM = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (!hSCM)
{
_tprintf(_T("OpenSCManger 失败!,错误代码:%d\n"),GetLastError());
return FALSE;
}
else
{
_tprintf(_T("OpenSCManger 成功!\n")); hServie = CreateService(hSCM,
lpszDriverName, //服务的名称
lpszDriverName, //显示的名称DisplayName
SERVICE_ALL_ACCESS, //访问所有权
SERVICE_KERNEL_DRIVER, //表示加载的服务是驱动程序
SERVICE_DEMAND_START, //启动类型为手动启动
SERVICE_ERROR_IGNORE, //忽略错误
szDriverPath, //驱动文件名(保护路径),注册表中的ImagePath值
NULL,
NULL,
NULL,
NULL,
NULL); if (!hServie)
{
_tprintf(_T("CreateService 失败!,错误代码:%d,尝试使用OpenService\n"),GetLastError());
hServie = OpenService(hSCM,lpszDriverName,SERVICE_ALL_ACCESS);
if (!hServie)
{
_tprintf(_T("OpenService 失败!,错误代码:%d\n"),GetLastError());
//清理
CloseServiceHandle(hSCM);
return FALSE;
}
else
{
_tprintf(_T("OpenService 成功!\n"));
}
}
else
_tprintf(_T("CreateService 成功!\n")); //打开或创建服务成功后,开启服务
BOOL bRet = StartService(hServie,NULL,NULL); if (!bRet)
{
_tprintf(_T("StartService 失败!,错误代码:%d\n"),GetLastError());
bRet = FALSE;
}
else
{
bRet = TRUE;
_tprintf(_T("加载驱动...成功!\n")); }
//先关掉服务句柄,再关掉服务管理器句柄
if (hServie)
{
CloseServiceHandle(hServie);
}
if (hSCM)
{
CloseServiceHandle(hSCM);
} return bRet; } // hSCM } BOOL UnloadNTDriver(TCHAR * szSvrName)
{
SC_HANDLE hSCM = NULL; //SCManger
SC_HANDLE hService = NULL; _tprintf(_T("卸载驱动...\n"));
hSCM = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (!hSCM) //打开失败
{
_tprintf(_T("OpenSCManger 失败!,错误代码:%d\n"),GetLastError());
return FALSE;
}
else
{
_tprintf(_T("OpenSCManager 成功!\n"));
hService = OpenService(hSCM,szSvrName,SERVICE_ALL_ACCESS); if (!hService) //打开服务失败
{
_tprintf(_T("OpenService 失败,错误代码:%d\n"),GetLastError());
CloseServiceHandle(hSCM);
return FALSE;
}
else
{
_tprintf(_T("OpenService 成功!\n")); SERVICE_STATUS SvrSta = {0};
//停止服务。停止服务后,服务才能完成卸载。
if (!ControlService(hService,SERVICE_CONTROL_STOP,&SvrSta))
{
_tprintf(_T("停止服务 失败,错误代码:%d\n"),GetLastError());
}
else
{
_tprintf(_T("停止服务 成功!\n"));
} BOOL bRet = FALSE;
//动态卸载服务
bRet = DeleteService(hService);
if (!bRet)
{
//卸载失败
_tprintf(_T("DeleteService 失败,错误代码:%d\n"),GetLastError());
}
else
{
//卸载成功
_tprintf(_T("DeleteService 成功!\n")); _tprintf(_T("卸载驱动...成功!\n"));
}
//清理
if (hService)
{
CloseServiceHandle(hService);
}
if (hSCM)
{
CloseServiceHandle(hSCM);
}
return bRet;
} // hService }
} int _tmain(int argc, _TCHAR* argv[])
{
TCHAR szDriverPath[256];
TCHAR szDriverName[25]; setlocale(LC_ALL, "chs");//需要实现本地化,以实现中文正常输出 _tcscpy_s(szDriverPath,sizeof(szDriverPath)/sizeof(szDriverPath[0]),_T("Driver.sys"));
_tcscpy_s(szDriverName,sizeof(szDriverPath)/sizeof(szDriverPath[0]),_T("TestDDK")); BOOL bRet = LoadNTDriver(szDriverName,szDriverPath);
if (bRet)
{
printf("输入任意键来卸载驱动程序.\n");
getchar();
UnloadNTDriver(szDriverName);
}
else
_tprintf(_T("加载驱动...失败!\n"));
getchar();
return 0;
}

【结果】

加载:

 

卸载: 
 

编写软件动态加载NT式驱动的更多相关文章

  1. 《Windows驱动开发技术详解》之编程加载NT式驱动

    之前我们加载驱动都是利用INSTDRV这个应用,其原理是在注册表中写入相应的字段,这一节我们手动编写代码去加载驱动,其原理类似:

  2. 在Win64系统上动态加载无签名驱动:WIN64LUD

    1.WIN64LUD的全称是WIN64 Load Unsigned Driver,功能如其名,在WIN64系统上加载无签名的驱动. 2.支持Windows 7/8/8.1/2008R2/2012/20 ...

  3. Linux中mod相关的命令 内核模块化 mod相关命令都是用来动态加载内核模块/驱动程序模块

    Linux中mod相关的命令 内核模块化   mod相关命令都是用来动态加载内核模块/驱动程序模块 http://baike.baidu.com/link?url=lxiKxFvYm-UfJIxMjz ...

  4. 从零开始实现ASP.NET Core MVC的插件式开发(一) - 使用ApplicationPart动态加载控制器和视图

    标题:从零开始实现ASP.NET Core MVC的插件式开发(一) - 使用Application Part动态加载控制器和视图 作者:Lamond Lu 地址:http://www.cnblogs ...

  5. QT/C++插件式框架、利用智能指针管理内存空间的实现、动态加载动态库文件

    QT.C++插件式框架.主要原理还是 动态库的动态加载. dlopen()函数.下面为动态加载拿到Plugininstance对应指针.void**pp=(void**)dlsym(handle,&q ...

  6. 为了实现动态加载而编写的自己的ClassLoader

    Copy备用 之前客户要求在不重启应用的前提下实现动态增加服务及交易,在网上查了很长时间也没发现类似的技术,最后研究了一下ClassLoader.因为项目是与Spring,一开始我和同事尝试替换源码的 ...

  7. 在VC中动态加载ODBC的方法

    在使用VC.VB.Delphi等高级语言编写数据库应用程序时,往往需要用户自己在控制面板中配置ODBC数据源.对于一般用户而言,配置ODBC数据源可能是一件比较困难的工作.而且,在实际应用中,用户往往 ...

  8. C++第四十篇 -- 研究一下Windows驱动开发(三)-- NT式驱动的基本结构

    对于NT式驱动来说,主要的函数是DriverEntry例程.卸载例程及各个IRP的派遣例程. 一.驱动加载过程与驱动入口函数(DriverEntry) 和编写普通应用程序一样,驱动程序有个入口函数,也 ...

  9. CS.动态加载DLL.动态生成.运行代码.BS.AutoFac管理实现类

    以英雄联盟为例.界面上经常有Load....xxxx.dll.一般都是加载子系统.比如装备系统.英雄系统等.在实际开发中很多项目非常庞大.都会分割成独立子解决方案开发.后期就需要加载回来.一般都是利用 ...

随机推荐

  1. LInux的服务器编码格式的查看与更改

    1.locale 命令查看字符编码 然后修改/etc/sysconfig/i18n,如改成中文编码: LANG=en_US.UTF-8 改为 LANG="zh_CN.GBK" 然后 ...

  2. 嵊州普及Day1T2

    题意:走迷宫.求走到a[n][n]需要多久. 考场上想的dfs,听老师说最多50分.代码懒得码了,知道是走迷宫就好. 正解:bfs,时间复杂度O(n). 见代码: #include<iostre ...

  3. Java线程池 ThreadPoolExecutor类

    什么是线程池? java线程池是将大量的线程集中管理的类, 包括对线程的创建, 资源的管理, 线程生命周期的管理. 当系统中存在大量的异步任务的时候就考虑使用java线程池管理所有的线程, 从而减少系 ...

  4. IDA使用初步

    按空格看结构图,再按空格看汇编代码,按F5反编译 shift+F12 搜索中文字符串,通过字符串所在位置定位关键信息. 双击可能出flag的语句跳转至关键字符串. 想F5生成C伪代码,先crtl+X打 ...

  5. 吴裕雄--天生自然java开发常用类库学习笔记:比较器

    class Student implements Comparable<Student> { // 指定类型为Student private String name ; private i ...

  6. 吴裕雄--天生自然java开发常用类库学习笔记:排序及重复元素说明

    import java.util.Set ; import java.util.HashSet ; class Person{ private String name ; private int ag ...

  7. 基于共享内存、信号、命名管道和Select模型实现聊天窗口

    问题模型 A.B两个进程通过管道通信,A 进程每次接收到的数据通过共享内存传递给A1进程显示,同理,B进程每次接收到的数据通过共享内存传递给B1进程显示: 对于A.B 进程,采用ctrl+c(实际为S ...

  8. Solve Error: Could not find the certificate xxxx.com. at ServerlessCustomDomain.<anonymous>

    When runs "serverless create_domain", we may get the following error: Could not find the c ...

  9. 虚拟化安全服务器のIP修改配置

    1)进入网卡配置文件目录(以centos7.6为例) cd /etc/sysconfig/network-scripts     2)编辑配置文件(根据自己机器实际网卡进行编辑), 如Vi ifcfg ...

  10. Delphi MD5

    unit uMD5; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics; type MD5Count = ...