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;
}

【结果】

加载:

 

卸载: 
 

最新文章

  1. 原生js事件的添加和删除
  2. javascript平时例子⑧(大屏轮播)
  3. HTML5 postMessage 和 onmessage API 详细应用
  4. 【Linux】浅谈段页式内存管理
  5. Ajax基础之封装3
  6. rubycas-client单点登录
  7. [linux] linux 破解版confluence安装
  8. .NET开源工作流RoadFlow-流程设计-流程步骤设置-数据设置
  9. 垃圾回收GC——JVM之七
  10. 实战nginx 基础知识总结(一)1.1
  11. Head First设计模式之策略模式
  12. [Swift]LeetCode1024. 视频拼接 | Video Stitching
  13. maven eclipse web 项目 问题 cannot change version of project facet dynamic web module to 3.0
  14. django中的一对一、一对多、多对多及ForeignKey()
  15. 多线程shell脚本检测主机存活
  16. WINDBG常用方法
  17. 【洛谷P3792】由乃与大母神原型和偶像崇拜
  18. 由VC2010与VC2017数据结构差异造成的程序错误
  19. Android Logcat信息级别解读
  20. 如何合理的规划jvm性能调优

热门文章

  1. 51nod 1444:破坏道路 广度优先搜索
  2. grep -o -E
  3. 运行jar包中的main方法
  4. vscode dart 插件 关闭自动注释
  5. Day7 - I - Semi-prime H-numbers POJ - 3292
  6. Day6 - F - KiKi&#39;s K-Number HDU - 2852
  7. 标准C的标记化结构初始化语法
  8. NO11 SSH故障排查思路和netstat命令
  9. Java容器--Queue
  10. javacv 通过rtsp 获取视频流 设置帧率