long do_sys_open(int dfd, const char _user *filename, int flags, int mode) { char *tmp = getname(fllename); int fd = PTR_ERR(tmp); if(!IS_ERR(tmp)) { fd = get_unused_fd_flags(flags); if(fd>=0) { struct file *f= do_fllp_open(dfd, tmp, flags, mode, 0); if(IS_ERR(f)) { put_unused_fd(fd); fd = PTR_ERR(f); } else { /* 通知 fsnotify_open() */ fsnotify_open(f->f_path.dentry); /* 将 fd 与/dev/nul 关联 */ fd_install(fd, f); trace_do_sys_open(tmp, flags, mode); putname(tmp); return fd; 使用fd_install()将新的文件描述符与对应的文件(files_struct *)相关联。 void fd_install(unsigned int fd, struct file *file) { struct flles_struct *files = current->files; struct fdtable *fdt; spin_lock(&files->file_lock); fdt = files_fdtable(files); BUG_ON(fdt->fd[fd] !=NULL); rcu_assign_pointer(fdt->fd[fd], file); spin_unlock(&flles->flle_lock); 图 10. Fd_install(M^^o 因为^0的^^已经是个很普通的话题了,因此本文将用一些其他的kpr0be例子。首先 我们来讨论匕6〖口『(^6的实现。我们将看到更多的kprobes限制。 4. Kretprobes 实现 Kretprobe的实现很有趣,因为其很多创新性并且是很优秀的工程代码。摘自 kprobes. txt: 当调用作818161_^^1口『(^6 0时,1^?:(^368在函数的入口点建立了一"i^ kprobeo当该函 数被调用时并且该kprobe被触发了,kprobes保存一份返回地址并且将返回地址替换成我 们的地址。新地址所在的位置是任意的一段代码,例如nop指令。 /usr/src/linux/kerne/kprobes. c 中的 register_kretprobe()f(^$nT^ int _kprobes register_kretprobe(struct kretprobe *rp) { int ret — 0; struct kretprobe_instance *inst; inti; ~ void *addr; ...<codc^*... rp->kp .pre_handler = pre_handler_kretprobe; rp->kp.post_handler = NULL; rp->kp. fault_handler = NULL; rp->kp.break_handler = NULL; ...<codc^5*... 图10.register_kretprobe()代码。 当 kretprobe 被触发时,pre_handler_kretprobe ()将调用 pre_handler_kretprobe (), 该函数将保存原始的返回地址并插入新的返回地址: void _kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) unsigned long *sara = stack_addr(regs); ri->ret_addr = (kprobe_opcode_t *) *sara; *sara = (unsigned long) &kretprobe_trampoline; 图 11. ArchXf probea 0代码。 代码的最后一行是将返回地址设置为新的地址,叫做trampoline。Trampoline是用汇编代码定义的,在x86上的定义如下^: asm volatile ( 'global kretprobetrampolineW’ nkretprobe_trampoline: W’ ’’ subl $16, %spW’ ’’ pushl 0/fsW’ " pushl 0/esW’ " pushl %isW’ ’’ pushl %axW’ " pushl %ebpW’ " pushl %diW’ " pushl %siW’ ’’ pushl %dxW’ ’’ pushl %cxW’ ’’ pushl 0々bxW’ ’’ movl %sp, %axW’ ’’ call tampoline_handler^i?? ’’ movl 56^>esp), %dxW’ ?? movl %dx, 52%esp)h" ’’ movl %eax, 56(%esp^n" ’’ popl 0々bxW’ ’’ popl %cxW’ ’’ popl %dxW’ °/csiW? o/edM" °/ebpW %axW' $24,%e; #endif 图 12. X86 上 trampoline 的定义。 当寄存器状态已经保存到栈上后上述代码将调用trampoline_handler(),该函数将执行 与其关联的处理函数。下面是trampoline_handler() static _used _kprobes void *trampoline_handler(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; DsfIT_HLIST_HEAD(&empty_rp); ~ kretprobe_hash_lock(current, &head, &flags); #ifdef CONFIG_X86_64 regs->cs = KERNEL CS; #else regs->cs = KERNEL CS | get_kemel_rpl(); regs->gs = 0; #endif regs->ip = trampoline_address; regs->orig_ax = ~0UL; hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { if (ri->task != current) continue; if (ri->rp && ri->rp->handler) { —get_cpu_var(current_kprobe) = &ri->rp->kp; get_kprobe_ctlbUc()->kprobe_status = KPROBE_HIT_ACTWE; ri->rp->handler(ri, regs); —get_cpu_var(current_kprobe) = NULL; } "" " orig_ret_address = (unsigned long)ri->ret_addr; recycle_rp_mst(ri, &empty_rp); if (orig_ret_address != trampoline_address) break; |