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. anaconda 创建虚拟环境(自己版本)

    首先安装anaconda(3) Anacond的介绍Anaconda指的是一个开源的Python发行版本,其包含了conda.Python等180多个科学包及其依赖项. 因为包含了大量的科学包,Ana ...

  2. day03-Python运维开发基础-(数据类型强转、运算符、逻辑短路、isinstance)

    1. 强制转换成容器数据类型 # ### 强制类型转换 容器类型数据 (str list tuple set ) var1 = "你好世界" var2 = ["陈博文&q ...

  3. shell-Startup-Files

    shell-Startup-Files 1. 相关阅读 2. 主流shell 3. shell实例类型 4. Shell启动文件的必要元素 4.1 路径: 命令路径, 4.2 提示符 5. 主流she ...

  4. Manacher算法[O(n)]

    问题描述: 输入一个字符串,求出其中最大的回文子串.子串的含义是:在原串中连续出现的字符串片段.回文的含义是:正着看和倒着看相同,如abba和yyxyy. 算法基本要点: 首先用一个非常巧妙的方式,将 ...

  5. ubnutu18.4 修改交换分区大小

    ubuntu18.04默认的swap文件在根目录/下,名字是swapfile 1.查看交换分区大小  也可以使用系统监视器查看 free -m 2.创建一个swap文件 :大小为8g count= 3 ...

  6. WinForm_UI设计

    下载

  7. linux 查看运行进程详细信息

    Linux在启动一个进程时,系统会在/proc下创建一个以PID命名的文件夹,在该文件夹下会有我们的进程的信息 通过ll或ls –l命令即可查看. ll /proc/PID cwd符号链接的是进程运行 ...

  8. HTTP和HTTPS的区别及HTTPS加密算法

    一.HTTP和HTTPS的概念              HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传 ...

  9. IP地址 网络地址 子网掩码

    提纲 (1)IP地址 (2)网络地址 (3)子网掩码     IP地址=网络地址+主机地址,图1中的IP地址是192.168.100.1,这个地址中包含了很多含义.如下所示:网络地址(相当于街道地址) ...

  10. mfc WebBrowser打开本地网页

    本地路径要用file协议,例子:file:///c:/abc/def.html注意点:file:后面是3个正斜杠,路径中用正斜杠(不是标准的反斜杠).如果你觉得IE地址栏支持标准的路径写法,那么你就错 ...