• 用kprobes实现内核反射机制


    kprobes实现内核反射机制

     

    转载时请注明出处和作者联系方式:http://blog.csdn.net/absurd

    作者联系方式:Li XianJing <xianjimli at hotmail dot com>

    更新时间:2007-5-29

     

    前几天在设计事件管理器时,我就在考虑磁盘满的问题,磁盘满是一个典型的系统事件,没有什么好说的,问题是应该何时何地触发它呢?如果由应用程序在操作文件时触发,那将有很多地方需要修改,这不是我们期望的。如果能在一个地方统一处理,那就省事多了,说到统一处理,我们自然会想到修改glibc或者内核的代码。

     

    但修改glibc或者内核的代码也非我所愿,对于这些标准的软件包,除非是有BUG,否则我是不情愿去修改它们的,特别是加入这种专用功能,因为那让以后的维护比较麻烦,升级不方便。最好的办法就是不用修改代码,而动态的改变某些函数的行为。

     

    gcc有一个选项-finstrument-functions,它可以在函数调用前后注入指定代码,利用这些注入的代码来改变函数的行为,这是一个非常酷的特性,常用作高级的调试技巧。但我可不想给glibc中每一个函数调用都注入代码,那可能会对性能造成严重的影响。

     

    ELF文件都是由/lib/ld-linux.so.2加载的,ld-linux提供了一种PRELOAD机制,它用于优先加载指定的共享库,可以通过LD_PRELOAD环境变量或/etc/preload.conf配置文件来指定共享库。这倒是一个不错的方法,在PRELOAD的共享库去实现部分文件操作函数,这样就可以方便的为这些函数增加新功能了。

     

    今天无意中发现了一种更酷的方法,原来内核提供了一种称为kprobes的功能,利用它我们可以很容易实现反射机制,动态的修改某些函数的行为。下面是一个从linux-2.6.21/Documentation/kprobes.txt中抄出来的例子:

    kretprobe-example.c

    #include <linux/kernel.h>

    #include <linux/module.h>

    #include <linux/kprobes.h>

     

    static const char *probed_func = "sys_open";

     

    /* Return-probe handler: If the probed function fails, log the return value. */

    static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)

    {

        int retval = regs_return_value(regs);

        if (retval < 0) {

            printk("%s returns %d/n", probed_func, retval);

        }

        return 0;

    }

     

    static struct kretprobe my_kretprobe = {

        .handler = ret_handler,

        /* Probe up to 20 instances concurrently. */

        .maxactive = 20

    };

     

    static int __init kretprobe_init(void)

    {

        int ret;

        my_kretprobe.kp.symbol_name = (char *)probed_func;

     

        if ((ret = register_kretprobe(&my_kretprobe)) < 0) {

            printk("register_kretprobe failed, returned %d/n", ret);

            return -1;

        }

        printk("Planted return probe at %p/n", my_kretprobe.kp.addr);

        return 0;

    }

     

    static void __exit kretprobe_exit(void)

    {

        unregister_kretprobe(&my_kretprobe);

        printk("kretprobe unregistered/n");

        /* nmissed > 0 suggests that maxactive was set too low. */

        printk("Missed probing %d instances of %s/n",

            my_kretprobe.nmissed, probed_func);

    }

     

    module_init(kretprobe_init)

    module_exit(kretprobe_exit)

    MODULE_LICENSE("GPL");

     

    Makefile

    obj-m := kretprobe-example.o

    KDIR := /lib/modules/$(shell uname -r)/build

    PWD := $(shell pwd)

     

    default: 

        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

    clean:

        rm -f *.mod.c *.ko *.o

     

    Make之后用inmod插入kretprobe-example.ko:

    make;insmod kretprobe-example.ko

     

    再用vim打开/var/log/messages,可以看到诸如:

    May 29 20:35:49 lixj kernel: sys_open returns –2

    之类的信息。

     

    不过,遗憾的是它只支持下面几个平台,没有ARM版本的实现,让我白开心了一回。

    - i386

    - x86_64 (AMD-64, EM64T)

    - ppc64

    - ia64 (Does not support probes on instruction slot1.)

    -          sparc64 (Return probes not yet implemented.)

     

    更详细的内容可以阅读linux-2.6.21/Documentation/kprobes.txt

     

    参考资源:

    http://blog.linux.org.tw/~jserv/archives/2007_01.html

    http://research.microsoft.com/sn/detours/

    linux-2.6.21/Documentation/kprobes.txt

     

    ~~end~~

     

     

     

     
  • 相关阅读:
    C#删除程序自身【总结】
    X86(32位)与X64(64位)有什么区别,如何选择对应的操作系统和应用程序?
    【转】关于C#接口和抽象类的一些说明
    C# 的可空合并运算符(??)到底是怎样的宝宝?
    第三章 “我要点爆”微信小程序云开发之点爆方式页面和爆炸之音页面制作
    微信小程序云开发之云函数的创建与环境配置
    第五章 “我要点爆”微信小程序云开发实例之从云端获取数据制作首页
    第一章 “我要点爆”微信小程序云开发之项目建立与我的页面功能实现
    第四章 “我要点爆”微信小程序云开发之疯狂点击与糖果点爆页面制作
    Git的使用方法与GitHub项目托管方法
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6167721.html
Copyright © 2020-2023  润新知