Из практики админа 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, пока его не удалят.
Проверьте себя
- Что сообщает событие 41 источника Kernel-Power и чего оно не говорит?
Что система перезагрузилась без корректного завершения; код остановки хранится в десятичном виде (239 =
0xEF). Что именно упало, событие не объясняет. - Почему отладчик ставят на рабочее место, а дамп открывают по сети? Чтобы не ставить инструменты на боевой сервер и не копировать файл на несколько гигабайт: отладчик принимает сетевые пути (UNC), а платформа дампа может отличаться от платформы отладчика.
- Что означают параметры
0xEFв примере выше? Первый — объект процесса; второй — 0, если завершился процесс, и 1, если поток; третий и четвёртый зарезервированы. - Чем дамп режима ядра ограничен в этом разборе? Он обычно не содержит пользовательскую память, поэтому командную строку процесса из него не получить: нужны журналы, пользовательский дамп или полный/активный дамп в будущем.
- Достаточно ли отключить перезапись дампа, чтобы получать дампы и следующих аварий? Нет: прошлый файл не затрётся, но Microsoft советует ещё менять имя файла после каждой остановки; файл переименовывают или переносят.
Что дальше
- Поддержка пользователей 03. Диагностика ПК с Windows — журналы событий и Просмотр событий: где искать то, чего нет в дампе.
- Windows Server с нуля 12. Безопасность и обслуживание — безопасный удалённый доступ к серверам, минимальные привилегии, аудит и регулярные проверки.
- BSoD: синий экран смерти и его исправление — краткие общие советы для начинающих (материал 2020 года: диск, драйверы, память, перегрев); разбора дампа в нём нет.
Источники
- Install the Windows debugger — WinDbg, установка через Microsoft Store, установщик и winget, поддерживаемые системы, обновление
- WinDbg Release Notes — консольные отладчики в пакете WinDbg с версии 1.2506.12002.0, имена
cdbX64.exeи другие - Debugging Tools for Windows — состав набора, CDB и WinDbg на одном движке, установка из Windows SDK
- Using the !analyze extension —
!analyze -v, поля STACK_TEXT, MODULE_NAME, IMAGE_NAME, BUCKET_ID, Followup и triage.ini - Bug check 0xEF: CRITICAL_PROCESS_DIED — параметры, рекомендации по разбору, пользовательский дамп, журнал событий
- Symbol path for Windows debuggers —
srv*, кэш,_NT_SYMBOL_PATH, ключи-yи-n, адрес сервера символов Microsoft - Varieties of kernel-mode dump files — пять видов дампа, пользовательская память, скорость записи
- Configure system failure and recovery options in Windows — виды дампа и их значения, подкачка на системном томе, перезапись и смена имени файла,
MEMORY.DMP, копия реестра - Win32_OSRecoveryConfiguration class — свойства
DebugInfoType,DebugFilePath,AutoRebootи другие - CDB command-line options —
-z,-c,-logo,-y,-n - Analyze a kernel-mode dump file by using WinDbg — сетевые пути и UNC, платформа дампа,
!analyze,.bugcheck - Advanced troubleshooting for Event ID 41 — событие 41, источник Kernel-Power, код остановки в десятичном виде
- Get-WinEvent —
-ComputerName,-FilterHashtable,-MaxEvents, доступ к журналам удалённых компьютеров - New-PSDrive —
-Persist,-Credentialдля UNC-пути, временные диски видны только PowerShell, отключение сетевого диска черезRemove-PSDrive - Windows Management Instrumentation Command-line (WMIC) removal from Windows — страница KB5067470: удаление WMIC, замена на PowerShell