You've already forked KernelSU
mirror of
https://github.com/tiann/KernelSU.git
synced 2025-08-27 23:46:34 +00:00
Compare commits
508 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76b1165572 | ||
|
|
80c85b3bb9 | ||
|
|
900652a82b | ||
|
|
6cbef7d987 | ||
|
|
01d66834f0 | ||
|
|
a40eae9b8c | ||
|
|
23b46bde2b | ||
|
|
b38ada30e6 | ||
|
|
3452841752 | ||
|
|
3b9cab3432 | ||
|
|
2f6df20085 | ||
|
|
609ea40d1c | ||
|
|
515a309831 | ||
|
|
02c2228ab7 | ||
|
|
ca246ba67d | ||
|
|
f9008b67c4 | ||
|
|
e228197906 | ||
|
|
d98e324618 | ||
|
|
1880fdfda9 | ||
|
|
d86b524558 | ||
|
|
70fa38a589 | ||
|
|
ea93140b8a | ||
|
|
45c229dc9f | ||
|
|
2b5ac95165 | ||
|
|
38c65e7e4b | ||
|
|
f40eaf4499 | ||
|
|
3a667ef389 | ||
|
|
f35e03d816 | ||
|
|
2bfd4d71d8 | ||
|
|
2bc84014c2 | ||
|
|
a3c72c22c1 | ||
|
|
ddf2b7e9ff | ||
|
|
04d1bee7b4 | ||
|
|
0c9ecf3abc | ||
|
|
c8b4798b61 | ||
|
|
550c8ae45d | ||
|
|
0b4f4683b3 | ||
|
|
5504b0dd29 | ||
|
|
f0dcddd391 | ||
|
|
75be1e1bd6 | ||
|
|
c569d803c5 | ||
|
|
48cd6e112b | ||
|
|
d41354e1d7 | ||
|
|
9a5e36c0a4 | ||
|
|
f963e40a5f | ||
|
|
f1e2402316 | ||
|
|
31bb8c75f5 | ||
|
|
fb4ab44aa5 | ||
|
|
71216b1458 | ||
|
|
c969ff4011 | ||
|
|
dc45eb6ed4 | ||
|
|
29aaaae4b4 | ||
|
|
1fe53b9549 | ||
|
|
6274dbebc0 | ||
|
|
a1cd3ab6fa | ||
|
|
c058cb8848 | ||
|
|
029061177b | ||
|
|
9c8e813642 | ||
|
|
7be61b9657 | ||
|
|
2f67d24ec9 | ||
|
|
17d6f1cdf1 | ||
|
|
a0c34b40c6 | ||
|
|
00856f8cb9 | ||
|
|
3df6387ee0 | ||
|
|
fe1c826b1b | ||
|
|
0bda101d4d | ||
|
|
5e738129d9 | ||
|
|
39b5014add | ||
|
|
3c6560ade9 | ||
|
|
183d1a91c1 | ||
|
|
d711ab8b1f | ||
|
|
6c1a48952e | ||
|
|
a343aa5eb0 | ||
|
|
273a0b0b99 | ||
|
|
2f1e64dc1b | ||
|
|
a46d4ecd3e | ||
|
|
5f04954a5c | ||
|
|
d065a7ca22 | ||
|
|
2f8373f9c5 | ||
|
|
2d36e98246 | ||
|
|
f2d8f1ee60 | ||
|
|
e7881c350c | ||
|
|
ffe3e68c35 | ||
|
|
4d6fafd778 | ||
|
|
2bbf77ab4c | ||
|
|
f9df8d3351 | ||
|
|
8f4299ef62 | ||
|
|
fabaa61279 | ||
|
|
f17de87952 | ||
|
|
93a607083a | ||
|
|
7544558173 | ||
|
|
c96b0e8f8b | ||
|
|
b181147922 | ||
|
|
862d12a904 | ||
|
|
ddbc71b273 | ||
|
|
c68ea8abbd | ||
|
|
cd5c7f599a | ||
|
|
1bb3a23bc4 | ||
|
|
b191ec94a5 | ||
|
|
8f910fbb75 | ||
|
|
693d827144 | ||
|
|
effcaf0cde | ||
|
|
5be8fe39d0 | ||
|
|
8d12f96d8b | ||
|
|
c1b30458d6 | ||
|
|
48e76f9096 | ||
|
|
6fc8cc7e8e | ||
|
|
a3874dd089 | ||
|
|
bdd5e31b6f | ||
|
|
ed0e9cff4b | ||
|
|
62bbee56db | ||
|
|
ec4a233e04 | ||
|
|
9efab243ae | ||
|
|
219970bba5 | ||
|
|
dcbd196c02 | ||
|
|
814d65cc28 | ||
|
|
8c222add7c | ||
|
|
ab07557aa5 | ||
|
|
7be405e4e4 | ||
|
|
726f44bc60 | ||
|
|
c4c597da9e | ||
|
|
e5617e236c | ||
|
|
b1af4ada60 | ||
|
|
284b962d64 | ||
|
|
86ff022dc6 | ||
|
|
dcbbbab11e | ||
|
|
14b2afe78d | ||
|
|
ba4ffa7598 | ||
|
|
92ae0e5460 | ||
|
|
1ace028cef | ||
|
|
43ca2b9831 | ||
|
|
ecd5af76ab | ||
|
|
be452a22f0 | ||
|
|
6d6f793c69 | ||
|
|
30abd9e310 | ||
|
|
d0e3b2672d | ||
|
|
3b8a3ca26f | ||
|
|
33a096da22 | ||
|
|
72ba3ba086 | ||
|
|
5dd430e6a6 | ||
|
|
77056a7100 | ||
|
|
76b9790ffb | ||
|
|
54d2962a0d | ||
|
|
7846b2a440 | ||
|
|
8bbfe0c26d | ||
|
|
5c67334889 | ||
|
|
0b1b73a05d | ||
|
|
203dc42e75 | ||
|
|
9f6e9f5db6 | ||
|
|
ee09b9f9f4 | ||
|
|
c534ef672e | ||
|
|
c34a5ae2a6 | ||
|
|
1c65048813 | ||
|
|
3b8d0b83d4 | ||
|
|
b2c39af069 | ||
|
|
5c3df7e7a5 | ||
|
|
ffa5a93c75 | ||
|
|
794b725928 | ||
|
|
ffc06525fb | ||
|
|
3fe99712ba | ||
|
|
765c2b7d1f | ||
|
|
f789bb8c53 | ||
|
|
745f109686 | ||
|
|
8ed3bd53ac | ||
|
|
40d7d62af2 | ||
|
|
09fb118d22 | ||
|
|
0c3731b0bd | ||
|
|
349fd09440 | ||
|
|
bd3773f32a | ||
|
|
572708c283 | ||
|
|
fafdacfc41 | ||
|
|
c3ba483b81 | ||
|
|
e309a03515 | ||
|
|
82a304e054 | ||
|
|
b76d973f3a | ||
|
|
237e477876 | ||
|
|
47bcccdce3 | ||
|
|
ba8ca1f9f2 | ||
|
|
4837f2101c | ||
|
|
37d2914611 | ||
|
|
849164e4de | ||
|
|
683ba112aa | ||
|
|
e743722449 | ||
|
|
a5ee2ef93b | ||
|
|
3e66f5e8cf | ||
|
|
83b0aed52a | ||
|
|
498763505a | ||
|
|
8cc4ad4d80 | ||
|
|
6ec0c25173 | ||
|
|
93bcd78f89 | ||
|
|
ebf6a52237 | ||
|
|
a2906093ec | ||
|
|
a161c318a1 | ||
|
|
3f1ee2f784 | ||
|
|
60de2e4a6e | ||
|
|
d5bb79edd5 | ||
|
|
7264a00813 | ||
|
|
cf21767975 | ||
|
|
8fbdd996de | ||
|
|
2c3dcae117 | ||
|
|
b024b5d006 | ||
|
|
d5bab2317e | ||
|
|
0c8b4a48de | ||
|
|
f9b3218ded | ||
|
|
acc37fb387 | ||
|
|
f50b4dfe34 | ||
|
|
9ce7351aaa | ||
|
|
c691a1adb2 | ||
|
|
9a2a21ec5d | ||
|
|
a9fd0aa132 | ||
|
|
198674d889 | ||
|
|
203a5683ac | ||
|
|
eeb8cda175 | ||
|
|
b268971323 | ||
|
|
051fc53a4f | ||
|
|
55602f1f16 | ||
|
|
42428345ff | ||
|
|
ca950d909b | ||
|
|
20ff530962 | ||
|
|
a5dbbf4881 | ||
|
|
e91b1fc89a | ||
|
|
4e35e4ae86 | ||
|
|
6d15cb7e33 | ||
|
|
095acad8a6 | ||
|
|
c187d1ad8a | ||
|
|
f6967d2cfb | ||
|
|
388d2b0b59 | ||
|
|
d9aecbcbca | ||
|
|
eabdf3e78c | ||
|
|
a1fb7c5fdf | ||
|
|
89394245b1 | ||
|
|
91f1eb2d6a | ||
|
|
ab5f6db54b | ||
|
|
626642af76 | ||
|
|
473f02396f | ||
|
|
aa4b1bf9d8 | ||
|
|
8e5a72fc35 | ||
|
|
7302653879 | ||
|
|
b2b563547c | ||
|
|
cc29ad151a | ||
|
|
a4a93d8945 | ||
|
|
3389cd0aea | ||
|
|
d26956ff72 | ||
|
|
66284bfbe3 | ||
|
|
14be75629b | ||
|
|
d77ab8dbff | ||
|
|
c2ac548ac7 | ||
|
|
ad4d8e939e | ||
|
|
aa7a00b299 | ||
|
|
1ff421365e | ||
|
|
0a12d0139d | ||
|
|
181ab4f545 | ||
|
|
3e29e98f2c | ||
|
|
a66c1de660 | ||
|
|
0c322a33bc | ||
|
|
cd33a6dd07 | ||
|
|
c1dceaf11f | ||
|
|
3181dd17bc | ||
|
|
c93fa1af59 | ||
|
|
9f4a8d3dfc | ||
|
|
4da829792f | ||
|
|
1a073224c3 | ||
|
|
175de861bf | ||
|
|
aa73c34db2 | ||
|
|
ed2176af8c | ||
|
|
49f7d56fdd | ||
|
|
71cc166f72 | ||
|
|
8ee00839dc | ||
|
|
6239662a7f | ||
|
|
216c2aa2cd | ||
|
|
70b8b43b48 | ||
|
|
430c2e709f | ||
|
|
b975950b07 | ||
|
|
ed42cf42d2 | ||
|
|
d80c282644 | ||
|
|
a05edb3872 | ||
|
|
43b8987b4b | ||
|
|
219ea1c458 | ||
|
|
bea93f6ad7 | ||
|
|
e4267848f0 | ||
|
|
bc5953b510 | ||
|
|
e41b7cd117 | ||
|
|
cab78e7893 | ||
|
|
199f5cc223 | ||
|
|
962649f7ca | ||
|
|
7b32c0e37b | ||
|
|
12f353a1ae | ||
|
|
c26e170c87 | ||
|
|
6a706de09e | ||
|
|
2a4675e25b | ||
|
|
0bc36b3299 | ||
|
|
dc902b16d4 | ||
|
|
86998a032e | ||
|
|
1727ec41c4 | ||
|
|
3eb812be5b | ||
|
|
64c2f6ba5c | ||
|
|
2db7da0766 | ||
|
|
d1e7bad18f | ||
|
|
d7cef25665 | ||
|
|
23f41145b0 | ||
|
|
a969af8159 | ||
|
|
001fa00355 | ||
|
|
a6dddd32b3 | ||
|
|
cd825e34da | ||
|
|
2bba088319 | ||
|
|
4905bd9bb8 | ||
|
|
3f625a000b | ||
|
|
3bfee10a0d | ||
|
|
d5a05da5b8 | ||
|
|
d8042a36c3 | ||
|
|
85bf01eb65 | ||
|
|
4f2b8b7077 | ||
|
|
417ff8a6c5 | ||
|
|
681c4a3f0d | ||
|
|
619dd1ace1 | ||
|
|
1cd18a643d | ||
|
|
3519d61636 | ||
|
|
46913671a8 | ||
|
|
b7ff6b1a51 | ||
|
|
62be9eb589 | ||
|
|
ab6d483c32 | ||
|
|
2d77f84736 | ||
|
|
c2f7963a43 | ||
|
|
b9e27621ee | ||
|
|
2766e24007 | ||
|
|
45911ac3c4 | ||
|
|
ad89c5a80f | ||
|
|
3413f4a4fe | ||
|
|
2a88cca50c | ||
|
|
634978c14f | ||
|
|
093f7d9758 | ||
|
|
40960f60f8 | ||
|
|
25a4c2930a | ||
|
|
342344db0c | ||
|
|
7785d2a3f8 | ||
|
|
bd6b0d3d12 | ||
|
|
a871b92dc9 | ||
|
|
47d15c47f3 | ||
|
|
684283c585 | ||
|
|
d2d9b0eaad | ||
|
|
22e6b1eec5 | ||
|
|
f6301c5a7c | ||
|
|
b191415173 | ||
|
|
59542bc99a | ||
|
|
0e4b5b3765 | ||
|
|
a986251773 | ||
|
|
0e651fdc99 | ||
|
|
b1e279bd2a | ||
|
|
e76cf08934 | ||
|
|
4cf930a9dc | ||
|
|
599723515e | ||
|
|
e1e8d53da5 | ||
|
|
20a2c0092e | ||
|
|
a40ad07e2b | ||
|
|
65d80aad03 | ||
|
|
905c041a76 | ||
|
|
0dae6ebaee | ||
|
|
d41954b09c | ||
|
|
6e8771b8d9 | ||
|
|
cf5bcc09e8 | ||
|
|
413a8d0a2f | ||
|
|
1852652029 | ||
|
|
76c88a72c6 | ||
|
|
f6c249bd77 | ||
|
|
ee7065bc12 | ||
|
|
8446f07a91 | ||
|
|
09b9aefed7 | ||
|
|
f810ce2d0d | ||
|
|
ed86635b3c | ||
|
|
080aeee5e3 | ||
|
|
2ebb363350 | ||
|
|
da38815ce0 | ||
|
|
db600d5ea0 | ||
|
|
22b66b6672 | ||
|
|
06aaae1335 | ||
|
|
810087b2a7 | ||
|
|
2f970f7ab8 | ||
|
|
5fb8316e46 | ||
|
|
57806df17e | ||
|
|
0857cf8835 | ||
|
|
64d78c31bc | ||
|
|
47a736f567 | ||
|
|
423f4aaf1f | ||
|
|
1d55c987a6 | ||
|
|
e5044b4b6f | ||
|
|
fbd7d0f90d | ||
|
|
87d05fbbdc | ||
|
|
1475a85c58 | ||
|
|
b4bc2e66e6 | ||
|
|
780a42d823 | ||
|
|
3ca9537d8a | ||
|
|
4c28932c4c | ||
|
|
6bccd757fd | ||
|
|
00630cb4f3 | ||
|
|
254d19f8a3 | ||
|
|
f8583fbef0 | ||
|
|
fe11fcd6c6 | ||
|
|
c27302136c | ||
|
|
fbbe2166e6 | ||
|
|
6e02cbde8a | ||
|
|
aced1088f7 | ||
|
|
ec41eec1a0 | ||
|
|
505b5ea767 | ||
|
|
08165211eb | ||
|
|
ec0158e2e8 | ||
|
|
b7bed72c6d | ||
|
|
553b26c1cb | ||
|
|
58dc00c17c | ||
|
|
e79e4fa05e | ||
|
|
95ea1b2dfb | ||
|
|
a408a46168 | ||
|
|
bc539569e1 | ||
|
|
7a275e8e2a | ||
|
|
87fcfdf9e6 | ||
|
|
664981628a | ||
|
|
a255a5e3c1 | ||
|
|
c252492a42 | ||
|
|
b328fd96bc | ||
|
|
2844df865b | ||
|
|
da377a9fa9 | ||
|
|
deac6163d6 | ||
|
|
448fcc07e7 | ||
|
|
830c27361e | ||
|
|
5b2efd65b1 | ||
|
|
2724c6211a | ||
|
|
9a3451a8ae | ||
|
|
3d5effb678 | ||
|
|
965412377d | ||
|
|
d8009500a4 | ||
|
|
7347ae2301 | ||
|
|
babd6edbc2 | ||
|
|
2a1e91cb34 | ||
|
|
ab36e1fa0c | ||
|
|
2ab60750d4 | ||
|
|
b9dd673518 | ||
|
|
bf3edfd0ef | ||
|
|
710b933b08 | ||
|
|
ece66a3c38 | ||
|
|
ebfa64cc08 | ||
|
|
10000c098d | ||
|
|
7c585905ac | ||
|
|
1ca06e1f1a | ||
|
|
fb73a7f83e | ||
|
|
2362a8d169 | ||
|
|
f98066cb99 | ||
|
|
2222a999b9 | ||
|
|
00b1d7538d | ||
|
|
886c7d97a6 | ||
|
|
ae8a5d047b | ||
|
|
4f99d865cb | ||
|
|
a30aab1da6 | ||
|
|
bff4303dbb | ||
|
|
2359b16526 | ||
|
|
fb4b9d0173 | ||
|
|
aafa24094c | ||
|
|
c3f462d260 | ||
|
|
97de71d09e | ||
|
|
5a8acea84e | ||
|
|
8274f6a1ff | ||
|
|
0b86cd69fc | ||
|
|
5e77d1cd5b | ||
|
|
50f44fff9e | ||
|
|
21b0d6ceaf | ||
|
|
6e483a025d | ||
|
|
a748bed807 | ||
|
|
177d6dba15 | ||
|
|
dd905a1852 | ||
|
|
5bad1d0b53 | ||
|
|
3577b91a3c | ||
|
|
ece1cbe48e | ||
|
|
f75ca3a4f1 | ||
|
|
e173081cae | ||
|
|
954e06bbcc | ||
|
|
bf87b134de | ||
|
|
acd6f44c7e | ||
|
|
e37d97c7c7 | ||
|
|
4f9feeff19 | ||
|
|
761f68b24f | ||
|
|
d9f656031d | ||
|
|
848712e417 | ||
|
|
0e0a812a9c | ||
|
|
26f80b7107 | ||
|
|
a2ec618c49 | ||
|
|
43bea50605 | ||
|
|
94d0d5ac6b | ||
|
|
7c3a78b83f | ||
|
|
0c6de7a1ed | ||
|
|
36aff66835 | ||
|
|
63ebec2f76 | ||
|
|
d9542d6a9c | ||
|
|
09d9e4aa02 | ||
|
|
7140f04243 | ||
|
|
76ff69dedb | ||
|
|
1c4e51aec5 | ||
|
|
7942f4c495 | ||
|
|
bc9daf1583 | ||
|
|
89288a0c59 | ||
|
|
ea3cfbd0ca | ||
|
|
06cc677278 | ||
|
|
edf7c9a8b7 | ||
|
|
977d247ee0 | ||
|
|
3d9fc18149 | ||
|
|
f86c14b672 | ||
|
|
e37d0148e1 | ||
|
|
800f66467c | ||
|
|
df2a9a8a75 | ||
|
|
9e1ee1c513 | ||
|
|
ed2fb62034 |
33
.github/ISSUE_TEMPLATE/add_device.yml
vendored
Normal file
33
.github/ISSUE_TEMPLATE/add_device.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Contribute to Unofficially Supported Device
|
||||
description: Add your device kernel source to KernelSU's Unofficially Supported Device List
|
||||
title: "[Add Device]: "
|
||||
labels: ["add-device"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for supporting KernelSU !
|
||||
- type: input
|
||||
id: repo-url
|
||||
attributes:
|
||||
label: Repository URL
|
||||
description: Your repository URL
|
||||
placeholder: https://github.com/tiann/KernelSU
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: device
|
||||
attributes:
|
||||
label: Device
|
||||
description: Please describe the device maintained by you.
|
||||
placeholder: GKI 2.0 Device
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you should be the maintainer of the repository.
|
||||
options:
|
||||
- label: I'm the maintainer of this repository
|
||||
required: true
|
||||
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Custom issue template
|
||||
about: Describe this issue template's purpose here.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
48
.github/patches/5.10/0001-Makefile-Use-CCACHE-for-faster-compilation.patch
vendored
Normal file
48
.github/patches/5.10/0001-Makefile-Use-CCACHE-for-faster-compilation.patch
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
From f1e398602b989ac197cdd0fda4a7c4c323b03eb9 Mon Sep 17 00:00:00 2001
|
||||
From: DozNaka <dozdguide@gmail.com>
|
||||
Date: Mon, 11 Apr 2022 20:43:45 -0400
|
||||
Subject: [PATCH] Makefile: Use CCACHE for faster compilation
|
||||
|
||||
---
|
||||
Makefile | 20 ++++++++++----------
|
||||
1 file changed, 10 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/Makefile b/Makefile
|
||||
index e8b8d5894..51e8aac6e 100644
|
||||
--- a/Makefile
|
||||
+++ b/Makefile
|
||||
@@ -442,21 +442,21 @@ KBUILD_HOSTLDLIBS := $(HOST_LFS_LIBS) $(HOSTLDLIBS)
|
||||
# Make variables (CC, etc...)
|
||||
CPP = $(CC) -E
|
||||
ifneq ($(LLVM),)
|
||||
-CC = clang
|
||||
-LD = ld.lld
|
||||
-AR = llvm-ar
|
||||
+CC = $(CCACHE) clang
|
||||
+LD = $(CCACHE) ld.lld
|
||||
+AR = $(CCACHE) llvm-ar
|
||||
NM = llvm-nm
|
||||
-OBJCOPY = llvm-objcopy
|
||||
-OBJDUMP = llvm-objdump
|
||||
+OBJCOPY = $(CCACHE) llvm-objcopy
|
||||
+OBJDUMP = $(CCACHE) llvm-objdump
|
||||
READELF = llvm-readelf
|
||||
STRIP = llvm-strip
|
||||
else
|
||||
-CC = $(CROSS_COMPILE)gcc
|
||||
-LD = $(CROSS_COMPILE)ld
|
||||
-AR = $(CROSS_COMPILE)ar
|
||||
+CC = $(CCACHE) $(CROSS_COMPILE)gcc
|
||||
+LD = $(CCACHE) $(CROSS_COMPILE)ld
|
||||
+AR = $(CCACHE) $(CROSS_COMPILE)ar
|
||||
NM = $(CROSS_COMPILE)nm
|
||||
-OBJCOPY = $(CROSS_COMPILE)objcopy
|
||||
-OBJDUMP = $(CROSS_COMPILE)objdump
|
||||
+OBJCOPY = $(CCACHE) $(CROSS_COMPILE)objcopy
|
||||
+OBJDUMP = $(CCACHE) $(CROSS_COMPILE)objdump
|
||||
READELF = $(CROSS_COMPILE)readelf
|
||||
STRIP = $(CROSS_COMPILE)strip
|
||||
endif
|
||||
--
|
||||
2.37.2
|
||||
|
||||
43
.github/patches/5.10/0001-setlocalversion-don-t-check-for-uncommitted-changes.patch
vendored
Normal file
43
.github/patches/5.10/0001-setlocalversion-don-t-check-for-uncommitted-changes.patch
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
From dbdd2906c0b3a967ca28c6b870b46f905c170661 Mon Sep 17 00:00:00 2001
|
||||
From: Park Ju Hyung <qkrwngud825@gmail.com>
|
||||
Date: Wed, 13 Mar 2019 13:36:37 +0900
|
||||
Subject: [PATCH] setlocalversion: don't check for uncommitted changes
|
||||
|
||||
I ofter push after the build is done and I hate seeing "-dirty"
|
||||
|
||||
Signed-off-by: Park Ju Hyung <qkrwngud825@gmail.com>
|
||||
Signed-off-by: Danny Lin <danny@kdrag0n.dev>
|
||||
Signed-off-by: Divyanshu-Modi <divyan.m05@gmail.com>
|
||||
Change-Id: I240c516520879da680794fd144b1f273f9e21e13
|
||||
Signed-off-by: Divyanshu-Modi <divyan.m05@gmail.com>
|
||||
---
|
||||
scripts/setlocalversion | 13 -------------
|
||||
1 file changed, 13 deletions(-)
|
||||
|
||||
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
|
||||
index 842936656b84..ef27a273ebf5 100755
|
||||
--- a/scripts/setlocalversion
|
||||
+++ b/scripts/setlocalversion
|
||||
@@ -107,19 +107,6 @@ scm_version()
|
||||
printf -- '-svn%s' "$(git svn find-rev $head)"
|
||||
fi
|
||||
|
||||
- # Check for uncommitted changes.
|
||||
- # First, with git-status, but --no-optional-locks is only
|
||||
- # supported in git >= 2.14, so fall back to git-diff-index if
|
||||
- # it fails. Note that git-diff-index does not refresh the
|
||||
- # index, so it may give misleading results. See
|
||||
- # git-update-index(1), git-diff-index(1), and git-status(1).
|
||||
- if {
|
||||
- git --no-optional-locks status -uno --porcelain 2>/dev/null ||
|
||||
- git diff-index --name-only HEAD
|
||||
- } | grep -qvE '^(.. )?scripts/package'; then
|
||||
- printf '%s' -dirty
|
||||
- fi
|
||||
-
|
||||
# All done with git
|
||||
return
|
||||
fi
|
||||
--
|
||||
2.37.2
|
||||
|
||||
48
.github/patches/5.15/0001-Makefile-Use-CCACHE-for-faster-compilation.patch
vendored
Normal file
48
.github/patches/5.15/0001-Makefile-Use-CCACHE-for-faster-compilation.patch
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
From f1e398602b989ac197cdd0fda4a7c4c323b03eb9 Mon Sep 17 00:00:00 2001
|
||||
From: DozNaka <dozdguide@gmail.com>
|
||||
Date: Mon, 11 Apr 2022 20:43:45 -0400
|
||||
Subject: [PATCH] Makefile: Use CCACHE for faster compilation
|
||||
|
||||
---
|
||||
Makefile | 20 ++++++++++----------
|
||||
1 file changed, 10 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/Makefile b/Makefile
|
||||
index e8b8d5894..51e8aac6e 100644
|
||||
--- a/Makefile
|
||||
+++ b/Makefile
|
||||
@@ -442,21 +442,21 @@ KBUILD_HOSTLDLIBS := $(HOST_LFS_LIBS) $(HOSTLDLIBS)
|
||||
# Make variables (CC, etc...)
|
||||
CPP = $(CC) -E
|
||||
ifneq ($(LLVM),)
|
||||
-CC = clang
|
||||
-LD = ld.lld
|
||||
-AR = llvm-ar
|
||||
+CC = $(CCACHE) clang
|
||||
+LD = $(CCACHE) ld.lld
|
||||
+AR = $(CCACHE) llvm-ar
|
||||
NM = llvm-nm
|
||||
-OBJCOPY = llvm-objcopy
|
||||
-OBJDUMP = llvm-objdump
|
||||
+OBJCOPY = $(CCACHE) llvm-objcopy
|
||||
+OBJDUMP = $(CCACHE) llvm-objdump
|
||||
READELF = llvm-readelf
|
||||
STRIP = llvm-strip
|
||||
else
|
||||
-CC = $(CROSS_COMPILE)gcc
|
||||
-LD = $(CROSS_COMPILE)ld
|
||||
-AR = $(CROSS_COMPILE)ar
|
||||
+CC = $(CCACHE) $(CROSS_COMPILE)gcc
|
||||
+LD = $(CCACHE) $(CROSS_COMPILE)ld
|
||||
+AR = $(CCACHE) $(CROSS_COMPILE)ar
|
||||
NM = $(CROSS_COMPILE)nm
|
||||
-OBJCOPY = $(CROSS_COMPILE)objcopy
|
||||
-OBJDUMP = $(CROSS_COMPILE)objdump
|
||||
+OBJCOPY = $(CCACHE) $(CROSS_COMPILE)objcopy
|
||||
+OBJDUMP = $(CCACHE) $(CROSS_COMPILE)objdump
|
||||
READELF = $(CROSS_COMPILE)readelf
|
||||
STRIP = $(CROSS_COMPILE)strip
|
||||
endif
|
||||
--
|
||||
2.37.2
|
||||
|
||||
46
.github/patches/5.15/0001-setlocalversion-don-t-check-for-uncommitted-changes-5.15.patch
vendored
Normal file
46
.github/patches/5.15/0001-setlocalversion-don-t-check-for-uncommitted-changes-5.15.patch
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
From bbb9e7fb1ccadac47b58ba615e6874ddeaa9e628 Mon Sep 17 00:00:00 2001
|
||||
From: Park Ju Hyung <qkrwngud825@gmail.com>
|
||||
Date: Wed, 13 Mar 2019 13:36:37 +0900
|
||||
Subject: [PATCH] setlocalversion: don't check for uncommitted changes
|
||||
|
||||
I ofter push after the build is done and I hate seeing "-dirty"
|
||||
|
||||
Signed-off-by: Park Ju Hyung <qkrwngud825@gmail.com>
|
||||
Signed-off-by: Danny Lin <danny@kdrag0n.dev>
|
||||
Signed-off-by: Divyanshu-Modi <divyan.m05@gmail.com>
|
||||
Change-Id: I240c516520879da680794fd144b1f273f9e21e13
|
||||
Signed-off-by: Divyanshu-Modi <divyan.m05@gmail.com>
|
||||
---
|
||||
scripts/setlocalversion | 16 ----------------
|
||||
1 file changed, 16 deletions(-)
|
||||
|
||||
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
|
||||
index 1b733ae4c..2a3ea7684 100755
|
||||
--- a/scripts/setlocalversion
|
||||
+++ b/scripts/setlocalversion
|
||||
@@ -90,22 +90,6 @@ scm_version()
|
||||
printf '%s%s' -g "$(echo $head | cut -c1-12)"
|
||||
fi
|
||||
|
||||
- # Check for uncommitted changes.
|
||||
- # This script must avoid any write attempt to the source tree,
|
||||
- # which might be read-only.
|
||||
- # You cannot use 'git describe --dirty' because it tries to
|
||||
- # create .git/index.lock .
|
||||
- # First, with git-status, but --no-optional-locks is only
|
||||
- # supported in git >= 2.14, so fall back to git-diff-index if
|
||||
- # it fails. Note that git-diff-index does not refresh the
|
||||
- # index, so it may give misleading results. See
|
||||
- # git-update-index(1), git-diff-index(1), and git-status(1).
|
||||
- if {
|
||||
- git --no-optional-locks status -uno --porcelain 2>/dev/null ||
|
||||
- git diff-index --name-only HEAD
|
||||
- } | read dummy; then
|
||||
- printf '%s' -dirty
|
||||
- fi
|
||||
fi
|
||||
}
|
||||
|
||||
--
|
||||
2.37.2
|
||||
|
||||
56
.github/scripts/build_a12.sh
vendored
Normal file
56
.github/scripts/build_a12.sh
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
build_from_image() {
|
||||
export TITLE
|
||||
TITLE=kernel-aarch64-${1//Image-/}
|
||||
echo "[+] title: $TITLE"
|
||||
|
||||
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
|
||||
unzip gki-kernel.zip && rm gki-kernel.zip
|
||||
|
||||
echo '[+] Unpack prebuilt boot.img'
|
||||
BOOT_IMG=$(find . -maxdepth 1 -name "boot*.img")
|
||||
$UNPACK_BOOTIMG --boot_img="$BOOT_IMG"
|
||||
rm "$BOOT_IMG"
|
||||
|
||||
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-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 '[+] Compress images'
|
||||
for image in boot*.img; do
|
||||
$GZIP -n -f -9 "$image"
|
||||
mv "$image".gz "${1//Image-/}"-"$image".gz
|
||||
done
|
||||
|
||||
echo "[+] Images to upload"
|
||||
find . -type f -name "*.gz"
|
||||
|
||||
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 ..
|
||||
fi
|
||||
done
|
||||
43
.github/scripts/build_a13.sh
vendored
Normal file
43
.github/scripts/build_a13.sh
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
build_from_image() {
|
||||
export TITLE
|
||||
TITLE=kernel-aarch64-${1//Image-/}
|
||||
|
||||
echo "[+] title: $TITLE"
|
||||
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
|
||||
$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
|
||||
$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
|
||||
$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 "${1//Image-/}"-"$image".gz
|
||||
done
|
||||
|
||||
echo '[+] Images to upload'
|
||||
find . -type f -name "*.gz"
|
||||
|
||||
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 ..
|
||||
fi
|
||||
done
|
||||
59
.github/workflows/add-device.yml
vendored
Normal file
59
.github/workflows/add-device.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: handle-add-device-issue
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
|
||||
jobs:
|
||||
handle-add-device:
|
||||
if: github.event.label.name == 'add-device'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
ISSUE_CONTENT: ${{ github.event.issue.body }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Parse issue body
|
||||
id: handle-add-device
|
||||
run: |
|
||||
python3 scripts/add_device_handler.py website/docs/repos.json || true
|
||||
- name: Commit
|
||||
if: steps.handle-add-device.outputs.success == 'true'
|
||||
run: |
|
||||
git config --local user.name "GitHub Actions"
|
||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add website/docs/repos.json
|
||||
git commit -m "add device: ${{ steps.handle-add-device.outputs.device }}"
|
||||
- name: Make pull request
|
||||
if: steps.handle-add-device.outputs.success == 'true'
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commit-message: "[add device]: ${{ steps.handle-add-device.outputs.device }}"
|
||||
title: "[add device]: ${{ steps.handle-add-device.outputs.device }}"
|
||||
body: |
|
||||
${{ steps.handle-add-device.outputs.device }} has been added to the website.
|
||||
Related issue: ${{ github.event.issue.html_url }}
|
||||
branch: "add-device-${{ github.event.issue.number }}"
|
||||
labels: add-device
|
||||
delete-branch: true
|
||||
- name: Check outputs
|
||||
if: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
run: |
|
||||
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
|
||||
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
|
||||
- uses: ben-z/actions-comment-on-issue@1.0.2
|
||||
if: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
with:
|
||||
message: "Automatically created pull request: ${{ steps.cpr.outputs.pull-request-url }}"
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: ben-z/actions-comment-on-issue@1.0.2
|
||||
if: steps.handle-add-device.outputs.success != 'true'
|
||||
with:
|
||||
message: "Cannot create pull request. Please check the issue content. Or you can create a pull request manually."
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: close issue
|
||||
uses: peter-evans/close-issue@v1
|
||||
with:
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
121
.github/workflows/build-kernel-a12.yml
vendored
Normal file
121
.github/workflows/build-kernel-a12.yml
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
name: Build Kernel - Android 12
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "ci"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-a12.yml"
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
- ".github/scripts/build_a12.sh"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-a12.yml"
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
- ".github/scripts/build-a12.sh"
|
||||
- "kernel/**"
|
||||
workflow_call:
|
||||
jobs:
|
||||
build-kernel:
|
||||
if: github.event_name != 'pull_request'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- sub_level: 66
|
||||
os_patch_level: 2021-11
|
||||
- sub_level: 81
|
||||
os_patch_level: 2022-03
|
||||
- sub_level: 101
|
||||
os_patch_level: 2022-05
|
||||
- sub_level: 110
|
||||
os_patch_level: 2022-07
|
||||
- sub_level: 136
|
||||
os_patch_level: 2022-11
|
||||
- sub_level: 149
|
||||
os_patch_level: 2023-01
|
||||
- sub_level: 160
|
||||
os_patch_level: 2023-02
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
version: android12-5.10
|
||||
version_name: android12-5.10.${{ matrix.sub_level }}
|
||||
tag: android12-5.10-${{ matrix.os_patch_level }}
|
||||
os_patch_level: ${{ matrix.os_patch_level }}
|
||||
patch_path: "5.10"
|
||||
upload-artifacts:
|
||||
needs: build-kernel
|
||||
runs-on: ubuntu-latest
|
||||
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 }}
|
||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
path: KernelSU
|
||||
fetch-depth: 0
|
||||
|
||||
- name: List artifacts
|
||||
run: |
|
||||
tree
|
||||
|
||||
- name: Download prebuilt toolchain
|
||||
run: |
|
||||
AOSP_MIRROR=https://android.googlesource.com
|
||||
BRANCH=master-kernel-build-2022
|
||||
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||
pip3 install python-telegram-bot
|
||||
|
||||
- name: Set boot sign key
|
||||
env:
|
||||
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
|
||||
run: |
|
||||
if [ ! -z "$BOOT_SIGN_KEY" ]; then
|
||||
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
fi
|
||||
|
||||
- name: Setup mutex for uploading
|
||||
uses: ben-z/gh-action-mutex@v1.0-alpha-7
|
||||
|
||||
- name: Build boot images
|
||||
run: |
|
||||
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
|
||||
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
|
||||
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
|
||||
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) + 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
|
||||
with:
|
||||
version: android12-5.10
|
||||
version_name: android12-5.10.160
|
||||
tag: android12-5.10-2023-02
|
||||
os_patch_level: 2023-02
|
||||
patch_path: "5.10"
|
||||
132
.github/workflows/build-kernel-a13.yml
vendored
Normal file
132
.github/workflows/build-kernel-a13.yml
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
name: Build Kernel - Android 13
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "ci"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-a13.yml"
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
- ".github/scripts/build_a13.sh"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-a13.yml"
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
- ".github/scripts/build-a13.sh"
|
||||
- "kernel/**"
|
||||
workflow_call:
|
||||
jobs:
|
||||
build-kernel:
|
||||
if: github.event_name != 'pull_request'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 107
|
||||
os_patch_level: 2022-11
|
||||
- version: "5.10"
|
||||
sub_level: 149
|
||||
os_patch_level: 2023-01
|
||||
- version: "5.10"
|
||||
sub_level: 157
|
||||
os_patch_level: 2023-02
|
||||
- version: "5.15"
|
||||
sub_level: 41
|
||||
os_patch_level: 2022-11
|
||||
- version: "5.15"
|
||||
sub_level: 74
|
||||
os_patch_level: 2023-01
|
||||
- version: "5.15"
|
||||
sub_level: 78
|
||||
os_patch_level: 2023-02
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
version: android13-${{ matrix.version }}
|
||||
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||
patch_path: ${{ matrix.version }}
|
||||
upload-artifacts:
|
||||
needs: build-kernel
|
||||
runs-on: ubuntu-latest
|
||||
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 }}
|
||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
path: KernelSU
|
||||
fetch-depth: 0
|
||||
|
||||
- name: List artifacts
|
||||
run: |
|
||||
tree
|
||||
|
||||
- name: Download prebuilt toolchain
|
||||
run: |
|
||||
AOSP_MIRROR=https://android.googlesource.com
|
||||
BRANCH=master-kernel-build-2022
|
||||
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||
pip3 install python-telegram-bot
|
||||
|
||||
- name: Set boot sign key
|
||||
env:
|
||||
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
|
||||
run: |
|
||||
if [ ! -z "$BOOT_SIGN_KEY" ]; then
|
||||
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
fi
|
||||
|
||||
- name: Setup mutex for uploading
|
||||
uses: ben-z/gh-action-mutex@v1.0-alpha-7
|
||||
|
||||
- name: Build boot images
|
||||
run: |
|
||||
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
|
||||
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
|
||||
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
|
||||
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) + 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'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 149
|
||||
os_patch_level: 2022-11
|
||||
- version: "5.15"
|
||||
sub_level: 74
|
||||
os_patch_level: 2023-01
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android13-${{ matrix.version }}
|
||||
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||
patch_path: ${{ matrix.version }}
|
||||
150
.github/workflows/build-kernel-wsa.yml
vendored
Normal file
150
.github/workflows/build-kernel-wsa.yml
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
name: Build Kernel - WSA
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-wsa.yml"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-wsa.yml"
|
||||
- "kernel/**"
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, arm64]
|
||||
version: ["5.10.117.2", "5.15.78.1"]
|
||||
include:
|
||||
- arch: x86_64
|
||||
file_name: "bzImage"
|
||||
- arch: arm64
|
||||
file_name: "Image"
|
||||
cross_compile: "aarch64-linux-gnu"
|
||||
- version: "5.10.117.2"
|
||||
arch: x86_64
|
||||
make_config: config-wsa-5.10
|
||||
- version: "5.10.117.2"
|
||||
arch: arm64
|
||||
make_config: config-wsa-arm64-5.10
|
||||
- version: "5.15.78.1"
|
||||
arch: x86_64
|
||||
make_config: config-wsa-x64
|
||||
- version: "5.15.78.1"
|
||||
arch: arm64
|
||||
make_config: config-wsa-arm64
|
||||
- version: "5.10.117.2"
|
||||
device_code: latte
|
||||
kernel_version: "5.10"
|
||||
- version: "5.15.78.1"
|
||||
device_code: latte-2
|
||||
kernel_version: "5.15"
|
||||
|
||||
name: Build WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }}
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
|
||||
CCACHE_NOHASHDIR: "true"
|
||||
CCACHE_HARDLINK: "true"
|
||||
|
||||
steps:
|
||||
- name: Install Build Tools
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y --no-install-recommends bc bison build-essential ca-certificates flex git gnupg libelf-dev libssl-dev lsb-release software-properties-common wget libncurses-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu nuget gzip
|
||||
export LLVM_VERSION=12
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh $LLVM_VERSION
|
||||
rm ./llvm.sh
|
||||
sudo ln -s --force /usr/bin/clang-$LLVM_VERSION /usr/bin/clang
|
||||
sudo ln -s --force /usr/bin/ld.lld-$LLVM_VERSION /usr/bin/ld.lld
|
||||
sudo ln -s --force /usr/bin/llvm-objdump-$LLVM_VERSION /usr/bin/llvm-objdump
|
||||
sudo ln -s --force /usr/bin/llvm-ar-$LLVM_VERSION /usr/bin/llvm-ar
|
||||
sudo ln -s --force /usr/bin/llvm-nm-$LLVM_VERSION /usr/bin/llvm-nm
|
||||
sudo ln -s --force /usr/bin/llvm-strip-$LLVM_VERSION /usr/bin/llvm-strip
|
||||
sudo ln -s --force /usr/bin/llvm-objcopy-$LLVM_VERSION /usr/bin/llvm-objcopy
|
||||
sudo ln -s --force /usr/bin/llvm-readelf-$LLVM_VERSION /usr/bin/llvm-readelf
|
||||
sudo ln -s --force /usr/bin/clang++-$LLVM_VERSION /usr/bin/clang++
|
||||
|
||||
- name: Checkout KernelSU
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: KernelSU
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup kernel source
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: microsoft/WSA-Linux-Kernel
|
||||
ref: android-lts/${{ matrix.device_code }}/${{ matrix.version }}
|
||||
path: WSA-Linux-Kernel
|
||||
|
||||
- name: Setup Ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }}
|
||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||
max-size: 2G
|
||||
|
||||
- name: Setup KernelSU
|
||||
working-directory: WSA-Linux-Kernel
|
||||
run: |
|
||||
echo "[+] KernelSU setup"
|
||||
KERNEL_ROOT=$GITHUB_WORKSPACE/WSA-Linux-Kernel
|
||||
echo "[+] KERNEL_ROOT: $KERNEL_ROOT"
|
||||
echo "[+] Copy KernelSU driver to $KERNEL_ROOT/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $KERNEL_ROOT/drivers/kernelsu
|
||||
echo "[+] Add KernelSU driver to Makefile"
|
||||
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
|
||||
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
|
||||
echo "[+] Apply KernelSU patches"
|
||||
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/${{ matrix.kernel_version }}/*.patch
|
||||
echo "[+] KernelSU setup done."
|
||||
cd $GITHUB_WORKSPACE/KernelSU
|
||||
VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "VERSION: $VERSION"
|
||||
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Build Kernel
|
||||
working-directory: WSA-Linux-Kernel
|
||||
run: |
|
||||
cp configs/wsa/${{ matrix.make_config }} .config
|
||||
make olddefconfig
|
||||
make -j`nproc` LLVM=1 ARCH=${{ matrix.arch }} CROSS_COMPILE=${{ matrix.cross_compile }} ${{ matrix.file_name }} CCACHE="/usr/bin/ccache"
|
||||
echo "file_path=WSA-Linux-Kernel/arch/${{ matrix.arch }}/boot/${{ matrix.file_name }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload kernel-${{ matrix.arch }}-${{ matrix.version }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: kernel-WSA-${{ matrix.arch }}-${{ matrix.version }}
|
||||
path: "${{ env.file_path }}"
|
||||
|
||||
- name: Post to Telegram
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
env:
|
||||
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
|
||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
run: |
|
||||
TITLE=kernel-${{ matrix.arch }}-WSA-${{ matrix.version }}
|
||||
echo "[+] title: $TITLE"
|
||||
export TITLE
|
||||
export VERSION="${{ env.kernelsu_version }}"
|
||||
echo "[+] Compress images"
|
||||
gzip -n -f -9 "${{ env.file_path }}"
|
||||
echo "[+] Image to upload"
|
||||
ls -l "${{ env.file_path }}.gz"
|
||||
if [ -n "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
pip3 install python-telegram-bot
|
||||
python3 "$GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py" "${{ env.file_path }}.gz"
|
||||
fi
|
||||
103
.github/workflows/build-kernel.yml
vendored
103
.github/workflows/build-kernel.yml
vendored
@@ -1,103 +0,0 @@
|
||||
name: Build Kernel
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- '.github/workflows/build-kernel.yml'
|
||||
- 'kernel/**'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'kernel/**'
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: android12-5.10-66
|
||||
tag: android12-5.10-2021-11
|
||||
os_version: 12.0.0
|
||||
os_patch_level: 2021-11
|
||||
- version: android12-5.10-81
|
||||
tag: android12-5.10-2022-03
|
||||
os_version: 12.0.0
|
||||
os_patch_level: 2022-03
|
||||
- version: android12-5.10-101
|
||||
tag: android12-5.10-2022-05
|
||||
os_version: 12.0.0
|
||||
os_patch_level: 2022-05
|
||||
- version: android12-5.10-110
|
||||
tag: android12-5.10-2022-07
|
||||
os_version: 12.0.0
|
||||
os_patch_level: 2022-07
|
||||
- version: android12-5.10-136
|
||||
tag: android12-5.10-2022-11
|
||||
os_version: 12.0.0
|
||||
os_patch_level: 2022-11
|
||||
|
||||
name: Build aarch64-${{ matrix.version }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
path: KernelSU
|
||||
- name: Download kernel source
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE
|
||||
git clone https://gerrit.googlesource.com/git-repo
|
||||
mkdir android-kernel && cd android-kernel
|
||||
../git-repo/repo init -u https://android.googlesource.com/kernel/manifest -b common-${{ matrix.tag }}
|
||||
../git-repo/repo sync
|
||||
echo "[+] KernelSU setup"
|
||||
GKI_ROOT=$(pwd)
|
||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
||||
echo "[+] Copy kernel su driver to $GKI_ROOT/common/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
||||
echo "[+] Add kernel su driver to Makefile"
|
||||
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
|
||||
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
|
||||
echo "[+] KernelSU setup Done."
|
||||
curl -Lo gki-kernel.zip https://dl.google.com/android/gki/gki-certified-boot-${{ matrix.tag }}_r1.zip
|
||||
unzip gki-kernel.zip
|
||||
tools/mkbootimg/unpack_bootimg.py --boot_img=$(find . -maxdepth 1 -name "*.img")
|
||||
|
||||
- name: Build boot.img
|
||||
working-directory: android-kernel
|
||||
run: BUILD_BOOT_IMG=1 SKIP_VENDOR_BOOT=1 KERNEL_BINARY=Image GKI_RAMDISK_PREBUILT_BINARY=out/ramdisk AVB_SIGN_BOOT_IMG=1 AVB_BOOT_PARTITION_SIZE=$((64*1024*1024)) AVB_BOOT_ALGORITHM=SHA256_RSA2048 AVB_BOOT_KEY=prebuilts/kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem BOOT_IMAGE_HEADER_VERSION=4 LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
|
||||
|
||||
- name: Build boot-lz4.img
|
||||
working-directory: android-kernel
|
||||
run: |
|
||||
tools/mkbootimg/mkbootimg.py --header_version 4 --kernel ./out/android12-5.10/dist/Image.lz4 --ramdisk out/ramdisk --output ./out/android12-5.10/dist/boot-lz4.img --os_version ${{ matrix.os_version }} --os_patch_level ${{ matrix.os_patch_level }}
|
||||
./build/build-tools/path/linux-x86/avbtool add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image out/android12-5.10/dist/boot-lz4.img --algorithm SHA256_RSA2048 --key ./prebuilts/kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
|
||||
- name: Build boot-gz.img
|
||||
working-directory: android-kernel
|
||||
run: |
|
||||
cat out/android12-5.10/dist/Image | ./prebuilts/build-tools/path/linux-x86/gzip -n -f -9 > out/android12-5.10/dist/Image.gz
|
||||
tools/mkbootimg/mkbootimg.py --header_version 4 --kernel ./out/android12-5.10/dist/Image.gz --ramdisk out/ramdisk --output ./out/android12-5.10/dist/boot-gz.img --os_version ${{ matrix.os_version }} --os_patch_level ${{ matrix.os_patch_level }}
|
||||
./build/build-tools/path/linux-x86/avbtool add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image out/android12-5.10/dist/boot-gz.img --algorithm SHA256_RSA2048 --key ./prebuilts/kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
|
||||
- name: Upload Image.gz
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: kernel-aarch64-${{ matrix.version }}-Image.gz
|
||||
path: android-kernel/out/*/dist/Image.gz
|
||||
|
||||
- name: Upload boot.img
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: kernel-aarch64-${{ matrix.version }}-boot.img
|
||||
path: android-kernel/out/*/dist/boot.img
|
||||
|
||||
- name: Upload boot-lz4.img
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: kernel-aarch64-${{ matrix.version }}-boot-lz4.img
|
||||
path: android-kernel/out/*/dist/boot-lz4.img
|
||||
|
||||
- name: Upload boot-gz.img
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: kernel-aarch64-${{ matrix.version }}-boot-gz.img
|
||||
path: android-kernel/out/*/dist/boot-gz.img
|
||||
25
.github/workflows/build-ksud.yml
vendored
Normal file
25
.github/workflows/build-ksud.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Build KSUD
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "ci" ]
|
||||
paths:
|
||||
- '.github/workflows/build-ksud.yml'
|
||||
- '.github/workflows/ksud.yml'
|
||||
- 'userspace/ksud/**'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- '.github/workflows/build-ksud.yml'
|
||||
- '.github/workflows/ksud.yml'
|
||||
- 'userspace/ksud/**'
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
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 }}
|
||||
70
.github/workflows/build-manager.yml
vendored
70
.github/workflows/build-manager.yml
vendored
@@ -5,24 +5,50 @@ on:
|
||||
paths:
|
||||
- '.github/workflows/build-manager.yml'
|
||||
- 'manager/**'
|
||||
- 'userspace/ksud/**'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'manager/**'
|
||||
workflow_call:
|
||||
jobs:
|
||||
build:
|
||||
build-ksud:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-linux-android
|
||||
- target: x86_64-linux-android
|
||||
uses: ./.github/workflows/ksud.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
build-manager:
|
||||
needs: build-ksud
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./manager
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup need_upload
|
||||
id: need_upload
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'temurin'
|
||||
cache: gradle
|
||||
- uses: nttld/setup-ndk@v1
|
||||
with:
|
||||
ndk-version: r25b
|
||||
local-cache: true
|
||||
- name: Extract keystore
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
run: |
|
||||
@@ -33,13 +59,49 @@ jobs:
|
||||
echo KEYSTORE_FILE='../key.jks' >> sign.properties
|
||||
echo ${{ secrets.KEYSTORE }} | base64 --decode > key.jks
|
||||
fi
|
||||
- name: Download arm64 ksud
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ksud-aarch64-linux-android
|
||||
path: .
|
||||
- name: Download x86_64 ksud
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ksud-x86_64-linux-android
|
||||
path: .
|
||||
- name: Copy ksud to app jniLibs
|
||||
run: |
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
mkdir -p app/src/main/jniLibs/x86_64
|
||||
cp -f ../aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud.so
|
||||
cp -f ../x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud.so
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
run: ./gradlew clean assembleRelease
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: manager
|
||||
path: manager/app/build/outputs/apk/release/*.apk
|
||||
|
||||
- name: Setup mutex for uploading
|
||||
uses: ben-z/gh-action-mutex@v1.0-alpha-7
|
||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
||||
- name: Upload to telegram
|
||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
||||
env:
|
||||
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
|
||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
TITLE: Manager
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
export VERSION=$(git rev-list --count HEAD)
|
||||
APK=$(find ./app/build/outputs/apk/release -name "*.apk")
|
||||
pip3 install python-telegram-bot
|
||||
python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK
|
||||
fi
|
||||
|
||||
62
.github/workflows/build-su.yml
vendored
Normal file
62
.github/workflows/build-su.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
name: Build SU
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- '.github/workflows/build-su.yml'
|
||||
- 'userspace/su/**'
|
||||
- 'scripts/ksubot.py'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'userspace/su/**'
|
||||
jobs:
|
||||
build-su:
|
||||
name: Build userspace su
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup need_upload
|
||||
id: need_upload
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- uses: nttld/setup-ndk@v1
|
||||
with:
|
||||
ndk-version: r25b
|
||||
local-cache: true
|
||||
- name: Build su
|
||||
working-directory: ./userspace/su
|
||||
run: ndk-build
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: su
|
||||
path: ./userspace/su/libs
|
||||
- name: Setup mutex for uploading
|
||||
uses: ben-z/gh-action-mutex@v1.0-alpha-7
|
||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
||||
- name: Upload to telegram
|
||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
||||
env:
|
||||
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
|
||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
TITLE: SU
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
export VERSION=$(git rev-list --count HEAD)
|
||||
pip3 install python-telegram-bot
|
||||
mv ./userspace/su/libs/arm64-v8a/su su-arm64
|
||||
mv ./userspace/su/libs/x86_64/su su-x86_64
|
||||
python3 scripts/ksubot.py su-arm64 su-x86_64
|
||||
fi
|
||||
29
.github/workflows/build-userspace.yml
vendored
29
.github/workflows/build-userspace.yml
vendored
@@ -1,29 +0,0 @@
|
||||
name: Build Userspace
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- '.github/workflows/build-userspace.yml'
|
||||
- 'userspace/**'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'userspace/**'
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: nttld/setup-ndk@v1
|
||||
with:
|
||||
ndk-version: r25b
|
||||
local-cache: true
|
||||
- name: Build with NDK
|
||||
working-directory: ./userspace
|
||||
run: ndk-build
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: su
|
||||
path: ./userspace/libs
|
||||
|
||||
36
.github/workflows/clippy.yml
vendored
Normal file
36
.github/workflows/clippy.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: Clippy check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- '.github/workflows/clippy.yml'
|
||||
- 'userspace/ksud/**'
|
||||
pull_request:
|
||||
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 --release
|
||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android --release
|
||||
37
.github/workflows/deploy-website.yml
vendored
Normal file
37
.github/workflows/deploy-website.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Deploy Website
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- website
|
||||
paths:
|
||||
- '.github/workflows/deploy-website.yml'
|
||||
- 'website/**'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./website
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: yarn
|
||||
cache-dependency-path: website/yarn.lock
|
||||
- run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: yarn docs:build
|
||||
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: website/docs/.vitepress/dist
|
||||
cname: kernelsu.org # if wanna deploy to custom domain
|
||||
152
.github/workflows/gki-kernel.yml
vendored
Normal file
152
.github/workflows/gki-kernel.yml
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
name: GKI Kernel Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
description: >
|
||||
Output directory of gki,
|
||||
for example: android12-5.10
|
||||
version_name:
|
||||
required: true
|
||||
type: string
|
||||
description: >
|
||||
With SUBLEVEL of kernel,
|
||||
for example: android12-5.10.66
|
||||
tag:
|
||||
required: true
|
||||
type: string
|
||||
description: >
|
||||
Part of branch name of common kernel manifest,
|
||||
for example: android12-5.10-2021-11
|
||||
os_patch_level:
|
||||
required: false
|
||||
type: string
|
||||
description: >
|
||||
Patch level of common kernel manifest,
|
||||
for example: 2021-11
|
||||
default: 2022-05
|
||||
patch_path:
|
||||
required: true
|
||||
type: string
|
||||
description: >
|
||||
Directory name of .github/patches/<patch_path>
|
||||
for example: 5.10
|
||||
use_cache:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
embed_ksud:
|
||||
required: false
|
||||
type: string
|
||||
default: ksud-aarch64-linux-android
|
||||
description: >
|
||||
Artifact name of prebuilt ksud to be embedded
|
||||
for example: ksud-aarch64-linux-android
|
||||
secrets:
|
||||
BOOT_SIGN_KEY:
|
||||
required: false
|
||||
CHAT_ID:
|
||||
required: false
|
||||
CACHE_CHAT_ID:
|
||||
required: false
|
||||
BOT_TOKEN:
|
||||
required: false
|
||||
MESSAGE_THREAD_ID:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build ${{ inputs.version_name }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
|
||||
CCACHE_NOHASHDIR: "true"
|
||||
CCACHE_HARDLINK: "true"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
path: KernelSU
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup need_upload
|
||||
id: need_upload
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Setup kernel source
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE
|
||||
git clone https://gerrit.googlesource.com/git-repo
|
||||
mkdir android-kernel && cd android-kernel
|
||||
../git-repo/repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }}
|
||||
../git-repo/repo sync -j$(nproc --all)
|
||||
|
||||
- name: Setup KernelSU
|
||||
env:
|
||||
PATCH_PATH: ${{ inputs.patch_path }}
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/android-kernel
|
||||
echo "[+] KernelSU setup"
|
||||
GKI_ROOT=$(pwd)
|
||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
||||
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
||||
echo "[+] Add KernelSU driver to Makefile"
|
||||
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
|
||||
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
|
||||
echo "[+] Apply KernelSU patches"
|
||||
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch
|
||||
echo "[+] KernelSU setup done."
|
||||
|
||||
- name: Symbol magic
|
||||
run: |
|
||||
echo "[+] Export all symbol from abi_gki_aarch64.xml"
|
||||
COMMON_ROOT=$GITHUB_WORKSPACE/android-kernel/common
|
||||
KSU_ROOT=$GITHUB_WORKSPACE/KernelSU
|
||||
ABI_XML=$COMMON_ROOT/android/abi_gki_aarch64.xml
|
||||
SYMBOL_LIST=$COMMON_ROOT/android/abi_gki_aarch64
|
||||
# python3 $KSU_ROOT/scripts/abi_gki_all.py $ABI_XML > $SYMBOL_LIST
|
||||
echo "[+] Add KernelSU symbols"
|
||||
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
|
||||
|
||||
- name: Setup ccache
|
||||
if: inputs.use_cache == true
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: gki-kernel-aarch64-${{ inputs.version_name }}
|
||||
max-size: 2G
|
||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||
|
||||
- name: Build boot.img
|
||||
working-directory: android-kernel
|
||||
run: CCACHE="/usr/bin/ccache" LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
|
||||
|
||||
- name: Prepare artifacts
|
||||
id: prepareArtifacts
|
||||
run: |
|
||||
OUTDIR=android-kernel/out/${{ inputs.version }}/dist
|
||||
mkdir output
|
||||
cp $OUTDIR/Image ./output/
|
||||
cp $OUTDIR/Image.lz4 ./output/
|
||||
git clone https://github.com/Kernel-SU/AnyKernel3
|
||||
rm -rf ./AnyKernel3/.git
|
||||
cp $OUTDIR/Image ./AnyKernel3/
|
||||
|
||||
- name: Upload Image and Image.gz
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Image-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
|
||||
path: ./output/*
|
||||
|
||||
- name: Upload AnyKernel3
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
|
||||
path: ./AnyKernel3/*
|
||||
36
.github/workflows/ksud.yml
vendored
Normal file
36
.github/workflows/ksud.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: Build ksud
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
use_cache:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# cross build failed after Rust 1.68, see https://github.com/cross-rs/cross/issues/1222
|
||||
- run: rustup default 1.67.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
|
||||
|
||||
- name: Upload ksud artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ksud-${{ inputs.target }}
|
||||
path: userspace/ksud/target/**/release/ksud
|
||||
56
.github/workflows/release.yml
vendored
Normal file
56
.github/workflows/release.yml
vendored
Normal 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-kernel-wsa.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
33
.github/workflows/rustfmt.yml
vendored
Normal 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.3.1
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
options: --manifest-path userspace/ksud/Cargo.toml
|
||||
27
.github/workflows/shellcheck.yml
vendored
Normal file
27
.github/workflows/shellcheck.yml
vendored
Normal 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
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.vscode/
|
||||
36
README.md
36
README.md
@@ -1,32 +1,38 @@
|
||||
**English** | [中文](README_CN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
A Kernel based root solution for Android GKI.
|
||||
A Kernel based root solution for Android devices.
|
||||
|
||||
## Features
|
||||
|
||||
1. Kernel-based `su` and root access management.
|
||||
2. Module system based on overlayfs.
|
||||
|
||||
## Compatibility State
|
||||
|
||||
KernelSU officially supports Android GKI 2.0 devices(with kernel 5.10+), old kernels(4.14+) is also compatiable, but you need to build kernel yourself.
|
||||
|
||||
WSA and containter-based Android should also work with KernelSU integrated.
|
||||
|
||||
And the current supported ABIs are : `arm64-v8a` and `x86_64`
|
||||
|
||||
## Usage
|
||||
|
||||
1. Flash a custom kernel with KernelSU, you can build it yourself or [download it from CI](https://github.com/tiann/KernelSU/actions/workflows/build-kernel.yml).
|
||||
2. Install Manager App and enjoy :)
|
||||
[Installation](https://kernelsu.org/guide/installation.html)
|
||||
|
||||
## Build
|
||||
|
||||
### Build GKI Kernel
|
||||
|
||||
1. Download the GKI source first, you can refer the [GKI build instruction](https://source.android.com/docs/setup/build/building-kernels)
|
||||
2. cd `<GKI kernel source dir>`
|
||||
3. `curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -`
|
||||
4. Build the kernel.
|
||||
|
||||
### Build the Manager App
|
||||
|
||||
Android Studio / Gradle
|
||||
[How to build?](https://kernelsu.org/guide/how-to-build.html)
|
||||
|
||||
### Discussion
|
||||
|
||||
[@KernelSU](https://t.me/KernelSU)
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
42
README_CN.md
Normal file
42
README_CN.md
Normal file
@@ -0,0 +1,42 @@
|
||||
[English](README.md) | **中文**
|
||||
|
||||
# KernelSU
|
||||
|
||||
一个 Android 上基于内核的 root 方案。
|
||||
|
||||
## 特性
|
||||
|
||||
- 基于内核的 su 和权限管理。
|
||||
- 基于 overlayfs 的模块系统。
|
||||
|
||||
## 兼容状态
|
||||
|
||||
KernelSU 官方支持 GKI 2.0 的设备(内核版本5.10以上);旧内核也是兼容的(最低4.14+),不过需要自己编译内核。
|
||||
|
||||
WSA 和运行在容器上的 Android 也可以与 KernelSU 一起工作。
|
||||
|
||||
目前支持架构 : `arm64-v8a` 和 `x86_64`
|
||||
|
||||
## 使用方法
|
||||
|
||||
[安装教程](https://kernelsu.org/zh_CN/guide/installation.html)
|
||||
|
||||
## 构建
|
||||
|
||||
[如何构建?](https://kernelsu.org/zh_CN/guide/how-to-build.html)
|
||||
|
||||
### 讨论
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## 许可证
|
||||
|
||||
- 目录 `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)
|
||||
|
||||
## 鸣谢
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/):KernelSU 的灵感。
|
||||
- [true](https://github.com/brevent/genuine/):apk v2 签名验证。
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技巧。
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk):sepolicy 的实现。
|
||||
548
kernel/.clang-format
Normal file
548
kernel/.clang-format
Normal file
@@ -0,0 +1,548 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# clang-format configuration file. Intended for clang-format >= 4.
|
||||
#
|
||||
# For more information, see:
|
||||
#
|
||||
# Documentation/process/clang-format.rst
|
||||
# https://clang.llvm.org/docs/ClangFormat.html
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
#
|
||||
---
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
#AlignEscapedNewlines: Left # Unknown to clang-format-4.0
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
#AfterExternBlock: false # Unknown to clang-format-5.0
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
#SplitEmptyFunction: true # Unknown to clang-format-4.0
|
||||
#SplitEmptyRecord: true # Unknown to clang-format-4.0
|
||||
#SplitEmptyNamespace: true # Unknown to clang-format-4.0
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Custom
|
||||
#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
#CompactNamespaces: false # Unknown to clang-format-4.0
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 8
|
||||
ContinuationIndentWidth: 8
|
||||
Cpp11BracedListStyle: false
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
#FixNamespaceComments: false # Unknown to clang-format-4.0
|
||||
|
||||
# Taken from:
|
||||
# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \
|
||||
# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \
|
||||
# | sort | uniq
|
||||
ForEachMacros:
|
||||
- 'apei_estatus_for_each_section'
|
||||
- 'ata_for_each_dev'
|
||||
- 'ata_for_each_link'
|
||||
- '__ata_qc_for_each'
|
||||
- 'ata_qc_for_each'
|
||||
- 'ata_qc_for_each_raw'
|
||||
- 'ata_qc_for_each_with_internal'
|
||||
- 'ax25_for_each'
|
||||
- 'ax25_uid_for_each'
|
||||
- '__bio_for_each_bvec'
|
||||
- 'bio_for_each_bvec'
|
||||
- 'bio_for_each_bvec_all'
|
||||
- 'bio_for_each_integrity_vec'
|
||||
- '__bio_for_each_segment'
|
||||
- 'bio_for_each_segment'
|
||||
- 'bio_for_each_segment_all'
|
||||
- 'bio_list_for_each'
|
||||
- 'bip_for_each_vec'
|
||||
- 'bitmap_for_each_clear_region'
|
||||
- 'bitmap_for_each_set_region'
|
||||
- 'blkg_for_each_descendant_post'
|
||||
- 'blkg_for_each_descendant_pre'
|
||||
- 'blk_queue_for_each_rl'
|
||||
- 'bond_for_each_slave'
|
||||
- 'bond_for_each_slave_rcu'
|
||||
- 'bpf_for_each_spilled_reg'
|
||||
- 'btree_for_each_safe128'
|
||||
- 'btree_for_each_safe32'
|
||||
- 'btree_for_each_safe64'
|
||||
- 'btree_for_each_safel'
|
||||
- 'card_for_each_dev'
|
||||
- 'cgroup_taskset_for_each'
|
||||
- 'cgroup_taskset_for_each_leader'
|
||||
- 'cpufreq_for_each_entry'
|
||||
- 'cpufreq_for_each_entry_idx'
|
||||
- 'cpufreq_for_each_valid_entry'
|
||||
- 'cpufreq_for_each_valid_entry_idx'
|
||||
- 'css_for_each_child'
|
||||
- 'css_for_each_descendant_post'
|
||||
- 'css_for_each_descendant_pre'
|
||||
- 'device_for_each_child_node'
|
||||
- 'dma_fence_chain_for_each'
|
||||
- 'do_for_each_ftrace_op'
|
||||
- 'drm_atomic_crtc_for_each_plane'
|
||||
- 'drm_atomic_crtc_state_for_each_plane'
|
||||
- 'drm_atomic_crtc_state_for_each_plane_state'
|
||||
- 'drm_atomic_for_each_plane_damage'
|
||||
- 'drm_client_for_each_connector_iter'
|
||||
- 'drm_client_for_each_modeset'
|
||||
- 'drm_connector_for_each_possible_encoder'
|
||||
- 'drm_for_each_bridge_in_chain'
|
||||
- 'drm_for_each_connector_iter'
|
||||
- 'drm_for_each_crtc'
|
||||
- 'drm_for_each_encoder'
|
||||
- 'drm_for_each_encoder_mask'
|
||||
- 'drm_for_each_fb'
|
||||
- 'drm_for_each_legacy_plane'
|
||||
- 'drm_for_each_plane'
|
||||
- 'drm_for_each_plane_mask'
|
||||
- 'drm_for_each_privobj'
|
||||
- 'drm_mm_for_each_hole'
|
||||
- 'drm_mm_for_each_node'
|
||||
- 'drm_mm_for_each_node_in_range'
|
||||
- 'drm_mm_for_each_node_safe'
|
||||
- 'flow_action_for_each'
|
||||
- 'for_each_active_dev_scope'
|
||||
- 'for_each_active_drhd_unit'
|
||||
- 'for_each_active_iommu'
|
||||
- 'for_each_aggr_pgid'
|
||||
- 'for_each_available_child_of_node'
|
||||
- 'for_each_bio'
|
||||
- 'for_each_board_func_rsrc'
|
||||
- 'for_each_bvec'
|
||||
- 'for_each_card_auxs'
|
||||
- 'for_each_card_auxs_safe'
|
||||
- 'for_each_card_components'
|
||||
- 'for_each_card_dapms'
|
||||
- 'for_each_card_pre_auxs'
|
||||
- 'for_each_card_prelinks'
|
||||
- 'for_each_card_rtds'
|
||||
- 'for_each_card_rtds_safe'
|
||||
- 'for_each_card_widgets'
|
||||
- 'for_each_card_widgets_safe'
|
||||
- 'for_each_cgroup_storage_type'
|
||||
- 'for_each_child_of_node'
|
||||
- 'for_each_clear_bit'
|
||||
- 'for_each_clear_bit_from'
|
||||
- 'for_each_cmsghdr'
|
||||
- 'for_each_compatible_node'
|
||||
- 'for_each_component_dais'
|
||||
- 'for_each_component_dais_safe'
|
||||
- 'for_each_comp_order'
|
||||
- 'for_each_console'
|
||||
- 'for_each_cpu'
|
||||
- 'for_each_cpu_and'
|
||||
- 'for_each_cpu_not'
|
||||
- 'for_each_cpu_wrap'
|
||||
- 'for_each_dapm_widgets'
|
||||
- 'for_each_dev_addr'
|
||||
- 'for_each_dev_scope'
|
||||
- 'for_each_displayid_db'
|
||||
- 'for_each_dma_cap_mask'
|
||||
- 'for_each_dpcm_be'
|
||||
- 'for_each_dpcm_be_rollback'
|
||||
- 'for_each_dpcm_be_safe'
|
||||
- 'for_each_dpcm_fe'
|
||||
- 'for_each_drhd_unit'
|
||||
- 'for_each_dss_dev'
|
||||
- 'for_each_efi_memory_desc'
|
||||
- 'for_each_efi_memory_desc_in_map'
|
||||
- 'for_each_element'
|
||||
- 'for_each_element_extid'
|
||||
- 'for_each_element_id'
|
||||
- 'for_each_endpoint_of_node'
|
||||
- 'for_each_evictable_lru'
|
||||
- 'for_each_fib6_node_rt_rcu'
|
||||
- 'for_each_fib6_walker_rt'
|
||||
- 'for_each_free_mem_pfn_range_in_zone'
|
||||
- 'for_each_free_mem_pfn_range_in_zone_from'
|
||||
- 'for_each_free_mem_range'
|
||||
- 'for_each_free_mem_range_reverse'
|
||||
- 'for_each_func_rsrc'
|
||||
- 'for_each_hstate'
|
||||
- 'for_each_if'
|
||||
- 'for_each_iommu'
|
||||
- 'for_each_ip_tunnel_rcu'
|
||||
- 'for_each_irq_nr'
|
||||
- 'for_each_link_codecs'
|
||||
- 'for_each_link_cpus'
|
||||
- 'for_each_link_platforms'
|
||||
- 'for_each_lru'
|
||||
- 'for_each_matching_node'
|
||||
- 'for_each_matching_node_and_match'
|
||||
- 'for_each_member'
|
||||
- 'for_each_mem_region'
|
||||
- 'for_each_memblock_type'
|
||||
- 'for_each_memcg_cache_index'
|
||||
- 'for_each_mem_pfn_range'
|
||||
- '__for_each_mem_range'
|
||||
- 'for_each_mem_range'
|
||||
- '__for_each_mem_range_rev'
|
||||
- 'for_each_mem_range_rev'
|
||||
- 'for_each_migratetype_order'
|
||||
- 'for_each_msi_entry'
|
||||
- 'for_each_msi_entry_safe'
|
||||
- 'for_each_net'
|
||||
- 'for_each_net_continue_reverse'
|
||||
- 'for_each_netdev'
|
||||
- 'for_each_netdev_continue'
|
||||
- 'for_each_netdev_continue_rcu'
|
||||
- 'for_each_netdev_continue_reverse'
|
||||
- 'for_each_netdev_feature'
|
||||
- 'for_each_netdev_in_bond_rcu'
|
||||
- 'for_each_netdev_rcu'
|
||||
- 'for_each_netdev_reverse'
|
||||
- 'for_each_netdev_safe'
|
||||
- 'for_each_net_rcu'
|
||||
- 'for_each_new_connector_in_state'
|
||||
- 'for_each_new_crtc_in_state'
|
||||
- 'for_each_new_mst_mgr_in_state'
|
||||
- 'for_each_new_plane_in_state'
|
||||
- 'for_each_new_private_obj_in_state'
|
||||
- 'for_each_node'
|
||||
- 'for_each_node_by_name'
|
||||
- 'for_each_node_by_type'
|
||||
- 'for_each_node_mask'
|
||||
- 'for_each_node_state'
|
||||
- 'for_each_node_with_cpus'
|
||||
- 'for_each_node_with_property'
|
||||
- 'for_each_nonreserved_multicast_dest_pgid'
|
||||
- 'for_each_of_allnodes'
|
||||
- 'for_each_of_allnodes_from'
|
||||
- 'for_each_of_cpu_node'
|
||||
- 'for_each_of_pci_range'
|
||||
- 'for_each_old_connector_in_state'
|
||||
- 'for_each_old_crtc_in_state'
|
||||
- 'for_each_old_mst_mgr_in_state'
|
||||
- 'for_each_oldnew_connector_in_state'
|
||||
- 'for_each_oldnew_crtc_in_state'
|
||||
- 'for_each_oldnew_mst_mgr_in_state'
|
||||
- 'for_each_oldnew_plane_in_state'
|
||||
- 'for_each_oldnew_plane_in_state_reverse'
|
||||
- 'for_each_oldnew_private_obj_in_state'
|
||||
- 'for_each_old_plane_in_state'
|
||||
- 'for_each_old_private_obj_in_state'
|
||||
- 'for_each_online_cpu'
|
||||
- 'for_each_online_node'
|
||||
- 'for_each_online_pgdat'
|
||||
- 'for_each_pci_bridge'
|
||||
- 'for_each_pci_dev'
|
||||
- 'for_each_pci_msi_entry'
|
||||
- 'for_each_pcm_streams'
|
||||
- 'for_each_physmem_range'
|
||||
- 'for_each_populated_zone'
|
||||
- 'for_each_possible_cpu'
|
||||
- 'for_each_present_cpu'
|
||||
- 'for_each_prime_number'
|
||||
- 'for_each_prime_number_from'
|
||||
- 'for_each_process'
|
||||
- 'for_each_process_thread'
|
||||
- 'for_each_property_of_node'
|
||||
- 'for_each_registered_fb'
|
||||
- 'for_each_requested_gpio'
|
||||
- 'for_each_requested_gpio_in_range'
|
||||
- 'for_each_reserved_mem_range'
|
||||
- 'for_each_reserved_mem_region'
|
||||
- 'for_each_rtd_codec_dais'
|
||||
- 'for_each_rtd_codec_dais_rollback'
|
||||
- 'for_each_rtd_components'
|
||||
- 'for_each_rtd_cpu_dais'
|
||||
- 'for_each_rtd_cpu_dais_rollback'
|
||||
- 'for_each_rtd_dais'
|
||||
- 'for_each_set_bit'
|
||||
- 'for_each_set_bit_from'
|
||||
- 'for_each_set_clump8'
|
||||
- 'for_each_sg'
|
||||
- 'for_each_sg_dma_page'
|
||||
- 'for_each_sg_page'
|
||||
- 'for_each_sgtable_dma_page'
|
||||
- 'for_each_sgtable_dma_sg'
|
||||
- 'for_each_sgtable_page'
|
||||
- 'for_each_sgtable_sg'
|
||||
- 'for_each_sibling_event'
|
||||
- 'for_each_subelement'
|
||||
- 'for_each_subelement_extid'
|
||||
- 'for_each_subelement_id'
|
||||
- '__for_each_thread'
|
||||
- 'for_each_thread'
|
||||
- 'for_each_unicast_dest_pgid'
|
||||
- 'for_each_wakeup_source'
|
||||
- 'for_each_zone'
|
||||
- 'for_each_zone_zonelist'
|
||||
- 'for_each_zone_zonelist_nodemask'
|
||||
- 'fwnode_for_each_available_child_node'
|
||||
- 'fwnode_for_each_child_node'
|
||||
- 'fwnode_graph_for_each_endpoint'
|
||||
- 'gadget_for_each_ep'
|
||||
- 'genradix_for_each'
|
||||
- 'genradix_for_each_from'
|
||||
- 'hash_for_each'
|
||||
- 'hash_for_each_possible'
|
||||
- 'hash_for_each_possible_rcu'
|
||||
- 'hash_for_each_possible_rcu_notrace'
|
||||
- 'hash_for_each_possible_safe'
|
||||
- 'hash_for_each_rcu'
|
||||
- 'hash_for_each_safe'
|
||||
- 'hctx_for_each_ctx'
|
||||
- 'hlist_bl_for_each_entry'
|
||||
- 'hlist_bl_for_each_entry_rcu'
|
||||
- 'hlist_bl_for_each_entry_safe'
|
||||
- 'hlist_for_each'
|
||||
- 'hlist_for_each_entry'
|
||||
- 'hlist_for_each_entry_continue'
|
||||
- 'hlist_for_each_entry_continue_rcu'
|
||||
- 'hlist_for_each_entry_continue_rcu_bh'
|
||||
- 'hlist_for_each_entry_from'
|
||||
- 'hlist_for_each_entry_from_rcu'
|
||||
- 'hlist_for_each_entry_rcu'
|
||||
- 'hlist_for_each_entry_rcu_bh'
|
||||
- 'hlist_for_each_entry_rcu_notrace'
|
||||
- 'hlist_for_each_entry_safe'
|
||||
- '__hlist_for_each_rcu'
|
||||
- 'hlist_for_each_safe'
|
||||
- 'hlist_nulls_for_each_entry'
|
||||
- 'hlist_nulls_for_each_entry_from'
|
||||
- 'hlist_nulls_for_each_entry_rcu'
|
||||
- 'hlist_nulls_for_each_entry_safe'
|
||||
- 'i3c_bus_for_each_i2cdev'
|
||||
- 'i3c_bus_for_each_i3cdev'
|
||||
- 'ide_host_for_each_port'
|
||||
- 'ide_port_for_each_dev'
|
||||
- 'ide_port_for_each_present_dev'
|
||||
- 'idr_for_each_entry'
|
||||
- 'idr_for_each_entry_continue'
|
||||
- 'idr_for_each_entry_continue_ul'
|
||||
- 'idr_for_each_entry_ul'
|
||||
- 'in_dev_for_each_ifa_rcu'
|
||||
- 'in_dev_for_each_ifa_rtnl'
|
||||
- 'inet_bind_bucket_for_each'
|
||||
- 'inet_lhash2_for_each_icsk_rcu'
|
||||
- 'key_for_each'
|
||||
- 'key_for_each_safe'
|
||||
- 'klp_for_each_func'
|
||||
- 'klp_for_each_func_safe'
|
||||
- 'klp_for_each_func_static'
|
||||
- 'klp_for_each_object'
|
||||
- 'klp_for_each_object_safe'
|
||||
- 'klp_for_each_object_static'
|
||||
- 'kunit_suite_for_each_test_case'
|
||||
- 'kvm_for_each_memslot'
|
||||
- 'kvm_for_each_vcpu'
|
||||
- 'list_for_each'
|
||||
- 'list_for_each_codec'
|
||||
- 'list_for_each_codec_safe'
|
||||
- 'list_for_each_continue'
|
||||
- 'list_for_each_entry'
|
||||
- 'list_for_each_entry_continue'
|
||||
- 'list_for_each_entry_continue_rcu'
|
||||
- 'list_for_each_entry_continue_reverse'
|
||||
- 'list_for_each_entry_from'
|
||||
- 'list_for_each_entry_from_rcu'
|
||||
- 'list_for_each_entry_from_reverse'
|
||||
- 'list_for_each_entry_lockless'
|
||||
- 'list_for_each_entry_rcu'
|
||||
- 'list_for_each_entry_reverse'
|
||||
- 'list_for_each_entry_safe'
|
||||
- 'list_for_each_entry_safe_continue'
|
||||
- 'list_for_each_entry_safe_from'
|
||||
- 'list_for_each_entry_safe_reverse'
|
||||
- 'list_for_each_prev'
|
||||
- 'list_for_each_prev_safe'
|
||||
- 'list_for_each_safe'
|
||||
- 'llist_for_each'
|
||||
- 'llist_for_each_entry'
|
||||
- 'llist_for_each_entry_safe'
|
||||
- 'llist_for_each_safe'
|
||||
- 'mci_for_each_dimm'
|
||||
- 'media_device_for_each_entity'
|
||||
- 'media_device_for_each_intf'
|
||||
- 'media_device_for_each_link'
|
||||
- 'media_device_for_each_pad'
|
||||
- 'nanddev_io_for_each_page'
|
||||
- 'netdev_for_each_lower_dev'
|
||||
- 'netdev_for_each_lower_private'
|
||||
- 'netdev_for_each_lower_private_rcu'
|
||||
- 'netdev_for_each_mc_addr'
|
||||
- 'netdev_for_each_uc_addr'
|
||||
- 'netdev_for_each_upper_dev_rcu'
|
||||
- 'netdev_hw_addr_list_for_each'
|
||||
- 'nft_rule_for_each_expr'
|
||||
- 'nla_for_each_attr'
|
||||
- 'nla_for_each_nested'
|
||||
- 'nlmsg_for_each_attr'
|
||||
- 'nlmsg_for_each_msg'
|
||||
- 'nr_neigh_for_each'
|
||||
- 'nr_neigh_for_each_safe'
|
||||
- 'nr_node_for_each'
|
||||
- 'nr_node_for_each_safe'
|
||||
- 'of_for_each_phandle'
|
||||
- 'of_property_for_each_string'
|
||||
- 'of_property_for_each_u32'
|
||||
- 'pci_bus_for_each_resource'
|
||||
- 'pcm_for_each_format'
|
||||
- 'ping_portaddr_for_each_entry'
|
||||
- 'plist_for_each'
|
||||
- 'plist_for_each_continue'
|
||||
- 'plist_for_each_entry'
|
||||
- 'plist_for_each_entry_continue'
|
||||
- 'plist_for_each_entry_safe'
|
||||
- 'plist_for_each_safe'
|
||||
- 'pnp_for_each_card'
|
||||
- 'pnp_for_each_dev'
|
||||
- 'protocol_for_each_card'
|
||||
- 'protocol_for_each_dev'
|
||||
- 'queue_for_each_hw_ctx'
|
||||
- 'radix_tree_for_each_slot'
|
||||
- 'radix_tree_for_each_tagged'
|
||||
- 'rbtree_postorder_for_each_entry_safe'
|
||||
- 'rdma_for_each_block'
|
||||
- 'rdma_for_each_port'
|
||||
- 'rdma_umem_for_each_dma_block'
|
||||
- 'resource_list_for_each_entry'
|
||||
- 'resource_list_for_each_entry_safe'
|
||||
- 'rhl_for_each_entry_rcu'
|
||||
- 'rhl_for_each_rcu'
|
||||
- 'rht_for_each'
|
||||
- 'rht_for_each_entry'
|
||||
- 'rht_for_each_entry_from'
|
||||
- 'rht_for_each_entry_rcu'
|
||||
- 'rht_for_each_entry_rcu_from'
|
||||
- 'rht_for_each_entry_safe'
|
||||
- 'rht_for_each_from'
|
||||
- 'rht_for_each_rcu'
|
||||
- 'rht_for_each_rcu_from'
|
||||
- '__rq_for_each_bio'
|
||||
- 'rq_for_each_bvec'
|
||||
- 'rq_for_each_segment'
|
||||
- 'scsi_for_each_prot_sg'
|
||||
- 'scsi_for_each_sg'
|
||||
- 'sctp_for_each_hentry'
|
||||
- 'sctp_skb_for_each'
|
||||
- 'shdma_for_each_chan'
|
||||
- '__shost_for_each_device'
|
||||
- 'shost_for_each_device'
|
||||
- 'sk_for_each'
|
||||
- 'sk_for_each_bound'
|
||||
- 'sk_for_each_entry_offset_rcu'
|
||||
- 'sk_for_each_from'
|
||||
- 'sk_for_each_rcu'
|
||||
- 'sk_for_each_safe'
|
||||
- 'sk_nulls_for_each'
|
||||
- 'sk_nulls_for_each_from'
|
||||
- 'sk_nulls_for_each_rcu'
|
||||
- 'snd_array_for_each'
|
||||
- 'snd_pcm_group_for_each_entry'
|
||||
- 'snd_soc_dapm_widget_for_each_path'
|
||||
- 'snd_soc_dapm_widget_for_each_path_safe'
|
||||
- 'snd_soc_dapm_widget_for_each_sink_path'
|
||||
- 'snd_soc_dapm_widget_for_each_source_path'
|
||||
- 'tb_property_for_each'
|
||||
- 'tcf_exts_for_each_action'
|
||||
- 'udp_portaddr_for_each_entry'
|
||||
- 'udp_portaddr_for_each_entry_rcu'
|
||||
- 'usb_hub_for_each_child'
|
||||
- 'v4l2_device_for_each_subdev'
|
||||
- 'v4l2_m2m_for_each_dst_buf'
|
||||
- 'v4l2_m2m_for_each_dst_buf_safe'
|
||||
- 'v4l2_m2m_for_each_src_buf'
|
||||
- 'v4l2_m2m_for_each_src_buf_safe'
|
||||
- 'virtio_device_for_each_vq'
|
||||
- 'while_for_each_ftrace_op'
|
||||
- 'xa_for_each'
|
||||
- 'xa_for_each_marked'
|
||||
- 'xa_for_each_range'
|
||||
- 'xa_for_each_start'
|
||||
- 'xas_for_each'
|
||||
- 'xas_for_each_conflict'
|
||||
- 'xas_for_each_marked'
|
||||
- 'xbc_array_for_each_value'
|
||||
- 'xbc_for_each_key_value'
|
||||
- 'xbc_node_for_each_array_value'
|
||||
- 'xbc_node_for_each_child'
|
||||
- 'xbc_node_for_each_key_value'
|
||||
- 'zorro_for_each_dev'
|
||||
|
||||
#IncludeBlocks: Preserve # Unknown to clang-format-5.0
|
||||
IncludeCategories:
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IndentCaseLabels: false
|
||||
#IndentPPDirectives: None # Unknown to clang-format-5.0
|
||||
IndentWidth: 8
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
|
||||
ObjCBlockIndentWidth: 8
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
|
||||
# Taken from git's rules
|
||||
#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
|
||||
PenaltyBreakBeforeFirstCallParameter: 30
|
||||
PenaltyBreakComment: 10
|
||||
PenaltyBreakFirstLessLess: 0
|
||||
PenaltyBreakString: 10
|
||||
PenaltyExcessCharacter: 100
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
|
||||
PointerAlignment: Right
|
||||
ReflowComments: false
|
||||
SortIncludes: false
|
||||
#SortUsingDeclarations: false # Unknown to clang-format-4.0
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0
|
||||
#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0
|
||||
SpaceBeforeParens: ControlStatements
|
||||
#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp03
|
||||
TabWidth: 8
|
||||
UseTab: Always
|
||||
...
|
||||
@@ -1,3 +1,4 @@
|
||||
Diagnostics:
|
||||
UnusedIncludes: Strict
|
||||
ClangTidy:
|
||||
Remove: bugprone-sizeof-expression
|
||||
@@ -2,5 +2,13 @@ config KSU
|
||||
tristate "KernelSU module"
|
||||
default y
|
||||
depends on KPROBES
|
||||
depends on OVERLAY_FS
|
||||
help
|
||||
This is the KSU privilege driver for android system.
|
||||
|
||||
config KSU_DEBUG
|
||||
tristate "KernelSU module debug mode"
|
||||
default n
|
||||
depends on KSU
|
||||
help
|
||||
This enables debug mode for KSU
|
||||
339
kernel/LICENSE
Normal file
339
kernel/LICENSE
Normal 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.
|
||||
@@ -1,14 +1,32 @@
|
||||
obj-y += ksu.o
|
||||
obj-y += allowlist.o
|
||||
obj-y += apk_sign.o
|
||||
kernelsu-objs := apk_sign.o
|
||||
obj-y += kernelsu.o
|
||||
obj-y += module_api.o
|
||||
obj-y += sucompat.o
|
||||
obj-y += uid_observer.o
|
||||
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)
|
||||
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count HEAD)
|
||||
ccflags-y += -DKSU_GIT_VERSION=$(KSU_GIT_VERSION)
|
||||
endif
|
||||
|
||||
ifndef EXPECTED_SIZE
|
||||
EXPECTED_SIZE := 0x033b
|
||||
endif
|
||||
|
||||
ifndef EXPECTED_HASH
|
||||
EXPECTED_HASH := 0xb0b91415
|
||||
endif
|
||||
|
||||
ccflags-y += -DEXPECTED_SIZE=$(EXPECTED_SIZE)
|
||||
ccflags-y += -DEXPECTED_HASH=$(EXPECTED_HASH)
|
||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||
ccflags-y += -Wno-macro-redefined -Wno-declaration-after-statement
|
||||
ccflags-y += -Wno-macro-redefined -Wno-declaration-after-statement
|
||||
|
||||
@@ -1,30 +1,19 @@
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/memory.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/uidgid.h>
|
||||
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fs_struct.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/rcupdate.h>
|
||||
|
||||
#include <linux/delay.h> // msleep
|
||||
|
||||
#include "klog.h"
|
||||
#include "linux/delay.h"
|
||||
#include "linux/fs.h"
|
||||
#include "linux/kernel.h"
|
||||
#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
|
||||
|
||||
static DEFINE_MUTEX(allowlist_mutex);
|
||||
|
||||
struct perm_data {
|
||||
struct list_head list;
|
||||
uid_t uid;
|
||||
@@ -33,15 +22,25 @@ 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 workqueue_struct *ksu_workqueue;
|
||||
static struct work_struct ksu_save_work;
|
||||
static struct work_struct ksu_load_work;
|
||||
|
||||
bool persistent_allow_list(void);
|
||||
|
||||
bool ksu_allow_uid(uid_t uid, bool allow)
|
||||
void ksu_show_allow_list(void)
|
||||
{
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
pr_info("ksu_show_allow_list");
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("uid :%d, allow: %d\n", p->uid, p->allow);
|
||||
}
|
||||
}
|
||||
|
||||
bool ksu_allow_uid(uid_t uid, bool allow, bool persist)
|
||||
{
|
||||
// find the node first!
|
||||
struct perm_data *p = NULL;
|
||||
@@ -49,7 +48,6 @@ bool ksu_allow_uid(uid_t uid, bool allow)
|
||||
bool result = false;
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("ksu_allow_uid :%d, allow: %d\n", p->uid, p->allow);
|
||||
if (uid == p->uid) {
|
||||
p->allow = allow;
|
||||
result = true;
|
||||
@@ -66,12 +64,14 @@ bool ksu_allow_uid(uid_t uid, bool allow)
|
||||
p->uid = uid;
|
||||
p->allow = allow;
|
||||
|
||||
pr_info("allow_uid: %d, allow: %d", uid, allow);
|
||||
|
||||
list_add_tail(&p->list, &allow_list);
|
||||
result = true;
|
||||
|
||||
exit:
|
||||
|
||||
persistent_allow_list();
|
||||
if (persist)
|
||||
persistent_allow_list();
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -104,7 +104,7 @@ bool ksu_get_allow_list(int *array, int *length, bool allow)
|
||||
int i = 0;
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
|
||||
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
|
||||
if (p->allow == allow) {
|
||||
array[i++] = p->uid;
|
||||
}
|
||||
@@ -121,7 +121,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);
|
||||
|
||||
@@ -131,12 +131,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;
|
||||
@@ -146,8 +146,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:
|
||||
@@ -161,38 +161,35 @@ 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);
|
||||
if (IS_ERR(fp)) {
|
||||
int errno = PTR_ERR(fp);
|
||||
pr_err("load_allow_list open '/data/adb': %d\n", PTR_ERR(fp));
|
||||
if (errno == -ENOENT) {
|
||||
msleep(2000);
|
||||
queue_work(ksu_workqueue, &ksu_load_work);
|
||||
return;
|
||||
} else {
|
||||
pr_info("load_allow list dir exist now!");
|
||||
}
|
||||
} else {
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
KWORKER_INSTALL_KEYRING();
|
||||
|
||||
// load allowlist now!
|
||||
fp = filp_open(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
|
||||
|
||||
if (IS_ERR(fp)) {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
int errno = PTR_ERR(fp);
|
||||
if (errno == -ENOENT) {
|
||||
ksu_allow_uid(2000, true,
|
||||
true); // allow adb shell by default
|
||||
} else {
|
||||
pr_err("load_allow_list open file failed: %d\n",
|
||||
PTR_ERR(fp));
|
||||
}
|
||||
#else
|
||||
pr_err("load_allow_list open file failed: %d\n", PTR_ERR(fp));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -203,59 +200,78 @@ 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);
|
||||
|
||||
ksu_allow_uid(uid, allow);
|
||||
ksu_allow_uid(uid, allow, false);
|
||||
}
|
||||
|
||||
exit:
|
||||
|
||||
ksu_show_allow_list();
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
|
||||
static int init_work(void)
|
||||
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data)
|
||||
{
|
||||
ksu_workqueue = alloc_workqueue("kernelsu_work_queue", 0, 0);
|
||||
INIT_WORK(&ksu_save_work, do_persistent_allow_list);
|
||||
INIT_WORK(&ksu_load_work, do_load_allow_list);
|
||||
return 0;
|
||||
struct perm_data *np = NULL;
|
||||
struct perm_data *n = NULL;
|
||||
|
||||
bool modified = false;
|
||||
// TODO: use RCU!
|
||||
mutex_lock(&allowlist_mutex);
|
||||
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||
uid_t uid = np->uid;
|
||||
if (!is_uid_exist(uid, data)) {
|
||||
modified = true;
|
||||
pr_info("prune uid: %d\n", uid);
|
||||
list_del(&np->list);
|
||||
kfree(np);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&allowlist_mutex);
|
||||
|
||||
if (modified) {
|
||||
persistent_allow_list();
|
||||
}
|
||||
}
|
||||
|
||||
// make sure allow list works cross boot
|
||||
bool persistent_allow_list(void)
|
||||
{
|
||||
queue_work(ksu_workqueue, &ksu_save_work);
|
||||
return true;
|
||||
return ksu_queue_work(&ksu_save_work);
|
||||
}
|
||||
|
||||
bool ksu_load_allow_list(void)
|
||||
{
|
||||
queue_work(ksu_workqueue, &ksu_load_work);
|
||||
return true;
|
||||
return ksu_queue_work(&ksu_load_work);
|
||||
}
|
||||
|
||||
bool ksu_allowlist_init(void)
|
||||
void ksu_allowlist_init(void)
|
||||
{
|
||||
INIT_LIST_HEAD(&allow_list);
|
||||
|
||||
init_work();
|
||||
|
||||
// start load allow list, we load it before app_process exec now, refer: sucompat#execve_handler_pre
|
||||
// ksu_load_allow_list();
|
||||
|
||||
return true;
|
||||
INIT_WORK(&ksu_save_work, do_persistent_allow_list);
|
||||
INIT_WORK(&ksu_load_work, do_load_allow_list);
|
||||
}
|
||||
|
||||
bool ksu_allowlist_exit(void)
|
||||
void ksu_allowlist_exit(void)
|
||||
{
|
||||
destroy_workqueue(ksu_workqueue);
|
||||
struct perm_data *np = NULL;
|
||||
struct perm_data *n = NULL;
|
||||
|
||||
return true;
|
||||
do_persistent_allow_list(NULL);
|
||||
|
||||
// free allowlist
|
||||
mutex_lock(&allowlist_mutex);
|
||||
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||
list_del(&np->list);
|
||||
kfree(np);
|
||||
}
|
||||
mutex_unlock(&allowlist_mutex);
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
#ifndef __KSU_H_ALLOWLIST
|
||||
#define __KSU_H_ALLOWLIST
|
||||
|
||||
bool ksu_allowlist_init();
|
||||
#include "linux/types.h"
|
||||
|
||||
bool ksu_allowlist_exit();
|
||||
void ksu_allowlist_init(void);
|
||||
|
||||
bool ksu_is_allow_uid(uid_t uid);
|
||||
|
||||
bool ksu_allow_uid(uid_t uid, bool allow);
|
||||
|
||||
bool ksu_get_allow_list(int *array, int *length, bool allow);
|
||||
void ksu_allowlist_exit(void);
|
||||
|
||||
bool ksu_load_allow_list(void);
|
||||
|
||||
void ksu_show_allow_list(void);
|
||||
|
||||
bool ksu_is_allow_uid(uid_t uid);
|
||||
|
||||
bool ksu_allow_uid(uid_t uid, bool allow, bool persist);
|
||||
|
||||
bool ksu_get_allow_list(int *array, int *length, bool allow);
|
||||
|
||||
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data);
|
||||
|
||||
#endif
|
||||
@@ -1,10 +1,12 @@
|
||||
#include <linux/fs.h>
|
||||
#include "linux/fs.h"
|
||||
#include "linux/moduleparam.h"
|
||||
|
||||
#include "apk_sign.h"
|
||||
#include "klog.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "kernel_compat.h"
|
||||
|
||||
static int check_v2_signature(char *path, unsigned expected_size,
|
||||
unsigned expected_hash)
|
||||
static __always_inline int
|
||||
check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
|
||||
{
|
||||
unsigned char buffer[0x11] = { 0 };
|
||||
u32 size4;
|
||||
@@ -13,21 +15,25 @@ static int check_v2_signature(char *path, unsigned expected_size,
|
||||
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;
|
||||
}
|
||||
@@ -40,17 +46,17 @@ static int check_v2_signature(char *path, unsigned expected_size,
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -58,47 +64,47 @@ static int check_v2_signature(char *path, unsigned expected_size,
|
||||
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);
|
||||
hash = 31 * hash + c;
|
||||
}
|
||||
offset += size4;
|
||||
pr_info(" size: 0x%04x, hash: 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u);
|
||||
int hash = 1;
|
||||
signed char c;
|
||||
for (i = 0; i < size4; ++i) {
|
||||
ksu_kernel_read_compat(fp, &c, 0x1, &pos);
|
||||
hash = 31 * hash + c;
|
||||
}
|
||||
offset += size4;
|
||||
pr_info(" size: 0x%04x, hash: 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u);
|
||||
#else
|
||||
if (size4 == expected_size) {
|
||||
int hash = 1;
|
||||
signed char c;
|
||||
for (unsigned i = 0; i < size4; ++i) {
|
||||
kernel_read(fp, &c, 0x1, &pos);
|
||||
for (i = 0; i < size4; ++i) {
|
||||
ksu_kernel_read_compat(fp, &c, 0x1, &pos);
|
||||
hash = 31 * hash + c;
|
||||
}
|
||||
offset += size4;
|
||||
@@ -121,7 +127,54 @@ clean:
|
||||
return sign;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
|
||||
unsigned ksu_expected_size = EXPECTED_SIZE;
|
||||
unsigned ksu_expected_hash = EXPECTED_HASH;
|
||||
|
||||
#include "manager.h"
|
||||
|
||||
static int set_expected_size(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
int rv = param_set_uint(val, kp);
|
||||
ksu_invalidate_manager_uid();
|
||||
pr_info("ksu_expected_size set to %x", ksu_expected_size);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int set_expected_hash(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
int rv = param_set_uint(val, kp);
|
||||
ksu_invalidate_manager_uid();
|
||||
pr_info("ksu_expected_hash set to %x", ksu_expected_hash);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static struct kernel_param_ops expected_size_ops = {
|
||||
.set = set_expected_size,
|
||||
.get = param_get_uint,
|
||||
};
|
||||
|
||||
static struct kernel_param_ops expected_hash_ops = {
|
||||
.set = set_expected_hash,
|
||||
.get = param_get_uint,
|
||||
};
|
||||
|
||||
module_param_cb(ksu_expected_size, &expected_size_ops, &ksu_expected_size,
|
||||
S_IRUSR | S_IWUSR);
|
||||
module_param_cb(ksu_expected_hash, &expected_hash_ops, &ksu_expected_hash,
|
||||
S_IRUSR | S_IWUSR);
|
||||
|
||||
int is_manager_apk(char *path)
|
||||
{
|
||||
return check_v2_signature(path, ksu_expected_size, ksu_expected_hash);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int is_manager_apk(char *path)
|
||||
{
|
||||
return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
#define __KSU_H_APK_V2_SIGN
|
||||
|
||||
// return 0 if signature match
|
||||
int is_manager_apk(char* path);
|
||||
int is_manager_apk(char *path);
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef __KSU_H_ARCH
|
||||
#define __KSU_H_ARCH
|
||||
|
||||
#include "linux/version.h"
|
||||
|
||||
#if defined(__aarch64__)
|
||||
|
||||
#define __PT_PARM1_REG regs[0]
|
||||
@@ -8,13 +10,18 @@
|
||||
#define __PT_PARM3_REG regs[2]
|
||||
#define __PT_PARM4_REG regs[3]
|
||||
#define __PT_PARM5_REG regs[4]
|
||||
#define __PT_PARM6_REG regs[5]
|
||||
#define __PT_RET_REG regs[30]
|
||||
#define __PT_FP_REG regs[29] /* Works only with CONFIG_FRAME_POINTER */
|
||||
#define __PT_RC_REG regs[0]
|
||||
#define __PT_SP_REG sp
|
||||
#define __PT_IP_REG pc
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||
#define PRCTL_SYMBOL "__arm64_sys_prctl"
|
||||
#else
|
||||
#define PRCTL_SYMBOL "sys_prctl"
|
||||
#endif
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
|
||||
@@ -25,13 +32,17 @@
|
||||
#define __PT_PARM4_REG r10
|
||||
// #define __PT_PARM4_REG cx
|
||||
#define __PT_PARM5_REG r8
|
||||
#define __PT_PARM6_REG r9
|
||||
#define __PT_RET_REG sp
|
||||
#define __PT_FP_REG bp
|
||||
#define __PT_RC_REG ax
|
||||
#define __PT_SP_REG sp
|
||||
#define __PT_IP_REG ip
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||
#define PRCTL_SYMBOL "__x64_sys_prctl"
|
||||
#else
|
||||
#define PRCTL_SYMBOL "sys_prctl"
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error "Unsupported arch"
|
||||
@@ -47,10 +58,11 @@
|
||||
#define PT_REGS_PARM3(x) (__PT_REGS_CAST(x)->__PT_PARM3_REG)
|
||||
#define PT_REGS_PARM4(x) (__PT_REGS_CAST(x)->__PT_PARM4_REG)
|
||||
#define PT_REGS_PARM5(x) (__PT_REGS_CAST(x)->__PT_PARM5_REG)
|
||||
#define PT_REGS_PARM6(x) (__PT_REGS_CAST(x)->__PT_PARM6_REG)
|
||||
#define PT_REGS_RET(x) (__PT_REGS_CAST(x)->__PT_RET_REG)
|
||||
#define PT_REGS_FP(x) (__PT_REGS_CAST(x)->__PT_FP_REG)
|
||||
#define PT_REGS_RC(x) (__PT_REGS_CAST(x)->__PT_RC_REG)
|
||||
#define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG)
|
||||
#define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
570
kernel/core_hook.c
Normal file
570
kernel/core_hook.c
Normal file
@@ -0,0 +1,570 @@
|
||||
#include "linux/cred.h"
|
||||
#include "linux/dcache.h"
|
||||
#include "linux/err.h"
|
||||
#include "linux/init.h"
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/kprobes.h"
|
||||
#include "linux/lsm_hooks.h"
|
||||
#include "linux/path.h"
|
||||
#include "linux/printk.h"
|
||||
#include "linux/uaccess.h"
|
||||
#include "linux/uidgid.h"
|
||||
#include "linux/version.h"
|
||||
#include "linux/mount.h"
|
||||
|
||||
#include "linux/fs.h"
|
||||
#include "linux/namei.h"
|
||||
#include "linux/rcupdate.h"
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "arch.h"
|
||||
#include "core_hook.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksu.h"
|
||||
#include "ksud.h"
|
||||
#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);
|
||||
|
||||
static inline bool is_allow_su()
|
||||
{
|
||||
if (is_manager()) {
|
||||
// we are manager, allow!
|
||||
return true;
|
||||
}
|
||||
return ksu_is_allow_uid(current_uid().val);
|
||||
}
|
||||
|
||||
static inline bool is_isolated_uid(uid_t uid)
|
||||
{
|
||||
#define FIRST_ISOLATED_UID 99000
|
||||
#define LAST_ISOLATED_UID 99999
|
||||
#define FIRST_APP_ZYGOTE_ISOLATED_UID 90000
|
||||
#define LAST_APP_ZYGOTE_ISOLATED_UID 98999
|
||||
uid_t appid = uid % 100000;
|
||||
return (appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID) ||
|
||||
(appid >= FIRST_APP_ZYGOTE_ISOLATED_UID &&
|
||||
appid <= LAST_APP_ZYGOTE_ISOLATED_UID);
|
||||
}
|
||||
|
||||
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
|
||||
|
||||
void escape_to_root(void)
|
||||
{
|
||||
struct cred *cred;
|
||||
|
||||
cred = (struct cred *)__task_cred(current);
|
||||
|
||||
memset(&cred->uid, 0, sizeof(cred->uid));
|
||||
memset(&cred->gid, 0, sizeof(cred->gid));
|
||||
memset(&cred->suid, 0, sizeof(cred->suid));
|
||||
memset(&cred->euid, 0, sizeof(cred->euid));
|
||||
memset(&cred->egid, 0, sizeof(cred->egid));
|
||||
memset(&cred->fsuid, 0, sizeof(cred->fsuid));
|
||||
memset(&cred->fsgid, 0, sizeof(cred->fsgid));
|
||||
memset(&cred->cap_inheritable, 0xff, sizeof(cred->cap_inheritable));
|
||||
memset(&cred->cap_permitted, 0xff, sizeof(cred->cap_permitted));
|
||||
memset(&cred->cap_effective, 0xff, sizeof(cred->cap_effective));
|
||||
memset(&cred->cap_bset, 0xff, sizeof(cred->cap_bset));
|
||||
memset(&cred->cap_ambient, 0xff, sizeof(cred->cap_ambient));
|
||||
|
||||
// disable seccomp
|
||||
#if defined(CONFIG_GENERIC_ENTRY) && \
|
||||
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||
current_thread_info()->syscall_work &= ~SYSCALL_WORK_SECCOMP;
|
||||
#else
|
||||
current_thread_info()->flags &= ~(TIF_SECCOMP | _TIF_SECCOMP);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECCOMP
|
||||
current->seccomp.mode = 0;
|
||||
current->seccomp.filter = NULL;
|
||||
#else
|
||||
#endif
|
||||
|
||||
// setgroup to root
|
||||
if (cred->group_info)
|
||||
put_group_info(cred->group_info);
|
||||
cred->group_info = get_group_info(&root_groups);
|
||||
|
||||
setup_selinux();
|
||||
}
|
||||
|
||||
int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
|
||||
{
|
||||
if (!current->mm) {
|
||||
// skip kernel threads
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (current_uid().val != 1000) {
|
||||
// skip non system uid
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!old_dentry || !new_dentry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// /data/system/packages.list.tmp -> /data/system/packages.list
|
||||
if (strcmp(new_dentry->d_iname, "packages.list")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char path[128];
|
||||
char *buf = dentry_path_raw(new_dentry, path, sizeof(path));
|
||||
if (IS_ERR(buf)) {
|
||||
pr_err("dentry_path_raw failed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(buf, "/system/packages.list")) {
|
||||
return 0;
|
||||
}
|
||||
pr_info("renameat: %s -> %s, new path: %s", old_dentry->d_iname,
|
||||
new_dentry->d_iname, buf);
|
||||
|
||||
update_uid();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
// if success, we modify the arg5 as result!
|
||||
u32 *result = (u32 *)arg5;
|
||||
u32 reply_ok = KERNEL_SU_OPTION;
|
||||
|
||||
if (KERNEL_SU_OPTION != option) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// always ignore isolated app uid
|
||||
if (is_isolated_uid(current_uid().val)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uid_t last_failed_uid = -1;
|
||||
if (last_failed_uid == current_uid().val) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// pr_info("option: 0x%x, cmd: %ld\n", option, arg2);
|
||||
|
||||
if (arg2 == CMD_BECOME_MANAGER) {
|
||||
// quick check
|
||||
if (is_manager()) {
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("become_manager: prctl reply error\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (ksu_is_manager_uid_valid()) {
|
||||
pr_info("manager already exist: %d\n",
|
||||
ksu_get_manager_uid());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// someone wants to be root manager, just check it!
|
||||
// arg3 should be `/data/user/<userId>/<manager_package_name>`
|
||||
char param[128];
|
||||
if (copy_from_user(param, arg3, sizeof(param))) {
|
||||
pr_err("become_manager: copy param err\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// for user 0, it is /data/data
|
||||
// for user 999, it is /data/user/999
|
||||
const char *prefix;
|
||||
char prefixTmp[64];
|
||||
int userId = current_uid().val / 100000;
|
||||
if (userId == 0) {
|
||||
prefix = "/data/data";
|
||||
} else {
|
||||
snprintf(prefixTmp, sizeof(prefixTmp), "/data/user/%d", userId);
|
||||
prefix = prefixTmp;
|
||||
}
|
||||
|
||||
if (startswith(param, (char *)prefix) != 0) {
|
||||
pr_info("become_manager: invalid param: %s\n", param);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// stat the param, app must have permission to do this
|
||||
// otherwise it may fake the path!
|
||||
struct path path;
|
||||
if (kern_path(param, LOOKUP_DIRECTORY, &path)) {
|
||||
pr_err("become_manager: kern_path err\n");
|
||||
return 0;
|
||||
}
|
||||
if (path.dentry->d_inode->i_uid.val != current_uid().val) {
|
||||
pr_err("become_manager: path uid != current uid\n");
|
||||
path_put(&path);
|
||||
return 0;
|
||||
}
|
||||
char *pkg = param + strlen(prefix);
|
||||
pr_info("become_manager: param pkg: %s\n", pkg);
|
||||
|
||||
bool success = become_manager(pkg);
|
||||
if (success) {
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("become_manager: prctl reply error\n");
|
||||
}
|
||||
}
|
||||
path_put(&path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_GRANT_ROOT) {
|
||||
if (is_allow_su()) {
|
||||
pr_info("allow root for: %d\n", current_uid());
|
||||
escape_to_root();
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("grant_root: prctl reply error\n");
|
||||
}
|
||||
} else {
|
||||
pr_info("deny root for: %d\n", current_uid());
|
||||
// add it to deny list!
|
||||
ksu_allow_uid(current_uid().val, false, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Both root manager and root processes should be allowed to get version
|
||||
if (arg2 == CMD_GET_VERSION) {
|
||||
if (is_manager() || 0 == current_uid().val) {
|
||||
u32 version = KERNEL_SU_VERSION;
|
||||
if (copy_to_user(arg3, &version, sizeof(version))) {
|
||||
pr_err("prctl reply error, cmd: %d\n", arg2);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_REPORT_EVENT) {
|
||||
if (0 != current_uid().val) {
|
||||
return 0;
|
||||
}
|
||||
switch (arg3) {
|
||||
case EVENT_POST_FS_DATA: {
|
||||
static bool post_fs_data_lock = false;
|
||||
if (!post_fs_data_lock) {
|
||||
post_fs_data_lock = true;
|
||||
pr_info("post-fs-data triggered");
|
||||
on_post_fs_data();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EVENT_BOOT_COMPLETED: {
|
||||
static bool boot_complete_lock = false;
|
||||
if (!boot_complete_lock) {
|
||||
boot_complete_lock = true;
|
||||
pr_info("boot_complete triggered");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_SET_SEPOLICY) {
|
||||
if (0 != current_uid().val) {
|
||||
return 0;
|
||||
}
|
||||
if (!handle_sepolicy(arg3, arg4)) {
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("sepolicy: prctl reply error\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_CHECK_SAFEMODE) {
|
||||
if (!is_manager() && 0 != current_uid().val) {
|
||||
return 0;
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// all other cmds are for 'root manager'
|
||||
if (!is_manager()) {
|
||||
last_failed_uid = current_uid().val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we are already manager
|
||||
if (arg2 == CMD_ALLOW_SU || arg2 == CMD_DENY_SU) {
|
||||
bool allow = arg2 == CMD_ALLOW_SU;
|
||||
bool success = false;
|
||||
uid_t uid = (uid_t)arg3;
|
||||
success = ksu_allow_uid(uid, allow, true);
|
||||
if (success) {
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %d\n", arg2);
|
||||
}
|
||||
}
|
||||
ksu_show_allow_list();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_appuid(kuid_t uid)
|
||||
{
|
||||
#define PER_USER_RANGE 100000
|
||||
#define FIRST_APPLICATION_UID 10000
|
||||
#define LAST_APPLICATION_UID 19999
|
||||
|
||||
uid_t appid = uid.val % PER_USER_RANGE;
|
||||
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
|
||||
}
|
||||
|
||||
static bool should_umount(struct path *path)
|
||||
{
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) {
|
||||
const char *fstype = path->mnt->mnt_sb->s_type->name;
|
||||
return strcmp(fstype, "overlay") == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void try_umount(const char *mnt)
|
||||
{
|
||||
struct path path;
|
||||
int err = kern_path(mnt, 0, &path);
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we are only interest in some specific mounts
|
||||
if (!should_umount(&path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||
err = path_umount(&path, 0);
|
||||
if (err) {
|
||||
pr_info("umount %s failed: %d\n", mnt, err);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
{
|
||||
if (!new || !old) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
kuid_t new_uid = new->uid;
|
||||
kuid_t old_uid = old->uid;
|
||||
|
||||
if (0 != old_uid.val) {
|
||||
// old process is not root, ignore it.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// todo: check old process's selinux context, if it is not zygote, ignore it!
|
||||
|
||||
if (!is_appuid(new_uid)) {
|
||||
// pr_info("handle setuid ignore non application uid: %d\n", new_uid.val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ksu_is_allow_uid(new_uid.val)) {
|
||||
// pr_info("handle setuid ignore allowed application: %d\n", new_uid.val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// umount the target mnt
|
||||
pr_info("handle umount for uid: %d\n", new_uid.val);
|
||||
|
||||
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
||||
// filter the mountpoint whose target is `/data/adb`
|
||||
try_umount("/system");
|
||||
try_umount("/vendor");
|
||||
try_umount("/product");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Init functons
|
||||
|
||||
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||
struct pt_regs *real_regs = (struct pt_regs *)PT_REGS_PARM1(regs);
|
||||
#else
|
||||
struct pt_regs *real_regs = regs;
|
||||
#endif
|
||||
int option = (int)PT_REGS_PARM1(real_regs);
|
||||
unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs);
|
||||
unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs);
|
||||
unsigned long arg4 = (unsigned long)PT_REGS_PARM4(real_regs);
|
||||
unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs);
|
||||
|
||||
return ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
static struct kprobe prctl_kp = {
|
||||
.symbol_name = PRCTL_SYMBOL,
|
||||
.pre_handler = handler_pre,
|
||||
};
|
||||
|
||||
static int renameat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
// https://elixir.bootlin.com/linux/v5.12-rc1/source/include/linux/fs.h
|
||||
struct renamedata *rd = PT_REGS_PARM1(regs);
|
||||
struct dentry *old_entry = rd->old_dentry;
|
||||
struct dentry *new_entry = rd->new_dentry;
|
||||
#else
|
||||
struct dentry *old_entry = (struct dentry *)PT_REGS_PARM2(regs);
|
||||
struct dentry *new_entry = (struct dentry *)PT_REGS_PARM4(regs);
|
||||
#endif
|
||||
|
||||
return ksu_handle_rename(old_entry, new_entry);
|
||||
}
|
||||
|
||||
static struct kprobe renameat_kp = {
|
||||
.symbol_name = "vfs_rename",
|
||||
.pre_handler = renameat_handler_pre,
|
||||
};
|
||||
|
||||
__maybe_unused int ksu_kprobe_init(void)
|
||||
{
|
||||
int rc = 0;
|
||||
rc = register_kprobe(&prctl_kp);
|
||||
|
||||
if (rc) {
|
||||
pr_info("prctl kprobe failed: %d.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = register_kprobe(&renameat_kp);
|
||||
pr_info("renameat kp: %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
__maybe_unused int ksu_kprobe_exit(void)
|
||||
{
|
||||
unregister_kprobe(&prctl_kp);
|
||||
unregister_kprobe(&renameat_kp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return ksu_handle_rename(old_dentry, new_dentry);
|
||||
}
|
||||
|
||||
static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
|
||||
int flags)
|
||||
{
|
||||
return ksu_handle_setuid(new, old);
|
||||
}
|
||||
|
||||
static struct security_hook_list ksu_hooks[] = {
|
||||
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
|
||||
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
|
||||
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),
|
||||
#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)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu");
|
||||
#else
|
||||
// https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892
|
||||
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks));
|
||||
#endif
|
||||
}
|
||||
|
||||
void __init ksu_core_init(void)
|
||||
{
|
||||
#ifndef MODULE
|
||||
pr_info("ksu_lsm_hook_init\n");
|
||||
ksu_lsm_hook_init();
|
||||
#else
|
||||
pr_info("ksu_kprobe_init\n");
|
||||
ksu_kprobe_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ksu_core_exit(void)
|
||||
{
|
||||
#ifndef MODULE
|
||||
pr_info("ksu_kprobe_exit\n");
|
||||
ksu_kprobe_exit();
|
||||
#endif
|
||||
}
|
||||
9
kernel/core_hook.h
Normal file
9
kernel/core_hook.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef __KSU_H_KSU_CORE
|
||||
#define __KSU_H_KSU_CORE
|
||||
|
||||
#include "linux/init.h"
|
||||
|
||||
void __init ksu_core_init(void);
|
||||
void ksu_core_exit(void);
|
||||
|
||||
#endif
|
||||
5
kernel/embed_ksud.c
Normal file
5
kernel/embed_ksud.c
Normal file
@@ -0,0 +1,5 @@
|
||||
// WARNING: THIS IS A STUB FILE
|
||||
// This file will be regenerated by CI
|
||||
|
||||
unsigned int ksud_size = 0;
|
||||
const char ksud[0] = {};
|
||||
2
kernel/export_symbol.txt
Normal file
2
kernel/export_symbol.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
register_kprobe
|
||||
unregister_kprobe
|
||||
28
kernel/include/ksu_hook.h
Normal file
28
kernel/include/ksu_hook.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef __KSU_H_KSHOOK
|
||||
#define __KSU_H_KSHOOK
|
||||
|
||||
#include "linux/fs.h"
|
||||
#include "linux/types.h"
|
||||
|
||||
// For sucompat
|
||||
|
||||
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||
int *flags);
|
||||
|
||||
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
||||
|
||||
// For ksud
|
||||
|
||||
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||
size_t *count_ptr, loff_t **pos);
|
||||
|
||||
// For ksud and sucompat
|
||||
|
||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||
void *envp, int *flags);
|
||||
|
||||
// For volume button
|
||||
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||
int *value);
|
||||
|
||||
#endif
|
||||
34
kernel/kernel_compat.c
Normal file
34
kernel/kernel_compat.c
Normal 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
42
kernel/kernel_compat.h
Normal 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
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef __KSU_H_KLOG
|
||||
#define __KSU_H_KLOG
|
||||
|
||||
#include <linux/printk.h>
|
||||
|
||||
#ifdef pr_fmt
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "KernelSU: " fmt
|
||||
|
||||
360
kernel/ksu.c
360
kernel/ksu.c
@@ -1,334 +1,86 @@
|
||||
#include "linux/gfp.h"
|
||||
#include "linux/uidgid.h"
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/memory.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm-generic/errno-base.h>
|
||||
#include "linux/fs.h"
|
||||
#include "linux/module.h"
|
||||
#include "linux/workqueue.h"
|
||||
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fs_struct.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
#include <linux/delay.h> // mslepp
|
||||
|
||||
#include "selinux/selinux.h"
|
||||
#include "klog.h"
|
||||
#include "apk_sign.h"
|
||||
#include "allowlist.h"
|
||||
#include "arch.h"
|
||||
#include "core_hook.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksu.h"
|
||||
#include "uid_observer.h"
|
||||
|
||||
#define KERNEL_SU_VERSION 8
|
||||
static struct workqueue_struct *ksu_workqueue;
|
||||
|
||||
#define KERNEL_SU_OPTION 0xDEADBEEF
|
||||
|
||||
#define CMD_GRANT_ROOT 0
|
||||
|
||||
#define CMD_BECOME_MANAGER 1
|
||||
#define CMD_GET_VERSION 2
|
||||
#define CMD_ALLOW_SU 3
|
||||
#define CMD_DENY_SU 4
|
||||
#define CMD_GET_ALLOW_LIST 5
|
||||
#define CMD_GET_DENY_LIST 6
|
||||
|
||||
void escape_to_root()
|
||||
bool ksu_queue_work(struct work_struct *work)
|
||||
{
|
||||
struct cred *cred;
|
||||
return queue_work(ksu_workqueue, work);
|
||||
}
|
||||
|
||||
cred = (struct cred *)__task_cred(current);
|
||||
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags);
|
||||
|
||||
memset(&cred->uid, 0, sizeof(cred->uid));
|
||||
memset(&cred->gid, 0, sizeof(cred->gid));
|
||||
memset(&cred->suid, 0, sizeof(cred->suid));
|
||||
memset(&cred->euid, 0, sizeof(cred->euid));
|
||||
memset(&cred->egid, 0, sizeof(cred->egid));
|
||||
memset(&cred->fsuid, 0, sizeof(cred->fsuid));
|
||||
memset(&cred->fsgid, 0, sizeof(cred->fsgid));
|
||||
memset(&cred->cap_inheritable, 0xff, sizeof(cred->cap_inheritable));
|
||||
memset(&cred->cap_permitted, 0xff, sizeof(cred->cap_permitted));
|
||||
memset(&cred->cap_effective, 0xff, sizeof(cred->cap_effective));
|
||||
memset(&cred->cap_bset, 0xff, sizeof(cred->cap_bset));
|
||||
memset(&cred->cap_ambient, 0xff, sizeof(cred->cap_ambient));
|
||||
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags);
|
||||
|
||||
// disable seccomp
|
||||
#ifdef CONFIG_GENERIC_ENTRY
|
||||
current_thread_info()->syscall_work &= ~SYSCALL_WORK_SECCOMP;
|
||||
#else
|
||||
current_thread_info()->flags &= ~(TIF_SECCOMP | _TIF_SECCOMP);
|
||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||
void *envp, int *flags)
|
||||
{
|
||||
ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags);
|
||||
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp,
|
||||
flags);
|
||||
}
|
||||
|
||||
extern void ksu_enable_sucompat();
|
||||
extern void ksu_enable_ksud();
|
||||
|
||||
int __init kernelsu_init(void)
|
||||
{
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_alert("*************************************************************");
|
||||
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||
pr_alert("** **");
|
||||
pr_alert("** You are running DEBUG version of KernelSU **");
|
||||
pr_alert("** **");
|
||||
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||
pr_alert("*************************************************************");
|
||||
#endif
|
||||
current->seccomp.mode = 0;
|
||||
current->seccomp.filter = NULL;
|
||||
|
||||
setup_selinux();
|
||||
}
|
||||
ksu_core_init();
|
||||
|
||||
int startswith(char *s, char *prefix)
|
||||
{
|
||||
return strncmp(s, prefix, strlen(prefix));
|
||||
}
|
||||
|
||||
int endswith(const char *s, const char *t)
|
||||
{
|
||||
size_t slen = strlen(s);
|
||||
size_t tlen = strlen(t);
|
||||
if (tlen > slen)
|
||||
return 1;
|
||||
return strcmp(s + slen - tlen, t);
|
||||
}
|
||||
|
||||
static uid_t __manager_uid;
|
||||
|
||||
static bool is_manager()
|
||||
{
|
||||
return __manager_uid == current_uid().val;
|
||||
}
|
||||
|
||||
static bool become_manager(char *pkg)
|
||||
{
|
||||
struct fdtable *files_table;
|
||||
int i = 0;
|
||||
struct path files_path;
|
||||
char *cwd;
|
||||
char *buf;
|
||||
bool result = false;
|
||||
|
||||
// must be zygote's direct child, otherwise any app can fork a new process and open manager's apk
|
||||
if (task_uid(current->real_parent).val != 0) {
|
||||
pr_info("parent is not zygote!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__manager_uid != 0) {
|
||||
pr_info("manager already exist: %d\n", __manager_uid);
|
||||
return is_manager();
|
||||
}
|
||||
|
||||
buf = (char *)kmalloc(PATH_MAX, GFP_ATOMIC);
|
||||
if (!buf) {
|
||||
pr_err("kalloc path failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
files_table = files_fdtable(current->files);
|
||||
|
||||
// todo: use iterate_fd
|
||||
while (files_table->fd[i] != NULL) {
|
||||
files_path = files_table->fd[i]->f_path;
|
||||
if (!d_is_reg(files_path.dentry)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
cwd = d_path(&files_path, buf, PATH_MAX);
|
||||
if (startswith(cwd, "/data/app/") == 0 &&
|
||||
endswith(cwd, "/base.apk") == 0) {
|
||||
// we have found the apk!
|
||||
pr_info("found apk: %s", cwd);
|
||||
if (!strstr(cwd, pkg)) {
|
||||
pr_info("apk path not match package name!\n");
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (is_manager_apk(cwd) == 0) {
|
||||
// check passed
|
||||
uid_t uid = current_uid().val;
|
||||
pr_info("manager uid: %d\n", uid);
|
||||
|
||||
__manager_uid = uid;
|
||||
|
||||
result = true;
|
||||
goto clean;
|
||||
} else {
|
||||
pr_info("manager signature invalid!");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
clean:
|
||||
kfree(buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool is_allow_su()
|
||||
{
|
||||
uid_t uid = current_uid().val;
|
||||
if (uid == __manager_uid) {
|
||||
// we are manager, allow!
|
||||
return true;
|
||||
}
|
||||
|
||||
return ksu_is_allow_uid(uid);
|
||||
}
|
||||
|
||||
extern void enable_sucompat();
|
||||
|
||||
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = (struct pt_regs *)PT_REGS_PARM1(regs);
|
||||
int option = (int)PT_REGS_PARM1(real_regs);
|
||||
unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs);
|
||||
unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs);
|
||||
unsigned long arg4 = (unsigned long)PT_REGS_PARM4(real_regs);
|
||||
unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs);
|
||||
|
||||
// if success, we modify the arg5 as result!
|
||||
u32 *result = (u32 *)arg5;
|
||||
u32 reply_ok = KERNEL_SU_OPTION;
|
||||
|
||||
if (KERNEL_SU_OPTION != option) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_info("option: 0x%x, cmd: %ld\n", option, arg2);
|
||||
|
||||
if (arg2 == CMD_BECOME_MANAGER) {
|
||||
// someone wants to be root manager, just check it!
|
||||
// arg3 should be `/data/data/<manager_package_name>`
|
||||
char param[128];
|
||||
const char *prefix = "/data/data/";
|
||||
if (copy_from_user(param, arg3, sizeof(param))) {
|
||||
pr_err("become_manager: copy param err\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (startswith(param, (char *)prefix) != 0) {
|
||||
pr_info("become_manager: invalid param: %s\n", param);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// stat the param, app must have permission to do this
|
||||
// otherwise it may fake the path!
|
||||
struct path path;
|
||||
if (kern_path(param, LOOKUP_DIRECTORY, &path)) {
|
||||
pr_err("become_manager: kern_path err\n");
|
||||
return 0;
|
||||
}
|
||||
if (path.dentry->d_inode->i_uid.val != current_uid().val) {
|
||||
pr_err("become_manager: path uid != current uid\n");
|
||||
path_put(&path);
|
||||
return 0;
|
||||
}
|
||||
char *pkg = param + strlen(prefix);
|
||||
pr_info("become_manager: param pkg: %s\n", pkg);
|
||||
|
||||
bool success = become_manager(pkg);
|
||||
if (success) {
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("become_manager: prctl reply error\n");
|
||||
}
|
||||
}
|
||||
path_put(&path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_GRANT_ROOT) {
|
||||
if (is_allow_su()) {
|
||||
pr_info("allow root for: %d\n", current_uid());
|
||||
escape_to_root();
|
||||
} else {
|
||||
pr_info("deny root for: %d\n", current_uid());
|
||||
// add it to deny list!
|
||||
ksu_allow_uid(current_uid().val, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// all other cmds are for 'root manager'
|
||||
if (!is_manager()) {
|
||||
pr_info("Only manager can do cmd: %d\n", arg2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we are already manager
|
||||
if (arg2 == CMD_ALLOW_SU || arg2 == CMD_DENY_SU) {
|
||||
bool allow = arg2 == CMD_ALLOW_SU;
|
||||
bool success = false;
|
||||
uid_t uid = (uid_t)arg3;
|
||||
success = ksu_allow_uid(uid, allow);
|
||||
if (success) {
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %d\n", arg2);
|
||||
}
|
||||
}
|
||||
} 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");
|
||||
}
|
||||
}
|
||||
} else if (arg2 == CMD_GET_VERSION) {
|
||||
u32 version = KERNEL_SU_VERSION;
|
||||
if (copy_to_user(arg3, &version, sizeof(version))) {
|
||||
pr_err("prctl reply error, cmd: %d\n", arg2);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kprobe kp = {
|
||||
.symbol_name = PRCTL_SYMBOL,
|
||||
.pre_handler = handler_pre,
|
||||
};
|
||||
|
||||
int kernelsu_init(void)
|
||||
{
|
||||
int rc = 0;
|
||||
ksu_workqueue = alloc_workqueue("kernelsu_work_queue", 0, 0);
|
||||
|
||||
ksu_allowlist_init();
|
||||
|
||||
rc = register_kprobe(&kp);
|
||||
if (rc) {
|
||||
pr_info("prctl kprobe failed: %d, please check your kernel config.\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
ksu_uid_observer_init();
|
||||
|
||||
enable_sucompat();
|
||||
#ifdef CONFIG_KPROBES
|
||||
ksu_enable_sucompat();
|
||||
ksu_enable_ksud();
|
||||
#else
|
||||
#warning("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html")
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kernelsu_exit(void)
|
||||
{
|
||||
// should never happen...
|
||||
unregister_kprobe(&kp);
|
||||
|
||||
ksu_allowlist_exit();
|
||||
|
||||
ksu_uid_observer_exit();
|
||||
|
||||
destroy_workqueue(ksu_workqueue);
|
||||
|
||||
ksu_core_exit();
|
||||
}
|
||||
|
||||
module_init(kernelsu_init);
|
||||
module_exit(kernelsu_exit);
|
||||
|
||||
#ifndef CONFIG_KPROBES
|
||||
#error("`CONFIG_KPROBES` must be enabled for KernelSU!")
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("weishu");
|
||||
MODULE_DESCRIPTION("Android GKI KernelSU");
|
||||
MODULE_IMPORT_NS(
|
||||
VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); // 5+才需要导出命名空间
|
||||
MODULE_DESCRIPTION("Android KernelSU");
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
||||
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
|
||||
#endif
|
||||
|
||||
45
kernel/ksu.h
Normal file
45
kernel/ksu.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef __KSU_H_KSU
|
||||
#define __KSU_H_KSU
|
||||
|
||||
#include "linux/workqueue.h"
|
||||
|
||||
#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
|
||||
|
||||
#define CMD_GRANT_ROOT 0
|
||||
#define CMD_BECOME_MANAGER 1
|
||||
#define CMD_GET_VERSION 2
|
||||
#define CMD_ALLOW_SU 3
|
||||
#define CMD_DENY_SU 4
|
||||
#define CMD_GET_ALLOW_LIST 5
|
||||
#define CMD_GET_DENY_LIST 6
|
||||
#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
|
||||
|
||||
bool ksu_queue_work(struct work_struct *work);
|
||||
|
||||
static inline int startswith(char *s, char *prefix)
|
||||
{
|
||||
return strncmp(s, prefix, strlen(prefix));
|
||||
}
|
||||
|
||||
static inline int endswith(const char *s, const char *t)
|
||||
{
|
||||
size_t slen = strlen(s);
|
||||
size_t tlen = strlen(t);
|
||||
if (tlen > slen)
|
||||
return 1;
|
||||
return strcmp(s + slen - tlen, t);
|
||||
}
|
||||
|
||||
#endif
|
||||
499
kernel/ksud.c
Normal file
499
kernel/ksud.c
Normal file
@@ -0,0 +1,499 @@
|
||||
#include "asm/current.h"
|
||||
#include "linux/string.h"
|
||||
#include "linux/cred.h"
|
||||
#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[] =
|
||||
"\n"
|
||||
|
||||
"on post-fs-data\n"
|
||||
" start logd\n"
|
||||
// We should wait for the post-fs-data finish
|
||||
" exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n"
|
||||
"\n"
|
||||
|
||||
"on nonencrypted\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 -- " KSUD_PATH " services\n"
|
||||
"\n"
|
||||
|
||||
"on property:sys.boot_completed=1\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)
|
||||
{
|
||||
static bool done = false;
|
||||
if (done) {
|
||||
pr_info("on_post_fs_data already done");
|
||||
return;
|
||||
}
|
||||
done = true;
|
||||
pr_info("on_post_fs_data!");
|
||||
ksu_load_allow_list();
|
||||
// sanity check, this may influence the performance
|
||||
stop_input_hook();
|
||||
}
|
||||
|
||||
#define MAX_ARG_STRINGS 0x7FFFFFFF
|
||||
struct user_arg_ptr {
|
||||
#ifdef CONFIG_COMPAT
|
||||
bool is_compat;
|
||||
#endif
|
||||
union {
|
||||
const char __user *const __user *native;
|
||||
#ifdef CONFIG_COMPAT
|
||||
const compat_uptr_t __user *compat;
|
||||
#endif
|
||||
} ptr;
|
||||
};
|
||||
|
||||
static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
|
||||
{
|
||||
const char __user *native;
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
if (unlikely(argv.is_compat)) {
|
||||
compat_uptr_t compat;
|
||||
|
||||
if (get_user(compat, argv.ptr.compat + nr))
|
||||
return ERR_PTR(-EFAULT);
|
||||
|
||||
return compat_ptr(compat);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (get_user(native, argv.ptr.native + nr))
|
||||
return ERR_PTR(-EFAULT);
|
||||
|
||||
return native;
|
||||
}
|
||||
|
||||
/*
|
||||
* count() counts the number of strings in array ARGV.
|
||||
*/
|
||||
static int count(struct user_arg_ptr argv, int max)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (argv.ptr.native != NULL) {
|
||||
for (;;) {
|
||||
const char __user *p = get_user_arg_ptr(argv, i);
|
||||
|
||||
if (!p)
|
||||
break;
|
||||
|
||||
if (IS_ERR(p))
|
||||
return -EFAULT;
|
||||
|
||||
if (i >= max)
|
||||
return -E2BIG;
|
||||
++i;
|
||||
|
||||
if (fatal_signal_pending(current))
|
||||
return -ERESTARTNOHAND;
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags)
|
||||
{
|
||||
#ifndef CONFIG_KPROBES
|
||||
if (!execveat_hook) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
struct filename *filename;
|
||||
|
||||
static const char app_process[] = "/system/bin/app_process";
|
||||
static bool first_app_process = true;
|
||||
static const char system_bin_init[] = "/system/bin/init";
|
||||
static bool init_second_stage_executed = false;
|
||||
|
||||
if (!filename_ptr)
|
||||
return 0;
|
||||
|
||||
filename = *filename_ptr;
|
||||
if (IS_ERR(filename)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!memcmp(filename->name, system_bin_init,
|
||||
sizeof(system_bin_init) - 1)) {
|
||||
// /system/bin/init executed
|
||||
struct user_arg_ptr *ptr = (struct user_arg_ptr*) argv;
|
||||
int argc = count(*ptr, MAX_ARG_STRINGS);
|
||||
pr_info("/system/bin/init argc: %d\n", argc);
|
||||
if (argc > 1 && !init_second_stage_executed) {
|
||||
const char __user *p = get_user_arg_ptr(*ptr, 1);
|
||||
if (p && !IS_ERR(p)) {
|
||||
char first_arg[16];
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||
strncpy_from_user_nofault(first_arg, p, sizeof(first_arg));
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
||||
strncpy_from_unsafe_user(first_arg, p, sizeof(first_arg));
|
||||
#else
|
||||
strncpy_from_user(first_arg, p, sizeof(first_arg));
|
||||
#endif
|
||||
pr_info("first arg: %s\n", first_arg);
|
||||
if (!strcmp(first_arg, "second_stage")) {
|
||||
pr_info("/system/bin/init second_stage executed\n");
|
||||
apply_kernelsu_rules();
|
||||
init_second_stage_executed = true;
|
||||
}
|
||||
} else {
|
||||
pr_err("/system/bin/init parse args err!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first_app_process &&
|
||||
!memcmp(filename->name, app_process, sizeof(app_process) - 1)) {
|
||||
first_app_process = false;
|
||||
pr_info("exec app_process, /data prepared, second_stage: %d\n", init_second_stage_executed);
|
||||
on_post_fs_data(); // we keep this for old ksud
|
||||
stop_execve_hook();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *);
|
||||
static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *);
|
||||
static struct file_operations fops_proxy;
|
||||
static ssize_t read_count_append = 0;
|
||||
|
||||
static ssize_t read_proxy(struct file *file, char __user *buf, size_t count,
|
||||
loff_t *pos)
|
||||
{
|
||||
bool first_read = file->f_pos == 0;
|
||||
ssize_t ret = orig_read(file, buf, count, pos);
|
||||
if (first_read) {
|
||||
pr_info("read_proxy append %ld + %ld", ret, read_count_append);
|
||||
ret += read_count_append;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to)
|
||||
{
|
||||
bool first_read = iocb->ki_pos == 0;
|
||||
ssize_t ret = orig_read_iter(iocb, to);
|
||||
if (first_read) {
|
||||
pr_info("read_iter_proxy append %ld + %ld", ret,
|
||||
read_count_append);
|
||||
ret += read_count_append;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||
size_t *count_ptr, loff_t **pos)
|
||||
{
|
||||
#ifndef CONFIG_KPROBES
|
||||
if (!vfs_read_hook) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
struct file *file;
|
||||
char __user *buf;
|
||||
size_t count;
|
||||
|
||||
if (strcmp(current->comm, "init")) {
|
||||
// we are only interest in `init` process
|
||||
return 0;
|
||||
}
|
||||
|
||||
file = *file_ptr;
|
||||
if (IS_ERR(file)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!d_is_reg(file->f_path.dentry)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *short_name = file->f_path.dentry->d_name.name;
|
||||
if (strcmp(short_name, "atrace.rc")) {
|
||||
// we are only interest `atrace.rc` file name file
|
||||
return 0;
|
||||
}
|
||||
char path[256];
|
||||
char *dpath = d_path(&file->f_path, path, sizeof(path));
|
||||
|
||||
if (IS_ERR(dpath)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(dpath, "/system/etc/init/atrace.rc")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we only process the first read
|
||||
static bool rc_inserted = false;
|
||||
if (rc_inserted) {
|
||||
// we don't need this kprobe, unregister it!
|
||||
stop_vfs_read_hook();
|
||||
return 0;
|
||||
}
|
||||
rc_inserted = true;
|
||||
|
||||
// now we can sure that the init process is reading
|
||||
// `/system/etc/init/atrace.rc`
|
||||
buf = *buf_ptr;
|
||||
count = *count_ptr;
|
||||
|
||||
size_t rc_count = strlen(KERNEL_SU_RC);
|
||||
|
||||
pr_info("vfs_read: %s, comm: %s, count: %d, rc_count: %d\n", dpath,
|
||||
current->comm, count, rc_count);
|
||||
|
||||
if (count < rc_count) {
|
||||
pr_err("count: %d < rc_count: %d", count, rc_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t ret = copy_to_user(buf, KERNEL_SU_RC, rc_count);
|
||||
if (ret) {
|
||||
pr_err("copy ksud.rc failed: %d\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we've succeed to insert ksud.rc, now we need to proxy the read and modify the result!
|
||||
// But, we can not modify the file_operations directly, because it's in read-only memory.
|
||||
// We just replace the whole file_operations with a proxy one.
|
||||
memcpy(&fops_proxy, file->f_op, sizeof(struct file_operations));
|
||||
orig_read = file->f_op->read;
|
||||
if (orig_read) {
|
||||
fops_proxy.read = read_proxy;
|
||||
}
|
||||
orig_read_iter = file->f_op->read_iter;
|
||||
if (orig_read_iter) {
|
||||
fops_proxy.read_iter = read_iter_proxy;
|
||||
}
|
||||
// replace the file_operations
|
||||
file->f_op = &fops_proxy;
|
||||
read_count_append = rc_count;
|
||||
|
||||
*buf_ptr = buf + rc_count;
|
||||
*count_ptr = count - rc_count;
|
||||
|
||||
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
|
||||
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
int *fd = (int *)&PT_REGS_PARM1(regs);
|
||||
struct filename **filename_ptr =
|
||||
(struct filename **)&PT_REGS_PARM2(regs);
|
||||
void *argv = (void *)&PT_REGS_PARM3(regs);
|
||||
void *envp = (void *)&PT_REGS_PARM4(regs);
|
||||
int *flags = (int *)&PT_REGS_PARM5(regs);
|
||||
|
||||
return ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags);
|
||||
}
|
||||
|
||||
static int read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct file **file_ptr = (struct file **)&PT_REGS_PARM1(regs);
|
||||
char __user **buf_ptr = (char **)&PT_REGS_PARM2(regs);
|
||||
size_t *count_ptr = (size_t *)&PT_REGS_PARM3(regs);
|
||||
loff_t **pos_ptr = (loff_t **)&PT_REGS_PARM4(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)
|
||||
.symbol_name = "__do_execve_file",
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
||||
.symbol_name = "do_execveat_common",
|
||||
#endif
|
||||
.pre_handler = execve_handler_pre,
|
||||
};
|
||||
|
||||
static struct kprobe vfs_read_kp = {
|
||||
.symbol_name = "vfs_read",
|
||||
.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);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
bool ret = schedule_work(&stop_vfs_read_work);
|
||||
pr_info("unregister vfs_read kprobe: %d!\n", ret);
|
||||
#else
|
||||
vfs_read_hook = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void stop_execve_hook()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
bool ret = schedule_work(&stop_execve_hook_work);
|
||||
pr_info("unregister execve kprobe: %d!\n", ret);
|
||||
#else
|
||||
execveat_hook = false;
|
||||
#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()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
int ret;
|
||||
|
||||
ret = register_kprobe(&execve_kp);
|
||||
pr_info("ksud: execve_kp: %d\n", ret);
|
||||
|
||||
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
|
||||
}
|
||||
10
kernel/ksud.h
Normal file
10
kernel/ksud.h
Normal file
@@ -0,0 +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
|
||||
80
kernel/manager.c
Normal file
80
kernel/manager.c
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "linux/cred.h"
|
||||
#include "linux/gfp.h"
|
||||
#include "linux/slab.h"
|
||||
#include "linux/uidgid.h"
|
||||
#include "linux/version.h"
|
||||
|
||||
#include "linux/fdtable.h"
|
||||
#include "linux/fs.h"
|
||||
#include "linux/rcupdate.h"
|
||||
|
||||
#include "apk_sign.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksu.h"
|
||||
#include "manager.h"
|
||||
|
||||
uid_t ksu_manager_uid = INVALID_UID;
|
||||
|
||||
bool become_manager(char *pkg)
|
||||
{
|
||||
struct fdtable *files_table;
|
||||
int i = 0;
|
||||
struct path files_path;
|
||||
char *cwd;
|
||||
char *buf;
|
||||
bool result = false;
|
||||
|
||||
// must be zygote's direct child, otherwise any app can fork a new process and
|
||||
// open manager's apk
|
||||
if (task_uid(current->real_parent).val != 0) {
|
||||
pr_info("parent is not zygote!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
buf = (char *)kmalloc(PATH_MAX, GFP_ATOMIC);
|
||||
if (!buf) {
|
||||
pr_err("kalloc path failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
files_table = files_fdtable(current->files);
|
||||
|
||||
// todo: use iterate_fd
|
||||
while (files_table->fd[i] != NULL) {
|
||||
files_path = files_table->fd[i]->f_path;
|
||||
if (!d_is_reg(files_path.dentry)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
cwd = d_path(&files_path, buf, PATH_MAX);
|
||||
if (startswith(cwd, "/data/app/") == 0 &&
|
||||
endswith(cwd, "/base.apk") == 0) {
|
||||
// we have found the apk!
|
||||
pr_info("found apk: %s", cwd);
|
||||
if (!strstr(cwd, pkg)) {
|
||||
pr_info("apk path not match package name!\n");
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (is_manager_apk(cwd) == 0) {
|
||||
// check passed
|
||||
uid_t uid = current_uid().val;
|
||||
pr_info("manager uid: %d\n", uid);
|
||||
|
||||
ksu_set_manager_uid(uid);
|
||||
|
||||
result = true;
|
||||
goto clean;
|
||||
} else {
|
||||
pr_info("manager signature invalid!");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
clean:
|
||||
kfree(buf);
|
||||
return result;
|
||||
}
|
||||
38
kernel/manager.h
Normal file
38
kernel/manager.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef __KSU_H_KSU_MANAGER
|
||||
#define __KSU_H_KSU_MANAGER
|
||||
|
||||
#include "linux/cred.h"
|
||||
#include "linux/types.h"
|
||||
|
||||
#define INVALID_UID -1
|
||||
|
||||
extern uid_t ksu_manager_uid; // DO NOT DIRECT USE
|
||||
|
||||
static inline bool ksu_is_manager_uid_valid()
|
||||
{
|
||||
return ksu_manager_uid != INVALID_UID;
|
||||
}
|
||||
|
||||
static inline bool is_manager()
|
||||
{
|
||||
return ksu_manager_uid == current_uid().val;
|
||||
}
|
||||
|
||||
static inline uid_t ksu_get_manager_uid()
|
||||
{
|
||||
return ksu_manager_uid;
|
||||
}
|
||||
|
||||
static inline void ksu_set_manager_uid(uid_t uid)
|
||||
{
|
||||
ksu_manager_uid = uid;
|
||||
}
|
||||
|
||||
static inline void ksu_invalidate_manager_uid()
|
||||
{
|
||||
ksu_manager_uid = INVALID_UID;
|
||||
}
|
||||
|
||||
bool become_manager(char *pkg);
|
||||
|
||||
#endif
|
||||
@@ -1,20 +1,20 @@
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include "linux/kallsyms.h"
|
||||
|
||||
#define RE_EXPORT_SYMBOL1(ret, func, t1, v1) \
|
||||
ret ksu_##func(t1 v1) { \
|
||||
return func(v1); \
|
||||
} \
|
||||
EXPORT_SYMBOL(ksu_##func); \
|
||||
#define RE_EXPORT_SYMBOL1(ret, func, t1, v1) \
|
||||
ret ksu_##func(t1 v1) \
|
||||
{ \
|
||||
return func(v1); \
|
||||
} \
|
||||
EXPORT_SYMBOL(ksu_##func);
|
||||
|
||||
#define RE_EXPORT_SYMBOL2(ret, func, t1, v1, t2, v2) \
|
||||
ret ksu_##func(t1 v1, t2 v2) { \
|
||||
return func(v1, v2); \
|
||||
} \
|
||||
EXPORT_SYMBOL(ksu_##func); \
|
||||
#define RE_EXPORT_SYMBOL2(ret, func, t1, v1, t2, v2) \
|
||||
ret ksu_##func(t1 v1, t2 v2) \
|
||||
{ \
|
||||
return func(v1, v2); \
|
||||
} \
|
||||
EXPORT_SYMBOL(ksu_##func);
|
||||
|
||||
|
||||
RE_EXPORT_SYMBOL1(unsigned long, kallsyms_lookup_name, const char*, name)
|
||||
RE_EXPORT_SYMBOL1(unsigned long, kallsyms_lookup_name, const char *, name)
|
||||
|
||||
// RE_EXPORT_SYMBOL2(int, register_kprobe, struct kprobe *, p)
|
||||
// RE_EXPORT_SYMBOL2(void, unregister_kprobe, struct kprobe *, p)
|
||||
|
||||
@@ -2,6 +2,15 @@ obj-y += selinux.o
|
||||
obj-y += sepolicy.o
|
||||
obj-y += rules.o
|
||||
|
||||
ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
||||
ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID
|
||||
endif
|
||||
|
||||
ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0)
|
||||
ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE
|
||||
endif
|
||||
|
||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion
|
||||
ccflags-y += -Wno-macro-redefined -Wno-declaration-after-statement
|
||||
ccflags-y += -Wno-macro-redefined -Wno-declaration-after-statement -Wno-unused-function
|
||||
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
|
||||
ccflags-y += -I$(objtree)/security/selinux
|
||||
@@ -1 +0,0 @@
|
||||
#include "../../../security/selinux/av_permissions.h"
|
||||
@@ -1 +0,0 @@
|
||||
#include "../../../security/selinux/flask.h"
|
||||
@@ -1,78 +1,464 @@
|
||||
#include "sepolicy.h"
|
||||
#include "linux/uaccess.h"
|
||||
#include "linux/types.h"
|
||||
#include "linux/version.h"
|
||||
|
||||
#include "../klog.h" // IWYU pragma: keep
|
||||
#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
|
||||
#endif
|
||||
|
||||
#define KERNEL_SU_DOMAIN "su"
|
||||
#define KERNEL_SU_FILE "ksu_file"
|
||||
#define KERNEL_EXEC_TYPE "ksu_exec"
|
||||
#define ALL NULL
|
||||
|
||||
static struct policydb *get_policydb(void)
|
||||
{
|
||||
struct policydb *db;
|
||||
// selinux_state does not exists before 4.19
|
||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||
#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;
|
||||
}
|
||||
|
||||
void apply_kernelsu_rules()
|
||||
{
|
||||
struct selinux_policy *policy;
|
||||
struct policydb *db;
|
||||
|
||||
if (!getenforce()) {
|
||||
pr_info("SELinux permissive or disabled, don't apply rules.");
|
||||
return;
|
||||
pr_info("SELinux permissive or disabled, apply rules!");
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
policy = rcu_dereference(selinux_state.policy);
|
||||
db = &policy->policydb;
|
||||
struct policydb *db = get_policydb();
|
||||
|
||||
permissive(db, KERNEL_SU_DOMAIN);
|
||||
typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
|
||||
typeattribute(db, KERNEL_SU_DOMAIN, "netdomain");
|
||||
typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain");
|
||||
ksu_permissive(db, KERNEL_SU_DOMAIN);
|
||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
|
||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain");
|
||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain");
|
||||
|
||||
// Create unconstrained file type
|
||||
ksu_type(db, KERNEL_SU_FILE, "file_type");
|
||||
ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject");
|
||||
ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL);
|
||||
|
||||
// allow all!
|
||||
allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL);
|
||||
ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL);
|
||||
|
||||
// allow us do any ioctl
|
||||
if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
|
||||
allowxperm(db, KERNEL_SU_DOMAIN, ALL, "blk_file", ALL);
|
||||
allowxperm(db, KERNEL_SU_DOMAIN, ALL, "fifo_file", ALL);
|
||||
allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
|
||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "blk_file", ALL);
|
||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "fifo_file", ALL);
|
||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
|
||||
}
|
||||
|
||||
// we need to save allowlist in /data/adb
|
||||
allow(db, "kernel", "adb_data_file", "dir", ALL);
|
||||
allow(db, "kernel", "adb_data_file", "file", ALL);
|
||||
// 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
|
||||
allow(db, "kernel", "shell_data_file", "file", ALL);
|
||||
|
||||
ksu_allow(db, "kernel", "shell_data_file", "file", ALL);
|
||||
// we need to read /data/system/packages.list
|
||||
ksu_allow(db, "kernel", "kernel", "capability", "dac_override");
|
||||
// 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
|
||||
allow(db, "init", "adb_data_file", "file", "execute");
|
||||
allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
|
||||
ksu_allow(db, "init", "adb_data_file", "file", ALL);
|
||||
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
|
||||
|
||||
// copied from Magisk rules
|
||||
// suRights
|
||||
allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
||||
allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "read");
|
||||
allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
||||
allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
||||
allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr");
|
||||
allow(db, ALL, KERNEL_SU_DOMAIN, "process", "sigchld");
|
||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "read");
|
||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr");
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "process", "sigchld");
|
||||
|
||||
// allowLog
|
||||
allow(db, "logd", KERNEL_SU_DOMAIN, "dir", "search");
|
||||
allow(db, "logd", KERNEL_SU_DOMAIN, "file", "read");
|
||||
allow(db, "logd", KERNEL_SU_DOMAIN, "file", "open");
|
||||
allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr");
|
||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "dir", "search");
|
||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "read");
|
||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "open");
|
||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr");
|
||||
|
||||
// dumpsys
|
||||
allow(db, ALL, KERNEL_SU_DOMAIN, "fd", "use");
|
||||
allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "write");
|
||||
allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "read");
|
||||
allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "open");
|
||||
allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "getattr");
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fd", "use");
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "write");
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "read");
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "open");
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "getattr");
|
||||
|
||||
// bootctl
|
||||
allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
||||
allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
||||
allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
||||
allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process", "getattr");
|
||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process",
|
||||
"getattr");
|
||||
|
||||
// Allow all binder transactions
|
||||
allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
||||
|
||||
// Allow system server devpts
|
||||
ksu_allow(db, "system_server", "untrusted_app_all_devpts", "chr_file",
|
||||
"read");
|
||||
ksu_allow(db, "system_server", "untrusted_app_all_devpts", "chr_file",
|
||||
"write");
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_SEPOL_LEN 128
|
||||
|
||||
#define CMD_NORMAL_PERM 1
|
||||
#define CMD_XPERM 2
|
||||
#define CMD_TYPE_STATE 3
|
||||
#define CMD_TYPE 4
|
||||
#define CMD_TYPE_ATTR 5
|
||||
#define CMD_ATTR 6
|
||||
#define CMD_TYPE_TRANSITION 7
|
||||
#define CMD_TYPE_CHANGE 8
|
||||
#define CMD_GENFSCON 9
|
||||
|
||||
struct sepol_data {
|
||||
u32 cmd;
|
||||
u32 subcmd;
|
||||
char __user *sepol1;
|
||||
char __user *sepol2;
|
||||
char __user *sepol3;
|
||||
char __user *sepol4;
|
||||
char __user *sepol5;
|
||||
char __user *sepol6;
|
||||
char __user *sepol7;
|
||||
};
|
||||
|
||||
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
||||
char **object)
|
||||
{
|
||||
if (!user_object) {
|
||||
*object = ALL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*object = buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
||||
static void reset_avc_cache()
|
||||
{
|
||||
#ifndef KSU_COMPAT_USE_SELINUX_STATE
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!getenforce()) {
|
||||
pr_info("SELinux permissive or disabled when handle policy!\n");
|
||||
}
|
||||
|
||||
struct sepol_data data;
|
||||
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
||||
pr_err("sepol: copy sepol_data failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u32 cmd = data.cmd;
|
||||
u32 subcmd = data.subcmd;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
struct policydb *db = get_policydb();
|
||||
|
||||
int ret = -1;
|
||||
if (cmd == CMD_NORMAL_PERM) {
|
||||
char src_buf[MAX_SEPOL_LEN];
|
||||
char tgt_buf[MAX_SEPOL_LEN];
|
||||
char cls_buf[MAX_SEPOL_LEN];
|
||||
char perm_buf[MAX_SEPOL_LEN];
|
||||
|
||||
char *s, *t, *c, *p;
|
||||
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||
pr_err("sepol: copy tgt failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
|
||||
pr_err("sepol: copy cls failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (get_object(perm_buf, data.sepol4, sizeof(perm_buf), &p) <
|
||||
0) {
|
||||
pr_err("sepol: copy perm failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (subcmd == 1) {
|
||||
success = ksu_allow(db, s, t, c, p);
|
||||
} else if (subcmd == 2) {
|
||||
success = ksu_deny(db, s, t, c, p);
|
||||
} else if (subcmd == 3) {
|
||||
success = ksu_auditallow(db, s, t, c, p);
|
||||
} else if (subcmd == 4) {
|
||||
success = ksu_dontaudit(db, s, t, c, p);
|
||||
} else {
|
||||
pr_err("sepol: unknown subcmd: %d", subcmd);
|
||||
}
|
||||
ret = success ? 0 : -1;
|
||||
|
||||
} else if (cmd == CMD_XPERM) {
|
||||
char src_buf[MAX_SEPOL_LEN];
|
||||
char tgt_buf[MAX_SEPOL_LEN];
|
||||
char cls_buf[MAX_SEPOL_LEN];
|
||||
|
||||
char __maybe_unused
|
||||
operation[MAX_SEPOL_LEN]; // it is always ioctl now!
|
||||
char perm_set[MAX_SEPOL_LEN];
|
||||
|
||||
char *s, *t, *c;
|
||||
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||
pr_err("sepol: copy tgt failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
|
||||
pr_err("sepol: copy cls failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(operation, data.sepol4,
|
||||
sizeof(operation)) < 0) {
|
||||
pr_err("sepol: copy operation failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(perm_set, data.sepol5, sizeof(perm_set)) <
|
||||
0) {
|
||||
pr_err("sepol: copy perm_set failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (subcmd == 1) {
|
||||
success = ksu_allowxperm(db, s, t, c, perm_set);
|
||||
} else if (subcmd == 2) {
|
||||
success = ksu_auditallowxperm(db, s, t, c, perm_set);
|
||||
} else if (subcmd == 3) {
|
||||
success = ksu_dontauditxperm(db, s, t, c, perm_set);
|
||||
} else {
|
||||
pr_err("sepol: unknown subcmd: %d", subcmd);
|
||||
}
|
||||
ret = success ? 0 : -1;
|
||||
} else if (cmd == CMD_TYPE_STATE) {
|
||||
char src[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (subcmd == 1) {
|
||||
success = ksu_permissive(db, src);
|
||||
} else if (subcmd == 2) {
|
||||
success = ksu_enforce(db, src);
|
||||
} else {
|
||||
pr_err("sepol: unknown subcmd: %d", subcmd);
|
||||
}
|
||||
if (success)
|
||||
ret = 0;
|
||||
|
||||
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
|
||||
char type[MAX_SEPOL_LEN];
|
||||
char attr[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(type, data.sepol1, sizeof(type)) < 0) {
|
||||
pr_err("sepol: copy type failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(attr, data.sepol2, sizeof(attr)) < 0) {
|
||||
pr_err("sepol: copy attr failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (cmd == CMD_TYPE) {
|
||||
success = ksu_type(db, type, attr);
|
||||
} else {
|
||||
success = ksu_typeattribute(db, type, attr);
|
||||
}
|
||||
if (!success) {
|
||||
pr_err("sepol: %d failed.\n", cmd);
|
||||
goto exit;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
} else if (cmd == CMD_ATTR) {
|
||||
char attr[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(attr, data.sepol1, sizeof(attr)) < 0) {
|
||||
pr_err("sepol: copy attr failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (!ksu_attribute(db, attr)) {
|
||||
pr_err("sepol: %d failed.\n", cmd);
|
||||
goto exit;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
} else if (cmd == CMD_TYPE_TRANSITION) {
|
||||
char src[MAX_SEPOL_LEN];
|
||||
char tgt[MAX_SEPOL_LEN];
|
||||
char cls[MAX_SEPOL_LEN];
|
||||
char default_type[MAX_SEPOL_LEN];
|
||||
char object[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
|
||||
pr_err("sepol: copy tgt failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
|
||||
pr_err("sepol: copy cls failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(default_type, data.sepol4,
|
||||
sizeof(default_type)) < 0) {
|
||||
pr_err("sepol: copy default_type failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
char *real_object;
|
||||
if (data.sepol5 == NULL) {
|
||||
real_object = NULL;
|
||||
} else {
|
||||
if (strncpy_from_user(object, data.sepol5,
|
||||
sizeof(object)) < 0) {
|
||||
pr_err("sepol: copy object failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
real_object = object;
|
||||
}
|
||||
|
||||
bool success = ksu_type_transition(db, src, tgt, cls,
|
||||
default_type, real_object);
|
||||
if (success)
|
||||
ret = 0;
|
||||
|
||||
} else if (cmd == CMD_TYPE_CHANGE) {
|
||||
char src[MAX_SEPOL_LEN];
|
||||
char tgt[MAX_SEPOL_LEN];
|
||||
char cls[MAX_SEPOL_LEN];
|
||||
char default_type[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
|
||||
pr_err("sepol: copy tgt failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
|
||||
pr_err("sepol: copy cls failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(default_type, data.sepol4,
|
||||
sizeof(default_type)) < 0) {
|
||||
pr_err("sepol: copy default_type failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
bool success = false;
|
||||
if (subcmd == 1) {
|
||||
success = ksu_type_change(db, src, tgt, cls,
|
||||
default_type);
|
||||
} else if (subcmd == 2) {
|
||||
success = ksu_type_member(db, src, tgt, cls,
|
||||
default_type);
|
||||
} else {
|
||||
pr_err("sepol: unknown subcmd: %d", subcmd);
|
||||
}
|
||||
if (success)
|
||||
ret = 0;
|
||||
} else if (cmd == CMD_GENFSCON) {
|
||||
char name[MAX_SEPOL_LEN];
|
||||
char path[MAX_SEPOL_LEN];
|
||||
char context[MAX_SEPOL_LEN];
|
||||
if (strncpy_from_user(name, data.sepol1, sizeof(name)) < 0) {
|
||||
pr_err("sepol: copy name failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(path, data.sepol2, sizeof(path)) < 0) {
|
||||
pr_err("sepol: copy path failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(context, data.sepol3, sizeof(context)) <
|
||||
0) {
|
||||
pr_err("sepol: copy context failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!ksu_genfscon(db, name, path, context)) {
|
||||
pr_err("sepol: %d failed.\n", cmd);
|
||||
goto exit;
|
||||
}
|
||||
ret = 0;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
#include "../../../security/selinux/include/security.h"
|
||||
@@ -1,20 +1,10 @@
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/memory.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "../../../security/selinux/ss/sidtab.h"
|
||||
#include "../../../security/selinux/ss/services.h"
|
||||
#include "../../../security/selinux/include/objsec.h"
|
||||
|
||||
#include "selinux.h"
|
||||
#include "../klog.h"
|
||||
#include "objsec.h"
|
||||
#include "linux/version.h"
|
||||
#include "../klog.h" // IWYU pragma: keep
|
||||
#ifndef KSU_COMPAT_USE_SELINUX_STATE
|
||||
#include "avc.h"
|
||||
#endif
|
||||
|
||||
#define KERNEL_SU_DOMAIN "u:r:su:s0"
|
||||
|
||||
@@ -49,8 +39,6 @@ static int transive_to_domain(const char *domain)
|
||||
return error;
|
||||
}
|
||||
|
||||
static bool is_domain_permissive;
|
||||
|
||||
void setup_selinux()
|
||||
{
|
||||
if (transive_to_domain(KERNEL_SU_DOMAIN)) {
|
||||
@@ -59,36 +47,60 @@ void setup_selinux()
|
||||
}
|
||||
|
||||
/* we didn't need this now, we have change selinux rules when boot!
|
||||
if (!is_domain_permissive) {
|
||||
if (set_domain_permissive() == 0) {
|
||||
is_domain_permissive = true;
|
||||
}
|
||||
}*/
|
||||
if (!is_domain_permissive) {
|
||||
if (set_domain_permissive() == 0) {
|
||||
is_domain_permissive = true;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void setenforce(bool enforce)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||
selinux_state.enforcing = enforce;
|
||||
#else
|
||||
selinux_enforcing = enforce;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool getenforce()
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||
if (selinux_state.disabled) {
|
||||
#else
|
||||
if (selinux_disabled) {
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||
return selinux_state.enforcing;
|
||||
#else
|
||||
return false;
|
||||
return selinux_enforcing;
|
||||
#endif
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && !defined(KSU_COMPAT_HAS_CURRENT_SID)
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
#ifndef __KSU_H_SELINUX
|
||||
#define __KSU_H_SELINUX
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "linux/types.h"
|
||||
#include "linux/version.h"
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || defined(KSU_COMPAT_HAS_SELINUX_STATE)
|
||||
#define KSU_COMPAT_USE_SELINUX_STATE
|
||||
#endif
|
||||
|
||||
void setup_selinux();
|
||||
|
||||
|
||||
@@ -1,7 +1,76 @@
|
||||
#include "sepolicy.h"
|
||||
#include "../klog.h"
|
||||
#include "linux/gfp.h"
|
||||
#include "linux/printk.h"
|
||||
#include "linux/slab.h"
|
||||
#include "linux/version.h"
|
||||
|
||||
// Invert is adding rules for auditdeny; in other cases, invert is removing rules
|
||||
#include "../klog.h" // IWYU pragma: keep
|
||||
#include "ss/symtab.h"
|
||||
|
||||
#define KSU_SUPPORT_ADD_TYPE
|
||||
|
||||
/*
|
||||
* Adapt to Huawei HISI kernel without affecting other kernels ,
|
||||
* Huawei Hisi Kernel EBITMAP Enable or Disable Flag ,
|
||||
* From ss/ebitmap.h
|
||||
*/
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) && \
|
||||
LINUX_VERSION_CODE <= KERNEL_VERSION(4, 9, 250)
|
||||
#ifdef HISI_SELINUX_EBITMAP_RO
|
||||
#define CONFIG_IS_HW_HISI
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Declaration
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
static struct avtab_node *get_avtab_node(struct policydb *db,
|
||||
struct avtab_key *key,
|
||||
struct avtab_extended_perms *xperms);
|
||||
|
||||
static bool add_rule(struct policydb *db, const char *s, const char *t,
|
||||
const char *c, const char *p, int effect, bool invert);
|
||||
|
||||
static void add_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
struct type_datum *tgt, struct class_datum *cls,
|
||||
struct perm_datum *perm, int effect, bool invert);
|
||||
|
||||
static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
struct type_datum *tgt, struct class_datum *cls,
|
||||
uint16_t low, uint16_t high, int effect,
|
||||
bool invert);
|
||||
static bool add_xperm_rule(struct policydb *db, const char *s, const char *t,
|
||||
const char *c, const char *range, int effect,
|
||||
bool invert);
|
||||
|
||||
static bool add_type_rule(struct policydb *db, const char *s, const char *t,
|
||||
const char *c, const char *d, int effect);
|
||||
|
||||
static bool add_filename_trans(struct policydb *db, const char *s,
|
||||
const char *t, const char *c, const char *d,
|
||||
const char *o);
|
||||
|
||||
static bool add_genfscon(struct policydb *db, const char *fs_name,
|
||||
const char *path, const char *context);
|
||||
|
||||
static bool add_type(struct policydb *db, const char *type_name, bool attr);
|
||||
|
||||
static bool set_type_state(struct policydb *db, const char *type_name,
|
||||
bool permissive);
|
||||
|
||||
static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
|
||||
struct type_datum *attr);
|
||||
|
||||
static bool add_typeattribute(struct policydb *db, const char *type,
|
||||
const char *attr);
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Implementation
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
// Invert is adding rules for auditdeny; in other cases, invert is removing
|
||||
// rules
|
||||
#define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert)
|
||||
|
||||
#define hash_for_each(node_ptr, n_slot, cur) \
|
||||
@@ -9,26 +78,29 @@
|
||||
for (i = 0; i < n_slot; ++i) \
|
||||
for (cur = node_ptr[i]; cur; cur = cur->next)
|
||||
|
||||
// htable is a struct instead of pointer above 5.8.0:
|
||||
// https://elixir.bootlin.com/linux/v5.8-rc1/source/security/selinux/ss/symtab.h
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||
#define hashtab_for_each(htab, cur) hash_for_each (htab.htable, htab.size, cur)
|
||||
#else
|
||||
#define hashtab_for_each(htab, cur) \
|
||||
hash_for_each (htab->htable, htab->size, cur)
|
||||
#endif
|
||||
|
||||
// symtab_search is introduced on 5.9.0:
|
||||
// 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) \
|
||||
hash_for_each (avtab.htable, avtab.nslot, cur) \
|
||||
;
|
||||
|
||||
static bool is_redundant(struct avtab_node *node)
|
||||
{
|
||||
switch (node->key.specified) {
|
||||
case AVTAB_AUDITDENY:
|
||||
return node->datum.u.data == ~0U;
|
||||
case AVTAB_XPERMS:
|
||||
return node->datum.u.xperms == NULL;
|
||||
default:
|
||||
return node->datum.u.data == 0U;
|
||||
}
|
||||
}
|
||||
|
||||
struct avtab_node *get_avtab_node(struct policydb *db, struct avtab_key *key,
|
||||
struct avtab_extended_perms *xperms)
|
||||
static struct avtab_node *get_avtab_node(struct policydb *db,
|
||||
struct avtab_key *key,
|
||||
struct avtab_extended_perms *xperms)
|
||||
{
|
||||
struct avtab_node *node;
|
||||
|
||||
@@ -54,9 +126,9 @@ struct avtab_node *get_avtab_node(struct policydb *db, struct avtab_key *key,
|
||||
if (!node) {
|
||||
struct avtab_datum avdatum = {};
|
||||
/*
|
||||
* AUDITDENY, aka DONTAUDIT, are &= assigned, versus |= for
|
||||
* others. Initialize the data accordingly.
|
||||
*/
|
||||
* AUDITDENY, aka DONTAUDIT, are &= assigned, versus |= for
|
||||
* others. Initialize the data accordingly.
|
||||
*/
|
||||
if (key->specified & AVTAB_XPERMS) {
|
||||
avdatum.u.xperms = xperms;
|
||||
} else {
|
||||
@@ -66,14 +138,13 @@ struct avtab_node *get_avtab_node(struct policydb *db, struct avtab_key *key,
|
||||
/* 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;
|
||||
}
|
||||
@@ -81,8 +152,8 @@ struct avtab_node *get_avtab_node(struct policydb *db, struct avtab_key *key,
|
||||
return node;
|
||||
}
|
||||
|
||||
bool add_rule(struct policydb *db, const char *s, const char *t, const char *c,
|
||||
const char *p, int effect, bool invert)
|
||||
static bool add_rule(struct policydb *db, const char *s, const char *t,
|
||||
const char *c, const char *p, int effect, bool invert)
|
||||
{
|
||||
struct type_datum *src = NULL, *tgt = NULL;
|
||||
struct class_datum *cls = NULL;
|
||||
@@ -132,9 +203,9 @@ bool add_rule(struct policydb *db, const char *s, const char *t, const char *c,
|
||||
return true;
|
||||
}
|
||||
|
||||
void add_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
struct type_datum *tgt, struct class_datum *cls,
|
||||
struct perm_datum *perm, int effect, bool invert)
|
||||
static void add_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
struct type_datum *tgt, struct class_datum *cls,
|
||||
struct perm_datum *perm, int effect, bool invert)
|
||||
{
|
||||
if (src == NULL) {
|
||||
struct hashtab_node *node;
|
||||
@@ -214,9 +285,10 @@ void add_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
#define xperm_set(x, p) (p[x >> 5] |= (1 << (x & 0x1f)))
|
||||
#define xperm_clear(x, p) (p[x >> 5] &= ~(1 << (x & 0x1f)))
|
||||
|
||||
void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
struct type_datum *tgt, struct class_datum *cls,
|
||||
uint16_t low, uint16_t high, int effect, bool invert)
|
||||
static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
struct type_datum *tgt, struct class_datum *cls,
|
||||
uint16_t low, uint16_t high, int effect,
|
||||
bool invert)
|
||||
{
|
||||
if (src == NULL) {
|
||||
struct hashtab_node *node;
|
||||
@@ -267,9 +339,9 @@ void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
xperms.specified = AVTAB_XPERMS_IOCTLFUNCTION;
|
||||
xperms.driver = ioctl_driver(low);
|
||||
}
|
||||
|
||||
int i;
|
||||
if (xperms.specified == AVTAB_XPERMS_IOCTLDRIVER) {
|
||||
for (int i = ioctl_driver(low); i <= ioctl_driver(high);
|
||||
for (i = ioctl_driver(low); i <= ioctl_driver(high);
|
||||
++i) {
|
||||
if (invert)
|
||||
xperm_clear(i, xperms.perms.p);
|
||||
@@ -277,8 +349,7 @@ void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
xperm_set(i, xperms.perms.p);
|
||||
}
|
||||
} else {
|
||||
for (int i = ioctl_func(low); i <= ioctl_func(high);
|
||||
++i) {
|
||||
for (i = ioctl_func(low); i <= ioctl_func(high); ++i) {
|
||||
if (invert)
|
||||
xperm_clear(i, xperms.perms.p);
|
||||
else
|
||||
@@ -306,8 +377,9 @@ void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
}
|
||||
}
|
||||
|
||||
bool add_xperm_rule(struct policydb *db, const char *s, const char *t,
|
||||
const char *c, const char *range, int effect, bool invert)
|
||||
static bool add_xperm_rule(struct policydb *db, const char *s, const char *t,
|
||||
const char *c, const char *range, int effect,
|
||||
bool invert)
|
||||
{
|
||||
struct type_datum *src = NULL, *tgt = NULL;
|
||||
struct class_datum *cls = NULL;
|
||||
@@ -354,8 +426,8 @@ bool add_xperm_rule(struct policydb *db, const char *s, const char *t,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add_type_rule(struct policydb *db, const char *s, const char *t,
|
||||
const char *c, const char *d, int effect)
|
||||
static bool add_type_rule(struct policydb *db, const char *s, const char *t,
|
||||
const char *c, const char *d, int effect)
|
||||
{
|
||||
struct type_datum *src, *tgt, *def;
|
||||
struct class_datum *cls;
|
||||
@@ -393,23 +465,407 @@ bool add_type_rule(struct policydb *db, const char *s, const char *t,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add_filename_trans(const char *s, const char *t, const char *c,
|
||||
const char *d, const char *o)
|
||||
// 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;
|
||||
unsigned long hash;
|
||||
unsigned int byte_num;
|
||||
unsigned char focus;
|
||||
|
||||
hash = ft->ttype ^ ft->tclass;
|
||||
|
||||
byte_num = 0;
|
||||
while ((focus = ft->name[byte_num++]))
|
||||
hash = partial_name_hash(focus, hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
static int filenametr_cmp(const void *k1, const void *k2)
|
||||
{
|
||||
const struct filename_trans_key *ft1 = k1;
|
||||
const struct filename_trans_key *ft2 = k2;
|
||||
int v;
|
||||
|
||||
v = ft1->ttype - ft2->ttype;
|
||||
if (v)
|
||||
return v;
|
||||
|
||||
v = ft1->tclass - ft2->tclass;
|
||||
if (v)
|
||||
return v;
|
||||
|
||||
return strcmp(ft1->name, ft2->name);
|
||||
}
|
||||
|
||||
static const struct hashtab_key_params filenametr_key_params = {
|
||||
.hash = filenametr_hash,
|
||||
.cmp = filenametr_cmp,
|
||||
};
|
||||
#endif
|
||||
|
||||
static bool add_filename_trans(struct policydb *db, const char *s,
|
||||
const char *t, const char *c, const char *d,
|
||||
const char *o)
|
||||
{
|
||||
struct type_datum *src, *tgt, *def;
|
||||
struct class_datum *cls;
|
||||
|
||||
src = symtab_search(&db->p_types, s);
|
||||
if (src == NULL) {
|
||||
pr_warn("source type %s does not exist\n", s);
|
||||
return false;
|
||||
}
|
||||
tgt = symtab_search(&db->p_types, t);
|
||||
if (tgt == NULL) {
|
||||
pr_warn("target type %s does not exist\n", t);
|
||||
return false;
|
||||
}
|
||||
cls = symtab_search(&db->p_classes, c);
|
||||
if (cls == NULL) {
|
||||
pr_warn("class %s does not exist\n", c);
|
||||
return false;
|
||||
}
|
||||
def = symtab_search(&db->p_types, d);
|
||||
if (def == NULL) {
|
||||
pr_warn("default type %s does not exist\n", d);
|
||||
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
|
||||
trans->otype = def->value;
|
||||
return true;
|
||||
}
|
||||
if (trans->otype == def->value)
|
||||
break;
|
||||
last = trans;
|
||||
trans = trans->next;
|
||||
}
|
||||
|
||||
if (trans == NULL) {
|
||||
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
|
||||
1, GFP_ATOMIC);
|
||||
struct filename_trans_key *new_key =
|
||||
(struct filename_trans_key *)kmalloc(sizeof(*new_key),
|
||||
GFP_ATOMIC);
|
||||
*new_key = key;
|
||||
new_key->name = kstrdup(key.name, GFP_ATOMIC);
|
||||
trans->next = last;
|
||||
trans->otype = def->value;
|
||||
hashtab_insert(&db->filename_trans, new_key, trans,
|
||||
filenametr_key_params);
|
||||
}
|
||||
|
||||
db->compat_filename_trans_count++;
|
||||
return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0;
|
||||
#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
|
||||
}
|
||||
|
||||
static bool add_genfscon(struct policydb *db, const char *fs_name,
|
||||
const char *path, const char *context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool add_genfscon(const char *fs_name, const char *path, const char *context)
|
||||
static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
{
|
||||
#ifdef KSU_SUPPORT_ADD_TYPE
|
||||
struct type_datum *type = symtab_search(&db->p_types, type_name);
|
||||
if (type) {
|
||||
pr_warn("Type %s already exists\n", type_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 value = ++db->p_types.nprim;
|
||||
type = (struct type_datum *)kzalloc(sizeof(struct type_datum),
|
||||
GFP_ATOMIC);
|
||||
if (!type) {
|
||||
pr_err("add_type: alloc type_datum failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
type->primary = 1;
|
||||
type->value = value;
|
||||
type->attribute = attr;
|
||||
|
||||
char *key = kstrdup(type_name, GFP_ATOMIC);
|
||||
if (!key) {
|
||||
pr_err("add_type: alloc key failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (symtab_insert(&db->p_types, key, type)) {
|
||||
pr_err("add_type: insert symtab failed.\n");
|
||||
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));
|
||||
|
||||
struct type_datum **new_type_val_to_struct =
|
||||
krealloc(db->type_val_to_struct,
|
||||
sizeof(*db->type_val_to_struct) * db->p_types.nprim,
|
||||
GFP_ATOMIC);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
char **new_val_to_name_types =
|
||||
krealloc(db->sym_val_to_name[SYM_TYPES],
|
||||
sizeof(char *) * db->symtab[SYM_TYPES].nprim,
|
||||
GFP_KERNEL);
|
||||
if (!new_val_to_name_types) {
|
||||
pr_err("add_type: alloc val_to_name failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
db->type_attr_map_array = new_type_attr_map_array;
|
||||
ebitmap_init(&db->type_attr_map_array[value - 1]);
|
||||
ebitmap_set_bit(&db->type_attr_map_array[value - 1], value - 1, 1);
|
||||
|
||||
db->type_val_to_struct = new_type_val_to_struct;
|
||||
db->type_val_to_struct[value - 1] = type;
|
||||
|
||||
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
|
||||
db->sym_val_to_name[SYM_TYPES][value - 1] = key;
|
||||
|
||||
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;
|
||||
#elif defined(CONFIG_IS_HW_HISI)
|
||||
/*
|
||||
* Huawei use type_attr_map and type_val_to_struct.
|
||||
* And use ebitmap not flex_array.
|
||||
*/
|
||||
size_t new_size = sizeof(struct ebitmap) * db->p_types.nprim;
|
||||
struct ebitmap *new_type_attr_map =
|
||||
(krealloc(db->type_attr_map, new_size, GFP_ATOMIC));
|
||||
|
||||
struct type_datum **new_type_val_to_struct =
|
||||
krealloc(db->type_val_to_struct,
|
||||
sizeof(*db->type_val_to_struct) * db->p_types.nprim,
|
||||
GFP_ATOMIC);
|
||||
|
||||
if (!new_type_attr_map) {
|
||||
pr_err("add_type: alloc type_attr_map failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!new_type_val_to_struct) {
|
||||
pr_err("add_type: alloc type_val_to_struct failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char **new_val_to_name_types =
|
||||
krealloc(db->sym_val_to_name[SYM_TYPES],
|
||||
sizeof(char *) * db->symtab[SYM_TYPES].nprim,
|
||||
GFP_KERNEL);
|
||||
if (!new_val_to_name_types) {
|
||||
pr_err("add_type: alloc val_to_name failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
db->type_attr_map = new_type_attr_map;
|
||||
ebitmap_init(&db->type_attr_map[value - 1], HISI_SELINUX_EBITMAP_RO);
|
||||
ebitmap_set_bit(&db->type_attr_map[value - 1], value - 1, 1);
|
||||
|
||||
db->type_val_to_struct = new_type_val_to_struct;
|
||||
db->type_val_to_struct[value - 1] = type;
|
||||
|
||||
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
|
||||
db->sym_val_to_name[SYM_TYPES][value - 1] = key;
|
||||
|
||||
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;
|
||||
#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
|
||||
}
|
||||
|
||||
bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set_type_state(struct policydb *db, const char *type_name, bool permissive)
|
||||
static bool set_type_state(struct policydb *db, const char *type_name,
|
||||
bool permissive)
|
||||
{
|
||||
struct type_datum *type;
|
||||
if (type_name == NULL) {
|
||||
@@ -437,11 +893,22 @@ bool set_type_state(struct policydb *db, const char *type_name, bool permissive)
|
||||
return true;
|
||||
}
|
||||
|
||||
void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
|
||||
struct type_datum *attr)
|
||||
static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
|
||||
struct type_datum *attr)
|
||||
{
|
||||
ebitmap_set_bit(&db->type_attr_map_array[type->value - 1],
|
||||
attr->value - 1, 1);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
||||
struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1];
|
||||
#elif defined(CONFIG_IS_HW_HISI)
|
||||
/*
|
||||
* HISI_SELINUX_EBITMAP_RO is Huawei's unique features.
|
||||
*/
|
||||
struct ebitmap *sattr = &db->type_attr_map[type->value - 1],
|
||||
HISI_SELINUX_EBITMAP_RO;
|
||||
#else
|
||||
struct ebitmap *sattr =
|
||||
flex_array_get(db->type_attr_map_array, type->value - 1);
|
||||
#endif
|
||||
ebitmap_set_bit(sattr, attr->value - 1, 1);
|
||||
|
||||
struct hashtab_node *node;
|
||||
struct constraint_node *n;
|
||||
@@ -462,7 +929,8 @@ void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
|
||||
};
|
||||
}
|
||||
|
||||
bool add_typeattribute(struct policydb *db, const char *type, const char *attr)
|
||||
static bool add_typeattribute(struct policydb *db, const char *type,
|
||||
const char *attr)
|
||||
{
|
||||
struct type_datum *type_d = symtab_search(&db->p_types, type);
|
||||
if (type_d == NULL) {
|
||||
@@ -486,104 +954,111 @@ bool add_typeattribute(struct policydb *db, const char *type, const char *attr)
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Operation on types
|
||||
bool type(struct policydb *db, const char *name, const char *attr)
|
||||
bool ksu_type(struct policydb *db, const char *name, const char *attr)
|
||||
{
|
||||
return add_type(db, name, false) && add_typeattribute(db, name, attr);
|
||||
}
|
||||
|
||||
bool attribute(struct policydb *db, const char *name)
|
||||
bool ksu_attribute(struct policydb *db, const char *name)
|
||||
{
|
||||
return add_type(db, name, true);
|
||||
}
|
||||
|
||||
bool permissive(struct policydb *db, const char *type)
|
||||
bool ksu_permissive(struct policydb *db, const char *type)
|
||||
{
|
||||
return set_type_state(db, type, true);
|
||||
}
|
||||
|
||||
bool enforce(struct policydb *db, const char *type)
|
||||
bool ksu_enforce(struct policydb *db, const char *type)
|
||||
{
|
||||
return set_type_state(db, type, false);
|
||||
}
|
||||
|
||||
bool typeattribute(struct policydb *db, const char *type, const char *attr)
|
||||
bool ksu_typeattribute(struct policydb *db, const char *type, const char *attr)
|
||||
{
|
||||
return add_typeattribute(db, type, attr);
|
||||
}
|
||||
|
||||
bool exists(struct policydb *db, const char *type)
|
||||
bool ksu_exists(struct policydb *db, const char *type)
|
||||
{
|
||||
return symtab_search(&db->p_types, type) != NULL;
|
||||
}
|
||||
|
||||
// Access vector rules
|
||||
bool allow(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm)
|
||||
bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm)
|
||||
{
|
||||
return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, false);
|
||||
}
|
||||
|
||||
bool deny(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm)
|
||||
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm)
|
||||
{
|
||||
return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, true);
|
||||
}
|
||||
|
||||
bool auditallow(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm)
|
||||
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm)
|
||||
{
|
||||
return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITALLOW, false);
|
||||
}
|
||||
bool dontaudit(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm)
|
||||
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm)
|
||||
{
|
||||
return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITDENY, true);
|
||||
}
|
||||
|
||||
// Extended permissions access vector rules
|
||||
bool allowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *range)
|
||||
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *range)
|
||||
{
|
||||
return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_ALLOWED,
|
||||
false);
|
||||
}
|
||||
|
||||
bool auditallowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *range)
|
||||
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *range)
|
||||
{
|
||||
return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_AUDITALLOW,
|
||||
false);
|
||||
}
|
||||
|
||||
bool dontauditxperm(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *range)
|
||||
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *range)
|
||||
{
|
||||
return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_DONTAUDIT,
|
||||
false);
|
||||
}
|
||||
|
||||
// Type rules
|
||||
bool type_transition(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *def, const char *obj)
|
||||
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *def, const char *obj)
|
||||
{
|
||||
return false;
|
||||
if (obj) {
|
||||
return add_filename_trans(db, src, tgt, cls, def, obj);
|
||||
} else {
|
||||
return add_type_rule(db, src, tgt, cls, def, AVTAB_TRANSITION);
|
||||
}
|
||||
}
|
||||
|
||||
bool type_change(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *def)
|
||||
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *def)
|
||||
{
|
||||
return false;
|
||||
return add_type_rule(db, src, tgt, cls, def, AVTAB_CHANGE);
|
||||
}
|
||||
bool type_member(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *def)
|
||||
|
||||
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *def)
|
||||
{
|
||||
return false;
|
||||
return add_type_rule(db, src, tgt, cls, def, AVTAB_MEMBER);
|
||||
}
|
||||
|
||||
// File system labeling
|
||||
bool genfscon(struct policydb *db, const char *fs_name, const char *path,
|
||||
const char *ctx)
|
||||
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
|
||||
const char *ctx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return add_genfscon(db, fs_name, path, ctx);
|
||||
}
|
||||
|
||||
@@ -1,63 +1,46 @@
|
||||
#ifndef __KSU_H_SEPOLICY
|
||||
#define __KSU_H_SEPOLICY
|
||||
|
||||
#include "../../../security/selinux/ss/sidtab.h"
|
||||
#include "../../../security/selinux/ss/services.h"
|
||||
#include "../../../security/selinux/include/objsec.h"
|
||||
#include "linux/types.h"
|
||||
|
||||
#include "ss/policydb.h"
|
||||
|
||||
// Operation on types
|
||||
bool type(struct policydb* db, const char* name, const char* attr);
|
||||
bool attribute(struct policydb* db, const char* name);
|
||||
bool permissive(struct policydb* db, const char* type);
|
||||
bool enforce(struct policydb* db, const char* type);
|
||||
bool typeattribute(struct policydb* db, const char* type, const char* attr);
|
||||
bool exists(struct policydb* db, const char* type);
|
||||
bool ksu_type(struct policydb *db, const char *name, const char *attr);
|
||||
bool ksu_attribute(struct policydb *db, const char *name);
|
||||
bool ksu_permissive(struct policydb *db, const char *type);
|
||||
bool ksu_enforce(struct policydb *db, const char *type);
|
||||
bool ksu_typeattribute(struct policydb *db, const char *type, const char *attr);
|
||||
bool ksu_exists(struct policydb *db, const char *type);
|
||||
|
||||
// Access vector rules
|
||||
bool allow(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* perm);
|
||||
bool deny(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* perm);
|
||||
bool auditallow(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* perm);
|
||||
bool dontaudit(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* perm);
|
||||
bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm);
|
||||
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm);
|
||||
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm);
|
||||
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *perm);
|
||||
|
||||
// Extended permissions access vector rules
|
||||
bool allowxperm(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* range);
|
||||
bool auditallowxperm(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* range);
|
||||
bool dontauditxperm(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* range);
|
||||
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *range);
|
||||
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *range);
|
||||
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *range);
|
||||
|
||||
// Type rules
|
||||
bool type_transition(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* def, const char* obj);
|
||||
bool type_change(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* def);
|
||||
bool type_member(struct policydb* db, const char* src, const char* tgt, const char* cls, const char* def);
|
||||
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *def, const char *obj);
|
||||
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *def);
|
||||
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
|
||||
const char *cls, const char *def);
|
||||
|
||||
// File system labeling
|
||||
bool genfscon(struct policydb* db, const char* fs_name, const char* path, const char* ctx);
|
||||
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
|
||||
const char *ctx);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Internal use
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
struct avtab_node* get_avtab_node(struct policydb* db, struct avtab_key *key, struct avtab_extended_perms *xperms);
|
||||
|
||||
bool add_rule(struct policydb* db, const char *s, const char *t, const char *c, const char *p, int effect, bool invert);
|
||||
void add_rule_raw(struct policydb* db, struct type_datum *src, struct type_datum *tgt, struct class_datum *cls, struct perm_datum *perm, int effect, bool invert);
|
||||
|
||||
void add_xperm_rule_raw(struct policydb* db, struct type_datum *src, struct type_datum *tgt,
|
||||
struct class_datum *cls, uint16_t low, uint16_t high, int effect, bool invert);
|
||||
bool add_xperm_rule(struct policydb* db, const char *s, const char *t, const char *c, const char *range, int effect, bool invert);
|
||||
|
||||
bool add_type_rule(struct policydb* db, const char *s, const char *t, const char *c, const char *d, int effect);
|
||||
|
||||
bool add_filename_trans(const char *s, const char *t, const char *c, const char *d, const char *o);
|
||||
|
||||
bool add_genfscon(const char *fs_name, const char *path, const char *context);
|
||||
|
||||
bool add_type(struct policydb* db, const char *type_name, bool attr);
|
||||
|
||||
bool set_type_state(struct policydb* db, const char *type_name, bool permissive);
|
||||
|
||||
void add_typeattribute_raw(struct policydb* db, struct type_datum *type, struct type_datum *attr);
|
||||
|
||||
bool add_typeattribute(struct policydb* db, const char *type, const char *attr);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,19 +1,42 @@
|
||||
#! /bin/bash
|
||||
|
||||
set -x
|
||||
|
||||
git clone https://github.com/tiann/KernelSU
|
||||
#!/bin/sh
|
||||
set -eux
|
||||
|
||||
GKI_ROOT=$(pwd)
|
||||
|
||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
||||
echo "[+] Copy kernel su driver to $GKI_ROOT/common/drivers"
|
||||
|
||||
ln -sf $(pwd)/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
||||
if test -d "$GKI_ROOT/common/drivers"; then
|
||||
DRIVER_DIR="$GKI_ROOT/common/drivers"
|
||||
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.'
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "[+] Add kernel su driver to Makefile"
|
||||
test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/tiann/KernelSU
|
||||
cd "$GKI_ROOT/KernelSU"
|
||||
git stash
|
||||
if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then
|
||||
git checkout main
|
||||
fi
|
||||
git pull
|
||||
if [ -z "${1-}" ]; then
|
||||
git checkout "$(git describe --abbrev=0 --tags)"
|
||||
else
|
||||
git checkout "$1"
|
||||
fi
|
||||
cd "$GKI_ROOT"
|
||||
|
||||
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
|
||||
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
|
||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
||||
echo "[+] Copy kernel su driver to $DRIVER_DIR"
|
||||
|
||||
echo "[+] Done."
|
||||
test -e "$DRIVER_DIR/kernelsu" || ln -sf "$GKI_ROOT/KernelSU/kernel" "$DRIVER_DIR/kernelsu"
|
||||
|
||||
echo '[+] Add kernel su driver to Makefile'
|
||||
|
||||
DRIVER_MAKEFILE=$DRIVER_DIR/Makefile
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-y += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
||||
|
||||
echo '[+] Done.'
|
||||
|
||||
@@ -1,34 +1,21 @@
|
||||
#include "asm/current.h"
|
||||
#include "linux/cred.h"
|
||||
#include "linux/err.h"
|
||||
#include "linux/fs.h"
|
||||
#include "linux/kprobes.h"
|
||||
#include "linux/types.h"
|
||||
#include "linux/uaccess.h"
|
||||
#include "linux/version.h"
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
#include "linux/sched/task_stack.h"
|
||||
#else
|
||||
#include "linux/sched.h"
|
||||
#endif
|
||||
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <asm/current.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/memory.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm-generic/errno-base.h>
|
||||
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fs_struct.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
#include "klog.h"
|
||||
#include "arch.h"
|
||||
#include "allowlist.h"
|
||||
#include "selinux/selinux.h"
|
||||
#include "arch.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksud.h"
|
||||
|
||||
#define SU_PATH "/system/bin/su"
|
||||
#define SH_PATH "/system/bin/sh"
|
||||
@@ -37,7 +24,8 @@ extern void escape_to_root();
|
||||
|
||||
static void __user *userspace_stack_buffer(const void *d, size_t len)
|
||||
{
|
||||
/* To avoid having to mmap a page in userspace, just write below the stack pointer. */
|
||||
/* To avoid having to mmap a page in userspace, just write below the stack
|
||||
* pointer. */
|
||||
char __user *p = (void __user *)current_user_stack_pointer() - len;
|
||||
|
||||
return copy_to_user(p, d, len) ? NULL : p;
|
||||
@@ -50,7 +38,8 @@ static char __user *sh_user_path(void)
|
||||
return userspace_stack_buffer(sh_path, sizeof(sh_path));
|
||||
}
|
||||
|
||||
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||
int *flags)
|
||||
{
|
||||
struct filename *filename;
|
||||
const char su[] = SU_PATH;
|
||||
@@ -59,14 +48,14 @@ static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
filename = getname(PT_REGS_PARM2(regs));
|
||||
filename = getname(*filename_user);
|
||||
|
||||
if (IS_ERR(filename)) {
|
||||
return 0;
|
||||
}
|
||||
if (!memcmp(filename->name, su, sizeof(su))) {
|
||||
pr_info("faccessat su->sh!\n");
|
||||
PT_REGS_PARM2(regs) = sh_user_path();
|
||||
*filename_user = sh_user_path();
|
||||
}
|
||||
|
||||
putname(filename);
|
||||
@@ -74,7 +63,7 @@ static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
||||
{
|
||||
// const char sh[] = SH_PATH;
|
||||
struct filename *filename;
|
||||
@@ -84,14 +73,18 @@ static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
filename = getname(PT_REGS_PARM2(regs));
|
||||
if (!filename_user) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
filename = getname(*filename_user);
|
||||
|
||||
if (IS_ERR(filename)) {
|
||||
return 0;
|
||||
}
|
||||
if (!memcmp(filename->name, su, sizeof(su))) {
|
||||
pr_info("newfstatat su->sh!\n");
|
||||
PT_REGS_PARM2(regs) = sh_user_path();
|
||||
*filename_user = sh_user_path();
|
||||
}
|
||||
|
||||
putname(filename);
|
||||
@@ -99,40 +92,21 @@ static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864
|
||||
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags)
|
||||
{
|
||||
struct filename *filename;
|
||||
const char sh[] = SH_PATH;
|
||||
const char sh[] = KSUD_PATH;
|
||||
const char su[] = SU_PATH;
|
||||
|
||||
static const char app_process[] = "/system/bin/app_process";
|
||||
static bool first_app_process = true;
|
||||
static const char system_bin_init[] = "/system/bin/init";
|
||||
static int init_count = 0;
|
||||
if (!filename_ptr)
|
||||
return 0;
|
||||
|
||||
filename = PT_REGS_PARM2(regs);
|
||||
filename = *filename_ptr;
|
||||
if (IS_ERR(filename)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!memcmp(filename->name, system_bin_init, sizeof(system_bin_init) - 1)) {
|
||||
// /system/bin/init executed
|
||||
if (++init_count == 2) {
|
||||
// 1: /system/bin/init selinux_setup
|
||||
// 2: /system/bin/init second_stage
|
||||
pr_info("/system/bin/init second_stage executed\n");
|
||||
apply_kernelsu_rules();
|
||||
}
|
||||
}
|
||||
|
||||
if (first_app_process &&
|
||||
!memcmp(filename->name, app_process, sizeof(app_process) - 1)) {
|
||||
first_app_process = false;
|
||||
pr_info("exec app_process, /data prepared!\n");
|
||||
ksu_load_allow_list();
|
||||
}
|
||||
|
||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -147,136 +121,88 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char KERNEL_SU_RC[] =
|
||||
"\n"
|
||||
#ifdef CONFIG_KPROBES
|
||||
|
||||
"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"
|
||||
"\n"
|
||||
|
||||
"on property:sys.boot_completed=1\n"
|
||||
" exec u:r:su:s0 root -- /data/adb/ksud boot-completed\n"
|
||||
"\n"
|
||||
|
||||
"\n"
|
||||
;
|
||||
|
||||
static void unregister_vfs_read_kp();
|
||||
static struct work_struct unregister_vfs_read_work;
|
||||
|
||||
static int read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct file *file;
|
||||
char __user *buf;
|
||||
size_t count;
|
||||
int *dfd = (int *)PT_REGS_PARM1(regs);
|
||||
const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs);
|
||||
int *mode = (int *)&PT_REGS_PARM3(regs);
|
||||
int *flags = (int *)&PT_REGS_PARM4(regs);
|
||||
|
||||
if (strcmp(current->comm, "init")) {
|
||||
// we are only interest in `init` process
|
||||
return 0;
|
||||
}
|
||||
return ksu_handle_faccessat(dfd, filename_user, mode, flags);
|
||||
}
|
||||
|
||||
file = PT_REGS_PARM1(regs);
|
||||
if (IS_ERR(file)) {
|
||||
return 0;
|
||||
}
|
||||
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *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
|
||||
|
||||
if (!d_is_reg(file->f_path.dentry)) {
|
||||
return 0;
|
||||
}
|
||||
return ksu_handle_stat(dfd, filename_user, flags);
|
||||
}
|
||||
|
||||
const char *short_name = file->f_path.dentry->d_name.name;
|
||||
if (strcmp(short_name, "atrace.rc")) {
|
||||
// we are only interest `atrace.rc` file name file
|
||||
return 0;
|
||||
}
|
||||
char path[PATH_MAX];
|
||||
char* dpath = d_path(&file->f_path, path, PATH_MAX);
|
||||
if (IS_ERR(dpath)) {
|
||||
return 0;
|
||||
}
|
||||
// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864
|
||||
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
int *fd = (int *)&PT_REGS_PARM1(regs);
|
||||
struct filename **filename_ptr =
|
||||
(struct filename **)&PT_REGS_PARM2(regs);
|
||||
void *argv = (void *)&PT_REGS_PARM3(regs);
|
||||
void *envp = (void *)&PT_REGS_PARM4(regs);
|
||||
int *flags = (int *)&PT_REGS_PARM5(regs);
|
||||
|
||||
if (strcmp(dpath, "/system/etc/init/atrace.rc")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we only process the first read
|
||||
static bool rc_inserted = false;
|
||||
if (rc_inserted) {
|
||||
// we don't need this kprobe, unregister it!
|
||||
unregister_vfs_read_kp();
|
||||
return 0;
|
||||
}
|
||||
rc_inserted = true;
|
||||
|
||||
// now we can sure that the init process is reading `/system/etc/init/atrace.rc`
|
||||
buf = PT_REGS_PARM2(regs);
|
||||
count = PT_REGS_PARM3(regs);
|
||||
|
||||
size_t rc_count = strlen(KERNEL_SU_RC);
|
||||
|
||||
pr_info("vfs_read: %s, comm: %s, count: %d, rc_count: %d\n", dpath, current->comm, count, rc_count);
|
||||
|
||||
if (count < rc_count) {
|
||||
pr_err("count: %d < rc_count: %d", count, rc_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t ret = copy_to_user(buf, KERNEL_SU_RC, rc_count);
|
||||
if (ret) {
|
||||
pr_err("copy ksud.rc failed: %d\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PT_REGS_PARM2(regs) = buf + rc_count;
|
||||
PT_REGS_PARM3(regs) = count - rc_count;
|
||||
|
||||
return 0;
|
||||
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp,
|
||||
flags);
|
||||
}
|
||||
|
||||
static struct kprobe faccessat_kp = {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
||||
.symbol_name = "do_faccessat",
|
||||
#else
|
||||
.symbol_name = "sys_faccessat",
|
||||
#endif
|
||||
.pre_handler = faccessat_handler_pre,
|
||||
};
|
||||
|
||||
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)
|
||||
.symbol_name = "__do_execve_file",
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
||||
.symbol_name = "do_execveat_common",
|
||||
#endif
|
||||
.pre_handler = execve_handler_pre,
|
||||
};
|
||||
|
||||
static struct kprobe vfs_read_kp = {
|
||||
.symbol_name = "vfs_read",
|
||||
.pre_handler = read_handler_pre,
|
||||
};
|
||||
|
||||
static void do_unregister_vfs_read_kp(struct work_struct *work) {
|
||||
unregister_kprobe(&vfs_read_kp);
|
||||
}
|
||||
|
||||
static void unregister_vfs_read_kp() {
|
||||
bool ret = schedule_work(&unregister_vfs_read_work);
|
||||
pr_info("unregister vfs_read kprobe: %d!\n", ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
// sucompat: permited process can execute 'su' to gain root access.
|
||||
void enable_sucompat()
|
||||
void ksu_enable_sucompat()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
int ret;
|
||||
|
||||
ret = register_kprobe(&execve_kp);
|
||||
pr_info("execve_kp: %d\n", ret);
|
||||
pr_info("sucompat: execve_kp: %d\n", ret);
|
||||
ret = register_kprobe(&newfstatat_kp);
|
||||
pr_info("newfstatat_kp: %d\n", ret);
|
||||
pr_info("sucompat: newfstatat_kp: %d\n", ret);
|
||||
ret = register_kprobe(&faccessat_kp);
|
||||
pr_info("faccessat_kp: %d\n", ret);
|
||||
|
||||
ret = register_kprobe(&vfs_read_kp);
|
||||
pr_info("vfs_read_kp: %d\n", ret);
|
||||
|
||||
INIT_WORK(&unregister_vfs_read_work, do_unregister_vfs_read_kp);
|
||||
pr_info("sucompat: faccessat_kp: %d\n", ret);
|
||||
#endif
|
||||
}
|
||||
|
||||
139
kernel/uid_observer.c
Normal file
139
kernel/uid_observer.c
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "linux/err.h"
|
||||
#include "linux/fs.h"
|
||||
#include "linux/list.h"
|
||||
#include "linux/slab.h"
|
||||
#include "linux/string.h"
|
||||
#include "linux/types.h"
|
||||
#include "linux/version.h"
|
||||
#include "linux/workqueue.h"
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#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;
|
||||
|
||||
struct uid_data {
|
||||
struct list_head list;
|
||||
u32 uid;
|
||||
};
|
||||
|
||||
static bool is_uid_exist(uid_t uid, void *data)
|
||||
{
|
||||
struct list_head *list = (struct list_head *)data;
|
||||
struct uid_data *np;
|
||||
|
||||
bool exist = false;
|
||||
list_for_each_entry (np, list, list) {
|
||||
if (np->uid == uid) {
|
||||
exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return exist;
|
||||
}
|
||||
|
||||
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
|
||||
" failed: %d\n",
|
||||
ERR_PTR(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
struct list_head uid_list;
|
||||
INIT_LIST_HEAD(&uid_list);
|
||||
|
||||
char chr = 0;
|
||||
loff_t pos = 0;
|
||||
loff_t line_start = 0;
|
||||
char buf[128];
|
||||
for (;;) {
|
||||
ssize_t count = ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
|
||||
if (count != sizeof(chr))
|
||||
break;
|
||||
if (chr != '\n')
|
||||
continue;
|
||||
|
||||
count = ksu_kernel_read_compat(fp, buf, sizeof(buf), &line_start);
|
||||
|
||||
struct uid_data *data =
|
||||
kmalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
||||
if (!data) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
char *tmp = buf;
|
||||
const char *delim = " ";
|
||||
strsep(&tmp, delim); // skip package
|
||||
char *uid = strsep(&tmp, delim);
|
||||
if (!uid) {
|
||||
pr_err("update_uid: uid is NULL!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
u32 res;
|
||||
if (kstrtou32(uid, 10, &res)) {
|
||||
pr_err("update_uid: uid parse err\n");
|
||||
continue;
|
||||
}
|
||||
data->uid = res;
|
||||
list_add_tail(&data->list, &uid_list);
|
||||
// reset line start
|
||||
line_start = pos;
|
||||
}
|
||||
|
||||
// now update uid list
|
||||
struct uid_data *np;
|
||||
struct uid_data *n;
|
||||
|
||||
// first, check if manager_uid exist!
|
||||
bool manager_exist = false;
|
||||
list_for_each_entry (np, &uid_list, list) {
|
||||
// if manager is installed in work profile, the uid in packages.list is still equals main profile
|
||||
// don't delete it in this case!
|
||||
int manager_uid = ksu_get_manager_uid() % 100000;
|
||||
if (np->uid == manager_uid) {
|
||||
manager_exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!manager_exist && ksu_is_manager_uid_valid()) {
|
||||
pr_info("manager is uninstalled, invalidate it!\n");
|
||||
ksu_invalidate_manager_uid();
|
||||
}
|
||||
|
||||
// then prune the allowlist
|
||||
ksu_prune_allowlist(is_uid_exist, &uid_list);
|
||||
out:
|
||||
// free uid_list
|
||||
list_for_each_entry_safe (np, n, &uid_list, list) {
|
||||
list_del(&np->list);
|
||||
kfree(np);
|
||||
}
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
|
||||
void update_uid()
|
||||
{
|
||||
ksu_queue_work(&ksu_update_uid_work);
|
||||
}
|
||||
|
||||
int ksu_uid_observer_init()
|
||||
{
|
||||
INIT_WORK(&ksu_update_uid_work, do_update_uid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksu_uid_observer_exit()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
10
kernel/uid_observer.h
Normal file
10
kernel/uid_observer.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef __KSU_H_UID_OBSERVER
|
||||
#define __KSU_H_UID_OBSERVER
|
||||
|
||||
int ksu_uid_observer_init();
|
||||
|
||||
int ksu_uid_observer_exit();
|
||||
|
||||
void update_uid();
|
||||
|
||||
#endif
|
||||
@@ -9,6 +9,8 @@ plugins {
|
||||
android {
|
||||
namespace = "me.weishu.kernelsu"
|
||||
|
||||
ndkVersion = "25.1.8937393"
|
||||
|
||||
defaultConfig {
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -17,6 +19,10 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
lint {
|
||||
checkReleaseBuilds = false
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
@@ -55,29 +61,34 @@ android {
|
||||
dependencies {
|
||||
val accompanistVersion = "0.28.0"
|
||||
val composeDestinationsVersion = "1.7.27-beta"
|
||||
implementation(platform("androidx.compose:compose-bom:2022.12.00"))
|
||||
|
||||
implementation(platform("androidx.compose:compose-bom:2023.04.00"))
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
implementation("androidx.activity:activity-compose:1.6.1")
|
||||
implementation("androidx.activity:activity-compose:1.7.0")
|
||||
implementation("androidx.compose.material:material:1.5.0-alpha01")
|
||||
implementation("androidx.compose.material:material-icons-extended")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.core:core-ktx:1.9.0")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
|
||||
implementation("androidx.navigation:navigation-compose:2.5.3")
|
||||
implementation("com.google.accompanist:accompanist-drawablepainter:$accompanistVersion")
|
||||
implementation("com.google.accompanist:accompanist-navigation-animation:$accompanistVersion")
|
||||
implementation("com.google.accompanist:accompanist-swiperefresh:$accompanistVersion")
|
||||
implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion")
|
||||
implementation("io.github.raamcosta.compose-destinations:animations-core:$composeDestinationsVersion")
|
||||
|
||||
implementation("io.coil-kt:coil-compose:2.2.2")
|
||||
implementation("io.coil-kt:coil-compose:2.3.0")
|
||||
implementation("me.zhanghai.android.appiconloader:appiconloader-coil:1.5.0")
|
||||
|
||||
implementation("com.github.topjohnwu.libsu:core:5.0.3")
|
||||
implementation("com.github.alorma:compose-settings-ui-m3:0.15.0")
|
||||
val libsuVersion = "5.0.5"
|
||||
// change to official build(com.github.topjohnwu.libsu) when this pr is merged:
|
||||
// https://github.com/topjohnwu/libsu/pull/151
|
||||
implementation("com.github.tiann.libsu:core:$libsuVersion")
|
||||
implementation("com.github.tiann.libsu:service:$libsuVersion")
|
||||
implementation("dev.rikka.rikkax.parcelablelist:parcelablelist:2.0.0")
|
||||
|
||||
implementation("com.github.alorma:compose-settings-ui-m3:0.22.0")
|
||||
|
||||
ksp("io.github.raamcosta.compose-destinations:ksp:$composeDestinationsVersion")
|
||||
|
||||
|
||||
@@ -2,15 +2,12 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
|
||||
<application
|
||||
android:name=".KernelSUApplication"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:extractNativeLibs="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
@@ -31,6 +28,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>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// IKsuInterface.aidl
|
||||
package me.weishu.kernelsu;
|
||||
|
||||
import android.content.pm.PackageInfo;
|
||||
import rikka.parcelablelist.ParcelableListSlice;
|
||||
|
||||
interface IKsuInterface {
|
||||
ParcelableListSlice<PackageInfo> getPackages(int flags);
|
||||
}
|
||||
@@ -15,6 +15,4 @@ add_library(kernelsu
|
||||
|
||||
find_library(log-lib log)
|
||||
|
||||
target_link_libraries(kernelsu ${log-lib})
|
||||
|
||||
add_executable(libksu.so su.c)
|
||||
target_link_libraries(kernelsu ${log-lib})
|
||||
@@ -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();
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ksu.h"
|
||||
|
||||
@@ -19,6 +20,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;
|
||||
@@ -28,7 +30,14 @@ static bool ksuctl(int cmd, void* arg1, void* arg2) {
|
||||
|
||||
bool become_manager(const char* pkg) {
|
||||
char param[128];
|
||||
sprintf(param, "/data/data/%s", pkg);
|
||||
uid_t uid = getuid();
|
||||
uint32_t userId = uid / 100000;
|
||||
if (userId == 0) {
|
||||
sprintf(param, "/data/data/%s", pkg);
|
||||
} else {
|
||||
snprintf(param, sizeof(param), "/data/user/%d/%s", userId, pkg);
|
||||
}
|
||||
|
||||
return ksuctl(CMD_BECOME_MANAGER, param, nullptr);
|
||||
}
|
||||
|
||||
@@ -51,4 +60,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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
int main(){
|
||||
int32_t result = 0;
|
||||
prctl(0xdeadbeef, 0, 0, 0, &result);
|
||||
system("/system/bin/sh");
|
||||
return 0;
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package me.weishu.kernelsu
|
||||
import android.app.Application
|
||||
import coil.Coil
|
||||
import coil.ImageLoader
|
||||
import me.weishu.kernelsu.ui.util.install
|
||||
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
|
||||
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
|
||||
|
||||
@@ -25,8 +24,6 @@ class KernelSUApplication : Application() {
|
||||
}
|
||||
.build()
|
||||
)
|
||||
|
||||
install()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package me.weishu.kernelsu.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.IBinder;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.topjohnwu.superuser.ipc.RootService;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import me.weishu.kernelsu.IKsuInterface;
|
||||
import rikka.parcelablelist.ParcelableListSlice;
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2023/4/18.
|
||||
*/
|
||||
|
||||
public class KsuService extends RootService {
|
||||
|
||||
private static final String TAG = "KsuService";
|
||||
|
||||
class Stub extends IKsuInterface.Stub {
|
||||
@Override
|
||||
public ParcelableListSlice<PackageInfo> getPackages(int flags) {
|
||||
List<PackageInfo> list = getInstalledPackagesAll(flags);
|
||||
Log.i(TAG, "getPackages: " + list.size());
|
||||
return new ParcelableListSlice<>(list);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(@NonNull Intent intent) {
|
||||
return new Stub();
|
||||
}
|
||||
|
||||
List<Integer> getUserIds() {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
|
||||
List<UserHandle> userProfiles = um.getUserProfiles();
|
||||
for (UserHandle userProfile : userProfiles) {
|
||||
int userId = userProfile.hashCode();
|
||||
result.add(userProfile.hashCode());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ArrayList<PackageInfo> getInstalledPackagesAll(int flags) {
|
||||
ArrayList<PackageInfo> packages = new ArrayList<>();
|
||||
for (Integer userId : getUserIds()) {
|
||||
Log.i(TAG, "getInstalledPackagesAll: " + userId);
|
||||
packages.addAll(getInstalledPackagesAsUser(flags, userId));
|
||||
}
|
||||
return packages;
|
||||
}
|
||||
|
||||
List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
|
||||
try {
|
||||
PackageManager pm = getPackageManager();
|
||||
Method getInstalledPackagesAsUser = pm.getClass().getDeclaredMethod("getInstalledPackagesAsUser", int.class, int.class);
|
||||
return (List<PackageInfo>) getInstalledPackagesAsUser.invoke(pm, flags, userId);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "err", e);
|
||||
}
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -15,12 +16,14 @@ import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||
import androidx.navigation.NavHostController
|
||||
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
|
||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||
import me.weishu.kernelsu.ui.component.rememberDialogHostState
|
||||
import me.weishu.kernelsu.ui.screen.BottomBarDestination
|
||||
import me.weishu.kernelsu.ui.screen.NavGraphs
|
||||
import me.weishu.kernelsu.ui.screen.appCurrentDestinationAsState
|
||||
import me.weishu.kernelsu.ui.screen.destinations.Destination
|
||||
import me.weishu.kernelsu.ui.screen.startAppDestination
|
||||
import me.weishu.kernelsu.ui.theme.KernelSUTheme
|
||||
import me.weishu.kernelsu.ui.util.LocalDialogHost
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
@@ -37,7 +40,10 @@ class MainActivity : ComponentActivity() {
|
||||
bottomBar = { BottomBar(navController) },
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) }
|
||||
) { innerPadding ->
|
||||
CompositionLocalProvider(LocalSnackbarHost provides snackbarHostState) {
|
||||
CompositionLocalProvider(
|
||||
LocalSnackbarHost provides snackbarHostState,
|
||||
LocalDialogHost provides rememberDialogHostState(),
|
||||
) {
|
||||
DestinationsNavHost(
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
navGraph = NavGraphs.root,
|
||||
@@ -52,31 +58,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
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.widget.TextView
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
||||
import me.weishu.kernelsu.BuildConfig
|
||||
import me.weishu.kernelsu.R
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun AboutCard() {
|
||||
ElevatedCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(24.dp)
|
||||
) {
|
||||
AboutCardContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AboutDialog(showAboutDialog: MutableState<Boolean>) {
|
||||
if (showAboutDialog.value) {
|
||||
Dialog(onDismissRequest = { showAboutDialog.value = false }) {
|
||||
AboutCard()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AboutCardContent() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.bodyMedium) {
|
||||
|
||||
val drawable = ResourcesCompat.getDrawable(
|
||||
LocalContext.current.resources,
|
||||
R.mipmap.ic_launcher,
|
||||
LocalContext.current.theme
|
||||
)
|
||||
|
||||
Row {
|
||||
Image(
|
||||
painter = rememberDrawablePainter(drawable),
|
||||
contentDescription = "icon",
|
||||
modifier = Modifier.size(40.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
Column {
|
||||
|
||||
Text(
|
||||
stringResource(id = R.string.app_name),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
Text(
|
||||
BuildConfig.VERSION_NAME,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
HtmlText(
|
||||
html = stringResource(
|
||||
id = R.string.about_source_code,
|
||||
"<b><a href=\"https://github.com/tiann/KernelSU\">GitHub</a></b>",
|
||||
"<b><a href=\"https://t.me/KernelSU\">Telegram</a></b>"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HtmlText(html: String, modifier: Modifier = Modifier) {
|
||||
AndroidView(
|
||||
modifier = modifier,
|
||||
factory = { context ->
|
||||
TextView(context).also {
|
||||
it.movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
},
|
||||
update = { it.text = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_COMPACT) }
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
@@ -22,21 +20,18 @@ 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.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootNavGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.*
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.LinkifyText
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.reboot
|
||||
import me.weishu.kernelsu.ui.util.*
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@RootNavGraph(start = true)
|
||||
@@ -59,10 +54,15 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
) {
|
||||
val kernelVersion = getKernelVersion()
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
SideEffect {
|
||||
if (isManager) install()
|
||||
}
|
||||
val ksuVersion = if (isManager) Natives.getVersion() else null
|
||||
|
||||
StatusCard(kernelVersion, ksuVersion)
|
||||
InfoCard()
|
||||
DonateCard()
|
||||
LearnMoreCard()
|
||||
Spacer(Modifier)
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,8 @@ private fun TopBar(onSettingsClick: () -> Unit) {
|
||||
|
||||
RebootDropdownItem(id = R.string.reboot)
|
||||
|
||||
val pm = LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
val pm =
|
||||
LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
|
||||
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
|
||||
}
|
||||
@@ -127,21 +128,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))
|
||||
@@ -149,6 +158,16 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
||||
text = stringResource(R.string.home_working_version, ksuVersion),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.home_superuser_count, getSuperuserCount()),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.home_module_count, getModuleCount()),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
kernelVersion.isGKI() -> {
|
||||
@@ -185,11 +204,69 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LearnMoreCard() {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
ElevatedCard {
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
uriHandler.openUri("https://kernelsu.org/guide/what-is-kernelsu.html")
|
||||
}
|
||||
.padding(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column() {
|
||||
Text(
|
||||
text = stringResource(R.string.home_learn_kernelsu),
|
||||
style = MaterialTheme.typography.titleSmall
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.home_click_to_learn_kernelsu),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DonateCard() {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
ElevatedCard {
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
uriHandler.openUri("https://patreon.com/weishu")
|
||||
}
|
||||
.padding(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column() {
|
||||
Text(
|
||||
text = stringResource(R.string.home_support_title),
|
||||
style = MaterialTheme.typography.titleSmall
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.home_support_content),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoCard() {
|
||||
val context = LocalContext.current
|
||||
val snackbarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
ElevatedCard {
|
||||
Column(
|
||||
@@ -209,38 +286,23 @@ 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(16.dp))
|
||||
InfoCardItem(stringResource(R.string.home_manager_version), getManagerVersion(context))
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
InfoCardItem(stringResource(R.string.home_version), uname.version)
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
InfoCardItem(stringResource(R.string.home_api), Build.VERSION.SDK_INT.toString())
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
InfoCardItem(stringResource(R.string.home_abi), Build.SUPPORTED_ABIS.joinToString(", "))
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(stringResource(R.string.home_fingerprint), Build.FINGERPRINT)
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
InfoCardItem(stringResource(R.string.home_securitypatch), Build.VERSION.SECURITY_PATCH)
|
||||
|
||||
val copiedMessage = stringResource(R.string.home_copied_to_clipboard)
|
||||
TextButton(
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
onClick = {
|
||||
val cm = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
cm.setPrimaryClip(ClipData.newPlainText("KernelSU", contents.toString()))
|
||||
scope.launch { snackbarHost.showSnackbar(copiedMessage) }
|
||||
},
|
||||
content = { Text(stringResource(android.R.string.copy)) }
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(stringResource(R.string.home_selinux_status), getSELinuxStatus())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getManagerVersion(context: Context): String {
|
||||
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
|
||||
return "${packageInfo.versionName} (${packageInfo.versionCode})"
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun StatusCardPreview() {
|
||||
|
||||
@@ -24,7 +24,6 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.installModule
|
||||
import me.weishu.kernelsu.ui.util.reboot
|
||||
@@ -73,7 +72,7 @@ fun InstallScreen(navigator: DestinationsNavigator, uri: Uri) {
|
||||
val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault())
|
||||
val date = format.format(Date())
|
||||
val file = File(
|
||||
ksuApp.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
"KernelSU_install_log_${date}.log"
|
||||
)
|
||||
file.writeText(text)
|
||||
|
||||
@@ -8,31 +8,35 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
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.screen.destinations.InstallScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
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)
|
||||
@@ -40,9 +44,6 @@ import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
|
||||
@Composable
|
||||
fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
val viewModel = viewModel<ModuleViewModel>()
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (viewModel.moduleList.isEmpty()) {
|
||||
@@ -50,11 +51,18 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar()
|
||||
},
|
||||
floatingActionButton = {
|
||||
val isSafeMode = Natives.isSafeMode()
|
||||
val isKSUVersionInvalid = Natives.getVersion() < 0
|
||||
val hasMagisk = hasMagisk()
|
||||
|
||||
val hideInstallButton = isSafeMode || isKSUVersionInvalid || hasMagisk
|
||||
|
||||
Scaffold(topBar = {
|
||||
TopBar()
|
||||
}, floatingActionButton = if (hideInstallButton) {
|
||||
{ /* Empty */ }
|
||||
} else {
|
||||
{
|
||||
val moduleInstall = stringResource(id = R.string.module_install)
|
||||
val selectZipLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult()
|
||||
@@ -80,76 +88,170 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
icon = { Icon(Icons.Filled.Add, moduleInstall) },
|
||||
text = { Text(text = moduleInstall) },
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
||||
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)
|
||||
val successUninstall = stringResource(R.string.module_uninstall_success)
|
||||
val swipeState = rememberSwipeRefreshState(viewModel.isRefreshing)
|
||||
// TODO: Replace SwipeRefresh with RefreshIndicator when it's ready
|
||||
if (Natives.getVersion() < 8) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(stringResource(R.string.require_kernel_version_8))
|
||||
}
|
||||
return@Scaffold
|
||||
}
|
||||
SwipeRefresh(
|
||||
state = swipeState,
|
||||
onRefresh = {
|
||||
scope.launch { viewModel.fetchModuleList() }
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
val isEmpty = viewModel.moduleList.isEmpty()
|
||||
if (isEmpty) {
|
||||
swipeState.isRefreshing = false
|
||||
}) { innerPadding ->
|
||||
|
||||
ConfirmDialog()
|
||||
|
||||
when {
|
||||
isKSUVersionInvalid -> {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(stringResource(R.string.module_empty))
|
||||
Text(stringResource(R.string.require_kernel_version_8))
|
||||
}
|
||||
} else {
|
||||
LazyColumn {
|
||||
}
|
||||
hasMagisk -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(24.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.module_magisk_conflict),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
ModuleList(
|
||||
viewModel = viewModel,
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier) {
|
||||
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)
|
||||
val successUninstall = stringResource(R.string.module_uninstall_success)
|
||||
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)
|
||||
|
||||
val dialogHost = LocalDialogHost.current
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
|
||||
suspend fun onModuleUninstall(module: ModuleViewModel.ModuleInfo) {
|
||||
val dialogResult = dialogHost.showDialog(
|
||||
moduleStr,
|
||||
content = moduleUninstallConfirm.format(module.name),
|
||||
confirm = uninstall,
|
||||
dismiss = cancel
|
||||
)
|
||||
if (dialogResult != DialogResult.Confirmed) {
|
||||
return
|
||||
}
|
||||
|
||||
val success = uninstallModule(module.id)
|
||||
if (success) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
val refreshState = rememberPullRefreshState(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
onRefresh = { viewModel.fetchModuleList() }
|
||||
)
|
||||
Box(modifier.pullRefresh(refreshState)) {
|
||||
if (viewModel.isOverlayAvailable) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = remember {
|
||||
PaddingValues(
|
||||
start = 16.dp,
|
||||
top = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 16.dp
|
||||
+ 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */
|
||||
)
|
||||
},
|
||||
) {
|
||||
val isEmpty = viewModel.moduleList.isEmpty()
|
||||
if (isEmpty) {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(stringResource(R.string.module_empty))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
items(viewModel.moduleList) { module ->
|
||||
var isChecked by rememberSaveable(module) { mutableStateOf(module.enabled) }
|
||||
ModuleItem(module,
|
||||
isChecked,
|
||||
onUninstall = {
|
||||
val scope = rememberCoroutineScope()
|
||||
ModuleItem(module, isChecked, onUninstall = {
|
||||
scope.launch { onModuleUninstall(module) }
|
||||
}, onCheckChanged = {
|
||||
val success = toggleModule(module.id, !isChecked)
|
||||
if (success) {
|
||||
isChecked = it
|
||||
scope.launch {
|
||||
val result = uninstallModule(module.id)
|
||||
snackBarHost.showSnackbar(
|
||||
if (result) {
|
||||
successUninstall.format(module.name)
|
||||
} else {
|
||||
failedUninstall.format(module.name)
|
||||
}
|
||||
viewModel.fetchModuleList()
|
||||
|
||||
val result = snackBarHost.showSnackbar(
|
||||
rebootToApply, actionLabel = reboot
|
||||
)
|
||||
if (result == SnackbarResult.ActionPerformed) {
|
||||
reboot()
|
||||
}
|
||||
}
|
||||
},
|
||||
onCheckChanged = {
|
||||
val success = toggleModule(module.id, !isChecked)
|
||||
if (success) {
|
||||
isChecked = it
|
||||
} else scope.launch {
|
||||
val message = if (isChecked) failedDisable else failedEnable
|
||||
snackBarHost.showSnackbar(message.format(module.name))
|
||||
}
|
||||
})
|
||||
} 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(stringResource(R.string.module_overlay_fs_not_available))
|
||||
}
|
||||
}
|
||||
|
||||
PullRefreshIndicator(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
state = refreshState,
|
||||
modifier = Modifier.align(
|
||||
Alignment.TopCenter
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar() {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.module)) }
|
||||
)
|
||||
TopAppBar(title = { Text(stringResource(R.string.module)) })
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -160,62 +262,69 @@ private fun ModuleItem(
|
||||
onCheckChanged: (Boolean) -> Unit
|
||||
) {
|
||||
ElevatedCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
colors =
|
||||
CardDefaults.elevatedCardColors(containerColor = MaterialTheme.colorScheme.surface)
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.elevatedCardColors(containerColor = MaterialTheme.colorScheme.surface)
|
||||
) {
|
||||
|
||||
val textDecoration = if (!module.remove) null else TextDecoration.LineThrough
|
||||
|
||||
Column(modifier = Modifier.padding(16.dp, 16.dp, 16.dp, 0.dp)) {
|
||||
Row {
|
||||
Column {
|
||||
Column(modifier = Modifier.padding(24.dp, 16.dp, 24.dp, 0.dp)) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
val moduleVersion = stringResource(id = R.string.module_version)
|
||||
val moduleAuthor = stringResource(id = R.string.module_author)
|
||||
|
||||
Column(modifier = Modifier.fillMaxWidth(0.8f)) {
|
||||
Text(
|
||||
text = module.name,
|
||||
fontSize = MaterialTheme.typography.titleLarge.fontSize,
|
||||
fontFamily = MaterialTheme.typography.titleLarge.fontFamily,
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.titleMedium.fontFamily,
|
||||
textDecoration = textDecoration,
|
||||
)
|
||||
|
||||
Row {
|
||||
Text(
|
||||
text = module.version,
|
||||
fontFamily = MaterialTheme.typography.titleMedium.fontFamily,
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
Text(
|
||||
text = "$moduleVersion: ${module.version}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
Text(
|
||||
text = module.author,
|
||||
fontFamily = MaterialTheme.typography.titleMedium.fontFamily,
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
|
||||
}
|
||||
Text(
|
||||
text = "$moduleAuthor: ${module.author}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Switch(
|
||||
enabled = !module.update,
|
||||
checked = isChecked,
|
||||
onCheckedChange = onCheckChanged
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
Switch(
|
||||
enabled = !module.update,
|
||||
checked = isChecked,
|
||||
onCheckedChange = onCheckChanged
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
Text(
|
||||
text = module.description,
|
||||
fontFamily = MaterialTheme.typography.bodyMedium.fontFamily,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodyMedium.lineHeight,
|
||||
fontWeight = MaterialTheme.typography.bodyMedium.fontWeight,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontWeight = MaterialTheme.typography.bodySmall.fontWeight,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 4,
|
||||
textDecoration = textDecoration
|
||||
|
||||
@@ -1,21 +1,34 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
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.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color.Companion.White
|
||||
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.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
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.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.BuildConfig
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.util.LinkifyText
|
||||
import me.weishu.kernelsu.ui.component.AboutDialog
|
||||
import me.weishu.kernelsu.ui.util.LocalDialogHost
|
||||
import me.weishu.kernelsu.ui.util.getBugreportFile
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
@@ -34,46 +47,60 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
) { paddingValues ->
|
||||
|
||||
var openDialog by remember { mutableStateOf(false) }
|
||||
val showAboutDialog = remember { mutableStateOf(false) }
|
||||
AboutDialog(showAboutDialog)
|
||||
|
||||
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))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
var showLoadingDialog by remember { mutableStateOf(false) }
|
||||
LoadingDialog(showLoadingDialog)
|
||||
|
||||
Column(modifier = Modifier.padding(paddingValues)) {
|
||||
|
||||
SettingsSwitch(
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val dialogHost = LocalDialogHost.current
|
||||
SettingsMenuLink(
|
||||
title = {
|
||||
Text(stringResource(id = R.string.settings_system_rw))
|
||||
Text(stringResource(id = R.string.send_log))
|
||||
},
|
||||
subtitle = {
|
||||
Text(stringResource(id = R.string.settings_system_rw_summary))
|
||||
onClick = {
|
||||
scope.launch {
|
||||
showLoadingDialog = true
|
||||
|
||||
val bugreport = withContext(Dispatchers.IO) {
|
||||
getBugreportFile(context)
|
||||
}
|
||||
|
||||
showLoadingDialog = false
|
||||
|
||||
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)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
SettingsMenuLink(title = {
|
||||
Text(stringResource(id = R.string.about))
|
||||
},
|
||||
|
||||
val about = stringResource(id = R.string.about)
|
||||
SettingsMenuLink(
|
||||
title = {
|
||||
Text(about)
|
||||
},
|
||||
onClick = {
|
||||
openDialog = true
|
||||
showAboutDialog.value = true
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -93,18 +120,23 @@ private fun TopBar(onBack: () -> Unit = {}) {
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun SupportCard() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
fun LoadingDialog(showLoadingDialog: Boolean) {
|
||||
if (!showLoadingDialog) {
|
||||
return
|
||||
}
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = { },
|
||||
DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false)
|
||||
) {
|
||||
CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.bodyMedium) {
|
||||
LinkifyText("Author: weishu")
|
||||
LinkifyText("Github: https://github.com/tiann/KernelSU")
|
||||
LinkifyText("Telegram: https://t.me/KernelSU")
|
||||
LinkifyText("QQ: https://pd.qq.com/s/8lipl1brp")
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.size(100.dp)
|
||||
.background(White, shape = RoundedCornerShape(8.dp))
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,38 @@
|
||||
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.ExperimentalMaterialApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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
|
||||
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.SearchAppBar
|
||||
import me.weishu.kernelsu.ui.util.LocalDialogHost
|
||||
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)
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
||||
@Destination
|
||||
@Composable
|
||||
fun SuperUserScreen() {
|
||||
@@ -60,35 +52,100 @@ 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 ->
|
||||
val failMessage = stringResource(R.string.superuser_failed_to_grant_root)
|
||||
|
||||
// TODO: Replace SwipeRefresh with RefreshIndicator when it's ready
|
||||
SwipeRefresh(
|
||||
state = rememberSwipeRefreshState(viewModel.isRefreshing),
|
||||
onRefresh = {
|
||||
scope.launch { viewModel.fetchAppList() }
|
||||
},
|
||||
ConfirmDialog()
|
||||
|
||||
val refreshState = rememberPullRefreshState(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
onRefresh = { scope.launch { viewModel.fetchAppList() } },
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
.pullRefresh(refreshState)
|
||||
) {
|
||||
LazyColumn {
|
||||
items(viewModel.appList) { app ->
|
||||
val failMessage = stringResource(R.string.superuser_failed_to_grant_root)
|
||||
|
||||
LazyColumn(Modifier.fillMaxSize()) {
|
||||
items(viewModel.appList, key = { it.packageName + it.uid }) { app ->
|
||||
var isChecked by rememberSaveable(app) { mutableStateOf(app.onAllowList) }
|
||||
val dialogHost = LocalDialogHost.current
|
||||
val content =
|
||||
stringResource(id = R.string.superuser_allow_root_confirm, app.label)
|
||||
val confirm = stringResource(id = android.R.string.ok)
|
||||
val cancel = stringResource(id = android.R.string.cancel)
|
||||
|
||||
AppItem(app, isChecked) { checked ->
|
||||
val success = Natives.allowRoot(app.uid, checked)
|
||||
if (success) {
|
||||
isChecked = checked
|
||||
} else scope.launch {
|
||||
snackbarHost.showSnackbar(failMessage.format(app.uid))
|
||||
scope.launch {
|
||||
if (checked) {
|
||||
val dialogResult = dialogHost.showDialog(
|
||||
app.label,
|
||||
content = content,
|
||||
confirm = confirm,
|
||||
dismiss = cancel
|
||||
)
|
||||
if (dialogResult != DialogResult.Confirmed) {
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
val success = Natives.allowRoot(app.uid, checked)
|
||||
if (success) {
|
||||
isChecked = checked
|
||||
} else {
|
||||
snackbarHost.showSnackbar(failMessage.format(app.uid))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PullRefreshIndicator(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
state = refreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.view.ViewCompat
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
|
||||
@@ -47,6 +48,12 @@ fun KernelSUTheme(
|
||||
color = colorScheme.surface,
|
||||
darkIcons = !darkTheme
|
||||
)
|
||||
|
||||
// To match the App Navbar color
|
||||
systemUiController.setNavigationBarColor(
|
||||
color = colorScheme.surfaceColorAtElevation(8.dp),
|
||||
darkIcons = !darkTheme,
|
||||
)
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
@@ -54,4 +61,4 @@ fun KernelSUTheme(
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
@@ -7,7 +7,10 @@ import com.topjohnwu.superuser.CallbackList
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import me.weishu.kernelsu.BuildConfig
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
|
||||
import org.json.JSONArray
|
||||
import java.io.File
|
||||
|
||||
|
||||
@@ -21,19 +24,27 @@ private fun getKsuDaemonPath(): String {
|
||||
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud.so"
|
||||
}
|
||||
|
||||
object KsuCli {
|
||||
val SHELL: Shell = createRootShell()
|
||||
}
|
||||
|
||||
fun getRootShell(): Shell {
|
||||
return KsuCli.SHELL
|
||||
}
|
||||
|
||||
fun createRootShell(): Shell {
|
||||
Shell.enableVerboseLogging = BuildConfig.DEBUG
|
||||
val su = ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksu.so"
|
||||
val builder = Shell.Builder.create()
|
||||
return try {
|
||||
builder.build(su)
|
||||
builder.build(getKsuDaemonPath(), "debug", "su")
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "su failed: ", e)
|
||||
builder.build("sh")
|
||||
}
|
||||
}
|
||||
|
||||
fun execKsud(args: String): Boolean {
|
||||
val shell = createRootShell()
|
||||
val shell = getRootShell()
|
||||
return ShellUtils.fastCmdResult(shell, "${getKsuDaemonPath()} $args")
|
||||
}
|
||||
|
||||
@@ -44,10 +55,23 @@ fun install() {
|
||||
}
|
||||
|
||||
fun listModules(): String {
|
||||
val shell = createRootShell()
|
||||
val shell = getRootShell()
|
||||
|
||||
val out = shell.newJob().add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out
|
||||
return out.joinToString("\n")
|
||||
val out =
|
||||
shell.newJob().add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out
|
||||
return out.joinToString("\n").ifBlank { "[]" }
|
||||
}
|
||||
|
||||
fun getModuleCount(): Int {
|
||||
val result = listModules()
|
||||
runCatching {
|
||||
val array = JSONArray(result)
|
||||
return array.length()
|
||||
}.getOrElse { return 0 }
|
||||
}
|
||||
|
||||
fun getSuperuserCount(): Int {
|
||||
return Natives.getAllowList().size
|
||||
}
|
||||
|
||||
fun toggleModule(id: String, enable: Boolean): Boolean {
|
||||
@@ -61,14 +85,14 @@ fun toggleModule(id: String, enable: Boolean): Boolean {
|
||||
return result
|
||||
}
|
||||
|
||||
fun uninstallModule(id: String) : Boolean {
|
||||
fun uninstallModule(id: String): Boolean {
|
||||
val cmd = "module uninstall $id"
|
||||
val result = execKsud(cmd)
|
||||
Log.i(TAG, "uninstall module $id result: $result")
|
||||
return result
|
||||
}
|
||||
|
||||
fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Unit) : Boolean {
|
||||
fun installModule(uri: Uri, onFinish: (Boolean) -> Unit, onOutput: (String) -> Unit): Boolean {
|
||||
val resolver = ksuApp.contentResolver
|
||||
with(resolver.openInputStream(uri)) {
|
||||
val file = File(ksuApp.cacheDir, "module.zip")
|
||||
@@ -77,7 +101,7 @@ fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Uni
|
||||
}
|
||||
val cmd = "module install ${file.absolutePath}"
|
||||
|
||||
val shell = createRootShell()
|
||||
val shell = getRootShell()
|
||||
|
||||
val callbackList: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
@@ -85,7 +109,8 @@ fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Uni
|
||||
}
|
||||
}
|
||||
|
||||
val result = shell.newJob().add("${getKsuDaemonPath()} $cmd").to(callbackList, callbackList).exec()
|
||||
val result =
|
||||
shell.newJob().add("${getKsuDaemonPath()} $cmd").to(callbackList, callbackList).exec()
|
||||
Log.i("KernelSU", "install module $uri result: $result")
|
||||
|
||||
file.delete()
|
||||
@@ -96,10 +121,23 @@ fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Uni
|
||||
}
|
||||
|
||||
fun reboot(reason: String = "") {
|
||||
val shell = createRootShell()
|
||||
val shell = getRootShell()
|
||||
if (reason == "recovery") {
|
||||
// KEYCODE_POWER = 26, hide incorrect "Factory data reset" message
|
||||
ShellUtils.fastCmd(shell, "/system/bin/input keyevent 26")
|
||||
}
|
||||
ShellUtils.fastCmd(shell, "/system/bin/svc power reboot $reason || /system/bin/reboot $reason")
|
||||
}
|
||||
|
||||
fun overlayFsAvailable(): Boolean {
|
||||
val shell = getRootShell()
|
||||
// check /proc/filesystems
|
||||
return ShellUtils.fastCmdResult(shell, "cat /proc/filesystems | grep overlay")
|
||||
}
|
||||
|
||||
fun hasMagisk(): Boolean {
|
||||
val shell = getRootShell()
|
||||
val result = shell.newJob().add("nsenter --mount=/proc/1/ns/mnt which magisk").exec()
|
||||
Log.i(TAG, "has magisk: ${result.isSuccess}")
|
||||
return result.isSuccess
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package me.weishu.kernelsu.ui.util
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.system.Os
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
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 pstoreFile = File(bugreportDir, "pstore.tar.gz")
|
||||
val diagFile = File(bugreportDir, "diag.tar.gz")
|
||||
val bootlogFile = File(bugreportDir, "bootlog.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, "packages.txt")
|
||||
val propFile = File(bugreportDir, "props.txt")
|
||||
val allowListFile = File(bugreportDir, "allowlist.bin")
|
||||
|
||||
val shell = getRootShell()
|
||||
|
||||
shell.newJob().add("dmesg > ${dmesgFile.absolutePath}").exec()
|
||||
shell.newJob().add("logcat -d > ${logcatFile.absolutePath}").exec()
|
||||
shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} -C /data/tombstones .").exec()
|
||||
shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec()
|
||||
shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec()
|
||||
shell.newJob().add("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag .").exec()
|
||||
shell.newJob().add("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec()
|
||||
|
||||
shell.newJob().add("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec()
|
||||
shell.newJob().add("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec()
|
||||
shell.newJob().add("ls -alRZ /data/adb > ${ksuFileTree.absolutePath}").exec()
|
||||
shell.newJob().add("cp /data/system/packages.list ${appListFile.absolutePath}").exec()
|
||||
shell.newJob().add("getprop > ${propFile.absolutePath}").exec()
|
||||
shell.newJob().add("cp /data/adb/ksu/.allowlist ${allowListFile.absolutePath}").exec()
|
||||
|
||||
val selinux = ShellUtils.fastCmd(shell, "getenforce");
|
||||
|
||||
// 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))
|
||||
pw.println("SELinux: $selinux")
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package me.weishu.kernelsu.ui.util
|
||||
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import me.weishu.kernelsu.R
|
||||
|
||||
@Composable
|
||||
fun getSELinuxStatus(): String {
|
||||
val shell = Shell.Builder.create()
|
||||
.setFlags(Shell.FLAG_REDIRECT_STDERR)
|
||||
.build("sh")
|
||||
|
||||
val list = ArrayList<String>()
|
||||
val result = shell.newJob().add("getenforce").to(list, list).exec()
|
||||
val output = result.out.joinToString("\n").trim()
|
||||
|
||||
if (result.isSuccess) {
|
||||
return when (output) {
|
||||
"Enforcing" -> stringResource(R.string.selinux_status_enforcing)
|
||||
"Permissive" -> stringResource(R.string.selinux_status_permissive)
|
||||
"Disabled" -> stringResource(R.string.selinux_status_disabled)
|
||||
else -> stringResource(R.string.selinux_status_unknown)
|
||||
}
|
||||
}
|
||||
|
||||
return if (output.endsWith("Permission denied")) {
|
||||
stringResource(R.string.selinux_status_enforcing)
|
||||
} else {
|
||||
stringResource(R.string.selinux_status_unknown)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -8,9 +7,11 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.ui.util.listModules
|
||||
import me.weishu.kernelsu.ui.util.overlayFsAvailable
|
||||
import org.json.JSONArray
|
||||
import java.text.Collator
|
||||
import java.util.*
|
||||
@@ -37,6 +38,9 @@ class ModuleViewModel : ViewModel() {
|
||||
var isRefreshing by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
var isOverlayAvailable by mutableStateOf(overlayFsAvailable())
|
||||
private set
|
||||
|
||||
val moduleList by derivedStateOf {
|
||||
val comparator = compareBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id)
|
||||
modules.sortedWith(comparator).also {
|
||||
@@ -44,12 +48,16 @@ class ModuleViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun fetchModuleList() {
|
||||
withContext(Dispatchers.IO) {
|
||||
fun fetchModuleList() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
isRefreshing = true
|
||||
|
||||
val oldModuleList = modules
|
||||
|
||||
val start = SystemClock.elapsedRealtime()
|
||||
|
||||
kotlin.runCatching {
|
||||
isOverlayAvailable = overlayFsAvailable()
|
||||
|
||||
val result = listModules()
|
||||
|
||||
@@ -63,9 +71,9 @@ class ModuleViewModel : ViewModel() {
|
||||
ModuleInfo(
|
||||
obj.getString("id"),
|
||||
obj.getString("name"),
|
||||
obj.getString("author"),
|
||||
obj.getString("version"),
|
||||
obj.getInt("versionCode"),
|
||||
obj.optString("author", "Unknown"),
|
||||
obj.optString("version", "Unknown"),
|
||||
obj.optInt("versionCode", 0),
|
||||
obj.getString("description"),
|
||||
obj.getBoolean("enabled"),
|
||||
obj.getBoolean("update"),
|
||||
@@ -74,8 +82,14 @@ class ModuleViewModel : ViewModel() {
|
||||
}.toList()
|
||||
}.onFailure { e ->
|
||||
Log.e(TAG, "fetchModuleList: ", e)
|
||||
isRefreshing = false
|
||||
}
|
||||
|
||||
// when both old and new is kotlin.collections.EmptyList
|
||||
// moduleList update will don't trigger
|
||||
if (oldModuleList === modules) {
|
||||
isRefreshing = false
|
||||
}
|
||||
|
||||
Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}, modules: $modules")
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package me.weishu.kernelsu.ui.viewmodel
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.IBinder
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
@@ -10,13 +13,19 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.IKsuInterface
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import me.weishu.kernelsu.ui.KsuService
|
||||
import me.weishu.kernelsu.ui.util.HanziToPinyin
|
||||
import me.weishu.kernelsu.ui.util.KsuCli
|
||||
import java.text.Collator
|
||||
import java.util.*
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class SuperUserViewModel : ViewModel() {
|
||||
|
||||
@@ -35,6 +44,7 @@ class SuperUserViewModel : ViewModel() {
|
||||
)
|
||||
|
||||
var search by mutableStateOf("")
|
||||
var showSystemApps by mutableStateOf(false)
|
||||
var isRefreshing by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
@@ -53,20 +63,69 @@ class SuperUserViewModel : ViewModel() {
|
||||
|
||||
val appList by derivedStateOf {
|
||||
sortedList.filter {
|
||||
it.label.contains(search) || it.packageName.contains(search) || HanziToPinyin.getInstance().toPinyinString(it.label).contains(search)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
private suspend inline fun connectKsuService(
|
||||
crossinline onDisconnect: () -> Unit = {}
|
||||
): Pair<IBinder, ServiceConnection> = suspendCoroutine {
|
||||
val connection = object : ServiceConnection {
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
onDisconnect()
|
||||
}
|
||||
|
||||
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
|
||||
it.resume(binder as IBinder to this)
|
||||
}
|
||||
}
|
||||
|
||||
val intent = Intent(ksuApp, KsuService::class.java);
|
||||
|
||||
val task = KsuService.bindOrTask(
|
||||
intent,
|
||||
Shell.EXECUTOR,
|
||||
connection,
|
||||
)
|
||||
val shell = KsuCli.SHELL
|
||||
task?.let { it1 -> shell.execTask(it1) }
|
||||
}
|
||||
|
||||
private fun stopKsuService() {
|
||||
val intent = Intent(ksuApp, KsuService::class.java);
|
||||
KsuService.stop(intent)
|
||||
}
|
||||
|
||||
suspend fun fetchAppList() {
|
||||
|
||||
isRefreshing = true
|
||||
|
||||
val result = connectKsuService {
|
||||
Log.w(TAG, "KsuService disconnected")
|
||||
}
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
isRefreshing = true
|
||||
val pm = ksuApp.packageManager
|
||||
val allowList = Natives.getAllowList().toSet()
|
||||
val denyList = Natives.getDenyList().toSet()
|
||||
Log.i(TAG, "allowList: $allowList")
|
||||
Log.i(TAG, "denyList: $denyList")
|
||||
val start = SystemClock.elapsedRealtime()
|
||||
apps = pm.getInstalledPackages(0).map {
|
||||
|
||||
val binder = result.first
|
||||
val allPackages = IKsuInterface.Stub.asInterface(binder).getPackages(0)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
stopKsuService()
|
||||
}
|
||||
|
||||
val packages = allPackages.list
|
||||
|
||||
apps = packages.map {
|
||||
val appInfo = it.applicationInfo
|
||||
val uid = appInfo.uid
|
||||
AppInfo(
|
||||
@@ -77,7 +136,7 @@ class SuperUserViewModel : ViewModel() {
|
||||
onAllowList = uid in allowList,
|
||||
onDenyList = uid in denyList
|
||||
)
|
||||
}
|
||||
}.filter { it.packageName != ksuApp.packageName }
|
||||
Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}")
|
||||
}
|
||||
}
|
||||
|
||||
15
manager/app/src/main/res/drawable/ic_launcher_background.xml
Normal file
15
manager/app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<group
|
||||
android:scaleX="0.135"
|
||||
android:scaleY="0.135">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 0 0 H 800 V 800 H 0 V 0 Z" />
|
||||
</group>
|
||||
</vector>
|
||||
22
manager/app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
22
manager/app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<group
|
||||
android:scaleX="0.135"
|
||||
android:scaleY="0.135">
|
||||
<path
|
||||
android:pathData="M 259 259 H 541 V 541 H 259 V 259 Z"
|
||||
android:strokeWidth="18"
|
||||
android:strokeColor="#1e110d" />
|
||||
<path
|
||||
android:fillColor="#1e110d"
|
||||
android:pathData="M 257 257 H 407 V 407 H 257 V 257 Z" />
|
||||
<path
|
||||
android:fillColor="#1e110d"
|
||||
android:pathData="M 393 393 H 543 V 543 H 393 V 393 Z" />
|
||||
</group>
|
||||
</vector>
|
||||
22
manager/app/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
22
manager/app/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<group
|
||||
android:scaleX="0.135"
|
||||
android:scaleY="0.135">
|
||||
<path
|
||||
android:pathData="M 259 259 H 541 V 541 H 259 V 259 Z"
|
||||
android:strokeWidth="18"
|
||||
android:strokeColor="#000000" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 257 257 H 407 V 407 H 257 V 257 Z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 393 393 H 543 V 543 H 393 V 393 Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
63
manager/app/src/main/res/values-in/strings.xml
Normal file
63
manager/app/src/main/res/values-in/strings.xml
Normal file
@@ -0,0 +1,63 @@
|
||||
<?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_superuser_count">Superuser: %d</string>
|
||||
<string name="home_module_count">Module: %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="superuser_allow_root_confirm">Ijinkan akses root untuk aplikasi %s?</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 module</string>
|
||||
<string name="install">Memasang module...</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="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>
|
||||
<string name="module_magisk_conflict">Module akan di nonaktifkan karna konflik dengan magisk\'s!</string>
|
||||
<string name="home_learn_kernelsu">Belajar KernelSU</string>
|
||||
<string name="home_click_to_learn_kernelsu">Cara menginstall KernelSu dan menggunakan Module</string>
|
||||
<string name="home_support_title">Dukung kami</string>
|
||||
<string name="home_support_content">KernelSU akan selalu gratis dan open source. Namun Anda dapat menunjukkan kepada kami bahwa Anda peduli dengan memberikan sedikit donasi.</string>
|
||||
<string name="about_source_code"><![CDATA[Lihat sumber kode di %1$s<br/>Gabung sekarang %2$s channel]]></string>
|
||||
</resources>
|
||||
54
manager/app/src/main/res/values-ja/strings.xml
Normal file
54
manager/app/src/main/res/values-ja/strings.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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">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="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>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user