Основы использования отладчика WinDbg
22 декабря 2015
Поговорим об отладчиках для Microsoft Windows. Их существует довольно много, вспомнить хотя бы всеми любимый OllyDbg, некогда популярный, но в настоящее время практически умерший SoftIce, а также Sycer, Immunity Debugger, x64dbg и бесчисленное количество отладчиков, встроенных в IDE. По моим наблюдениям, WinDbg нравится далеко не всем. Думаю, в основном это связано с командным интерфейсом отладчика. Любителям Linux и FreeBSD, бесспорно, он пришелся бы по душе. Но закоренелым пользователям Windows он кажется странным и неудобным. А тем временем, по функционалу WinDbg ничем не уступает другим отладчикам. Как минимум, он точно нечем не хуже классического GDB или там LLDB. В чем мы сегодня с вами и убедимся.
В мире Windows, все, как обычно, немного через жопу. Официальный инсталятор WinDbg можно скачать на сайте MS. Инсталятор этот помимо WinDbg также поставит вам свежую версию .NET Framework и перезагрузит систему без спроса. После установки не факт, что отладчик вообще заработает, особенно под старыми версиями Windows. Поэтому лучше скачать неофициальную сборку WinDbg здесь или здесь. Настоятельно советую воспользоваться именно одной из этих версий — это самый простой и быстрый способ установить WinDbg.
Есть две версии WinDbg, x86 и x64. Чтобы не возникало никаких проблем, отлаживайте x86 приложения с помощью x86 дебагера, а x64 приложения — с помощью x64 дебагера. После первого запуска выглядеть WinDbg будет довольно убого. Но не беспокойтесь по этому поводу. Поработав буквально несколько минут с WinDbg, вы подстроите его под себя и выглядеть он будет вполне няшненько. Например, как-то так (кликабельно, 51 Кб, 1156x785):
На этом скриншоте изображена отладка программы из заметки Получаем список запущенных процессов на Windows API. Как видите, WinDbg подцепил исходники программы. Справа отображаются значения локальных переменных. Внизу находится окно для ввода команд, где при помощи команды kn
был выведен стэк вызовов. В верхней части отладчика находятся кнопки, при помощи которых можно выполнять простые действия вроде «шаг вперед», а также открыть дополнительные окна. При помощи этих окон можно посмотреть содержимое оперативной памяти, значения регистров, дизассемблерный листинг программы и много других интересных вещей.
Вообще, очень многое в WinDbg можно делать через GUI. Например, в окне с исходным кодом можно поставить курсор на нужном месте и нажатием на икноку с ладонью создать там точку останова. Или выполнить run to cursor. Также выполнение команды можно в любой момент остановить при помощи кнопки Break в панели вверху. На этом всем подробно останавливаться не будем. Но учтите, что такие возможности есть и они заслуживают изучения!
Прежде, чем приступить к отладке при помощи WinDbg, нужно сделать несколько несложных телодвижений. Откройте File → Symbol File Path и введите:
Затем нажмите Browse и укажите путь до файлов c отладочной информацией (.pdb) вашего проекта. Аналогичным образом в File → Source File Path укажите путь до каталога с исходниками. Если сомневаетесь, укажите путь, по которому находится файл проекта Visual Studio, не ошибетесь. Затем скажите File → Save Workspace, чтобы все эти пути не приходилось указывать заново при каждом запуске WinDbg.
Теперь, когда все настроено, есть несколько способов начать отладку. Можно запустить новый процесс под отладчиком, можно подключиться к уже существующему, можно открыть крэшдамп. Все это делается через меню File. Особого внимания заслуживает возможность удаленной отладки. Например, если при помощи WinDbg вы отлаживаете драйверы, то особого выбора, кроме как использовать удаленную отладку, у вас как бы и нет. Что не удивительно, ведь малейшая ошибка в коде драйвера может привести к BSOD.
Если вы уже отлаживаете процесс, но хотели бы начать делать это удаленно, говорим:
Нужно проверить, что порт открыт, что особенно актуально на Windows Server. На клиенте делаем File → Connect to a Remote Session, вводим:
Кроме того, можно запустить сервер, позволяющий отлаживать любой процесс в системе:
На клиенте подключаемся через File → Connect to Remote Stub. После этого через интерфейс можно как обычно выбрать процесс из списка или запустить новый. Только работать процессы будут на удаленной машине.
Теперь, наконец-то, рассмотрим основные команды WinDbg.
Важно! Иногда команды могут выполняется очень долго, например, если вы решили загрузить сразу все отладочные символы. Если устанете ждать, просто нажмите Ctr+C в поле ввода команды, и WinDbg тут же перестанет делать то, что он сейчас делает.
Справка:
.hh команда
Очистить вывод в окне Command:
Добавить путь, по которому WinDbg будет искать отладочные символы (та же команда без знака плюс затрет все ранее прописанные пути):
Перезагрузить символы:
Показать список модулей:
Ищем символы:
Загрузить символы для модуля:
Если WinDbg почему-то не находит .pdb файлы и в стектрейсах вместо имен .c/.cpp файлов с номерами строк вы видите что-то вроде module+0x19bc, попробуйте выполнить следующую последовательность команд — так вы получите больше информации о возможных причинах проблемы:
.reload MyModule.dll
Указать путь к каталогу исходников:
Поставить точку останова:
bp `mysorucefile.cpp:123`
bp `MyModule!mysorucefile.cpp:123`
bp @@(Full::Class:Name::method)
Поставить точку остановка, которая сработает только один раз:
Поставить точку останова, которая сработает на 5-ый раз, после 4-х проходов:
Можно автоматически выполнять команды каждый раз, когда срабатывает точка останова:
У аппаратных точек останова синтаксис такой:
Где mode это e, r или w — выполнение, чтение, запись. При mode = e параметр size может быть только 1. Например:
Список точек останова:
Деактивировать точку останова:
Активировать точку останова:
Полное удаление точек останова:
bc *
Показать команды, которые нужно ввести для восстановления текущих точек останова:
Писать лог в файл:
Перестать писать лог в файл:
Выполнить команды из файла:
Для сохранения точек останова в файл можно выполнить (обязательно в одну строку!) следующие команды:
Показать дизассемблерный листинг:
Показать значение регистров:
Показать стек вызовов:
То же самое с номерами фреймов:
Перемещение к фрейму:
Показать локальные переменные:
Показать структуру:
Показать структуру рекурсивно:
Дамп памяти по адресу:
Дамп памяти в виде word/dword/qword:
dd адрес
dq адрес
Дамп в виде битов:
Дамп ascii строки:
Дамп unicode строки:
Редактирование памяти:
Поиск по памяти:
Продолжить выполнение программы:
Шаг через функцию:
Шаг внутрь функции:
Выполнение до ближайшего возврата:
Список нитей:
Переключиться на нитку с номером 3:
Выполнение любой команды в контексте нитки, например, получение стека вызовов:
Suspend/resume нитки:
m
Freeze/unfreeze нитки:
u
Можно использовать имена переменных и регистры как-то так:
db eax + 0xA
dd this
При этом в WinDbg есть псевдорегистры $teb и $peb:
? $peb
Также можно использовать разыменование адресов:
Показать загруженные модули:
Показать Process Environment Block:
Показать Thread Environment Block:
Показать Exception Handler Chain:
Информация о страницах памяти:
Обратите внимание, что если ввести пустую команду, то есть, просто нажать Enter, вы как бы «продолжите» выполнение последней команды. Например, выполнение «пустой команды» после выполнения команды u
будет показывать ассмеблерный листинг с того места, где закончился вывод предыдущей команды.
В целом, приведенной выше информации вам будет более чем достаточно для отладки в 90% случаев. В качестве источников дополнительной информации можно рекомендовать, например, эту и эту статью. В первой рассказывается, как сделать условные точки останова, а вторая повествует о вычислении более сложных выражений в стиле @@c++(sizeof(&(this->m_item)))
.
Дополнение: О том, как настроить запись корок, рассказывает статья Collecting User-Mode Dumps на MSDN.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.