服务的基本操作
驱动以Windows服务的方式存在,服务有不同的类型,驱动只是其中一种类型的服务。本小节把驱动等同于服务。
服务的基本操作有注册(创建)、启动、暂停、停止操作。但内核驱动类型的服务不支持暂停操作。
服务管理器 的主要工作是管理操作系统上的所有服务,其中包括跟踪、维护服务的各种状态,以及对服务发起具体的操作。开发者可以通过服务管理器来查询服务状态、修改服务配置、注册(创建)新服务、启动服务等。开发者通过API操作服务,API内部首先会通过一个称为"LPC(本地方法调用)"的方式,把请求发送给服务管理器,服务管理器再处理具体的请求。
打开服务管理器
OpenSCManager
系统提供了一个API用来打开服务管理器。
打开服务管理器的函数为OpenSCManager,原型如下:
函数原型
SC_HANDLE OpenSCManager(
LPCSTR lpMachineName,
LPCSTR lpDatabaseName,
DWORD dwDesiredAccess
);
参数详解
lpMachineName - 一个字符串常量,表示机器的名字,可以简单传一个NULL,表示打开的是本机器的服务管理器。
lpDatabaseName - 一个字符串常量,表示数据库的名字,可以传一个NULL,表示打开的是一个活动(Active) 数据库。
dwDesiredAccess - 权限。开发者通过服务管理器去操作服务时,不同的操作需要不同的权限。常用的服务管理器权限有:
- SC_MANAGER_CREATE_SERVICE - 表示拥有注册(创建)服务的权限。
- SC_MANAGER_ENUMERATE_SERVICE - 表示拥有枚举系统服务的权限。
- SC_MANAGER_ALL_ACCESS - 表示拥有一切权限。
返回值
OpenSCManager 函数返回一个类型为SC_HANDLE的句柄,表示服务管理器的句柄,开发者可以通过这个句柄,结合其他API来操作服务。当这个服务管理器句柄不在需要使用,开发者需要调用CloseServiceHandle函数来关闭句柄。CloseServiceHandle函数只有一个参数,使用非常简单。
BOOL CloseServiceHandle(
SC_HANDLE hSCObject
);
服务的注册
CreateService
注册(创建)一个服务使用的函数为 CreateService。
函数原型
SC_HANDLE CreateService(
SC_HANDLE hSCManager,
LPCSTR lpServiceName,
LPCSTR lpDisplayName,
DWORD dwDesiredAccess,
DWORD dwServiceType,
DWORD dwStartType,
DWORD dwErrorControl,
LPCSTR lpBinaryPathName,
LPCSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPCSTR lpDependencies,
LPCSTR lpServiceStartName,
LPCSTR lpPassword
);
参数详解
hSCManager - 表示"服务管理器"句柄由于操作是注册(创建)服务,所以在打开服务管理器时,需要使用SC_MANAGER_CREATE_SERVICE权限
lpServiceName - 表示创建服务的名字,这个名字不能与其他存在服务的名字相同。服务名字时服务的唯一标识。
lpDisplayName - 服务显示名字
dwDesiredAccess - 服务的权限,常见的权限有:
SERVICE_START - 表示启动服务的权限。
SERVICE_STOP - 表示停止服务的权限。
SERVICE_QUERY_STATUS - 拥有查询服务状态的权限。
SERVICE_ALL_ACCESS - 拥有一切权限。
dwServiceType - 表示需要创建何种类型的服务,服务的类型有:
SERVICE_FILE_SYSTEM_DRIVER - 文件系统服务
SERVICE_KERNEL_DRIVER - 内核驱动服务
SERVICE_WIN32_OWN_PROCESS - 应用层服务
SERVICE_WIN32_SHARE_PROCESS - 应用层共享EXE服务
其他服务类型请查阅WDK文档。
dwStartType - 表示服务的启动方式,启动选项参数可选值:
SERVICE_BOOT_START - 操作系统引导阶段启动的服务,一般由 Winload模块负责加载服务对应的可执行文件到内存
SERVICE_SYSTEM_START - 操作系统启动阶段启动的服务,由系统NT模块负责加载服务对应的可执行文件到内存。
SERVICE_AUTO_START - 操作系统启动完毕后启动的服务
SERVICE_DEMAND_START - 需要手动启动的服务
dwErrorControl - 错误控制,具体是指服务启动失败的情况下,操作系统需要执行何种操作,常见的错误控制可选参数:
SERVICE_ERROR_CRITICAL、SERVICE_ERROR_IGNORE、SERVICE_ERROR_NORMAL及SERVICE_ERROR_SEVERE,
对于驱动程序来说,简单指定SERVICE_ERROR_IGNORE即可,表示系统忽略这个驱动启动失败信息。
lpBinaryPathName - 该服务对应可执行文件的全路径,对于驱动类型的服务来说,这里指定的就是sys文件所在的路径。
lpLoadOrderGroup - 服务所在分组的名字。操作系统内置了一系列服务的分组,开发者也可以创建新的分组。一个分组里面可以有多个服务,但是一个服务最多只能关联一个分组;不同分组的启动顺序不同。如果开发者关心服务的启动顺序,可以通过这个参数来设置分组。如果不关心启动顺序,可以简单把该参数设置成NULL,表示不关联任何分组。分组信息在注册表中的位置为HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ ServiceGroupOrder,名字为list的值保存了所有分组的信息
lpdwTagId - 表示服务在分组内的一个标识,针对SERVICE_BOOT_START,以及SERVICE_SYSTEM_STAR类型的服务。服务的启动顺序由所属的分组决定,但同一分组内的服务启动顺序怎么决定呢?答案是通过lpdwTagId参数来指定。同样地,如果开发者不关心服务启动顺序,可以把该参数设置为0。该标识对应注册表的位置为HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ GroupOrderList。
lpDependencies - 表示当前所注册的服务,需要依赖其他服务名的列表。
lpServiceStartName - 服务将在其下运行的帐户的名称。
lpPassword - lpServiceStartName参数指定的帐户名的密码
返回值
如果函数成功,则返回值是服务的句柄。
如果函数失败,则返回值为NULL。
开发者可以通过CreateService返回服务的服务句柄来操作服务,使用完毕后需要调用CloseServiceHandle函数关闭句柄。
开发者需要操作服务,但服务已经存在,开发者不需要重新注册(创建)服务,在这种情况下,就需要OpenService函数。
OpenService
开发者需要操作服务,但服务已经存在,开发者不需要重新注册(创建)服务,在这种情况下,开发者需要“打开”已经存在的服务,通过OpenService函数打开服务。
函数原型
SC_HANDLE WINAPI OpenService(
SC_HANDLE hSCManager,
LPCTSTR lpServiceName,
DWORD dwDesiredAccess
);
参数详解
hSCManager - 标识服务管理器句柄
lpServiceName - 表示需要打开的服务的名字
dwDesiredAccess - 表示需要以何种权限打开服务
返回值
OpenService成功打开服务后返回服务的句柄,开发者可以通过这个句柄操作服务,使用完毕后需要调用CloseServiceHandle函数关闭句柄。
服务的启动与停止
StartService
启动服务
函数原型
BOOL WINAPI StartService(
SC_HANDLE hService,
DWORD dwNumServiceArgs,
LPCTSTR *lpServiceArgVectors
);
参数详解
hService - 服务的句柄,表示需要启动的服务
dwNumServiceArgs - 服务启动时所需要传递的参数
lpServiceArgVectors - - 服务启动时所需要传递的参数
对于内核驱动类型的服务,可以忽略dwNumServiceArgs和lpServiceArgVectors 参数,设置为NULL即可。
说明
对于内核驱动服务来说,StartService操作意味着把磁盘的sys文件加载到内核中,具体过程为:StartService函数内部通过服务管理器,让系统SYSTEM进程加载驱动并调用内核驱动的DriverEntry入口函数,这一系列操作是同步的。此外,StartService函数内部还会等待DriverEntry入口函数执行返回,获取其返回值,如果DriverEntry函数返回成功,StartService函数相应的也返回成功,否则返回失败。
虽然说DriverEntry的返回值会影响该服务启动的成败,但是这并不是唯一的影响因素,StartService函数返回失败的情况很多,如服务已经被删除、hService参数没有相应地启动服务权限、服务已经在运行等。
作为一个完整生命周期的服务来说,启动后也会有相应的停止操作。停止一个内核驱动类型的服务意味着驱动模块从内存中删除,站在开发者的角度来看,当内核类型服务停止时,必须清理掉所使用的资源,避免造成资源泄露或系统异常。对于驱动来说,如何感知自身要被停止呢?答案是驱动对象的DriverUnload函数。
ControlService
停止服务、暂停服务、恢复服务等,大部分操作都是针对用户态服务。
函数原型
BOOL WINAPI ControlService(
SC_HANDLE hService,
DWORD dwControl,
LPSERVICE_STATUS lpServiceStatus
);
参数详解
hService - 需要操作的服务的句柄
dwControl - 控制码,表示对服务进行何种操作,系统定义了一些列值,如SERVICE_CONTROL_PAUSE、SERVICE_CONTROL_STOP、SERVICE_CONTROL_CONTINUE等
lpServiceStatus - 返回参数,表示服务当前最新的状态,这些状态保存在SERVICE_STATUS结构体中。
服务的删除
DeleteService
函数原型
BOOL WINAPI DeleteService(SC_HANDLE hService);
参数详解
hService - 表示需要删除的服务。
常见的操作是:开发者调用OpenService函数(dwDesiredAccess为DELETE权限)打开一个需要删除的服务,成功打开后获取到需要删除的服务句柄,然后把服务句柄传递给DeleteService函数。
服务的例子
假设前面介绍的驱动MyDriver.sys文件放在C盘下,例子代码中需要把这个驱动注册成服务,并且运行,具体代码:
#include "windows.h"
#include <tchar.h>
#include <stdio.h>
#define SER_NAME _T("MyDriver")
int main()
{
SC_HANDLE hSCM = NULL;
SC_HANDLE hSer = NULL;
do
{
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE | SC_MANAGER_CONNECT);
if (hSCM == NULL)
{
break;
}
hSer = CreateService(
hSCM, //句柄
SER_NAME, //服务名称
SER_NAME, //服务显示名字
SERVICE_ALL_ACCESS, //所有权限打开
SERVICE_KERNEL_DRIVER, //内核类型的服务
SERVICE_DEMAND_START, //服务启动类型。这里是手动启动
SERVICE_ERROR_IGNORE, //错误类型
_T("C:\\MyDriver.sys"), //sys文件所在的磁盘目录
NULL, //服务所在的组,不关心顺序
NULL, //服务的Tag,这里不关心
NULL, //服务的依赖,没有依赖
NULL, //服务的启动用户名
NULL //服务启动用户名对应的密码
);
if (hSer == NULL)
{
DWORD dwErrorCode = GetLastError();
if (dwErrorCode == ERROR_SERVICE_EXISTS)
{
//ERROR_SERVICE_EXITS 表示服务已经存在,不能重复注册,属于正常的情况
hSer = OpenService(hSCM, SER_NAME, SERVICE_ALL_ACCESS);
if (hSer == NULL)
{
//打开失败
break;
}
}
else
{
break;
}
}
printf("CreateService or openService success.\n");
getchar();
// 准备启动服务
BOOL bSucc = StartService(hSer, NULL, NULL);
printf("StartService: %u\n", bSucc);
getchar();
//停止服务
SERVICE_STATUS SerStatus = { 0 };
bSucc = ControlService(hSer, SERVICE_CONTROL_STOP,&SerStatus);
printf("ControlService: %u\n", bSucc);
getchar();
//删除服务
DeleteService(hSer);
} while (FALSE);
if (hSCM != NULL)
{
CloseServiceHandle(hSCM);
hSCM = NULL;
}
if (hSer != NULL)
{
CloseServiceHandle(hSer);
hSer = NULL;
}
return 0;
}
运行程序注意事项
首先在管理员模式下运行或者以管理员模式打开Visual Studio。
然后计算机必须在"禁用驱动程序强制签名"状态下才可以启动驱动成功。