使用ptrace()取消系统调用

使用ptrace()取消系统调用,c,linux,ptrace,C,Linux,Ptrace,出于某种安全目的,我使用ptrace获取系统调用号,如果这是一个危险的调用(如10表示取消链接),我想取消这个系统调用 下面是测试程序的源代码del.c。使用gcc-odel.c编译 #include <stdio.h> #include <stdlib.h> int main() { remove("/root/abc.out"); return 0; } #include <signal.h> #include <syscall.h

出于某种安全目的,我使用ptrace获取系统调用号,如果这是一个危险的调用(如10表示取消链接),我想取消这个系统调用

下面是测试程序的源代码
del.c
。使用
gcc-odel.c
编译

#include <stdio.h>
#include <stdlib.h>
int main()
{
    remove("/root/abc.out");
    return 0;
}
#include <signal.h>
#include <syscall.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <sys/user.h>
#include <sys/reg.h>
#include <sys/syscall.h>

int main()
{
    int i;
    pid_t child;
    int status;
    long orig_eax;
    child = fork();
    if(child == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("/root/del", "del",  NULL);
    }
    else {
        i = 0;
        while(1){
            wait(&status);
            if (WIFEXITED(status) || WIFSIGNALED(status) )break;

            orig_eax = ptrace(PTRACE_PEEKUSER,
                          child, 4 * ORIG_EAX,
                          NULL);
            if (orig_eax == 10){
                fprintf(stderr, "Got it\n");
                kill(child, SIGKILL);
            }
            printf("%d time,"
               "system call %ld\n", i++, orig_eax);
            ptrace(PTRACE_SYSCALL, child, NULL, NULL);
        }
    }
    return 0;
}
创建
abc.out
文件,然后运行测试程序:

cd/root
触碰
/测试
文件
/root/abc.out
应该仍然存在


如何实现这一要求?

好吧,似乎有时候
PTRACE\u KILL
工作不太好,您可以使用
KILL

if (orig_eax == 10)
{
    kill(pid, SIGKILL);
}
编辑:我用这个程序在我的机器(Ubuntu内核3.4)上测试,一切正常:

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

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

    child = fork();
    if(child == 0) 
    {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("/bin/ls", "ls", NULL);
    }
    else 
    {
        /* Both wait and waitpid works */
        //wait(NULL);
        waitpid(child, &status, 0);
        orig_eax = ptrace(PTRACE_PEEKUSER, child, 4 * ORIG_EAX, NULL);
        /* Tracking execve syscall */
        if (orig_eax == 11)
        {
            /* Both PTRACE_KILL and kill() works on my 3.4.4 Kernel */
            fprintf(stdout, "GOT IT\n");
            //ptrace(PTRACE_KILL, child, NULL, NULL);
            kill(child, SIGKILL);
        }
    }

    return 0;
}
编辑:我尝试了这段代码,所有的wroks都很好(执行
CALL\u REMOVE
后,文件
abc.out
仍然存在)


有很多低级/聪明(并且容易出错)的方法可以解决这个问题,但是现代Linux内核(3.x,您的发行版可能需要一个后端口补丁)支持一种叫做seccomp的东西,它允许您限制进程可以进行的系统调用。沙盒使用此功能,包括Chromium

您可以在StackOverflow上看到关于这一点的讨论,也可以在Google上看到文档和示例实现。这里有很多信息。

释义

一旦系统调用启动,就无法取消它。但是,您可以修改系统调用的参数,或者在系统调用返回后修改其返回值。因此,您可以这样做:

1) 等待跟踪的进程调用系统调用

2) 用无效的内容替换系统呼叫号码。为此,读取跟踪进程的寄存器,修改它们,然后将它们写回进程

3) 继续这一进程;系统调用将被执行,内核将返回一个错误,因为进程调用了一个未实现的系统调用


4) 当ptrace跟踪系统调用的返回时,您可以选择用其他代码替换内核的错误代码(例如,将内核的“未实现的系统调用”替换为“无权限错误”)。

您是否在强制分叉进程等待,以查看您的安全管理器是否将杀死它?据我所知,
waitpid()
将使当前进程等待分叉进程终止。因此,您当然无法停止系统调用。当您能够获得系统调用代码时,分叉进程已经执行了它。@aroth,我确信
waitpid()
有效,我的问题是如何取消系统调用?我尝试了,但系统调用也成功了。@laifjei:真奇怪!如果您使用wait(NULL)而不是waitpid(),那又如何?@laifjei:在我的Ubuntu(3.4内核)上,两对(wait,waitpid)和(PTRACE_KILL,KILL())都可以正常工作。您使用的是什么系统内核?
Linux ubuntu 2.6.38-8-generic\42 ubuntu SMP周一4月11日03:31:50 UTC 2011 i686 i686 i386 GNU/Linux
我有一个解决方案:修改系统调用的参数,使系统调用失败。是那份工作吗
if (orig_eax == 11)
{
    /* Both PTRACE_KILL and kill() works on my 3.4.4 Kernel */
    fprintf(stdout, "INSIDE THE TRAP, FILE WILL NOT BE REMOVED\n");
    ptrace(PTRACE_KILL, child, NULL, NULL);
    //kill(child, SIGKILL);
}
/*
 * REMOVE.c
 * gcc -Wall REMOVE.c -o REMOVE
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char **argv)
{
        /* Both calls work */
        //remove("/root/abc.out");
        unlink("/root/abc.out");

        return 0;
}

/*
 * CALL_REMOVE.c
 * gcc -Wall CALL_REMOVE.c -o CALL_REMOVE
 */

#include <signal.h>
#include <syscall.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <sys/user.h>
#include <sys/reg.h>
#include <sys/syscall.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
        int i;
        pid_t child;
        int status;
        long orig_eax;
        int kill_ret = 0;

        child = fork();

        if(child == 0)
        {
                ptrace(PTRACE_TRACEME, 0, NULL, NULL);
                execl("/root/REMOVE", "REMOVE",  NULL);
        }
        else
        {
                i = 0;
                while(1)
                {
                        wait(&status);
                        if (WIFEXITED(status) || WIFSIGNALED(status) )
                                break;

                        orig_eax = ptrace(PTRACE_PEEKUSER, child, 4 * ORIG_EAX, NULL);
                        if (orig_eax == 10)
                        {
                                fprintf(stderr, "Got it\n");
                                kill_ret = kill(child, SIGKILL);
                                if (kill_ret == -1)
                                {
                                    fprintf(stderr, "Failed to kill ---> %s\n", strerror(errno));
                                }
                        }
                        printf("%d time, system call %ld\n", i++, orig_eax);
                        ptrace(PTRACE_SYSCALL, child, NULL, NULL);
                }
        }

        return 0;
}
root@UnixServer:/root# ll
total 28K
-rw-r--r-- 1 root root    6 2012-08-18 19:37 abc.out
-rw-r--r-- 1 root root 1023 2012-08-18 19:39 CALL_REMOVE.c
-rw-r--r-- 1 root root  213 2012-08-18 19:39 REMOVE.c
-rwxr-xr-x 1 root root 7,3K 2012-08-18 19:39 CALL_REMOVE
-rwxr-xr-x 1 root root 7,0K 2012-08-18 19:39 REMOVE
root@UnixServer:/root# ./CALL_REMOVE 
0 time, system call 11
1 time, system call 45
2 time, system call 45
3 time, system call 33
4 time, system call 33
5 time, system call 192
6 time, system call 192
7 time, system call 33
8 time, system call 33
9 time, system call 5
10 time, system call 5
11 time, system call 197
12 time, system call 197
13 time, system call 192
14 time, system call 192
15 time, system call 6
16 time, system call 6
17 time, system call 33
18 time, system call 33
19 time, system call 5
20 time, system call 5
21 time, system call 3
22 time, system call 3
23 time, system call 197
24 time, system call 197
25 time, system call 192
26 time, system call 192
27 time, system call 192
28 time, system call 192
29 time, system call 192
30 time, system call 192
31 time, system call 6
32 time, system call 6
33 time, system call 192
34 time, system call 192
35 time, system call 243
36 time, system call 243
37 time, system call 125
38 time, system call 125
39 time, system call 125
40 time, system call 125
41 time, system call 125
42 time, system call 125
43 time, system call 91
44 time, system call 91
Got it
45 time, system call 10
root@UnixServer:/root# ll
total 28K
-rw-r--r-- 1 root root    6 2012-08-18 19:37 abc.out
-rw-r--r-- 1 root root 1023 2012-08-18 19:39 CALL_REMOVE.c
-rw-r--r-- 1 root root  213 2012-08-18 19:39 REMOVE.c
-rwxr-xr-x 1 root root 7,3K 2012-08-18 19:39 CALL_REMOVE
-rwxr-xr-x 1 root root 7,0K 2012-08-18 19:39 REMOVE
root@UnixServer:/root#