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

Перехват системных вызовов в linux под x86-64

В интернете опубликовано множество статей по перехвату системных вызовов под x32. В рамках решения одной задачи появилась необходимость в перехвате системных вызовов под архитектурой x86-64 при помощи загружаемого модуля ядра. Приступим:

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

Поиск адреса таблицы системных вызовов

Первый вариант: можно найти через таблицу дескрипторов прерываний (IDT), IDT — служит для связи обработчика прерывания с номером прерывания. В защищённом режиме адрес в физической памяти и размер таблицы прерываний определяется 80-битным регистром IDTR.В защищённом режиме элементом IDT является шлюз прерывания длиной 10 байт, содержащий сегментный (логический) адрес обработчика прерывания, права доступа и др. Нам такой метод не интересен, т.к. мы получим адрес обработчика, который сделан для совместимости с х32

Второй вариант, более интересен.

Для начала не большой экскурс: MSR – machine state register это набор регистров процессоров Интел, используемых в семействе x86 и x86-64 процессоров. Эти регистры предоставляют возможность контролировать и получать информацию о состоянии процессора. Все MSR регистры доступны только для системных функций и не доступны из пользовательских программ. Нас в частности интересует следующий регистр:MSR_LSTAR — 0xc0000082 (long mode SYSCALL target)
(полный список можно посмотреть в /usr/include/asm/msr-index.h).
В этом регистре хранится адрес обработчика прерываний для x86-64.
Получить адрес можно следующим образом:
int i, lo, hi;
asm volatile(«rdmsr» : «=a» (lo), «=d» (hi) : «c» (MSR_LSTAR));
system_call = (void*)(((long)hi <<32) | lo); Далее найдем адрес самой таблицы. Перейдем на только что полученный адрес и найдем в памяти последовательность \xff\x14\xc5(эти магические числа берутся, если посмотреть на код ядра, в частности, на код функции system_call, в которой происходит вызов обработчика из искомой). Считав следующие за ней 4 байта, мы получим адрес таблицы системных вызовов syscall_table. Зная ее адрес, мы можем получить содержимое этой таблицы (адреса всех системных функций) и изменить адрес любого системного вызова, перехватив его.
код для нахождения адреса таблицы системных вызовов:
unsigned char *ptr;
for (ptr=system_call, i=0; i if (ptr[0] == 0xff && ptr[1] == 0x14 && ptr[2] == 0xc5)
return (void*)(0xffffffff00000000 | *((unsigned int*)(ptr+3)));
ptr++;
>

Читайте также:  Manjaro linux установка через терминал
Подмена на адреса новых системных вызовов
  • Отключаем защиту памяти
  • Переписываем адрес на адрес нашего обработчика
  • Включаем защиту памяти

Этих знаний достаточно для подмены системных вызовов в Linux x86-64. Надеюсь кому-нибудь это будет полезным.
Спасибо за внимание.

Источник

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

ptrace (от process trace) — системный вызов в некоторых unix-подобных системах (в том числе в Linux, FreeBSD, Max OS X), который позволяет трассировать или отлаживать выбранный процесс. Можно сказать, что ptrace дает полный контроль над процессом: можно изменять ход выполнения программы, смотреть и изменять значения в памяти или состояния регистров. Стоит оговориться, что никаких дополнительных прав при этом мы не получаем — возможные действия ограничены правами запущенного процесса. К тому же, при трассировке программы с setuid битом, этот самый бит не работает — привилегии не повышаются.

В статье будет показано, как перехватывать системные вызовы на примере ОС Linux.

1. Немного о ptrace

  • request — это действие, которое необходимо осуществить, например PTRACE_CONT, PTRACE_PEEKTEXT
  • pid — индентификатор трассируемого процесса
  • addr и data зависят от request‘а
  • PTRACE_SINGLESTEP — пошаговое выполнение программы, управление будет передаваться после выполнения каждой инструкции; такая трассировка достаточна медленна
  • PTRACE_SYSCALL — продолжить выполнение программы до входа или выхода из системного вызова
  • PTRACE_CONT — просто продолжить выполнение программы

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

Напишем программу для вывода списка системных вызовов, используемых программой (простенький аналог утилиты strace).

Итак, для начала необходимо сделать fork — родительский процесс будет отлаживать дочерний:

int main( int argc, char *argv[]) pid_t pid = fork();
if (pid)
parent(pid);
else
child();
return 0;
>

В дочернем процессе все просто — начинаем трассировку с PTRACE_TRACEME и запускаем нужную программу:

void child() ptrace(PTRACE_TRACEME, 0, 0, 0);
execl( «/bin/echo» , «/bin/echo» , «Hello, world!» , NULL);
perror( «execl» );
>

При выполнении execl трассируемый процесс остановится, передав свое новое состояние родительскому. Поэтому родительский процесс сначала должен подождать запуска программы с помощью waitpid (можно просто wait, так как дочерний процесс всего один):

Чтобы как-то различать системные вызовы и другие остановки (например SIGTRAP), предусмотрен специальный параметр PTRACE_O_TRACESYSGOOD — при остановке на системном вызове родительский процесс получит в статусе SIGTRAP | 0x80:

Читайте также:  Pantum 3010d драйвер linux

Теперь можно в цикле выполнять PTRACE_SYSCALL до выхода из программы, и смотреть значение регистра eax для определения номера системного вызова. Для этого используем PTRACE_GETREGS. Следует отметить, что регистр eax в момент остановки заменен, и поэтому необходимо использовать сохраненный state.orig_eax:

struct user_regs_struct state;

ptrace(PTRACE_SYSCALL, pid, 0, 0);
waitpid(pid, &status, 0);

// skip after syscall
ptrace(PTRACE_SYSCALL, pid, 0, 0);
waitpid(pid, &status, 0);
>

Запустив программу, увидим нечто подобное:

.
SYSCALL 6 at b783a430
SYSCALL 197 at b783a430
SYSCALL 192 at b783a430
SYSCALL 4 at b783a430
Hello, world!
SYSCALL 6 at b783a430
SYSCALL 91 at b783a430
SYSCALL 6 at b783a430
SYSCALL 252 at b783a430

Как видно, после системного вызова №4 (а это sys_write) выводится наш текст.

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

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

  • ebx: fd — файловый дескриптор (номер)
  • ecx: buf — указатель на текст для вывода
  • edx: n — количество байт

// sys_write
if (state.orig_eax == 4) char * text = ( char *)state.ecx;
ptrace(PTRACE_POKETEXT, pid, ( void *)(text+7), 0x72626168); //habr
ptrace(PTRACE_POKETEXT, pid, ( void *)(text+11), 0x00000a21); //!\n
>

.
SYSCALL 6 at 00556416
SYSCALL 197 at 00556416
SYSCALL 192 at 00556416
SYSCALL 4 at 00556416
Hello, habr!
SYSCALL 6 at 00556416
SYSCALL 91 at 00556416
SYSCALL 6 at 00556416
SYSCALL 252 at 00556416

Таким образом, мы перехватили системный вызов sys_write в программе /bin/echo для вывода своего текста. Это всего лишь простой пример использования ptrace. С его помощью также можно легко делать дампы памяти (это, кстати, очень помогает при решении линуксовых крэкмисов), устанавливать breakpoint’ы (с помощью PTRACE_SINGLESTEP или заменой интсрукции на 0xCC), анализировать регистры/переменные и многое другое. ptrace очень полезен, например, когда до проблемного участка кода быстро не добраться — если в отладчике приходится многократно прыгать, подменять данные, а потом программа умирает и приходится все делать заново; если же написать программу для отладки ptrace’ом — все эти действия необходимо описать только один раз, и они будут выполняться автоматически. Конечно, в некоторых отладчиках можно писать скрипты — но по возможностям они навернякак уступают.

UPD: забыл выложить полный исходник

Источник

Перехват системных вызовов в linux под x86-64

В интернете опубликовано множество статей по перехвату системных вызовов под x32. В рамках решения одной задачи появилась необходимость в перехвате системных вызовов под архитектурой x86-64 при помощи загружаемого модуля ядра. Приступим:

Читайте также:  Linux kernel run qemu

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

Поиск адреса таблицы системных вызовов

Первый вариант: можно найти через таблицу дескрипторов прерываний (IDT), IDT — служит для связи обработчика прерывания с номером прерывания. В защищённом режиме адрес в физической памяти и размер таблицы прерываний определяется 80-битным регистром IDTR.В защищённом режиме элементом IDT является шлюз прерывания длиной 10 байт, содержащий сегментный (логический) адрес обработчика прерывания, права доступа и др. Нам такой метод не интересен, т.к. мы получим адрес обработчика, который сделан для совместимости с х32

Второй вариант, более интересен.

Для начала не большой экскурс: MSR – machine state register это набор регистров процессоров Интел, используемых в семействе x86 и x86-64 процессоров. Эти регистры предоставляют возможность контролировать и получать информацию о состоянии процессора. Все MSR регистры доступны только для системных функций и не доступны из пользовательских программ. Нас в частности интересует следующий регистр:MSR_LSTAR — 0xc0000082 (long mode SYSCALL target)
(полный список можно посмотреть в /usr/include/asm/msr-index.h).
В этом регистре хранится адрес обработчика прерываний для x86-64.
Получить адрес можно следующим образом:
int i, lo, hi;
asm volatile(«rdmsr» : «=a» (lo), «=d» (hi) : «c» (MSR_LSTAR));
system_call = (void*)(((long)hi <<32) | lo); Далее найдем адрес самой таблицы. Перейдем на только что полученный адрес и найдем в памяти последовательность \xff\x14\xc5(эти магические числа берутся, если посмотреть на код ядра, в частности, на код функции system_call, в которой происходит вызов обработчика из искомой). Считав следующие за ней 4 байта, мы получим адрес таблицы системных вызовов syscall_table. Зная ее адрес, мы можем получить содержимое этой таблицы (адреса всех системных функций) и изменить адрес любого системного вызова, перехватив его.
код для нахождения адреса таблицы системных вызовов:
unsigned char *ptr;
for (ptr=system_call, i=0; i if (ptr[0] == 0xff && ptr[1] == 0x14 && ptr[2] == 0xc5)
return (void*)(0xffffffff00000000 | *((unsigned int*)(ptr+3)));
ptr++;
>

Подмена на адреса новых системных вызовов
  • Отключаем защиту памяти
  • Переписываем адрес на адрес нашего обработчика
  • Включаем защиту памяти

Этих знаний достаточно для подмены системных вызовов в Linux x86-64. Надеюсь кому-нибудь это будет полезным.
Спасибо за внимание.

Источник

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