linux – 使用ptrace跟踪跨子节点的所有execve()调用
发布时间:2020-12-13 16:59:05 所属栏目:Linux 来源:网络整理
导读:我正在尝试在 Linux CentOS上编写一个工具来跟踪所有生成的进程以及运行的内容.本质上,我有兴趣遍历所有fork / clone并从execve()发出所有命令行. Strace已经(某些)做了这个,但它也截断了调用和参数.我还想更好地理解ptrace()的工作原理. 因此,第一个障碍是
|
我正在尝试在
Linux CentOS上编写一个工具来跟踪所有生成的进程以及运行的内容.本质上,我有兴趣遍历所有fork / clone并从execve()发出所有命令行. Strace已经(某些)做了这个,但它也截断了调用和参数.我还想更好地理解ptrace()的工作原理.
因此,第一个障碍是弄清楚如何使用ptrace()来执行fork / clone而不需要跟踪程序需要分叉自己的副本.我挖了进去,发现了这个怎么样.因为fork是在Linux上用clone实现的,所以我注意到strace将一些比特放入克隆系统调用中,以便在没有任何额外麻烦的情况下启用子跟踪. 所以,实质上代码只是一个大问题: while (1) {
int pid = wait3(-1,...);
/* process what happened */
ptrace(PTRACE_SYSCALL,pid,...);
}
这适用于相对简单的进程,如/ bin / sh,但是,某些进程导致wait()无限期挂起.我唯一能够确定的是我正在跟踪的进程是对它的子进行sys_rt_sigsuspend()(因此,跟踪器的孙子)然后事情楔入. 我很好奇是否有一种理智的方式可以调试可能发生的事情.有些事情显然阻止了流程树的进展 这是有问题的程序的源代码: #include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
/* For the clone flags
*/
#include <sched.h>
/* #include <errno.h> */
#include <sys/ptrace.h>
#include <sys/user.h>
/* Defines our syscalls like
*/
#include <sys/syscall.h>
#include <sys/reg.h>
#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <map>
using namespace std;
char bufstr[4096];
#ifdef __x86_64__
#define REG_ACC RAX
#define REG_ARG1 RDI
#define REG_ARG2 RSI
#else
#define REG_ACC EAX
#define REG_ARG1 EBX
#define REG_ARG2 ECX
#endif
/* Trace control structure per PID that we're tracking
*/
class tcb {
int pid_;
int entering_;
public:
tcb(int pid,int entering = 1) : pid_(pid),entering_(entering) {};
tcb() : pid_(-1) {};
// tcb(const tcb& p) : pid_(pid.pid()),entering_(entering.entering()) {};
int& pid() { return pid_; }
int& entering() { return entering_; }
};
/* Fetch a string from process (pid) at location (ptr). Buf is the place
* to store the data with size limit (size). Return the number of bytes
* copied.
*/
int get_string(int pid,long ptr,char *buf,int size)
{
long data;
char *p = (char *) &data;
int j = 0;
while ((data = ptrace(PTRACE_PEEKTEXT,(void *) ptr,0)) && j < size) {
int i;
for (i = 0; i < sizeof(data) && j < size; i++,j++) {
if (!(buf[j] = p[i]))
goto done;
}
ptr += sizeof(data);
}
done:
buf[j] = ' ';
return j;
}
int main(int argc,char *argv[])
{
int status = 0;
long scno = 0;
// int entering = 1;
struct user_regs_struct regs;
map<int,tcb> pidTable;
struct sigaction sa;
/* Setup
*/
int pid = fork();
if (!pid && argc) {
if (ptrace(PTRACE_TRACEME,0) < 0) {
perror("ptrace(PTRACE_ME,... ");
exit(1);
}
execvp(argv[1],&argv[1]);
} else {
sa.sa_flags = 0;
sa.sa_handler = SIG_DFL;
sigemptyset(&sa.sa_mask);
sigaction(SIGCHLD,&sa,NULL);
waitpid(pid,&status,0);
pidTable[pid] = tcb(pid);
fprintf(stderr,"pid is %dn",pidTable[pid].pid());
while (!pidTable.empty()) {
if (pid > 0) {
//fprintf(stderr,"%d: Restarting %dn",getpid(),pid);
if (ptrace(PTRACE_SYSCALL,0) < 0) {
perror("ptrace(PTRACE_SYSCALL,...");
exit(1);
}
}
// waitpid(pid,0);
// pid = waitpid(-1,0);
pid = wait3(&status,__WALL,0);
// fprintf(stderr,"Pid from wait is %dn",pid);
if (pid < 0) {
perror("waitpid");
break;
} else {
/* fprintf(stderr,"%d: Status is: ",pid); */
/*
if (WIFEXITED(status)) {
fprintf(stderr,"exited");
} else if (WIFSIGNALED(status)) {
fprintf(stderr,"exited");
} else if (WIFSTOPPED(status),"stopped") {
fprintf(stderr,"stopped");
} else if (WIFCONTINUED(status)) {
fprintf(stderr,"continued");
}
fprintf(stderr,"n");
*/
if (WIFEXITED(status) || WIFSIGNALED(status)) {
/* Probably empty the table here */
pidTable.erase(pid);
fprintf(stderr,"Detect process term/kill %dn",pid);
/* if (ptrace(PTRACE_DETACH,0) < 0) {
perror("ptrace");
} */
pid = -1;
continue;
}
}
ptrace(PTRACE_GETREGS,®s);
#ifdef __x86_64__
scno = regs.orig_rax;
#else
scno = regs.orig_eax;
#endif /* __x86_64__ */
if (scno == SYS_execve) {
fprintf(stderr,"%d: Exec branchn",pid);
if (pidTable[pid].entering()) {
long ldata,ptr,ptr1;
ptrace(PTRACE_GETREGS,®s);
#ifdef __x86_64__
ptr = regs.rdi;
#else
ptr = regs.ebx;
#endif /* __x86_64__ */
fprintf(stderr,"%d: exec(",pid);
if (ptr) {
get_string(pid,bufstr,sizeof(bufstr));
fprintf(stderr,"%s",bufstr);
}
#ifdef __x86_64__
ptr1 = regs.rsi;
#else
ptr1 = regs.ecx;
#endif /* __x86_64__ */
for (; ptr1; ptr1 += sizeof(unsigned long)) {
ptr = ptr1;
/* Indirect through ptr since we have char *argv[] */
ptr = ptrace(PTRACE_PEEKTEXT,0);
if (!ptr)
break;
get_string(pid,sizeof(bufstr));
fprintf(stderr,",%s",bufstr);
}
fprintf(stderr,")n");
pidTable[pid].entering() = 0;
}
else {
long acc = ptrace(PTRACE_PEEKUSER,sizeof(unsigned long) * REG_ACC,0);
pidTable[pid].entering() = 1;
fprintf(stderr,"%d: Leaving exec: eax is %ldn",acc);
}
} else if (scno == SYS_fork || scno == SYS_clone) {
fprintf(stderr,"%d: fork/clone branchn",pid);
if (pidTable[pid].entering()) {
long flags = ptrace(PTRACE_PEEKUSER,(sizeof(unsigned long) * REG_ARG1),0);
fprintf(stderr,"%d: Entering fork/clonen",pid);
pidTable[pid].entering() = 0;
if (ptrace(PTRACE_POKEUSER,flags | CLONE_PTRACE &
~(flags & CLONE_VFORK ?
CLONE_VFORK | CLONE_VM : 0)) < 0) {
perror("ptrace");
}
if (ptrace(PTRACE_POKEUSER,(sizeof(unsigned long) * REG_ARG2),0) < 0) {
perror("ptrace");
}
} else {
// int child;
ptrace(PTRACE_GETREGS,®s);
#ifdef __x86_64__
fprintf(stderr,"%d: Leaving fork/clone: rax = %ldn",regs.rax);
#else
fprintf(stderr,"%d: Leaving fork/clone: eax = %ldn",regs.eax);
#endif
pidTable[pid].entering() = 1;
#ifdef __x86_64__
if (regs.rax <= 0) {
#else
if (regs.eax <= 0) {
#endif
continue;
}
#ifdef __x86_64__
int newpid = regs.rax;
#else
int newpid = regs.eax;
#endif
pidTable[newpid] = tcb(newpid,0);
//pidTable[newpid] = tcb(newpid,1);
//pidTable[newpid] = pidTable[pid];
fprintf(stderr,"%d: forked child is %dn",newpid);
}
} else if (scno == SYS_exit) {
fprintf(stderr,"%d: exit syscall detectedn",pid);
} else if (scno < 0) {
fprintf(stderr,"Negative syscall number for %dn",pid);
exit(1);
} else {
fprintf(stderr,"%d: Scno is %ldn",scno);
}
}
}
return 0;
}
解决方法
有ptrace PTRACE_SETOPTIONS子标志的标志:PTRACE_O_TRACEFORK,PTRACE_O_TRACEEXEC和PTRACE_O_TRACEEXIT.更多内容是ptrace的手册页.
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
