📄 ptrace.c
字号:
/* ptrace.c *//* By Ross Biro 1/23/92 *//* * Pentium III FXSR, SSE support * Gareth Hughes <gareth@valinux.com>, May 2000 * * x86-64 port 2000-2002 Andi Kleen */#include <linux/kernel.h>#include <linux/sched.h>#include <linux/mm.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/errno.h>#include <linux/ptrace.h>#include <linux/user.h>#include <linux/security.h>#include <linux/audit.h>#include <linux/seccomp.h>#include <linux/signal.h>#include <asm/uaccess.h>#include <asm/pgtable.h>#include <asm/system.h>#include <asm/processor.h>#include <asm/i387.h>#include <asm/debugreg.h>#include <asm/ldt.h>#include <asm/desc.h>#include <asm/proto.h>#include <asm/ia32.h>/* * does not yet catch signals sent when the child dies. * in exit.c or in signal.c. *//* determines which flags the user has access to. *//* 1 = access 0 = no access */#define FLAG_MASK 0x44dd5UL/* set's the trap flag. */#define TRAP_FLAG 0x100UL/* * eflags and offset of eflags on child stack.. */#define EFLAGS offsetof(struct pt_regs, eflags)#define EFL_OFFSET ((int)(EFLAGS-sizeof(struct pt_regs)))/* * this routine will get a word off of the processes privileged stack. * the offset is how far from the base addr as stored in the TSS. * this routine assumes that all the privileged stacks are in our * data space. */ static inline unsigned long get_stack_long(struct task_struct *task, int offset){ unsigned char *stack; stack = (unsigned char *)task->thread.rsp0; stack += offset; return (*((unsigned long *)stack));}static inline struct pt_regs *get_child_regs(struct task_struct *task){ struct pt_regs *regs = (void *)task->thread.rsp0; return regs - 1;}/* * this routine will put a word on the processes privileged stack. * the offset is how far from the base addr as stored in the TSS. * this routine assumes that all the privileged stacks are in our * data space. */static inline long put_stack_long(struct task_struct *task, int offset, unsigned long data){ unsigned char * stack; stack = (unsigned char *) task->thread.rsp0; stack += offset; *(unsigned long *) stack = data; return 0;}#define LDT_SEGMENT 4unsigned long convert_rip_to_linear(struct task_struct *child, struct pt_regs *regs){ unsigned long addr, seg; addr = regs->rip; seg = regs->cs & 0xffff; /* * We'll assume that the code segments in the GDT * are all zero-based. That is largely true: the * TLS segments are used for data, and the PNPBIOS * and APM bios ones we just ignore here. */ if (seg & LDT_SEGMENT) { u32 *desc; unsigned long base; down(&child->mm->context.sem); desc = child->mm->context.ldt + (seg & ~7); base = (desc[0] >> 16) | ((desc[1] & 0xff) << 16) | (desc[1] & 0xff000000); /* 16-bit code segment? */ if (!((desc[1] >> 22) & 1)) addr &= 0xffff; addr += base; up(&child->mm->context.sem); } return addr;}static int is_at_popf(struct task_struct *child, struct pt_regs *regs){ int i, copied; unsigned char opcode[16]; unsigned long addr = convert_rip_to_linear(child, regs); copied = access_process_vm(child, addr, opcode, sizeof(opcode), 0); for (i = 0; i < copied; i++) { switch (opcode[i]) { /* popf */ case 0x9d: return 1; /* CHECKME: 64 65 */ /* opcode and address size prefixes */ case 0x66: case 0x67: continue; /* irrelevant prefixes (segment overrides and repeats) */ case 0x26: case 0x2e: case 0x36: case 0x3e: case 0x64: case 0x65: case 0xf0: case 0xf2: case 0xf3: continue; /* REX prefixes */ case 0x40 ... 0x4f: continue; /* CHECKME: f0, f2, f3 */ /* * pushf: NOTE! We should probably not let * the user see the TF bit being set. But * it's more pain than it's worth to avoid * it, and a debugger could emulate this * all in user space if it _really_ cares. */ case 0x9c: default: return 0; } } return 0;}static void set_singlestep(struct task_struct *child){ struct pt_regs *regs = get_child_regs(child); /* * Always set TIF_SINGLESTEP - this guarantees that * we single-step system calls etc.. This will also * cause us to set TF when returning to user mode. */ set_tsk_thread_flag(child, TIF_SINGLESTEP); /* * If TF was already set, don't do anything else */ if (regs->eflags & TRAP_FLAG) return; /* Set TF on the kernel stack.. */ regs->eflags |= TRAP_FLAG; /* * ..but if TF is changed by the instruction we will trace, * don't mark it as being "us" that set it, so that we * won't clear it by hand later. * * AK: this is not enough, LAHF and IRET can change TF in user space too. */ if (is_at_popf(child, regs)) return; child->ptrace |= PT_DTRACE;}static void clear_singlestep(struct task_struct *child){ /* Always clear TIF_SINGLESTEP... */ clear_tsk_thread_flag(child, TIF_SINGLESTEP); /* But touch TF only if it was set by us.. */ if (child->ptrace & PT_DTRACE) { struct pt_regs *regs = get_child_regs(child); regs->eflags &= ~TRAP_FLAG; child->ptrace &= ~PT_DTRACE; }}/* * Called by kernel/ptrace.c when detaching.. * * Make sure the single step bit is not set. */void ptrace_disable(struct task_struct *child){ clear_singlestep(child);}static int putreg(struct task_struct *child, unsigned long regno, unsigned long value){ unsigned long tmp; /* Some code in the 64bit emulation may not be 64bit clean. Don't take any chances. */ if (test_tsk_thread_flag(child, TIF_IA32)) value &= 0xffffffff; switch (regno) { case offsetof(struct user_regs_struct,fs): if (value && (value & 3) != 3) return -EIO; child->thread.fsindex = value & 0xffff; return 0; case offsetof(struct user_regs_struct,gs): if (value && (value & 3) != 3) return -EIO; child->thread.gsindex = value & 0xffff; return 0; case offsetof(struct user_regs_struct,ds): if (value && (value & 3) != 3) return -EIO; child->thread.ds = value & 0xffff; return 0; case offsetof(struct user_regs_struct,es): if (value && (value & 3) != 3) return -EIO; child->thread.es = value & 0xffff; return 0; case offsetof(struct user_regs_struct,ss): if ((value & 3) != 3) return -EIO; value &= 0xffff; return 0; case offsetof(struct user_regs_struct,fs_base): if (value >= TASK_SIZE_OF(child)) return -EIO; child->thread.fs = value; return 0; case offsetof(struct user_regs_struct,gs_base): if (value >= TASK_SIZE_OF(child)) return -EIO; child->thread.gs = value; return 0; case offsetof(struct user_regs_struct, eflags): value &= FLAG_MASK; tmp = get_stack_long(child, EFL_OFFSET); tmp &= ~FLAG_MASK; value |= tmp; break; case offsetof(struct user_regs_struct,cs): if ((value & 3) != 3) return -EIO; value &= 0xffff; break; case offsetof(struct user_regs_struct, rip): /* Check if the new RIP address is canonical */ if (value >= TASK_SIZE_OF(child)) return -EIO; break; } put_stack_long(child, regno - sizeof(struct pt_regs), value); return 0;}static unsigned long getreg(struct task_struct *child, unsigned long regno){ unsigned long val; switch (regno) { case offsetof(struct user_regs_struct, fs): return child->thread.fsindex; case offsetof(struct user_regs_struct, gs): return child->thread.gsindex; case offsetof(struct user_regs_struct, ds): return child->thread.ds; case offsetof(struct user_regs_struct, es): return child->thread.es; case offsetof(struct user_regs_struct, fs_base): return child->thread.fs; case offsetof(struct user_regs_struct, gs_base): return child->thread.gs; default: regno = regno - sizeof(struct pt_regs); val = get_stack_long(child, regno); if (test_tsk_thread_flag(child, TIF_IA32)) val &= 0xffffffff; return val; }}long arch_ptrace(struct task_struct *child, long request, long addr, long data){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -