天天看點

堆記憶體破壞檢測實戰--附完整調試過程

首先解釋一下,什麼是堆記憶體?

堆是一種常見的記憶體管理器,應用程式通過堆來動态地配置設定和釋放記憶體,通常使用堆的情況是無法預先知道所需要的記憶體大小,或者申請記憶體太大,無法通過棧記憶體來自動配置設定,下面讓我們再來看一段英文解釋。

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.

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

繼續調試,此時我們已經找到了出問題的堆快,注意觀察上面的輸出中有這樣一條語句:

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的發生..

注:本文的附圖和代碼靈感源自網絡,具體出處不詳,其他内容為原創..

繼續閱讀