Внедряемся в святая святых системы минуя стандартные механизмы
Сегодня мы попробуем залезть в реестр Windows с черного хода, без
использования штатных WinAPI-функций, для этого предназначенных. Что нам
это даст в итоге? Возможность писать и читать из реестра напрямую, в
обход ограничений, установленных разработчиками антивирусных решений!
Забегая вперед, отмечу: тема эта интересна, но тут целый набор серьезных проблем. Хотя кто сказал, что нам это не по плечу? :)
Что такое реестр, или немного лирики
С точки зрения операционной системы Windows, реестр — это уникальная
кладовка. В этой своеобразно выстроенной иерархической базе данных
хранятся настройки, данные, регистрационная информация и прочая хрень
почти обо всем в системе, начиная с программ и заканчивая настройками
конкретного пользователя. В реестре хранится практически все. Несмотря
на то что некоторые программы предпочитают хранить свои настройки в
ini-конфигах (особенно программы, написанные для Win 3.11. – Прим.
ред.), сама Windows всю нужную информацию о самой себе считывает из
реестра. Справедливости ради отметим, что в *nix-like операционных
системах до сих пор господствует система хранения настроек во
всевозможных конфигах.
Новичков — системных администраторов при начале работы с реестром
старшие товарищи пугают, что неправильная настройка и изменение
параметров реестра могут напрочь завалить систему с последующей ее
переустановкой. И это действительно так.
К примеру, так называемые точки восстановления — это копии реестра.
Они широко применяются пользователями при возникновении различных
проблем как с операционной системой, так и с программным и аппаратным
обеспечением.
Надо сказать, что 99% информации о реестре Windows — это описание
основных ключей плюс советы, как с ними работать. Но как работает с
реестром сама операционная система? И сможем ли мы эмулировать ее
действия? Давай немного порассуждаем.
Ну и что?
Реестр — одновременно и сильная и слабая сторона Windows. Сильная
сторона реестра в том, что для разработчиков программного обеспечения
отпадает необходимость манипулировать туевой хучей конфигов, как это,
например, реализовано в никсах. Удобен реестр и для создателей
COM-компонентов — система автоматом регистрирует такой компонент в
реестре и облегчает задачу по его дальнейшему использованию.
Слабость реестра в том, что доступ к модификации чувствительных
областей реестра позволяет управлять Windows любой программе, написанной
каким-нибудь новоявленным малварщиком. Вспомни хотя бы самую знаменитую
ветку реестра Windows, позволяющую запускать программы на старте ОС :).
Если в Windows 98 реестр могли починять все, кому это взбредет в
голову, то начиная с Windows XP доступ к реестру имеют только
пользователи с учетной записью администратора. В Vista+ доступ к реестру
находится под защитой UAC. Оно и понятно.
Надо признать, что с выходом Win7 концепции безопасности при работе с
реестром были пересмотрены в лучшую сторону. Например, под защитой
находится ключевая ветвь реестра HKEY_LOCAL_MACHINE. В общем случае
попытка что-то записать в нее будет перенаправлена в соответствующую
ветку HKEY_CURRENT_USER для текущего пользователя.
Интерфейс
Для работы с реестром напрямую Windows предлагает программисту целый
набор WinAPI, которые должны быть знакомы любому системному
разработчику, — это Reg*-функции, такие как RegOpenKey, RegQueryValue и
так далее. В ядре Win это NtOpenKey, NtQueryValueKey и целый ряд других.
Описывать их особого смысла нет — всю документацию по надлежащему
использованию этих функций можно найти в MSDN.
Здесь стоит отметить вот что. Антивирусы и проактивки для контроля за
пользовательскими действиями устанавливали перехваты на упомянутые
функции, как в ядре, так и в юзермоде.
С выходом Win7 x64 ситуация изменилась, и я уже об этом как-то писал.
Разработчики Windows решили отказаться от возможности перехватывать
потенциально опасные функции в ядре Win. Теперь переменная
KeServiceDescriptorTable в x64 больше экспортируется, да и переписать
нужный участок кода не выйдет — PatchGuard не даст. Есть, конечно,
садомазохистские решения по обходу этих ограничений — но там гемора
будет больше, чем профита. Тем более что Microsoft предлагает удобные
колбеки ObRegisterCallbacks для контроля за реестром.
INFO
Информации в Сети о структурах, описывающих основные файлы реестра, очень мало.
И почти все они на английском. Начальные знания можно найти здесь.
Кроме этого, хорошо про реестр написано в библии системщика «Внутреннее
устройство Windows» от товарищей М. Руссиновича и Д. Соломона.
А теперь — о самом интересом
Но что же такое реестр на самом деле? Если заглянуть в папку
WINDOWS\system32\config, то можно увидеть там несколько файлов: system,
software, security, SAM и несколько других.
Это файлы реестра.
Однако несправедливо будет говорить о реестре просто как о некоем
сочетании файлов, загруженных в память. Многое из того, что содержит
реестр, носит динамический характер, то есть ряд значений высчитывается
на этапе загрузки самой системы, в первую очередь это касается
определенных параметров железа. Например, таков подраздел реестра
HKEY_DYN_DATA, данные которого при загрузке операционной системы
размещаются в оперативной памяти и находятся там вплоть до завершения
работы операционной системы. То же, кстати, можно сказать и о ключевом
подразделе HKEY_LOCAL_MACHINE, который не имеет своего соответствующего
файла на диске, но фактически формируется из других файлов реестра,
таких как software, system и прочие.
Таким образом, реестр изнутри можно весьма приблизительно назвать
«виртуальным сочетанием файлов реестра». После старта системы эти файлы
находятся как в файле подкачки (paged pool), так и в невыгружаемой
памяти (nonpaged).
Структура реестра
Для того чтобы научиться работать с реестром напрямую, без знаний его
внутренней структуры не обойтись никак. В целом Microsoft никогда не
раскрывала тайны внутренней структуры файлов, составляющих реестр,
поскольку это угрожает безопасности. По моим наблюдениям, все имеющиеся
описания файлов реестра и его структуры (а их, кстати совсем чуть-чуть) —
результаты изысканий пионэров-исследователей. Наиболее законченное, на
мой взгляд, такое «исследование» можно взять здесь, принадлежит оно товарищу Питеру Норрису.
Не будем вдаваться сейчас в подробности организации и структуры
реестра, дело это долгое, нудное и в рамки статьи точно не вписывается.
Здесь важно уяснить, что реестр — иерархическая древоподобная структура,
иногда также говорят, что она похожа на пчелиные соты.
И что со всем этим теперь делать?
Сразу огорчу: запросто пошаманить напрямую с реестром в юзермоде не
получится, система не даст этого сделать, как это обычно бывает с
файлами, занятыми другими процессами. Если попытаться извернуться, то
можно только прочесть такой «занятый» файл, и то если угадать с флагами,
с которыми он был открыт. К сожалению, записать в интересующий нас
«файл реестра» информацию не выйдет. Кстати, фича с записью нужной
информации в реестр может прокатить, если писать в реестровские
*.BAK-файлы, они точно доступны под запись.
А вот из ядра не то что прочитать, но и записать нужную информацию в файлы реестра очень даже можно.
Итак, следи за рукой :).
Первое, что может прийти в твою светлую голову, — открыть файл реестра напрямую и что-то туда записать.
Теоретически так сделать можно, для этого нужно, во-первых, уметь
работать с «занятыми» файлами (способы ищи в Сети) и, во-вторых, как я
уже говорил выше, надо знать внутреннюю структуру файлов реестра. Метод
этот довольно топорный, но, несмотря на свою бредовость, он вполне
жизнеспособен, хотя его и трудно реализовать на практике (попробуй
поэкспериментировать с ним самостоятельно).
Здесь же я предложу два способа, которые помогут тебе распилить реестр на мелкие кусочки.
Первый способ заключается в том, что для конфигурационного менеджера
(Configuration Manager, часть операционной системы, если ты не в курсе)
реестр есть не более чем набор строго определенных структур в
операционной памяти, с которыми, как оказывается, очень даже легко
работать. Какие это структуры, спросишь ты? HBASE_BLOCK, HHIVE, HBIN,
HCELL, HMAP_ENTRY, HMAP_DIRECTORY, куча CM_* структур, используемых
конфиг-менеджером для управления реестром. С точки зрения операционной
системы, реестр — это просто набор регламентированных структур в
оперативной памяти. К примеру, сигнатура «regf», определяющая «файл
реестра», есть заранее определенная константа:
То есть смысл всего этого моего монолога в том, что существует
шикарная возможность манипуляции с реестром на уровне операционной
системы, но при этом не используя ее штатные средства. Как это возможно?
Мы просто сэмулируем действия самой операционной системы, точно так,
как она сама работает с реестром! Важно, как я уже говорил, понять, что
для самой ОС реестр не более чем набор соответствующих структур в
памяти.
Если у нас будет доступ к файлам реестра на уровне ядра, то чем мы хуже самой ОС, чтобы установить свой порядок?
И тут на сцене появляется наиболее интересный вопрос — как найти эти
самые структуры в памяти? Верно, штатных средств системы для решения
этого вопроса нет, поэтому придется выкручиваться по-хитрому.
Зная, как выглядят структуры, нужно вспомнить, что каждый файл, улей
реестра, имеет свою константную сигнатуру. Например, «regf» — это
0x66676572. Для улья сигнатура будет равна 0xBEE0BEE0. Имея доступ к
памяти из ядра, мы можем довольно легко найти эти сигнатуры в памяти,
просто просканив ее. Еще можно просканить память в поисках сигнатуры
«CM10» — именно она присваивается конфиг-менеджером блоку подкачиваемой
памяти, который выделяется под структуру CMHIVE. Полагаю, найдя в памяти
интересующий нас элемент, ты придумаешь, что делать с ним дальше :).
Как, к примеру, изменить значение ячейки реестра? Значение хранится в
поле CM_KEY_VALUE->Data, поэтому, если у тебя возникнет задача
изменить какое-либо поле в конкретном ключе реестра, ищи значение именно
там:
typedef struct _CM_KEY_VALUE
{
WORD Signature; // #define CM_KEY_VALUE_SIGNATURE 0x6B76
WORD NameLength;
ULONG DataLength;
ULONG Data; //<---------- данные ячейки будут здесь
ULONG Type;
WORD Flags;
WORD Spare;
WCHAR Name[1];
} CM_KEY_VALUE, *PCM_KEY_VALUE;
Второй вариант является своеобразной модификацией первого. Если
знаешь, существует одна особенность при работе с реестром — все
изменения, то есть «создание новых ключей / запись / удаление ключей»,
как правило, вступают в силу после перезагрузки системы (ну или после
перезагрузки эксплорера, это такой хак-метод). До этого все изменения
находятся словно в подвешенном, «dirty»-состоянии. Мало того, система
при обращении с реестром общается с ним через кеш файловой системы. Это
понятно — обращений к реестру может быть сотни в секунду,
соответственно, полагаться при этом на быстродействие файловой системы
неразумно, тут никакое быстродействие не спасет. Поэтому система и
работает с реестром, что называется, виртуально, через кеш файловой
системы. И тут, чтобы вытащить кишки реестра на свет, надо залезть в
кеш! Как это делается, уже описывалось в тырнетах, в том числе и в www.xakep.ru.
Pro & Cons, или вместо заключения
Что сказать в итоге? Предложенная читателю в статье вариация на тему
прямого контроля за реестром носит исключительно экспериментальный
характер. Не спорю, она тяжеловата для практической реализации, и многие
скажут, что уж лучше использовать нормальные WinAPI-функции,
предназначенные для работы с реестром, — и будут в чем-то правы. Однако
реализованная die_hard на деле либа, основанная на приведенных в статье
принципах, будет обладать поистине термоядерной силой, неподвластной ни
аверам, ни самой операционной системе.
Засим закончу. Удачного компилирования и да пребудет с тобой Сила!
WWW
Обязательна к прочтению статья Марка Руссиновича о реестре «Inside the Registry», нашелся даже русский перевод.
Замечательная тулза для сбора информации о реестре: http://goo.gl/iSSVy.