Вход на хостинг
IT-новости
20.04.2016 iPhone 2017 года поместят в водонепроницаемый корпус из стекла
Линейка iPhone в новом году серьезно поменяется. В этом уверен аналитический исследователь Мин Чи Ку......
30.07.2015 Ищем уникальный контент для сайта
Ищем уникальный контент для сайта Без уникального контента Ваш сайт обречен на то, что его страницы......
Часть 2
Владимир Мешков
Общая методика перехвата
Рассмотрим сначала теоретически, как осуществляется перехват методом прямого доступа к адресному пространству ядра, а затем приступим к практической реализации.
Прямой доступ к адресному пространству ядра
обеспечивает файл устройства /dev/kmem. В этом файле отображено все доступное
виртуальное адресное пространство, включая раздел подкачки (swap-область). Для
работы с файлом kmem используются стандартные системные функции – open(), read(),
write(). Открыв стандартным способом /dev/kmem, мы можем обратиться к любому
адресу в системе, задав его как смещение в этом файле. Данный метод был
разработан Сильвио Чезаре (Silvio Cesare) (см. статью «Runtime kernel kmem patching»,
Silvio Cesare,
Вспомним кратко механизм функционирования системных вызовов в ОС Linux (см. мою статью «Перехват системных вызовов в ОС Linux». – Журнал «Системный администратор». – 2003 г. №3(4). с.40-44).
Обращение к системной функции осуществляется посредством загрузки параметров функции в регистры процессора и последующим вызовом программного прерывания int $0x80. Обработчик этого прерывания, функция system_call, помещает параметры вызова в стек, извлекает из таблицы sys_call_table адрес вызываемой системной функции и передает управление по этому адресу.
Имея полный доступ к адресному пространству ядра, мы можем получить все содержимое таблицы системных вызовов, т.е. адреса всех системных функций. Изменив адрес любого системного вызова, мы, тем самым, осуществим его перехват. Но для этого необходимо знать адрес таблицы, или, другими словами, смещение в файле /dev/kmem, по которому эта таблица расположена.
Чтобы определить адрес таблицы sys_call_table, предварительно необходимо вычислить адрес функции system_call. Поскольку данная функция является обработчиком прерывания, давайте рассмотрим, как обрабатываются прерывания в защищенном режиме.
В реальном режиме процессор при регистрации прерывания обращается к таблице векторов прерываний, находящейся всегда в самом начале памяти и содержащей двухсловные адреса программ обработки прерываний. В защищенном режиме аналогом таблицы векторов прерываний является таблица дескрипторов прерываний (IDT, Interrupt Descriptor Table), располагающаяся в операционной системе защищенного режима. Для того чтобы процессор мог обратиться к этой таблице, ее адрес следует загрузить в регистр IDTR (Interrupt Descriptor Table Register, регистр таблицы дескрипторов прерываний). Таблица IDT содержит дескрипторы обработчиков прерываний, в которые, в частности, входят их адреса. Эти дескрипторы называются шлюзами (вентилями). Процессор, зарегистрировав прерывание, по его номеру извлекает из IDT шлюз, определяет адрес обработчика и передает ему управление.
Для вычисления адреса функции system_call из таблицы IDT необходимо извлечь шлюз прерывания int $0x80, а из него – адрес соответствующего обработчика, т.е. адрес функции system_call. В функции system_call обращение к таблице sys_call_table выполняет команда call <адрес таблицы>(,%eax,4) (см. файл arch/i386/kernel/entry.S). Найдя опкод (сигнатуру) этой команды в файле /dev/kmem, мы найдем и адрес таблицы системных вызовов.
Для определения опкода воспользуемся отладчиком gdb. Загрузим отладчик:
gdb -q /usr/src/linux/vmlinux
Дизассемблируем функцию system_call:
disass system_call
В ответ на экран будет выведен ассемблерный листинг. В этом листинге ищем строку типа:
0xc010904d <system_call+45>: call *0xc0200520(,%eax,4)
Это и есть обращение к таблице sys_call_table. Значение 0xc0200520 – адрес таблицы (скорее всего, у вас числа будут другими). Получим опкод этой команды:
x/xw (system_call+45)
Результат:
0xc010904d <system_call+45>: 0x208514ff
Мы нашли опкод команды обращения к таблице sys_call_table. Он равен xffx14x85. Следующие за ним 4 байта – это адрес таблицы. Убедиться в этом можно, введя команду:
x/xw (system_call+45+3)