Хотя значение хранится в реестре как REG_SZ, это не означает, что данные действительно заканчиваются корректным терминатором. На самом нижнем уровне реестр - просто иерархически организованная база данных имя/значение.
И вы можете соврать, и это может сойти вам с рук.
Множество людей врут насчёт своих данных в реестре. Вы можете найти множество вещей, которые должны быть REG_DWORD, хранимых как 4-х байтовый REG_BINARY (частично это наследие из реестра Windows 95, который не поддерживал REG_DWORD).
Одна из наиболее коварной лжи касается длины строки, записываемой в реестр. Рассмотрим такую программу:
program Project1; {$APPTYPE CONSOLE} uses Windows; var cb, dwRc: DWORD; sz: array[0..1] of WideChar; begin RegSetValueExW(HKEY_CURRENT_USER, 'Scratch', 0, REG_SZ, PWideChar('12'), 2); cb := 0; RegQueryValueExW(HKEY_CURRENT_USER, 'Scratch', nil, nil, nil, @cb); Writeln('Size is ', cb, ' bytes'); sz[0] := #$FFFF; sz[1] := #$FFFF; cb := SizeOf(sz); dwRc := RegQueryValueExW(HKEY_CURRENT_USER, 'Scratch', nil, nil, @sz, @cb); Writeln('RegQueryValueExW requesting ', SizeOf(sz), ' bytes => ', dwRc); Writeln(cb, ' bytes required'); if dwRc = ERROR_SUCCESS then begin Writeln('sz[0] = ', Word(sz[0])); Writeln('sz[1] = ', Word(sz[1])); end; RegDeleteValueW(HKEY_CURRENT_USER, 'Scratch'); end.Если вы запустите эту программу, вы получите следующее:
Size is 2 bytesЧто же произошло?
RegQueryValueExW requesting 4 bytes => 0
2 bytes required
sz[0] = 49
sz[1] = 65535
Для начала, заметим что вызов RegSetValueExW врёт о длине строки, утверждая что она длиной два байта, хотя фактически их шесть! (два WideChar-а плюс терминатор).
Реестр ответственно выполняет запись этой лжи и показывает её потом всем интересующимся.
Первый вызов RegQueryValueExW спрашивает, насколько велика строка - и реестр отвечает: два байта, поскольку именно это значение ему передали, когда сохраняли начальную строчку.
Чтобы показать, что у строки нет нуль терминатора, мы просим реестр прочитать эти два байта в наш буфер, предзаполненный паттерном, так что мы сможем увидеть, какие значения обновились, а какие - нет.
И вот, значение было прочитано из реестра и только два байта были прочитаны. sz[0] содержит символ '1', а sz[1] остаётся не инициализированным.
Это имеет последствия в плане безопасности.
Если ваша программа подразумевает, что строки в реестре всегда хранятся нуль-терминированными, то вам могут устроить переполнение буфера (buffer overflow), если вам подсунут строку, не заканчивающуюся нуль-терминатором.
Примечание: я не собираюсь ввязываться в спор, должно ли быть возможным создать такую ситуацию вообще. Я не проектировал реестр. Спор о прошлом не изменит настоящего, а настоящее - это то, как есть сейчас, и вам лучше бы быть готовым к нему.
Упражнение: изменить последний параметр RegSetValueExW на 3 и запустить программу снова. Объяснить результаты и обсудить их последствия.
Прим. пер.: упражнение от меня - объяснить, как и почему эта проблема решается в Delphi при использовании TRegistry.
Комментариев нет:
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.
Примечание. Отправлять комментарии могут только участники этого блога.