diff --git a/README.md b/README.md new file mode 100644 index 00000000..9bef55f3 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# KernelSU + +A Kernel based root solution for Android GKI. + +## Usage + +1. Flash a custom kernel with KernelSU, you can build it yourself or use prebuilt boot.img. +2. Install Manager App and enjoy :) + +## Build + +### Build GKI Kernel + +1. Put the `kernel` directory to GKI source's `common/drivers` +2. Edit `common/drivers/Makefile` and add the driver to target +3. Follow the [GKI build instruction](https://source.android.com/docs/core/architecture/kernel/generic-kernel-image) and build the kernel. + +### Build App + +Just open Android Studio and import the project. + +## License + +[GPL-3](http://www.gnu.org/copyleft/gpl.html) + +## Credits + +- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) +- [genuine](https://github.com/brevent/genuine/) +- [Diamorphine](https://github.com/m0nad/Diamorphine) \ No newline at end of file diff --git a/kernel/Kconfig b/kernel/Kconfig new file mode 100644 index 00000000..f8789b53 --- /dev/null +++ b/kernel/Kconfig @@ -0,0 +1,5 @@ +config KSU + tristate "KernelSU module" + default y + help + This is the KSU privilege driver for android system. diff --git a/kernel/Makefile b/kernel/Makefile new file mode 100644 index 00000000..a44378fc --- /dev/null +++ b/kernel/Makefile @@ -0,0 +1,7 @@ +obj-y += ksu.o +obj-y += allowlist.o +obj-y += apk_sign.o +obj-y += module_api.o +obj-y += selinux/ + +ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat diff --git a/kernel/allowlist.c b/kernel/allowlist.c new file mode 100644 index 00000000..3748aa1f --- /dev/null +++ b/kernel/allowlist.c @@ -0,0 +1,211 @@ +#include "linux/uidgid.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include // msleep + +#include "klog.h" + +struct perm_data { + struct list_head list; + uid_t uid; + bool allow; +}; + +static struct list_head allow_list; + +#define KERNEL_SU_DIR "/data/adb/kernelsu" + +static struct workqueue_struct *ksu_workqueue; +static struct work_struct ksu_save_work; +static struct work_struct ksu_load_work; + +bool persistent_allow_list(); + +bool ksu_allow_uid(uid_t uid, bool allow) { + + // find the node first! + struct perm_data* p; + struct list_head* pos; + bool result; + list_for_each(pos, &allow_list) { + p = list_entry(pos, struct perm_data, list); + pr_info("ksu_allow_uid :%d, allow: %d\n", p->uid, p->allow); + if (uid == p->uid) { + p->allow = allow; + result = true; + goto exit; + } + } + + // not found, alloc a new node! + p = (struct perm_data*) kmalloc(sizeof(struct perm_data), GFP_KERNEL); + if (!p) { + pr_err("alloc allow node failed.\n"); + return false; + } + p->uid = uid; + p->allow = allow; + + list_add_tail(&p->list, &allow_list); + result = true; + +exit: + + persistent_allow_list(); + + return result; +} + +bool ksu_is_allow_uid(uid_t uid) { + struct perm_data* p; + struct list_head* pos; + list_for_each(pos, &allow_list) { + p = list_entry(pos, struct perm_data, list); + pr_info("uid :%d, allow: %d\n", p->uid, p->allow); + if (uid == p->uid) { + return p->allow; + } + } + + return false; +} + +bool ksu_get_allow_list(int* array, int* length, bool allow) { + struct perm_data* p; + struct list_head* pos; + int i = 0; + list_for_each(pos, &allow_list) { + p = list_entry(pos, struct perm_data, list); + pr_info("uid: %d allow: %d\n", p->uid, p->allow); + if (p->allow == allow) { + array[i++] = p->uid; + } + } + *length = i; + + return true; +} + + + + + +void do_persistent_allow_list(struct work_struct *work) +{ + struct perm_data* p; + struct list_head* pos; + loff_t off; + + struct file* fp = filp_open("/data/adb/ksu_list", O_WRONLY|O_CREAT, 0644); + + if (IS_ERR(fp)) { + pr_err("work creat file failed: %d\n", PTR_ERR(fp)); + return; + } + pr_info("work create file success!\n"); + + list_for_each(pos, &allow_list) { + p = list_entry(pos, struct perm_data, list); + pr_info("uid :%d, allow: %d\n", p->uid, p->allow); + kernel_write(fp, &p->uid, sizeof(p->uid), &off); + kernel_write(fp, &p->allow, sizeof(p->allow), &off); + } + + filp_close(fp, 0); +} + +void do_load_allow_list(struct work_struct *work) { + + loff_t off; + ssize_t ret; + struct file* fp; + + fp = filp_open("/data/adb/", O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("work open '/data/adb' failed: %d\n", PTR_ERR(fp)); + mdelay(2000); + + queue_work(ksu_workqueue, &ksu_load_work); + return; + } + filp_close(fp, 0); + + // load allowlist now! + fp = filp_open("/data/adb/ksu_list", O_RDONLY, 0); + + if (IS_ERR(fp)) { + pr_err("work open file failed: %d\n", PTR_ERR(fp)); + return; + } + pr_info("work open file success!\n"); + + while (true) { + u32 uid; + bool allow; + ret = kernel_read(fp, &uid, sizeof(uid), &off); + pr_info("kernel read ret: %d, off: %ld\n", ret, off); + if (ret <= 0) { + pr_info("read err: %d\n", ret); + break; + } + ret = kernel_read(fp, &allow, sizeof(allow), &off); + + pr_info("load_allow_uid: %d, allow: %d\n", uid, allow); + + ksu_allow_uid(uid, allow); + } + + filp_close(fp, 0); +} + +static int init_work(void) { + ksu_workqueue = alloc_workqueue("kernelsu_work", 0, 0); + INIT_WORK(&ksu_save_work, do_persistent_allow_list); + INIT_WORK(&ksu_load_work, do_load_allow_list); + return 0; +} + +// make sure allow list works cross boot +bool persistent_allow_list() { + queue_work(ksu_workqueue, &ksu_save_work); + return true; +} + +bool load_allow_list() { + queue_work(ksu_workqueue, &ksu_load_work); + return true; +} + +bool ksu_allowlist_init() { + + INIT_LIST_HEAD(&allow_list); + + init_work(); + + // load_allow_list(); + + return true; +} + +bool ksu_allowlist_exit() { + + destroy_workqueue(ksu_workqueue); + + return true; +} \ No newline at end of file diff --git a/kernel/allowlist.h b/kernel/allowlist.h new file mode 100644 index 00000000..3ccef7b7 --- /dev/null +++ b/kernel/allowlist.h @@ -0,0 +1,14 @@ +#ifndef __KSU_H_ALLOWLIST +#define __KSU_H_ALLOWLIST + +bool ksu_allowlist_init(); + +bool ksu_allowlist_exit(); + +bool ksu_is_allow_uid(uid_t uid); + +bool ksu_allow_uid(uid_t uid, bool allow); + +bool ksu_get_allow_list(int* array, int* length, bool allow); + +#endif \ No newline at end of file diff --git a/kernel/apk_sign.c b/kernel/apk_sign.c new file mode 100644 index 00000000..647a860d --- /dev/null +++ b/kernel/apk_sign.c @@ -0,0 +1,120 @@ +#include + +#include "apk_sign.h" +#include "klog.h" + +static int check_v2_signature(char* path, unsigned expected_size, unsigned expected_hash) { + unsigned char buffer[0x11] = {0}; + u32 size4; + u64 size8, size_of_block; + + loff_t pos; + + int sign = -1; + struct file* fp = filp_open(path, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("open %s error.", path); + return PTR_ERR(fp); + } + + sign = 1; + // https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD) + for (int i = 0;; ++i) { + unsigned short n; + pos = generic_file_llseek(fp, -i - 2, SEEK_END); + kernel_read(fp, &n, 2, &pos); + if (n == i) { + pos -= 22; + kernel_read(fp, &size4, 4, &pos); + if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) { + break; + } + } + if (i == 0xffff) { + pr_info("error: cannot find eocd\n"); + goto clean; + } + } + + pos += 12; + // offset + kernel_read(fp, &size4, 0x4, &pos); + pos = size4 - 0x18; + + kernel_read(fp, &size8, 0x8, &pos); + kernel_read(fp, buffer, 0x10, &pos); + if (strcmp((char *) buffer, "APK Sig Block 42")) { + goto clean; + } + + pos = size4 - (size8 + 0x8); + kernel_read(fp, &size_of_block, 0x8, &pos); + if (size_of_block != size8) { + goto clean; + } + + for (;;) { + uint32_t id; + uint32_t offset; + kernel_read(fp, &size8, 0x8, &pos); // sequence length + if (size8 == size_of_block) { + break; + } + kernel_read(fp, &id, 0x4, &pos); // id + offset = 4; + pr_info("id: 0x%08x\n", id); + if ((id ^ 0xdeadbeefu) == 0xafa439f5u || (id ^ 0xdeadbeefu) == 0x2efed62f) { + kernel_read(fp, &size4, 0x4, &pos); // signer-sequence length + kernel_read(fp, &size4, 0x4, &pos); // signer length + kernel_read(fp, &size4, 0x4, &pos); // signed data length + offset += 0x4 * 3; + + kernel_read(fp, &size4, 0x4, &pos); // digests-sequence length + pos += size4; + offset += 0x4 + size4; + + kernel_read(fp, &size4, 0x4, &pos); // certificates length + kernel_read(fp, &size4, 0x4, &pos); // certificate length + offset += 0x4 * 2; +#if 0 + int hash = 1; + signed char c; + for (unsigned i = 0; i < size4; ++i) { + kernel_read(fp, &c, 0x1, &pos); + hash = 31 * hash + c; + } + offset += size4; + pr_info(" size: 0x%04x, hash: 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u); +#else + if (size4 == expected_size) { + int hash = 1; + signed char c; + for (unsigned i = 0; i < size4; ++i) { + kernel_read(fp, &c, 0x1, &pos); + hash = 31 * hash + c; + } + offset += size4; + if ((((unsigned) hash) ^ 0x14131211u) == expected_hash) { + sign = 0; + break; + } + } + // don't try again. + break; +#endif + } + pos += (size8 - offset); + } + +clean: + filp_close(fp, 0); + + return sign; +} + +#define EXPECTED_SIZE 0x023f +#define EXPECTED_HASH 0x9eb9c1ea + +int is_manager_apk(char* path) { + return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH); +} \ No newline at end of file diff --git a/kernel/apk_sign.h b/kernel/apk_sign.h new file mode 100644 index 00000000..d4ba8beb --- /dev/null +++ b/kernel/apk_sign.h @@ -0,0 +1,7 @@ +#ifndef __KSU_H_APK_V2_SIGN +#define __KSU_H_APK_V2_SIGN + +// return 0 if signature match +int is_manager_apk(char* path); + +#endif \ No newline at end of file diff --git a/kernel/klog.h b/kernel/klog.h new file mode 100644 index 00000000..a84341a2 --- /dev/null +++ b/kernel/klog.h @@ -0,0 +1,9 @@ +#ifndef __KSU_H_KLOG +#define __KSU_H_KLOG + +#ifdef pr_fmt +#undef pr_fmt +#define pr_fmt(fmt) "KernelSU: " fmt +#endif + +#endif \ No newline at end of file diff --git a/kernel/ksu.c b/kernel/ksu.c new file mode 100644 index 00000000..998df17c --- /dev/null +++ b/kernel/ksu.c @@ -0,0 +1,250 @@ +#include "linux/uidgid.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include // mslepp + +#include "selinux/selinux.h" +#include "klog.h" +#include "apk_sign.h" +#include "allowlist.h" + +#define KERNEL_SU_VERSION 3 + +#define KERNEL_SU_OPTION 0xDEADBEEF + +#define CMD_GRANT_ROOT 0 + +#define CMD_BECOME_MANAGER 1 +#define CMD_GET_VERSION 2 +#define CMD_ALLOW_SU 3 +#define CMD_DENY_SU 4 +#define CMD_GET_ALLOW_LIST 5 +#define CMD_GET_DENY_LIST 6 + +static void escape_to_root(void) { + struct cred* cred; + + cred = (struct cred *)__task_cred(current); + + memset(&cred->uid, 0, sizeof(cred->uid)); + memset(&cred->gid, 0, sizeof(cred->gid)); + memset(&cred->suid, 0, sizeof(cred->suid)); + memset(&cred->euid, 0, sizeof(cred->euid)); + memset(&cred->egid, 0, sizeof(cred->egid)); + memset(&cred->fsuid, 0, sizeof(cred->fsuid)); + memset(&cred->fsgid, 0, sizeof(cred->fsgid)); + memset(&cred->cap_inheritable, 0xff, sizeof(cred->cap_inheritable)); + memset(&cred->cap_permitted, 0xff, sizeof(cred->cap_permitted)); + memset(&cred->cap_effective, 0xff, sizeof(cred->cap_effective)); + memset(&cred->cap_bset, 0xff, sizeof(cred->cap_bset)); + memset(&cred->cap_ambient, 0xff, sizeof(cred->cap_ambient)); + + // DISABLE SECCOMP + current_thread_info()->flags = 0; + current->seccomp.mode = 0; + current->seccomp.filter = NULL; + + setup_selinux(); +} + +int startswith(char* s, char* prefix) { + return strncmp(s, prefix, strlen(prefix)); +} + +int endswith(const char *s, const char *t) +{ + size_t slen = strlen(s); + size_t tlen = strlen(t); + if (tlen > slen) return 1; + return strcmp(s + slen - tlen, t); +} + +static uid_t __manager_uid; + +static bool is_manager() { + return __manager_uid == current_uid().val; +} + +static bool become_manager() { + if (__manager_uid != 0) { + pr_info("manager already exist: %d\n", __manager_uid); + return true; + } + // list current process's files + struct files_struct *current_files; + struct fdtable *files_table; + int i = 0; + struct path files_path; + char *cwd; + char *buf = (char *)kmalloc(GFP_KERNEL, PATH_MAX); + bool result = false; + + current_files = current->files; + files_table = files_fdtable(current_files); + + // todo: use iterate_fd + while(files_table->fd[i] != NULL) { + files_path = files_table->fd[i]->f_path; + if (!d_is_reg(files_path.dentry)) { + i++; + continue; + } + cwd = d_path(&files_path, buf, PATH_MAX); + if (startswith(cwd, "/data/app/") == 0 && endswith(cwd, "/base.apk") == 0) { + // we have found the apk! + pr_info("found apk: %s", cwd); + if (is_manager_apk(cwd) == 0) { + // check passed + uid_t uid = current_uid().val; + pr_info("manager uid: %d\n", uid); + + __manager_uid = uid; + + result = true; + goto clean; + } else { + pr_info("manager signature invalid!"); + } + + break; + } + i++; + } + +clean: + kfree(buf); + return result; +} + +static bool is_allow_su() { + uid_t uid = current_uid().val; + if (uid == __manager_uid) { + // we are manager, allow! + return true; + } + + if (uid == 0) { + // we are already root, allow! + return true; + } + + return ksu_is_allow_uid(uid); +} + +static int handler_pre(struct kprobe *p, struct pt_regs *regs) { + + struct pt_regs* real_regs = (struct pt_regs*) regs->regs[0]; + int option = (int) real_regs->regs[0]; + unsigned long arg2 = (unsigned long) real_regs->regs[1]; + unsigned long arg3 = (unsigned long) real_regs->regs[2]; + unsigned long arg4 = (unsigned long) real_regs->regs[3]; + unsigned long arg5 = (unsigned long) real_regs->regs[4]; + + // if success, we modify the arg5 as result! + u32* result = (u32*) arg5; + u32 reply_ok = KERNEL_SU_OPTION; + + if (KERNEL_SU_OPTION != option) { + return 0; + } + + pr_info("option: 0x%x, cmd: %ld\n", option, arg2); + + if (arg2 == CMD_BECOME_MANAGER) { + // someone wants to be root manager, just check it! + bool success = become_manager(); + if (success) { + copy_to_user(result, &reply_ok, sizeof(reply_ok)); + } + return 0; + } + + if (arg2 == CMD_GRANT_ROOT) { + if (is_allow_su()) { + pr_info("allow root for: %d\n", current_uid()); + escape_to_root(); + } else { + pr_info("deny root for: %d\n", current_uid()); + // add it to deny list! + ksu_allow_uid(current_uid().val, false); + } + return 0; + } + + // all other cmds are for 'root manager' + if (!is_manager()) { + pr_info("Only manager can do cmd: %d\n", arg2); + return 0; + } + + // we are already manager + if (arg2 == CMD_ALLOW_SU || arg2 == CMD_DENY_SU) { + bool allow = arg2 == CMD_ALLOW_SU; + bool success = false; + uid_t uid = (uid_t) arg3; + success = ksu_allow_uid(uid, allow); + if (success) { + copy_to_user(result, &reply_ok, sizeof(reply_ok)); + } + } else if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) { + u32 array[128]; + u32 array_length; + bool success = ksu_get_allow_list(array, &array_length, arg2 == CMD_GET_ALLOW_LIST); + if (success) { + copy_to_user(arg4, &array_length, sizeof(array_length)); + copy_to_user(arg3, array, sizeof(u32) * array_length); + + copy_to_user(result, &reply_ok, sizeof(reply_ok)); + } + } else if (arg2 == CMD_GET_VERSION) { + u32 version = KERNEL_SU_VERSION; + copy_to_user(arg3, &version, sizeof(version)); + } + + return 0; +} + +static struct kprobe kp = { + .symbol_name = "__arm64_sys_prctl", + .pre_handler = handler_pre, +}; + +int kernelsu_init(void){ + int rc = 0; + + ksu_allowlist_init(); + + rc = register_kprobe(&kp); + + return rc; +} + +void kernelsu_exit(void){ + // should never happen... + unregister_kprobe(&kp); + + ksu_allowlist_exit(); +} + +module_init(kernelsu_init); +module_exit(kernelsu_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("weishu"); +MODULE_DESCRIPTION("Android GKI KernelSU"); \ No newline at end of file diff --git a/kernel/module_api.c b/kernel/module_api.c new file mode 100644 index 00000000..4a838f95 --- /dev/null +++ b/kernel/module_api.c @@ -0,0 +1,33 @@ +#include +#include + +#define RE_EXPORT_SYMBOL1(ret, func, t1, v1) \ + ret ksu_##func(t1 v1) { \ + return func(v1); \ + } \ + EXPORT_SYMBOL(ksu_##func); \ + +#define RE_EXPORT_SYMBOL2(ret, func, t1, v1, t2, v2) \ + ret ksu_##func(t1 v1, t2 v2) { \ + return func(v1, v2); \ + } \ + EXPORT_SYMBOL(ksu_##func); \ + + +RE_EXPORT_SYMBOL1(unsigned long, kallsyms_lookup_name, const char*, name) + +// RE_EXPORT_SYMBOL2(int, register_kprobe, struct kprobe *, p) +// RE_EXPORT_SYMBOL2(void, unregister_kprobe, struct kprobe *, p) + +// RE_EXPORT_SYMBOL2(int, register_kprobe, struct kprobe *, p) +// RE_EXPORT_SYMBOL2(void, unregister_kprobe, struct kprobe *, p) + +// int ksu_register_kprobe(struct kprobe *p); +// void ksu_unregister_kprobe(struct kprobe *p); +// int ksu_register_kprobes(struct kprobe **kps, int num); +// void ksu_unregister_kprobes(struct kprobe **kps, int num); + +// int ksu_register_kretprobe(struct kretprobe *rp); +// void unregister_kretprobe(struct kretprobe *rp); +// int register_kretprobes(struct kretprobe **rps, int num); +// void unregister_kretprobes(struct kretprobe **rps, int num); \ No newline at end of file diff --git a/kernel/selinux/Makefile b/kernel/selinux/Makefile new file mode 100644 index 00000000..b6a532f0 --- /dev/null +++ b/kernel/selinux/Makefile @@ -0,0 +1,3 @@ +obj-y += selinux.o + +ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion diff --git a/kernel/selinux/av_permissions.h b/kernel/selinux/av_permissions.h new file mode 100644 index 00000000..ed175813 --- /dev/null +++ b/kernel/selinux/av_permissions.h @@ -0,0 +1 @@ +#include "../../../security/selinux/av_permissions.h" \ No newline at end of file diff --git a/kernel/selinux/flask.h b/kernel/selinux/flask.h new file mode 100644 index 00000000..ec2b2f8c --- /dev/null +++ b/kernel/selinux/flask.h @@ -0,0 +1 @@ +#include "../../../security/selinux/flask.h" \ No newline at end of file diff --git a/kernel/selinux/security.h b/kernel/selinux/security.h new file mode 100644 index 00000000..bd14e7be --- /dev/null +++ b/kernel/selinux/security.h @@ -0,0 +1 @@ +#include "../../../security/selinux/include/security.h" \ No newline at end of file diff --git a/kernel/selinux/selinux.c b/kernel/selinux/selinux.c new file mode 100644 index 00000000..5e4e0fa5 --- /dev/null +++ b/kernel/selinux/selinux.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../security/selinux/ss/sidtab.h" +#include "../../../security/selinux/ss/services.h" +#include "../../../security/selinux/include/objsec.h" + +#include "selinux.h" +#include "../klog.h" + +#define KERNEL_SU_DOMAIN "u:r:su:s0" + +static int transive_to_domain(const char* domain) { + struct cred* cred; + struct task_security_struct* tsec; + u32 sid; + int error; + + cred = (struct cred *)__task_cred(current); + + tsec = cred->security; + if (!tsec) { + pr_err("tsec == NULL!\n"); + return -1; + } + + error = security_secctx_to_secid(domain, strlen(domain), &sid); + pr_info("error: %d, sid: %d\n", error, sid); + if (!error) { + tsec->sid = sid; + tsec->create_sid = 0; + tsec->keycreate_sid = 0; + tsec->sockcreate_sid = 0; + } + return error; +} + +static int set_domain_permissive() { + u32 sid; + struct selinux_policy *policy; + struct sidtab_entry *entry; + struct ebitmap *permissive; + + sid = current_sid(); + pr_info("set sid (%d) to permissive", sid); + + rcu_read_lock(); + policy = rcu_dereference(selinux_state.policy); + + entry = sidtab_search_entry(policy->sidtab, sid); + if (entry == NULL){ + pr_info("entry == NULL"); + rcu_read_unlock(); + return -EFAULT; + } + // FIXME: keep mls + permissive = &(policy->policydb.permissive_map); + ebitmap_set_bit(permissive, entry->context.type, 1); + + rcu_read_unlock(); + return 0; +} + +static bool is_domain_permissive; + +void setup_selinux() { + + if (transive_to_domain(KERNEL_SU_DOMAIN)) { + pr_err("transive domain failed."); + return; + } + + if (!is_domain_permissive) { + if (set_domain_permissive() == 0) { + is_domain_permissive = true; + } + } +} \ No newline at end of file diff --git a/kernel/selinux/selinux.h b/kernel/selinux/selinux.h new file mode 100644 index 00000000..2e9c7c73 --- /dev/null +++ b/kernel/selinux/selinux.h @@ -0,0 +1,8 @@ +#ifndef __KSU_H_SELINUX +#define __KSU_H_SELINUX + + + +void setup_selinux(); + +#endif \ No newline at end of file diff --git a/manager/.gitignore b/manager/.gitignore new file mode 100644 index 00000000..aa724b77 --- /dev/null +++ b/manager/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/manager/.idea/.gitignore b/manager/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/manager/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/manager/.idea/compiler.xml b/manager/.idea/compiler.xml new file mode 100644 index 00000000..fb7f4a8a --- /dev/null +++ b/manager/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/manager/.idea/gradle.xml b/manager/.idea/gradle.xml new file mode 100644 index 00000000..b49a10ac --- /dev/null +++ b/manager/.idea/gradle.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/manager/.idea/inspectionProfiles/Project_Default.xml b/manager/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..ed76bea3 --- /dev/null +++ b/manager/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,37 @@ + + + + \ No newline at end of file diff --git a/manager/.idea/misc.xml b/manager/.idea/misc.xml new file mode 100644 index 00000000..360e6d41 --- /dev/null +++ b/manager/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/manager/app/.gitignore b/manager/app/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/manager/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/manager/app/build.gradle b/manager/app/build.gradle new file mode 100644 index 00000000..a55943f1 --- /dev/null +++ b/manager/app/build.gradle @@ -0,0 +1,83 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'me.weishu.kernelsu' + compileSdk 33 + + defaultConfig { + applicationId "me.weishu.kernelsu" + minSdk 31 + targetSdk 32 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary true + } + externalNativeBuild { + cmake { + cppFlags '' + } + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + buildFeatures { + compose true + } + composeOptions { + kotlinCompilerExtensionVersion '1.1.1' + } + packagingOptions { + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } + } + externalNativeBuild { + cmake { + path file('src/main/cpp/CMakeLists.txt') + version '3.18.1' + } + } +} + +dependencies { + + implementation 'androidx.core:core-ktx:1.9.0' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' + implementation 'androidx.activity:activity-compose:1.6.0' + implementation "androidx.compose.ui:ui:$compose_ui_version" + implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version" + implementation 'androidx.compose.material:material:1.3.1' + + def nav_version = "2.5.3" + implementation "androidx.navigation:navigation-compose:$nav_version" + + implementation "com.google.accompanist:accompanist-drawablepainter:0.28.0" + + implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1" + + + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version" + debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version" + debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version" +} \ No newline at end of file diff --git a/manager/app/proguard-rules.pro b/manager/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/manager/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/manager/app/src/androidTest/java/me/weishu/kernelsu/ExampleInstrumentedTest.kt b/manager/app/src/androidTest/java/me/weishu/kernelsu/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..533af712 --- /dev/null +++ b/manager/app/src/androidTest/java/me/weishu/kernelsu/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package me.weishu.kernelsu + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("me.weishu.kernelsu", appContext.packageName) + } +} \ No newline at end of file diff --git a/manager/app/src/main/AndroidManifest.xml b/manager/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..313184b3 --- /dev/null +++ b/manager/app/src/main/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/manager/app/src/main/cpp/CMakeLists.txt b/manager/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 00000000..ff6c3b9b --- /dev/null +++ b/manager/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,18 @@ + +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html + +# Sets the minimum version of CMake required to build the native library. +cmake_minimum_required(VERSION 3.18.1) + +project("kernelsu") + +add_library(kernelsu + SHARED + jni.cc + ksu.cc + ) + +find_library(log-lib log) + +target_link_libraries(kernelsu ${log-lib}) diff --git a/manager/app/src/main/cpp/jni.cc b/manager/app/src/main/cpp/jni.cc new file mode 100644 index 00000000..ddb18d89 --- /dev/null +++ b/manager/app/src/main/cpp/jni.cc @@ -0,0 +1,60 @@ +#include + +#include + +#include + +#include "ksu.h" + +#define LOG_TAG "KernelSu" +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) + +extern "C" +JNIEXPORT jboolean JNICALL +Java_me_weishu_kernelsu_Natives_becomeManager(JNIEnv *env, jclass clazz) { + return become_manager(); +} + +extern "C" +JNIEXPORT jint JNICALL +Java_me_weishu_kernelsu_Natives_getVersion(JNIEnv *env, jclass clazz) { + return get_version(); +} + +extern "C" +JNIEXPORT jintArray JNICALL +Java_me_weishu_kernelsu_Natives_getAllowList(JNIEnv *env, jclass clazz) { + int uids[1024]; + int size = 0; + bool result = get_allow_list(uids, &size); + LOGD("getAllowList: %d, size: %d", result, size); + if (result) { + auto array = env->NewIntArray(size); + env->SetIntArrayRegion(array, 0, size, uids); + return array; + } + return env->NewIntArray(0); +} + +extern "C" +JNIEXPORT jintArray JNICALL +Java_me_weishu_kernelsu_Natives_getDenyList(JNIEnv *env, jclass clazz) { + int uids[1024]; + int size = 0; + bool result = get_deny_list(uids, &size); + if (result) { + // success! + auto array = env->NewIntArray(size); + env->SetIntArrayRegion(array, 0, size, uids); + return array; + } + return env->NewIntArray(0); +} + +extern "C" +JNIEXPORT jboolean JNICALL +Java_me_weishu_kernelsu_Natives_allowRoot(JNIEnv *env, jclass clazz, jint uid, jboolean allow) { + return allow_su(uid, allow); +} + + diff --git a/manager/app/src/main/cpp/ksu.cc b/manager/app/src/main/cpp/ksu.cc new file mode 100644 index 00000000..ef662a9f --- /dev/null +++ b/manager/app/src/main/cpp/ksu.cc @@ -0,0 +1,50 @@ +// +// Created by weishu on 2022/12/9. +// + +#include +#include + +#include "ksu.h" + +#define KERNEL_SU_OPTION 0xDEADBEEF + +#define CMD_GRANT_ROOT 0 + +#define CMD_BECOME_MANAGER 1 +#define CMD_GET_VERSION 2 +#define CMD_ALLOW_SU 3 +#define CMD_DENY_SU 4 +#define CMD_GET_ALLOW_LIST 5 +#define CMD_GET_DENY_LIST 6 + +static bool ksuctl(int cmd, void* arg1, void* arg2) { + int32_t result = 0; + prctl(KERNEL_SU_OPTION, cmd, arg1, arg2, &result); + return result == KERNEL_SU_OPTION; +} + +bool become_manager() { + return ksuctl(CMD_BECOME_MANAGER, nullptr, nullptr); +} + +int get_version() { + int32_t version = -1; + if (ksuctl(CMD_GET_VERSION, &version, nullptr)) { + return version; + } + return version; +} + +bool allow_su(int uid, bool allow) { + int cmd = allow ? CMD_ALLOW_SU : CMD_DENY_SU; + return ksuctl(cmd, (void*) uid, nullptr); +} + +bool get_allow_list(int *uids, int *size) { + return ksuctl(CMD_GET_ALLOW_LIST, uids, size); +} + +bool get_deny_list(int *uids, int *size) { + return ksuctl(CMD_GET_DENY_LIST, uids, size); +} \ No newline at end of file diff --git a/manager/app/src/main/cpp/ksu.h b/manager/app/src/main/cpp/ksu.h new file mode 100644 index 00000000..d4680470 --- /dev/null +++ b/manager/app/src/main/cpp/ksu.h @@ -0,0 +1,18 @@ +// +// Created by weishu on 2022/12/9. +// + +#ifndef KERNELSU_KSU_H +#define KERNELSU_KSU_H + +bool become_manager(); + +int get_version(); + +bool allow_su(int uid, bool allow); + +bool get_allow_list(int *uids, int *size); + +bool get_deny_list(int *uids, int *size); + +#endif //KERNELSU_KSU_H diff --git a/manager/app/src/main/java/me/weishu/kernelsu/HyperlinkText.kt b/manager/app/src/main/java/me/weishu/kernelsu/HyperlinkText.kt new file mode 100644 index 00000000..789956ec --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/HyperlinkText.kt @@ -0,0 +1,73 @@ +package me.weishu.kernelsu + +import androidx.compose.foundation.text.ClickableText +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.unit.TextUnit + +@Composable +fun HyperlinkText( + modifier: Modifier = Modifier, + fullText: String, + hyperLinks: Map, + textStyle: TextStyle = TextStyle.Default, + linkTextColor: Color = Color.Blue, + linkTextFontWeight: FontWeight = FontWeight.Normal, + linkTextDecoration: TextDecoration = TextDecoration.None, + fontSize: TextUnit = TextUnit.Unspecified +) { + val annotatedString = buildAnnotatedString { + append(fullText) + + for((key, value) in hyperLinks){ + + val startIndex = fullText.indexOf(key) + val endIndex = startIndex + key.length + addStyle( + style = SpanStyle( + color = linkTextColor, + fontSize = fontSize, + fontWeight = linkTextFontWeight, + textDecoration = linkTextDecoration + ), + start = startIndex, + end = endIndex + ) + addStringAnnotation( + tag = "URL", + annotation = value, + start = startIndex, + end = endIndex + ) + } + addStyle( + style = SpanStyle( + fontSize = fontSize + ), + start = 0, + end = fullText.length + ) + } + + val uriHandler = LocalUriHandler.current + + ClickableText( + modifier = modifier, + text = annotatedString, + style = textStyle, + onClick = { + annotatedString + .getStringAnnotations("URL", it, it) + .firstOrNull()?.let { stringAnnotation -> + uriHandler.openUri(stringAnnotation.item) + } + } + ) +} \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/MainActivity.kt b/manager/app/src/main/java/me/weishu/kernelsu/MainActivity.kt new file mode 100644 index 00000000..ff6355bf --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/MainActivity.kt @@ -0,0 +1,149 @@ +package me.weishu.kernelsu + +import AboutDialog +import Home +import Module +import SuperUser +import SuperUserData +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.annotation.StringRes +import androidx.compose.foundation.layout.* +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Color.Companion.Gray +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.navigation.NavDestination.Companion.hierarchy +import androidx.navigation.NavGraph.Companion.findStartDestination +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.currentBackStackEntryAsState +import androidx.navigation.compose.rememberNavController +import me.weishu.kernelsu.ui.theme.KernelSUTheme + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + KernelSUTheme { + // A surface container using the 'background' color from the theme + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colors.background + ) { + Greeting() + } + } + } + } +} + +@Composable +fun Greeting() { + + val items = listOf( + Screen.Home, + Screen.SuperUser, + Screen.Module + ) + + val navController = rememberNavController() + + var showAboutDialog by remember { mutableStateOf(false) } + + AboutDialog(openDialog = showAboutDialog, onDismiss = { + showAboutDialog = false + }) + + Scaffold( + topBar = { + TopAppBar( + title = { + Text(text = "KernelSU") + }, + backgroundColor = MaterialTheme.colors.primary, + contentColor = Color.White, + elevation = 12.dp, + actions = { + IconButton(onClick = { /*TODO*/ }) { + Icon(Icons.Filled.Search, contentDescription = "Search") + } + IconButton(onClick = { + showAboutDialog = true + }) { + Icon(Icons.Filled.MoreVert, contentDescription = "More") + } + } + ) + }, + bottomBar = { + BottomNavigation( + backgroundColor = MaterialTheme.colors.background, + ) { + val navBackStackEntry by navController.currentBackStackEntryAsState() + val currentDestination = navBackStackEntry?.destination + items.forEach { screen -> + BottomNavigationItem( + icon = { Icon(painterResource(id = screen.icon), null) }, + label = { Text(stringResource(screen.resourceId)) }, + selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true, + selectedContentColor = MaterialTheme.colors.primary, + unselectedContentColor = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium), + onClick = { + navController.navigate(screen.route) { + // Pop up to the start destination of the graph to + // avoid building up a large stack of destinations + // on the back stack as users select items + popUpTo(navController.graph.findStartDestination().id) { + saveState = true + } + // Avoid multiple copies of the same destination when + // reselecting the same item + launchSingleTop = true + // Restore state when reselecting a previously selected item + restoreState = true + } + } + ) + } + } + } + ) { innerPadding -> + NavHost( + navController, + startDestination = Screen.Home.route, + Modifier.padding(innerPadding) + ) { + composable(Screen.Home.route) { Home() } + composable(Screen.SuperUser.route) { SuperUser() } + composable(Screen.Module.route) { Module() } + } + } + +} + +@Preview(showBackground = true) +@Composable +fun DefaultPreview() { + KernelSUTheme { + Greeting() + } +} + +sealed class Screen(val route: String, @StringRes val resourceId: Int, val icon: Int) { + object Home : Screen("home", R.string.home, R.drawable.ic_home) + object SuperUser : Screen("superuser", R.string.superuser, R.drawable.ic_superuser) + object Module: Screen("module", R.string.module, R.drawable.ic_module) +} + diff --git a/manager/app/src/main/java/me/weishu/kernelsu/Natives.java b/manager/app/src/main/java/me/weishu/kernelsu/Natives.java new file mode 100644 index 00000000..08ce8744 --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/Natives.java @@ -0,0 +1,24 @@ +package me.weishu.kernelsu; + +/** + * @author weishu + * @date 2022/12/8. + */ +public final class Natives { + + static { + System.loadLibrary("kernelsu"); + } + + // become root manager, return true if success. + public static native boolean becomeManager(); + + public static native int getVersion(); + + // get the uid list of allowed su processes. + public static native int[] getAllowList(); + + public static native int[] getDenyList(); + + public static native boolean allowRoot(int uid, boolean allow); +} diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AboutDialog.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AboutDialog.kt new file mode 100644 index 00000000..89aecae9 --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AboutDialog.kt @@ -0,0 +1,86 @@ +import androidx.compose.foundation.layout.Column +import androidx.compose.material.AlertDialog +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextAlign +import me.weishu.kernelsu.HyperlinkText + +@Composable +fun AboutDialog(openDialog: Boolean, onDismiss: () -> Unit) { + + if (openDialog) { + AlertDialog( + onDismissRequest = { + onDismiss() + }, + title = { + Text(text = "About") + }, + text = { + Column { + + HyperlinkText( + fullText = "Author: weishu", + hyperLinks = mutableMapOf( + "weishu" to "https://github.com/tiann", + ), + textStyle = TextStyle( + textAlign = TextAlign.Center, + color = Color.Gray + ), + linkTextColor = MaterialTheme.colors.secondary, + ) + + HyperlinkText( + fullText = "Github: https://github.com/tiann/KernelSU", + hyperLinks = mutableMapOf( + "https://github.com/tiann/KernelSU" to "https://github.com/tiann/KernelSU", + ), + textStyle = TextStyle( + textAlign = TextAlign.Center, + color = Color.Gray + ), + linkTextColor = MaterialTheme.colors.secondary, + ) + + HyperlinkText( + fullText = "Telegram: https://t.me/KernelSU", + hyperLinks = mutableMapOf( + "https://t.me/KernelSU" to "https://t.me/KernelSU", + ), + textStyle = TextStyle( + textAlign = TextAlign.Center, + color = Color.Gray + ), + linkTextColor = MaterialTheme.colors.secondary, + ) + + HyperlinkText( + fullText = "QQ: https://pd.qq.com/s/8lipl1brp", + hyperLinks = mutableMapOf( + "https://pd.qq.com/s/8lipl1brp" to "https://pd.qq.com/s/8lipl1brp", + ), + textStyle = TextStyle( + textAlign = TextAlign.Center, + color = Color.Gray + ), + linkTextColor = MaterialTheme.colors.secondary, + ) + + } + }, + confirmButton = { + Button( + onClick = { + onDismiss() + }) { + Text("OK") + } + }, + ) + } +} \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt new file mode 100644 index 00000000..7e857b1a --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt @@ -0,0 +1,118 @@ +import android.os.Build +import android.system.Os +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material.Card +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Done +import androidx.compose.material.icons.filled.Warning +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.ClipboardManager +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import me.weishu.kernelsu.Natives +import java.util.* + +@Composable +fun Info(label: String, value: String) { + Text( + buildAnnotatedString { + append("$label: ") + withStyle( + style = SpanStyle( + fontWeight = FontWeight.W900, + color = MaterialTheme.colors.secondary + ) + ) { + append(value) + } + } + ) +} + +@Composable +fun Home() { + + val isManager = Natives.becomeManager() + + Column(modifier = Modifier.fillMaxWidth()) { + + Card( + modifier = Modifier + .fillMaxWidth() + .padding(6.dp) + .clickable { }, + elevation = 10.dp, + backgroundColor = MaterialTheme.colors.secondary + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(10.dp) + ) { + Image( + if (isManager) Icons.Filled.Done else Icons.Filled.Warning, + null, + modifier = Modifier + .size(64.dp) + ) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(start = 10.dp), + ) { + Text( + text = if (isManager) "Installed" else "Not Installed", + fontSize = 20.sp, + fontWeight = FontWeight.Bold + ) + + Text( + text = if (isManager) "Version: ${Natives.getVersion()}" else "Click to Install", + fontSize = 15.sp, + fontWeight = FontWeight.Normal + ) + } + } + + } + + Card( + modifier = Modifier + .fillMaxWidth() + .padding(6.dp) + .clickable { }, + elevation = 10.dp + ) { + Column( + modifier = Modifier.padding(10.dp) + ) { + + Os.uname().let { uname -> + Info("Kernel", uname.release) + Info("Arch", uname.machine) + Info("Version", uname.version) + } + + Info("API Level", Build.VERSION.SDK_INT.toString()) + + Info("ABI", Build.SUPPORTED_ABIS.joinToString(", ")) + + Info("Fingerprint", Build.FINGERPRINT) + + Info("Security Patch", Build.VERSION.SECURITY_PATCH) + + } + } + + } + +} \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt new file mode 100644 index 00000000..373a4bf8 --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt @@ -0,0 +1,29 @@ +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Card +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun Module() { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(6.dp) + .clickable { }, + elevation = 10.dp + ) { + Column( + modifier = Modifier.padding(10.dp) + ) { + + + Text("Coming Soon..") + + } + } +} \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt new file mode 100644 index 00000000..200f3e48 --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt @@ -0,0 +1,174 @@ +import android.annotation.SuppressLint +import android.content.Context +import android.content.pm.ApplicationInfo +import android.graphics.drawable.Drawable +import android.util.Log +import android.widget.Toast +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.Checkbox +import androidx.compose.material.Switch +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.google.accompanist.drawablepainter.rememberDrawablePainter +import me.weishu.kernelsu.Natives +import java.util.* + +class SuperUserData( + val name: CharSequence, + val description: String, + val icon: Drawable, + val uid: Int, + initialChecked: Boolean = false +) { + var checked: Boolean by mutableStateOf(initialChecked) +} + +@Composable +fun SuperUserItem( + superUserData: SuperUserData, + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + onItemClick: () -> Unit +) { + Row(modifier = Modifier + .fillMaxWidth() + .clickable { + onItemClick() + }) { + + Image( + painter = rememberDrawablePainter(drawable = superUserData.icon), + contentDescription = superUserData.name.toString(), + modifier = Modifier + .padding(4.dp) + .width(48.dp) + .height(48.dp) + ) + + Column { + Text( + superUserData.name.toString(), + modifier = Modifier.padding(4.dp), + color = Color.Black, + fontSize = 16.sp + ) + Text( + superUserData.description, + modifier = Modifier.padding(4.dp), + color = Color.Gray, + fontSize = 12.sp + ) + } + + Spacer(modifier = Modifier.weight(1f)) + + Switch( + checked = checked, + onCheckedChange = onCheckedChange, + modifier = Modifier.padding(4.dp) + ) + } +} + +private fun getAppList(context: Context): List { + val pm = context.packageManager + val allowList = Natives.getAllowList() + val denyList = Natives.getDenyList(); + + Log.i("mylog", "allowList: ${Arrays.toString(allowList)}") + Log.i("mylog", "denyList: ${Arrays.toString(denyList)}") + + val result = mutableListOf() + + // add allow list + for (uid in allowList) { + val packagesForUid = pm.getPackagesForUid(uid) + if (packagesForUid == null || packagesForUid.isEmpty()) { + continue + } + + packagesForUid.forEach { packageName -> + val applicationInfo = pm.getApplicationInfo(packageName, 0) + result.add( + SuperUserData( + name = applicationInfo.loadLabel(pm), + description = applicationInfo.packageName, + icon = applicationInfo.loadIcon(pm), + uid = uid, + initialChecked = true + ) + ) + } + } + + // add deny list + for (uid in denyList) { + val packagesForUid = pm.getPackagesForUid(uid) + if (packagesForUid == null || packagesForUid.isEmpty()) { + continue + } + + packagesForUid.forEach { packageName -> + val applicationInfo = pm.getApplicationInfo(packageName, 0) + result.add( + SuperUserData( + name = applicationInfo.loadLabel(pm), + description = applicationInfo.packageName, + icon = applicationInfo.loadIcon(pm), + uid = uid, + initialChecked = false + ) + ) + } + } + + return result +} + +@SuppressLint("QueryPermissionsNeeded") +@Composable +fun SuperUser() { + + val context = LocalContext.current + + val list = getAppList(context) + val apps = remember { list.toMutableStateList() } + + if (apps.isEmpty()) { + Text("No apps found") + return + } + + LazyColumn() { + items(apps, key = { it.description }) { app -> + SuperUserItem( + superUserData = app, + checked = app.checked, + onCheckedChange = { checked -> + val success = Natives.allowRoot(app.uid, checked) + if (success) { + app.checked = checked + } else { + Toast.makeText( + context, + "Failed to allow root: ${app.uid}", + Toast.LENGTH_SHORT + ).show() + } + }, + onItemClick = { + // TODO + } + ) + } + } +} \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Color.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Color.kt new file mode 100644 index 00000000..155cf4e2 --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Color.kt @@ -0,0 +1,10 @@ +package me.weishu.kernelsu.ui.theme + +import androidx.compose.ui.graphics.Color + +val YELLOW = Color(0xFFeed502) +val YELLOW_LIGHT = Color(0xFFffff52) +val SECONDARY_LIGHT = Color(0xffa9817f) + +val YELLOW_DARK = Color(0xFFb7a400) +val SECONDARY_DARK = Color(0xFF4c2b2b) \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Shape.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Shape.kt new file mode 100644 index 00000000..59179c69 --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Shape.kt @@ -0,0 +1,11 @@ +package me.weishu.kernelsu.ui.theme + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Shapes +import androidx.compose.ui.unit.dp + +val Shapes = Shapes( + small = RoundedCornerShape(4.dp), + medium = RoundedCornerShape(4.dp), + large = RoundedCornerShape(0.dp) +) \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt new file mode 100644 index 00000000..8e5fda24 --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt @@ -0,0 +1,36 @@ +package me.weishu.kernelsu.ui.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.MaterialTheme +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color + +private val DarkColorPalette = darkColors( + primary = YELLOW, + primaryVariant = YELLOW_DARK, + secondary = SECONDARY_DARK +) + +private val LightColorPalette = lightColors( + primary = YELLOW, + primaryVariant = YELLOW_LIGHT, + secondary = SECONDARY_LIGHT +) + +@Composable +fun KernelSUTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { + val colors = if (darkTheme) { + DarkColorPalette + } else { + LightColorPalette + } + + MaterialTheme( + colors = colors, + typography = Typography, + shapes = Shapes, + content = content + ) +} \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Type.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Type.kt new file mode 100644 index 00000000..424f5260 --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Type.kt @@ -0,0 +1,28 @@ +package me.weishu.kernelsu.ui.theme + +import androidx.compose.material.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + body1 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp + ) + /* Other default text styles to override + button = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W500, + fontSize = 14.sp + ), + caption = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 12.sp + ) + */ +) \ No newline at end of file diff --git a/manager/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/manager/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/manager/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/manager/app/src/main/res/drawable/ic_home.xml b/manager/app/src/main/res/drawable/ic_home.xml new file mode 100644 index 00000000..2970ad6e --- /dev/null +++ b/manager/app/src/main/res/drawable/ic_home.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/manager/app/src/main/res/drawable/ic_launcher_background.xml b/manager/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/manager/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/manager/app/src/main/res/drawable/ic_module.xml b/manager/app/src/main/res/drawable/ic_module.xml new file mode 100644 index 00000000..14945c0b --- /dev/null +++ b/manager/app/src/main/res/drawable/ic_module.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/manager/app/src/main/res/drawable/ic_superuser.xml b/manager/app/src/main/res/drawable/ic_superuser.xml new file mode 100644 index 00000000..b441ea2c --- /dev/null +++ b/manager/app/src/main/res/drawable/ic_superuser.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/manager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/manager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/manager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/manager/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/manager/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/manager/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/manager/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/manager/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 00000000..c209e78e Binary files /dev/null and b/manager/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/manager/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/manager/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 00000000..b2dfe3d1 Binary files /dev/null and b/manager/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/manager/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/manager/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 00000000..4f0f1d64 Binary files /dev/null and b/manager/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/manager/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/manager/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 00000000..62b611da Binary files /dev/null and b/manager/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/manager/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/manager/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 00000000..948a3070 Binary files /dev/null and b/manager/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/manager/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/manager/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..1b9a6956 Binary files /dev/null and b/manager/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 00000000..28d4b77f Binary files /dev/null and b/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9287f508 Binary files /dev/null and b/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 00000000..aa7d6427 Binary files /dev/null and b/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9126ae37 Binary files /dev/null and b/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/manager/app/src/main/res/values/colors.xml b/manager/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..876e0d4f --- /dev/null +++ b/manager/app/src/main/res/values/colors.xml @@ -0,0 +1,11 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + #FFb7a400 + \ No newline at end of file diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..ef0412ba --- /dev/null +++ b/manager/app/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + KernelSU + Home + Superuser + Module + \ No newline at end of file diff --git a/manager/app/src/main/res/values/themes.xml b/manager/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..75da9ebd --- /dev/null +++ b/manager/app/src/main/res/values/themes.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/manager/app/src/main/res/xml/backup_rules.xml b/manager/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 00000000..fa0f996d --- /dev/null +++ b/manager/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/manager/app/src/main/res/xml/data_extraction_rules.xml b/manager/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 00000000..9ee9997b --- /dev/null +++ b/manager/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/manager/app/src/test/java/me/weishu/kernelsu/ExampleUnitTest.kt b/manager/app/src/test/java/me/weishu/kernelsu/ExampleUnitTest.kt new file mode 100644 index 00000000..1645b81c --- /dev/null +++ b/manager/app/src/test/java/me/weishu/kernelsu/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package me.weishu.kernelsu + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/manager/build.gradle b/manager/build.gradle new file mode 100644 index 00000000..da262964 --- /dev/null +++ b/manager/build.gradle @@ -0,0 +1,10 @@ +buildscript { + ext { + compose_ui_version = '1.1.1' + } +}// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '7.3.0' apply false + id 'com.android.library' version '7.3.0' apply false + id 'org.jetbrains.kotlin.android' version '1.6.10' apply false +} \ No newline at end of file diff --git a/manager/gradle/wrapper/gradle-wrapper.jar b/manager/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/manager/gradle/wrapper/gradle-wrapper.jar differ diff --git a/manager/gradle/wrapper/gradle-wrapper.properties b/manager/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..effe611c --- /dev/null +++ b/manager/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Dec 08 17:40:48 CST 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/manager/gradlew b/manager/gradlew new file mode 100755 index 00000000..4f906e0c --- /dev/null +++ b/manager/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/manager/gradlew.bat b/manager/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/manager/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/manager/settings.gradle b/manager/settings.gradle new file mode 100644 index 00000000..2dec3bd8 --- /dev/null +++ b/manager/settings.gradle @@ -0,0 +1,16 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "KernelSU" +include ':app' diff --git a/userspace/.gitignore b/userspace/.gitignore new file mode 100644 index 00000000..720289cc --- /dev/null +++ b/userspace/.gitignore @@ -0,0 +1,2 @@ +/obj +/libs diff --git a/userspace/jni/Android.mk b/userspace/jni/Android.mk new file mode 100644 index 00000000..0183f485 --- /dev/null +++ b/userspace/jni/Android.mk @@ -0,0 +1,8 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := su +LOCAL_SRC_FILES := su.c +LOCAL_LDFLAGS := -static + +include $(BUILD_EXECUTABLE) diff --git a/userspace/jni/Application.mk b/userspace/jni/Application.mk new file mode 100644 index 00000000..db8e4956 --- /dev/null +++ b/userspace/jni/Application.mk @@ -0,0 +1,3 @@ +APP_ABI := arm64-v8a +APP_PLATFORM := android-24 +APP_STL := c++_static diff --git a/userspace/jni/su.c b/userspace/jni/su.c new file mode 100644 index 00000000..695c1cce --- /dev/null +++ b/userspace/jni/su.c @@ -0,0 +1,10 @@ +#include +#include +#include + +int main(){ + int32_t result = 0; + prctl(0xdeadbeef, 0, 0, 0, &result); + system("/system/bin/sh"); + return 0; +}