Зависает на classpnp.sys

Метки:  , , , , , , , , , ,

Данный материал представляет собой своего рода исследование, эдакую незавершенную попытку разобраться в одной, набившей уже оскомину, загадке этапа загрузки операционной системы Windows. Многие специалисты согласятся со мной, что на практике довольно часто приходится наблюдать ситуацию, в которых станция, загружаясь в безопасном режиме отображает на экране монитора список загруженных драйверов (последним из которых часто является classpnp.sys) и сообщение "Подождите, пожалуйста...", после чего благополучно подвисает:

classpnp.sys

При этом, та же проблема имеет зеркальное отображение и в обычном режиме загрузки: в редких случаях загрузка останавливается на анимированном логотипе (значке) Windows (bootscreen), который может "видоизменяться" на экране приветствия бесконечно долго:

bootscreen

чаще же либо система может подвисать на более позднем этапе, после переключения в графический режим, соответствующий "родному" разрешению монитора:

black screen of death

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

Подопытная конфигурация: Windows 7 SP1 Professional RUS x86, 32-битная выбрана как наименее капризная в плане препятствий, чинимых на пути исследователя, защита у неё определенно проще, патчить легче, бороться с PatchGuard не приходится!! :)

Теория

На данный исторический момент уровень моих знаний оставляет желать лучшего, тем не менее, все же попытаемся выйти за привычный ареал обитания, распахнуть, так сказать, рамки собственного познания, тем самым устремившись навстречу эволюции :) В очередной раз не перестаешь удивляться отсутствию у одной из самых популярных настольных операционных систем продуманной диагностической подсистемы. Удивляюсь, конечно же, не я один.. на официальных форумах Microsoft по теме зависания на classpnp.sys можно встретить достаточно много вопросов, к примеру, пользователь с ником Дима_413 пишет:

classpnp.sys freeze

Странно вот что, ведь на ранних стадиях загрузки операционной системы (начиная с кода сектора MBR, Bootmgr) уже обеспечена простая диагностика, которая выводит сообщения в текстовом режиме в качестве реакции на ошибки. Получается, что отсутствие подобного подхода при зависании на classpnp.sys наблюдается по причине возникновения блокировок в коде ядра в части обработки ошибок/таймаутов от сторонних драйверов [устройств] на раннем этапе загрузки. Поэтому:

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

Во всех ветках обсуждений на тему зависания на classpnp.sys, найденных в Сети, все обычно сводится к рассуждениям относительно природы этого драйвера, структуры, функциональных задач, роли, которую он играет в процессе загрузки и, главное, как можно привести его (обратно) в работоспособное состояние? Очевидно что драйвер уже однозначно определен в виновники происходящего зависания, при том, что во всех дискуссиях начисто отсутствует практическая исследовательская часть, что, конечно же, понижает объективность данных теорий. Ну что же, давайте и мы последуем в рамках описанного "слепого" тренда и начнем повествование с попытки описания функциональных особенностей драйвера.

Что есть сам classpnp.sys?

Classpnp - общая библиотека класса устройств хранения информации (жестких/твердотельных накопителей, ленточных устройств, CD/DVD-ROM'ов и аналогичных), используемая драйверами жестких дисков, CD/DVD-ROM, ленточных накопителей (включая SATA/SCSI-накопители).

Из определения следует, что любой драйвер, относящийся к классу устройств хранения информации (магнитных, ленточных, оптических), использует функции данной библиотеки в процессе функционирования. Действительно, образ представляет собой типовую системную библиотеку (DLL), содержащую набор устройствонезависимых процедур (функций), используемых классом отмеченных ранее устройств. Похоже что данная библиотека - это библиотека, функции которой используются всеми драйверами накопителей (устройств хранения) в операционной системе Windows. Библиотека обеспечивает для системных/сторонних драйверов уровня ядра общие процедуры работы с устройствами хранения на уровне обслуживания пакетов IRP, поддержки функций PnP и управления питанием, выполнения общих алгоритмов работы с памятью, чтения, записи, инициализации, конфигурации устройств, работа с уровнями IRQL, обработку ошибок и много прочей аналогичной необходимой требухи.
В Windows XP и последующих операционных системах, некоторые из наиболее часто используемых сервисов, ранее предоставляемых прямыми вызовами к библиотеке classpnp.sys, теперь предоставляются драйвером класса. Поэтому в Windows XP (и более поздних системах) обычно нет необходимости прямого вызова функции classpnp.sys из сторонних минипорт-драйверов.

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

Ну что же, бегло ознакомились с функциональными особенностями виновника торжества? Теперь нам необходимо разобраться со структурой данного "драйвера", понять как и когда он загружается и загружается ли вообще, памятуя что это всё-таки библиотека?

Общая теория загрузки

Перед тем как понять, какое же место classpnp.sys занимает в системе, нам необходимо освежить в памяти общую теорию загрузки. Из неё мы знаем, что весь процесс запуска операционной системы Windows 7 на начальных стадиях упрощенно можно описать следующим образом:

  • Выполняется код микропрограммы BIOS/UEFI (POST);
  • Выполняется код MBR;
  • Выполняется код PBR;
  • Выполняется код Bootmgr(.efi);
  • Выполняется код Winload.exe;
  • Выполняется код ядра (ntoskrnl.exe, ntkrnpa.exe и ...);
  • Выполняется код SMSS;
  • Выполняется код CSRSS;
  • Выполняется код WinLogon -> LSASS, services. LogonUI;
  • Запускается копия SvcHost;
  • Выполняется LogonUI -> UserInit -> выводится оболочка (Explorer);
  • Загружаются модули из Автозагрузки;

Есть примета, что если зависает на classpnp.sys - это к апгрейду :) Если серьезно, то за все время компьютерной практики относительно данной темы накопилось несколько наблюдений:

  1. В разные периоды практики удавалось понаблюдать очень похожие между собой сбои, когда возникали ситуации, в которых загрузка в безопасном режиме зависала не на самом драйвере classpnp.sys, а на следующих за ним, то есть этот драйвер не был последним в списке. Например, иногда загрузка вставала на agp440.sys).
  2. На нормально загружающихся в безопасном режиме системах периодически наблюдал картину, когда текстовый режим кончался на выводе строки classpnp.sys с надписью "Подождите, пожалуйста...", после чего присутствовала совсем уж короткая пауза и загрузка уходила в графический режим и продолжалась. Делаем выводы, что приведенная строка ожидания характеризует конец одного (визуально определяемого нами как "текстовый") этапа загрузки и переход к следующему (визуально "графическому").

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

Этап Winload

Где же впервые в коде модулей запуска встречается загрузка каких-либо системных или сторонних драйверов/библиотек? Очевидно что на этапе Winload.exe, поскольку именно в коде данного модуля впервые начинают загружаться системные драйвера с флагом BOOT_START. С целью анализа нам придется изучать исходный код, для этого расчехляем IDA и дизассемблируем код модуля Winload.exe. После продолжительного изучения алгоритмов можно прийти к выводу, что исполнение кода модуля начинается с точки входа в процедуре OslMain. Уже из этой процедуры вызывается дочерняя функция OslInitializeCodeIntegrity, которая проверяем целостность модулей, участвующих в загрузке. В коде основной функции встречается интересная вложенная функция под названием OslpLoadAllModules, которая используется разнообразным кодом для обеспечения загрузки системных модулей (они же - драйвера/библиотеки режима ядра). При этом, все модули, загружаемые через неё на начальной стадии, делятся на:

  • жестко заданные во внутренней переменной OslMicrosoftBootImages;
  • загружаемые уже при подключении и разборе ветви реестра HKLM\SYSTEM\CurrentControlSet\services;
  • загружаемые при разборе зависимостей используемых функций;

Непосредственно сама загрузка производится через вложенную функцию OslLoadImage (и подчиненную LoadImageEx). Теперь настало время ознакомиться с полным списком загружаемых кодом модуля Winload.exe драйверов:

Имя Официальное описание Зависимости
ntoskrnl.exe NT Kernel & System pshed.dll, hal.dll, bootvid.dll, kdcom.dll, clfs.sys, ci.dll
hal.dll Hardware Abstraction Layer DLL ntoskrnl.exe, pshed.dll, kdcom.dll
kdcom.dll Serial Kernel Debugger ntoskrnl.exe, hal.dll
pshed.dll Драйвер аппаратных ошибок, специфичных для платформы ntoskrnl.exe, hal.dll
bootvid.dll VGA Boot Driver ntoskrnl.exe, hal.dll
ci.dll Code Integrity Module ntoskrnl.exe
clfs.sys Common Log File System Driver ntoskrnl.exe, hal.dll
fileinfo.sys Fileinfo Filter Driver ntoskrnl.exe, hal.dll, fltmgr.sys
fltmgr.sys Диспетчер фильтров файловых систем Майкрософт ntoskrnl.exe, hal.dll
atapi.sys ATAPI IDE Miniport Driver ntoskrnl.exe, ataport.sys
ataport.sys ATAPI Driver Extension ntoskrnl.exe, hal.dll
wmilib.sys WMILIB WMI support library DLL ntoskrnl.exe
amdxata.sys Storage Filter Driver ntoskrnl.exe, hal.dll
mountmgr.sys Диспетчер точек подключения ntoskrnl.exe, hal.dll
msahci.sys MS AHCI 1.0 Standard Driver ntoskrnl.exe, pciidex.sys
pciide.sys Generic PCI IDE Bus Driver ntoskrnl.exe, pciidex.sys
pciidex.sys PCI IDE Bus Driver Extension ntoskrnl.exe, hal.dll
msisadrv.sys ISA Driver ntoskrnl.exe, wdfldr.sys
wdfldr.sys Kernel Mode Driver Framework Loader ntoskrnl.exe, hal.dll
acpi.sys ACPI драйвер для NT ntoskrnl.exe, hal.dll, wmilib.sys
partmgr.sys Partition Management Driver ntoskrnl.exe, hal.dll, wmilib.sys
pci.sys NT Plug and Play PCI-перечислитель ntoskrnl.exe, hal.dll, pshed.dll
vdrvroot.sys Корневой перечислитель виртуальных дисков ntoskrnl.exe, wdfldr.sys
volmgr.sys Volume Manager Driver ntoskrnl.exe, hal.dll, wmilib.sys
volmgrx.sys Драйвер расширения диспетчера томов ntoskrnl.exe, hal.dll
wdf01000.sys Среда выполнения платформы драйвера режима ядра ntoskrnl.exe, hal.dll, wdfldr.sys
msrpc.sys Kenrel Remote Procedure Call Provider ntoskrnl.exe
cng.sys Kernel Cryptography, Next Generation ntoskrnl.exe, hal.dll
pcw.sys Perfomance Counters for Windows Driver ntoskrnl.exe
fs_rec.sys File System Recognizer Driver ntoskrnl.exe
ndis.sys Драйвер NDIS 6.20 ntoskrnl.exe, hal.dll, netio.sys
ksecpkg.sys Kernel Security Support Provider Interface Packages ntoskrnl.exe, ksecdd.sys, cng.sys
ksecdd.sys Kernel Security Support Provider Interface Packages ntoskrnl.exe, hal.dll, msrpc.sys
tcpip.sys Драйвер TCP/IP ntoskrnl.exe, hal.dll, msrpc.sys, ksecdd.sys, fwpkclnt.sys, fltmgr.sys, ndis.sys, netio.sys
fwpkclnt.sys FWP/IPSec Kernel-Mode API ntoskrnl.exe, hal.dll, msrpc.sys, ndis.sys, netio.sys
netio.sys Network I/O Subsystem ntoskrnl.exe, hal.dll, ndis.sys, msrpc.sys
vmstorfl.sys Virtual Storage Filter Driver ntoskrnl.exe, hal.dll, wdfldr.sys
volsnap.sys Драйвер теневого копирования тома ntoskrnl.exe, hal.dll
spldr.sys loader for security processor ntoskrnl.exe
rdyboost.sys ReadyBoost Driver ntoskrnl.exe, hal.dll, ksecdd.sys
mup.sys Драйвер поставщика множественных UNC-имен ntoskrnl.exe, hal.dll
hwpolicy.sys Hardware Policy Driver ntoskrnl.exe
fvevol.sys BitLocker Drive Encryption Driver ntoskrnl.exe, hal.dll
disk.sys PnP Disk Driver ntoskrnl.exe, hal.dll, classpnp.sys
classpnp.sys SCSI Class System DLL ntoskrnl.exe, hal.dll

В таблице представлены (сведены) драйверы, у которых соответствующий параметр START выставлен в значение 0, то есть приведенные в таблице драйвера можно смело назвать группировкой драйверов режима загрузки (BOOT). Столбец зависимостей приведен в таблице не случайно, он то как раз нужен нам с целью определения функциональных взаимосвязей (зависимостей на уровне функций) того или иного драйвера.

В модуле winload.exe драйвер с именем classpnp.sys вы не найдете ни в жесткозакодированных (вкомпилированных) переменных, ни в ветвях реестра. Он загружается при разборе зависимостей от драйвера disk.sys, который использует некоторые функции classpnp.sys.

Одним из первых загружается образ ядра (ntoskrnl.exe) и уровень аппаратных абстракций HAL.DLL, но секции импорта у этих модулей на данном этапе не разрешаются (то есть указанные модули просто подгружаются в память без связывания). Соответственно и код библиотеки на этом этапе всего-лишь загружается в адресное пространство ядра, для того что бы в последствии, после инициализации, функции были "видимыми" (доступными) для кода других драйверов. Затем, таблица импорта актуального модуля ядра (ntoskrnl.exe и аналогичные) заполняется и связывается (при помощи функции LoadImports и вложенной в неё BindImportRefences). После того, как все подобные подготовительные процедуры отработали, управление передается ядру при помощи функции OslArchTransferToKernel. То есть, давайте резюмируем данный этап загрузки:

В процессе функционирования модуля Winload.exe драйвера режима загрузки только лишь подгружаются в память, но не инициализируются.

Отсюда рождается ряд вопросов:

  • может ли этот этап (Winload.exe) загрузки зависнуть по причине какого-либо типа повреждения файлов подгружаемых драйверов (в том числе и библиотеки classpnp.sys)?
  • если да, то по каким именно причинам: повреждение записи файла в файловой системе ntfs? неправильная версия classpnp.sys? нулевая длина файла?

Этап ntoskrnl

Собственно, это ни что иное как ядро операционной системы. На самом деле имя ntoskrnl.exe используется только в одноядерной системе (без режимов SMP/PAE). Имена ядра определяются следующим образом:

  • ntoskrnl.exe -- (1 ядро ЦП);
  • ntkrnlmp.exe -- (N ядер ЦП, SMP);
  • ntkrnlpa.exe -- (1 ядро ЦП, PAE);
  • ntkrpamp.exe -- (N ядер ЦП, SMP, PAE);

Как мы знаем из теории загрузки операционной системы Windows, после передачи управления на код модуля ядра, в нем происходит последовательная инициализация множества подсистем ядра. Поэтому дизассемблируем и начинаем изучать исходный текст актуального (на моей тестовой конфигурации это был ntkrnlpa.exe) ядра. Если честно, задача перед нами стоит не такая уж и тривиальная и по уму нам предстоит проанализировать множество этапов загрузки операционной системы и попытаться связать кодовые ветвления с событиями, происходящими на экране монитора. Тем не менее, при отсутствии исходников на языке C и знаний по декомпиляции кода из машинного языка в листинг на C, разобраться в дизассемблированных исходных кодах будет непросто, благо что разработчиком предоставляются символы ядра.
Итак, выше мы уже упоминали, что визуально на экране надпись "Подождите, пожалуйста..." знаменует окончание какого-то одного этапа загрузки и переход к следующему, при том что деление это чисто формальное, но для нас оно требуется с целью упрощения понимания происходящего. Теперь, за неимением другой внятной логики, нам надо визуально привязаться к тому, что происходит на экране, но как отследить момент начала текстового этапа и его конец? Поскольку я не умею пока работать с удаленной отладкой, был использован следующий алгоритм действий: мы будем расставлять так называемые "точки зацикливания" (точки "подвисания"), представляющие собой пару машинных опкодов EB FE (команда "прыжка на месте" - jmp $) и вводящие процессор в бесконечный цикл выполнения.

Ага, прямо так вот сразу взяли и модифицировали! Не все так просто как хотелось бы, дело в том, что ядро проверяет собственную целостность, поэтому если вы не выключили проверку подлинности кода ядра (флаги DISABLE_INTEGRITY_CHECKS и TESTSIGNING), то при попытке внесения изменений ядро "валится" в BSOD. Поэтому сперва нам следует отключить проверку целостности bootmgr и winload.

Первая точка останова

Как уже было рассмотрено, процесс загрузки некоторых драйверов категории BOOT_START начинается на этапе работы модуля winload.exe. Поэтому было решено начать с модуля winload.exe и попытаться обнаружить в нем участок кода, в котором процесс загрузки может подвиснуть на том самом текстовом участке (после вывода списка драйверов и сообщения "Подождите, пожалуйста.."). Далее, мы постараемся модифицировать найденный фрагмент таким образом, чтобы ввести в бесконечный цикл (подвесить), тем самым найдя первую точку останова. Самая удачная, по моему мнению, точка - это непосредственно перед передачей управления коду ядра ntoskrnl.exe. Где осуществляется передача управления? Для этого стартуем с начала основной процедуры OslMain и доходим до её финальной части, где код передачи управления происходит через вызов процедуры OslArchTransferToKernel. Вот так выглядит обрамляющий участок кода:

после непродолжительного оттупления, было решено заменить два маркированных (выделены цветом) пуша на инструкцию зацикленного прыжка jmp $ (опкоды EB FE). Изменения выполняем в шестнадцатеричном редакторе методом поиска длинной сигнатуры и замены (двух) байтов. После сохраняем изменений в Winload.exe, обязательно модифицируем контрольную сумму (CRC) образа, затем перезагружаемся, загрузка в безопасном режиме и.. результат:

classpnp.sys подвисает

Удача!! Произошло зависание процесса загрузки внутри "текстового" этапа на строке, содержащей имя драйвера classpnp.sys. Значит, мы попали своей модификацией в нужное место. Условимся, что отправная первая точка останова найдена.

Вторая точка останова

Теперь давайте попробуем определиться со второй точкой останова.

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

Судя по всему, ядро принимает от кода модуля Winload.exe управление в собственной точке входа, которая является первой инструкцией функции KiSystemStartup. После этого процесс инициализации ядра системы проходит несколько внутренних этапов, во время которых на экране отображается все тот же "текстовый" режим (с уже знакомым нам списком загруженных драйверов). Затем происходит переключение в графический режим с разрешением 640x480 и выводом анимированного логотипа загрузки (bootscreen). Анимация этого логотипа обеспечивается группой функций с суффиксом Invb*, которые занимаются инициализацией драйвера видеокарты и последующим выводом разнообразных графических примитивов. Например, функция InbvUpdateProgressBar в определенном режиме обновляет прогресс-бар времени загрузки.
Но это все представляет для нас лишь опосредованный интерес, поскольку после текстового режима загрузка уходит в графический режим далеко не сразу. Этап с выводом логотипа находится в модуле ядра, а вот переход к следующему (графическому) этапу происходит позже, во время загрузки диспетчера (менеджера) сессий SMSS (модуль smss.exe). Вычислил я это экспериментальным путем, проставляя точки останова по ходу исполнения этапов инициализации ядра. В конце концов я обнаружил интересную процедуру с именем Phase1Initialization, в которой происходит вызов вложенной процедуры Phase1InitializationDiscard, в конце которой мы находим следующий фрагмент кода:

Как помечено цветовой маркировкой, тут у нас вызывается функция _StartFirstUserProcess которая и создает процесс SMSS (Диспетчер сеанса/сессии, Session Manager Subsystem Service). Фактически именно в коде данной функции и происходит (визуально) переключение из графического режима 640x480 в "родное" разрешение установленного монитора (при условии, что установлены драйвера видеоадаптера и выставлено правильное разрешение). Таким образом, инструкция вызова функции _StartFirstUserProcess и знаменует собой:

  • в безопасном режиме: окончание "текстового" этапа загрузки модуля ядра;
  • в нормальном режиме: начало этапа анимированного логотипа (bootscreen);

Но поскольку мы сейчас разбираем именно безопасный режим загрузки, то данная функция является последней точкой блока кода, зависание внутри которого подвешивает загрузку со списком драйверов на экране. Таким образом мы (вероятно) получили вторую точку останова, тем самым обозначив участок возникновения проблем!!

Данным экспериментом мы подтвердили, что от момента вывода на экран строки о загрузке библиотеки classpnp.sys и до перехода в графический режим происходит выполнение довольно большого фрагмента кода, на протяжении которого возможно (теоретически) огромное количество подвисаний в абсолютно разносортных по назначению процедурах.

Между точками: инициализация драйвера classpnp.sys

Помните мы говорили, что загрузка большинства драйверов происходит на этапе работы модуля Winload.exe, а вот связывание и инициализация этих драйверов происходит уже на этапе работы модуля ядра (ntoskrnl.exe). Выходит что и инициализация интересующей нас библиотеки classpnp.sys происходит на этапе функционирования ядра. Давайте проверим, могут ли проблемы с инициализацией быть причиной зависания, для этого изучим внутреннюю структуру драйвера classpnp.sys. Как и в любом другом драйвере, после загрузки в адресное пространство ядра, требуется выполнить инициализацию, поэтому вызывается процедура инициализации драйвера, которая традиционно носит название DriverEntry.

и код вызываемой подфункции:

Ну и что мы тут видим? Похоже процедура инициализации этого драйвера не выполняет никаких специфических действий, по большому счету не делает вообще ничего, что могло бы её подвесить, даже не инициализирует привычных структур драйвера и не содержит никаких вложенных вызовов, просто-напросто возвращает управление с кодом STATUS_SUCCESS (EAX=0). И о чем нам это может говорить? Это говорит нам о том, что:

Сам по себе код classpnp.sys на этапе инициализации не является источником проблем, поскольку это библиотека для стороннего использования и процедура инициализации её исключительно формальна (предельно проста).

Такое возможно, однако требуется дополнительное подтверждение. Вспоминаем, что драйвер classpnp.sys является библиотекой класса устройств и его функции вызываются из других драйверов. Надо проверить все драйвера режима BOOT_START на предмет использования функций данной библиотеки. Судя по всему, функции библиотеки classpnp.sys на начальном "текстовом" этапе использует всего один-лишь драйвер disk.sys.

Между точками: инициализация драйвера disk.sys

Ну что же, тогда перейдем к драйверу disk.sys и проверить гипотезу о причастности его к подвисанию, с этой целью дизассемблируем и заглянем в код. Данный драйвер является драйвером класса дисковых накопителей и обеспечивает монтирование сконфигурированных в системе дисковых томов. В драйвере процедура инициализации (DriverEntry) выглядит уже намного сложнее, в ней мы обнаруживаем вызовы функции ClassInitialize, которая не является собственной внутренней функцией драйвера, а импортируется из библиотеки класса устройств classpnp.sys. Возможно что функция ClassInitialize может вызываться любым драйвером класса устройства при выполнении его собственной функции инициализации драйвера DriverEntry.

Один интересный момент: на экране (в списке) мы видим пункт с драйвером disk.sys ДО classpnp.sys, тем не менее, для описанного вызова функции, вторая библиотека должна быть УЖЕ загружена и инициализирована ДО загрузки и инициализации disk.sys. Этот факт лишний раз подтверждает, что та последовательность загрузки драйверов, которую мы наблюдаем на экране при запуске в защищенном режиме, всего-навсего отражает очередность загрузки драйверов/библиотек в память на этапе работы модуля загрузчика Winload, но никак не ядра Ntoskrnl. Что, в свою очередь, укрепляет нас в предположении, что видимые нами на экране драйвера не обязательно являются причиной зависания!!

Хорошо, переключаемся на изучение исходного кода драйвера classpnp.sys и анализируем код функции ClassInitialize, а вот он то как раз ужасающе огромен :) Но ошибочные коды возврата все же удалось обнаружить:

  • Функция возвращает C0000059 (STATUS_REVISION_MISMATCH): в случае расхождения размера структуры InitializationData, фактически проверка версии файла classpnp.sys.
  • Функция возвращает C0000059 (STATUS_REVISION_MISMATCH): если требуемые поля структуры (фактически ссылки на соответствующие функции драйвера) нулевые (NULL).
  • Функция возвращает C000009A (STATUS_INSUFFICIENT_RESOURCES): если нулевое значение буфера RegistryPath.Buffer в расширениях driverExtension. То есть похоже не выделился буфер по каким-то причинам.
  • Функция возвращает C0000035 (STATUS_OBJECT_NAME_COLLISION): в любом ином случае.

Драйвер disk.sys обеспечивает функционирование стека устройств хранения в операционной системе Windows, и ниже данного драйвера в стеке располагаются:

  • драйверы многопутевого ввода-вывода (MPIO-драйвера, обеспечивающие доступность томов по нескольким путям) (mpio.sys и прч.);
  • драйверы порта (обеспечивает поддержку транспортного протокола: SCSI/SAS/SATA/ATAPI);
  • драйвер минипорта (обеспечивает функциональность контроллера на материнской плате);
Но, опять же, сам драйвер disk.sys на этапе функционирования ядра (ntoskrnl.exe) не может быть причиной зависания, поскольку тут он инициализируется: вызывается функция инициализации драйвера DriverEntry. А как мы видели выше, в процедуре инициализации в случае проблем возвращаются конкретные коды ошибок, которые будут выведены на экран в случае сбоя, что исключает "тихое" подвисание.

Что еще между точками?

Ну хорошо, помимо инициализации вышеописанных драйверов, что у нас еще расположено между найденными нами точками останова? Да там адская прорва кода!! Получается, что весь код, размещающийся от начала кода модуля ядра в функции KiSystemStartup и до окончания в функции Phase1InitializationDiscard (фактически всей фазы 1), может, потенциально, являться причиной изучаемого нами подвисания (в безопасном режиме). Да уж, тут, что называется, без комментариев!!
Но не все так страшно, как кажется на первый взгляд. В большинстве расположенного на этом участке кода ядра выполняется обработка возникающих ошибок, что тем или иным образом (в виде сообщений) становится известно пользователю. А вот где действительно могут скрываться "мертвые" зависания процесса загрузки, так это на стыке хорошо отлаженного кода ядра и кода сторонних драйверов (то есть на этапе загрузки/инициализации драйверов сторонних разработчиков). Судя по всему в ядре существуют несколько цепочек кода загрузки подобных драйверов:

  • Start = 0 (режим BOOT_START) : IoInitSystem -> IopInitializeBootDrivers -> PnpInitializeBootStartDriver -> IopInitializeBuiltinDriver;
  • Start = 1 (режим SYSTEM_START) : IoInitSystem -> IopInitializeSystemDrivers -> IopLoadDriver -> MmLoadSystemImage;
  • Start = 2 (режим AUTO_START) : NtLoadDriver -> IopLoadUnloadDriver -> IopLoadDriver -> MmLoadSystemImage;
  • Start = 3 (режим DEMAND_START) : ???

Вот перечисленные то функции нам в первую очередь и интересны. В дополнение участвует функция MmLoadSystemImage, которая выполняет загрузку исполняемого образа драйвера в адресное пространство ядра (создает секции и производит связывание, заполняет таблицы импорта, перемещения, выполняет проверки безопасности и прочие задачи.). Еще одна функция IopLoadDriver работает с реестром, ответственная за открытие файла драйвера, создание объекта драйвера и вызова точки входа (процедуры инициализации DriverEntry). Для драйверов режима BOOT_START, функции IopLoadDriver и MmLoadSystemImage не участвуют в процессе, поскольку, как мы писали ранее, данные драйвера загружаются еще на этапе winload.exe.

Если предположить, что я допустил ошибку в оценке области подвисания, то в эту область еще должны входить последующие этапы, начиная с smss.exe и до самого logonui.exe. А мы знаем, что на этих этапах уязвимым для сбоев местом являются сервисы/службы режимов AUTO_START и DEMAND_START как загружаемые на схожих с драйверами принципах.

. . .

ПРОДОЛЖЕНИЕ СЛЕДУЕТ

. . .

Причины

Общие причины подвисания следующие:

  • установленное на станции железо: после загрузки общих библиотек/драйверов (в том числе и classpnp.sys) начинается перечисление устройств и загрузка драйверов к ним.
  • подключение системных томов: возможно ядро не может проинициализировать устройство (накопитель), на котором располагается один из сконфигурированных в системе разделов.
  • установленное на станции железо: возможно загрузка какого-либо драйвера устройства проводит первичную инициализацию устройства (через вызов инициализации драйвера и вложенных процедур), которая не может завершиться, подвешивая весь процесс запуска системы.

Более частные причины [решения]

предположение: проблема как то связана с загрузочным носителем либо контроллером или шлейфом.. одним словом с дисковой подсистемой.
решения:

  • Аппаратная проблема (BIOS): замена режима загрузки с AHCI на IDE (и наоборот) или Compatible/Enhanced;
  • Аппаратная проблема (BIOS): перепрошивка BIOS на последнюю версию;
  • Программная проблема: с режимом загрузки Legacy/UEFI: Сброс BIOS;
  • Аппаратная проблема: попробовать другой порт IDE/SATA на материнской плате;
  • Аппаратная проблема: заменить кабели данных/питания;
  • Аппаратная проблема: вышедшие из строя сторонние аппаратные модули (например, WIFI/Bluetooth/): отключение их в BIOS или физически на материнской плате;
  • Загрузиться с LiveCD, подцепить реестр сбойной машины, в ветке HKLM\SYSTEM\CurrentControlSet\services пройтись по всем ключам и для каждого драйвера с параметром START = 0 проверить физическое наличие в файловой системе соответствующего драйвера.
  • Загрузиться с LiveCD, подцепить реестр сбойной машины, в ветке HKLM\SYSTEM\CurrentControlSet\services пройтись по всем ключам и для каждого драйвера из верхнего списка проверить дабы параметр START был равен 0.
  • Аппаратная проблема с диском: проверка сторонними утилитами (paragon) файловой системы диска. Если имеется долгое подвисание процесса проверки на classpnp-файлах - то, вероятно, диск имеет проблемные секторы и происходит автоматический ремап.
  • Аппаратная проблема: посыпавшийся/сыпящийся винчестер;
  • Случай подмены/повреждения файлов: замена файлов classpnp.sys/disk.sys и остальных файлов загрузки из доверенного источника.
  • Сокрытие/отображение локальных дисков винды при помощи сторонних утилит (например, Acronis);
  • Программная проблема: отключить проверку подписей драйверов. в меню загрузки (F8) нужно просто отключить проверку подписей драйверов;

Выводы

При изучении некоторых функций модулей загрузки, я вышел на некий термин Adding Event Tracing to Kernel-Mode Drivers, вероятно возможность появилась начиная с версии Windows Vista. ETW и WPP - два инструмента диагностики для системных приложений (в том числе и драйверов). Интересно, можно включить через утилиту perfmon логгирование для classpnp/disk? Памятка: Группы сборщиков данных - сеансы отслеживания событий - ПКМ - создать - группа сборщиков данных - создать вручную (для опытных) - далее - в окне поставщики жмем добавить - Disk Class Driver Tracing Provider и Classpnp. Тем не менее, тут же возникает резонный вопрос: как это применимо к уже подвисающим станциям? Как на них можно включить логгирование и получить отчет?
К тому же, интересно, описанная в статье проблема зависания на classpnp.sys решена в Windows 10? И как та же логика реализована в Windows 10, имеются ли там "визуальные" зависания этапа загрузки и как изменился алгоритм обработки отказа в загрузке сторонних драйверов?

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

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

Помощь сайту