Windows服务有三个组件构成:服务应用程序,服务控制程序(service control program,如services.msc),以及服务控制管理器(sevice control manager)。

Windows服务三组件

SCM(服务控制管理器)

SCM进程启动后,干了以下事情:

1. 启动autostart和demandstart的驱动和服务

配置为引导-启动和系统-启动的驱动程序,在任何用户模式进程执行起来以前,由I/O管理器将这些程序加载到系统中

Wininit.exe在系统引导的早期将SCM(services.exe)启动起来,SCM会将配置为自动启动与延迟启动的驱动程序和服务按序启动起来。

  1. 创建SvcctrlStartEvent_A3752DX的同步事件,当SCM完成必要步骤准备接收SCP命令时,会将此事件设置为有信号状态。SCP与SCM建立对话的函数是OpenSCManager,该函数会等待SvcctrlStartEvent_A3752DX事件。
  2. 调用ScGenerateServiceDB,读取HKLM\SYSTEM\CurrentControlSet\Control\ServiceGroupOrder\List和HKLM\SYSTEM\CurrentControlSet\Services来建立SCM的内部服务数据库。

Windows服务管理 - 图1

  1. 有的服务配置为非本地系统账户登录,因此SCM会启动LSASS进程,此外Wininit也会启动LSASS进程,二者的初始化是同步进行的。
  2. 调用ScGetBootAndSystemDriverState确定引导-启动、系统-启动的驱动程序是否已成功启动,通过搜索\Driver的对象管理器名字空间目录中是否存在该驱动程序名称。

Windows服务管理 - 图2

  1. 创建\Pipe\Ntsvcs命令管道,在该管道上监听SCP的进入消息。然后将SvcctrlStartEvent_A3752DX置成有信号状态。

Windows服务管理 - 图3

  1. 调用ScAutoStartServices来启动那些配置为自动-启动的服务及驱动程序。以下除非特别指定,服务指服务及驱动程序。
  • 根据第2步建立的内部服务数据库,将所有属于此阶段的组的那些服务条目标记出来,验证依赖性关系没问题
  • 一旦要决定启动一个服务,它就调用ScStartService
  • 对于服务程序,启动过程如下
    • 在SCM内部的image database中,检查此服务的ImagePath值在数据库中是否有记录,如果没有则记录ImagePath与服务的登录账户名;如果有,则检查服务指定的登录账号与数据库中ImagePath对应的是否一致。这是为了保证SERVICE_WINDOWS_SHARE_PROCESS类型的服务,一个进程只能以一个账户的身份来登录。
    • 如果服务配置了非系统账户,则调用ScLogonAndStartImage来登录一个服务,并启动该服务的进程。
      • 调用LSASS函数LogonUserEx来登录账户,口令保存在HKLM\SECURITY\Policy\Secrets下面,这是配置服务时SCM利用LsaStorePrivateData函数来指示LSASS将登录口令保存在Secrets子键下。如果登录成功,则返回一个安全令牌。
      • 如果此账户的轮廓信息还没有加载的话,调用LoadUserProfile加载轮廓信息
      • 调用Windows函数CreateProcessAsUser以来启动此进程,并将该进程状态设置为挂起状态
      • 创建一个命名管道\Pipe\Net\NtControlPipeX,此后使用此命名管道与服务通信
      • ResumeThread恢复服务线程执行,等待服务连接到上述命名管道上,此时服务应该调用调用StarServiceCtrlDispatcher来进行服务应用的启动了。默认30s间隔内SCM没有收到消息的话,就终止进程。
      • 如果服务通过命名管道连接到SCM,SCM向服务发送一个启动命令,如果默认间隔内SCM没有收到响应,则记录一个错误日志。
  • 对于驱动程序,调用ScLoadDeviceDriver来加载
  • 循环处理下一个组、下一个阶段,直到处理完所有自动-启动的服务
  • 调用ScInitDelayStart处理延迟的自动-启动的服务,它把这些服务加到一个工作队列中,由另一个专门的辅助线程来处理。延迟的自动-启动的设计目标主要是为了那些在引导过程早期必须的服务,如RPC服务,因为其他的服务要依赖它们;如果仅仅只是为了让服务自动运行,如Windows update服务,则可将服务标记为延迟的自动-启动,这样可以让关键服务启动得更快、更快地准备好用户桌面。

svchost进程

在一个进程中共享多个服务,可节省系统资源;但共享进程也意味着,如果该进程中的任何一个服务出现了错误因而导致进程退出,则该进程所有的服务都终止了。

Windows服务管理 - 图4

Windows服务管理 - 图5

当SCM在服务启动过程中碰到一个SvcHost服务,并且其ImagePath与映像数据库中已有的记录相匹配时,它不会再激发第二个进程,而是向早先为了该ImagePath值而启动的SvcHost发送一个启动命令。该SvcHost进程读取待启动服务配置的ServiceDll字段,将该Dll加载到它的进程中,以便启动此服务。

这种ServiceDll称之为SvcHost DLL,恶意代码常会加入一个已存在的组或者覆盖一个无关紧要的服务DLL,以利用SvcHost进程实现持久化和隐藏自身。

Windows服务管理 - 图6


服务标记

SCM会调用ScGenerateServiceTag来设置服务标记,此服务标记被保存在每个线程的线程环境块SubProcessTag域中,从而可以区分同一进程内的线程属于哪个服务。

Windows服务管理 - 图7

2. 更新网络驱动器字母

当新增或删除一个网络驱动器时,MPR会把变更通知给SCM,SCM获取最新的驱动器列表,发送一个WM_DEVICECHANGE Windows广播消息,从而Windows Explorer可更新文件夹窗口。

3. 维护Last Know Good Control Set

SCM要决定何时将系统的注册表配置HKLM\SYSTEM\CurrentControlSet保存为Last Know Good Control Set,默认情况下,一次成功的引导是由“所有自动-启动服务的成功启动”和“一次成功的用户登录”构成的。

SCM自己知道何时所有自动-启动服务都成功启动,而当一个用户登录的时候、Winlogin调用NotifyBootConfigStatus来通知SCM,开发人员也可以编写自己的验证程序,在注册表CurrentControlSet\Control\BootVerificationProgram中保存对应的位置,SCM在完成自动-启动服务的启动后,会启动该验证程序,然后等待该验证程序调用NotifyBootConfigStatus通知SCM。

SCM调用NtInitializeRegistry,用CurrentControlSet来更新Last Know Good Control Set。

Windows服务管理 - 图8

服务应用

https://www.codeproject.com/Articles/499465/Simple-Windows-Service-in-Cplusplus

服务应用也是个exe,只是这个exe不能双击执行,要CreateService注册为服务后由SCM来执行。

  1. sc create "My Sample Service" binPath= C:\SampleService.exe

Windows服务管理 - 图9

  1. main函数里首先要调用StartServiceCtrlDispatcher,来告诉SCM其服务入口点函数是哪个。StartServiceCtrlDispater会创建一个线程,调用服务入口点函数;主线程则接收SCM命令,再调用注册的ServiceCtrlHandler来处理。

Windows服务管理 - 图10

  1. 服务入口点函数,即ServiceMain,要干以下几件事:
  • 调用RegisterServiceCtrlHandler,注册ServiceCtrlHandler,从而能响应各种SCM命令:启动、停止、暂停、恢复等

Windows服务管理 - 图11

Windows服务管理 - 图12

  • 初始化工作
  • 创建一个ServiceWorkerThread,真正开始做服务应用想干的事,比如web服务应用会监听TCP 80端口,处理客户端请求

Windows服务管理 - 图13

  • 每个关键节点,调用SetServiceStatus来告诉SCM此服务的状态

服务控制程序

服务控制程序是标准的Windows应用程序,它用到了SCM服务管理函数,包括CreateService、OpenService、StartService、ControlService、QueryServiceStatus和DeleteService。为了使用这些SCM函数,SCP必须首先调用OpenSCManager函数以打开一个通向SCM的通信通道。在调用该打开函数的时候,SCP必须指定它想要执行的动作的类型,只有管理员组才可以请求以创建或删除服务的访问方式来打开SCM。

最典型的的SCP程序有services.msc。

交互式服务和会话0隔离

Windows Sysinternals : Windows Core Concepts - Sessions, Window Stations, Desktops, and Window Messages

Windows服务管理 - 图14

Windows服务管理 - 图15

  • 当用户使用如控制台、远程桌面、远程APP等登录机器时,会建立一个Terminal Services Session,会话ID从1开始。而session 0留给了非交互式的系统进程和Windows服务,它属于非交互式会话。
  • session 0包含多个Windows station(窗口站),session 1开始一个session 包含一个Windows Station 0,一个Windows Station包含多个桌面。Windows只能与一个交互式会话、一个桌面关联,无法两个终端登录同时操作鼠标、键盘。
  • 只有WinSta0是可见窗口站,所有的交互式进程都必须在WinSta0下;而session 0下的系统进程和Windows服务,Windows会将Service-0x0-3E7$窗口站分配给它,从而任何窗口都是不可见的。

Windows服务管理 - 图16

服务账户

  1. local system account
    • SID S-1-5-18,属于Administrators组
    • 权限很高,有很多特权
  2. network service account
    • SID S-1-5-20,属于Network Service组
    • 可以利用计算机账户向网络上其他的机器认证身份
  3. local service account
    • SID S-1-5-19,属于Local Service组
    • 只能访问那些允许匿名访问的网络资源

虚拟服务账户

Windows服务管理 - 图17

Windows服务管理 - 图18

Windows服务管理 - 图19

原文

Windows服务管理