Delen via


Voorbeeld 12: Het gebruik van paginaheap-verificatie om een bug op te sporen

De volgende reeks opdrachten laat zien hoe u de functies voor heap-verificatie van pagina's van GFlags en het NTSD-foutopsporingsprogramma gebruikt om een fout in heap-geheugengebruik te detecteren. In dit voorbeeld vermoedt de programmeur dat een fictieve toepassing, pheap-buggy.exe, een heap-fout heeft en een reeks tests doorloopt om de fout te identificeren.

Zie Foutopsporing met CDB- en NTSD-voor meer informatie over NTSD.

Stap 1: Standaardpagina-heapverificatie inschakelen

Met de volgende opdracht wordt de standaard paginaheapverificatie ingeschakeld voor pheap-buggy.exe:

gflags /p /enable pheap-buggy.exe

Stap 2: Controleren of pagina-heap is ingeschakeld

De volgende opdracht bevat de afbeeldingsbestanden waarvoor verificatie van de pagina heap is ingeschakeld:

gflags /p

Als reactie geeft GFlags de volgende lijst met programma's weer. In deze weergave geven traces aan dat er sprake is van standaard pagina-heap-verificatie, en volledige traces geven aan dat er sprake is van volledige pagina-heap-verificatie. In dit geval wordt pheap-buggy.exe weergegeven met sporen, waarmee wordt aangegeven dat standaardpagina-heapverificatie is ingeschakeld, zoals bedoeld.

pheap-buggy.exe: page heap enabled with flags (traces )

Stap 3: Het foutopsporingsprogramma uitvoeren

Met de volgende opdracht wordt de functie CorruptAfterEnd van pheap-buggy.exe uitgevoerd in NTSD met de parameters -g (eerste onderbrekingspunt negeren) en -x (tweede kans-einde instellen voor uitzonderingen voor toegangsschendingen):

ntsd -g -x pheap-buggy CorruptAfterEnd

Wanneer de toepassing mislukt, genereert NTSD de volgende weergave, wat aangeeft dat er een fout is gedetecteerd in pheap-buggy.exe:

===========================================================
VERIFIER STOP 00000008: pid 0xAA0: corrupted suffix pattern

        00C81000 : Heap handle 
        00D81EB0 : Heap block 
        00000100 : Block size 
#         00000000 :
===========================================================

Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00d81eb0 ecx=77f7e257 edx=0006fa18 esi=00000008 edi=00c81000
eip=77f7e098 esp=0006fc48 ebp=0006fc5c iopl=0         nv up ei pl zr na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000246
ntdll!DbgBreakPoint:
77f7e098 cc               int     3

De headerinformatie bevat het adres van de heap met het beschadigde blok (00C81000 : Heap-ingang), het adres van het beschadigde blok (00D81EB0 : Heap-blok) en de grootte van de toewijzing (00000100: Blokgrootte).

Het bericht 'beschadigd achtervoegselpatroon' geeft aan dat de toepassing het patroon voor gegevensintegriteit heeft geschonden dat GFlags na het einde van de pheap-buggy.exe heaptoewijzing heeft ingevoegd.

Stap 4: De aanroepstack weergeven

Gebruik in de volgende stap de adressen die door NTSD zijn gerapporteerd om de functie te vinden die de fout heeft veroorzaakt. Met de volgende twee opdrachten schakelt u het dumpen van regelnummers in het foutopsporingsprogramma in en geeft u de aanroepstack weer met regelnummers.

C:\>.lines

Line number information will be loaded 

C:\>kb

ChildEBP RetAddr  Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0006fc5c 77f9e6dd 00000008 77f9e3e8 00c81000 ntdll!DbgBreakPoint
0006fcd8 77f9f3c8 00c81000 00000004 00d81eb0 ntdll!RtlpNtEnumerateSubKey+0x2879
0006fcfc 77f9f5bb 00c81000 01001002 00000010 ntdll!RtlpNtEnumerateSubKey+0x3564
0006fd4c 77fa261e 00c80000 01001002 00d81eb0 ntdll!RtlpNtEnumerateSubKey+0x3757
0006fdc0 77fc0dc2 00c80000 01001002 00d81eb0 ntdll!RtlpNtEnumerateSubKey+0x67ba
0006fe78 77fbd87b 00c80000 01001002 00d81eb0 ntdll!RtlSizeHeap+0x16a8
0006ff24 010013a4 00c80000 01001002 00d81eb0 ntdll!RtlFreeHeap+0x69
0006ff3c 01001450 00000000 00000001 0006ffc0 pheap-buggy!TestCorruptAfterEnd+0x2b [d:\nttest\base\testsrc\kernel\rtl\pageheap\pheap-buggy.cxx @ 185]
0006ff4c 0100157f 00000002 00c65a68 00c631d8 pheap-buggy!main+0xa9 [d:\nttest\base\testsrc\kernel\rtl\pageheap\pheap-buggy.cxx @ 69]
0006ffc0 77de43fe 00000000 00000001 7ffdf000 pheap-buggy!mainCRTStartup+0xe3 [crtexe.c @ 349]
0006fff0 00000000 0100149c 00000000 78746341 kernel32!DosPathToSessionPathA+0x204

Als gevolg hiervan geeft het foutopsporingsprogramma de aanroepstack weer voor pheap-buggy.exe met regelnummers. De weergave van de aanroepstack laat zien dat de fout is opgetreden toen de functie TestCorruptAfterEnd in pheap-buggy.exe probeerde een toewijzing op 0x00c80000 vrij te maken door HeapFree aan te roepen, een omleiding naar RtlFreeHeap.

De meest waarschijnlijke oorzaak van deze fout is dat het programma voorbij het einde van de buffer heeft geschreven die het in deze functie heeft toegewezen.

Stap 5: Volledige pagina heap-verificatie inschakelen

In tegenstelling tot standaard pagina-heapverificatie kan volledige pagina-heapverificatie het misbruik van deze heap-buffer direct ondervangen zodra het zich voordoet. Met de volgende opdracht wordt volledige pagina heap-verificatie ingeschakeld voor pheap-buggy.exe:

gflags /p /enable pheap-buggy.exe /full

Stap 6: Controleren of volledige paginaheap is ingeschakeld

Met de volgende opdracht worden de programma's vermeld waarvoor paginahoopverificatie is ingeschakeld.

gflags /p

Als reactie geeft GFlags de volgende lijst met programma's weer. In deze weergave duiden traces op standaard pagina-heap-verificatie en volledige traces duiden op volledige pagina-heap-verificatie. In dit geval wordt pheap-buggy.exe weergegeven met volledige traceringen, waarmee wordt aangegeven dat volledige pagina-heap-verificatie is ingeschakeld, zoals bedoeld.

pheap-buggy.exe: page heap enabled with flags (full traces )

Stap 7: voer het foutopsporingsprogramma opnieuw uit

Met de volgende opdracht wordt de functie CorruptAfterEnd van pheap-buggy.exe uitgevoerd in het foutopsporingsprogramma van NTSD met de parameters -g (eerste onderbrekingspunt negeren) en -x (tweede kans-einde instellen voor uitzonderingen voor toegangsschendingen):

ntsd -g -x pheap-buggy CorruptAfterEnd

Wanneer de toepassing mislukt, genereert NTSD de volgende weergave, wat aangeeft dat er een fout is gedetecteerd in pheap-buggy.exe:

Page heap: process 0x5BC created heap @ 00880000 (00980000, flags 0x3)
ModLoad: 77db0000 77e8c000   kernel32.dll
ModLoad: 78000000 78046000   MSVCRT.dll
Page heap: process 0x5BC created heap @ 00B60000 (00C60000, flags 0x3)
Page heap: process 0x5BC created heap @ 00C80000 (00D80000, flags 0x3)
Access violation - code c0000005 (first chance)
Access violation - code c0000005 (!!! second chance !!!)
eax=00c86f00 ebx=00000000 ecx=77fbd80f edx=00c85000 esi=00c80000 edi=00c16fd0
eip=01001398 esp=0006ff2c ebp=0006ff4c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000206
pheap-buggy!TestCorruptAfterEnd+1f:
01001398 889801010000     mov     [eax+0x101],bl          ds:0023:00c87001=??

Wanneer heap-verificatie op volledige pagina is ingeschakeld, stopt de debugger bij een toegangsfout. Als u de exacte locatie van de toegangsfout wilt vinden, schakelt u dumping van regelnummers in en geeft u de aanroepstacktracering weer.

De genummerde aanroepstacktracering wordt als volgt weergegeven:

ChildEBP RetAddr  Args to Child
0006ff3c 01001450 00000000 00000001 0006ffc0 pheap-buggy!TestCorruptAfterEnd+0x1f [d:\nttest\base\testsrc\kernel\rtl\pageheap\pheap-buggy.cxx @ 184]
0006ff4c 0100157f 00000002 00c16fd0 00b70eb0 pheap-buggy!main+0xa9 [d:\nttest\base\testsrc\kernel\rtl\pageheap\pheap-buggy.cxx @ 69]
0006ffc0 77de43fe 00000000 00000001 7ffdf000 pheap-buggy!mainCRTStartup+0xe3 [crtexe.c @ 349]
WARNING: Stack unwind information not available. Following frames may be wrong.
0006fff0 00000000 0100149c 00000000 78746341 kernel32!DosPathToSessionPathA+0x204

De stacktracering laat zien dat het probleem optreedt in regel 184 van pheap-buggy.exe. Omdat volledige paginaheapverificatie is ingeschakeld, begint de aanroepstapel in de programmacode en niet in een systeem-DLL. Als gevolg hiervan werd de overtreding opgespoord op de plek waar het plaatsvond, in plaats van toen het heapblok werd vrijgemaakt.

Stap 8: Zoek de fout in de code

Een snelle inspectie toont de oorzaak van het probleem: het programma probeert te schrijven naar de 257e byte (0x101) van een buffer van 256 byte (0x100), een veelvoorkomende off-by-one-fout.

*((PCHAR)Block + 0x100) = 0;