一、使用 #
https://www.kernel.org/doc/html/v4.19/trace/ftrace-uses.html
1. ftrace hook内核函数 #
1.1. 前置判断 #
- 想要hook某个函数,需要内核开启编译选项进行支持
- 正常默认编译选项中可以hook非内联的所有函数(包括静态函数)
- 查看函数是否可以hook可以通过下面命令进行搜索
- hook如果要调用自己函数(调用instruction_pointer_set),在arm64上只有5.5以上支持,x86上是3.x之后就支持了
1=> grep "tcp_options_write" /proc/kallsyms
2ffffffffafa03a90 t __pfx_tcp_options_write
3ffffffffafa03aa0 t tcp_options_write
1.2. 基础实现 #
- Makefile
1NAME := sdp_toa
2
3obj-m := $(NAME).o
4
5ifeq ($(KERNDIR), )
6KDIR := /lib/modules/$(shell uname -r)/build
7else
8KDIR := $(KERNDIR)
9endif
10PWD := $(shell pwd)
11COMPILE_DATE=$(shell date "+%Y%m%d")
12COMPILE_TIME=$(shell date -d $(COMPILE_DATE) "+%s")
13$(info COMPILE_TIME=$(COMPILE_TIME))
14
15$(NAME)-objs += main.o \
16 ftrace_hook.o
17
18# $(NAME)-y +=
19# $(NAME)-n +=
20
21ccflags-y += -DHIDS_FILTER_COMPILE_TIME=$(COMPILE_TIME)
22ifeq ($(DEBUG), 1)
23ccflags-y += -g -O0 -DDEBUG
24endif
25
26all:
27 $(MAKE) -C $(KDIR) M=$(PWD) modules
28
29clean :
30 $(MAKE) -C $(KDIR) M=$(PWD) modules clean
- 源文件
1// ftrace_hook_utils.h
2#ifndef _FTRACE_HOOK_UTILS_H_
3#define _FTRACE_HOOK_UTILS_H_
4
5#define __SC_DECL(t, a) t a
6#define __FTRACE_HOOK_DEFINE_VAR0(m, ...)
7#define __FTRACE_HOOK_DEFINE_VAR1(m, t, a, ...) m(t, a)
8#define __FTRACE_HOOK_DEFINE_VAR2(m, t, a, ...) m(t, a), __FTRACE_HOOK_DEFINE_VAR1(m, __VA_ARGS__)
9#define __FTRACE_HOOK_DEFINE_VAR3(m, t, a, ...) m(t, a), __FTRACE_HOOK_DEFINE_VAR2(m, __VA_ARGS__)
10#define __FTRACE_HOOK_DEFINE_VAR4(m, t, a, ...) m(t, a), __FTRACE_HOOK_DEFINE_VAR3(m, __VA_ARGS__)
11#define __FTRACE_HOOK_DEFINE_VAR5(m, t, a, ...) m(t, a), __FTRACE_HOOK_DEFINE_VAR4(m, __VA_ARGS__)
12#define __FTRACE_HOOK_DEFINE_VAR6(m, t, a, ...) m(t, a), __FTRACE_HOOK_DEFINE_VAR5(m, __VA_ARGS__)
13#define FTRACE_HOOK_DEFINE_VAR(n, ...) __FTRACE_HOOK_DEFINE_VAR##n(__VA_ARGS__)
14
15#endif // _FTRACE_HOOK_UTILS_H_
1// ftrace_hook.h
2#ifndef _FTRACE_H_
3#define _FTRACE_H_
4
5#include <linux/atomic.h>
6#include <linux/ftrace.h>
7
8#include "ftrace_hook_utils.h"
9
10// 自定义log方式就定义hook_log,代码内部会使用hook_log_info等函数进行调用
11#ifndef TAG
12#define TAG "ftrace_hook"
13#endif
14
15#ifndef HOOK_LOG_INFO
16#ifdef DEBUG
17#define HOOK_LOG_INFO(fmt, ...) pr_info("[%s] " fmt "\n", TAG, ##__VA_ARGS__)
18#else
19#define HOOK_LOG_INFO(fmt, ...) pr_info("[%s][I][%s:%d] " fmt "\n", TAG, __FUNCTION__, __LINE__, ##__VA_ARGS__)
20#endif
21#endif
22
23#ifndef HOOK_LOG_WARN
24#ifdef DEBUG
25#define HOOK_LOG_WARN(fmt, ...) pr_warn("[%s] " fmt "\n", TAG, ##__VA_ARGS__)
26#else
27#define HOOK_LOG_WARN(fmt, ...) pr_info("[%s][W][%s:%d] " fmt "\n", TAG, __FUNCTION__, __LINE__, ##__VA_ARGS__)
28#endif
29#endif
30
31#ifndef HOOK_LOG_ERR
32#ifdef DEBUG
33#define HOOK_LOG_ERR(fmt, ...) pr_err("[%s] " fmt "\n", TAG, ##__VA_ARGS__)
34#else
35#define HOOK_LOG_ERR(fmt, ...) pr_info("[%s][E][%s:%d] " fmt "\n", TAG, __FUNCTION__, __LINE__, ##__VA_ARGS__)
36#endif
37#endif
38
39/**
40 * @brief Hook项
41 *
42 */
43struct ftrace_hook {
44 /** 函数符号名 */
45 const char *name;
46
47 /** 替换函数地址 */
48 void *replacement;
49
50 /** 原始函数地址 */
51 void **original;
52
53 /** 符号的原始地址 */
54 unsigned long _address;
55
56 /** ftrace 相关结构体 */
57 struct ftrace_ops ops;
58};
59
60/**
61 * @brief 正在运行的钩子数,如果大于 0 建议不要卸载,等待其变为 0
62 */
63extern atomic_t g_running_hooks;
64
65/**
66 * @brief 钩子进入时调用
67 *
68 * @param name 函数符号名
69 */
70static inline void ftrace_hook_func_enter(const char *name) {
71 // 增加引用计数,避免模块被释放
72 atomic_inc(&g_running_hooks);
73}
74
75/**
76 * @brief 钩子结束时调用,由宏完成,不必手动调用
77 *
78 * @param name 函数符号名
79 */
80static inline void ftrace_hook_func_exit(const char *name) {
81 // 减少引用计数
82 atomic_dec(&g_running_hooks);
83}
84
85/**
86 * @brief hook多个函数
87 *
88 * @param hooks
89 * @param count
90 * @return int 0 表示全部成功,否则返回 -errno
91 */
92int ftrace_hook_multi_register(struct ftrace_hook hooks[], size_t count);
93/**
94 * @brief unhook多个函数,按照反方向unhook
95 *
96 * @param hooks
97 * @param count
98 */
99void ftrace_hook_multi_unregister(struct ftrace_hook hooks[], size_t count);
100
101/**
102 * @brief 启用ftrace的hook功能
103 *
104 */
105void enable_ftrace_hook(void);
106/**
107 * @brief 禁用ftrace的hook功能
108 *
109 */
110void disable_ftrace_hook(void);
111/**
112 * @brief 返回当前是否启用ftrace的hook功能
113 *
114 * @return true
115 * @return false
116 */
117bool is_ftrace_hook_enabled(void);
118
119// 替换的函数名
120#define FTRACE_HOOK_REPLACE_NAME(name) __ftrace_hook_replace_##name
121// 原始函数名
122#define FTRACE_HOOK_ORIG_NAME(name) __ftrace_hook_orig_##name
123// 定义结构体
124#define FTRACE_HOOK_STRUCT(name) \
125 { \
126 #name, FTRACE_HOOK_REPLACE_NAME(name), (void **)&FTRACE_HOOK_ORIG_NAME(name), 0, {} \
127 }
128
129/**
130 * @brief 定义要替换的函数
131 * @param n 函数参数个数
132 * @param name 函数名
133 * @param return_type 函数返回类型
134 * @param ... 函数参数按照type, var的方式传入
135 *
136 */
137#define FTRACE_HOOK_FUNC_REPLACE(n, name, return_type, ...) \
138 static return_type (*FTRACE_HOOK_ORIG_NAME(name))(FTRACE_HOOK_DEFINE_VAR(n, __SC_DECL, __VA_ARGS__)); \
139 static return_type FTRACE_HOOK_REPLACE_NAME(name)(FTRACE_HOOK_DEFINE_VAR(n, __SC_DECL, __VA_ARGS__)) { \
140 static const char *__name = #name; \
141 ftrace_hook_func_enter(#name);
142
143// 结束符
144#define FTRACE_HOOK_FUNC_END }
145
146// 调用原始函数
147#define FTRACE_HOOK_FUNC_ORIG(name, ...) FTRACE_HOOK_ORIG_NAME(name)(__VA_ARGS__)
148
149/**
150 * @brief 钩子返回,减少引用计数
151 * @note 所有钩子中必须使用这个宏返回数据
152 *
153 */
154#define FTRACE_HOOK_RETURN(x) \
155 do { \
156 ftrace_hook_func_exit(__name); \
157 return x; \
158 } while (0)
159
160#define FTRACE_HOOK_RETURN_VOID() \
161 do { \
162 ftrace_hook_func_exit(__name); \
163 return; \
164 } while (0)
165
166#endif // _FTRACE_H_
1// ftrace_hook.c
2#include "ftrace_hook.h"
3
4// 记录当前是否启用hook功能,0表示未启用,1表示启用
5atomic_t g_hook_enable = ATOMIC_INIT(0);
6
7void enable_ftrace_hook(void) { atomic_set_release(&g_hook_enable, 1); }
8
9void disable_ftrace_hook(void) { atomic_set_release(&g_hook_enable, 0); }
10
11bool is_ftrace_hook_enabled(void) { return atomic_read_acquire(&g_hook_enable) == 1; }
12
13atomic_t g_running_hooks = ATOMIC_INIT(0);
14
15/**
16 * @brief 中转函数,在每次调用hook函数前调用,如果regs->ip被修改,则调用修改后的函数,否则调用原始函数
17 *
18 */
19static void notrace ftrace_hook_trampoline(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops,
20 struct pt_regs *regs) {
21 struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);
22 ftrace_hook_func_enter("ftrace_hook_trampoline");
23
24 if (hook->replacement) {
25 // 修改调用自己的函数
26 instruction_pointer_set(regs, (unsigned long)hook->replacement);
27 }
28
29 ftrace_hook_func_exit("ftrace_hook_trampoline");
30}
31
32static int hook_by_ftrace(struct ftrace_hook *hook) {
33 int ret;
34
35 // 查找符号
36 hook->_address = kallsyms_lookup_name(hook->name);
37 if (!hook->_address) {
38 HOOK_LOG_WARN("Failed to resolve %s address", hook->name);
39 return -ENOENT;
40 }
41
42 // 跳过开头的 ftrace entry 指令,避免再次进入
43 if (hook->original) {
44 *(hook->original) = (void *)(hook->_address + MCOUNT_INSN_SIZE);
45 }
46
47 hook->ops.func = ftrace_hook_trampoline;
48 // FTRACE_OPS_FL_SAVE_REGS: 需要访问参数
49 // FTRACE_OPS_FL_RECURSION_SAFE: 我们自己处理重入,减少性能损失
50 // FTRACE_OPS_FL_IPMODIFY: 我们需要修改 IP 寄存器
51 hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_IPMODIFY;
52
53 // 此函数支持符号名唯一的hook,如果存在多个函数同一个符号则需要使用ftrace_set_filter_ip来进行处理
54 ret = ftrace_set_filter(&hook->ops, (unsigned char *)hook->name, strlen(hook->name), 0);
55 if (ret < 0) {
56 HOOK_LOG_WARN("Failed to set ftrace ip filter(%s, 0x%p): %d", hook->name, (void *)hook->_address, ret);
57 return ret;
58 }
59
60 ret = register_ftrace_function(&hook->ops);
61 if (ret < 0) {
62 // 清理
63 ftrace_set_notrace(&hook->ops, (unsigned char *)hook->name, strlen(hook->name), 0);
64 HOOK_LOG_WARN("Failed to register ftrace function(%s): %d", hook->name, ret);
65 return ret;
66 }
67
68 return 0;
69}
70
71static void unhook_by_ftrace(struct ftrace_hook *hook) {
72 int ret;
73 ret = unregister_ftrace_function(&hook->ops);
74 if (ret < 0) {
75 HOOK_LOG_WARN("Failed to unregister ftrace function(%s): %d", hook->name, ret);
76 }
77
78 ret = ftrace_set_notrace(&hook->ops, (unsigned char *)hook->name, strlen(hook->name), 0);
79 if (ret < 0) {
80 HOOK_LOG_WARN("Failed to remove ftrace ip filter(%s, 0x%p): %d", hook->name, (void *)hook->_address, ret);
81 }
82}
83
84static int ftrace_hook_register(struct ftrace_hook *hook) {
85 if (!hook || !hook->name || !hook->name[0] || !hook->replacement) {
86 return -EINVAL;
87 }
88
89 return hook_by_ftrace(hook);
90}
91
92static void ftrace_hook_unregister(struct ftrace_hook *hook) {
93 if (!hook || !hook->name || !hook->name[0]) {
94 return;
95 }
96
97 unhook_by_ftrace(hook);
98}
99
100/**
101 * @brief 注册多个
102 *
103 * @param hooks
104 * @param count
105 * @return int 0 表示全部成功,否则返回 -errno
106 */
107int ftrace_hook_multi_register(struct ftrace_hook hooks[], size_t count) {
108 int ret = 0;
109 int i = 0;
110
111 if (unlikely(hooks == NULL)) {
112 return -EINVAL;
113 }
114
115 for (; i < count; ++i) {
116 ret = ftrace_hook_register(hooks + i);
117 if (ret < 0) {
118 HOOK_LOG_WARN("Failed to register hook(%s): %d", hooks[i].name, ret);
119 goto failed;
120 }
121 }
122
123 return 0;
124
125failed:
126 for (--i; i >= 0; --i) {
127 ftrace_hook_unregister(hooks + i);
128 }
129 return ret;
130}
131
132/**
133 * @brief 注销多个
134 *
135 * @param hooks
136 * @param count
137 */
138void ftrace_hook_multi_unregister(struct ftrace_hook hooks[], size_t count) {
139 int i = (int)count;
140 if (unlikely(hooks == NULL)) {
141 return;
142 }
143 for (--i; i >= 0; --i) {
144 ftrace_hook_unregister(hooks + i);
145 }
146}
1.3. 使用示例 #
1// main.c
2#include "ftrace_hook.h"
3
4// hook tcp_established_options
5// 下面的宏定义了需要赋值的原函数指针并声明了替换的函数,在函数开头会添加引用计数
6FTRACE_HOOK_FUNC_REPLACE(4, tcp_established_options, unsigned int, struct sock *, sk, struct sk_buff *, skb,
7 struct tcp_out_options *, opts, struct tcp_md5sig_key **, md5) {
8 unsigned int size = 0;
9 // 调用原函数
10 size = FTRACE_HOOK_FUNC_ORIG(tcp_established_options, sk, skb, opts, md5);
11 // do something
12 FTRACE_HOOK_RETURN(size); // 使用宏进行返回,会将引用计数减一
13}
14FTRACE_HOOK_FUNC_END
15
16static struct ftrace_hook toa_hooks[] = {
17 FTRACE_HOOK_STRUCT(tcp_established_options),
18};
19
20int toa_init(void) {
21 int ret;
22 ret = ftrace_hook_multi_register(toa_hooks, ARRAY_SIZE(toa_hooks));
23
24 if (ret) {
25 // do something
26 goto hook_err;
27 }
28
29 // 上面只是注册,需要下面的调用来启动hook函数调用
30 enable_ftrace_hook();
31 return 0;
32hook_err:
33 return ret;
34}
35
36void toa_exit(void) {
37 int running_hooks;
38
39 disable_ftrace_hook(); // 非必要,不调也可以卸载
40 ftrace_hook_multi_unregister(toa_hooks, ARRAY_SIZE(toa_hooks));
41
42 // 下面的处理防止驱动退出后,已经调用到hook函数内部的函数返回到非法内存地址
43 // 这里等待引用计数减到0后再退出释放内存
44 while ((running_hooks = atomic_read_acquire(&g_running_hooks))) {
45 SDP_LOG_INFO("waiting for running hooks %d", running_hooks);
46 msleep(1);
47 }
48}
49
50MODULE_LICENSE("GPL");
51MODULE_DESCRIPTION("ftrace test");
52MODULE_AUTHOR("xxx");
53module_init(toa_init);
54module_exit(toa_exit);