威努特工控安全 05月07日 09:20
安全编程:揭秘威努特工控安全产品如何锻造卓越品质
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍了Application Verifier,一款由微软开发的、免费的调测工具,专为C/C++开发的工控软件提供质量保障。文章阐述了Application Verifier的安装、配置及使用方法,并以具体代码为例,展示了如何利用该工具排查内存踩踏问题。通过监控内存使用、资源释放和异常捕捉,Application Verifier能够有效提升软件质量与可靠性,确保工控软件在复杂环境中的稳定运行。

🛠️ Application Verifier是一款由微软公司提供的免费调测工具,专为C/C++开发的工控软件设计,旨在提升软件质量和可靠性。

⚙️ 该工具具备全面监控能力,能够检测内存泄漏、非法内存访问、资源释放流程等问题,尤其适用于Windows操作系统环境。

💡 通过实例分析,Application Verifier能够精准定位内存踩踏等问题,帮助开发者快速修复代码缺陷,确保软件在长时间运行后的稳定性。

01

引  言


在工业控制领域,一般工业软件的设计生命周期要达到15年—20年,对产品的稳定性、可用性要求极高。C/C++凭借其强大的功能和卓越的性能,成为开发高性能工控软件首选。同时,相伴而生的是C/C++的复杂性容易导致内存泄漏、非法内存访问等诸多隐患,给系统的稳定运行与安全保障带来了严峻考验。


Application Verifier由微软公司研发,免费向开发者开放,是一款功能强大的调测工具,可以为C/C++开发的软件保驾护航,助力提升软件质量与可靠性。Application Verifier能够以极高的精度对应用程序的内存使用状况、资源释放流程以及异常捕捉环节实施全方位监控,特别是在 Windows 操作系统环境下,与C/C++应用程序紧密契合,可以深度剖析问题根源,精准定位故障节点,从而有效规避系统在长时间运行后出现的各类异常甚至崩溃。


本文详细介绍了Application Verifier的安装配置及使用,并以一段具体代码为例,讲述借助该工具排查问题的过程。


02

Application Verifier的安装


Application Verifier包含在 Windows 软件开发工具包 (SDK) 中,在安装 SDK 时选中该复选框即可。


图 1 SDK安装


03

Application Verifier的使用


首先,我们打开 Application Verifier 工具,启动后将看到如下界面。


图 2 主界面


接下来,我们将需要监控的程序添加到 Application Verifier 中,依次点击”File”->”Add Application”。


图 3 添加程序


选择程序后,可以在左侧的程序列表中看到其名称。


单击已加入的测试程序后,右侧的面板会自动显示该程序的测试项目。此时,可以对选定的程序启用或禁用一系列验证功能,以确保程序在不同场景下的稳定性和安全性。右侧的测试项目面板分为多个部分,每个部分都对应着程序执行中的特定检查项。


测试项目的设置项包括但不限于以下几种常见验证功能:


01

程序异常捕获

启用运行时错误监控后,Application Verifier 会检测程序在运行时是否发生了常见的错误,例如访问非法内存、使用无效指针等。这项功能能够帮助开发者发现潜在的程序缺陷,特别是在复杂或长时间运行的应用中,减少不可预见的崩溃和问题。


02

句柄验证

句柄验证功能会检查程序是否正确创建、使用和关闭操作系统资源句柄(如文件、网络连接、线程等)。启用此项后,Application Verifier 会跟踪程序在运行过程中对这些句柄的所有操作,确保资源在使用后得到正确释放,避免句柄泄漏和资源浪费。


03

堆栈保护

启用堆栈保护后,工具会检测程序是否存在栈溢出或栈破坏的潜在问题。堆栈溢出通常是导致程序崩溃的一个常见原因,启用此项后,Application Verifier 会在栈操作时进行额外的检查,防止栈溢出的风险。


04

内存验证

启用内存验证后,Application Verifier 会监控程序在运行过程中对内存的使用情况。它能够检测诸如内存泄漏、缓冲区溢出、未初始化内存的读取等常见问题。通过这种方式,开发者可以及时发现内存管理中的错误,避免内存相关的崩溃或数据损坏。


05

线程同步验证

该验证功能用于检查程序中的多线程操作是否存在竞争条件或死锁等同步问题。在启用此项后,Application Verifier 会追踪程序中所有线程的执行顺序,检测是否存在不安全的共享资源访问或线程同步错误,确保程序在多线程环境中的稳定性。


06

异常处理验证

异常处理验证选项检查程序是否能够正确处理运行时错误。例如,当程序发生内存访问错误或其他系统异常时,是否能够及时捕获并处理这些错误,避免程序异常崩溃。


图 4 测试项目


右击某一个测试项目,弹出菜单选项,选择测试属性,打开属性窗口,如下图:


图 5 测试属性


各属性的说明如下:

选项

类型

描述

Full

Boolean

(布尔型)

TRUE

如果设置为 TRUE,则启用全页堆(full page heap);如果设置为 FALSE,则为普通页堆(normal page heap)。全页堆分配会为目标 DLL 仅进行页堆分配,有助于更严格地检测堆相关问题,但可能会对性能产生一定影响。

Dlls

String

(字符串型)

(空白)

用于指定仅对特定的二进制文件(DLL)进行页堆分配。需要输入二进制文件的名称(包括扩展名),多个 DLL 之间用空格分隔。如果不填写,则对所有相关的 DLL 进行默认的堆测试设置。

Size

Boolean

(布尔型)

FALSE

如果设置为 TRUE,则启用基于大小范围的页堆分配。

SizeStart

DWORD

(32位无符号整数)

0(0x0)

指定大小范围的起始值(以字节为单位),如果启用了 Size 选项,在此处设置起始大小。

SizeEnd

DWORD

(32位无符号整数)

4294967295

指定大小范围的结束值(以字节为单位),与 SizeStart 配合使用,确定进行特殊堆测试的内存分配大小范围。

Random

Boolean

(布尔型)

FALSE

如果设置为 TRUE,则以随机方式选择要进行特殊堆测试的内存分配,而不是基于连续的大小范围。

RandRate

DWORD

(32位无符号整数)

0

当 Random 选项设置为 TRUE 时,此值用于设置随机选择的比率。例如,如果设置为 10,则大约 10% 的内存分配将被随机选择进行特殊堆测试。

Backward

Boolean

(布尔型)

FALSE

如果设置为 TRUE,则启用反向堆遍历,这在某些情况下可以帮助检测特定类型的堆损坏问题,但可能会增加测试的复杂性和时间。

Unalign

Boolean

(布尔型)

FALSE

如果设置为 TRUE,则允许未对齐的内存分配,这可以用于测试应用程序对未对齐内存访问的处理,但可能会导致一些兼容性问题或错误。

Traces

Boolean

(布尔型)

TRUE

如果设置为 TRUE,则启用堆操作的跟踪记录,这些记录可以在调试时提供详细的信息,帮助分析堆相关的问题,但会产生额外的日志记录开销。

Protect

Boolean

(布尔型)

FALSE

如果设置为 TRUE,则对堆进行保护,防止某些类型的内存损坏,例如缓冲区溢出等。这可以增强应用程序的安全性,但可能会影响性能。

NoLock

Boolean

(布尔型)

FALSE

如果设置为 TRUE,则禁用堆锁,这在某些多线程测试场景中可以用于检测与锁相关的问题,但可能会导致数据竞争和其他并发问题。

Faults

Boolean

(布尔型)

FALSE

如果设置为 TRUE,则在堆操作中引入人为的错误(faults),用于测试应用程序对错误的处理能力,但需要谨慎使用,因为可能会导致应用程序崩溃。

FaultRate

DWORD

(32位无符号整数)

0

当 Faults 选项设置为 TRUE 时,此值用于设置引入错误的比率。例如,如果设置为 5,则大约 5% 的堆操作将被引入人为错误。

TimeOut

DWORD

(32位无符号整数)

0

设置堆操作的超时时间(以毫秒为单位),如果堆操作超过此时间,则可能会触发相应的错误处理或日志记录,用于检测长时间运行的堆操作问题。

Addr

Boolean

(布尔型)

FALSE

如果设置为 TRUE,则基于地址范围进行堆测试。

AddrStart

DWORD

(32位无符号整数)

0

指定地址范围的起始地址,与 Addr 选项配合使用,确定进行特殊堆测试的内存地址范围。

AddrEnd

DWORD

(32位无符号整数)

4294967295

指定地址范围的结束地址,与 AddrStart 一起确定地址范围。

UseLFHGuardPages

Boolean

(布尔型)

FALSE

可能与低碎片堆(LFH)的保护机制相关,具体功能需要根据完整的选项名称和文档来确定。

InPlaceShrinkAlloc

Boolean

(布尔型)

FALSE

如果设置为 TRUE,则启用原地收缩分配(in-place shrink allocation),这可能会影响堆的内存管理策略和性能。

DelayFreeSizelnMB

DWORD

(32位无符号整数)

64

设置延迟释放内存的大小阈值(以 MB 为单位),当要释放的内存块大小超过此阈值时,可能会触发延迟释放机制,用于测试应用程序对内存释放延迟的处理。

表 1 测试属性


各个属性的具体描述在属性栏中有体现,测试属性默认是关闭的,我们可以通过菜单栏“View”->“Property Window”打开测试属性栏,如下图:


图 6 测试属性


04

利用Application Verifier排查踩内存问题


为了演示需要,我们编写了测试程序heap_test.exe,此程序主要实现了主动内存泄露和踩内存功能。本文例子中我们主要测试踩内存功能,并通过Application Verifier定位踩内存的位置。


在前面说明,我们已经将heap_test.exe加入了Application Verifier工程进行监控。这里可以直接双击运行heap_test.exe,界面如下图所示:


图 7 heap_test.exe


查看Application Verifier的界面,检测到程序错误,在log页面中显示详情。


图 8 日志


将报告导出,查看具体内容:


图 9 日志内容


接下来,我们将对此份报告进行详细分析。


错误概述

报告第4行中指出了一个严重性为“Error”的问题,停止代码为“0xF”,出现在“Heaps”层。具体错误描述是“Corrupted suffix pattern for heap block”,意味着堆块的后缀模式出现了损坏,这很可能会影响到内存中堆数据结构的完整性以及相关内存操作的正确性。


参数详情

参数 1(avrf:parameter1)

指出堆句柄为“77a1000”,它代表了在相关调用中涉及到的具体堆,后续排查问题时可以通过这个句柄进一步查看对应堆的详细状态和相关操作记录。


参数 2(avrf:parameter2)

明确此次操作中涉及到的堆块地址为“7946fe0”,这有助于定位具体是哪个内存块出现了后缀模式损坏的情况。


参数 3(avrf:parameter3)

堆块大小为“14”字节,了解其大小对于判断是否存在越界访问等相关内存操作违规有一定参考价值。


参数 4(avrf:parameter4)

明确了损坏地址是“7946ff4”,可以聚焦在这个具体地址位置查找导致后缀模式损坏的源头,比如是否在该地址附近有非法写入等操作。


调用栈分析

从提供的调用栈(avrf:stackTrace)来看:


首先涉及到 “vrfcore”、“verifier”等相关模块中的函数,这些都是Application Verifier自身工具相关的函数调用,比如“VerifierUnregisterLayer”、“VerifierStopMessage”等,它们在检测到错误时进行了相应的记录和处理流程。


之后调用链指向了“MSVCR90!free+1c”,说明在执行内存释放(free 函数)操作时出现了这个问题,很有可能是释放的内存块本身已经处于不正常状态,进而触发了堆块后缀模式损坏的检测。


最后定位到应用程序自身的代码,日志中标记“heap_test!wmain+b4 (d:\src\ars-linux\test\pk_test\windows\heap_test\heap_test.cpp @ 74)”,需要重点查看heap_test.cpp文件中74行以及其周围的内存操作逻辑,是否存在对堆块的不当使用,比如在释放之前已经对堆块的后缀部分进行了错误修改等情况。


代码分析

查看heap_test.cpp文件代码如下:


图 10 代码


内存分配

函数首先通过malloc方法分配了能存放5个整数的内存空间,这一步操作正常获取了一块有效的堆内存,其内存块地址等相关信息与错误报告中的堆块地址参数等后续可以进行对应分析。


越界访问

关键问题出现在 arr[5] = 100; 这一行代码,这里尝试访问了超出原本分配空间(下标为5,超出了分配的0 - 4范围)的内存,属于典型的踩内存行为。这种越界访问很可能破坏了堆块后续的结构,其中就包括堆块的后缀部分,进而导致了Application Verifier检测到的“Corrupted suffix pattern for heap block”错误。因为堆块在内存中是有其特定结构布局的,后缀部分往往包含了一些用于内存管理和验证完整性的信息,越界写入可能改写了这些关键的后缀内容。


内存释放

之后执行free(arr) 操作时,由于之前堆块的后缀模式已经被破坏,在释放内存时Application Verifier检测到了这个错误,也就对应上了错误报告中调用栈里 MSVCR90!free+1c 处触发问题的情况,因为此时释放的是一个已经被破坏了结构完整性的堆块。


综上所述,根据错误报告结合代码分析,大概率是 StepMemory 函数中对内存的越界访问(踩内存)操作破坏了堆块结构,导致在释放该堆块时被 Application Verifier 检测出堆块后缀模式损坏的错误,后续修复可以重点针对 StepMemory 函数中这个越界访问的代码行进行调整,确保内存访问都在合法分配的范围内。


05

总  结


Application Verifier的主要作用是监控应用程序与Windows操作系统的交互,并分析其对对象、注册表、文件系统和Win32 API的使用情况,包括堆、句柄和锁。Application Verifier是一个强大的工具,它不仅可以帮助开发者在开发过程中发现和修复错误,还可以在发布阶段和支持服务阶段确保应用程序的安全性和稳定性。


威努特的工控安全产品在开发和优化过程中,都经过了Application Verifier的多项严格测试,涵盖了内存管理、资源释放、异常处理等多个方面,验证了其在高风险和高负载环境下的性能和安全性,确保相关产品在复杂工控环境里能够可靠、稳定地运行。Application Verifier在威努特的产品研发过程中起到了不可或缺的作用。



渠道合作咨询   田先生 15611262709

稿件合作   微信:shushu12121

📍发表于:中国 北京

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Application Verifier 工控软件 C/C++ 内存管理 软件测试
相关文章