truebad0ur@home:~$

Стек и куча - основные структуры памяти. Посмотрим на утечки в куче, как располагаются данные в стеке и всё такое

Как растёт стек

Структура стека:

Stack Structure

  • The ESP egister points to the current stack location of a thread
  • If a program attempts to access an address within a guard page, the system raises a STATUS_GUARD_PAGE_VIOLATION (0x80000001) exception. A guard page provides a one-shot alarm for memory page access
  • If a stack grows until the end of reserved memory, a STATUS_STACK_OVERFLOW is raised

Заполним стек

kd> !teb
...
StackBase:            0000000000700000
StackLimit:           00000000006fc000
...

kd> dt nt!_TEB -y DeallocationStack 000000000033f000
+0x1478 DeallocationStack : 0x00000000`00500000 Void

kd> ?0000000000700000 - 00000000006fc000
Evaluate expression: 16384 = 00000000`00004000

//TODO прописать заполнение адреса

А что там с кучей то?

If page heap is disabled (а он по дефолту выключен), apply the following structs:

  • _HEAP struct
    • defined в ntdll.dll: dt nt!_HEAP
    • for every HeapCreate there is a unique _HEAP
    • !heap -p -all to get addresses for all _HEAP structs in process

  • _HEAP_ENTRY struct
    • defined in ntdll: dt nt!_HEAP_ENTRY
    • for every HeapAlloc there is a unique _HEAP_ENTRY
    • !heap -p -all to get addresses for all heap entries in process

If page heap is enabled, apply the following structs:

  • _DPH_HEAP_ROOT struct
    • defined в ntdll.dll: dt nt!_DPH_HEAP_ROOT
    • for every HeapCreate there is a unique _DPH_HEAP_ROOT
    • !heap -p -all to get addresses for all heap roots in process
    • Usually address of a _DPH_HEAP_ROOT = value of HeapHandle + 0x1000

  • _DPH_HEAP_BLOCK struct
    • defined in ntdll: dt nt!_DPH_HEAP_BLOCK
    • for every HeapAlloc there is a unique _DPH_HEAP_BLOCK
    • !heap -p -all to get addresses for all heap blocks in process

Кто вызвал HeapAlloc?

Включаем stack traces и page heap для процесса:

  • Либо в gui gflags.exe тыкаем Create user mode stack trace database и Enable page heap
  • Либо gflags.exe /i +ust +hpa

Меняем контекст процесса:

kd> .process /i ffffe78c24835240
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.

kd> g
Break instruction exception - code 80000003 (first chance)
nt!DbgBreakPointWithStatus:
fffff803`7d9ff050 cc              int     3


Идем до места, где вызываем HeapAlloc и получаем адрес возврата, который является DPH_HEAP_BLOCK

kd> !heap -p -a 282bff41000
    address 00000282bff41000 found in
    _DPH_HEAP_ROOT @ 282bfd11000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                             282bfd17478:      282bff41000             2000 -      282bff40000             4000
ReadMemory error for address 00000282bff41000
    00007ff97ca6867b ntdll!RtlDebugAllocateHeap+0x000000000000003b
    00007ff97c99d255 ntdll!RtlpAllocateHeap+0x00000000000000f5
    00007ff97c99b44d ntdll!RtlpAllocateHeapInternal+0x0000000000000a2d
    00007ff72cfa107a +0x00007ff72cfa107a
    00007ff72cfa12e0 +0x00007ff72cfa12e0
    00007ff97bcd7034 +0x00007ff97bcd7034
    00007ff97c9c2651 ntdll!RtlUserThreadStart+0x0000000000000021


kd> dt ntdll!_DPH_HEAP_BLOCK StackTrace 282bfd17478
   +0x060 StackTrace : 0x00000282`be539400 _RTL_TRACE_BLOCK


посмотрим на stack trace

kd> dq /c1 0x00000282`be539400 L10
00000282`be539400  00000000`00000000
00000282`be539408  00070000`00003801        0033:00007ff9`7ca68675  call    qword ptr [ntdll!_guard_dispatch_icall_fptr (00007ff9`7caf3000)]
00000282`be539410  00007ff9`7ca6867b ---->  0033:00007ff9`7ca6867b  mov     rbx,qword ptr [rsp+70h]

                                            0033:00007ff9`7c99d250  call    ntdll!RtlDebugAllocateHeap (00007ff9`7ca68640)
00000282`be539418  00007ff9`7c99d255 ---->  0033:00007ff9`7c99d255  jmp     ntdll!RtlpAllocateHeap+0xc6 (00007ff9`7c99d226)

                                            0033:00007ff9`7c99b448  call    ntdll!RtlpAllocateHeap (00007ff9`7c99d160)
00000282`be539420  00007ff9`7c99b44d ---->  0033:00007ff9`7c99b44d  mov     rdi,rax


непосредственный вызов функи HeapAlloc из моего кода
                                            0033:00007ff7`2cfa1074  call    qword ptr [00007ff7`2cfa2008] (HeapAlloc)
00000282`be539428  00007ff7`2cfa107a ---->  0033:00007ff7`2cfa107a  call    qword ptr [00007ff7`2cfa2010] ds:002b:00007ff7`2cfa2010=00007ff97bcd5bb0 (GetProcessHeap)

                                            0033:00007ff7`2cfa12db  call    00007ff7`2cfa1000 (main из crt start)
00000282`be539430  00007ff7`2cfa12e0 ---->  0033:00007ff7`2cfa12e0  mov     ebx,eax
00000282`be539438  00007ff9`7bcd7034

                                             ntdll!RtlUserThreadStart:
                                             0033:00007ff9`7c9c2630  sub     rsp,78h
                                             0033:00007ff9`7c9c2634  mov     r9,rcx
                                             0033:00007ff9`7c9c2637  mov     rax,qword ptr [ntdll!Kernel32ThreadInitThunkFunction (00007ff9`7cad9ff0)]
                                             0033:00007ff9`7c9c263e  test    rax,rax
                                             0033:00007ff9`7c9c2641  je      ntdll!RtlUserThreadStart+0x23 (00007ff9`7c9c2653)
                                             0033:00007ff9`7c9c2643  mov     r8,rdx
                                             0033:00007ff9`7c9c2646  mov     rdx,rcx
                                             0033:00007ff9`7c9c2649  xor     ecx,ecx
                                             0033:00007ff9`7c9c264b  call    qword ptr [ntdll!_guard_dispatch_icall_fptr (00007ff9`7caf3000)]
00000282`be539440  00007ff9`7c9c2651 ----->  0033:00007ff9`7c9c2651  jmp     ntdll!RtlUserThreadStart+0x43 (00007ff9`7c9c2673)
00000282`be539448  00000000`00000000
00000282`be539450  00000000`00000000
00000282`be539458  00000000`00000000
00000282`be539460  00000000`00000000
00000282`be539468  00000000`00000000
00000282`be539470  00000000`00000000
00000282`be539478  00000000`00000000

Ищем утечки памяти на хипе

Summary about memory usage for your process. If RegionUsageHeap or RegionUsagePageHeap is growing constantly, then you might have a memory leak on the heap. Proceed with the following steps.

!address --summary

Сделаем фиктивную утечку памяти и посмотрим на неё, можно что-то в этом духе:

#include <Windows.h>
#include <iostream>

int main(int argc, char *argv[]) {

	PVOID Heap = NULL;
	std::cout << "Allocate: ";
	int size;
	std::cin >> size;
	int *array = new int[size];
	std::cout << "Allocated!\n";

	Sleep(3000);

	return 0;
}
До аллокации

kd> !heap -stat -h 0
Allocations statistics for
heap @ 0000021a6eb40000
group-by: TOTSIZE max-display: 20
    size     #blocks     total     ( %) (percent of total busy bytes)
    1000 3 - 3000  (27.66)
    1200 1 - 1200  (10.37)
    c38 1 - c38  (7.04)
    120 9 - a20  (5.83)
    400 2 - 800  (4.61)
    200 4 - 800  (4.61)
    100 8 - 800  (4.61)
    790 1 - 790  (4.36)
    6de 1 - 6de  (3.96)
    1d8 3 - 588  (3.19)
    470 1 - 470  (2.56)
    228 2 - 450  (2.48)
    390 1 - 390  (2.05)
    50 b - 370  (1.98)
    348 1 - 348  (1.89)
    238 1 - 238  (1.28)
    10 1d - 1d0  (1.04)
    20 c - 180  (0.86)
    168 1 - 168  (0.81)
    158 1 - 158  (0.77)
Allocations statistics for
 heap @ 0000021a6e8e0000
group-by: TOTSIZE max-display: 20
    size     #blocks     total     ( %) (percent of total busy bytes)


После аллокации

kd> !heap -stat -h 0
Allocations statistics for
heap @ 0000021a6eb40000
group-by: TOTSIZE max-display: 20
    size     #blocks     total     ( %) (percent of total busy bytes)
    2625a00 1 - 2625a00  (99.89)
    1000 3 - 3000  (0.03)
    1200 1 - 1200  (0.01)
    c38 1 - c38  (0.01)
    120 9 - a20  (0.01)
    400 2 - 800  (0.01)
    200 4 - 800  (0.01)
    100 8 - 800  (0.01)
    790 1 - 790  (0.00)
    6de 1 - 6de  (0.00)
    1d8 3 - 588  (0.00)
    470 1 - 470  (0.00)
    228 2 - 450  (0.00)
    390 1 - 390  (0.00)
    50 b - 370  (0.00)
    348 1 - 348  (0.00)
    238 1 - 238  (0.00)
    10 20 - 200  (0.00)
    20 c - 180  (0.00)
    168 1 - 168  (0.00)
Allocations statistics for
 heap @ 0000021a6e8e0000
group-by: TOTSIZE max-display: 20
    size     #blocks     total     ( %) (percent of total busy bytes)


Найдём все аллокации с нашим размером 2625a00
kd> !heap -flt s 2625a00 
    _DPH_HEAP_ROOT @ 297e9ed1000
    Freed and decommitted blocks
      DPH_HEAP_BLOCK : VirtAddr VirtSize
    Busy allocations
      DPH_HEAP_BLOCK : UserAddr  UserSize - VirtAddr VirtSize
        00000297e9edb068 : 00000297ec170600 0000000002625a00 - 00000297ec170000 0000000002627000
    _HEAP @ 297eaf60000
    _DPH_HEAP_ROOT @ 297eb061000
    Freed and decommitted blocks
      DPH_HEAP_BLOCK : VirtAddr VirtSize
    Busy allocations
      DPH_HEAP_BLOCK : UserAddr  UserSize - VirtAddr VirtSize
    _HEAP @ 297ec160000


kd> !heap -p -a 00000297ec170600 
    address 00000297ec170600 found in
    _DPH_HEAP_ROOT @ 297e9ed1000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                             297e9edb068:      297ec170600          2625a00 -      297ec170000          2627000
    00007ff97ca6867b ntdll!RtlDebugAllocateHeap+0x000000000000003b
    00007ff97c99d255 ntdll!RtlpAllocateHeap+0x00000000000000f5
    00007ff97c99b44d ntdll!RtlpAllocateHeapInternal+0x0000000000000a2d
    00007ff97a8cfde6 ucrtbase!_malloc_base+0x0000000000000036
    00007ff724531717 threads+0x0000000000001717
    00007ff724531058 threads+0x0000000000001058
    00007ff7245314f8 threads+0x00000000000014f8
    00007ff97bcd7034 KERNEL32!BaseThreadInitThunk+0x0000000000000014
    00007ff97c9c2651 ntdll!RtlUserThreadStart+0x0000000000000021

Critical Sections

kd> !cs
...
-----------------------------------------
DebugInfo          = 0x00000297e9f95fd0
Critical section   = 0x00007ff97a52d000 (KERNELBASE!ConsoleStateLock+0x0)
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0000000000000000
-----------------------------------------
...


kd> !cs -s -o 0x00007ff97a52d000
-----------------------------------------
Critical section   = 0x00007ff97a52d000 (KERNELBASE!ConsoleStateLock+0x0)
DebugInfo          = 0x00000297e9f95fd0
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x0000000000000000


Stack trace for DebugInfo = 0x00000297e9f95fd0:

0x00007ff97a2b0cee: KERNELBASE!_KernelBaseBaseDllInitialize+0x44E
0x00007ff97a2b071d: KERNELBASE!KernelBaseDllInitialize+0xD
0x00007ff97c989a1d: ntdll!LdrpCallInitRoutine+0x61
0x00007ff97c9dc1e7: ntdll!LdrpInitializeNode+0x1D3
0x00007ff97c9dbf7a: ntdll!LdrpInitializeGraphRecurse+0x42
0x00007ff97c9dc000: ntdll!LdrpInitializeGraphRecurse+0xC8
0x00007ff97c9ad937: ntdll!LdrpPrepareModuleForExecution+0xBF
0x00007ff97c98fbae: ntdll!LdrpLoadDllInternal+0x19A
0x00007ff97c9873e4: ntdll!LdrpLoadDll+0xA8
0x00007ff97c986af4: ntdll!LdrLoadDll+0xE4
0x00007ff97ca4372f: ntdll!LdrpInitializeProcess+0x1ACF
0x00007ff97c9e4cdb: ntdll!LdrpInitialize+0x15F
0x00007ff97c9e4b63: ntdll!LdrpInitialize+0x3B
0x00007ff97c9e4b0e: ntdll!LdrInitializeThunk+0xE

напочитать:

Классная преза, с кучей полезного по windbg

WinDbg. From A to Z! - Robert Kuster