diff --git a/.github/workflows/build-lkm.yml b/.github/workflows/build-lkm.yml
index 2da532cc0659..e5ed7eac475e 100644
--- a/.github/workflows/build-lkm.yml
+++ b/.github/workflows/build-lkm.yml
@@ -5,6 +5,7 @@ on:
jobs:
build-lkm:
strategy:
+ fail-fast: false
matrix:
kmi:
- android12-5.10
diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml
index 9031b92da45d..e3cd051d5e77 100644
--- a/.github/workflows/build-manager.yml
+++ b/.github/workflows/build-manager.yml
@@ -7,19 +7,24 @@ on:
- '.github/workflows/build-manager.yml'
- '.github/workflows/build-lkm.yml'
- '.github/workflows/ksud.yml'
+ - '.github/workflows/ddl-lkm.yml'
- 'manager/**'
- 'kernel/**'
- 'userspace/**'
+ - 'scripts/ksubot.py'
pull_request:
branches: [ "main", "dev" ]
paths:
- '.github/workflows/build-manager.yml'
- '.github/workflows/build-lkm.yml'
- '.github/workflows/ksud.yml'
+ - '.github/workflows/ddl-lkm.yml'
- 'manager/**'
- 'kernel/**'
- 'userspace/**'
+ - 'scripts/ksubot.py'
workflow_call:
+ workflow_dispatch:
jobs:
build-lkm:
@@ -79,14 +84,14 @@ jobs:
- name: Setup need_upload
id: need_upload
run: |
- if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
+ if [ ! -z "${{ secrets.BOT_TOKEN }}" ] && [ "${{ github.event_name }}" != "workflow_dispatch" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Write key
- if: ${{ ( github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' )) || github.ref_type == 'tag' }}
+ if: ${{ github.event_name != 'pull_request' || github.ref_type == 'tag' }}
run: |
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
{
@@ -95,7 +100,7 @@ jobs:
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}'
echo KEYSTORE_FILE='key.jks'
} >> gradle.properties
- echo ${{ secrets.KEYSTORE }} | base64 -d > key.jks
+ echo "${{ secrets.KEYSTORE }}" | base64 -d > key.jks
fi
- name: Setup Java
@@ -157,11 +162,10 @@ jobs:
- name: Upload to telegram
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
env:
- CHAT_ID: ${{ secrets.CHAT_ID }}
+ CHAT_ID: ${{ vars.CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
- MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
- COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
- COMMIT_URL: ${{ github.event.head_commit.url }}
+ MESSAGE_THREAD_ID: ${{ vars.MESSAGE_THREAD_ID }}
+ GITHUB_EVENT: ${{ toJson(github.event) }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
TITLE: Manager
BRANCH: ${{ github.ref_name }}
diff --git a/.github/workflows/ddk-lkm.yml b/.github/workflows/ddk-lkm.yml
index 4e8e041b39ff..b51722720d91 100644
--- a/.github/workflows/ddk-lkm.yml
+++ b/.github/workflows/ddk-lkm.yml
@@ -31,6 +31,21 @@ jobs:
cd kernel
+ if [[ "${{ inputs.kmi }}" == *"5.1"* ]]; then
+ echo "=== fix modpost ==="
+ sed -i '/s->module = exp->module;/s/^/\/\//' /opt/ddk/src/*/scripts/mod/modpost.c
+ pushd /opt/ddk/kdir/*
+ CMD_O=$(grep 'cmd_scripts/mod/modpost.o := ' scripts/mod/.modpost.o.cmd | sed 's/cmd_scripts\/mod\/modpost.o := //g')
+ CMD_2=$(grep 'cmd_scripts/mod/file2alias.o := ' scripts/mod/.file2alias.o.cmd | sed 's/cmd_scripts\/mod\/file2alias.o := //g')
+ CMD_3=$(grep 'cmd_scripts/mod/sumversion.o := ' scripts/mod/.sumversion.o.cmd | sed 's/cmd_scripts\/mod\/sumversion.o := //g')
+ CMD_L=$(grep 'cmd_scripts/mod/modpost := ' scripts/mod/.modpost.cmd | sed 's/cmd_scripts\/mod\/modpost := //g')
+ $CMD_O
+ $CMD_2
+ $CMD_3
+ $CMD_L
+ popd
+ fi
+
echo "=== Building kernelsu.ko for KMI: ${{ inputs.kmi }} ==="
CONFIG_KSU=m CC=clang make
diff --git a/.gitignore b/.gitignore
index 9e7e95876351..79d923791aec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
.idea
-.vscode
+/.vscode
CLAUDE.md
AGENTS.md
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index 03a9346ea904..7a9148fa8a54 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,10 +1,10 @@
**English** | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
-# KernelSU
+# My KernelSU
-A kernel-based root solution for Android devices.
+A [KernelSU](https://github.com/tiann/KernelSU/commit/423eefe38598ba8090bdc43144e7d5d25a412031)-based root solution for Android devices.
[](https://github.com/tiann/KernelSU/releases/latest)
[](https://hosted.weblate.org/engage/kernelsu)
diff --git a/kernel/.gitignore b/kernel/.gitignore
index 15547df1c7de..85fecbf8cdb4 100644
--- a/kernel/.gitignore
+++ b/kernel/.gitignore
@@ -12,7 +12,7 @@ compile_commands.json
*.mod.c
*.symvers*
*.order
-.*.ko.cmd
+.*.cmd
.tmp_versions/
libs/
obj/
diff --git a/kernel/.vscode/generate_compdb.py b/kernel/.vscode/generate_compdb.py
index 8866913868f4..22c5a0007d8c 100755
--- a/kernel/.vscode/generate_compdb.py
+++ b/kernel/.vscode/generate_compdb.py
@@ -44,6 +44,8 @@ def gen_compile_commands(cmd_file_search_path, out_dir):
if not cmd_file_search_path:
cmd_file_search_path = [out_dir]
+ else:
+ cmd_file_search_path += [out_dir]
cmd_files = []
for search_path in cmd_file_search_path:
diff --git a/kernel/Kbuild b/kernel/Kbuild
index 70511859bd85..5f7157e62ce0 100644
--- a/kernel/Kbuild
+++ b/kernel/Kbuild
@@ -14,7 +14,10 @@ kernelsu-objs += feature.o
kernelsu-objs += ksud.o
kernelsu-objs += seccomp_cache.o
kernelsu-objs += file_wrapper.o
-kernelsu-objs += util.o
+kernelsu-objs += pte.o
+kernelsu-objs += patch.o
+kernelsu-objs += inline_hook.o
+kernelsu-objs += trampoline.o
kernelsu-objs += selinux/selinux.o
kernelsu-objs += selinux/sepolicy.o
@@ -60,12 +63,35 @@ $(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git repo
ccflags-y += -DKSU_VERSION=16
endif
+KSU_NEW_DCACHE_FLUSH := $(shell grep -q __flush_dcache_area $(srctree)/arch/arm64/include/asm/cacheflush.h ; echo $$?)
+$(info -- KSU_NEW_DCACHE_FLUSH: $(KSU_NEW_DCACHE_FLUSH))
+
+KSU_MTE_SYNC_TAGS_DEF := $(shell grep 'void mte_sync_tags' $(srctree)/arch/arm64/include/asm/mte.h | grep -v 'static inline')
+# 6.6
+KSU_MTE_SYNC_TAGS_NR_PAGES := $(shell echo '$(KSU_MTE_SYNC_TAGS_DEF)' | grep -q 'nr_pages' && echo 1 || echo 0)
+# 6.1
+KSU_MTE_SYNC_TAGS_NORMAL := $(shell echo '$(KSU_MTE_SYNC_TAGS_DEF)' | grep -q '(pte_t pte)' && echo 1 || echo 0)
+# 13-5.10
+KSU_MTE_SYNC_TAGS_OLD_PTE := $(shell echo '$(KSU_MTE_SYNC_TAGS_DEF)' | grep -q 'old_pte' && echo 1 || echo 0)
+# 12-5.10
+KSU_MTE_SYNC_TAGS_PTEP := $(shell echo '$(KSU_MTE_SYNC_TAGS_DEF)' | grep -q '*ptep' && echo 1 || echo 0)
+KSU_DEF_MTE_SYNC_TAGS := 'extern $(KSU_MTE_SYNC_TAGS_DEF)'
+
+$(info -- KSU_DEF_MTE_SYNC_TAGS: $(KSU_DEF_MTE_SYNC_TAGS))
+$(info -- KSU_MTE_SYNC_TAGS_NR_PAGES: $(KSU_MTE_SYNC_TAGS_NR_PAGES))
+$(info -- KSU_MTE_SYNC_TAGS_PTEP: $(KSU_MTE_SYNC_TAGS_PTEP))
+$(info -- KSU_MTE_SYNC_TAGS_OLD_PTE: $(KSU_MTE_SYNC_TAGS_OLD_PTE))
+$(info -- KSU_MTE_SYNC_TAGS_NORMAL: $(KSU_MTE_SYNC_TAGS_NORMAL))
+
+OFFICIAL_EXPECTED_SIZE := 0x033b
+OFFICIAL_EXPECTED_HASH := c371061b19d8c7d7d6133c6a9bafe198fa944e50c1b31c9d8daa8d7f1fc2d2d6
+
ifndef KSU_EXPECTED_SIZE
-KSU_EXPECTED_SIZE := 0x033b
+KSU_EXPECTED_SIZE := 384
endif
ifndef KSU_EXPECTED_HASH
-KSU_EXPECTED_HASH := c371061b19d8c7d7d6133c6a9bafe198fa944e50c1b31c9d8daa8d7f1fc2d2d6
+KSU_EXPECTED_HASH := 7e0c6d7278a3bb8e364e0fcba95afaf3666cf5ff3c245a3b63c8833bd0445cc4
endif
ifdef KSU_MANAGER_PACKAGE
@@ -78,6 +104,16 @@ $(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
+ccflags-y += -DOFFICIAL_EXPECTED_SIZE=$(OFFICIAL_EXPECTED_SIZE)
+ccflags-y += -DOFFICIAL_EXPECTED_HASH=\"$(OFFICIAL_EXPECTED_HASH)\"
+
+ccflags-y += -DKSU_NEW_DCACHE_FLUSH=$(KSU_NEW_DCACHE_FLUSH)
+
+ccflags-y += -DKSU_DEF_MTE_SYNC_TAGS=$(KSU_DEF_MTE_SYNC_TAGS)
+ccflags-y += -DKSU_MTE_SYNC_TAGS_NR_PAGES=$(KSU_MTE_SYNC_TAGS_NR_PAGES)
+ccflags-y += -DKSU_MTE_SYNC_TAGS_PTEP=$(KSU_MTE_SYNC_TAGS_PTEP)
+ccflags-y += -DKSU_MTE_SYNC_TAGS_OLD_PTE=$(KSU_MTE_SYNC_TAGS_OLD_PTE)
+ccflags-y += -DKSU_MTE_SYNC_TAGS_NORMAL=$(KSU_MTE_SYNC_TAGS_NORMAL)
ccflags-y += -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat -Wno-missing-prototypes
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
diff --git a/kernel/Makefile b/kernel/Makefile
index 28e5a3a802d6..4cda37033424 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -7,7 +7,7 @@ $(info -- MDIR: $(MDIR))
.PHONY: all compdb clean format check-format
all: check_symbol
- make -C $(KDIR) M=$(MDIR) modules
+ CONFIG_KSU=m make -C $(KDIR) M=$(MDIR) modules
./check_symbol kernelsu.ko $(KDIR)/vmlinux
compdb:
python3 $(MDIR)/.vscode/generate_compdb.py -O $(KDIR) $(MDIR)
diff --git a/kernel/allowlist.c b/kernel/allowlist.c
index 25de2a7ffd9a..e45c5a8d6d72 100644
--- a/kernel/allowlist.c
+++ b/kernel/allowlist.c
@@ -14,6 +14,8 @@
#include
#include
#include
+#include
+#include
#include "klog.h" // IWYU pragma: keep
#include "ksu.h"
@@ -35,26 +37,9 @@ static DEFINE_MUTEX(allowlist_mutex);
static struct root_profile default_root_profile;
static struct non_root_profile default_non_root_profile;
-static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly
- __aligned(PAGE_SIZE);
-static int allow_list_pointer __read_mostly = 0;
-
-static void remove_uid_from_arr(uid_t uid)
-{
- int i;
- for (i = 0; i < allow_list_pointer; i++) {
- if (allow_list_arr[i] == uid) {
- int remaining = allow_list_pointer - 1 - i;
- if (remaining > 0) {
- memmove(&allow_list_arr[i], &allow_list_arr[i + 1],
- remaining * sizeof(allow_list_arr[0]));
- }
- allow_list_pointer--;
- allow_list_arr[allow_list_pointer] = -1;
- return;
- }
- }
-}
+// protected by rcu
+static struct root_profile *current_default_root_profile;
+static struct non_root_profile *current_default_non_root_profile;
static void init_default_profiles()
{
@@ -68,21 +53,24 @@ static void init_default_profiles()
sizeof(default_root_profile.capabilities.effective));
default_root_profile.namespaces = KSU_NS_INHERITED;
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
+ current_default_root_profile = &default_root_profile;
// This means that we will umount modules by default!
default_non_root_profile.umount_modules = true;
+ current_default_non_root_profile = &default_non_root_profile;
}
struct perm_data {
- struct list_head list;
+ struct hlist_node list;
struct rcu_head rcu;
+ struct kref ref;
struct app_profile profile;
};
-static struct list_head allow_list;
-
-static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
-#define BITMAP_UID_MAX ((sizeof(allow_list_bitmap) * BITS_PER_BYTE) - 1)
+// protected by rcu
+#define ALLOW_LIST_BITS 8
+static DEFINE_HASHTABLE(allow_list, ALLOW_LIST_BITS);
+static u16 allow_list_count = 0;
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
@@ -90,11 +78,12 @@ void ksu_persistent_allow_list(void);
void ksu_show_allow_list(void)
{
+ int i;
struct perm_data *p = NULL;
pr_info("ksu_show_allow_list\n");
rcu_read_lock();
- list_for_each_entry_rcu (p, &allow_list, list) {
- pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
+ hash_for_each_rcu (allow_list, i, p, list) {
+ pr_info("uid :%d, allow: %d\n", p->profile.curr_uid,
p->profile.allow_su);
}
rcu_read_unlock();
@@ -106,7 +95,7 @@ static void ksu_grant_root_to_shell()
struct app_profile profile = {
.version = KSU_APP_PROFILE_VER,
.allow_su = true,
- .current_uid = 2000,
+ .curr_uid = 2000,
};
strcpy(profile.key, "com.android.shell");
strcpy(profile.rp_config.profile.selinux_domain,
@@ -115,25 +104,25 @@ static void ksu_grant_root_to_shell()
}
#endif
-bool ksu_get_app_profile(struct app_profile *profile)
+struct app_profile *ksu_get_app_profile(uid_t uid)
{
struct perm_data *p = NULL;
- bool found = false;
- rcu_read_lock();
- list_for_each_entry_rcu (p, &allow_list, list) {
- bool uid_match = profile->current_uid == p->profile.current_uid;
- if (uid_match) {
+ hash_for_each_possible_rcu (allow_list, p, list, uid) {
+ if (uid == p->profile.curr_uid) {
// found it, override it with ours
- memcpy(profile, &p->profile, sizeof(*profile));
- found = true;
- goto exit;
+ break;
}
}
-exit:
- rcu_read_unlock();
- return found;
+ if (!p)
+ return NULL;
+
+ if (!kref_get_unless_zero(&p->ref)) {
+ return NULL;
+ }
+
+ return &p->profile;
}
static inline bool forbid_system_uid(uid_t uid)
@@ -167,11 +156,34 @@ static bool profile_valid(struct app_profile *profile)
return true;
}
+static void release_perm_data(struct kref *ref)
+{
+ struct perm_data *p = container_of(ref, struct perm_data, ref);
+ kfree(p);
+}
+
+static void put_perm_data(struct perm_data *data)
+{
+ kref_put(&data->ref, release_perm_data);
+}
+
+static void release_perm_data_rcu(struct kref *ref)
+{
+ struct perm_data *p = container_of(ref, struct perm_data, ref);
+ kfree_rcu(p, rcu);
+}
+
+static void put_perm_data_rcu(struct perm_data *data)
+{
+ kref_put(&data->ref, release_perm_data_rcu);
+}
+
int ksu_set_app_profile(struct app_profile *profile)
{
- struct perm_data *p = NULL, *np;
+ struct perm_data *p, *np;
+ struct root_profile *old_current_default_root_profile;
+ struct non_root_profile *old_current_default_non_root_profile;
int result = 0;
- u16 count = 0;
if (!profile_valid(profile)) {
pr_err("Failed to set app profile: invalid profile!\n");
@@ -180,11 +192,13 @@ int ksu_set_app_profile(struct app_profile *profile)
mutex_lock(&allowlist_mutex);
- list_for_each_entry (p, &allow_list, list) {
- ++count;
- // both uid and package must match, otherwise it will break multiple package with different user id
- if (profile->current_uid == p->profile.current_uid &&
- !strcmp(profile->key, p->profile.key)) {
+ hash_for_each_possible (allow_list, p, list, profile->curr_uid) {
+ if (profile->curr_uid == p->profile.curr_uid) {
+ if (strcmp(profile->key, p->profile.key) != 0) {
+ pr_warn(
+ "ksu_set_app_profile: key changed: uid=%d orig=%s new=%s\n",
+ profile->curr_uid, p->profile.key, profile->key);
+ }
// found it, just override it all!
np = (struct perm_data *)kzalloc(sizeof(struct perm_data),
GFP_KERNEL);
@@ -192,75 +206,77 @@ int ksu_set_app_profile(struct app_profile *profile)
result = -ENOMEM;
goto out_unlock;
}
+ kref_init(&np->ref);
memcpy(&np->profile, profile, sizeof(*profile));
- list_replace_rcu(&p->list, &np->list);
- kfree_rcu(p, rcu);
+ hlist_replace_rcu(&p->list, &np->list);
+ put_perm_data_rcu(p);
goto out;
}
}
- if (unlikely(count == U16_MAX)) {
+ if (unlikely(allow_list_count == U16_MAX)) {
pr_err("too many app profile\n");
result = -E2BIG;
goto out_unlock;
}
// not found, alloc a new node!
- p = (struct perm_data *)kzalloc(sizeof(struct perm_data), GFP_KERNEL);
- if (!p) {
+ np = (struct perm_data *)kzalloc(sizeof(struct perm_data), GFP_KERNEL);
+ if (!np) {
pr_err("ksu_set_app_profile alloc failed\n");
result = -ENOMEM;
goto out_unlock;
}
- memcpy(&p->profile, profile, sizeof(*profile));
+ kref_init(&np->ref);
+ memcpy(&np->profile, profile, sizeof(*profile));
if (profile->allow_su) {
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
- profile->key, profile->current_uid,
- profile->rp_config.profile.gid,
+ profile->key, profile->curr_uid, profile->rp_config.profile.gid,
profile->rp_config.profile.selinux_domain);
} else {
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
- profile->key, profile->current_uid,
+ profile->key, profile->curr_uid,
profile->nrp_config.profile.umount_modules);
}
- list_add_tail_rcu(&p->list, &allow_list);
+ hash_add_rcu(allow_list, &np->list, np->profile.curr_uid);
+ ++allow_list_count;
out:
result = 0;
// check if the default profiles is changed, cache it to a single struct to accelerate access.
- if (unlikely(!strcmp(profile->key, "$"))) {
- // set default non root profile
- memcpy(&default_non_root_profile, &profile->nrp_config.profile,
- sizeof(default_non_root_profile));
- } else if (unlikely(!strcmp(profile->key, "#"))) {
- // set default root profile
- // TODO: Do we really need this?
- memcpy(&default_root_profile, &profile->rp_config.profile,
- sizeof(default_root_profile));
- } else if (profile->current_uid <= BITMAP_UID_MAX) {
- if (profile->allow_su)
- allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |=
- 1 << (profile->current_uid % BITS_PER_BYTE);
- else
- allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &=
- ~(1 << (profile->current_uid % BITS_PER_BYTE));
- } else {
- if (profile->allow_su) {
- /*
- * 1024 apps with uid higher than BITMAP_UID_MAX
- * registered to request superuser?
- */
- if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
- pr_err("too many apps registered\n");
- WARN_ON(1);
- } else {
- allow_list_arr[allow_list_pointer++] = profile->current_uid;
+ if (unlikely(profile->curr_uid == KSU_APP_PROFILE_PRESERVE_UID)) {
+ if (unlikely(!strcmp(profile->key, "$"))) {
+ // set default non root profile
+ kref_get(&np->ref);
+ old_current_default_non_root_profile =
+ rcu_dereference_protected(current_default_non_root_profile,
+ lockdep_is_held(&allowlist_mutex));
+ rcu_assign_pointer(current_default_non_root_profile,
+ &np->profile.nrp_config.profile);
+ if (unlikely(old_current_default_non_root_profile !=
+ &default_non_root_profile)) {
+ p = container_of(old_current_default_non_root_profile,
+ struct perm_data, profile.nrp_config.profile);
+ put_perm_data_rcu(p);
+ }
+ } else if (unlikely(!strcmp(profile->key, "#"))) {
+ // set default root profile
+ // TODO: Do we really need this?
+ kref_get(&np->ref);
+ old_current_default_root_profile =
+ rcu_dereference_protected(current_default_root_profile,
+ lockdep_is_held(&allowlist_mutex));
+ rcu_assign_pointer(current_default_root_profile,
+ &np->profile.rp_config.profile);
+ if (unlikely(old_current_default_root_profile !=
+ &default_root_profile)) {
+ p = container_of(old_current_default_root_profile,
+ struct perm_data, profile.rp_config.profile);
+ put_perm_data_rcu(p);
}
- } else {
- remove_uid_from_arr(profile->current_uid);
}
}
@@ -271,7 +287,7 @@ int ksu_set_app_profile(struct app_profile *profile)
bool __ksu_is_allow_uid(uid_t uid)
{
- int i;
+ struct perm_data *p;
if (forbid_system_uid(uid)) {
// do not bother going through the list if it's system
@@ -284,15 +300,18 @@ bool __ksu_is_allow_uid(uid_t uid)
return true;
}
- if (likely(uid <= BITMAP_UID_MAX)) {
- return !!(allow_list_bitmap[uid / BITS_PER_BYTE] &
- (1 << (uid % BITS_PER_BYTE)));
- } else {
- for (i = 0; i < allow_list_pointer; i++) {
- if (allow_list_arr[i] == uid)
- return true;
+ if (unlikely(allow_shell) && uid == 2000) {
+ return true;
+ }
+
+ rcu_read_lock();
+ hash_for_each_possible_rcu (allow_list, p, list, uid) {
+ if (uid == p->profile.curr_uid && p->profile.allow_su) {
+ rcu_read_unlock();
+ return true;
}
}
+ rcu_read_unlock();
return false;
}
@@ -308,54 +327,87 @@ bool __ksu_is_allow_uid_for_current(uid_t uid)
bool ksu_uid_should_umount(uid_t uid)
{
- struct app_profile profile = { .current_uid = uid };
+ struct app_profile *profile;
+ bool res;
if (likely(ksu_is_manager_appid_valid()) &&
unlikely(ksu_get_manager_appid() == uid % PER_USER_RANGE)) {
// we should not umount on manager!
return false;
}
- bool found = ksu_get_app_profile(&profile);
- if (!found) {
+ rcu_read_lock();
+ profile = ksu_get_app_profile(uid);
+ if (!profile) {
// no app profile found, it must be non root app
- return default_non_root_profile.umount_modules;
- }
- if (profile.allow_su) {
+ res = current_default_non_root_profile->umount_modules;
+ } else if (profile->allow_su) {
// if found and it is granted to su, we shouldn't umount for it
- return false;
+ res = false;
} else {
// found an app profile
- if (profile.nrp_config.use_default) {
- return default_non_root_profile.umount_modules;
+ if (profile->nrp_config.use_default) {
+ res = current_default_non_root_profile->umount_modules;
} else {
- return profile.nrp_config.profile.umount_modules;
+ res = profile->nrp_config.profile.umount_modules;
}
}
+ rcu_read_unlock();
+
+ if (profile)
+ ksu_put_app_profile(profile);
+ return res;
}
-void ksu_get_root_profile(uid_t uid, struct root_profile *profile)
+void ksu_put_app_profile(struct app_profile *profile)
+{
+ struct perm_data *p = container_of(profile, struct perm_data, profile);
+ put_perm_data(p);
+}
+
+struct root_profile *ksu_get_root_profile(uid_t uid)
{
struct perm_data *p = NULL;
+ struct root_profile *res = NULL;
+ rcu_read_lock();
if (is_uid_manager(uid)) {
goto use_default;
}
- rcu_read_lock();
- list_for_each_entry_rcu (p, &allow_list, list) {
- if (uid == p->profile.current_uid && p->profile.allow_su) {
+ if (unlikely(allow_shell && uid == SHELL_UID)) {
+ goto use_default;
+ }
+
+ hash_for_each_possible_rcu (allow_list, p, list, uid) {
+ if (uid == p->profile.curr_uid && p->profile.allow_su) {
if (!p->profile.rp_config.use_default) {
- memcpy(profile, &p->profile.rp_config.profile,
- sizeof(*profile));
- rcu_read_unlock();
- return;
+ if (kref_get_unless_zero(&p->ref))
+ res = &p->profile.rp_config.profile;
}
+ break;
}
}
+
+ if (unlikely(!res)) {
+ use_default:
+ res = current_default_root_profile;
+ if (unlikely(res != &default_root_profile)) {
+ if (kref_get_unless_zero(&p->ref))
+ p = container_of(res, struct perm_data,
+ profile.rp_config.profile);
+ }
+ }
+
rcu_read_unlock();
+ return res;
+}
-use_default:
- // use default profile
- memcpy(profile, &default_root_profile, sizeof(*profile));
+void ksu_put_root_profile(struct root_profile *profile)
+{
+ if (likely(profile == &default_root_profile))
+ return;
+ struct perm_data *p =
+ container_of(profile, struct perm_data, profile.rp_config.profile);
+ put_perm_data(p);
}
bool ksu_get_allow_list(int *array, u16 length, u16 *out_length, u16 *out_total,
@@ -363,13 +415,14 @@ bool ksu_get_allow_list(int *array, u16 length, u16 *out_length, u16 *out_total,
{
struct perm_data *p = NULL;
u16 i = 0, j = 0;
+ int iter;
rcu_read_lock();
- list_for_each_entry_rcu (p, &allow_list, list) {
+ hash_for_each_rcu (allow_list, iter, p, list) {
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
if (p->profile.allow_su == allow &&
- !is_uid_manager(p->profile.current_uid)) {
+ !is_uid_manager(p->profile.curr_uid)) {
if (j < length) {
- array[j++] = p->profile.current_uid;
+ array[j++] = p->profile.curr_uid;
}
++i;
}
@@ -392,6 +445,7 @@ static void do_persistent_allow_list(struct callback_head *_cb)
u32 version = FILE_FORMAT_VERSION;
struct perm_data *p = NULL;
loff_t off = 0;
+ int i;
const struct cred *saved = override_creds(ksu_cred);
struct file *fp =
@@ -413,9 +467,9 @@ static void do_persistent_allow_list(struct callback_head *_cb)
}
mutex_lock(&allowlist_mutex);
- list_for_each_entry (p, &allow_list, list) {
+ hash_for_each (allow_list, i, p, list) {
pr_info("save allow list, name: %s uid :%d, allow: %d\n",
- p->profile.key, p->profile.current_uid, p->profile.allow_su);
+ p->profile.key, p->profile.curr_uid, p->profile.allow_su);
kernel_write(fp, &p->profile, sizeof(p->profile), &off);
}
@@ -432,11 +486,14 @@ void ksu_persistent_allow_list()
{
struct task_struct *tsk;
+ rcu_read_lock();
tsk = get_pid_task(find_vpid(1), PIDTYPE_PID);
if (!tsk) {
+ rcu_read_unlock();
pr_err("save_allow_list find init task err\n");
return;
}
+ rcu_read_unlock();
struct callback_head *cb =
kzalloc(sizeof(struct callback_head), GFP_KERNEL);
@@ -499,7 +556,7 @@ void ksu_load_allow_list()
}
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n", profile.key,
- profile.current_uid, profile.allow_su);
+ profile.curr_uid, profile.allow_su);
ksu_set_app_profile(&profile);
}
@@ -512,7 +569,8 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *),
void *data)
{
struct perm_data *np = NULL;
- struct perm_data *n = NULL;
+ struct hlist_node *tmp;
+ int i;
if (!ksu_boot_completed) {
pr_info("boot not completed, skip prune\n");
@@ -521,21 +579,17 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *),
bool modified = false;
mutex_lock(&allowlist_mutex);
- list_for_each_entry_safe (np, n, &allow_list, list) {
- uid_t uid = np->profile.current_uid;
+ hash_for_each_safe (allow_list, i, tmp, np, list) {
+ uid_t uid = np->profile.curr_uid;
char *package = np->profile.key;
// we use this uid for special cases, don't prune it!
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
if (!is_preserved_uid && !is_uid_valid(uid, package, data)) {
modified = true;
pr_info("prune uid: %d, package: %s\n", uid, package);
- list_del_rcu(&np->list);
- kfree_rcu(np, rcu);
- if (likely(uid <= BITMAP_UID_MAX)) {
- allow_list_bitmap[uid / BITS_PER_BYTE] &=
- ~(1 << (uid % BITS_PER_BYTE));
- }
- remove_uid_from_arr(uid);
+ hlist_del_rcu(&np->list);
+ put_perm_data_rcu(np);
+ --allow_list_count;
}
}
mutex_unlock(&allowlist_mutex);
@@ -548,29 +602,31 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *),
void ksu_allowlist_init(void)
{
- int i;
-
- BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
- BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
-
- for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
- allow_list_arr[i] = -1;
-
- INIT_LIST_HEAD(&allow_list);
-
init_default_profiles();
}
void ksu_allowlist_exit(void)
{
struct perm_data *np = NULL;
- struct perm_data *n = NULL;
+ struct hlist_node *tmp;
+ int i;
// free allowlist
mutex_lock(&allowlist_mutex);
- list_for_each_entry_safe (np, n, &allow_list, list) {
- list_del(&np->list);
- kfree(np);
+ if (unlikely(current_default_non_root_profile !=
+ &default_non_root_profile)) {
+ np = container_of(current_default_non_root_profile, struct perm_data,
+ profile.nrp_config.profile);
+ put_perm_data_rcu(np);
+ }
+ if (unlikely(current_default_root_profile != &default_root_profile)) {
+ np = container_of(current_default_root_profile, struct perm_data,
+ profile.rp_config.profile);
+ put_perm_data_rcu(np);
+ }
+ hash_for_each_safe (allow_list, i, tmp, np, list) {
+ hlist_del(&np->list);
+ put_perm_data_rcu(np);
}
mutex_unlock(&allowlist_mutex);
}
diff --git a/kernel/allowlist.h b/kernel/allowlist.h
index 2afc303836c2..418a3a9d4197 100644
--- a/kernel/allowlist.h
+++ b/kernel/allowlist.h
@@ -35,11 +35,16 @@ void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, char *, void *),
void *data);
void ksu_persistent_allow_list();
-bool ksu_get_app_profile(struct app_profile *);
+// should be called with rcu read lock
+struct app_profile *ksu_get_app_profile(uid_t uid);
+// only used to put the app_profile returned by ksu_get_app_profile
+void ksu_put_app_profile(struct app_profile *);
int ksu_set_app_profile(struct app_profile *);
bool ksu_uid_should_umount(uid_t uid);
-void ksu_get_root_profile(uid_t uid, struct root_profile *);
+struct root_profile *ksu_get_root_profile(uid_t uid);
+// only used to put the root_profile returned by ksu_get_root_profile
+void ksu_put_root_profile(struct root_profile *);
static inline bool is_appuid(uid_t uid)
{
@@ -53,3 +58,5 @@ static inline bool is_isolated_process(uid_t uid)
return appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID;
}
#endif
+
+extern bool allow_shell;
diff --git a/kernel/apk_sign.c b/kernel/apk_sign.c
index 1c84127bd367..65a2f3fe26db 100644
--- a/kernel/apk_sign.c
+++ b/kernel/apk_sign.c
@@ -360,5 +360,7 @@ bool is_manager_apk(char *path)
return false;
}
#endif
- return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH);
+ return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH) ||
+ check_v2_signature(path, OFFICIAL_EXPECTED_SIZE,
+ OFFICIAL_EXPECTED_HASH);
}
\ No newline at end of file
diff --git a/kernel/app_profile.c b/kernel/app_profile.c
index f544dd8de4a3..453aee65dd0d 100644
--- a/kernel/app_profile.c
+++ b/kernel/app_profile.c
@@ -66,7 +66,7 @@ static void disable_seccomp(void)
{
struct task_struct *fake;
- fake = kmalloc(sizeof(*fake), GFP_ATOMIC);
+ fake = kmalloc(sizeof(*fake), GFP_KERNEL);
if (!fake) {
pr_warn("failed to alloc fake task_struct\n");
return;
@@ -107,7 +107,7 @@ void escape_with_root_profile(void)
struct cred *cred;
struct task_struct *p = current;
struct task_struct *t;
- struct root_profile profile;
+ struct root_profile *profile;
cred = prepare_creds();
if (!cred) {
@@ -121,34 +121,34 @@ void escape_with_root_profile(void)
return;
}
- ksu_get_root_profile(cred->uid.val, &profile);
+ profile = ksu_get_root_profile(cred->uid.val);
- cred->uid.val = profile.uid;
- cred->suid.val = profile.uid;
- cred->euid.val = profile.uid;
- cred->fsuid.val = profile.uid;
+ cred->uid.val = profile->uid;
+ cred->suid.val = profile->uid;
+ cred->euid.val = profile->uid;
+ cred->fsuid.val = profile->uid;
- cred->gid.val = profile.gid;
- cred->fsgid.val = profile.gid;
- cred->sgid.val = profile.gid;
- cred->egid.val = profile.gid;
+ cred->gid.val = profile->gid;
+ cred->fsgid.val = profile->gid;
+ cred->sgid.val = profile->gid;
+ cred->egid.val = profile->gid;
cred->securebits = 0;
- BUILD_BUG_ON(sizeof(profile.capabilities.effective) !=
+ BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
sizeof(kernel_cap_t));
// setup capabilities
// we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process
// we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec!
- u64 cap_for_ksud = profile.capabilities.effective | CAP_DAC_READ_SEARCH;
+ u64 cap_for_ksud = profile->capabilities.effective | CAP_DAC_READ_SEARCH;
memcpy(&cred->cap_effective, &cap_for_ksud, sizeof(cred->cap_effective));
- memcpy(&cred->cap_permitted, &profile.capabilities.effective,
+ memcpy(&cred->cap_permitted, &profile->capabilities.effective,
sizeof(cred->cap_permitted));
- memcpy(&cred->cap_bset, &profile.capabilities.effective,
+ memcpy(&cred->cap_bset, &profile->capabilities.effective,
sizeof(cred->cap_bset));
- setup_groups(&profile, cred);
- setup_selinux(profile.selinux_domain, cred);
+ setup_groups(profile, cred);
+ setup_selinux(profile->selinux_domain, cred);
commit_creds(cred);
@@ -158,7 +158,8 @@ void escape_with_root_profile(void)
ksu_set_task_tracepoint_flag(t);
}
- setup_mount_ns(profile.namespaces);
+ setup_mount_ns(profile->namespaces);
+ ksu_put_root_profile(profile);
}
void escape_to_root_for_init(void)
diff --git a/kernel/app_profile.h b/kernel/app_profile.h
index 357d0a1db947..2b54ff55333d 100644
--- a/kernel/app_profile.h
+++ b/kernel/app_profile.h
@@ -41,7 +41,7 @@ struct app_profile {
// this is usually the package of the app, but can be other value for special apps
char key[KSU_MAX_PACKAGE_NAME];
- int32_t current_uid;
+ int32_t curr_uid;
bool allow_su;
union {
diff --git a/kernel/hook.h b/kernel/hook.h
new file mode 100644
index 000000000000..fab52d5a77ea
--- /dev/null
+++ b/kernel/hook.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2023 bmax121. All Rights Reserved.
+ */
+
+#ifndef __KSU_H_HOOK_
+#define __KSU_H_HOOK_
+#include "linux/types.h" // IWYU pragma: keep
+#include "linux/version.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0)
+#include "asm/patching.h" // IWYU pragma: keep
+#else
+#include "asm/insn.h" // IWYU pragma: keep
+#endif
+
+// https://github.com/bmax121/KernelPatch/blob/94e5be9cc3f8a6fbd9574155e3e9753200ab9bfb/kernel/include/hook.h#L54
+
+#define HOOK_INTO_BRANCH_FUNC
+
+typedef enum {
+ HOOK_NO_ERR = 0,
+ HOOK_BAD_ADDRESS = 4095,
+ HOOK_DUPLICATED = 4094,
+ HOOK_NO_MEM = 4093,
+ HOOK_BAD_RELO = 4092,
+ HOOK_TRANSIT_NO_MEM = 4091,
+ HOOK_CHAIN_FULL = 4090,
+} hook_err_t;
+
+#define HOOK_MEM_REGION_NUM 4
+#define TRAMPOLINE_MAX_NUM 6
+#define RELOCATE_INST_NUM (4 * 8 + 8 - 4)
+
+#define HOOK_CHAIN_NUM 0x10
+
+#define ARM64_NOP 0xd503201f
+#define ARM64_BTI_C 0xd503245f
+#define ARM64_BTI_J 0xd503249f
+#define ARM64_BTI_JC 0xd50324df
+#define ARM64_PACIASP 0xd503233f
+#define ARM64_PACIBSP 0xd503237f
+
+typedef struct {
+ // in
+ uint64_t func_addr;
+ uint64_t origin_addr;
+ uint64_t replace_addr;
+ uint64_t relo_addr;
+ // out
+ int32_t tramp_insts_num;
+ int32_t relo_insts_num;
+ uint32_t origin_insts[TRAMPOLINE_MAX_NUM] __attribute__((aligned(8)));
+ uint32_t tramp_insts[TRAMPOLINE_MAX_NUM] __attribute__((aligned(8)));
+ uint32_t relo_insts[RELOCATE_INST_NUM] __attribute__((aligned(8)));
+} hook_t __attribute__((aligned(8)));
+
+static inline int is_bad_address(void *addr)
+{
+ return ((uint64_t)addr & 0x8000000000000000) != 0x8000000000000000;
+}
+
+int32_t branch_from_to(uint32_t *tramp_buf, uint64_t src_addr,
+ uint64_t dst_addr);
+int32_t branch_relative(uint32_t *buf, uint64_t src_addr, uint64_t dst_addr);
+int32_t branch_absolute(uint32_t *buf, uint64_t addr);
+int32_t ret_absolute(uint32_t *buf, uint64_t addr);
+
+hook_err_t hook_prepare(hook_t *hook);
+int hook_install(hook_t *hook);
+
+/**
+ * @brief Inline-hook function which address is @param func with function @param replace,
+ * after hook, original @param func is backuped in @param backup.
+ *
+ * @note If multiple modules hook this function simultaneously,
+ * it will cause abnormality when unload the modules. Please use hook_wrap instead
+ *
+ * @see hook_wrap
+ *
+ * @param func
+ * @param replace
+ * @param backup
+ * @return hook_err_t
+ */
+hook_err_t hook(void *func, void *replace, void **backup);
+
+#define KSU_PATCH_TEXT_FLUSH_DCACHE 1
+#define KSU_PATCH_TEXT_FLUSH_ICACHE 2
+
+int ksu_patch_text(void *dst, void *src, size_t len, int flags);
+
+#endif
diff --git a/kernel/inline_hook.c b/kernel/inline_hook.c
new file mode 100644
index 000000000000..9a991db17799
--- /dev/null
+++ b/kernel/inline_hook.c
@@ -0,0 +1,607 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2023 bmax121. All Rights Reserved.
+ */
+#include "hook.h"
+#include "klog.h" // IWYU pragma: keep
+#include "linux/compiler.h"
+#include "linux/cpumask.h"
+#include "linux/gfp.h" // IWYU pragma: keep
+#include "linux/uaccess.h"
+#include "linux/vmalloc.h"
+#include "linux/stop_machine.h"
+#include "asm/cacheflush.h"
+#include "asm-generic/fixmap.h"
+#include "asm/pgtable.h"
+
+#include "pte.h"
+
+// https://github.com/bmax121/KernelPatch/blob/94e5be9cc3f8a6fbd9574155e3e9753200ab9bfb/kernel/base/hook.c#L562-L603
+
+#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)
+#define align_ceil(x, align) \
+ (((u64)(x) + (u64)(align) - 1) & ~((u64)(align) - 1))
+
+typedef uint32_t inst_type_t;
+typedef uint32_t inst_mask_t;
+
+#define INST_B 0x14000000
+#define INST_BC 0x54000000
+#define INST_BL 0x94000000
+#define INST_ADR 0x10000000
+#define INST_ADRP 0x90000000
+#define INST_LDR_32 0x18000000
+#define INST_LDR_64 0x58000000
+#define INST_LDRSW_LIT 0x98000000
+#define INST_PRFM_LIT 0xD8000000
+#define INST_LDR_SIMD_32 0x1C000000
+#define INST_LDR_SIMD_64 0x5C000000
+#define INST_LDR_SIMD_128 0x9C000000
+#define INST_CBZ 0x34000000
+#define INST_CBNZ 0x35000000
+#define INST_TBZ 0x36000000
+#define INST_TBNZ 0x37000000
+#define INST_HINT 0xD503201F
+#define INST_IGNORE 0x0
+
+#define MASK_B 0xFC000000
+#define MASK_BC 0xFF000010
+#define MASK_BL 0xFC000000
+#define MASK_ADR 0x9F000000
+#define MASK_ADRP 0x9F000000
+#define MASK_LDR_32 0xFF000000
+#define MASK_LDR_64 0xFF000000
+#define MASK_LDRSW_LIT 0xFF000000
+#define MASK_PRFM_LIT 0xFF000000
+#define MASK_LDR_SIMD_32 0xFF000000
+#define MASK_LDR_SIMD_64 0xFF000000
+#define MASK_LDR_SIMD_128 0xFF000000
+#define MASK_CBZ 0x7F000000u
+#define MASK_CBNZ 0x7F000000u
+#define MASK_TBZ 0x7F000000u
+#define MASK_TBNZ 0x7F000000u
+#define MASK_HINT 0xFFFFF01F
+#define MASK_IGNORE 0x0
+
+static inst_mask_t masks[] = {
+ MASK_B, MASK_BC, MASK_BL, MASK_ADR,
+ MASK_ADRP, MASK_LDR_32, MASK_LDR_64, MASK_LDRSW_LIT,
+ MASK_PRFM_LIT, MASK_LDR_SIMD_32, MASK_LDR_SIMD_64, MASK_LDR_SIMD_128,
+ MASK_CBZ, MASK_CBNZ, MASK_TBZ, MASK_TBNZ,
+ MASK_IGNORE,
+};
+static inst_type_t types[] = {
+ INST_B, INST_BC, INST_BL, INST_ADR,
+ INST_ADRP, INST_LDR_32, INST_LDR_64, INST_LDRSW_LIT,
+ INST_PRFM_LIT, INST_LDR_SIMD_32, INST_LDR_SIMD_64, INST_LDR_SIMD_128,
+ INST_CBZ, INST_CBNZ, INST_TBZ, INST_TBNZ,
+ INST_IGNORE,
+};
+
+static int32_t relo_len[] = {
+ 6, 8, 8, 4, 4, 6, 6, 6, 8, 8, 8, 8, 6, 6, 6, 6, 2
+};
+
+// static uint64_t sign_extend(uint64_t x, uint32_t len)
+// {
+// char sign_bit = bit(x, len - 1);
+// unsigned long sign_mask = 0 - sign_bit;
+// x |= ((sign_mask >> len) << len);
+// return x;
+// }
+
+static int is_in_tramp(hook_t *hook, uint64_t addr)
+{
+ uint64_t tramp_start = hook->origin_addr;
+ uint64_t tramp_end = tramp_start + hook->tramp_insts_num * 4;
+ if (addr >= tramp_start && addr < tramp_end) {
+ return 1;
+ }
+ return 0;
+}
+
+static uint64_t relo_in_tramp(hook_t *hook, uint64_t addr)
+{
+ uint64_t tramp_start = hook->origin_addr;
+ uint64_t tramp_end = tramp_start + hook->tramp_insts_num * 4;
+ if (!(addr >= tramp_start && addr < tramp_end))
+ return addr;
+ uint32_t addr_inst_index = (addr - tramp_start) / 4;
+ uint64_t fix_addr = hook->relo_addr;
+ for (int i = 0; i < addr_inst_index; i++) {
+ inst_type_t inst = hook->origin_insts[i];
+ for (int j = 0; j < sizeof(relo_len) / sizeof(relo_len[0]); j++) {
+ if ((inst & masks[j]) == types[j]) {
+ fix_addr += relo_len[j] * 4;
+ break;
+ }
+ }
+ }
+ return fix_addr;
+}
+
+#ifdef HOOK_INTO_BRANCH_FUNC
+
+#endif
+
+static hook_err_t relo_b(hook_t *hook, uint64_t inst_addr, uint32_t inst,
+ inst_type_t type)
+{
+ uint32_t *buf = hook->relo_insts + hook->relo_insts_num;
+ uint64_t imm64;
+ if (type == INST_BC) {
+ uint64_t imm19 = bits32(inst, 23, 5);
+ imm64 = sign64_extend(imm19 << 2u, 21u);
+ } else {
+ uint64_t imm26 = bits32(inst, 25, 0);
+ imm64 = sign64_extend(imm26 << 2u, 28u);
+ }
+ uint64_t addr = inst_addr + imm64;
+ addr = relo_in_tramp(hook, addr);
+
+ uint32_t idx = 0;
+ if (type == INST_BC) {
+ buf[idx++] = (inst & 0xFF00001F) | 0x40u; // B. #8
+ buf[idx++] = 0x14000006; // B #24
+ }
+ buf[idx++] = 0x58000051; // LDR X17, #8
+ buf[idx++] = 0x14000003; // B #12
+ buf[idx++] = addr & 0xFFFFFFFF;
+ buf[idx++] = addr >> 32u;
+ if (type == INST_BL) {
+ buf[idx++] = 0x1000001E; // ADR X30, .
+ buf[idx++] = 0x910033DE; // ADD X30, X30, #12
+ buf[idx++] = 0xD65F0220; // RET X17
+ } else {
+ buf[idx++] = 0xD65F0220; // RET X17
+ }
+ buf[idx++] = ARM64_NOP;
+ return HOOK_NO_ERR;
+}
+
+static hook_err_t relo_adr(hook_t *hook, uint64_t inst_addr, uint32_t inst,
+ inst_type_t type)
+{
+ uint32_t *buf = hook->relo_insts + hook->relo_insts_num;
+
+ uint32_t xd = bits32(inst, 4, 0);
+ uint64_t immlo = bits32(inst, 30, 29);
+ uint64_t immhi = bits32(inst, 23, 5);
+ uint64_t addr;
+
+ if (type == INST_ADR) {
+ addr = inst_addr + sign64_extend((immhi << 2u) | immlo, 21u);
+ } else {
+ addr =
+ (inst_addr + sign64_extend((immhi << 14u) | (immlo << 12u), 33u)) &
+ 0xFFFFFFFFFFFFF000;
+ if (is_in_tramp(hook, addr))
+ return -HOOK_BAD_RELO;
+ }
+ buf[0] = 0x58000040u | xd; // LDR Xd, #8
+ buf[1] = 0x14000003; // B #12
+ buf[2] = addr & 0xFFFFFFFF;
+ buf[3] = addr >> 32u;
+ return HOOK_NO_ERR;
+}
+
+static hook_err_t relo_ldr(hook_t *hook, uint64_t inst_addr, uint32_t inst,
+ inst_type_t type)
+{
+ uint32_t *buf = hook->relo_insts + hook->relo_insts_num;
+
+ uint32_t rt = bits32(inst, 4, 0);
+ uint64_t imm19 = bits32(inst, 23, 5);
+ uint64_t offset = sign64_extend((imm19 << 2u), 21u);
+ uint64_t addr = inst_addr + offset;
+
+ if (is_in_tramp(hook, addr) && type != INST_PRFM_LIT)
+ return -HOOK_BAD_RELO;
+
+ addr = relo_in_tramp(hook, addr);
+
+ if (type == INST_LDR_32 || type == INST_LDR_64 || type == INST_LDRSW_LIT) {
+ buf[0] = 0x58000060u | rt; // LDR Xt, #12
+ if (type == INST_LDR_32) {
+ buf[1] = 0xB9400000 | rt | (rt << 5u); // LDR Wt, [Xt]
+ } else if (type == INST_LDR_64) {
+ buf[1] = 0xF9400000 | rt | (rt << 5u); // LDR Xt, [Xt]
+ } else {
+ // LDRSW_LIT
+ buf[1] = 0xB9800000 | rt | (rt << 5u); // LDRSW Xt, [Xt]
+ }
+ buf[2] = 0x14000004; // B #16
+ buf[3] = ARM64_NOP;
+ buf[4] = addr & 0xFFFFFFFF;
+ buf[5] = addr >> 32u;
+ } else {
+ buf[0] = 0xA93F47F0; // STP X16, X17, [SP, -0x10]
+ buf[1] = 0x58000091; // LDR X17, #16
+ if (type == INST_PRFM_LIT) {
+ buf[2] = 0xF9800220 | rt; // PRFM Rt, [X17]
+ } else if (type == INST_LDR_SIMD_32) {
+ buf[2] = 0xBD400220 | rt; // LDR St, [X17]
+ } else if (type == INST_LDR_SIMD_64) {
+ buf[2] = 0xFD400220 | rt; // LDR Dt, [X17]
+ } else {
+ // LDR_SIMD_128
+ buf[2] = 0x3DC00220u | rt; // LDR Qt, [X17]
+ }
+ buf[3] = 0xF85F83F1; // LDR X17, [SP, -0x8]
+ buf[4] = 0x14000004; // B #16
+ buf[5] = ARM64_NOP;
+ buf[6] = addr & 0xFFFFFFFF;
+ buf[7] = addr >> 32u;
+ }
+ return HOOK_NO_ERR;
+}
+
+static hook_err_t relo_cb(hook_t *hook, uint64_t inst_addr, uint32_t inst,
+ inst_type_t type)
+{
+ uint32_t *buf = hook->relo_insts + hook->relo_insts_num;
+
+ uint64_t imm19 = bits32(inst, 23, 5);
+ uint64_t offset = sign64_extend((imm19 << 2u), 21u);
+ uint64_t addr = inst_addr + offset;
+ addr = relo_in_tramp(hook, addr);
+
+ buf[0] = (inst & 0xFF00001F) | 0x40u; // CB(N)Z Rt, #8
+ buf[1] = 0x14000005; // B #20
+ buf[2] = 0x58000051; // LDR X17, #8
+ buf[3] = 0xD65F0220; // RET X17
+ buf[4] = addr & 0xFFFFFFFF;
+ buf[5] = addr >> 32u;
+ return HOOK_NO_ERR;
+}
+
+static hook_err_t relo_tb(hook_t *hook, uint64_t inst_addr, uint32_t inst,
+ inst_type_t type)
+{
+ uint32_t *buf = hook->relo_insts + hook->relo_insts_num;
+
+ uint64_t imm14 = bits32(inst, 18, 5);
+ uint64_t offset = sign64_extend((imm14 << 2u), 16u);
+ uint64_t addr = inst_addr + offset;
+ addr = relo_in_tramp(hook, addr);
+
+ buf[0] = (inst & 0xFFF8001F) | 0x40u; // TB(N)Z Rt, #, #8
+ buf[1] = 0x14000005; // B #20
+ buf[2] = 0x58000051; // LDR X17, #8
+ buf[3] = 0xd65f0220; // RET X17
+ buf[4] = addr & 0xFFFFFFFF;
+ buf[5] = addr >> 32u;
+ return HOOK_NO_ERR;
+}
+
+static hook_err_t relo_ignore(hook_t *hook, uint64_t inst_addr, uint32_t inst,
+ inst_type_t type)
+{
+ uint32_t *buf = hook->relo_insts + hook->relo_insts_num;
+ buf[0] = inst;
+ buf[1] = ARM64_NOP;
+ return HOOK_NO_ERR;
+}
+
+static uint32_t can_b_rel(uint64_t src_addr, uint64_t dst_addr)
+{
+#define B_REL_RANGE ((1 << 25) << 2)
+ return ((dst_addr >= src_addr) && (dst_addr - src_addr <= B_REL_RANGE)) ||
+ ((src_addr >= dst_addr) && (src_addr - dst_addr <= B_REL_RANGE));
+}
+
+int32_t branch_relative(uint32_t *buf, uint64_t src_addr, uint64_t dst_addr)
+{
+ if (can_b_rel(src_addr, dst_addr)) {
+ buf[0] = 0x14000000u |
+ (((dst_addr - src_addr) & 0x0FFFFFFFu) >> 2u); // B