Драйверы режима ядра: Часть 10: Базовая техника: Реестр

Дата публикации 23 дек 2003

Драйверы режима ядра: Часть 10: Базовая техника: Реестр — Архив WASM.RU


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

Реестр (Registry) - это централизованная база данных, играющая ключевую роль в конфигурировании и управлении системой. Структура реестра аналогична структуре логического диска, но содержимое реестра не является статической совокупностью хранящихся на жестком диске данных, а динамически изменяется в процессе работы системы. Реестр состоит из разделов (keys), напоминающих дисковые каталоги. Разделы самого верхнего уровня называются корневыми (root keys). Разделы представляют собой контейнер, содержащий другие разделы, называемые подразделами (subkeys), и/или параметры (values), которые можно сравнить с файлами на диске. Параметры хранят собственно данные. За реализацию и управление реестром отвечает Диспетчер конфигурации (Configuration Manager).

Существует шесть корневых разделов:

HKEY_USER

- содержит информацию обо всех учётных записях;

HKEY_CURRENT_USER

- хранит профиль текущего зарегистрированного пользователя;

HKEY_LOCAL_MACHINE

- хранит информацию о конфигурации системы: описание аппаратного обеспечения, политики безопасности, пароли пользователей, настройки приложений, а также конфигурацию сервисов и драйверов;

HKEY_CURRENT_CONFIG

- содержит текущий профиль оборудования;

HKEY_CLASSES_ROOT

- хранит сопоставления расширений файлов и регистрационные данные COM-классов;

HKEY_PERFORMANCE_DATA

- содержит значения счетчиков производительности.

HKEY_PERFORMANCE_DATA особый раздел, обращаться к которому напрямую вряд ли потребуется. В пользовательском режиме информация о счетчиках производительности доступна через библиотеку Performance Data Helper, реализованную в модуле pdh.dll. Стандартная оснастка "Производительность" (Performance Monitor) использует именно эту библиотеку. Помимо счетчиков производительности этот раздел содержит массу дополнительной информации. Например, функции Process Status (реализованы в psapi.dll), перечисляющие процессы, потоки, модули и т.п., черпают информацию именно из раздела HKEY_PERFORMANCE_DATA. Редакторы реестра Regedit и Regedt32 не показывают содержимое этого раздела, т.к. редактировать его нет смысла.

Корневые разделы HKEY_CURRENT_USER, HKEY_CURRENT_CONFIG и HKEY_CLASSES_ROOT не содержат никакой информации. На самом деле это ссылки на другие подразделы реестра.

HKEY_CURRENT_USER

- ссылка на подраздел HKEY_USER\<SID_текущего_пользователя>, соответствующий текущему зарегистрированному в системе пользователю.

HKEY_CURRENT_CONFIG

- ссылка на подраздел HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Hardware Profiles\Current.

HKEY_CLASSES_ROOT

- ссылка на подразделы HKEY_LOCAL_MACHINE\SOFTWARE\Classes и HKEY_CURRENT_USER\SOFTWARE\Classes.



10.2 Доступ к реестру из драйвера

Как же добраться до реестра из режима ядра? Доступ к реестру осуществляется точно также, как и к любому другому именованному объекту, т.е. через пространство имен диспетчера объектов (подробнее см. 3 часть). Для интеграции пространства имен реестра и диспетчера объектов Диспетчер конфигурации (Configuration Manager) создает объект типа "раздел реестра" (key object) под именем "Registry" и помещает его в корневой каталог пространства имен диспетчера объектов. Для компонентов режима ядра это и есть точка входа в реестр.

Рис. 10-1. Объект "раздел реестра" Registry в пространстве имен диспетчера объектов

Все функции ядра, осуществляющие доступ к именованным объектам получают их имена в составе структуры OBJECT_ATRIBUTES - это нам уже давно известно. В случае объекта типа "раздел реестра" это имя должно начинаться с "\Registry". Остаётся добавить имена корневых разделов. Сразу скажу, что я не знаю, какое имя используется для открытия коневого раздела HKEY_PERFORMANCE_DATA и вообще, используется ли для этого какое-то имя. Возможно, здесь применяется другой механизм. Я предпринял кое-какие усилия в этом направлении, но они оказались напрасны. Если вам что-то известно по этому вопросу - просьба поделиться со мной этими знаниями. Для оставшихся двух корневых разделов всё просто.

Раздел

Имя

HKEY_USER

"\Registry\User"

HKEY_LOCAL_MACHINE

"\Registry\Machine"

Для обращения к трём разделам-ссылкам потребуются дополнительные манипуляции. Например, чтобы обратиться к разделу HKEY_CURRENT_CONFIG, надо знать, что он является ссылкой на подраздел HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Hardware Profiles\Current и подставить имя этого корневого раздела. Т.о. получится такое имя \Registry\Machine\SYSTEM\CurrentControlSet\Hardware Profiles\Current. К сожалению, определить такую длинную unicode-строку, используя макросы CTW0, $CTW0 и т.п. не удастся, т.к. эта строка превышает лимит в 47 символов. Можете воспользоваться любым доступным вам способом.



10.3 Исходный текст драйвера RegistryWorks

Теперь, когда нам известны имена корневых разделов - добраться до них не составит большого труда.

Код (Text):
  1.  
  2.  
  3.  ;@echo off
  4.  ;goto make
  5.  
  6.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  7.  ;
  8.  ;  RegistryWorks - Пример работы с реестром
  9.  ;
  10.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  11.  
  12.  .386
  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.  .const
  33.  
  34.  CCOUNTED_UNICODE_STRING "\\Registry\\Machine\\Software\\CoolApp", g_usMachineKeyName, 4
  35.  CCOUNTED_UNICODE_STRING "SomeData", g_usValueName, 4
  36.  
  37.  CTW0 "It's just a string", g_wszStringData, 4
  38.  
  39.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  40.  ;                                           К О Д                                                  
  41.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  42.  
  43.  .code
  44.  
  45.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  46.  ;                                       CreateKey                                                  
  47.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  48.  
  49.  CreateKey proc
  50.  
  51.  local oa:OBJECT_ATTRIBUTES
  52.  local hKey:HANDLE
  53.  local dwDisposition:DWORD
  54.  
  55.      invoke DbgPrint, $CTA0("\nRegistryWorks: *** Creating registry key\n")
  56.  
  57.      lea ecx, oa
  58.      InitializeObjectAttributes ecx, offset g_usMachineKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL
  59.      invoke ZwCreateKey, addr hKey, KEY_WRITE, addr oa, 0, NULL, REG_OPTION_VOLATILE, addr dwDisposition
  60.      .if eax == STATUS_SUCCESS
  61.  
  62.          .if dwDisposition == REG_CREATED_NEW_KEY
  63.              invoke DbgPrint, $CTA0("RegistryWorks: Registry key \\Registry\\Machine\\Software\\CoolApp created\n")
  64.          .elseif dwDisposition == REG_OPENED_EXISTING_KEY
  65.              invoke DbgPrint, $CTA0("RegistryWorks: Registry key \\Registry\\Machine\\Software\\CoolApp opened\n")
  66.          .endif
  67.  
  68.          invoke ZwClose, hKey
  69.          invoke DbgPrint, $CTA0("RegistryWorks: Registry key handle closed\n")
  70.      .else
  71.          invoke DbgPrint, $CTA0("RegistryWorks: Can't create registry key. Status: %08X\n"), eax
  72.      .endif
  73.  
  74.      ret
  75.  
  76.  CreateKey endp
  77.  
  78.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  79.  ;                                       SetValueKey                                                
  80.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  81.  
  82.  SetValueKey proc
  83.  
  84.  local oa:OBJECT_ATTRIBUTES
  85.  local hKey:HANDLE
  86.  
  87.      invoke DbgPrint, $CTA0("\nRegistryWorks: *** Opening registry key to set new value\n")
  88.      lea ecx, oa
  89.      InitializeObjectAttributes ecx, offset g_usMachineKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL
  90.      invoke ZwOpenKey, addr hKey, KEY_SET_VALUE, ecx
  91.  
  92.      .if eax == STATUS_SUCCESS
  93.          invoke DbgPrint, $CTA0("RegistryWorks: Registry key openeded\n")
  94.  
  95.          invoke ZwSetValueKey, hKey, addr g_usValueName, 0, REG_SZ, \
  96.                                  addr g_wszStringData, sizeof g_wszStringData
  97.          .if eax == STATUS_SUCCESS
  98.              invoke DbgPrint, $CTA0("RegistryWorks: Registry key value added\n")
  99.          .else
  100.              invoke DbgPrint, \
  101.                      $CTA0("RegistryWorks: Can't set registry key value. Status: %08X\n"), eax
  102.          .endif
  103.  
  104.          invoke ZwClose, hKey
  105.          invoke DbgPrint, $CTA0("RegistryWorks: Registry key handle closed\n")
  106.      .else
  107.          invoke DbgPrint, $CTA0("RegistryWorks: Can't open registry key. Status: %08X\n"), eax
  108.      .endif
  109.  
  110.      ret
  111.  
  112.  SetValueKey endp
  113.  
  114.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  115.  ;                                      QueryValueKey                                                
  116.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  117.  
  118.  QueryValueKey proc
  119.  
  120.  local oa:OBJECT_ATTRIBUTES
  121.  local hKey:HANDLE
  122.  local cb:DWORD
  123.  local ppi:PKEY_VALUE_PARTIAL_INFORMATION
  124.  local as:ANSI_STRING
  125.  local us:UNICODE_STRING
  126.  
  127.      invoke DbgPrint, $CTA0("\nRegistryWorks: *** Opening registry key to read value\n")
  128.      lea ecx, oa
  129.      InitializeObjectAttributes ecx, offset g_usMachineKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL
  130.      invoke ZwOpenKey, addr hKey, KEY_QUERY_VALUE, ecx
  131.  
  132.      .if eax == STATUS_SUCCESS
  133.          invoke DbgPrint, $CTA0("RegistryWorks: Registry key openeded\n")
  134.  
  135.          invoke ZwQueryValueKey, hKey, addr g_usValueName, \
  136.                                  KeyValuePartialInformation, NULL, 0, addr cb
  137.          .if cb != 0
  138.              invoke ExAllocatePool, PagedPool, cb
  139.              .if eax != NULL
  140.                  mov ppi, eax
  141.  
  142.                  invoke ZwQueryValueKey, hKey, addr g_usValueName, \
  143.                                      KeyValuePartialInformation, ppi, cb, addr cb
  144.                  .if ( eax == STATUS_SUCCESS ) && ( cb != 0 )
  145.  
  146.                      mov eax, ppi
  147.                      .if [KEY_VALUE_PARTIAL_INFORMATION PTR [eax]]._Type == REG_SZ
  148.                          lea eax, (KEY_VALUE_PARTIAL_INFORMATION PTR [eax]).Data
  149.                          invoke RtlInitUnicodeString, addr us, eax
  150.                          invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE
  151.                          .if eax == STATUS_SUCCESS
  152.                              invoke DbgPrint, \
  153.                                  $CTA0("RegistryWorks: Registry key value is: \=%s\=\n"), as.Buffer
  154.                              invoke RtlFreeAnsiString, addr as
  155.                          .endif
  156.                      .endif
  157.                  .else
  158.                      invoke DbgPrint, \
  159.                              $CTA0("RegistryWorks: Can't query registry key value. Status: %08X\n"), eax
  160.                  .endif
  161.                  invoke ExFreePool, ppi
  162.              .else
  163.                  invoke DbgPrint, $CTA0("RegistryWorks: Can't allocate memory. Status: %08X\n"), eax
  164.              .endif
  165.          .else
  166.              invoke DbgPrint, \
  167.              $CTA0("RegistryWorks: Can't get bytes count needed for key partial information. Status: %08X\n"), eax
  168.          .endif
  169.          invoke ZwClose, hKey
  170.          invoke DbgPrint, $CTA0("RegistryWorks: Registry key handle closed\n")
  171.      .else
  172.          invoke DbgPrint, $CTA0("RegistryWorks: Can't open registry key. Status: %08X\n"), eax
  173.      .endif
  174.  
  175.      ret
  176.  
  177.  QueryValueKey endp
  178.  
  179.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  180.  ;                                        DeleteKey                                                  
  181.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  182.  
  183.  DeleteKey proc
  184.  
  185.  local oa:OBJECT_ATTRIBUTES
  186.  local hKey:HANDLE
  187.  
  188.      invoke DbgPrint, $CTA0("\nRegistryWorks: *** Deleting registry key\n")
  189.  
  190.      lea ecx, oa
  191.      InitializeObjectAttributes ecx, offset g_usMachineKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL
  192.      invoke ZwOpenKey, addr hKey, KEY_ALL_ACCESS, ecx
  193.  
  194.      .if eax == STATUS_SUCCESS
  195.          invoke DbgPrint, $CTA0("RegistryWorks: Registry key opened\n")
  196.          invoke ZwDeleteKey, hKey
  197.          .if eax == STATUS_SUCCESS
  198.              invoke DbgPrint, $CTA0("RegistryWorks: Registry key deleted\n")
  199.          .else
  200.              invoke DbgPrint, $CTA0("RegistryWorks: Can't delete registry key. Status: %08X\n"), eax
  201.          .endif
  202.          invoke ZwClose, hKey
  203.          invoke DbgPrint, $CTA0("RegistryWorks: Registry key handle closed\n")
  204.      .else
  205.          invoke DbgPrint, $CTA0("RegistryWorks: Can't open registry key. Status: %08X\n"), eax
  206.      .endif
  207.  
  208.      ret
  209.  
  210.  DeleteKey endp
  211.  
  212.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  213.  ;                                      EnumerateKey                                                
  214.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  215.  
  216.  EnumerateKey proc
  217.  
  218.  local oa:OBJECT_ATTRIBUTES
  219.  local hKey:HANDLE
  220.  local cb:DWORD
  221.  local pbi:PKEY_BASIC_INFORMATION
  222.  local pfi:PKEY_FULL_INFORMATION
  223.  local as:ANSI_STRING
  224.  local us:UNICODE_STRING
  225.  local dwSubKeys:DWORD
  226.  local pwszKeyName:PWCHAR
  227.  
  228.      invoke DbgPrint, $CTA0("\nRegistryWorks: *** Opening \\Registry\\User key to enumerate\n")
  229.  
  230.      CCOUNTED_UNICODE_STRING "\\Registry\\User", g_usUserKeyName, 4
  231.  
  232.      lea ecx, oa
  233.      InitializeObjectAttributes ecx, offset g_usUserKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL
  234.      invoke ZwOpenKey, addr hKey, KEY_ENUMERATE_SUB_KEYS, ecx
  235.  
  236.      .if eax == STATUS_SUCCESS
  237.          invoke DbgPrint, $CTA0("RegistryWorks: Registry key openeded\n")
  238.  
  239.          invoke ZwQueryKey, hKey, KeyFullInformation, NULL, 0, addr cb
  240.          .if cb != 0
  241.  
  242.              invoke ExAllocatePool, PagedPool, cb
  243.              .if eax != NULL
  244.                  mov pfi, eax
  245.  
  246.                  invoke ZwQueryKey, hKey, KeyFullInformation, pfi, cb, addr cb
  247.                  .if ( eax == STATUS_SUCCESS ) && ( cb != 0 )
  248.  
  249.                      mov eax, pfi
  250.                      push (KEY_FULL_INFORMATION PTR [eax]).SubKeys
  251.                      pop dwSubKeys
  252.  
  253.                      invoke DbgPrint, \
  254.                          $CTA0("RegistryWorks: ---------- Starting enumerate subkeys ----------\n")
  255.  
  256.                      push ebx
  257.                      xor ebx, ebx
  258.                      .while ebx < dwSubKeys
  259.                          invoke ZwEnumerateKey, hKey, ebx, KeyBasicInformation, NULL, 0, addr cb
  260.                          .if cb != 0
  261.                              invoke ExAllocatePool, PagedPool, cb
  262.                              .if eax != NULL
  263.                                  mov pbi, eax
  264.  
  265.                                  invoke ZwEnumerateKey, hKey, ebx, KeyBasicInformation, pbi, cb, addr cb
  266.                                  .if ( eax == STATUS_SUCCESS ) && ( cb != 0 )
  267.  
  268.                                      mov eax, pbi
  269.                                      mov eax, (KEY_BASIC_INFORMATION PTR [eax]).NameLength
  270.                                      add eax, sizeof WCHAR
  271.                                      mov cb, eax
  272.                                      invoke ExAllocatePool, PagedPool, cb
  273.                                      .if eax != NULL
  274.                                          mov pwszKeyName, eax
  275.  
  276.                                          invoke memset, pwszKeyName, 0, cb
  277.  
  278.                                          mov ecx, pbi
  279.                                          mov eax, (KEY_BASIC_INFORMATION PTR [ecx]).NameLength
  280.                                          shr eax, 1
  281.                                          lea ecx, (KEY_BASIC_INFORMATION PTR [ecx])._Name
  282.                                          invoke wcsncpy, pwszKeyName, ecx, eax
  283.  
  284.                                          invoke RtlInitUnicodeString, addr us, pwszKeyName
  285.                                          invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE
  286.                                          .if eax == STATUS_SUCCESS
  287.                                              invoke DbgPrint, $CTA0("RegistryWorks: \=%s\=\n"), as.Buffer
  288.                                              invoke RtlFreeAnsiString, addr as
  289.                                          .endif
  290.  
  291.                                          invoke ExFreePool, pwszKeyName
  292.                                      .endif
  293.                                  .else
  294.                                      invoke DbgPrint, \
  295.                                          $CTA0("RegistryWorks: Can't enumerate registry keys. Status: %08X\n"), eax                              
  296.                                  .endif
  297.                                  invoke ExFreePool, pbi
  298.                              .endif
  299.                          .endif
  300.                          inc ebx
  301.                      .endw
  302.                      pop ebx
  303.  
  304.                      invoke DbgPrint, \
  305.                          $CTA0("RegistryWorks: ------------------------------------------------\n")
  306.  
  307.                  .else
  308.                      invoke DbgPrint, \
  309.                          $CTA0("RegistryWorks: Can't query registry key information. Status: %08X\n"), eax
  310.                  .endif
  311.                  invoke ExFreePool, pfi
  312.              .else
  313.                  invoke DbgPrint, $CTA0("RegistryWorks: Can't allocate memory. Status: %08X\n"), eax
  314.              .endif
  315.          .endif
  316.  
  317.          invoke ZwClose, hKey
  318.          invoke DbgPrint, $CTA0("RegistryWorks: Registry key handle closed\n")
  319.  
  320.      .else
  321.          invoke DbgPrint, $CTA0("RegistryWorks: Can't open registry key. Status: %08X\n"), eax
  322.      .endif
  323.  
  324.      ret
  325.  
  326.  EnumerateKey endp
  327.  
  328.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  329.  ;                                       DriverEntry                                                
  330.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  331.  
  332.  DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
  333.  
  334.      invoke DbgPrint, $CTA0("\nRegistryWorks: Entering DriverEntry\n")
  335.        
  336.  
  337.      ;:::::::::::::::::::::::::::::::::::::::
  338.      ; Создаём новый подраздел              ;
  339.      ;:::::::::::::::::::::::::::::::::::::::
  340.  
  341.      invoke CreateKey
  342.  
  343.      ;:::::::::::::::::::::::::::::::::::::::
  344.      ; Создаем в этом подразделе параметр   ;
  345.      ;:::::::::::::::::::::::::::::::::::::::
  346.  
  347.      invoke SetValueKey
  348.  
  349.      ;:::::::::::::::::::::::::::::::::::::::
  350.      ; Получаем значение параметра          ;
  351.      ;:::::::::::::::::::::::::::::::::::::::
  352.  
  353.      invoke QueryValueKey
  354.  
  355.      ;:::::::::::::::::::::::::::::::::::::::
  356.      ; Удаляем подраздел                    ;
  357.      ;:::::::::::::::::::::::::::::::::::::::
  358.  
  359.      invoke DeleteKey
  360.  
  361.      ;:::::::::::::::::::::::::::::::::::::::
  362.      ; Перечисляем содержимое раздела       ;
  363.      ;:::::::::::::::::::::::::::::::::::::::
  364.  
  365.      invoke EnumerateKey
  366.  
  367.  
  368.      invoke DbgPrint, $CTA0("\nRegistryWorks: Leaving DriverEntry\n")
  369.  
  370.      mov eax, STATUS_DEVICE_CONFIGURATION_ERROR
  371.      ret
  372.  
  373.  DriverEntry endp
  374.  
  375.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  376.  ;                                                                                                  
  377.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  378.  
  379.  end DriverEntry
  380.  
  381.  :make
  382.  
  383.  set drv=RegistryWorks
  384.  
  385.  \masm32\bin\ml /nologo /c /coff %drv%.bat
  386.  \masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native %drv%.obj
  387.  
  388.  del %drv%.obj
  389.  
  390.  echo.
  391.  pause
  392.  
  393.  

Код драйвера состоит из нескольких автономных процедур: CreateKey, SetValueKey, QueryValueKey, DeleteKey и EnumerateKey, каждая из которых работает с реестром «с нуля» - для учебного примера так нагляднее.



10.3.1 Создаём и открываем подраздел реестра

В процедуре CreateKey, вызовом функции ZwCreateKey, создаем новый подраздел Registry\Machine\Software\CoolApp.

Код (Text):
  1.  
  2.  
  3.      invoke ZwCreateKey, addr hKey, KEY_WRITE, addr oa, 0, NULL, REG_OPTION_VOLATILE, addr dwDisposition
  4.  
  5.  

Флаг REG_OPTION_VOLATILE запрещает запись созданного подраздела в куст реестра (hiev)- один из файлов реестра на диске. Т.е. этот подраздел будет существовать только до следующей перезагрузки системы. В данном случае, использовать этот флаг не обязательно, т.к. мы всё равно удалим весь подраздел. Если вы захотите прописать подраздел в реестр на постоянное место жительства, то использовать этот флаг не следует.

Код (Text):
  1.  
  2.  
  3.      .if eax == STATUS_SUCCESS
  4.          .if dwDisposition == REG_CREATED_NEW_KEY
  5.          .elseif dwDisposition == REG_OPENED_EXISTING_KEY
  6.          .endif
  7.  
  8.  

После успешного вызова ZwCreateKey по значению переменной dwDisposition можно определить, был ли создан новый подраздел (REG_CREATED_NEW_KEY) или такой подраздел уже существовал в реестре (REG_OPENED_EXISTING_KEY) и поэтому был открыт.

Код (Text):
  1.  
  2.  
  3.      invoke ZwOpenKey, addr hKey, KEY_SET_VALUE, ecx
  4.      invoke ZwOpenKey, addr hKey, KEY_QUERY_VALUE, ecx
  5.      invoke ZwOpenKey, addr hKey, KEY_ALL_ACCESS, ecx
  6.      invoke ZwOpenKey, addr hKey, KEY_ENUMERATE_SUB_KEYS, ecx
  7.  
  8.  

В остальных процедурах вызовом функции ZwOpenKey открываем существующий раздел, запрашивая только необходимые в данный момент права доступа.



10.3.2 Создаём параметр реестра

Теперь, создадим в нашем подразделе строковый параметр с именем "SomeData".

Код (Text):
  1.  
  2.  
  3.  . . .
  4.  CCOUNTED_UNICODE_STRING "SomeData", g_usValueName, 4
  5.  CTW0 "It's just a string", g_wszStringData, 4
  6.  . . .
  7.          invoke ZwSetValueKey, hKey, addr g_usValueName, 0, REG_SZ, \
  8.                                  addr g_wszStringData, sizeof g_wszStringData
  9.  
  10.  

Константа REG_SZ определяет тип создаваемого параметра: unicode-строка завершающаяся нулём. Существует масса других типов - все они описаны в DDK.



10.3.3 Получаем значение параметра реестра

Получим значение нашего параметра SomeData.

Код (Text):
  1.  
  2.  
  3.          invoke ZwQueryValueKey, hKey, addr g_usValueName, \
  4.                                  KeyValuePartialInformation, NULL, 0, addr cb
  5.  
  6.  

Третий параметр функции ZwQueryValueKey определяет тип запрашиваемой информации. В DDK определено три значения: KeyValueBasicInformation, KeyValueFullInformation и KeyValuePartialInformation, каждому из которых соответствует своя структура: KEY_VALUE_BASIC_INFORMATION, KEY_VALUE_FULL_INFORMATION и KEY_VALUE_PARTIAL_INFORMATION. В данном случае, мы хотим получить содержимое параметра. Для этого вполне годится KeyValuePartialInformation.

Т.к. объём информации нам заранее не известен, вызываем ZwQueryValueKey, передавая NULL в четвертом параметре (указатель на буфер) и 0 - в пятом (размер буфера). Функция ZwQueryValueKey подсчитает необходимый ей размер буфера и вернет это значение в переменной cb (многие, но не все, Zw* функции работают подобным образом).

Код (Text):
  1.  
  2.  
  3.          .if cb != 0
  4.              invoke ExAllocatePool, PagedPool, cb
  5.              .if eax != NULL
  6.                  mov ppi, eax
  7.  
  8.                  invoke ZwQueryValueKey, hKey, addr g_usValueName, \
  9.                                      KeyValuePartialInformation, ppi, cb, addr cb
  10.  
  11.  

Выделив необходимое пространство памяти, вызываем ZwQueryValueKey ещё раз - теперь уже с указателем на буфер.

Код (Text):
  1.  
  2.  
  3.                  .if ( eax == STATUS_SUCCESS ) && ( cb != 0 )
  4.                      mov eax, ppi
  5.                      .if [KEY_VALUE_PARTIAL_INFORMATION PTR [eax]]._Type == REG_SZ
  6.  
  7.  

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

Код (Text):
  1.  
  2.  
  3.                          lea eax, (KEY_VALUE_PARTIAL_INFORMATION PTR [eax]).Data
  4.  
  5.  

Если тип параметра REG_SZ, то поле Data структуры KEY_VALUE_PARTIAL_INFORMATION содержит завершенную нулем unicode-строку. Нам надо вывести эту строку в отладочном сообщении, поэтому необходимо преобразовать её в ansi-строку.

Код (Text):
  1.  
  2.  
  3.                          invoke RtlInitUnicodeString, addr us, eax
  4.  
  5.  

Функция RtlInitUnicodeString измеряет unicode-строку, адрес которой передан во втором параметре и заполняет структуру UNICODE_STRING, адрес которой передан в первом параметре.

Код (Text):
  1.  
  2.  
  3.                          invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE
  4.  
  5.  

Функция RtlUnicodeStringToAnsiString конвертирует unicode-строку в ansi. Если последний параметр установлен в TRUE, функция сама выделит буфер, запишет туда преобразованную строку и заполнит структуру ANSI_STRING (переменную as, в нашем случае). Поле Buffer структуры ANSI_STRING будет указывать на выделенный буфер с преобразованной ansi-строкой. Если последний параметр установить в FALSE, то буфер для ansi-строки надо выделить заранее и поместить указатель на него в поле Buffer структуры ANSI_STRING. В данном случае, мы просим функцию RtlUnicodeStringToAnsiString выделить буфер за нас.

Код (Text):
  1.  
  2.  
  3.                          .if eax == STATUS_SUCCESS
  4.                              invoke DbgPrint, \
  5.                                  $CTA0("RegistryWorks: Registry key value is: \=%s\=\n"), as.Buffer
  6.                              invoke RtlFreeAnsiString, addr as
  7.                          .endif
  8.  
  9.  

В DDK (по крайней мере, в 2000 DDK) описание функции RtlUnicodeStringToAnsiString, весьма туманно, я бы даже сказал двусмысленно. IFS DDK описывает работу этой функции значительно лучше. Чтобы расставить «точки над i» рассмотрим простой пример.

Код (Text):
  1.  
  2.  
  3.  wsz db 'a', 0, 'b', 0, 'c', 0, 0, 0
  4.  us  UNICODE_STRING
  5.  as  ANSI_STRING    
  6.  
  7.  

Изначально переменные us и as неопределены. wsz - unicode-строка, подлежащая переводу в формат ANSI.

Код (Text):
  1.  
  2.  
  3.  us._Length        = ?
  4.  us.MaximumLength  = ?
  5.  us.Buffer         = ?
  6.  
  7.  as._Length        = ?
  8.  as.MaximumLength  = ?
  9.  as.Buffer         = ?
  10.  
  11.  

RtlInitUnicodeString заполняет переменную us на основании размера строки wsz.

Код (Text):
  1.  
  2.  
  3.  invoke RtlInitUnicodeString, addr us, addr wsz
  4.  
  5.  us._Length        = 6
  6.  us.MaximumLength  = 8
  7.  us.Buffer         = offset wsz
  8.  
  9.  as._Length        = ?
  10.  as.MaximumLength  = ?
  11.  as.Buffer         = ?
  12.  
  13.  

По последнему аргументу RtlUnicodeStringToAnsiString видит, что она должна выделить буфер размера us.MaximumLength / sizeof WCHAR. Выделив буфер и поместив указатель на него в поле as.Buffer, функция RtlUnicodeStringToAnsiString приступает к собственно конвертации строки. Если эта операция проходит успешно, то переменная as будет содержать полное описание конвертированной строки.

Код (Text):
  1.  
  2.  
  3.  invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE
  4.  
  5.  us._Length        = 6
  6.  us.MaximumLength  = 8
  7.  us.Buffer         = offset wsz
  8.  
  9.  as._Length        = 3
  10.  as.MaximumLength  = 4
  11.  as.Buffer         = -> 'a', 'b', 'c', 0  ; указатель на выделенный функцией RtlUnicodeStringToAnsiString,,,TRUE буфер,
  12.                                           ; который содержит преобразованную строку wsz в формате ANSI.
  13.  
  14.  

После использования, буфер, выделенные функцией RtlUnicodeStringToAnsiString, надо освободить, вызвав RtlFreeAnsiString, причем в качестве аргумента ей передается не указатель на сам буфер, а указатель на структуру ANSI_STRING. RtlFreeAnsiString освобождает буфер as.Buffer и обнуляет всю переменную as.

Код (Text):
  1.  
  2.  
  3.  invoke RtlFreeAnsiString, addr as
  4.  
  5.  us._Length        = 6
  6.  us.MaximumLength  = 8
  7.  us.Buffer         = offset wsz
  8.  
  9.  as._Length        = 0
  10.  as.MaximumLength  = 0
  11.  as.Buffer         = NULL
  12.  
  13.  

Надеюсь, теперь всё понятно.



10.3.4 Удаляем подраздел реестра

Думаю, тут вы разберетесь и без меня.



10.3.5 Перебираем содержимое подраздела реестра

Теперь посмотрим, что лежит в разделе \Registry\User.

Код (Text):
  1.  
  2.  
  3.          invoke ZwQueryKey, hKey, KeyFullInformation, NULL, 0, addr cb
  4.          .if cb != 0
  5.  
  6.              invoke ExAllocatePool, PagedPool, cb
  7.              .if eax != NULL
  8.                  mov pfi, eax
  9.  
  10.                  invoke ZwQueryKey, hKey, KeyFullInformation, pfi, cb, addr cb
  11.                  .if ( eax == STATUS_SUCCESS ) && ( cb != 0 )
  12.  
  13.                      mov eax, pfi
  14.                      push (KEY_FULL_INFORMATION PTR [eax]).SubKeys
  15.                      pop dwSubKeys
  16.  
  17.  

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

Код (Text):
  1.  
  2.  
  3.                      push ebx
  4.                      xor ebx, ebx
  5.                      .while ebx < dwSubKeys
  6.                          invoke ZwEnumerateKey, hKey, ebx, KeyBasicInformation, NULL, 0, addr cb
  7.                          .if cb != 0
  8.  
  9.                              invoke ExAllocatePool, PagedPool, cb
  10.                              .if eax != NULL
  11.                                  mov pbi, eax
  12.  
  13.                                  invoke ZwEnumerateKey, hKey, ebx, KeyBasicInformation, pbi, cb, addr cb
  14.                                  .if ( eax == STATUS_SUCCESS ) && ( cb != 0 )
  15.  
  16.  

Ползуясь функцией ZwEnumerateKey с информацмонным классом KeyBasicInformation, организуем цикл перебора. Как и в предыдущих случаях, вызываем её два раза: первый раз для того, чтобы узнать размер информации, второй - для того, чтобы эту информацию получить.

Код (Text):
  1.  
  2.  
  3.                                      mov eax, pbi
  4.                                      mov eax, (KEY_BASIC_INFORMATION PTR [eax]).NameLength
  5.                                      add eax, sizeof WCHAR
  6.                                      mov cb, eax
  7.                                      invoke ExAllocatePool, PagedPool, cb
  8.  
  9.  

В поле _Name структуры KEY_BASIC_INFORMATION вернётся имя подраздела/параметра в виде unicode-строки, но строка эта не завершена нулём. Для последующего преобразования её в ansi-строку (для вывода в отладочном сообщении) нам нужна завершенная нулем строка - выделим для неё временный буфер.

Код (Text):
  1.  
  2.  
  3.                                      .if eax != NULL
  4.                                          mov pwszKeyName, eax
  5.  
  6.                                          invoke memset, pwszKeyName, 0, cb
  7.  
  8.                                          mov ecx, pbi
  9.                                          mov eax, (KEY_BASIC_INFORMATION PTR [ecx]).NameLength
  10.                                          shr eax, 1
  11.                                          lea ecx, (KEY_BASIC_INFORMATION PTR [ecx])._Name
  12.                                          invoke wcsncpy, pwszKeyName, ecx, eax
  13.  
  14.  

Скопируем имя подраздела/параметра во временный буфер, предварительно обнулив его.

Код (Text):
  1.  
  2.  
  3.                                          invoke RtlInitUnicodeString, addr us, pwszKeyName
  4.                                          invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE
  5.                                          .if eax == STATUS_SUCCESS
  6.                                              invoke DbgPrint, $CTA0("RegistryWorks: \=%s\=\n"), as.Buffer
  7.                                              invoke RtlFreeAnsiString, addr as
  8.                                          .endif
  9.  
  10.  

Делаем необходимые преобразования, подробно рассмотренные выше и выводим имя подраздела/параметра в отладочном сообщении.

Код (Text):
  1.  
  2.  
  3.                                          invoke ExFreePool, pwszKeyName
  4.                                      .endif
  5.                                  .endif
  6.                                  invoke ExFreePool, pbi
  7.                              .endif
  8.                          .endif
  9.                          inc ebx
  10.                      .endw
  11.                      pop ebx
  12.                  .endif
  13.                  invoke ExFreePool, pfi
  14.              .endif
  15.          .endif
  16.  
  17.  

Проводим необходимую очистку ресурсов.

Исходный код драйвера в архиве.

© Four-F

0 1.235
archive

archive
New Member

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