bootstat.dat

Метки:  , , , , ,

Намедни задался вопросом, при каких условиях появляется экран утилиты восстановления загрузки (Startup Repair). Восстановление загрузки это автоматизированное средство, которое обнаруживает большое количество общих проблем загрузки Windows и пытается их устранить в автоматическом режиме. Данная функциональная особенность последних версий Windows зачастую достаточно серьезно достает пользователей, поскольку в ожидании окончания процесса восстановления можно провести не один десяток минут, полностью выпадая из рабочего процесса на достаточно продолжительное время. Сама утилита восстановления конечно же интересна, но это тема отдельной статьи, а на тот момент мне просто хотелось выяснить по каким же условиям она стартует. Было выдвинуто предположение, что экран появляется лишь при внезапном выключении питания. Данная версия сразу была поставлена под сомнение, поскольку в памяти у меня всплыли примеры ситуаций, когда пользователи жаловались на автовосстановление загрузки даже при нормальном (с виду) завершении работы. С целью выяснить причины запуска средства восстановления загрузки решено было провести небольшой эксперимент. Хочу сразу оговориться, что рассматривать данный материал следует применительно к операционной системе Windows 7 SP1, поскольку все тесты проводились именно на этой конфигурации, поэтому учтите, что в других версиях ситуация могла кардинально поменяться.

Начать поиски я решил в исходном коде распакованного файла bootmgr.exe, всё же как-никак самый что ни на есть начальный этап загрузки. Необходимой информации по теме в Сети я найти так и не смог, а с чего то надо было начинать! Памятуя, что в различных окнах английских версий системы я видел названия recovery tool и startup repair, я просто решил поискать по словам "recovery" (восстановление) и repair (исправление, ремонт), однако простым поиском по исходнику мне ничего подобного обнаружить не удалось, в файле не было упоминаний ни про какое восстановление. Далее я перешел к исходному коду дизассемблированного Winload.exe всё в той же IDA и просто решил поискать всё то же слово "recovery". В листинге файла мной была найдена переменная, автоименованная дизассемблером как aRecoveryTrue, которая имела значение recovery=true и на которую в коде ссылались две функции: OslpDisplayAutoAdvMenu и OslpDisplayAdvancedBootMenu. Должно быть это опция в конфигурации загрузки, по которой код Winload.exe, судя по всему, должен быть производить некие действия по восстановлению работоспособности. Предположим что это ОНО! Опираясь на некоторые внутренние критерии я отбросил вторую функцию и сконцентрировался на функции с именем OslpDisplayAutoAdvMenu, которая вызывалась из OslpDisplayAdvancedOptionsProcess, а та, в свою очередь, вызывалась из основной функции OslpMain.

Дополнение: Функция OslpDisplayAdvancedOptionsProcess интересна тем, что через неё происходит вызов функции OslpDisplayAdvancedBootMenu ,которая выводит дополнительные сервисные меню, в том числе и меню восстановления. Просматривая её код, я наткнулся на запрос опций по кодовому обозначению BCD элемента 14000008h. Оказалось, что данный идентификатор носит имя BcdLibraryObjectList_RecoverySequence (BCDE_LIBRARY_TYPE_RECOVERY_SEQUENCE) или recoverysequence и содержит список идентификаторов GUID, указывающих на элементы загрузки среды восстановления. Запустив команду bcdedit /enum all я получил полный список идентификаторов, размещенных в хранилище данных конфигурации загрузки, среди которых мне встретилось и символическое имя recoverysequence и связанный с ним идентификатор 8be0cdd3-7c4b-11e3-b403-24be05175d79, который указывал на путь [C]:\Recovery\8be0cdd3-7c4b-11e3-b403-24be05175d79\.... По этому пути на основном системном диске располагается файл winre.wim, который и представляет собой среду восстановления. Теперь хотя бы понятно, где искать код восстановления. :)

Но, вернемся к основной линии повествования. Так вот, чуть выше по коду в функции OslpMain я обнаружил некие условия перехода, по которым либо функция OslpDisplayAdvancedOptionsProcess получает управление, либо нет. Упомянутые условия являются результатом работы с выходными параметрами функции OslpGetBootStatusData, которая, судя по моим догадкам и названию, получает и проверяет некий статус загрузки. В самой функции OslpGetBootStatusData вызывается функция OslpGetSetBootStatusData, которая, судя по всему, и оперирует с неким источником статуса. Так что же является источником статуса? Начав просматривать код функции, я обнаружил указание на файл bootstat.dat, и, поскольку файл этот попадался мне довольно часто, то вспомнил, что знания мои о нем ограничивались лишь пониманием, что это файл, в котором хранится некий статус загрузки, более ничего о нем я не знал. В Сети исчерпывающего описания по нему я не встретил, поэтому попытаюсь предложить собственное определение:

Boot Status Data (BSD) или Boot Status File - Данные статуса загрузки, которые содержат информацию о прохождении системы через различные стадии жизненного цикла, включая стадии загрузки и завершения. Эти данные используются для обнаружения ошибок этапов загрузки/завершения операционной системы для предоставления пользователю вариантов восстановления на этапе загрузки или автоматического запуска среды восстановления. Представляет собой ГЛОБАЛЬНЫЙ СТАТУС всех режимов загрузки-выключения. Не путать с BCD!.

В дополнение ко всему, как будет показано далее, файл статуса загрузки содержит разнообразную дополнительную информацию. Если мне не изменяет память, файл появился еще в операционной системе Windows XP, однако значимость его для системы со временем явно менялась.
Я вспомнил, что Bootmgr тоже работает с данным файлом, во всяком случае у меня в памяти осталось стойкое на то ощущение. Вернувшись к коду Bootmgr и поискав файл bootstat.dat, я удостоверился, что к нему действительно происходит обращение через функцию BmpInitializeBootStatusDatalog, то есть он начинает использоваться уже до этапа Winload.exe, на этапе Bootmgr. В коде функции BmpInitializeBootStatusDatalog вызывается:

  • функция _BlGetBootOptionDevice@16 с идентификатором BCD элемента 11000043h, который указывает на устройство.
  • функция _BlGetBootOptionString@12 с идентификатором BCD элемента 12000044h, который указывает на путь к файлу.
  • функция _BlGetBootOptionBoolean@12 с идентификатором BCD элемента 16000045h, который указывает на то, сохранились ли записи BSD с предыдущей сессии или BSD обнуляется при каждой сессии.

Эту логику можно отследить по следующему фрагменту кода:

Получается что-то вроде этого:

Элемент BCD Формат Значение
0x11000043 Устройство Устройство, на котором находится BSD файл.
0x12000044 Строка Путь к файлу BSD.
0x16000045 Логическая переменная Истина - сохранились BSD записи от предыдущей сессии;
Ложь - BSD файл обнуляется при каждой сессии.

Получается, что записи о файле bootstat.dat хранятся в базе конфигурации загрузки (BCD).

Но вот меня насторожило, в то время как Winload.exe обращается к файлу bootstat.dat без указания явного пути, со ссылкой на некое устройство загрузки, то вот код Bootmgr обращается к \boot\bootstat.dat с указанием полного пути.

Опять же, это может ни о чем не говорить, я просто мог ошибиться с выводами, поскольку глубина понимания кода у меня небольшая. И тем не менее, после обнаружения этой особенности в сознание закралось смутное подозрение на счет того, что коды различных стадий загрузки используют разные файлы bootstat.dat. После этого я решил поискать файл в других расположениях и нашел его в %SystemRoot%\bootstat.dat. Становится интереснее!! Требовалось подтверждение, с этой целью был использован Process Monitor с активированной опцией "Enable Boot Logging" и по результатам записи перезагрузки были отфильтрованы события с целевым значением "bootstat":

файл bootstat.dat

Глядя на скриншот можно было говорить что это определенно разные файлы :), и что можно сделать два предположения:

  • Файл \boot\bootstat.dat, размещенный на отдельном разделе System Reserved, используется только модулями стадий загрузки и служит для передачи сведений о состоянии между различными фазами загрузки.
  • Файл %SystemRoot%\bootstat.dat используется компонентами операционной системы на различных этапах загрузки/завершения.

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

Структура bootstat.dat

Мне наиболее интересен был файл, относящийся к системе, а не к этапу загрузки. Поэтому я решил начать именно с файла %SystemRoot%\bootstat.dat. Ну и первое что мы сделаем, так это уже наконец-то посмотрим на содержимое файла:

bootstat.dat содержимое

Вся прочая информация, которая содержится в файле - это нули. Да, информации в файле совсем немного, присутствует совсем миниатюрный блок в самом начале (скорее всего заголовок) и где-то в теле блок поувесистее (скорее всего записи). Внутренняя структура файла bootstat.dat не документирована, поэтому разобраться в том, какие именно параметры присутствуют в файле было проблематично. Однако на просторах Сети я обнаружил материал по структуре файлу bootstat.dat, который хоть как-то приоткрывал завесу неизвестности относительно внутреннего устройства. Хотя автор тоже делал всего-лишь предположения, уже можно было на что-то опереться. Имелась небольшая загвоздка, дело в том, что автор описал свои предположения относительно структуры файла по отношению к файлу \boot\bootstat.dat, тогда как структура файла %SystemRoot%\bootstat.dat, судя по всему, некоторым образом должна отличаться, надеюсь что не сильно.

Заголовок

Первые 10h (16) байт предположительно являются заголовком:

Смещение Размер Значение
0x00 dword Предположительно версия файла:

  • 02 - \boot\bootstat.dat - версия для Bootmgr;
  • 20 - %SystemRoot%\bootstat.dat - версия системная;
0x04 dword Предположительно какой-то идентификатор:

  • 10 - для версии Bootmgr;
  • 01 - для системной версии;
0x08 dword Было предположение, что это размер всего файла. Однако сомнительно что он занимает 4 байта.

  • 00010000 - для Bootmgr версии, размер 65536.
  • 00011E00 - для системной версии, размер 67584.
  • 01011E00 - для системной версии, размер 67584.
0x0C dword Размер актуальных данный в файле (байт). Размер включает все данные вместе с заголовком и всеми записями. Значение 00000000 - файл не содержит данных.

Записи

Каждая запись начинается с заголовка за которым следуют непосредственно данные записи.

Смещение Размер Значение
0x00 dword Метка времени (секунды). Предположительно время, прошедшее с момента запуска, либо с начала дня, в котором ОС начала загрузку.
0x04 dword Всегда ноль. Неизвестно.
0x08 16 байт GUID владельца записи. Все нули - владельцем записи является Bootmgr;
0x18 dword Размер записи (байт). Начиная от временной метки и заканчивая последним байтом данных текущей записи.
0x1C dword Категория записи. 01 - информация, 03 - ошибка.
0x20 dword Какой-то идентификатор. Встречаются значения 02 и 03.
0x24 dword Идентификатор события. См. таблицу ниже.
0x28 варьируется Дополнительные данные. Размер зависит от идентификатора события.

Идентификатор события может принимать следующие значения:

Идентификатор Событие Следующие за идентификатором данные
0x01 Инициализация приложения. см. таблицу ниже.
0x11 Приложение запущено. см. таблицу ниже.
0x12 Приложение завершено. см. таблицу ниже.
0x13 Ошибка загрузки приложения стадии загрузки Код NTSTATUS (4 байта), путь к приложению, завершающий 0.
0x14 Ошибка в файле BCD (данные конфигурации загрузки) Код NTSTATUS (4 байта), путь к файлу BCD, завершающий 0.
0x15 Нет записей для приложения этапа загрузки в файле BCD. Код NTSTATUS (4 байта), путь к файлу BCD, завершающий 0.
0x16 Общая ошибка Код NTSTATUS (4 байта).
0x31 Завершение приложения. Данные отсутствуют. Данные записи тут заканчиваются.

Размерность NTSTATUS равна 4 байта. Путь к файлу записывается в кодировке UTF-8 и заканчивается 0. Для общей ошибки, тот же код NTSTATUS имеет размерность 4 байта.

События инициализации

Дополнительные данные для всех событий инициализации имеют следующий формат:

Смещение Размерность Значение
0x00 16 байт Время (структура):
0x00 :: word :: год
0x02 :: word :: месяц
0x04 :: word :: день
0x06 :: word :: час
0x08 :: word :: минуты
0x0A :: word :: секунды
0x0C :: word :: всегда 0, неизвестно.
0x0E :: word :: всегда 7, неизвестно.
0x10 dword всегда равно 1, назначение неизвестно.
0x14 dword всегда равно 0, назначение неизвестно.

События запуска

Данные записи для запуска приложения имеют следующую форму:

Смещение Размерность Значение
0x00 16 байт GUID приложения стадии загрузки.
0x10 dword Тип запуска.

  • 0 - штатный запуск.
  • 1 - режим восстановления.
  • 2 - режим восстановления.
0x14 варьируется Путь до приложения стадии загрузки, формат UTF-8, заканчивается 0.

События завершения

Актуально только для приложений этапа загрузки, для обычных приложений я не рассматривал. Когда приложения завершилось (в результате успешной работы, сбоя или отмены) данные в записи следующие:

Смещение Размерность Значение
0x00 16 байт GUID приложения этапа загрузки.
0x10 dword всегда 0, назначение неизвестно.

Основываясь на наших предположениях, в имеющемся у нас в наличии файле C:\Windows\bootstat.dat присутствуют две записи. Маловато как-то для глобального файла статуса загрузки/завершения, не находите?

Эксперимент

После того, как мы предположили внутреннюю структуру файла, хорошо бы попробовать на практике произвести какие-нибудь изменения в файле и посмотреть на результат. Я начал с файла, расположенного в C:\Windows\bootstat.dat и попытался изменить уже существующую запись (начинающуюся по смещению 00000810), отредактировав её категорию на значение 03 (ошибка), в дополнение к этому я изменил в заголовке значение размера актуальных байтов с нулевого на значение, включающее все существующие записи. Результат был отрицательным, изменения не произвели никакого эффекта, загрузка произошла в нормальном режиме. Затем я решил создать новую записи после двух уже имеющихся, за эталон я взял уже существующую запись и изменил в ней время, категорию записи на 03 (ошибка), идентификатор события на 16 (общая ошибка), подправил длину записи. Перезагрузка привела к аналогичному отрицательному результату, загрузка прошла в нормальном режиме, никаких проблем система не обнаружила. Далее я вовсе решил удалить файл C:\Windows\bootstat.dat. Удивило меня то обстоятельство, что загрузка опять прошла в штатном режиме, а файл так и не появился. Выходит, его никто и не создал. Удаление я проводил на машине со стандартной PCAT (MBR) загрузкой. После этого я уже совсем распоясался и удалил для полноты ощущений еще и файл \boot\bootstat.dat из несмонтированного системного раздела. И тот же результат: загрузка прошла нормально, файлы не появились. Как же так? Я помню, что в своё время повреждение файла bootstat.dat доставляло множество проблем не только в Windows XP и Vista, но даже и в начальных версиях Windows 7? Неужели на каком то этапе разработки Windows 7 (SP1 версии 6.1.7601) разработчики пересмотрели функциональные характеристики данного файла и он более не играет ключевой роли? Но ведь код в системе в многочисленных модулях сохраняется. Это пока для меня загадка, вероятно я упустил некоторые особенности. Вероятно, файл начинает использоваться при включении каких-то компонентов? Мы так и не получили ответ на основной вопрос статьи: по каким именно критериям система запускает автовосстановление запуска? Цель не была достигнута.
Прорабатываемые предположения:

  • Файл не имеет ключевой роли в качестве триггера проблем жизненного цикла системы;
  • Код Bootmgr/Winload реагирует ТОЛЬКО на определенные значения определенных параметров в файле bootstat.dat, которые я не догадался поменять.

Однако, дальнейшие эксперименты, проводимые уже после написания данной статьи, натолкнули меня на мысль, что инициирование запуска среды восстановления загрузки может происходить как по условиям в коде Winload.exe, так и в коде сторонних модулей, которые могут модифицировать файл BCD через какой-нибудь API, фактически выставляя опцию восстановления выбором по умолчанию при загрузке системы, тем самым инициируя запуск средства без вмешательства пользователя. Осталось отыскать эти условия. Тема не закрыта!

  • Поделиться:

5 комментариев:

  1. На самом деле заголовок файла bootstat.dat описывается следующей структурой, по крайней мере для XP:
    typedef struct _BSD_FILE_HEADER
    {
    ULONG VersionNumber;
    NT_PRODUCT_TYPE ProductType;
    BOOLEAN AabEnabled;
    USHORT AabTimeout; // 30 секунд
    BOOLEAN BootGood;
    BOOLEAN BootShutdown;
    } BSD_FILE_HEADER, PBSD_FILE_HEADER;
    Которое по сути является более полным описанием перечисления RTL_BSD_ITEM_TYPE. Для последующих версий заголовок просто немного разбухает после добавления новых значений, например, в восьмёрке там от 9 до 10.
    Размер же файла либо не входит в заголовок, либо идёт последним, так как оно записывается в других местах.

    • einaare

      просто мегаценная для меня информация, спасибо вам огромное!! а есть источник на почитать? ну, какой-нибудь материал в Сети?

      • Я и есть источник, сам только что нашёл))
        Вообще для реверса ядра рекомендую посматривать на WRK, отладочные сборки самой винды и ReactOS. В WRK в файле ntoswrk.lib предварительно собранные части. Их в отличии от ядра реверсить несколько проще, так как в нём во-первых все функции разбиты по объектным файлам, имена которых соответствуют имени файла в исходных кодах винды, а во-вторых, внешние символы в объектниках ещё не определены, и чётко видны в IDA. Конкретно эту информацию я отрыл в bootstatus, функция RtlCreateBootStatusDataFile. В отличии от кода РеактОС и релизных сборок винды, тут чётко видно, что по смещению 0х04 от начала заголовка записывается результат вызова RtlGetNtProductType. Посмотрев на файл от сервера, я там увидел закономерную двойку. Так же в функции RtlGetSetBootStatusData есть массив смещений и размеров структур, с которым идёт сверка при чтении/записи в этот файл. Сверив эти смещения с существующими определениями, где хоть как-то встречается упоминание ProductType, я и наткнулся на структуру RTL_BSD_ITEM_TYPE, которая идеально легла на смещения. А подтверждением послужило то, что по смещению AabTimeout в файле bootstat.dat находится значение 1E, то есть стандартные 30 секунд.
        только я ненастоящий реверсер, я маску нашёл я лишь обложился всеми найденными исходниками винды и IDA.
        Заглядывайте в тред на дваче https://2ch.hk/pr/res/1015083.html я его веду. Хочу как-нибудь выложить результаты моего труда, но гитхаб, боюсь, такое нафиг снесёт ))

        • einaare

          ну, WRK для меня вообще открытие :))) Как говорится, век живи - век учись!! Исходники ReactOS встречал и по ним как раз некоторые моменты стали понятны. Спасибо, как будет время может займусь осмыслением.

    • Кстати тут USHORT читать как UCHAR, заметил только после компиляции.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *