Compare commits

...

147 Commits

Author SHA1 Message Date
tiann
c3ba483b81 ksud: fix clippy 2023-02-21 22:09:41 +08:00
tiann
e309a03515 ksud: Fix windows compile 2023-02-21 21:39:14 +08:00
tiann
82a304e054 ksud: fmt 2023-02-21 20:24:10 +08:00
tiann
b76d973f3a ksud: restore stock mount after overlay mount. close #233 2023-02-21 20:22:00 +08:00
github-actions[bot]
237e477876 [add device]: Miatoll [curtana, excalibur, gram, joyeuse] (#265)
Miatoll [curtana, excalibur, gram, joyeuse] has been added to the
website.
Related issue: https://github.com/tiann/KernelSU/issues/264

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-21 18:50:58 +08:00
tiann
47bcccdce3 ksud: fmt 2023-02-21 13:10:32 +08:00
tiann
ba8ca1f9f2 ksud: fmt 2023-02-21 13:09:03 +08:00
tiann
4837f2101c ksud: fix incorrect umask 2023-02-21 13:06:37 +08:00
tiann
37d2914611 ksud: Fix common script may not be executed 2023-02-21 12:54:05 +08:00
weishu
849164e4de kernel: fix compile err 2023-02-20 20:06:56 +07:00
weishu
683ba112aa kernel: add prefix to avoid symbol confliction 2023-02-20 18:51:59 +07:00
weishu
e743722449 kernel: make some compiler happy 2023-02-20 18:51:59 +07:00
Ylarod
a5ee2ef93b ksud: setns to init for module commands (#262) 2023-02-20 19:43:49 +08:00
Re*Index. (ot_inc)
3e66f5e8cf fix translation (#244)
Japanese Translation Adjustments and Fixes

---------

Co-authored-by: weishu <twsxtd@gmail.com>
2023-02-20 19:37:35 +08:00
weishu
83b0aed52a kernel: apply rules should return success when selinux is disabled or permissive 2023-02-20 18:13:53 +07:00
Amicia De Rune
498763505a manager: update id translation language (#259)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-20 14:33:16 +08:00
Wahid Khan
8cc4ad4d80 website: fix typos (#258) 2023-02-20 14:13:21 +08:00
TinyHai
6ec0c25173 manager: show confirm dialog before uninstall module (#257)
manager: Add Dialog component, show confirm dialog before uninstall
module, fix a bug in listModules
2023-02-20 10:52:23 +08:00
TinyHai
93bcd78f89 manager: Update the bottom bar navigation logic(#254)
close #232
2023-02-20 10:37:40 +08:00
TinyHai
ebf6a52237 manager: Add key to LazyColumn's items to prevent incorrect allow sta… (#255) 2023-02-20 10:31:58 +08:00
tiann
a2906093ec ksud: Fix denpendency 2023-02-20 10:07:30 +08:00
Nullptr
a161c318a1 kernel: allow root processes to get allow/deny list (#256) 2023-02-19 16:09:21 +08:00
github-actions[bot]
3f1ee2f784 [add device]: (#250)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/249

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-18 09:26:51 +08:00
Ylarod
60de2e4a6e manager: send app list in bug report (#248)
We need this for uid in dmesg
2023-02-16 23:39:21 +08:00
tiann
d5bb79edd5 website: Add safemode integrate instruction 2023-02-16 12:08:30 +08:00
tiann
7264a00813 ci: Fix ksud version incorrect 2023-02-16 10:05:35 +08:00
Arya Hajalizade
cf21767975 Update faq.md (#246)
Changed a line towards a better grammar and made it more readable.
2023-02-16 10:00:20 +08:00
Enes Sastim
8fbdd996de use selinux_state for 4.9.212+ (#245)
_selinux_state_ is backported to 4.9 kernel with the 4.9.212 release,
use it to fix the build.
many thanks to @reallysnow for figuring this out.
inspired by
d7c2c5f02a
2023-02-15 11:39:16 +08:00
Amicia De Rune
2c3dcae117 manager: update id translation language (#243)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-14 22:41:41 +08:00
weishu
b024b5d006 kernel: support add_type for 4.x kernel
Co-authored-by: Ookiineko <chiisaineko@protonmail.com>
2023-02-14 19:08:18 +07:00
weishu
d5bab2317e kernel: correctly handle add attr 2023-02-14 18:52:48 +07:00
tiann
0c8b4a48de ksud: ignore empty sepolicy rule 2023-02-14 18:10:39 +08:00
weishu
f9b3218ded kernel: fix policydb length 2023-02-14 14:41:00 +07:00
Amicia De Rune
acc37fb387 manager: Update ID language translation (#239)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>

Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-14 13:02:16 +08:00
tiann
f50b4dfe34 ksud: fmt 2023-02-14 12:59:21 +08:00
tiann
9ce7351aaa ksud: support check_sepolicy in scripts 2023-02-14 12:57:08 +08:00
tiann
c691a1adb2 manager: Add reboot shortcut for module screen 2023-02-14 12:01:05 +08:00
tiann
9a2a21ec5d manager: Add more info to bugreport 2023-02-14 11:44:57 +08:00
tiann
a9fd0aa132 manager: Add kernel version to bugreport 2023-02-14 11:40:24 +08:00
tiann
198674d889 manager: show manager version 2023-02-14 11:37:37 +08:00
tiann
203a5683ac manager: show safemode in homescreen 2023-02-14 11:29:16 +08:00
tiann
eeb8cda175 manager: show module state in safemode 2023-02-13 23:33:56 +08:00
tiann
b268971323 ksud: support module disable in safemode 2023-02-13 23:33:56 +08:00
weishu
051fc53a4f kernel: count time of volumedown to enter safemode 2023-02-13 22:01:59 +07:00
tiann
55602f1f16 manager: ui state for safemode 2023-02-13 22:41:32 +08:00
tiann
42428345ff ksud: check kernel safemode 2023-02-13 22:28:43 +08:00
weishu
ca950d909b kernel: press KEY_VOLUMEDOWN over 2 seconds will enter safemode and disable all modules
Co-authored-by: Ylarod <me@ylarod.cn>
2023-02-13 21:23:28 +07:00
weishu
20ff530962 kernel: fix sepolicy apply may not work #227
Co-authored-by: sekaiacg <sekaiacg@gmail.com>
2023-02-13 09:12:31 +07:00
tiann
a5dbbf4881 Revert "[add device]: GKI 1.0 (#236)"
This reverts commit e91b1fc89a.
2023-02-13 09:38:03 +08:00
github-actions[bot]
e91b1fc89a [add device]: GKI 1.0 (#236)
GKI 1.0 has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/235

---------

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: weishu <twsxtd@gmail.com>
2023-02-12 18:44:31 +08:00
tiann
4e35e4ae86 ksud: fix vendor only overlay not working 2023-02-12 18:29:27 +08:00
Aquarius223
6d15cb7e33 kernel: Fix f6967d2c lost parameters after ack-linux 4.14.y (>=163) (#234)
* [android-4.14-stable tree:
https://github.com/aosp-mirror/kernel_common/commit/5d0939e1]

Change-Id: Ice92dd83df4c4f1ae272156cb57f95998e45819f

Co-authored-by: stic-server-open <1138705738@qq.com>
2023-02-12 18:11:28 +08:00
tiann
095acad8a6 ksud: Fix mount ext4 failed of sys-mount 2023-02-12 18:07:50 +08:00
skbeh
c187d1ad8a ksud: remove tailing "/" from PATH (#230) 2023-02-12 18:07:07 +08:00
weishu
f6967d2cfb kernel: reset avc cache for sepolicy
Co-authored-by: sekaiacg <sekaiacg@gmail.com>
2023-02-12 13:09:00 +07:00
weishu
388d2b0b59 kernel: disable inofity for fd opened for manager 2023-02-12 12:15:58 +07:00
github-actions[bot]
d9aecbcbca [add device]: (#229)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/228

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-11 19:55:23 +08:00
rxuglr
eabdf3e78c manager: Add Russian language (#225) 2023-02-11 15:26:55 +08:00
tiann
a1fb7c5fdf ksud: don't hardcode system unzip becuase we use busybox now 2023-02-10 18:51:22 +08:00
f19
89394245b1 fix KSU_GIT_VERSION is not defined while importing KSU by 'git submodule add' (#224)
.git is a text file while the module is imported by 'git submodule add'.
2023-02-10 16:34:47 +08:00
Re*Index. (ot_inc)
91f1eb2d6a Added Japanese language to KernelSU Manager (#223)
Japanese translation added.
2023-02-09 23:51:53 +08:00
tiann
ab5f6db54b website: Update installation 2023-02-09 14:04:41 +08:00
tiann
626642af76 ksud: e2fsck before resize2fs 2023-02-09 13:53:07 +08:00
tiann
473f02396f website: Update installation 2023-02-09 10:51:16 +08:00
tiann
aa4b1bf9d8 website: Update installation instruction 2023-02-09 09:28:16 +08:00
tiann
8e5a72fc35 ci: Fix release build 2023-02-08 23:58:47 +08:00
weishu
7302653879 Add release ci (#218) 2023-02-08 22:31:43 +08:00
github-actions[bot]
b2b563547c [add device]: (#220)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/219

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-08 22:18:05 +08:00
tiann
cc29ad151a ksud: Add KSU_KERNEL_VER_CODE 2023-02-08 15:09:36 +08:00
tiann
a4a93d8945 ksud: clippy 2023-02-08 12:33:00 +08:00
tiann
3389cd0aea ksud: Fix module operation order 2023-02-08 12:26:33 +08:00
tiann
d26956ff72 ksud: Fix confliction with stock system overlayfs 2023-02-08 12:14:31 +08:00
Aquarius223
66284bfbe3 kernel: Correctly judge the version of selinux_state (#214)
* Merged related changes in android-common-kernel-4.14.163 [1] Handle
correct checks for older versions (below 4.14.163, 4.9, 4.4)
* fixes 14be7562, it is required in 4.14.163 kernel, it is not needed in
4.14.163 and later

[1]: https://github.com/MiCode/Xiaomi_Kernel_OpenSource/commits/1cfd8419

Change-Id: Iade104312d058a249667ca836259d5608418bb55

Co-authored-by: admin <paper@localhost>
2023-02-07 15:02:48 +08:00
初羽
14be75629b kernel: selinux: Fix build error (#213)
* d77ab8d although fixed some typo, but since there is no include avc.h,
this will cause the build to fail
* Error:


/mnt/HDD/fish/aospa/kernel/msm-4.9/drivers/staging/ksu/selinux/selinux.c:60:2:
error: use of undeclared identifier 'selinux_enforcing'
        selinux_enforcing = enforce;
        ^

/mnt/HDD/fish/aospa/kernel/msm-4.9/drivers/staging/ksu/selinux/selinux.c:81:9:
error: use of undeclared identifier 'selinux_enforcing'
        return selinux_enforcing;
               ^
2 errors generated.

Change-Id: I4cc8917b2b73ca467873222e02d498da8718988e
2023-02-07 09:50:55 +08:00
小さい猫
d77ab8dbff kernel: selinux: fix more typos introduced in #166 (#212)
i didn't realize these typos in #181

Signed-off-by: Ookiineko <chiisaineko@protonmail.com>
2023-02-06 16:31:26 +08:00
weishu
c2ac548ac7 kernel: allow use without git submodule 2023-02-06 15:30:58 +07:00
tiann
ad4d8e939e ksud: clippy 2023-02-06 09:16:15 +08:00
tiann
aa7a00b299 ksud: don't mount module when in safe mode 2023-02-06 08:58:02 +08:00
tiann
1ff421365e ksud: pass KSU_VER and KSU_VER_CODE to installer script 2023-02-06 08:53:18 +08:00
weishu
0a12d0139d kernel: make sure KernelSU is a submodule to avoding incorrect git version 2023-02-05 16:31:50 +07:00
Daybit
181ab4f545 [add device]:Pixel4XL (#210)
[add device]:Pixel4XL
2023-02-05 13:21:40 +08:00
github-actions[bot]
3e29e98f2c [add device]: (#209)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/208

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-05 11:42:58 +08:00
Amicia De Rune
a66c1de660 manager: update translation ID language (#207)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>

Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-05 07:15:21 +08:00
f19
0c322a33bc kernel: fix filp_open on older kernel's kworker (#205)
On older kernel, kworker missing keyring from init process , and this
keyring is related to FBE , which causes filp_open return ENOKEY or
other errors.To fix this,just install init's keyring to per
kworkers.This works on Kernel 4.4 and 4.9.
2023-02-05 07:14:59 +08:00
小さい猫
cd33a6dd07 website: document the replacement of do_faccessat for some non-GKI (#206)
Signed-off-by: Ookiineko <chiisaineko@protonmail.com>
2023-02-04 21:56:04 +08:00
github-actions[bot]
c1dceaf11f [add device]: (#204)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/203

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-04 15:35:26 +08:00
skbeh
3181dd17bc ci: add CI for clippy, rustfmt and shell scripts (#193) 2023-02-04 13:52:20 +08:00
VINC3NT
c93fa1af59 [add device]: Redmi K40s / POCO F4 (#201) 2023-02-04 13:36:01 +08:00
skbeh
9f4a8d3dfc ksud, kernel: update explanation and fix some issues (#194) 2023-02-04 13:16:51 +08:00
weishu
4da829792f kernel: unify version 2023-02-03 23:01:02 +07:00
github-actions[bot]
1a073224c3 [add device]: Redmi K40 / POCO F3 (#198)
Redmi K40 / POCO F3 has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/197

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-03 23:33:36 +08:00
tiann
175de861bf ksud: Fix incorrect dependency 2023-02-03 23:19:22 +08:00
tiann
aa73c34db2 ksud: unify version 2023-02-03 21:02:11 +08:00
github-actions[bot]
ed2176af8c [add device]: (#196)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/195

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-03 19:12:44 +08:00
tiann
49f7d56fdd manager: Add send log 2023-02-03 18:14:42 +08:00
tiann
71cc166f72 ksud: Fix macOS compile 2023-02-03 14:46:48 +08:00
tiann
8ee00839dc ksud: Fix script exec 2023-02-03 14:26:26 +08:00
github-actions[bot]
6239662a7f [add device]: Redmi Note 9 Pro/ Mi 10T Lite/ Mi 10i (#190)
Redmi Note 9 Pro/ Mi 10T Lite/ Mi 10i has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/189

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-03 14:12:24 +08:00
Huy Minh
216c2aa2cd website: vi_VN: Update FAQs (#191) 2023-02-03 14:03:05 +08:00
tiann
70b8b43b48 ksud: support sepolicy check 2023-02-03 13:16:17 +08:00
tiann
430c2e709f website: Add some faq 2023-02-03 11:11:41 +08:00
tiann
b975950b07 ksud: fix module update 2023-02-03 10:05:09 +08:00
tiann
ed42cf42d2 ksud: restore syscon before module install script; this revert 2a4675e25b 2023-02-03 10:00:52 +08:00
tiann
d80c282644 ksud: tweaks for setcon 2023-02-03 09:55:03 +08:00
tiann
a05edb3872 manager: coming soon for system writable 2023-02-03 09:45:42 +08:00
tiann
43b8987b4b manager: open installation guide when install 2023-02-03 09:45:42 +08:00
skbeh
219ea1c458 ksud: fix issues found by clippy (#167)
These issues are mostly found by `cargo clippy -- -W clippy::pedantic`.
2023-02-03 09:45:07 +08:00
skbeh
bea93f6ad7 ksud: compress embed assets (#186) 2023-02-03 09:31:32 +08:00
耀風
e4267848f0 manager: Update Traditional Chinese (#185) 2023-02-03 08:37:07 +08:00
Huy Minh
bc5953b510 manager: vi_VN: small edit (#184) 2023-02-03 05:15:32 +08:00
Arthur
e41b7cd117 website: fix description error (#183) 2023-02-03 05:14:36 +08:00
Nguyễn Long
cab78e7893 manager: add vi translation (#182)
Signed-off-by: HoangLong-Lumi <hoanglong.lumi404@gmail.com>
2023-02-02 23:39:10 +08:00
小さい猫
199f5cc223 fix some typos introduced in #166 (#181)
Signed-off-by: Ookiineko <chiisaineko@protonmail.com>
Co-authored-by: f19 <58457605+F-19-F@users.noreply.github.com>
Co-authored-by: Scirese <nuclearlight91@gmail.com>
2023-02-02 23:38:04 +08:00
Amicia De Rune
962649f7ca manager: add id translation (#179)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-02 22:40:33 +08:00
tiann
7b32c0e37b ksud: opti module mount 2023-02-02 21:56:08 +08:00
tiann
12f353a1ae ksud: Add bin path to script PATH 2023-02-02 21:42:04 +08:00
tiann
c26e170c87 ksud: default umask 022(0644 for file) 2023-02-02 21:24:11 +08:00
tiann
6a706de09e ksud: use busybox to exec all shell scripts 2023-02-02 20:37:17 +08:00
tiann
2a4675e25b ksud: set /system permission after installation script 2023-02-02 20:37:17 +08:00
tiann
0bc36b3299 ksud: fix incorrecy RAII 2023-02-02 20:37:17 +08:00
tiann
dc902b16d4 ksud: copy when rename failed 2023-02-02 20:37:17 +08:00
tiann
86998a032e ksud: fallback to system mount when rust lib mount failed 2023-02-02 20:37:17 +08:00
github-actions[bot]
1727ec41c4 [add device]: OnePlus 8 Serials (#178)
OnePlus 8 Serials has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/177

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-02 20:10:51 +08:00
tiann
3eb812be5b ksud: Fix module may mount failed 2023-02-02 14:02:02 +08:00
Amicia De Rune
64c2f6ba5c website: update translation in ID language (#176)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>

Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-02 13:59:10 +08:00
WeeAris
2db7da0766 website:fix devices info error (#175)
Fix devices info error for unofficially supported devices.
#173
2023-02-02 11:23:21 +08:00
weishu
d1e7bad18f kernel: version 15 2023-02-02 09:05:37 +07:00
tiann
d7cef25665 manager: Superuser support refresh and hide system apps 2023-02-02 09:59:40 +08:00
tiann
23f41145b0 ksud: Fix module resize error 2023-02-02 09:25:41 +08:00
tiann
a969af8159 relicense kernel source code to GPL-v2 2023-02-02 09:04:07 +08:00
tiann
001fa00355 ksud: Add logs for module installation 2023-02-02 08:58:34 +08:00
tiann
a6dddd32b3 website: fix build err 2023-02-02 08:40:28 +08:00
tiann
cd825e34da ksud: support common post-fs-data.d and service.d 2023-02-02 08:39:26 +08:00
github-actions[bot]
2bba088319 [add device]: non-GKI devices, 4.19.x kernel, sm8250, repo for AOSP only. (#171)
non-GKI devices, 4.19.x kernel, sm8250, repo for AOSP only. has been
added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/170

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-02 03:20:19 +08:00
Amicia De Rune
4905bd9bb8 website: fix missing directory in ID language (#169)
sry missing /guide/

Signed-off-by: RooGhz720 <rooghz720@gmail.com>

Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-02 03:19:27 +08:00
Amicia De Rune
3f625a000b website: fix missing pages in ID language (#168)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>

Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-01 22:41:53 +08:00
tiann
3bfee10a0d ksud: set SELinux context for /vendor 2023-02-01 21:10:16 +08:00
tiann
d5a05da5b8 ci: don't build kernel when ksud changes 2023-02-01 21:01:32 +08:00
tiann
d8042a36c3 ksud: fix post-fs-data.sh and service.sh may be not executed 2023-02-01 20:28:38 +08:00
tiann
85bf01eb65 ksud: use logcat log 2023-02-01 20:28:38 +08:00
f19
4f2b8b7077 kernel: backport to 4.4 (#166)
These changes make KernelSU work on kernel4.4
[link](https://github.com/F-19-F/android_kernel_oneplus_msm8998).
LINUX_VERSION_CODE macro changes have been vertied on 4.4 4.9 4.14.
For kernel 4.4,just pick two commits
* [introduce
KernelSU](2993524f2f)
* [allow init exec ksud under
nosuid](3df9df42a6)
2023-02-01 19:48:36 +08:00
tiann
417ff8a6c5 ksud: fix module installation failed when size < 1M 2023-02-01 19:30:39 +08:00
tiann
681c4a3f0d ksud: minor tweaks 2023-02-01 19:12:25 +08:00
tiann
619dd1ace1 ksud: make it compilable for non-android 2023-02-01 19:04:33 +08:00
tiann
1cd18a643d ksud: some refactor 2023-02-01 18:33:38 +08:00
tiann
3519d61636 ksud: format code 2023-02-01 17:59:16 +08:00
skbeh
46913671a8 kernel, ksud: collect binaries into /data/adb/ksu (#161) 2023-02-01 17:58:58 +08:00
92 changed files with 4115 additions and 720 deletions

View File

@@ -1,51 +1,56 @@
build_from_image(){
export TITLE=kernel-aarch64-$(echo $1 | sed 's/Image-//g')
#!/bin/bash
set -euo pipefail
build_from_image() {
export TITLE
TITLE=kernel-aarch64-${1//Image-/}
echo "[+] title: $TITLE"
export PATCH_LEVEL=$(echo $1 | awk -F_ '{ print $2}')
export PATCH_LEVEL
PATCH_LEVEL=$(echo "$1" | awk -F_ '{ print $2}')
echo "[+] patch level: $PATCH_LEVEL"
echo "[+] Download prebuilt ramdisk"
curl -Lo gki-kernel.zip https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-${PATCH_LEVEL}_r1.zip
echo '[+] Download prebuilt ramdisk'
curl -Lo gki-kernel.zip https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-"${PATCH_LEVEL}"_r1.zip
unzip gki-kernel.zip && rm gki-kernel.zip
echo "[+] Unpack prebuilt boot.img"
echo '[+] Unpack prebuilt boot.img'
BOOT_IMG=$(find . -maxdepth 1 -name "boot*.img")
$UNPACK_BOOTIMG --boot_img=$BOOT_IMG
rm $BOOT_IMG
$UNPACK_BOOTIMG --boot_img="$BOOT_IMG"
rm "$BOOT_IMG"
echo "[+] Building Image.gz"
cat Image | $GZIP -n -f -9 > Image.gz
echo '[+] Building Image.gz'
$GZIP -n -k -f -9 Image >Image.gz
echo "[+] Building boot.img"
$MKBOOTIMG --header_version 4 --kernel Image --output boot.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level ${PATCH_LEVEL}
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
echo '[+] Building boot.img'
$MKBOOTIMG --header_version 4 --kernel Image --output boot.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}"
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
echo "[+] Building boot-gz.img"
$MKBOOTIMG --header_version 4 --kernel Image.gz --output boot-gz.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level ${PATCH_LEVEL}
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
echo '[+] Building boot-gz.img'
$MKBOOTIMG --header_version 4 --kernel Image.gz --output boot-gz.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}"
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
echo "[+] Building boot-lz4.img"
$MKBOOTIMG --header_version 4 --kernel Image.lz4 --output boot-lz4.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level ${PATCH_LEVEL}
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
echo '[+] Building boot-lz4.img'
$MKBOOTIMG --header_version 4 --kernel Image.lz4 --output boot-lz4.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}"
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
echo "[+] Compress images"
for image in boot*.img; do
$GZIP -n -f -9 $image
mv $image.gz ksu-$VERSION-$image.gz
done
echo '[+] Compress images'
for image in boot*.img; do
$GZIP -n -f -9 "$image"
mv "$image".gz ksu-"$VERSION"-"$1"-"$image".gz
done
echo "[+] Images to upload"
find . -type f -name "*.gz"
echo "[+] Images to upload"
find . -type f -name "*.gz"
find . -type f -name "*.gz" | xargs python3 $GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py
find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU/scripts/ksubot.py {} +
}
for dir in Image*; do
if [ -d "$dir" ]; then
echo "----- Building $dir -----"
cd $dir
build_from_image $dir
cd "$dir"
build_from_image "$dir"
cd ..
fi
done
done

View File

@@ -1,38 +1,43 @@
build_from_image(){
export TITLE=kernel-aarch64-$(echo $1 | sed 's/Image-//g')
#!/bin/bash
set -euo pipefail
build_from_image() {
export TITLE
TITLE=kernel-aarch64-${1//Image-/}
echo "[+] title: $TITLE"
echo "[+] Building Image.gz"
cat Image | $GZIP -n -f -9 > Image.gz
echo '[+] Building Image.gz'
$GZIP -n -k -f -9 Image >Image.gz
echo "[+] Building boot.img"
echo '[+] Building boot.img'
$MKBOOTIMG --header_version 4 --kernel Image --output boot.img
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
echo "[+] Building boot-gz.img"
echo '[+] Building boot-gz.img'
$MKBOOTIMG --header_version 4 --kernel Image.gz --output boot-gz.img
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
echo "[+] Building boot-lz4.img"
echo '[+] Building boot-lz4.img'
$MKBOOTIMG --header_version 4 --kernel Image.lz4 --output boot-lz4.img
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
echo "[+] Compress images"
for image in boot*.img; do
$GZIP -n -f -9 $image
mv $image.gz ksu-$VERSION-$image.gz
done
echo '[+] Compress images'
for image in boot*.img; do
$GZIP -n -f -9 "$image"
mv "$image".gz ksu-"$VERSION"-"$1"-"$image".gz
done
echo "[+] Images to upload"
find . -type f -name "*.gz"
echo '[+] Images to upload'
find . -type f -name "*.gz"
find . -type f -name "*.gz" | xargs python3 $GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py
find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU/scripts/ksubot.py {} +
}
for dir in Image*; do
if [ -d "$dir" ]; then
echo "----- Building $dir -----"
cd $dir
build_from_image $dir
cd "$dir"
build_from_image "$dir"
cd ..
fi
done
done

View File

@@ -9,6 +9,7 @@ on:
branches: [ "main" ]
paths:
- 'kernel/**'
workflow_call:
jobs:
build:
strategy:

View File

@@ -7,7 +7,6 @@ on:
- ".github/workflows/gki-kernel.yml"
- ".github/scripts/build_a12.sh"
- "kernel/**"
- "userspace/ksud/**"
pull_request:
branches: ["main"]
paths:
@@ -15,7 +14,7 @@ on:
- ".github/workflows/gki-kernel.yml"
- ".github/scripts/build-a12.sh"
- "kernel/**"
- "userspace/ksud/**"
workflow_call:
jobs:
build-kernel:
if: github.event_name != 'pull_request'
@@ -43,7 +42,7 @@ jobs:
upload-artifacts:
needs: build-kernel
runs-on: ubuntu-latest
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
env:
CHAT_ID: ${{ secrets.CHAT_ID }}
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
@@ -93,11 +92,20 @@ jobs:
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
cd $GITHUB_WORKSPACE/KernelSU
export VERSION=$(git rev-list --count HEAD)
export VERSION=$(($(git rev-list --count HEAD) + 10200))
echo "VERSION: $VERSION"
cd -
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a12.sh
- name: Display structure of boot files
run: ls -R
- name: Upload images artifact
uses: actions/upload-artifact@v3
with:
name: boot-images-android12
path: Image-android12*/*.img.gz
check-build-kernel:
if: github.event_name == 'pull_request'
uses: ./.github/workflows/gki-kernel.yml

View File

@@ -7,7 +7,6 @@ on:
- ".github/workflows/gki-kernel.yml"
- ".github/scripts/build_a13.sh"
- "kernel/**"
- "userspace/ksud/**"
pull_request:
branches: ["main"]
paths:
@@ -15,7 +14,7 @@ on:
- ".github/workflows/gki-kernel.yml"
- ".github/scripts/build-a13.sh"
- "kernel/**"
- "userspace/ksud/**"
workflow_call:
jobs:
build-kernel:
if: github.event_name != 'pull_request'
@@ -44,7 +43,7 @@ jobs:
upload-artifacts:
needs: build-kernel
runs-on: ubuntu-latest
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
env:
CHAT_ID: ${{ secrets.CHAT_ID }}
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
@@ -94,10 +93,19 @@ jobs:
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
cd $GITHUB_WORKSPACE/KernelSU
export VERSION=$(git rev-list --count HEAD)
export VERSION=$(($(git rev-list --count HEAD) + 10200))
echo "VERSION: $VERSION"
cd -
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh
- name: Display structure of boot files
run: ls -R
- name: Upload images artifact
uses: actions/upload-artifact@v3
with:
name: boot-images-android13
path: Image-android13*/*.img.gz
check-build-kernel:
if: github.event_name == 'pull_request'

View File

@@ -19,6 +19,7 @@ jobs:
include:
- target: aarch64-linux-android
- target: x86_64-linux-android
- target: x86_64-pc-windows-gnu # only for build
uses: ./.github/workflows/ksud.yml
with:
target: ${{ matrix.target }}

View File

@@ -10,6 +10,7 @@ on:
branches: [ "main" ]
paths:
- 'manager/**'
workflow_call:
jobs:
build-ksud:
strategy:

22
.github/workflows/clippy-pr.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: Clippy check for pull request
on:
pull_request:
branches:
- 'main'
paths:
- '.github/workflows/clippy-pr.yml'
- 'userspace/ksud/**'
permissions:
checks: write
jobs:
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: giraffate/clippy-action@v1
with:
workdir: userspace/ksud

30
.github/workflows/clippy.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Clippy check
on:
push:
branches:
- 'main'
paths:
- '.github/workflows/clippy.yml'
- 'userspace/ksud/**'
env:
RUSTFLAGS: '-Dwarnings'
jobs:
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2
with:
workspaces: userspace/ksud
- name: Install cross
run: cargo install cross
- name: Run clippy
run: |
cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android

View File

@@ -14,19 +14,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up cargo cache
if: inputs.use_cache == true
uses: actions/cache@v3
continue-on-error: false
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
fetch-depth: 0
- uses: Swatinem/rust-cache@v2
with:
workspaces: userspace/ksud
cache-targets: false
- name: Install cross
run: cargo install cross
- name: Build ksud
run: cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud/Cargo.toml
@@ -35,5 +32,5 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: ksud-${{ inputs.target }}
path: ./userspace/ksud/target/**/release/ksud
path: userspace/ksud/target/**/release/ksud

56
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
name: Release
on:
push:
tags:
- "v*"
workflow_dispatch:
jobs:
build-manager:
uses: ./.github/workflows/build-manager.yml
secrets: inherit
build-a12-kernel:
uses: ./.github/workflows/build-kernel-a12.yml
build-a13-kernel:
uses: ./.github/workflows/build-kernel-a13.yml
build-wsa-kernel:
uses: ./.github/workflows/build-WSA-5.10.117-kernel.yml
release:
needs:
- build-manager
- build-a12-kernel
- build-a13-kernel
- build-wsa-kernel
runs-on: ubuntu-latest
steps:
- name: Download artifacts
uses: actions/download-artifact@v3
- name: Zip AnyKernel3
run: |
for dir in AnyKernel3-*; do
if [ -d "$dir" ]; then
echo "----- Zip $dir -----"
(cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..)
fi
done
- name: Zip WSA kernel
run: |
for dir in kernel-WSA-*; do
if [ -d "$dir" ]; then
echo "------ Zip $dir ----------"
(cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..)
fi
done
- name: Display structure of downloaded files
run: ls -R
- name: release
uses: softprops/action-gh-release@v1
with:
files: |
manager/*.apk
AnyKernel3-*.zip
boot-images-*/Image-*/*.img.gz
kernel-WSA*.zip

33
.github/workflows/rustfmt.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Rustfmt check
on:
push:
branches:
- 'main'
paths:
- '.github/workflows/rustfmt.yml'
- 'userspace/ksud/**'
pull_request:
branches:
- 'main'
paths:
- '.github/workflows/rustfmt.yml'
- 'userspace/ksud/**'
permissions:
checks: write
jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt
- uses: LoliGothick/rustfmt-check@v0.2
with:
token: ${{ github.token }}
options: --manifest-path userspace/ksud/Cargo.toml

27
.github/workflows/shellcheck.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: ShellCheck
on:
push:
branches:
- 'main'
paths:
- '.github/workflows/shellcheck.yml'
- '**/*.sh'
pull_request:
branches:
- 'main'
paths:
- '.github/workflows/shellcheck.yml'
- '**/*.sh'
jobs:
shellcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@2.0.0
with:
ignore_names: gradlew
ignore_paths: ./userspace/ksud/src/installer.sh

View File

@@ -31,7 +31,8 @@ And the current supported ABIs are : `arm64-v8a` and `x86_64`
## License
[GPL-3](http://www.gnu.org/copyleft/gpl.html)
- Files under `kernel` directory are [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
- All other parts except `kernel` directory are [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html)
## Credits

View File

@@ -31,7 +31,8 @@ WSA 和运行在容器上的 Android 也可以与 KernelSU 一起工作。
## 许可证
[GPL-3](http://www.gnu.org/copyleft/gpl.html)
- 目录 `kernel` 下所有文件为 [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
-`kernel` 目录的其他部分均为 [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html)
## 鸣谢

339
kernel/LICENSE Normal file
View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -9,8 +9,15 @@ obj-y += manager.o
obj-y += core_hook.o
obj-y += ksud.o
obj-y += embed_ksud.o
obj-y += kernel_compat.o
obj-y += selinux/
# .git is a text file while the module is imported by 'git submodule add'.
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
# We must use the absolute path to git, otherwise the build will fail if git is not in the PATH
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src);/usr/bin/git rev-list --count HEAD)
ccflags-y += -DKSU_GIT_VERSION=$(KSU_GIT_VERSION)
endif
ifndef EXPECTED_SIZE
EXPECTED_SIZE := 0x033b

View File

@@ -4,9 +4,10 @@
#include "linux/list.h"
#include "linux/printk.h"
#include "linux/slab.h"
#include "linux/version.h"
#include "klog.h" // IWYU pragma: keep
#include "selinux/selinux.h"
#include "kernel_compat.h"
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
#define FILE_FORMAT_VERSION 1 // u32
@@ -21,7 +22,7 @@ struct perm_data {
static struct list_head allow_list;
#define KERNEL_SU_ALLOWLIST "/data/adb/.ksu_allowlist"
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
static struct work_struct ksu_save_work;
static struct work_struct ksu_load_work;
@@ -118,7 +119,7 @@ void do_persistent_allow_list(struct work_struct *work)
struct perm_data *p = NULL;
struct list_head *pos = NULL;
loff_t off = 0;
KWORKER_INSTALL_KEYRING();
struct file *fp =
filp_open(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT, 0644);
@@ -128,12 +129,12 @@ void do_persistent_allow_list(struct work_struct *work)
}
// store magic and version
if (kernel_write(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
pr_err("save_allow_list write magic failed.\n");
goto exit;
}
if (kernel_write(fp, &version, sizeof(version), &off) !=
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
sizeof(version)) {
pr_err("save_allow_list write version failed.\n");
goto exit;
@@ -143,8 +144,8 @@ void do_persistent_allow_list(struct work_struct *work)
p = list_entry(pos, struct perm_data, list);
pr_info("save allow list 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);
ksu_kernel_write_compat(fp, &p->uid, sizeof(p->uid), &off);
ksu_kernel_write_compat(fp, &p->allow, sizeof(p->allow), &off);
}
exit:
@@ -158,8 +159,8 @@ void do_load_allow_list(struct work_struct *work)
struct file *fp = NULL;
u32 magic;
u32 version;
fp = filp_open("/data/adb/", O_RDONLY, 0);
KWORKER_INSTALL_KEYRING();
fp = filp_open("/data/adb", O_RDONLY, 0);
if (IS_ERR(fp)) {
int errno = PTR_ERR(fp);
pr_err("load_allow_list open '/data/adb': %d\n", PTR_ERR(fp));
@@ -194,13 +195,13 @@ void do_load_allow_list(struct work_struct *work)
}
// verify magic
if (kernel_read(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
magic != FILE_MAGIC) {
pr_err("allowlist file invalid: %d!\n", magic);
goto exit;
}
if (kernel_read(fp, &version, sizeof(version), &off) !=
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) !=
sizeof(version)) {
pr_err("allowlist read version: %d failed\n", version);
goto exit;
@@ -211,12 +212,12 @@ void do_load_allow_list(struct work_struct *work)
while (true) {
u32 uid;
bool allow = false;
ret = kernel_read(fp, &uid, sizeof(uid), &off);
ret = ksu_kernel_read_compat(fp, &uid, sizeof(uid), &off);
if (ret <= 0) {
pr_info("load_allow_list read err: %d\n", ret);
break;
}
ret = kernel_read(fp, &allow, sizeof(allow), &off);
ret = ksu_kernel_read_compat(fp, &allow, sizeof(allow), &off);
pr_info("load_allow_uid: %d, allow: %d\n", uid, allow);

View File

@@ -3,6 +3,7 @@
#include "apk_sign.h"
#include "klog.h" // IWYU pragma: keep
#include "kernel_compat.h"
static __always_inline int
check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
@@ -14,21 +15,25 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
loff_t pos;
int sign = -1;
int i;
struct file *fp = filp_open(path, O_RDONLY, 0);
if (IS_ERR(fp)) {
pr_err("open %s error.", path);
return PTR_ERR(fp);
}
// disable inotify for this file
fp->f_mode |= FMODE_NONOTIFY;
sign = 1;
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
for (int i = 0;; ++i) {
for (i = 0;; ++i) {
unsigned short n;
pos = generic_file_llseek(fp, -i - 2, SEEK_END);
kernel_read(fp, &n, 2, &pos);
ksu_kernel_read_compat(fp, &n, 2, &pos);
if (n == i) {
pos -= 22;
kernel_read(fp, &size4, 4, &pos);
ksu_kernel_read_compat(fp, &size4, 4, &pos);
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
break;
}
@@ -41,17 +46,17 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
pos += 12;
// offset
kernel_read(fp, &size4, 0x4, &pos);
ksu_kernel_read_compat(fp, &size4, 0x4, &pos);
pos = size4 - 0x18;
kernel_read(fp, &size8, 0x8, &pos);
kernel_read(fp, buffer, 0x10, &pos);
ksu_kernel_read_compat(fp, &size8, 0x8, &pos);
ksu_kernel_read_compat(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);
ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos);
if (size_of_block != size8) {
goto clean;
}
@@ -59,37 +64,37 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
for (;;) {
uint32_t id;
uint32_t offset;
kernel_read(fp, &size8, 0x8, &pos); // sequence length
ksu_kernel_read_compat(fp, &size8, 0x8, &pos); // sequence length
if (size8 == size_of_block) {
break;
}
kernel_read(fp, &id, 0x4, &pos); // id
ksu_kernel_read_compat(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,
ksu_kernel_read_compat(fp, &size4, 0x4,
&pos); // signer-sequence length
kernel_read(fp, &size4, 0x4, &pos); // signer length
kernel_read(fp, &size4, 0x4,
ksu_kernel_read_compat(fp, &size4, 0x4, &pos); // signer length
ksu_kernel_read_compat(fp, &size4, 0x4,
&pos); // signed data length
offset += 0x4 * 3;
kernel_read(fp, &size4, 0x4,
ksu_kernel_read_compat(fp, &size4, 0x4,
&pos); // digests-sequence length
pos += size4;
offset += 0x4 + size4;
kernel_read(fp, &size4, 0x4,
ksu_kernel_read_compat(fp, &size4, 0x4,
&pos); // certificates length
kernel_read(fp, &size4, 0x4,
ksu_kernel_read_compat(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);
ksu_kernel_read_compat(fp, &c, 0x1, &pos);
hash = 31 * hash + c;
}
offset += size4;
@@ -99,7 +104,7 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
int hash = 1;
signed char c;
for (unsigned i = 0; i < size4; ++i) {
kernel_read(fp, &c, 0x1, &pos);
ksu_kernel_read_compat(fp, &c, 0x1, &pos);
hash = 31 * hash + c;
}
offset += size4;

View File

@@ -22,6 +22,7 @@
#include "manager.h"
#include "selinux/selinux.h"
#include "uid_observer.h"
#include "kernel_compat.h"
extern int handle_sepolicy(unsigned long arg3, void __user *arg4);
@@ -242,6 +243,39 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
return 0;
}
if (arg2 == CMD_CHECK_SAFEMODE) {
if (ksu_is_safe_mode()) {
pr_warn("safemode enabled!\n");
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("safemode: prctl reply error\n");
}
}
return 0;
}
if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) {
if (is_manager() || 0 == current_uid().val) {
u32 array[128];
u32 array_length;
bool success = ksu_get_allow_list(array, &array_length,
arg2 == CMD_GET_ALLOW_LIST);
if (success) {
if (!copy_to_user(arg4, &array_length,
sizeof(array_length)) &&
!copy_to_user(arg3, array,
sizeof(u32) * array_length)) {
if (copy_to_user(result, &reply_ok,
sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n",
arg2);
}
} else {
pr_err("prctl copy allowlist error\n");
}
}
}
}
// all other cmds are for 'root manager'
if (!is_manager()) {
pr_info("Only manager can do cmd: %d\n", arg2);
@@ -260,25 +294,6 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
}
}
ksu_show_allow_list();
} 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) {
if (!copy_to_user(arg4, &array_length,
sizeof(array_length)) &&
!copy_to_user(arg3, array,
sizeof(u32) * array_length)) {
if (copy_to_user(result, &reply_ok,
sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n",
arg2);
}
} else {
pr_err("prctl copy allowlist error\n");
}
}
}
return 0;
@@ -356,7 +371,26 @@ static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
return -ENOSYS;
}
// kernel 4.4 and 4.9
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
static int ksu_key_permission(key_ref_t key_ref,
const struct cred *cred,
unsigned perm)
{
if (init_session_keyring != NULL)
{
return 0;
}
if (strcmp(current->comm, "init"))
{
// we are only interested in `init` process
return 0;
}
init_session_keyring = cred->session_keyring;
pr_info("kernel_compat: got init_session_keyring");
return 0;
}
#endif
static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
struct inode *new_inode, struct dentry *new_dentry)
{
@@ -366,6 +400,9 @@ static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
static struct security_hook_list ksu_hooks[] = {
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
LSM_HOOK_INIT(key_permission, ksu_key_permission)
#endif
};
void __init ksu_lsm_hook_init(void)

34
kernel/kernel_compat.c Normal file
View File

@@ -0,0 +1,34 @@
#include "linux/version.h"
#include "linux/fs.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
#include "linux/key.h"
#include "linux/errno.h"
struct key *init_session_keyring = NULL;
#endif
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, loff_t *pos){
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
return kernel_read(p, buf, count, pos);
#else
loff_t offset = pos ? *pos : 0;
ssize_t result = kernel_read(p, offset, (char *)buf, count);
if (pos && result > 0)
{
*pos = offset + result;
}
return result;
#endif
}
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, loff_t *pos){
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
return kernel_write(p, buf, count, pos);
#else
loff_t offset = pos ? *pos : 0;
ssize_t result = kernel_write(p, buf, count, offset);
if (pos && result > 0)
{
*pos = offset + result;
}
return result;
#endif
}

42
kernel/kernel_compat.h Normal file
View File

@@ -0,0 +1,42 @@
#ifndef __KSU_H_KERNEL_COMPAT
#define __KSU_H_KERNEL_COMPAT
#include "linux/fs.h"
#include "linux/key.h"
#include "linux/version.h"
extern struct key *init_session_keyring;
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, loff_t *pos);
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, loff_t *pos);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
static inline int install_session_keyring(struct key *keyring)
{
struct cred *new;
int ret;
new = prepare_creds();
if (!new)
return -ENOMEM;
ret = install_session_keyring_to_cred(new, keyring);
if (ret < 0) {
abort_creds(new);
return ret;
}
return commit_creds(new);
}
#define KWORKER_INSTALL_KEYRING() \
static bool keyring_installed = false; \
if (init_session_keyring != NULL && !keyring_installed) \
{ \
install_session_keyring(init_session_keyring); \
keyring_installed = true; \
}
#else
#define KWORKER_INSTALL_KEYRING()
#endif
#endif

View File

@@ -3,7 +3,12 @@
#include "linux/workqueue.h"
#define KERNEL_SU_VERSION 14
#ifndef KSU_GIT_VERSION
#warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git submodule!"
#define KERNEL_SU_VERSION (16)
#else
#define KERNEL_SU_VERSION (10000 + KSU_GIT_VERSION + 200) // major * 10000 + git version + 200 for historical reasons
#endif
#define KERNEL_SU_OPTION 0xDEADBEEF
@@ -16,6 +21,7 @@
#define CMD_GET_DENY_LIST 6
#define CMD_REPORT_EVENT 7
#define CMD_SET_SEPOLICY 8
#define CMD_CHECK_SAFEMODE 9
#define EVENT_POST_FS_DATA 1
#define EVENT_BOOT_COMPLETED 2

View File

@@ -3,16 +3,19 @@
#include "linux/dcache.h"
#include "linux/err.h"
#include "linux/fs.h"
#include "linux/input-event-codes.h"
#include "linux/kprobes.h"
#include "linux/printk.h"
#include "linux/types.h"
#include "linux/uaccess.h"
#include "linux/version.h"
#include "linux/workqueue.h"
#include "linux/input.h"
#include "allowlist.h"
#include "arch.h"
#include "klog.h" // IWYU pragma: keep
#include "ksud.h"
#include "selinux/selinux.h"
static const char KERNEL_SU_RC[] =
@@ -20,32 +23,35 @@ static const char KERNEL_SU_RC[] =
"on post-fs-data\n"
// We should wait for the post-fs-data finish
" exec u:r:su:s0 root -- /data/adb/ksud post-fs-data\n"
" exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n"
"\n"
"on nonencrypted\n"
" exec u:r:su:s0 root -- /data/adb/ksud services\n"
" exec u:r:su:s0 root -- " KSUD_PATH " services\n"
"\n"
"on property:vold.decrypt=trigger_restart_framework\n"
" exec u:r:su:s0 root -- /data/adb/ksud services\n"
" exec u:r:su:s0 root -- " KSUD_PATH " services\n"
"\n"
"on property:sys.boot_completed=1\n"
" exec u:r:su:s0 root -- /data/adb/ksud boot-completed\n"
" exec u:r:su:s0 root -- " KSUD_PATH " boot-completed\n"
"\n"
"\n";
static void stop_vfs_read_hook();
static void stop_execve_hook();
static void stop_input_hook();
#ifdef CONFIG_KPROBES
static struct work_struct stop_vfs_read_work;
static struct work_struct stop_execve_hook_work;
static struct work_struct stop_input_hook_work;
#else
static bool vfs_read_hook = true;
static bool execveat_hook = true;
static bool input_hook = true;
#endif
void on_post_fs_data(void)
@@ -58,6 +64,8 @@ void on_post_fs_data(void)
done = true;
pr_info("ksu_load_allow_list");
ksu_load_allow_list();
// sanity check, this may influence the performance
stop_input_hook();
}
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
@@ -183,6 +191,57 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
return 0;
}
static unsigned int volumedown_pressed_count = 0;
static bool is_volumedown_enough(unsigned int count) {
return count >= 3;
}
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
int *value)
{
#ifndef CONFIG_KPROBES
if (!input_hook) {
return 0;
}
#endif
if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) {
int val = *value;
pr_info("KEY_VOLUMEDOWN val: %d\n", val);
if (val) {
// key pressed, count it
volumedown_pressed_count += 1;
if (is_volumedown_enough(volumedown_pressed_count)) {
stop_input_hook();
}
}
}
return 0;
}
bool ksu_is_safe_mode() {
static bool safe_mode = false;
if (safe_mode) {
// don't need to check again, userspace may call multiple times
return true;
}
// stop hook first!
stop_input_hook();
pr_info("volumedown_pressed_count: %d\n", volumedown_pressed_count);
if (is_volumedown_enough(volumedown_pressed_count)) {
// pressed over 3 times
pr_info("KEY_VOLUMEDOWN pressed max times, safe mode detected!\n");
safe_mode = true;
return true;
}
return false;
}
#ifdef CONFIG_KPROBES
// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864
@@ -208,14 +267,21 @@ static int read_handler_pre(struct kprobe *p, struct pt_regs *regs)
return ksu_handle_vfs_read(file_ptr, buf_ptr, count_ptr, pos_ptr);
}
static int input_handle_event_handler_pre(struct kprobe *p,
struct pt_regs *regs)
{
unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs);
unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs);
int *value = (int *)&PT_REGS_PARM4(regs);
return ksu_handle_input_handle_event(type, code, value);
}
static struct kprobe execve_kp = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
.symbol_name = "do_execveat_common",
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) && \
LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
.symbol_name = "__do_execve_file",
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) && \
LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
.symbol_name = "do_execveat_common",
#endif
.pre_handler = execve_handler_pre,
@@ -226,6 +292,11 @@ static struct kprobe vfs_read_kp = {
.pre_handler = read_handler_pre,
};
static struct kprobe input_handle_event_kp = {
.symbol_name = "input_handle_event",
.pre_handler = input_handle_event_handler_pre,
};
static void do_stop_vfs_read_hook(struct work_struct *work)
{
unregister_kprobe(&vfs_read_kp);
@@ -235,6 +306,11 @@ static void do_stop_execve_hook(struct work_struct *work)
{
unregister_kprobe(&execve_kp);
}
static void do_stop_input_hook(struct work_struct *work)
{
unregister_kprobe(&input_handle_event_kp);
}
#endif
static void stop_vfs_read_hook()
@@ -257,6 +333,21 @@ static void stop_execve_hook()
#endif
}
static void stop_input_hook()
{
static bool input_hook_stopped = false;
if (input_hook_stopped) {
return;
}
input_hook_stopped = true;
#ifdef CONFIG_KPROBES
bool ret = schedule_work(&stop_input_hook_work);
pr_info("unregister input kprobe: %d!\n", ret);
#else
input_hook = false;
#endif
}
// ksud: module support
void ksu_enable_ksud()
{
@@ -269,7 +360,11 @@ void ksu_enable_ksud()
ret = register_kprobe(&vfs_read_kp);
pr_info("ksud: vfs_read_kp: %d\n", ret);
ret = register_kprobe(&input_handle_event_kp);
pr_info("ksud: input_handle_event_kp: %d\n", ret);
INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook);
INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook);
INIT_WORK(&stop_input_hook_work, do_stop_input_hook);
#endif
}

View File

@@ -1,6 +1,10 @@
#ifndef __KSU_H_KSUD
#define __KSU_H_KSUD
#define KSUD_PATH "/data/adb/ksud"
void on_post_fs_data(void);
bool ksu_is_safe_mode(void);
#endif

View File

@@ -6,6 +6,8 @@
#include "selinux.h"
#include "sepolicy.h"
#include "ss/services.h"
#include "linux/lsm_audit.h"
#include "xfrm.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
#define SELINUX_POLICY_INSTEAD_SELINUX_SS
@@ -19,12 +21,17 @@
static struct policydb *get_policydb(void)
{
struct policydb *db;
// selinux_state does not exists before 4.19
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 212)
#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS
struct selinux_policy *policy = rcu_dereference(selinux_state.policy);
db = &policy->policydb;
#else
struct selinux_ss *ss = rcu_dereference(selinux_state.ss);
db = &ss->policydb;
#endif
#else
db = &policydb;
#endif
return db;
}
@@ -59,7 +66,7 @@ void apply_kernelsu_rules()
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
}
// we need to save allowlist in /data/adb
// we need to save allowlist in /data/adb/ksu
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
// we may need to do mount on shell
@@ -69,12 +76,14 @@ void apply_kernelsu_rules()
// Android 10+:
// http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512
ksu_allow(db, "kernel", "packages_list_file", "file", ALL);
// Kernel 4.4
ksu_allow(db, "kernel", "packages_list_file", "dir", ALL);
// Android 9-:
// http://aospxref.com/android-9.0.0_r61/xref/system/sepolicy/private/file_contexts#360
ksu_allow(db, "kernel", "system_data_file", "file", ALL);
ksu_allow(db, "kernel", "system_data_file", "dir", ALL);
// our ksud triggered by init
ksu_allow(db, "init", "adb_data_file", "file", "execute");
ksu_allow(db, "init", "adb_data_file", "file", ALL);
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
// copied from Magisk rules
@@ -159,6 +168,21 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz,
return 0;
}
// reset avc cache table, otherwise the new rules will not take effect if already denied
static void reset_avc_cache() {
#if ((KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 163))) || (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 212))
avc_ss_reset(0);
selnl_notify_policyload(0);
selinux_status_update_policyload(0);
#else
struct selinux_avc *avc = selinux_state.avc;
avc_ss_reset(avc, 0);
selnl_notify_policyload(0);
selinux_status_update_policyload(&selinux_state, 0);
#endif
selinux_xfrm_notify_policyload();
}
int handle_sepolicy(unsigned long arg3, void __user *arg4)
{
if (!arg4) {
@@ -167,7 +191,7 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
if (!getenforce()) {
pr_info("SELinux permissive or disabled, don't apply policies.");
return -1;
return 0;
}
struct sepol_data data;
@@ -427,11 +451,15 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
}
ret = 0;
} else {
pr_err("sepol: unknown cmd: %d");
pr_err("sepol: unknown cmd: %d\n", cmd);
}
exit:
rcu_read_unlock();
// only allow and xallow needs to reset avc cache, but we cannot do that because
// we are in atomic context. so we just reset it every time.
reset_avc_cache();
return ret;
}
}

View File

@@ -1,7 +1,10 @@
#include "selinux.h"
#include "objsec.h"
#include "linux/version.h"
#include "../klog.h" // IWYU pragma: keep
#if ((KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 163))) || (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 212))
#include "avc.h"
#endif
#define KERNEL_SU_DOMAIN "u:r:su:s0"
@@ -54,26 +57,50 @@ if (!is_domain_permissive) {
void setenforce(bool enforce)
{
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 163)) || ((KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 212)))
selinux_state.enforcing = enforce;
#else
selinux_enforcing = enforce;
#endif
#endif
}
bool getenforce()
{
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 163)) || ((KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 212)))
if (selinux_state.disabled) {
#else
if (selinux_disabled) {
#endif
return false;
}
#endif
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 163)) || ((KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 212)))
return selinux_state.enforcing;
#else
return false;
return selinux_enforcing;
#endif
#else
return true;
#endif
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 212)
/*
* get the subjective security ID of the current task
*/
static inline u32 current_sid(void)
{
const struct task_security_struct *tsec = current_security();
return tsec->sid;
}
#endif
bool is_ksu_domain()
{
return ksu_sid && current_sid() == ksu_sid;
}
}

View File

@@ -7,8 +7,8 @@
#include "../klog.h" // IWYU pragma: keep
#include "ss/symtab.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
// TODO: backport to lower kernel
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) || LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)
// 5.4 is not tested
#define KSU_SUPPORT_ADD_TYPE
#endif
@@ -82,6 +82,7 @@ static bool add_typeattribute(struct policydb *db, const char *type,
// https://elixir.bootlin.com/linux/v5.9-rc1/source/security/selinux/ss/symtab.h
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
#define symtab_search(s, name) hashtab_search((s)->table, name)
#define symtab_insert(s, name, datum) hashtab_insert((s)->table, name, datum)
#endif
#define avtab_for_each(avtab, cur) \
@@ -128,14 +129,13 @@ static struct avtab_node *get_avtab_node(struct policydb *db,
/* this is used to get the node - insertion is actually unique */
node = avtab_insert_nonunique(&db->te_avtab, key, &avdatum);
int grow_size = sizeof(u16) * 4;
int grow_size = sizeof(struct avtab_key);
grow_size += sizeof(struct avtab_datum);
if (key->specified & AVTAB_XPERMS) {
grow_size += sizeof(u8);
grow_size += sizeof(u8);
grow_size += sizeof(u32) *
ARRAY_SIZE(avdatum.u.xperms->perms.p);
} else {
grow_size += sizeof(u32) * 1;
}
db->len += grow_size;
}
@@ -456,7 +456,9 @@ static bool add_type_rule(struct policydb *db, const char *s, const char *t,
return true;
}
#ifdef KSU_SUPPORT_ADD_TYPE
// 5.9.0 : static inline int hashtab_insert(struct hashtab *h, void *key, void *datum, struct hashtab_key_params key_params)
// 5.8.0: int hashtab_insert(struct hashtab *h, void *k, void *d);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
static u32 filenametr_hash(const void *k)
{
const struct filename_trans_key *ft = k;
@@ -500,7 +502,6 @@ static bool add_filename_trans(struct policydb *db, const char *s,
const char *t, const char *c, const char *d,
const char *o)
{
#ifdef KSU_SUPPORT_ADD_TYPE
struct type_datum *src, *tgt, *def;
struct class_datum *cls;
@@ -525,14 +526,21 @@ static bool add_filename_trans(struct policydb *db, const char *s,
return false;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
struct filename_trans_key key;
key.ttype = tgt->value;
key.tclass = cls->value;
key.name = (char *)o;
struct filename_trans_datum *last = NULL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
struct filename_trans_datum *trans =
policydb_filenametr_search(db, &key);
#else
struct filename_trans_datum *trans =
hashtab_search(&db->filename_trans, &key);
#endif
while (trans) {
if (ebitmap_get_bit(&trans->stypes, src->value - 1)) {
// Duplicate, overwrite existing data and return
@@ -559,8 +567,35 @@ static bool add_filename_trans(struct policydb *db, const char *s,
db->compat_filename_trans_count++;
return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0;
#else
return false;
#else // < 5.7.0, has no filename_trans_key, but struct filename_trans
struct filename_trans key;
key.ttype = tgt->value;
key.tclass = cls->value;
key.name = (char *)o;
struct filename_trans_datum *trans =
hashtab_search(db->filename_trans, &key);
if (trans == NULL) {
trans = (struct filename_trans_datum*) kcalloc(sizeof(*trans), 1, GFP_ATOMIC);
if (!trans) {
pr_err("add_filename_trans: Failed to alloc datum");
return false;
}
struct filename_trans *new_key =
(struct filename_trans*) kmalloc(sizeof(*new_key), GFP_ATOMIC);
if (!new_key) {
pr_err("add_filename_trans: Failed to alloc new_key");
return false;
}
*new_key = key;
new_key->name = kstrdup(key.name, GFP_ATOMIC);
trans->otype = def->value;
hashtab_insert(db->filename_trans, new_key, trans);
}
return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) == 0;
#endif
}
@@ -580,7 +615,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
}
u32 value = ++db->p_types.nprim;
type = (struct type_datum *)kmalloc(sizeof(struct type_datum),
type = (struct type_datum *)kzalloc(sizeof(struct type_datum),
GFP_ATOMIC);
if (!type) {
pr_err("add_type: alloc type_datum failed.\n");
@@ -589,6 +624,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
type->primary = 1;
type->value = value;
type->attribute = attr;
char *key = kstrdup(type_name, GFP_ATOMIC);
if (!key) {
@@ -601,6 +637,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
return false;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
size_t new_size = sizeof(struct ebitmap) * db->p_types.nprim;
struct ebitmap *new_type_attr_map_array =
(krealloc(db->type_attr_map_array, new_size, GFP_ATOMIC));
@@ -646,6 +683,112 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
}
return true;
#else
// flex_array is not extensible, we need to create a new bigger one instead
struct flex_array *new_type_attr_map_array = flex_array_alloc(sizeof(struct ebitmap),
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO);
struct flex_array *new_type_val_to_struct = flex_array_alloc(sizeof(struct type_datum *),
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO);
struct flex_array *new_val_to_name_types = flex_array_alloc(sizeof(char *),
db->symtab[SYM_TYPES].nprim, GFP_ATOMIC | __GFP_ZERO);
if (!new_type_attr_map_array) {
pr_err("add_type: alloc type_attr_map_array failed\n");
return false;
}
if (!new_type_val_to_struct) {
pr_err("add_type: alloc type_val_to_struct failed\n");
return false;
}
if (!new_val_to_name_types) {
pr_err("add_type: alloc val_to_name failed\n");
return false;
}
// preallocate so we don't have to worry about the put ever failing
if (flex_array_prealloc(new_type_attr_map_array, 0,
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO)) {
pr_err("add_type: prealloc type_attr_map_array failed\n");
return false;
}
if (flex_array_prealloc(new_type_val_to_struct, 0,
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO)) {
pr_err("add_type: prealloc type_val_to_struct_array failed\n");
return false;
}
if (flex_array_prealloc(new_val_to_name_types, 0,
db->symtab[SYM_TYPES].nprim, GFP_ATOMIC | __GFP_ZERO)) {
pr_err("add_type: prealloc val_to_name_types failed\n");
return false;
}
int j;
void *old_elem;
// copy the old data or pointers to new flex arrays
for (j = 0; j < db->type_attr_map_array->total_nr_elements; j++) {
old_elem = flex_array_get(db->type_attr_map_array, j);
if (old_elem)
flex_array_put(new_type_attr_map_array, j,
old_elem, GFP_ATOMIC | __GFP_ZERO);
}
for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) {
old_elem = flex_array_get_ptr(db->type_val_to_struct_array, j);
if (old_elem)
flex_array_put_ptr(new_type_val_to_struct, j,
old_elem, GFP_ATOMIC | __GFP_ZERO);
}
for (j = 0; j < db->symtab[SYM_TYPES].nprim; j++) {
old_elem = flex_array_get_ptr(db->sym_val_to_name[SYM_TYPES], j);
if (old_elem)
flex_array_put_ptr(new_val_to_name_types, j,
old_elem, GFP_ATOMIC | __GFP_ZERO);
}
// store the pointer of old flex arrays first, when assigning new ones we should free it
struct flex_array *old_fa;
old_fa = db->type_attr_map_array;
db->type_attr_map_array = new_type_attr_map_array;
if (old_fa) {
flex_array_free(old_fa);
}
ebitmap_init(flex_array_get(db->type_attr_map_array, value - 1));
ebitmap_set_bit(flex_array_get(db->type_attr_map_array, value - 1),
value - 1, 1);
old_fa = db->type_val_to_struct_array;
db->type_val_to_struct_array = new_type_val_to_struct;
if (old_fa) {
flex_array_free(old_fa);
}
flex_array_put_ptr(db->type_val_to_struct_array, value - 1,
type, GFP_ATOMIC | __GFP_ZERO);
old_fa = db->sym_val_to_name[SYM_TYPES];
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
if (old_fa) {
flex_array_free(old_fa);
}
flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1,
key, GFP_ATOMIC | __GFP_ZERO);
int i;
for (i = 0; i < db->p_roles.nprim; ++i) {
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
0);
}
return true;
#endif
#else
return false;
#endif

View File

@@ -1,6 +1,5 @@
#! /bin/bash
set -x
#!/bin/sh
set -eux
GKI_ROOT=$(pwd)
@@ -11,8 +10,8 @@ if test -d "$GKI_ROOT/common/drivers"; then
elif test -d "$GKI_ROOT/drivers"; then
DRIVER_DIR="$GKI_ROOT/drivers"
else
echo "[ERROR] "drivers/" directory is not found."
echo "[+] You should modify this scrpit by yourself."
echo '[ERROR] "drivers/" directory is not found.'
echo '[+] You should modify this scrpit by yourself.'
exit 127
fi
@@ -26,9 +25,9 @@ echo "[+] Copy kernel su driver to $DRIVER_DIR"
test -e "$DRIVER_DIR/kernelsu" || ln -sf "$GKI_ROOT/KernelSU/kernel" "$DRIVER_DIR/kernelsu"
echo "[+] Add kernel su driver to Makefile"
echo '[+] Add kernel su driver to Makefile'
DRIVER_MAKEFILE=$DRIVER_DIR/Makefile
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
grep -q "kernelsu" "$DRIVER_MAKEFILE" || echo "obj-y += kernelsu/" >>"$DRIVER_MAKEFILE"
echo "[+] Done."
echo '[+] Done.'

View File

@@ -134,9 +134,15 @@ static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
int *dfd = (int *)PT_REGS_PARM1(regs);
int *dfd = (int *)&PT_REGS_PARM1(regs);
const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
// static int vfs_statx(int dfd, const char __user *filename, int flags, struct kstat *stat, u32 request_mask)
int *flags = (int *)&PT_REGS_PARM3(regs);
#else
// int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,int flag)
int *flags = (int *)&PT_REGS_PARM4(regs);
#endif
return ksu_handle_stat(dfd, filename_user, flags);
}
@@ -165,18 +171,20 @@ static struct kprobe faccessat_kp = {
};
static struct kprobe newfstatat_kp = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
.symbol_name = "vfs_statx",
#else
.symbol_name = "vfs_fstatat",
#endif
.pre_handler = newfstatat_handler_pre,
};
static struct kprobe execve_kp = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
.symbol_name = "do_execveat_common",
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) && \
LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
.symbol_name = "__do_execve_file",
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) && \
LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
.symbol_name = "do_execveat_common",
#endif
.pre_handler = execve_handler_pre,

View File

@@ -12,6 +12,7 @@
#include "ksu.h"
#include "manager.h"
#include "uid_observer.h"
#include "kernel_compat.h"
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list"
static struct work_struct ksu_update_uid_work;
@@ -38,6 +39,7 @@ static bool is_uid_exist(uid_t uid, void *data)
static void do_update_uid(struct work_struct *work)
{
KWORKER_INSTALL_KEYRING();
struct file *fp = filp_open(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
if (IS_ERR(fp)) {
pr_err("do_update_uid, open " SYSTEM_PACKAGES_LIST_PATH
@@ -54,13 +56,13 @@ static void do_update_uid(struct work_struct *work)
loff_t line_start = 0;
char buf[128];
for (;;) {
ssize_t count = kernel_read(fp, &chr, sizeof(chr), &pos);
ssize_t count = ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
if (count != sizeof(chr))
break;
if (chr != '\n')
continue;
count = kernel_read(fp, buf, sizeof(buf), &line_start);
count = ksu_kernel_read_compat(fp, buf, sizeof(buf), &line_start);
struct uid_data *data =
kmalloc(sizeof(struct uid_data), GFP_ATOMIC);

View File

@@ -32,6 +32,16 @@
android:name="android.app.lib_name"
android:value="" />
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
</manifest>

View File

@@ -61,3 +61,9 @@ Java_me_weishu_kernelsu_Natives_allowRoot(JNIEnv *env, jclass clazz, jint uid, j
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_me_weishu_kernelsu_Natives_isSafeMode(JNIEnv *env, jclass clazz) {
return is_safe_mode();
}

View File

@@ -19,6 +19,7 @@
#define CMD_DENY_SU 4
#define CMD_GET_ALLOW_LIST 5
#define CMD_GET_DENY_LIST 6
#define CMD_CHECK_SAFEMODE 9
static bool ksuctl(int cmd, void* arg1, void* arg2) {
int32_t result = 0;
@@ -51,4 +52,8 @@ bool get_allow_list(int *uids, int *size) {
bool get_deny_list(int *uids, int *size) {
return ksuctl(CMD_GET_DENY_LIST, uids, size);
}
bool is_safe_mode() {
return ksuctl(CMD_CHECK_SAFEMODE, nullptr, nullptr);
}

View File

@@ -15,4 +15,6 @@ bool get_allow_list(int *uids, int *size);
bool get_deny_list(int *uids, int *size);
bool is_safe_mode();
#endif //KERNELSU_KSU_H

View File

@@ -21,4 +21,6 @@ public final class Natives {
public static native int[] getDenyList();
public static native boolean allowRoot(int uid, boolean allow);
public static native boolean isSafeMode();
}

View File

@@ -6,8 +6,9 @@ import androidx.activity.compose.setContent
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@@ -52,31 +53,35 @@ class MainActivity : ComponentActivity() {
@Composable
private fun BottomBar(navController: NavHostController) {
val currentDestination: Destination = navController.appCurrentDestinationAsState().value
val topDestination: Destination = navController.appCurrentDestinationAsState().value
?: NavGraphs.root.startAppDestination
var topDestination by rememberSaveable { mutableStateOf(currentDestination.route) }
LaunchedEffect(currentDestination) {
val queue = navController.backQueue
if (queue.size == 2) topDestination = queue[1].destination.route!!
else if (queue.size > 2) topDestination = queue[2].destination.route!!
val bottomBarRoutes = remember {
BottomBarDestination.values().map { it.direction.route }
}
NavigationBar(tonalElevation = 8.dp) {
BottomBarDestination.values().forEach { destination ->
NavigationBarItem(
selected = topDestination == destination.direction.route,
selected = topDestination.route == destination.direction.route,
onClick = {
val firstRoute = navController.backQueue.reversed().first {
it.destination.route in bottomBarRoutes
}.destination.route
navController.navigate(destination.direction.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
saveState = firstRoute != destination.direction.route
}
launchSingleTop = true
restoreState = true
}
},
icon = {
if (topDestination == destination.direction.route) Icon(destination.iconSelected, stringResource(destination.label))
else Icon(destination.iconNotSelected, stringResource(destination.label))
if (topDestination.route == destination.direction.route) {
Icon(destination.iconSelected, stringResource(destination.label))
} else {
Icon(destination.iconNotSelected, stringResource(destination.label))
}
},
label = { Text(stringResource(destination.label)) },
alwaysShowLabel = false

View File

@@ -0,0 +1,167 @@
package me.weishu.kernelsu.ui.component
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.*
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import me.weishu.kernelsu.ui.util.LocalDialogHost
import kotlin.coroutines.resume
sealed interface DialogResult {
object Confirmed : DialogResult
object Dismissed : DialogResult
}
interface DialogVisuals {
val title: String
val content: String
val confirm: String?
val dismiss: String?
}
interface DialogData {
val visuals: DialogVisuals
fun confirm()
fun dismiss()
}
class DialogHostState {
private data class DialogVisualsImpl(
override val title: String,
override val content: String,
override val confirm: String?,
override val dismiss: String?
) : DialogVisuals
private data class DialogDataImpl(
override val visuals: DialogVisuals,
val continuation: CancellableContinuation<DialogResult>
) : DialogData {
override fun confirm() {
if (continuation.isActive) continuation.resume(DialogResult.Confirmed)
}
override fun dismiss() {
if (continuation.isActive) continuation.resume(DialogResult.Dismissed)
}
}
private val mutex = Mutex()
var currentDialogData by mutableStateOf<DialogData?>(null)
private set
suspend fun showDialog(
title: String,
content: String,
confirm: String? = null,
dismiss: String? = null
): DialogResult = mutex.withLock {
try {
return@withLock suspendCancellableCoroutine { continuation ->
currentDialogData = DialogDataImpl(
visuals = DialogVisualsImpl(title, content, confirm, dismiss),
continuation = continuation
)
}
} finally {
currentDialogData = null
}
}
}
@Composable
fun rememberDialogHostState(): DialogHostState {
return remember {
DialogHostState()
}
}
@Composable
fun BaseDialog(
state: DialogHostState = LocalDialogHost.current,
title: @Composable (String) -> Unit,
confirmButton: @Composable (String?, () -> Unit) -> Unit,
dismissButton: @Composable (String?, () -> Unit) -> Unit,
content: @Composable (String) -> Unit = { Text(text = it) },
) {
val currentDialogData = state.currentDialogData ?: return
val visuals = currentDialogData.visuals
AlertDialog(
onDismissRequest = {
currentDialogData.dismiss()
},
title = {
title(visuals.title)
},
text = {
content(visuals.content)
},
confirmButton = {
confirmButton(visuals.confirm, currentDialogData::confirm)
},
dismissButton = {
dismissButton(visuals.dismiss, currentDialogData::dismiss)
}
)
}
@Composable
fun SimpleDialog(
state: DialogHostState = LocalDialogHost.current,
content: @Composable (String) -> Unit
) {
BaseDialog(
state = state,
title = {
Text(text = it)
},
confirmButton = { text, confirm ->
text?.let {
TextButton(onClick = confirm) {
Text(text = it)
}
}
},
dismissButton = { text, dismiss ->
text?.let {
TextButton(onClick = dismiss) {
Text(text = it)
}
}
},
content = content
)
}
@Composable
fun ConfirmDialog(state: DialogHostState = LocalDialogHost.current) {
BaseDialog(
state = state,
title = {
Text(text = it)
},
confirmButton = { text, confirm ->
text?.let {
TextButton(onClick = confirm) {
Text(text = it)
}
}
},
dismissButton = { text, dismiss ->
text?.let {
TextButton(onClick = dismiss) {
Text(text = it)
}
}
},
)
}

View File

@@ -10,8 +10,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.*
@@ -22,9 +21,11 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import me.weishu.kernelsu.R
private const val TAG = "SearchBar"
@@ -36,7 +37,8 @@ fun SearchAppBar(
onSearchTextChange: (String) -> Unit,
onClearClick: () -> Unit,
onBackClick: (() -> Unit)? = null,
onConfirm: (() -> Unit)? = null
onConfirm: (() -> Unit)? = null,
dropdownContent: @Composable (() -> Unit)? = null,
) {
val keyboardController = LocalSoftwareKeyboardController.current
val focusRequester = remember { FocusRequester() }
@@ -116,6 +118,11 @@ fun SearchAppBar(
content = { Icon(Icons.Filled.Search, null) }
)
}
if (dropdownContent != null) {
dropdownContent()
}
}
)
}

View File

@@ -22,6 +22,7 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.tooling.preview.Preview
@@ -130,21 +131,29 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
else MaterialTheme.colorScheme.errorContainer
})
) {
val uriHandler = LocalUriHandler.current
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
// TODO: Install kernel
if (kernelVersion.isGKI() && ksuVersion == null) {
uriHandler.openUri("https://kernelsu.org/guide/installation.html")
}
}
.padding(24.dp),
verticalAlignment = Alignment.CenterVertically
) {
when {
ksuVersion != null -> {
val appendText = if (Natives.isSafeMode()) {
" [${stringResource(id = R.string.safe_mode)}]"
} else {
""
}
Icon(Icons.Outlined.CheckCircle, stringResource(R.string.home_working))
Column(Modifier.padding(start = 20.dp)) {
Text(
text = stringResource(R.string.home_working),
text = stringResource(R.string.home_working) + appendText,
style = MaterialTheme.typography.titleMedium
)
Spacer(Modifier.height(4.dp))
@@ -213,10 +222,7 @@ private fun InfoCard() {
InfoCardItem(stringResource(R.string.home_kernel), uname.release)
Spacer(Modifier.height(24.dp))
InfoCardItem(stringResource(R.string.home_arch), uname.machine)
Spacer(Modifier.height(24.dp))
InfoCardItem(stringResource(R.string.home_version), uname.version)
InfoCardItem(stringResource(R.string.home_manager_version), getManagerVersion(context))
Spacer(Modifier.height(24.dp))
InfoCardItem(stringResource(R.string.home_api), Build.VERSION.SDK_INT.toString())
@@ -247,6 +253,11 @@ private fun InfoCard() {
}
}
fun getManagerVersion(context: Context) : String {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
return "${packageInfo.versionName} (${packageInfo.versionCode})"
}
@Preview
@Composable
private fun StatusCardPreview() {

View File

@@ -30,11 +30,11 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.ConfirmDialog
import me.weishu.kernelsu.ui.component.DialogResult
import me.weishu.kernelsu.ui.component.rememberDialogHostState
import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
import me.weishu.kernelsu.ui.util.overlayFsAvailable
import me.weishu.kernelsu.ui.util.toggleModule
import me.weishu.kernelsu.ui.util.uninstallModule
import me.weishu.kernelsu.ui.util.*
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
@OptIn(ExperimentalMaterial3Api::class)
@@ -52,11 +52,14 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
}
}
Scaffold(
topBar = {
TopBar()
},
floatingActionButton = {
val isSafeMode = Natives.isSafeMode()
Scaffold(topBar = {
TopBar()
}, floatingActionButton = if (isSafeMode) {
{ /* Empty */ }
} else {
{
val moduleInstall = stringResource(id = R.string.module_install)
val selectZipLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
@@ -83,7 +86,11 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
text = { Text(text = moduleInstall) },
)
}
) { innerPadding ->
}) { innerPadding ->
val dialogState = rememberDialogHostState()
ConfirmDialog(dialogState)
val failedEnable = stringResource(R.string.module_failed_to_enable)
val failedDisable = stringResource(R.string.module_failed_to_disable)
val failedUninstall = stringResource(R.string.module_uninstall_failed)
@@ -97,11 +104,9 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
return@Scaffold
}
SwipeRefresh(
state = swipeState,
onRefresh = {
state = swipeState, onRefresh = {
scope.launch { viewModel.fetchModuleList() }
},
modifier = Modifier
}, modifier = Modifier
.padding(innerPadding)
.padding(16.dp)
.fillMaxSize()
@@ -121,42 +126,69 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
Text(stringResource(R.string.module_empty))
}
} else {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(15.dp),
contentPadding = remember { PaddingValues(bottom = 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */ ) }
) {
LazyColumn(verticalArrangement = Arrangement.spacedBy(15.dp),
contentPadding = remember { PaddingValues(bottom = 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */) }) {
items(viewModel.moduleList) { module ->
var isChecked by rememberSaveable(module) { mutableStateOf(module.enabled) }
ModuleItem(module,
isChecked,
onUninstall = {
scope.launch {
val result = uninstallModule(module.id)
if (result) {
viewModel.fetchModuleList()
}
snackBarHost.showSnackbar(
if (result) {
successUninstall.format(module.name)
} else {
failedUninstall.format(module.name)
}
)
val reboot = stringResource(id = R.string.reboot)
val rebootToApply = stringResource(id = R.string.reboot_to_apply)
val moduleStr = stringResource(id = R.string.module)
val uninstall = stringResource(id = R.string.uninstall)
val cancel = stringResource(id = android.R.string.cancel)
val moduleUninstallConfirm =
stringResource(id = R.string.module_uninstall_confirm)
ModuleItem(module, isChecked, onUninstall = {
scope.launch {
val dialogResult = dialogState.showDialog(
moduleStr,
content = moduleUninstallConfirm.format(module.name),
confirm = uninstall,
dismiss = cancel
)
if (dialogResult != DialogResult.Confirmed) {
return@launch
}
},
onCheckChanged = {
val success = toggleModule(module.id, !isChecked)
val success = uninstallModule(module.id)
if (success) {
isChecked = it
scope.launch {
viewModel.fetchModuleList()
}
} else scope.launch {
val message = if (isChecked) failedDisable else failedEnable
snackBarHost.showSnackbar(message.format(module.name))
viewModel.fetchModuleList()
}
val message = if (success) {
successUninstall.format(module.name)
} else {
failedUninstall.format(module.name)
}
val actionLabel = if (success) {
reboot
} else {
null
}
val result = snackBarHost.showSnackbar(
message, actionLabel = actionLabel
)
if (result == SnackbarResult.ActionPerformed) {
reboot()
}
}
)
}, onCheckChanged = {
val success = toggleModule(module.id, !isChecked)
if (success) {
isChecked = it
scope.launch {
viewModel.fetchModuleList()
val result = snackBarHost.showSnackbar(
rebootToApply, actionLabel = reboot
)
if (result == SnackbarResult.ActionPerformed) {
reboot()
}
}
} else scope.launch {
val message = if (isChecked) failedDisable else failedEnable
snackBarHost.showSnackbar(message.format(module.name))
}
})
// fix last item shadow incomplete in LazyColumn
Spacer(Modifier.height(1.dp))
}
@@ -169,9 +201,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopBar() {
TopAppBar(
title = { Text(stringResource(R.string.module)) }
)
TopAppBar(title = { Text(stringResource(R.string.module)) })
}
@Composable

View File

@@ -1,22 +1,30 @@
package me.weishu.kernelsu.ui.screen
import android.content.Intent
import android.net.Uri
import android.widget.Toast
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Info
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.FileProvider
import com.alorma.compose.settings.ui.*
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch
import me.weishu.kernelsu.ui.util.getBugreportFile
import me.weishu.kernelsu.BuildConfig
import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.SimpleDialog
import me.weishu.kernelsu.ui.component.rememberDialogHostState
import me.weishu.kernelsu.ui.util.LinkifyText
/**
* @author weishu
* @date 2023/1/1.
@@ -34,46 +42,57 @@ fun SettingScreen(navigator: DestinationsNavigator) {
}
) { paddingValues ->
var openDialog by remember { mutableStateOf(false) }
val dialogState = rememberDialogHostState()
if (openDialog) {
AlertDialog(
onDismissRequest = {
openDialog = false
},
title = {
Text(text = stringResource(id = R.string.about))
},
text = {
SupportCard()
},
confirmButton = {
TextButton(
onClick = {
openDialog = false
}
) {
Text(stringResource(id = android.R.string.ok))
}
},
)
SimpleDialog(dialogState) {
SupportCard()
}
Column(modifier = Modifier.padding(paddingValues)) {
val context = LocalContext.current
SettingsSwitch(
title = {
Text(stringResource(id = R.string.settings_system_rw))
},
subtitle = {
Text(stringResource(id = R.string.settings_system_rw_summary))
},
onCheckedChange = {
Toast.makeText(context, "coming soon", Toast.LENGTH_SHORT).show()
}
)
SettingsMenuLink(title = {
Text(stringResource(id = R.string.about))
Text(stringResource(id = R.string.send_log))
},
onClick = {
openDialog = true
val bugreport = getBugreportFile(context)
val uri: Uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.fileprovider", bugreport)
val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.putExtra(Intent.EXTRA_STREAM, uri)
shareIntent.setDataAndType(uri, "application/zip")
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
context.startActivity(
Intent.createChooser(
shareIntent,
context.getString(R.string.send_log)
)
)
}
)
val about = stringResource(id = R.string.about)
val ok = stringResource(id = android.R.string.ok)
val scope = rememberCoroutineScope()
SettingsMenuLink(title = {
Text(about)
},
onClick = {
scope.launch {
dialogState.showDialog(about, content = "unused", confirm = ok)
}
}
)
}

View File

@@ -1,14 +1,10 @@
package me.weishu.kernelsu.ui.screen
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
@@ -17,17 +13,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.ImageLoader
import coil.compose.AsyncImage
import coil.compose.rememberAsyncImagePainter
import coil.compose.rememberImagePainter
import coil.decode.DataSource
import coil.fetch.DrawableResult
import coil.fetch.FetchResult
import coil.fetch.Fetcher
import coil.request.CachePolicy
import coil.request.ImageRequest
import coil.request.Options
import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.ramcosta.composedestinations.annotation.Destination
@@ -37,7 +24,6 @@ import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.SearchAppBar
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
import java.util.*
@OptIn(ExperimentalMaterial3Api::class)
@@ -60,7 +46,44 @@ fun SuperUserScreen() {
title = { Text(stringResource(R.string.superuser)) },
searchText = viewModel.search,
onSearchTextChange = { viewModel.search = it },
onClearClick = { viewModel.search = "" }
onClearClick = { viewModel.search = "" },
dropdownContent = {
var showDropdown by remember { mutableStateOf(false) }
IconButton(
onClick = { showDropdown = true },
) {
Icon(
imageVector = Icons.Filled.MoreVert,
contentDescription = stringResource(id = R.string.settings)
)
DropdownMenu(expanded = showDropdown, onDismissRequest = {
showDropdown = false
}) {
DropdownMenuItem(text = {
Text(stringResource(R.string.refresh))
}, onClick = {
scope.launch {
viewModel.fetchAppList()
}
showDropdown = false
})
DropdownMenuItem(text = {
Text(
if (viewModel.showSystemApps) {
stringResource(R.string.hide_system_apps)
} else {
stringResource(R.string.show_system_apps)
}
)
}, onClick = {
viewModel.showSystemApps = !viewModel.showSystemApps
showDropdown = false
})
}
}
},
)
}
) { innerPadding ->
@@ -77,7 +100,7 @@ fun SuperUserScreen() {
.fillMaxSize()
) {
LazyColumn {
items(viewModel.appList) { app ->
items(viewModel.appList, key = { it.packageName }) { app ->
var isChecked by rememberSaveable(app) { mutableStateOf(app.onAllowList) }
AppItem(app, isChecked) { checked ->
val success = Natives.allowRoot(app.uid, checked)

View File

@@ -2,7 +2,12 @@ package me.weishu.kernelsu.ui.util
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.compositionLocalOf
import me.weishu.kernelsu.ui.component.DialogHostState
val LocalSnackbarHost = compositionLocalOf<SnackbarHostState> {
error("CompositionLocal LocalSnackbarController not present")
}
val LocalDialogHost = compositionLocalOf<DialogHostState> {
error("CompositionLocal LocalDialogController not present")
}

View File

@@ -0,0 +1,80 @@
package me.weishu.kernelsu.ui.util
import android.content.Context
import android.os.Build
import android.system.Os
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.ui.screen.getManagerVersion
import java.io.File
import java.io.FileWriter
import java.io.PrintWriter
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
fun getBugreportFile(context: Context): File {
val bugreportDir = File(context.cacheDir, "bugreport")
bugreportDir.mkdirs()
val dmesgFile = File(bugreportDir, "dmesg.txt")
val logcatFile = File(bugreportDir, "logcat.txt")
val tombstonesFile = File(bugreportDir, "tombstones.tar.gz")
val dropboxFile = File(bugreportDir, "dropbox.tar.gz")
val mountsFile = File(bugreportDir, "mounts.txt")
val fileSystemsFile = File(bugreportDir, "filesystems.txt")
val ksuFileTree = File(bugreportDir, "ksu_tree.txt")
val appListFile = File(bugreportDir, "app_list.txt")
val shell = createRootShell()
shell.newJob().add("dmesg > ${dmesgFile.absolutePath}").exec()
shell.newJob().add("logcat -d > ${logcatFile.absolutePath}").exec()
shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} /data/tombstones").exec()
shell.newJob().add("tar -czf ${dropboxFile.absolutePath} /data/system/dropbox").exec()
shell.newJob().add("cat /proc/mounts > ${mountsFile.absolutePath}").exec()
shell.newJob().add("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec()
shell.newJob().add("ls -alRZ /data/adb/ksu > ${ksuFileTree.absolutePath}").exec()
shell.newJob().add("cat /data/system/packages.list > ${appListFile.absolutePath}").exec()
// basic information
val buildInfo = File(bugreportDir, "basic.txt")
PrintWriter(FileWriter(buildInfo)).use { pw ->
pw.println("Kernel: ${System.getProperty("os.version")}")
pw.println("BRAND: " + Build.BRAND)
pw.println("MODEL: " + Build.MODEL)
pw.println("PRODUCT: " + Build.PRODUCT)
pw.println("MANUFACTURER: " + Build.MANUFACTURER)
pw.println("SDK: " + Build.VERSION.SDK_INT)
pw.println("PREVIEW_SDK: " + Build.VERSION.PREVIEW_SDK_INT)
pw.println("FINGERPRINT: " + Build.FINGERPRINT)
pw.println("DEVICE: " + Build.DEVICE)
pw.println("Manager: " + getManagerVersion(context))
val uname = Os.uname()
pw.println("KernelRelease: ${uname.release}")
pw.println("KernelVersion: ${uname.version}")
pw.println("Mahcine: ${uname.machine}")
pw.println("Nodename: ${uname.nodename}")
pw.println("Sysname: ${uname.sysname}")
val ksuKernel = Natives.getVersion()
pw.println("KernelSU: $ksuKernel")
val safeMode = Natives.isSafeMode()
pw.println("SafeMode: $safeMode")
}
// modules
val modulesFile = File(bugreportDir, "modules.json")
modulesFile.writeText(listModules())
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm")
val current = LocalDateTime.now().format(formatter)
val targetFile = File(context.cacheDir, "KernelSU_bugreport_${current}.tar.gz")
shell.newJob().add("tar czf ${targetFile.absolutePath} -C ${bugreportDir.absolutePath} .").exec()
shell.newJob().add("rm -rf ${bugreportDir.absolutePath}").exec()
shell.newJob().add("chmod 0644 ${targetFile.absolutePath}").exec()
return targetFile
}

View File

@@ -1,6 +1,5 @@
package me.weishu.kernelsu.ui.viewmodel
import android.content.Context
import android.os.SystemClock
import android.util.Log
import androidx.compose.runtime.derivedStateOf
@@ -74,6 +73,7 @@ class ModuleViewModel : ViewModel() {
}.toList()
}.onFailure { e ->
Log.e(TAG, "fetchModuleList: ", e)
isRefreshing = false
}

View File

@@ -35,6 +35,7 @@ class SuperUserViewModel : ViewModel() {
)
var search by mutableStateOf("")
var showSystemApps by mutableStateOf(false)
var isRefreshing by mutableStateOf(false)
private set
@@ -55,6 +56,9 @@ class SuperUserViewModel : ViewModel() {
sortedList.filter {
it.label.contains(search) || it.packageName.contains(search) || HanziToPinyin.getInstance()
.toPinyinString(it.label).contains(search)
}.filter {
it.uid == 2000 // Always show shell
|| showSystemApps || it.icon.applicationInfo.flags.and(ApplicationInfo.FLAG_SYSTEM) == 0
}
}

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="home">Beranda</string>
<string name="home_not_installed">Tidak terinstall</string>
<string name="home_click_to_install">Klik untuk menginstall</string>
<string name="home_working">Bekerja</string>
<string name="home_working_version">Versi: %d</string>
<string name="home_unsupported">Tidak didukung</string>
<string name="home_unsupported_reason">Saat ini kernelSu hanya mendukung GKI kernel</string>
<string name="home_copied_to_clipboard">Salin ke clipboard</string>
<string name="home_support">Dukungan</string>
<string name="home_kernel">Kernel</string>
<string name="home_arch">Arch</string>
<string name="home_manager_version">Versi manager</string>
<string name="home_api">API Level</string>
<string name="home_abi">ABI</string>
<string name="home_fingerprint">Fingerprint</string>
<string name="home_securitypatch">Patch keamanan</string>
<string name="home_selinux_status">Status SElinux</string>
<string name="selinux_status_disabled">Cacat</string>
<string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">Tidak tersedia</string>
<string name="superuser">Superuser</string>
<string name="superuser_failed_to_grant_root">Gagal mengizinkan root untuk %d</string>
<string name="module_failed_to_enable">Gagal mengaktifkan module: %s</string>
<string name="module_failed_to_disable">Gagal menonaktifkan module: %s</string>
<string name="module_empty">Tidak ada module terpasang</string>
<string name="module">Module</string>
<string name="uninstall">Hapus</string>
<string name="module_install">Pasang</string>
<string name="install">Pasang</string>
<string name="reboot">Reboot perangkat</string>
<string name="settings">Pengaturan</string>
<string name="reboot_userspace">Soft Reboot</string>
<string name="reboot_recovery">Reboot ke Recovery</string>
<string name="reboot_bootloader">Reboot ke Bootloader</string>
<string name="reboot_download">Reboot ke Download</string>
<string name="reboot_edl">Reboot ke EDL</string>
<string name="settings_system_rw">Buat system agar dapat ditulis sebagai OverlayFs</string>
<string name="settings_system_rw_summary">restart perangkat setelah mengaktifkan fitur ini</string>
<string name="about">Tentang</string>
<string name="require_kernel_version_8">Membutuhkan KernelSU Versi 8+</string>
<string name="module_uninstall_confirm">Apakah anda yakin ingin menghapus module ini %s?</string>
<string name="module_uninstall_success">%s terhapus</string>
<string name="module_uninstall_failed">Gagal untuk menghapus: %s</string>
<string name="module_version">Versi</string>
<string name="module_author">Pembuat</string>
<string name="module_overlay_fs_not_available">overlayfs tidak tersedia, module tidak bekerja!</string>
<string name="refresh">Segarkan</string>
<string name="show_system_apps">Tampilkan system apps</string>
<string name="hide_system_apps">Sembunyikan system apps</string>
<string name="send_log">Kirim logs</string>
<string name="safe_mode">Mode aman</string>
<string name="reboot_to_apply">Restart untuk menerapkan</string>
</resources>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">KernelSU</string>
<string name="home">ホーム</string>
<string name="home_not_installed">未インストール</string>
<string name="home_click_to_install">タップでインストール</string>
<string name="home_working">動作中</string>
<string name="home_working_version">バージョン: %d</string>
<string name="home_unsupported">非対応</string>
<string name="home_unsupported_reason">KernelSUは現在、GKIカーネルのみサポートをしています</string>
<string name="home_copied_to_clipboard">クリップボードにコピーしました</string>
<string name="home_support">対応</string>
<string name="home_kernel">カーネル</string>
<string name="home_arch">アーキテクチャ</string>
<string name="home_manager_version">バージョン</string>
<string name="home_api">APIレベル</string>
<string name="home_abi">ABI</string>
<string name="home_fingerprint">Fingerprint</string>
<string name="home_securitypatch">セキュリティパッチ</string>
<string name="home_selinux_status">SELinuxの状態</string>
<string name="selinux_status_disabled">無効</string>
<string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">不明</string>
<string name="superuser">スーパーユーザー</string>
<string name="superuser_failed_to_grant_root">%dの権限の付与に失敗しました</string>
<string name="module_failed_to_enable">モジュールの有効化に失敗: %s</string>
<string name="module_failed_to_disable">モジュールの無効化に失敗: %s</string>
<string name="module_empty">モジュールはインストールされていません</string>
<string name="module">モジュール</string>
<string name="uninstall">アンインストール</string>
<string name="module_install">インストール</string>
<string name="install">インストール</string>
<string name="reboot">再起動</string>
<string name="settings">設定</string>
<string name="reboot_userspace">ソフトリブート</string>
<string name="reboot_recovery">リカバリーで再起動</string>
<string name="reboot_bootloader">Bootloaderで再起動</string>
<string name="reboot_download">ダウンロードモードで再起動</string>
<string name="reboot_edl">EDLで再起動</string>
<string name="settings_system_rw">システムを書き込み可能にする</string>
<string name="settings_system_rw_summary">OverlayFSを使用してシステムを書き込み可能にします。\nこの機能は再起動後に有効になります。</string>
<string name="about">アプリについて</string>
<string name="require_kernel_version_8">KernelSU バージョン8以降が必要です</string>
<string name="module_uninstall_success">%sをアンインストールしました</string>
<string name="module_uninstall_failed">アンインストールに失敗: %s</string>
<string name="module_version">バージョン</string>
<string name="module_author">作者</string>
<string name="module_overlay_fs_not_available">OverlayFSが有効でないためモジュールは動作しません</string>
<string name="refresh">更新</string>
<string name="show_system_apps">システムアプリを表示</string>
<string name="hide_system_apps">システムアプリを非表示</string>
<string name="send_log">ログを送信</string>
<string name="safe_mode">セーフモード</string>
<string name="reboot_to_apply">再起動をして有効化する</string>
</resources>

View File

@@ -0,0 +1,58 @@
<resources>
<string name="home">Главная</string>
<string name="home_not_installed">Не установлен</string>
<string name="home_click_to_install">Нажмите чтобы установить</string>
<string name="home_working">Работает</string>
<string name="home_working_version">Версия: %d</string>
<string name="home_unsupported">Не поддерживается</string>
<string name="home_unsupported_reason">KernelSU поддерживает только GKI ядра</string>
<string name="home_copied_to_clipboard">Скопировано в буфер обмена</string>
<string name="home_support">Поддержка</string>
<string name="home_kernel">Ядро</string>
<string name="home_arch">Архитектура</string>
<string name="home_manager_version">Версия</string>
<string name="home_api">Уровень API</string>
<string name="home_abi">ABI</string>
<string name="home_fingerprint">Подпись</string>
<string name="home_securitypatch">Патч Безопасности</string>
<string name="home_selinux_status">Состояние SELinux</string>
<string name="selinux_status_disabled">Выключен</string>
<string name="selinux_status_enforcing">Принудительный</string>
<string name="selinux_status_permissive">Разрешающий</string>
<string name="selinux_status_unknown">Неизвестно</string>
<string name="superuser">Superuser</string>
<string name="superuser_failed_to_grant_root">Failed to grant root for %d</string>
<string name="module_failed_to_enable">Не удалось включить модуль: %s</string>
<string name="module_failed_to_disable">Не удалось отключить модуль: %s</string>
<string name="module_empty">Нет установленных модулей</string>
<string name="module">Модули</string>
<string name="uninstall">Удалить</string>
<string name="module_install">Установить</string>
<string name="install">Установить</string>
<string name="reboot">Перезагрузка</string>
<string name="settings">Настройки</string>
<string name="reboot_userspace">Soft Reboot</string>
<string name="reboot_recovery">Reboot to Recovery</string>
<string name="reboot_bootloader">Reboot to Bootloader</string>
<string name="reboot_download">Reboot to Download</string>
<string name="reboot_edl">Reboot to EDL</string>
<string name="settings_system_rw">Сделать систему доступной для записи</string>
<string name="settings_system_rw_summary">Использовать overlayfs, чтобы сделать системный раздел доступным для записи, необходима перезагрузка.</string>
<string name="about">О KernelSU</string>
<string name="require_kernel_version_8">Требуется KernelSU версии 8 и выше</string>
<string name="module_uninstall_success">%s удален</string>
<string name="module_uninstall_failed">Не удалось удалить: %s</string>
<string name="module_version">Версия</string>
<string name="module_author">Автор</string>
<string name="module_overlay_fs_not_available">overlayfs выключен, модуль не будет работать!</string>
<string name="refresh">Обновить</string>
<string name="show_system_apps">Показать системные приложения</string>
<string name="hide_system_apps">Скрыть системные приложения</string>
<string name="send_log">Отправить лог</string>
<string name="safe_mode">Безопасный режим</string>
<string name="reboot_to_apply">Перезагрузите, чтобы вступить в силу</string>
</resources>

View File

@@ -0,0 +1,53 @@
<resources>
<string name="home">Trang chủ</string>
<string name="home_not_installed">Chưa được cài đặt</string>
<string name="home_click_to_install">Nhấn đề cài dặt</string>
<string name="home_working">Đang chạy</string>
<string name="home_working_version">Phiên bản: %d</string>
<string name="home_unsupported">Không hỗ trợ</string>
<string name="home_unsupported_reason">KernelSU hiện tại chỉ hỗ trợ kernel GKI</string>
<string name="home_copied_to_clipboard">Sao chép vào clipboard</string>
<string name="home_support">Ủng hộ</string>
<string name="home_kernel">Kernel</string>
<string name="home_arch">Kiến trúc</string>
<string name="home_manager_version">Phiên bản</string>
<string name="home_api">Cấp độ API</string>
<string name="home_abi">ABI</string>
<string name="home_fingerprint">Fingerprint</string>
<string name="home_securitypatch">Bản vá bảo mật</string>
<string name="home_selinux_status">Trạng thái SELinux</string>
<string name="selinux_status_disabled">Vô hiệu hóa</string>
<string name="selinux_status_enforcing">Thực thi</string>
<string name="selinux_status_permissive">Cho phép</string>
<string name="selinux_status_unknown">Không rõ</string>
<string name="superuser">Superuser</string>
<string name="superuser_failed_to_grant_root">Không thể cấp quyền root cho %d</string>
<string name="module_failed_to_enable">Không thể kích hoạt mô-đun: %s</string>
<string name="module_failed_to_disable">Không thể vô hiệu hóa mô-đun: %s</string>
<string name="module_empty">Chưa có mô-đun nào được cài đặt</string>
<string name="module">Mô-đun</string>
<string name="uninstall">Gỡ cài đặt</string>
<string name="module_install">Cài đặt</string>
<string name="install">Cài đặt</string>
<string name="reboot">Khởi động lại</string>
<string name="settings">Thiết lập</string>
<string name="reboot_userspace">Khởi động mềm</string>
<string name="reboot_recovery">Khởi động lại vào Recovery</string>
<string name="reboot_bootloader">Khởi động lại vào Bootloader</string>
<string name="reboot_download">Khởi động lại vào Download Mode</string>
<string name="reboot_edl">Khởi động vào EDL</string>
<string name="settings_system_rw">Cho phép ghi vào phân vùng system</string>
<string name="settings_system_rw_summary">Sử dụng overlayfs để làm cho phân vùng system có thể ghi được, khởi động lại để áp dụng</string>
<string name="about">Thông tin</string>
<string name="require_kernel_version_8">Yêu cầu KernelSU phiên bản 8+</string>
<string name="module_uninstall_success">%s đã được gỡ cài đặt</string>
<string name="module_uninstall_failed">Lỗi khi gỡ cài đặt: %s</string>
<string name="module_version">Phiên bản</string>
<string name="module_author">Tác giả</string>
<string name="module_overlay_fs_not_available">Không tìm thấy overlayfs, mô-đun sẽ không thể hoạt động!</string>
<string name="refresh">Làm mới</string>
<string name="show_system_apps">Hiển thị ứng dụng hệ thống</string>
<string name="hide_system_apps">Ẩn ứng dụng hệ thống</string>
<string name="safe_mode">Chế độ an toàn</string>
<string name="reboot_to_apply">Khởi động lại để có hiệu lực</string>
</resources>

View File

@@ -11,7 +11,7 @@
<string name="home_support">支持</string>
<string name="home_kernel">内核版本</string>
<string name="home_arch">设备架构</string>
<string name="home_version">系统版本</string>
<string name="home_manager_version">管理器版本</string>
<string name="home_api">API 版本</string>
<string name="home_abi">ABI 支持</string>
<string name="home_fingerprint">系统指纹</string>
@@ -41,9 +41,16 @@
<string name="settings_system_rw_summary">使用 overlayfs 使系统分区可写, 重启生效</string>
<string name="about">关于</string>
<string name="require_kernel_version_8">需要 KernelSU 版本 8+</string>
<string name="module_uninstall_confirm">确定要卸载模块 %s 吗?</string>
<string name="module_uninstall_success">%s 已卸载</string>
<string name="module_uninstall_failed">卸载失败: %s</string>
<string name="module_version">版本</string>
<string name="module_author">作者</string>
<string name="module_overlay_fs_not_available">内核不支持 overlayfs模块功能无法运作</string>
<string name="refresh">刷新</string>
<string name="show_system_apps">显示系统应用</string>
<string name="hide_system_apps">隐藏系统应用</string>
<string name="send_log">发送日志</string>
<string name="safe_mode">安全模式</string>
<string name="reboot_to_apply">重启生效</string>
</resources>

View File

@@ -11,7 +11,7 @@
<string name="home_support">支持</string>
<string name="home_kernel">内核版本</string>
<string name="home_arch">设备架构</string>
<string name="home_version">系统版本</string>
<string name="home_manager_version">管理器版本</string>
<string name="home_api">API 版本</string>
<string name="home_abi">ABI 支持</string>
<string name="home_fingerprint">系统指纹</string>
@@ -46,4 +46,6 @@
<string name="module_version">版本</string>
<string name="module_author">作者</string>
<string name="module_overlay_fs_not_available">內核不支持 overlayfs模塊功能無法運作</string>
<string name="safe_mode">安全模式</string>
<string name="reboot_to_apply">重啟生效</string>
</resources>

View File

@@ -1,49 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="home"></string>
<string name="home"></string>
<string name="home_not_installed">未安裝</string>
<string name="home_click_to_install">点击安装</string>
<string name="home_click_to_install">點擊安裝</string>
<string name="home_working">工作中</string>
<string name="home_working_version">版本: %d</string>
<string name="home_unsupported">不支</string>
<string name="home_unsupported_reason">KernelSU 在只支 GKI </string>
<string name="home_unsupported">不支</string>
<string name="home_unsupported_reason">KernelSU 在只支 GKI </string>
<string name="home_copied_to_clipboard">已複製到剪貼簿</string>
<string name="home_support"></string>
<string name="home_kernel">核版本</string>
<string name="home_arch">设备架构</string>
<string name="home_version">系统版本</string>
<string name="home_support"></string>
<string name="home_kernel">核版本</string>
<string name="home_arch">設備架構</string>
<string name="home_manager_version">管理器版本</string>
<string name="home_api">API 版本</string>
<string name="home_abi">ABI 支</string>
<string name="home_fingerprint">统指纹</string>
<string name="home_securitypatch">安全补丁</string>
<string name="home_abi">ABI 支</string>
<string name="home_fingerprint">統指紋</string>
<string name="home_securitypatch">安全性更新</string>
<string name="home_selinux_status">SELinux 狀態</string>
<string name="selinux_status_disabled"></string>
<string name="selinux_status_disabled"></string>
<string name="selinux_status_enforcing">強制執行</string>
<string name="selinux_status_permissive">寬容模式</string>
<string name="selinux_status_unknown">未知</string>
<string name="superuser">超級用戶</string>
<string name="superuser_failed_to_grant_root">无法为 %d 授 Root</string>
<string name="module_failed_to_enable">无法启用模: %s</string>
<string name="module_failed_to_disable">法禁用模: %s</string>
<string name="module_empty">有安装模块</string>
<string name="module"></string>
<string name="uninstall">卸载</string>
<string name="module_install"></string>
<string name="install"></string>
<string name="reboot"></string>
<string name="superuser_failed_to_grant_root">無法為 %d 授 Root</string>
<string name="module_failed_to_enable">無法啟用模: %s</string>
<string name="module_failed_to_disable">法禁用模: %s</string>
<string name="module_empty">有安裝模組</string>
<string name="module"></string>
<string name="uninstall">解除安裝</string>
<string name="module_install"></string>
<string name="install"></string>
<string name="reboot">新開機</string>
<string name="settings">設定</string>
<string name="reboot_userspace">软重启</string>
<string name="reboot_recovery">到 Recovery</string>
<string name="reboot_bootloader">到 BootLoader</string>
<string name="reboot_download">到 Download</string>
<string name="reboot_edl">到 EDL</string>
<string name="settings_system_rw">使系统可写</string>
<string name="settings_system_rw_summary">使用 overlayfs 使系统分区可写, 重启生效</string>
<string name="about">关于</string>
<string name="reboot_userspace">重新啟動</string>
<string name="reboot_recovery">新啟動到 Recovery</string>
<string name="reboot_bootloader">新啟動到 BootLoader</string>
<string name="reboot_download">新啟用到 Download</string>
<string name="reboot_edl">新啟用到 EDL</string>
<string name="settings_system_rw">讓系統可以寫入</string>
<string name="settings_system_rw_summary">使用 overlayfs 使系統分區可以寫入,重新啟動後生效</string>
<string name="about">關於</string>
<string name="require_kernel_version_8">需要 KernelSU 版本 8+</string>
<string name="module_uninstall_success">%s 已卸載</string>
<string name="module_uninstall_failed">卸載失敗: %s</string>
<string name="module_uninstall_success">%s 已解除安裝</string>
<string name="module_uninstall_failed">解除安裝失敗: %s</string>
<string name="module_version">版本</string>
<string name="module_author">作者</string>
<string name="module_overlay_fs_not_available">內核不支持 overlayfs功能無法運作!</string>
<string name="module_overlay_fs_not_available">內核不支持 overlayfs功能不能運作!</string>
<string name="refresh">重新整理</string>
<string name="show_system_apps">顯示系統應用程式</string>
<string name="hide_system_apps">隱藏系統應用程式</string>
<string name="safe_mode">安全模式</string>
<string name="reboot_to_apply">重啟生效</string>
</resources>

View File

@@ -13,7 +13,7 @@
<string name="home_kernel">Kernel</string>
<string name="home_arch">Arch</string>
<string name="home_version">Version</string>
<string name="home_manager_version">Manager Version</string>
<string name="home_api">API Level</string>
<string name="home_abi">ABI</string>
<string name="home_fingerprint">Fingerprint</string>
@@ -45,10 +45,17 @@
<string name="settings_system_rw_summary">Use overlayfs to make system partition writable, reboot to take effect.</string>
<string name="about">About</string>
<string name="require_kernel_version_8">Require KernelSU version 8+</string>
<string name="module_uninstall_confirm">Are you sure you want to uninstall module %s?</string>
<string name="module_uninstall_success">%s uninstalled</string>
<string name="module_uninstall_failed">Failed to uninstall: %s</string>
<string name="module_version">Version</string>
<string name="module_author">Author</string>
<string name="module_overlay_fs_not_available">overlayfs is not available, module cannot work!</string>
<string name="refresh">Refresh</string>
<string name="show_system_apps">Show system apps</string>
<string name="hide_system_apps">Hide system apps</string>
<string name="send_log">Send Log</string>
<string name="safe_mode">Safe mode</string>
<string name="reboot_to_apply">Reboot to take effect</string>
</resources>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache" path="."/>
<root-path name="root" path="." />
</paths>

View File

@@ -8,6 +8,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aes"
version = "0.7.5"
@@ -35,6 +41,33 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
[[package]]
name = "android_log-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27f0fc03f560e1aebde41c2398b691cb98b5ea5996a6184a7a67bbbb77448969"
[[package]]
name = "android_logger"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d70a974711fabdc7555de36765bbc237f093f5992d62cab3dcaa0b0bfc7d756"
dependencies = [
"android_log-sys",
"env_logger",
"log",
"once_cell",
]
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.68"
@@ -55,9 +88,9 @@ checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
[[package]]
name = "bindgen"
version = "0.59.2"
version = "0.60.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6"
dependencies = [
"bitflags",
"cexpr",
@@ -87,6 +120,12 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "byteorder"
version = "1.4.3"
@@ -138,6 +177,18 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
dependencies = [
"iana-time-zone",
"num-integer",
"num-traits",
"winapi",
]
[[package]]
name = "cipher"
version = "0.3.0"
@@ -195,6 +246,16 @@ dependencies = [
"os_str_bytes",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "const_format"
version = "0.2.30"
@@ -221,6 +282,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cpufeatures"
version = "0.2.5"
@@ -316,6 +383,50 @@ dependencies = [
"typenum",
]
[[package]]
name = "cxx"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9"
dependencies = [
"cc",
"cxxbridge-flags",
"cxxbridge-macro",
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d"
dependencies = [
"cc",
"codespan-reporting",
"once_cell",
"proc-macro2",
"quote",
"scratch",
"syn",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a"
[[package]]
name = "cxxbridge-macro"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "derive-new"
version = "0.5.9"
@@ -492,9 +603,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "heck"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
@@ -505,6 +616,12 @@ dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hmac"
version = "0.12.1"
@@ -530,13 +647,71 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "io-lifetimes"
version = "1.0.4"
name = "iana-time-zone"
version = "0.1.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
dependencies = [
"cxx",
"cxx-build",
]
[[package]]
name = "include-flate"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfdcb449c721557c1cf89bbd3412bf33fa963289e26e9badbd824a960912e148"
dependencies = [
"include-flate-codegen-exports",
"lazy_static",
"libflate",
]
[[package]]
name = "include-flate-codegen"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a7d6e1419fa3129eb0802b4c99603c0d425c79fb5d76191d5a20d0ab0d664e8"
dependencies = [
"libflate",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "include-flate-codegen-exports"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75657043ffe3d8280f1cb8aef0f505532b392ed7758e0baeac22edadcee31a03"
dependencies = [
"include-flate-codegen",
"proc-macro-hack",
]
[[package]]
name = "io-lifetimes"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
dependencies = [
"libc",
"windows-sys",
"windows-sys 0.45.0",
]
[[package]]
@@ -548,7 +723,7 @@ dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys",
"windows-sys 0.42.0",
]
[[package]]
@@ -586,6 +761,15 @@ dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "jwalk"
version = "0.8.1"
@@ -601,6 +785,7 @@ name = "ksud"
version = "0.1.0"
dependencies = [
"android-properties",
"android_logger",
"anyhow",
"clap",
"const_format",
@@ -615,12 +800,15 @@ dependencies = [
"libc",
"log",
"nom",
"proc-mounts",
"procfs",
"regex",
"retry",
"rust-embed",
"serde",
"serde_json",
"sys-mount",
"zip 0.6.3",
"zip 0.6.4",
"zip-extensions",
]
@@ -642,6 +830,26 @@ version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "libflate"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05605ab2bce11bcfc0e9c635ff29ef8b2ea83f29be257ee7d730cac3ee373093"
dependencies = [
"adler32",
"crc32fast",
"libflate_lz77",
]
[[package]]
name = "libflate_lz77"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39a734c0493409afcd49deee13c006a04e3586b9761a03543c6272c9c51f2f5a"
dependencies = [
"rle-decode-fast",
]
[[package]]
name = "libloading"
version = "0.7.4"
@@ -658,6 +866,15 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
[[package]]
name = "link-cplusplus"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
dependencies = [
"cc",
]
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
@@ -675,9 +892,8 @@ dependencies = [
[[package]]
name = "loopdev"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bfa0855b04611e38acaff718542e9e809cddfc16535d39f9d9c694ab19f7388"
version = "0.5.0"
source = "git+https://github.com/tiann/loopdev?branch=loopfix#b6ca5e3ea163f66239f6a835874fe231b2a9286f"
dependencies = [
"bindgen",
"errno",
@@ -724,6 +940,25 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.15.0"
@@ -752,6 +987,15 @@ version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "partition-identity"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa925f9becb532d758b0014b472c576869910929cf4c3f8054b386f19ab9e21"
dependencies = [
"thiserror",
]
[[package]]
name = "password-hash"
version = "0.4.2"
@@ -823,6 +1067,12 @@ dependencies = [
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.20+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
version = "1.0.50"
@@ -832,6 +1082,30 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "proc-mounts"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d652f8435d0ab70bf4f3590a6a851d59604831a458086541b95238cc51ffcf2"
dependencies = [
"partition-identity",
]
[[package]]
name = "procfs"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "943ca7f9f29bab5844ecd8fdb3992c5969b6622bb9609b9502fef9b4310e3f1f"
dependencies = [
"bitflags",
"byteorder",
"chrono",
"flate2",
"hex",
"lazy_static",
"rustix",
]
[[package]]
name = "quote"
version = "1.0.23"
@@ -919,6 +1193,47 @@ dependencies = [
"rand",
]
[[package]]
name = "rle-decode-fast"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422"
[[package]]
name = "rust-embed"
version = "6.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "283ffe2f866869428c92e0d61c2f35dfb4355293cdfdc48f49e895c15f1333d1"
dependencies = [
"include-flate",
"rust-embed-impl",
"rust-embed-utils",
"walkdir",
]
[[package]]
name = "rust-embed-impl"
version = "6.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "7.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054"
dependencies = [
"sha2",
"walkdir",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
@@ -936,7 +1251,7 @@ dependencies = [
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
"windows-sys 0.42.0",
]
[[package]]
@@ -945,12 +1260,27 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "scratch"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
[[package]]
name = "serde"
version = "1.0.152"
@@ -1032,9 +1362,8 @@ dependencies = [
[[package]]
name = "sys-mount"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793d38aa916d55e979587ea7cf551106f5f091e548a91e89ee4f0698bc9cf289"
version = "2.0.2"
source = "git+https://github.com/tiann/sys-mount#328928573feb9dbe6318d7ebcb8c8c9b681c47cd"
dependencies = [
"bitflags",
"libc",
@@ -1090,10 +1419,8 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
dependencies = [
"itoa",
"serde",
"time-core",
"time-macros",
]
[[package]]
@@ -1102,15 +1429,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]]
name = "time-macros"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
dependencies = [
"time-core",
]
[[package]]
name = "tracing"
version = "0.1.37"
@@ -1155,6 +1473,12 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "unicode-xid"
version = "0.2.4"
@@ -1167,6 +1491,17 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
@@ -1179,6 +1514,60 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
[[package]]
name = "winapi"
version = "0.3.9"
@@ -1225,6 +1614,30 @@ dependencies = [
"windows_x86_64_msvc",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.1"
@@ -1283,9 +1696,9 @@ dependencies = [
[[package]]
name = "zip"
version = "0.6.3"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080"
checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef"
dependencies = [
"aes",
"byteorder",
@@ -1331,9 +1744,9 @@ dependencies = [
[[package]]
name = "zstd-sys"
version = "2.0.5+zstd.1.5.2"
version = "2.0.6+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc50ffce891ad571e9f9afe5039c4837bede781ac4bb13052ed7ae695518596"
checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b"
dependencies = [
"cc",
"libc",

View File

@@ -22,13 +22,25 @@ encoding = "0.2.33"
retry = "2.0.0"
humansize = "2.0.0"
libc = "0.2"
sys-mount = "2.0.1"
android-properties = { version = "0.2.2", features = ["bionic-deprecated"] }
extattr = "1.0.0"
jwalk = "0.8.1"
is_executable = "1.0.1"
nom = "7"
derive-new = "0.5"
rust-embed = { version = "6.4.2", features = [
"debug-embed",
"compression", # must clean build after updating binaries
] }
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
sys-mount = { git = "https://github.com/tiann/sys-mount" }
# some android specific dependencies which compiles under unix are also listed here for convenience of coding
android-properties = { version = "0.2.2", features = ["bionic-deprecated"] }
procfs = "0.15"
proc-mounts = "0.3"
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.13"
[profile.release]
strip = true

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

46
userspace/ksud/build.rs Normal file
View File

@@ -0,0 +1,46 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::Command;
fn get_git_version() -> (u32, String) {
let version_code = String::from_utf8(
Command::new("git")
.args(["rev-list", "--count", "HEAD"])
.output()
.expect("Failed to get git count")
.stdout,
)
.expect("Failed to read git count stdout");
let version_code: u32 = version_code
.trim()
.parse()
.expect("Failed to parse git count");
let version_code = 10000 + 200 + version_code; // For historical reasons
let version_name = String::from_utf8(
Command::new("git")
.args(["describe", "--tags", "--always"])
.output()
.expect("Failed to get git version")
.stdout,
)
.expect("Failed to read git version stdout");
(version_code, version_name)
}
fn main() {
let (code, name) = get_git_version();
let out_dir = env::var("OUT_DIR").expect("Failed to get $OUT_DIR");
let out_dir = Path::new(&out_dir);
File::create(Path::new(out_dir).join("VERSION_CODE"))
.expect("Failed to create VERSION_CODE")
.write_all(code.to_string().as_bytes())
.expect("Failed to write VERSION_CODE");
File::create(Path::new(out_dir).join("VERSION_NAME"))
.expect("Failed to create VERSION_NAME")
.write_all(name.trim().as_bytes())
.expect("Failed to write VERSION_NAME");
}

View File

@@ -1,6 +1,5 @@
use std::io::{Read, Seek, SeekFrom};
use anyhow::{ensure, Result};
use std::io::{Read, Seek, SeekFrom};
pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
let mut buffer = [0u8; 0x10];
@@ -17,13 +16,13 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
f.read_exact(&mut n)?;
let n = u16::from_le_bytes(n);
if n as i64 == i {
if i64::from(n) == i {
f.seek(SeekFrom::Current(-22))?;
f.read_exact(&mut size4)?;
if u32::from_le_bytes(size4) ^ 0xcafebabeu32 == 0xccfbf1eeu32 {
if u32::from_le_bytes(size4) ^ 0xcafe_babe_u32 == 0xccfb_f1ee_u32 {
if i > 0 {
println!("warning: comment length is {}", i);
println!("warning: comment length is {i}");
}
break;
}
@@ -37,14 +36,14 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
f.seek(SeekFrom::Current(12))?;
// offset
f.read_exact(&mut size4)?;
f.seek(SeekFrom::Start(u32::from_le_bytes(size4) as u64 - 0x18))?;
f.seek(SeekFrom::Start(u64::from(u32::from_le_bytes(size4)) - 0x18))?;
f.read_exact(&mut size8)?;
f.read_exact(&mut buffer)?;
ensure!(&buffer == b"APK Sig Block 42", "Can not found sig block");
let pos = u32::from_le_bytes(size4) as u64 - (u64::from_le_bytes(size8) + 0x8);
let pos = u64::from(u32::from_le_bytes(size4)) - (u64::from_le_bytes(size8) + 0x8);
f.seek(SeekFrom::Start(pos))?;
f.read_exact(&mut size_of_block)?;
@@ -62,7 +61,7 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
f.read_exact(&mut id)?; // id
let id = u32::from_le_bytes(id);
if (id ^ 0xdeadbeefu32) == 0xafa439f5u32 || (id ^ 0xdeadbeefu32) == 0x2efed62fu32 {
if (id ^ 0xdead_beef_u32) == 0xafa4_39f5_u32 || (id ^ 0xdead_beef_u32) == 0x2efe_d62f_u32 {
f.read_exact(&mut size4)?; // signer-sequence length
f.read_exact(&mut size4)?; // signer length
f.read_exact(&mut size4)?; // signed data length
@@ -70,7 +69,7 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
f.read_exact(&mut size4)?; // digests-sequcence length
let pos = u32::from_le_bytes(size4);
f.seek(SeekFrom::Current(pos as i64))?;
f.seek(SeekFrom::Current(i64::from(pos)))?;
// offset += 0x4 + pos;
f.read_exact(&mut size4)?; // certificates length
@@ -83,18 +82,20 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
let j = u32::from_le_bytes(size4);
for _ in 0..j {
f.read_exact(&mut c)?;
hash = hash.wrapping_mul(31).wrapping_add(c[0] as i8 as i32);
hash = hash.wrapping_mul(31).wrapping_add(i32::from(c[0] as i8));
}
// offset += j;
let out_size = j;
let out_hash = (hash as u32) ^ 0x14131211u32;
let out_hash = (hash as u32) ^ 0x1413_1211_u32;
return Ok((out_size, out_hash));
}
f.seek(SeekFrom::Current(i64::from_le_bytes(size8) - offset as i64))?;
f.seek(SeekFrom::Current(
i64::from_le_bytes(size8) - i64::from(offset),
))?;
}
Err(anyhow::anyhow!("Unknown error"))

View File

@@ -0,0 +1,28 @@
use anyhow::Result;
use const_format::concatcp;
use rust_embed::RustEmbed;
use crate::{defs::BINARY_DIR, utils};
pub const RESETPROP_PATH: &str = concatcp!(BINARY_DIR, "resetprop");
pub const BUSYBOX_PATH: &str = concatcp!(BINARY_DIR, "busybox");
#[cfg(target_arch = "aarch64")]
#[derive(RustEmbed)]
#[folder = "bin/aarch64"]
struct Asset;
#[cfg(target_arch = "x86_64")]
#[derive(RustEmbed)]
#[folder = "bin/x86_64"]
struct Asset;
pub fn ensure_binaries() -> Result<()> {
for file in Asset::iter() {
utils::ensure_binary(
format!("{BINARY_DIR}{file}"),
&Asset::get(&file).unwrap().data,
)?
}
Ok(())
}

View File

@@ -1,11 +1,16 @@
use anyhow::{Ok, Result};
use clap::Parser;
use crate::{apk_sign, debug, event, module};
#[cfg(target_os = "android")]
use android_logger::Config;
#[cfg(target_os = "android")]
use log::LevelFilter;
use crate::{apk_sign, debug, defs, event, module, utils};
/// KernelSU userspace cli
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
#[command(author, version = defs::VERSION_NAME, about, long_about = None)]
struct Args {
#[command(subcommand)]
command: Commands,
@@ -84,6 +89,12 @@ enum Sepolicy {
/// sepolicy file path
file: String,
},
/// Check if sepolicy statement is supported/valid
Check {
/// sepolicy statements
sepolicy: String,
},
}
#[derive(clap::Subcommand, Debug)]
@@ -117,28 +128,44 @@ enum Module {
}
pub fn run() -> Result<()> {
#[cfg(target_os = "android")]
android_logger::init_once(
Config::default()
.with_max_level(LevelFilter::Trace) // limit log level
.with_tag("KernelSU"), // logs will show under mytag tag
);
#[cfg(not(target_os = "android"))]
env_logger::init();
let cli = Args::parse();
log::info!("command: {:?}", cli.command);
let result = match cli.command {
Commands::Daemon => event::daemon(),
Commands::PostFsData => event::on_post_data_fs(),
Commands::BootCompleted => event::on_boot_completed(),
Commands::Module { command } => {
env_logger::init();
#[cfg(any(target_os = "linux", target_os = "android"))]
{
utils::switch_mnt_ns(1)?;
utils::unshare_mnt_ns()?;
}
match command {
Module::Install { zip } => module::install_module(zip),
Module::Uninstall { id } => module::uninstall_module(id),
Module::Enable { id } => module::enable_module(id),
Module::Disable { id } => module::disable_module(id),
Module::Install { zip } => module::install_module(&zip),
Module::Uninstall { id } => module::uninstall_module(&id),
Module::Enable { id } => module::enable_module(&id),
Module::Disable { id } => module::disable_module(&id),
Module::List => module::list_modules(),
}
}
Commands::Install => event::install(),
Commands::Sepolicy { command } => match command {
Sepolicy::Patch { sepolicy } => crate::sepolicy::live_patch(&sepolicy),
Sepolicy::Apply { file } => crate::sepolicy::apply_file(&file),
Sepolicy::Apply { file } => crate::sepolicy::apply_file(file),
Sepolicy::Check { sepolicy } => crate::sepolicy::check_rule(&sepolicy),
},
Commands::Services => event::on_services(),
@@ -154,7 +181,7 @@ pub fn run() -> Result<()> {
Ok(())
}
Debug::Su => crate::ksu::grant_root(),
Debug::Test => todo!()
Debug::Test => todo!(),
},
};

View File

@@ -55,7 +55,7 @@ pub fn set_manager(pkg: &str) -> Result<()> {
);
let path = get_apk_path(pkg).with_context(|| format!("{pkg} does not exist!"))?;
let sign = get_apk_signature(path.as_str())?;
let sign = get_apk_signature(&path)?;
set_kernel_param(sign.0, sign.1)?;
Ok(())
}

View File

@@ -1,7 +1,11 @@
use const_format::concatcp;
pub const DAEMON_PATH: &str = "/data/adb/ksud";
pub const WORKING_DIR: &str = "/data/adb/ksu/";
pub const ADB_DIR: &str = "/data/adb/";
pub const DAEMON_PATH: &str = concatcp!(ADB_DIR, "ksud");
pub const WORKING_DIR: &str = concatcp!(ADB_DIR, "ksu/");
pub const BINARY_DIR: &str = concatcp!(WORKING_DIR, "bin/");
pub const MODULE_DIR: &str = concatcp!(WORKING_DIR, "modules/");
pub const MODULE_IMG: &str = concatcp!(WORKING_DIR, "modules.img");
@@ -15,3 +19,6 @@ pub const MODULE_UPDATE_TMP_DIR: &str = concatcp!(WORKING_DIR, "modules_update/"
pub const DISABLE_FILE_NAME: &str = "disable";
pub const UPDATE_FILE_NAME: &str = "update";
pub const REMOVE_FILE_NAME: &str = "remove";
pub const VERSION_CODE: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_CODE"));
pub const VERSION_NAME: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_NAME"));

View File

@@ -1,41 +1,57 @@
use anyhow::{bail, Context, Result};
use log::{info, warn};
use std::{collections::HashMap, path::Path};
use crate::{
defs,
utils::{ensure_clean_dir, mount_image},
assets, defs, mount,
utils::{self, ensure_clean_dir, ensure_dir_exists},
};
use anyhow::{bail, Result};
use sys_mount::{FilesystemType, Mount, MountFlags};
fn mount_partition(partition: &str, lowerdir: &mut Vec<String>) {
fn mount_partition(partition: &str, lowerdir: &mut Vec<String>) -> Result<()> {
if lowerdir.is_empty() {
println!("partition: {partition} lowerdir is empty");
return;
warn!("partition: {partition} lowerdir is empty");
return Ok(());
}
// if /partition is a symlink and linked to /system/partition, then we don't need to overlay it separately
if Path::new(&format!("/{}", partition)).read_link().is_ok() {
println!("partition: {} is a symlink", partition);
return;
if Path::new(&format!("/{partition}")).read_link().is_ok() {
warn!("partition: {partition} is a symlink");
return Ok(());
}
// handle stock mounts under /partition, we should restore the mount point after overlay
let stock_mount = mount::StockMount::new(&format!("/{partition}/"))
.with_context(|| format!("get stock mount of partition: {partition} failed"))?;
let result = stock_mount.umount();
if result.is_err() {
let remount_result = stock_mount.remount();
if remount_result.is_err() {
log::error!("remount stock mount of failed: {:?}", remount_result);
}
bail!("umount stock mount of failed: {:?}", result);
}
// add /partition as the lowerest dir
let lowest_dir = format!("/{partition}");
lowerdir.push(lowest_dir.clone());
let lowerdir = lowerdir.join(":");
println!("partition: {partition} lowerdir: {lowerdir}");
info!("partition: {partition} lowerdir: {lowerdir}");
if let Err(err) = Mount::builder()
.fstype(FilesystemType::from("overlay"))
.flags(MountFlags::RDONLY)
.data(&format!("lowerdir={lowerdir}"))
.mount("overlay", lowest_dir)
{
println!("mount partition: {partition} overlay failed: {err}");
let result = mount::mount_overlay(&lowerdir, &lowest_dir);
if result.is_ok() && stock_mount.remount().is_err() {
// if mount overlay ok but stock remount failed, we should umount overlay
warn!("remount stock mount of failed, umount overlay {lowest_dir} now");
if mount::umount_dir(&lowest_dir).is_err() {
warn!("umount overlay {lowest_dir} failed");
}
}
result
}
pub fn do_systemless_mount(module_dir: &str) -> Result<()> {
pub fn mount_systemlessly(module_dir: &str) -> Result<()> {
// construct overlay mount params
let dir = std::fs::read_dir(module_dir);
let Ok(dir) = dir else {
@@ -47,7 +63,7 @@ pub fn do_systemless_mount(module_dir: &str) -> Result<()> {
let partition = vec!["vendor", "product", "system_ext", "odm", "oem"];
let mut partition_lowerdir: HashMap<String, Vec<String>> = HashMap::new();
for ele in &partition {
partition_lowerdir.insert(ele.to_string(), Vec::new());
partition_lowerdir.insert((*ele).to_string(), Vec::new());
}
for entry in dir.flatten() {
@@ -57,16 +73,14 @@ pub fn do_systemless_mount(module_dir: &str) -> Result<()> {
}
let disabled = module.join(defs::DISABLE_FILE_NAME).exists();
if disabled {
println!("module: {} is disabled, ignore!", module.display());
info!("module: {} is disabled, ignore!", module.display());
continue;
}
let module_system = Path::new(&module).join("system");
if !module_system.as_path().exists() {
println!("module: {} has no system overlay.", module.display());
continue;
if module_system.exists() {
system_lowerdir.push(format!("{}", module_system.display()));
}
system_lowerdir.push(format!("{}", module_system.display()));
for part in &partition {
// if /partition is a mountpoint, we would move it to $MODPATH/$partition when install
@@ -82,11 +96,15 @@ pub fn do_systemless_mount(module_dir: &str) -> Result<()> {
}
// mount /system first
mount_partition("system", &mut system_lowerdir);
if let Err(e) = mount_partition("system", &mut system_lowerdir) {
warn!("mount system failed: {e}");
}
// mount other partitions
for (k, mut v) in partition_lowerdir {
mount_partition(&k, &mut v);
if let Err(e) = mount_partition(&k, &mut v) {
warn!("mount {k} failed: {e}");
}
}
Ok(())
@@ -94,6 +112,9 @@ pub fn do_systemless_mount(module_dir: &str) -> Result<()> {
pub fn on_post_data_fs() -> Result<()> {
crate::ksu::report_post_fs_data();
utils::umask(0);
let module_update_img = defs::MODULE_UPDATE_IMG;
let module_img = defs::MODULE_IMG;
let module_dir = defs::MODULE_DIR;
@@ -105,6 +126,8 @@ pub fn on_post_data_fs() -> Result<()> {
// we should clean the module mount point if it exists
ensure_clean_dir(module_dir)?;
assets::ensure_binaries().with_context(|| "Failed to extract bin assets")?;
if Path::new(module_update_img).exists() {
if module_update_flag.exists() {
// if modules_update.img exists, and the the flag indicate this is an update
@@ -119,42 +142,75 @@ pub fn on_post_data_fs() -> Result<()> {
}
}
// If there isn't any image exist, do nothing for module!
if !Path::new(target_update_img).exists() {
// no image exist, do nothing for module!
return Ok(());
}
println!("mount {} to {}", target_update_img, module_dir);
mount_image(target_update_img, module_dir)?;
// we should always mount the module.img to module dir
// becuase we may need to operate the module dir in safe mode
info!("mount module image: {target_update_img} to {module_dir}");
mount::AutoMountExt4::try_new(target_update_img, module_dir, false)
.with_context(|| "mount module image failed".to_string())?;
// check safe mode first.
if crate::utils::is_safe_mode() {
warn!("safe mode, skip post-fs-data scripts and disable all modules!");
if let Err(e) = crate::module::disable_all_modules() {
warn!("disable all modules failed: {}", e);
}
return Ok(());
}
// Then exec common post-fs-data scripts
if let Err(e) = crate::module::exec_common_scripts("post-fs-data.d", true) {
warn!("exec common post-fs-data scripts failed: {}", e);
}
// load sepolicy.rule
if (crate::module::load_sepolicy_rule().is_err()) {
println!("load sepolicy.rule failed");
if crate::module::load_sepolicy_rule().is_err() {
warn!("load sepolicy.rule failed");
}
// mount systemless overlay
if let Err(e) = do_systemless_mount(module_dir) {
println!("do systemless mount failed: {}", e);
// exec modules post-fs-data scripts
// TODO: Add timeout
if let Err(e) = crate::module::exec_post_fs_data() {
warn!("exec post-fs-data scripts failed: {}", e);
}
// module mounted, exec modules post-fs-data scripts
if !crate::utils::is_safe_mode() {
// todo: Add timeout
let _ = crate::module::exec_post_fs_data();
let _ = crate::module::load_system_prop();
} else {
println!("safe mode, skip module post-fs-data scripts");
// load system.prop
if let Err(e) = crate::module::load_system_prop() {
warn!("load system.prop failed: {}", e);
}
// Finally, we should do systemless mount
// But we should umount all stock overlayfs and remount them after module mounted
let stock_overlay = mount::StockOverlay::new();
stock_overlay.umount_all();
// mount moduke systemlessly by overlay
if let Err(e) = mount_systemlessly(module_dir) {
warn!("do systemless mount failed: {}", e);
}
stock_overlay.mount_all();
Ok(())
}
pub fn on_services() -> Result<()> {
// exec modules service.sh scripts
if !crate::utils::is_safe_mode() {
let _ = crate::module::exec_services();
} else {
println!("safe mode, skip module service scripts");
utils::umask(0);
// check safe mode first.
if crate::utils::is_safe_mode() {
warn!("safe mode, skip module service scripts");
return Ok(());
}
if let Err(e) = crate::module::exec_common_scripts("service.d", false) {
warn!("Failed to exec common service scripts: {}", e);
}
if let Err(e) = crate::module::exec_services() {
warn!("Failed to exec service scripts: {}", e);
}
Ok(())
@@ -176,9 +232,9 @@ pub fn daemon() -> Result<()> {
}
pub fn install() -> Result<()> {
let src = "/proc/self/exe";
let dst = defs::DAEMON_PATH;
ensure_dir_exists(defs::ADB_DIR)?;
std::fs::copy("/proc/self/exe", defs::DAEMON_PATH)?;
std::fs::copy(src, dst)?;
Ok(())
// install binary assets
assets::ensure_binaries().with_context(|| "Failed to extract assets")
}

View File

@@ -1,9 +1,12 @@
#!/system/bin/sh
############################################
# KernelSU installer script
# Credit to Magisk!!!
# mostly from module_installer.sh
# and util_functions.sh in Magisk
############################################
umask 022
ui_print() {
if $BOOTMODE; then
echo "$1"
@@ -68,6 +71,11 @@ print_title() {
ui_print "$bar"
}
check_sepolicy() {
/data/adb/ksud sepolicy check "$1"
return $?
}
######################
# Environment Related
######################
@@ -267,10 +275,6 @@ request_size_check() {
reqSizeM=`du -ms "$1" | cut -f1`
}
unzip() {
/system/bin/unzip -q "$@"
}
request_zip_size_check() {
reqSizeM=`unzip -l "$1" | tail -n 1 | awk '{ print int(($1 - 1) / 1048576 + 1) }'`
}
@@ -366,7 +370,7 @@ install_module() {
set_perm_recursive $MODPATH/system/bin 0 2000 0755 0755
set_perm_recursive $MODPATH/system/xbin 0 2000 0755 0755
set_perm_recursive $MODPATH/system/system_ext/bin 0 2000 0755 0755
set_perm_recursive $MODPATH/system/vendor/bin 0 2000 0755 0755 u:object_r:vendor_file:s0
set_perm_recursive $MODPATH/system/vendor 0 2000 0755 0755 u:object_r:vendor_file:s0
fi
# Load customization script
@@ -416,8 +420,5 @@ NVBASE=/data/adb/ksu
TMPDIR=/dev/tmp
# Some modules dependents on this
MAGISK_VER=25.2
MAGISK_VER_CODE=25200
# KSU to recognize
KSU=true
export MAGISK_VER=25.2
export MAGISK_VER_CODE=25200

View File

@@ -1,9 +1,11 @@
#![allow(dead_code, unused_mut, unused_variables, unused_imports)]
use anyhow::Result;
use anyhow::{ensure, Result};
#[cfg(unix)]
use anyhow::ensure;
#[cfg(unix)]
use std::os::unix::process::CommandExt;
pub const KERNEL_SU_OPTION: u32 = 0xDEADBEEF;
pub const KERNEL_SU_OPTION: u32 = 0xDEAD_BEEF;
const CMD_GRANT_ROOT: u64 = 0;
// const CMD_BECOME_MANAGER: u64 = 1;
@@ -14,52 +16,76 @@ const CMD_GET_VERSION: u64 = 2;
// const CMD_GET_DENY_LIST: u64 = 6;
const CMD_REPORT_EVENT: u64 = 7;
pub const CMD_SET_SEPOLICY: u64 = 8;
pub const CMD_CHECK_SAFEMODE: u64 = 9;
const EVENT_POST_FS_DATA: u64 = 1;
const EVENT_BOOT_COMPLETED: u64 = 2;
#[cfg(target_os = "android")]
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn grant_root() -> Result<()> {
let mut result: u32 = 0;
unsafe {
#[allow(clippy::cast_possible_wrap)]
libc::prctl(
KERNEL_SU_OPTION as i32,
KERNEL_SU_OPTION as i32, // supposed to overflow
CMD_GRANT_ROOT,
0,
0,
&mut result as *mut _ as *mut libc::c_void,
std::ptr::addr_of_mut!(result).cast::<libc::c_void>(),
);
}
ensure!(result == KERNEL_SU_OPTION, "grant root failed");
return Err(std::process::Command::new("sh").exec().into());
Err(std::process::Command::new("sh").exec().into())
}
#[cfg(not(target_os = "android"))]
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn grant_root() -> Result<()> {
unimplemented!("grant_root is only available on android");
}
pub fn get_version() -> i32 {
let mut result: i32 = 0;
#[cfg(target_os = "android")]
#[cfg(any(target_os = "linux", target_os = "android"))]
unsafe {
#[allow(clippy::cast_possible_wrap)]
libc::prctl(
KERNEL_SU_OPTION as i32,
KERNEL_SU_OPTION as i32, // supposed to overflow
CMD_GET_VERSION,
&mut result as *mut _ as *mut libc::c_void,
std::ptr::addr_of_mut!(result).cast::<libc::c_void>(),
);
}
result
}
fn report_event(event: u64) {
#[cfg(target_os = "android")]
#[cfg(any(target_os = "linux", target_os = "android"))]
unsafe {
libc::prctl(KERNEL_SU_OPTION as i32, CMD_REPORT_EVENT, event);
#[allow(clippy::cast_possible_wrap)]
libc::prctl(
KERNEL_SU_OPTION as i32, // supposed to overflow
CMD_REPORT_EVENT,
event,
);
}
}
pub fn check_kernel_safemode() -> bool {
let mut result: i32 = 0;
#[cfg(any(target_os = "linux", target_os = "android"))]
unsafe {
#[allow(clippy::cast_possible_wrap)]
libc::prctl(
KERNEL_SU_OPTION as i32, // supposed to overflow
CMD_CHECK_SAFEMODE,
0,
0,
std::ptr::addr_of_mut!(result).cast::<libc::c_void>(),
);
}
result == KERNEL_SU_OPTION as i32
}
pub fn report_post_fs_data() {
report_event(EVENT_POST_FS_DATA);
}

View File

@@ -1,13 +1,15 @@
mod apk_sign;
mod assets;
mod cli;
mod debug;
mod defs;
mod event;
mod ksu;
mod module;
mod mount;
mod restorecon;
mod utils;
mod sepolicy;
mod utils;
fn main() -> anyhow::Result<()> {
cli::run()

View File

@@ -1,32 +1,59 @@
use crate::{defs, restorecon, sepolicy};
use crate::{restorecon::setsyscon, utils::*};
#[allow(clippy::wildcard_imports)]
use crate::utils::*;
use crate::{
assets, defs, mount,
restorecon::{restore_syscon, setsyscon},
sepolicy,
};
use anyhow::{anyhow, bail, ensure, Context, Result};
use const_format::concatcp;
use is_executable::is_executable;
use java_properties::PropertiesIter;
use log::{info, warn};
use std::{
collections::HashMap,
fs::{create_dir_all, remove_dir_all, File, OpenOptions},
io::{Cursor, Read, Write},
os::unix::{prelude::PermissionsExt, process::CommandExt},
env::var as env_var,
fs::{remove_dir_all, set_permissions, File, OpenOptions, Permissions},
io::{Cursor, Write},
path::{Path, PathBuf},
process::{Command, Stdio},
str::FromStr,
};
use zip_extensions::zip_extract_file_to_memory;
use anyhow::{bail, ensure, Context, Result};
#[cfg(unix)]
use std::os::unix::{prelude::PermissionsExt, process::CommandExt};
const UTIL_FUNCTIONS: &str = include_str!("./installer.sh");
const INSTALL_MODULE_SCRIPT: &str =
concatcp!(UTIL_FUNCTIONS, "\n", "install_module", "\n", "exit 0", "\n");
const INSTALLER_CONTENT: &str = include_str!("./installer.sh");
const INSTALL_MODULE_SCRIPT: &str = concatcp!(
INSTALLER_CONTENT,
"\n",
"install_module",
"\n",
"exit 0",
"\n"
);
fn exec_install_script(module_file: &str) -> Result<()> {
let realpath = std::fs::canonicalize(module_file)
.with_context(|| format!("realpath: {module_file} failed"))?;
let result = Command::new("sh")
.args(["-c", INSTALL_MODULE_SCRIPT])
let result = Command::new(assets::BUSYBOX_PATH)
.args(["sh", "-c", INSTALL_MODULE_SCRIPT])
.env("ASH_STANDALONE", "1")
.env(
"PATH",
format!(
"{}:{}",
env_var("PATH").unwrap(),
defs::BINARY_DIR.trim_end_matches('/')
),
)
.env("KSU", "true")
.env("KSU_KERNEL_VER_CODE", crate::ksu::get_version().to_string())
.env("KSU_VER", defs::VERSION_NAME)
.env("KSU_VER_CODE", defs::VERSION_CODE)
.env("OUTFD", "1")
.env("ZIPFILE", realpath)
.stderr(Stdio::null())
@@ -48,33 +75,24 @@ fn ensure_boot_completed() -> Result<()> {
}
fn mark_update() -> Result<()> {
let update_file = Path::new(defs::WORKING_DIR).join(defs::UPDATE_FILE_NAME);
if update_file.exists() {
return Ok(());
}
std::fs::File::create(update_file)?;
Ok(())
ensure_file_exists(concatcp!(defs::WORKING_DIR, defs::UPDATE_FILE_NAME))
}
fn mark_module_state(module: &str, flag_file: &str, create_or_delete: bool) -> Result<()> {
let module_state_file = Path::new(defs::MODULE_DIR).join(module).join(flag_file);
if create_or_delete {
if module_state_file.exists() {
return Ok(());
}
std::fs::File::create(module_state_file)?;
ensure_file_exists(module_state_file)
} else {
if !module_state_file.exists() {
return Ok(());
if module_state_file.exists() {
std::fs::remove_file(module_state_file)?;
}
std::fs::remove_file(module_state_file)?;
Ok(())
}
Ok(())
}
fn get_minimal_image_size(img: &str) -> Result<u64> {
check_image(img)?;
let output = Command::new("resize2fs")
.args(["-P", img])
.stdout(Stdio::piped())
@@ -84,8 +102,8 @@ fn get_minimal_image_size(img: &str) -> Result<u64> {
println!("- {}", output.trim());
let regex = regex::Regex::new(r"filesystem: (\d+)")?;
let result = regex
.captures(output.as_ref())
.ok_or_else(|| anyhow::anyhow!("regex not match"))?;
.captures(&output)
.ok_or(anyhow::anyhow!("regex not match"))?;
let result = &result[1];
let result = u64::from_str(result)?;
Ok(result)
@@ -112,8 +130,8 @@ fn check_image(img: &str) -> Result<()> {
}
fn grow_image_size(img: &str, extra_size: u64) -> Result<()> {
let minimal_size = get_minimal_image_size(img)?;
let target_size = minimal_size + extra_size;
let minimal_size = get_minimal_image_size(img)?; // the minimal size is in KB
let target_size = minimal_size * 1024 + extra_size;
// check image
check_image(img)?;
@@ -122,7 +140,7 @@ fn grow_image_size(img: &str, extra_size: u64) -> Result<()> {
"- Target image size: {}",
humansize::format_size(target_size, humansize::DECIMAL)
);
let target_size = target_size / 1024 + 1;
let target_size = target_size / 1024 + 1024;
let result = Command::new("resize2fs")
.args([img, &format!("{target_size}K")])
@@ -131,6 +149,8 @@ fn grow_image_size(img: &str, extra_size: u64) -> Result<()> {
.with_context(|| format!("Failed to exec resize2fs {img}"))?;
ensure!(result.success(), "Failed to resize2fs: {}", result);
check_image(img)?;
Ok(())
}
@@ -160,17 +180,6 @@ fn switch_cgroups() {
}
}
fn is_executable(path: &Path) -> bool {
let mut buffer: [u8; 2] = [0; 2];
is_executable::is_executable(path)
&& File::open(path).unwrap().read_exact(&mut buffer).is_ok()
&& (
buffer == [0x23, 0x21] // shebang #!
|| buffer == [0x7f, 0x45]
// ELF magic number 0x7F 'E'
)
}
pub fn load_sepolicy_rule() -> Result<()> {
let modules_dir = Path::new(defs::MODULE_DIR);
let dir = std::fs::read_dir(modules_dir)?;
@@ -178,7 +187,7 @@ pub fn load_sepolicy_rule() -> Result<()> {
let path = entry.path();
let disabled = path.join(defs::DISABLE_FILE_NAME);
if disabled.exists() {
println!("{} is disabled, skip", path.display());
info!("{} is disabled, skip", path.display());
continue;
}
@@ -186,16 +195,57 @@ pub fn load_sepolicy_rule() -> Result<()> {
if !rule_file.exists() {
continue;
}
println!("load policy: {}", &rule_file.display());
info!("load policy: {}", &rule_file.display());
if sepolicy::apply_file(&rule_file).is_err() {
println!("Failed to load sepolicy.rule for {}", &rule_file.display());
warn!("Failed to load sepolicy.rule for {}", &rule_file.display());
}
}
Ok(())
}
fn exec_script<T: AsRef<Path>>(path: T, wait: bool) -> Result<()> {
info!("exec {}", path.as_ref().display());
let mut command = &mut Command::new(assets::BUSYBOX_PATH);
#[cfg(unix)]
{
command = command.process_group(0);
command = unsafe {
command.pre_exec(|| {
// ignore the error?
switch_cgroups();
Ok(())
})
};
}
command = command
.current_dir(path.as_ref().parent().unwrap())
.arg("sh")
.arg(path.as_ref())
.env("ASH_STANDALONE", "1")
.env("KSU", "true")
.env("KSU_KERNEL_VER_CODE", crate::ksu::get_version().to_string())
.env("KSU_VER_CODE", defs::VERSION_CODE)
.env("KSU_VER", defs::VERSION_NAME)
.env(
"PATH",
format!(
"{}:{}",
env_var("PATH").unwrap(),
defs::BINARY_DIR.trim_end_matches('/')
),
);
let result = if wait {
command.status().map(|_| ())
} else {
command.spawn().map(|_| ())
};
result.map_err(|err| anyhow!("Failed to exec {}: {}", path.as_ref().display(), err))
}
/// execute every modules' post-fs-data.sh
pub fn exec_post_fs_data() -> Result<()> {
let modules_dir = Path::new(defs::MODULE_DIR);
@@ -204,7 +254,7 @@ pub fn exec_post_fs_data() -> Result<()> {
let path = entry.path();
let disabled = path.join(defs::DISABLE_FILE_NAME);
if disabled.exists() {
println!("{} is disabled, skip", path.display());
warn!("{} is disabled, skip", path.display());
continue;
}
@@ -212,32 +262,30 @@ pub fn exec_post_fs_data() -> Result<()> {
if !post_fs_data.exists() {
continue;
}
println!("exec {} post-fs-data.sh", path.display());
let mut command_new;
let mut command;
if is_executable(&post_fs_data) {
command_new = Command::new("sh");
command = command_new.arg(&post_fs_data);
} else {
command_new = Command::new(&post_fs_data);
command = &mut command_new;
};
exec_script(&post_fs_data, true)?;
}
command = command
.process_group(0)
.current_dir(path)
.env("KSU", "true");
unsafe {
command = command.pre_exec(|| {
// ignore the error?
switch_cgroups();
Ok(())
});
Ok(())
}
pub fn exec_common_scripts(dir: &str, wait: bool) -> Result<()> {
let script_dir = Path::new(defs::WORKING_DIR).join(dir);
if !script_dir.exists() {
info!("{} not exists, skip", script_dir.display());
return Ok(());
}
let dir = std::fs::read_dir(&script_dir)?;
for entry in dir.flatten() {
let path = entry.path();
if !is_executable(&path) {
warn!("{} is not executable, skip", path.display());
continue;
}
command
.status()
.with_context(|| format!("Failed to exec {}", post_fs_data.display()))?;
exec_script(path, wait)?;
}
Ok(())
@@ -251,7 +299,7 @@ pub fn exec_services() -> Result<()> {
let path = entry.path();
let disabled = path.join(defs::DISABLE_FILE_NAME);
if disabled.exists() {
println!("{} is disabled, skip", path.display());
warn!("{} is disabled, skip", path.display());
continue;
}
@@ -259,58 +307,21 @@ pub fn exec_services() -> Result<()> {
if !service.exists() {
continue;
}
println!("exec {} service.sh", path.display());
let mut command_new;
let mut command;
if is_executable(&service) {
command_new = Command::new("sh");
command = command_new.arg(&service);
} else {
command_new = Command::new(&service);
command = &mut command_new;
};
command = command
.process_group(0)
.current_dir(path)
.env("KSU", "true");
unsafe {
command = command.pre_exec(|| {
// ignore the error?
switch_cgroups();
Ok(())
});
}
command
.spawn() // don't wait
.with_context(|| format!("Failed to exec {}", service.display()))?;
exec_script(&service, false)?;
}
Ok(())
}
const RESETPROP: &[u8] = include_bytes!("./resetprop");
const RESETPROP_PATH: &str = concatcp!(defs::WORKING_DIR, "/resetprop");
fn ensure_resetprop() -> Result<()> {
if Path::new(RESETPROP_PATH).exists() {
return Ok(());
}
std::fs::write(RESETPROP_PATH, RESETPROP)?;
std::fs::set_permissions(RESETPROP_PATH, std::fs::Permissions::from_mode(0o755))?;
Ok(())
}
pub fn load_system_prop() -> Result<()> {
ensure_resetprop()?;
let modules_dir = Path::new(defs::MODULE_DIR);
let dir = std::fs::read_dir(modules_dir)?;
for entry in dir.flatten() {
let path = entry.path();
let disabled = path.join(defs::DISABLE_FILE_NAME);
if disabled.exists() {
println!("{} is disabled, skip", path.display());
info!("{} is disabled, skip", path.display());
continue;
}
@@ -318,10 +329,10 @@ pub fn load_system_prop() -> Result<()> {
if !system_prop.exists() {
continue;
}
println!("load {} system.prop", path.display());
info!("load {} system.prop", path.display());
// resetprop -n --file system.prop
Command::new(RESETPROP_PATH)
Command::new(assets::RESETPROP_PATH)
.arg("-n")
.arg("--file")
.arg(&system_prop)
@@ -332,27 +343,22 @@ pub fn load_system_prop() -> Result<()> {
Ok(())
}
pub fn install_module(zip: String) -> Result<()> {
fn _install_module(zip: &str) -> Result<()> {
ensure_boot_completed()?;
// print banner
println!(include_str!("banner"));
// first check if workding dir is usable
let working_dir = Path::new(defs::WORKING_DIR);
if !working_dir.exists() {
create_dir_all(working_dir)?;
}
assets::ensure_binaries().with_context(|| "Failed to extract assets")?;
ensure!(
working_dir.is_dir(),
"working dir exists but it is not a regular directory!"
);
// first check if workding dir is usable
ensure_dir_exists(defs::WORKING_DIR).with_context(|| "Failed to create working dir")?;
ensure_dir_exists(defs::BINARY_DIR).with_context(|| "Failed to create bin dir")?;
// read the module_id from zip, if faild if will return early.
let mut buffer: Vec<u8> = Vec::new();
let entry_path = PathBuf::from_str("module.prop")?;
let zip_path = PathBuf::from_str(&zip)?;
let zip_path = PathBuf::from_str(zip)?;
zip_extract_file_to_memory(&zip_path, &entry_path, &mut buffer)?;
let mut module_prop = HashMap::new();
@@ -361,11 +367,11 @@ pub fn install_module(zip: String) -> Result<()> {
module_prop.insert(k, v);
},
)?;
info!("module prop: {:?}", module_prop);
let Some(module_id) = module_prop.get("id") else {
bail!("module id not found in module.prop!");
};
info!("module id: {}", module_id);
let modules_img = Path::new(defs::MODULE_IMG);
let modules_update_img = Path::new(defs::MODULE_UPDATE_IMG);
@@ -382,9 +388,18 @@ pub fn install_module(zip: String) -> Result<()> {
}
let default_reserve_size = 64 * 1024 * 1024;
let zip_uncompressed_size = get_zip_uncompressed_size(&zip)?;
let zip_uncompressed_size = get_zip_uncompressed_size(zip)?;
let grow_size = default_reserve_size + zip_uncompressed_size;
info!(
"zip uncompressed size: {}",
humansize::format_size(zip_uncompressed_size, humansize::DECIMAL)
);
info!(
"grow size: {}",
humansize::format_size(grow_size, humansize::DECIMAL)
);
println!("- Preparing image");
println!(
"- Module size: {}",
@@ -394,6 +409,7 @@ pub fn install_module(zip: String) -> Result<()> {
if !modules_img_exist && !modules_update_img_exist {
// if no modules and modules_update, it is brand new installation, we should create a new img
// create a tmp module img and mount it to modules_update
info!("Creating brand new module image");
File::create(tmp_module_img)
.context("Failed to create ext4 image file")?
.set_len(grow_size)
@@ -413,6 +429,7 @@ pub fn install_module(zip: String) -> Result<()> {
check_image(tmp_module_img)?;
} else if modules_update_img_exist {
// modules_update.img exists, we should use it as tmp img
info!("Using existing modules_update.img as tmp image");
std::fs::copy(modules_update_img, tmp_module_img).with_context(|| {
format!(
"Failed to copy {} to {}",
@@ -424,6 +441,7 @@ pub fn install_module(zip: String) -> Result<()> {
grow_image_size(tmp_module_img, grow_size)?;
} else {
// modules.img exists, we should use it as tmp img
info!("Using existing modules.img as tmp image");
std::fs::copy(modules_img, tmp_module_img).with_context(|| {
format!(
"Failed to copy {} to {}",
@@ -441,52 +459,59 @@ pub fn install_module(zip: String) -> Result<()> {
// mount the modules_update.img to mountpoint
println!("- Mounting image");
mount_image(tmp_module_img, module_update_tmp_dir)?;
let _dontdrop = mount::AutoMountExt4::try_new(tmp_module_img, module_update_tmp_dir, true)?;
info!("mounted {} to {}", tmp_module_img, module_update_tmp_dir);
setsyscon(module_update_tmp_dir)?;
let result = {
let module_dir = format!("{}/{}", module_update_tmp_dir, module_id);
ensure_clean_dir(&module_dir)?;
info!("module dir: {}", module_dir);
let module_dir = format!("{module_update_tmp_dir}/{module_id}");
ensure_clean_dir(&module_dir)?;
info!("module dir: {}", module_dir);
// unzip the image and move it to modules_update/<id> dir
let file = File::open(&zip)?;
let mut archive = zip::ZipArchive::new(file)?;
archive.extract(&module_dir)?;
// unzip the image and move it to modules_update/<id> dir
let file = File::open(zip)?;
let mut archive = zip::ZipArchive::new(file)?;
archive.extract(&module_dir)?;
// set selinux for module/system dir
let mut module_system_dir = PathBuf::from(module_dir);
module_system_dir.push("system");
let module_system_dir = module_system_dir.as_path();
if module_system_dir.exists() {
let path = format!("{}", module_system_dir.display());
restorecon::restore_syscon(&path)?;
}
// set permission and selinux context for $MOD/system
let module_system_dir = PathBuf::from(module_dir).join("system");
if module_system_dir.exists() {
#[cfg(unix)]
set_permissions(&module_system_dir, Permissions::from_mode(0o755))?;
restore_syscon(&module_system_dir)?;
}
exec_install_script(&zip)
};
// umount the modules_update.img
let _ = umount_dir(module_update_tmp_dir);
// remove modules_update dir, ignore the error
let _ = remove_dir_all(module_update_tmp_dir);
// return if exec script failed
result.with_context(|| format!("Failed to execute install script for {}", module_id))?;
exec_install_script(zip)?;
info!("rename {tmp_module_img} to {}", defs::MODULE_UPDATE_IMG);
// all done, rename the tmp image to modules_update.img
if std::fs::rename(tmp_module_img, defs::MODULE_UPDATE_IMG).is_err() {
warn!("Rename image failed, try copy it.");
std::fs::copy(tmp_module_img, defs::MODULE_UPDATE_IMG)
.with_context(|| "Failed to copy image.".to_string())?;
let _ = std::fs::remove_file(tmp_module_img);
}
mark_update()?;
info!("Module install successfully!");
Ok(())
}
fn do_module_update<F>(update_dir: &str, id: &str, func: F) -> Result<()>
pub fn install_module(zip: &str) -> Result<()> {
let result = _install_module(zip);
if let Err(ref e) = result {
// error happened, do some cleanup!
let _ = std::fs::remove_file(defs::MODULE_UPDATE_TMP_IMG);
let _ = mount::umount_dir(defs::MODULE_UPDATE_TMP_DIR);
println!("- Error: {e}");
}
result
}
fn update_module<F>(update_dir: &str, id: &str, func: F) -> Result<()>
where
F: Fn(&str, &str) -> Result<()>,
{
@@ -517,28 +542,27 @@ where
ensure_clean_dir(update_dir)?;
// mount the modules_update img
mount_image(defs::MODULE_UPDATE_TMP_IMG, update_dir)?;
let _dontdrop = mount::AutoMountExt4::try_new(defs::MODULE_UPDATE_TMP_IMG, update_dir, true)?;
// call the operation func
let result = func(id, update_dir);
// umount modules_update.img
let _ = umount_dir(update_dir);
let _ = remove_dir_all(update_dir);
std::fs::rename(modules_update_tmp_img, defs::MODULE_UPDATE_IMG)?;
if let Err(e) = std::fs::rename(modules_update_tmp_img, defs::MODULE_UPDATE_IMG) {
warn!("Rename image failed: {e}, try copy it.");
std::fs::copy(modules_update_tmp_img, defs::MODULE_UPDATE_IMG)
.with_context(|| "Failed to copy image.".to_string())?;
let _ = std::fs::remove_file(modules_update_tmp_img);
}
mark_update()?;
result
}
pub fn uninstall_module(id: String) -> Result<()> {
do_module_update(defs::MODULE_UPDATE_TMP_DIR, &id, |mid, update_dir| {
pub fn uninstall_module(id: &str) -> Result<()> {
update_module(defs::MODULE_UPDATE_TMP_DIR, id, |mid, update_dir| {
let dir = Path::new(update_dir);
if !dir.exists() {
bail!("No module installed");
}
ensure!(dir.exists(), "No module installed");
// iterate the modules_update dir, find the module to be removed
let dir = std::fs::read_dir(dir)?;
@@ -563,24 +587,22 @@ pub fn uninstall_module(id: String) -> Result<()> {
}
// santity check
let target_module_path = format!("{}/{}", update_dir, mid);
let target_module_path = format!("{update_dir}/{mid}");
let target_module = Path::new(&target_module_path);
if target_module.exists() {
remove_dir_all(target_module)?;
}
let _ = mark_module_state(&id, defs::REMOVE_FILE_NAME, true);
let _ = mark_module_state(id, defs::REMOVE_FILE_NAME, true);
Ok(())
})
}
fn do_enable_module(module_dir: &str, mid: &str, enable: bool) -> Result<()> {
let src_module_path = format!("{}/{}", module_dir, mid);
fn _enable_module(module_dir: &str, mid: &str, enable: bool) -> Result<()> {
let src_module_path = format!("{module_dir}/{mid}");
let src_module = Path::new(&src_module_path);
if !src_module.exists() {
bail!("module: {} not found!", mid);
}
ensure!(src_module.exists(), "module: {} not found!", mid);
let disable_path = src_module.join(defs::DISABLE_FILE_NAME);
if enable {
@@ -589,8 +611,8 @@ fn do_enable_module(module_dir: &str, mid: &str, enable: bool) -> Result<()> {
format!("Failed to remove disable file: {}", &disable_path.display())
})?;
}
} else if !disable_path.exists() {
std::fs::File::create(disable_path)?;
} else {
ensure_file_exists(disable_path)?;
}
let _ = mark_module_state(mid, defs::DISABLE_FILE_NAME, !enable);
@@ -598,19 +620,33 @@ fn do_enable_module(module_dir: &str, mid: &str, enable: bool) -> Result<()> {
Ok(())
}
pub fn enable_module(id: String) -> Result<()> {
do_module_update(defs::MODULE_UPDATE_TMP_DIR, &id, |mid, update_dir| {
do_enable_module(update_dir, mid, true)
pub fn enable_module(id: &str) -> Result<()> {
update_module(defs::MODULE_UPDATE_TMP_DIR, id, |mid, update_dir| {
_enable_module(update_dir, mid, true)
})
}
pub fn disable_module(id: String) -> Result<()> {
do_module_update(defs::MODULE_UPDATE_TMP_DIR, &id, |mid, update_dir| {
do_enable_module(update_dir, mid, false)
pub fn disable_module(id: &str) -> Result<()> {
update_module(defs::MODULE_UPDATE_TMP_DIR, id, |mid, update_dir| {
_enable_module(update_dir, mid, false)
})
}
fn do_list_modules(path: &str) -> Vec<HashMap<String, String>> {
pub fn disable_all_modules() -> Result<()> {
// we assume the module dir is already mounted
let dir = std::fs::read_dir(defs::MODULE_DIR)?;
for entry in dir.flatten() {
let path = entry.path();
let disable_flag = path.join(defs::DISABLE_FILE_NAME);
if let Err(e) = ensure_file_exists(disable_flag) {
warn!("Failed to disable module: {}: {}", path.display(), e);
}
}
Ok(())
}
fn _list_modules(path: &str) -> Vec<HashMap<String, String>> {
// first check enabled modules
let dir = std::fs::read_dir(path);
let Ok(dir) = dir else {
@@ -631,7 +667,7 @@ fn do_list_modules(path: &str) -> Vec<HashMap<String, String>> {
warn!("Failed to read file: {}", module_prop.display());
continue;
};
let mut module_prop_map = HashMap::new();
let mut module_prop_map: HashMap<String, String> = HashMap::new();
let encoding = encoding::all::UTF_8;
let result =
PropertiesIter::new_with_encoding(Cursor::new(content), encoding).read_into(|k, v| {
@@ -643,9 +679,9 @@ fn do_list_modules(path: &str) -> Vec<HashMap<String, String>> {
let update = path.join(defs::UPDATE_FILE_NAME).exists();
let remove = path.join(defs::REMOVE_FILE_NAME).exists();
module_prop_map.insert("enabled".to_string(), enabled.to_string());
module_prop_map.insert("update".to_string(), update.to_string());
module_prop_map.insert("remove".to_string(), remove.to_string());
module_prop_map.insert("enabled".to_owned(), enabled.to_string());
module_prop_map.insert("update".to_owned(), update.to_string());
module_prop_map.insert("remove".to_owned(), remove.to_string());
if result.is_err() {
warn!("Failed to parse module.prop: {}", module_prop.display());
@@ -658,7 +694,7 @@ fn do_list_modules(path: &str) -> Vec<HashMap<String, String>> {
}
pub fn list_modules() -> Result<()> {
let modules = do_list_modules(defs::MODULE_DIR);
let modules = _list_modules(defs::MODULE_DIR);
println!("{}", serde_json::to_string_pretty(&modules)?);
Ok(())
}

343
userspace/ksud/src/mount.rs Normal file
View File

@@ -0,0 +1,343 @@
use anyhow::{Ok, Result};
#[cfg(any(target_os = "linux", target_os = "android"))]
use anyhow::Context;
#[cfg(any(target_os = "linux", target_os = "android"))]
use retry::delay::NoDelay;
#[cfg(any(target_os = "linux", target_os = "android"))]
use sys_mount::{unmount, FilesystemType, Mount, MountFlags, Unmount, UnmountFlags};
#[cfg(any(target_os = "linux", target_os = "android"))]
use procfs::process::{MountInfo, Process};
#[cfg(any(target_os = "linux", target_os = "android"))]
use std::collections::HashSet;
pub struct AutoMountExt4 {
mnt: String,
#[cfg(any(target_os = "linux", target_os = "android"))]
mount: Option<Mount>,
auto_umount: bool,
}
impl AutoMountExt4 {
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn try_new(src: &str, mnt: &str, auto_umount: bool) -> Result<Self> {
let result = Mount::builder()
.fstype(FilesystemType::from("ext4"))
.flags(MountFlags::empty())
.mount(src, mnt)
.map(|mount| {
Ok(Self {
mnt: mnt.to_string(),
mount: Some(mount),
auto_umount,
})
});
if let Err(e) = result {
println!("- Mount failed: {e}, retry with system mount");
let result = std::process::Command::new("mount")
.arg("-t")
.arg("ext4")
.arg(src)
.arg(mnt)
.status();
if let Err(e) = result {
Err(anyhow::anyhow!(
"mount partition: {src} -> {mnt} failed: {e}"
))
} else {
Ok(Self {
mnt: mnt.to_string(),
mount: None,
auto_umount,
})
}
} else {
result.unwrap()
}
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn try_new(_src: &str, _mnt: &str, _auto_umount: bool) -> Result<Self> {
unimplemented!()
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn umount(&self) -> Result<()> {
if let Some(ref mount) = self.mount {
mount
.unmount(UnmountFlags::empty())
.map_err(|e| anyhow::anyhow!(e))
} else {
let result = std::process::Command::new("umount").arg(&self.mnt).status();
if let Err(e) = result {
Err(anyhow::anyhow!("umount: {} failed: {e}", self.mnt))
} else {
Ok(())
}
}
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
impl Drop for AutoMountExt4 {
fn drop(&mut self) {
log::info!(
"AutoMountExt4 drop: {}, auto_umount: {}",
self.mnt,
self.auto_umount
);
if self.auto_umount {
let _ = self.umount();
}
}
}
#[allow(dead_code)]
#[cfg(any(target_os = "linux", target_os = "android"))]
fn mount_image(src: &str, target: &str, autodrop: bool) -> Result<()> {
if autodrop {
Mount::builder()
.fstype(FilesystemType::from("ext4"))
.mount_autodrop(src, target, UnmountFlags::empty())
.with_context(|| format!("Failed to do mount: {src} -> {target}"))?;
} else {
Mount::builder()
.fstype(FilesystemType::from("ext4"))
.mount(src, target)
.with_context(|| format!("Failed to do mount: {src} -> {target}"))?;
}
Ok(())
}
#[allow(dead_code)]
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn mount_ext4(src: &str, target: &str, autodrop: bool) -> Result<()> {
// umount target first.
let _ = umount_dir(target);
let result = retry::retry(NoDelay.take(3), || mount_image(src, target, autodrop));
result
.map_err(|e| anyhow::anyhow!("mount partition: {src} -> {target} failed: {e}"))
.map(|_| ())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn umount_dir(src: &str) -> Result<()> {
unmount(src, UnmountFlags::empty()).with_context(|| format!("Failed to umount {src}"))?;
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn mount_overlay(lowerdir: &str, mnt: &str) -> Result<()> {
Mount::builder()
.fstype(FilesystemType::from("overlay"))
.flags(MountFlags::RDONLY)
.data(&format!("lowerdir={lowerdir}"))
.mount("overlay", mnt)
.map(|_| ())
.map_err(|e| anyhow::anyhow!("mount partition: {mnt} overlay failed: {e}"))
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn mount_ext4(_src: &str, _target: &str, _autodrop: bool) -> Result<()> {
unimplemented!()
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn umount_dir(_src: &str) -> Result<()> {
unimplemented!()
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn mount_overlay(_lowerdir: &str, _mnt: &str) -> Result<()> {
unimplemented!()
}
pub struct StockOverlay {
#[cfg(any(target_os = "linux", target_os = "android"))]
mountinfos: Vec<MountInfo>,
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
impl StockOverlay {
pub fn new() -> Self {
unimplemented!()
}
pub fn mount_all(&self) {
unimplemented!()
}
pub fn umount_all(&self) {
unimplemented!()
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
impl StockOverlay {
pub fn new() -> Self {
if let std::result::Result::Ok(process) = Process::myself() {
if let std::result::Result::Ok(mountinfos) = process.mountinfo() {
let overlay_mounts = mountinfos
.into_iter()
.filter(|m| m.fs_type == "overlay")
.collect::<Vec<_>>();
return Self {
mountinfos: overlay_mounts,
};
}
}
Self { mountinfos: vec![] }
}
pub fn mount_all(&self) {
log::info!("stock overlay: mount all: {:?}", self.mountinfos);
for mount in self.mountinfos.clone() {
let Some(mnt) = mount.mount_point.to_str() else {
log::warn!("Failed to get mount point");
continue;
};
if mnt == "/system" {
log::warn!("stock overlay found /system, skip!");
continue;
}
let (_flags, b): (HashSet<_>, HashSet<_>) = mount
.mount_options
.into_iter()
.chain(mount.super_options)
.partition(|(_, m)| m.is_none());
let mut overlay_opts = vec![];
for (opt, val) in b {
if let Some(val) = val {
overlay_opts.push(format!("{opt}={val}"));
} else {
log::warn!("opt empty: {}", opt);
}
}
let overlay_data = overlay_opts.join(",");
let result = Mount::builder()
.fstype(FilesystemType::from("overlay"))
.flags(MountFlags::RDONLY)
.data(&overlay_data)
.mount("overlay", mnt);
if let Err(e) = result {
log::error!(
"stock mount overlay: {} failed: {}",
mount.mount_point.display(),
e
);
} else {
log::info!(
"stock mount :{} overlay_opts: {}",
mount.mount_point.display(),
overlay_opts.join(",")
);
}
}
}
pub fn umount_all(&self) {
log::info!("stock overlay: umount all: {:?}", self.mountinfos);
for mnt in &self.mountinfos {
let Some(p) = mnt.mount_point.to_str() else {
log::warn!("Failed to umount: {}", mnt.mount_point.display());
continue;
};
let result = umount_dir(p);
log::info!("stock umount {}: {:?}", p, result);
}
}
}
// some ROMs mount device(ext4,exfat) to /vendor, when we do overlay mount, it will overlay
// the stock mounts, these mounts include bt_firmware, wifi_firmware, etc.
// so we to remount these mounts when we do overlay mount.
// this is a workaround, we should find a better way to do this.
#[derive(Debug)]
pub struct StockMount {
mnt: String,
#[cfg(any(target_os = "linux", target_os = "android"))]
mountlist: proc_mounts::MountList,
}
#[cfg(any(target_os = "linux", target_os = "android"))]
impl StockMount {
pub fn new(mnt: &str) -> Result<Self> {
let mountlist = proc_mounts::MountList::new()?;
Ok(Self {
mnt: mnt.to_string(),
mountlist,
})
}
fn get_target_mounts(&self) -> Vec<&proc_mounts::MountInfo> {
let mounts = self
.mountlist
.destination_starts_with(std::path::Path::new(&self.mnt))
.filter(|m| m.fstype != "overlay");
mounts.collect()
}
pub fn umount(&self) -> Result<()> {
let mounts = self.get_target_mounts();
log::info!("stock mount for {} : {:?}", self.mnt, mounts);
for m in mounts {
let dst = m
.dest
.to_str()
.ok_or(anyhow::anyhow!("Failed to get dst"))?;
umount_dir(dst)?;
log::info!("umount: {:?}", m);
}
Ok(())
}
pub fn remount(&self) -> Result<()> {
let mounts = self.get_target_mounts();
for m in mounts {
let src = std::fs::canonicalize(&m.source)?;
let src = src.to_str().ok_or(anyhow::anyhow!("Failed to get src"))?;
let dst = m
.dest
.to_str()
.ok_or(anyhow::anyhow!("Failed to get dst"))?;
let fstype = m.fstype.as_str();
let options = m.options.join(",");
log::info!("mount: {:?}", m);
std::process::Command::new("mount")
.arg("-t")
.arg(fstype)
.arg("-o")
.arg(options)
.arg(src)
.arg(dst)
.status()
.with_context(|| format!("Failed to mount {:?}", m))?;
}
Ok(())
}
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
impl StockMount {
pub fn new(mnt: &str) -> Result<Self> {
Ok(Self {
mnt: mnt.to_string(),
})
}
pub fn umount(&self) -> Result<()> {
unimplemented!()
}
pub fn remount(&self) -> Result<()> {
unimplemented!()
}
}

Binary file not shown.

View File

@@ -1,31 +1,44 @@
use anyhow::{Context, Ok, Result};
use extattr::{setxattr, Flags as XattrFlags};
use anyhow::Result;
use jwalk::{Parallelism::Serial, WalkDir};
use std::path::Path;
#[cfg(any(target_os = "linux", target_os = "android"))]
use anyhow::{Context, Ok};
#[cfg(any(target_os = "linux", target_os = "android"))]
use extattr::{setxattr, Flags as XattrFlags};
const SYSTEM_CON: &str = "u:object_r:system_file:s0";
const _ADB_CON: &str = "u:object_r:adb_data_file:s0";
const SELINUX_XATTR: &str = "security.selinux";
pub fn setcon(path: &str, con: &str) -> Result<()> {
setxattr(path, "security.selinux", con, XattrFlags::empty())
.with_context(|| format!("Failed to change SELinux context for {path}"))?;
pub fn setcon<P: AsRef<Path>>(path: P, con: &str) -> Result<()> {
#[cfg(any(target_os = "linux", target_os = "android"))]
setxattr(&path, SELINUX_XATTR, con, XattrFlags::empty()).with_context(|| {
format!(
"Failed to change SELinux context for {}",
path.as_ref().display()
)
})?;
Ok(())
}
pub fn setsyscon(path: &str) -> Result<()> {
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn setsyscon<P: AsRef<Path>>(path: P) -> Result<()> {
setcon(path, SYSTEM_CON)
}
pub fn restore_syscon(dir: &str) -> Result<()> {
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn setsyscon<P: AsRef<Path>>(path: P) -> Result<()> {
unimplemented!()
}
pub fn restore_syscon<P: AsRef<Path>>(dir: P) -> Result<()> {
for dir_entry in WalkDir::new(dir).parallelism(Serial) {
if let Some(path) = dir_entry.ok().map(|dir_entry| dir_entry.path()) {
setxattr(&path, "security.selinux", SYSTEM_CON, XattrFlags::empty()).with_context(
|| {
format!(
"Failed to change SELinux context for {}",
path.to_str().unwrap()
)
},
)?;
#[cfg(any(target_os = "linux", target_os = "android"))]
setxattr(&path, SELINUX_XATTR, SYSTEM_CON, XattrFlags::empty()).with_context(|| {
format!("Failed to change SELinux context for {}", path.display())
})?;
}
}
Ok(())

View File

@@ -1,4 +1,4 @@
use anyhow::Result;
use anyhow::{bail, Result};
use derive_new::new;
use nom::{
branch::alt,
@@ -11,7 +11,7 @@ use nom::{
sequence::Tuple,
IResult, Parser,
};
use std::{vec, path::Path};
use std::{path::Path, vec};
type SeObject<'a> = Vec<&'a str>;
@@ -345,15 +345,20 @@ impl<'a> PolicyStatement<'a> {
}
}
fn parse_sepolicy<'a, 'b>(input: &'b str) -> Result<Vec<PolicyStatement<'a>>>
fn parse_sepolicy<'a, 'b>(input: &'b str, strict: bool) -> Result<Vec<PolicyStatement<'a>>>
where
'b: 'a,
{
let mut statements = vec![];
for line in input.split(['\n', ';']) {
if line.trim().is_empty() {
continue;
}
if let Ok((_, statement)) = PolicyStatement::parse(line.trim()) {
statements.push(statement);
} else if strict {
bail!("Failed to parse policy statement: {}", line)
}
}
Ok(statements)
@@ -401,6 +406,7 @@ impl TryFrom<&str> for PolicyObject {
/// normal statement would be expand to atomic statement, for example:
/// allow domain1 domain2:file1 { read write }; would be expand to two atomic statement
/// allow domain1 domain2:file1 read;allow domain1 domain2:file1 write;
#[allow(clippy::too_many_arguments)]
#[derive(Debug, new)]
struct AtomicStatement {
cmd: u32,
@@ -552,8 +558,7 @@ impl<'a> TryFrom<&'a TypeAttr<'a>> for Vec<AtomicStatement> {
impl<'a> TryFrom<&'a Attr<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a Attr<'a>) -> Result<Self> {
let mut result = vec![];
result.push(AtomicStatement {
let result = vec![AtomicStatement {
cmd: CMD_ATTR,
subcmd: 0,
sepol1: perm.name.try_into()?,
@@ -563,7 +568,7 @@ impl<'a> TryFrom<&'a Attr<'a>> for Vec<AtomicStatement> {
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
}];
Ok(result)
}
}
@@ -670,9 +675,8 @@ struct FfiPolicy {
fn to_c_ptr(pol: &PolicyObject) -> *const libc::c_char {
match pol {
PolicyObject::None => std::ptr::null(),
PolicyObject::All => std::ptr::null(),
PolicyObject::One(s) => s.as_ptr() as *const libc::c_char,
PolicyObject::None | PolicyObject::All => std::ptr::null(),
PolicyObject::One(s) => s.as_ptr().cast::<libc::c_char>(),
}
}
@@ -692,35 +696,45 @@ impl From<AtomicStatement> for FfiPolicy {
}
}
fn apply_one_rule<'a>(statement: &'a PolicyStatement<'a>) -> Result<()> {
#[cfg(any(target_os = "linux", target_os = "android"))]
fn apply_one_rule<'a>(statement: &'a PolicyStatement<'a>, strict: bool) -> Result<()> {
let policies: Vec<AtomicStatement> = statement.try_into()?;
for policy in policies {
let mut result: u32 = 0;
let cpolicy = FfiPolicy::from(policy);
unsafe {
#[allow(clippy::cast_possible_wrap)]
libc::prctl(
crate::ksu::KERNEL_SU_OPTION as i32,
crate::ksu::KERNEL_SU_OPTION as i32, // supposed to overflow
crate::ksu::CMD_SET_SEPOLICY,
0,
&cpolicy as *const _ as *const libc::c_void,
&mut result as *mut _ as *mut libc::c_void,
std::ptr::addr_of!(cpolicy).cast::<libc::c_void>(),
std::ptr::addr_of_mut!(result).cast::<libc::c_void>(),
);
}
if result != crate::ksu::KERNEL_SU_OPTION {
log::warn!("apply rule failed: {result}");
log::warn!("apply rule: {:?} failed.", statement);
if strict {
return Err(anyhow::anyhow!("apply rule {:?} failed.", statement));
}
}
}
Ok(())
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn apply_one_rule<'a>(_statement: &'a PolicyStatement<'a>, _strict: bool) -> Result<()> {
unimplemented!()
}
pub fn live_patch(policy: &str) -> Result<()> {
let result = parse_sepolicy(policy.trim())?;
let result = parse_sepolicy(policy.trim(), false)?;
for statement in result {
println!("{statement:?}");
apply_one_rule(&statement)?;
apply_one_rule(&statement, false)?;
}
Ok(())
}
@@ -729,3 +743,17 @@ pub fn apply_file<P: AsRef<Path>>(path: P) -> Result<()> {
let input = std::fs::read_to_string(path)?;
live_patch(&input)
}
pub fn check_rule(policy: &str) -> Result<()> {
let path = Path::new(policy);
let policy = if path.exists() {
std::fs::read_to_string(path)?
} else {
policy.to_string()
};
let result = parse_sepolicy(policy.trim(), true)?;
for statement in result {
apply_one_rule(&statement, true)?;
}
Ok(())
}

View File

@@ -1,49 +1,92 @@
use std::path::Path;
use anyhow::{bail, Context, Error, Ok, Result};
use std::{
fs::{create_dir_all, write, File},
io::ErrorKind::AlreadyExists,
path::Path,
};
use anyhow::{ensure, Context, Result};
use retry::delay::NoDelay;
use sys_mount::{unmount, FilesystemType, Mount, UnmountFlags};
fn do_mount_image(src: &str, target: &str) -> Result<()> {
Mount::builder()
.fstype(FilesystemType::from("ext4"))
.mount(src, target)
.with_context(|| format!("Failed to do mount: {src} -> {target}"))?;
Ok(())
}
pub fn mount_image(src: &str, target: &str) -> Result<()> {
// umount target first.
let _ = umount_dir(target);
let result = retry::retry(NoDelay.take(3), || do_mount_image(src, target));
ensure!(result.is_ok(), "Failed to mount {} -> {}", src, target);
Ok(())
}
pub fn umount_dir(src: &str) -> Result<()> {
unmount(src, UnmountFlags::empty()).with_context(|| format!("Failed to umount {src}"))?;
Ok(())
}
#[allow(unused_imports)]
use std::fs::{set_permissions, Permissions};
#[cfg(unix)]
use std::os::unix::prelude::PermissionsExt;
pub fn ensure_clean_dir(dir: &str) -> Result<()> {
let path = Path::new(dir);
log::debug!("ensure_clean_dir: {}", path.display());
if path.exists() {
log::debug!("ensure_clean_dir: {} exists, remove it", path.display());
std::fs::remove_dir_all(path)?;
}
Ok(std::fs::create_dir_all(path)?)
}
pub fn ensure_file_exists<T: AsRef<Path>>(file: T) -> Result<()> {
match File::options().write(true).create_new(true).open(&file) {
std::result::Result::Ok(_) => Ok(()),
Err(err) => {
if err.kind() == AlreadyExists && file.as_ref().is_file() {
Ok(())
} else {
Err(Error::from(err))
.with_context(|| format!("{} is not a regular file", file.as_ref().display()))
}
}
}
}
pub fn ensure_dir_exists<T: AsRef<Path>>(dir: T) -> Result<()> {
let result = create_dir_all(&dir).map_err(Error::from);
if dir.as_ref().is_dir() {
result
} else if result.is_ok() {
bail!("{} is not a regular directory", dir.as_ref().display())
} else {
result
}
}
pub fn ensure_binary<T: AsRef<Path>>(path: T, contents: &[u8]) -> Result<()> {
if path.as_ref().exists() {
return Ok(());
}
ensure_dir_exists(path.as_ref().parent().ok_or_else(|| {
anyhow::anyhow!(
"{} does not have parent directory",
path.as_ref().to_string_lossy()
)
})?)?;
write(&path, contents)?;
#[cfg(unix)]
set_permissions(&path, Permissions::from_mode(0o755))?;
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn getprop(prop: &str) -> Option<String> {
android_properties::getprop(prop).value()
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn getprop(_prop: &str) -> Option<String> {
unimplemented!()
}
pub fn is_safe_mode() -> bool {
getprop("persist.sys.safemode")
let safemode = getprop("persist.sys.safemode")
.filter(|prop| prop == "1")
.is_some()
|| getprop("ro.sys.safemode")
.filter(|prop| prop == "1")
.is_some()
.is_some();
log::info!("safemode: {}", safemode);
if safemode {
return true;
}
let safemode = crate::ksu::check_kernel_safemode();
log::info!("kernel_safemode: {}", safemode);
safemode
}
pub fn get_zip_uncompressed_size(zip_path: &str) -> Result<u64> {
@@ -53,3 +96,32 @@ pub fn get_zip_uncompressed_size(zip_path: &str) -> Result<u64> {
.sum();
Ok(total)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn switch_mnt_ns(pid: i32) -> Result<()> {
use anyhow::ensure;
use std::os::fd::AsRawFd;
let path = format!("/proc/{}/ns/mnt", pid);
let fd = std::fs::File::open(path)?;
let ret = unsafe { libc::setns(fd.as_raw_fd(), libc::CLONE_NEWNS) };
ensure!(ret == 0, "switch mnt ns failed");
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn unshare_mnt_ns() -> Result<()> {
use anyhow::ensure;
let ret = unsafe { libc::unshare(libc::CLONE_NEWNS) };
ensure!(ret == 0, "unshare mnt ns failed");
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn umask(mask: u32) {
unsafe { libc::umask(mask) };
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn umask(_mask: u32) {
unimplemented!("umask is not supported on this platform")
}

View File

@@ -49,6 +49,7 @@ function sidebarGuide() {
{ text: 'Instalasi', link: '/id_ID/guide/installation' },
{ text: 'Bagaimana cara buildnya?', link: '/id_ID/guide/how-to-build' },
{ text: 'Integrasi untuk perangkat non-GKI', link: '/id_ID/guide/how-to-integrate-for-non-gki'},
{ text: 'Perangkat yang didukung secara tidak resmi', link: '/id_ID/guide/unofficially-support-devices.md' },
{ text: 'FAQ', link: '/id_ID/guide/faq' },
]
}

View File

@@ -12,11 +12,11 @@ Certainly, yes.
## Does KernelSU support modules?
Yes, But it is in early version, may be buggy. Please waiting it to be stable :)
Yes, But it is in early version, it may be buggy. Please wait for it to be stable :)
## Does KernelSU support Xposed?
Yes, [Dreamland](https://github.com/canyie/Dreamland) and [TaiChi](https::/taichi.cool) partially works now, And we are trying to make other Xposed Framework work.
Yes, [Dreamland](https://github.com/canyie/Dreamland) and [TaiChi](https::/taichi.cool) partially works now. For LSPosed, you can follow [Zygisk on KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU)
## Is KernelSU compatible with Magisk?
@@ -46,3 +46,19 @@ It is possible, KernelSU is backported to kernel 4.14 now, for older kernel, you
## How to integrate KernelSU for old kernel?
Please refer [guide](how-to-integrate-for-non-gki)
## Why my Android version is 13, and the kernel shows "android12-5.10"?
The Kernel version has nothing to do with Android version, if you need to flash kernel, always use the kernel version, Android version is not so important.
## Is there any --mount-master/global mount namespace in KernelSU?
There isn't now(maybe in the future), But you can use `nsenter -t 1 -m sh` to enter global mount namespace instead.
## Does KernelSU support Zygisk?
KernelSU has no builtin Zygisk support, but you can use [Zygisk on KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU) instead.
## I am GKI1.0, can i use this?
GKI1 is completely different from GKI2, you must compile kernel by yourself.

View File

@@ -37,7 +37,7 @@ But if you encounter a boot loop when integrated KernelSU, it is maybe *kprobe i
:::tip How to check if kprobe is broken
comment out `ksu_enable_sucompat()` and `ksu_enable_ksud()` in `KernelSU/kernel/ksu.c`, the the device boot normally, then kprobe may be broken.
comment out `ksu_enable_sucompat()` and `ksu_enable_ksud()` in `KernelSU/kernel/ksu.c`, if the device boots normally, then kprobe may be broken.
:::
## Manully modify the kernel source
@@ -142,4 +142,85 @@ You should found the four functions in kernel source:
3. vfs_read, usually in `fs/read_write.c`
4. vfs_statx, usually in `fs/stat.c`
If your kernel does not have the `vfs_statx`, use `vfs_fstatat` instead:
```diff
diff --git a/fs/stat.c b/fs/stat.c
index 068fdbcc9e26..5348b7bb9db2 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
}
EXPORT_SYMBOL(vfs_fstat);
+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
+
int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
int flag)
{
@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
int error = -EINVAL;
unsigned int lookup_flags = 0;
+ ksu_handle_stat(&dfd, &filename, &flag);
+
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
AT_EMPTY_PATH)) != 0)
goto out;
```
For kernels eariler than 4.17, if you cannot find `do_faccessat`, just go to the definition of the `faccessat` syscall and place the call there:
```diff
diff --git a/fs/open.c b/fs/open.c
index 2ff887661237..e758d7db7663 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
return error;
}
+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
+ int *flags);
+
/*
* access() needs to use the real uid/gid, not the effective uid/gid.
* We do this by temporarily clearing all FS-related capabilities and
@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
int res;
unsigned int lookup_flags = LOOKUP_FOLLOW;
+ ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;
```
To enable KernelSU's builtin SafeMode, You should also modify `input_handle_event` in `drivers/input/input.c`:
:::tip
It is strongly recommended to enable this feature, it is very helpful for recusing from bootloop!
:::
```diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 45306f9ef247..815091ebfca4 100755
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
return disposition;
}
+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
+
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = input_get_disposition(dev, type, code, &value);
+ ksu_handle_input_handle_event(&type, &code, &value);
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
add_input_randomness(type, code, value);
```
Finally, build your kernel again, KernelSU should works well.

View File

@@ -2,31 +2,167 @@
## Check if your device is supported
Download the KernelSU manager app from [github releases](https://github.com/tiann/KernelSU/releases) or [github actions](https://github.com/tiann/KernelSU/actions/workflows/build-manager.yml), and then install the app to device and open the app:
Download KernelSU manager APP from [GitHub Releases](https://github.com/tiann/KernelSU/releases) or [Coolapk market](https://www.coolapk.com/apk/me.weishu.kernelsu), and install it to your device:
- If the app shows `Unsupported`, it means **You should compile the kernel yourself**, KernelSU won't and never provide a boot image for you to flash.
- If the app shows `Not installed`, then your devices is officially supported by KernelSU.
## Find proper boot.img
:::info
For devices showing `Unsupported`, here is an [Unofficially-support-devices](unofficially-support-devices.md), you can compile the kernel yourself.
:::
KernelSU provides a general boot.img for GKI devices, you should flash the boot.img to the boot partition of your device.
## Backup stock boot.img
You can download the boot.img from [github actions for kernel](https://github.com/tiann/KernelSU/actions/workflows/build-kernel.yml), Please be aware that your should use the right version of boot.img. For example, if your device show that the kernel is `5.10.101`, then you need to download the `5.10.101-xxxx.boot.xxx`.
Before flashing, you must first backup your stock boot.img. If you encounter any bootloop, you can always restore the system by flashing back to the stock factory boot using fastboot.
And also, please check your stock boot.img's format, you should use the right format, such as `lz4``gz`.
::: warning
Flashing may cause data loss, be sure to do this step well before proceeding to the next step!! You can also back up all the data on your phone if necessary.
:::
## Flash the boot.img to device
## Necessary knowledge
Connect your device with `adb` and then execute `adb reboot bootloader` to enter fastboot mode, and then use this command to flash KernelSU:
### ADB and fastboot
By default, you will use ADB and fastboot tools in this tutorial, so if you don't know them, we recommend using a search engine to learn about them first.
### KMI
Kernel Module Interface (KMI), kernel versions with the same KMI are **compatible** This is what "genernal" means in GKI; conversely, if the KMI is different, then these kernels are not compatible with each other, and brushing in a kernel image with a different KMI than your device may cause a bootloop.
Specifically, for GKI devices, the kernel version format should be as follows:
```txt
KernelRelease :=
Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
w .x .y -zzz -k -something
```
`w.x-zzz-k` is the KMI version. For example, if a device kernel version is `5.10.101-android12-9-g30979850fc20`, then its KMI is `5.10-android12-9`; theoretically, it can boot up normally with other KMI kernels.
::: tip
Note that the SubLevel in the kernel version is not part of the KMI! That means that `5.10.101-android12-9-g30979850fc20` has the same KMI as `5.10.137-android12-9-g30979850fc20`!
:::
### Kernel version vs. Android version
Please note: **Kernel version and Android version are not necessarily the same!**
If you find that your kernel version is `android12-5.10.101`, but your Android system version is Android 13 or other; please don't be surprised, because the version number of the Android system is not necessarily the same as the version number of the Linux kernel; The version number of the Linux kernel is generally consistent with the version of the Android system that comes with the **device when it is shipped**. If the Android system is upgraded later, the kernel version will generally not change. If you need to flash, **please always refer to the kernel version!!**
## Introduction
There are several installation methods for KernelSU, each suitable for a different scenario, so please choose as needed.
1. Install with custom Recovery (e.g. TWRP)
2. Install with a kernel flash app, such as Franco Kernel Manager
3. Install with fastboot using the boot.img provided by KernelSU
4. Repair the boot.img manually and install it
## Install with custom Recovery
Prerequisite: Your device must have a custom Recovery, such as TWRP; if not or only official Recovery is available, use another method.
Step:
1. From the [Release page](https://github.com/tiann/KernelSU/releases) of KernelSU, download the zip package starting with AnyKernel3 that matches your phone version; for example, the phone kernel version is `android12-5.10. 66`, then you should download the file `AnyKernel3-android12-5.10.66_yyyy-MM.zip` (where `yyyy` is the year and `MM` is the month).
2. Reboot the phone into TWRP.
3. Use adb to put AnyKernel3-*.zip into the phone /sdcard and choose to install it in the TWRP GUI; or you can directly `adb sideload AnyKernel-*.zip` to install.
PS. This method is suitable for any installation (not limited to initial installation or subsequent upgrades), as long as you use TWRP.
## Install with Kernel Flasher
Prerequisite: Your device must be rooted. For example, you have installed Magisk to get root, or you have installed an old version of KernelSU and need to upgrade to another version of KernelSU; if your device is not rooted, please try other methods.
Step:
1. Download the AnyKernel3 zip; refer to the section *Installing with Custom Recovery* for downloading instructions.
2. Open the Kernel Flash App and use the provided AnyKernel3 zip to flash.
If you haven't used the Kernel flash App before, the following are the more popular ones.
1. [Franco Kernel Manager](https://play.google.com/store/apps/details?id=com.franco.kernel)
2. [Ex Kernel Manager](https://play.google.com/store/apps/details?id=flar2.exkernelmanager)
PS. This method is more convenient when upgrading KernelSU and can be done without a computer (backup first!). .
## Install with boot.img provided by KernelSU
This method does not require you to have TWRP, nor does it require your phone to have root privileges; it is suitable for your first installation of KernelSU.
### Find proper boot.img
KernelSU provides a generic boot.img for GKI devices and you should flush the boot.img to the boot partition of the device.
You can download boot.img from [GitHub Release](https://github.com/tiann/KernelSU/releases), please note that you should use the correct version of boot.img. For example, if your device displays the kernel `android12-5.10.101` , you need to download `android-5.10.101_yyyy-MM.boot-<format>.img`. , you need to download `android-5.10.101_yyyy-MM.boot-<format>.img`.(Keep KMI consistent!)
Where `<format>` refers to the kernel compression format of your official boot.img, please check the kernel compression format of your original boot.img, you should use the correct format, e.g. `lz4`, `gz`; if you use an incorrect compression format, you may encounter bootloop.
::: info
1. You can use magiskboot to get the compression format of your original boot; of course you can also ask other, more experienced kids with the same model as your device. Also, the compression format of the kernel usually does not change, so if you boot successfully with a certain compression format, you can try that format later.
2. Xiaomi devices usually use `gz` or **uncompressed**.
3. For Pixel devices, follow below instructions.
:::
### flash boot.img to device
Use `adb` to connect your device, then execute `adb reboot bootloader` to enter fastboot mode, then use this command to flash KernelSU:
```sh
fastboot flash boot boot.img
```
## Reboot
::: info
If your device supports `fastboot boot`, you can first use `fastboot boot boot.img` to try to use boot.img to boot the system first. If something unexpected happens, restart it again to boot.
:::
When flashed, you should reboot your device:
### reboot
After flashing is complete, you should reboot your device:
```sh
fastboot reboot
```
## Patch boot.img manully
For some devices, the boot.img format is not so common, such as not `lz4`, `gz` and uncompressed; the most typical is Pixel, its boot.img format is `lz4_legacy` compressed, ramdisk may be `gz` may also be `lz4_legacy` compression; at this time, if you directly flash the boot.img provided by KernelSU, the phone may not be able to boot; at this time, you can manually patch the boot.img to achieve.
There are generally two patch methods:
1. [Android-Image-Kitchen](https://forum.xda-developers.com/t/tool-android-image-kitchen-unpack-repack-kernel-ramdisk-win-android-linux-mac.2073775/)
2. [magiskboot](https://github.com/topjohnwu/Magisk/releases)
Among them, Android-Image-Kitchen is suitable for operation on PC, and magiskboot needs the cooperation of mobile phone.
### Preparation
1. Get your phone's stock boot.img; you can get it from your device manufacturers, you may need [payload-dumper-go](https://github.com/ssut/payload-dumper-go)
2. Download the AnyKernel3 zip file provided by KernelSU that matches the KMI version of your device (you can refer to the *Install with custom Recovery*).
3. Unpack the AnyKernel3 package and get the `Image` file, which is the kernel file of KernelSU.
### Using Android-Image-Kitchen
1. Download Android-Image-Kitchen to your computer.
2. Put stock boot.img to Android-Image-Kitchen's root folder.
3. Execute `./unpackimg.sh boot.img` at root directory of Android-Image-Kitchen, this command would unpack boot.img and you will get some files.
4. Replace `boot.img-kernel` in the `split_img` directory with the `Image` you extracted from AnyKernel3 (note the name change to boot.img-kernel).
5. Execute `./repackimg.sh` at root directory of 在 Android-Image-Kitchen; And you will get a file named `image-new.img`; Flash this boot.img by fastboot(Refer to the previous section).
### Using magiskboot
1. Download latest Magisk from [Release Page](https://github.com/topjohnwu/Magisk/releases)
2. Rename Magisk-*.apk to Magisk-vesion.zip and unzip it.
3. Push `Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so` to your device by adb: `adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/adb/tmp/magiskboot`
4. Push stock boot.img and Image in AnyKernel3 to your device.
5. Enter adb shell and cd `/data/local/tmp/` directory, then `chmod +x magiskboot`
6. Enter adb shell and cd `/data/local/tmp/` directory, execute `./magiskboot unpack boot.img` to unpack `boot.img`, you will get a `kernel` file, this is your stock kernel.
7. Replace `kernel` with `Image`: `mv -f Image kernel`
8. Execute `./magiskboot repack boot.img` to repack boot img, and you will get a `new-boot.img` file, flash this file to device by fastboot.
## Other methods
In fact, all these installation methods have only one main idea, which is to **replace the original kernel for the one provided by KernelSU**; as long as this can be achieved, it can be installed; for example, the following are other possible methods.
1. First install Magisk, get root privileges through Magisk and then use the kernel flasher to flash in the AnyKernel zip from KernelSU.
2. Use some flashing toolkit on PCs to flash in the kernel provided KernelSU.

View File

@@ -137,4 +137,85 @@ Anda harus menemukan empat fungsi dalam kernel source:
3. vfs_read, usually in `fs/read_write.c`
4. vfs_statx, usually in `fs/stat.c`
Jika kernel anda tidak memiliki `vfs_statx`, maka gunakan `vfs_fstatat` alih-alih:
```diff
diff --git a/fs/stat.c b/fs/stat.c
index 068fdbcc9e26..5348b7bb9db2 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
}
EXPORT_SYMBOL(vfs_fstat);
+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
+
int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
int flag)
{
@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
int error = -EINVAL;
unsigned int lookup_flags = 0;
+ ksu_handle_stat(&dfd, &filename, &flag);
+
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
AT_EMPTY_PATH)) != 0)
goto out;
```
Untuk kernel lebih awal dari 4.17, jika anda menemukan `do_faccessat`, hanya pergi ke definisi yang sama `faccessat` syscall dan tempatkan pemanggil di sini:
```diff
diff --git a/fs/open.c b/fs/open.c
index 2ff887661237..e758d7db7663 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
return error;
}
+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
+ int *flags);
+
/*
* access() needs to use the real uid/gid, not the effective uid/gid.
* We do this by temporarily clearing all FS-related capabilities and
@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
int res;
unsigned int lookup_flags = LOOKUP_FOLLOW;
+ ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;
```
To enable KernelSU's builtin SafeMode, You should also modify `input_handle_event` in `drivers/input/input.c`:
:::tip
Fitur ini sangat direkomendasikan, serta sangat membantu untuk memulihkan pada saat bootloop!
:::
```diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 45306f9ef247..815091ebfca4 100755
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
return disposition;
}
+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
+
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = input_get_disposition(dev, type, code, &value);
+ ksu_handle_input_handle_event(&type, &code, &value);
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
add_input_randomness(type, code, value);
```
Terakhir, edit `KernelSU/kernel/ksu.c` dan beri komentar pada `enable_sucompat()` lalu build kernel Anda lagi, KernelSU akan bekerja dengan baik.

View File

@@ -0,0 +1,30 @@
# Perangkat Yang Didukung Tidak Resmi
:::peringatan
di halaman ini, terdapat kernel untuk perangkat non-GKI yang mendukung KernelSU yang dikelola oleh pengembang lain.
:::
:::peringatan
Halaman ini hanya untuk Anda yang ingin menemukan kode sumber yang sesuai dengan perangkat Anda, itu **BUKAN** berarti kode sumber telah ditinjau oleh _KernelSU Developers_. Anda harus menggunakannya dengan risiko Anda sendiri.
:::
<script setup>
import data from '../../repos.json'
</script>
<table>
<thead>
<tr>
<th>Pengelola</th>
<th>Repository</th>
<th>Perangkat yang didukung</th>
</tr>
</thead>
<tbody>
<tr v-for="repo in data" :key="repo.devices">
<td><a :href="repo.maintainer_link" target="_blank" rel="noreferrer">{{ repo.maintainer }}</a></td>
<td><a :href="repo.kernel_link" target="_blank" rel="noreferrer">{{ repo.kernel_name }}</a></td>
<td>{{ repo.devices }}</td>
</tr>
</tbody>
</table>

View File

@@ -19,7 +19,7 @@ hero:
features:
- title: Kernel-based
details: KernslSU bekerja dalam mode Linux kernel, dan mempunyai kelebihan diatas aplikasi userspace.
details: KernelSU bekerja dalam mode Linux kernel, dan mempunyai kelebihan diatas aplikasi userspace.
- title: Kontrol akses daftar putih
details: Hanya aplikasi yang diberikan izin root yang bisa mengakses `su`, aplikasi lain tidak bisa mengakses su.
- title: Dukungan modul

View File

@@ -19,7 +19,7 @@ hero:
features:
- title: Kernel-based
details: KernslSU is working in Linux kernel mode, it has more control over userspace applications.
details: KernelSU is working in Linux kernel mode, it has more control over userspace applications.
- title: Whitelist access control
details: Only App that is granted root permission can access `su`, other apps cannot perceive su.
- title: Module support

View File

@@ -20,6 +20,13 @@
"kernel_link": "https://github.com/hmtheboy154/Darkmatter-kernel",
"devices": "Generic x86_64 devices running Android-x86"
},
{
"maintainer": "Vincent4440",
"maintainer_link": "https://github.com/Vincent4440",
"kernel_name": "noxious_kernel_xiaomi_sm8250",
"kernel_link": "https://github.com/Vincent4440/noxious_kernel_xiaomi_sm8250/tree/thirteen",
"devices": "Poco F4: munch"
},
{
"maintainer": "Asuka-mio",
"maintainer_link": "https://github.com/asuka-mio",
@@ -75,5 +82,89 @@
"kernel_name": "android_kernel_xiaomi_sdm845",
"kernel_link": "https://github.com/OnlyTomInSecond/android_kernel_xiaomi_sdm845",
"devices": "Mi 8 (dipper) for LineageOS"
},
{
"maintainer": "WeeAris",
"maintainer_link": "https://github.com/WeeAris",
"kernel_name": "Realking_su_xiaomi_sm8250",
"kernel_link": "https://github.com/WeeAris/Realking_su_xiaomi_sm8250",
"devices": "Apollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro)\uff0cAlioth(Redmi K40/POCO F3/Mi 11X)\uff0cMunch(Redmi K40S/POCO F4), repo for AOSP only."
},
{
"maintainer": "lateautumn233",
"maintainer_link": "https://github.com/lateautumn233",
"kernel_name": "android_kernel_oneplus_sm8250",
"kernel_link": "https://github.com/lateautumn233/android_kernel_oneplus_sm8250",
"devices": "OnePlus 8 Serials"
},
{
"maintainer": "Molyuu",
"maintainer_link": "https://github.com/Molyuu",
"kernel_name": "neko_kernel_xiaomi_gauguin",
"kernel_link": "https://github.com/Molyuu/neko_kernel_xiaomi_gauguin",
"devices": "Redmi Note 9 Pro/ Mi 10T Lite/ Mi 10i "
},
{
"maintainer": "guh0613",
"maintainer_link": "https://github.com/guh0613",
"kernel_name": "android_kernel_oppo_sm8150",
"kernel_link": "https://github.com/guh0613/android_kernel_oppo_sm8150",
"devices": "OPPO Reno Ace (OP4A89)"
},
{
"maintainer": "LeviMarvin",
"maintainer_link": "https://github.com/LeviMarvin",
"kernel_name": "kernel_xiaomi_alioth",
"kernel_link": "https://github.com/LeviMarvin/kernel_xiaomi_alioth",
"devices": "Redmi K40 / POCO F3"
},
{
"maintainer": "cibimo",
"maintainer_link": "https://github.com/cibimo",
"kernel_name": "kernel_xiaomi_raphael_ksu",
"kernel_link": "https://github.com/cibimo/kernel_xiaomi_raphael_ksu",
"devices": "Xiaomi Redmi K20 Pro / Mi 9T Pro (raphael)"
},
{
"maintainer": "EndCredits",
"maintainer_link": "https://github.com/EndCredits",
"kernel_name": "kernel_xiaomi_sm7250",
"kernel_link": "https://github.com/EndCredits/kernel_xiaomi_sm7250",
"devices": "Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)"
},
{
"maintainer": "msnx",
"maintainer_link": "https://github.com/msnx",
"kernel_name": "android-msm-coral-4.14-android13",
"kernel_link": "https://github.com/msnx/KernelSU-Pixel4XL",
"devices": "Pixel 4 XL"
},
{
"maintainer": "SoDebug",
"maintainer_link": "https://github.com/SoDebug",
"kernel_name": "kernel_xiaomi_raphael",
"kernel_link": "https://github.com/SoDebug/kernel_xiaomi_raphael",
"devices": "Xiaomi Redmi K20 Pro / Mi 9T Pro (raphael)"
},
{
"maintainer": "rxuglr",
"maintainer_link": "https://github.com/rxuglr",
"kernel_name": "android_kernel_xiaomi_surya",
"kernel_link": "https://github.com/rxuglr/android_kernel_xiaomi_surya",
"devices": "Xiaomi POCO X3 / NFC (surya)"
},
{
"maintainer": "H1mJT",
"maintainer_link": "https://github.com/H1mJT",
"kernel_name": "kernel_realme_RMX1901",
"kernel_link": "https://github.com/H1mJT/kernel_realme_RMX1901",
"devices": "Realme X (RMX1901/RMX1901CN)"
},
{
"maintainer": "SonalSingh18",
"maintainer_link": "https://github.com/SonalSingh18",
"kernel_name": "android_kernel_xiaomi_sm6250.git",
"kernel_link": "https://github.com/SonalSingh18/android_kernel_xiaomi_sm6250.git",
"devices": "Miatoll [curtana, excalibur, gram, joyeuse]"
}
]

View File

@@ -16,7 +16,7 @@ Có, nhưng ở những phiên bản thử nghiệm này có thể có rất nhi
## KernelSU có hỗ trợ Xposed không?
Có, [Dreamland](https://github.com/canyie/Dreamland) và [TaiChi](https::/taichi.cool) hiện đã hoạt động được một phần nào đó và chúng tôi đang cố gắng làm cho các Xposed Framework khác có thể hoạt động.
Có, [Dreamland](https://github.com/canyie/Dreamland) và [TaiChi](https::/taichi.cool) hiện đã hoạt động được một phần nào đó. Với Lsposed, bạn có thể thử [Zygisk trên KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU)
## KernelSU có tương thích với Magisk không?
@@ -45,4 +45,20 @@ Có thể, KernelSU hiện đã được backport xuống kernel 4.14, đối v
## Làm cách nào để tích hợp KernelSU cho kernel cũ?
Vui lòng tham khảo [hướng dẫn này](how-to-integrate-for-non-gki)
Vui lòng tham khảo [hướng dẫn này](how-to-integrate-for-non-gki)
## Tại sao tôi đang chạy Android 13 nhưng kernel lại ghi "android12-5.10" ?
Phiên bản kernel hoàn toàn không liên quan gì đến phiên bản Android, nếu bạn muốn flash kernel thì hãy luôn để ý đến **phiên bản kernel**, phiên bản Android ở phần đầu (VD : android12-\*) thường không quan trọng lắm.
## Đã có mount namespace --mount-master/global trên KernelSU chưa ?
Hiện tại chưa có (hoặc có thể sẽ có trong tương lại), nhưng bạn có thể dùng `nsenter -t 1 -m sh` để vào global mount namespace.
## KernelSU có hỗ trợ Zygisk không ?
KernelSU không có Zygisk bên trong, nhưng bạn có thể dùng [Zygisk trên KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU)
## Tôi đang ở GKI 1.0, tôi dùng được cái này chứ ?
GKI1 khác hoàn toàn với GKI2 nên bạn sẽ phải tự compile kernel cho mình.

View File

@@ -46,3 +46,19 @@ KernelSU 的模块系统与 Magisk 的 magic mount 有冲突,如果 KernelSU
## 如何为旧内核集成 KernelSU
参考[教程](how-to-integrate-for-non-gki)
## 为什么我手机系统是 Android 13但内核版本却是 "android12-5.10"?
内核版本与 Android 版本无关,如果你需要刷入 KernelSU请永远使用**内核版本**而非 Android 版本,如果你为 "android12-5.10" 的设备刷入 Android 13 的内核,等待你的将是 bootloop.
## KernelSU 支持 --mount-master/全局挂载命名空间吗?
目前没有(未来可能会支持),现在你可以用 `nsenter -t 1 -m sh` 在 root shell 里面进入全局命名空间。
## KernelSU 支持 Zygisk 吗?
KernelSU 本体不支持 Zygisk但是你可以用 [Zygisk on KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU) 来使用 Zygisk 模块。
## 我是 GKI1.0, 能用 KernelSU 吗?
GKI1 跟 GKI2 完全是两个东西,所以你需要自行编译内核。

View File

@@ -142,4 +142,85 @@ index 376543199b5a..82adcef03ecc 100644
3. vfs_read通常位于 `fs/read_write.c`
4. vfs_statx通常位于 `fs/stat.c`
如果你的内核没有 `vfs_statx`, 使用 `vfs_fstatat` 来代替它:
```diff
diff --git a/fs/stat.c b/fs/stat.c
index 068fdbcc9e26..5348b7bb9db2 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
}
EXPORT_SYMBOL(vfs_fstat);
+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
+
int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
int flag)
{
@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
int error = -EINVAL;
unsigned int lookup_flags = 0;
+ ksu_handle_stat(&dfd, &filename, &flag);
+
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
AT_EMPTY_PATH)) != 0)
goto out;
```
对于早于 4.17 的内核,如果没有 `do_faccessat`,可以直接找到 `faccessat` 系统调用的定义然后修改:
```diff
diff --git a/fs/open.c b/fs/open.c
index 2ff887661237..e758d7db7663 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
return error;
}
+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
+ int *flags);
+
/*
* access() needs to use the real uid/gid, not the effective uid/gid.
* We do this by temporarily clearing all FS-related capabilities and
@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
int res;
unsigned int lookup_flags = LOOKUP_FOLLOW;
+ ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;
```
要使用 KernelSU 内置的安全模式,你还需要修改 `drivers/input/input.c` 中的 `input_handle_event` 方法:
:::tip
强烈建议开启此功能,对用户救砖会非常有帮助!
:::
```diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 45306f9ef247..815091ebfca4 100755
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
return disposition;
}
+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
+
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = input_get_disposition(dev, type, code, &value);
+ ksu_handle_input_handle_event(&type, &code, &value);
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
add_input_randomness(type, code, value);
```
改完之后重新编译内核即可。

View File

@@ -2,20 +2,109 @@
## 检查您的设备是否被支持
从 [GitHub Releases](https://github.com/tiann/KernelSU/releases) 或 [GitHub Actions](https://github.com/tiann/KernelSU/actions/workflows/build-manager.yml) 下载 KernelSU 管理器应用,然后将应用程序安装到设备并打开:
从 [GitHub Releases](https://github.com/tiann/KernelSU/releases) 或 [酷安](https://www.coolapk.com/apk/me.weishu.kernelsu) 下载 KernelSU 管理器应用,然后将应用程序安装到设备并打开:
- 如果应用程序显示 “不支持”,则表示您的设备不支持 KernelSU你需要自己编译设备的内核才能使用KernelSU 官方不会也永远不会为你提供一个可以刷写的 boot 镜像。
- 如果应用程序显示 “未安装”,那么 KernelSU 支持您的设备。
- 如果应用程序显示 “未安装”,那么 KernelSU 支持您的设备;可以进行下一步操作
## 找到合适的 boot.img
:::info
对于显示“不支持”的设备,这里有一个[非官方支持设备列表](unofficially-support-devices.md),你可以用这个列表里面的内核自行编译。
:::
KernelSU 为 GKI 设备提供了通用的 boot.img您应该将 boot.img 刷写到设备的引导分区。
## 备份你的 boot.img
您可以从 [GitHub Actions for Kernel](https://github.com/tiann/KernelSU/actions/workflows/build-kernel.yml) 下载 boot.img, 请注意您应该使用正确版本的 boot.img. 例如,如果您的设备显示内核是 `5.10.101`, 需要下载 `5.10.101-xxxx.boot.xxx`.
在进行刷机操作之前,你必须先备份好自己的原厂 boot.img。如果你后续刷机出现了任何问题你都可以通过使用 fastboot 刷回原厂 boot 来恢复系统。
另外,请检查您原有 boot.img 的内核压缩格式,您应该使用正确的格式,例如 `lz4``gz`
::: warning
任何刷机操作都是有风险的,请务必做好这一步再进行下一步操作!!必要时你还可以备份你手机的所有数据。
:::
## 将 boot.img 刷入设备
## 必备知识
### ADB 和 fastboot
此教程默认你会使用 ADB 和 fastboot 工具,如果你没有了解过,建议使用搜索引擎先学习相关知识。
### KMI
KMI 全称 Kernel Module Interface相同 KMI 的内核版本是**兼容的** 这也是 GKI 中“通用”的含义所在;反之,如果 KMI 不同,那么这些内核之间无法互相兼容,刷入与你设备 KMI 不同的内核镜像可能会导致死机。
具体来说,对 GKI 的设备,其内核版本格式应该如下:
```txt
KernelRelease :=
Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
w .x .y -zzz -k -something
```
其中,`w.x-zzz-k` 为 KMI 版本。例如,一个设备内核版本为`5.10.101-android12-9-g30979850fc20`,那么它的 KMI 为 `5.10-android12-9`;理论上刷入其他这个 KMI 的内核也能正常开机。
::: tip
请注意,内核版本中的 SubLevel 不属于 KMI 的范畴!也就是说 `5.10.101-android12-9-g30979850fc20``5.10.137-android12-9-g30979850fc20` 的 KMI 相同!
:::
### 内核版本与 Android 版本
请注意:**内核版本与 Android 版本并不一定相同!**
如果您发现您的内核版本是 `android12-5.10.101`,然而你 Android 系统的版本为 Android 13 或者其他;请不要觉得奇怪,因为 Android 系统的版本与 Linux 内核的版本号不一定是一致的Linux 内核的版本号一般与**设备出厂的时候自带的 Android 系统的版本一致**,如果后续 Android 系统升级,内核版本一般不会发生变化。如果你需要刷机,**请以内核版本为准!!**
## 安装介绍
KernelSU 的安装方法有如下几种,各自适用于不同的场景,请按需选择:
1. 使用自定义 Recovery如 TWRP安装
2. 使用内核刷写 AppFranco Kernel Manager安装
3. 使用 KernelSU 提供的 boot.img 使用 fastboot 安装
4. 手动修补 boot.img 然后安装
## 使用自定义 Recovery 安装
前提:你的设备必须有自定义的 Recovery如 TWRP如果没有或者只有官方 Recovery请使用其他方法。
步骤:
1. 在 KernelSU 的 [Release 页面](https://github.com/tiann/KernelSU/releases) 下载与你手机版本匹配的以 AnyKernel3 开头的 zip 刷机包;例如,手机内核版本为 `android12-5.10.66`,那么你应该下载 `AnyKernel3-android12-5.10.66_yyyy-MM.zip` 这个文件(其中 `yyyy` 为年份,`MM` 为月份)。
2. 重启手机进入 TWRP。
3. 使用 adb 将 AnyKernel3-*.zip 放到手机 /sdcard 然后在 TWRP 图形界面选择安装;或者你也可以直接 `adb sideload AnyKernel-*.zip` 安装。
PS. 这种方法适用于任何情况下的安装(不限于初次安装或者后续升级),只要你用 TWRP 就可以操作。
## 使用内核刷写 App 安装
前提:你的设备必须已经 root。例如你已经安装了 Magisk 获取了 root或者你已经安装了旧版本的 KernelSU 需要升级到其他版本的 KernelSU如果你的设备无 root请尝试其他方法。
步骤:
1. 下载 AnyKernel3 的刷机包;下载方法参考 *使用自定义 Recovery 安装*那一节的内容。
2. 打开内核刷写 App 使用提供的 AnyKernel3 刷机包刷入。
如果你之前没有用过内核刷写 App那么下面几个是比较流行的
1. [Franco Kernel Manager](https://play.google.com/store/apps/details?id=com.franco.kernel)
2. [Ex Kernel Manager](https://play.google.com/store/apps/details?id=flar2.exkernelmanager)
PS. 这种方法在升级 KernelSU 的时候较为方便,无需电脑即可完成(注意备份!)。
## 使用 KernelSU 提供的 boot.img 安装
这张方法无需你有 TWRP也不需要你的手机有 root 权限;适用于你初次安装 KernelSU。
### 找到合适的 boot.img
KernelSU 为 GKI 设备提供了通用的 boot.img您应该将 boot.img 刷写到设备的 boot 分区。
您可以从 [GitHub Release](https://github.com/tiann/KernelSU/releases) 下载 boot.img, 请注意您应该使用正确版本的 boot.img. 例如,如果您的设备显示内核是 `android12-5.10.101`, 需要下载 `android-5.10.101_yyyy-MM.boot-<format>.img`.
其中 `<format>` 指的是你的官方 boot.img 的内核压缩格式,请检查您原有 boot.img 的内核压缩格式,您应该使用正确的格式,例如 `lz4``gz`;如果是用不正确的压缩格式,刷入 boot 后可能无法开机。
::: info
1. 您可以通过 magiskboot 来获取你原来 boot 的压缩格式;当然您也可以询问与您机型相同的其他更有经验的童鞋。另外,内核的压缩格式通常不会发生变化,如果您使用某个压缩格式成功开机,后续可优先尝试这个格式。
2. 小米设备通常使用 `gz` 或者 **不压缩**
3. Pixel 设备有些特殊,请查看下面的教程。
:::
### 将 boot.img 刷入设备
使用 `adb` 连接您的设备,然后执行 `adb reboot bootloader` 进入 fastboot 模式,然后使用此命令刷入 KernelSU
@@ -23,10 +112,57 @@ KernelSU 为 GKI 设备提供了通用的 boot.img您应该将 boot.img 刷
fastboot flash boot boot.img
```
## 重启
::: info
如果你的设备支持 `fastboot boot`,可以先使用 `fastboot boot boot.img` 来先尝试使用 boot.img 引导系统,如果出现意外,再重启一次即可开机。
:::
### 重启
刷入完成后,您应该重新启动您的设备:
```sh
fastboot reboot
```
## 手动修补 boot.img
对于某些设备来说,其 boot.img 格式不那么常见,比如不是 `lz4`, `gz` 和未压缩;最典型的就是 Pixel它 boot.img 的格式是 `lz4_legacy` 压缩ramdisk 可能是 `gz` 也可能是 `lz4_legacy` 压缩;此时如果你直接刷入 KernelSU 提供的 boot.img手机可能无法开机这时候你可以通过手动修补 boot.img 来实现。
修补方法总体有两种:
1. [Android-Image-Kitchen](https://forum.xda-developers.com/t/tool-android-image-kitchen-unpack-repack-kernel-ramdisk-win-android-linux-mac.2073775/)
2. [magiskboot](https://github.com/topjohnwu/Magisk/releases)
其中Android-Image-Kitchen 适用于 PC 上操作magiskboot 需要手机配合。
### 准备
1. 获取你手机的原厂 boot.img你可以通过你手机的线刷包解压后之间获取如果你是卡刷包那你也许需要[payload-dumper-go](https://github.com/ssut/payload-dumper-go)
2. 下载 KernelSU 提供的与你设备 KMI 版本一致的 AnyKernel3 刷机包(可以参考 *自定义 TWRP 刷入一节*)。
3. 解压缩 AnyKernel3 刷机包,获取其中的 `Image` 文件,此文件为 KernelSU 的内核文件。
### 使用 Android-Image-Kitchen
1. 下载 Android-Image-Kitchen 至你电脑
2. 将手机原厂 boot.img 放入 Android-Image-Kitchen 根目录
3. 在 Android-Image-Kitchen 根目录执行 `./unpackimg.sh boot.img`;此命名会将 boot.img 拆开,你会得到若干文件。
4.`split_img` 目录中的 `boot.img-kernel` 替换为你从 AnyKernel3 解压出来的 `Image`(注意名字改为 boot.img-kernel)。
5. 在 Android-Image-Kitchecn 根目录执行 `./repackimg.sh`;此时你会得到一个 `image-new.img` 的文件;使用此 boot.img 通过 fastboot 刷入即可(刷入方法参考上一节)。
### 使用 magiskboot
1. 在 Magisk 的 [Release 页面](https://github.com/topjohnwu/Magisk/releases) 下载最新的 Magisk 安装包。
2. 将 Magisk-*.apk 重命名为 Magisk-vesion.zip 然后解压缩。
3. 将解压后的 `Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so` 文件,使用 adb push 到手机:`adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/adb/tmp/magiskboot`
4. 使用 adb 将原厂 boot.img 和 AnyKernel3 中的 Image 推送到手机
5. adb shell 进入 /data/local/tmp/ 目录,然后赋予刚 push 文件的可执行权限 `chmod +x magiskboot`
6. adb shell 进入 /data/local/tmp/ 目录,执行 `./magiskboot unpack boot.img` 此时会解包 `boot.img` 得到一个叫做 `kernel` 的文件,这个文件为你原厂的 kernel
7. 使用 `Image` 替换 `kernel`: `mv -f Image kernel`
8. 执行 `./magiskboot repack boot.img` 打包 img此时你会得到一个 `new-boot.img` 的文件,使用这个文件 fastboot 刷入设备即可。
## 其他变通方法
其实所有这些安装方法的主旨只有一个,那就是**替换原厂的内核为 KernelSU 提供的内核**;只要能实现这个目的,就可以安装;比如以下是其他可行的方法:
1. 首先安装 Magisk通过 Magisk 获取 root 权限后使用内核刷写器刷入 KernelSU 的 AnyKernel 包。
2. 使用某些 PC 上的刷机工具箱刷入 KernelSU 提供的内核。

View File

@@ -19,7 +19,7 @@ hero:
features:
- title: 基于内核
details: KernslSU 运行在内核空间,对用户空间应用有更强的掌控。
details: KernelSU 运行在内核空间,对用户空间应用有更强的掌控。
- title: 白名单访问控制
details: 只有被授权的 App 才可以访问 `su`,而其他 App 无法感知其存在。
- title: 模块支持