Марк Руссинович - 4.Внутреннее устройство Windows (гл. 12-14)
Система обнаруживает любые переполнения буфера, содержащегося в странице, поскольку они приводят к ошибке страницы: происходит обращение к недействительной странице, которая идет за буфером. Сигнатура нужна, чтобы перехватывать выход за конец буфера в момент, когда драйвер освобождает буфер: при выходе за конец будет нарушена целостность шаблона, помещенного в эту область при выделении памяти под буфер.
Чтобы посмотреть, как с помощью особого пула вызвать крах системы, который легко диагностировать с помощью механизма автоматического анализа, запустите DriverVerifier Manager (Диспетчер проверки драйверов). В Windows 2000 перейдите на вкладку Settings (Параметры), введите myfault.sys в текстовое поле внизу страницы, предназначенное для задания дополнительных драйверов, установите флажок особого пула, сохраните изменения, выйдите из Driver Verifier Manager и перезагрузитесь. B Windows XP и Windows Server 2003 выберите Create Custom Settings (For Code Developers) [Создать не стандартные параметры (для кода программ)] на первой странице мастера, на второй — Select Individual Settings From A Full List (Выбрать параметры из списка), на третьей — Special Pool (Особый пул). Далее выберите Select Drivers From A List (Выберите имя драйвера из списка), а на странице, где перечислены типы драйверов, введите myfault.sys в диалоговом окне, открываемом после нажатия кнопки добавления незагруженных драйверов. (He ищите в этом диалоговом окне файл myfault.sys — просто введите его имя.) Затем отметьте драйвер myfault.sys, выйдите из мастера и перезагрузитесь.
Когда вы запустите Notmyfault и вызовете переполнение буфера, сразу же произойдет крах системы, а анализ дампа даст следующий результат:
Probably caused by: myfault.sys (myfault+3f1)
При детальном анализе вы получите следующее описание стоп-кода:
Благодаря особому пулу трудноуловимая ошибка немедленно проявила себя, и анализ стал тривиальным.
Перезапись кода и защита системного кода от записиДрайвер, в котором из-за «бага» происходит повреждение или неправильная интерпретация его собственных структур данных, может обращаться к не принадлежащей ему памяти, воспринимая поврежденные данные как указатель на область памяти. Такой некорректный указатель может указывать на что угодно в адресном пространстве, в том числе на данные, принадлежащие другим драйверам, недействительные страницы памяти или на код других драйверов или ядра. Как и при переполнении буфера, драйвер, вызвавший повреждение данных, обычно не удается идентифицировать в момент, когда повреждение обнаруживается и происходит крах системы. Использование особого пула увеличивает вероятность выявления «багов», связанных с некорректными указателями, но не выявляет повреждение кода.
Если вы запустите Notmyfault и выберете переключатель Code Overwrite, драйвер Myfault повредит точку входа функции NtReadFile. Далее возможны два варианта. Если ваша система работает под управлением Windows 2000 и оснащена не более чем 127 Мб физической памяти или работает под управлением Windows XP или Windows Server 2003 и оснащена не более чем 255 Мб физической памяти, произойдет крах и анализ дампа укажет на Myfault.sys.
B описании стоп-кода, выводимом при детальном анализе, говорится, что драйвер Myfault попытался записать данные в память, доступную только для чтения:
ATTEMPTED_WRITE_TO_READONLY_MEMORY (be)
An attempt was made to write to readonly memory. The guilty driver is on the stack trace (and is typically the current instruction pointer). When possible, the guilty driver's name (Unicode string) is printed on the bugcheck screen and saved in KiBugCheckDriver.
Однако, если у вас Windows 2000 и более 127 Мб памяти либо Windows XP или Windows Server 2003 и более 255 Мб памяти, произойдет крах другого типа, так как повреждение памяти сразу не проявится. Поскольку NtReadFile — широко используемая системная функция, к которой подсистема Windows обращается при считывании ввода с клавиатуры или от мыши, крах системы произойдет почти сразу же, как только какой-либо поток попытается выполнить поврежденный код. Возникнет ошибка из-за выполнения недопустимой инструкции. Анализ аварийного дампа, выполняемый в этом случае, может давать разные результаты, но они обязательно будут неправильными. Обычно механизм анализа приходит к выводу, что наиболее вероятными источниками ошибки являются Windows.sys или Ntoskrnl.exe. При таком крахе выводится следующее описание стоп-кода:
Разные конфигурации ведут себя по-разному в связи с тем, что в Windows 2000 введен механизм защиты системного кода от записи (system code write protection). B таблице 14-2 показано, в каких конфигурациях защита системного кода от записи не используется по умолчанию.
Если защита системного кода от записи включена, диспетчер памяти проецирует Ntoskrnl.exe, HAL и загрузочные драйверы как стандартные физические страницы (4 Кб для x86 и x64, 8 Кб для IA64). Поскольку при проецировании образов обеспечивается детализация с точностью до размера стандартной страницы, диспетчер памяти может защитить страницы, содержащие код, от записи и генерировать ошибку доступа при попытке их модификации (что вы и видели при первом крахе). Ho когда защита системного кода от записи отключена, диспетчер памяти использует при проецировании Ntoskrnl.exe большие страницы (4 Мб для x86 или 16 Мб для IA64 и x64). Taкой режим по умолчанию действует в Windows 2000 при наличии более чем 127 Мб памяти, а в Windows XP или Windows Server 2003 — при наличии более чем 255 Мб памяти. Диспетчер памяти не может защитить код, поскольку код и данные могут находиться на одной странице.
Если защита системного кода от записи отключена и при анализе аварийного дампа сообщается о маловероятных причинах краха или если вы подозреваете, что произошло повреждение кода, следует включить защиту. Для этого проще всего включить проверку хотя бы одного драйвера с помощью Driver Verifier. Кроме того, можно включить защиту вручную, добавив два параметра в раздел реестра HKLMSystemCurrentControlSetControlSession ManagerMemory Management. Сначала укажите максимально возможное значение для объема памяти, начиная с которого диспетчер памяти использует при проецировании Ntoskrnl.exe большие страницы вместо стандартных. Создайте параметр LargePageMinimum типа DWORD, присвойте ему значение 0xFFFFFFFF. Добавьте еще один параметр типа DWORD — Enforce-WriteProtection — и присвойте ему значение 1. Чтобы изменения вступили в силу, перезагрузите компьютер.
ПРИМЕЧАНИЕ Когда отладчик имеет доступ к файлам образов, включенным в аварийный дамп, при анализе на внутреннем уровне выполняется команда отладчика!chkimg, которая проверяет, совпадает ли копия образа в аварийном дампе с образом на диске, и сообщает о различиях. Заметьте: если вы активизируете Driver Verifier, chkimg обязательно обнаружит различия при сравнении с файлом Ntoskrnl.exe.
Углубленный анализ аварийных дамповB предыдущем разделе рассказывалось о том, как с помощью Driver Verifier получать аварийные дампы, автоматический анализ которых может решить проблему. Тем не менее, возможны случаи, когда невозможно добиться, чтобы система сгенерировала дамп, который легко проанализировать. B таких случаях нужен анализ вручную, чтобы попытаться определить, в чем заключается проблема.
• C помощью команды отладчика !process 0 0 посмотрите, какие процессы выполняются, и убедитесь, что вам понятно назначение каждого из них. Попробуйте завершить или удалить приложения и сервисы, без которых можно обойтись.
• C помощью команды Im с параметром kv выведите список загруженных драйверов режима ядра. Убедитесь, что вам понятно назначение каждого из драйверов сторонних поставщиков и что вы используете самые последние версии.
• C помощью команды !vm проверьте, не исчерпаны ли виртуальная память системы, пул подкачиваемой памяти и пул неподкачиваемой памяти. Если исчерпана виртуальная память, объем переданных страниц будет близок к пределу. B этом случае попытайтесь выявить потенциальную утечку памяти: просмотрите список процессов и выберите те из них, которым передано много памяти. Если исчерпан пул подкачиваемой или неподкачиваемой памяти (т. е. объем занятой памяти близок к максимуму), см. эксперимент «Анализ утечки памяти в пуле» в главе 7.
Существуют и другие отладочные команды, которые могут оказаться полезными, но для их применения нужны более глубокие знания. Одной из таких команд является /irp. B следующем разделе показано, как с ее помощью идентифицировать подозрительные драйверы.
Засорение стекаПереполнение или засорение стека (stack trashing) вызывается ошибками, связанными с выходом за конец или начало буфера. Однако в таких случаях буфер находится не в пуле, а в стеке потока, выполняющего ошибочный код. Ошибки этого типа также трудны в отладке, поскольку стек играет важную роль при любом анализе аварийного дампа.