Основы использования отладчика 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):

Внешний вид отладчика WinDbg

На этом скриншоте изображена отладка программы из заметки Получаем список запущенных процессов на Windows API. Как видите, WinDbg подцепил исходники программы. Справа отображаются значения локальных переменных. Внизу находится окно для ввода команд, где при помощи команды kn был выведен стэк вызовов. В верхней части отладчика находятся кнопки, при помощи которых можно выполнять простые действия вроде «шаг вперед», а также открыть дополнительные окна. При помощи этих окон можно посмотреть содержимое оперативной памяти, значения регистров, дизассемблерный листинг программы и много других интересных вещей.

Вообще, очень многое в WinDbg можно делать через GUI. Например, в окне с исходным кодом можно поставить курсор на нужном месте и нажатием на икноку с ладонью создать там точку останова. Или выполнить run to cursor. Также выполнение команды можно в любой момент остановить при помощи кнопки Break в панели вверху. На этом всем подробно останавливаться не будем. Но учтите, что такие возможности есть и они заслуживают изучения!

Прежде, чем приступить к отладке при помощи WinDbg, нужно сделать несколько несложных телодвижений. Откройте File → Symbol File Path и введите:

SRV*C:\symbols*http://msdl.microsoft.com/download/symbols

Затем нажмите Browse и укажите путь до файлов c отладочной информацией (.pdb) вашего проекта. Аналогичным образом в File → Source File Path укажите путь до каталога с исходниками. Если сомневаетесь, укажите путь, по которому находится файл проекта Visual Studio, не ошибетесь. Затем скажите File → Save Workspace, чтобы все эти пути не приходилось указывать заново при каждом запуске WinDbg.

Теперь, когда все настроено, есть несколько способов начать отладку. Можно запустить новый процесс под отладчиком, можно подключиться к уже существующему, можно открыть крэшдамп. Все это делается через меню File. Особого внимания заслуживает возможность удаленной отладки. Например, если при помощи WinDbg вы отлаживаете драйверы, то особого выбора, кроме как использовать удаленную отладку, у вас как бы и нет. Что не удивительно, ведь малейшая ошибка в коде драйвера может привести к BSOD.

Если вы уже отлаживаете процесс, но хотели бы начать делать это удаленно, говорим:

.server tcp:port=3003

Нужно проверить, что порт открыт, что особенно актуально на Windows Server. На клиенте делаем File → Connect to a Remote Session, вводим:

tcp:Port=3003,Server=10.110.0.10

Кроме того, можно запустить сервер, позволяющий отлаживать любой процесс в системе:

dbgsrv.exe -t tcp:port=3003

На клиенте подключаемся через File → Connect to Remote Stub. После этого через интерфейс можно как обычно выбрать процесс из списка или запустить новый. Только работать процессы будут на удаленной машине.

Теперь, наконец-то, рассмотрим основные команды WinDbg.

Важно! Иногда команды могут выполняется очень долго, например, если вы решили загрузить сразу все отладочные символы. Если устанете ждать, просто нажмите Ctr+C в поле ввода команды, и WinDbg тут же перестанет делать то, что он сейчас делает.

Справка:

.help
.hh команда

Очистить вывод в окне Command:

.cls

Добавить путь, по которому WinDbg будет искать отладочные символы (та же команда без знака плюс затрет все ранее прописанные пути):

.sympath+ c:\pdbs

Перезагрузить символы:

.reload

Показать список модулей:

x *!

Ищем символы:

x *!Ololo::My::Namespace::*

Загрузить символы для модуля:

ld имя_модуля

Если WinDbg почему-то не находит .pdb файлы и в стектрейсах вместо имен .c/.cpp файлов с номерами строк вы видите что-то вроде module+0x19bc, попробуйте выполнить следующую последовательность команд — так вы получите больше информации о возможных причинах проблемы:

!sym noisy
.reload MyModule.dll

Указать путь к каталогу исходников:

.srcpath c:\src

Поставить точку останова:

bp kernel32!CreateProcessA
bp `mysorucefile.cpp:123`
bp `MyModule!mysorucefile.cpp:123`
bp @@(Full::Class:Name::method)

Поставить точку остановка, которая сработает только один раз:

bp target /1

Поставить точку останова, которая сработает на 5-ый раз, после 4-х проходов:

bp target 5

Можно автоматически выполнять команды каждый раз, когда срабатывает точка останова:

bp kernel32!LoadLibraryA ".echo \"Variables:\n\"; dv"

У аппаратных точек останова синтаксис такой:

ba <mode> <size> <address> [passes=1]

Где mode это e, r или w — выполнение, чтение, запись. При mode = e параметр size может быть только 1. Например:

ba e 1 kernel32!LoadLibraryA

Список точек останова:

bl

Деактивировать точку останова:

bd номер

Активировать точку останова:

be номер

Полное удаление точек останова:

bc номер
bc *

Показать команды, которые нужно ввести для восстановления текущих точек останова:

.bpcmds

Писать лог в файл:

.logopen c:\1.txt

Перестать писать лог в файл:

.logclose

Выполнить команды из файла:

$<c:\1.txt

Для сохранения точек останова в файл можно выполнить (обязательно в одну строку!) следующие команды:

.logopen c:\1.txt; .bpcmds; .logclose

Показать дизассемблерный листинг:

u

Показать значение регистров:

r

Показать стек вызовов:

k

То же самое с номерами фреймов:

kn

Перемещение к фрейму:

.frame номер

Показать локальные переменные:

dv

Показать структуру:

dt имя_переменной

Показать структуру рекурсивно:

dt -r имя_переменной

Дамп памяти по адресу:

db адрес

Дамп памяти в виде word/dword/qword:

dw адрес
dd адрес
dq адрес

Дамп в виде битов:

dyb адрес

Дамп ascii строки:

da адрес

Дамп unicode строки:

du адрес

Редактирование памяти:

e[b|w|d] адрес новое_значение_1 новое_значение_2 ...

Поиск по памяти:

s [-d|-w|-b|-a|-u] адрес L?число_элементов значение_1 значение_2 ...

Продолжить выполнение программы:

g

Шаг через функцию:

p

Шаг внутрь функции:

t

Выполнение до ближайшего возврата:

pt

Список нитей:

~

Переключиться на нитку с номером 3:

~3 s

Выполнение любой команды в контексте нитки, например, получение стека вызовов:

~номер kn

Suspend/resume нитки:

n
m

Freeze/unfreeze нитки:

f
u

Можно использовать имена переменных и регистры как-то так:

? ebp - esp
db eax + 0xA
dd this

При этом в WinDbg есть псевдорегистры $teb и $peb:

? $teb
? $peb

Также можно использовать разыменование адресов:

? poi(ebp + 4)

Показать загруженные модули:

!dlls

Показать Process Environment Block:

!peb

Показать Thread Environment Block:

!teb

Показать Exception Handler Chain:

!exchain

Информация о страницах памяти:

!vadump

Обратите внимание, что если ввести пустую команду, то есть, просто нажать Enter, вы как бы «продолжите» выполнение последней команды. Например, выполнение «пустой команды» после выполнения команды u будет показывать ассмеблерный листинг с того места, где закончился вывод предыдущей команды.

В целом, приведенной выше информации вам будет более чем достаточно для отладки в 90% случаев. В качестве источников дополнительной информации можно рекомендовать, например, эту и эту статью. В первой рассказывается, как сделать условные точки останова, а вторая повествует о вычислении более сложных выражений в стиле @@c++(sizeof(&(this->m_item))).

Дополнение: О том, как настроить запись корок, рассказывает статья Collecting User-Mode Dumps на MSDN.

Метки: , .


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