Обуздать Windbg: Простые приемы сложного отладчикаWindbg – это мощный отладчик как для юзермодных приложений, так и драйверов. Множество плагинов, команд, скриптовый язык... Все это может смутить и опытного в отладке человека. Особенно актуальна эта тема для реверсеров, которым пришлось полностью или частично перейти на 64-битные платформы. Ведь, по сути, ни один имеющийся x64-отладчик не способен сравниться с WinDbg по функционалу, поэтому речь в основном пойдет про 64-битные версии Windows, но и обладатели 32-битных смогут обнаружить для себя интересную информацию. Патчим ndiskdWinDbg славится обилием различных расширений. Загрузка расширения делается командой .load имя_расширения, а выгрузка – командой .unload. К сожалению, довольно удобный плагин для отладки ndis-драйверов, ndiskd, отказался работать в моем Windbg 6.11.1.404 (ndiskd.dll можно найти в WDK или в директории \Debugging Tools for Windows (x64)\winxp). Например, при попытке выполнить команду ответ был неизменным: «Can't get offset of Link in NDIS_IF_BLOCK!». Аналогичный результат давало выполнение команд opens, protocols и остальных. Перезагрузка символов результата не давала, все структуры были в порядке. Отказываться от такого удобного расширения у меня не было желания, поэтому я решил прибегнуть к патчу, если это возможно (были бы исходники – можно было бы переписать, например). Загружаю плагин в Ida. Начнем исследование с команды interfaces. Все команды, имеющиеся в расширении – это экспортируемые функции (что очень упрощает мою задачу). То есть достаточно найти функцию interfaces в экспорте и проанализировать ее. Что ж, приступим:
Видим вывод сообщения о том, что невозможно получить смещение поля Link в структуре NDIS_IF_BLOCK_NAME. Посмотрим, что за строка NDIS_IF_BLOCK_NAME. Выполнение команды: Сразу дает ответ, почему плагин рушится: «Symbol ndis!NDIS_IF_BLOCK not found». Тогда как dt ndis!_NDIS_IF_BLOCK нормально выводит структуру. Очевидно, надо изменить строки и ссылки на них, чтобы все работало нормально. Для нашей задачи воспользуемся каким-нибудь адекватным hex-редактором. Мне нравится 010 Editor. Исправим строки. В основном выравнивание позволяет нам безболезненно добавлять 1 символ в строку, не сдвигая остальные, но для пары строк это не так. Поэтому нужно найти ссылки на эти строки и исправить их. На каждую строку в данном случае по одной ссылке, находящейся в секции данных, на которую ссылаются инструкции, поэтому достаточно исправить ее. Например, на строку «ndis!LIST_ENTRY» ссылка выглядит так:
Смотрим, что в хексе это соответствует последовательности «68 14 00 80 01 00 00 00». Теперь нужно найти ее в хекс-редакторе и исправить первые байты. После этого небольшого патча ndiskd выводит всю нужную информацию. Отлавливаем загрузку драйвераА как остановиться на DriverEntry отлаживаемого драйвера? Можно, конечно, поставить int 3 в начале функции, но не всегда хочется уродовать код брейками. Так может быть есть альтернативное решение? Оказывается, что есть. Как я уже говорил, WinDbg – мощный отладчик с огромным количеством возможностей, поэтому грех не воспользоваться ими снова. Но для начала немного теории. Непосредственный переход на точку входа драйвера осуществляет неэкспортируемая функция IopLoadDriver. Да, ты правильно понял, – неэкспортируемая – это значит, надо иметь отладочные символы (и вообще, без них отладка ядра может превратиться в настоящий ад). В моей Vista используется такой код (его можно найти или однократным трейсом или идой, дизассемблируя ядерную IopLoadDriver): Собственно, убедиться, что по смещению 0x58 в DRIVER_OBJECT находится именно DriverInit, можно в kd командой dt _DRIVER_OBJECT. Что соответствует последовательности байт: Тогда, чтобы найти искомый адрес, нужно ввести в командной строке WinDbg: Где s – это команда поиска последовательности, nt!IopLoadDriver – адрес начала поиска, L2000 – количество байт, ограничивающее поиск, «48 8B D6 48 8B CB FF 53 58» – байты, которые мы ищем. Отладчик ответит что-то вроде: Вот и все. Теперь можно ставить точку останова на полученный адрес. В нашем случае – fffff800`01c0940a. Отныне мы будем получать управление как раз на переходе на точку входа. Этот метод довольно неплох, а что использовать – int 3 или его – решать тебе. СкриптингНаписание скриптов для WinDbg – это отдельная, очень большая тема. В рамках данной статьи я сделаю только введение. Для примера напишем простой скрипт для расстановки бряков на всех Major-функциях в DRIVER_OBJECT’е, в качестве аргумента принимает указатель на driver object выбранного драйвера. Потом получаем указатель на таблицу DRIVER_OBJECT.MajorFunction. Затем мотаем цикл по всем Major-функциям (всего их 27 - 0x1b). И как только мы находим подходящий указатель (не нулевой), ставим на него бряк (bp @$t3). Отладочные сообщения выводим функцией .printf. Скрипт написан с учетом размера 64-битного указателя (8 байт), что видно из строки «r $t2 = @$t0+@$t1*8». Чтобы скрипт заработал на 32-битной системе, нужно просто изменить размер указателя на 4. Обрати внимание – перед внутренним регистром во время операции присваивания ставится r, когда тип числовой, и r? – когда нечисловой. Для тестирования скрипта нужно сначала получить указатель на объект-драйвер какого-нибудь драйвера с помощью команды !drvobj или иным способом. В примере ниже показано получение адреса driver object tdx.sys.
Скрипты бывают многострочные и однострочные. Многострочные запускаются из командной строки $$><файл_скрипта, а однострочные – $$<текст_скрипта (или $<). Теперь модифицируем скрипт выше, чтобы он ставил брейк на конкретный обработчик device object’a, а не на все сразу. Комментарии начинаются с символов $$. Проверяем теперь два аргумента. Первый без изменений – поле DeviceObject.Type=4, а второй – это принадлежность к диапазону 0-1B, то есть допустимые обработчики от IRP_MJ_CREATE до IRP_MJ_PNP. Обрати внимание на строку u @$t3 – здесь осуществляется переход по адресу интересующего обработчика. Результат работы скрипта выглядит следующим образом (для разных вариантов входных аргументов):
Вообще, это довольно простые задачи. Иногда требуются более сложные узкоспециализированные скрипты, рассмотрение которых выходит за рамки настоящей статьи. ЗаключениеКонечно, рассмотренный здесь материал – это капля в море возможностей WinDbg. Я не коснулся, например, такой обширной темы, как написание плагинов. Чтобы освоить этот гигантский функционал, нужно время, терпение и опыт. Свои вопросы, как всегда, шли мне на e-mail. WWW
|