Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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; \
Expand Down
35 changes: 9 additions & 26 deletions app/src/main/jni/bootstrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
13 changes: 9 additions & 4 deletions lkm/Makefile
Original file line number Diff line number Diff line change
@@ -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) \
Expand Down
58 changes: 21 additions & 37 deletions lkm/dfroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
#include <linux/kprobes.h>
#include <linux/module.h>
#include <linux/namei.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <asm/cpufeature.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DFRoot LKM");

Expand All @@ -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);
}

Expand All @@ -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" };
Expand Down Expand Up @@ -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);
Loading