Ассемблер под Windows для чайников. Часть 16

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

Обычно при работе с реестром возникает необходимость производить следующие основные действия:
— создавать или открывать ключи реестра;
— устанавливать их значения;
— читать значения;
— изменять и удалять значения;
— удалять ключи.

Собственно, этому мы сегодня и научимся. Для создания ключа реестра используется функция RegCreateKeyEx. Если указанный ключ уже имеется в реестре, то функция его откроет. Параметры функции следующие:
1. Дескриптор открытого ключа, полученный при помощи RegCreateKeyEx или RegOpenKeyEx, либо одно из зарезервированных значений:
HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS.
2. Указатель на завершающуюся нулем строку, содержащую имя создаваемого подраздела. Строка не должна начинаться с символа "\". Параметр не может быть нулем.
3. Зарезервированный параметр, должен быть нулем;
4. Указатель на завершающуюся нолем строку, содержащую имя класса создаваемого ключа. Данный параметр может быть проигнорирован и может быть нулем.
5. Специальные опции — одно из следующих значений: REG_OPTION_BACKUP_RESTORE, REG_OPTION_NON_VOLATILE, REG_OPTION_VOLATILE;
6. Права доступа: KEY_ALL_ACCESS — полный доступ (комбинация значений: STANDARD_RIGHTS_REQUIRED, KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY, KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, KEY_CREATE_LINK), KEY_CREATE_LINK — зарезервировано для системного доступа, KEY_CREATE_SUB_KEY — разрешение создавать подразделы, KEY_ENUMERATE_SUB_KEYS — разрешение перечислять подразделы, KEY_EXECUTE — эквивалент значения KEY_READ, KEY_NOTIFY — разрешение на получение уведомлений об изменениях ключа или подраздела ключа, KEY_QUERY_VALUE — разрешение получать значения ключа, KEY_READ — разрешение на чтение (комбинация значений: STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY), KEY_SET_VALUE — разрешение удалять или изменять значения ключа, KEY_WRITE — разрешение на запись (комбинация значений: STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY).
7. Указатель на структуру атрибутов безопасности SECURITY_ATTRIBUTES, которая определяет возможность наследования дескриптора созданного ключа дочерними процессами. Если параметр ноль, дескриптор не может использоваться дочерними процессами.
8. Указатель на переменную, в которой будет сохранен дескриптор созданного или открытого ключа.
9. Указатель на переменную, в которой будет сохранено значение результата функции: REG_CREATED_NEW_KEY — ключ отсутствовал, но был создан, REG_OPENED_EXISTING_KEY — ключ существовал и был открыт. Если в параметре указать ноль, то данная информация не будет получена.

В случае успешного выполнения функция возвращает значение ERROR_SUCCESS. В случае ошибки возвращается код системной ошибки. Следует отметить, что ключ, создаваемый данной функцией, не содержит значений. Для установки значений ключа следует использовать функцию RegSetValueEx. Приложение не должно создавать новые подразделы прямо в HKEY_USERS или HKEY_LOCAL_MACHINE — только уровнем ниже, в уже имеющихся подразделах. Открытие уже существующих в реестре ключей производится функцией RegOpenKeyEx. Ее параметры:

1. Дескриптор открытого ключа, полученный при помощи RegCreateKeyEx или RegOpenKeyEx, либо одно из зарезервированных значений:
HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS.
2. Указатель на строку, завершающуюся нулем, содержащую имя подраздела. Если указан ноль, или пустая строка, функция откроет новый
дескриптор для указанного в первом параметре ключа.
3. Зарезервированный параметр, должен быть нулем.
4. Права доступа (смотрите 6-й параметр функции RegCreateKeyEx).
5. Указатель на переменную, в которой будет сохранен дескриптор открытого ключа.

В случае успешного выполнения функция возвращает значение ERROR_SUCCESS. В случае ошибки возвращается код системной ошибки. Для удаления ключа реестра используется функция RegDeleteKey. Параметры:
1. Дескриптор открытого ключа, полученный при помощи RegCreateKeyEx или RegOpenKeyEx, либо одно из зарезервированных значений:
HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS.
2. Указатель на строку завершающуюся нулем, содержащую имя удаляемого подраздела. Удаляемый подраздел не должен сам иметь подразделы. В случае успешного выполнения функция возвращает значение ERROR_SUCCESS. В случае ошибки возвращается код системной ошибки. Помните, что удаляемый ключ не будет удален до тех пор, пока существуют его открытые дескрипторы. Удаляемый ключ сам не должен содержать еще ключи. Чтобы удалить ключ, внутри которого имеются другие ключи, следует предварительно удалить каждый из них по отдельности. Функция RegCloseKey закрывает открытый ранее ключ реестра. Единственный параметр этой функции — дескриптор открытого ключа. Не забывайте применять эту функцию, когда более не требуется обращаться к открытому или созданному ключу реестра. Возвращаемые значения такие же, как и в ранее перечисленных функциях. Значения в реестре могут храниться в различных форматах. Сохраняя данные в реестре, вам следует выбрать подходящий тип значения из доступных типов: REG_BINARY — бинарные данные в любой форме.

REG_DWORD — 32-битное значение.
REG_DWORD_LITTLE_ENDIAN — 32-битное значение в формате little-endian (то же самое, что и REG_DWORD). Little-endian, называемый в народе интеловским порядком следования байтов, — это порядок, при котором младший байт располагается по младшему адресу. От младшего к старшему. То есть число 0x01020304 будет записано как 0x04030201. Этот порядок наиболее распространен в процессорах Intel и AMD, и, естественно, в операционных системах, предназначенных для работы с ними.
REG_DWORD_BIG_ENDIAN — 32-битное значение в формате big-endian. Порядок следования байтов — старший байт по младшему адресу. В таком порядке записываются привычные для нас арабские числа, поэтому такой порядок более удобен для восприятия человеком, но менее удобен для процессора, поскольку арифметические операции над длинными числами производятся начиная с младшего байта.
REG_EXPAND_SZ — завершающаяся нулем строка, содержащая неразвернутую сноску на системную переменную (например "%PATH%").
REG_LINK — зарезервировано для системного использования.
REG_MULTI_SZ — массив завершающихся нулем строк, завершающийся двумя нолями.
REG_NONE — значение неопределенного типа.
REG_SZ — завершающаяся нулем строка.

Чтобы создать в открытом ключе значение, а также чтобы изменить имеющееся значение, используется функция RegSetValueEx. Параметры:
1. Дескриптор открытого ключа, полученный при помощи RegCreateKeyEx или RegOpenKeyEx, либо одно из зарезервированных значений:
HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS.
2. Указатель на завершающуюся нулем строку, содержащую имя создаваемого или изменяемого значения. Если указатель — ноль или указывает на пустую строку, то функция обработает безымянное значение (значение по умолчанию). По сути, ключи реестра не имеют "значений по
умолчанию", но могут содержать одно безымянное значение любого типа.
3. Зарезервирован. Пишем ноль.
4. Тип создаваемого значения. Допустимые типы приведены выше.
5. Указатель на создаваемое значение.
6. Размер создаваемого значения в байтах включая завершающий строку ноль для строковых значений.

Возвращаемые значения такие же, как и в ранее перечисленных функциях. Для получения типа и содержимого определенного значения ключа используется функция RegQueryValueEx. Параметры функции:
1. Дескриптор открытого ключа, полученный при помощи RegCreateKeyEx или RegOpenKeyEx, либо одно из зарезервированных значений:
HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS.
2. Указатель на завершающуюся нулем строку, содержащую имя значения. Нулевой указатель, или указатель на пустую строку, позволяет получить тип и содержимое безымянного значения, если таковое существует.
3. Зарезервирован.
4. Указатель на переменную, в которую будет сообщен тип указанного значения. Если тип значения не требуется, можно указать ноль.
5. Указатель на буфер для получения содержимого указанного значения. Если содержимое не требуется, можно указать ноль.
6. Указатель на переменную, содержащую размер буфера, определенного для получения содержимого значения. Размер должен включать нуль- терминаторы, если таковые присутствуют в содержимом значения. При успешном выполнении функции в данной переменной будет сохранен размер полученного содержимого. Если размер буфера не позволяет вместить содержимое, в данной переменной будет сохранен требуемый размер, а функция вернет ошибку ERROR_MORE_DATA. Данный параметр может быть нулем только в случае, если содержимое не требуется, и предыдущий параметр — тоже ноль. Если предыдущий параметр — ноль, а данный параметр отличен от нуля, то функция вернет ERROR_SUCCESS, а в переменной, указанной в данном параметре, сохранит текущий размер содержимого значения. Таким образом, можно корректно определить размер требуемого буфера под содержимое значения.

В остальном возвращаемые данной функцией значения не отличаются от возвращаемых значений описанных ранее функций.
Для удаления значения из ключа реестра применяют функцию RegDeleteValue. Функция имеет лишь два параметра, аналогичных первым двум
параметрам функций RegSetValueEx и RegQueryValueEx. Возвращаемые значения те же. Для получения информации обо всех значениях ключа можно использовать функцию RegEnumValue. Она перечисляет все значения указанного открытого ключа реестра. Причем вызывать ее придется в цикле; для каждого очередного значения — отдельный вызов функции. Подробности — ниже по тексту, а параметры функции следующие:
1. Дескриптор открытого ключа.
2. Индекс запрашиваемого значения. Для первого вызова функции он должен быть нулем, а при следующих вызовах увеличивается на единицу. Ввиду того, что значения в ключах не упорядочены по каким либо свойствам, кроме индекса, функция будет возвращать их в произвольном порядке. 3. Указатель на буфер, в который будет помещено имя значения в виде завершающейся нулем строки.
4. Указатель на переменную, содержащую размер буфера под имя значения в символах. По возвращению из функции в данную переменную будет помещено количество записанных в буфер символов без учета завершающего строку нуля.
5. Зарезервирован.
6. Указатель на переменную, в которую будет сообщен тип указанного значения. Если тип значения не требуется, можно указать ноль.
7. Указатель на буфер для получения содержимого указанного значения. Если содержимое не требуется, можно указать ноль.
8. Указатель на переменную, содержащую размер буфера, определенного для получения содержимого значения. По возвращению из функции в данную переменную будет помещено количество записанных в буфер байтов. Данный параметр может быть нулем только в случае, если содержимое не требуется, и предыдущий параметр — тоже ноль. Размер должен включать нуль-терминаторы, если таковые присутствуют в содержимом значения. Если размер буфера не позволяет вместить содержимое, в данной переменной будет сохранен требуемый размер, а функция вернет ошибку ERROR_MORE_DATA.

Возвращаемые функцией значения такие же, как и в перечисленных ранее функциях за небольшим исключением. Если в ключе больше не осталось неперечисленных значений, функция возвращает ошибку ERROR_NO_MORE_ITEMS. По получению такой ошибки можно определить, что все имеющиеся в ключе значения были перечислены. Аналогичным образом используется функция RegEnumKeyEx для получения информации обо всех подразделах ключа, то есть о ключах, находящихся в данном ключе. Ее параметры:
1. Дескриптор открытого ключа.
2. Индекс запрашиваемого подраздела.
3. Указатель на буфер, в который будет помещено имя подраздела в виде завершающейся нулем строки. Функция сообщает только имя подраздела, а не полное его имя в иерархии ключей реестра.
4. Указатель на переменную, содержащую размер буфера под имя подраздела в символах, с учетом завершающего нуля. По возвращению из функции в данную переменную будет помещено количество записанных в буфер символов без учета завершающего строку нуля.
5. Зарезервирован.
6. Указатель на буфер, в который будет помещен класс подраздела в виде завершающейся нулем строки. Можно указать ноль.
7. Указатель на переменную, содержащую размер буфера под класс подраздела в символах, с учетом завершающего ноля. По возвращению из функции в данную переменную будет помещено количество записанных в буфер символов без учета завершающего строку нуля. Данный параметр может быть нулем только если предыдущий параметр тоже ноль.
8. Указатель на переменную, в которую будет помещено время последней записи подраздела. Параметр может быть нулем.
Если в ключе больше не осталось неперечисленных подразделов, функция возвращает ошибку ERROR_NO_MORE_ITEMS. В остальном возвращаемые значения аналогичны значениям описанных ранее функций.

Для получения значений или подразделов ключа при помощи функций RegEnumValue или RegEnumKeyEx мы можем в цикле увеличивать второй параметр функции до получения ошибки ERROR_NO_MORE_ITEMS. Или же, наоборот, двигаться от последнего индекса до нуля. Индекс последнего значения или подраздела ключа можно узнать при помощи функции RegQueryInfoKey. Функция предназначена для получения общей информации об открытом ключе реестра. Ее параметры:
1. Дескриптор открытого ключа.
2. Указатель на буфер, в который будет помещен класс ключа в виде завершающейся нулем строки. Можно указать ноль.
3. Указатель на переменную, содержащую размер буфера под класс ключа в символах, с учетом завершающего нуля. По возвращению из функции в данную переменную будет помещено количество записанных в буфер символов без учета завершающего строку нуля. В случае недостаточного размера буфера будет выдана ошибка ERROR_MORE_DATA, а в данную переменную будет помещен требуемый размер буфера в символах без учета завершающего нуля. Данный параметр может быть нулем только если предыдущий параметр тоже ноль.
4. Зарезервирован.
5. Указатель на переменную, в которую будет помещено количество подразделов данного ключа. Параметр может быть нулем.
6. Указатель на переменную, в которую будет помещен размер самого длинного имени подраздела данного ключа в символах Юникода без учета завершающего нуля. Параметр может быть нулем.
7. Указатель на переменную, в которую будет помещен размер самого длинного имени класса подразделов данного ключа в символах Юникода без учета завершающего нуля. Параметр может быть нулем.
8. Указатель на переменную, в которую будет помещено количество значений данного ключа. Параметр может быть нулем.
9. Указатель на переменную, в которую будет помещен размер самого длинного имени значения данного ключа в символах Юникода без учета завершающего нуля. Параметр может быть нулем.
10. Указатель на переменную, в которую будет помещен размер самого длинного содержимого значений данного ключа в байтах. Параметр может быть нулем.
11. Указатель на переменную, в которую будет помещен размер дескриптора прав доступа к данному ключу в байтах. Параметр может быть нулем. 12. Указатель на 64-битную структуру FILETIME, в которую будет помещено время последнего изменения ключа или его значения. Параметр может быть нулем.

При успешном выполнении функция возвращает значение ERROR_SUCCESS, в случае ошибки — код системной ошибки.
Вот и все, что касается основных функций работы с реестром. Примеров их использования сегодня не будет, зато их будет предостаточно в следующей части. А до ее выхода попробуйте самостоятельно разобраться с этим делом. Только учтите, что реестр очень легко повредить, а исправить намного сложнее. Так что, если в чем-то сильно сомневаетесь, лучше подождите следующего выпуска. Ну, а если вы уверены в себе, то вам больше и объяснять ничего не требуется. Скажу лишь, что функции работы с реестром находятся в отдельной библиотеке — advapi32. Поэтому не забудьте добавить в секцию импортируемых данных строки:
advapi32,'ADVAPI32.DLL',\

include 'api\advapi32.inc'

Желаю удачи, до новых встреч!

BarMentaLisk, SASecurity gr., barmentalisk@sa-sec.org


Компьютерная газета. Статья была опубликована в номере 39 за 2008 год в рубрике программирование

©1997-2024 Компьютерная газета