编写软件动态加载NT式驱动
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 |
文件系统的驱动 |
SERVICE_KERNEL_DRIVER |
普通程序的驱动,一般使用此项。 |
dwStartType
服务启动选项,亦即打开服务的时间,有以下几种选择:
Value |
Meaning |
SERVICE_AUTO_START |
系统启动时由服务控制管理器自动启动该服务程序。 |
SERVICE_BOOT_START |
用于由系统加载器创建的设备驱动程序。 只能用于驱动服务程序。 |
SERVICE_DEMAND_START |
由服务控制管理器(SCM)启动的服务,即手动启动。 |
SERVICE_DISABLED |
表示该服务不可启动。 |
SERVICE_SYSTEM_START |
用于由IoInitSystem函数创建的设备驱动程序。 |
dwErrorControl当该启动服务失败时产生错误的严重程度以及采取的保护措施。此参数可以是下列值之一:
Value |
Meaning |
SERVICE_ERROR_CRITICAL |
服务启动程序将把该错误记录到事件日志中 |
SERVICE_ERROR_IGNORE |
服务启动程序将忽略该错误并返回继续执行 |
SERVICE_ERROR_NORMAL |
服务启动程序将把该错误记录到事件日志中并返回继续执行 |
SERVICE_ERROR_SEVERE |
服务启动程序将把该错误记录到事件日志中。 否则将返回继续执行。 |
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 |
针对暂停的服务发出继续运行的指令。 |
SERVICE_CONTROL_PAUSE |
暂停正在运行中的服务。 |
SERVICE_CONTROL_STOP |
停止正在运行的服务。 |
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; }
【结果】
加载:
卸载: