使用kprobes查看内核内部信息

17 篇文章 0 订阅
17 篇文章 0 订阅

http://blog.chinaunix.net/uid-9543173-id-3571748.html


使用kprobes查看内核内部信息

wushuan10141 2013-04-08 16:50:43

  一、首先内核必须支持kprobes、jprobes:

#makemenuconfig

General setup  --->

[*]Kprobes

使内核支持kprobes。

二、kprobes的使用方法:
1、分配一个kprobe结构体供kprobes运行时使用。
2、在kprobe结构体的addr成员中设置要插入侦测器的函数地址,可以通过查看system.map查找,或者通过proc文件系统查找

# cat/proc/kallsyms | grep "do_execve"

c00bbfb8T do_execve
3、如果不想设置addr成员,可以设置symbol_name成员为"do_execve"(想要侦测的函数),需要内核版本在2.6.19以上才支持。

4、在kprob结构的pre_handler成员设置侦测函数exec_pre_handler。

5、调用register_kprobe函数注册侦测器函数。


以下是一个用kprobes监测do_execve()函数的例子,在每次调用do_execve()函数之前,就会打印当前的进程pid、jiffies值等全局变量信息。#include <linux/module.h>

  1. #include <linux/kprobes.h>
  2. #include <linux/kallsyms.h>

  3. struct kprobe exec_kp;

  4. static int exec_pre_handler(struct kprobe *p, struct pt_regs *regs)
  5. {
  6.     printk("Enter into %s\n", __func__);
  7.     printk("pt_regs:%p, pid:%d, jiffies:%ld\n", regs, current->tgid, jiffies);
  8.     
  9.     return 0;
  10. }

  11. static __init int kprobes_exec_init(void)
  12. {    

  13.     /*设置要检测的函数do_execve()的地址*/
  14.     //exec_kp.addr = (kprobe_opcode_t *)0xc00bbfb8;

  15.     /*如果不想直接给addr成员设置地址,也可以使用符号名*/
  16.     exec_kp.symbol_name = "do_execve";

  17.     exec_kp.pre_handler = exec_pre_handler;

  18.     /*注册kprobes*/
  19.     register_kprobe(&exec_kp);
  20.     
  21.     return 0;
  22. }



  23. static __exit void kprobes_exec_cleanup(void)
  24. {
  25.     /*撤销kprobes注册*/
  26.     unregister_kprobe(&exec_kp);
  27. }

  28. module_init(kprobes_exec_init);
  29. module_exit(kprobes_exec_cleanup);
  30. MODULE_LICENSE("GPL");


编译并insmod上述ko,那么在do_execve()函数执行之前就会先执行kprobes的pre_handler所指向的exec_pre_handler,打印内核的当前进程pid、jiffies等全局变量的值。由于do_exec函数用于生成进程,因此每次执行ls等命令都会显示一次:

pt_regs:c52ebf08,pid:110, jiffies:155286

pt_regs:c5313f08,pid:113, jiffies:159137

pt_regs:c52ebf08,pid:112, jiffies:159137

利用kprobes也可以查看内核函数内部任意位置的信息,此时需要把内核函数反汇编,确定想要查看位置相对于函数起始地址的偏移量,在初始化kprobes时,设置kprobe结构体的offset成员,就可以查看内核内部函数任意位置的信息。

三、jprobes的使用方法

用kprobes可以方便的查看内核内部任意位置的信息,相反,jprobes则是特别为函数开头的侦测准备的,通过jprobes,可以更容易的获取传递给函数的参数。使用jprobes与使用kprobes大体上一样,主要有以下区别:

1、分配给jprobes的结构体是jprobe结构体,并将指针传递给register_jprobe。jprobe结构的成员如下所示,只包括kprobe和entry两者。


  1. struct jprobe {
  2.     struct kprobe kp;
  3.     void *entry;    /* probe handling code to jump to */
  4. };


struct kprobe kp成员设置的是要侦测函数的符号或地址,entry中为通过JPROBE_ENTRY()宏处理过的侦测器处理程序。

2、侦测器处理程序的参数应当与需要侦测的函数的参数相同,这样可以方便的打印被侦测函数的形参。使用kprobes时,必须通过寄存器或者栈才能计算出参数的值。而计算方法还依赖于cpu架构,如果使用jprobes,则无须了解架构的详细知识,也能查看参数的值。
3、侦测器处理函数的尾部必须加上jprobe_return();


以下是使用jprobes打印do_execve函数的参数的例子,每次调用do_execve之前,都会打印相应的参数:

  1. #include <linux/module.h>
  2. #include <linux/kprobes.h>
  3. #include <linux/kallsyms.h>

  4. struct jprobe exec_jp;


  5. int jp_do_execve(const char * filename,
  6.     const char __user *const __user *argv,
  7.     const char __user *const __user *envp,
  8.     struct pt_regs * regs)
  9. {
  10.     int cnt = 0;

  11.     printk("filename = %s\n", filename);

  12.     for(; *argv != NULL;argv++,cnt++)
  13.         printk("argv[%d] = %s\n", cnt, *argv);

  14.     jprobe_return();
  15.     return 0;
  16. }


  17. static __init int jprobes_exec_init(void)
  18. {    
  19.     exec_jp.kp.symbol_name = "do_execve";

  20.     exec_jp.entry = JPROBE_ENTRY(jp_do_execve);

  21.     /*注册jprobes*/
  22.     register_jprobe(&exec_jp);
  23.     
  24.     return 0;
  25. }



  26. static __exit void jprobes_exec_cleanup(void)
  27. {
  28.     /*撤销jprobes注册*/
  29.     unregister_jprobe(&exec_jp);
  30. }

  31. module_init(jprobes_exec_init);
  32. module_exit(jprobes_exec_cleanup);
  33. MODULE_LICENSE("GPL");

每次执行ls时都会调用do_execve函数,具体的打印如下:

# ls

filename = /bin/ls

argv[0] = ls


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值