diff --git a/Makefile b/Makefile index e0334c3a..26d6eff0 100644 --- a/Makefile +++ b/Makefile @@ -62,12 +62,6 @@ $(LKM_KOS): $(LKM_SRCS) bash -c 'set -e; \ cd /proj/lkm; \ make -j$$(nproc); \ - llvm-objcopy --strip-unneeded \ - -R .comment -R .note.gnu.property \ - -R .note.Linux -R .note.GNU-stack \ - -R .BTF -R .BTF.base -R .llvm_addrsig \ - -R .hyp.text -R .hyp.bss -R .hyp.rodata -R .hyp.event_ids \ - -R .hyp.patchable_function_entries -R .hyp.data dfroot.ko; \ mkdir -p /proj/app/src/main/jni/ko; \ cp dfroot.ko /proj/app/src/main/jni/ko/dfroot-$$KMI.ko; \ make clean; \ diff --git a/app/src/main/jni/bootstrap.c b/app/src/main/jni/bootstrap.c index 9f379bc0..b05447c4 100644 --- a/app/src/main/jni/bootstrap.c +++ b/app/src/main/jni/bootstrap.c @@ -219,33 +219,16 @@ int main(void) } } - touch("/dev/dfm5"); - // ksu only allows init to switch to ksud context and we are in kernel context now - // once ksu starts, selinux is enforcing, but here its still permissive - // switch our context while we still can - pid_t pid = fork(); - if (pid < 0) - return 1; - if (pid == 0) { - int fd = open("/proc/self/attr/current", O_WRONLY); - if (fd >= 0) { - const char *ctx = "u:r:init:s0"; - write(fd, ctx, strlen(ctx)); - close(fd); + touch("/dev/dfm5"); + if (run_ctx(NULL, (char *[]){ KSUD_STAGE, "late-load", "--package-name", su_manager, NULL }) == 0) { + touch("/dev/dfm6"); + if (soft_reboot) { + while (late_load_running()) sleep(1); + run_ctx("u:r:ksu:s0", (char *[]){ KSUD, "soft-reboot", NULL }); } - if (run_ctx(NULL, (char *[]){ KSUD_STAGE, "late-load", "--package-name", su_manager, NULL }) == 0) { - touch("/dev/dfm6"); - if (soft_reboot) { - while (late_load_running()) sleep(1); - run_ctx("u:r:ksu:s0", (char *[]){ KSUD, "soft-reboot", NULL }); - } - } else { - touch("/dev/dfme2"); - } - _exit(0); + } else { + touch("/dev/dfme2"); } - int status; - waitpid(pid, &status, 0); - + return 0; } diff --git a/lkm/Makefile b/lkm/Makefile index a87535bc..e3038944 100644 --- a/lkm/Makefile +++ b/lkm/Makefile @@ -1,15 +1,20 @@ obj-m += dfroot.o -# Size diet: this ko is written through the exploit page by page, -# so every byte counts. -Os shrinks code; dropping unwind tables kills -# .eh_frame (unused for modules). -CFLAGS_dfroot.o := -Os -fno-asynchronous-unwind-tables -fno-unwind-tables +CFLAGS_dfroot.o := -Os -Qn -fno-addrsig -fno-asynchronous-unwind-tables \ + -fno-unwind-tables all: $(MAKE) -C $(KDIR) M=$(CURDIR) \ ARCH=arm64 \ LLVM=1 \ modules + $(OBJCOPY) --strip-unneeded \ + -R .comment -R .note.gnu.property \ + -R .note.Linux -R .note.GNU-stack \ + -R .BTF -R .BTF.base -R .llvm_addrsig \ + -R .hyp.text -R .hyp.bss -R .hyp.rodata -R .hyp.event_ids \ + -R .hyp.patchable_function_entries -R .hyp.data \ + dfroot.ko clean: $(MAKE) -C $(KDIR) M=$(CURDIR) \ diff --git a/lkm/dfroot.c b/lkm/dfroot.c index 4a6ee4d2..9976c072 100644 --- a/lkm/dfroot.c +++ b/lkm/dfroot.c @@ -4,9 +4,6 @@ #include #include #include -#include -#include -#include MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("DFRoot LKM"); @@ -24,38 +21,18 @@ static insn_patch_t insn_patch_text; static __nocfi void patch_fn_ret_zero(void *fn) { - u32 insn = *(u32 *)fn; - int skip = 0; - bool pac_a = false, pac_b = false; - void *addrs[2]; - u32 insns[2]; - - while (skip < 2) { - switch (insn) { - case 0xD503233F: pac_a = true; goto skip_insn; /* PACIASP */ - case 0xD503237F: pac_b = true; goto skip_insn; /* PACIBSP */ - case 0xD503241F: /* BTI */ - case 0xD503245F: /* BTI c */ - case 0xD503249F: /* BTI j */ - case 0xD50324DF: /* BTI jc */ - skip_insn: - insn = *(u32 *)((u8 *)fn + ++skip * 4); - continue; - } - break; - } + void *addrs[3]; + u32 insns[3]; + + addrs[0] = fn; + addrs[1] = (u8 *)fn + 4; + addrs[2] = (u8 *)fn + 8; - addrs[0] = (u8 *)fn + skip * 4; - addrs[1] = (u8 *)fn + skip * 4 + 4; - insns[0] = 0xD2800000; - /* PACIASP/PACIBSP are hint-space NOPs on CPUs without ARMv8.3 pointer auth, - * but RETAA/RETAB are not — they fault as undefined. Only emit them when - * hardware auth is actually present and the signed LR needs stripping. */ - insns[1] = (pac_b && system_supports_address_auth()) ? 0xD65F0FFF : - (pac_a && system_supports_address_auth()) ? 0xD65F0BFF : - 0xD65F03C0; - - if (insn_patch_text(addrs, insns, 2)) + insns[0] = 0xD503245F; // BTI C (valid landing pad, NOP when BTI not enforced) + insns[1] = 0xD2800000; // MOV x0, #0 + insns[2] = 0xD65F03C0; // RET + + if (insn_patch_text(addrs, insns, 3)) pr_warn("dfroot: patch failed at %px\n", fn); } @@ -81,7 +58,7 @@ static int __nocfi __init dfroot_init(void) static char cmd[512]; static char *envp[] = { "PATH=/system/bin", NULL }; static char *argv[] = { (char *)sh, "-c", cmd, NULL }; - snprintf(cmd, sizeof(cmd), "touch /dev/dfm0; exec %s", bootstrap); + snprintf(cmd, sizeof(cmd), "touch /dev/dfm0; echo -n 'u:r:init:s0' > /proc/self/attr/exec; exec %s", bootstrap); // Symbol finder kln_kp = (struct kprobe){ .symbol_name = "kallsyms_lookup_name" }; @@ -180,8 +157,15 @@ static int __nocfi __init dfroot_init(void) ret = umh_exec(info, UMH_WAIT_EXEC); pr_info("dfroot: usermodehelper_exec returned %d\n", ret); - return -ENODEV; /* return any error to unload module */ + /* intentionally fail init to unload immediately; avoids triggering + * mrdump module-loaded callbacks on MTK devices */ + return -ENODEV; } -/* no module_exit: we never unload; saves .exit sections */ +/* some kernels' cfi_init walks both __cfi_jt_init_module and __cfi_jt_cleanup_module + * at load time; without a module_exit, the cleanup_module CFI jump table entry is + * absent and cfi_init crashes in strcmp */ +static void __exit dfroot_exit(void) {} + module_init(dfroot_init); +module_exit(dfroot_exit);