Драйверы режима ядра: Часть 7: Работа с памятью. Использование ассоциативных списков

Дата публикации 27 окт 2003

Драйверы режима ядра: Часть 7: Работа с памятью. Использование ассоциативных списков — Архив WASM.RU



7.1 Ассоциативные списки

Диспетчер куч (heap manager) управляет кучами, как системными, так и пользовательскими, разбивая пространство кучи на блоки и организуя списки блоков одинакового размера. Если приходит запрос на выделение блока памяти из кучи, то диспетчер куч пытается подобрать свободный блок подходящего размера. На это, естественно, требуется какое-то время. Если же заранее известно, что потребуются блоки памяти фиксированного размера, но количество этих блоков и частота их использования не известны, то следует использовать, по соображениям лучшей производительности, так называемые ассоциативные списки (look-aside lists), которые существуют только в ядре. Главное отличие ассоциативных списков от пулов в том, что из ассоциативных списков можно выделять блоки памяти только фиксированного и заранее определенного размера, а из пулов любого, причем память из ассоциативных списков выделяется быстрее, так как нет необходимости подбирать подходящую область свободной памяти.

Если Вам когда-либо придется работать с ассоциативным списком, то первой же проблемой, которую придется решить, кроме, конечно, создания самого ассоциативного списка, будет проблема управления блоками памяти, которые Вы из этого ассоциативного списка будете выделять. В частности, где и как хранить адреса блоков - ведь к ним надо обращаться, а потом еще и освобождать. Поскольку количество этих блоков заранее не известно, то проблема достаточно серьезная. Для решения подобных задач существуют особые конструкции. Всего таких конструкций три:

  • Односвязный список (singly linked list);
  • S-список (S-list, sequenced singly-linked list.), являющийся развитием односвязного списка;
  • Двусвязный список (doubly linked list).

Мы рассмотрим только двусвязный список, как наиболее универсальный.

Код, представленный в этой статье довольно прост, но может показаться очень сложным, если Вы впервые сталкиваетесь с такими понятиями как ассоциативный и двусвязный списки.

Хотя обе эти конструкции называются списками, по сути, это совершенно разные вещи. Перевод английского термина look-aside list на русский язык крайне абстрактен и плохо отражает суть. Look-aside дословно можно перевести как: "смотреть по сторонам". Смысл с том, что look-aside list представляет собой набор или список заранее выделенных системой блоков памяти. В каждый момент времени какие-то блоки могут быть свободны, а какие-то заняты. При запросе на выделение очередного блока задача системы пройти по списку ("посмотреть по сторонам ") и найти близлежащий свободный блок. В русском же переводе, look-aside превращается в "ассоциативный", и становится совершенно непонятно, что с чем здесь ассоциируется. Тем не менее, перевод этот устоявшийся - хочешь, не хочешь, придется применять. Т.о. ассоциативный список - это фактически особая системная куча, работающая по определенным правилам.

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



7.2 Исходный текст драйвера LookasideList

Сколько я не думал, мне так и не удалось изобразить что-то осмысленное и в то же время простое. Поэтому, этот драйвер будет делать достаточно бессмысленные манипуляции. Однако, это не должно помешать понять принципы работы с ассоциативным и двусвязным списками.

Программы управления драйвером не будет. Используйте KmdManager (входит в пакет KmdKit) или аналогичную утилиту, а отладочные сообщения, выдаваемые драйвером, контролируйте с помощью утилиты DebugView (http://www.sysinternals.com) или консоли SoftICE.

Код (Text):
  1.  
  2.  
  3.  ;@echo off
  4.  ;goto make
  5.  
  6.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  7.  ;
  8.  ;  LookasideList - Пример использования и управления ассоциативным списком.
  9.  ;
  10.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  11.  
  12.  .486
  13.  .model flat, stdcall
  14.  option casemap:none
  15.  
  16.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  17.  ;                              В К Л Ю Ч А Е М Ы Е    Ф А Й Л Ы
  18.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  19.  
  20.  include \masm32\include\w2k\ntstatus.inc
  21.  include \masm32\include\w2k\ntddk.inc
  22.  include \masm32\include\w2k\ntoskrnl.inc
  23.  
  24.  includelib \masm32\lib\w2k\ntoskrnl.lib
  25.  
  26.  include \masm32\Macros\Strings.mac
  27.  
  28.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  29.  ;                                       С Т Р У К Т У Р Ы                                          
  30.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  31.  
  32.  SOME_STRUCTURE STRUCT
  33.      SomeField1    DWORD        ?
  34.      SomeField2    DWORD        ?
  35.      ; . . .                          ; Любое кол-во дополнительно необходимых членов
  36.  
  37.      ListEntry    LIST_ENTRY        ; Двусвязный список для управления структурами
  38.                                       ; Зтот член удобнее располагать первым в структуре,
  39.                                       ; но здесь используется более общее решение
  40.  
  41.      ; . . .                          ; Любое кол-во дополнительно необходимых членов
  42.      SomeFieldX    DWORD        ?
  43.  SOME_STRUCTURE ENDS
  44.  
  45.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  46.  ;                       И Н И Ц И А Л И З И Р О В А Н Н Ы Е    Д А Н Н Ы Е
  47.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  48.  
  49.  .data?
  50.  
  51.  g_pPagedLookasideList    PPAGED_LOOKASIDE_LIST    ?
  52.  g_ListHead               LIST_ENTRY              
  53.  g_dwIndex                DWORD                    ?
  54.  
  55.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  56.  ;                                           К О Д                                                  
  57.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  58.  
  59.  .code
  60.  
  61.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  62.  ;                                         AddEntry                                                  
  63.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  64.  
  65.  AddEntry proc uses esi
  66.  
  67.      invoke ExAllocateFromPagedLookasideList, g_pPagedLookasideList
  68.      .if eax != NULL
  69.          mov esi, eax
  70.  
  71.          invoke DbgPrint, \
  72.                 $CTA0("LookasideList: + Memory block allocated from lookaside list at address %08X\n"), esi
  73.  
  74.          invoke memset, esi, 0, sizeof SOME_STRUCTURE
  75.  
  76.          assume esi:ptr SOME_STRUCTURE
  77.  
  78.          lea eax, g_ListHead
  79.          lea ecx, [esi].ListEntry
  80.          InsertHeadList eax, ecx
  81.  
  82.          inc g_dwIndex
  83.          mov eax, g_dwIndex
  84.          mov [esi].SomeField1, eax
  85.  
  86.          invoke DbgPrint, $CTA0("LookasideList: + Entry #%d added\n"), [esi].SomeField1
  87.  
  88.          assume esi:nothing
  89.  
  90.      .else
  91.          invoke DbgPrint, $CTA0("LookasideList: Very bad. Couldn't allocate from lookaside list\n")
  92.      .endif
  93.  
  94.      ret
  95.  
  96.  AddEntry endp
  97.  
  98.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  99.  ;                                       RemoveEntry                                                
  100.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  101.  
  102.  RemoveEntry proc uses esi
  103.  
  104.      IsListEmpty addr g_ListHead
  105.      .if eax != TRUE
  106.  
  107.          lea eax, g_ListHead
  108.          RemoveHeadList eax
  109.          sub eax, SOME_STRUCTURE.ListEntry
  110.          mov esi, eax
  111.  
  112.          invoke DbgPrint, $CTA0("LookasideList: - Entry #%d removed\n"), \
  113.                              (SOME_STRUCTURE PTR [esi]).SomeField1
  114.  
  115.          invoke ExFreeToPagedLookasideList, g_pPagedLookasideList, esi
  116.  
  117.          invoke DbgPrint, \
  118.              $CTA0("LookasideList: - Memory block at address %08X returned to lookaside list\n"), esi
  119.      .else
  120.          invoke DbgPrint, \
  121.              $CTA0("LookasideList: An attempt was made to remove entry from empty lookaside list\n")
  122.      .endif
  123.  
  124.      ret
  125.  
  126.  RemoveEntry endp
  127.  
  128.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  129.  ;                                       DriverEntry                                                
  130.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  131.  
  132.  DriverEntry proc uses ebx pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
  133.  
  134.      invoke DbgPrint, $CTA0("\nLookasideList: Entering DriverEntry\n")
  135.  
  136.      invoke ExAllocatePool, NonPagedPool, sizeof PAGED_LOOKASIDE_LIST
  137.      .if eax != NULL
  138.  
  139.          mov g_pPagedLookasideList, eax
  140.        
  141.          invoke DbgPrint, \
  142.          $CTA0("LookasideList: Nonpaged memory for lookaside list allocated at address %08X\n"), \
  143.          g_pPagedLookasideList
  144.  
  145.          invoke ExInitializePagedLookasideList, g_pPagedLookasideList, NULL, NULL, \
  146.                                                 0, sizeof SOME_STRUCTURE, ' msaW', 0
  147.  
  148.          invoke DbgPrint, $CTA0("LookasideList: Lookaside list initialized\n")
  149.  
  150.          lea eax, g_ListHead
  151.          InitializeListHead eax
  152.  
  153.          invoke DbgPrint, $CTA0("LookasideList: Doubly linked list head initialized\n")
  154.  
  155.          invoke DbgPrint, $CTA0("\nLookasideList: Start to allocate/free from/to lookaside list\n")
  156.  
  157.          and g_dwIndex, 0
  158.  
  159.          xor ebx, ebx
  160.          .while ebx < 5
  161.  
  162.              invoke AddEntry
  163.              invoke AddEntry
  164.  
  165.              invoke RemoveEntry
  166.  
  167.              inc ebx
  168.          .endw
  169.  
  170.          invoke DbgPrint, $CTA0("\nLookasideList: Free the rest to lookaside list\n")
  171.  
  172.          .while TRUE
  173.  
  174.              invoke RemoveEntry
  175.  
  176.              lea eax, g_ListHead
  177.              IsListEmpty eax
  178.              .if eax == TRUE
  179.                  invoke DbgPrint, $CTA0("LookasideList: Doubly linked list is empty\n\n")
  180.                  .break
  181.              .endif
  182.  
  183.          .endw
  184.  
  185.          invoke ExDeletePagedLookasideList, g_pPagedLookasideList
  186.  
  187.          invoke DbgPrint, $CTA0("LookasideList: Lookaside list deleted\n")
  188.  
  189.  
  190.  
  191.          invoke ExFreePool, g_pPagedLookasideList
  192.  
  193.          invoke DbgPrint, \
  194.          $CTA0("LookasideList: Nonpaged memory for lookaside list at address %08X released\n"), \
  195.          g_pPagedLookasideList
  196.  
  197.      .else
  198.          invoke DbgPrint, \
  199.         $CTA0("LookasideList: Couldn't allocate nonpaged memory for lookaside list control structure")
  200.      .endif
  201.  
  202.      invoke DbgPrint, $CTA0("LookasideList: Leaving DriverEntry\n")
  203.  
  204.      mov eax, STATUS_DEVICE_CONFIGURATION_ERROR
  205.      ret
  206.  
  207.  DriverEntry endp
  208.  
  209.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  210.  ;                                                                                                  
  211.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  212.  
  213.  end DriverEntry
  214.  
  215.  :make
  216.  
  217.  set drv=LookasideList
  218.  
  219.  \masm32\bin\ml /nologo /c /coff %drv%.bat
  220.  \masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native %drv%.obj
  221.  
  222.  del %drv%.obj
  223.  
  224.  echo.
  225.  pause
  226.  
  227.  



7.3 Работаем с ассоциативным списком

Код (Text):
  1.  
  2.  
  3.      invoke ExAllocatePool, NonPagedPool, sizeof PAGED_LOOKASIDE_LIST
  4.      .if eax != NULL
  5.  
  6.          mov g_pPagedLookasideList, eax
  7.  
  8.  

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

Код (Text):
  1.  
  2.  
  3.          invoke ExInitializePagedLookasideList, g_pPagedLookasideList, NULL, NULL, \
  4.                                                 0, sizeof SOME_STRUCTURE, ' msaW', 0
  5.  
  6.  

Вызываем функцию ExInitializePagedLookasideList, которая заполняет выделенную на предыдущем этапе структуру PAGED_LOOKASIDE_LIST. Только после этого ассоциативный список готов к использованию.

Обратите внимание на то, что при инициализации ассоциативного списка мы нигде не указываем, сколько всего блоков памяти нам может потребоваться. Откуда же система знает, сколько памяти она должна заблаговременно выделить? Ведь если этого не сделать заранее, то ассоциативный список не будет работать быстрее, чем системный пул. Дело в том, что изначально система выделяет всего несколько блоков, количество которых определяется самой системой. Если теперь мы начнем выделять память из ассоциативного списка, то будут возвращаться указатели именно на эти заранее выделенные блоки. Один раз в секунду система проводит настройку всех ассоциативных списков в системе, вызывая функцию ExAdjustLookasideDepth (В книге Дэвида Соломона и Марка Руссиновича "Внутреннее устройство Microsoft Windows 2000" в соответствующем разделе говорится, что эта функция называется KiAdjustLookasideDepth. Скорее всего это ошибка - такой функции я не нашел - и имелась ввиду ExAdjustLookasideDepth.). Если при настройке ассоциативного списка система увидит, что резерв свободных блоков уменьшился, то выделит новые. Количество вновь выделяемых блоков зависит от нагрузки на ассоциативный список, т.е. от частоты с которой мы выделяем из него память. Система пытается настроить ассоциативный список так, чтобы он работал наиболее эффективно. Если в интервале времени между двумя настройками мы исчерпали лимит заранее выделенных блоков, то система начинает использовать системный пул до следующей настройки, когда она увидит, что резерв равен нулю и выделит необходимый лимит. Здесь важно понять, что если скорость выделения блоков уж слишком велика, то можно и не получить выигрыша в производительности по сравнению с системным пулом. В этом случае придется организовывать собственную кучу. Оценить насколько эффективно работает ассоциативный список можно с помощью команды !lookaside отладчика MS Kernel Debugger либо в ручную, просмотрев с помощью SoftICE дамп структуры PAGED_LOOKASIDE_LIST. Информацию, извлекаемую именно из этой структуры, система использует для выбора стратегии управления ассоциативным списком.

Код (Text):
  1.  
  2.  
  3.  kd> !lookaside ed374840
  4.  
  5.  Lookaside "" @ ed374840 "Regm"
  6.      Type     =     0001 PagedPool
  7.      Current Depth  =        2   Max Depth  =        4
  8.      Size           =     1024   Max Alloc  =     4096
  9.      AllocateMisses =        4   FreeMisses =        0
  10.      TotalAllocates =  1319722   TotalFrees =  1319720
  11.      Hit Rate       =       99%  Hit Rate   =      100%
  12.  
  13.  

Это пример того, насколько эффективно работает ассоциативный список утилиты RegMon (http://www.sysinternals.com). Как видите, при огромном (более одного миллиона!) количестве операций выделения/освобождения эффективность стремится к 100%. Причина такой высокой эффективность в том, что RegMon не держит долго выделенный блок, а тут же использует и возвращает назад.

Код (Text):
  1.  
  2.  
  3.          lea eax, g_ListHead
  4.          InitializeListHead eax
  5.  
  6.  

Вызовом макроса InitializeListHead, инициализируем голову двусвязного списка. Теперь оба поля структуры LIST_ENTRY содержат указатели на саму эту структуру. Это означает, что двусвязный список пуст (см. рис 7-1, иллюстрация 1).

Рис. 7-1. Этот рисунок позволит нагляднее увидеть работу двусвязного списка.

Код (Text):
  1.  
  2.  
  3.          and g_dwIndex, 0
  4.  
  5.  

Эта глобальная переменная нужна нам только для того, чтобы что-то помещать в выделяемые из ассоциативного списка структуры SOME_STRUCTURE и наблюдать этот процесс через отладочные сообщения.

Код (Text):
  1.  
  2.  
  3.          xor ebx, ebx
  4.          .while ebx < 5
  5.  
  6.              invoke AddEntry
  7.              invoke AddEntry
  8.  
  9.              invoke RemoveEntry
  10.  
  11.              inc ebx
  12.          .endw
  13.  
  14.  

Пять раз прогоняем цикл, который добавляет два элемента и удаляет один. Каждый элемент представляет собой структуру SOME_STRUCTURE. Все выделенные структуры связаны посредством двусвязного списка. Функции AddEntry и RemoveEntry рассмотрим позже.

Этот цикл - имитация процесса, когда блоки памяти выделяются и возвращаются в ассоциативный список в случайном порядке. Т.е. здесь предполагается, что мы каким-то образом работаем с выделяемыми элементами. Например, мы могли бы написать драйвер, который перехватывает вызовы какого-нибудь системного сервиса, скажем ZwOpenKey , и записывает информацию о перехвате в структуру, выделенную из ассоциативного списка.

Код (Text):
  1.  
  2.  
  3.          .while TRUE
  4.  
  5.              invoke RemoveEntry
  6.  
  7.              lea eax, g_ListHead
  8.              IsListEmpty eax
  9.              .if eax == TRUE
  10.                  .break
  11.              .endif
  12.  
  13.          .endw
  14.  
  15.  

Т.к. в предыдущем цикле мы добавляли два элемента, а удаляли один, двусвязный список, а значит и ассоциативный список тоже, содержат сейчас какое-то количество элементов. Мы предполагаем, что эти элементы нам больше не нужны и хотим освободить вся занятую память.

В бесконечном цикле вызываем процедуру RemoveEntry, которая удаляет один элемент. Цикл работает до тех пор, пока двусвязный список не окажется пустым. Макрос IsListEmpty проверяет, пуст ли двусвязный список. Признаком пустоты является ситуация, когда оба поля головы двусвязного списка - структуры LIST_ENTRY - указывают на саму эту структуру, т.е. голова указывает на саму себя. В этот момент мы приходим к тому же состоянию, которое мы имели сразу после вызова макроса InitializeListHead (см. рис 7-1, иллюстрация 1).

Код (Text):
  1.  
  2.  
  3.          invoke ExDeletePagedLookasideList, g_pPagedLookasideList
  4.  
  5.  

Если двусвязный список пуст, то и в ассоциативном списке нет больше элементов, т.к. мы использовали двусвязный список как способ организации выделенных из ассоциативного списка блоков. Теперь мы можем удалить сам ассоциативный список, вызовом функции ExDeletePagedLookasideList. При этом система освобождает выделенную для ассоциативного списка память и прекращает его периодическую настройку.

Код (Text):
  1.  
  2.  
  3.          invoke ExFreePool, g_pPagedLookasideList
  4.  
  5.  

Вызовом функции ExFreePool, освобождаем неподкачиваемую память, занятую под структуру PAGED_LOOKASIDE_LIST. Если перед этим Вы забудете (я этого не забыл) вызвать ExDeletePagedLookasideList, то примерно через секунду увидите BSOD, т.к. система попытается произвести настройку отсутствующего ассоциативного списка.

Код (Text):
  1.  
  2.  
  3.      mov eax, STATUS_DEVICE_CONFIGURATION_ERROR
  4.      ret
  5.  
  6.  

Мы освободили все выделенные нам ресурсы - драйвер можно выгружать.



7.4 Процедура AddEntry

Если нам нужен новый элемент, мы вызываем процедуру AddEntry, которая выделяет новый блок памяти из ассоциативного списка и добавляет его в двусвязный список.

Код (Text):
  1.  
  2.  
  3.      invoke ExAllocateFromPagedLookasideList, g_pPagedLookasideList
  4.      .if eax != NULL
  5.          mov esi, eax
  6.  
  7.  

Вызовом функции ExAllocateFromPagedLookasideList, получаем указатель на новый блок памяти и сохраняем его в регистре esi. Обратите внимание на то, что мы не сообщаем системе размер блока, т.к. этот размер определен ранее в вызове функции ExInitializePagedLookasideList и равен размеру структуры SOME_STRUCTURE.

Код (Text):
  1.  
  2.  
  3.          invoke memset, esi, 0, sizeof SOME_STRUCTURE
  4.  
  5.  

Обнуляем выделенный блок, хотя здесь этого и не требуется.

Код (Text):
  1.  
  2.  
  3.          assume esi:ptr SOME_STRUCTURE
  4.  
  5.  

Теперь мы имеем новый экземпляр структуры SOME_STRUCTURE. Но пока он существует сам по себе. Мы должны "привязать" его к нашему двусвязному списку, который при первом вызове AddEnrty еще пуст.

Код (Text):
  1.  
  2.  
  3.          lea eax, g_ListHead
  4.          lea ecx, [esi].ListEntry
  5.          InsertHeadList eax, ecx
  6.  
  7.  

После первого вызова макроса InsertHeadList мы имеем ситуацию изображенную на рис. 7-1, иллюстрация 2. Обратите внимание на то, что вторым параметром в макрос InsertHeadList мы передаем адрес не самой структуры SOME_STRUCTURE, а адрес её поля ListEntry. Т.е. макрос InsertHeadList получает указатель на две структуры LIST_ENTRY, первая из которых является головой двусвязного списка, а вторая элементом структуры, которую необходимо к этому двусвязному списку привязать. Причем макрос InsertHeadList привязывает новую структуру в голову двусвязного списка (справа, если смотреть на рис 7-1). Можно привязать и в хвост (слева по рисунку), если воспользоваться макросом InsertTailList.

При добавлении первой структуры в двусвязный список оба макроса произведут одинаковый эффект и двусвязный список будет выглядеть так, как изображено на рис 7-1, иллюстрация 2.

Если же двусвязный список не пустой, то макрос InsertHeadList "раздвинет" его между головой и идущей справа от нее структурой и вставит на это место новую структуру. Мы получим ситуацию изображенную на рис 7-1, иллюстрация 3. Если бы мы воспользовались макросом InsertTailList, то он сделал бы то же самое, но с хвоста двусвязного списка (см. рис 7-1, иллюстрация 4).

Надеюсь, здесь всё ясно.

Код (Text):
  1.  
  2.  
  3.          inc g_dwIndex
  4.          mov eax, g_dwIndex
  5.          mov [esi].SomeField1, eax
  6.  
  7.  

Запишем в поле SomeField1, добавленной только что структуры, её порядковый номер. Порядок, в котором структуры добавляются/удаляются можно будет посмотреть в окне DbgView.



7.5 Процедура RemoveEntry

Процедура RemoveEntry диаметрально противоположна AddEntry. Т.е. она отвязывает элемент с головы двусвязного списка и возвращает этот блок памяти в ассоциативный список.

Код (Text):
  1.  
  2.  
  3.      IsListEmpty addr g_ListHead
  4.      .if eax != TRUE
  5.  
  6.  

На всякий случай, проверяем, не пуст ли двусвязный список.

Код (Text):
  1.  
  2.  
  3.          lea eax, g_ListHead
  4.          RemoveHeadList eax
  5.  
  6.  

Отвязываем структуру с головы двусвязного списка (как Вы наверное догадываетесь, макрос RemoveTailList делает это с хвоста. Можно удалить и произвольный элемент макросом RemoveEntryList). В этот момент, извлеченная из двусвязного списка структура, опять начинает существовать сама по себе, а двусвязный список замыкается, чтобы связать оставшиеся элементы.

Код (Text):
  1.  
  2.  
  3.          sub eax, SOME_STRUCTURE.ListEntry
  4.          mov esi, eax
  5.  
  6.  

Обратите внимание на этот момент. Макросы RemoveTailList/RemoveHeadList/RemoveEntryList возвращают указатель на связующий элемент (вложенную структуру LIST_ENTRY) отвязанной структуры, а не указатель на саму отвязанную структуру. Это вполне естественно, ведь эти макросы не знают, в каком месте располагается связующий элемент. И узнать это они никак не могут. Это знаем только мы. Поэтому мы должны вычесть из полученного указателя смещение поля ListEntry в структуре SOME_STRUCTURE и получить указатель на саму структуру (в DDK это делает макрос CONTAINING_RECORD). Вот теперь этот блок памяти можно возвратить в ассоциативный список.

Код (Text):
  1.  
  2.  
  3.          invoke ExFreeToPagedLookasideList, g_pPagedLookasideList, esi
  4.  
  5.  

Передаем функции ExFreeToPagedLookasideList адрес блока, который мы хотим вернуть в ассоциативный список.

Исходный код драйвера в архиве. Для компиляции требуется KmdKit версии не ниже 1.3 - берите на сайте

© Four-F

0 1.435
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532