堆内存破坏检测实战--附完整调试过程

简介:

首先解释一下,什么是堆内存?

堆是一种常见的内存管理器,应用程序通过堆来动态地分配和释放内存,通常使用堆的情况是无法预先知道所需要的内存大小,或者申请内存太大,无法通过栈内存来自动分配,下面让我们再来看一段英文解释。

A heap is a form of memory manager that an application can use when it needs to allocate and free memory dynamically. Common situations that call for the use of a heap are when the size of the memory needed is not known ahead of time and the size of the memory is too large to neatly fit on the stack (automatic memory).

常见的情况是由于效率或特殊需求一个进程中同时使用几个堆,如下图:

 

 

 

下面通过一个完整的demo来带大家调试一个对破坏问题,demo代码如下:

 

复制代码
 
#define SZ_MAX_LEN  10
void __cdecl wmain (int argc, WCHAR* args[])
{
    if(argc==2)
    {
        wprintf(L"Press any key to start\n");
        _getch();
        DupString(args[1]);
    }
    else
    {
        wprintf(L"Please enter a string");
    }
}
BOOL DupString(WCHAR* psz)
{
    BOOL bRet=FALSE;
    
    if(psz!=NULL)
    {
        pszCopy=(WCHAR*) HeapAlloc(GetProcessHeap(), 0, SZ_MAX_LEN*sizeof(WCHAR));
        if(pszCopy)
        {
            wcscpy(pszCopy, psz);
            wprintf(L"Copy of string: %s", pszCopy);
            HeapFree(GetProcessHeap(), 0, pszCopy);
            bRet=TRUE;
        }
    }
    return bRet;
 
复制代码

 

在应用程序验证器下启用普通页堆,配置gflags, 运行build出来的代码,

输入参数为:SolidmangoSolidmangoSolidmango

得到如下输出:

 

复制代码
 
CommandLine: C:\WinXP.x86.chk\06overrun.exe SolidmangoSolidmangoSolidmango
Executable search path is: 
ModLoad: 01000000 01005000   06overrun.exe
ModLoad: 7c900000 7c9b2000   ntdll.dll
AVRF: 06overrun.exe: pid 0x120C: flags 0x8044B026: application verifier enabled
ModLoad: 5ad10000 5ad59000   C:\WINDOWS\System32\verifier.dll
ModLoad: 10000000 10029000   C:\WINDOWS\System32\vrfcore.dll
ModLoad: 003a0000 003dc000   C:\WINDOWS\System32\vfbasics.dll
ModLoad: 7c800000 7c8f6000   C:\WINDOWS\system32\kernel32.dll
AVRF: verifier.dll provider initialized for 06overrun.exe with flags 0x8044B026 
ModLoad: 77c10000 77c68000   C:\WINDOWS\system32\msvcrt.dll
(120c.1700): Break instruction exception - code 80000003 (first chance)
eax=00391ec4 ebx=7ffd8000 ecx=00000004 edx=00000010 esi=00391f98 edi=00391ec4
eip=7c90120e esp=0006fb20 ebp=0006fc94 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
ntdll!DbgBreakPoint:
7c90120e cc              int     3
0:000> g0:000> g
 
复制代码

 

我们会看到一个访问违例继续运行得到如下输出,说明应用程序验证器验证成功:

 

复制代码
 
=======================================
VERIFIER STOP 00000008: pid 0x120C: Corrupted heap block. 

    00081000 : Heap handle used in the call.
    001E2B60 : Heap block involved in the operation.
    00000014 : Size of the heap block.
    00000000 : Reserved
=======================================
This verifier stop is not continuable. Process will be terminated 
when you use the `go' debugger command.
=======================================
(120c.1700): Break instruction exception - code 80000003 (first chance)
eax=1000e848 ebx=1000cd44 ecx=00000001 edx=0006f939 esi=00000000 edi=1000e848
eip=7c90120e esp=0006f9cc ebp=0006fbd0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
ntdll!DbgBreakPoint:
7c90120e cc              int     3
 
复制代码

 

继续调试,此时我们已经找到了出问题的堆快,注意观察上面的输出中有这样一条语句:

001E2B60 : Heap block involved in the operation,好的,让我们看看这个堆块里面是什么东西,

复制代码
 
0:000> dt _DPH_BLOCK_INFORMATION 001E2B60-0x20
ntdll!_DPH_BLOCK_INFORMATION
   +0x000 StartStamp       : 0xabcdaaaa
   +0x004 Heap             : 0x80081000 Void
   +0x008 RequestedSize    : 0x14
   +0x00c ActualSize       : 0x3c
   +0x010 FreeQueue        : _LIST_ENTRY [ 0x1e - 0x0 ]
   +0x010 TraceIndex       : 0x1e
   +0x018 StackTrace       : 0x00286c3c Void
   +0x01c EndStamp         : 0xdcbaaaaa
0:000> dds 0x00286c3c //callstack
00286c3c  abcdaaaa
00286c40  00000001
00286c44  00000007
00286c48  00000001
00286c4c  00000014
00286c50  00081000
00286c54  00000000
00286c58  00286c5c
00286c5c  7c94b244 ntdll!RtlAllocateHeapSlowly+0x44
00286c60  7c919c0c ntdll!RtlAllocateHeap+0xe64
00286c64  003afd2c vfbasics!AVrfpRtlAllocateHeap+0xb1
00286c68  010012f4 06overrun!DupString+0x24 [c:\awd\chapter6\overrun\overrun.cpp @ 41]
00286c6c  010012ab 06overrun!wmain+0x2b [c:\awd\chapter6\overrun\overrun.cpp @ 28]
00286c70  010014b8 06overrun!__wmainCRTStartup+0x102 [d:\vistartm\base\crts\crtw32\dllstuff\crtexe.c @ 711]
00286c74  7c817077 kernel32!BaseProcessStart+0x23
00286c78  00000000

我们找到了出问题的callstack:
0:000> kb
ChildEBP RetAddr  Args to Child              
0006f9c8 10003b68 10062cb0 00000008 001e2b60 ntdll!DbgBreakPoint
0006fbd0 100078c9 1000c540 00000008 00081000 vrfcore!VerifierStopMessageEx+0x4d1
0006fbf4 7c96c06e 00000008 7c96c314 00081000 vrfcore!VfCoreRedirectedStopMessage+0x81
0006fc70 7c96d147 00081000 00000004 001e2b60 ntdll!RtlpDphReportCorruptedBlock+0x17c
0006fc94 7c96d34a 00081000 01000002 00000010 ntdll!RtlpDphNormalHeapFree+0x2e
0006fce4 7c9703eb 00080000 01000002 001e2b60 ntdll!RtlpDebugPageHeapFree+0x79
0006fd58 7c94bafc 00080000 01000002 001e2b60 ntdll!RtlDebugFreeHeap+0x2c
0006fe40 7c91a1ba 00080000 01000002 001e2b60 ntdll!RtlFreeHeapSlowly+0x37
0006ff10 003afe9c 00080000 00000000 001e2b60 ntdll!RtlFreeHeap+0xf9
0006ff58 01001340 00080000 00000000 00000014 vfbasics!AVrfpRtlFreeHeap+0xf8
0006ff70 010012ab 00a64692 0006ffc0 010014b8 06overrun!DupString+0x70 [c:\awd\chapter6\overrun\overrun.cpp @ 47]
0006ff7c 010014b8 00000002 00a64648 00a66e98 06overrun!wmain+0x2b [c:\awd\chapter6\overrun\overrun.cpp @ 28]
0006ffc0 7c817077 00daf6ee 00daf784 7ffd8000 06overrun!__wmainCRTStartup+0x102 [d:\vistartm\base\crts\crtw32\dllstuff\crtexe.c @ 711]
0006fff0 00000000 010015f6 00000000 78746341 kernel32!BaseProcessStart+0x23
0:000> du 00a64692 
00a64692  "SolidmangoSolidmangoSolidmango"
 
复制代码

 

总结:

原来是我们的参数破坏了了堆内存,终于找到了根源,我们代码中定义的堆的大小为10,而我们使用的时候,由于堆块越界,破坏了堆块的完整性,从而导致了crash的发生..

 

注:本文的附图和代码灵感源自网络,具体出处不详,其他内容为原创..

目录
相关文章
|
17天前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
150 1
|
18天前
|
监控 JavaScript Java
Node.js中内存泄漏的检测方法
检测内存泄漏需要综合运用多种方法,并结合实际的应用场景和代码特点进行分析。及时发现和解决内存泄漏问题,可以提高应用的稳定性和性能,避免潜在的风险和故障。同时,不断学习和掌握内存管理的知识,也是有效预防内存泄漏的重要途径。
114 52
|
11天前
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
41 7
|
11天前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
39 5
|
29天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
46 6
|
2月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
73 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
188 9
|
1月前
|
监控 JavaScript 前端开发
如何检测和解决 JavaScript 中内存泄漏问题
【10月更文挑战第25天】解决内存泄漏问题需要对代码有深入的理解和细致的排查。同时,不断优化和改进代码的结构和逻辑也是预防内存泄漏的重要措施。
46 6
|
1月前
|
Web App开发 缓存 JavaScript
如何检测和解决闭包引起的内存泄露
闭包引起的内存泄露是JavaScript开发中常见的问题。本文介绍了闭包导致内存泄露的原因,以及如何通过工具检测和代码优化来解决这些问题。
|
2月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
55 2