用ptrace()取消系统调用

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

这里是testing程序del.c的源代码。 用gcc -o del del.c编译。

 #include <stdio.h> #include <stdlib.h> int main() { remove("/root/abc.out"); return 0; } 

这里是安全pipe理器源代码test.cgcc -o test test.c编译。

 #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; } 

创buildabc.out文件,然后运行testing程序:

 cd /root touch abc.out ./test 

文件/root/abc.out应该仍然存在。

我如何实现这个要求?

Solutions Collecting From Web of "用ptrace()取消系统调用"

那么看起来有时PTRACE_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 coreel */ fprintf(stdout, "GOT IT\n"); //ptrace(PTRACE_KILL, child, NULL, NULL); kill(child, SIGKILL); } } return 0; } 

更新 :问题是,你正在使用10跟踪系统调用,而不是11 (因为你正在执行execve命令),这段代码将与您的rm命令一起工作:

 if (orig_eax == 11) { /* Both PTRACE_KILL and kill() works on my 3.4.4 coreel */ fprintf(stdout, "INSIDE THE TRAP, FILE WILL NOT BE REMOVED\n"); ptrace(PTRACE_KILL, child, NULL, NULL); //kill(child, SIGKILL); } 

编辑 :我试试这个代码,所有的工作正常(文件abc.out仍然存在后执行CALL_REMOVE

 /* * 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# 

有很多低级/巧妙(容易出错)的方法可以解决这个问题,但现代Linux内核(3.x,你的发行版可能需要一个backported补丁)支持一种叫做seccomp的东西,它允许你限制系统调用一个过程可以做到。 沙箱使用此功能,包括Chromium。

你可以在这里看到有关这个StackOverflow的讨论,或者只是Google的文档和示例实现。 那里有相当多的信息。