по
Меню журнала
> Архив номеров > Рубрики > О журнале > Авторы > О журнале > Требования к статьям > Редакция и редакционный совет > Порядок рецензирования статей > Рецензирование за 24 часа – как это возможно? > Политика издания > Ретракция статей > Этические принципы > Политика открытого доступа > Оплата за публикации в открытом доступе > Публикация за 72 часа: что это? > Политика авторских прав и лицензий > Политика цифрового хранения публикации > Политика идентификации статей > Политика проверки на плагиат
Журналы индексируются
Реквизиты журнала

Публикация за 72 часа - теперь это реальность!
При необходимости издательство предоставляет авторам услугу сверхсрочной полноценной публикации. Уже через 72 часа статья появляется в числе опубликованных на сайте издательства с DOI и номерами страниц.
По первому требованию предоставляем все подтверждающие публикацию документы!
ГЛАВНАЯ > Вернуться к содержанию
Кибернетика и программирование
Правильная ссылка на статью:

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

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

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

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

Revnivykh Aleksandr Vladimirovich

PhD in Technical Science

Associate Professor, Department of Information Security, Novosibirsk State University of Economics and Management 

630099, Russia, Novosibirskaya oblast', g. Novosibirsk, ul. Kamenskaya, 56

al.revnivykh@mail.ru
Другие публикации этого автора
 

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

Специалист, ФГБОУ ВО «Тюменский индустриальный университет»»

625000, Россия, Тюменская область, г. Тюмень, ул. Володарского, 38

Velizhanin Anatolii Sergeevich

Specialist, Tyumen Industrial University

625000, Russia, Tyumenskaya oblast', g. Tyumen', ul. Volodarskogo, 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


Abstract.

The subject of the study is a potential  buffer overflow vulnerability in various software related to the function of the standard C / C ++ strcpy programming language library and approaches and methods for finding such vulnerabilities. The object of the study is the data of the machine code of the compilers when the program is assembled in various modes. The purpose of the study is to analyze some features of the machine code generated by various compilers for Windows and Linux in the Debug and Release modes, including, on the basis of this, a review of the buffer overflow vulnerability. Research methods. The paper reviews and develops methods for constructing algorithms for searching for buffer overflow vulnerabilities, examines the characteristics of this vulnerability at the level of machine code. This is done using the Visual C ++ compilers, Intel C ++ compilers, g ++ compilers, as well as the WinDBG, GDB debuggers. Key findings. Building programs in different modes leads to the formation of differences in the executable code, which is made from the completely same high-level programming language code; these differences manifest themselves in differences in program behavior. In the course of researching software in search of vulnerabilities, it is important to analyze computer code in order to identify hidden patterns. The novelty of the study lies in identifying differences in the machine code obtained after assembling the same high-level code, identifying compiler stamps when executing the assembly of the program in different modes. A special contribution of the author to the study of the topic is the development of methods for constructing algorithms for searching for buffer overflow vulnerabilities.

Keywords:

Debug mode, Compiler stamps, Buffer overflow, Disassembling, Code analysis, Vulnerabilities, Information security, Release mode, Algorithm construction methods, WinDBG debugger

Введение

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

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

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

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

В качестве одного из типов уязвимости рассмотрим переполнение буфера. Переполнение буфера (Buffer Overflow) — явление, возникающее, когда компьютерная программа записывает данные за пределами выделенного в памяти буфера [3]. Следует отметить, что в настоящее время имеется множество технологий защиты от некоторых последствий данного вида атак. В частности, технологии 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[1]);

14: return 0;

15: }

16: }

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

Листинг 2.

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

13: strcpy(arr, argv[1]);

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[1]);

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 можно прочитать в [4], а также в официальной документации на сайте производителя процессора [5].

В результате запуска программы из листинга 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 5f pop 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.

Листинг 5.

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

13 00000001`3f9f105e e885010000 call exp_1!strcpy (00000001`3f9f11e8)

14 00000001`3f9f1063 33c0 xor eax,eax

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

16 00000001`3f9f1065 488bf8 mov rdi,rax

16 00000001`3f9f1068 488bcc mov rcx,rsp

16 00000001`3f9f106b 488d156e570000 lea rdx,[exp_1!__xi_z+0x180 (00000001`3f9f67e0)]

16 00000001`3f9f1072 e8a9010000 call exp_1!_RTC_CheckStackVars (00000001`3f9f1220)

16 00000001`3f9f1077 488bc7 mov rax,rdi

16 00000001`3f9f107a 488b4c2450 mov rcx,qword ptr [rsp+50h]

16 00000001`3f9f107f 4833cc xor rcx,rsp

16 00000001`3f9f1082 e879010000 call exp_1!__security_check_cookie (00000001`3f9f1200)

16 00000001`3f9f1087 4883c460 add rsp,60h

16 00000001`3f9f108b 5f pop rdi

16 00000001`3f9f108c c3 ret

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

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

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

Листинг 6.

Breakpoint 0 hit

exp_1!main:

00000001`3f9f1010 4889542410 mov qword ptr [rsp+10h],rdx ss:00000000`0027f878={exp_1!__xc_z (00000001`3f9f6220)}

0:000> g

Breakpoint 1 hit

exp_1!main+0x62:

00000001`3f9f1072 e8a9010000 call exp_1!_RTC_CheckStackVars (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`3f9f1c38 cc int 3

0:000> g

Breakpoint 2 hit

exp_1!main+0x72:

00000001`3f9f1082 e879010000 call exp_1!__security_check_cookie (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`77ac9361 cc int 3

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

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

Листинг 7.

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

13 00000001`3fd1102d 488b4a08 mov rcx,qword ptr [rdx+8]

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

13 00000001`3fd11031 0fb601 movzx eax,byte ptr [rcx]

13 00000001`3fd11034 48ffc1 inc rcx

13 00000001`3fd11037 84c0 test al,al

13 00000001`3fd11039 75f6 jne exp_1!main+0x31 (00000001`3fd11031)

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

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

Листинг 8.

Breakpoint 0 hit

exp_1!main:

00000001`3fd11000 4883ec28 sub rsp,28h

0:000> p

exp_1!main+0x13:

00000001`3fd11013 83f902 cmp ecx,2

0:000> g

Breakpoint 1 hit

exp_1!main+0x2d:

00000001`3fd1102d 488b4a08 mov rcx,qword ptr [rdx+8] ds:00000000`000d2408=00000000000d246c

0:000> g

Breakpoint 2 hit

exp_1!main+0x45:

00000001`3fd11045 e816000000 call exp_1!__security_check_cookie (00000001`3fd11060)

0:000> g

Breakpoint 3 hit

exp_1!main+0x4e:

00000001`3fd1104e c3 ret

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

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

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

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

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

Листинг 9.

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

13 00000001`3f8c10cc e88f020000 call exp_1!strcpy (00000001`3f8c1360)

13 00000001`3f8c10d1 48894520 mov qword ptr [rbp+20h],rax

14 00000001`3f8c10d5 b800000000 mov eax,0

14 00000001`3f8c10da 89452c mov dword ptr [rbp+2Ch],eax

14 00000001`3f8c10dd 4889e9 mov rcx,rbp

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

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

Однако, сравнивая листинги, мы можем также заметить различные способы выполнения некоторых действий. Так, например, в листинге 9, полученном при сборке программы компилятором от Intel, мы можем наблюдать обнуление регистра процессора eax, прямой записью в него нулевого значения в строке по адресу 00000001`3f8c10d5. В листинге 5, с которым мы проводим сравнение, и полученным в результате сборки программы соответствующим компилятором от Microsoft, обнуление производится через операцию xor в строке по адресу 00000001`3f9f1063. Кроме того, в компиляторе Visual C++ обращение к стековым переменным происходит через регистр rdi, в то время как Intel C++ для данной операции использует смещения от регистра rbp. Для большей ясности приведем отрывок дизассемблированного листинга (листинг 10) функции «main», где происходит формирование значения регистра rdi (компилятор Microsoft Visual C++).

Листинг 10.

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

7 00000001`3f201010 4889542410 mov qword ptr [rsp+10h],rdx

7 00000001`3f201015 894c2408 mov dword ptr [rsp+8],ecx

7 00000001`3f201019 57 push rdi

7 00000001`3f20101a 4883ec60 sub rsp,60h

7 00000001`3f20101e 488bfc mov rdi,rsp

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

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

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

Листинг 11.

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

7 00000001`3f8c1010 55 push rbp

7 00000001`3f8c1011 4883ec60 sub rsp,60h

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

Проводя исследование полного кода функций «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 LSB 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

0x00000000004005a1 <+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 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa

*** 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.

(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-05-0.
3.
Википедия. Переполнение буфера [Электронный ресурс] URL: http://en.wikipedia.org/wiki/Переполнение_буфера
4.
Аблязов Р. З. Программирование на ассемблере на платформе х86_64. Учеб. пособие / Р. З. Аблязов. — Москва: ДМК Пресс, 2011. — 305 c. — ISBN: 978-5-94074-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/article/v/issledovanie-programmnyh-uyazvimostey-v-kompyuternyh-sistemah-i-analiz-primenyaemogo-programmnogo-obespecheniya-dlya-provedeniya-atak
8.
Нурмухаметов А. Р., Курмангалеев Ш. Ф., Каушан В. В., Гайсарян С. С. Применение компиляторных преобразований для противодействия эксплуатации уязвимостей программного обеспечения // Труды института системного программирования РАН. — 2014. — Т. 26. — № 3. — С. 113–124. — ISSN 2079-8156.
9.
Федотов А. Н. Метод оценки эксплуатируемости программных дефектов // Труды института системного программирования РАН. — 2016. — Т. 28. — № 4. — С. 137–148. — DOI: 10.15514/ISPRAS-2016-28(4)-8.
10.
Федотов А. Н., Каушан В. В., Гайсарян С. С., Курмангалеев Ш. Ф. Построение предикатов безопасности для некоторых типов программных дефектов // Труды института системного программирования РАН. — 2017. — Т. 29. — № 6. — С. 151–162. — 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. — Bып. 3(16). — С. 25–32. — ISSN 2311-3456. — URL: https://cyberleninka.ru/article/v/obosnovanie-vozmozhnosti-primeneniya-verifikatsii-programm-dlya-obnaruzheniya-vredonosnogo-kod
References (transliterated)
1.
Mukhanova A. A., Revnivykh A. V., Fedotov A. M. Klassifikatsiya ugroz i uyazvimostei informatsionnoi bezopasnosti v korporativnykh sistemakh // Vestnik NGU. Ser.: Informatsionnye tekhnologii. — 2013. — T. 11. — №
2.
–—S. 55–72. — ISSN 1818-7900. 2.Velizhanin A. S., Revnivykh A. V. Evristicheskii metod poiska uyazvimostei v PO bez ispol'zovaniya iskhodnogo koda // XIV Rossiiskaya konferentsiya s mezhdunarodnym uchastiem "Raspredelennye informatsionnye i vychislitel'nye resursy" (DICR-2012). — ISBN 978-5-905569-05-0.
3.
Vikipediya. Perepolnenie bufera [Elektronnyi resurs] URL: http://en.wikipedia.org/wiki/Perepolnenie_bufera
4.
Ablyazov R. Z. Programmirovanie na assemblere na platforme kh86_64. Ucheb. posobie / R. Z. Ablyazov. — Moskva: DMK Press, 2011. — 305 c. — ISBN: 978-5-94074-676-8
5.
Ofitsial'nyi sait kompanii Intel. [Elektronnyi resurs] URL: www.intel.com
6.
Revnivykh A. V., Fedotov A. M. Monitoring informatsionnoi infrastruktury organizatsii // Vestnik NGU. Ser.: Informatsionnye tekhnologii. — 2013. — T. 11. — № 4. — S. 84–91. — URL: https://nsu.ru/xmlui/bitstream/handle/nsu/1295/2013_V11_N4_8.pdf.
7.
Voropaev D. P., Zaugolkov I. A. Issledovanie programmnykh uyazvimostei v komp'yuternykh sistemakh i analiz primenyaemogo programmnogo obespecheniya dlya provedeniya atak na vychislitel'nuyu sistemu // Vestnik Tambovskogo universiteta. Seriya: Estestvennye i tekhnicheskie nauki. — 2014. — T. 19. — № 2. — S. 637–638. — ISSN 1810-0198. —URL: https://cyberleninka.ru/article/v/issledovanie-programmnyh-uyazvimostey-v-kompyuternyh-sistemah-i-analiz-primenyaemogo-programmnogo-obespecheniya-dlya-provedeniya-atak
8.
Nurmukhametov A. R., Kurmangaleev Sh. F., Kaushan V. V., Gaisaryan S. S. Primenenie kompilyatornykh preobrazovanii dlya protivodeistviya ekspluatatsii uyazvimostei programmnogo obespecheniya // Trudy instituta sistemnogo programmirovaniya RAN. — 2014. — T. 26. — № 3. — S. 113–124. — ISSN 2079-8156.
9.
Fedotov A. N. Metod otsenki ekspluatiruemosti programmnykh defektov // Trudy instituta sistemnogo programmirovaniya RAN. — 2016. — T. 28. — № 4. — S. 137–148. — DOI: 10.15514/ISPRAS-2016-28(4)-8.
10.
Fedotov A. N., Kaushan V. V., Gaisaryan S. S., Kurmangaleev Sh. F. Postroenie predikatov bezopasnosti dlya nekotorykh tipov programmnykh defektov // Trudy instituta sistemnogo programmirovaniya RAN. — 2017. — T. 29. — № 6. — S. 151–162. — DOI: 10.15514/ISPRAS-2017-29(6)-8.
11.
Shudrak M. O., Kheirkhabarov T. S. Avtomatizirovannyi poisk uyazvimostei v binarnom kode // Reshetnevskie chteniya. Sibirskii gosudarstvennyi aerokosmicheskii universitet im. akad. M. F. Reshetneva. — 2012. —T. 16. — № 2. — S. 691–692.
12.
Vakhrushev I. A., Kaushan V. V., Padaryan V. A., Fedotov A. N. Metod poiska uyazvimosti formatnoi stroki // Trudy instituta sistemnogo programmirovaniya RAN. — 2015 — T. 27. — № 4. — S. 23–34. — ISSN 2079-8156. — DOI: 10.15514/ISPRAS-2015-27(4)-2
13.
Padaryan V. A., Kaushan V. V., Fedotov A. N. Avtomatizirovannyi metod postroeniya eksploitov dlya uyazvimosti perepolneniya bufera na steke // Trudy instituta sistemnogo programmirovaniya RAN. — 2014. — T. 26. — № 6. — S. 127–144. — ISSN 2079-8156.
14.
Nurmukhametov A. R., Zhabotinskii E. A., Kurmangaleev Sh. F., Gaisaryan S. S., Vishnyakov A. V. Melkogranulyarnaya randomizatsiya adresnogo prostranstva programmy pri zapuske // Trudy instituta sistemnogo programmirovaniya RAN. — 2014. — T. 29. — № 6. — S. 163–182. — ISSN 2079-8156.
15.
Fedotov A. N., Padaryan V. A., Kaushan V. V., Kurmangaleev Sh. F., Vishnyakov A. V., Nurmukhametov A. R. Otsenka kritichnosti programmnykh defektov v usloviyakh raboty sovremennykh zashchitnykh mekhanizmov // Trudy instituta sistemnogo programmirovaniya RAN. — 2016. — T. 28. — № 5. — S. 73–92. — DOI: 10.15514/ISPRAS-2016-28(5)-4.
16.
Nadezhdin E. N., Shchiptsova E. I., Shershakova T. L. Analiz uyazvimostei programmnogo obespecheniya pri proektirovanii mekhanizma integrirovannoi zashchity korporativnoi informatsionnoi sistemy // Sovremennye naukoemkie tekhnologii. — 2017. — № 10. — S. 32–38. — ISSN 1812-7320. — URL: http://www.top-technologies.ru/ru/article/view?id=36824
17.
Mironov S. V., Kulikov G. V. Tekhnologii kontrolya bezopasnosti avtomatizirovannykh sistem na osnove strukturnogo i povedencheskogo testirovaniya programmnogo obespecheniya // Kibernetika i programmirovanie. — 2015. — № 5. — S.158–172. — ISSN 2306-4196. — DOI: 10.7256/2306-4196.2017.1.20351
18.
Azymshin I. M., Chukanov V. O. Analiz bezopasnosti programmnogo obespecheniya // Bezopasnost' informatsionnykh tekhnologii. — 2014. —№ 1. — S. 45–47. — ISSN 2074-7136.
19.
Sosnin Yu. V., Kulikov G. V., Nepomnyashchikh A. V. Kompleks matematicheskikh modelei optimizatsii konfiguratsii sredstv zashchity informatsii ot nesanktsionirovannogo dostupa // Programmnye sistemy i vychislitel'nye metody. — 2015. — № 1. — S. 32–44. — ISSN 2305-6061. — DOI: 10.7256/2305-6061.2015.1.14124
20.
Nepomnyashchikh A. V., Kulikov G. V., Sosnin Yu. V., Nashchekin P. A. Metody otsenivaniya zashchishchennosti informatsii v avtomatizirovannykh sistemakh ot nesanktsionirovannogo dostupa // Voprosy zashchity informatsii. — 2014. — № 1 (104). — S. 3–12. — ISSN 2073-2600.
21.
Kozachok A. V., Kochetkov E. V. Obosnovanie vozmozhnosti primeneniya verifikatsii programm dlya obnaruzheniya vredonosnogo koda. Voprosy kiberbezopasnosti. — 2016. — Byp. 3(16). — S. 25–32. — ISSN 2311-3456. — URL: https://cyberleninka.ru/article/v/obosnovanie-vozmozhnosti-primeneniya-verifikatsii-programm-dlya-obnaruzheniya-vredonosnogo-kod
Ссылка на эту статью

Просто выделите и скопируйте ссылку на эту статью в буфер обмена. Вы можете также попробовать найти похожие статьи


Другие сайты издательства:
Официальный сайт издательства NotaBene / Aurora Group s.r.o.
Сайт исторического журнала "History Illustrated"