Windows App 运行控制机制 Smart App Control 的内部安全架构分析(下)

下面的伪代码或多或少地展示了SmartLocker非继承属性的最后一部分是如何工作的。

12.png

注意: 根据稍后如何使用此函数中的值来填充TraceLogging字符串,我们知道防御措施将评估过程的所有这一部分视为: Is防御措施Shell。

这或多或少就是我们通过在调用之后立即双击从资源管理器启动的进程所需要的:

13.png

CipCheckSmartlockerEAandProcessToken的情况就这样了,现在再说说CipExternalAuthorizationCallback。

现在,让我们说说Intelligent Security Graph所使用的代码段,它现在已被扩展以添加一些SAC功能。首先,将再次检查策略选项Intelligent Security Graph Authorization(智能安全图授权),如果未设置,函数将使用从CipCheckSmartlockerEAandProcessToken获取的值退出。如果该值在策略中处于活动状态(SAC策略就是这种情况),该函数将使用前面讨论的IsTrustedSigning来确定它是否应该继续。如果映像可信,将执行以下检查:

如果ValidatedSigningLevel等于“由使用AMPPL(7)的产品的AV签名”,并且策略的值为VerifiedAndReputableAllowAntiMalware,则分数将用值AllowAnti Malware(0x100000)进行异或运算,函数将返回。

如果映像不可信,则函数将继续查询防御措施。如上所述,向防御措施发出查询的函数是CiCatDbSmartlockerDefenderCheck。此函数将接收两个MPFILE_TRUST_EXTRA_INFO结构,一个填充请求数据,另一个接收回复数据。代码还将从FileObject传递FileName。MPFILE_TRUST_EXTRA_INFO结构如下所示。

14.png

双方之间的通信是使用RPC实现的,CI.dll将实现客户端,服务器将在cryptcatsvc.dll中实现。为了记录,RPC存根的IID是f50aac00-c7f3-428e-a022a6b71bfb9d43。

cryptcatsvc在服务CryptSvc中运行。在用于RPC服务器的发送函数中,我们重点关注以下函数:

s_SSCatDBSmartlockerDefenderCheck (Already present in 22H1);s_SSCatDBSmartlockerDefenderCheck2 (New to 22H2);s_SSCatDBSendSmartAppControlBlockToast;s_SSCatDBSendSmartAppControlSwitchEnforceToast;

SmartLockerDefenderCheck函数的v1和v2之间的最大区别在于,在v2中,该函数接受请求和回复MPFILE_TRUST_EXTRA_INFO作为其参数的一部分。这两个函数最终都调用了助手函数CatDBSmartlockerDefenderCheckHelper。

CI将从这些函数调用s_SSCatDBSmartlockerDefenderCheck2,它将首先加载MpClient.dll。

注意:在第一次执行时,将在防御措施配置中启用SmartLocker。该函数将调用MpClient导出的函数MpSmartLockerEnable。此函数只需注册Defender ELAM证书信息(打开Wdboot.sys的句柄并调用InstallELAMCertificateInfo),然后使用RPC从MpSvc.dll调用方法ServerMpEnableSmartLocker,它将检查防御措施配置中是否设置了SmartLockerMode,如果没有,它将写入。

打开库的句柄后,该函数将使用CI.dll提供的文件名来打开一个文件句柄,该句柄将被传递给MpClient导出的函数MpQueryFileTrustByHandle2,该函数只在来自于DefenderCheck2时被调用,如果是旧版本的DefenderCheck,则将调用MpQueryFileTrustByHandle。

在MpQueryFileTrustByHandle2内部,代码将使用该文件的句柄来创建文件映射,该文件映射将被防御程序用于对其进行内存扫描。下面的InSequence函数将通过从MpClient(客户端)到MpSvc(服务器)发出RPC调用来执行。显然,我们刚才看到的所有函数调用都接受CI.dll设置的MPFILE_TRUST_EXTRA_INFO作为参数的一部分。

ServerMpRpcMemoryScanStart:设置CMpMemScanContext和CMpMemScanEngineVfz(使用GetAttributeTrustCheck作为GetAttributions函数),并进行异步扫描;

ServerMpRpcMemoryScanQueryNotification:检索扫描信息;

ServerMpRpcMemoryScanClose:关闭并清除CMpMemScanContext。

这些函数的内部结构不在本文所讲的范围,我想强调的是,当启用SAC时,防御措施将主动扫描文件并进行云查询。

从扫描检索到的信息中有三个可能的信号:

0x31001:检索到的MPTRUST_INFO(IGS);

0x31002:检索到的MPFILE_TRUST_EXTRA_INFO(SAC);

0x4005:与RSIG_VIRINFO相关;

最后完成防御措施通信,下图显示了代码到达防御措施时客户端(CI)和服务器(cryptcatsvc)堆栈。

15.png

需要注意的是,如果我们的SAC处于强制状态,并且设备中没有互联网连接,则默认操作是阻止该进程,并且将显示一条通知,提示“智能应用程序控制无法验证此应用程序,请检查您的互联网连接,然后重试”。

返回外部授权回调,如果RPC调用失败,则未设置策略设置VerifiedAndReputableAllowUnknown,并且ValidateSigningLevel不是以下任何一项:

Microsoft Store signed app PPL (Protected Process Light)Microsoft Store-signedMicrosoft signedWindows signedOnly used for signing of the .NET NGEN compilerWindows Trusted Computing Base signed

然后将验证分数与值Unattainable(0x40000)进行异或运算,函数将返回。如果RPC调用成功,则将调用函数CiHandleDefenderSignals。顾名思义,此函数将处理防御措施发送回的消息。它将遍历返回的元素数,其中每个元素的类型为MPFILE_TRUST_EXTRA_INFO。根据ReplyType字段,它将执行不同的操作。更有趣的两种情况是:首先,当返回信任结果时。在该示例中,信息将指向MP_INFO_RESULT,其中的值将复制到验证上下文:

16.png

第二个有趣的示例是信息指向MP_NW_CONTROL枚举。在该示例中,根据控制命令,该功能将被禁用或切换到强制模式。这基本上将更新VerifiedAndReputablePolicyState RegKey,并更新WorkItem中的策略。

17.png

在我们从学习模式更改为强制模式的情况下,将发出对函数s_SSCatDBSendSmartAppControlSwitchEnforceToast的RPC调用。在此函数中,DLL wldap . DLL将被加载,然后调用函数WldpSendSmartAppControlSwitchEnforceToast。

从信号处理程序回来后,有一些细微差别。如果NW控制命令设置了标志IsUnfriendlyFile,则Score将更新为值UnfriendalyFile(0x80000),函数将返回。如果未设置标志,则TrustInfo和FileObject将被传递到带有标志0x82的函数CipSetFileCache中,这意味着EA $Kernel.Purge.CIpCache将用于存储此信息。

最后,需要根据防御程序返回的信任调整分数,有5个选项:

Trust == 1:分数将使用值0x202进行异或运算,不过我对这个值不太了解;

Trust == -1 (0xFFFFFFFF):如果策略设置VerifiedAndReputableAllowUnknown被设置,则分数将使用值AllowUnderknown(0x20000)进行异或运算;

Trust == -2 (0xFFFFFFFE):分数将使用值Malicious (0x80)进行异或运算;

Trust == -3 (0xFFFFFFFD):分数将用PUA(0x100)值进行异或运算;

任何其他情况下,分数将用值0x42进行异或运算。

这几乎就是外部授权回调的全部内容,现在我们回到调用外部授权回调时的SIPolicyValidateImageInternal!

SIPolicyValidateImageInternal

在进入外部授权回调之前,我们将讨论SIPolicyObjectValidationEngine函数如何遍历策略并调用内部SIPolicy ValidateImageInternal,后者稍后将调用外部auth回调。现在,调用回调后,我们返回到SIPolicyValidateImageInternal,并返回验证分数。如果启用了SAC,则该函数将继续评估分数,并将此分数传播到验证引擎分数,并根据该得分设置相应的NTSTATUS。

18.png

如上图所示,在大多数分支中,它会将相应的NTSTATUS设置为验证状态,然后跳转到我所称为ProcessDbgAndReprieve的状态。这只不过是一种检查内核调试器是否附加到调试器控制台中以记录策略冲突的方法。

19.png

如果未遵循前一个映像中的任何分支,或者分数为Unattainable但设置了AllowUnknown,则函数将继续根据策略规范评估对象。首先检查文件规范,这将在函数SIPolicyMatchFileRules内完成。此函数将接收以下参数:

具有要评估的文件规范的策略;

OriginalFileName;InternalName;FileDescription;ProductName;

我强烈建议阅读MSDN的“理解Windows防御应用程序控制(WDAC)策略规范和文件规范”一节,以了解更多关于策略规范和可用于它们的不同选项的内容。

与我们在第1部分中看到的Policy Secure Settings类似,该函数将使用作为key传递到函数bsearch的数据建立一个结构。关键结构具有以下原型:

20.png

bsearch函数的base和num将取自SI_POLICY结构。将策略解析为SI_policy结构时,将设置一个包含两个场景的数组。每个场景都包含其特定的文件规范、允许的签名者、拒绝的签名者和异常规范。如上所述,当调用SIPolicyMatchFileRules时,要评估的场景的特定数量被传递给函数。此数字将用作函数的索引,以了解要选取Scenarios数组的哪个元素。每个场景都由以下结构表示:

21.png

如果没有FileName级别的文件规范匹配,则函数将继续计算哈希级别的文件规范:

22.png

如果FileName或Hash匹配,则SIPolicyMatchFileRules返回TRUE,验证状态将设置为status_SYSTEM_INTEGRITY_POLICY_VIOLATION。

如果对SAC策略使用的哈希和文件名感兴趣,可以查看策略的FileRule标签下的整个列表。

如果没有匹配的文件规范,则下一步(如果映像已签名)是根据“拒绝”和“允许”签名者验证签名链信息。首先,将检查被拒绝的签名者。如果与前面相同的规范在此匹配,该函数将把验证状态设置为status_system_integrity_policy_violate。如果没有拒绝签名者规范匹配,代码将继续检查允许的签名者规范。在该示例中,如果存在匹配,则会清除以前的任何状态/分数。根据策略签名验证映像签名的过程主要在函数SIPolicyValidateChainAgainstSigner中完成。此函数将作为第一个参数接收映像的SI_CHAIN_INFO,并在@r8中接收POLICY_SIGNERS_DATA。

关于这个POLICY_SIGNERS_DATA结构,基本上SI_POLICY结构保留一个POLICY-SIGNERS_DATA数组。这些代表两种方案的所有Allow和Deny签名。代码知道哪些规范适用于哪个场景的方式,这意味着要使用POLICY_SIGNERS_DATA数组的哪个索引是非常聪明的。这是我之前在文件规范中没有解释的事情,所以现在是检查它的好时机。如果你返回并检查SI_POLICY_SCENARIO结构,将看到对于每个规范类型结构(file, Allow, Deny),都有一个SI_RULES结构,其中包含一个我称为IndexArray的字段。基本上,这是一个索引数组,用于指示该特定场景和规则必须使用包含数据的数组中的哪个索引。让我们看一个快速的伪代码片段,以便更好地理解这一点。

23.png

这可能不是百分之百准确的,因为我省略了很多在中间进行的检查。

为了更好地了解签名是如何验证的,接下来你可以找到POLICY_SIGNERS_DATA的原型,注意,这将适用于允许签名者和拒绝签名者。

24.png

通过查看SI_CHAIN_INFO和POLICY_SIGNERS_DATA,你可以或多或少地了解如何在SIPolicyValidateChainAgainstSigner函数中进行比较。最后,为了总结Signer规范的验证,下面是在SIPolicyValidateChainAgainstSigner条目处使用SAC强制策略验证ProcessHacker时记录的映像。

25.png

老实说,为了达到这张图片的目的,我不得不稍微修改代码流。因为在第一次签名检查时,Type将匹配,然后它将退出循环。之所以会实现这一点,是因为这个POLICY_SIGNERS_DATA中的信息比第一个检查的要多。在第一次选中时,唯一的填充值是Type(设置为0x14)。我已尝试查找有关此Type值的信息,但找不到任何信息。

因此,在为每个活动策略和补充策略运行整个过程之后,我们将返回函数CipApplySiPolicyEx,为每个BasePolicy提供一个CI_VALIDATION_RESULT。补充策略的结果将写入与BasePolicy相同的CI_VALIDATION_RESULT中。此时,该函数只会遍历验证结果,将这些结果存储在validation Context中。此外,此时SmartLocker事件将记录在函数CiLogSIPolicySmartlockerEvent中。此处可以记录四种类型的事件:

SmartlockerOperationalAudit (EventId: 3091)SmartlockerOperationalFailure (EventId: 3092)SmartlockerVerbose (EventId: 3088)SmartlockerOperationalSuccess (EventId: 3090)

26.png

我们几乎完成了,现在将进入调用堆栈,将验证状态传递到上面的函数。最后,我们将回到CI入口点CiValidateImageHeader,与之前一样,我们不会对这个函数进行过多讨论。关于SAC唯一有趣的一点是,如果SigningLevel匹配以下任何一项:

尚未检查签名级别;

文件未签名;

受Windows防御措施应用程序控制策略信任;

开发者签名代码;

SAC结果是允许执行,然后将使用函数CipInstrumentNightsWatchAllow记录操作。此函数可以为提供程序CodeIntegrity编写四个基于TraceLogging的事件。具有以下名称的NWActivityVerbose & CodeIntegrity.NWActivity。

27.png

执行此函数时,将记录QuestionableAllow或Allow。如果采用了记录QuestionableAllow的路径,那么如果所需数据可用,还将写入QuestionaleAllowSignatureInfo&OriginClaimData。

由于这些是基于跟踪日志记录的事件,我们需要使用一些特殊办法来捕获跟踪。值得庆幸的是,Matt已经做了所有艰苦的工作来研究和记录这类事件的过程。看了他的文章《Windows RE使用WPP和TraceLogging》后,我们可以使用powershell中的以下4行代码来启动ETW会话,该会话将捕获NWActivity和NWActovityVerbose提供程序。

28.png

开始跟踪并使用一些应用程序/安装程序后,你应该有一个可以用EventViewer打开的EventLog,你可以发现防御措施最终信任ProcessHacker之类的事情。

29.png

总结

就个人而言,我认为微软为提高操作系统的安全性而采取的措施是很好的,其最终目标是让用户更加安全。另一方面,我确实看到了SAC和Windows 10 S之间的一些相似之处。对于SAC,当设置为强制时,限制由具有数字签名的应用程序设置,如果没有签名,则由防御措施云认为可信的应用程序进行设置。

第一种选择,即使你知道数字签名可以很好地验证应用程序,许多开源项目或自由开发者负担不起,不幸的是,这给开发者带来了一些限制。

第二个选项,即对“智能云安全服务”的查询,这也是我希望微软提供更多信息的地方,因为基本上应用程序能否运行的决定将完全取决于微软。