我正在建立一个在线评判系统。关键是跟踪系统调用。
我选择了ptrace。由于SIGTRAP将进入一个系统调用,子进程将停止,然后父进程将读取子进程的orig_rax(orig_eax)寄存器以获取系统调用号。
当代码在Opensuse13.1 32bit下运行时没问题,其输出与linux命令strace相同。
但我只是在Opensuse13.1 64bit,Ubuntu12.04 64bit下测试代码,输出错误。
可以在此处下载演示代码:https://gist.github.com/kainwen/41a7bd0198099d766bda
在64位Linux系统下,您保存代码strace_ls.c
,将其编译为gcc strace_ls.c
,然后运行./a.out 2>out
。输出为here
所以我的代码输出很奇怪。
我的代码及其输出如下:
#include <sys/resource.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
void judge_exe()
{
pid_t pid ;
int insyscall = 0 ;
struct user context ;
pid = fork() ;
if (pid == 0) { //child
ptrace(PTRACE_TRACEME,0,NULL,NULL) ;
execl("/usr/bin/ls","ls",NULL) ;
}
else {//parent
int status ;
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
while (1) {
wait(&status) ;
if (WIFEXITED(status)) //normally terminated
break;
else if (WIFSTOPPED(status) && (WSTOPSIG(status)==SIGTRAP)) {
if (!insyscall) {
insyscall = 1;
ptrace(PTRACE_GETREGS,pid,NULL,&context.regs);
fprintf(stderr,"syscall num: %d \n",context.regs.orig_rax);
} else
insyscall = 0;
}
ptrace(PTRACE_SYSCALL,pid,NULL,NULL);
}
return;
}
}
int main(int argc, char *argv[]) {
judge_exe();
}
上面代码的输出是(只是标题行):
syscall num: 59
syscall num: 12
syscall num: 9
syscall num: 21
syscall num: 2
syscall num: 4
linux命令strace ls
的输出标题行是:
execve("/usr/bin/ls", ["ls"], [/* 101 vars */]) = 0
brk(0) = 0x1bab000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7efff4326000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/usr/lib64/mpi/gcc/openmpi/lib64/tls/x86_64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/mpi/gcc/openmpi/lib64/tls/x86_64", 0x7fff00c49b80) = -1 ENOENT (No such file or directory)
系统调用sys_execve的数量是11,而不是59(我的代码的输出)
我的方法是对的。系统调用号在64位和32位下不同。