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

kprobe原理1(x86架构下没有开启ftrace)

2023-05-25 10:39:30
8
0

kprobe是一种重要的内核调试技术, 本文主要从内核代码实现的角度, 分析kprobe程序是如何运行的。

由于不同架构不同配置对应的执行流程都有所不同, 不文介绍的环境是x86架构下,且没有开启ftrace。

还是以下面的demo为例介绍。

第一步就是调用register_kprobe函数。
我们传入的一个struct kprobe类型的变量。该变量包含了回调函数pre_set_state,内核函数名tcp_set_state。
register_kprobe函数会根据内核函数名, 在符号表中找到对应的函数地址, 并保存原始指令。并根据内核函
数地址计算hash key,将kprobe实例存放到全局hash表kprobe_table中。最后就是将原始指令替换成一个int3指令。

第二部就是int3指令的执行。
由于原始内核函数的指令被替换成了int3指令, 这样在执行到该处时,就会执行int3指令,进入了内核的do_int3函数逻辑。
这里会根据地址再次找到之前保存的kprobe实例和原始内核指令。依次执行回调函数和内核原始指令。

我们这里忽略掉了int3指令相关的初始化,这和内核启动时初始化操作相关,我们后续再看。

#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原理1(x86架构下没有开启ftrace)

2023-05-25 10:39:30
8
0

kprobe是一种重要的内核调试技术, 本文主要从内核代码实现的角度, 分析kprobe程序是如何运行的。

由于不同架构不同配置对应的执行流程都有所不同, 不文介绍的环境是x86架构下,且没有开启ftrace。

还是以下面的demo为例介绍。

第一步就是调用register_kprobe函数。
我们传入的一个struct kprobe类型的变量。该变量包含了回调函数pre_set_state,内核函数名tcp_set_state。
register_kprobe函数会根据内核函数名, 在符号表中找到对应的函数地址, 并保存原始指令。并根据内核函
数地址计算hash key,将kprobe实例存放到全局hash表kprobe_table中。最后就是将原始指令替换成一个int3指令。

第二部就是int3指令的执行。
由于原始内核函数的指令被替换成了int3指令, 这样在执行到该处时,就会执行int3指令,进入了内核的do_int3函数逻辑。
这里会根据地址再次找到之前保存的kprobe实例和原始内核指令。依次执行回调函数和内核原始指令。

我们这里忽略掉了int3指令相关的初始化,这和内核启动时初始化操作相关,我们后续再看。

#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