From 0096f7a40399ffc2bc2ea73cc67f514ad3346144 Mon Sep 17 00:00:00 2001 From: lzghzr Date: Sun, 19 May 2024 22:00:36 +0800 Subject: [PATCH] =?UTF-8?q?[hosts=5Fredirect]=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=9B=B8=E5=AF=B9=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hosts_redirect/Makefile | 38 ++++-- hosts_redirect/hosts_redirect.c | 213 ++++++++++++++++++++++++++------ hosts_redirect/hosts_redirect.h | 74 +++++++++++ hosts_redirect/hr_utils.h | 87 +++++++++++++ 4 files changed, 366 insertions(+), 46 deletions(-) create mode 100644 hosts_redirect/hosts_redirect.h create mode 100644 hosts_redirect/hr_utils.h diff --git a/hosts_redirect/Makefile b/hosts_redirect/Makefile index c93d258..ad4d797 100644 --- a/hosts_redirect/Makefile +++ b/hosts_redirect/Makefile @@ -1,14 +1,27 @@ -ifndef TARGET_COMPILE - $(error TARGET_COMPILE not set) -endif +HR_VERSION := 1.2.0 ifndef KP_DIR KP_DIR = ../KernelPatch endif +OS_NAME = $(shell uname | tr A-Z a-z) +MACHINE = $(shell uname -m) +NDK_BIN_DIR := toolchains/llvm/prebuilt/$(OS_NAME)-$(MACHINE)/bin +ifdef ANDROID_NDK_LATEST_HOME + NDK_PATH ?= $(ANDROID_NDK_LATEST_HOME)/$(NDK_BIN_DIR) +else ifdef ANDROID_NDK + NDK_PATH ?= $(ANDROID_NDK)/$(NDK_BIN_DIR) +endif + +ifdef TARGET_COMPILE + CC := $(TARGET_COMPILE)gcc + LD := $(TARGET_COMPILE)ld +else ifdef NDK_PATH + CC := $(NDK_PATH)/aarch64-linux-android31-clang + LD := $(NDK_PATH)/ld.lld +endif -CC = $(TARGET_COMPILE)gcc -LD = $(TARGET_COMPILE)ld +CFLAGS = -Wall -O2 -fno-PIC -fno-asynchronous-unwind-tables -fno-stack-protector -fno-common -DHR_VERSION=\"$(HR_VERSION)$(HR_VER)\" INCLUDE_DIRS := . include patch/include linux/include linux/arch/arm64/include linux/tools/arch/arm64/include @@ -16,15 +29,22 @@ INCLUDE_FLAGS := $(foreach dir,$(INCLUDE_DIRS),-I$(KP_DIR)/kernel/$(dir)) objs := hosts_redirect.o -all: hosts_redirect.kpm +all: hosts_redirect_$(HR_VERSION).kpm + +debug: CFLAGS += -DDEBUG +debug: HR_VER := _d +debug: hosts_redirect_$(HR_VERSION)_debug.kpm + +hosts_redirect_$(HR_VERSION).kpm: ${objs} + ${CC} -r -o $@ $^ -hosts_redirect.kpm: ${objs} +hosts_redirect_$(HR_VERSION)_debug.kpm: ${objs} ${CC} -r -o $@ $^ %.o: %.c - ${CC} $(CFLAGS) $(INCLUDE_FLAGS) -T../demo.lds -c -O2 -o $@ $< + ${CC} $(CFLAGS) $(INCLUDE_FLAGS) -c -o $@ $< .PHONY: clean clean: rm -rf *.kpm - find . -name "*.o" | xargs rm -f \ No newline at end of file + find . -name "*.o" | xargs rm -f diff --git a/hosts_redirect/hosts_redirect.c b/hosts_redirect/hosts_redirect.c index 34de40e..d4b9205 100644 --- a/hosts_redirect/hosts_redirect.c +++ b/hosts_redirect/hosts_redirect.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* + * Copyright (C) 2024 bmax121. All Rights Reserved. * Copyright (C) 2024 lzghzr. All Rights Reserved. */ @@ -7,81 +8,219 @@ #include #include #include +#include +#include +#include #include +#include #include +#include #include -#include +#include -#include "../demo.h" +#include "hosts_redirect.h" +#include "hr_utils.h" KPM_NAME("hosts_redirect"); -KPM_VERSION("1.1.1"); +KPM_VERSION(HR_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); +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 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; +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); } - hosts_target[16] = num; - return 0; } -void do_filp_open_before(hook_fargs3_t *args, void *udata) -{ +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 + printk("hosts_redirect: 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 + printk("hosts_redirect: 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 + printk("hosts_redirect: 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); } - char **fname = *(char ***)args->arg1; - if (unlikely(!memcmp(fname, hosts_source, sizeof(hosts_source)))) - { - struct task_ext *ext = get_current_task_ext(); - args->local.data0 = (uint64_t)ext; - ext->priv_selinux_allow = true; - - memcpy(fname, hosts_target, sizeof(hosts_target)); +} + +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; } } -void do_filp_open_after(hook_fargs3_t *args, void *udata) -{ - if (unlikely(args->local.data0)) - { - char **fname = *(char ***)args->arg1; - memcpy(fname, hosts_source, sizeof(hosts_source)); +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); - struct task_ext *ext = (struct task_ext *)args->local.data0; - ext->priv_selinux_allow = false; + uint32_t* proc_cwd_link_src = (uint32_t*)proc_cwd_link; + for (u32 i = 0; i < 0x30; i++) { + 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 , , #0 + if (task_struct_alloc_lock_offset == 0) { + task_struct_alloc_lock_offset = UZERO; + } + } + } + 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) -{ +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) - { + 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) -{ +static long inline_hook_exit(void* __user reserved) { unhook_func(do_filp_open); + return 0; } KPM_INIT(inline_hook_init); diff --git a/hosts_redirect/hosts_redirect.h b/hosts_redirect/hosts_redirect.h new file mode 100644 index 0000000..3baffb4 --- /dev/null +++ b/hosts_redirect/hosts_redirect.h @@ -0,0 +1,74 @@ +#ifndef __HR_HOSTS_REDIRECT_H +#define __HR_HOSTS_REDIRECT_H + +#include +#include +#include + +#define HASH_LEN_DECLARE u32 hash; u32 len +#define DNAME_INLINE_LEN 32 +#define LOOKUP_FOLLOW 0x0001 + +struct vfsmount { + struct dentry* mnt_root; + struct super_block* mnt_sb; + int mnt_flags; + struct user_namespace* mnt_userns; + // unknow +}; +struct hlist_bl_node { + struct hlist_bl_node* next, ** pprev; +}; +struct qstr { + union { + struct { + HASH_LEN_DECLARE; + }; + u64 hash_len; + }; + const unsigned char* name; +}; +struct dentry { + unsigned int d_flags; + spinlock_t d_seq; + struct hlist_bl_node d_hash; + struct dentry* d_parent; + struct qstr d_name; + struct inode* d_inode; + unsigned char d_iname[DNAME_INLINE_LEN]; + // unknow +}; + +struct path { + struct vfsmount* mnt; + struct dentry* dentry; +}; + +struct file { + union { + struct llist_node fu_llist; + struct rcu_head fu_rcuhead; + } f_u; + struct path f_path; + struct inode* f_inode; + // unknow +}; + +struct fs_struct { + int users; + spinlock_t lock; + spinlock_t seq; + int umask; + int in_exec; + struct path root, pwd; +}; + +struct open_flags { + int open_flag; + umode_t mode; + int acc_mode; + int intent; + int lookup_flags; +}; + +#endif /* __HR_HOSTS_REDIRECT_H */ diff --git a/hosts_redirect/hr_utils.h b/hosts_redirect/hr_utils.h new file mode 100644 index 0000000..52f7c40 --- /dev/null +++ b/hosts_redirect/hr_utils.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 bmax121. All Rights Reserved. + * Copyright (C) 2024 lzghzr. All Rights Reserved. + */ +#ifndef __HR_UTILS_H +#define __HR_UTILS_H + +#include +#include + +#define bits32(n, high, low) ((uint32_t)((n) << (31u - (high))) >> (31u - (high) + (low))) +#define bit(n, st) (((n) >> (st)) & 1) +#define sign64_extend(n, len) \ + (((uint64_t)((n) << (63u - (len - 1))) >> 63u) ? ((n) | (0xFFFFFFFFFFFFFFFF << (len))) : n) + +typedef uint32_t inst_type_t; +typedef uint32_t inst_mask_t; + +#define INST_ADD_64 0x91000000u +#define INST_ADD_64_X0 0x91000000u +#define INST_LDP_64_ 0xA9400000u +#define INST_LDR_64_ 0xF9400000u +#define INST_LDR_64_X0 0xF9400000u +#define INST_LDR_64_SP 0xF94003E0u +#define INST_LDRB 0x39400000u +#define INST_LDRH 0x79400000u +#define INST_TBZ 0x36000000u +#define INST_TBNZ 0x37000000u +#define INST_TBNZ_5 0x37280000u + +#define MASK_ADD_64 0xFF800000u +#define MASK_ADD_64_X0 0xFF8003E0u +#define MASK_LDP_64_ 0xFFC00000u +#define MASK_LDR_64_ 0xFFC00000u +#define MASK_LDR_64_X0 0xFFC003E0u +#define MASK_LDR_64_SP 0xFFC003E0u +#define MASK_LDRB 0xFFC00000u +#define MASK_LDRH 0xFFC00000u +#define MASK_TBZ 0x7F000000u +#define MASK_TBNZ 0x7F000000u +#define MASK_TBNZ_5 0xFFF80000u + +#define ARM64_RET 0xD65F03C0 + +#define lookup_name(func) \ + func = 0; \ + func = (typeof(func))kallsyms_lookup_name(#func); \ + pr_info("kernel function %s addr: %llx\n", #func, func); \ + if (!func) { \ + return -21; \ + } + +#define hook_func(func, argv, before, after, udata) \ + if (!func) { \ + return -22; \ + } \ + hook_err_t hook_err_##func = hook_wrap(func, argv, before, after, udata); \ + if (hook_err_##func) { \ + func = 0; \ + pr_err("hook %s error: %d\n", #func, hook_err_##func); \ + return -23; \ + } else { \ + pr_info("hook %s success\n", #func); \ + } + +#define unhook_func(func) \ + if (func && !is_bad_address(func)) { \ + unhook(func); \ + func = 0; \ + } + +extern char* kfunc_def(d_path)(const struct path* path, char* buf, int buflen); +static inline char* d_path(const struct path* path, char* buf, int buflen) { + kfunc_call(d_path, path, buf, buflen); + kfunc_not_found(); + return NULL; +} + +extern int kfunc_def(kern_path)(const char* name, unsigned int flags, struct path* path); +static inline int kern_path(const char* name, unsigned int flags, struct path* path) { + kfunc_call(kern_path, name, flags, path); + kfunc_not_found(); + return -ESRCH; +} + +#endif /* __HR_UTILS_H */