Погодин Евгений — ИТ Заметки
🪟Windows Обновлено 10.11.2026

Из практики админа 07. Синий экран: от дампа до причины с WinDbg

Note

Материал сверен с официальной документацией (Microsoft Learn и support.microsoft.com; ссылки — в конце статьи). Статья основана на реальном случае, описанном обезличенно: названия, адреса и значения изменены. Примеры вывода — иллюстрации. Перед работой на боевых системах проверьте шаги на тестовой машине.

Сервер перезагрузился сам. Всё снова работает, и появляется соблазн записать случившееся в «разовый сбой». Но синий экран — не случайность, а решение ядра: оно остановило систему, потому что продолжать было небезопасно. И перед остановкой ядро оставило улику — аварийный дамп памяти.

Это разбор такого случая. Сервер с Windows Server ушёл в синий экран и аварийно перезагрузился, а дамп занимал несколько гигабайт. Копировать его на рабочее место целиком не хотелось. Мы разобрали дамп, не снимая его с сервера: консольный отладчик открыл файл по сети, и мы получили код остановки, имя умершего процесса, сведения о том, кем он завершён, и стек вызовов. Что именно оказалось причиной и что с ней сделали, мы не приводим: статья про метод. Компонент, на который указывает вывод, называем «условным» — это может быть драйвер, служба или приложение.

В лаборатории сервер называется FS1 (192.168.10.30), рабочее место администратора — PC1 (192.168.10.101) в домене lab.example.

Что нужно знать заранее

  • Bug check (проверка ошибок, «синий экран», Stop error) — аварийная остановка Windows: ядро обнаружило состояние, при котором безопасно работать нельзя. У остановки есть числовой код и четыре параметра. В нашем случае код — 0xEF (CRITICAL_PROCESS_DIED): завершился процесс, без которого система работать не может.
  • Дамп режима ядра (kernel-mode dump) — файл со снимком памяти на момент сбоя. Видов пять: малый (Small, 64 КБ), ядра (Kernel), полный (Complete), активный (Active) и автоматический (Automatic). Полный дамп самый большой и содержит часть пользовательской памяти; дамп ядра её обычно опускает. Автоматический по содержимому равен дампу ядра, но Windows гибче подбирает размер файла подкачки. Чем больше дамп, тем он полезнее и тем дольше пишется: перезагрузка не начнётся, пока файл не записан.
  • Критический процесс — процесс, при завершении которого Windows сама останавливает систему, потому что целостность ОС под вопросом. Встроенные примеры: csrss.exe, wininit.exe, smss.exe, services.exe, winlogon.exe и другие. Кроме них, разработчик может создать службу с действием при сбое «Перезагрузить компьютер» (Restart the Computer). Поэтому код 0xEF сам по себе не говорит, виновата ли система, драйвер или приложение: он говорит, что умерло то, без чего Windows решила дальше не работать.
  • Где лежит дамп. По умолчанию — %SystemRoot%\MEMORY.DMP, малые дампы — в %SystemRoot%\Minidump.
  • Символы (symbols) — файлы с именами функций и модулей. Без них отладчик покажет голые адреса. Microsoft публикует символы своих компонентов на публичном сервере.
  • WinDbg — отладчик с графическим окном; вместе с ним ставятся консольные отладчики на том же движке (для x64 это cdbX64.exe; в наборе Debugging Tools — cdb.exe). Для разбора одного дампа консоль удобнее: команду можно записать в одну строку, а вывод сохранить в файл.
  • UNC (Universal Naming Convention) — запись пути вида \\сервер\ресурс. Отладчик принимает сетевые пути, поэтому дамп можно открыть, не копируя.
  • Среда в примерах. PC1 — Windows 11, где стоит отладчик; FS1 — сервер со сбоем; LAB\admin-lab — учётная запись с правами администратора на FS1. Команды выполняются в PowerShell на PC1.

Значения DebugInfoType (по статье Microsoft о настройке сбоя системы; на странице самого класса перечислены только 0–3, значение 7 описано в статье, а полный и активный дамп различает параметр FilterPages в реестре):

DebugInfoType Вид дампа
0 дамп не пишется
1 полный (с FilterPages в реестре — активный)
2 ядра
3 малый
7 автоматический

Симптомы

  • Сервер перезагрузился без команды администратора.
  • В журнале System после загрузки обычно есть событие 41 источника Kernel-Power: «система перезагрузилась без корректного завершения». Само по себе оно не объясняет, что случилось, но если причиной была остановка ядра, в его данных есть поле BugcheckCode — в десятичном виде. Число 239 — это 0xEF.
  • В каталоге C:\Windows лежит MEMORY.DMP размером в несколько гигабайт со свежей датой.

Код можно прочитать без отладчика. Для запроса к удалённому серверу нужен доступ к журналу событий: в брандмауэре должен быть разрешён удалённый доступ к службе журнала. Если его нет, выполните команду на самом сервере.

$filter = @{
    LogName      = 'System'
    ProviderName = 'Microsoft-Windows-Kernel-Power'
    Id           = 41
}
$e = Get-WinEvent -ComputerName FS1.lab.example -MaxEvents 1 `
    -FilterHashtable $filter
$data = ([xml]$e.ToXml()).Event.EventData.Data
$code = ($data | Where-Object Name -eq 'BugcheckCode').'#text'
'{0} -> 0x{1:X}' -f $code, [int]$code
239 -> 0xEF

Пример вывода. Если BugcheckCode равен нулю, остановку ядра событие не зафиксировало: так бывает при потере питания, зависании, принудительном выключении, а также когда система не успела записать код. Тогда начинать нужно не с отладчика, а с вопроса, что происходило с питанием и не зависал ли сервер.

Как искали причину

Шаги 1 и 9 — рекомендации по документации, а не ход нашего случая; в нашем случае основу составляют шаги 5, 7 и 8.

Шаг 1. Убедиться, что дамп есть и каков он. Настройки сбоя читаются классом WMI (Windows Management Instrumentation — интерфейс управления Windows), ничего не меняя. Команда с -ComputerName использует удалённое управление WinRM (протокол WS-Management); если оно закрыто, выполните её на сервере.

$params = @{
    ClassName    = 'Win32_OSRecoveryConfiguration'
    ComputerName = 'FS1.lab.example'
}
Get-CimInstance @params |
    Format-List DebugInfoType, DebugFilePath, AutoReboot,
                WriteToSystemLog, OverwriteExistingDebugFile
DebugInfoType              : 7
DebugFilePath              : %SystemRoot%\MEMORY.DMP
AutoReboot                 : True
WriteToSystemLog           : True
OverwriteExistingDebugFile : True

Пример вывода: автоматический дамп (7) с перезаписью. Следующий сбой затрёт этот файл, поэтому его нужно разобрать или сохранить до новой аварии.

Шаг 2. Поставить отладчик на рабочее место, а не на сервер. Так на боевой сервер ничего не устанавливается, а платформа отладчика может отличаться от платформы системы, создавшей дамп. На PC1 используйте winget (Windows Package Manager — менеджер пакетов Windows):

winget install Microsoft.WinDbg

Пакет работает на Windows 10 версии 1607 и новее и на Windows 11. Отдельный набор Debugging Tools for Windows можно поставить из установщика Windows SDK, выбрав только этот компонент.

Шаг 3. Найти консольный отладчик. Начиная с версии WinDbg 1.2506.12002.0 консольные отладчики входят в пакет, а их имена содержат архитектуру: для x64 это cdbX64.exe. Запасной вариант — путь внутри пакета amd64\cdb.exe (практика автора: в документации он не описан).

$cdb = (Get-Command cdbX64.exe -ErrorAction SilentlyContinue).Source
if (-not $cdb) {
    $pkg = Get-AppxPackage -Name '*WinDbg*'
    $cdb = Join-Path $pkg.InstallLocation 'amd64\cdb.exe'
}
Test-Path $cdb

Если файла нет или пакетов несколько, возьмите cdb.exe из Debugging Tools for Windows.

Шаг 4. Настроить символы. Путь к символам задают переменной _NT_SYMBOL_PATH или ключом -y. Формат srv*<кэш>*<сервер> говорит: брать символы с сервера и складывать в локальный кэш, чтобы каждый файл скачивался один раз.

$cache  = 'C:\symbols'
$server = 'https://msdl.microsoft.com/download/symbols'
$env:_NT_SYMBOL_PATH = "srv*$cache*$server"

Первый запуск медленнее остальных: символы скачиваются, а PC1 должен выходить к серверу символов по HTTPS. Если в стеке остались голые адреса, добавьте в начало списка команд !sym noisy (подробный вывод загрузки символов): по нему видно, откуда отладчик их искал и почему не нашёл. Тот же режим включает ключ -n. Символов чужих драйверов там, как правило, нет, и тогда вместо имени функции будет модуль со смещением (практика автора).

Шаг 5. Подключить административную общую папку сервера. Нужны права администратора на FS1.

Warning

Дамп — это содержимое памяти сервера в момент сбоя. Полный и активный дампы включают пользовательскую память, а в памяти могут оказаться учётные данные, ключи и сеансы пользователей. Храните дамп и лог отладчика как конфиденциальные данные (практика автора): не пересылайте, не выкладывайте на форумы и в публичные анализаторы, не расширяйте права на MEMORY.DMP, а после разбора удалите дамп с сервера или перенесите в защищённое хранилище. Подключайтесь с разрешения владельца сервера. В лаборатории для краткости это admin-lab; в боевой сети нужна отдельная учётная запись с правами администратора только на этом сервере, а не доменный администратор. Пароль в скрипт не пишите. Для локальных (не доменных) учётных записей доступ к c$ не проверялся. Подключённый диск остаётся в Windows после закрытия окна, поэтому его обязательно отключают (шаг 9).

Временный PSDrive виден только самому PowerShell: другие программы, включая cdb и .NET, его не увидят. Нужен сетевой диск Windows — параметр -Persist:

$cred = Get-Credential -UserName 'LAB\admin-lab' -Message 'Admin FS1'
New-PSDrive -Name Z -PSProvider FileSystem -Persist -Scope Global `
    -Root '\\FS1.lab.example\c$' -Credential $cred
Get-Item 'Z:\Windows\MEMORY.DMP' |
    Select-Object Name, Length, LastWriteTime

Шаг 6. Быстро узнать код остановки без отладчика (практика автора). Заголовок дампа читают прямо с сетевого диска: нужны первые 96 байт (0x60), весь файл не копируется. В Learn смещения в заголовке не описаны; структура заголовка (DUMP_HEADER64) опубликована в метаданных Windows SDK, но формат может измениться, поэтому считайте это подсказкой, а подтверждением — !analyze. Сигнатура в начале файла — PAGEDU64 (заголовок дампа 64-разрядной системы; вид дампа она не определяет), код остановки лежит по смещению 0x38, параметры — с 0x40.

$path = 'Z:\Windows\MEMORY.DMP'
$fs = [System.IO.File]::Open($path, 'Open', 'Read', 'ReadWrite')
try {
    $b = New-Object byte[] 0x60
    [void]$fs.Read($b, 0, $b.Length)
} finally { $fs.Dispose() }
[Text.Encoding]::ASCII.GetString($b, 0, 8)
'{0:X}' -f [BitConverter]::ToUInt32($b, 0x38)
'{0:X}' -f [BitConverter]::ToUInt64($b, 0x40)
'{0:X}' -f [BitConverter]::ToUInt64($b, 0x48)
PAGEDU64
EF
FFFFC10A2B3C4D80
0

Пример вывода. Для 0xEF первый параметр — объект процесса, второй — 0, если завершился процесс, и 1, если поток. Здесь ноль: умер процесс, а не поток.

Шаг 7. Запустить анализ. Одна команда открывает дамп по сети, выполняет !analyze -v и выходит; весь вывод идёт в файл:

$dump = 'Z:\Windows\MEMORY.DMP'
$log  = 'C:\dumplogs\fs1-analyze.log'
New-Item -ItemType Directory -Force -Path (Split-Path $log) | Out-Null
& $cdb -z $dump -c '!analyze -v; q' -logo $log

Ключ -z задаёт файл дампа, -c — команды при старте (через точку с запятой), q завершает отладчик, -logo пишет журнал сеанса; существующий файл перезаписывается. По нашему наблюдению отладчик запрашивает нужные страницы дампа, а не файл целиком, поэтому несколько гигабайт копировать не пришлось (практика автора). Если нужен интерактивный разбор, уберите ; q и вводите команды вручную: базовые сведения о сбое покажет .bugcheck.

Шаг 8. Прочитать результат. Допустим, умерший процесс — svchost.exe (общий процесс служб). Ниже условный фрагмент: набор строк зависит от версии отладчика.

CRITICAL_PROCESS_DIED (ef)
A critical system process died
Arguments:
Arg1: ffffc10a2b3c4d80, Process object or thread object
Arg2: 0000000000000000, If zero, a process died. If 1, a thread died.
Arg3: 0000000000000000
Arg4: 0000000000000000

PROCESS_NAME:  svchost.exe

Key  : CriticalProcessDied.Process
Value: svchost.exe

Key  : CriticalProcessDied.TerminatedBy
Value: <условный компонент: кто завершил процесс>

STACK_TEXT:
<верхние кадры: функции ядра, завершающие критический процесс>
<кадры ниже: кто их вызвал>

MODULE_NAME:  <модуль, который отладчик счёл вероятным виновником>
FAILURE_BUCKET_ID:  <категория сбоя>

Для чтения хватает документированных полей. STACK_TEXT — стек вызовов в месте сбоя; MODULE_NAME и IMAGE_NAME — модуль, который отладчик счёл вероятным виновником (либо вызвавший его код); BUCKET_ID — категория сбоя (в новых версиях отладчика — FAILURE_BUCKET_ID). Поле FOLLOWUP_NAME осмысленно, только если вы заранее описали владельцев модулей в файле triage.ini. В свежих версиях в блоке ключей анализа встречаются поля CriticalProcessDied.Process (кто умер) и CriticalProcessDied.TerminatedBy (кем завершён); в Learn они не описаны, а названия зависят от версии (практика автора). Если процесс завершён из пользовательского режима, в стеке ядра стороннего драйвера может не быть, и виновника видно именно по полю «кем завершён» (наше предположение).

Читать вывод удобно по трём вопросам: какой процесс умер, кто и как его завершил (поле «кем завершён» и верх STACK_TEXT) и что стоит ниже в стеке. Ответы на них мы получили; название условного компонента в статье не приводим.

Шаг 9. Убрать за собой. Сначала сохраните выжимку (шаг 8) и решите, нужен ли лог для заявки. Потом отключите сетевой диск и удалите только свой лог: каталог C:\dumplogs мог существовать раньше, поэтому рекурсивное удаление здесь не нужно. Команда с -WhatIf только покажет, что будет удалено.

Remove-PSDrive -Name Z -Scope Global
Remove-Item -Path $log -WhatIf
Remove-Item -Path $log

-Scope Global нужен, если диск создан в глобальной области, как в шаге 5 (практика автора). Кэш символов C:\symbols содержит публичные символы Microsoft, его можно оставить для следующего разбора.

Что не помогло

Дамп не ответил на вопрос, который напрашивался первым: что именно работало внутри умершего процесса. Допустим, в выводе значился общий процесс служб svchost.exe, и хотелось узнать, к какой группе служб он относится. Это определяет командная строка процесса (у svchost.exe — параметр -k), а хранится она в пользовательской памяти процесса (практика автора). Дамп ядра такой памяти, как правило, не содержит: он обычно её опускает, а при разборе пользовательского кода из дампа ядра есть ограничения, включая отсутствующие и выгруженные страницы.

Заголовок дампа тоже даёт только код и параметры, а не причину: для причины нужны символы и стек.

Что делать, когда дамп ядра исчерпан, подсказывает страница про 0xEF: изучить несколько дампов, а не один; посмотреть журнал событий перед сбоем; поискать пользовательский дамп, который во многих случаях создаётся до остановки системы. Эти шаги выходят за рамки нашего разбора.

Что сработало

В нашем случае сработало следующее: сеанс SMB с учётными данными администратора к административной общей папке сервера; консольный отладчик с командой cdb -z <путь к дампу> -c "!analyze -v; q" -logo <лог>; из вывода — код остановки, имя умершего процесса, сведения о том, кем он завершён, и стек вызовов. Дамп остался на сервере; по нашему наблюдению, по сети прошли только страницы, нужные отладчику (практика автора). Итог — какой компонент оказался причиной и что с ним сделали — мы не приводим.

Остальные шаги (событие 41, чтение настроек сбоя, отдельная учётная запись, уборка) — рекомендации, которых в нашем случае могло и не быть: подаём их как типовой пример.

Если вопрос уходит выше — к поставщику компонента или в поддержку, — отправляйте не дамп, а выжимку (практика автора): код и параметры, имя процесса, STACK_TEXT, дату и время сбоя, версию Windows. Перед отправкой просмотрите её: в логе отладчика есть имя сервера, пути и список модулей.

Чтобы следующий разбор был проще, подготовьте сервер заранее. Откройте «Дополнительные параметры системы» → вкладка «Дополнительно» → «Загрузка и восстановление» → «Параметры» (Advanced system settings → Startup and Recovery → Settings) и выберите вид дампа осознанно. Малый дамп занимает 64 КБ: код и параметры, стек упавшего потока, список драйверов; для ошибок вне упавшего потока его мало. Дамп ядра и автоматический — разумный компромисс. Полный и активный дают больше, в том числе пользовательскую память, но крупнее, дольше пишутся перед перезагрузкой и содержат больше чувствительных данных. Параметр OverwriteExistingDebugFile решает судьбу прошлого дампа: при отключённой перезаписи файл с тем же именем не затирается. Но Windows пишет в файл с тем же именем, поэтому для серии дампов Microsoft советует ещё менять имя файла после каждой остановки; переименовывайте или переносите MEMORY.DMP после каждой аварии (при неизменном имени новый дамп, возможно, не будет записан — наше предположение).

Warning

Параметры сбоя меняет администратор, в окно обслуживания. Перед правкой реестра сделайте его резервную копию: об этом предупреждает страница Microsoft. Подкачка должна лежать на системном (загрузочном) томе: для дампа ядра на 64-разрядной системе нужен объём «размер ОЗУ плюс 128 МБ». Автоматический дамп допускает системный размер подкачки. Откат — вернуть прежний вид дампа в том же окне.

Выводы и чек-лист

  • Синий экран оставляет улики: код, параметры и дамп. Перезагрузка — не повод ограничиться «разовым сбоем».
  • Код остановки берут из события 41 (десятичное число) или из заголовка дампа; подтверждают его !analyze -v.
  • Отладчик ставят на рабочее место, а не на боевой сервер.
  • Символы настраивают через srv*<кэш>*<сервер>; без них вывод малоинформативен.
  • Дамп открывают по сети и хранят как конфиденциальные данные; после работы отключают диск и удаляют лишнее.
  • Дамп ядра не покажет пользовательскую память: командную строку процесса ищите в журналах, пользовательском дампе или будущем полном/активном дампе.
  • Заранее проверьте вид дампа, подкачку на системном томе, место на диске и порядок сохранения файлов после аварии.

Состояние на ноябрь 2026 года (проверьте актуальность). WinDbg устанавливают через Microsoft Store, установщик или winget; страница установки обновлялась в апреле 2025 года, поддерживаются Windows 10 версии 1607 и новее и Windows 11, архитектуры x64 и ARM64. При установке через winget обновление запускают вручную: winget upgrade Microsoft.WinDbg. Консольные отладчики входят в пакет WinDbg начиная с версии 1.2506.12002.0 (WinDbg Release Notes) и запускаются по имени с архитектурой. Статья Microsoft о настройке сбоя системы относится к клиентским версиям Windows и приводит команды wmic. Страница KB5067470 сообщает, что утилита WMIC удалена из поддерживаемых версий Windows 11 (предварительные обновления августа 2026 года для 24H2 и 25H2 и сентября 2026 года для 26H1), на серверах всё зависит от версии; поэтому здесь использован Get-CimInstance. Состав вывода !analyze зависит от версии отладчика.

Типичные ошибки и недоразумения

  • Запускают отладчик без символов. Вместо имён функций — адреса. Сначала _NT_SYMBOL_PATH или -y.
  • Копируют дамп «для удобства» на ноутбук, в почту или в публичный сервис. Дамп содержит память сервера: это данные, а не безобидный технический файл.
  • Ставят отладчик на боевой сервер. Не нужно: достаточно рабочего места и сетевого доступа к дампу.
  • Принимают MODULE_NAME за приговор. Это вероятный виновник по эвристике, а для 0xEF советуют сравнивать несколько дампов.
  • Путают десятичный и шестнадцатеричный код. В событии 41 код записан десятичным числом.
  • Ждут от дампа ядра командную строку процесса. Она хранится в пользовательской памяти, а в дампе ядра её, как правило, нет.
  • Думают, что отключения перезаписи достаточно. Файл всё равно нужно переименовать или перенести.
  • Меняют настройки сбоя на ходу. Параметры восстановления и подкачки меняют в окно обслуживания, с копией реестра.
  • Забывают отключить сетевой диск. Он остаётся в Windows, пока его не удалят.

Проверьте себя

  1. Что сообщает событие 41 источника Kernel-Power и чего оно не говорит? Что система перезагрузилась без корректного завершения; код остановки хранится в десятичном виде (239 = 0xEF). Что именно упало, событие не объясняет.
  2. Почему отладчик ставят на рабочее место, а дамп открывают по сети? Чтобы не ставить инструменты на боевой сервер и не копировать файл на несколько гигабайт: отладчик принимает сетевые пути (UNC), а платформа дампа может отличаться от платформы отладчика.
  3. Что означают параметры 0xEF в примере выше? Первый — объект процесса; второй — 0, если завершился процесс, и 1, если поток; третий и четвёртый зарезервированы.
  4. Чем дамп режима ядра ограничен в этом разборе? Он обычно не содержит пользовательскую память, поэтому командную строку процесса из него не получить: нужны журналы, пользовательский дамп или полный/активный дамп в будущем.
  5. Достаточно ли отключить перезапись дампа, чтобы получать дампы и следующих аварий? Нет: прошлый файл не затрётся, но Microsoft советует ещё менять имя файла после каждой остановки; файл переименовывают или переносят.

Что дальше

Источники

Ещё по теме «Windows»