Электронная библиотека диссертаций и авторефератов России
dslib.net
Библиотека диссертаций
Навигация
Каталог диссертаций России
Англоязычные диссертации
Диссертации бесплатно
Предстоящие защиты
Рецензии на автореферат
Отчисления авторам
Мой кабинет
Заказы: забрать, оплатить
Мой личный счет
Мой профиль
Мой авторский профиль
Подписки на рассылки



расширенный поиск

Разработка метода, алгоритмов и программ для автоматического поиска уязвимостей программного обеспечения в условиях отсутствия исходного кода Благодаренко, Артем Васильевич

Разработка метода, алгоритмов и программ для автоматического поиска уязвимостей программного обеспечения в условиях отсутствия исходного кода
<
Разработка метода, алгоритмов и программ для автоматического поиска уязвимостей программного обеспечения в условиях отсутствия исходного кода Разработка метода, алгоритмов и программ для автоматического поиска уязвимостей программного обеспечения в условиях отсутствия исходного кода Разработка метода, алгоритмов и программ для автоматического поиска уязвимостей программного обеспечения в условиях отсутствия исходного кода Разработка метода, алгоритмов и программ для автоматического поиска уязвимостей программного обеспечения в условиях отсутствия исходного кода Разработка метода, алгоритмов и программ для автоматического поиска уязвимостей программного обеспечения в условиях отсутствия исходного кода
>

Диссертация - 480 руб., доставка 10 минут, круглосуточно, без выходных и праздников

Автореферат - бесплатно, доставка 10 минут, круглосуточно, без выходных и праздников

Благодаренко, Артем Васильевич. Разработка метода, алгоритмов и программ для автоматического поиска уязвимостей программного обеспечения в условиях отсутствия исходного кода : диссертация ... кандидата технических наук : 05.13.19 / Благодаренко Артем Васильевич; [Место защиты: Юж. федер. ун-т].- Таганрог, 2011.- 140 с.: ил. РГБ ОД, 61 12-5/251

Содержание к диссертации

Введение

1. Анализ методов автоматического поиска уязвимостей по без исходного кода 11

1.1. Основные понятия 11

1.1.1 Проблема уязвимости ПО //

1.1.2 Задача поиска уязвимостей 16

1.2. Обзор методов, используемых при тестировании 20

1.2.1 Профилактика уязвимостей 20

1.2.2 Модульное и интеграционное тестирование 21

1.2.3 Внедрение ошибок и отслеживание утечки ресурсов 24

1.3. Обзор методов, используемых при верификации по 27

1.3.1 Ручной поиск уязвимостей 27

1.3.2 Поиск шаблонов уязвимостей 30

1.3.3 Тестирование методом ««серого ящика»» 32

1.3.4 Фаззинг в памяти

1.4. Цель и задачи работы 37

1.5. Выводы 40

2. Разработка алгоритма оценки покрытия кода тестами

2.1. выбор критерия оценки покрытия кода тестами 42

2.1.1 Классы критериев 42

2.1.2 Интегральная структурная оценка степени завершенности тестирования ПО 45

2.2. Алгоритм оценки покрытия кода 46

2.2.1 Оценка покрытия с использованием датчиков исполнения 46

2.2.2 Расстановка датчиков исполнения на основе статического анализа 47

2.2.3 Расстановка датчиков исполнения на основе динамического анализа 49

2.3. Отслеживание реакции по на входные данные 52

2.3.1 Динамическая оценка покрытия 52

2.3.2 Сохранение значений параметров функций 53

2.4. Оценка покрытия с использованием аппаратных средств процессора 55

2.4.1 Аппаратные средства отладки процессоров Intel. 55

2.4.2 Разработка средства оценки покрытия кода на основе аппаратных возможностей 60

2.5. Выводы 63

3. Разработка алгоритмов обеспечения и улучшения покрытия кода тестами 64

3.1. Алгоритм обеспечения кода тестами 64

3.1.1 Аналог модульного тестирования для ПО в условиях отсутствия исходного кода... 64

3.1.2 Критерий оценки охвата тестирования

3.1.3 Оценка охвата тестирования 67

3.1.4 Алгоритм внедрения данных. Генерация интерфейсов 70

3.1.5 Алгоритм внедрения данных. Динамическая компоновка 76

3.2. Генерация тестовых данных 78

3.2.1 Использование данных, поступающих из спутникового канала 78

3.3. АЛГОРИТМ улучшения покрытия тестами 81

3.3.1 Алгоритм последовательного внедрения ошибок 81

3.3.2 Последовательное внедрение ошибок в бинарный код 86

3.4. Выводы 88

4. Разработка метода автоматического поиска уязвимостеи по в условиях отсутствия исходного кода 88

4.1. ОПИСАНИЕ 88

4.1.1 Входные данные 88

4.1.2 Выходные данные 89

4.1.3 Ограничения 89

4.1.4 Последовательность шагов 89

4.1.5 Выбор точки внедрения данных 90

4.1.6 Выбор цели 90

4.1.7 Предварительный анализ 91

4.1.8 Выбор точки внедрения данных 92

4.2. Внедрение данных и отслеживание реакции 95

4.2.1 Подготовка тестов 95

4.2.2 Пример создания теста 96

4.2.3 Исполнение теста 97

4.2.4 Оценка результатов

4.3. Анализ уязвимости на основе данных о сбое 100

4.4. Выводы 104

5. Экспериментальное исследование метода, алгоритмов и программ 105

5.1. Описание системы 105

5.2. Сравнение предложенного алгоритма оценки покрытия с аналогами 108

5.2.1 Сравнение с алгоритмом, использующим аппаратны е средства 108

5.3. Сравнение предложенного алгоритма внедрения данных с аналогами 112

5.3.1 Сравнение с тестированием «черного ящика» в памяти 112

5.4. Сравнение предложенного алгоритма увеличения покрытия с аналогами 114

5.4.1 Алгоритм последовательного внедрения ошибок в действии 114

5.4.2 Сравнение с алгоритмом случайного внедрения ошибок. 120

5.5. Выводы 123

Заключение 124

Список использованных источников 126

Введение к работе

Актуальность Диссертационная работа посвящена актуальной проблеме разработки методов обнаружения уязвимостей в программном обеспечении (ПО) без исходных кодов. В последние годы задача обнаружения уязвимостей в ПО становится все более актуальной. В редких случаях технологии разработки ПО, применяемые разработчиками, включают обязательный этап тестирования безопасности и проверки функциональной безопасности. При таком подходе остается надеяться на то, что уязвимости обнаружатся на этапе функционального тестирования, хотя данный этап на это не нацелен. В значительном числе случаев предварительное функциональное тестирование продукта не решает данную задачу, и уязвимости обнаруживаются уже после выпуска программного обеспечения в эксплуатацию. При этом уязвимое программное обеспечение может эксплуатироваться годами до момента обнаружения уязвимости производителем. Эти факторы приводят к тому, что основной технической причиной нарушения безопасности информационных систем (ИС) является эксплуатация уязвимостей, содержащихся в программном обеспечении ИС. Данная ситуация является недопустимой, если речь идет об информационных системах, к которым предъявляются высокие требования по безопасности функционирования.

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

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

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

Таким образом, задача разработки и исследования методов поиска уязвимостей в ПО без исходных кодов требует проведения интенсивных исследований и является актуальной.

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

Исходя из основной цели данной работы, определяется перечень решаемых задач:

  1. Разработка алгоритма оценки покрытия программного обеспечения тестами, позволяющего оценить степень завершенности процесса тестирования.

  2. Разработка принципа обеспечения покрытия на основе методологии модульного тестирования и фаззинга в памяти, который уменьшает объем входных данных для обеспечения заданного покрытия.

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

Основные положения, выносимые на защиту

  1. Разработанный метод поиска уязвимостей в ПО в условиях отсутствия исходного кода позволяет автоматически исследовать ПО на наличие уязвимостей по его исполняемым файлам.

  2. Разработанный алгоритм оценки покрытия ПО тестами в условиях отсутствия исходного кода предоставляет количественную характеристику завершения тестовых испытаний.

  3. Разработанный принцип обеспечения покрытия тестами ПО без исходных кодов позволяет автоматически выбирать точку внедрения, что позволяет уменьшить объем входных данных, для обеспечения заданного покрытия.

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

Научная новизна работы заключается в следующем:

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

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

Практическая значимость результатов диссертации заключается в

следующем:

  1. Разработанный алгоритм оценки покрытия может быть использован в процессе поиска уязвимостей в ПО в условиях отсутствия исходного кода. При этом будет получена количественная оценка (коэффициент завершенности тестирования) эффективности данного процесса.

  2. Разработанный принцип обеспечения покрытия тестами ПО в условиях отсутствия исходного кода может быть использован для проведения тестирования черного ящика «в памяти» во время лабораторных испытаний ПО. Алгоритм позволяет обойти ограничения интерфейсного кода, такие как ограничение количества сессий и тайм ауты между вызовами.

  1. Разработанный алгоритм последовательного внедрения ошибок позволяет улучшить покрытие кода тестами за счет покрытия ветвей кода, отвечающего за сбойные ситуации, и таким образом увеличить эффективность тестирования ПО.

  2. Разработанные метод и система автоматического поиска уязвимостей ПО в условиях отсутствия исходного кода могут быть использованы для лабораторных испытаний ПО.

Использование результатов Основные результаты исследований были использованы на кафедре Безопасности информационных технологий ТТИ ЮФУ при проведении следующих научно-исследовательских и опытно-конструкторских работ: «Исследование методов моделирования и противодействия компьютерным атакам, осуществляемым системами несанкционированного управления», «Разработка и развитие технологий и методов использования уязвимостей систем в открытых сетях», «Исследование возможностей построения транспортной подсистемы на основе узлов сети интернет»; научных исследований, поддержанных грантом РФФИ №07-07-00138 в учебном процессе на кафедре БИТ ТТИ ЮФУ при проведении лабораторных работ по курсу «Низкоуровневое программирование в задачах защиты информации», в ФГНУ «НИИ «Спецвузавтоматика» при выполнении составной части опытно-конструкторской работы «Фокстрот-Р».

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

Апробация работы. По теме диссертации опубликовано 12 научных статей и тезисов докладов. Основные результаты, полученные в ходе работы над диссертацией, были представлены на:

  1. VIII Всероссийской научной конференции студентов и аспирантов «Техническая кибернетика, радиоэлектроника и системы управления», Таганрог (2006 г.).

  2. XIV всероссийской научной конференции «Проблемы информационной безопасности в системе высшей школы: труды XIV всероссийской научной конференции», Москва (2007 г.).

  3. XIV общероссийская научно-техническая конференция «Методы и технические средства обеспечения безопасности информации». Санкт-Петербург (2007 г.).

  4. LIII научно-технической конференции профессорско-преподавательского состава, аспирантов и сотрудников ТТИ ЮФУ. Таганрог (2008 г.).

  5. Первой всероссийской молодежной конференции по проблемам информационной безопасности "ПЕРСПЕКТИВА - 2009". Таганрог (2009 г.).

  6. Конференции «Молодежь и современные информационные технологии». Томск (2010 г.). Таганрог (2009 г.).

  7. VI Ежегодная научная конференция студентов и аспирантов базовых кафедр Южного научного центра РАН, Ростов-на-Дону (2010 г.).

  8. XI Международной научно-практической конференции «Информационная безопасность». Таганрог (2010 г.)

9. Тринадцатой международной конференции «РусКрипто'2011». Москва

(2011).

Публикации

По теме диссертации опубликовано 12 научных статей (из них 2 в изданиях, рекомендованных ВАК) и тезисов докладов. Имеются 2 свидетельства об официальной регистрации программ для ЭВМ (№2010614872 и №2010614871).

Объем и структура диссертации. Диссертация состоит из введения, пяти глав, заключения, списка литературы. Основной текст диссертации изложен на 129 страницах, включая 38 рисунков и 18 таблиц.

Обзор методов, используемых при тестировании

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

По данным Web Application Security Consortium (WASC)[20] «Около 49% Web-приложений содержат уязвимости высокой степеней риска (Urgent и Critical), обнаруженные при автоматическом сканировании систем. Однако при детальной ручной и автоматизированной оценке методом «белого ящика» вероятность обнаружения таких уязвимостей высокой степени риска достигает 80-96%. Вероятность же обнаружения уязвимостей степени риска выше среднего (критерий соответствия требованиям PCI DSS) составляет более 86% при любом методе работ. В то же время при проведении более глубокого анализа 99% Web-приложений не удовлетворяет требованиям стандарта по защите информации в индустрии платежных карт». Согласно статистике из этого же источника[7] количество сайтов, на которых обнаружено более одной уязвимости, зависит от применяемого метода исследования, но различия эти не значительны (рисунок 1.1). Столбец «Scans» описывает уязвимости, найденные с помощью автоматических средств со стандартными настройками. Статистика из столбца «BlackBox» достигнута с помощью детального анализа, но без доступа к кодам ПО. Столбец «WhiteBox» - уязвимости, найденные с помощью статического и динамического анализа исполняемых и исходных кодов ПО серверов. Улучшение результатов в зависимости от детальности изучения ПО связано с тем, что оценка рисков становится более адекватной, и учитывается не только тип уязвимости, но и реальные последствия ее эксплуатации с учетом архитектуры и реализации приложения. В статистике об автоматическом тестировании участвуют также сайты, которые не имеют активных элементов, а такие сайты заведомо будут иметь меньше уязвимостей. Результаты автоматизированных сканирований описывают среднестатистические Интернет-сайты, результаты более детальных тестов относятся к интерактивным Web-приложениям.

Если рассматривать зависимость среднего количества найденных уязвимостей на одном сайте от выбранного метода, то статический и динамический анализ сетевого ПО значительно эффективнее исследований методом ««черного ящика»». На рисунке 1.2 колонка "WhiteBox" описывает эффективность работы методов, для которых необходим доступ к исходным или исполняемым кодам приложений. Следует учесть, что методы из ряда «WhiteBox» могут содержать элементы методов ««черного ящика»». Данные для приложения могут генерироваться специальным образом и соответствующая реакция на них фиксироваться с помощью динамического анализа.

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

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

Важно понимать, что лучший способ борьбы с уязвимостями ПО это профилактика их появления. По этой причине в разделе описываются не только методы верификации ПО (поиска уязвимостей в готовых программных продуктах), но и методы, которые используются на этапе его создания. Модульное тестирование - один из самых значительных вкладов в дисциплину программирования методологии экстремального программирования [11,12,13]. В настоящее время подход стал стандартом при разработке программного обеспечения. Однако с его помощью невозможно выполнить все необходимые этапы проверки качества ПО. По этой причине используются дополнительные методы, такие как метод внедрения ошибок[14,15,16,17], а также имитация нехватки ресурсов.

В разделе не освещены методы профилактики безопасности кода проводимые с использованием исходных кодов программ, такие как специальная аннотация кода, использование безопасных библиотечных функций и типов[18] и сигнатурное сканирование исходных кодов ПО, так как предметом исследования является ПО без исходных кодов.

Методы верификации имеют свою специфику из-за того, что могут проводиться сторонними исследователями. В этом случает количество известной о ПО информации минимально. Анализ выполняются по бинарным кодам. К методам верификации ПО без исходных кодов можно отнести ручной анализ дизассемблированного кода, автоматизированный сигнатурный анализ. Нашел распространение метод воздействии на ПО синтезированными данными (метод известен под названием «fuzzing»)[19].

Для проведения тестирования требуется сочетать вышеописанные методы. Несмотря на большое разнообразие различных методов, призванных упростить исследование ПО без исходных кодов с целью поиска ранее известных и не известных уязвимостей, методы, которые бы включали бы в себя все необходимые для тестирования этапы развиты плохо. Одним из продуктов, претендующим на звание интернированной автоматизированной системы поиска уязвимостей является пакет comRider[20]. Его описанию посвящена отдельная глава.

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

Цикл разработки ПО принято разделять на три этапа[5]: проектирование, разработка, тестирование. Широко известна [21] оценка по которой трудоемкость между этапами распределяется в процентном отношении 40%-20%-40%. Автоматизация тестирования призвана уменьшить трудоемкость последнего этапа. Результат, к которому может стремиться современная индустрия разработки ПО распределение 60%-20%-20%.

Оценка покрытия с использованием датчиков исполнения

Тестирование программы Р по выбранному критерию С означает покрытие всех компонентов программы РМ = {т ..., тп} по элементам или по связям. В предыдущем пункте был выбран структурный критерий с компонентами ветвей. Пусть имеется кортеж неизбыточных тестовГ = {tj, ..., tn]. Тест tj является неизбыточным, если существует покрытый им компонентою М(Р,С), который не был покрыт ни одним из предыдущих тестов t1..ti_1. Сложность тестирования программы Р по критерию С V(P,C) измеряется максимальным числом неизбыточных тестов, покрывающих все элементы множества М(Р,С). Полагая, что каждый тест набора будет покрывать всего один непокрытый компонент, можно использовать общее количество ветвей кода в проекте в качестве значения сложности тестирования.

Остаточная сложность тестирования программы Р по критерию С DV=(P,C,T) измеряется максимальным числом неизбыточных тестов, покрывающих элементы множества М(Р,С), оставшихся непокрытыми, после прогона набора тестов Т. Также может быть использовано количество непокрытых ветвей кода. Величина DV строго и монотонно убывает от V до О [21].

Таким образом, оценка степени завершенности тестирования программы Р рассчитывается по следующей формуле: V-DV TV(P, С, Т) = — — Критерий окончания тестирования TV{P, С, Г) L, где 1 L О Параметр L - степень завершенности тестирования, заданный в требованиях к программному продукту. При ручной подготовке тестов для программного продукта при наличии полной спецификации и исходных кодов практически всегда имеется возможность подготовить набор тестов, который обеспечит необходимое покрытие в соответствии с заданным уровнем завершенности тестирования L. При генерации тестов необходимо обеспечить возможность досрочного завершения тестирования в случае, если генератор не способен улучшить степень завершенности тестирования программы. Для этого может быть использован один из статистических критериев, в зависимости от распределения вероятностей покрытия кода генератором.

Структурный критерий покрытия кода данными опирается на информацию о количестве выполненных в ходе исполнения ветвей кода. Для отслеживания выполнения ветвей, или иными словами «покрытия ветвей данными», при тестировании ПО с исходными кодами эффективно применяются датчики исполнения. Датчики внедряются в код на этапе препроцессорной обработки ( например в «Аист-С») либо на этапе компиляции, как это реализовано в компиляторе GCC[49]. Далее, когда ветка кода, содержащая внедренный датчик получает управление, фиксируется ее покрытие. После сбора статистики результат может быть визуализирован на уровне строк исходного кода.

Использование данного метода применительно к ПО без исходных кодов затруднено. Внедрение дополнительных инструкций в исполняемый образ потребует полной пересборки файла. Должны будут обновлены все основные структуры: от массива релокации и заканчивая контрольной суммой файла[50].

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

Расстановка датчиков исполнения на основе статического анализа Для оценки покрытия датчик должен быть установлен в начало каждого линейного блока кода. Линейный блок заканчивается там, где. - Встречается инструкция условной передачи управления. - Встречается инструкция безусловной передачи управления. - На следующую инструкцию за текущей инструкцией передается управление. - Происходит возврат управления из подпрограммы. - Завершается память, помеченная как исполняемая. Началом блока служит. - Инструкция, следующая за инструкцией условной передачи управления. - Инструкция, на которую передается управление. - Точка входа в подпрограмму. При статическом анализе бинарного кода можно действовать по одному из двух следующих принципах. - Анализировать все байты секций, помеченных для выполнения. При этом начала функций выявляются по некоторым эвристическим признакам, таким, как пролог функции[31]. - Начинать анализ с точки входа в исполняемый файл. Помещать в список все встреченные при анализе функции и выполнять их анализ. При проведении статического анализа используется второй из предложенных подходов, потому как он гарантирует отсутствие ложного распознавания подпрограмм. Функция анализируется только после того, как в коде встречен ее вызов.

По такому же принципу выполняется анализ ветвей кода. Ветвь кода анализируется при нахождении оператора передачи управления на нее.

Рисунок 2.1 иллюстрирует расстановку маркеров начала и конца линейного блока для типов команд, завершающих линейных блок, а также команд вызова подпрограмм в случае, когда адреса передачи управления известны (каждый тип команд иллюстрирует определенная команда данного типа).

Закрывающий маркер текущего блока. Маркер, закрывающий линейный блок, к которому принадлежит команда условной передачи управления(нижний в блоке 1 рисунка 2.1). - Открывающий маркер левой ветви исполнения. Блок ветви, которая выполняется при несоответствии условию (верхний в блоке 4 рисунка 2.1).

Открывающий маркер правой ветви исполнения. Блок ветви, которая выполняется при соответствии условию (верхний в блоке 2 рисунка 2.1).

Закрывающий маркер блока предшествующего блоку правой ветви исполнения (нижний в блоке 5 рисунка 2.1).

В случае если к моменту возникновения оператора условной передачи управления код предшествующий блоку 2 рисунка 2.1 не был анализирован, то установка закрывающего маркера блока 5 переносится на дополнительный цикл обработки, выполняемый после анализа всех функций.

Оператор безусловной передачи управления (jmp на рисунке 2.1) порождает создание трех маркеров: закрывающего текущего, открывающего правой ветви, и закрывающего блока, предшествующего блоку правой ветви. Отсутствие четвертого маркера объясняется тем, что у операторов безусловной передачи управления нет левой ветви исполнения. Оператор выхода из подпрограммы (ret на рисунке 2.1) порождает один маркер - маркер завершения текущего линейного блока. Оператор вызова подпрограммы (call на рисуноке 2.1) инициирует анализ вызываемой функции. Открывающий маркер линейного блока -отправная точка анализа.

Критерий оценки охвата тестирования

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

Y = іпУь гДе У г количество ветвей і-ой функции. В данном примере имеется один корневой элемент и выбор очевиден. Однако, в общем случае корень не один. Также не обязательно, что функция с хорошим показателем Y будет удобна для внедрения воздействий. На рисунке 3.4 представлен рейтинг перспективных для фаззинга функций (справа налево). Функция EntryPoint хотя и получила наибольшую оценку не содержит в себе логику программы. В ней производятся стандартные для запуска программы операции. Поэтому имеет смысл сдвинуться к функции с меньшим значением Y. functionstestlsublOCO соответствует функции 0 из рисунка 3.3. Из нее доступны все функции, реализующие функционал.

Алгоритм внедрения данных. Генерация интерфейсов Следующей задачей, которую нужно решить для построения системы внедрения воздействий в произвольное место программы является построение интерфейсов функций. В штатном режиме, если функция вызывается извне модуля, то обязательно предоставляется описание ее прототипа. Например, для случая статической компоновки модулей в программах, написанных на языке Си, предоставляется файл заголовков (.h).

Рисунок 3.5 - Охват кода из выбранной точки В бинарном коде программы информация о прототипах внутренних функций практически теряется [5 7]. Прототипы внутренних функций известны на этапе компиляции модуля. Опираясь на эти данные, компилятор формирует код вызова функции. Следующие важные для компилятора данные содержатся в прототипе функций:

Существует множество соглашений о вызовах функций, которые используются различными компиляторами. В таблице 3.1 представлено описание наиболее популярных соглашений [5 8, 59]. Таблица 3.1 - популярные соглашения о вызовах Назван ие Передача параметров Очистка памяти Возврат значений cdecl Аргументы передаются через стек, справа налево вызывающая подпрограмма Регистр еах pascal Аргументы передаются через стек, слева направо вызываемая подпрограмма неявный параметр stdcall/ winapi Аргументы передаются через стек, справа налево вызываемая подпрограмма Регистр еах fastcall Аргументы передаются через регистры и стек. Используется для внутренних функций, поэтому регистры выбирает компилятор вызываемая подпрограмма Регистр еах safecall Аргументы передаются через стек, справа налево вызываемая подпрограмма Возвращает HResult через неявный параметр thiscall Аргументы передаются через стек, справа налево вызываемая подпрограмма Регистр еах usercall произвольное произвольное произвольное Не стандартизированные соглашения принято называть usercall. Помимо описанных выше соглашений существует еще ряд менее распространенных или вышедших из употребления. По той причине, что не все соглашения строго описывают порядок использования регистров (к примеру, fastcalln usercall) восстановить прототип к виду, доступному до компиляции не представляется возможным. Однако, для внедрения воздействий в функцию такой прототип не обязателен. Следующие данные о функции необходимы:

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

Количество параметров, передаваемых через стек для случая, когда стек очищает вызываемый код, определяется по аргументу retn. Количество аргументов определяется по следующей формуле: N = п/4, где N - количество аргументов, передаваемых через стек, а п-значение аргумента инструкции retn. Для случая, когда стек очищает вызывающий код, задача определения количества параметров не тривиальна. Однако, решить ее можно, из-за особенностей генерации кода компиляторами. Каждую функцию (за редкими исключениями) компилятор начинает с т.н. пролога:

Далее в программе обращение к параметрам происходит через конструкции вида [ebp+n], где п - число кратное 4. Ячейка [ebp+0] содержит сохраненное значение ebp, [ebp+8] содержит адрес возврата из функции. Начиная с [bsp+8] сохранены переданные в функцию параметры. Порядок параметров зависит от типа вызываемой функции (прямой для ссіесіи stdcall, обратный для pascal). Однако для задачи построения прототипа функции с целью использования для фаззинга порядок аргументов значения не имеет.

Следующая подзадача задачи восстановления прототипа функции -определение параметров, передаваемых через регистры. В условиях отсутствия какой-либо дополнительной информации об используемых компилятором регистрах имеет смысл считать параметром каждый регистр, значение которого было использовано без инициализации входным параметром. Исключением являются регистры, значение которых было размещено на стек вначале функции и вынуто оттуда в конце. Значение таких регистров не изменяются в функции, но и не используются. Сформулируем предложенный критерий для регистра-параметра. Зададим массивы А = (aki) и В = (Ькг), где ак1=\ в случае, если в блоке

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

Сравнение с алгоритмом, использующим аппаратны е средства

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

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

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

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

В соответствие с алгоритмом, представленном в пункте 2.3, для правильной оценки покрытия, инструкция точки останова должна быть размещена в начале каждого линейного блока. После выполнения данной инструкции инициируется выполнение обработчика исключения точки останова. Пошаговая трассировка с взведенным флагом ВТРинициирует вызов обработчика прерывания intl (в пространстве ядра), а также обработчика шага трассировки в начале выполнения каждого линейного блока.

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

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

Для идентификации точки останова и причины пропуска точки останова будет полезна информация, полученная из подсистемы, работающей в режиме ядра. Для этого при каждом срабатывании обработчика шага трассировки из ядреной подсистемы запрашивается адрес, откуда был совершен переход. Текущий адрес может быть получен из контекста потока. Предложенный алгоритм проверки правильного алгоритма помог выявить следующие потенциальные проблемы: - не фиксируется передача управления при возврате из функции по инструкции; - не фиксируется межмодульная передача управления при вызове функций исследуемого модуля из других модулей. Первая из найденных проблем фактически не мешает правильной работе алгоритма. Вызов функций на само деле не прерывает линейный блок, так как после вызова управление передается строго на следующую инструкцию ( рисунок 5.3 ). і mov еах, ebx xor ebx, еах add еах, есх call foo mov ebx, eax cmp ebx, ecx je foojablel ——— —— inc ecx dec eax and ecx, eax cmp ecx, ds:88h xor eax, eax ret J Рисунок 5.3 - Линейный блок с вызовом функции ПО Вторая проблема требует решения, так как большая часть кода модуля может быть доступна через экспортируемые функции. Эксперименты подтвердили данное утверждение. Решить проблему можно одним из следующих способов: - динамически следить за передачей управления между модулями исследуемой системы. Проводить статический анализ для каждой новой точки входа; - во время загрузки модуля проводить статический анализ всех точек входа, включая экспортируемые функции.

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

Практика использования динамически загружаемых модулей (а для формата РЕ динамически может подгружаться даже исполняемый модуль .ехе) подразумевает использование секций импорта и экспорта для организации межмодульных вызовов. Секция экспорта содержит адреса и имена всех функций, доступных для вызова из других модулей.

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

Ранее были представлены достоинства эталонного алгоритма оценки покрытия, использующего аппаратные возможности процессора. Единственным недостатком, не позволяющим использовать алгоритм в качестве основного, является низкая скорость его работы. Оценим экспериментально разницу в работе алгоритмов. Для этого оценим скорость загрузки нескольких популярных приложений для просмотра документов различного формата под управлением каждого из двух рассмотренных алгоритмов. Оценим время загрузки, общий размер исполняемого кода основных модулей программ, размер задействованного при загрузке исполняемого модуля. Полученные результаты представлены в таблице 5.1. - увеличение скорости тестирования за счет уменьшения накладных расходов. Помимо описания двух подходов к тестированию «черного ящика» в памяти представленного в [19] в настоящее время известна только одна общедоступная реализация[78].Сравним качественные характеристики реализации тестирования «черного ящика», описанного в [78] с предложенным в данной работе.

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