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

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

CC BY-NC
217
37
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
ИНФОРМАЦИОННАЯ БЕЗОПАСНОСТЬ / УЯЗВИМОСТИ / АНАЛИЗ КОДА / ДИЗАССЕМБЛИРОВАНИЕ / ПЕРЕПОЛНЕНИЕ БУФЕРА / ШТАМПЫ КОМПИЛЯТОРОВ / РЕЖИМ DEBUG / РЕЖИМ RELEASE / МЕТОДЫ ПОСТРОЕНИЯ АЛГОРИТМОВ / ОТЛАДЧИК WINDBG

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Ревнивых Александр Владимирович, Велижанин Анатолий Сергеевич

Предметом исследования является потенциальная уязвимость, в частности на переполнение буфера, в различном программном обеспечении, связанная с функцией стандартной библиотеки языка программирования С/С++ strcpy и подходы и методы поиска таковой уязвимости. Объектом исследования выступают данные машинного кода компиляторов при выполнении сборки программы в различных режимах. Целью исследования является провести анализ некоторых особенностей машинного кода, генерируемого различными компиляторами для Windows и Linux в режимах Debug и Release, в том числе, проведя на основе этого обзор уязвимости переполнения буфера. Методы исследования. В работе рассматриваются и развиваются методы построения алгоритмов поиска уязвимости переполнения буфера, исследуются характеристики данной уязвимости на уровне машинного кода. Для этого используются компиляторы Visual C++, Intel C++, g++, а также отладчики WinDBG, GDB. Ключевые выводы. Сборка программ в различных режимах приводит к формированию различий в исполняемом коде, который сделан из полностью одного и того же кода языка программирования высокого уровня; эти различия проявляются в отличиях в поведении программы. В ходе исследования программного обеспечения в поисках уязвимостей важно проводить анализ машинного кода с целью выявления скрытых закономерностей. Новизна исследования заключается в выявлении отличий в машинном коде, полученном после сборки одинакового высокоуровневого кода, определении «штампов» компиляторов при выполнении сборки программы в различных режимах. Особым вкладом автора в исследование темы является развитие методов построения алгоритмов поиска уязвимости переполнения буфера.

i Надоели баннеры? Вы всегда можете отключить рекламу.

Похожие темы научных работ по компьютерным и информационным наукам , автор научной работы — Ревнивых Александр Владимирович, Велижанин Анатолий Сергеевич

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Текст научной работы на тему «Исследование дизассемблированного представления исполняемых файлов, сформированных различными компиляторами. Пример уязвимости на переполнение буфера»

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

Ревнивьк Александр Владимирович

кандидат технических наук

доцент, кафедра Информационной безопасности, ФГБОУ ВО «Новосибирский государственный

университет экономики и управления «НИНХ»

630099, Россия, Новосибирская область, г. Новосибирск, ул. Каменская, 56

И al.revnivykh@mail.ru

Велижанин Анатолий Сергеевич

Специалист, ФГБОУ ВО «Тюменский индустриальный университет»»» 625000, Россия, Тюменская область, г. Тюмень, ул. Володарского, 38

И anatoliy.velizhanin@gmail.com

Статья из рубрики "Показатели качества и повышение надежности программных систем"

Аннотация.

Предметом исследования является потенциальная уязвимость, в частности на переполнение буфера, в различном программном обеспечении, связанная с функцией стандартной библиотеки языка программирования С/С + + strcpy и подходы и методы поиска таковой уязвимости. Объектом исследования выступают данные машинного кода компиляторов при выполнении сборки программы в различных режимах.Целью исследования является провести анализ некоторых особенностей машинного кода, генерируемого различными компиляторами для Windows и Linux в режимах Debug и Release, в том числе, проведя на основе этого обзор уязвимости переполнения буфера. Методы исследования. В работе рассматриваются и развиваются методы построения алгоритмов поиска уязвимости переполнения буфера, исследуются характеристики данной уязвимости на уровне машинного кода. Для этого используются компиляторы Visual C + + , Intel C + + , g + + , а также отладчики WinDBG, GDB. Ключевые выводы. Сборка программ в различных режимах приводит к формированию различий в исполняемом коде, который сделан из полностью одного и того же кода языка программирования высокого уровня; эти различия проявляются в отличиях в поведении программы. В ходе исследования программного обеспечения в поисках уязвимостей важно проводить анализ машинного кода с целью выявления скрытых закономерностей.Новизна исследования заключается в выявлении отличий в машинном коде, полученном после сборки одинакового высокоуровневого кода, определении «штампов» компиляторов при выполнении сборки программы в различных режимах. Особым вкладом автора в исследование темы является развитие методов построения алгоритмов поиска уязвимости переполнения буфера.

Ключевые слова: Информационная безопасность, Уязвимости, Анализ кода, Дизассемблирование, Переполнение буфера, Штампы компиляторов, Режим Debug,

Режим Release, Методы построения алгоритмов, отладчик WinDBG

DOI:

10.25136/2306-4196.2019.1.28238

Дата направления в редакцию:

06-12-2018

Дата рецензирования:

06-12-2018

Дата публикации:

13-12-2018

Введение

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

исследования, направленные на поиск уязвимостей в программном обеспечении [1, 6]. Разнообразие типов программных решений и из базовых компонентов приводит к значительным различиям в подходах и методах поиска уязвимостей. Так, некоторые уязвимости характерны по большей части для Web-приложений, в то время как другие — для Desktop-решений. Тем не менее, взаимная интеграция программных систем, вероятно, приводит к миграции некоторых уязвимостей из одного программного

компонента в другой J21.

В настоящее время, несмотря на стремительное развитие Web-технологий, значительное количество программного обеспечения разрабатывается в виде Desktop-решений на различных языках программирования. К ним относятся и компилируемые под определенную платформу языки программирования (такие, как C/C + + ), так и языки программирования с JIT-компиляцией (например, C#). К сожалению, методы поиска уязвимостей в Native-программном обеспечении часто сводятся к «ручному» поиску, где эксперт вынужден проводить анализ самостоятельно, применяя вспомогательные средства для выполнения базовых задач.

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

подходящего под каждый из этих типов, вероятно, является неэффективным. [7-21] В связи с этим, для начала мы рассмотрим определённый тип уязвимости и исследуем его характеристики на уровне машинного кода.

Уязвимость переполнения буфера, некоторые особенности кода, генерируемого различными компиляторами

В качестве одного из типов уязвимости рассмотрим переполнение буфера. Переполнение буфера (Buffer Overflow) — явление, возникающее, когда компьютерная программа

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

Рассмотрим несколько примеров, содержащих уязвимость данного типа. Наиболее простой пример на языке программирования С/С + + изложен в листинге 1.

Листинг 1. Простейший пример уязвимого кода на С/С+ + .

01: #include <stdio.h>

02: #include <string.h>

03:

04: #define ARRAY_SIZE 10 05:

06: int main(int argc, char *argv[]) 07: {

08: char arr[ARRAY_SIZE];

09: if(argc < 2)

10: return -1;

11: else

12: {

13: strcpy(arr, argv^); 14: return 0; 15: } 16: }

Создадим приложение типа «Empty project» на С/С + + в среде Visual Studio 2010. Создание именно данного типа проекта необходимо для минимизации встраивания дополнительного кода на С/С++, генерируемого средой Visual Studio 2010, в проект, а также минимизации встраивания в результирующий машинный код дополнительных

вызовов. Это даст возможность минимизировать объём результирующего исполняемого файла и увеличить наглядность данного примера. Затем добавим файл с исходным кодом из листинга 1. Данный пример имеет простейшую уязвимость переполнения буфера в строке 13. Рассмотрим дизассемблированное представление наиболее важных участков данного кода в стандартном режиме компиляции Debug x32 (Листинг 2.).

Листинг 2.

. . .(код вырезан) . . . 13: strcpy(arr, argv^);

008913E5 8B 45 0C mov eax,dword ptr [ebp + 0Ch] 008913E8 8B 48 04 mov ecx,dword ptr [eax+4] 008913EB 51 push ecx 008913EC 8D 55 EC lea edx,[ebp-14h] 008913EF 52 push edx

008913F0 E8 AB FC FF FF call @ILT+155(_strcpy) (8910A0h) . . .(код вырезан) . . .

Из данного листинга можно сделать вывод о передаче 2-ух параметров через стек, которые были сохранены из регистров процессора ecx и edx. Рассмотрим аналогичный код для платформы x64 (Листинг 3).

Листинг 3.

. . .(код вырезан) . . . 13: strcpy(arr, argv^);

000000013FD01050 48 8B 44 24 78 mov rax,qword ptr [rsp + 78h] 000000013FD01055 48 8B 50 08 mov rdx,qword ptr [rax+8] 000000013FD01059 48 8D 4C 24 28 lea rcx,[rsp + 28h] 000000013FD0105E E8 85 01 00 00 call strcpy (13FD011E8h) . . .(код вырезан) . . .

В данной архитектуре происходит несколько другой способ передачи параметров в вызываемую функцию. Так, в примере из листинга 3 регистр rdx указывает на копируемую строку, а rcx на буфер, куда будет произведено копирование. Подробнее о соглашениях вызова и программировании на языке Assember для x86_64 можно прочитать в №1, а также в официальной документации на сайте производителя процессора ^^

В результате запуска программы из листинга 1, собранной в режиме Debug x32, с аргументом командной строки длиной 20 символов, мы получаем ошибку, изображенную на рис. 1. Аналогичное поведение мы имеем для Debug x64. Таким образом мы видим работу стандартного встроенного средства проверки, добавленного компилятором

Microsoft Visual C + + 2010 в приложения, собранные в режиме Debug. Некоторую часть реализации механизмов защиты мы можем увидеть в листинге 4. При еще более длинном аргументе (скажем, 2000 символов) возникает необрабатываемое исключение вызывающее крах программы, изображенное на рис. 2.

Вкратце рассмотрим отрывок дизассемблированного кода приложения, собранного в режиме Debug x32 (Листинг 4).

Листинг 4.

. . .(код вырезан) . . .

13 012013f0 e8abfcffff call exp_1!ILT+155(_strcpy) (012010a0)

13 012013f5 83c408 add esp,8

14 012013f8 33c0 xor eax,eax

exp_1!main+0x4a [c:usersanatoliydocumentsvisual studio 2010projectsexp_1exp_1main.cpp @ 16]:

16 012013fa 52 push edx 16 012013fb 8bcd mov ecx,ebp 16 012013fd 50 push eax

16 012013fe 8d152c142001 lea edx,[exp_1!main + 0x7c (0120142c)]

16 01201404 e879fcffff call exp_1!ILT+125(_RTC_CheckStackVars (01201082)

16 01201409 58 pop eax

16 0120140a 5a pop edx

16 0120140b 5fpop edi

16 0120140c 5e pop esi

16 0120140d 5b pop ebx

16 0120140e 8b4dfc mov ecx,dword ptr [ebp-4] 16 01201411 33cd xor ecx,ebp

16 01201413 e8fcfbffff call exp_1!ILT+15(__security_check_cookie (01201014)

16 01201418 81c4d8000000 add esp,0D8h 16 0120141e 3bec cmp ebp,esp

16 01201420 e811fdffff call exp_1!ILT+305(__RTC_CheckEsp) (01201136)

16 01201425 8be5 mov esp,ebp 16 01201427 5d pop ebp 16 01201428 c3 ret . . .(код вырезан) . . .

Похожий код (по большому счету основная разницы с х32 кодом лишь в соглашениях вызова и некоторых других деталях) мы можем увидеть при отладке Debug x64. Пример приведен в листинге 5.

14 00000001

exp_1!main+0x55 [c:usersanatoliydocumentsvisual studio 2010projectsexp_1exp_1main.cpp @ 16]:

16 00000001

16 00000001

16 00000001 3f9f67e0)]

16 00000001 3f9f1220)

16 00000001 16 00000001 16 00000001

16 00000001 3f9f1200)

16 00000001 16 00000001 16 00000001 . . .(код вырезан) . . .

В коде из листинга 5 мы так же видим наличие защитных средств, предназначенных для контроля целостности стека.

В листинге 6 приведен пример отладки в WinDBG программы, собранной в режиме Debug х64. В данном случае отладчик сообщает нам о переполнении буфера. Аналогичный результат мы получаем при эксперименте со сборкой Debug x32. Далее отдельные примеры листингов для x32 приводить не будем.

Листинг 5.

. .(код вырезан) . .

13 00000001

3f9f11e8)

Листинг 6.

Breakpoint 0 hit

exp_1!main:

00000001

0027f878 =

{exp_1!__xc_z (00000001

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

0:000> g

Breakpoint 1 hit

exp_1!main+0x62:

00000001 3f9f1220)

0:000> p

WARNING: This break is not a step/trace completion.

The last command has been cleared to prevent

accidental continuation of this unrelated event.

Check the event, location and thread before resuming.

(1230.1200): Break instruction exception - code 80000003 (first chance)

exp_1!failwithmessage+0x228:

00000001

0:000> g

Breakpoint 2 hit

exp_1!main + 0x72:

00000001 3f9f1200)

0:000> p

STATUS_STACK_BUFFER_OVERRUN encountered WARNING: This break is not a step/trace completion. The last command has been cleared to prevent accidental continuation of this unrelated event. Check the event, location and thread before resuming. (1230.1200): Break instruction exception - code 80000003 (first chance) kernel32!UnhandledExceptionFilter+0x71: 00000000

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

Теперь рассмотрим дизассемблированный листинг Release х64 версии исследуемого ПО. Пример изображен в листинге 7.

Листинг 7.

. . .(код вырезан) . . . 13 00000001

exp_1!main+0x31 [c:usersanatoliydocumentsvisual studio 2010projectsexp_1exp_1main.cpp

@ 13]: 13 00000001 13 00000001 13 00000001

13 00000001 3fd11031)

. . .(код вырезан) . . .

Обратим внимание, что в приведенном листинге 7 отсутствует непосредственный вызов функций strcpy. Он заменен последовательностью команд по адресам 00000001 3fd11039, которые производят копирование строки, заменяя собой функционал стандартной функции strcpy. В листинге 8 приведен пример отладки программы, аналогичный листингу 6, но для Release сборки.

Листинг 8.

Breakpoint 0 hit exp_1!main: 00000001 0:000> p

exp_1!main+0x13: 00000001 0:000> g Breakpoint 1 hit exp_1!main+0x2d: 00000001

000d2408=00000000000d246c 0:000> g Breakpoint 2 hit exp_1!main + 0x45:

00000001 3fd11060)

0:000> g

Breakpoint 3 hit

exp_1!main+0x4e:

00000001

ЩТпЫ Lzr— rrundai 7.57 ■ iidMii

Рис. 1. Исключение, вызванное переполнением буфера строкой 20 символов для Debug

сборки

Рис. 2. Исключение, вызванное переполнением буфера, длиной 2000 символов для

Debug сборки

Таким образом, при сборке проекта в режиме Release, наблюдается несколько иная ситуация. Сообщение из рис. 1 не появляется. Отметим, что на рис. 2. выделено значение Exception Offset равное 61616161. Обратим внимание, что строка, вызвавшая переполнение, состояла из множества символов «a», а 61 является шестнадцатеричным представлением данного символа. Это является классическим переполнением буфера, когда адрес возврата из функции был перезаписан данными, отправленными злоумышленником в систему. Отсутствие многих защитных механизмов, которые мы могли увидеть в листингах для Debug сборок можно объяснить тем, что тип сборки Release предназначен для распространения приложения, а значит такая сборка должна обладать наибольшей производительностью, что требует оптимизацией кода в том числе за счет уменьшения количества встраиваемых компилятором проверок вплоть до полного их

исключения.

Сравнив особенности переполнения для Debug и Release сборок приложений, скомпилированных с помощью Microsoft Visual C++ 2010, можем отметить, что для успешного использования уязвимостей необходимо учитывать тип сборки проекта . Кроме того, некоторые функции могут быть развернуты компилятором в собираемый модуль. Так, например, функция стандартной библиотеки strcpy была развернута в последовательность команд по адресам 00000001 3fd11039 из

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

Рассмотрим результаты экспериментов, проведенных с использованием компилятора Intel C + + , входящего в стандартную поставку Intel Parallel Studio XE. Данный программный продукт совместим с Microsoft Visual Studio 2005-2010, а так же имеет встроенные средства интеграции. Для начала приведем пример дизассемблированного в WinDBG листинга части функции main, собранного из среды Visual Studio 2010 компилятором C + + от Intel (Листинг 9).

13 00000001

14 00000001 14 00000001 14 00000001

. . .(код вырезан) . . .

Сравним листинг, полученный при дизассемблировании части функции «main» в диапазоне от функции «strcpy» до конца функции «main» файла, собранного при помощи компилятора Intel C + + Debug x64 в среде Visual Studio 2010, и соответствующую часть листинга, полученную после дизассемблирования аналогичной функции файла, собранного компилятором Visual C + + Debug x64. Первое, что бросается в глаза -небольшая разница в объеме кода, что может объяснять и несколько разный объем, занимаемый на жестком диске исполняемыми файлами, полученными в результате компиляции. Сравнив полный код функции «main», разница в объеме будет заметна сильнее.

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

= ZZ □

3f9f1063. Кроме того, в компиляторе Visual C + + обращение к стековым переменным происходит через регистр rdi, в то время как Intel C + + для данной операции использует смещения от регистра rbp. Для большей ясности приведем отрывок дизассемблированного листинга (листинг 10) функции «main», где происходит

Листинг 9.

. .(код вырезан) . .

13 00000001

3f8c1360)

формирование значения регистра rdi (компилятор Microsoft Visual C + + ).

Листинг 10.

. . .(код вырезан) . . . 7 00000001 7 00000001 7 00000001 7 00000001 7 00000001 . . .(код вырезан) . . .

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

В листинге 10 было показано начало функции «main» для компилятора от Microsoft. Приведем аналогичный участок кода для компилятора от Intel в листинге 11.

Листинг 11.

. . .(код вырезан) . . . 7 00000001 7 00000001 . . .(код вырезан) . . .

Проводя исследование полного кода функций «main» можно отметить, что в случае использования обоих вышеприведенных компиляторов имеют место неоднократные вызовы дополнительных функций. В частности, вызовы защитных функций «exp_1!_RTC_CheckStackVars» и «exp_1!__security_check_cookie».

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

Вернемся к рассмотрению уязвимости, вызванной использованием функции «strcpy». Рассмотрим ситуацию с компилятором g + + 4.6.3 в Linux. В наших экспериментах будет использоваться дистрибутив Ubuntu 12.04. В качестве отладчика будем использовать GDB 7.4-2012.04. NetBeans 7.2 возьмем как интегрированную среду разработки. Создадим проект типа C/C + + Application. Интегрированная среда разработки автоматически сгенерирует Makefile. Проведем сборку проекта в стандартных конфигурациях Debug и Release. NetBeans сохранит результирующие исполняемые файлы в каталог ./dist/Debug/GNU-Linux-x86/. Проверим, что мы получили файлы для х64 (Листинг 12).

Листинг 12.

anatoliy@QRt62Mto7:~/Documents/NetBeansProjects/exp_1$ file ./dist/Debug/GNU-Linux-x86/exp_1

./dist/Debug/GNU-Linux-x86/exp_1 : ELF 64-bit LSB executable, x86-64, version 1 (SYSV) dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1] = 0x55aba3afa1735b359b250309a5da228d28378e1b, not stripped

anatoliy@QRt62Mto7:~$ file ./Documents/NetBeansProjects/exp_1/dist/Release/GNU-Linux-x86/exp_1

./Documents/NetBeansProjects/exp_1/dist/Release/GNU-Linux-x86/exp_1: ELF 64-bit LSE executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1] = 0x39db2aecacbf46f59a4cfdf672804c1ca7431452, not stripped

anatoliy@QRt62Mto7:~$

Аналогичную проверку можно провести и для Release версии сборки. Посмотрим на дизассемблированный в отладчике GDB код функции main (Листинг 13).

Листинг 13.

. . .(код вырезан) . . .

0x000000000040058f <+43>: mov rax,QWORD PTR [rbp-0x30] 0x0000000000400593 <+47>: add rax,0x8 0x0000000000400597 < + 51>: mov rdx,QWORD PTR [rax] 0x000000000040059a < + 54>: lea rax,[rbp-0x20] 0x000000000040059e < + 58>: mov rsi,rdx 0x00000000004005a 1 <+61>: mov rdi,rax 0x00000000004005a4 <+64>: call 0x400450 <strcpy@plt> 0x00000000004005a9 <+69>: mov eax,0x0 . . .(код вырезан) . . .

Вызов уязвимой функции происходит по адресу 0x00000000004005a4. Регистр rsi в строке по адресу 0x000000000040059e получает значение, принятое в качестве параметра функцией main и являющееся буфером-источником по отношению к функции strcpy. В регистр rdi командой по адресу 0x00000000004005a1 заносится адрес буфера-назначения. Так же обратим внимание на несколько многоступенчатое формирование адреса в регистре rdi.

Запустим программу, собранную в режиме Debug x64, передав в качестве параметра строку, размер которой значительно превышает объем буфера-приемника (Листинг 14).

Листинг 14.

anatoliy@QRt62Mto7:~/Documents/NetBeansProjects/exp_1$ ./dist/Debug/GNU-Linux-

x86/exp_1

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA *** stack smashing detected ***: ./dist/Debug/GNU-Linux-x86/exp_1 terminated Segmentation fault (core dumped)

anatoliy@QRt62Mto7:~/Documents/NetBeansProjects/exp_1$

Как мы видим, программа завершила свою работу с фатальной ошибкой, и был создан аварийный дамп. Отметим, что подобные дампы эффективно используются для поиска причины аварийного завершения работы приложения, как для программ пользовательского уровня, так и для отладки кода ядра операционной системы. Для операционной системы Linux подобные дампы памяти можно рассматривать с использованием отладчика GBD, а в Windows это может быть, например, WinDBG.

Для сравнения в листинге 15 приведен код дизассемблированной функции «main» сборки типа Release x64 в отладчике GDB.

Листинг 15.

Dump of assembler code for function main: . . .(код вырезан) . . .

0x00000000004004be < + 30>: mov rsi,QWORD PTR [rsi + 0x8] 0x00000000004004c2 < + 34>: mov edx,0xa 0x00000000004004c7 < + 39>: mov rdi,rsp

0x00000000004004ca <+42>: call 0x400490 <__strcpy_chk@plt>

0x00000000004004cf <+47>: xor eax,eax . . .(код вырезан) . . .

В данном листинге мы можем отметить разницу в объеме кода. Наиболее интересным является факт, что непосредственно до вызова (когда параметры для вызова функции копирования уже помещены в регистры rsi и rdi), значения этих регистров приведены в листинге 16.

Листинг 16.

(gdb) x $rsi

0x7fffffffe451: "AAAAA" (gdb) x $rdi 0x7fffffffe040: ""

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

Листинг 17.

(gdb) x $rsi 0x7fffffffe456: "" (gdb) x $rdi 0x7fffffffe045: ""

Рассмотрев листинги 16 и 17, мы можем заметить, что регистры rsi и rdi начали указывать на другие адреса памяти, с чем связан и вывод команд из листинга 17. Указав же команде «х» отладчика GDB в качестве адресов в памяти предыдущие (уменьшив текущие значения регистров на 5 байт, что соответствовало количеству символов в строке, указанной в качестве входного параметра запускаемой программе), мы получим, как и следовало ожидать, результат из листинга 18.

Листинг 18.

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

(gdb) x $rsi-0x5 0x7fffffffe451: "AAAAA" (gdb) x $rdi-0x5 0x7fffffffe040: "AAAAA"

Как мы видим, произошло копирование строки, но регистры rsi и rdi поменяли хранимые

в них значения внутри функции <__strcpy_chk@plt>, вызываемой по адресу

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

Так же отметим, что регистры, через которые производится передача параметров, могут быть несколько отличными. Так, например, в Windows параметры обычно передавались через регистры rcx, rdx, а в экспериментах с Linux передача производилась через rsi, rdi. В общем случае данное различие не является критичным.

Заключение

Выше мы рассмотрели потенциальную уязвимость в различном программном обеспечении, связанную лишь с функцией стандартной библиотеки языка программирования С/С + + strcpy. Однако уязвимости переполнения буфера связаны не только с этой функцией стандартной библиотеки.

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

Результирующий машинный код для сборок режима Debug и Release может иметь в значительной степени различную структуру. В частности, Release сборки при компиляции Visual C + + 2010 не содержат многих защитных механизмов. Это так же является причиной, обосновывающей необходимость учитывать тип сборки при эксплуатации уязвимостей. Кроме того, в ходе компиляции некоторые функции могут быть развернуты компилятором в соответствующий программный код. Отличия особенностей

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

Библиография

1. Муханова А. А., Ревнивых А. В., Федотов А. М. Классификация угроз и уязвимостей информационной безопасности в корпоративных системах // Вестник НГУ. Сер.: Информационные технологии. — 2013. — Т. 11. — №

2. —С. 55-72. — ISSN 1818-7900. 2.Велижанин А. С., Ревнивых А. В. Эвристический метод поиска уязвимостей в ПО без использования исходного кода // XIV Российская конференция с международным участием "Распределенные информационные и вычислительные ресурсы" (DICR-2012). — ISBN 978-5-905569-050.

3. Википедия. Переполнение буфера [Электронный ресурс] URL: http://en.wikipedia.org/wiki/Переполнение_буфера

4. Аблязов Р. З. Программирование на ассемблере на платформе х86_64. Учеб. пособие / Р. З. Аблязов. — Москва: ДМК Пресс, 2011. — 305 c. — ISBN: 978-594074-676-8

5. Официальный сайт компании Intel. [Электронный ресурс] URL: www.intel.com

6. Ревнивых А. В., Федотов А. М. Мониторинг информационной инфраструктуры организации // Вестник НГУ. Сер.: Информационные технологии. — 2013. — Т. 11. — № 4. — С. 84-91. — URL:

https://nsu.ru/xmlui/bitstream/handle/nsu/1295/2013_V11_N4_8.pdf.

7. Воропаев Д. П., Зауголков И. А. Исследование программных уязвимостей в компьютерных системах и анализ применяемого программного обеспечения для проведения атак на вычислительную систему // Вестник Тамбовского университета. Серия: Естественные и технические науки. — 2014. — Т. 19. — № 2. — С. 637-638.

— ISSN 1810-0198. —URL: https://cyberleninka.ru/artide/v/issledovanie-programmnyh-uyazvimostey-v-kompyutemyh-sistemah-i-analiz-primenyaemogo-programmnogo-obespecheniya-dlya-provedeniya-atak

8. Нурмухаметов А. Р., Курмангалеев Ш. Ф., Каушан В. В., Гайсарян С. С. Применение компиляторных преобразований для противодействия эксплуатации уязвимостей программного обеспечения // Труды института системного программирования РАН.

— 2014. — Т. 26. — № 3. — С. 113-124. — ISSN 2079-8156.

9. Федотов А. Н. Метод оценки эксплуатируемости программных дефектов // Труды института системного программирования РАН. — 2016. — Т. 28. — № 4. — С. 137148. — DOI: 10.15514/ISPRAS-2016-28(4)-8.

10. Федотов А. Н., Каушан В. В., Гайсарян С. С., Курмангалеев Ш. Ф. Построение

предикатов безопасности для некоторых типов программных дефектов // Труды института системного программирования РАН. — 2017. — Т. 29. — № 6. — С. 151162. — DOI: 10.15514/ISPRAS-2017-29(6)-8.

11. Шудрак М. О., Хеирхабаров Т. С. Автоматизированный поиск уязвимостей в бинарном коде // Решетневские чтения. Сибирский государственный аэрокосмический университет им. акад. М. Ф. Решетнева. — 2012. —Т. 16. — № 2. — С. 691-692.

12. Вахрушев И. А., Каушан В. В., Падарян В. А., Федотов А. Н. Метод поиска уязвимости форматной строки // Труды института системного программирования РАН. — 2015 — Т. 27. — № 4. — С. 23-34. — ISSN 2079-8156. — DOI: 10.15514/ISPRAS-2015-27(4)-2

13. Падарян В. А., Каушан В. В., Федотов А. Н. Автоматизированный метод построения эксплойтов для уязвимости переполнения буфера на стеке // Труды института системного программирования РАН. — 2014. — Т. 26. — № 6. — С. 127-144. — ISSN 2079-8156.

14. Нурмухаметов А. Р., Жаботинский Е. А., Курмангалеев Ш. Ф., Гайсарян С. С., Вишняков А. В. Мелкогранулярная рандомизация адресного пространства программы при запуске // Труды института системного программирования РАН. — 2014. — Т. 29. — № 6. — С. 163-182. — ISSN 2079-8156.

15. Федотов А. Н., Падарян В. А., Каушан В. В., Курмангалеев Ш. Ф., Вишняков А. В., Нурмухаметов А. Р. Оценка критичности программных дефектов в условиях работы современных защитных механизмов // Труды института системного программирования РАН. — 2016. — Т. 28. — № 5. — С. 73-92. — DOI: 10.15514/ISPRAS-2016-28(5)-4.

16. Надеждин Е. Н., Щипцова Е. И., Шершакова Т. Л. Анализ уязвимостей программного обеспечения при проектировании механизма интегрированной защиты корпоративной информационной системы // Современные наукоёмкие технологии. — 2017. — № 10. — С. 32-38. — ISSN 1812-7320. — URL: http://www.top-technologies.ru/ru/article/view?id = 36824

17. Миронов С. В., Куликов Г. В. Технологии контроля безопасности автоматизированных систем на основе структурного и поведенческого тестирования программного обеспечения // Кибернетика и программирование. — 2015. — № 5. — С.158-172. — ISSN 2306-4196. — DOI: 10.7256/2306-4196.2017.1.20351

18. Азымшин И. М., Чуканов В. О. Анализ безопасности программного обеспечения // Безопасность информационных технологий. — 2014. —№ 1. — С. 45-47. — ISSN 2074-7136.

19. Соснин Ю. В., Куликов Г. В., Непомнящих А. В. Комплекс математических моделей оптимизации конфигурации средств защиты информации от несанкционированного доступа // Программные системы и вычислительные методы. — 2015. — № 1. — С. 32-44. — ISSN 2305-6061. — DOI: 10.7256/2305-6061.2015.1.14124

20. Непомнящих А. В., Куликов Г. В., Соснин Ю. В., Нащёкин П. А. Методы оценивания защищённости информации в автоматизированных системах от несанкционированного доступа // Вопросы защиты информации. — 2014. — № 1 (104). — С. 3-12. — ISSN 2073-2600.

21. Козачок А. В., Кочетков Е. В. Обоснование возможности применения верификации программ для обнаружения вредоносного кода. Вопросы кибербезопасности. — 2016. — Вып. 3(16). — С. 25-32. — ISSN 2311-3456. — URL:

https ://cyberleninka.ru/article/v/obosnovanie-vozmozhnosti-primeneniya-verifikatsii-

programm-dlya-obnaruzheniya-vredonosnogo-kod

i Надоели баннеры? Вы всегда можете отключить рекламу.