编写软件动态加载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;
}
【结果】
加载:

卸载:

编写软件动态加载NT式驱动的更多相关文章
- 《Windows驱动开发技术详解》之编程加载NT式驱动
之前我们加载驱动都是利用INSTDRV这个应用,其原理是在注册表中写入相应的字段,这一节我们手动编写代码去加载驱动,其原理类似:
- 在Win64系统上动态加载无签名驱动:WIN64LUD
1.WIN64LUD的全称是WIN64 Load Unsigned Driver,功能如其名,在WIN64系统上加载无签名的驱动. 2.支持Windows 7/8/8.1/2008R2/2012/20 ...
- Linux中mod相关的命令 内核模块化 mod相关命令都是用来动态加载内核模块/驱动程序模块
Linux中mod相关的命令 内核模块化 mod相关命令都是用来动态加载内核模块/驱动程序模块 http://baike.baidu.com/link?url=lxiKxFvYm-UfJIxMjz ...
- 从零开始实现ASP.NET Core MVC的插件式开发(一) - 使用ApplicationPart动态加载控制器和视图
标题:从零开始实现ASP.NET Core MVC的插件式开发(一) - 使用Application Part动态加载控制器和视图 作者:Lamond Lu 地址:http://www.cnblogs ...
- QT/C++插件式框架、利用智能指针管理内存空间的实现、动态加载动态库文件
QT.C++插件式框架.主要原理还是 动态库的动态加载. dlopen()函数.下面为动态加载拿到Plugininstance对应指针.void**pp=(void**)dlsym(handle,&q ...
- 为了实现动态加载而编写的自己的ClassLoader
Copy备用 之前客户要求在不重启应用的前提下实现动态增加服务及交易,在网上查了很长时间也没发现类似的技术,最后研究了一下ClassLoader.因为项目是与Spring,一开始我和同事尝试替换源码的 ...
- 在VC中动态加载ODBC的方法
在使用VC.VB.Delphi等高级语言编写数据库应用程序时,往往需要用户自己在控制面板中配置ODBC数据源.对于一般用户而言,配置ODBC数据源可能是一件比较困难的工作.而且,在实际应用中,用户往往 ...
- C++第四十篇 -- 研究一下Windows驱动开发(三)-- NT式驱动的基本结构
对于NT式驱动来说,主要的函数是DriverEntry例程.卸载例程及各个IRP的派遣例程. 一.驱动加载过程与驱动入口函数(DriverEntry) 和编写普通应用程序一样,驱动程序有个入口函数,也 ...
- CS.动态加载DLL.动态生成.运行代码.BS.AutoFac管理实现类
以英雄联盟为例.界面上经常有Load....xxxx.dll.一般都是加载子系统.比如装备系统.英雄系统等.在实际开发中很多项目非常庞大.都会分割成独立子解决方案开发.后期就需要加载回来.一般都是利用 ...
随机推荐
- docker中mysql数据库
在docker中安装mysql数据库,直接上代码,pull 并run 补充20190809=============== 如果要挂载数据库实现数据持久化到本地的时候,会出现权限问题,这个原因是: 在执 ...
- 【pwnable.tw】 applestore
做到这道题的时候正赶上iPhone 8上市,撒花~(虽然不知道为啥) 程序分析: 先进到main函数,比较简单. myCart位于bss段上,是一个长度为0x10. 主要的处理函数是handler函数 ...
- 微信小程序提示:https://api.map.baidu.com 不在以下 request 合法域名列表中
如果你想利用百度地图API定位来获得当前位置,但却出现了如标题所示问题,那么请接着看: 1.首先我们需要在百度地图开放平台(https://lbs.baidu.com/apiconsole/key?a ...
- excel表格数据导入导出
/** * 导出数据到excel表格 * Created by shenjianhua on 2018-12-28 */ package com.luer.comm.excel; import jav ...
- class(二)--派生类的继承
前言 从我之前的一篇笔记对象的继承中, 我们可以知道JS的继承方式依赖原型链,而比较好的继承方式是寄生组合式继承 先来温习下什么是寄生组合式继承 function Rectangle(length, ...
- Java 日期与时间
章节 Java 基础 Java 简介 Java 环境搭建 Java 基本语法 Java 注释 Java 变量 Java 数据类型 Java 字符串 Java 类型转换 Java 运算符 Java 字符 ...
- Linux每日练习-批量删除用户,非脚本运行方式 20200225
- python 小数相加报错 invalid literal for int() with base 10
for i in column1: x = int(i) s += xprint "sum:",s,"count:",len(column1)# round ( ...
- v2??? 重启失败
v2??? 重启失败, 提示 Authorization not available. Check if polkit service is running or see debug message ...
- scala 语言特性
Scala 语言特性 Unit 表示无值, 等价于java, C++中的void Null 表示空值或空引用 Nothing 所有其他类型的子类型, 表示没有值 Any 所有类型的超类, 任何实例都属 ...