什么是KernelPatch Module (KPM)
KPM是一个ELF文件,可以通过KernelPatch在内核空间中加载和运行
官方文档与代码案例
官方提供了3个案例:
1.hello world
2.inline-hook
3.syscallhook
Hello
//代码比较简单
//这里声明了模块名称, 版本, 作者协议, 描述等
KPM_NAME("kpm-hello-demo");
KPM_VERSION("1.0.0");
KPM_LICENSE("GPL v2");
KPM_AUTHOR("bmax121");
KPM_DESCRIPTION("KernelPatch Module Example");
static long hello_init(const char *args, const char *event, void *__user reserved)
{
pr_info("kpm hello init, event: %s, args: %s\n", event, args);
pr_info("kernelpatch version: %x\n", kpver);
return 0;
}
static long hello_control0(const char *args, char *__user out_msg, int outlen)
{
pr_info("kpm hello control0, args: %s\n", args);
char echo[64] = "echo: ";
strncat(echo, args, 48);
compat_copy_to_user(out_msg, echo, sizeof(echo));
return 0;
}
static long hello_control1(void *a1, void *a2, void *a3)
{
pr_info("kpm hello control1, a1: %llx, a2: %llx, a3: %llx\n", a1, a2, a3);
return 0;
}
static long hello_exit(void *__user reserved)
{
pr_info("kpm hello exit\n");
return 0;
}
//这里注册了几个函数:
KPM_INIT(hello_init); //初始化
KPM_CTL0(hello_control0); //ctl0
KPM_CTL1(hello_control1); //ctl1
KPM_EXIT(hello_exit); //退出
JavaScript
inline hook
//同上
KPM_NAME("kpm-inline-hook-demo");
KPM_VERSION("1.0.0");
KPM_LICENSE("GPL v2");
KPM_AUTHOR("bmax121");
KPM_DESCRIPTION("KernelPatch Module Inline Hook Example");
int __noinline add(int a, int b)
{
logkd("origin add called\n");
int ret = a + b;
return ret;
}
void before_add(hook_fargs2_t *args, void *udata)
{
logkd("before add arg0: %d, arg1: %d\n", (int)args->arg0, (int)args->arg1);
}
void after_add(hook_fargs2_t *args, void *udata)
{
logkd("after add arg0: %d, arg1: %d, ret: %d\n", (int)args->arg0, (int)args->arg1, (int)args->ret);
args->ret = 100; //修改返回值
}
static long inline_hook_demo_init(const char *args, const char *event, void *__user reserved)
{
logkd("kpm inline-hook-demo init\n");
int a = 20;
int b = 10;
int ret = add(a, b);
logkd("%d + %d = %d\n", a, b, ret);
hook_err_t err = hook_wrap2((void *)add, before_add, after_add, 0);
logkd("hook err: %d\n", err);
ret = add(a, b);
logkd("%d + %d = %d\n", a, b, ret);
return 0;
}
static long inline_hook_control0(const char *args, char *__user out_msg, int outlen)
{
pr_info("kpm control, args: %s\n", args);
return 0;
}
static long inline_hook_demo_exit(void *__user reserved)
{
unhook((void *)add);
int a = 20;
int b = 10;
int ret = add(a, b);
logkd("%d + %d = %d\n", a, b, ret);
logkd("kpm inline-hook-demo exit\n");
}
KPM_INIT(inline_hook_demo_init);
KPM_CTL0(inline_hook_control0);
KPM_EXIT(inline_hook_demo_exit);
JavaScript
static long inline_hook_demo_init(const char *args, const char *event, void *__user reserved)
{
logkd("kpm inline-hook-demo init\n");
int a = 20;
int b = 10;
int ret = add(a, b);
logkd("%d + %d = %d\n", a, b, ret);
hook_err_t err = hook_wrap2((void *)add, before_add, after_add, 0);
logkd("hook err: %d\n", err);
ret = add(a, b);
logkd("%d + %d = %d\n", a, b, ret);
return 0;
}
JavaScript
定义一个测试函数:__noinline add
__noinline 避免被内联优化, 方便hook
执行hook
hook_err_t err = hook_wrap2((void *)add, before_add, after_add, 0);
//参数很好理解
第一个是被hook的函数, 第二个是before hook, 第三个是after hook, 第四个是用户数据指针
JavaScript
syscall hook
const char *margs = 0;
enum hook_type hook_type = NONE;
enum pid_type
{
PIDTYPE_PID,
PIDTYPE_TGID,
PIDTYPE_PGID,
PIDTYPE_SID,
PIDTYPE_MAX,
};
struct pid_namespace;
//函数指针
pid_t (*__task_pid_nr_ns)(struct task_struct *task, enum pid_type type, struct pid_namespace *ns) = 0;
void before_openat_0(hook_fargs4_t *args, void *udata)
{
int dfd = (int)syscall_argn(args, 0);
const char __user *filename = (typeof(filename))syscall_argn(args, 1);
int flag = (int)syscall_argn(args, 2);
umode_t mode = (int)syscall_argn(args, 3);
char buf[1024];
compat_strncpy_from_user(buf, filename, sizeof(buf));
struct task_struct *task = current;
pid_t pid = -1, tgid = -1;
if (__task_pid_nr_ns) {
pid = __task_pid_nr_ns(task, PIDTYPE_PID, 0);
tgid = __task_pid_nr_ns(task, PIDTYPE_TGID, 0);
}
args->local.data0 = (uint64_t)task;
pr_info("hook_chain_0 task: %llx, pid: %d, tgid: %d, openat dfd: %d, filename: %s, flag: %x, mode: %d\n", task, pid,
tgid, dfd, buf, flag, mode);
}
uint64_t open_counts = 0;
void before_openat_1(hook_fargs4_t *args, void *udata)
{
uint64_t *pcount = (uint64_t *)udata;
(*pcount)++;
pr_info("hook_chain_1 before openat task: %llx, count: %llx\n", args->local.data0, *pcount);
}
void after_openat_1(hook_fargs4_t *args, void *udata)
{
pr_info("hook_chain_1 after openat task: %llx\n", args->local.data0);
}
static long syscall_hook_demo_init(const char *args, const char *event, void *__user reserved)
{
margs = args;
pr_info("kpm-syscall-hook-demo init ..., args: %s\n", margs);
//赋值给函数指针
__task_pid_nr_ns = (typeof(__task_pid_nr_ns))kallsyms_lookup_name("__task_pid_nr_ns");
pr_info("kernel function __task_pid_nr_ns addr: %llx\n", __task_pid_nr_ns);
if (!margs) {
pr_warn("no args specified, skip hook\n");
return 0;
}
hook_err_t err = HOOK_NO_ERR;
if (!strcmp("function_pointer_hook", margs)) {
pr_info("function pointer hook ...");
hook_type = FUNCTION_POINTER_CHAIN;
err = fp_hook_syscalln(__NR_openat, 4, before_openat_0, 0, 0);
if (err) goto out;
err = fp_hook_syscalln(__NR_openat, 4, before_openat_1, after_openat_1, &open_counts);
} else if (!strcmp("inline_hook", margs)) {
pr_info("inline hook ...");
hook_type = INLINE_CHAIN;
err = inline_hook_syscalln(__NR_openat, 4, before_openat_0, 0, 0);
} else {
pr_warn("unknown args: %s\n", margs);
return 0;
}
out:
if (err) {
pr_err("hook openat error: %d\n", err);
} else {
pr_info("hook openat success\n");
}
return 0;
}
static long syscall_hook_control0(const char *args, char *__user out_msg, int outlen)
{
pr_info("syscall_hook control, args: %s\n", args);
return 0;
}
static long syscall_hook_demo_exit(void *__user reserved)
{
pr_info("kpm-syscall-hook-demo exit ...\n");
if (hook_type == INLINE_CHAIN) {
inline_unhook_syscall(__NR_openat, before_openat_0, 0);
} else if (hook_type == FUNCTION_POINTER_CHAIN) {
fp_unhook_syscall(__NR_openat, before_openat_0, 0);
fp_unhook_syscall(__NR_openat, before_openat_1, after_openat_1);
} else {
}
return 0;
}
KPM_INIT(syscall_hook_demo_init);
KPM_CTL0(syscall_hook_control0);
KPM_EXIT(syscall_hook_demo_exit);
JavaScript
初始化
static long syscall_hook_demo_init(const char *args, const char *event, void *__user reserved) {
margs = args;
// 获取内核函数地址复制给函数指针, 方便后面调用
__task_pid_nr_ns = (typeof(__task_pid_nr_ns))kallsyms_lookup_name("__task_pid_nr_ns");
if (!margs) {
pr_warn("no args specified, skip hook\n");
return 0;
}
hook_err_t err = HOOK_NO_ERR;
// 根据参数选择hook类型
if (!strcmp("function_pointer_hook", margs)) {
// 函数指针方式hook
hook_type = FUNCTION_POINTER_CHAIN;
// 安装两个钩子形成链
err = fp_hook_syscalln(__NR_openat, 4, before_openat_0, 0, 0);
if (err) goto out;
err = fp_hook_syscalln(__NR_openat, 4, before_openat_1, after_openat_1, &open_counts);
}
else if (!strcmp("inline_hook", margs)) {
// 内联方式hook
hook_type = INLINE_CHAIN;
err = inline_hook_syscalln(__NR_openat, 4, before_openat_0, 0, 0);
}
out:
return err ? -1 : 0;
}
JavaScript
如果参数是function_pointer_hook, 使用fp_hook_syscalln来hook系统函数, hook了openat
如果是inline_hook, 则使用inline_hook_syscalln来hook系统函数
void before_openat_0(hook_fargs4_t *args, void *udata)
{
//获取openat四个参数
int dfd = (int)syscall_argn(args, 0); //fd
const char __user *filename = (typeof(filename))syscall_argn(args, 1); //filename
int flag = (int)syscall_argn(args, 2); //打开标志
umode_t mode = (int)syscall_argn(args, 3); //模式
//从用户空间复制文件名
char buf[1024];
compat_strncpy_from_user(buf, filename, sizeof(buf));
//获取当前进程
struct task_struct *task = current;
pid_t pid = -1, tgid = -1;
//调用__task_pid_nr_ns函数指针获取pid和tgid(线程组id)
if (__task_pid_nr_ns) {
pid = __task_pid_nr_ns(task, PIDTYPE_PID, 0);
tgid = __task_pid_nr_ns(task, PIDTYPE_TGID, 0);
}
//保存到local data中
args->local.data0 = (uint64_t)task;
pr_info("hook_chain_0 task: %llx, pid: %d, tgid: %d, openat dfd: %d, filename: %s, flag: %x, mode: %d\n", task, pid,
tgid, dfd, buf, flag, mode);
}
JavaScript
通过syscall_argn来获取参数值
uint64_t open_counts = 0;
void before_openat_1(hook_fargs4_t *args, void *udata)
{
//获取参数复制给pcount
uint64_t *pcount = (uint64_t *)udata;
(*pcount)++; //累加
pr_info("hook_chain_1 before openat task: %llx, count: %llx\n", args->local.data0, *pcount);
}
void after_openat_1(hook_fargs4_t *args, void *udata)
{
pr_info("hook_chain_1 after openat task: %llx\n", args->local.data0); //这里打印出累加后的值
}
JavaScript
实际案例分析
模块作用:redirect /system/etc/hosts to /data/adb/hosts
KPM_NAME("hosts_redirect");
KPM_VERSION(MYKPM_VERSION);
KPM_LICENSE("GPL v2");
KPM_AUTHOR("lzghzr");
KPM_DESCRIPTION("redirect /system/etc/hosts to /data/adb/hosts/{n}");
#define IZERO (1UL << 0x10)
#define UZERO (1UL << 0x20)
struct open_flags;
struct file* (*do_filp_open)(int dfd, struct filename* pathname, const struct open_flags* op);
char* kfunc_def(d_path)(const struct path* path, char* buf, int buflen);
int kfunc_def(kern_path)(const char* name, unsigned int flags, struct path* path);
void kfunc_def(_raw_spin_lock)(raw_spinlock_t* lock);
void kfunc_def(_raw_spin_unlock)(raw_spinlock_t* lock);
static uint64_t task_struct_fs_offset = UZERO, task_struct_alloc_lock_offset = UZERO,
fs_struct_pwd_offset = UZERO, fs_struct_lock_offset = UZERO;
char hosts_source[] = "/system/etc/hosts";
char hosts_target[] = "/data/adb/hosts/0";
static uid_t current_uid() {
struct cred* cred = *(struct cred**)((uintptr_t)current + task_struct_offset.cred_offset);
uid_t uid = *(uid_t*)((uintptr_t)cred + cred_offset.uid_offset);
return uid;
}
static inline void set_priv_selinx_allow(struct task_struct* task, int val) {
struct task_ext* ext = get_task_ext(task);
if (likely(task_ext_valid(ext))) {
ext->priv_selinux_allow = val;
dsb(ish);
}
}
static bool endWith(const char* str, const char* suffix) {
if (!str || !suffix)
return false;
size_t lenstr = strlen(str);
size_t lensuffix = strlen(suffix);
if (lensuffix > lenstr)
return false;
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
}
static void do_filp_open_before(hook_fargs3_t* args, void* udata) {
args->local.data0 = 0;
if (unlikely(hosts_target[16] == '0'))
return;
if (current_uid() != 0)
return;
struct filename* pathname = (struct filename*)args->arg1;
if (unlikely(!strcmp(pathname->name, hosts_source))) {
args->local.data0 = (uint64_t)pathname->name;
pathname->name = hosts_target;
set_priv_selinx_allow(current, true);
} else if (unlikely(endWith(pathname->name, "hosts"))) {
struct task_struct* task = current;
spinlock_t task_lock = *(spinlock_t*)((uintptr_t)task + task_struct_alloc_lock_offset);
spin_lock(&task_lock);
struct fs_struct* fs = *(struct fs_struct**)((uintptr_t)task + task_struct_fs_offset);
if (likely(fs)) {
// spinlock_t fs_lock = *(spinlock_t*)((uintptr_t)fs + fs_struct_lock_offset);
// spin_lock(&fs_lock);
spin_lock(&fs->lock);
struct path* pwd = (struct path*)((uintptr_t)fs + fs_struct_pwd_offset);
if (likely(pwd)) {
char buf[PATH_MAX];
memset(&buf, 0, PATH_MAX);
char* pwd_path = d_path(pwd, buf, PATH_MAX);
#ifdef DEBUG
logkm("pwd_path=%s\n", pwd_path);
#endif /* DEBUG */
* buf = '\0';
if (pathname->name[0] != '/') {
strncat(buf, pwd_path, strlen(pwd_path));
strncat(buf, "/", strlen("/"));
}
strncat(buf, pathname->name, strlen(pathname->name));
#ifdef DEBUG
logkm("full_path=%s\n", buf);
#endif /* DEBUG */
struct path path;
int err = kern_path(buf, LOOKUP_FOLLOW, &path);
if (likely(!err)) {
memset(&buf, 0, PATH_MAX);
char* hosts_name = d_path(&path, buf, PATH_MAX);
#ifdef DEBUG
logkm("hosts_name=%s\n", hosts_name);
#endif /* DEBUG */
if (likely(!IS_ERR(hosts_name) && !strcmp(hosts_name, hosts_source))) {
args->local.data0 = (uint64_t)pathname->name;
pathname->name = hosts_target;
set_priv_selinx_allow(task, true);
}
}
}
spin_unlock(&fs->lock);
}
spin_unlock(&task_lock);
}
}
static void do_filp_open_after(hook_fargs3_t* args, void* udata) {
if (unlikely(args->local.data0)) {
set_priv_selinx_allow(current, false);
struct filename* pathname = (struct filename*)args->arg1;
pathname->name = (char*)args->local.data0;
}
}
static long inline_hook_control0(const char* ctl_args, char* __user out_msg, int outlen) {
char num = ctl_args ? *ctl_args : '1';
if (unlikely(num < '0' || num > '9')) {
return -11;
}
hosts_target[16] = num;
char msg[64];
snprintf(msg, sizeof(msg), "_(._.)_");
compat_copy_to_user(out_msg, msg, sizeof(msg));
return 0;
}
static long calculate_offsets() {
// 获取 pwd 相关偏移
// task->fs
// fs->pwd
int (*proc_cwd_link)(struct dentry* dentry, struct path* path);
lookup_name(proc_cwd_link);
uint32_t* proc_cwd_link_src = (uint32_t*)proc_cwd_link;
for (u32 i = 0; i < 0x30; i++) {
#ifdef CONFIG_DEBUG
logkm("proc_cwd_link %x %llx\n", i, proc_cwd_link[i]);
#endif /* CONFIG_DEBUG */
if (proc_cwd_link_src[i] == ARM64_RET) {
break;
} else if ((proc_cwd_link_src[i] & MASK_LDP_64_) == INST_LDP_64_) {
uint64_t imm7 = bits32(proc_cwd_link_src[i], 21, 15);
fs_struct_pwd_offset = sign64_extend((imm7 << 0b11u), 16u);
break;
} else if (task_struct_alloc_lock_offset != UZERO && (proc_cwd_link_src[i] & MASK_ADD_64) == INST_ADD_64) {
uint32_t sh = bit(proc_cwd_link_src[i], 22);
uint64_t imm12 = imm12 = bits32(proc_cwd_link_src[i], 21, 10);
if (sh) {
fs_struct_lock_offset = sign64_extend((imm12 << 12u), 16u);
} else {
fs_struct_lock_offset = sign64_extend((imm12), 16u);
}
} else if (task_struct_alloc_lock_offset != UZERO && (proc_cwd_link_src[i] & MASK_LDR_64_) == INST_LDR_64_) {
uint64_t imm12 = bits32(proc_cwd_link_src[i], 21, 10);
task_struct_fs_offset = sign64_extend((imm12 << 0b11u), 16u);
} else if (task_struct_alloc_lock_offset == UZERO && (proc_cwd_link_src[i] & MASK_ADD_64) == INST_ADD_64) {
uint32_t sh = bit(proc_cwd_link_src[i], 22);
uint64_t imm12 = imm12 = bits32(proc_cwd_link_src[i], 21, 10);
if (sh) {
task_struct_alloc_lock_offset = sign64_extend((imm12 << 12u), 16u);
} else {
task_struct_alloc_lock_offset = sign64_extend((imm12), 16u);
}
// MOV (to/from SP) is an alias of ADD <Xd|SP>, <Xn|SP>, #0
if (task_struct_alloc_lock_offset == 0) {
task_struct_alloc_lock_offset = UZERO;
}
}
}
#ifdef CONFIG_DEBUG
logkm("task_struct_fs_offset=0x%llx\n", task_struct_fs_offset);
logkm("task_struct_alloc_lock_offset=0x%llx\n", task_struct_alloc_lock_offset);
logkm("fs_struct_pwd_offset=0x%llx\n", fs_struct_pwd_offset);
logkm("fs_struct_lock_offset=0x%llx\n", fs_struct_lock_offset);
#endif /* CONFIG_DEBUG */
if (task_struct_fs_offset == UZERO || task_struct_alloc_lock_offset == UZERO || fs_struct_pwd_offset == UZERO || fs_struct_lock_offset == UZERO) {
return -11;
}
return 0;
}
static long inline_hook_init(const char* args, const char* event, void* __user reserved) {
int rc = inline_hook_control0(args, NULL, NULL);
if (rc < 0) {
return rc;
}
kfunc_lookup_name(d_path);
kfunc_lookup_name(kern_path);
kfunc_lookup_name(_raw_spin_lock);
kfunc_lookup_name(_raw_spin_unlock);
rc = calculate_offsets();
if (rc < 0) {
return rc;
}
lookup_name(do_filp_open);
hook_func(do_filp_open, 3, do_filp_open_before, do_filp_open_after, 0);
return 0;
}
static long inline_hook_exit(void* __user reserved) {
unhook_func(do_filp_open);
return 0;
}
KPM_INIT(inline_hook_init);
KPM_CTL0(inline_hook_control0);
KPM_EXIT(inline_hook_exit);
JavaScript
这个模块主要是hook VFS层do_sys_open, do_sys_open是open/openat的一个实现. VFS层是比syscal 更底层的实现

具体代码分析
int rc = inline_hook_control0(args, NULL, NULL);
if (rc < 0) {
return rc;
}
kfunc_lookup_name(d_path);
kfunc_lookup_name(kern_path);
kfunc_lookup_name(_raw_spin_lock);
kfunc_lookup_name(_raw_spin_unlock);
rc = calculate_offsets();
if (rc < 0) {
return rc;
}
lookup_name(do_filp_open);
hook_func(do_filp_open, 3, do_filp_open_before, do_filp_open_after, 0);
JavaScript
声明do_filp_open函数指针
struct file* (*do_filp_open)(int dfd, struct filename* pathname, const struct open_flags* op);
第一个参数: 目录文件描述符
第二个参数: 文件名结构体指针
第三个参数: 打开标志结构体指针
JavaScript
inline_hook_control0函数是设置文件编号, 假设是2
char hosts_target[] = “/data/adb/hosts/0”;
变成:
char hosts_target[] = “/data/adb/hosts/2”;
static long inline_hook_control0(const char* ctl_args, char* __user out_msg, int outlen) {
char num = ctl_args ? *ctl_args : '1';
if (unlikely(num < '0' || num > '9')) {
return -11;
}
hosts_target[16] = num;
char msg[64];
snprintf(msg, sizeof(msg), "_(._.)_");
compat_copy_to_user(out_msg, msg, sizeof(msg));
return 0;
}
JavaScript
查找并保存几个重要的内核函数地址
// 查找并保存几个重要的内核函数地址
kfunc_lookup_name(d_path); // 获取d_path函数的地址
kfunc_lookup_name(kern_path); // 获取kern_path函数的地址
kfunc_lookup_name(_raw_spin_lock); // 获取_raw_spin_lock函数的地址
kfunc_lookup_name(_raw_spin_unlock);// 获取_raw_spin_unlock函数的地址
JavaScript
hook do_filp_open
lookup_name(do_filp_open); //获取do_filp_open的函数地址
hook_func(do_filp_open, 3, do_filp_open_before, do_filp_open_after, 0);
JavaScript
do_filp_open_before的实现
获取参数filename指针, 再获取pathname , 判断绝对路径和相对路径
绝对路径判断相等, 则执行重定向和设置selinux allow = true, hook完成后会改回去false
相对路径则获取工作目录拼接成绝对路径, 再执行相同的逻辑
static void do_filp_open_before(hook_fargs3_t* args, void* udata) {
args->local.data0 = 0; //设置local.data0为0
if (unlikely(hosts_target[16] == '0'))
return;
if (current_uid() != 0) //判断current_uid()是否为0, 也就是判断当前用户是否为root用户
return;
struct filename* pathname = (struct filename*)args->arg1; //文件name struct
if (unlikely(!strcmp(pathname->name, hosts_source))) { //判断路径是否== /system/etc/hosts
args->local.data0 = (uint64_t)pathname->name; //保存old pathname
pathname->name = hosts_target; //重定向到新的目录/data/adb/hosts/2
set_priv_selinx_allow(current, true); //设置selinux权限
} else if (unlikely(endWith(pathname->name, "hosts"))) { //判断是否hosts结尾, 相对路径模式
struct task_struct* task = current;
spinlock_t task_lock = *(spinlock_t*)((uintptr_t)task + task_struct_alloc_lock_offset);
spin_lock(&task_lock);
struct fs_struct* fs = *(struct fs_struct**)((uintptr_t)task + task_struct_fs_offset);
if (likely(fs)) {
// spinlock_t fs_lock = *(spinlock_t*)((uintptr_t)fs + fs_struct_lock_offset);
// spin_lock(&fs_lock);
spin_lock(&fs->lock);
struct path* pwd = (struct path*)((uintptr_t)fs + fs_struct_pwd_offset);
if (likely(pwd)) {
char buf[PATH_MAX];
memset(&buf, 0, PATH_MAX);
char* pwd_path = d_path(pwd, buf, PATH_MAX);
#ifdef DEBUG
logkm("pwd_path=%s\n", pwd_path);
#endif /* DEBUG */
* buf = '\0';
if (pathname->name[0] != '/') {
strncat(buf, pwd_path, strlen(pwd_path));
strncat(buf, "/", strlen("/"));
}
strncat(buf, pathname->name, strlen(pathname->name));
#ifdef DEBUG
logkm("full_path=%s\n", buf);
#endif /* DEBUG */
struct path path;
int err = kern_path(buf, LOOKUP_FOLLOW, &path);
if (likely(!err)) {
memset(&buf, 0, PATH_MAX);
char* hosts_name = d_path(&path, buf, PATH_MAX);
#ifdef DEBUG
logkm("hosts_name=%s\n", hosts_name);
#endif /* DEBUG */
if (likely(!IS_ERR(hosts_name) && !strcmp(hosts_name, hosts_source))) { //判断组装后的完整路径相等, 执行重定向操作
args->local.data0 = (uint64_t)pathname->name;
pathname->name = hosts_target;
set_priv_selinx_allow(task, true);
}
}
}
spin_unlock(&fs->lock);
}
spin_unlock(&task_lock);
}
}
C
do_filp_open_after的实现
static void do_filp_open_after(hook_fargs3_t* args, void* udata) {
if (unlikely(args->local.data0)) {
set_priv_selinx_allow(current, false); //seliux = false
struct filename* pathname = (struct filename*)args->arg1;
pathname->name = (char*)args->local.data0; //还原pathname
}
}
JavaScript
编译(Ubuntu)
官方编译指南: https://github.com/bmax121/KernelPatch/blob/main/doc/en/build.md
先下载交叉编译
下载 AArch64 bare-metal target (aarch64-none-elf)解压
设置环境变量
export PATH=$PATH:/work/test/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-elf/bin
export TARGET_COMPILE=aarch64-none-elf-
JavaScript
编译
cd /work/test/APatch_kpm/hosts_redirect
make
JavaScript
完成!
KPM模块安装
- 作者:拓森Leo
- 链接:https://www.uzilol.cn/article/12dbf20f-1d11-802a-8cad-cf296b4a0950
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。