Blog. Just Blog

Получение размера динамической памяти приложения (Heap)

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Heap или куча - особая структура данных, с помощью которой приложению выделяется динамически распределяемая память. Крайне удобная штука, когда надо быстренько выделить немного памяти под сиюминутные нужды. Но при активной работе с кучей может возникнуть ситуация, когда надо узнать размер оставшейся памяти, общий размер кучи или максимальный размер непрерывных данных, которые туда можно записать. Что-то из этого можно узнать при помощи штатных функций, а что-то придется получать копанием в недрах системы. Но сперва несколько структур и констант для работы. Их нет даже в MSDN, не говоря уже о FASM.
  1. struct DEBUG_BUFFER
  2.         SectionHandle        dd ?
  3.         SectionBase          dd ?
  4.         RemoteSectionBase    dd ?
  5.         SectionBaseDelta     dd ?
  6.         EventPairHandle      dd ?
  7.         Unknown              rd 2
  8.         RemoteThreadHandle   dd ?
  9.         InfoClassMask        dd ?
  10.         SizeOfInfo           dd ?
  11.         AllocatedSize        dd ?
  12.         SectionSize          dd ?
  13.         ModuleInformation    dd ?
  14.         BackTraceInformation dd ?
  15.         HeapInformation      dd ?
  16.         LockInformation      dd ?
  17.         Reserved             rd 8
  18. ends
  19.  
  20. struct DEBUG_HEAP_INFORMATION
  21.         Base        dd ?
  22.         Flags       dd ?
  23.         Granularity dw ?
  24.         Unknown     dw ?
  25.         Allocated   dd ?
  26.         Committed   dd ?
  27.         TagCount    dd ?
  28.         BlockCount  dd ?
  29.         Reserved    rd 7
  30.         Tags        dd ?
  31.         Blocks      dd ?
  32. ends
  33.  
  34. PDI_HEAPS = 0x04
  35. PDI_HEAP_BLOCKS = 0x10
Каждый процесс может иметь больше одного объекта кучи, для получения данных об этих объектах воспользуемся недокументированной функцией RtlQueryProcessDebugInformation, предварительно подготовив буфер для приема данных еще одной недокументированной функцией RtlCreateQueryDebugBuffer. Указатель на интересующие нас данные содержится в поле HeapInformation структуры DEBUG_BUFFER. Это массив из нескольких структур типа DEBUG_HEAP_INFORMATION, каждая из которых соответствует одному из объектов кучи процесса. Перебирая их по очереди и сравнивая поле Base с искомым хэндлом, находим нужный объект. В следующем код предполагается, что в переменной hHeap содержится хэндл созданной кучи, по которой мы хотим получить информацию.
  1.         ; Зарезервировать буфер для отладочной информации
  2.         invoke  RtlCreateQueryDebugBuffer,0,FALSE
  3.         mov     [debug_buf],eax
  4.  
  5.         ; Получить информацию о кучах текущего процесса
  6.         invoke  GetCurrentProcessId
  7.         invoke  RtlQueryProcessDebugInformation,eax,\
  8.                 PDI_HEAPS+PDI_HEAP_BLOCKS,[debug_buf]
  9.         mov     eax,[debug_buf]
  10.         ; Указатель на информацию о кучах
  11.         mov     eax,[eax+DEBUG_BUFFER.HeapInformation]
  12.         ; Количество записей
  13.         mov     ecx,[eax]
  14.         ; Пропустить заголовок
  15.         add     eax,4
  16. .loc_heap_scan:
  17.         or      ecx,ecx
  18.         jz      .loc_heap_done
  19.  
  20.         ; Это наша куча?
  21.         mov     edx,[eax+DEBUG_HEAP_INFORMATION.Base]
  22.         cmp     edx,[hHeap]
  23.         jne     .loc_heap_next
  24.  
  25.         ...
  26.         ; В структуре DEBUG_HEAP_INFORMATION информация о куче
  27.         ...
  28.  
  29.         jmp     .loc_heap_done
  30.  
  31. .loc_heap_next:
  32.         add     eax,sizeof.DEBUG_HEAP_INFORMATION
  33.         dec     ecx
  34.         jmp     .loc_heap_scan
  35.  
  36. .loc_heap_done:
  37.         ; Прибраться за собой
  38.         invoke  RtlDestroyQueryDebugBuffer,[debug_buf]
Нужный объект кучи найден. Теперь давайте повнимательнее посмотрим на содержимое других полей структуры DEBUG_HEAP_INFORMATION. Запрошенный размер памяти Allocated - суммарный объем всех блоков, выделенных с помощью функции HeapAlloc. Committed - фактически выделенный размер памяти. Максимальный размер кучи Blocks - параметр dwMaximumSize, указанный при вызове функции HeapCreate и выровненный до размера страницы памяти. Размер страницы можно получить при помощи функции GetSystemInfo. BlockCount - количество выделенных блоков памяти в куче, оно может отличаться от количества вызовов функции HeapAlloc, так как содержит еще и служебные блоки. Также важно понимать, что из-за сегментации кучи размер блока, который можно разово выделить в ней, не обязательно равен разнице между значениями Blocks и Allocated. Для получения максимального размера непрерывного блока памяти надо воспользоваться функцией HeapCompact. В случае фрагментированной кучи эта функция вернет размер наибольшего свободного участка.

Ну и раз уж мы затронули работу с объектом кучи, то давайте извлечем содержащиеся в нем данные. Для этого нам понадобятся следующие структуры и константы:
  1. struct BLOCK
  2.     hMem       dd ?
  3.     dwReserved rd 3
  4. ends
  5.  
  6. struct REGION
  7.     dwCommittedSize   dd ?
  8.     dwUnCommittedSize dd ?
  9.     lpFirstBlock      dd ?
  10.     lpLastBlock       dd ?
  11. ends
  12.  
  13. struct PROCESS_HEAP_ENTRY
  14.     lpData       dd ?
  15.     cbData       dd ?
  16.     cbOverhead   db ?
  17.     iRegionIndex db ?
  18.     wFlags       dw ?
  19.  
  20.     union
  21.         Block  BLOCK
  22.         Region REGION
  23.     ends
  24. ends
  25.  
  26. PROCESS_HEAP_ENTRY_BUSY = 0x0004
Сам процесс обработки объекта выполняется при помощи функции HeapWalk. На вход подается указатель на структуру PROCESS_HEAP_ENTRY, на выходе получаем заполненные данные. При первом вызове надо обязательно обнулить поле lpData, тем самым мы указываем функции, что обработку надо начать с самой первой записи.
  1.         ; Начать с первой записи
  2.         mov     [hentry.lpData],0
  3. .loc_scan_heap:
  4.         ; Получить информацию о записи
  5.         invoke  HeapWalk,[hHeap],hentry
  6.         ; Записи закончились?
  7.         or      eax,eax
  8.         jz      .loc_scan_done
  9.         ; Это данные?
  10.         test    [hentry.wFlags],PROCESS_HEAP_ENTRY_BUSY
  11.         jz      .loc_scan_heap
  12.  
  13.         ...
  14.         ; [hentry.lpData] -> сохраненные данные
  15.         ; [hentry.cbData] -> размер выделенного блока
  16.         ...
  17.  
  18.         jmp     .loc_scan_heap
  19. .loc_scan_done:
Дальше все просто: для каждой записи проверяем значение поля wFlags, если взведен флаг PROCESS_HEAP_ENTRY_BUSY, то это блок данных. Поле lpData указывает на его содержимое, а поле cbData содержит размер блока в байтах, указанный при его резервировании функцией HeapAlloc. Все прочие блоки пропускаем или обрабатываем согласно поставленной задаче.

В приложении примеры программ с исходными текстами, одна из которых получает информацию об объекте кучи текущего процесса, а вторая отображает содержащиеся в нем данные.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

Get.Heap.Info.Demo.zip (4,386 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 1467 | Комментариев: 0

Метки: Assembler
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
Комментариeв нет

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0056 сек.) / Память: 4.5 Mb
Наверх