在软件开发生命周期中使用应用程序验证器.docx
《在软件开发生命周期中使用应用程序验证器.docx》由会员分享,可在线阅读,更多相关《在软件开发生命周期中使用应用程序验证器.docx(12页珍藏版)》请在冰豆网上搜索。
![在软件开发生命周期中使用应用程序验证器.docx](https://file1.bdocx.com/fileroot1/2023-1/22/39f6358e-f074-4537-b00c-e3b05f04cb04/39f6358e-f074-4537-b00c-e3b05f04cb041.gif)
在软件开发生命周期中使用应用程序验证器
在软件开发生命周期中使用应用程序验证器
摘要
Microsoft®ApplicationVerifier(AppVerifier)是针对非托管代码的运行时验证工具,它有助于找到细小的编程错误、安全问题和受限的用户帐户特权问题,使用常规的应用程序测试技术很难识别出这些错误和问题。
本页内容
简介
什么是AppVerifier?
AppVerifier功能
何时使用AppVerifier
如何使用AppVerifier
分析AppVerifier数据
有关AppVerifier的要点
附录A:
DriverVerifier
附录B:
使用ApplicationVerifier测试安全功能
附录C:
参考站点
简介
程序员、软件架构师、测试人员以及安全咨询师所面临的最大难题之一是,了解其应用程序部署到产品时的结果。
即使具备对源代码的访问,也很难掌握执行过程中将发生的每个操作,这归咎于存在多种依赖项(例如,服务于代码或利用外部组件的多个组)。
在帮助管理这种复杂性以及错误带来的潜在的副面影响方面,MicrosoftAppVerifier扮演着重要的角色。
AppVerifier有助于找到细小的编程错误、安全问题和用户帐户特权问题,这些错误和问题在典型的测试过程中是很难识别的。
本文提供的信息,涉及AppVerifier如何工作以及在软件开发生命周期(SoftwareDevelopmentLifecycle)过程中如何应用AppVerifier。
返回页首
什么是AppVerifier?
AppVerifier可以免费下载,特别用于检测和帮助调试内存损坏、危险的安全漏洞以及受限的用户帐户特权问题。
AppVerifier有助于创建可靠且安全的应用程序,方法是监视应用程序与Microsoft®Windows®操作系统的交互,并配置应用程序使用的对象、注册表、文件系统和Win32API(包括堆、句柄和锁)。
AppVerifier还包括检查,以便预测应用程序在非管理员环境中的执行情况。
在整个软件开发生命周期中使用时,AppVerifier可节约开发工作的成本,因为它能方便地在早期就识别出问题,而在早期修复错误是比较容易且成本较低的。
AppVerifier还有助于检测出可能被忽视掉的错误,并确保最终的应用程序能够在受限的(例如,非管理员)环境中执行(最后这一点对于WindowsVista应用程序更为重要)。
AppVerifier识别的问题
AppVerifier有助于确定:
∙应用程序正确地使用API的时刻:
o不安全的TerminateThreadAPI。
o正确使用线程本地存储(ThreadLocalStorage,TLS)API。
o正确使用虚拟空间操作(例如,VirtualAlloc和MapViewOfFile)。
∙应用程序是否使用结构化的异常处理隐藏访问冲突。
∙应用程序是否试图使用无效的句柄。
∙堆中是否有内存损坏或存在内存问题。
∙应用程序是否在资源不足的情况下用尽了内存。
∙是否正确使用了临界区。
∙运行在管理员环境中的应用程序在具有较低特权的环境中是否能良好运行。
∙当应用程序作为受限用户运行时是否会存在潜在的问题。
∙在线程的上下文中,是否在将来的函数调用中会存在未初始化的变量。
返回页首
AppVerifier功能
AppVerifier创建于2001年,在登录过程中使用。
然而,在DownloadCenter的3.0版中进行了一些更改。
用户界面经过了更改,增加了对安全的关注,更新的体系结构可以提供更健壮的报告(基于XML的日志),灵活性方面表现为能添加新的测试(例如,LuaPriv等更多的新特性)。
AppVerifier测试
AppVerifier包含称为"验证层"的测试集。
可以针对进行测试的每个应用程序打开或关闭它们。
通过在测试区域内扩展验证层,将显示特定的测试。
要打开针对该应用程序的测试,选择它旁边的复选框。
要打开整个验证层(例如,Basics),选择最上层的复选框。
AppVerifier可以执行四种不同类型的测试(在版本3.0.28中):
∙Basics 包含针对以下内容的测试:
oExceptions —使用结构化的异常处理确保应用程序不隐藏访问冲突。
oHandles —确保应用程序不尝试使用无效的句柄。
oHeaps —查看堆中的内存损坏问题。
oLocks —验证临界区的正确使用。
oMemory —确保正确使用虚拟空间操作的API。
oTLS —确保正确使用线程本地存储API。
∙Lowresourcesimulation 尝试在资源不足的情况下模拟环境,因此,您可定义一个编号(0–100)来指示关于以下内容的错误可能性调用:
oWait(例如,WaitForXXXXAPI)。
oHeap_Alloc(堆分配API)。
oVirtual_Alloc(虚拟内存分配API)。
o注册表(注册表API)。
o文件(诸如CreateFile的文件API)。
o事件(诸如CreateEvent的事件API)。
oMapView(诸如CreateMapViewMapViewAPI的CreateMapView)。
oOle_Alloc(诸如SysAllocString的OleAPI)。
∙LuaPriv 包含31种不同的测试,它们可在以下两种方案中使用:
oPredictive —确定一个以管理权限运行的应用程序如果以较小特权(通常,作为常规用户)运行时是否能正常工作。
例如,如果应用程序对只允许管理员访问的文件进行写操作,那么如果该应用程序以非管理员身份运行,则它将无法写入此类文件。
oDiagnostic —以非管理员身份运行时,将识别可能已经存在于当前运行中的潜在问题。
例如,如果应用程序尝试写入只允许管理员访问的文件,则应用程序将产生一个ACCESS_DENIED错误。
∙Miscellaneous 包含针对以下方面的测试:
oDangerousAPI—进行跟踪,查看应用程序是否正在使用以下不安全的操作:
▪对TerminateThread的危险调用。
▪内存不足情况下潜在的堆栈溢出。
▪在多个线程仍然运行的情况下退出调用的进程。
▪LoadLibrary在DllMain过程中调用。
▪FreeLibrary在DllMain过程中调用。
▪DirtyStacks 用内存模式填充(周期性地)未使用的堆栈部分。
这有助于检测线程上下文的将来函数调用中未初始化的变量。
属性
AppVerifier中有两组属性。
一组基于测试,一组基于应用程序。
每组属性按以下方式定义:
∙Name-针对每个属性的唯一名称。
∙Type-Boolean、DWORD、String和MultipleString。
∙Value-基于类型变化的可更改元素。
∙Description-该描述解释属性的内容。
针对每个应用程序编辑属性
对于每个应用程序,以下属性可用于编辑:
∙Propagate —将验证器设置从父进程传播到子进程。
False(不选中该框)不传播这些设置,而True(选中该框)则传播这些设置。
∙AutoClr —当指定的图像开始运行之后,验证的图像将清除自己的设置。
False(不选中该框)不进行此操作;True(选中该框)则进行此操作。
∙AutoDisableStop —AppVerifier对于一个错误将只解释一次。
如果再次发现该问题,它不会生成错误。
False在每次发现问题时都将生成错误。
True将只生成一个错误。
∙LoggingWithLocksHeld —将记录dll加载/卸载事件。
验证器在加载器锁处于保持状态时进行I/O。
这可能会挂起应用程序。
False不会记录该事件,而True将记录该事件。
编辑测试属性
有关特定测试的属性的详细信息,参见AppVerifier中的Help内容。
要编辑这些属性,可采用以下两种方法之一:
∙Individual —在属性窗口中,双击要编辑的属性。
将显示一个对话框,允许编辑该项并将其设置回默认内容。
更改该项并单击"OK"。
∙Group —从Test区域,单击要编辑的测试或验证层。
使用右键单击选项显示包含所有属性的屏幕。
更改这些属性或重置为默认内容,并单击"OK"。
日志
运行应用程序而且生成错误时,创建日志。
日志为XML格式,可以使用浏览器、XML或XLST查看。
返回页首
何时使用AppVerifier
您应该在软件开发生命周期(SDL,又称为安全开发生命周期SecurityDevelopmentLifecycle)的整个过程中使用AppVerifier:
∙需求 阶段 —规划AppVerifier的使用,并安排它的执行与后续操作。
∙设计阶段 —定义将测试哪些组件(模块、Dll或EXE)。
∙实现阶段 —从开发中不同组件的稳定版本(从Alpha到RTM)运行AppVerifier(单独和共同测试组件很重要)。
∙验证阶段 —测试人员应该使用AppVerifier进行他们的所有测试(手动和自动),因为这是第一次对应用程序进行限制,而且将提交意外的行为和数据。
AppVerifier对于安全咨询师而言也是一个进行审核(黑盒和白盒)的强大工具,因为它能够快速枚举实际(或潜在)的攻击/破坏向量。
∙发布阶段 —客户和安全咨询师可以对发布的二进制文件使用AppVerifier,从而识别出潜在的安全漏洞。
∙支持和服务阶段 —使用AppVerifier确保代码更改(例如,错误修复)不会引入回归。
返回页首
如何使用AppVerifier
安装和配置
∙安装:
从 MicrosoftDownloadCenter 下载最新版的AppVerifier,其中您将发现针对以下支持的体系结构的安装二进制文件:
x86、ia64和amd64。
∙调试器安装:
已验证的应用程序应该运行于用户模式的调试器中或者系统应该运行于内核调试器中,因为出现错误时,将中断调试器。
参阅Help内容获取有关调试器的详细信息。
∙设置:
对于运行中的进程,不能启用AppVerifier。
需要按照下面描述的方式创建设置,然后启动应用程序。
除非显式删除,否则这些设置将一直保留;无论启动应用程序多少次,在启动时都会启用AppVerifier,除非删除这些设置。
注 AppVerifier也可以在VisualStudio2005TeamSystem中使用。
使用AppVerifierBasic测试
下面的方案展示推荐的命令行和用户界面选项。
这些内容应该在执行代码的所有测试中运行以确保完全覆盖。
这些方案的预期目标是,应用程序不 中断调试器,而且所有测试在不启用AppVerifier运行时以相同的通过率通过。
∙针对要测试的应用程序(一个或多个)启用验证器,通过:
从命令行:
appverif/verifyMyApp.exe
从用户界面:
oa.右键单击Applications区域并单击 AddApplication 添加应用程序。
ob.从Tests区域选择 Basics。
oc.单击 Save 按钮。
o注:
▪/verify 将启用Basic测试。
▪如果要测试一个DLL应用程序,必须为执行该DLL的测试.exe文件启用AppVerifier。
▪单独运行验证层。
例如,在一个会话中启用所有Basic,而在另一个会话中启用所有LuaPriv检查。
∙运行执行应用程序的所有测试。
∙分析遇到的任何调试器中断(一个或多个)。
如果发生一个中断,则需要了解中断的原因以及如何修复它。
(Help内容提供关于中断的详细信息以及如何研究它们。
)
∙完成时,删除所有设置:
从命令行:
appverif/nMyApp.exe
从用户界面:
oa.右键单击Applications区域并单击 DeleteApplication 删除应用程序。
ob.单击 Save 按钮。
使用AppVerifier低资源模拟(错误注入)
该方案的预期目标是,应用程序不中断该调试器。
这意味着没有需要解决的错误。
由于在常规操作中引入随机错误注入,因此这些测试的通过率可能显著降低。
∙针对一个或多个应用程序启用应用程序验证器低资源模拟(错误注入):
从命令行:
Appverif/verifyMyApp.exe/faults;
从用户界面:
oa. 右键单击Applications区域并单击 AddApplication 添加应用程序。
ob.从Tests区域选择 LowResourceSimulation。
oc.单击 Save 按钮。
注 如果要测试一个DLL,可以在特定的DLL上(而非整个过程中)应用低资源模拟(错误注入)。
命令行格式如下所示:
appverif/verifyTARGET[/faults[PROBABILITY[TIMEOUT[DLL?
]]]]Example:
appverif/verifymytest.exe/faults51000d3d9.dll
∙运行执行应用程序的所有测试。
∙完成时,删除所有设置。
分析遇到的任何调试器中断(一个或多个)。
如果出现中断,需要了解中断的原因以及如何修复它。
∙完成时,删除所有设置:
从命令行:
appverif/nMyApp.exe
从用户界面:
oa.右键单击Applications区域并单击 DeleteApplication 删除应用程序。
ob.单击 Save 按钮。
注 在应用程序中是否使用错误注入将导致执行完全不同的代码路径,因此为了完全受益于AppVerifier必须运行这两个方案。
返回页首
分析AppVerifier数据
在AppVerifier分析过程中创建的所有数据存储在一个二进制格式的%ALLUSERSPROFILE%\AppVerifierLogs文件夹中。
然后,这些日志可通过用户界面或命令行转换为XML以供进一步分析。
要查看XML文件,使用以下方式之一:
∙Web 浏览器 —现在,所有的Web浏览器都能够以结构化以及颜色编码的格式显示XML文件。
∙XSL 转换 — 创建一个XSLT,将原始内容转换为只包含相关信息的报告。
∙导入到 Excel —将XML文件导入到Excel,使用筛选器或Pivot表识别并分析收集的数据。
∙导入到数据库 — 保存XML文件,并将其导入到一个数据库中(例如,SQL、Access数据库等)。
返回页首
有关AppVerifier的要点
本节提供有关潜在AppVerifier用户遇到的最常见问题的信息:
系统要求
AppVerifier旨在测试WindowsXP、WindowsServer2003和WindowsVista上的非托管应用程序(例如,非.NETFramework应用程序)。
当运行一个完整的页面堆时,建议最少需要1GB。
尽管应用程序的符号或调试信息的可用性在所收集数据的质量和有用性方面差异巨大,但AppVerifier在执行测试时不需要访问源代码。
AppVerifier将验证什么?
AppVerifier用于在WindowsXP、WindowsServer2003和WindowsVista中测试用户模式的应用程序。
∙要测试驱动程序或内核模块,使用DriverVerifier。
有关更多信息,请参阅附录A:
DriverVerifier以及知识库文章 HowtoUseDriverVerifiertoTroubleshootWindowsDrivers。
∙要测试WindowsCE,使用WindowsCETestKit(CETK)中包含的特定的PlatformAppVerifier。
∙需要注意的重要一点是,AppVerifier将只测试在测试执行过程中调用的函数。
这意味着,如果单元测试可用(从目标应用程序/模块),它们应该用于确保最大的代码覆盖-如果调用生成请求的方法,则只检测错误。
AppVerifier如何工作?
AppVerifier的工作方式是修改非托管DLLMethodTable,以便在执行实际函数之前执行所需的检查(也称为"函数挂钩")。
例如,Win32API CreateFileA 方法的地址被替换为一个内部的AppVerifier方法,后者将触发需要记录的一系列测试。
当新进程启动时,使用AppVerifier的MethodTableHooking技术将由在特定注册表键中创建的项进行控制。
如果存在注册表项,那么AppVerifierDLL将加载到新创建的进程中,该进程将在现有的以及后续加载的DLL中处理MethodTable替换。
由于这些挂钩在加载DLL时生成的,因此在已经运行的进程上使用AppVerifier3.0是可能的。
AppVerifier用户界面(UI)用于控制注册表键设置并提供关于现有日志的信息。
当在UI中设置了应用程序和测试并单击"Save"按钮时,将进行Registry设置。
然后需要重新启动该应用程序,这将启动监视操作。
需要注意的重要一点是,这些设置将一直持续到应用程序从AppVerifier中移除。
当发现一个问题时,验证器将停止。
提供的编号用于标识问题发生的实质和原因。
要更好地了解停止操作,参见AppVerifierHelp文件中包含的详细解释:
appverif.chm。
页面堆技术细节
为了检测堆损坏(上溢或下溢),AppVerifier将修改内存分配方式,方法是在分配内存之前和之后使用完整的不可写的页或特殊标记填充所请求的内存。
当使用完整的不可写的页填充所请求的内存时(在页面堆的属性部分启用FULL设置并使用默认设置),AppVerifier将使用大量虚拟内存,而且它具有个优势,即当上溢或下溢发生时,将实时缓存堆损坏事件。
谨记,该模式中的内存可能如下所示
[AppVerifierRead-OnlyHeapPage(4k)][AmountofmemoryrequestedbyApplicationundertest]orlikethis[AmountofmemoryrequestedbyApplicationundertest][AppVerifierRead-OnlyHeapPage(4k)]。
堆检查将根据 Backward 属性在分配的开始或结尾放置一个保护页。
如果 Backward 设置为False(默认值),则它将在分配的末尾放置一个保护页以捕获缓冲区溢出。
如果它设置为True,则该保护页放置在分配的开始以捕获缓冲区的不足。
当使用特殊标记(通过清除堆属性中的"Full"复选框项来启动)填充所需的内存时,如果释放该内存,AppVerifier将进行检查并向您发出警告。
使用这些技巧时的主要问题是,有时内存损坏只能在内存释放时(最小的内存块是8字节)进行检测,因此操作一个3字节的变量或发生5字节溢出时,不会立即检测到它。
对于一个下溢事件,将尝试写入一个只读页。
这将引发一个异常。
请注意,仅当目标应用程序在调试器中执行时才引发该异常。
还需注意,完整的页面堆模式也将检测这些错误,因为它使用填充+保护页。
使用轻量级页面堆的原因是,防止计算机无法承受完整页面堆的高内存约束。
对于内存密集型应用程序,或者需要长期使用AppVerifier时(例如,压力测试),最好运行常规(轻量)堆测试而非完整的模式,以防止性能降低。
但当您遇到问题时,请打开完整的页面堆进行深入研究。