堆内存腐败异常(STATUS_HEAP_CORRUPTION---0xC0000374)

什么是内存腐败

当堆内存位置的内容由于编程行为而被修改,超出了原始程序构造的意图时,计算机程序就会发生内存腐败;这被称为违反内存安全。内存腐败的最可能原因是编程错误。当腐败的内存内容稍后在该程序中使用时,它要么导致程序崩溃,要么导致奇怪的程序行为。Windows系统上近10%的应用程序崩溃是由于堆腐败造成的。像C和C++这样的现代编程语言具有显式内存管理和指针运算的强大功能。这些特性是为开发高效的应用程序和系统软件而设计的。但是,错误地使用这些功能可能会导致内存腐败错误。

内存腐败是最难处理的编程错误之一,原因有二:

  • 内存腐败的根源和表现形式可能相距甚远,很难将因果联系起来。
  • 症状出现在不寻常的情况下,使得很难持续地再现错误。

产生的原因

内存腐败错误大致可分为四类:

  • 使用未初始化内存:未初始化内存的内容被视为垃圾值。使用这些值可能会导致不可预测的程序行为。
  • 使用非自有内存:通常使用指针访问和修改内存。如果此类指针是空指针、悬挂指针(指向已释放的内存)或指向当前堆栈或堆边界之外的内存位置,则它指的是程序当时未拥有的内存。使用这样的指针是一个严重的编程缺陷。访问这样的内存通常会导致操作系统异常,这些异常通常会导致程序崩溃(除非正在使用合适的内存保护软件)。
  • 使用已分配内存以外的内存(缓冲区溢出):如果在循环中使用数组,且终止条件不正确,则可能意外操作数组边界以外的内存。缓冲区溢出是计算机病毒利用的最常见的编程缺陷之一,在广泛使用的程序中会导致严重的计算机安全问题(如返回libc攻击、堆栈崩溃保护)。在某些情况下,程序也可能在缓冲区启动之前错误地访问内存。
  • 堆内存管理错误:内存泄漏和释放非堆或未分配的内存是堆内存管理错误导致的最常见错误。

表现形式

根据被破会的内存位置,程序会有不同的表现形式,常见就是程序崩溃掉。如果被破坏的是是堆/堆段/堆块等管理边界结构,那么在win32里,一般会触STATUS_HEAP_CORRUPTION

异常,此时的异常结构EXCEPTION_RECORD成员值一般如下:

EXCEPTION_RECORD:  
ExceptionAddress:异常地址
ExceptionCode: c0000374
ExceptionFlags: 00000001
NumberParameters: 1
Parameter[0]: 导致此异常的最终函数

0xC0000374就是堆内存腐败的异常代码,他定义如下

file:..../winnt.h
#define STATUS_HEAP_CORRUPTION           ((DWORD   )0xC0000374L)   

怎么办

一是保证代码质量,二是提早发现。在win32里,由于堆腐败造成的崩溃几乎不可能在事后调试。避免这些问题的最佳方法是使用应用程序验证程序中的页面堆功能进行测试。页面堆有两种类型:“Full”和“Light”。Full是默认值;它将在检测到损坏时立即强制调试器停止。此功能必须在调试器下运行。然而,它也是最需要资源的。如果用户有时间问题并且已经在“完整”页面堆下运行了一个场景,那么将其设置为“轻”可能会解决这些问题。此外,轻页堆在进程退出之前不会崩溃。它确实提供了对分配的堆栈跟踪,但诊断所需的时间要比充分利用它的完全对应项要长得多。

结语

内存管理是一项众所周知的任务,需要软件开发人员高度的纪律性和谨慎性。 忽略这些需求不仅会导致代码不稳定,还会导致代码易受攻击。利用内存损坏漏洞可使应用程序崩溃、操作数据,或者在最坏情况下使攻击者能够在运行应用程序的计算机上执行任意代码。根据易受攻击的应用程序的上下文,这可能意味着进一步的攻击步骤,如权限提升、后门安装或机密数据泄漏。
因此,大多数现代编程语言决定接管内存管理任务,并拥有包含垃圾收集器的运行时环境。不过,直接或间接使用手动内存管理有几个原因。像Java或C#这样的高级语言提供了一个与低级代码交互的二进制接口。这个特性经常被用来以高效的编程语言实现应用程序的性能关键部分,或者利用现有的其他语言编写的库。这些语言的解释器或虚拟机本身通常是用本地语言编写的,生成处理器可以理解的编译工件。手动内存管理的另一个原因是嵌入式系统和物联网设备的分布越来越广。它们促进了高效的实现,以降低功耗和最小化硬件需求。所以在使用想c/c++这样的语言时,我们一定要维护好我们的指针/内存/缓冲区。尽量避免发生问题。

猜你喜欢

转载自www.cnblogs.com/yilang/p/11842828.html