Вход на хостинг
IT-новости
20.04.2016 iPhone 2017 года поместят в водонепроницаемый корпус из стекла
Линейка iPhone в новом году серьезно поменяется. В этом уверен аналитический исследователь Мин Чи Ку......
30.07.2015 Ищем уникальный контент для сайта
Ищем уникальный контент для сайта Без уникального контента Ваш сайт обречен на то, что его страницы......
Владимир Мешков
За последние годы операционная система Linux прочно заняла лидирующее положение в качестве серверной платформы, опережая многие коммерческие разработки. Тем не менее вопросы защиты информационных систем, построенных на базе этой ОС, не перестают быть актуальными. Существует большое количество технических средств, как программных, так и аппаратных, которые позволяют обеспечить безопасность системы. Это средства шифрования данных и сетевого трафика, разграничения прав доступа к информационным ресурсам, защиты электронной почты, веб-серверов, антивирусной защиты, и т. д. Список, как вы понимаете, достаточно длинный. В данной статье предлагаем вам рассмотреть механизм защиты, основанный на перехвате системных вызовов операционной системы Linux. Данный механизм позволяет взять под контроль работу любого приложения и тем самым предотвратить возможные деструктивные действия, которые оно может выполнить.
Системные вызовы
Начнем с определения. Системные вызовы – это набор функций, реализованных в ядре ОС. Любой запрос приложения пользователя в конечном итоге трансформируется в системный вызов, который выполняет запрашиваемое действие. Полный перечень системных вызовов ОС Linux находится в файле /usr/include/asm/unistd.h. Давайте рассмотрим общий механизм выполнения системных вызовов на примере. Пусть в исходном тексте приложения вызывается функция creat() для создания нового файла. Компилятор, встретив вызов данной функции, преобразует его в ассемблерный код, обеспечивая загрузку номера системного вызова, соответствующего данной функции, и ее параметров в регистры процессора и последующий вызов прерывания 0x80. В регистры процессора загружаются следующие значения:
n в регистр EAX – номер системного вызова. Так, для нашего случая номер системного вызова будет равен 8 (см. __NR_creat);
n в регистр EBX – первый параметр функции (для creat это указатель на строку, содержащую имя создаваемого файла);
n в регистр ECX – второй параметр (права доступа к файлу).
В регистр EDX загружается третий параметр, в данном случае он у нас отсутствует. Для выполнения системного вызова в ОС Linux используется функция system_call, которая определена в файле /usr/src/liux/arch/i386/kernel/entry.S. Эта функция – точка входа для всех системных вызовов. Ядро реагирует на прерывание 0x80 обращением к функции system_call, которая, по сути, представляет собой обработчик прерывания 0x80.
Чтобы убедиться, что мы на правильном пути, напишем небольшой тестовый фрагмент на ассемблере. В нем увидим, во что превращается функция creat() после компиляции. Файл назовем test.S. Вот его содержание:
.globl _start
.text
_start:
В регистр EAX загружаем номер системного вызова:
movl $8, %eax
В регистр EBX – первый параметр, указатель на строку с именем файла:
movl $filename, %ebx
В регистр ECX – второй параметр, права доступа:
movl $0, %ecx
Вызываем прерывание:
int $0x80
Выходим из программы. Для этого вызовем функцию exit(0):
movl $1, %eax movl $0, %ebx int $0x80
В сегменте данных укажем имя создаваемого файла:
.data
filename: .string "file.txt"
Компилируем:
gcc -с test.S
ld -s -o test test.o
В текущем каталоге появится исполняемый файл test. Запустив его, мы создадим новый файл с именем file.txt.
А теперь давайте вернемся к рассмотрению механизма системных вызовов. Итак, ядро вызывает обработчик прерывания 0x80 – функцию system_call. System_call помещает копии регистров, содержащих параметры вызова, в стек при помощи макроса SAVE_ALL и командой call вызывает нужную системную функцию. Таблица указателей на функции ядра, которые реализуют системные вызовы, расположена в массиве sys_call_table (см. файл arch/i386/kernel/entry.S). Номер системного вызова, который находится в регистре EAX, является индексом в этом массиве. Таким образом, если в EAX находится значение 8, будет вызвана функция ядра sys_creat(). Зачем нужен макрос SAVE_ALL? Объяснение тут очень простое. Так как практически все системные функции ядра написаны на С, то свои параметры они ищут в стеке. А параметры помещаются в стек при помощи макроса SAVE_ALL! Возвращаемое системным вызовом значение сохраняется в регистр EAX.