Дело о неработающем блокноте

Пару недель назад разработчикам Майкрософт нанес визит Дейв Соломон (Dave Solomon) - ведущий семинара по внутреннему устройству ОС Windows. До перехода в Майкрософт я учил уму-разуму специалистов корпорации вместе с ним, но теперь в связи с появлением дополнительных обязанностей ограничиваюсь одним-двумя выступлениями - если, конечно, позволяет график. В этот раз я вещал на тему безопасности - конкретнее, о моделях входа в систему (проверки подлинности) и проверки доступа (авторизации). Отдельным пунктом мы обсуждали функцию контроля учетных записей (User Account Control, UAC), реализованную в ОС Windows Vista. Она совмещает сразу несколько технологий, среди которых - виртуализация и новая модель обязательного контроля целостности (Mandatory Integrity Control, MIC), работающая «поверх» существующей модели управления доступом на уровне пользователей, которая впервые появилась еще в первом выпуске ОС Windows NT.

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

  1. Файл манифеста Windows Vista в составе исполняемого образа с указанием желательности или необходимости получения прав администратора (такой манифест внедряется разработчиком образа).
  2. Наличие записи исполняемого файла в базе данных совместимости приложений ОС Windows Vista, относящей его к числу устаревших приложений, для корректной работы которых необходимы права администратора.
  3. Явный запрос на повышение прав, поданный пользователем с помощью команды Run as administrator (Запуск от имени администратора) в контекстном меню проводника для исполняемых файлов (эту команду также можно вызывать через ярлык с дополнительным свойством). Отмечу, что при вызове этой команды исполняемый файл запускается не в контексте учетной записи администратора, а в рамках учетной записи текущего пользователя с разрешенными маркером безопасности процесса правами группы администраторов.
  4. Идентификация исполняемого файла как программы установки (например, по наличию слов “setup” или “update” в имени образа).

Чаще всего потребность в получении прав администратора испытывают программы установки, которые. Они, как правило, не могут провести корректную установку без доступа к ветви реестра HKLM\Software и каталогу \Program Files, права записи в которых предоставляются только администраторам. Намереваясь продемонстрировать последний из перечисленных механизмов, во время своей презентации я скопировал файл \Windows\Notepad.exe в каталог профиля своей учетной записи, переименовав его в Notepad-setup.exe. Запустив переименованный файл, я ожидал увидеть диалоговое окно подтверждения запуска программы (вроде того, что изображено на иллюстрации), позволяющее наделить блокнот правами администратора.

clip_image001

К моему немалому удивлению, диалоговое окно не появилось. Точнее, не произошло вообще ничего. Повторив попытку, я получил тот же результат. Озадаченный, но не имеющий возможность исследовать причину такого поведения в присутствии слушателей, я приступил к дальнейшему изложению.

Когда через некоторое время у меня появилась возможность разобраться в произошедшем, я запустил файл Notepad-setup.exe через отладчик Windbg (входящий в бесплатный набор инструментов отладки для Windows). Для этого я сначала выбрал пункт Open Executable (Открыть исполняемый файл) в меню File (Файл), а затем запустил команду Debug (Отладка) > Go (Выполнить) (ее также можно вызвать нажатием клавиши F5). В ходе отладки я изучил первые инструкции в точке входа блокнота, Winmain. Как выяснилось, в ней вызывается функция инициализации NPInit, которая посредством функции LoadAccelerators загружает клавиатурные ускорители блокнота. Как ни странно, при выполнении функции LoadAccelerators происходил сбой, в результате чего функция NPInit возвращала функции Winmain ошибку, и блокнот закрывался без предупреждения. Возникает вопрос: почему блокноту не удалось загрузить ускорители, которые, по идее, должны быть включены в его собственный образ?

Далее мне нужно было выяснить, играет ли какую-либо роль переименование файла. Я попытался запустить файл с первоначальным именем Notepad.exe из каталога профиля, но поведение не изменилось. Оставалось обратиться к программе Filemon.

Нужно было зафиксировать операцию успешного исполнения блокнота и сравнить ее с запуском, вызывающим сбой. Я открыл программу Filemon, настроил в фильтрах включение процесса Notepad.exe и исключение процессов, которые при запуске блокнота ссылаются на его образ - в частности, Svchost (где выполняется предвыборка) и проводник (при помощи которого я запускал блокнот).

clip_image002

После записи двух трассировок и перед их сравнением нужно было удалить столбцы, содержимое которых всегда различается в любых двух трассировках исполнения: Sequence (Последовательность), Timestamp (Отметка времени) и Process (Процесс). Для этого я загрузил трассировки в редактор Excel, выделил данные в первых трех столбцах, удалил их и сохранил файлы в виде текста с разделителями-символами табуляции. Кстати говоря, эти два файла можно загрузить отсюда.

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

Первые отмеченные программой Windiff строки соответствуют чтению блокнотом файла предвыборки. Имена этого файла в трассировках разные, поскольку полный путь к образу блокнота хэшируется.

clip_image003

Далее отмечаются операции, имеющиеся только в трассировке успешного выполнения блокнота. По-видимому, это запросы к некоему глобальному кэшу ресурсов Windows, появившемуся в ОС Windows Vista.

clip_image004

Поскольку для меня было не вполне понятно, почему в одном случае ссылки на кэш есть, а в другом - нет, я продолжил изучение различий. Расхождения в строках 47-51 объяснялись тем, что двум копиям блокнота соответствуют разные пути.

clip_image005

Наконец, в строке 121 я обнаружил нечто, похожее на источник проблемы.

clip_image006

При исполнении файла \Windows\Notepad.exe производится успешное чтение данных из файла Notepad.exe.mui, находящегося в подкаталоге \Windows\En-us. Далее, в строке 172, трассировка неудачного запуска блокнота фиксирует попытку чтения файла с тем же именем из подкаталога En-us, но в силу того, что этот подкаталог не найден, операция завершается ошибкой:

clip_image007

Известно, что в файлах с расширением MUI хранятся языковые ресурсы, в том числе строки и ускорители, поэтому мне стало ясно, что неспособность блокнота загрузить ускорители связана с тем, что соответствующий файл ресурсов для моего региона (US English (En-us)) был не найден. Чтобы проверить эту версию, я создал в каталоге своего профиля подкаталог En-us, скопировал в него файл Notepad.exe.mui, запустил из каталога профиля блокнот, и все пошло как по маслу.

В предыдущих версиях ОС Windows файлы MUI служили для выделения языковых данных из исполняемых файлов, но я не знал, что в ОС Windows Vista эта возможность доступна для приложений. Поддержка файлов MUI хороша тем, что функции, работающие с ресурсами, такие как LoadAccelerators и FindResourceEx, берут на себя взаимодействие с языковыми файлами ресурсов, и для работы с ними разработчикам приложений не нужно писать дополнительный код.

Заставив блокнот работать вне каталога Windows, я решил разобраться, почему при его запуске на экран не выводится диалоговое окно подтверждения, которое позволило бы мне предоставить блокноту права администратора. Как мне удалось установить эмпирическим путем (и впоследствии подтвердить свои выводы после прочтения статьи «Понятие и настройка контроля учетных записей в ОС Windows Vista» на веб-узле Microsoft.com), эвристическое распознавание установочных файлов осуществляется только применительно к файлам, не имеющим встроенного манифеста с указанным уровнем доверия (TrustLevel). В блокноте, как и во всех прочих исполняемых файлах ОС Windows Vista, такой манифест есть. Чтобы ознакомиться с ним, достаточно сбросить на консоль строки блокнота при помощи служебной программы Sysinternals Strings.

clip_image008

Итак, благодаря программе Filemon дело о блокноте, который отказывался запускаться, удалось раскрыть!