GCC как заблокировать системные вызовы внутри программы? - PullRequest
5 голосов
/ 01 июня 2010

Кто-нибудь подскажет, как заблокировать некоторые конкретные системные вызовы внутри программы, пожалуйста?Я строю систему, которая берет кусок исходного кода на C, компилирует его с помощью gcc и запускает его.По соображениям безопасности мне нужно запретить скомпилированной программе вызывать некоторые системные вызовы.Есть ли способ сделать это, от уровня исходного кода (например, очистка заголовочных файлов gcc, обнаружение вредоносных внешних вызовов, ...) до уровня исполняемого файла?

Отредактировано # 1: Добавить сведения о вредоносных вызовах.

Отредактировано # 2: Моя система работает под управлением GNU / Linux.

Отредактировано # 3:

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

  1. Сканирование исходного кода не решает основную проблему, так как его / ее исходный файл на C всегда можно запутать.
  2. «Переопределение символа C» хорошо работает для библиотек, но для системных вызовов я не достиг того, чего хотел.Эта идея не умерла, однако, выполнение этого определенно заставило бы меня много времени взламывать (gcc и / или ld).
  3. Деэскалация разрешений работает как шарм.Я мог бы использовать fakeroot или «гостевого» пользователя, чтобы сделать это.Этот метод также самый простой в реализации.

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

Ответы [ 6 ]

8 голосов
/ 01 июня 2010

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

Однако, возможно, вы сможете продвинуться вперед с осторожным использованием механизма LD_PRELOAD, если ваша платформа поддерживает его (например, Linux): вы пишете разделяемую библиотеку с теми же именами символов, что и в библиотеке C, которые называются вместо предполагаемые функции libc. (Например, Electric Fence создается как общая библиотека в системах на основе Debian и перехватывает вызовы malloc, free и др.)

Я подозреваю, что вы могли бы использовать этот механизм для перехвата или проверки аргументов вызовов любых функций libc, которые вам не нравятся, и, возможно, чтобы отметить те, которые вы считаете безусловно безопасными. Тогда было бы разумно просканировать скомпилированный исполняемый файл на наличие кода, соответствующего INT 80, чтобы отследить любые попытки сделать необработанные системные вызовы (0xcd 0x80 - хотя и остерегайтесь ложных срабатываний). Однако я только подумал об этом, я мог бы легко что-то упустить или это может оказаться нецелесообразным ...

4 голосов
/ 01 июня 2010

Вы можете запустить скомпилированную программу, разветвив ее из оболочки и используя средство Linux ptrace (2) для перехвата и проверки всех системных вызовов, вызванных программой.

В следующем примере кода показана оболочка, которая выполняет команду / usr / bin / w, печатает каждый системный вызов, вызванный командой, и завершает команду, если она пытается вызвать системный вызов write (2).

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <sys/reg.h>

#define BAD_SYSCALL __NR_write

int main(int argc, char *argv)
{
    pid_t child;
    int status, syscall_nr;

    child = fork();
    if (child == 0) {
        /* In child. */
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("/usr/bin/w", NULL, NULL);
        // not reached
    }

    /* In parent. */
    while (1) {
        wait(&status);

        /* Abort loop if child has exited. */
        if (WIFEXITED(status) || WIFSIGNALED(status))
            break;

        /* Obtain syscall number from the child's process context. */
        syscall_nr = ptrace(PTRACE_PEEKUSER, child, 4 * ORIG_EAX, NULL);
        printf("Child wants to execute system call %d: ", syscall_nr);

        if (syscall_nr != BAD_SYSCALL) {
            /* Allow system call. */
            printf("allowed.\n");
            ptrace(PTRACE_SYSCALL, child, NULL, NULL);
        } else {
            /* Terminate child. */
            printf("not allowed. Terminating child.\n");
            ptrace(PTRACE_KILL, child, NULL, NULL);
        }
    }

    exit(EXIT_SUCCESS);
}

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

Хорошее введение можно найти в этой статье журнала Linux и ее последующих действиях .

3 голосов
/ 01 июня 2010

Вы не можете.

Даже эта программа:

#include <stdio.h>

int main()
{
    printf("Hello, World\n");
    return 0;
}

совершает хотя бы один системный вызов (для отправки строки "Hello, World \ n" на стандартный выход) Системные вызовы - единственный способ взаимодействия программы с внешним миром. Для безопасности используйте модель безопасности операционной системы.

Отредактировано для этого комментария:

Я имел в виду не все системные вызовы, а злонамеренные системные вызовы, например execv () может использоваться для выполнения сценария BASH, который стирает мои данные на диске.

Ваша операционная система уже содержит механизмы, позволяющие предотвратить подобные вещи. Например, чтобы bash-скрипт уничтожил ваши данные, у процесса уже должен быть доступ на запись к этим данным. Это означает, что он должен быть запущен вами или пользователем root. Ваш единственный реальный вариант - не устанавливать ненадежное программное обеспечение.

Кстати, в зависимости от вашей платформы execv не обязательно является системным вызовом. В Linux это оболочка библиотеки C для реального системного вызова (execve).

1 голос
/ 01 июня 2010

В некоторых проектах есть похожая идея, которую вы можете посмотреть на nacl: http://code.google.com/p/nativeclient/

1 голос
/ 01 июня 2010

Что ж, если вы просто хотите заблокировать определенные вызовы, почему бы просто не выполнить grep через исходный код, прежде чем пытаться скомпилировать его? И отклонить программы, которые используют небезопасные системные вызовы.

1 голос
/ 01 июня 2010

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

int main() {
    return 0;
}

совершает более 20 системных вызовов, как сообщается с использованием strace. Вызовы включают open (дважды), что является одним из вызовов, которые вы хотите заблокировать.

...