} "" " kretprobe_assert(ri, orig ret address,trampoline_address); kretprobe_hash_unlock(current, &flags); hlist for each entry safe(ri, node, tmp, &empty rp, hlist) { hlist_del(&ri->hlist); kfree(ri); return (void *)orig_ret_address; } 图 13. trampoline_handler()代码。 5.修改只读内核数据段 如果你对使用trampoline来劫持arch_prepare_kretprobe()感兴趣,那么需要记住现 在的intelCPU有了WRITE_PROTECT位来防止对只读段的修改。因此,任何时候如果你想修 改^0也^段中的内核数据结构,你需要使用如下的代码来实现。下面的几种数据结构通常 在内核的1611段: 1. Void **sys_call_table 2. Const struct fi1e_operat ions <fs_fops_name> 3. Const struct vm_ops <vma_vmops_name> 4. 内核函数 定义为” const”的数据结构将被放在^03&七&段中。该段在16义1段的末尾处而内核代 码通常在.text段。试图对这些区域进行写操作将产生错误。 有些人修改只读段相对应的页表项数据,但是下面的代码更简单: static void disable_wp(void) { unsigned int crO_value; asm volatile ("movl %%cr0, %0” : "=r" (crO_value)); crO_value &=?(1 ? 16); asm volatile (’’movl %0,%%crO":: \ "r" (crO_value)); j static void enable_wp(void) { unsigned int crO_value; asm volatile ("movl %%cr0, %0” : "=r" (crO_value)); crO_value |= (1 ? 16); asm volatile (’’movl %0,%%crO" :: "r" (crO_value)); } — 图14.修改只读段的代码。 因此如果你想修改text段(sys^^_ife^p<中的某个内核函数指针:首先调用 disable_wp();然后调用 svs call ^afc^kf M> writel = (void *)n_sys_write;最后再 调用 enable_wp ()。 6. Kretprobe对于黑客的用处^^ 如匸0&68对于内核补丁_现的主要限制在于,我们无法修改匕61口『(^^8中的返回值。我 们可以实现一个于吐的口扮_^实现。这样我们就可以利用kprobes来修改代码并可以 修改返回值了。对于文件隐藏的例子,我们就可以对于衍11(1化64直接返回0。 如果读者能够深入研究下/usr/src/linux/kernel/kprobes.c中kretprobbes的实现, 就会发现我们^^^个更灵活的kretprobe。下面本文将给出一个框架: int register_rpe(struct kretprobe *rp) { ...<code> ... rp->kp.pre_handler = pre_handler_rpe; ...<code> ... } static int pre_handler_rpe(struct kprobe *p, struct pt_regs *regs) { arch_prepare_rpe(regs); } _ void arch_prepare_rpe(struct pt_regs *regs) { unsigned long *ret = stack_addr(regs); ret_addr = (kprobe_opcode_t *) *sara; *ret = (unsigned long) &rpe_trampoline; } _ 图15. —个更灵活的kretprobe结构。 Rpe_trampoline既可以是一段汇编也可以是一个真正的函数。 总结 本文介绍了 kprobe接口,其主要用于内核调试并可被用于修改内核从而实现攻击。本 文分析了 kprobes的作用、弱点并提供了几个jprobe和kretprobe的实例。另外,本文还提出了一个更灵活的kretprobe实现。 最后,如果仅用kprobes来实现内核rootkit并在sysfs中隐藏自己,rootkit检测程 序可以很容易地检测哪些内核函数被hook 了。 |