Windows服务有三个组件构成:服务应用程序,服务控制程序(service control program,如services.msc),以及服务控制管理器(sevice control manager)。
Windows服务三组件
SCM(服务控制管理器)
SCM进程启动后,干了以下事情:
1. 启动autostart和demandstart的驱动和服务
配置为引导-启动和系统-启动的驱动程序,在任何用户模式进程执行起来以前,由I/O管理器将这些程序加载到系统中
Wininit.exe在系统引导的早期将SCM(services.exe)启动起来,SCM会将配置为自动启动与延迟启动的驱动程序和服务按序启动起来。
- 创建SvcctrlStartEvent_A3752DX的同步事件,当SCM完成必要步骤准备接收SCP命令时,会将此事件设置为有信号状态。SCP与SCM建立对话的函数是OpenSCManager,该函数会等待SvcctrlStartEvent_A3752DX事件。
- 调用ScGenerateServiceDB,读取HKLM\SYSTEM\CurrentControlSet\Control\ServiceGroupOrder\List和HKLM\SYSTEM\CurrentControlSet\Services来建立SCM的内部服务数据库。
- 有的服务配置为非本地系统账户登录,因此SCM会启动LSASS进程,此外Wininit也会启动LSASS进程,二者的初始化是同步进行的。
- 调用ScGetBootAndSystemDriverState确定引导-启动、系统-启动的驱动程序是否已成功启动,通过搜索\Driver的对象管理器名字空间目录中是否存在该驱动程序名称。
- 创建\Pipe\Ntsvcs命令管道,在该管道上监听SCP的进入消息。然后将SvcctrlStartEvent_A3752DX置成有信号状态。
- 调用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进程
在一个进程中共享多个服务,可节省系统资源;但共享进程也意味着,如果该进程中的任何一个服务出现了错误因而导致进程退出,则该进程所有的服务都终止了。
当SCM在服务启动过程中碰到一个SvcHost服务,并且其ImagePath与映像数据库中已有的记录相匹配时,它不会再激发第二个进程,而是向早先为了该ImagePath值而启动的SvcHost发送一个启动命令。该SvcHost进程读取待启动服务配置的ServiceDll字段,将该Dll加载到它的进程中,以便启动此服务。
这种ServiceDll称之为SvcHost DLL,恶意代码常会加入一个已存在的组或者覆盖一个无关紧要的服务DLL,以利用SvcHost进程实现持久化和隐藏自身。
服务标记
SCM会调用ScGenerateServiceTag来设置服务标记,此服务标记被保存在每个线程的线程环境块SubProcessTag域中,从而可以区分同一进程内的线程属于哪个服务。
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。
服务应用
https://www.codeproject.com/Articles/499465/Simple-Windows-Service-in-Cplusplus
服务应用也是个exe,只是这个exe不能双击执行,要CreateService注册为服务后由SCM来执行。
sc create "My Sample Service" binPath= C:\SampleService.exe
- main函数里首先要调用StartServiceCtrlDispatcher,来告诉SCM其服务入口点函数是哪个。StartServiceCtrlDispater会创建一个线程,调用服务入口点函数;主线程则接收SCM命令,再调用注册的ServiceCtrlHandler来处理。
- 服务入口点函数,即ServiceMain,要干以下几件事:
- 调用RegisterServiceCtrlHandler,注册ServiceCtrlHandler,从而能响应各种SCM命令:启动、停止、暂停、恢复等
- 初始化工作
- 创建一个ServiceWorkerThread,真正开始做服务应用想干的事,比如web服务应用会监听TCP 80端口,处理客户端请求
- 每个关键节点,调用SetServiceStatus来告诉SCM此服务的状态
服务控制程序
服务控制程序是标准的Windows应用程序,它用到了SCM服务管理函数,包括CreateService、OpenService、StartService、ControlService、QueryServiceStatus和DeleteService。为了使用这些SCM函数,SCP必须首先调用OpenSCManager函数以打开一个通向SCM的通信通道。在调用该打开函数的时候,SCP必须指定它想要执行的动作的类型,只有管理员组才可以请求以创建或删除服务的访问方式来打开SCM。
最典型的的SCP程序有services.msc。
交互式服务和会话0隔离
- 当用户使用如控制台、远程桌面、远程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$窗口站分配给它,从而任何窗口都是不可见的。
服务账户
- local system account
- SID S-1-5-18,属于Administrators组
- 权限很高,有很多特权
- network service account
- SID S-1-5-20,属于Network Service组
- 可以利用计算机账户向网络上其他的机器认证身份
- local service account
- SID S-1-5-19,属于Local Service组
- 只能访问那些允许匿名访问的网络资源