searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

kprobe使用样例介绍

2023-05-25 08:41:38
19
0

kprobe是一种重要的内核调试技术, 可以在内核运行过程中, 向内核函数中添加回调函数,打印调试信息或修改内核执行逻辑。

本文介绍的是一种较为常见的使用方式, 即通过编写内核模块来使用kprobe.

 

以下是一个demo.  我们定义了一个回调函数set_state_pre, 通过调用kprobe的接口register_kprobe,hook到内核函数tcp_set_state

中. 最终的效果就是,在执行内核函数tcp_set_state之前,会执行我们注册的回调函数set_state_pre. 在回调函数中,我们可以做很多事

情,本例中, 我们是获取了内核的参数sk和state, 并打印四元组和state信息。

 

和普通的内核模块编译一样, 安装好内核kernel-devel包,写好对应的makefile文件,make生成对应ko文件。使用insmod加载对应ko文

件即可以运行

#include <linux/kprobes.h>
#include <net/tcp.h>

#define INET_ADDRSTRLEN                   16
#define INET6_ADDRSTRLEN                  46

static int set_state_pre(struct kprobe *p, struct pt_regs *regs)
{
        /* 从寄存器获取函数参数 */
        struct sock *sk = (struct sock *)regs->di;
        int state = (int)regs->si;

        struct inet_sock *inet = inet_sk(sk);
        u16 dport = ntohs(sk->__sk_common.skc_dport);

        char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN];
        if (sk->sk_family == AF_INET) {
                snprintf(saddr, INET_ADDRSTRLEN, "%pI4", &inet->inet_saddr);
                snprintf(daddr, INET_ADDRSTRLEN, "%pI4", &inet->inet_daddr);
        } else {
                snprintf(saddr, INET6_ADDRSTRLEN, "%pI6", &inet->inet_saddr);
                snprintf(daddr, INET6_ADDRSTRLEN, "%pI6", &inet->inet_daddr);
        }

        pr_info("%s %u %s %u sk=%p, pre_state=%u, state=%u\n",
                daddr, dport, saddr, ntohs(inet->inet_sport), sk, sk->sk_state, state);

        return 0;
}

static struct kprobe set_state_kp = {
        .symbol_name    =       "tcp_set_state",
        .pre_handler    =       set_state_pre,
};

static int __init kprobe_init(void)
{
        return register_kprobe(&set_state_kp);
}

static void __exit kprobe_exit(void)
{
        unregister_kprobe(&set_state_kp);
}

module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");

 

0条评论
0 / 1000
卢****畅
5文章数
0粉丝数
卢****畅
5 文章 | 0 粉丝
原创

kprobe使用样例介绍

2023-05-25 08:41:38
19
0

kprobe是一种重要的内核调试技术, 可以在内核运行过程中, 向内核函数中添加回调函数,打印调试信息或修改内核执行逻辑。

本文介绍的是一种较为常见的使用方式, 即通过编写内核模块来使用kprobe.

 

以下是一个demo.  我们定义了一个回调函数set_state_pre, 通过调用kprobe的接口register_kprobe,hook到内核函数tcp_set_state

中. 最终的效果就是,在执行内核函数tcp_set_state之前,会执行我们注册的回调函数set_state_pre. 在回调函数中,我们可以做很多事

情,本例中, 我们是获取了内核的参数sk和state, 并打印四元组和state信息。

 

和普通的内核模块编译一样, 安装好内核kernel-devel包,写好对应的makefile文件,make生成对应ko文件。使用insmod加载对应ko文

件即可以运行

#include <linux/kprobes.h>
#include <net/tcp.h>

#define INET_ADDRSTRLEN                   16
#define INET6_ADDRSTRLEN                  46

static int set_state_pre(struct kprobe *p, struct pt_regs *regs)
{
        /* 从寄存器获取函数参数 */
        struct sock *sk = (struct sock *)regs->di;
        int state = (int)regs->si;

        struct inet_sock *inet = inet_sk(sk);
        u16 dport = ntohs(sk->__sk_common.skc_dport);

        char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN];
        if (sk->sk_family == AF_INET) {
                snprintf(saddr, INET_ADDRSTRLEN, "%pI4", &inet->inet_saddr);
                snprintf(daddr, INET_ADDRSTRLEN, "%pI4", &inet->inet_daddr);
        } else {
                snprintf(saddr, INET6_ADDRSTRLEN, "%pI6", &inet->inet_saddr);
                snprintf(daddr, INET6_ADDRSTRLEN, "%pI6", &inet->inet_daddr);
        }

        pr_info("%s %u %s %u sk=%p, pre_state=%u, state=%u\n",
                daddr, dport, saddr, ntohs(inet->inet_sport), sk, sk->sk_state, state);

        return 0;
}

static struct kprobe set_state_kp = {
        .symbol_name    =       "tcp_set_state",
        .pre_handler    =       set_state_pre,
};

static int __init kprobe_init(void)
{
        return register_kprobe(&set_state_kp);
}

static void __exit kprobe_exit(void)
{
        unregister_kprobe(&set_state_kp);
}

module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");

 

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0