本文的侧重点是对kprobe背后的原理做一个简单的梳理,感兴趣的同学可以按照这个线索自己去看代码(我强烈建议仔细去看代码,因为里面可以挖掘到很多有价值的东西,特别是对那些在Linux系统下从事内核模块开发的同学)。
kprobe通过软中断int 3指令替换要窥探函数的开始处某条指令,达到插入自己的窥探函数的目的(这其实已经是调试中的软件断点功能的雏形了)。内核源码中关于这个int 3的调用链发生的时间先后顺序为:
1. arch/x86/kernel/traps.c -- __init early_trap_init() --> set_system_intr_gate_ist(3, &int3, DEBUG_STACK);
所以int 3指令执行时,要调用函数int3,后者的实现在:
2. arch/x86/kernel/entry_32.S:
- ENTRY(int3)
- RING0_INT_FRAME
- pushl_cfi $-1 # mark this as an int
- SAVE_ALL
- TRACE_IRQS_OFF
- xorl %edx,%edx # zero error code
- movl %esp,%eax # pt_regs pointer
- call do_int3
- jmp ret_from_exception
- CFI_ENDPROC
- END(int3)
- dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code)
- {
- ...
- #ifdef CONFIG_KPROBES
- if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP)
- == NOTIFY_STOP)
- return;
- #else
- if (notify_die(DIE_TRAP, "int3", regs, error_code, 3, SIGTRAP)
- == NOTIFY_STOP)
- return;
- #endif
- preempt_conditional_sti(regs);
- do_trap(3, SIGTRAP, "int3", regs, error_code, NULL);
- preempt_conditional_cli(regs);
- }
5. kprobe_exceptions_nb.notifier_call = kprobe_exceptions_notify, 所以kprobe_exceptions_notify被调用
6. kprobe_exceptions_notify() --> kprobe_handler() --> (struct kprobe *p)->pre_handler.
如果把中的init_kprobe_sample函数稍加改写,就可以验证kprobe的背后原理:
- static __init int init_kprobe_sample(void)
- {
- kprobe_opcode_t *addr;
- kp.symbol_name = "do_execve";
- kp.pre_handler = handler_pre;
- addr = (kprobe_opcode_t *)kallsyms_lookup_name(kp.symbol_name);
- printk("addr = %p, op_code = 0x%lx\n", addr, (unsigned long)*addr);
- register_kprobe(&kp);
- printk("addr = %p, op_code = 0x%lx\n", addr, (unsigned long)*addr);
- printk(KERN_INFO "---------------kprobe for execve--------------\n");
- return 0;
- }
- root@build-server:/home/dennis/debug/kprobe# dmesg -c
- [ 2172.049854] addr = ffffffff8119f5d0, op_code = 0x55
- [ 2172.050678] addr = ffffffff8119f5d0, op_code = 0xcc
- root@build-server:/home/dennis/Linux/linux-3.2.9# objdump -d vmlinux | grep ffffffff8119f5d0
- ffffffff810580c2: e8 09 75 14 00 callq ffffffff8119f5d0 <do_execve>
- ffffffff8119f5d0 <do_execve>:
- ffffffff8119f5d0: 55 push %rbp