GCC如何在程序中阻止系统调用?

时间:2010-06-01 09:07:32

标签: c gcc system-calls

有人告诉我如何阻止程序中的某些特定系统调用吗?我正在构建一个系统,它接受一段C源代码,用gcc编译并运行它。出于安全原因,我需要阻止已编译的程序调用某些系统调用。有没有办法做到这一点,从源代码级别(例如剥离gcc的头文件,检测恶意外部调用,......)到可执行级别?

已编辑#1:添加有关恶意电话的详细信息。

编辑#2:我的系统是GNU / Linux系统。

编辑#3:

我已经在几天内尝试了一些方法,这是我到目前为止得出的结论:

  1. 扫描源代码并不能解决主要问题,因为人们总能很好地保护他/她的C源文件。
  2. “覆盖C符号”适用于库,但对于系统调用,我没有达到我想要的效果。这个想法并没有死,但这样做肯定会让我花很多时间黑客攻击(gcc和/或ld)。
  3. 许可减少就像魅力一样。我可以使用fakeroot或“访客”用户来执行此操作。这种方法也是最容易实现的。
  4. 另一个是native client,我还没有尝试过,但由于项目和我的工作之间的共同点,我肯定会在不久的将来。

6 个答案:

答案 0 :(得分:8)

正如其他人所指出的那样,程序不可能避免进行系统调用,他们会在整个地方使用C库。

但是,如果您的平台支持LD_PRELOAD机制(例如Linux),您可能会小心翼翼地使用LD_PRELOAD机制:您编写的共享库具有与C库中相同的符号名称,这些名称被称为而不是预期的libc函数。 (例如,Electric Fence在基于Debian的系统上构建为共享库,并拦截对mallocfree等的调用。)

我怀疑你可以使用这种机制来捕获或参数检查你不喜欢的任何libc函数的调用,也许还要注意那些你认为无条件安全的函数。然后,扫描已编译的可执行文件以查找与INT 80对应的代码以捕获任何尝试进行原始系统调用(0xcd 0x80 - 尽管要注意误报)可能是合理的。但是我只是想了几分钟,我可能很容易错过一些东西,或者这可能会变得不切实际......

答案 1 :(得分:4)

您可以通过从包装器中分叉来运行已编译的程序,并使用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 Journal Article及其follow-up中找到一个很好的介绍。

答案 2 :(得分:3)

你做不到。

即使是这个程序:

#include <stdio.h>

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

进行至少一次系统调用(将字符串“Hello,World \ n”发送到标准输出)。系统调用是程序与外部世界交互的唯一方式。使用操作系统的安全模型来确保安全。

编辑此评论:

  

我的意思不是所有系统调用,而是恶意系统调用,例如execv()可用于执行擦除磁盘上数据的BASH脚本。

您的操作系统已经包含了阻止此类事件发生的机制。例如,为了使bash脚本消除您的数据,该进程必须已具有对该数据的写访问权。这意味着它必须由您或root启动。您唯一真正的选择是不要安装不值得信任的软件。

顺便说一句,根据您的平台,execv不一定是系统调用。在Linux上,它是真实系统调用的一个C库包装器(execve)。

答案 3 :(得分:1)

为了说明这是不可能的,以下程序:

int main() {
    return 0;
}

使用strace报告超过20次系统调用。调用包括open(两次),这是您似乎想要阻止的一个调用。

答案 4 :(得分:1)

好吧,如果您只想阻止特定的调用,为什么不在编译之前只通过源代码执行grep?并拒绝使用不安全系统调用的程序。

答案 5 :(得分:1)

有些项目有类似的想法,你可以看看nacl:http://code.google.com/p/nativeclient/