0:008> !avrf -net -wsastacks count
Current WSARefCount: 1 (WSAStartup call count minus WSACleanup call count for the target process)
0:008> !avrf -net -wsastacks
Current WSARefCount: 1 (WSAStartup call count minus WSACleanup call count for the target process)
THREAD ID: 0xe4c called WSAStartup
vfNet!WSAInitStacks<NetAllocatorViaPrivateHeap>::AddWSAStackTrace
vfNet!VfHookWSAStartup
WININET!LoadWinsock
WININET!GlobalDataInitialize
WININET!InternetSetOptionA
WININET!InternetSetOptionW
IEFRAME!LCIEUpdateSessionStartTime
IEFRAME!LCIETab_ThreadProc
iertutil!_IsoThreadProc
vfbasics!AVrfpStandardThreadFunction
kernel32!BaseThreadInitThunk
ntdll!__RtlUserThreadStart
ntdll!_RtlUserThreadStart
此应用程序验证程序插件监视单个进程对身份验证 API AcquireCredentialsHandle 和 InitializeSecurityContext 的调用,以检测 NTLM 协议的使用。 NTLM 是过时的身份验证协议,存在缺陷,可能会损害应用程序和操作系统的安全性,不应使用。
NTLM 身份验证风险
过时的 NTLM 身份验证协议最重要的缺点是缺少服务器身份验证,这可能导致攻击者欺骗用户连接到欺骗服务器。 作为缺少服务器身份验证的结果,使用 NTLM 的应用程序也可能容易受到称为“反射”攻击的攻击类型。 后者允许攻击者将用户的身份验证对话劫持到合法服务器,并使用它向用户的计算机对攻击者进行身份验证。 NTLM 的漏洞和利用方式是提高安全社区研究活动的目标。
尽管 Kerberos 已推出多年,但许多应用程序仍会编写为仅使用 NTLM。 这不需要减少应用程序的安全性。 但是,Kerberos 在所有方案中都不能替换 NTLM ,主要是那些客户端需要向未加入域的系统进行身份验证(家庭网络可能是其中最常见的网络)。 Negotiate 安全包允许使用 Kerberos 的向后兼容妥协(尽可能使用 Kerberos),并且仅在没有其他选项时还原到 NTLM。 切换代码以使用 Negotiate 而不是 NTLM 会显著增加客户的安全性,同时引入很少或没有应用程序兼容性。 协商本身不是一颗银弹 - 在某些情况下,攻击者可以强制降级到 NTLM,但这些攻击要更加困难。 但是,一个立即改进是,编写为正确使用 Negotiate 的应用程序自动不受 NTLM 反射攻击的影响。
通过对使用 NTLM 的最终警告:在 Windows 中,可以在操作系统级别禁用 NTLM 的使用。 如果应用程序对 NTLM 有硬依赖关系,则禁用 NTLM 时,它们将无法进行身份验证。
哪些因素会导致 NTLM 在应用程序中“硬编码”?
有两个因素将导致对 NTLM 的硬依赖。 第一个是显式选择 NTLM 作为应用程序要使用的身份验证包。 对于某些协议和 API,NTLM 的选择是显而易见的,例如调用 API AcquireCredentialsHandle ()。 对于其他协议,它可能不太明显。 例如,当 RPC 通过网络使用 RPC 时,RPC 的默认身份验证包(RPC_C_AUTHN_DEFAULT)实际上是 NTLM 的别名,甚至显式标志(RPC_C_AUTH_WINNT)中没有任何 NTLM 缩写。 这种构造使选择 NTLM 变得更容易,而无需知道你已这样做。
就 NTLM 而言,开发人员应使用其他身份验证方法,例如 Negotiate 包(有时也称为 SPNEGO 或 SNEGO 包)。 包选择需要在客户端和服务器组件上匹配,以便 Negotiate 能够尝试使用 Kerberos - 因此应用程序客户端和服务器部分都需要使用 Negotiate。 如果任一方都使用 NTLM(与旧版本的情况一样),协商仍然有效,但始终会还原到 NTLM。 如何告知应用程序使用 Negotiate 因协议而异。 部分最常见的协议(RPC、LDAP、DCOM、HTTP)将在主题 5000 中详细介绍 - 应用程序已显式选择 NTLM 包。
导致 NTLM 使用的第二个因素是客户端未向身份验证过程提供有效的服务器目标名称。 在支持或需要相互身份验证(如 Kerberos)的协议中,目标名称用于实现相互身份验证。 身份验证 API(如 InitializeSecurityContext)采用可选参数,通常称为“TargetName”、“PrincipalName”或“ServerPrincipalName”。 这是域控制器用于选择正确的域帐户来获取目标服务的凭据的标识符。 由于 NTLM 没有服务器身份验证的概念,因此 NTLM 不需要此参数才能成功进行身份验证。 另一方面,Kerberos 要求客户端获取对客户端进行身份验证的服务有效的服务票证。 如果未指定目标名称或无效的目标名称,Kerberos 身份验证将始终失败。 选择“协商”作为包时,不提供任何目标名称(或无效的目标名称)将导致完全跳过 Kerberos 并使用 NTLM。 大多数身份验证 API 将目标名称作为可选参数接受(如果 NULL 没有错误)。 除非开发人员重写它并提供显式目标名称 NTLM(此外,可反射 NTLM)是结果。
NTLM 插件的工作原理
验证程序插件检测到以下错误:
NTLM 包直接在对 AcquireCredentialsHandle(或更高级别的包装 API)的调用中指定。
对 InitializeSecurityContext 的调用中的目标名称为 NULL。 在这种情况下,Negotiate 直接回退到 NTLM。
对 InitializeSecurityContext 的调用中的目标名称不是格式正确的 SPN、UPN 或 NetBIOS 样式域名。 在这种情况下,域控制器返回“找不到主体”错误,这会导致 Negotiate 回退到 NTLM。
插件还会在检测到降级到 NTLM 时记录警告;例如,当域控制器找不到 SPN 时。 这些仅记录为警告,因为它们通常是合法的案例,例如,在向未加入域的系统进行身份验证时。
配置插件停止选项
默认情况下,所有分类为 Error 的事件都设置为导致调试中断。 所有警告事件都设置为仅记录事件详细信息。
错误事件导致停止/中断:
5000 – 应用程序已显式选择 NTLM 包
5001 – 协商包列表仅包含 NTLM
5002 – 协商包列表错误的 NTLM 排除
5003 – 服务器没有目标名称或格式不正确的目标名称
记录的警告事件:
5010 – 降级到检测到 NTLM
NTLM 停止
5000 – 应用程序已显式选择 NTLM 包
严重性 - 错误
应用程序或子系统在对 AcquireCredentialsHandle 的调用中显式选择 NTLM 而不是 Negotiate。 即使客户端和服务器有可能使用 Kerberos 进行身份验证,但 NTLM 的显式选择也阻止了这一点。
如何修复此错误
此错误的解决方法是选择协商包代替 NTLM。 完成此操作的方式取决于客户端或服务器使用的特定网络子系统。 下面给出了一些示例。 应查阅有关正在使用的特定库或 API 集的文档。
应用程序使用的 API(参数)
RPC 客户端:RPCBindingSetAuthInfoEx RPCBindingSetAuthInfoEx (AuthnSv) RPC 服务器:RPCServerRegisterAuthInfo(AuthnSvc)
RPC_C_AUTHN_WINNT或RPC_C_AUTH_DEFAULT
RPC_C_AUTH_GSS_NEGOTIATE
RPC 服务器注册 NTLM/WINNT 包不是错误。 这通常需要支持仅支持 NTLM 的旧客户端。 如果仅注册 NTLM 包,这是一个错误,因为这会强制所有客户端使用 NTLM,即使它们能够使用 Kerberos 也是如此。
DCOM:SetBlanket CoSetProxyBlanket (dwAuthnSvc) CoCreateInstanceEx (作为 COAUTHINFO 结构的 dwAuthnSvc 成员传递,而 COAUTHINFO 结构本身是传递给 API 的 COSERVERINFO 结构的成员)
RPC_C_AUTHN_WINNT
RPC_C_AUTHN_DEFAULT或RPC_C_AUTHN_GSS_NEGOTIATE
仅当通信始终通过网络发生时,才应使用协商。 如果同一台计算机上的客户端和服务器之间发生 DCOM 调用,则必须使用 DEFAULT 并允许 DCOM 选择要使用的正确包。
LDAP:ldap_bind_s(方法)
LDAP_AUTH_NTLM
LDAP_AUTH_NEGOTIATE
HTTP WinHTTPSetCredentials (AuthScheme)
WINHTTP_AUTH_SCHEME_NTLM
WINHTTP_AUTH_SCHEME_NEGOTIATE
5001 – 协商包列表仅包含 NTLM
严重性 - 错误
使用 AcquireCredentialsHandle 时,可以提供可供 Negotiate 使用或忽略的包列表。 根据指定的列表,这可能会替代 Negotiate 中内置的逻辑,以便选择最合适的安全身份验证包。 如果包列表仅包含 NTLM 或排除 Kerberos,则结果与完全绕过 Negotiate 完全相同,并直接显式选择 NTLM SSP 包。
仅当直接调用 AcquireCredentialsHandle 时,才能指定子包列表,因为大多数高层 API(如 RPC)不允许调用方控制 Negotiate 包列表。
Microsoft不建议应用程序尝试以这种方式操作协商包列表。
如何修复此错误
使用 Negotiate 包而不指定子包的列表,或确保包含 Kerberos。
应用程序使用的 API(参数)
AcquireCredentialsHandle (作为 pAuthData 参数传递SEC_WINNT_AUTH_IDENTITY_EX结构的 PackageList 成员)
“!Kerberos“或”NTLM”
NULL 或“Kerberos、NTLM”或“Kerberos,!NTLM“或”!NTLM”
5002 – 协商包列表错误的 NTLM 排除
严重性 - 警告
调用 AcquireCredentialsHandle 时,应用程序尝试从 Negotiate 支持的包列表中排除 NTLM。 但是,错误的语法已用于排除 NTLM,因此它仍保留在列表中。
如何修复此错误:使用以下语法从 Negotiate 中排除 NTLM 包:
应用程序使用的 API(参数)
AcquireCredentialsHandle (作为 pAuthData 参数传递SEC_WINNT_AUTH_IDENTITY_EX结构的 PackageList 成员)
“-NTLM”
“!NTLM”
严重性 - 错误
使用 Negotiate 包时,提供 null 或无效的目标名称(有时称为主体名称)将导致 Kerberos 失败,NTLM 将用于其位置。 进行身份验证调用时,应始终指定有效的目标名称。 目标名称是一个唯一标识符,允许域控制器获取应用程序尝试进行身份验证的服务器帐户详细信息。 域控制器获得此信息后,它可以生成客户端和服务器可以理解(可解密)的相应 Kerberos 票证。
如何修复此错误
目标名称可以采用三种不同的格式指定,每个名称都可以由域控制器用来查找正确的服务器帐户对象。 这些格式包括服务主体名称(SPN)、用户主体名称(UPN)和 NetBIOS 两部分的域\帐户名称。 SPN 是最常见的形式,也是与其他 Kerberos 实现互操作最多的形式。 对 SPN 的全面讨论超出了本文档的范围,但最简单的最常见的 SPN 窗体有两个部分:服务类和主机名。 服务类标识服务器应用程序的类型(例如 http 或 ldap 等特定应用程序类型或作为主机的泛型)。第二部分是服务器的完全限定域名或平面 (NetBIOS) 名称。 Windows 客户端和服务器会自动为 FQDN 和平面名称注册“主机”SPN。 域控制器还会将大约 40 个特定于应用程序的服务类映射到“主机”SPN,例如“http”、“ldap”、“rpc”、“tapi”等。
o 为在服务器操作系统(例如 localsystem、网络服务或 localservice)客户端应用程序的上下文中运行的应用程序指定目标名称,可以使用自动注册的“主机”SPN 或其别名之一。 若要对域用户帐户上下文中运行的应用程序进行身份验证,必须为该帐户注册 SPN。
对于用户帐户,也可以使用从用户帐户名称和帐户所在的域生成的隐式 UPN 窗体: useraccountname@domain.dom 尽管可以为用户帐户创建其他 UPN(使用可为每个域创建的 UPN 后缀),但这些将不能用作 Kerberos 目标名称 -- 只有与实际登录帐户名和实际域对应的 UPN 才能使用该帐户。
最后,在作为 localsystem、networkservice 或 localservice 运行的服务的情况下,仍可以使用 NT4 样式的域\用户名(或 domain\computername)。 这适用于在域用户帐户或计算机帐户上下文中运行的目标。
应用程序使用的 API(参数)
设置目标名称的参数
RPC 客户端:RPCBindingSetAuthInfoEx RPCBindingSetAuthInfoEx (AuthnSv)
ServerPrincipalName
这应该是运行服务器/服务的帐户的目标名称。 它不必与 RPCServerRegisterAuthInfo 中设置的值相同
DCOM:SetBlanket CoSetProxyBlanket (dwAuthnSvc) CoCreateInstanceEx (作为 COAUTHINFO 结构的 dwAuthnSvc 成员传递,而 COAUTHINFO 结构本身是传递给 API 的 COSERVERINFO 结构的成员)
pServerPrincName
可以使用COLE_DEFAULT_PRINCIPAL让 COM 从绑定信息中自动选择名称
LDAP:无
LDAP 客户端自动生成。
HTTP none
WinHTTP 和 WinInet 从 URL 服务器名称提供目标名称
5010 – 降级到检测到 NTLM
严重性 - 警告
即使应用程序特定的 Negotiate 并使用格式正确的目标名称,也会导致 Negotiate 降级到 NTLM。 根据情况,这可能表示错误或预期行为。 例如,当计算机不是域的一部分或正在使用域控制器无法访问的位置时,预计 Negotiate 会以无提示方式降级,以允许应用程序使用 NTLM 进行身份验证。 但是,如果域控制器可用且通常希望 Kerberos 使用它,则此停止几乎可以肯定地指示出现问题。
如何修复此错误
假设你已确定 Kerberos 应该在此情况下使用,而不是 NTLM,则降级的原因有很多:
• 即使目标名称的格式正确,但域(或林)中不存在。
o 应检查是否在客户端应用程序中生成了正确的目标名称。 服务类是否正确? 主机名是否正确?
o 服务器进程是否在计算机或其他域帐户的上下文中运行。 在以前的案例中,SPN 会自动注册,在后一种情况下,可能需要注册 SPN 或使用替代形式,例如隐式 UPN 或平面名称。
o 是否存在阻止与域控制器或 DNS 服务器的通信的网络连接问题?
o 目标 SPN 是否在多个帐户上注册? 这将导致域控制器拒绝身份验证尝试。
打印验证程序有助于查找并解决应用程序调用打印子系统时可能导致的问题。 打印验证工具面向打印子系统的两个层,即 PrintAPI 层和 PrintDriver 层。
打印 API 层
打印验证工具会测试程序与 Winspool.drv 和 prntvpt.dll 之间的接口,并测试这些 DLL 的接口。 你可以在由 winspool.drv 和 prntvpt.dll 导出的 API 的 MSDN 帮助部分中查看有关调用此接口中函数的规则。
打印驱动程序层
打印验证工具还测试核心打印驱动程序(例如 UNIDRV.DLL、UNIDRUI.DLL、PSCRIPT5.DLL、PS5UI.DLL 或 MXDWDRV.DLL)与打印驱动程序插件之间的接口。可以在 MSDN 和 WDK 中找到有关此接口的信息。
通常,只有调试版本运行应用程序验证工具,因此性能通常不是问题。 如果使用此检查或任何其他应用程序验证工具检查时导致性能问题,请一次运行一项检查,直到执行完所有需要的检查。
WebServices
Windows Webservices API (WWSAPI) 验证层
WWSAPI 插件允许开发人员捕获以下实例:
调用的 WWSAPI 引用无效的内部 WWSAPI 对象。
调用的 WWSAPI 引用已使用的单个线程对象。
一个通过异步调用挂起释放的内部对象。
从短线程调用阻止 API。
此外,此插件:
跟踪对象从实例化到删除的用法。
强制可以异步完成的调用以同步方式完成。 这是为了防止应用程序根据特定行为从调用中获取WS_ASYNC_CONTEXT。
当 API 传递错误对象或对象正在使用 !avrf –ws –obj 调试器扩展时,提供人工可读说明(如下所示)
对于通道、服务代理和服务主机,跟踪的每个调用将显示对象的当前状态。
默认情况下,启用以下检查器:
内部对象有效的 Property NameDescriptionValidateObjectValidates 在其生存期内使用对象
可以通过属性 UI 在此提供程序中启用的其他检查器包括:
属性 NameDescriptionCheckTimeoutValidates,异步函数在超时范围内完成,指定为 TimeoutValForceSyncForce,在向 API 提供WS_ASYNC_CONTEXT上下文时要采用的同步路径。
提供了调试器扩展(!avrf –ws –obj),它显示打开和关闭的内部 WWSAPI 对象。 如果此扩展由对象后缀,它将显示有关此对象的用法的详细信息。
!avrf -ws –obj
此命令显示正在跟踪的内部 WWSAPI 对象,这些对象同时被创建和关闭。 请注意,关闭的对象存储在循环队列中,因此跟踪的对象总数有上限。
成功完成以下 API 时添加对象:WsCreateChannel()、WsCreateChannelForListener()、WsCreateServiceHost()、WsCreateServiceProxy()、WsCreateServiceProxyFromTemplate()、WsCreateError()、WsCreateHeap()、WsCreateHeap()、WsCreateListener()、WsCreateMetadata()、WsCreateMessage()、WsCreateMessageForChannel()、WsCreateReader()、WsCreateWriter()、WsCreateXmlBuffer()、WsReadXmlBuffer()、WsReadXmlBufferFromBytes()
调用并完成相应的 WsFree*() 函数时,对象将从创建的列表中移动到释放列表。
!avrf –ws –obj [OBJECT]
此命令显示内部 WWSAPI 对象的用法。 使用信息包括创建、使用和释放对象的堆栈。 如果对象是通道、服务主机或服务代理,则会在使用对象调用 API 之前显示该对象的状态。
下面是 !avrf –ws –obj 用法选项的示例:
0:001> !avrf -ws -obj
Objects dependent on internal objects allocated:
Objects currently allocated:
0x00000000048566C0 (Type=Heap, Thread=0x000001bc, Pending Operations=0)
0x0000000001BE6780 (Type=Error, Thread=0x000001bc, Pending Operations=0)
0x0000000001C13580 (Type=Service Proxy, Thread=0x000001bc, Pending Operations=0)
Freed objects:
0x0000000001C17170 (Type=Service Proxy, Thread=0x000001bc)
0x0000000004856730 (Type=Heap, Thread=0x000001bc)
0x0000000001BE6820 (Type=Error, Thread=0x000001bc)
0:001> !avrf -ws -obj 0x0000000001C13580
Object @ 0x0000000001C13580
Type = Service Proxy
Thread = 0x000001bc
Internal Reference = 0x00000000026C5E80
Created stack:
vfnws!VfHookWsCreateServiceProxy+0x00aa
BLUESTONE!WST_WebServices::WsCreateServiceProxy+0x00d8
BLUESTONE!ServiceProxy::Connect+0x0116
BLUESTONE!ServiceModel_SimpleTest::SimpleClient+0x0607
BLUESTONE!ServiceModelTestGroup_Simple_Test02_Run+0x0041
BLUESTONE!Fnshell2::FnshellConfiguration::RunTest+0x002e
BLUESTONE!Fnshell2::TESTCASE::Run+0x00d6
BLUESTONE!fnsMsgProc+0x02d6
BLUESTONE!fnsRunTestsWorkerThread+0x085f
KERNEL32!BaseThreadInitThunk+0x000d
ntdll!RtlUserThreadStart+0x001d
Last 4 operations
Operation #1 created in thread 0x00000000000001BC
Service proxy state before operation = Created
Callstack:
vfnws!VfHookWsGetServiceProxyProperty+0x0053
BLUESTONE!WST_WebServices::WsGetServiceProxyProperty+0x009b
BLUESTONE!ServiceProxy::GetState+0x004b
BLUESTONE!ServiceProxy::VerifyState+0x001c
BLUESTONE!ServiceProxy::Connect+0x01c7
BLUESTONE!ServiceModel_SimpleTest::SimpleClient+0x0607
BLUESTONE!ServiceModelTestGroup_Simple_Test02_Run+0x0041
BLUESTONE!Fnshell2::FnshellConfiguration::RunTest+0x002e
BLUESTONE!Fnshell2::TESTCASE::Run+0x00d6
BLUESTONE!fnsMsgProc+0x02d6
BLUESTONE!fnsRunTestsWorkerThread+0x085f
KERNEL32!BaseThreadInitThunk+0x000d
ntdll!RtlUserThreadStart+0x001d
Operation #2 created in thread 0x00000000000001BC
Service proxy state before operation = Created
Callstack:
vfnws!VfHookWsOpenServiceProxy+0x0079
BLUESTONE!WST_WebServices::WsOpenServiceProxy+0x0092
BLUESTONE!ServiceProxy::Connect+0x03d3
BLUESTONE!ServiceModel_SimpleTest::SimpleClient+0x0607
BLUESTONE!ServiceModelTestGroup_Simple_Test02_Run+0x0041
BLUESTONE!Fnshell2::FnshellConfiguration::RunTest+0x002e
BLUESTONE!Fnshell2::TESTCASE::Run+0x00d6
BLUESTONE!fnsMsgProc+0x02d6
BLUESTONE!fnsRunTestsWorkerThread+0x085f
KERNEL32!BaseThreadInitThunk+0x000d
ntdll!RtlUserThreadStart+0x001d
Operation #3 created in thread 0x00000000000001BC
Service proxy state before operation = Open
Callstack:
vfnws!VfHookWsGetServiceProxyProperty+0x0053
BLUESTONE!WST_WebServices::WsGetServiceProxyProperty+0x009b
BLUESTONE!ServiceProxy::GetState+0x004b
BLUESTONE!ServiceProxy::VerifyState+0x001c
BLUESTONE!ServiceProxy::Connect+0x0484
BLUESTONE!ServiceModel_SimpleTest::SimpleClient+0x0607
BLUESTONE!ServiceModelTestGroup_Simple_Test02_Run+0x0041
BLUESTONE!Fnshell2::FnshellConfiguration::RunTest+0x002e
BLUESTONE!Fnshell2::TESTCASE::Run+0x00d6
BLUESTONE!fnsMsgProc+0x02d6
BLUESTONE!fnsRunTestsWorkerThread+0x085f
KERNEL32!BaseThreadInitThunk+0x000d
ntdll!RtlUserThreadStart+0x001d
Operation #4 created in thread 0x00000000000001BC
Service proxy state before operation = Open
Callstack:
vfnws!VfHookWsCall+0x00a6
BLUESTONE!DefaultBinding_ICalculator_Add+0x008b
BLUESTONE!ServiceModelTestGroup_Simple_Function+0x010a
BLUESTONE!ServiceModel_SimpleTest::SimpleClient+0x069a
BLUESTONE!ServiceModelTestGroup_Simple_Test02_Run+0x0041
BLUESTONE!Fnshell2::FnshellConfiguration::RunTest+0x002e
BLUESTONE!Fnshell2::TESTCASE::Run+0x00d6
BLUESTONE!fnsMsgProc+0x02d6
BLUESTONE!fnsRunTestsWorkerThread+0x085f
KERNEL32!BaseThreadInitThunk+0x000d
ntdll!RtlUserThreadStart+0x001d
Asynchronous Callback = BLUESTONE!ServiceModelTestGroup_Simple_Callback
Asynchronous CallbackState = 0x0000000005EBDC30
Completed asynchronously with HRESULT=0x00000000 in thread 0x00000000000001BC
Asynchronous callback stack:
vfnws!VfHookWsCall+0x00e3
BLUESTONE!DefaultBinding_ICalculator_Add+0x008b
BLUESTONE!ServiceModelTestGroup_Simple_Function+0x010a
BLUESTONE!ServiceModel_SimpleTest::SimpleClient+0x069a
BLUESTONE!ServiceModelTestGroup_Simple_Test02_Run+0x0041
BLUESTONE!Fnshell2::FnshellConfiguration::RunTest+0x002e
BLUESTONE!Fnshell2::TESTCASE::Run+0x00d6
BLUESTONE!fnsMsgProc+0x02d6
BLUESTONE!fnsRunTestsWorkerThread+0x085f
KERNEL32!BaseThreadInitThunk+0x000d
ntdll!RtlUserThreadStart+0x001d
Closed stack:
0:001>
服务测试,检查是否正确使用 Windows 服务。 例如,服务是否正常启动和停止。 有关这些测试生成的停止代码异常的信息,请参阅 应用程序验证程序 - 停止代码 - 服务。
Perf 测试检查是否有效地使用影响系统性能和能耗的 API,例如调用使用不正确的等待期的 Windows 函数。 有关这些测试生成的停止代码异常的信息,请参阅 应用程序验证程序 - 停止代码 - Perf。
Hangs 测试使用导致系统无响应的 API,例如 DllMain 线程正在等待被阻止的另一个线程。 有关这些测试生成的停止代码异常的信息,请参阅 应用程序验证程序 - 停止代码 - 挂起。
应用程序验证程序 - 概述
应用程序验证工具 - 功能
应用程序验证工具 - 测试应用程序
应用程序验证程序 - 停止代码和定义
应用程序验证程序 - 调试应用程序验证程序停止
应用程序验证工具 - 常见问题解答