Library
|
Your profile |
Cybernetics and programming
Reference:
Revnivykh A.V., Velizhanin A.S.
The study of the disassembled representation of executable files generated by different compilers. Example of buffer overflow vulnerability.
// Cybernetics and programming.
2019. № 1.
P. 1-17.
DOI: 10.25136/2644-5522.2019.1.28238 URL: https://en.nbpublish.com/library_read_article.php?id=28238
The study of the disassembled representation of executable files generated by different compilers. Example of buffer overflow vulnerability.
DOI: 10.25136/2644-5522.2019.1.28238Received: 03-12-2018Published: 13-12-2018Abstract: 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: Information security, Vulnerabilities, Code analysis, Disassembling, Buffer overflow, Compiler stamps, Debug mode, 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 не содержат многих защитных механизмов. Это так же является причиной, обосновывающей необходимость учитывать тип сборки при эксплуатации уязвимостей. Кроме того, в ходе компиляции некоторые функции могут быть развернуты компилятором в соответствующий программный код. Отличия особенностей дизассемблированного представления кода, собранного различными компиляторами, формируют предположение о возможности определить тип используемого при сборке какой-либо программы компилятора интеллектуально, основываясь на структуре кода и других отличительных особенностях дизассемблированного представления бинарного файла. Помимо прочего, исходные коды исследуемых на уязвимости программных решений доступны не всегда. В совокупности с выявленными различиями, в результирующем машинном коде в зависимости от режима компиляции это обосновывает необходимость развития средств поиска уязвимостей в программном обеспечении без использования исходного кода. Также, из изложенного материала видно, что реакция на возникновения ошибки (согласно предложенным примерам, на переполнение буфера) может так же быть разнообразной. Этот факт может послужить причиной, которая не позволит обнаружить наличие ошибки в минимальные сроки; и это подтверждает необходимость проведения качественного статического анализа дизассемблированных листингов перед началом динамического анализа. References
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 |