Перехват системных вызовов в операционной системе Linux. Часть 2::Журнал СА 5.2003
www.samag.ru
Журнал «БИТ. Бизнес&Информационные технологии»      
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Подписка
Архив номеров
Где купить
Наука и технологии
Авторам
Рекламодателям
Контакты
   

  Опросы
  Статьи

Работа с Debian  

О Linux с любовью или Debian: через знание к любви

Конечно, одним лишь перечислением замечательных качеств любовь к Linux не возникнет. Для

 Читать далее...

Опрос  

Защита личных и клиентских данных: как мошенники используют ИИ и как защититься?

По данным RED Security, общее число кибератак на российские компании в 2024

 Читать далее...

Опрос  

Облачные инструменты для разработчиков

Эксперты ИТ-отрасли отвечают на вопросы «Системного администратора» > Как с помощью облака сделать

 Читать далее...

Опрос  

Рынок мобильных приложений: что будет актуальным в 2025 году?

Эксперты ИТ-отрасли отвечают на вопросы «Системного администратора» > Ваши прогнозы: чего ожидать от

 Читать далее...

Рынок труда  

Как успешно пройти все этапы собеседования на ИТ-должность?

По оценкам государства, дефицит ИТ-специалистов составляет от 740 тысяч до 1 миллиона

 Читать далее...

1001 и 1 книга  
19.03.2018г.
Просмотров: 7598
Комментарии: 0
Машинное обучение с использованием библиотеки Н2О

 Читать далее...

12.03.2018г.
Просмотров: 7857
Комментарии: 0
Особенности киберпреступлений в России: инструменты нападения и защита информации

 Читать далее...

12.03.2018г.
Просмотров: 5216
Комментарии: 0
Глубокое обучение с точки зрения практика

 Читать далее...

12.03.2018г.
Просмотров: 3346
Комментарии: 0
Изучаем pandas

 Читать далее...

12.03.2018г.
Просмотров: 4139
Комментарии: 0
Программирование на языке Rust (Цветное издание)

 Читать далее...

19.12.2017г.
Просмотров: 4152
Комментарии: 0
Глубокое обучение

 Читать далее...

19.12.2017г.
Просмотров: 6649
Комментарии: 0
Анализ социальных медиа на Python

 Читать далее...

19.12.2017г.
Просмотров: 3488
Комментарии: 0
Основы блокчейна

 Читать далее...

19.12.2017г.
Просмотров: 3768
Комментарии: 0
Java 9. Полный обзор нововведений

 Читать далее...

16.02.2017г.
Просмотров: 7642
Комментарии: 0
Опоздавших не бывает, или книга о стеке

 Читать далее...

17.05.2016г.
Просмотров: 11006
Комментарии: 0
Теория вычислений для программистов

 Читать далее...

30.03.2015г.
Просмотров: 12730
Комментарии: 0
От математики к обобщенному программированию

 Читать далее...

18.02.2014г.
Просмотров: 14512
Комментарии: 0
Рецензия на книгу «Читаем Тьюринга»

 Читать далее...

13.02.2014г.
Просмотров: 9450
Комментарии: 0
Читайте, размышляйте, действуйте

 Читать далее...

12.02.2014г.
Просмотров: 7416
Комментарии: 0
Рисуем наши мысли

 Читать далее...

10.02.2014г.
Просмотров: 5699
Комментарии: 4
Страна в цифрах

 Читать далее...

18.12.2013г.
Просмотров: 4903
Комментарии: 0
Большие данные меняют нашу жизнь

 Читать далее...

18.12.2013г.
Просмотров: 3756
Комментарии: 0
Компьютерные технологии – корень зла для точки роста

 Читать далее...

04.12.2013г.
Просмотров: 3438
Комментарии: 0
Паутина в облаках

 Читать далее...

03.12.2013г.
Просмотров: 3668
Комментарии: 1
Рецензия на книгу «MongoDB в действии»

 Читать далее...

Друзья сайта  

 Перехват системных вызовов в операционной системе Linux. Часть 2

Архив номеров / 2003 / Выпуск №5 (6) / Перехват системных вызовов в операционной системе Linux. Часть 2

Рубрика: Программирование /  Аудит

ВЛАДИМИР МЕШКОВ

Перехват системных вызовов

в операционной системе Linux

Часть 2

Общая методика перехвата

Рассмотрим сначала теоретически, как осуществляется перехват методом прямого доступа к адресному пространству ядра, а затем приступим к практической реализации.

Прямой доступ к адресному пространству ядра обеспечивает файл устройства /dev/kmem. В этом файле отображено все доступное виртуальное адресное пространство, включая раздел подкачки (swap-область). Для работы с файлом kmem используются стандартные системные функции – open(), read(), write(). Открыв стандартным способом /dev/kmem, мы можем обратиться к любому адресу в системе, задав его как смещение в этом файле. Данный метод был разработан Сильвио Чезаре (Silvio Cesare) (см. статью «Runtime kernel kmem patching», Silvio Cesare, http://www.sans.org/rr/threats/rootkits.php).

Вспомним кратко механизм функционирования системных вызовов в ОС Linux (см. мою статью «Перехват системных вызовов в ОС Linux». – Журнал «Системный администратор». – 2003 г. №3(4). с.40-44).

Обращение к системной функции осуществляется посредством загрузки параметров функции в регистры процессора и последующим вызовом программного прерывания int $0x80. Обработчик этого прерывания, функция system_call, помещает параметры вызова в стек, извлекает из таблицы sys_call_table адрес вызываемой системной функции и передает управление по этому адресу.

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

Чтобы определить адрес таблицы sys_call_table, предварительно необходимо вычислить адрес функции system_call. Поскольку данная функция является обработчиком прерывания, давайте рассмотрим, как обрабатываются прерывания в защищенном режиме.

Для вычисления адреса функции 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 : call *0xc0200520(,%eax,4)

Это и есть обращение к таблице sys_call_table. Значение 0xc0200520 – адрес таблицы (скорее всего, у вас числа будут другими). Получим опкод этой команды:

x/xw (system_call+45)

Результат:

0xc010904d : 0x208514ff

Мы нашли опкод команды обращения к таблице sys_call_table. Он равен xffx14x85. Следующие за ним 4 байта – это адрес таблицы. Убедиться в этом можно, введя команду:

x/xw (system_call+45+3)

В ответ получим:

0xc0109050 : 0xc02000520

Таким образом, найдя в файле /dev/kmem последовательность xffx14x85 и считав следующие за ней 4 байта, мы получим адрес таблицы системных вызовов sys_call_table. Зная ее адрес, мы можем получить содержимое этой таблицы (адреса всех системных функций) и изменить адрес любого системного вызова, перехватить его.

Рассмотрим псевдокод, выполняющий операцию перехвата:

readaddr (&old_syscall, sct + SYS_CALL*4, 4);

writeaddr (new_syscall, sct + SYS_CALL*4, 4);

Функция readaddr считывает адрес системного вызова из таблицы системных вызовов и сохраняет его в переменной old_syscall. Каждая запись в таблице sys_call_table занимает 4 байта. Искомый адрес расположен по смещению sct+SYS_CALL*4 в файле /dev/kmem (здесь sct – адрес таблицы sys_call_table, SYS_CALL – порядковый номер системного вызова). Функция writeaddr перезаписывает адрес системного вызова SYS_CALL адресом функции new_syscall, и все обращения к системному вызову SYS_CALL будут обслуживаться этой функцией.

Кажется, все просто и цель достигнута. Однако давайте вспомним, что мы работаем в адресном пространстве пользователя. Если разместить новую системную функцию в этом адресном пространстве, то при вызове этой функции мы получим красивое сообщение об ошибке. Отсюда вывод – новый системный вызов необходимо разместить в адресном пространстве ядра. Для этого необходимо: получить блок памяти в пространстве ядра, разместить в этом блоке новый системный вызов.

Выделить память в пространстве ядра можно при помощи функции kmalloc. Но вызвать напрямую функцию ядра из адресного пространства пользователя нельзя, поэтому воспользуемся следующим алгоритмом:

  • зная адрес таблицы sys_call_table, получаем адрес некоторого системного вызова (например, sys_mkdir);
  • определяем функцию, выполняющую обращение к функции kmalloc. Эта функция возвращает указатель на блок памяти в адресном пространстве ядра. Назовем эту функцию get_kmalloc;
  • сохраняем первые N байт системного вызова sys_mkdir, где N – размер функции get_kmalloc;
  • перезаписываем первые N байт вызова sys_mkdir функцией get_kmalloc;
  • выполняем обращение к системному вызову sys_mkdir, тем самым запустив на выполнение функцию get_kmalloc;
  • восстанавливаем первые N байт системного вызова sys_mkdir.

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

Функция get_kmalloc выглядит следующим образом:

struct  kma_struc {

        ulong   (*kmalloc) (uint, int);

        int     size;

        int     flags;

        ulong   mem;

} __attribute__ ((packed));

int     get_kmalloc(struct kma_struc *k)

{

        k->mem = k->kmalloc(k->size, k->flags);

        return 0;

}

Поля структуры struct kma_struc заполняются следующими значениями:

  • поле size – требуемый размер блока памяти;
  • поле флаг – спецификатор GFP_KERNEL. Для версий ядра 2.4.9 и выше это значение составляет 0x1f0;
  • поле mem – в этом поле будет сохранен указатель на начало блока памяти длиной size, выделенного в адресном пространстве ядра (возвращаемое функцией kmalloc значение);
  • поле kmalloc – адрес функции kmalloc.

Адрес функции kmalloc необходимо найти. Сделать это можно несколькими способами. Самый простой путь – считать этот адрес из файла System.map или определить с помощью отладчика gdb (print &kmalloc). Если в ядре включена поддержка модулей, адрес kmalloc можно определить при помощи функции get_kernel_syms(). Этот вариант будет рассмотрен далее. Если же поддержка модулей ядра отсутствует, то адрес функции kmalloc придется искать по опкоду команды вызова kmalloc – аналогично тому, как было сделано для таблицы sys_call_table.

Функция kmalloc принимает два параметра: размер запрашиваемой памяти и спецификатор GFP. Вызов этой функции выглядит следующим образом:

push GFP_KERNEL

push size

call kmalloc

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

Загружаем отладчик:

gdb -q /usr/src/linux/vmlinux

Дизассемблируем функцию inter_module_register. Неважно, что делает эта функция, главное, в ней есть то, что нам нужно – вызов функции kmalloc:

disass inter_module_register

Сразу обращаем внимание на следующие строки:

0xc0110de4 <inter_module_register+4>: push $0x1f0

0xc0110de9 <inter_module_register+9>: push $0x14

0xc0110deb <inter_module_register+11>: call 0xc0121c38 <kmalloc>

Это и есть вызов функции kmalloc. Сначала в стек загружаются параметры, а затем следует вызов функции. Значение 0xc0121c38 в вызове call является адресом функции kmalloc. Первым в стек загружается спецификатор GFP (push $0x1f0). Как уже упоминалось, для версий ядра 2.4.9 и выше это значение составляет 0x1f0. Найдем опкод этой команды:

x/xw (inter_module_register+4)

В результате получаем:

0xc0110de4 : 0x0001f068

Если мы найдем этот опкод, то сможем вычислить адрес функции kmalloc. На первый взгляд, адрес этой функции является аргументом инструкции call, но это не совсем так. В отличии от функции system_call, здесь за инструкцией call стоит не адрес kmalloc, а смещение к нему относительно текущего адреса. Убедимся в этом, определив опкод команды call 0xc0121c38:

x/xw (inter_module_register+11)

В ответ получаем:

0xc0110deb : 0x010e48e8

Первый байт равен e8 – это опкод инструкции call. Найдем значение аргумента этой команды:

x/xw (inter_module_register+12)

Получим:

0xc0110deс : 0x00010e48

Теперь если мы сложим текущий адрес 0xc0110deb, смещение 0x00010e48 и 5 байт команды (1 байт инструкции call и 4 байта смещения), то получим искомый адрес функции kmalloc:

0xc0110deb + 0x00010e48 + 5 = 0xc0121c38

На этом завершим теоретические выкладки и, используя вышеприведенную методику, осуществим перехват системного вызова sys_mkdir.

Пример перехвата системного вызова

В начале, как всегда, заголовочные файлы:

#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <errno.h>

#include <linux/module.h>

#include <linux/unistd.h>

Определим имя файла устройства виртуальной памяти:

#define KMEM_FILE "/dev/kmem"

int main() {

Структура, описывающая формат регистра IDTR:

struct {

    unsigned short limit;

    unsigned int base;

} __attribute__ ((packed)) idtr;

Структура, описывающая формат шлюза прерывания таблицы IDT:

struct {

    unsigned short off1;

    unsigned short sel;

    unsigned char none, flags;

    unsigned short off2;

} __attribute__ ((packed)) idt;

Номер вызова, который мы будем перехватывать:

#define _SYS_MKDIR_ 39

Номер 39 соответствует системному вызову sys_mkdir.

Переменные и их назначение:

int kmem;

ulong get_kmalloc_size; – размер функции get_kmalloc

ulong get_kmalloc_addr; – адрес функции get_kmalloc

ulong new_mkdir_size; – размер функции-перехватчика

ulong new_mkdir_addr; – адрес функции-перехватчика вызова sys_mkdir

ulong sys_mkdir_addr; – адрес системного вызова sys_mkdir

ulong page_offset; – нижняя граница адресного пространства ядра

ulong sct; – адрес таблицы sys_call_table

ulong kma; – адрес функции kmalloc

unsigned char tmp[1024];

Значения адресов функций get_kmalloc и new_mkdir_call и их размеры нам предстоит определить. Пока что оставим эти поля пустыми.

struct  kma_struc {

        ulong   (*kmalloc) (uint, int);

        int     size;

        int     flags;

        ulong   mem;

} __attribute__ ((packed)) kmalloc;

int get_kmalloc(struct kma_struc *k)

{

        k->mem = k->kmalloc(k->size, k->flags);

        return 0;

}

Структура struct kma_struc и функция get_kmalloc и их назначение уже были рассмотрены.

int new_mkdir(const char *path)

{

    return 0;

}

Функция new_mkdir перехватывает системный вызов sys_mkdir. Она ничего не делает, просто возвращает нулевое значение.

Определим несколько функций для работы с файлом устройства /dev/kmem.

Функция чтения данных из kmem:

static inline int rkm(int fd, uint offset, void *buf, uint size)

{

        if (lseek(fd, offset, 0) != offset) return 0;

        if (read(fd, buf, size) != size) return 0;

        return size;

}

Функция записи данных в kmem:

static inline int wkm(int fd, uint offset, void *buf, uint size)

{

        if (lseek(fd, offset, 0) != offset) return 0;

        if (write(fd, buf, size) != size) return 0;

        return size;

}

Функция чтения 4-х байтового значения (int, unsigned long) из kmem:

static inline int rkml(int fd, uint offset, ulong *buf)

{

        return rkm(fd, offset, buf, sizeof(ulong));

}

Функция записи 4-х байтового значения в kmem:

static inline int wkml(int fd, uint offset, ulong buf)

{

        return wkm(fd, offset, &buf, sizeof(ulong));

}

Функция определения адреса таблицы sys_call_table:

ulong   get_sct()

{

 int       kmem;

  ulong    sys_call_off; – адрес обработчика прерывания int $0x80 (функция system_call)

  char     *p;

  char     sc_asm[128];

Командой SIDT получаем содержимое регистра таблицы дескрипторов прерываний. Результат команды поместим в структуру idtr:

asm("sidt %0" : "=m" (idtr));

В поле base структуры idtr находится адрес таблицы IDT. Зная этот адрес и размер шлюза в этой таблице (8 байт), получим содержимое шлюза прерывания int $0x80 и извлечем из него адрес обработчика:

 if (!rkm(kmem, idtr.base+(8*0x80), &idt, sizeof(idt)))

                return 0;

Содержимое шлюза поместим в структуру idt. Два поля этой структуры, off1 и off2, содержат адрес обработчика (функции system_call). В поле off1 находятся младшие 16 бит, а в поле off2 – старшие 16 бит адреса обработчика. Для получения адреса обработчика сложим содержимое этих полей следующим образом:

 sys_call_off = (idt.off2 << 16) | idt.off1;

Теперь нам известен адрес функции system_call (если точнее, это не адрес, а смещение в сегменте команд). Для получения адреса таблицы sys_call_table попытаемся найти опкод команды обращения к этой таблице.

Смещаемся по адресу функции system_call и считываем первые 128 байт обработчика в буфер sc_asm:

if (!rkm(kmem, sys_call_off, &sc_asm, 128)) return 0;

        close(kmem);

В этом буфере ищем опкод обращения к таблице sys_call_table. Поиск выполняется при помощи функции memmem. Данная функция возвращает указатель на позицию в буфере, в которой была найдена эталонная строка. В нашем случае эталонной строкой является опкод команды обращения к таблице sys_call_table – \xff\x14\x85. Если этот опкод найден, то следующие за ним 4 байта будут содержать адрес таблицы:

p = (char *)memmem(sc_asm, 128, "\xff\x14\x85", 3) + 3;

Если опкод удалось найти, возвращаем адрес таблицы системных вызовов:

if (p) return *(ulong *)p;

В случае неудачи возвращаем нулевое значение:

return 0;

}

Функция для определения адреса функции kmalloc:

ulong   get_kma(ulong pgoff)

{

    uint         i;

    unsigned char buf[0x10000], *p, *p1;

    int                 kmem;

    ulong        ret;

Функция принимает один параметр – величину нижней границы адресного пространства ядра. Это значение составляет 0xc0000000.

Если в ядре включена поддержка модулей, то воспользуемся этим:

 ret = get_sym("kmalloc");

    if (ret) {

    printf("\nZer gut!\n");

    return ret;

    }

Если нет, будем искать адрес по опкоду.

kmem = open("/dev/kmem", O_RDONLY);

    if (kmem < 0) return 0;

Для поиска нам придется просканировать все адресное пространство ядра. Для этого организуем цикл:

for (i = pgoff+0x100000; i < (pgoff + 0x1000000); i += 0x10000)

        {

Считываем в буфер buf содержимое адресного пространства ядра:

if (!rkm(kmem, i, buf, sizeof(buf))) return 0;

В этом буфере ищем опкод команды push $0x1f0, который, как нами установлено, равен \x68\xf0\x01\x00. Для поиска используем функцию memmem:

p1=(char *)memmem(buf,sizeof(buf),"\x68\xf0\x01\x00",4);

 if(p1) {

Если последовательность \x68\xf0\x01\x00 найдена, ищем опкод инструкции call (\xe8). Сразу за ним будет находиться смещение к функции kmalloc относительно текущего адреса:

p=(char *)memmem(p1+4,sizeof(buf),"\xe8",1)+1;

           if (p) {

В этом месте указатель p в буфере buf будет позиционирован на смещении к функции kmalloc. Закрываем файл устройства и возвращаем адрес kmalloc:

close(kmem);

               return *(unsigned long *)p+i+(p-buf)+4;

           }

        }

    }

Если ничего найти не удалось, возвращаем нулевое значение:

close(kmem);

return 0;

}

Функция get_sym используется, если в ядре включена поддержка модулей. Данная функция принимает строку, содержащую имя функции ядра, и возвращает ее адрес:

#define MAX_SYMS 4096

ulong   get_sym(char *n) {

        struct  kernel_sym tab[MAX_SYMS];

        int     numsyms;

        int     i;

        numsyms = get_kernel_syms(NULL);

        if (numsyms > MAX_SYMS || numsyms < 0) return 0;

        get_kernel_syms(tab);

        for (i = 0; i < numsyms; i++) {

                if (!strncmp(n, tab[i].name, strlen(n)))

                        return tab[i].value;

        }

        return 0;

}

Итак, все необходимые функции определены. Теперь приступим непосредственно к перехвату системного вызова sys_mkdir. Определим адреса таблицы системных вызовов (sct), функции kmalloc (kma) и нижней границы адресного пространства ядра (page_offset):

sct = get_sct();

page_offset = sct & 0xF0000000;

kma = get_kma(page_offset);

Отобразим полученные данные:

printf("OK\n"

                "page_offset\t\t:\t0x%08x\n"

                "sys_call_table[]\t:\t0x%08x\n"

                "kmalloc()\t\t:\t0x%08x\n",

                page_offset,

                sct,

                kma);

        kmem = open(KMEM_FILE, O_RDWR, 0);

        if (kmem < 0) return 0;

Для размещения функции new_mkdir в адресном пространстве ядра выделим блок памяти. Для этого воспользуемся вышеприведенным алгоритмом вызова функции ядра из пространства пользователя.

Получим адрес системного вызова sys_mkdir:

 if (!rkml(kmem, sct+(_SYS_MKDIR_*4), &sys_mkdir_addr)) {

  printf("Cannot get addr of %d syscall\n", _SYS_MKDIR_);

                return 1;

        }

Сохраним первые N байт этого вызова в буфере tmp, где N=get_kmalloc_size (get_kmalloc_size – это размер функции get_kmalloc и его предстоит определить):

if (!rkm(kmem, sys_mkdir_addr, tmp, get_kmalloc_size)) {

 printf("Cannot save old %d syscall!\n", _SYS_MKDIR_);

                return 1;

        }

Перезаписываем N сохраненных байт системного вызова sys_mkdir функцией get_kmalloc:

if (!wkm(kmem, sys_mkdir_addr,(void *)get_kmalloc_addr,  ї get_kmalloc_size)) {

printf("Can't overwrite our syscall %d!\n",_SYS_MKDIR_);

                return 1;

        }

Адрес функции get_kmalloc (get_kmalloc_addr) также предстоит определить.

Заполняем поля структуры struct kma_struc:

kmalloc.kmalloc = (void *) kma; – адрес функции kmalloc

kmalloc.size = new_mkdir_size; – размер запращевоемой

памяти (размер функции-перехватчика new_mkdir)

kmalloc.flags = 0x1f0; – спецификатор GFP

А теперь обращаемся к системному вызову sys_mkdir, запустив тем самым на выполнение функцию get_kmalloc:

mkdir((char *)&kmalloc,0);

В результате в пространстве ядра будет выделен блок памяти, указатель на который будет записан в поле mem структуры struct kma_struc. В этом блоке памяти мы разместим функцию new_mkdir, которая будет обслуживать все обращения к системному вызову sys_mkdir.

Восстанавливаем системный вызов sys_mkdir:

 if (!wkm(kmem, sys_mkdir_addr, tmp, get_kmalloc_size)) {

      printf("Can't restore syscall %d !\n",_SYS_MKDIR_);

                return 1;

        }

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

if (kmalloc.mem < page_offset) {

         printf("Allocated memory is too low (%08x < %08x)\n",

                        kmalloc.mem, page_offset);

                return 1;

        }

Отображаем результаты:

printf(

                "sys_mkdir_addr\t\t:\t0x%08x\n"

                "get_kmalloc_size\t:\t0x%08x (%d bytes)\n\n"

                "our kmem region\t\t:\t0x%08x\n"

                "size of our kmem\t:\t0x%08x (%d bytes)\n\n",

                sys_mkdir_addr,

                get_kmalloc_size, get_kmalloc_size,

                kmalloc.mem,

                kmalloc.size, kmalloc.size);

Размещаем в пространстве ядра функцию new_mkdir:

if(!wkm(kmem, kmalloc.mem, (void *)new_mkdir_addr, ї new_mkdir_size)) {

           printf("Unable to locate new system call !\n");

           return 1;

    }

и в таблице системных вызовов заменяем адрес функции sys_mkdir адресом new_mkdir:

if(!wkml(kmem, sct+(_SYS_MKDIR_*4), kmalloc.mem)) {

           printf("Eh ...");

           return 1;

    }

    return 1;

}

Сохраним вышеприведенный код в файле new_mkdir.c и получим исполняемый модуль командой:

gcc -o new_mkdir new_mkdir.c

Но запускать на выполнение полученный модуль пока рано. Нам еще необходимо определить адреса и размеры функций get_kmalloc и new_mkdir. Для этого воспользуемся утилитой objdump. Введем команду:

objdump -x ./new_mkdir > dump

Вывод перенаправим в файл dump. Откроем этот файл и найдем в нем следующие строки:

08048630 l      F .text      00000038      get_kmalloc.39

08048668 l      F .text      00000011      new_mkdir.43

Итак, адрес функции get_kmalloc – 0x08048630, размер – 56 байт (0x38), адрес функции new_mkdir – 0x08048668, размер – 17 байт (0x11).

Открываем файл new_mkdir.c и в разделе переменных заполняем полученными значениями соответствующие поля:

ulong get_kmalloc_size=56; размер функции get_kmalloc

ulong get_kmalloc_addr=0x08048630; – адрес функции get_kmalloc

ulong new_mkdir_size=17; – размер функции new_mkdir

ulong new_mkdir_addr=0x08048668; – адрес функции new_mkdir

После этого перекомпилируем модуль. Запустив его на выполнение, мы осуществим перехват системного вызова sys_mkdir. Все обращения к вызову sys_mkdir будут обслуживаться функцией new_mkdir. Чтобы убедиться, что вызов перехвачен, введем команду mkdir <имя каталога>. При этом ничего не произойдет, так как функция-перехватчик new_mkdir просто вернет нулевое значение.

Работоспособность вышеприведенного кода была проверена для ядер версий 2.4.17 и 2.4.20. При подготовке статьи были использованы материалы сайта www.phrack.org.


Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-45
E-mail: sa@samag.ru