本文为毕业设计过程中学习相关知识、动手实践记录下来的完整笔记,通过阅读本系列文章,您可以从零基础了解系统调用的底层原理并对系统调用进行拦截。由于本人能力有限,文章中可能会出现部分错误信息,如有错误欢迎指正。另外,本系列所有内容仅作为个人学习研究的笔记,转载请标明出处。感谢您的关注!
文章目录
- ptrace系统调用
- 利用ptrace获取进程的系统调用
ptrace系统调用
linux提供了ptrace系统调用接口,通过这个接口可以实现进程的跟踪功能。以此实现父进程对其子进程进行控制和改变子进程核心镜像,包括读写子进程空间的数据等。
其基本原理是当使用了ptrace跟踪后,所有发送给被跟踪的子进程的信号(除了SIGKILL),都会被转发给父进程,而子进程则会被阻塞,这时子进程的状态就会被系统标注为TASK_TRACED。而父进程收到信号后,就可以对停止下来的子进程进行检查和修改,然后让子进程继续运行。
ptrace函数原型如下
long ptrace(enum __ptrace_request request, pid_t pid, void *addr,
void *data);
参数解析
request的值决定了这个系统调用得行为
PTRACE_TRACEME:子进程向父进程提出被跟踪请求
PTRACE_ATTACH:跟踪pid指定的进程
PTRACE_PEEKDAT,PTRACE_PEEKTEXT,PTRACE_PEEKUSER:读取子进程的内存和用户空间的数据
PTRACE_POKEDATA,PTRACE_POKETEXT,PTRACE_POKEUSER:向子进程的内存和用户空间写数据
PTRACE_SYSCALL:让目标进程在下次进/出系统调用时返回给父进程
pid:指定要跟踪进程的pid
addr:指定要监控的内存地址
data:存放读取或要写入的数据
利用ptrace获取进程的系统调用
先准备一个待跟踪的程序a.c,并编译生成可执行文件gcc a.c -o a
int main(){
FILE *fp = fopen("./a.txt", "w");
fprintf(fp, "hello");
fclose(fp);
return 0;
}
测试程序b.c通过fork系统调用,让子进程执行a程序,在父进程中通过ptrace系统调用跟踪子进程的系统调用。
typedef struct{ //根据系统调用号获取系统调用的符号名
long code;
char *sysCallName;
}SysCallTable;
SysCallTable sysCallTable[]={ //系统调用号和符号对应表
{0, "read"},
{1, "write"},
{2, "open"},
{3, "close"},
{4, "stat"},
{5, "fstat"},
{6, "lstat"},
{7, "poll"},
{8, "lseek"},
{9, "mmap"},
{10, "mprotect"},
{11, "munmap"},
{12, "brk"},
{13, "rt_sigaction"},
{14, "rt_sigprocmask"},
{15, "rt_sigreturn"},
{16, "ioctl"},
{17, "pread64"},
{18, "pwrite64"},
{19, "readv"},
{20, "writev"},
{21, "access"},
{22, "pipe"},
{23, "select"},
{24, "sched_yield"},
{25, "mremap"},
{26, "msync"},
{27, "mincore"},
{28, "madvise"},
{29, "shmget"},
{30, "shmat"},
{31, "shmctl"},
{32, "dup"},
{33, "dup2"},
{34, "pause"},
{35, "nanosleep"},
{36, "getitimer"},
{37, "alarm"},
{38, "setitimer"},
{39, "getpid"},
{40, "sendfile"},
{41, "socket"},
{42, "connect"},
{43, "accept"},
{44, "sendto"},
{45, "recvfrom"},
{46, "sendmsg"},
{47, "recvmsg"},
{48, "shutdown"},
{49, "bind"},
{50, "listen"},
{51, "getsockname"},
{52, "getpeername"},
{53, "socketpair"},
{54, "setsockopt"},
{55, "getsockopt"},
{56, "clone"},
{57, "fork"},
{58, "vfork"},
{59, "execve"},
{60, "exit"},
{61, "wait4"},
{62, "kill"},
{63, "uname"},
{64, "semget"},
{65, "semop"},
{66, "semctl"},
{67, "shmdt"},
{68, "msgget"},
{69, "msgsnd"},
{70, "msgrcv"},
{71, "msgctl"},
{72, "fcntl"},
{73, "flock"},
{74, "fsync"},
{75, "fdatasync"},
{76, "truncate"},
{77, "ftruncate"},
{78, "getdents"},
{79, "getcwd"},
{80, "chdir"},
{81, "fchdir"},
{82, "rename"},
{83, "mkdir"},
{84, "rmdir"},
{85, "creat"},
{86, "link"},
{87, "unlink"},
{88, "symlink"},
{89, "readlink"},
{90, "chmod"},
{91, "fchmod"},
{92, "chown"},
{93, "fchown"},
{94, "lchown"},
{95, "umask"},
{96, "gettimeofday"},
{97, "getrlimit"},
{98, "getrusage"},
{99, "sysinfo"},
{100, "times"},
{101, "ptrace"},
{102, "getuid"},
{103, "syslog"},
{104, "getgid"},
{105, "setuid"},
{106, "setgid"},
{107, "geteuid"},
{108, "getegid"},
{109, "setpgid"},
{110, "getppid"},
{111, "getpgrp"},
{112, "setsid"},
{113, "setreuid"},
{114, "setregid"},
{115, "getgroups"},
{116, "setgroups"},
{117, "setresuid"},
{118, "getresuid"},
{119, "setresgid"},
{120, "getresgid"},
{121, "getpgid"},
{122, "setfsuid"},
{123, "setfsgid"},
{124, "getsid"},
{125, "capget"},
{126, "capset"},
{127, "rt_sigpending"},
{128, "rt_sigtimedwait"},
{129, "rt_sigqueueinfo"},
{130, "rt_sigsuspend"},
{131, "sigaltstack"},
{132, "utime"},
{133, "mknod"},
{134, "uselib"},
{135, "personality"},
{136, "ustat"},
{137, "statfs"},
{138, "fstatfs"},
{139, "sysfs"},
{140, "getpriority"},
{141, "setpriority"},
{142, "sched_setparam"},
{143, "sched_getparam"},
{144, "sched_setscheduler"},
{145, "sched_getscheduler"},
{146, "sched_get_priority_max"},
{147, "sched_get_priority_min"},
{148, "sched_rr_get_interval"},
{149, "mlock"},
{150, "munlock"},
{151, "mlockall"},
{152, "munlockall"},
{153, "vhangup"},
{154, "modify_ldt"},
{155, "pivot_root"},
{156, "_sysctl"},
{157, "prctl"},
{158, "arch_prctl"},
{159, "adjtimex"},
{160, "setrlimit"},
{161, "chroot"},
{162, "sync"},
{163, "acct"},
{164, "settimeofday"},
{165, "mount"},
{166, "umount2"},
{167, "swapon"},
{168, "swapoff"},
{169, "reboot"},
{170, "sethostname"},
{171, "setdomainname"},
{172, "iopl"},
{173, "ioperm"},
{174, "create_module"},
{175, "init_module"},
{176, "delete_module"},
{177, "get_kernel_syms"},
{178, "query_module"},
{179, "quotactl"},
{180, "nfsservctl"},
{181, "getpmsg"},
{182, "putpmsg"},
{183, "afs_syscall"},
{184, "tuxcall"},
{185, "security"},
{186, "gettid"},
{187, "readahead"},
{188, "setxattr"},
{189, "lsetxattr"},
{190, "fsetxattr"},
{191, "getxattr"},
{192, "lgetxattr"},
{193, "fgetxattr"},
{194, "listxattr"},
{195, "llistxattr"},
{196, "flistxattr"},
{197, "removexattr"},
{198, "lremovexattr"},
{199, "fremovexattr"},
{200, "tkill"},
{201, "time"},
{202, "futex"},
{203, "sched_setaffinity"},
{204, "sched_getaffinity"},
{205, "set_thread_area"},
{206, "io_setup"},
{207, "io_destroy"},
{208, "io_getevents"},
{209, "io_submit"},
{210, "io_cancel"},
{211, "get_thread_area"},
{212, "lookup_dcookie"},
{213, "epoll_create"},
{214, "epoll_ctl_old"},
{215, "epoll_wait_old"},
{216, "remap_file_pages"},
{217, "getdents64"},
{218, "set_tid_address"},
{219, "restart_syscall"},
{220, "semtimedop"},
{221, "fadvise64"},
{222, "timer_create"},
{223, "timer_settime"},
{224, "timer_gettime"},
{225, "timer_getoverrun"},
{226, "timer_delete"},
{227, "clock_settime"},
{228, "clock_gettime"},
{229, "clock_getres"},
{230, "clock_nanosleep"},
{231, "exit_group"},
{232, "epoll_wait"},
{233, "epoll_ctl"},
{234, "tgkill"},
{235, "utimes"},
{236, "vserver"},
{237, "mbind"},
{238, "set_mempolicy"},
{239, "get_mempolicy"},
{240, "mq_open"},
{241, "mq_unlink"},
{242, "mq_timedsend"},
{243, "mq_timedreceive"},
{244, "mq_notify"},
{245, "mq_getsetattr"},
{246, "kexec_load"},
{247, "waitid"},
{248, "add_key"},
{249, "request_key"},
{250, "keyctl"},
{251, "ioprio_set"},
{252, "ioprio_get"},
{253, "inotify_init"},
{254, "inotify_add_watch"},
{255, "inotify_rm_watch"},
{256, "migrate_pages"},
{257, "openat"},
{258, "mkdirat"},
{259, "mknodat"},
{260, "fchownat"},
{261, "futimesat"},
{262, "newfstatat"},
{263, "unlinkat"},
{264, "renameat"},
{265, "linkat"},
{266, "symlinkat"},
{267, "readlinkat"},
{268, "fchmodat"},
{269, "faccessat"},
{270, "pselect6"},
{271, "ppoll"},
{272, "unshare"},
{273, "set_robust_list"},
{274, "get_robust_list"},
{275, "splice"},
{276, "tee"},
{277, "sync_file_range"},
{278, "vmsplice"},
{279, "move_pages"},
{280, "utimensat"},
{281, "epoll_pwait"},
{282, "signalfd"},
{283, "timerfd_create"},
{284, "eventfd"},
{285, "fallocate"},
{286, "timerfd_settime"},
{287, "timerfd_gettime"},
{288, "accept4"},
{289, "signalfd4"},
{290, "eventfd2"},
{291, "epoll_create1"},
{292, "dup3"},
{293, "pipe2"},
{294, "inotify_init1"},
{295, "preadv"},
{296, "pwritev"},
{297, "rt_tgsigqueueinfo"},
{298, "perf_event_open"},
{299, "recvmmsg"},
{300, "fanotify_init"},
{301, "fanotify_mark"},
{302, "prlimit64"},
{303, "name_to_handle_at"},
{304, "open_by_handle_at"},
{305, "clock_adjtime"},
{306, "syncfs"},
{307, "sendmmsg"},
{308, "setns"},
{309, "getcpu"},
{310, "process_vm_readv"},
{311, "process_vm_writev"},
{312, "kcmp"},
{313, "finit_module"},
{314, "sched_setattr"},
{315, "sched_getattr"},
{316, "renameat2"},
{317, "seccomp"},
{318, "getrandom"},
{319, "memfd_create"},
{320, "kexec_file_load"},
{321, "bpf"},
{322, "execveat"},
{323, "userfaultfd"},
{324, "membarrier"},
{325, "mlock2"},
{326, "copy_file_range"},
{327, "preadv2"},
{328, "pwritev2"},
{329, "pkey_mprotect"},
{330, "pkey_alloc"},
{331, "pkey_free"},
{332, "statx"},
{512, "rt_sigaction"},
{513, "rt_sigreturn"},
{514, "ioctl"},
{515, "readv"},
{516, "writev"},
{517, "recvfrom"},
{518, "sendmsg"},
{519, "recvmsg"},
{520, "execve"},
{521, "ptrace"},
{522, "rt_sigpending"},
{523, "rt_sigtimedwait"},
{524, "rt_sigqueueinfo"},
{525, "sigaltstack"},
{526, "timer_create"},
{527, "mq_notify"},
{528, "kexec_load"},
{529, "waitid"},
{530, "set_robust_list"},
{531, "get_robust_list"},
{532, "vmsplice"},
{533, "move_pages"},
{534, "preadv"},
{535, "pwritev"},
{536, "rt_tgsigqueueinfo"},
{537, "recvmmsg"},
{538, "sendmmsg"},
{539, "process_vm_readv"},
{540, "process_vm_writev"},
{541, "setsockopt"},
{542, "getsockopt"},
{543, "io_setup"},
{544, "io_submit"},
{545, "execveat"},
{546, "preadv2"},
{547, "pwritev2"},
{-1, NULL}
};
//根据系统调用号找到对应符号名
char *getSysCallName(long code){
for (SysCallTable *ptr=sysCallTable; ptr->code!=-1; ptr++){
if (ptr->code == code){
return ptr->sysCallName;
}
}
}
//unsigned long转16进制字符串
void getHexCode(unsigned long n, char *result){
char res[128];
int pos=0;
if (n==0){
res[pos++]='0';
}
while (n){
unsigned t = n%16;
if (t>=0 && t<=9){
res[pos++]=t-0+'0';
}else{
switch (t){
case 10: res[pos++]='a';break;
case 11: res[pos++]='b';break;
case 12: res[pos++]='c';break;
case 13: res[pos++]='d';break;
case 14: res[pos++]='e';break;
case 15: res[pos++]='f';break;
}
}
n/=16;
}
res[pos++]='x'; res[pos]='0';
for (int i=0; i<=pos; i++){
result[i]=res[pos-i];
}
result[pos+1]='\0';
return;
}
int main(){
pid_t child = fork();
if (child==0){
//子进程
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
//execl("/bin/ls", "ls", NULL);
execl("/home/jgc/lab/a", "", NULL);
}else{
//父进程
int status; //子进程状态
struct user_regs_struct regs; //子进程的寄存器
int flag=0; //标记系统调用进入还是返回
wait(&status); //接收子进程发送过来的信号
if (WIFEXITED(status)){ //判断子程序是否退出的宏
return 0;
}
ptrace(PTRACE_SYSCALL, child, NULL, NULL);
char str[128];
while (1){
wait(&status); //等待信号
if(WIFEXITED(status)) {
return 0;
}
ptrace(PTRACE_GETREGS, child, 0, ®s); //读取子进程寄存器信息
if (flag==0){ //flag=0表示进入系统调用前
char *name = getSysCallName(regs.orig_rax);
strcat(str, name);
flag=1; //下次截获系统调用时就是完成系统调用后的返回
//根据不同系统调用获取参数
if (regs.orig_rax == SYS_write){
//size_t write(int fildes, const void *buf, size_t nbytes);
char parameterStr[64]="";
sprintf(parameterStr, "(%d, %p, %u)", regs.rdi, regs.rsi, regs.rdx);
strcat(str, parameterStr);
}else if (regs.orig_rax == SYS_read){
//size_t read(int fildes, void *buf, size_t nbytes);
char parameterStr[64]="";
sprintf(parameterStr, "(%d, %p, %u)", regs.rdi, regs.rsi, regs.rdx);
strcat(str, parameterStr);
}else if (regs.orig_rax == SYS_close){
//int close(int fildes);
char parameterStr[64];
sprintf(parameterStr, "(%d)", regs.rdi);
strcat(str, parameterStr);
}else{
//其他系统调用暂不做处理
;
}
}else{ //flag=1表示系统调用完成后返回到用户态的被跟踪进程
char res[32];
//getHexCode(regs.rax, res);
sprintf(res, "=%llu\n", regs.rax);
strcat(str, res);
printf("%s", str);
strcpy(str, "");
flag=0;
}
ptrace(PTRACE_SYSCALL, child, NULL, NULL);
}
}
return 0;
}
编译生成可执行文件b,并执行
gcc b.c -o b
./b
执行结果如下,成功记录下a程序执行过程中产生的一系列系统调用。
brk=21667840
access=18446744073709551614
access=18446744073709551614
open=3
fstat=0
mmap=140522899488768
close(3)=0
access=18446744073709551614
open=3
read(3, 0x7fff3ba89548, 832)=832
fstat=0
mmap=140522899484672
mmap=140522893357056
mprotect=0
mmap=140522897289216
mmap=140522897313792
close(3)=0
mmap=140522899480576
mmap=140522899476480
arch_prctl=0
mprotect=0
mprotect=0
mprotect=0
munmap=0
brk=21667840
brk=21803008
open=3
fstat=0
write(3, 0x14aa240, 5)=5
close(3)=0