⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 kprobes_64.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	return 1;no_kprobe:	preempt_enable_no_resched();	return ret;}/* * For function-return probes, init_kprobes() establishes a probepoint * here. When a retprobed function returns, this probe is hit and * trampoline_probe_handler() runs, calling the kretprobe's handler. */ void kretprobe_trampoline_holder(void) { 	asm volatile (  ".global kretprobe_trampoline\n" 			"kretprobe_trampoline: \n" 			"nop\n"); }/* * Called when we hit the probe point at kretprobe_trampoline */int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs){	struct kretprobe_instance *ri = NULL;	struct hlist_head *head, empty_rp;	struct hlist_node *node, *tmp;	unsigned long flags, orig_ret_address = 0;	unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline;	INIT_HLIST_HEAD(&empty_rp);	spin_lock_irqsave(&kretprobe_lock, flags);	head = kretprobe_inst_table_head(current);	/*	 * It is possible to have multiple instances associated with a given	 * task either because an multiple functions in the call path	 * have a return probe installed on them, and/or more then one return	 * return probe was registered for a target function.	 *	 * We can handle this because:	 *     - instances are always inserted at the head of the list	 *     - when multiple return probes are registered for the same	 *       function, the first instance's ret_addr will point to the	 *       real return address, and all the rest will point to	 *       kretprobe_trampoline	 */	hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {		if (ri->task != current)			/* another task is sharing our hash bucket */			continue;		if (ri->rp && ri->rp->handler)			ri->rp->handler(ri, regs);		orig_ret_address = (unsigned long)ri->ret_addr;		recycle_rp_inst(ri, &empty_rp);		if (orig_ret_address != trampoline_address)			/*			 * This is the real return address. Any other			 * instances associated with this task are for			 * other calls deeper on the call stack			 */			break;	}	kretprobe_assert(ri, orig_ret_address, trampoline_address);	regs->rip = orig_ret_address;	reset_current_kprobe();	spin_unlock_irqrestore(&kretprobe_lock, flags);	preempt_enable_no_resched();	hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {		hlist_del(&ri->hlist);		kfree(ri);	}	/*	 * By returning a non-zero value, we are telling	 * kprobe_handler() that we don't want the post_handler	 * to run (and have re-enabled preemption)	 */	return 1;}/* * Called after single-stepping.  p->addr is the address of the * instruction whose first byte has been replaced by the "int 3" * instruction.  To avoid the SMP problems that can occur when we * temporarily put back the original opcode to single-step, we * single-stepped a copy of the instruction.  The address of this * copy is p->ainsn.insn. * * This function prepares to return from the post-single-step * interrupt.  We have to fix up the stack as follows: * * 0) Except in the case of absolute or indirect jump or call instructions, * the new rip is relative to the copied instruction.  We need to make * it relative to the original instruction. * * 1) If the single-stepped instruction was pushfl, then the TF and IF * flags are set in the just-pushed eflags, and may need to be cleared. * * 2) If the single-stepped instruction was a call, the return address * that is atop the stack is the address following the copied instruction. * We need to make it the address following the original instruction. */static void __kprobes resume_execution(struct kprobe *p,		struct pt_regs *regs, struct kprobe_ctlblk *kcb){	unsigned long *tos = (unsigned long *)regs->rsp;	unsigned long copy_rip = (unsigned long)p->ainsn.insn;	unsigned long orig_rip = (unsigned long)p->addr;	kprobe_opcode_t *insn = p->ainsn.insn;	/*skip the REX prefix*/	if (*insn >= 0x40 && *insn <= 0x4f)		insn++;	regs->eflags &= ~TF_MASK;	switch (*insn) {	case 0x9c:	/* pushfl */		*tos &= ~(TF_MASK | IF_MASK);		*tos |= kcb->kprobe_old_rflags;		break;	case 0xc2:	/* iret/ret/lret */	case 0xc3:	case 0xca:	case 0xcb:	case 0xcf:	case 0xea:	/* jmp absolute -- ip is correct */		/* ip is already adjusted, no more changes required */		goto no_change;	case 0xe8:	/* call relative - Fix return addr */		*tos = orig_rip + (*tos - copy_rip);		break;	case 0xff:		if ((insn[1] & 0x30) == 0x10) {			/* call absolute, indirect */			/* Fix return addr; ip is correct. */			*tos = orig_rip + (*tos - copy_rip);			goto no_change;		} else if (((insn[1] & 0x31) == 0x20) ||	/* jmp near, absolute indirect */			   ((insn[1] & 0x31) == 0x21)) {	/* jmp far, absolute indirect */			/* ip is correct. */			goto no_change;		}	default:		break;	}	regs->rip = orig_rip + (regs->rip - copy_rip);no_change:	return;}int __kprobes post_kprobe_handler(struct pt_regs *regs){	struct kprobe *cur = kprobe_running();	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();	if (!cur)		return 0;	if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) {		kcb->kprobe_status = KPROBE_HIT_SSDONE;		cur->post_handler(cur, regs, 0);	}	resume_execution(cur, regs, kcb);	regs->eflags |= kcb->kprobe_saved_rflags;	trace_hardirqs_fixup_flags(regs->eflags);	/* Restore the original saved kprobes variables and continue. */	if (kcb->kprobe_status == KPROBE_REENTER) {		restore_previous_kprobe(kcb);		goto out;	}	reset_current_kprobe();out:	preempt_enable_no_resched();	/*	 * if somebody else is singlestepping across a probe point, eflags	 * will have TF set, in which case, continue the remaining processing	 * of do_debug, as if this is not a probe hit.	 */	if (regs->eflags & TF_MASK)		return 0;	return 1;}int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr){	struct kprobe *cur = kprobe_running();	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();	const struct exception_table_entry *fixup;	switch(kcb->kprobe_status) {	case KPROBE_HIT_SS:	case KPROBE_REENTER:		/*		 * We are here because the instruction being single		 * stepped caused a page fault. We reset the current		 * kprobe and the rip points back to the probe address		 * and allow the page fault handler to continue as a		 * normal page fault.		 */		regs->rip = (unsigned long)cur->addr;		regs->eflags |= kcb->kprobe_old_rflags;		if (kcb->kprobe_status == KPROBE_REENTER)			restore_previous_kprobe(kcb);		else			reset_current_kprobe();		preempt_enable_no_resched();		break;	case KPROBE_HIT_ACTIVE:	case KPROBE_HIT_SSDONE:		/*		 * We increment the nmissed count for accounting,		 * we can also use npre/npostfault count for accouting		 * these specific fault cases.		 */		kprobes_inc_nmissed_count(cur);		/*		 * We come here because instructions in the pre/post		 * handler caused the page_fault, this could happen		 * if handler tries to access user space by		 * copy_from_user(), get_user() etc. Let the		 * user-specified handler try to fix it first.		 */		if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))			return 1;		/*		 * In case the user-specified fault handler returned		 * zero, try to fix up.		 */		fixup = search_exception_tables(regs->rip);		if (fixup) {			regs->rip = fixup->fixup;			return 1;		}		/*		 * fixup() could not handle it,		 * Let do_page_fault() fix it.		 */		break;	default:		break;	}	return 0;}/* * Wrapper routine for handling exceptions. */int __kprobes kprobe_exceptions_notify(struct notifier_block *self,				       unsigned long val, void *data){	struct die_args *args = (struct die_args *)data;	int ret = NOTIFY_DONE;	if (args->regs && user_mode(args->regs))		return ret;	switch (val) {	case DIE_INT3:		if (kprobe_handler(args->regs))			ret = NOTIFY_STOP;		break;	case DIE_DEBUG:		if (post_kprobe_handler(args->regs))			ret = NOTIFY_STOP;		break;	case DIE_GPF:		/* kprobe_running() needs smp_processor_id() */		preempt_disable();		if (kprobe_running() &&		    kprobe_fault_handler(args->regs, args->trapnr))			ret = NOTIFY_STOP;		preempt_enable();		break;	default:		break;	}	return ret;}int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs){	struct jprobe *jp = container_of(p, struct jprobe, kp);	unsigned long addr;	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();	kcb->jprobe_saved_regs = *regs;	kcb->jprobe_saved_rsp = (long *) regs->rsp;	addr = (unsigned long)(kcb->jprobe_saved_rsp);	/*	 * As Linus pointed out, gcc assumes that the callee	 * owns the argument space and could overwrite it, e.g.	 * tailcall optimization. So, to be absolutely safe	 * we also save and restore enough stack bytes to cover	 * the argument area.	 */	memcpy(kcb->jprobes_stack, (kprobe_opcode_t *)addr,			MIN_STACK_SIZE(addr));	regs->eflags &= ~IF_MASK;	trace_hardirqs_off();	regs->rip = (unsigned long)(jp->entry);	return 1;}void __kprobes jprobe_return(void){	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();	asm volatile ("       xchg   %%rbx,%%rsp     \n"		      "       int3			\n"		      "       .globl jprobe_return_end	\n"		      "       jprobe_return_end:	\n"		      "       nop			\n"::"b"		      (kcb->jprobe_saved_rsp):"memory");}int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs){	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();	u8 *addr = (u8 *) (regs->rip - 1);	unsigned long stack_addr = (unsigned long)(kcb->jprobe_saved_rsp);	struct jprobe *jp = container_of(p, struct jprobe, kp);	if ((addr > (u8 *) jprobe_return) && (addr < (u8 *) jprobe_return_end)) {		if ((unsigned long *)regs->rsp != kcb->jprobe_saved_rsp) {			struct pt_regs *saved_regs = &kcb->jprobe_saved_regs;			printk("current rsp %p does not match saved rsp %p\n",			       (long *)regs->rsp, kcb->jprobe_saved_rsp);			printk("Saved registers for jprobe %p\n", jp);			show_registers(saved_regs);			printk("Current registers\n");			show_registers(regs);			BUG();		}		*regs = kcb->jprobe_saved_regs;		memcpy((kprobe_opcode_t *) stack_addr, kcb->jprobes_stack,		       MIN_STACK_SIZE(stack_addr));		preempt_enable_no_resched();		return 1;	}	return 0;}static struct kprobe trampoline_p = {	.addr = (kprobe_opcode_t *) &kretprobe_trampoline,	.pre_handler = trampoline_probe_handler};int __init arch_init_kprobes(void){	return register_kprobe(&trampoline_p);}int __kprobes arch_trampoline_kprobe(struct kprobe *p){	if (p->addr == (kprobe_opcode_t *)&kretprobe_trampoline)		return 1;	return 0;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -