保护反恶意软件服务

Windows 8.1 引入了新的保护服务概念,以保护反恶意软件服务,这是恶意软件攻击的常见目标。

了解如何保护反恶意软件 (AM) 用户模式服务,以及如何选择以在反恶意软件服务中包含此功能。

此信息适用于以下操作系统及其后续版本:

  • Windows 8.1
  • Windows Server 2012 R2

本主题末尾列出了此处讨论的引用和资源。

介绍

大多数反恶意软件解决方案包括了用户模式服务,用于执行专用操作来检测和移除系统中的恶意软件。 此用户模式服务还经常负责下载最新的病毒定义和签名。 此用户模式服务已成为恶意软件的常见攻击目标,因为它是在系统上禁用保护的单一故障点。 为了抵御对用户模式服务的攻击,反恶意软件供应商必须为其软件添加大量功能和启发式方法。 但此类技术并非完全万无一失,而且往往容易出错,因为它们必须识别 Windows 对其服务执行的功能,并有选择地启用该功能。

在 Windows 8.1 中,引入了受保护服务的新概念,以支持将反恶意软件用户模式服务作为受保护的服务来启动。 以受保护方式启动服务后,Windows 将使用代码完整性来仅允许将受信任的代码加载到受保护服务中。 Windows 还会保护这些进程免受代码注入和其他来自管理员进程的攻击。

本文档介绍了具有开机初期启动的反恶意软件 (ELAM) 驱动程序的反恶意软件供应商如何选择加入此功能,并将其反恶意软件服务作为受保护的服务启动。

系统保护的进程

从 Windows 8.1 开始,内核中建立了一个新的安全模型,以更好地抵御对系统关键组件的恶意攻击。 此新的安全模型将用于特定方案(如播放 DRM 内容)的早期版本 Windows 中受保护进程基础结构扩展到可供第三方反恶意软件供应商使用的常规用途模型。 受保护的进程基础结构仅允许加载受信任的签名代码,并且具有抵御代码注入攻击的内置防御。

注意

CodeIntegrity 在受保护进程内禁止使用以下脚本 DLL(无论是直接还是间接加载),例如通过 WinVerifyTrust 或 WinVerityTrustEx 来使用验证码检查脚本签名:scrobj.dllscrrun.dlljscript.dlljscript9.dllvbscript.dll

有关受保护进程的详细信息,请参阅 Windows Vista 中的受保护进程

新的安全模型使用一个略有不同的保护过程基础结构变体,它名为系统保护进程,且更适用于此功能,因为这样可以使 DRM 内容分开。 每个系统保护进程具有一个关联的级别或属性,可以指示允许在进程中加载的已签名代码的签名策略。 在反恶意软件服务选择加入受保护服务模式后,将仅允许在该进程中加载 Windows 签名代码或使用反恶意软件供应商证书来签名的代码。 同样,其他受保护的进程级别也具有由 Windows 强制执行的不同代码策略。

要求

要设置反恶意软件用户模式服务以便其作为受保护服务运行,反恶意软件供应商必须在 Windows 计算机上安装 ELAM 驱动程序。 除了现有 ELAM 驱动程序认证要求之外,驱动程序还必须具有嵌入的资源部分,其中包含用于对用户模式服务二进制文件进行签名的证书的信息。

重要

在 Windows 8.1 中,认证链必须是由驱动程序验证确定的已知根,或者必须包含根证书。

在启动过程中,将会从 ELAM 驱动程序中提取此资源部分,以验证该证书信息并注册反恶意软件服务。 还可以通过调用特殊 API 在反恶意软件安装过程中注册反恶意软件服务,如本文档稍后所述。

从 ELAM 驱动程序成功提取资源部分并注册用户模式服务后,将会允许以受保护服务方式启动该服务。 以受保护方式启动服务后,系统上的其他非受保护进程将无法注入线程,并且不允许将其写入受保护进程的虚拟内存中。

此外,任何加载到受保护进程的非 Windows DLL 都必须使用适当的证书进行签名。

有关 ELAM 驱动程序的详细信息,请参阅开机初期启动的反恶意软件

反恶意软件服务签名要求

需要以受保护方式启动的用户模式服务必须使用有效证书进行签名。 服务 EXE 必须经过页哈希签名,并且加载到服务中的任何非 Windows DLL 也必须使用相同的证书进行签名。 必须将这些证书的哈希添加到将会链接到 ELAM 驱动程序的资源文件中。

注意

必须使用 SHA256 文件/页哈希,但证书可以继续使用 SHA1。

我们建议反恶意软件供应商使用其现有的验证码证书对其反恶意软件服务二进制文件进行签名,并将此验证码证书的哈希包含在资源部分中,以指示用于对服务二进制文件进行签名的证书。 如果更新此证书,则必须使用更新后的证书哈希发布较新版本的 ELAM 驱动程序。

辅助签名(可选)

反恶意软件供应商可以选择设置专用 CA,并使用此 CA 中的证书对反恶意软件服务二进制文件进行代码签名以作为辅助签名。 使用专用 CA 的主要优点是,它支持供应商创建具有专用 EKU 属性的证书,以用于区分来自同一供应商的多个产品。 它还减少了由于证书过期而更新 ELAM 驱动程序的需求,因为专用 CA 证书通常具有更长的有效期。

请注意,如果使用专用 CA 证书对服务二进制文件进行签名,则二进制文件还必须使用现有的验证码证书进行双重签名。 如果二进制文件不是由已知的受信任 CA(例如 VeriSign)签名的,计算机用户将会对二进制文件没有信心,因为他们无法信任专用 CA。 使用现有验证码证书对二进制文件进行双重签名还会支持二进制文件在下层操作系统上运行。

有关如何设置和安装证书颁发机构的详细信息,请参阅设置证书颁发机构安装证书颁发机构

注意

为了与 Windows Vista 或 Windows XP(或不带 SHA2 补丁的 Windows 7)兼容,可以在使用 SHA256 文件/页哈希通过 SignTool.exe 对二进制文件签名时使用“/as”开关。 这会将签名添加为文件的辅助签名。 SHA1 首先对文件进行签名,因为 Windows XP、Windows Vista 和 Windows 7 只会看到第一个签名。

DLL 签名要求

如前所述,任何加载到受保护服务的非 Windows DLL 必须通过用于对反恶意软件服务进行签名的同一证书进行签名。

目录签名

反恶意软件供应商可以包括其他公司开发的包,而无需更新二进制签名。 这可以通过在目录中包括使用其验证码证书签名的二进制文件来实现,具体步骤如下:

  1. 使用 MakeCat 生成目录
  2. 将所有不带相应签名的二进制文件添加到目录中
  3. 使用验证码证书对目录进行签名,就像使用任何其他二进制文件一样
  4. 使用 add catalog 函数将目录包含在应用程序中。

当代码完整性遇到没有相应签名的包时,它将搜索带有已批准的签名的目录。 只要遵循这些步骤,它就会找到此目录,并将其与应用程序一起安装。

资源文件信息

必须创建资源文件并将其链接到 ELAM 驱动程序中。 必须将证书的哈希以及其他证书信息添加到资源文件中。

资源部分必须处于以下布局中,系统才能成功从二进制映像中提取资源并验证已嵌入的证书信息。

MicrosoftElamCertificateInfo  MSElamCertInfoID
{
      3, // count of entries
      L”CertHash1\0”,
      Algorithm,
      L”EKU1\0”,
      L”CertHash2\0”,
      Algorithm,
      L”\0”, //No EKU for cert hash 2
      L”CertHash3\0”,
      Algorithm,
      L”EKU3a;EKU3b;EKU3c\0”,  //multiple EKU entries supported (max: 3)
}

有关用户定义的资源文件的详细信息,请参阅用户定义的资源

CertHash

用于对反恶意软件服务进行签名的证书哈希。 Windows SDK 中随附的 CertUtil.exe 工具可用于获取哈希。

certutil.exe –v <path to the signed file>

例如:

anti-malware protected service certificate hash (certhash)

算法

算法值表示证书的算法。 支持以下算法值:

0x8004 - SHA1 0x800c - SHA256 0X800d - SHA384 0x800e - SHA512

请记住包括算法的值(如上所示),而不是算法的实际名称。 例如,如果证书基于 SHA256 算法,则请在资源部分中包括 0x800c。

EKU

EKU 对象表示证书的单个扩展密钥用法 (EKU) 属性。 这是可选的,并且如果证书未关联任何证书,则应指定“\0”。如果同一系统上运行的多个产品和服务来自单个反恶意软件供应商,则反恶意软件供应商可以使用专用 CA 证书的 EKU 属性来区分各个服务。 例如,如果系统上运行的两个服务来自同一反恶意软件供应商,并且由同一 CA 签名,则可以使用包含特殊 EKU 的 CA 颁发的证书对需要以受保护方式启动的服务进行签名。 此 EKU 必须添加到资源部分。 然后,系统会注册该 EKU,并将其与证书哈希配对,以验证和启动作为受保护服务的服务。

请注意,证书链必须包含代码签名 EKU (1.3.6.1.5.5.7.3.3),但此 EKU 不得包含在 ELAM 驱动程序的资源部分中。

注意

如果 EKU 信息包含在 ELAM 驱动程序的证书信息中,则必须在对二进制文件进行签名时使用相同的 EKU。

注意

EKU 中 OID 的 Windows 代码完整性字符串表示形式的最大长度为 64 个字符,其中包括零终止字符。  

注意

如果指定了多个 EKU,则使用 AND 逻辑对这些 EKU 进行评估。 最终实体证书必须满足给定条目的 ELAM 资源部分中指定的所有 EKU。

计数

如果使用验证码证书和专用 CA 证书对反恶意软件服务二进制文件进行签名,则必须在资源部分中仅添加专用 CA 证书信息。

启动处于受保护状态的反恶意软件服务

注册服务

必须向系统注册反恶意软件服务,然后才能以受保护方式启动它。 在安装反恶意软件期间,安装程序可以安装 ELAM 驱动程序并重新启动系统以自动注册服务。 系统将在启动时注册服务,方法是从链接到 ELAM 驱动程序的上述资源文件中提取证书信息。

在安装阶段,强烈建议重启系统,以便加载 ELAM 驱动程序并验证系统的状态。 但对于必须避免重新启动的情况,Windows 还会公开反恶意软件安装程序使用 API 将服务注册为受保护服务的机制。

在不重新启动系统的情况下注册服务

在安装过程中,反恶意软件安装程序可以调用 InstallELAMCertificateInfo API,并提供 ELAM 驱动程序文件的句柄。 系统将打开 ELAM 驱动程序,调用内部例程以确保对 ELAM 驱动程序正确签名,并从与 ELAM 驱动程序关联的资源部分提取证书信息。 有关函数语法,请参阅 InstallELAMCertificateInfo

代码示例:

HANDLE FileHandle = NULL;

FileHandle = CreateFile(<Insert Elam driver file name>,
                        FILE_READ_DATA,
                        FILE_SHARE_READ,
                        NULL,
                        OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL
                        );

if (InstallElamCertificateInfo(FileHandle) == FALSE)
{
    Result = GetLastError();
    goto exitFunc;
}

以受保护方式启动服务

安装程序可以按照以下步骤创建、配置和启动受保护的服务:

  1. 调用 CreateService API 以创建服务对象并将其添加到服务控制管理器 (SCM) 数据库

  2. 调用 SetServiceObjectSecurity API 以设置在步骤 1 中创建的服务对象的安全描述符

  3. 调用 ChangeServiceConfig2 API 以将服务标记为受保护服务,并指定已添加到 Winsvc.h(从 Windows 8.1 开始)的新 SERVICE_CONFIG_LAUNCH_PROTECTED 枚举值

    代码示例:

    SERVICE_LAUNCH_PROTECTED_INFO Info;
    SC_HANDLE hService;
    
    Info.dwLaunchProtected = SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT;
    
    hService = CreateService (/* ... */);
    
    if (ChangeServiceConfig2(hService,
                             SERVICE_CONFIG_LAUNCH_PROTECTED,
                             &Info) == FALSE)
    {
        Result = GetLastError();
    }
    
  4. 调用 StartService API 以启动服务。 以受保护方式启动服务时,SCM 会使用代码完整性 (CI) 子系统进行检查,以验证证书信息。 CI 对证书信息进行验证后,SCM 会以受保护方式启动该服务。

    1. 请注意,如果尚未通过调用 InstallELAMCertificateInfo API 注册服务,此步骤将会失败
    2. 如果服务已配置为在系统启动阶段自动启动,则可以避免此步骤,并且只需重新启动系统。 重新启动期间,系统会自动注册服务(如果 ELAM 驱动程序成功启动),并在受保护的模式下启动服务。
    3. 如果服务无法启动,请参阅代码完整性事件日志记录和系统审核中的信息,以及以下主题。 在此处,你将找到更详细的错误消息,以了解代码完整性系统阻止服务启动的原因。 这些日志还可以帮助你识别服务已尝试加载但无法加载的 DLL。

以受保护方式启动子进程

新的安全模型还支持反恶意软件保护的服务以受保护方式启动子进程。 这些子进程将与父级服务在同一保护级别运行,并且必须使用通过 ELAM 资源部分注册的同一证书进行对其二进制文件签名。

为了支持受反恶意软件保护的服务以受保护方式启动子进程,已公开新的扩展属性密钥 PROC_THREAD_ATTRIBUTE_PROTECTION_LEVEL 已公开,并且必须将其与 UpdateProcThreadAttribute API 一起使用。 必须将指向 PROTECTION_LEVEL_SAME 属性值的指针传递到 UpdateProcThreadAttribute API 中

注意:

  • 要使用此新属性,服务还必须在 CreateProcess 调用的进程创建标志参数中指定 CREATE_PROTECTED_PROCESS
  • 需要使用 /ac 开关对服务二进制文件进行签名,以包含跨证书来将其链接到已知的 CA。 没有正确链接到已知根 CA 的自签名证书将不起作用。

代码示例:

DWORD ProtectionLevel = PROTECTION_LEVEL_SAME;
SIZE_T AttributeListSize;

STARTUPINFOEXW StartupInfoEx = { 0 };

StartupInfoEx.StartupInfo.cb = sizeof(StartupInfoEx);

if (InitializeProcThreadAttributeList(NULL,
                                      1,
                                      0,
                                      &AttributeListSize) == FALSE)
{
    Result = GetLastError();
    goto exitFunc;
}

StartupInfoEx.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc(
    GetProcessHeap(),
    0,
    AttributeListSize
    );

if (InitializeProcThreadAttributeList(StartupInfoEx.lpAttributeList,
                                      1,
                                      0,
                                      &AttributeListSize) == FALSE)
{
    Result = GetLastError();
    goto exitFunc;
}

if (UpdateProcThreadAttribute(StartupInfoEx.lpAttributeList,
                              0,
                              PROC_THREAD_ATTRIBUTE_PROTECTION_LEVEL,
                              &ProtectionLevel,
                              sizeof(ProtectionLevel),
                              NULL,
                              NULL) == FALSE)
{
    Result = GetLastError();
    goto exitFunc;
}

PROCESS_INFORMATION ProcessInformation = { 0 };

if (CreateProcessW(ApplicationName,
                   CommandLine,
                   ProcessAttributes,
                   ThreadAttributes,
                   InheritHandles,
                   EXTENDED_STARTUPINFO_PRESENT | CREATE_PROTECTED_PROCESS,
                   Environment,
                   CurrentDirectory,
                   (LPSTARTUPINFOW)&StartupInfoEx,
                   &ProcessInformation) == FALSE)
{
    Result = GetLastError();
    goto exitFunc;
}

更新和服务

将反恶意软件服务作为保护项启动后,其他非受保护进程(甚至管理员)将无法停止该服务。 如果要更新服务二进制文件,反恶意软件服务需要从安装程序接收回调来停止自身,以便可以对其提供服务。 停止服务后,反恶意软件安装程序可以执行升级,然后按照上述“注册服务”和“以受保护方式启动服务”部分中的步骤注册证书并启动受保护的服务。

请注意,服务应确保只有受信任调用方才能停止服务。 允许不受信任的调用方停止服务将会违背保护服务的目的。

取消服务注册

卸载受保护服务时,服务必须通过调用 ChangeServiceConfig2 API 将自身标记为不受保护。 请注意,由于系统不允许任何非受保护进程更改受保护服务的配置,因此对 ChangeServiceConfig2 的调用必须由受保护服务本身进行。 将服务重新配置为以未受保护方式运行后,卸载程序只需采取适当的步骤即可从系统中移除反恶意软件。

调试受反恶意软件保护的服务

作为受保护进程安全模型的一部分,其他非受保护的进程无法将线程注入或写入到受保护进程的虚拟内存中。 但是,允许内核调试器 (KD) 调试任何受反恶意软件保护的进程。 KD 还可用于检查反恶意软件服务是否以受保护方式运行:

dt –r1 nt!_EPROCESS <Process Address>
+0x67a Protection       : _PS_PROTECTION
      +0x000 Level            : 0x31 '1'
      +0x000 Type             : 0y0001
      +0x000 Signer           : 0y0011

如果 Type 成员的值为 0y0001,则服务将以受保护方式运行

此外,只允许对反恶意软件保护的服务执行以下 SC 命令:

  • sc config start=Auto
  • sc qc
  • sc start
  • sc interrogate
  • sc sdshow

如果附加了调试器,则在将未签名(或未以适当方式签名)的二进制文件加载到反恶意软件保护的服务中时,请使用注册表中的以下标志中断调试器。

Key:   HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CI
Value: DebugFlags      REG_DWORD

将该值设置为 00000400 以在签名验证失败时中断调试器

注意

受保护的进程限制:

  1. 具有 UI 或 GUI 的进程无法受到保护,因为内核会将进程锁定在内存中,并且不允许对其进行写入。
  2. 在 Windows 10 版本 1703(创意者更新)之前,由于本地安全机构 (LSA) 和受保护进程之间证书共享的限制,受保护进程无法使用 TLS 或 SSL 通信协议。

资源

有关详细信息,请参阅:

本文引用了以下 Windows API 函数: