Средства отладки режима ядра операционной системы Windows. Хардкорная отладка с Linice: учимся работать в консольном отладчике ядра Краткое описание модуля HEVD

  • Авторы:

    Баринов С.С., Шевченко О.Г.

  • Год:
  • Источник:

    Информатика и компьютерные технологии / Материалы VI международной научно-технической конференции студентов, аспирантов и молодых ученых - 23-25 ноября 2010 г., Донецк, ДонНТУ. - 2010. - 448 с.

Аннотация

Приведен сравнительный анализ отладки режима пользователя и режима ядра применительно к операционной системе Microsoft Windows, выделены отличия и проблемы организации отладки последнего. На основе полученных результатов сформулированы основные требования к построению отладчиков режима ядра в случае аварийной и интерактивной отладки. Проведен анализ существующих решений на предмет соответствия требованиям. В частности, особое внимание уделено отладчику Microsoft Windows Debugger.

Основная часть

Отладка - это процесс определения и устранения причин ошибок в программном обеспечении. В некоторых проектах отладка занимает до 50 % общего времени разработки . Отладка может быть значительно упрощена при использовании специализированных инструментов, которые постоянно совершенствуются. Основным таким инструментом является отладчик, позволяющий контролировать выполнение ПО, наблюдать за его ходом и вмешиваться в него. Средства отладки ядра преимущественно используются разработчиками драйверов .

Инструментарий разработки прикладного программного обеспечения предлагает программисту широкий спектр возможностей. Любая интегрированная среда разработки включает в себя и возможность отладки без необходимости использования сторонних утилит. Если же речь идет о системном программном обеспечении и разработке драйверов в частности, то в силу его специфики процесс разработки чрезвычайно затруднен и мало автоматизирован. Все фазы разработки, в числе которых и отладка, являются раздельными. Для проведения каждой из них требуются особые условия: написание программного кода выполняется на полноценной компьютерной системе, отладка - на отладочной системе, тестирование - в зависимости от обстоятельств и т.д. Сам же отладчик режима ядра более сложен в освоении и, соответственно, менее дружественен.

В целом можно говорить о недостатке средств отладки ядра. Хотя таковые средства имеются в наличии, зачастую говорить об альтернативах не приходится. Например, отладчик Microsoft Windows Debugger имеет слишком высокий порог вхождения. Многие программисты говорят о первом негативном опыте при знакомстве с ним, а большинство его возможностей остаются незатребованными.

Исходя из структуры виртуального адресного пространства, если в приложении допущена ошибка, вследствие которой приложение выполнит запись данных в произвольное место памяти, то приложение повредит только собственную память и не повлияет на работу других приложений и операционной систему. Тогда как программный код режима ядра в состоянии повредить важные структуры данных операционной системы, что неминуемо приведет к общему сбою. Неэффективно написанный драйвер также может стать причиной серьезной деградации всей операционной системы.

    Современные отладчики обеспечивают следующие базовые функции:
  • отладка на уровне исходного кода;
  • управление выполнением;
  • просмотр и изменение памяти;
  • просмотр и изменение содержимого регистров процессора;
  • просмотр стека вызовов.

Для облегчения работы с дизассемблированным кодом применяются т.н. отладочные символы. Во время работы компоновщика, кроме образа исполняемого файла также может быть создан файл данных, содержащий информацию, которая не требуется при выполнении программы, но чрезвычайно полезна при ее отладке: имена функций, глобальных переменных, описание структур. Отладочные символы доступны для всех исполняемых файлов операционной системы Windows .

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

При прерывании кода режима ядра возникают следующая дилемма. Отладчик для взаимодействия с программистом использует интерфейс пользователя. Т.е. как минимум видимая часть отладчика выполняется в пользовательском режиме и для его построения естественно использует интерфейс прикладного программирования (Windows API), который в свою очередь опирается на модули режима ядра. Таким образом, приостановка кода режима ядра может привести к взаимной блокировке: система перестанет отвечать на запросы пользователя.

Для доступа к памяти ядра составные части отладчика также должны выполняться в режиме ядра. Это ведет к возникновению сразу двух проблем, являющиеся очевидным следствием организации памяти в защищенном режиме процессора.

Первая проблема касается трансляции виртуальных адресов памяти. Драйвера постоянно взаимодействуют с приложениями пользовательского режима, выполняя обращение к их памяти. Операционная система Windows транслирует виртуальные адреса в физические, руководствуясь понятием контекста потока. Контекст потока - структура, отражающая состояние потока и включающая, в частности, набор регистров и некоторую другую информацию. Когда управление передается другому потоку, возникает переключение контекста, при котором сохраняется информация об одном потоке и восстанавливается информации о другом. При переключении контекста потока на поток другого процесса переключается также и каталог страниц, используемый для трансляции виртуальных адресов в физические .

Особенность заключается в том, что при диспетчеризации системных вызовов операционная система Windows не переключает контекст. Благодаря этому код режима ядра может использовать виртуальные адреса пользовательского режима.

Иначе дело обстоит при диспетчеризации прерываний или выполнении системных потоков. Прерывание может произойти в любой момент, поэтому нельзя предугадать, какой контекст потока будет использоваться. Системные же потоки не принадлежат какому-либо процессу и не могут транслировать виртуальные адреса пользовательского режима . Отсюда следует, что в этих ситуациях нельзя обратиться к памяти пользовательского режима.

Второй проблемой является обращение к перемещаемой памяти. Большая часть информации в памяти является перемещаемой и в любой момент может быть перемещена из физической памяти на жесткий диск в страничный файл. Если обратиться к странице, которая отсутствует в физической памяти, в нормальной ситуации процессор сгенерирует прерывание Page Fault, которое будет обработано диспетчером памяти, и в результате страница будет прочитана из страничного файла и загружена в физическую память.

Описанное поведение нарушается, если программный код отладчика вынужден использовать высокий уровень запросов прерываний (interrupt request levels, IRQL). При IRQL, совпадающем с или превышающем IRQL диспетчера памяти последний не сможет загрузить отсутствующую страницу, т.к. операционная система будет блокировать прерывание Page Fault. Это приведет к краху операционной системы .

Отладку принято разделять на интерактивную и аварийную. При интерактивной локальной отладке отладчик выполняется в той же системе, что и объект отладки. При интерактивной удаленной отладке отладчик и объект отладки выполняется в разных системах. При отладке кода ядра система должна контролироваться, начиная с первых этапов ее загрузки, когда сеть еще не функционирует, поэтому для связи систем применяют простые последовательные интерфейсы, такие как COM, FireWire, USB. В последнее время, благодаря тенденциям развития виртуализации ПО на разных уровнях абстракций, все чаще привлекают виртуальные машины. Гостевая ОС выступает в качестве отлаживаемой, размещенная ОС включает интерфейс пользователя отладчика.

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

Основные средства отладки режима ядра предоставляются самим производителем операционной системы Windows в рамках свободно распространяемого пакета «Debugging Tools for Windows». Средства включают графический и консольный отладчики WinDbg и KD соответственно (далее Windows Debugger). Работа этих отладчиков опирается на механизмы, предусмотренные разработчиками операционной системы и заложенные в ее ядре.

Основным режимом для Windows Debugger является режим интерпретатора команд. Благодаря модульной структуре, наряду с поставляемыми разработчиками командами Windows Debugger поддерживает сторонние модули, называемыми расширениями. В действительности большинство встроенных команд также оформлено в виде расширений.

Windows Debugger ориентирован на удаленную интерактивную и аварийные отладки, при использовании которых раскрываются все его возможности. В тоже время полноценная локальная интерактивная отладка не поддерживается: отладчик позволяет только просматривать некоторые структуры ядра.

Существует модуль расширения для Windows Debugger под названием LiveKD, созданный Марком Руссиновичем, который в некотором смысле реализует локальную интерактивную отладку. LiveKD на ходу создает дамп памяти рабочей системы и использует его для отладки.

Пакет инструментов «Debugging Tools for Windows» регулярно обновляется и поддерживает все современные операционный системы Windows.

Отладчик ядра SoftICE, выпускавшийся компанией Compuware в пакете программ DriverStudio, традиционно выступал альтернативой пакету «Debugging Tools for Windows». Отличительной чертой SoftICE являлась реализация локальной интерактивной отладки на поддерживаемом аппаратном обеспечении. Отладчик практически полностью мог контролировать работу операционной системы.

С 3 апреля 2006 года продажа продуктов семейства «DriverStudio» было прекращена по причине «множества технических и деловых проблем, а также общего состояния рынка». Последней версией операционной системы, поддержка которой была реализована, является Windows XP Service Pack 2. Как правило, пакеты сервисных обновлений не изменяют прикладной интерфейс операционной системы, но номера системных вызовов и другая недокументированная информация может претерпевать изменение. Отладчик SoftICE опирался на жестко-прописанные адреса внутренних структур данных. Как следствие - с выходом Service Pack 3 совместимость была нарушена. Очевидно, что более поздние версии операционной системы Windows также не поддерживаются.

Syser Kernel Debugger создан небольшой китайской компанией Sysersoft как замена отладчику SoftICE. Первая финальная версия была выпущена в 2007 году. Как и SoftICE, Syser Kernel Debugger способен выполнять интерактивную отладку на работающей системе. Поддерживаемыми являются только 32-разрядные редакции современных версий Windows.

На данный момент Windows Debugger является основным инструментом среди разработчиков модулей ядра. Его также использует команда разработчиков ядра операционной системы Windows.

Отладчик - вторая после компилятора вещь, необходимая для создания программ. Однако многие из тех, кто пишет компьютерные программы и пользуется отладчиком, не в курсе того, каковы принципы и механизмы его работы.


Тяжело быть отладчиком...

В свете того, что программисты используют отладчик денно и нощно, особенно когда войдут в режим глубокой отладки, стоит сказать, что, будь отладчик не программой, а железякой, он бы наверняка перегрелся и сломался. Потому что столько работы, сколько её достаётся отладчику, не имеет даже компилятор.

Конечно, поскольку сейчас множество всяких различных языков программирования, то и отладчики для каждого из них свои. И, естественно, для разных категорий этих языков имеются различия в работе отладчиков: например, отладчик программ на интерпретируемом Ruby будет работать иначе, чем для компилируемого в байт-код языка Java, а отладчик для Java, в свою очередь, будет иметь отличия от отладчика Visual C++.

Я расскажу об отладке для платформы Windows. Поняв принципы работы отладчиков для неё, можно будет разобраться и с отладчиками под POSIX-системы, и с отладчиками, которые работают не на уровне операционной системы, а на уровне виртуальной машины или какого-либо интерпретатора.


Отладчики для Windows: два вида

Существуют два принципиально разных вида отладчиков под Windows. Думаю, с первыми сталкивались все, когда программировали на Delphi (не программировали на нём? С трудом верится. На чём же вы программировали в школе и на младших курсах?). Это отладчики пользовательских приложений. Их немало, и они существуют как по отдельности, так и (особенно, кстати, часто) в составе интегрированных сред разработки приложений. Среди отладчиков, распространяемых как отдельные программные продукты, традиционно выделяют OllyDbg, и о нём я когда-то писал в "Компьютерных вестях".

Второй вид отладчиков - это отладчики ядра операционной системы. Они встречаются и используются реже и по своему устройству значительно отличаются от отладчиков пользовательских приложений. Самый известный, и, одновременно, самый лучший из отладчиков ядра - это SoftIce. Возможно, вы о нём не только слышали, но даже пользовались.

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


Отладчик пользовательских приложений

Отладчик пользовательских приложений устроен проще, поскольку самую чёрную и грязную работу берёт на себя операционная система. В Windows есть специальные программные интерфейсы, которые предназначены для отладки приложений пользовательского уровня - называются они Windows Debugging API. Именно отладочными API пользуются все отладчики, которые встроены в популярные интегрированные среды разработки для Windows.

Для того чтобы отладка началась, отладчик должен запустить отлаживаемый процесс специальным образом - таким, чтобы система знала, что этот процесс будет находиться под отладкой. После этого начинается цикл отладки: программа выполняется до наступления определённого события, которое так и называется - отладочное событие, или debug event. При этом цикл отладки запускается в отдельном потоке, чтобы предотвратить зависание отладчика.

Но это только начало. Потому что самое интересное в работе отладчика начинается уже тогда, когда отладочное событие произошло. Ведь, по сути, в чём заключается работа отладчика? Чтобы помочь программисту локализовать ошибку с точностью до конкретной функции, конкретной операции, конкретной переменной. В этом нелёгком деле отладчику также может помочь операционная система.

Итак, отладочное событие произошло, и дальше надо каким-то образом узнать, как это связано с текстом программы. Это возможно только если в саму программу включена специальная отладочная информация - таблица отладочных символов. Она содержит в себе информацию о соответствии между адресами и именами функций, типов данных, номерами строк кода. Именно благодаря им возможна та отладка, с которой знаком каждый Windows-программист. Таблицы символов имеют разные форматы, а потому не всегда возможно отладить программу, скомпилированную компилятором одного разработчика, с помощью отладчика от другого производителя. Но, правда, самый распространённый формат всё же можно указать - это PDB (Program Database), и разработан он, естественно, корпорацией Microsoft.

Итак, если отладочная таблица символов имеет формат PDB, то можно воспользоваться специальным инструментом от корпорации Microsoft - символьным отладочным процессором. Когда-то он входил в ядро системы и назывался Imagehlp.dll, но уже давным-давно был выделен в отдельную библиотеку. Символьный процессор позволяет находить по заданному адресу ближайшую открытую функцию или глобальную переменную, а также номер строки и название файла с исходным текстом, в котором эта строка находится. Поддерживаются и обратные операции, например, поиск адреса функции по её имени.

Это, конечно, далеко не вся работа, которой занимается отладчик пользовательских приложений. Например, при отладке многопоточных приложений появляются многие очень тонкие моменты, связанные со взаимодействием потоков. Даже при отладке таких сравнительно простых вещей, как сервисы, есть свои нюансы.

Но на нюансах мы сейчас останавливаться не будем - в конце статьи я скажу вам, где о них почитать. Сейчас же давайте посмотрим на отладчики ядра.


Отладчик ядра

Отладчики ядра - программы намного более сложные, чем отладчики пользовательских приложений, и, полагаю, вполне понятно, почему: у них отсутствует помощник в виде операционной системы. Она в данном случае является их клиентом, ведь именно её они, в конечном счёте, должны отлаживать.

Большинство отладчиков ядра требует для своей работы два компьютера, соединённых нуль-модемным кабелем. Нуль-модем - это способ соединения двух компьютеров напрямую кабелем через их COM или LTP-порты. Второй компьютер нужен, потому что часть отладчика, сидящая на первом (на том, где установлена отлаживаемая система), имеет ограниченный доступ к аппаратному обеспечению, и поэтому весь вывод данных идёт по нуль-модему на второй компьютер.

В современных процессорах архитектуры Intel x86 имеются специальные отладочные регистры (и в стареньком 368-м, и в более новых моделях процессоров их всего восемь, они именуются как DR0-DR7). Эти регистры позволяют отладчику ставить контрольные точки на чтение и запись памяти, а также на порты ввода-вывода. В общем виде всё выглядит именно так, и я не думаю, что стоит сейчас расписывать подробно, за что отвечает каждый из отладочных регистров, какими прерываниями реализуются точки останова и давать прочую подобную информацию. Лучше расскажу о конкретных существующих отладчиках ядра для Windows.

Ну, во-первых, это отладчик, встроенный в само ядро операционной системы. Он есть во всех ОС линейки NT, начиная с Windows 2000. Это часть файла NTOSKRNL.EXE, и включить его можно, задав опцию "/Debug" для операционной системы в BOOT.INI. Этот отладчик нуждается в нуль-модемном соединении и втором компьютере с такой же ОС.

Есть ещё один отладчик ядра от Microsoft - WinDBG. Строго говоря, это не отладчик ядра, а гибридный отладчик, который можно использовать и для отладки приложений уровня пользователя. Он, в отличие от отладчика, встроенного в ядро, имеет графическую оболочку, а потому пользоваться им проще. Этот отладчик также поддерживает специальные расширения, которые могут пригодиться при решении некоторых задач отладки. Но и он для отладки ядра требует два компьютера.

Однако существует отладчик ядра, который может осуществлять отладку на одном-единственном компьютере. Это SoftIce. При этом SoftIce умеет отлаживать и прикладные программы. Использование этого отладчика для пользовательских программ оправдано, например, в случае отладки систем реального времени, привязываемых к системному таймеру. Если выполнять отладку с помощью обыкновенного отладчика, то результат может оказаться неправильным даже при правильной работе программы, а SoftIce остановит и программу, и таймер. Это полезно при отладке многопоточных приложений. Ко всему прочему, SoftIce имеет очень, очень хорошо развитые средства вывода информации обо всех потоках в системе, о синхронизации потоков для многопоточных приложений, информации о handle"ах... Единственный минус этого отладчика - его сложность для прикладного программиста. Но из отладчиков ядра это самый простой и эффективный.


Для самых любознательных

Сейчас, конечно, разговор об отладчиках для Windows-приложений не так актуален, как ещё лет десять назад. Весь мир заинтересовался Интернетом, и основными пользователями SoftIce стали крякеры, неутомимые труженики на ниве пиратства. Тем не менее, это не так уж плохо. Общение с SoftIce"ом, несомненно, развивает человека в плане знаний о компьютере, хотя, конечно, если общаться только с отладчиками и не общаться с живыми людьми, возможны некоторые побочные эффекты. Ну, об этом, я думаю, все и так догадываются.

Отладчики - одни из самых своеобразных видов программного обеспечения, но в плане разработки даже отладчики программ уровня пользователя довольно сложны. Но, тем не менее, если у вас есть желание и время для того, чтобы разработать собственный отладчик, ваши знания в области операционных систем и программирования существенно возрастут, а значит, возрастут и шансы на высокооплачиваемую работу.

Итак, если вам хочется создать собственный отладчик, то сначала стоит ознакомиться с материалами на эту тему. На мой взгляд, самым лучшим пособием для начала будет книга Джона Роббинса "Отладка Windows-приложений". Она уже старая, 2001-го года издания, но информация, изложенная в ней, актуальна и сейчас, поскольку имеет общий, даже в некотором роде фундаментальный характер. В этой книге есть примеры написания отладчиков для Windows, кроме того, она пригодится вам, если вы программируете на C++ и хотите лучше разобраться в обработке исключений. Собственно, именно из этой книги я и почерпнул сведения об отладчиках, изложенные в статье. Если же найти эту книгу не получится (всё-таки, она уже довольно старая), есть несколько адресов, которые могут вам пригодиться. Первый - вот такой: www.xakep.ru/post/19158/default.asp . Эта статья из журнала "Хакер" несколько подробнее рассказывает об отладчиках ядра, чем это сделал я, а кроме того, в ней приведён код простейшего отладчика. А по адресу kalashnikoff.ru/Assembler/issues/016.htm можно узнать о том, как написать DOS-отладчик. Но, конечно, лучше всего читать MSDN и попутно найти какой-нибудь отладчик с открытыми исходными текстами, чтобы в нём разобраться. Ну и, конечно, если вы взялись за написание отладчика, то успехов вам в этом нелёгком деле!

чПФ ОЕЛПФПТЩЕ ХЛБЪБОЙС РП ТБВПФЕ У ПФМБДЛПК СДТБ У БЧБТЙКОЩНЙ ДБНРБНЙ РБНСФЙ. лБЛ РТБЧЙМП, ЧБН ОХЦОП ВХДЕФ ЪБДБФШ ПДОП ЙЪ ХУФТПКУФЧ РПДЛБЮЛЙ, РЕТЕЮЙУМЕООЩИ Ч ЖБКМЕ /etc/fstab . уВТПУ ПВТБЪПЧ РБНСФЙ ОБ ХУФТПКУФЧБ, ОЕ СЧМСАЭЙЕУС ХУФТПКУФЧБНЙ РПДЛБЮЛЙ, ОБРТЙНЕТ, МЕОФЩ, Ч ДБООЩК НПНЕОФ ОЕ РПДДЕТЦЙЧБАФУС.

Note: йУРПМШЪХКФЕ ЛПНБОДХ dumpon (8) ДМС ХЛБЪБОЙС СДТХ НЕУФБ, ЗДЕ ОХЦОП УПИТБОСФШ БЧБТЙКОЩЕ ДБНРЩ. рПУМЕ ОБУФТПКЛЙ РП ЛПНБОДЕ swapon (8) ТБЪДЕМБ РПДЛБЮЛЙ ДПМЦОБ ВЩФШ ЧЩЪЧБОБ РТПЗТБННБ dumpon . пВЩЮОП ЬФП ЧЩРПМОСЕФУС ЪБДБОЙЕН РЕТЕНЕООПК dumpdev Ч ЖБКМЕ rc.conf (5) . еУМЙ ЪБДБОБ ЬФБ РЕТЕНЕООБС, ФП РПУМЕ УВПС РТЙ РЕТЧПК НОПЗПРПМШЪПЧБФЕМШУЛПК РЕТЕЪБЗТХЪЛЕ ВХДЕФ БЧФПНБФЙЮЕУЛЙ ЪБРХЭЕОБ РТПЗТБННБ savecore (8) . пОБ УПИТБОЙФ БЧБТЙКОЩК ДБНР СДТБ Ч ЛБФБМПЗ, ЪБДБООЩК Ч РЕТЕНЕООПК dumpdir ЖБКМБ rc.conf . рП ХНПМЮБОЙА ЛБФБМПЗПН ДМС БЧБТЙКОЩИ ДБНРПЧ СЧМСЕФУС /var/crash .

мЙВП ЧЩ НПЦЕФЕ ЪБДБФШ ХУФТПКУФЧП ДМС УВТПУБ ПВТБЪБ РБНСФЙ СЧОП ЮЕТЕЪ РБТБНЕФТ dump Ч УФТПЛЕ config ЛПОЖЙЗХТБГЙПООПЗП ЖБКМБ ЧБЫЕЗП СДТБ. фБЛПК УРПУПВ ЙУРПМШЪПЧБФШ ОЕ ТЕЛПНЕОДХЕФУС Й ПО ДПМЦЕО ЙУРПМШЪПЧБФШУС, ФПМШЛП ЕУМЙ ЧЩ ИПФЙФЕ РПМХЮБФШ БЧБТЙКОЩЕ ПВТБЪЩ РБНСФЙ СДТБ, ЛПФПТПЕ БЧБТЙКОП ЪБЧЕТЫБЕФ УЧПА ТБВПФХ РТЙ ЪБЗТХЪЛЕ.

Note: дБМЕЕ ФЕТНЙО gdb ПЪОБЮБЕФ ПФМБДЮЙЛ gdb , ЪБРХЭЕООЩК Ч ``ТЕЦЙНЕ ПФМБДЛЙ СДТБ"". рЕТЕИПД Ч ЬФПФ ТЕЦЙН ДПУФЙЗБЕФУС ЪБРХУЛПН gdb У РБТБНЕФТПН -k . ч ТЕЦЙНЕ ПФМБДЛЙ СДТБ gdb ЙЪНЕОСЕФ УЧПЈ РТЙЗМБЫЕОЙЕ ОБ (kgdb) .

Tip: еУМЙ ЧЩ ЙУРПМШЪХЕФЕ FreeBSD ЧЕТУЙЙ 3 ЙМЙ ВПМЕЕ ТБООАА, ЧЩ ДПМЦОЩ ЧЩРПМОЙФШ ХУЕЮЕОЙЕ ПФМБДПЮОПЗП СДТБ ЛПНБОДПК strip, Б ОЕ ХУФБОБЧМЙЧБФШ ВПМШЫПЕ ПФМБДПЮОПЕ СДТП:

# cp kernel kernel.debug # strip -g kernel

ьФПФ ЫБЗ ОЕ ФБЛ ХЦ Й ОЕПВИПДЙН, ОП ТЕЛПНЕОДХЕН. (чП FreeBSD 4 Й ВПМЕЕ РПЪДОЙИ ТЕМЙЪБИ ЬФПФ ЫБЗ ЧЩРПМОСЕФУС БЧФПНБФЙЮЕУЛЙ Ч ЛПОГЕ РТПГЕУУБ РПУФТПЕОЙС СДТБ make .) лПЗДБ СДТП ХУЕЮЕОП, БЧФПНБФЙЮЕУЛЙ ЙМЙ РТЙ РПНПЭЙ ЛПНБОД ЧЩЫЕ, ЧЩ НПЦЕФЕ ХУФБОПЧЙФШ ЕЗП ПВЩЮОЩН ПВТБЪПН, ОБВТБЧ make install .

ъБНЕФШФЕ, ЮФП Ч УФБТЩИ ЧЕТУЙСИ FreeBSD (ДП 3.1, ОЕ ЧЛМАЮБС ЬФПФ ТЕМЙЪ), ЙУРПМШЪХЕФУС СДТБ Ч ЖПТНБФЕ a.out, РПЬФПНХ ЙИ ФБВМЙГЩ УЙНЧПМПЧ ДПМЦОЩ ТБУРПМБЗБФШУС РПУФПСООП Ч РБНСФЙ. у ВПМШЫПК ФБВМЙГЕК УЙНЧПМПЧ Ч ОЕ ХУЕЮЕООПН ПФМБДПЮОПН СДТЕ ЬФП ЙЪМЙЫОСС ФТБФБ. рПУМЕДОЙЕ ТЕМЙЪЩ FreeBSD ЙУРПМШЪХАФ СДТБ Ч ЖПТНБФЕ ELF, ЗДЕ ЬФП ОЕ СЧМСЕФУС РТПВМЕНПК.

еУМЙ ЧЩ ФЕУФЙТХЕФЕ ОПЧПЕ СДТП, УЛБЦЕН, ОБВЙТБС ЙНС ОПЧПЗП СДТБ Ч РТЙЗМБЫЕОЙЙ ЪБЗТХЪЮЙЛБ, ОП ЧБН ОХЦОП ЪБЗТХЦБФШ Й ТБВПФБФШ У ДТХЗЙН СДТПН, ЮФПВЩ УОПЧБ ЧЕТОХФШУС Л ОПТНБМШОПНХ ЖХОЛГЙПОЙТПЧБОЙА, ЪБЗТХЦБКФЕ ЕЗП ФПМШЛП Ч ПДОПРПМШЪПЧБФЕМШУЛПН ТЕЦЙНЕ РТЙ РПНПЭЙ ЖМБЗБ -s , ХЛБЪЩЧБЕНПЗП РТЙ ЪБЗТХЪЛЕ, Б ЪБФЕН ЧЩРПМОЙФЕ ФБЛЙЕ ЫБЗЙ:

# fsck -p # mount -a -t ufs # so your filesystem for /var/crash is writable # savecore -N /kernel.panicked /var/crash # exit # ...to multi-user

ьФБ РПУМЕДПЧБФЕМШОПУФШ ХЛБЪЩЧБЕФ РТПЗТБННЕ savecore (8) ОБ ЙУРПМШЪПЧБОЙЕ ДТХЗПЗП СДТБ ДМС ЙЪЧМЕЮЕОЙС УЙНЧПМЙЮЕУЛЙИ ЙНЕО. йОБЮЕ ПОБ ВХДЕФ ЙУРПМШЪПЧБФШ СДТП, ТБВПФБАЭЕЕ Ч ДБООЩК НПНЕОФ Й, УЛПТЕЕ ЧУЕЗП, ОЙЮЕЗП ОЕ УДЕМБЕФ, РПФПНХ ЮФП БЧБТЙКОЩК ПВТБЪ РБНСФЙ Й УЙНЧПМЩ СДТБ ВХДХФ ПФМЙЮБФШУС.

б ФЕРЕТШ, РПУМЕ УВТПУБ БЧБТЙКОПЗП ДБНРБ, РЕТЕКДЙФЕ Ч ЛБФБМПЗ /sys/compile/WHATEVER Й ЪБРХУФЙФЕ ЛПНБОДХ gdb -k . йЪ РТПЗТБННЩ gdb УДЕМБКФЕ ЧПФ ЮФП:

Symbol-file kernel.debug exec-file /var/crash/kernel.0 core-file /var/crash/vmcore.0 Й ЧХБМС - ЧЩ НПЦЕФЕ ПФМБЦЙЧБФШ БЧБТЙКОЩК ДБНР, ЙУРПМШЪХС ЙУИПДОЩЕ ФЕЛУФЩ СДТБ ФПЮОП ФБЛЦЕ, ЛБЛ ЧЩ ЬФП ДЕМБЕФЕ У МАВПК ДТХЗПК РТПЗТБННПК.

чПФ ЦХТОБМ ЛПНБОД УЕБОУБ ТБВПФЩ gdb , ЙММАУФТЙТХАЭЙК ЬФХ РТПГЕДХТХ. дМЙООЩЕ УФТПЛЙ ВЩМЙ ТБЪПТЧБОЩ ДМС ХМХЮЫЕОЙС ЮЙФБВЕМШОПУФЙ Й ДМС ХДПВУФЧБ УФТПЛЙ ВЩМЙ РТПОХНЕТПЧБОЩ. чУЕ ПУФБМШОПЕ СЧМСЕФУС ФТБУУЙТПЧЛПК ПЫЙВЛЙ, ТЕБМШОП ЧПЪОЙЛОХЧЫЕК ЧП ЧТЕНС ТБВПФЩ ОБД ДТБКЧЕТПН ЛПОУПМЙ pcvt.

1:Script started on Fri Dec 30 23:15:22 1994 2: # cd /sys/compile/URIAH 3: # gdb -k kernel /var/crash/vmcore.1 4:Reading symbol data from /usr/src/sys/compile/URIAH/kernel ...done. 5:IdlePTD 1f3000 6:panic: because you said to! 7:current pcb at 1e3f70 8:Reading in symbols for ../../i386/i386/machdep.c...done. 9: (kgdb) where 10:#0 boot (arghowto=256) (../../i386/i386/machdep.c line 767) 11:#1 0xf0115159 in panic () 12:#2 0xf01955bd in diediedie () (../../i386/i386/machdep.c line 698) 13:#3 0xf010185e in db_fncall () 14:#4 0xf0101586 in db_command (-266509132, -266509516, -267381073) 15:#5 0xf0101711 in db_command_loop () 16:#6 0xf01040a0 in db_trap () 17:#7 0xf0192976 in kdb_trap (12, 0, -272630436, -266743723) 18:#8 0xf019d2eb in trap_fatal (...) 19:#9 0xf019ce60 in trap_pfault (...) 20:#10 0xf019cb2f in trap (...) 21:#11 0xf01932a1 in exception:calltrap () 22:#12 0xf0191503 in cnopen (...) 23:#13 0xf0132c34 in spec_open () 24:#14 0xf012d014 in vn_open () 25:#15 0xf012a183 in open () 26:#16 0xf019d4eb in syscall (...) 27: (kgdb) up 10 28:Reading in symbols for ../../i386/i386/trap.c...done. 29:#10 0xf019cb2f in trap (frame={tf_es = -260440048, tf_ds = 16, tf_\ 30:edi = 3072, tf_esi = -266445372, tf_ebp = -272630356, tf_isp = -27\ 31:2630396, tf_ebx = -266427884, tf_edx = 12, tf_ecx = -266427884, tf\ 32:_eax = 64772224, tf_trapno = 12, tf_err = -272695296, tf_eip = -26\ 33:6672343, tf_cs = -266469368, tf_eflags = 66066, tf_esp = 3072, tf_\ 34:ss = -266427884}) (../../i386/i386/trap.c line 283) 35:283 (void) trap_pfault(&frame, FALSE); 36: (kgdb) frame frame->tf_ebp frame->tf_eip 37:Reading in symbols for ../../i386/isa/pcvt/pcvt_drv.c...done. 38:#0 0xf01ae729 in pcopen (dev=3072, flag=3, mode=8192, p=(struct p\ 39:roc *) 0xf07c0c00) (../../i386/isa/pcvt/pcvt_drv.c line 403) 40:403 return ((*linesw.l_open)(dev, tp)); 41: (kgdb) list 42:398 43:399 tp->t_state |= TS_CARR_ON; 44:400 tp->t_cflag |= CLOCAL; /* cannot be a modem (:-) */ 45:401 46:402 #if PCVT_NETBSD || (PCVT_FREEBSD >= 200) 47:403 return ((*linesw.l_open)(dev, tp)); 48:404 #else 49:405 return ((*linesw.l_open)(dev, tp, flag)); 50:406 #endif /* PCVT_NETBSD || (PCVT_FREEBSD >= 200) */ 51:407 } 52: (kgdb) print tp 53:Reading in symbols for ../../i386/i386/cons.c...done. 54:$1 = (struct tty *) 0x1bae 55: (kgdb) print tp->t_line 56:$2 = 1767990816 57: (kgdb) up 58:#1 0xf0191503 in cnopen (dev=0x00000000, flag=3, mode=8192, p=(st\ 59:ruct proc *) 0xf07c0c00) (../../i386/i386/cons.c line 126) 60: return ((*cdevsw.d_open)(dev, flag, mode, p)); 61: (kgdb) up 62:#2 0xf0132c34 in spec_open () 63: (kgdb) up 64:#3 0xf012d014 in vn_open () 65: (kgdb) up 66:#4 0xf012a183 in open () 67: (kgdb) up 68:#5 0xf019d4eb in syscall (frame={tf_es = 39, tf_ds = 39, tf_edi =\ 69: 2158592, tf_esi = 0, tf_ebp = -272638436, tf_isp = -272629788, tf\ 70:_ebx = 7086, tf_edx = 1, tf_ecx = 0, tf_eax = 5, tf_trapno = 582, \ 71:tf_err = 582, tf_eip = 75749, tf_cs = 31, tf_eflags = 582, tf_esp \ 72:= -272638456, tf_ss = 39}) (../../i386/i386/trap.c line 673) 73:673 error = (*callp->sy_call)(p, args, rval); 74: (kgdb) up 75:Initial frame selected; you cannot go up. 76: (kgdb) quit 77: # exit 78:exit 79: 80:Script done on Fri Dec 30 23:18:04 1994

лПННЕОФБТЙЙ Л ЧЩЫЕРТЙЧЕДЕООПНХ ЦХТОБМХ:

УФТПЛБ 6:

ьФП ДБНР, ЧЪСФЩК РТЙ РПНПЭЙ DDB (УНПФТЙ ОЙЦЕ), РПЬФПНХ ЛПННЕОФБТЙК Л БЧБТЙКОПНХ ПУФБОПЧХ ЙНЕЕФ ЙНЕООП ЧЙД ``because you said to!"" Й ФТБУУЙТПЧЛБ УФЕЛБ ЗМХВПЛБ; ПДОБЛП ЙЪОБЮБМШОПК РТЙЮЙОПК РЕТЕИПДБ Ч DDB ВЩМБ БЧБТЙКОБС ПУФБОПЧЛБ РТЙ ЧПЪОЙЛОПЧЕОЙА ПЫЙВЛЙ УФТБОЙГЩ РБНСФЙ.

УФТПЛБ 20:

ьФП НЕУФПОБИПЦДЕОЙЕ ЖХОЛГЙЙ trap() Ч ФТБУУЙТПЧЛЕ УФЕЛБ.

УФТПЛБ 36:

рТЙОХДЙФЕМШОПЕ ЙУРПМШЪПЧБОЙЕ ОПЧПК ЗТБОЙГЩ УФЕЛБ; ФЕРЕТШ ЬФП ОЕ ОХЦОП. рТЕДРПМБЗБЕФУС, ЮФП ЗТБОЙГЩ УФЕЛБ ХЛБЪЩЧБАФ ОБ РТБЧЙМШОПЕ ТБУРПМПЦЕОЙЕ, ДБЦЕ Ч УМХЮБЕ БЧБТЙКОПЗП ПУФБОПЧБ. зМСДС ОБ УФТПЛХ ЙУИПДОПЗП ЛПДБ 403, НПЦОП УЛБЪБФШ, ЮФП ЧЕУШНБ ЧЕТПСФОП, ЮФП МЙВП ЧЙОПЧБФ ДПУФХР РП ХЛБЪБФЕМА ``tp"", МЙВП ВЩМ ЧЩИПД ЪБ ЗТБОЙГЩ НБУУЙЧБ.

УФТПЛБ 52:

рПИПЦЕ, ЮФП ЧЙОПЧБФ ХЛБЪБФЕМШ, ОП ПО СЧМСЕФУС ДПРХУФЙНЩН БДТЕУПН.

УФТПЛБ 56:

пДОБЛП, ПЮЕЧЙДОП, ЮФП ПО ХЛБЪЩЧБЕФ ОБ НХУПТ, ФБЛ ЮФП НЩ ОБЫМЙ ОБЫХ ПЫЙВЛХ! (дМС ФЕИ, ЛФП ОЕ ЪОБЛПН У ЬФПК ЮБУФША ЛПДБ: tp->t_line УМХЦЙФ ДМС ИТБОЕОЙС ТЕЦЙНБ ЛБОБМБ ЛПОУПМШОПЗП ХУФТПКУФЧБ, Й ЬФП ДПМЦОП ВЩФШ ДПУФБФПЮОП НБМЕОШЛПЕ ГЕМПЕ ЮЙУМП.)

Эта серия статей появилась по двум причинам. Во-первых, мне нравится работать с проектом HackSysExtremeVulnerableDriver . Во-вторых, я получил массу пожеланий , чтобы осветить эту тему.

Весь код, используемый при написании этой серии, находится в моем репозитории .

В данном цикле статей мы рассмотрим написание эксплоитов уровня ядра в ОС Windows. Важно отметить, что мы будем иметь дело с известными уязвимостями, и в реверс-инжиниринге нет необходимости (по крайней мере, для драйвера).

Предполагается, что после ознакомления со всеми статьями вы будете знать все наиболее распространенные классы брешей и методы эксплуатации, а также сможете портировать эксплоиты с архитектуры x86 на архитектуру x64 (если возможно) и ознакомитесь с новыми методами защиты в Windows 10.

Схема отладки ядра

В отличие от отладки на уровне пользователя, когда приостанавливается выполнение отдельного процесса, на уровне ядра задействуется вся система, и мы не сможем воспользоваться этим методом. Соответственно, нужна отдельная отладочная машина, которая сможет осуществлять коммуникацию с системой, где отлаживается ядро, просматривать память и структуры ядра, а также отлавливать крахи системы.

Дополнительный материал для изучения:

Эксплуатация уязвимостей ядра

Этот процесс проходит намного веселее, чем эксплуатация на уровне пользователя J.

Главная цель – добиться привилегированного выполнения в контексте ядра. А дальше уже все зависит от нашего воображения, начиная от застолья с домашним пивом и заканчивая внедрением вредоносов, спонсируемых государством.
В целом, наша задача заключается в том, чтобы получить шелл с системными привилегиями.

Темы статей этого цикла

  • Часть 1: Настройка рабочей среды
    • Конфигурирование трех виртуальных машин и системы, которая будет выступать в роли отладчика.
    • Конфигурирование отладчика WinDBG.
  • Часть 2: Полезные нагрузки
    • Изучение наиболее распространенных полезных нагрузок. В последующих частях будут рассматриваться конкретные уязвимости и, при необходимости, указываться ссылки на эту статью.
  • Остальные части.
    • Рассмотрение уязвимостей.

Жизненный цикл разработки эксплоита уровня ядра

  • Нахождение уязвимости . Эта тема не будет рассматриваться в данном цикле, поскольку мы уже точно знаем, где находятся бреши.
  • Перехват потока выполнения . Некоторые уязвимости предусматривают выполнение кода, для некоторых есть дополнительные требования.
  • Расширение привилегий . Главная цель – получить шелл с системными привилегиями.
  • Восстановление потока выполнения . Неучтенные исключения на уровне ядра приводят к краху системы. Если вы не собираетесь писать эксплоит для DoS-атаки, следует учитывать этот факт.

Типы целевых систем

Мы будем работать с уязвимостями в следующих системах (конкретная версия не принципиальна):

  • Win7 x86 VM
  • Win7 x64 VM
  • Win10 x64 VM

Начнем с архитектуры x86, и далее будем портировать эксплоит для системы Win7 x64. Некоторые эксплоиты не будут запускать на машинах с Win10 из-за присутствия новых защит. В этом случае мы либо будем изменять логику работы эксплоита, либо будем использовать полностью другой подход.

Используемое программное обеспечение:

  • Гипервизор (масса вариантов).
  • Windows 7 x86 VM
  • Windows 7 x64 VM
  • Windows 10 x64 VM

Настройка систем для отладки

Отладочные системы, с которыми мы будем взаимодействовать, предназначены для загрузки уязвимого драйвера. На этих машинах часто будут возникать крахи, поскольку большинство исключений в ядре способствуют явлениям подобного рода. Необходимо выделить достаточно оперативной памяти для этих систем.

На каждой машине, которая будет отлаживаться, нужно сделать следующее:

  • Внутри директории VirtualKD запустите файл target\vminstall.exe. Добавится новая загрузочная запись и будут доступны функции отладки и автоматическое подключение к серверу VirtualKD, установленному в системе, которая выступает в роли отладчика.

В случае с Windows 10 VM необходимо включить режим test signing, который позволяет загружать неподписанные драйвера в ядро.

После выполнения команды bcdedit /set testsinging on и перезагрузки на рабочем столе появится надпись «Test Mode».

Краткое описание модуля HEVD

Процедура DriverEntry является стартовой для каждого драйвера:

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) {
UINT32 i = 0;
PDEVICE_OBJECT DeviceObject = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
UNICODE_STRING DeviceName, DosDeviceName = {0};

UNREFERENCED_PARAMETER(RegistryPath);
PAGED_CODE();

RtlInitUnicodeString(&DeviceName, L"\\Device\\HackSysExtremeVulnerableDriver");
RtlInitUnicodeString(&DosDeviceName, L"\\DosDevices\\HackSysExtremeVulnerableDriver");

// Create the device
Status = IoCreateDevice(DriverObject,
0,
&DeviceName,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&DeviceObject);

  • Эта процедура содержит вызов функции IoCreateDevice, содержащей имя драйвера, которое мы будем использовать во время коммуникации.
  • В объект DriverObject будут добавлены нужные структуры и указатели на функции.
  • Для нас важен указатель функции, связанный с процедурой DriverObject->MajorFunction , отвечающей за обработку IOCTL (I/O Control; управление вводом/выводом);
  • В HEVD эта функция называется IrpDeviceIoCtlHandler , которая представляет собой большое условное выражение со множеством ответвлений для каждого IOCTL. Каждая уязвимость имеет уникальный IOCTL.

Пример: HACKSYS_EVD_IOCTL_STACK_OVERFLOW представляет собой IOCTL, используемый для активации бреши, связанной с переполнением стека.

На этом первая часть завершается. В следующей статье мы поговорим о полезных нагрузках. На данный момент доступна только полезная нагрузка, предназначенная для кражи токенов, которая будет использоваться в третьей части.

P.S. Я понимаю, что существует масса тонкостей и проблем, с которыми вы можете столкнуться. Поскольку в этом цикле основное внимание уделяется разработке эксплоитов, вам придется решать все попутные проблемы самостоятельно. Однако все возникающие вопросы вы можете задавать в комментариях.

Иногда у меня возникает ситуация, когда Windows ожидает время загрузки для отладчика ядра. Вы видите текст «Windows start», но не логотип.

Если я присоединяю отладчик сейчас, воспроизводится анимация логотипа Windows 7. После этого логотип начинает пульсировать. На этом этапе процесс загрузки больше не продвигается. Загрузка процессора снижается до минимума. Я жду обычно несколько минут, но ничего не происходит.

Это происходит не всегда. Однако, если это произойдет, сброс VM не поможет. Для устранения этой проблемы мне нужно использовать ремонт при запуске. К сожалению, это длится вечно.

Любые идеи, что я могу сделать, кроме запуска ремонта при запуске?

Заранее спасибо!

3

2 ответы

Чтобы устранить проблему, с которой вы столкнулись, достаточно просто нажать F10 во время загрузки. И удалить/отладить и связанные параметры. Затем нажмите enter.

Предложение: Не используйте параметр/debug для параметра меню загрузки по умолчанию. Скопируйте конфигурацию загрузки в новую запись. Затем установите его в режим отладки. Windows не знает, когда вы будете использовать отладчик. Поэтому он должен ждать.