zoukankan      html  css  js  c++  java
  • Linux内核调试方法总结之Jprobes

    Jprobes

    【用途】

    类似于Kprobes和Return Probes,区别在于,Kprobes可以在任意指令处插入探针,Jprobes只在函数入口插入探针,而Return Probes则是在函数返回时插入探针。

    【接口说明】【参考samples/kprobes/jprobe_example.c】

    #include <linux/kprobes.h>

    int register_jprobe(struct jprobe *jp);

    void unregister_jprobe(struct jprobe *jp);

    int enable_jprobe(struct jprobe *jp);

    int disable_jprobe(struct jprobe *jp);

    【番外篇】

    我这篇也就写点儿使用Kprobe和Jprobe的注意事项:

    用Kprobe和retprobe注册的handler运行于原子(atomic)上下文,因为它们都是运行在int3的异常处理函数或调试(debug)中断的处理函数中,并且两者都是在关闭当前cpu中断的前提下执行的。相比之下,Jprobe就灵活得多了,用它注册的handler的执行环境和原始的执行环境并没有两样,堆栈环境和cpu寄存器都是相同的。

    因为Jprobe执行用户注册的handler之前有保存现场(主要是int3之前的CPU寄存器的值)的工作,所以Jprobe的handler是没有办法破坏寄存器的值的,但是可以改变堆栈中的变量的值,Kprobe将没有这些限制,它对系统具有完全的访问权限,另一方面,Kprobe也比Jprobe危险得多,如果只是简单的打印一些系统信息,推荐用Jprobe,不要冒险!

    Jprobe机制和用户空间的setjmp和longjmp的实现比较类似,事实上,它的内核代码的注释部分也是如此作类比的,所以用register_jprobe注册的代码段,最后必须要用jprobe_return进行返回,当然,如果你想让它一去不复返除外。这点就象用kprobe注册的handler必须是可以返回的函数一样。目前,能想到的就这么多了。

    BTW: 看kprobe的内核代码的时侯,发现有的地方还是有些缺陷的,比如说某些地方用preempt_disable()关闭了抢占,但是当函数出错返回的时侯,并没有打开抢占。不知道最新的内核有没有修复这些细节,懒得去看了,应该没有哪个正式的发型版会打开这个选项的。所以文章标题中所提到的注入的可能性也就比较小了,本来这个东西出现的背景就是为了方便内核开发者的调试工作!

    【实例】

    /*jprobe_test.c */

    #include <linux/kernel.h>

    #include <linux/module.h>

    #include <linux/fs.h>

    #include <linux/uio.h>

    #include <linux/kprobes.h>

    #include <linux/kallsyms.h>

    /*

    * Jumper probe for do_fork.

    * Mirror principle enables access to arguments of the probed routine

    * from the probe handler.

    */

    /* Proxy routine having the same arguments as actual do_fork() routine */

    long jdo_fork(

      unsigned long clone_flags,

      unsigned long stack_start,

          struct pt_regs *regs,

      unsigned long stack_size,

      int __user * parent_tidptr,

      int __user * child_tidptr) {

      printk("jprobe: clone_flags=0x%lx, stack_size=0x%lx, regs=0x%p ",       clone_flags, stack_size, regs);

      /* Always end with a call to jprobe_return(). */

      jprobe_return();

      /*NOTREACHED*/

      return 0;

    }

    static struct jprobe my_jprobe = {

      .entry = (kprobe_opcode_t *) jdo_fork

    };

    int init_module(void) {

      int ret;

      my_jprobe.kp.addr = (kprobe_opcode_t *) kallsyms_lookup_name("do_fork");

      if (!my_jprobe.kp.addr) {

        printk("Couldn't find %s to plant jprobe ", "do_fork");

        return -1;

      }

      if ((ret = register_jprobe(&my_jprobe)) <0) {

        printk("register_jprobe failed, returned %d ", ret);

        return -1;

      }

      printk("Planted jprobe at %p, handler addr %p ",       my_jprobe.kp.addr, my_jprobe.entry);

      return 0;

    }

    void cleanup_module(void) {   unregister_jprobe(&my_jprobe);   printk("jprobe unregistered "); }

    MODULE_LICENSE("GPL");

  • 相关阅读:
    SWTDesigner注册器
    C# 创建、部署和调用WebService的简单示例
    (android实战)应用在线版本更新
    jQuery获取Select选择的Text和 Value(转)
    Android 判断sd卡和sim卡是否可用
    Android开发中如何固定屏幕显示!
    入侵网站简单方法总结
    【Android】防止UI界面被输入法遮挡(画面随输入法自适应)
    关于字符编码的问题
    最好用的mysql密码忘记的解决方法
  • 原文地址:https://www.cnblogs.com/justin-y-lin/p/5424595.html
Copyright © 2011-2022 走看看