ptrace函数深入分析
ptrace函数:进程跟踪。
形式:#include<sys/ptrace.h>
Int ptrace(int request,int pid,int addr,int data);
概述:
父进程控制子进程运行,检查和改变它的核心Image。Ptrace主要用来实现断点调试。当进程被中止,通知父进程,进程的内存空间可以被读写,父进程可以选择是子进程继续执行,还是中止。
根据ptrace的函数原形 int ptrace(int request, int pid, int addr, int data);
request的不同参数决定了系统调用的功能
|
PTRACE_TRACEME |
本进程被其父进程所跟踪。其父进程应该希望跟踪子进程 |
|
PTRACE_PEEKTEXT, PTRACE_PEEKDATA |
从内存地址中读取一个字节,内存地址由addr给出。 |
|
PTRACE_PEEKUSR |
从USER区域中读取一个字节,偏移量为addr。 |
|
PTRACE_POKETEXT, PTRACE_POKEDATA |
往内存地址中写入一个字节。内存地址由addr给出。 |
|
PTRACE_POKEUSR |
往USER区域中写入一个字节。偏移量为addr。 |
|
PTRACE_SYSCALL, PTRACE_CONT |
重新运行。 |
|
PTRACE_KILL |
杀掉子进程,使它退出。 |
|
PTRACE_SINGLESTEP |
设置单步执行标志 |
|
PTRACE_ATTACH |
跟踪指定pid 进程。 |
|
PTRACE_DETACH |
结束跟踪 |
Intel386特有:
|
PTRACE_GETREGS |
读取寄存器 |
|
PTRACE_SETREGS |
设置寄存器 |
|
PTRACE_GETFPREGS |
读取浮点寄存器 |
|
PTRACE_SETFPREGS |
设置浮点寄存器 |
init进程不可以使用此函数
返回值:
成功返回0。错误返回-1。errno被设置
错误:
|
EPERM |
特殊进程不可以被跟踪或进程已经被跟踪。 |
|
ESRCH |
指定的进程不存在 |
|
EIO |
请求非法 |
Ptrace功能:
1) PTRACE_TRACEME
形式:ptrace(PTRACE_TRACEME,0 ,0 ,0)
描述:本进程被其父进程所跟踪。其父进程应该希望跟踪子进程。
2) PTRACE_PEEKTEXT, PTRACE_PEEKDATA
形式:ptrace(PTRACE_PEEKTEXT, pid, addr, data)
ptrace(PTRACE_PEEKDATA, pid, addr, data)
描述:从内存地址中读取一个字节,pid表示被跟踪的子进程,内存地址由addr给出,data为用户变量地址用于返回读到的数据。在Linux(i386)中用户代码段与用户数据段重合所以读取代码段和数据段数据处理是一样的。
3) PTRACE_POKETEXT, PTRACE_POKEDATA
形式:ptrace(PTRACE_POKETEXT, pid, addr, data)
ptrace(PTRACE_POKEDATA, pid, addr, data)
描述:往内存地址中写入一个字节。pid表示被跟踪的子进程,内存地址由addr给出,data为所要写入的数据。
4) PTRACE_PEEKUSR
形式:ptrace(PTRACE_PEEKUSR, pid, addr, data)
描述:从USER区域中读取一个字节,pid表示被跟踪的子进程,USER区域地址由addr给出,data为用户变量地址用于返回读到的数据。USER结构为core文件的前面一部分,它描述了进程中止时的一些状态,如:寄存器值,代码、数据段大小,代码、数据段开始地址等。在Linux(i386)中通过PTRACE_PEEKUSER和PTRACE_POKEUSR可以访问USER结构的数据有寄存器和调试寄存器。
5) PTRACE_POKEUSR
形式:ptrace(PTRACE_POKEUSR, pid, addr, data)
描述:往USER区域中写入一个字节,pid表示被跟踪的子进程,USER区域地址由addr给出,data为需写入的数据。
6) PTRACE_CONT
形式:ptrace(PTRACE_CONT, pid, 0, signal)
描述:继续执行。pid表示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。
7) PTRACE_SYSCALL
形式:ptrace(PTRACE_SYS, pid, 0, signal)
描述:继续执行。pid表示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。与PTRACE_CONT不同的是进行系统调用跟踪。在被跟踪进程继续运行直到调用系统调用开始或结束时,被跟踪进程被中止,并通知父进程。
8) PTRACE_KILL
形式:ptrace(PTRACE_KILL,pid)
描述:杀掉子进程,使它退出。pid表示被跟踪的子进程。
9) PTRACE_SINGLESTEP
形式:ptrace(PTRACE_KILL, pid, 0, signle)
描述:设置单步执行标志,单步执行一条指令。pid表示被跟踪的子进程。signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。当被跟踪进程单步执行完一个指令后,被跟踪进程被中止,并通知父进程。
10) PTRACE_ATTACH
形式:ptrace(PTRACE_ATTACH,pid)
描述:跟踪指定pid 进程。pid表示被跟踪进程。被跟踪进程将成为当前进程的子进程,并进入中止状态。
11) PTRACE_DETACH
形式:ptrace(PTRACE_DETACH,pid)
描述:结束跟踪。 pid表示被跟踪的子进程。结束跟踪后被跟踪进程将继续执行。
12) PTRACE_GETREGS
形式:ptrace(PTRACE_GETREGS, pid, 0, data)
描述:读取寄存器值,pid表示被跟踪的子进程,data为用户变量地址用于返回读到的数据。此功能将读取所有17个基本寄存器的值。
13) PTRACE_SETREGS
形式:ptrace(PTRACE_SETREGS, pid, 0, data)
描述:设置寄存器值,pid表示被跟踪的子进程,data为用户数据地址。此功能将设置所有17个基本寄存器的值。
14) PTRACE_GETFPREGS
形式:ptrace(PTRACE_GETFPREGS, pid, 0, data)
描述:读取浮点寄存器值,pid表示被跟踪的子进程,data为用户变量地址用于返回读到的数据。此功能将读取所有浮点协处理器387的所有寄存器的值。
15) PTRACE_SETFPREGS
形式:ptrace(PTRACE_SETREGS, pid, 0, data)
描述:设置浮点寄存器值,pid表示被跟踪的子进程,data为用户数据地址。此功能将设置所有浮点协处理器387的所有寄存器的值。
代码分析
与Ptrace函数相关的代码:
1. sys_ptrace函数,完成ptrace系统调用的代码。
2. 为完成sys_ptrace功能所需调用的一些辅助函数,寄存器读写函数和内存读写函数。
3. 信号处理函数中,对被调试进程的处理(中止其运行、继续运行)。
4. syscall_trace函数,完成了系统调用调试下的处理。
5. 调试陷阱处理(异常1处理),完成单步执行和断点中断处理。
6. execve系统调用中对被调试进程装入后中止的实现。
1.sys_ptrace函数
sys_ptrace函数完成了ptrace系统调用功能。源码位置/linux/arch/i386/kernel/ptrace.c
sys_ptrace函数执行流程

. asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
. {
. struct task_struct *child;
. struct user * dummy;
. int i;
.
. dummy = NULL;
.
. if (request == PTRACE_TRACEME) {
. /* are we already being traced? */
. if (current->flags & PF_PTRACED)
. return -EPERM;
. /* set the ptrace bit in the process flags. */
. current->flags |= PF_PTRACED;
. return ;
. }
. if (pid == ) /* 进程不能被调试 */
. return -EPERM;
. if (!(child = get_task(pid)))
. return -ESRCH;
. if (request == PTRACE_ATTACH) {
. if (child == current)
. return -EPERM;
. if ((!child->dumpable ||
. (current->uid != child->euid) ||
. (current->uid != child->suid) ||
. (current->uid != child->uid) ||
. (current->gid != child->egid) ||
. (current->gid != child->sgid) ||
. (current->gid != child->gid)) && !suser())
. return -EPERM;
. /* 同一进程不能多次附加 */
. if (child->flags & PF_PTRACED)
. return -EPERM;
. child->flags |= PF_PTRACED;
. if (child->p_pptr != current) {
. REMOVE_LINKS(child);
. child->p_pptr = current;
. SET_LINKS(child);
. }
. send_sig(SIGSTOP, child, );
. return ;
. }
. if (!(child->flags & PF_PTRACED))
. return -ESRCH;
. if (child->state != TASK_STOPPED) {
. if (request != PTRACE_KILL)
. return -ESRCH;
. }
. if (child->p_pptr != current)
. return -ESRCH;
.
. switch (request) {
. /* when I and D space are separate, these will need to be fixed. */
. case PTRACE_PEEKTEXT: /* read word at location addr. */
. case PTRACE_PEEKDATA: {
. unsigned long tmp;
. int res;
.
. res = read_long(child, addr, &tmp);
. if (res < )
. return res;
. res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
. if (!res)
. put_fs_long(tmp,(unsigned long *) data);
. return res;
. }
.
. /* read the word at location addr in the USER area. */
. case PTRACE_PEEKUSR: {
. unsigned long tmp;
. int res;
.
. if ((addr & ) || addr < ||
. addr > sizeof(struct user) - )
. return -EIO;
.
. res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
. if (res)
. return res;
. tmp = ; /* Default return condition */
. if(addr < *sizeof(long)) {
. addr = addr >> ; /* temporary hack. */
.
. tmp = get_stack_long(child, sizeof(long)*addr - MAGICNUMBER);
. if (addr == DS || addr == ES ||
. addr == FS || addr == GS ||
. addr == CS || addr == SS)
. tmp &= 0xffff;
. };
. if(addr >= (long) &dummy->u_debugreg[] &&
. addr <= (long) &dummy->u_debugreg[]){
. addr -= (long) &dummy->u_debugreg[];
. addr = addr >> ;
. tmp = child->debugreg[addr];
. };
. put_fs_long(tmp,(unsigned long *) data);
. return ;
. }
.
. /* when I and D space are separate, this will have to be fixed. */
. case PTRACE_POKETEXT: /* write the word at location addr. */
. case PTRACE_POKEDATA:
. return write_long(child,addr,data);
.
. case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
. if ((addr & ) || addr < ||
. addr > sizeof(struct user) - )
. return -EIO;
.
. addr = addr >> ; /* temporary hack. */
.
. if (addr == ORIG_EAX)
. return -EIO;
. if (addr == DS || addr == ES ||
. addr == FS || addr == GS ||
. addr == CS || addr == SS) {
. data &= 0xffff;
. if (data && (data & ) != )
. return -EIO;
. }
. if (addr == EFL) { /* flags. */
. data &= FLAG_MASK;
. data |= get_stack_long(child, EFL*sizeof(long)-MAGICNUMBER) & ~FLAG_MASK;
. }
. /* Do not allow the user to set the debug register for kernel
127. address space */
. if(addr < ){
. if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data))
. return -EIO;
. return ;
. };
.
. /* We need to be very careful here. We implicitly
135. want to modify a portion of the task_struct, and we
136. have to be selective about what portions we allow someone
137. to modify. */
.
. addr = addr << ; /* Convert back again */
. if(addr >= (long) &dummy->u_debugreg[] &&
. addr <= (long) &dummy->u_debugreg[]){
.
. if(addr == (long) &dummy->u_debugreg[]) return -EIO;
. if(addr == (long) &dummy->u_debugreg[]) return -EIO;
. if(addr < (long) &dummy->u_debugreg[] &&
. ((unsigned long) data) >= 0xbffffffd) return -EIO;
.
. if(addr == (long) &dummy->u_debugreg[]) {
. data &= ~DR_CONTROL_RESERVED;
. for(i=; i<; i++)
. if ((0x5f54 >> ((data >> ( + *i)) & 0xf)) & )
. return -EIO;
. };
.
. addr -= (long) &dummy->u_debugreg;
. addr = addr >> ;
. child->debugreg[addr] = data;
. return ;
. };
. return -EIO;
.
. case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
. case PTRACE_CONT: { /* restart after signal. */
. long tmp;
.
. if ((unsigned long) data > NSIG)
. return -EIO;
. if (request == PTRACE_SYSCALL)
. child->flags |= PF_TRACESYS;
. else
. child->flags &= ~PF_TRACESYS;
. child->exit_code = data;
. wake_up_process(child);
. /* make sure the single step bit is not set. */
. tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
. put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
. return ;
. }
.
. /*
181. * make the child exit. Best I can do is send it a sigkill.
182. * perhaps it should be put in the status that it wants to
183. * exit.
184. */
. case PTRACE_KILL: {
. long tmp;
.
. if (child->state == TASK_ZOMBIE) /* already dead */
. return ;
. wake_up_process(child);
. child->exit_code = SIGKILL;
. /* make sure the single step bit is not set. */
. tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
. put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
. return ;
. }
.
. case PTRACE_SINGLESTEP: { /* set the trap flag. */
. long tmp;
.
. if ((unsigned long) data > NSIG)
. return -EIO;
. child->flags &= ~PF_TRACESYS;
. tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) | TRAP_FLAG;
. put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
. wake_up_process(child);
. child->exit_code = data;
. /* give it a chance to run. */
. return ;
. }
.
. case PTRACE_DETACH: { /* detach a process that was attached. */
. long tmp;
.
. if ((unsigned long) data > NSIG)
. return -EIO;
. child->flags &= ~(PF_PTRACED|PF_TRACESYS);
. wake_up_process(child);
. child->exit_code = data;
. REMOVE_LINKS(child);
. child->p_pptr = child->p_opptr;
. SET_LINKS(child);
. /* make sure the single step bit is not set. */
. tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
. put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
. return ;
. }
.
. default:
. return -EIO;
. }
. }
sys_ptrace函数
1) PTRACE_TRACEME处理
说明:此处理使当前进程进入调试状态。进程是否为调试状态由进程的标志PF_PTRACED表示。
流程:

. . if (request == PTRACE_TRACEME) {
. . /* 是否被跟踪 */
. . if (current->flags & PF_PTRACED)
. . return -EPERM;
. . /* 设置跟踪标志 */
. . current->flags |= PF_PTRACED;
. . return ;
. . }
PTRACE_TRACEME处理
2) PTRACE_ATTACH处理
说明:此处理设置开始调试某一进程,此进程可以是任何进程(init 进程除外)。对某一进程的调试需有对这一进程操作的权限。不能调试自身进程。一个进程不能ATTACH多次。为完成对一个进程的调试设置,首先设置进程标志置PF_PTRACED。再将需调试的进程设置为当前进程的子进程。最后向它发信号SIGSTOP中止它的运行,使它进入调试状态。
流程:

. . if (request == PTRACE_ATTACH) {
. . if (child == current) // 不能调试自身进程
. . return -EPERM;
. . if ((!child->dumpable ||
. . (current->uid != child->euid) ||
. . (current->uid != child->suid) ||
. . (current->uid != child->uid) ||
. . (current->gid != child->egid) ||
. . (current->gid != child->sgid) ||
. . (current->gid != child->gid)) && !suser()) // 检验用户权限
. . return -EPERM;
. . /* 同一进程不能多次附加 */
. . if (child->flags & PF_PTRACED)
. . return -EPERM;
. . child->flags |= PF_PTRACED; // 设置进程标志位
. . if (child->p_pptr != current) { // 设置进程为当前进程的子进程
. . REMOVE_LINKS(child);
. . child->p_pptr = current;
. . SET_LINKS(child);
. . }
. . send_sig(SIGSTOP, child, ); // 发送SIGSTOP信号中止运行
. . return ;
. . }
PTRACE_ATTACH处理
4) PTRACE_POKETEXT,PTRACE_POKEDATA处理
说明:与PTRACE_PEEKTEXT,PTRACE_PEEKDATA处理相反,此处理为写进程内存
流程:

. . case PTRACE_POKETEXT: /* write the word at location addr. */ . . case PTRACE_POKEDATA: . . return write_long(child,addr,data);
PTRACE_POKETEXT,PTRACE_POKEDATA处理
5) PTRACE_PEEKUSR处理
说明:在Linux(i386)中,读写USER区域的数据值有用户寄存器和调试寄存器的值。用户寄存器包括17个寄存器,它们分别是EBX、ECX、EDX、ESI、EDI、EBP、EAX、DS、ES、FS、GS、ORIG_EAX、EIP、CS、EFLAGS、ESP、SS。这些寄存器的读写由辅助函数putreg()和getreg()函数完成。调试寄存器为DR0—DR7。其中DR4和DR5为系统保留的寄存器,不可以写。DR0—DR3中的断点地址必须在用户的3G空间内,在核心内存设置断点非法。DR7中的RWE与LEN数据位必须合法(LEN≠10保留、RWE≠10保留、RWE=00时LEN=00指令断点为一字节)。
流程:

. . case PTRACE_PEEKUSR: {
. . unsigned long tmp;
. . int res;
. .
. . if ((addr & ) || addr < || // 越界或字节码未对齐出错
. . addr > sizeof(struct user) - )
. . return -EIO;
. .
. . res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
. . if (res)
. . return res;
. . tmp = ; /* Default return condition */
. . if(addr < *sizeof(long)) {
. . addr = addr >> ; /* temporary hack. */
. .
. . tmp = get_stack_long(child, sizeof(long)*addr - MAGICNUMBER);
. . if (addr == DS || addr == ES ||
. . addr == FS || addr == GS ||
. . addr == CS || addr == SS)
. . tmp &= 0xffff;
. . };
. . if(addr >= (long) &dummy->u_debugreg[] &&
. . addr <= (long) &dummy->u_debugreg[]){
. . addr -= (long) &dummy->u_debugreg[];
. . addr = addr >> ;
. . tmp = child->debugreg[addr];
. . };
. . put_fs_long(tmp,(unsigned long *) data);
. . return ;
. . }
PTRACE_PEEKUSR处理
6) PTRACE_POKEUSR处理
说明:与PTRACE_PEEKUSR处理相反,此处理为写USER区域。
流程:

源码:
. . case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
. . if ((addr & ) || addr < ||
. . addr > sizeof(struct user) - )
. . return -EIO;
. .
. . addr = addr >> ; /* temporary hack. */
. .
. . if (addr == ORIG_EAX)
. . return -EIO;
. . if (addr == DS || addr == ES ||
. . addr == FS || addr == GS ||
. . addr == CS || addr == SS) {
. . data &= 0xffff;
. . if (data && (data & ) != )
. . return -EIO;
. . }
. . if (addr == EFL) { /* flags. */
. . data &= FLAG_MASK;
. . data |= get_stack_long(child, EFL*sizeof(long)-MAGICNUMBER) & ~FLAG_MASK;
. . }
. . /* Do not allow the user to set the debug register for kernel
22. 127. address space */
. . if(addr < ){
. . if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data))
. . return -EIO;
. . return ;
. . };
. .
. . /* We need to be very careful here. We implicitly
30. 135. want to modify a portion of the task_struct, and we
31. 136. have to be selective about what portions we allow someone
32. 137. to modify. */
. .
. . addr = addr << ; /* Convert back again */
. . if(addr >= (long) &dummy->u_debugreg[] &&
. . addr <= (long) &dummy->u_debugreg[]){
. .
. . if(addr == (long) &dummy->u_debugreg[]) return -EIO;
. . if(addr == (long) &dummy->u_debugreg[]) return -EIO;
. . if(addr < (long) &dummy->u_debugreg[] &&
. . ((unsigned long) data) >= 0xbffffffd) return -EIO;
. .
. . if(addr == (long) &dummy->u_debugreg[]) {
. . data &= ~DR_CONTROL_RESERVED;
. . for(i=; i<; i++)
. . if ((0x5f54 >> ((data >> ( + *i)) & 0xf)) & )
. . return -EIO;
. . };
. .
. . addr -= (long) &dummy->u_debugreg;
. . addr = addr >> ;
. . child->debugreg[addr] = data;
. . return ;
. . };
. . return -EIO;
PTRACE_POKEUSR处理
7) PTRACE_SYSCALL,PTRACE_CONT处理
说明:PTRACE_SYSCALL和PTRACE_CONT有着相同的处理,都是让子进程继续运行,其区别PTRACE_SYSCALL设置了进程标志PF_TRACESYS。这样可以使进程在下一次系统调用开始或结束时中止运行。继续执行要保证清除单步执行标志。用户参数data为用户提供的信号,希望子进程继续处理此信号。如果为0则不处理,如果不为0则在唤醒子进程后向子进程发送此信号(在do_signal()和syscall_trace()函数中完成)。
流程:

. . case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
. . case PTRACE_CONT: { /* restart after signal. */
. . long tmp;
. .
. . if ((unsigned long) data > NSIG)
. . return -EIO;
. . if (request == PTRACE_SYSCALL)
. . child->flags |= PF_TRACESYS;
. . else
. . child->flags &= ~PF_TRACESYS;
. . child->exit_code = data;
. . wake_up_process(child);
. . /* make sure the single step bit is not set. */
. . tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
. . put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
. . return ;
. . }
. .
PTRACE_SYSCALL,PTRACE_CONT处理
8) PTRACE_KILL处理
说明:此功能完成杀死子进程的功能。以往杀死进程只要往此进程发送SIGKILL信号。在此处理类似于PTRACE_CONT处理,只是把子进程继续的信号设置为SIGKILL,则唤醒子进程后,子进程会受到SIGKILL信号。
流程:

. . case PTRACE_KILL: {
. . long tmp;
. .
. . if (child->state == TASK_ZOMBIE) /* 进程已经退出 */
. . return ;
. . wake_up_process(child);
. . child->exit_code = SIGKILL;
. . /* make sure the single step bit is not set. */
. . tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
. . put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
. . return ;
. . }
PTRACE_KILL处理
9) PTRACE_SINGLESTEP处理
说明:单步调试,子进程运行一条指令。此处理类似于PTRACE_CONT处理。不同的只是设置类单步调试标志TF。
流程:

. . case PTRACE_SINGLESTEP: { /* set the trap flag. */
. . long tmp;
. .
. . if ((unsigned long) data > NSIG)
. . return -EIO;
. . child->flags &= ~PF_TRACESYS;
. . tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) | TRAP_FLAG;
. . put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
. . wake_up_process(child);
. . child->exit_code = data;
. . /* give it a chance to run. */
. . return ;
. . }
PTRACE_SINGLESTEP处理
10) PTRACE_DETACH处理
说明:终止调试一个子进程。此处理与PTRACE_ATTACH处理相反。在此做了一些清理操作:清除PF_TRACESYS和PF_PTRACED进程标志,清除TF标志,父进程指针还原。最后唤醒此进程,让其继续执行。
流程:

. . case PTRACE_DETACH: { /* detach a process that was attached. */
. . long tmp;
. .
. . if ((unsigned long) data > NSIG)
. . return -EIO;
. . child->flags &= ~(PF_PTRACED|PF_TRACESYS);
. . wake_up_process(child);
. . child->exit_code = data;
. . REMOVE_LINKS(child);
. . child->p_pptr = child->p_opptr;
. . SET_LINKS(child);
. . /* make sure the single step bit is not set. */
. . tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
. . put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
. . return ;
. . }
PTRACE_DETACH处理
Ptrace的使用
ptrace:提供了一种父进程可以控制子进程运行,并检查和改变核心功能的调试器
1. 启动、中止调试程序
int pid;
pid = fork ();
if (pid < )
perror_with_name ("fork");
if (pid == )
{
ptrace (PTRACE_TRACEME, , , );
execv (program, allargs); /* char *program;
char **allargs; 指向程序名和参数
fprintf (stderr, "Cannot exec %s: %s.\n", program,
errno < sys_nerr ? sys_errlist[errno] : "unknown error");
fflush (stderr);
_exit (0177);
}
wait(pid);
ptrace (PTRACE_CONT, pid, 0, 0);
wait(pid);
2.对现有进程进行调试
使用PTRACE_ATTACH
. ptrace(PTRACE_ATTACH,pid, ,) . wait(pid);
3.退出进程调试
使用PTRACE_DETACH
. ptrace(PTRACE_DETACH,pid, ,)
4.终止调试进程运行
使用PTRACE_KILL
. ptrace(PTRACE_DETACH,pid, ,)
ptrace函数深入分析的更多相关文章
- 认识ptrace函数
认识ptrace函数 这是man对于ptrace这个系统调用的解释 http://man7.org/linux/man-pages/man2/ptrace.2.html #include <sy ...
- Hook ptrace 调试加入了ptrace函数的程序
Hook ptrace 调试加入了ptrace函数的程序 #import <substrate.h> #if !defined(PT_DENY_ATTACH)#define PT_DENY ...
- linux 使用ptrace函数时找不到头文件 .h 或者找不到某个宏的解决方法
例如: #include <stdio.h> #include <sys/ptrace.h> #include <sys/types.h> #include < ...
- Android Hook学习之ptrace函数的使用
Synopsis #include <sys/ptrace.h> long ptrace(enum __ptrace_request request, pid_t pid, void *a ...
- scanf()/getchar()和gets()深入分析
C/C++学习笔记1 - 深入了解scanf()/getchar()和gets()等函数 ---------------------------------------------------- | ...
- 【C语言】函数和自定义函数
函数,我之前也提到过一点点内容.其实函数是很好理解的,但是写起来又十分麻烦. 一. 函数引入 我们知道,C源程序是由函数组成的.请看下面的简单函数例子 #include <stdio.h ...
- linux ptrace II
第一篇 linux ptrace I 在之前的文章中我们用ptrace函数实现了查看系统调用参数的功能.在这篇文章中,我们会用ptrace函数实现设置断点,跟代码注入功能. 参考资料 Playing ...
- linux ptrace I
这几天通过<游戏安全--手游安全技术入门这本书>了解到linux系统中ptrace()这个函数可以实现外挂功能,于是在ubuntu 16.04 x86_64系统上对这个函数进行了学习. 参 ...
- 深入浅出scanf、getcha、gets、cin函数
转:问题描述一:(分析scanf()和getchar()读取字符) scanf(), getchar()等都是标准输入函数,一般人都会觉得这几个函数非常简单,没什么特殊的.但是有时候却就是因为使用这些 ...
随机推荐
- python爬虫中文乱码问题(request方式爬取)
https://blog.csdn.net/guoxinian/article/details/83047746 req = requests.get(url)返回的是类对象 其包括的属性有: r ...
- C++ - 结构体构造函数使用总结
关于结构体构造函数使用总结 三种结构体初始化方法 1.利用结构体自带的默认构造函数 2.利用带参数的构造函数 3.利用默认无参的构造函数 要点: 在建立结构体数组时,如果只写了带参数的构造函数将会出现 ...
- 执行DOS命令并返回结果
public static String excuteCommand(String command){ Runtime runtime = Runtime.getRuntime(); try { Pr ...
- MySQL学习笔记3——DCL
DCL(数据控制语言) 1.创建用户 *CREATE USER 用户名@IP地址 IDENTIFIED BY '密码'; >用户只能在指定的IP地址上登录*CREATE USER 用户名@'%' ...
- 快读&快写模板【附O2优化】
快读&快写模板 快读快写,顾名思义,就是提升输入和输出的速度.在这里简单介绍一下几种输入输出的优劣. C++ cin/cout 输入输出:优点是读入的时候不用管数据类型,也就是说不用背scan ...
- H5/纯JS实现:把网页中的文字复制到剪切板
copy =() => { const dom = document.getElementById(`collect-text-${t.Id}`) const selection = windo ...
- cicros安装
1.下载与安装依赖包 wget https://github.com/libgd/libgd/releases/download/gd-2.2.5/libgd-2.2.5.tar.gz tar zxv ...
- 用Node.js给邮箱发送邮件
首先我们需要做的是下载发送邮件的包 cnpm install nodemailer --save 然后写发送邮件的代码,代码如下: 实现原理是:用你的邮箱给其他邮箱发送邮件,所以这里需要填写你的邮箱和 ...
- MyEclipse清除所有断点的方法
今天调试网站时遇到点奇怪的问题,于是在宠大的代码段里加了N处断点,但从其它项目代码段链接代码加入断点后,关闭标签再次打开时发现断点看不到了,但运行到那段代码时依然会被中断.没有断点标记,不能手动取消怎 ...
- Paper | Beyond a Gaussian Denoiser: Residual Learning of Deep CNN for Image Denoising
目录 故事背景 网络结构 BN和残差学习 拓展到其他任务 发表在2017 TIP. 摘要 Discriminative model learning for image denoising has b ...