Что такое хуки linux

Hook в Линукс

Существует ли в линукс, что-нить на подобие виндовых хуков? киньте плиз пару ссылок на эту тему

Re: Hook в Линукс

Заранее извиняюсь, если неправильно вопрос понял.

В ядре Windows нет хуков (официально; бэкдоры не в счет). Хуки есть в стандартных виндовых либах, работающих в юзер-спэйсе — comdlg32.dll или что-то типа того.

Linux — это ядро, и в нем, так же как и в ядре Windows, хуков нет (автозапуск ядром /sbin/init, /sbin/modprobe и т.п. — это наверно не то что тебя интересует).

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

Может быть, уточнишь вопрос?

Re: Re: Hook в Линукс

Под хуками я понимал технику при помощи которой можно перехватывать оконные сообщения и другие события ситемы до того как они попадут окну адресату. класический пример клавиатурный шпион. Другими словами мне нужно получать все сообщения системы (адресованные не только моему приложению) — в винде это реализуется при помощи хуков либо перехвата API функций, а вот линуксе я не знаю, как это можно реализовать.

Re: Re: Re: Hook в Линукс

>Под хуками я понимал технику при помощи которой можно перехватывать оконные сообщения и другие события ситемы до того как они попадут окну адресату. класический пример клавиатурный шпион. Другими словами мне нужно получать все сообщения системы (адресованные не только моему приложению) — в винде это реализуется при помощи хуков либо перехвата API функций, а вот линуксе я не знаю, как это можно реализовать.

Дело в том что в Linux как это уже было сказано GUI не является частью «API» (если такие термины вам ближе)

Перехват клавиатурных сообщений в общем случае можно реализовать на уровне ядра (в Inet даже можно найти примеры на эту тему c примерами соответствующих LKM).

Если речь идет о X11

то на уровне Xt это всякие XGrab* и X*Hook*

Re: Re: Re: Hook в Линукс

> мне нужно получать все сообщения системы

Не прокатит. AFAIK, заграбить в иксах можно только то, что не было заграблено кем-то раньше. Оконный манагер всяко-разно перехватывает клаву и/или крысу — хотя бы некоторые кнопки/клики. Он стартует раньше твоей проги, а значит твоя прога обломится при попытке заграбить то же самое — получит BadAccess и вылетит (если ты не перехватишь ошибки через XSetErrorHandler).

Re: Re: Re: Re: Hook в Линукс

Всем спасибо! теперь понятно в какую сторону копать.

Источник

Перехват системных вызовов Linux с помощью LSM

Недавно поступили такие задачи: собрать ядро Linux, написать для него модуль и с его помощью перехватывать системные вызовы. И если первые две я выполнил без проблем, то в процессе выполнения третьей у меня возникло впечатление, что работа с системными вызовами вышла из моды лет 10 назад.

Читайте также:  Эмулятор виндовс приложений для линукс

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

Начальные условия

  • 4 ядра процессора Intel Core i7
  • 4 Гб оперативной памяти + 4 Гб swap
  • Ubuntu 16.10 x64 на виртуальной машине VirtualBox 5.1.10
  • Ядро Linux 4.9.0
  • gcc 6.2.0

Для редактирования конфигурации ядра в псевдографическом режиме нужен ncurses:

Сборка чистого ядра

Я рекомендую собрать чистое ядро прежде, чем начать разработку модулей. На это есть 2 причины:

    Первая сборка ядра — довольно продолжительный процесс. Чаще всего он длится от 20 минут до 3 часов. Если же провести сборку заранее, вы получите большую часть бинарников ядра, которые не будут нуждаться в перекомпиляции. Это позволит полностью сосредоточиться на разработке модуля, не мучаясь в ожидании ответа на вопрос “Запустится ли мой первый Hello World?”

  • Успешно собрав чистое ядро, вы убедитесь, что на этом этапе нет проблем и что можно приступать к следующему. Иногда загрузка со свежесобранным ядром может быть неуспешной, и, если вы собирали его вместе с модулем, сложно будет понять, что именно положило систему.
  • Итак, сборка ядра:

      Скачиваем архив с исходниками:

    где x.x.x — версия ядра.

    Либо можно скачать архив руками с kernel.org

    Извлекаем данные из архива:

    Переходим в только что распакованную папку:

    Генерируем конфигурацию ядра по умолчанию:

    Запускаем непосредственно сборку ядра и модулей:

    Сборка будет длиться от 20 минут до 3х часов.

    Где x — количество ядер процессора + 1. То есть в моём случае x = 5.
    Такое значение рекомендуют установить во всех руководствах, но на самом деле значение можно установить любое. Я решил “увеличить количество ядер вдвое”, то есть запустить сборку c параметром -j 9. Это не ускоряет сборку в 2 раза, но увеличивает конкурентоспособность процессов сборки по отношения ко всем другим процессам в системе.

    Кроме того, в системном мониторе(gnome-system-monitor) всем make-процессам я установил максимальный приоритет. Система после этого буквально зависла, но сборка прошла за 6 минут. Используйте этот метод на свой страх и риск.

    После успешной сборки нужно установить всё то, что мы собрали. Это требует root-прав.

    Установка непосредственно ядра:

    Команды установки должны сгенерировать начальный RAM-диск и обновить grub. Если вдруг начальный RAM-диск не сгенерировался — система с новым ядром не запустится.

    Проверить это можно по наличию файла «/boot/initrd.img-x.x.x» (x.x.x — версия ядра)
    Если файла не обнаружилось — генерируем его руками:

    Обновляем загрузчик grub:

    Готово! После перезапуска система запустится с новым ядром. Проверить текущую версию ядра:

    Если вдруг что-то пошло не так, и система не загружается с новым ядром, перезагрузите компьютер, в меню grub перейдите в advanced options и выберите другую версию ядра(ту, под которой вы загружались раньше, обычно у версий по умолчанию добавляют суффикс -general)

    Создание модуля

    Создание модуля ядра во многом напоминает написание обычной пользовательской программы на C, кроме некоторых отличий связанных с ядром:

      Ядро не имеет доступа к стандартным библиотекам языка C. Причина этого – скорость выполнения и объем кода. Часть функций, однако, можно найти в исходниках ядра. Например, обычные функции работы со строками описаны в файле lib/string.c

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

    Читайте также:  Restoring grub after windows install

    В ядре нельзя использовать вычисления с плавающей точкой. Активизация режима вычислений с плавающей точкой требует сохранения и проставления регистров устройства поддержки вычислений с плавающей точкой, помимо других рутинных операций.

  • Фиксированный стек(причём довольно небольшой). Именно поэтому не рекомендуется использовать рекурсию в ядре.
  • Hello world!

    Давайте на конкретном примере рассмотрим “Hello world” в виде модуля ядра. Создадим файл hello.c в любой удобной для вас папке:

    Обычная пользовательская программа начинается с вызова функции main() и работает, пока не возвратит системе какое-то значение.

    Модуль же по сути является частью кода самого ядра, в котором содержатся функции-обработчики некоторых событий. Простейший пример такого события — загрузка или выгрузка самого модуля.

    Функции, которые обрабатывают эти события — соответственно

    static int __init myinit(void)
    static void __exit myexit(void)

    Они отмечены макросами __init, __exit и зарегистрированы с помощью module_init и module_exit как обработчики событий. Название этих функций может быть любым, но не должно конфликтовать с другими функциями в ядре.

    Поскольку ядро не использует стандартную библиотеку C, мы не можем использовать stdio.h. Вместо этого мы подключаем файл kernel.h, в котором реализована функция printk. Эта функция аналогична printf с тем лишь отличием, что выводит сообщения не в окно терминала, а в системный лог (/var/log/syslog).

    В этот лог пишется очень много сообщений со всей системы, поэтому наши нужно пометить каким-то оригинальным «>тегом , чтобы потом с помощью утилиты grep можно было выделить только сообщения нашего модуля.

    Ещё одна непонятная строчка — MODULE_LICENSE(«GPL»);

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

    Сборка

    Для того чтобы собрать этот модуль в той же папке, где лежит исходный код модуля, создадим Makefile:

    После создания Makefile переходим непосредственно к сборке:

    Через пару секунд в нашей папке появится файл hello.ko — готовый скомпилированный модуль.

    Загрузка и выгрузка

    Существует 2 способа загрузки модуля в ядро:

      Сборка модуля вместе с ядром. В таком случае загрузка модуля происходит как часть запуска системы, а сам модуль становится частью кода ядра.

  • Динамическая загрузка в уже запущенной системе. Вышеописанный способ создания модуля предполагает именно такой способ загрузки. В этом случае загрузка модуля больше похожа на запуск обычной пользовательской программы.
  • Загрузить модуль:

    Команда insmod загружает модуль в пространство ядра, тем самым вызывая функцию инициализации.

    После этого модуль попадает в список загруженных. Проверить это можно командой lsmod:

    В функции инициализации мы добавили вызов printk, который выводит в системный лог наше сообщение.

    Для просмотра системного лога существует утилита dmesg:

    Вышеуказанная команда выведет

    После того, как мы загрузили модуль, он так и останется висеть в ядре до тех пор, пока его не выгрузят. Чтобы сделать это:

    Эта команда вызовет обработчик события __exit, но поскольку у нас там пустая функция, кроме выгрузки модуля из ядра ничего не произойдёт.

    Для того, чтобы каждый раз не вводить 2 команды для загрузки и выгрузки модуля во время отладки, в функции инициализации возвращают значение -1. Такой модуль при попытке загрузки выводит в терминал ошибку, после чего прекращает работу, но при этом функция инициализации отрабатывает полностью и корректно, превращаясь, по сути, в аналог функции main() пользовательских программ.

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

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

    Небезопасный способ

    Когда-то давно, ещё до ядра версии 2.6, для того, чтобы перехватить системный вызов, писали функцию-хук, которая её заменяла: выполняла другой код + вызывала непосредственно сам syscall(чтобы не нарушить работоспособность системы).

    Читайте также:  Настройка удаленного рабочего стола linux mint

    Так как каждый системный вызов подобно функции имеет свой адрес, а в Linux есть специальная таблица где эти адреса хранятся, задача сводилась к тому, чтобы в этой самой таблице заменить адрес системного вызова на адрес нашей функции.

    Позже, разработчики Linux пытались устранить возможность такого способа, но до сих пор существуют хаки, которые позволяют реализовать этот метод.

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

    Самое ценное для нас в данном фреймворке то, что он реализован через набор заранее предустановленных в ядро хуков (по сути, тот способ, который я описывал выше, но безопасный, потому что ядро заранее рассчитано на наличие таких хуков).

    LSM позволяет вставлять в код своих хуков вызов пользовательских, что позволяет безопасно работать с системными вызовами без изменения таблицы символов.

    Всё предельно просто. Рассмотрим пример создания модуля безопасности foobar, который перехватывает системный вызов mk_dir.

    Написание кода

      Находим в исходниках ядра папку security, создаём в ней папку для нашего модуля, а в ней — его исходный код foobar.c:

    Файл lsm_hooks.h содержит заголовки тех самых предустановленных хуков, LSM_HOOK_INIT регистрирует соответствие foobar_inode_mkdir() хуку inode_mkdir(), а security_add_hooks() добавляет нашу функцию в общий список пользовательских хуков LSM.

    Таким образом, при каждом вызове mkdir будет вызываться наша функция foobar_inode_mkdir().

    Добавляем заголовок нашей функции в файл “/include/linux/lsm_hooks.h”:

    Все вызовы происходят в исходном файле security.c (далее), этим шагом мы оповещаем его о существовании нашей функции.

    В файле “/security/security.c” находим функцию “int __init security_init(void)” и добавляем в её тело следующий вызов:

    Всё, зависимости в коде настроены корректно. Осталось только оповестить конфигурационные файлы ядра о том, что мы хотим собрать его вместе с нашим модулем.

    Конфигурация сборки

      В папке с нашим модулем(/security/foobar/) создадим файл Kconfig:

    Это создаст пункт меню с нашим модулем.

    Откроем файл /security/Kconfig и добавим следующий текст сразу за строчкой “menu «Security options»»:

    Это добавит наш пункт меню в глобальное меню настроек ядра.

    Создадим Makefile в папке с нашим модулем:

    Откроем Makefile всего раздела безопасности(/security/Makefile) и добавим в него следующие строчки(по аналогии с такими же строчками для других модулей):

    Запустим конфигурирование в псевдографическом режиме:

    Если перейти в подменю “Security options”, первым пунктом мы увидим наш модуль, отмеченный символом “y” (мы установили это значение по умолчанию, когда создавали файл Kconfig), что означает, что мы интегрируем наш модуль непосредственно в код ядра.

    Сборка

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

    make не требует параметра -j, поскольку пересоберёт ядро с нашим модулем за несколько секунд.

    Установка заголовков и модулей не требуется, это было произведено ранее.

    Осталось перезагрузить систему, после чего в ядре будет висеть наш модуль с перехватом mkdir. Как и говорил ранее, проверяем так:

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

    Надеюсь, кому-то это руководство будет полезным(если бы кто-то написал его вместо меня до того, как я начал копаться в ядре — он сэкономил бы мне 2-3 недели жизни).

    Любая критика приветствуется.
    Спасибо за внимание.

    Источник

    Оцените статью