Compare commits
732 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e65012c99 | ||
|
|
91ede12d23 | ||
|
|
cd12337f2a | ||
|
|
2baaecc7f0 | ||
|
|
ad5042b66e | ||
|
|
2e067fa5d1 | ||
|
|
2c1beac1ca | ||
|
|
84be28708e | ||
|
|
6242c6ca8b | ||
|
|
ca5e93f221 | ||
|
|
e382a83895 | ||
|
|
2bc727bfbd | ||
|
|
8bdde66eaf | ||
|
|
0582a7554c | ||
|
|
48016fac7b | ||
|
|
823a3f9767 | ||
|
|
56a028d60d | ||
|
|
d0e8faea77 | ||
|
|
a772a0f82d | ||
|
|
de6dc65a56 | ||
|
|
c81e0c6c0f | ||
|
|
f87e705e2f | ||
|
|
078ffdb5e1 | ||
|
|
5a6ab43ea4 | ||
|
|
de6b2794b7 | ||
|
|
c574f39ae3 | ||
|
|
954ecd9644 | ||
|
|
8250c0ecc2 | ||
|
|
cc78812f50 | ||
|
|
088996da9b | ||
|
|
52a3a04b11 | ||
|
|
f290653088 | ||
|
|
33c498078f | ||
|
|
fb0eea2994 | ||
|
|
2ea748dac1 | ||
|
|
429874b4d6 | ||
|
|
cd86589ad3 | ||
|
|
22cb7596a7 | ||
|
|
029ae8d389 | ||
|
|
58c8289890 | ||
|
|
94fa1e360a | ||
|
|
1d1ce396d3 | ||
|
|
99d58c8cfd | ||
|
|
ad2a23f55e | ||
|
|
f48d2e6cac | ||
|
|
d45676f059 | ||
|
|
0439a00f1d | ||
|
|
cbcaa07fd5 | ||
|
|
c4d8c49e5c | ||
|
|
c8020b2066 | ||
|
|
8442ebcb7a | ||
|
|
25fbc22f66 | ||
|
|
a4a9df3a25 | ||
|
|
19a67fb76c | ||
|
|
3a2d55237d | ||
|
|
98e617f1bd | ||
|
|
d84a88f059 | ||
|
|
ae7b4dcbed | ||
|
|
684a5d1ccd | ||
|
|
35b02e3c73 | ||
|
|
db6a59ec4e | ||
|
|
46846dce91 | ||
|
|
cd21af6728 | ||
|
|
92a483d222 | ||
|
|
04ca981e4d | ||
|
|
83209a5259 | ||
|
|
3cfc6d6a31 | ||
|
|
01ac06c3fd | ||
|
|
906c4bdb01 | ||
|
|
c17d7b38eb | ||
|
|
fa57ccccf4 | ||
|
|
7e7713ee4a | ||
|
|
7afcdb3059 | ||
|
|
5df821ed41 | ||
|
|
12fc2e6d5e | ||
|
|
9f869090d2 | ||
|
|
bb8b991110 | ||
|
|
10548f9243 | ||
|
|
96d33e62bb | ||
|
|
7be8c15b85 | ||
|
|
14bb6afd0b | ||
|
|
54dad4ceb2 | ||
|
|
a60395ba35 | ||
|
|
095385f814 | ||
|
|
7e595e1730 | ||
|
|
ded31c2043 | ||
|
|
a7f840d811 | ||
|
|
05cca26075 | ||
|
|
5ce6c210c4 | ||
|
|
8f49898155 | ||
|
|
fd3a22360a | ||
|
|
ef36a36e9a | ||
|
|
7a1a08064b | ||
|
|
36862d6175 | ||
|
|
cda7e4c6c0 | ||
|
|
0b63cc445c | ||
|
|
2433ced81a | ||
|
|
1f04f13e44 | ||
|
|
184467c691 | ||
|
|
05ed1a3714 | ||
|
|
163531fcd2 | ||
|
|
049956aaa9 | ||
|
|
6530d06710 | ||
|
|
88135d8363 | ||
|
|
548258f922 | ||
|
|
46b9f5fb4b | ||
|
|
413e9ab8a9 | ||
|
|
c3644da85b | ||
|
|
7b4b5b431f | ||
|
|
7479c0b81b | ||
|
|
0381d12be2 | ||
|
|
4425c88d5a | ||
|
|
7828c5c107 | ||
|
|
76046c84cd | ||
|
|
623dd15cbf | ||
|
|
ab13ed5c16 | ||
|
|
e171ca15cb | ||
|
|
4fc369a059 | ||
|
|
18ad2afadb | ||
|
|
3badbcd4bc | ||
|
|
1b5b235bd9 | ||
|
|
fdf5e7104e | ||
|
|
ed6b2e0a8e | ||
|
|
704f7cba32 | ||
|
|
860bdce295 | ||
|
|
d8a8ef6458 | ||
|
|
d37a78ea2d | ||
|
|
6c9bf69718 | ||
|
|
776bcc4d5d | ||
|
|
bf5cb885b5 | ||
|
|
a533a490bd | ||
|
|
c6d9f76c7b | ||
|
|
66032391af | ||
|
|
da0e16bd26 | ||
|
|
53d763cdf9 | ||
|
|
9ebddde0d5 | ||
|
|
03a164ebb7 | ||
|
|
4769065cfc | ||
|
|
9b209765c4 | ||
|
|
d7c101e244 | ||
|
|
a32f89403b | ||
|
|
2cd673d776 | ||
|
|
14fea6f8a3 | ||
|
|
02f1aec6e9 | ||
|
|
826661dffb | ||
|
|
f86c71efc5 | ||
|
|
06018a2f03 | ||
|
|
a2193841d5 | ||
|
|
1324a7f54e | ||
|
|
5df9431a22 | ||
|
|
e54989e51a | ||
|
|
cf50be122e | ||
|
|
7f9048724f | ||
|
|
3dde6d9a25 | ||
|
|
132e9ef8ed | ||
|
|
e6436b340c | ||
|
|
9cdf98782d | ||
|
|
dece57cacf | ||
|
|
3f07ea29ae | ||
|
|
c8e103062a | ||
|
|
91312effba | ||
|
|
fd60cda3b3 | ||
|
|
5323a500dd | ||
|
|
0ce7bc2627 | ||
|
|
c9c62b25d2 | ||
|
|
f8904b1b02 | ||
|
|
89ce65e8ba | ||
|
|
994fdfddf2 | ||
|
|
557e7f8153 | ||
|
|
9e9bb685f0 | ||
|
|
99bec0e439 | ||
|
|
0400b94674 | ||
|
|
247f7d4aee | ||
|
|
088ce97697 | ||
|
|
c0a86544d8 | ||
|
|
47bd84f3d1 | ||
|
|
06e714b4e7 | ||
|
|
1439e486a1 | ||
|
|
801bcb0e1f | ||
|
|
54b5fb5fdb | ||
|
|
1cc9fce2c6 | ||
|
|
46fefc299c | ||
|
|
23cc0ceff1 | ||
|
|
4a610af452 | ||
|
|
8177afa81e | ||
|
|
3588282b43 | ||
|
|
257f0ca6de | ||
|
|
c863ff6f49 | ||
|
|
e99a14290f | ||
|
|
18e60ededa | ||
|
|
22e4b69231 | ||
|
|
1853d9decf | ||
|
|
d286f49e11 | ||
|
|
7103779a11 | ||
|
|
4350d309da | ||
|
|
7051b22536 | ||
|
|
e0bce04e79 | ||
|
|
c75b041c40 | ||
|
|
d2a3f0fcad | ||
|
|
696c3059b6 | ||
|
|
ab8e966b7f | ||
|
|
7ece40bb2c | ||
|
|
d1aa6c8beb | ||
|
|
bfed2d700a | ||
|
|
59339b806a | ||
|
|
2433d64b6b | ||
|
|
a622657092 | ||
|
|
b4e682148a | ||
|
|
02474a5953 | ||
|
|
450dbf14fc | ||
|
|
d89eab2c34 | ||
|
|
a6b86a4f99 | ||
|
|
33d1f18395 | ||
|
|
8ebe60ca04 | ||
|
|
980613c6a9 | ||
|
|
47bcc956a3 | ||
|
|
00de4e1c64 | ||
|
|
97ec718fea | ||
|
|
5c96f951b5 | ||
|
|
7e446efac4 | ||
|
|
c06d694ebc | ||
|
|
bd0b07cba9 | ||
|
|
e54339cf4e | ||
|
|
320e08b8fb | ||
|
|
0da8ecb071 | ||
|
|
cc54abd273 | ||
|
|
ee781cf959 | ||
|
|
a21b18c5de | ||
|
|
1644f22c98 | ||
|
|
7a338b1b43 | ||
|
|
324dc0844f | ||
|
|
f7fe0cf748 | ||
|
|
bf1a45963b | ||
|
|
64ee09fd12 | ||
|
|
b9a84a15bc | ||
|
|
6a1e1d788b | ||
|
|
4b86989bf9 | ||
|
|
d3f8c128da | ||
|
|
bbb2748494 | ||
|
|
b9e6246d65 | ||
|
|
7bf13fbfca | ||
|
|
e9ee2304d3 | ||
|
|
f7b4b4b82d | ||
|
|
15b9c4dbbf | ||
|
|
3b966c536b | ||
|
|
b5e5be2572 | ||
|
|
253df1ea47 | ||
|
|
134684a139 | ||
|
|
9c07fa6889 | ||
|
|
c3f66e15e9 | ||
|
|
b6e2fa383a | ||
|
|
61f85a029e | ||
|
|
b1564b77a2 | ||
|
|
cc0a3590ce | ||
|
|
e793219c2b | ||
|
|
776a753206 | ||
|
|
7b6470cc79 | ||
|
|
eb5fdbbf3f | ||
|
|
8db55f56a9 | ||
|
|
62635879e0 | ||
|
|
86634aac3d | ||
|
|
af25f8d49e | ||
|
|
cd78c2693a | ||
|
|
8ff9fab414 | ||
|
|
b8eebcda5a | ||
|
|
85291de02a | ||
|
|
cb7abc88dd | ||
|
|
918e7ae0b7 | ||
|
|
0a804ba170 | ||
|
|
4c512dc7ff | ||
|
|
fcb7c3e99d | ||
|
|
b827360ac6 | ||
|
|
ca7b53370e | ||
|
|
230ca54d63 | ||
|
|
2f43ad4f76 | ||
|
|
9c1ff635e3 | ||
|
|
ef97f0e4d9 | ||
|
|
2e394903cc | ||
|
|
7978cbafa5 | ||
|
|
c89a3dbcd9 | ||
|
|
13c7912320 | ||
|
|
abbe385382 | ||
|
|
0b80137f17 | ||
|
|
c4ff89c13d | ||
|
|
ce3a7ec189 | ||
|
|
2bb789212a | ||
|
|
7ef9230d66 | ||
|
|
fbaa69f3fb | ||
|
|
e78ee720b5 | ||
|
|
04b603394a | ||
|
|
c9c7a5f4e3 | ||
|
|
044b4a2f9c | ||
|
|
59cd8d1c3b | ||
|
|
36617bf0a1 | ||
|
|
5b49054055 | ||
|
|
a17a220745 | ||
|
|
0729066a6f | ||
|
|
d4dcf610c9 | ||
|
|
78e0dc6da2 | ||
|
|
a9a10466b3 | ||
|
|
65d5d6a494 | ||
|
|
e552163d9e | ||
|
|
c950705044 | ||
|
|
9e7aabf3f7 | ||
|
|
a20a89da03 | ||
|
|
9551ca4fe8 | ||
|
|
a2431d50ce | ||
|
|
8b74f7d466 | ||
|
|
b5d9607e8e | ||
|
|
475b3998a1 | ||
|
|
23ed4384e6 | ||
|
|
0af1ab2845 | ||
|
|
c664aa66af | ||
|
|
7a449345d1 | ||
|
|
fa48b2fdaa | ||
|
|
f1f7c61aee | ||
|
|
ccc5cb74bf | ||
|
|
75fe8c9b6a | ||
|
|
a12c78d213 | ||
|
|
6c145179d4 | ||
|
|
ac05038e64 | ||
|
|
d5d2bb35d2 | ||
|
|
cb01bf428d | ||
|
|
7146210192 | ||
|
|
528df754a5 | ||
|
|
8a14f64964 | ||
|
|
bf4e12ce80 | ||
|
|
5497c0004d | ||
|
|
32baf8d9b0 | ||
|
|
763ce7e86c | ||
|
|
3a8ecf1253 | ||
|
|
7026c340b0 | ||
|
|
6dc803702b | ||
|
|
c64d196a10 | ||
|
|
c690d9c02c | ||
|
|
452fe3d508 | ||
|
|
e27d461eb0 | ||
|
|
a77faa1fe2 | ||
|
|
9a9623f578 | ||
|
|
cc1c66bb6f | ||
|
|
695e749e3e | ||
|
|
84c85ebe3c | ||
|
|
ed7dc01fb2 | ||
|
|
7d475e642f | ||
|
|
cead5b03f4 | ||
|
|
0c578e4518 | ||
|
|
fad6420405 | ||
|
|
415555aef8 | ||
|
|
f3e9033054 | ||
|
|
192323ad5c | ||
|
|
6973ce6a1f | ||
|
|
e8852223c4 | ||
|
|
42601b232c | ||
|
|
cd8b6ab382 | ||
|
|
3ed1d9aebc | ||
|
|
d83fc2aec6 | ||
|
|
098583f058 | ||
|
|
2521226745 | ||
|
|
942e5070f5 | ||
|
|
6726828e1d | ||
|
|
4418ebadc4 | ||
|
|
37368fb41c | ||
|
|
1e5db5d6bf | ||
|
|
067ced4a8c | ||
|
|
41d21e994c | ||
|
|
596dab1db8 | ||
|
|
71c0424512 | ||
|
|
49190feab6 | ||
|
|
9b37ec4a84 | ||
|
|
12f54b115c | ||
|
|
5586f9ce30 | ||
|
|
0789632efc | ||
|
|
c3533861f2 | ||
|
|
662a2b1237 | ||
|
|
436071a3f1 | ||
|
|
ea602be270 | ||
|
|
8c8f344161 | ||
|
|
1af8a87876 | ||
|
|
6640b5b7b6 | ||
|
|
b8b75349e0 | ||
|
|
3f3e520641 | ||
|
|
21aa58f1ee | ||
|
|
da164ebeed | ||
|
|
35ae324df5 | ||
|
|
fdda1f4ec0 | ||
|
|
770ed1fdf2 | ||
|
|
106c10d6f8 | ||
|
|
f10de68deb | ||
|
|
44db32e8de | ||
|
|
3ced30b427 | ||
|
|
624a8d9f86 | ||
|
|
16007f5892 | ||
|
|
6bb83fdb07 | ||
|
|
ec6991f98b | ||
|
|
f6337e2d52 | ||
|
|
6b2bf23946 | ||
|
|
dbc662486b | ||
|
|
e96194c7ff | ||
|
|
49b01aad74 | ||
|
|
656a23a250 | ||
|
|
11a628f536 | ||
|
|
57fcf86579 | ||
|
|
ecb2dae743 | ||
|
|
75e0cd05a9 | ||
|
|
1eb0f19ca6 | ||
|
|
a7ee0423a3 | ||
|
|
355e1c648a | ||
|
|
3cde3e1659 | ||
|
|
8dcc3f7c46 | ||
|
|
a041b90891 | ||
|
|
c1c648e34d | ||
|
|
0754fc8920 | ||
|
|
4d3bae1113 | ||
|
|
0fdd7d437f | ||
|
|
6e89c81407 | ||
|
|
f8b8c7f671 | ||
|
|
cd4edf97bd | ||
|
|
e3f1e49fe1 | ||
|
|
abe0dee4da | ||
|
|
bc3fcec514 | ||
|
|
d225f0bae9 | ||
|
|
48d7a13028 | ||
|
|
7e7d2a28af | ||
|
|
acfba3d0f8 | ||
|
|
bf5a8a8909 | ||
|
|
7c4d8da7d1 | ||
|
|
c656d87e42 | ||
|
|
1b7c7fd726 | ||
|
|
ea68183f80 | ||
|
|
b1ee07fee1 | ||
|
|
c42b4ffe4b | ||
|
|
cfd070f33c | ||
|
|
3ae1a3b10a | ||
|
|
d21f92d817 | ||
|
|
6bb66e2819 | ||
|
|
115206bcc6 | ||
|
|
5ba4f73eeb | ||
|
|
139899d05d | ||
|
|
d2ab325e18 | ||
|
|
be393ddb7c | ||
|
|
467d6e0838 | ||
|
|
46fcf97be3 | ||
|
|
72ed1bc4a2 | ||
|
|
b8544b4f53 | ||
|
|
60f0a721ce | ||
|
|
c62676d643 | ||
|
|
dd2b2e995a | ||
|
|
86456a4d95 | ||
|
|
11e9e37f43 | ||
|
|
a8a2aef4b5 | ||
|
|
77ac0d70fd | ||
|
|
2a00ef96c2 | ||
|
|
39ee1cc41d | ||
|
|
51556d1253 | ||
|
|
94df64f234 | ||
|
|
02f545b3fb | ||
|
|
62c7aac75b | ||
|
|
755d454960 | ||
|
|
9717fa0de6 | ||
|
|
bd6eb7fddd | ||
|
|
9f706873f2 | ||
|
|
c424d5bab4 | ||
|
|
e795387c30 | ||
|
|
044dd9471f | ||
|
|
213a15cdb6 | ||
|
|
a255ea9d56 | ||
|
|
6cbe13dafc | ||
|
|
fb5c7c2f9d | ||
|
|
b8f2d15bd1 | ||
|
|
2bd6929d24 | ||
|
|
42b883240e | ||
|
|
4840540038 | ||
|
|
7bfb37a11e | ||
|
|
dd6d695020 | ||
|
|
6a60b72e21 | ||
|
|
ea3a0cf73b | ||
|
|
1816d15ce8 | ||
|
|
79c8f7a709 | ||
|
|
b0cc0e6f6d | ||
|
|
eccc70c0c9 | ||
|
|
fc828ff3aa | ||
|
|
acb7cfff1b | ||
|
|
3729c22dd0 | ||
|
|
a84cf70730 | ||
|
|
2278fe49d2 | ||
|
|
be14da387e | ||
|
|
1fd13d9d8d | ||
|
|
4205db6870 | ||
|
|
70f03081a4 | ||
|
|
5ccb779b6a | ||
|
|
b07bc408ce | ||
|
|
7ee1fd63f1 | ||
|
|
3551441e42 | ||
|
|
4a1ab76322 | ||
|
|
2fedb051b8 | ||
|
|
10c35f4baa | ||
|
|
4f82eda003 | ||
|
|
80f89c0241 | ||
|
|
8399f14fad | ||
|
|
c49a66d1af | ||
|
|
d66b390361 | ||
|
|
9c290a8080 | ||
|
|
48efc28e8f | ||
|
|
634adad15c | ||
|
|
4532bab230 | ||
|
|
d3c9b6e739 | ||
|
|
8e4f980db0 | ||
|
|
cfee357ed1 | ||
|
|
9393459b27 | ||
|
|
60af173a7e | ||
|
|
23e2377f87 | ||
|
|
d45ba31849 | ||
|
|
c5705c2d5d | ||
|
|
dfae83cf58 | ||
|
|
cd5ba3ac3c | ||
|
|
2c2698f6bc | ||
|
|
f57fe79c5d | ||
|
|
91ae4c9650 | ||
|
|
01f44dc1d9 | ||
|
|
6e35b88041 | ||
|
|
c9c122d79b | ||
|
|
4bec5ae7b1 | ||
|
|
f9b3478dbb | ||
|
|
561c82de0a | ||
|
|
e96ceb84c9 | ||
|
|
ddbbeafc64 | ||
|
|
285478a778 | ||
|
|
00ffa86705 | ||
|
|
74ec20745c | ||
|
|
b7b995bf73 | ||
|
|
29b7f9e0ad | ||
|
|
00a4c69227 | ||
|
|
9c204496c3 | ||
|
|
519401cf39 | ||
|
|
f69eb5c115 | ||
|
|
82e96f4394 | ||
|
|
8e3db00b9b | ||
|
|
adf299d9f3 | ||
|
|
483a39c7ac | ||
|
|
c83baad6d5 | ||
|
|
2ff3b5ee06 | ||
|
|
b537b51034 | ||
|
|
bfb6ea3613 | ||
|
|
edf7685e9a | ||
|
|
f65f62360a | ||
|
|
af97488d58 | ||
|
|
6b1f73aa3d | ||
|
|
4eeece9559 | ||
|
|
4d7d5547ac | ||
|
|
7b74e70f97 | ||
|
|
d92f8fc8fd | ||
|
|
55f9de2fa9 | ||
|
|
a12b14ef46 | ||
|
|
4ce6ff6286 | ||
|
|
ce3566640c | ||
|
|
a0a9fb01f4 | ||
|
|
e1bd16d94f | ||
|
|
776ae8744c | ||
|
|
9285945e8b | ||
|
|
75e56038ec | ||
|
|
730d58f18b | ||
|
|
67a05e8813 | ||
|
|
e95a469bdb | ||
|
|
2ff122e235 | ||
|
|
2319452306 | ||
|
|
a0752d10c7 | ||
|
|
9110d89d61 | ||
|
|
39d6962320 | ||
|
|
7b314116e9 | ||
|
|
ef4101cbf9 | ||
|
|
85f5459c1d | ||
|
|
97e367aa92 | ||
|
|
7097986cf5 | ||
|
|
d6c8ef3737 | ||
|
|
d7a5e80d34 | ||
|
|
2d9783e3d4 | ||
|
|
9f407a94e3 | ||
|
|
99726a2c4e | ||
|
|
f3675e7f6e | ||
|
|
b84d528d99 | ||
|
|
0aab0c1d6b | ||
|
|
ab2367f7fa | ||
|
|
1bac30930f | ||
|
|
6a9186300b | ||
|
|
e6dea3c29e | ||
|
|
c873ff74cb | ||
|
|
7b6f451cfb | ||
|
|
73dea0b8e7 | ||
|
|
f71d617cb3 | ||
|
|
f0d8e42026 | ||
|
|
5bbd95e821 | ||
|
|
fa060dca58 | ||
|
|
9c7ba5b998 | ||
|
|
061136900a | ||
|
|
6375bf4b7c | ||
|
|
17288c086a | ||
|
|
15747ceaa5 | ||
|
|
675bb20f52 | ||
|
|
ec0b26a174 | ||
|
|
92f6f2f51e | ||
|
|
587e73b449 | ||
|
|
07c9cce4b9 | ||
|
|
1d34ea4995 | ||
|
|
d58ec6952c | ||
|
|
50631aade6 | ||
|
|
6df8f6f5d4 | ||
|
|
4aee26b48e | ||
|
|
3bbe415c7e | ||
|
|
892fa9040f | ||
|
|
cadc123eab | ||
|
|
3a27537648 | ||
|
|
6fa1a5c8b8 | ||
|
|
b772c8ece1 | ||
|
|
c0e839dd8e | ||
|
|
a6ed7befdc | ||
|
|
c210b00d54 | ||
|
|
13b5290598 | ||
|
|
b99516da69 | ||
|
|
fe8b5f2135 | ||
|
|
04e1b9bf77 | ||
|
|
b8aaf918fe | ||
|
|
54925188e8 | ||
|
|
3443e48ef1 | ||
|
|
53b3e84890 | ||
|
|
a5b85bfdad | ||
|
|
2817583e3c | ||
|
|
8a6116b4ec | ||
|
|
6a4270787a | ||
|
|
5457a4772b | ||
|
|
ee4c3bb03b | ||
|
|
dd1d17d2cf | ||
|
|
3c353e8f88 | ||
|
|
d743073309 | ||
|
|
a636911612 | ||
|
|
7a62f91752 | ||
|
|
b551a54c8f | ||
|
|
26d86aa2fe | ||
|
|
6ee9246650 | ||
|
|
1cd96fbdbf | ||
|
|
a030a026b1 | ||
|
|
8bf9cd0bee | ||
|
|
13b1aad4b8 | ||
|
|
916d956ce2 | ||
|
|
87a7650d26 | ||
|
|
3484e187da | ||
|
|
0835f330e2 | ||
|
|
8064472477 | ||
|
|
2281012e33 | ||
|
|
83eaeab1ba | ||
|
|
6405764df3 | ||
|
|
253276a27b | ||
|
|
855a71ac56 | ||
|
|
96dc53977f | ||
|
|
31111e68eb | ||
|
|
ac0de29872 | ||
|
|
9e2b722491 | ||
|
|
59627e6fe2 | ||
|
|
cd0b5fb378 | ||
|
|
48a3c64c7c | ||
|
|
62da804518 | ||
|
|
439b99cc4a | ||
|
|
64f0efc2c0 | ||
|
|
f196bf5b76 | ||
|
|
790968be6a | ||
|
|
83f0f9537f | ||
|
|
68ebfec918 | ||
|
|
8be4dea081 | ||
|
|
cfdbba45c3 | ||
|
|
d408c9f4bf | ||
|
|
8f4c58c4c3 | ||
|
|
7e88e9648f | ||
|
|
4516d136a4 | ||
|
|
1b85dfbed1 | ||
|
|
807ffb419a | ||
|
|
e826f43aed | ||
|
|
d619f5fafc | ||
|
|
b3e2f9b7ff | ||
|
|
99a39c6f52 | ||
|
|
22991e8740 | ||
|
|
7646ecb6f7 | ||
|
|
204db674bb | ||
|
|
99fe6623de | ||
|
|
f1f78d2485 | ||
|
|
b2ae20b796 | ||
|
|
83bd4e9642 | ||
|
|
767349798a | ||
|
|
ae38f4709b | ||
|
|
fc7001a11a | ||
|
|
9924809bdb | ||
|
|
58a4ff94e4 | ||
|
|
29033e9b80 | ||
|
|
ea24daf37c | ||
|
|
ebc16583fb | ||
|
|
2a10b41781 | ||
|
|
d5946047a1 | ||
|
|
4ff46a4911 | ||
|
|
b587216b5e | ||
|
|
245fce167e | ||
|
|
de9b82ffd5 | ||
|
|
e570f402e4 | ||
|
|
9c761b13fa | ||
|
|
cc4b135d20 | ||
|
|
ec5395c787 | ||
|
|
6d60e54a7d | ||
|
|
28aa34c0b6 | ||
|
|
0701967bab | ||
|
|
a76b1eece4 | ||
|
|
8e791c680e | ||
|
|
fc9f2ccf25 | ||
|
|
d4682fb06e | ||
|
|
377ea183a7 | ||
|
|
72361ab8bf | ||
|
|
f708e583c3 | ||
|
|
d753e1dc48 | ||
|
|
315a8a3805 | ||
|
|
129fed9c9f | ||
|
|
0baccb7621 | ||
|
|
842a8aa45a | ||
|
|
d17843479c | ||
|
|
0d70cc8e58 | ||
|
|
4e6cacb206 | ||
|
|
52514ba35b | ||
|
|
4d59ce435e | ||
|
|
b3b7fa6f4d | ||
|
|
c057c16391 | ||
|
|
dee7cc6f2b | ||
|
|
3d0d87cb0c | ||
|
|
6b66d9b3f8 | ||
|
|
a301d94858 | ||
|
|
01199470f2 | ||
|
|
9e7ea19567 | ||
|
|
cdc6a6cb4a |
5
.github/FUNDING.yml
vendored
@@ -1,5 +1,4 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: tiann
|
github: tiann
|
||||||
patreon: weishu
|
patreon: weishu
|
||||||
custom: https://vxposed.com/donate.html
|
open_collective: sukisu-ultra
|
||||||
|
|
||||||
|
|||||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +0,0 @@
|
|||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: Feature Request
|
|
||||||
url: https://github.com/tiann/KernelSU/issues/1705
|
|
||||||
about: "We do not accept external Feature Requests, see this link for more details."
|
|
||||||
39
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: Feature Request
|
||||||
|
description: "Suggest an idea for this project"
|
||||||
|
title: "[Feature]"
|
||||||
|
labels: "feature"
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
id: feature-info
|
||||||
|
attributes:
|
||||||
|
value: "## Feature Infomation"
|
||||||
|
- type: textarea
|
||||||
|
id: feature-main
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: "Is your feature request related to a problem? Please describe."
|
||||||
|
description: "A clear and concise description of what the problem is."
|
||||||
|
placeholder: "I'm always frustrated when [...]"
|
||||||
|
- type: textarea
|
||||||
|
id: feature-solution
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: "Describe the solution you'd like."
|
||||||
|
description: "A clear and concise description of what you want to happen."
|
||||||
|
- type: textarea
|
||||||
|
id: feature-describe
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: "Describe alternatives you've considered."
|
||||||
|
description: "A clear and concise description of any alternative solutions or features you've considered."
|
||||||
|
- type: textarea
|
||||||
|
id: feature-extra
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
attributes:
|
||||||
|
label: "Additional context"
|
||||||
|
description: "Add any other context or screenshots about the feature request here."
|
||||||
|
|
||||||
137
.github/workflows/avd-kernel.yml
vendored
@@ -1,137 +0,0 @@
|
|||||||
name: GKI Kernel Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
version_name:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
With SUBLEVEL of kernel,
|
|
||||||
for example: android12-5.10.66
|
|
||||||
arch:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Build arch: aarch64/x86_64
|
|
||||||
debug:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
manifest_name:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Local repo manifest xml path,
|
|
||||||
typically for AVD kernel build.
|
|
||||||
secrets:
|
|
||||||
BOOT_SIGN_KEY:
|
|
||||||
required: false
|
|
||||||
CHAT_ID:
|
|
||||||
required: false
|
|
||||||
BOT_TOKEN:
|
|
||||||
required: false
|
|
||||||
MESSAGE_THREAD_ID:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build ${{ inputs.version_name }}
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- name: Maximize build space
|
|
||||||
uses: easimon/maximize-build-space@master
|
|
||||||
with:
|
|
||||||
root-reserve-mb: 8192
|
|
||||||
temp-reserve-mb: 2048
|
|
||||||
remove-dotnet: 'true'
|
|
||||||
remove-android: 'true'
|
|
||||||
remove-haskell: 'true'
|
|
||||||
remove-codeql: 'true'
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
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: |
|
|
||||||
echo "Free space:"
|
|
||||||
df -h
|
|
||||||
cd $GITHUB_WORKSPACE
|
|
||||||
sudo apt-get install repo -y
|
|
||||||
mkdir android-kernel && cd android-kernel
|
|
||||||
repo init --depth=1 -u https://android.googlesource.com/kernel/manifest -m "$GITHUB_WORKSPACE/KernelSU/.github/manifests/${{ inputs.manifest_name }}" --repo-rev=v2.16
|
|
||||||
repo --version
|
|
||||||
repo --trace sync -c -j$(nproc --all) --no-tags
|
|
||||||
df -h
|
|
||||||
|
|
||||||
- name: Setup KernelSU
|
|
||||||
env:
|
|
||||||
PATCH_PATH: ${{ inputs.patch_path }}
|
|
||||||
IS_DEBUG_KERNEL: ${{ inputs.debug }}
|
|
||||||
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
|
|
||||||
DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig
|
|
||||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
|
||||||
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
|
|
||||||
echo "[+] Apply KernelSU patches"
|
|
||||||
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch || echo "[-] No patch found"
|
|
||||||
|
|
||||||
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
|
|
||||||
echo "[+] Enable debug features for kernel"
|
|
||||||
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
|
|
||||||
fi
|
|
||||||
repo status
|
|
||||||
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: Make working directory clean to avoid dirty
|
|
||||||
working-directory: android-kernel
|
|
||||||
run: |
|
|
||||||
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
|
||||||
git config --global user.email "bot@kernelsu.org"
|
|
||||||
git config --global user.name "KernelSUBot"
|
|
||||||
cd common/ && git add -A && git commit -a -m "Add KernelSU"
|
|
||||||
repo status
|
|
||||||
|
|
||||||
- name: Build kernel
|
|
||||||
working-directory: android-kernel
|
|
||||||
run: |
|
|
||||||
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
|
|
||||||
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
|
|
||||||
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
|
|
||||||
fi
|
|
||||||
tools/bazel run --config=fast --config=stamp --lto=thin //common-modules/virtual-device:virtual_device_${{ inputs.arch }}_dist -- --dist_dir=dist
|
|
||||||
NAME=kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
|
|
||||||
TARGET_IMAGE=dist/bzImage
|
|
||||||
if [ ! -e $TARGET_IMAGE ]; then
|
|
||||||
TARGET_IMAGE=dist/Image
|
|
||||||
fi
|
|
||||||
mv $TARGET_IMAGE $NAME
|
|
||||||
echo "file_path=android-kernel/$NAME" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Upload Kernel
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
|
|
||||||
path: "${{ env.file_path }}"
|
|
||||||
20
.github/workflows/build-gki-image.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: Build Android GKI Image
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-a12-kernel:
|
||||||
|
uses: ./.github/workflows/build-kernel-a12.yml
|
||||||
|
secrets: inherit
|
||||||
|
build-a13-kernel:
|
||||||
|
uses: ./.github/workflows/build-kernel-a13.yml
|
||||||
|
secrets: inherit
|
||||||
|
build-a14-kernel:
|
||||||
|
uses: ./.github/workflows/build-kernel-a14.yml
|
||||||
|
secrets: inherit
|
||||||
|
build-a15-kernel:
|
||||||
|
uses: ./.github/workflows/build-kernel-a15.yml
|
||||||
|
secrets: inherit
|
||||||
|
build-a16-kernel:
|
||||||
|
uses: ./.github/workflows/build-kernel-a16.yml
|
||||||
|
secrets: inherit
|
||||||
111
.github/workflows/build-kernel-a12.yml
vendored
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
name: Build Kernel - Android 12
|
||||||
|
on:
|
||||||
|
# push:
|
||||||
|
# branches: ["main", "ci", "checkci"]
|
||||||
|
# paths:
|
||||||
|
# - ".github/workflows/deps/gki/build-kernel-a12.yml"
|
||||||
|
# - ".github/workflows/deps/gki/gki-kernel.yml"
|
||||||
|
# - ".github/scripts/build_a12.sh"
|
||||||
|
# - "kernel/**"
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
jobs:
|
||||||
|
build-kernel:
|
||||||
|
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- sub_level: 209
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- sub_level: 218
|
||||||
|
os_patch_level: 2024-08
|
||||||
|
- sub_level: 226
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- sub_level: 233
|
||||||
|
os_patch_level: 2025-02
|
||||||
|
- sub_level: 236
|
||||||
|
os_patch_level: 2025-05
|
||||||
|
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"
|
||||||
|
debug: ${{ inputs.debug || false }}
|
||||||
|
|
||||||
|
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 }}
|
||||||
|
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@v4
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: KernelSU
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: List artifacts
|
||||||
|
run: |
|
||||||
|
tree
|
||||||
|
|
||||||
|
- name: Download prebuilt toolchain
|
||||||
|
run: |
|
||||||
|
AOSP_MIRROR=https://android.googlesource.com
|
||||||
|
BRANCH=main-kernel-build-2024
|
||||||
|
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 telethon
|
||||||
|
|
||||||
|
- 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: 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@v4
|
||||||
|
with:
|
||||||
|
name: boot-images-android12
|
||||||
|
path: Image-android12*/*.img.gz
|
||||||
146
.github/workflows/build-kernel-a13.yml
vendored
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
name: Build Kernel - Android 13
|
||||||
|
on:
|
||||||
|
# push:
|
||||||
|
# branches: ["main", "ci", "checkci"]
|
||||||
|
# paths:
|
||||||
|
# - ".github/workflows/deps/gki/build-kernel-a13.yml"
|
||||||
|
# - ".github/workflows/deps/gki/gki-kernel.yml"
|
||||||
|
# - ".github/scripts/build_a13.sh"
|
||||||
|
# - "kernel/**"
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
jobs:
|
||||||
|
build-kernel:
|
||||||
|
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 209
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 210
|
||||||
|
os_patch_level: 2024-06
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 214
|
||||||
|
os_patch_level: 2024-07
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 218
|
||||||
|
os_patch_level: 2024-08
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 223
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 228
|
||||||
|
os_patch_level: 2025-01
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 234
|
||||||
|
os_patch_level: 2025-03
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 148
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 149
|
||||||
|
os_patch_level: 2024-07
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 151
|
||||||
|
os_patch_level: 2024-08
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 153
|
||||||
|
os_patch_level: 2024-09
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 167
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 178
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 170
|
||||||
|
os_patch_level: 2025-01
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 178
|
||||||
|
os_patch_level: 2025-03
|
||||||
|
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 }}
|
||||||
|
os_patch_level: ${{ matrix.os_patch_level }}
|
||||||
|
patch_path: ${{ matrix.version }}
|
||||||
|
debug: ${{ inputs.debug || false }}
|
||||||
|
|
||||||
|
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 }}
|
||||||
|
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@v4
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: KernelSU
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: List artifacts
|
||||||
|
run: |
|
||||||
|
tree
|
||||||
|
|
||||||
|
- name: Download prebuilt toolchain
|
||||||
|
run: |
|
||||||
|
AOSP_MIRROR=https://android.googlesource.com
|
||||||
|
BRANCH=main-kernel-build-2024
|
||||||
|
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 telethon
|
||||||
|
|
||||||
|
- 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: 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@v4
|
||||||
|
with:
|
||||||
|
name: boot-images-android13
|
||||||
|
path: Image-android13*/*.img.gz
|
||||||
158
.github/workflows/build-kernel-a14.yml
vendored
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
name: Build Kernel - Android 14
|
||||||
|
on:
|
||||||
|
# push:
|
||||||
|
# branches: ["main", "ci", "checkci"]
|
||||||
|
# paths:
|
||||||
|
# - ".github/workflows/deps/gki/build-kernel-a14.yml"
|
||||||
|
# - ".github/workflows/deps/gki/gki-kernel.yml"
|
||||||
|
# - ".github/scripts/build_a13.sh"
|
||||||
|
# - "kernel/**"
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
jobs:
|
||||||
|
build-kernel:
|
||||||
|
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 148
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 149
|
||||||
|
os_patch_level: 2024-06
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 153
|
||||||
|
os_patch_level: 2024-07
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 158
|
||||||
|
os_patch_level: 2024-08
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 164
|
||||||
|
os_patch_level: 2024-09
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 167
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 170
|
||||||
|
os_patch_level: 2025-01
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 178
|
||||||
|
os_patch_level: 2025-03
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 75
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 78
|
||||||
|
os_patch_level: 2024-06
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 84
|
||||||
|
os_patch_level: 2024-07
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 90
|
||||||
|
os_patch_level: 2024-08
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 93
|
||||||
|
os_patch_level: 2024-09
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 99
|
||||||
|
os_patch_level: 2024-10
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 112
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 115
|
||||||
|
os_patch_level: 2024-12
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 118
|
||||||
|
os_patch_level: 2025-01
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 128
|
||||||
|
os_patch_level: 2025-03
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 134
|
||||||
|
os_patch_level: 2025-05
|
||||||
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
version: android14-${{ matrix.version }}
|
||||||
|
version_name: android14-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||||
|
tag: android14-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||||
|
os_patch_level: ${{ matrix.os_patch_level }}
|
||||||
|
patch_path: ${{ matrix.version }}
|
||||||
|
debug: ${{ inputs.debug || false }}
|
||||||
|
|
||||||
|
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 }}
|
||||||
|
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@v4
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: KernelSU
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: List artifacts
|
||||||
|
run: |
|
||||||
|
tree
|
||||||
|
|
||||||
|
- name: Download prebuilt toolchain
|
||||||
|
run: |
|
||||||
|
AOSP_MIRROR=https://android.googlesource.com
|
||||||
|
BRANCH=main-kernel-build-2024
|
||||||
|
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 telethon
|
||||||
|
|
||||||
|
- 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: 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@v4
|
||||||
|
with:
|
||||||
|
name: boot-images-android14
|
||||||
|
path: Image-android14*/*.img.gz
|
||||||
131
.github/workflows/build-kernel-a15.yml
vendored
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
name: Build Kernel - Android 15
|
||||||
|
on:
|
||||||
|
# push:
|
||||||
|
# branches: ["main", "ci", "checkci"]
|
||||||
|
# paths:
|
||||||
|
# - ".github/workflows/deps/gki/build-kernel-a15.yml"
|
||||||
|
# - ".github/workflows/deps/gki/gki-kernel.yml"
|
||||||
|
# - ".github/scripts/build_a13.sh"
|
||||||
|
# - "kernel/**"
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
jobs:
|
||||||
|
build-kernel:
|
||||||
|
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 30
|
||||||
|
os_patch_level: 2024-08
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 46
|
||||||
|
os_patch_level: 2024-09
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 50
|
||||||
|
os_patch_level: 2024-10
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 56
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 57
|
||||||
|
os_patch_level: 2024-12
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 58
|
||||||
|
os_patch_level: 2025-01
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 66
|
||||||
|
os_patch_level: 2025-02
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 77
|
||||||
|
os_patch_level: 2025-03
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 82
|
||||||
|
os_patch_level: 2025-04
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 87
|
||||||
|
os_patch_level: 2025-05
|
||||||
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
version: android15-${{ matrix.version }}
|
||||||
|
version_name: android15-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||||
|
tag: android15-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||||
|
os_patch_level: ${{ matrix.os_patch_level }}
|
||||||
|
patch_path: ${{ matrix.version }}
|
||||||
|
debug: ${{ inputs.debug || false }}
|
||||||
|
|
||||||
|
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 }}
|
||||||
|
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@v4
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: KernelSU
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: List artifacts
|
||||||
|
run: |
|
||||||
|
tree
|
||||||
|
|
||||||
|
- name: Download prebuilt toolchain
|
||||||
|
run: |
|
||||||
|
AOSP_MIRROR=https://android.googlesource.com
|
||||||
|
BRANCH=main-kernel-build-2024
|
||||||
|
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 telethon
|
||||||
|
|
||||||
|
- 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: 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@v4
|
||||||
|
with:
|
||||||
|
name: boot-images-android15
|
||||||
|
path: Image-android15*/*.img.gz
|
||||||
104
.github/workflows/build-kernel-a16.yml
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
name: Build Kernel - Android 16
|
||||||
|
on:
|
||||||
|
# push:
|
||||||
|
# branches: ["main", "ci", "checkci"]
|
||||||
|
# paths:
|
||||||
|
# - ".github/workflows/deps/gki/build-kernel-a16.yml"
|
||||||
|
# - ".github/workflows/deps/gki/gki-kernel.yml"
|
||||||
|
# - ".github/scripts/build_a13.sh"
|
||||||
|
# - "kernel/**"
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
jobs:
|
||||||
|
build-kernel:
|
||||||
|
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- version: "6.12"
|
||||||
|
sub_level: 38
|
||||||
|
os_patch_level: 2025-08
|
||||||
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
version: android16-${{ matrix.version }}
|
||||||
|
version_name: android16-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||||
|
tag: android16-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||||
|
os_patch_level: ${{ matrix.os_patch_level }}
|
||||||
|
patch_path: ${{ matrix.version }}
|
||||||
|
debug: ${{ inputs.debug || false }}
|
||||||
|
|
||||||
|
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 }}
|
||||||
|
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@v6
|
||||||
|
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
path: KernelSU
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: List artifacts
|
||||||
|
run: |
|
||||||
|
tree
|
||||||
|
|
||||||
|
- name: Download prebuilt toolchain
|
||||||
|
run: |
|
||||||
|
AOSP_MIRROR=https://android.googlesource.com
|
||||||
|
BRANCH=main-kernel-build-2024
|
||||||
|
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 telethon
|
||||||
|
|
||||||
|
- 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: 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@v5
|
||||||
|
with:
|
||||||
|
name: boot-images-android16
|
||||||
|
path: Image-android16*/*.img.gz
|
||||||
77
.github/workflows/build-lkm.yml
vendored
@@ -1,74 +1,21 @@
|
|||||||
name: Build LKM for KernelSU
|
name: Build LKM for KernelSU
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
|
||||||
upload:
|
|
||||||
required: true
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
description: "Whether to upload to branch"
|
|
||||||
secrets:
|
|
||||||
# username:github_pat
|
|
||||||
TOKEN:
|
|
||||||
required: true
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
|
||||||
upload:
|
|
||||||
required: true
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
description: "Whether to upload to branch"
|
|
||||||
jobs:
|
jobs:
|
||||||
build-lkm:
|
build-lkm:
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
kmi:
|
||||||
- version: "android12-5.10"
|
- android12-5.10
|
||||||
sub_level: 233
|
- android13-5.10
|
||||||
os_patch_level: 2025-02
|
- android13-5.15
|
||||||
- version: "android13-5.10"
|
- android14-5.15
|
||||||
sub_level: 234
|
- android14-6.1
|
||||||
os_patch_level: 2025-03
|
- android15-6.6
|
||||||
- version: "android13-5.15"
|
- android16-6.12
|
||||||
sub_level: 178
|
uses: ./.github/workflows/ddk-lkm.yml
|
||||||
os_patch_level: 2025-03
|
|
||||||
- version: "android14-5.15"
|
|
||||||
sub_level: 178
|
|
||||||
os_patch_level: 2025-03
|
|
||||||
- version: "android14-6.1"
|
|
||||||
sub_level: 129
|
|
||||||
os_patch_level: 2025-04
|
|
||||||
- version: "android15-6.6"
|
|
||||||
sub_level: 82
|
|
||||||
os_patch_level: 2025-04
|
|
||||||
# uses: ./.github/workflows/gki-kernel-mock.yml when debugging
|
|
||||||
uses: ./.github/workflows/gki-kernel.yml
|
|
||||||
with:
|
with:
|
||||||
version: ${{ matrix.version }}
|
kmi: ${{ matrix.kmi }}
|
||||||
version_name: ${{ matrix.version }}.${{ matrix.sub_level }}
|
ddk_release: '20251104'
|
||||||
tag: ${{ matrix.version }}-${{ matrix.os_patch_level }}
|
|
||||||
os_patch_level: ${{ matrix.os_patch_level }}
|
|
||||||
build_lkm: true
|
|
||||||
|
|
||||||
push-to-branch:
|
|
||||||
needs: [build-lkm]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ inputs.upload }}
|
|
||||||
steps:
|
|
||||||
- name: Download all workflow run artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: bin/
|
|
||||||
merge-multiple: true
|
|
||||||
- name: Push to branch LKM
|
|
||||||
run: |
|
|
||||||
cd bin
|
|
||||||
git config --global init.defaultBranch lkm
|
|
||||||
git init
|
|
||||||
git remote add origin https://${{ secrets.TOKEN }}@github.com/${{ github.repository }}
|
|
||||||
git config --local user.name "github-actions[bot]"
|
|
||||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
||||||
find . -type f
|
|
||||||
git add .
|
|
||||||
git commit -m "Upload LKM from ${{ github.sha }}" -m "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
|
||||||
git push --force --set-upstream origin lkm
|
|
||||||
|
|||||||
209
.github/workflows/build-manager.yml
vendored
@@ -2,116 +2,43 @@ name: Build Manager
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main", "ci" ]
|
branches: [ "main", "dev", "ci", "miuix" ]
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/build-manager.yml'
|
- '.github/workflows/build-manager.yml'
|
||||||
|
- '.github/workflows/build-lkm.yml'
|
||||||
- 'manager/**'
|
- 'manager/**'
|
||||||
- 'kernel/**'
|
- 'kernel/**'
|
||||||
- 'userspace/ksud/**'
|
- 'userspace/ksud/**'
|
||||||
- 'userspace/susfs/**'
|
- 'userspace/user_scanner/**'
|
||||||
- 'userspace/kpmmgr/**'
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [ "main", "dev", "miuix" ]
|
||||||
paths:
|
paths:
|
||||||
|
- '.github/workflows/build-manager.yml'
|
||||||
|
- '.github/workflows/build-lkm.yml'
|
||||||
- 'manager/**'
|
- 'manager/**'
|
||||||
|
- 'kernel/**'
|
||||||
|
- 'userspace/ksud/**'
|
||||||
|
- 'userspace/user_scanner/**'
|
||||||
workflow_call:
|
workflow_call:
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
build_lkm:
|
|
||||||
required: true
|
|
||||||
type: choice
|
|
||||||
default: "auto"
|
|
||||||
options:
|
|
||||||
- "true"
|
|
||||||
- "false"
|
|
||||||
- "auto"
|
|
||||||
description: "Whether to build lkm"
|
|
||||||
upload_lkm:
|
|
||||||
required: true
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
description: "Whether to upload lkm"
|
|
||||||
jobs:
|
|
||||||
check-build-lkm:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
build_lkm: ${{ steps.check-build.outputs.build_lkm }}
|
|
||||||
upload_lkm: ${{ steps.check-build.outputs.upload_lkm }}
|
|
||||||
steps:
|
|
||||||
- name: check build
|
|
||||||
id: check-build
|
|
||||||
run: |
|
|
||||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ inputs.build_lkm }}" != "auto" ]; then
|
|
||||||
kernel_changed="${{ inputs.build_lkm }}"
|
|
||||||
else
|
|
||||||
kernel_changed=true
|
|
||||||
mkdir tmp
|
|
||||||
cd tmp
|
|
||||||
git config --global init.defaultBranch bot
|
|
||||||
git config --global user.name 'Bot'
|
|
||||||
git config --global user.email 'bot@github.shirkneko.io'
|
|
||||||
git init .
|
|
||||||
git remote add origin https://github.com/${{ github.repository }}
|
|
||||||
CURRENT_COMMIT="${{ github.event.head_commit.id }}"
|
|
||||||
git fetch origin $CURRENT_COMMIT --depth=1
|
|
||||||
git fetch origin lkm --depth=1
|
|
||||||
LKM_COMMIT="$(git log --format=%B -n 1 origin/lkm | head -n 1)"
|
|
||||||
LKM_COMMIT="${LKM_COMMIT#Upload LKM from }"
|
|
||||||
LKM_COMMIT=$(echo "$LKM_COMMIT" | tr -d '[:space:]')
|
|
||||||
echo "LKM_COMMIT=$LKM_COMMIT"
|
|
||||||
git fetch origin "$LKM_COMMIT" --depth=1
|
|
||||||
git diff --quiet "$LKM_COMMIT" "$CURRENT_COMMIT" -- kernel :!kernel/setup.sh .github/workflows/build-lkm.yml .github/workflows/build-kernel-*.yml && kernel_changed=false
|
|
||||||
cd ..
|
|
||||||
rm -rf tmp
|
|
||||||
fi
|
|
||||||
if [ "${{ github.event_name }}" == "push" ] && [ "${{ github.ref }}" == 'refs/heads/main' ]; then
|
|
||||||
need_upload=true
|
|
||||||
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
|
||||||
need_upload="${{ inputs.upload_lkm }}"
|
|
||||||
else
|
|
||||||
need_upload=false
|
|
||||||
fi
|
|
||||||
echo "kernel changed: $kernel_changed"
|
|
||||||
echo "need upload: $need_upload"
|
|
||||||
echo "build_lkm=$kernel_changed" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "upload_lkm=$need_upload" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
build-lkm:
|
build-lkm:
|
||||||
needs: check-build-lkm
|
|
||||||
uses: ./.github/workflows/build-lkm.yml
|
uses: ./.github/workflows/build-lkm.yml
|
||||||
if: ${{ needs.check-build-lkm.outputs.build_lkm == 'true' }}
|
|
||||||
with:
|
|
||||||
upload: ${{ needs.check-build-lkm.outputs.upload_lkm == 'true' }}
|
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
build-susfs:
|
build-user_scanner:
|
||||||
if: ${{ always() }}
|
needs: build-lkm
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- target: aarch64-linux-android
|
- target: All-linux-android
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
uses: ./.github/workflows/susfs.yml
|
uses: ./.github/workflows/user_scanner.yml
|
||||||
with:
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
os: ${{ matrix.os }}
|
|
||||||
|
|
||||||
build-kpmmgr:
|
|
||||||
if: ${{ always() }}
|
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- target: aarch64-linux-android
|
|
||||||
os: ubuntu-latest
|
|
||||||
uses: ./.github/workflows/kpmmgr.yml
|
|
||||||
with:
|
with:
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
os: ${{ matrix.os }}
|
os: ${{ matrix.os }}
|
||||||
|
|
||||||
build-ksud:
|
build-ksud:
|
||||||
if: ${{ always() }}
|
needs: build-lkm
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
@@ -119,17 +46,19 @@ jobs:
|
|||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
- target: x86_64-linux-android
|
- target: x86_64-linux-android
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
|
- target: armv7-linux-androideabi
|
||||||
|
os: ubuntu-latest
|
||||||
uses: ./.github/workflows/ksud.yml
|
uses: ./.github/workflows/ksud.yml
|
||||||
with:
|
with:
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
os: ${{ matrix.os }}
|
os: ${{ matrix.os }}
|
||||||
pack_lkm: true
|
|
||||||
pull_lkm: ${{ needs.check-build-lkm.outputs.build_lkm != 'true' }}
|
|
||||||
|
|
||||||
build-manager:
|
build-manager:
|
||||||
if: ${{ always() }}
|
|
||||||
needs: build-ksud
|
needs: build-ksud
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
spoofed: ["true","false"]
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./manager
|
working-directory: ./manager
|
||||||
@@ -149,8 +78,34 @@ jobs:
|
|||||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
- name: Determine manager variant for telegram bot
|
||||||
|
id: determine
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.ref_name }}" == "miuix" ] && [ "${{ matrix.spoofed }}" == "true" ]; then
|
||||||
|
echo "SKIP=true" >> $GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ "${{ github.ref_name }}" == "miuix" ]; then
|
||||||
|
echo "title=Manager" >> $GITHUB_OUTPUT
|
||||||
|
echo "topicid=${{ vars.MESSAGE_MIUIX_THREAD_ID }}" >> $GITHUB_OUTPUT
|
||||||
|
elif [ "${{ matrix.spoofed }}" == "true" ]; then
|
||||||
|
echo "title=Spoofed-Manager" >> $GITHUB_OUTPUT
|
||||||
|
# maybe need a new var
|
||||||
|
echo "topicid=${{ vars.MESSAGE_SPOOFED_THREAD_ID }}" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "title=Manager" >> $GITHUB_OUTPUT
|
||||||
|
echo "topicid=${{ vars.MESSAGE_THREAD_ID }}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Run randomizer
|
||||||
|
if: ${{ matrix.spoofed == 'true' && steps.determine.outputs.SKIP != 'true' }}
|
||||||
|
run: |
|
||||||
|
chmod +x randomizer
|
||||||
|
./randomizer
|
||||||
|
|
||||||
- name: Write key
|
- name: Write key
|
||||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref == 'refs/heads/susfs' || github.ref_type == 'tag' }}
|
if: ${{ ( github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' )) || github.ref_type == 'tag' }}
|
||||||
run: |
|
run: |
|
||||||
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
|
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
|
||||||
{
|
{
|
||||||
@@ -174,16 +129,10 @@ jobs:
|
|||||||
- name: Setup Android SDK
|
- name: Setup Android SDK
|
||||||
uses: android-actions/setup-android@v3
|
uses: android-actions/setup-android@v3
|
||||||
|
|
||||||
- name: Download arm64 susfs
|
- name: Download all userscanner artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: susfs-aarch64-linux-android
|
name: userscanner-all-linux-android
|
||||||
path: .
|
|
||||||
|
|
||||||
- name: Download arm64 kpmmgr
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: kpmmgr-aarch64-linux-android
|
|
||||||
path: .
|
path: .
|
||||||
|
|
||||||
- name: Download arm64 ksud
|
- name: Download arm64 ksud
|
||||||
@@ -198,65 +147,59 @@ jobs:
|
|||||||
name: ksud-x86_64-linux-android
|
name: ksud-x86_64-linux-android
|
||||||
path: .
|
path: .
|
||||||
|
|
||||||
|
- name: Download arm ksud
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ksud-armv7-linux-androideabi
|
||||||
|
path: .
|
||||||
|
|
||||||
- name: Copy ksud to app jniLibs
|
- name: Copy ksud to app jniLibs
|
||||||
run: |
|
run: |
|
||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||||
mkdir -p app/src/main/jniLibs/x86_64
|
mkdir -p app/src/main/jniLibs/x86_64
|
||||||
cp -f ../aarch64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozako.so
|
mkdir -p app/src/main/jniLibs/armeabi-v7a
|
||||||
cp -f ../x86_64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/x86_64/libzakozako.so
|
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
|
||||||
|
cp -f ../armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud.so
|
||||||
|
|
||||||
- name: Copy kpmmgr to app jniLibs
|
- name: Copy user_scanner to app jniLibs
|
||||||
run: |
|
run: |
|
||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||||
cp -f ../arm64-v8a/kpmmgr ../manager/app/src/main/jniLibs/arm64-v8a/libkpmmgr.so
|
mkdir -p app/src/main/jniLibs/x86_64
|
||||||
|
mkdir -p app/src/main/jniLibs/armeabi-v7a
|
||||||
- name: Copy susfs to app jniLibs
|
cp -f ../arm64-v8a/uid_scanner ../manager/app/src/main/jniLibs/arm64-v8a/libuid_scanner.so
|
||||||
run: |
|
cp -f ../x86_64/uid_scanner ../manager/app/src/main/jniLibs/x86_64/libuid_scanner.so
|
||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
cp -f ../armeabi-v7a/uid_scanner ../manager/app/src/main/jniLibs/armeabi-v7a/libuid_scanner.so
|
||||||
cp -f ../arm64-v8a/zakozakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozakozako.so
|
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: |
|
if: ${{ steps.determine.outputs.SKIP != 'true' }}
|
||||||
{
|
run: ./gradlew clean assembleRelease
|
||||||
echo 'org.gradle.parallel=true'
|
|
||||||
echo 'org.gradle.vfs.watch=true'
|
|
||||||
echo 'org.gradle.jvmargs=-Xmx2048m'
|
|
||||||
echo 'android.native.buildOutput=verbose'
|
|
||||||
} >> gradle.properties
|
|
||||||
sed -i 's/org.gradle.configuration-cache=true//g' gradle.properties
|
|
||||||
./gradlew clean assembleRelease
|
|
||||||
- name: Upload build artifact
|
- name: Upload build artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
if: ${{ ( github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' )) || github.ref_type == 'tag' }}
|
||||||
with:
|
with:
|
||||||
name: manager
|
name: ${{ steps.determine.outputs.title }}
|
||||||
path: manager/app/build/outputs/apk/release/*.apk
|
path: manager/app/build/outputs/apk/release/*.apk
|
||||||
|
|
||||||
- name: Upload mappings
|
- name: Upload mappings
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
if: ${{ ( github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' )) || github.ref_type == 'tag' }}
|
||||||
with:
|
with:
|
||||||
name: "mappings"
|
name: "${{ steps.determine.outputs.title }}-mappings"
|
||||||
path: "manager/app/build/outputs/mapping/release/"
|
path: "manager/app/build/outputs/mapping/release/"
|
||||||
|
|
||||||
- name: Bot session cache
|
|
||||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
|
||||||
id: bot_session_cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: scripts/ksubot.session
|
|
||||||
key: ${{ runner.os }}-bot-session
|
|
||||||
|
|
||||||
- name: Upload to telegram
|
- name: Upload to telegram
|
||||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true' && steps.determine.outputs.SKIP != 'true'
|
||||||
env:
|
env:
|
||||||
CHAT_ID: ${{ vars.CHAT_ID }}
|
CHAT_ID: ${{ vars.CHAT_ID }}
|
||||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||||
MESSAGE_THREAD_ID: ${{ vars.MESSAGE_THREAD_ID }}
|
MESSAGE_THREAD_ID: ${{ steps.determine.outputs.topicid }}
|
||||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
TITLE: Manager
|
TITLE: ${{ steps.determine.outputs.title }}
|
||||||
|
BRANCH: ${{ github.ref_name }}
|
||||||
run: |
|
run: |
|
||||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||||
export VERSION=$(git rev-list --count HEAD)
|
export VERSION=$(git rev-list --count HEAD)
|
||||||
|
|||||||
2
.github/workflows/clippy.yml
vendored
@@ -34,4 +34,4 @@ jobs:
|
|||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
run: |
|
run: |
|
||||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android --release
|
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
|
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android --release
|
||||||
40
.github/workflows/crowdin.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
name: Crowdin Action
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
paths:
|
||||||
|
- 'manager/app/src/main/res/values/strings.xml'
|
||||||
|
- 'manager/app/src/main/res/values-*/strings.xml'
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
synchronize-with-crowdin:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Crowdin Action
|
||||||
|
uses: crowdin/github-action@v2
|
||||||
|
with:
|
||||||
|
upload_sources: true
|
||||||
|
upload_translations: true
|
||||||
|
auto_approve_imported: true
|
||||||
|
download_translations: true
|
||||||
|
skip_untranslated_files: false
|
||||||
|
skip_untranslated_strings: true
|
||||||
|
|
||||||
|
create_pull_request: true
|
||||||
|
localization_branch_name: "Crowdin"
|
||||||
|
pull_request_labels: 'enhancement, translation'
|
||||||
|
pull_request_title: 'opt: sync translation from Crowdin'
|
||||||
|
|
||||||
|
config: 'crowdin.yml'
|
||||||
|
crowdin_branch_name: "main"
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||||
|
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||||
|
CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }}
|
||||||
53
.github/workflows/ddk-lkm.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
name: Build KernelSU Kernel Module
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
kmi:
|
||||||
|
description: 'KMI version'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
ddk_release:
|
||||||
|
description: 'DDK release version'
|
||||||
|
required: false
|
||||||
|
default: '20251104'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-kernelsu-ko:
|
||||||
|
name: Build kernelsu.ko for ${{ inputs.kmi }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/ylarod/ddk:${{ inputs.kmi }}-${{ inputs.ddk_release }}
|
||||||
|
options: --privileged
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build kernelsu.ko
|
||||||
|
run: |
|
||||||
|
git config --global --add safe.directory /__w/SukiSU-Ultra/SukiSU-Ultra
|
||||||
|
cd kernel
|
||||||
|
|
||||||
|
echo "=== Building kernelsu.ko for KMI: ${{ inputs.kmi }} ==="
|
||||||
|
CONFIG_KSU=m CONFIG_KSU_MANUAL_SU=y make
|
||||||
|
|
||||||
|
echo "=== Build completed ==="
|
||||||
|
# Create output directory in GitHub workspace
|
||||||
|
mkdir -p /github/workspace/out
|
||||||
|
# Copy with KMI-specific naming
|
||||||
|
OUTPUT_NAME="${{ inputs.kmi }}_kernelsu.ko"
|
||||||
|
cp kernelsu.ko "/github/workspace/out/$OUTPUT_NAME"
|
||||||
|
|
||||||
|
echo "Copied to: /github/workspace/out/$OUTPUT_NAME"
|
||||||
|
ls -la "/github/workspace/out/$OUTPUT_NAME"
|
||||||
|
echo "Size: $(du -h "/github/workspace/out/$OUTPUT_NAME" | cut -f1)"
|
||||||
|
llvm-strip -d "/github/workspace/out/$OUTPUT_NAME"
|
||||||
|
echo "Size after stripping: $(du -h "/github/workspace/out/$OUTPUT_NAME" | cut -f1)"
|
||||||
|
|
||||||
|
- name: Upload kernelsu.ko artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.kmi }}-lkm
|
||||||
|
path: /github/workspace/out/${{ inputs.kmi }}_kernelsu.ko
|
||||||
2
.github/workflows/deploy-website.yml
vendored
@@ -64,4 +64,4 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v4
|
uses: actions/deploy-pages@v4
|
||||||
79
.github/workflows/gki-kernel-mock.yml
vendored
@@ -1,79 +0,0 @@
|
|||||||
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: false
|
|
||||||
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
|
|
||||||
debug:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
build_lkm:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
secrets:
|
|
||||||
BOOT_SIGN_KEY:
|
|
||||||
required: false
|
|
||||||
CHAT_ID:
|
|
||||||
required: false
|
|
||||||
BOT_TOKEN:
|
|
||||||
required: false
|
|
||||||
MESSAGE_THREAD_ID:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
mock_build:
|
|
||||||
name: Mock build ${{ inputs.version_name }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Create mocking ko
|
|
||||||
run: |
|
|
||||||
echo "${{ inputs.version }}_kernelsu.ko" > ${{ inputs.version }}_kernelsu.ko
|
|
||||||
- name: Upload LKM
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
if: ${{ inputs.build_lkm == true }}
|
|
||||||
with:
|
|
||||||
name: ${{ inputs.version }}-lkm
|
|
||||||
path: ./*_kernelsu.ko
|
|
||||||
45
.github/workflows/gki-kernel.yml
vendored
@@ -77,10 +77,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
root-reserve-mb: 8192
|
root-reserve-mb: 8192
|
||||||
temp-reserve-mb: 2048
|
temp-reserve-mb: 2048
|
||||||
remove-dotnet: 'true'
|
remove-dotnet: "true"
|
||||||
remove-android: 'true'
|
remove-android: "true"
|
||||||
remove-haskell: 'true'
|
remove-haskell: "true"
|
||||||
remove-codeql: 'true'
|
remove-codeql: "true"
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -103,7 +103,7 @@ jobs:
|
|||||||
cd $GITHUB_WORKSPACE
|
cd $GITHUB_WORKSPACE
|
||||||
sudo apt-get install repo -y
|
sudo apt-get install repo -y
|
||||||
mkdir android-kernel && cd android-kernel
|
mkdir android-kernel && cd android-kernel
|
||||||
repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.35
|
repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.16
|
||||||
REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/kernel/common ${{ inputs.tag }})
|
REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/kernel/common ${{ inputs.tag }})
|
||||||
DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
|
DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
|
||||||
if grep -q deprecated <<< $REMOTE_BRANCH; then
|
if grep -q deprecated <<< $REMOTE_BRANCH; then
|
||||||
@@ -195,12 +195,35 @@ jobs:
|
|||||||
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
|
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Append ashmem exports if missing
|
||||||
|
if: startsWith(inputs.version, 'android16-6.12')
|
||||||
|
working-directory: android-kernel
|
||||||
|
run: |
|
||||||
|
FILE=common/drivers/staging/android/ashmem.c
|
||||||
|
if [[ -f "$FILE" ]] && ! grep -q 'is_ashmem_file' "$FILE"; then
|
||||||
|
cat >>"$FILE" <<'EOF'
|
||||||
|
|
||||||
|
bool is_ashmem_file(struct file *file) { return false; }
|
||||||
|
int ashmem_area_name(struct file *file, char *name) { return 0; }
|
||||||
|
long ashmem_area_size(struct file *file) { return 0; }
|
||||||
|
struct file *ashmem_area_vmfile(struct file *file) { return NULL; }
|
||||||
|
EXPORT_SYMBOL_GPL(is_ashmem_file);
|
||||||
|
EXPORT_SYMBOL_GPL(ashmem_area_name);
|
||||||
|
EXPORT_SYMBOL_GPL(ashmem_area_size);
|
||||||
|
EXPORT_SYMBOL_GPL(ashmem_area_vmfile);
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
sed -i -E 's/\$\(CONFIG_ANDROID_BINDER_IPC_RUST\)/m/g' common/drivers/android/Makefile
|
||||||
|
|
||||||
- name: Make working directory clean to avoid dirty
|
- name: Make working directory clean to avoid dirty
|
||||||
working-directory: android-kernel
|
working-directory: android-kernel
|
||||||
run: |
|
run: |
|
||||||
if [ -e common/BUILD.bazel ]; then
|
# Fix bazel build error
|
||||||
sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' common/BUILD.bazel
|
if [ -f common/BUILD.bazel ]; then
|
||||||
|
[ -f android/abi_gki_protected_exports_aarch64 ] || sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' common/BUILD.bazel
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
||||||
git config --global user.email "bot@kernelsu.org"
|
git config --global user.email "bot@kernelsu.org"
|
||||||
git config --global user.name "KernelSUBot"
|
git config --global user.name "KernelSUBot"
|
||||||
@@ -217,7 +240,11 @@ jobs:
|
|||||||
if [ -e build/build.sh ]; then
|
if [ -e build/build.sh ]; then
|
||||||
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang"
|
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang"
|
||||||
else
|
else
|
||||||
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist
|
if [ "${{ inputs.version }}" == "android16-6.12" ]; then
|
||||||
|
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --destdir=dist
|
||||||
|
else
|
||||||
|
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Prepare artifacts
|
- name: Prepare artifacts
|
||||||
@@ -228,7 +255,7 @@ jobs:
|
|||||||
OUTDIR=android-kernel/dist
|
OUTDIR=android-kernel/dist
|
||||||
fi
|
fi
|
||||||
mkdir output
|
mkdir output
|
||||||
if [ "${{ inputs.build_lkm}}" = "true" ]; then
|
if [ "${{ inputs.build_lkm}}" = "true" ]; then
|
||||||
llvm-strip-15 -d $OUTDIR/kernelsu.ko
|
llvm-strip-15 -d $OUTDIR/kernelsu.ko
|
||||||
mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko
|
mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko
|
||||||
else
|
else
|
||||||
|
|||||||
40
.github/workflows/kpmmgr.yml
vendored
@@ -1,40 +0,0 @@
|
|||||||
name: Build kpmmgr
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "mian" ]
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/kpmmgr.yml'
|
|
||||||
- 'userspace/kpmmgr/**'
|
|
||||||
workflow_dispatch:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
target:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
os:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: self-hosted
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-susfs:
|
|
||||||
name: Build userspace kpmmgr
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Build kpmmgr
|
|
||||||
working-directory: ./userspace/kpmmgr
|
|
||||||
run: |
|
|
||||||
$ANDROID_NDK_HOME/ndk-build
|
|
||||||
|
|
||||||
- name: Upload a Build Artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: kpmmgr-aarch64-linux-android
|
|
||||||
path: ./userspace/kpmmgr/libs
|
|
||||||
35
.github/workflows/ksud.yml
vendored
@@ -9,10 +9,6 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: ubuntu-latest
|
default: ubuntu-latest
|
||||||
pull_lkm:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
pack_lkm:
|
pack_lkm:
|
||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -21,6 +17,8 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ${{ inputs.os }}
|
runs-on: ${{ inputs.os }}
|
||||||
@@ -29,36 +27,19 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Pull lkms from branch
|
- name: Download artifacts
|
||||||
if: ${{ inputs.pack_lkm && inputs.pull_lkm }}
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: lkm
|
|
||||||
path: lkm
|
|
||||||
|
|
||||||
- name: Download lkms from artifacts
|
|
||||||
if: ${{ inputs.pack_lkm && !inputs.pull_lkm }}
|
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
|
||||||
- name: Prepare LKM files
|
- name: Prepare LKM fies
|
||||||
if: ${{ inputs.pack_lkm && inputs.pull_lkm }}
|
if: ${{ inputs.pack_lkm }}
|
||||||
run: |
|
|
||||||
cp lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
|
|
||||||
|
|
||||||
- name: Prepare LKM files
|
|
||||||
if: ${{ inputs.pack_lkm && !inputs.pull_lkm }}
|
|
||||||
run: |
|
run: |
|
||||||
cp android*-lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
|
cp android*-lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
|
||||||
|
|
||||||
- name: Setup rustup
|
- name: Setup rustup
|
||||||
run: |
|
run: |
|
||||||
rustup update stable
|
rustup update stable
|
||||||
rustup target add x86_64-apple-darwin
|
rustup target add x86_64-apple-darwin
|
||||||
rustup target add aarch64-apple-darwin
|
rustup target add aarch64-apple-darwin
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
workspaces: userspace/ksud
|
|
||||||
cache-targets: false
|
|
||||||
|
|
||||||
- name: Install cross
|
- name: Install cross
|
||||||
run: |
|
run: |
|
||||||
@@ -71,4 +52,4 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ksud-${{ inputs.target }}
|
name: ksud-${{ inputs.target }}
|
||||||
path: userspace/ksud/target/**/release/zakozako*
|
path: userspace/ksud/target/**/release/ksud*
|
||||||
|
|||||||
2
.github/workflows/shellcheck.yml
vendored
@@ -16,7 +16,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
shellcheck:
|
shellcheck:
|
||||||
runs-on: self-hosted
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
name: Build susfs
|
name: Build user_scanner
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "mian" ]
|
branches: [ "mian" ]
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/susfs.yml'
|
- '.github/workflows/user_scanner.yml'
|
||||||
- 'userspace/susfs/**'
|
- 'userspace/user_scanner/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
inputs:
|
||||||
@@ -18,8 +18,8 @@ on:
|
|||||||
default: self-hosted
|
default: self-hosted
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-susfs:
|
build-user_scanner:
|
||||||
name: Build userspace susfs
|
name: Build userspace user_scanner
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -28,13 +28,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Build susfs
|
- name: Build user_scanner
|
||||||
working-directory: ./userspace/susfs
|
working-directory: ./userspace/user_scanner
|
||||||
run: |
|
run: |
|
||||||
$ANDROID_NDK_HOME/ndk-build
|
$ANDROID_NDK_HOME/ndk-build
|
||||||
|
|
||||||
- name: Upload a Build Artifact
|
- name: Upload a Build Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: susfs-aarch64-linux-android
|
name: userscanner-all-linux-android
|
||||||
path: ./userspace/susfs/libs
|
path: ./userspace/user_scanner/libs
|
||||||
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
.idea
|
.idea
|
||||||
.vscode
|
CLAUDE.md
|
||||||
|
.DS_Store
|
||||||
|
|||||||
6
crowdin.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
project_id_env: CROWDIN_PROJECT_ID
|
||||||
|
api_token_env: CROWDIN_API_TOKEN
|
||||||
|
preserve_hierarchy: 1
|
||||||
|
files:
|
||||||
|
- source: /manager/app/src/main/res/values/strings.xml
|
||||||
|
translation: /manager/app/src/main/res/values-%two_letters_code%/strings.xml
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
# SukiSU Ultra
|
|
||||||
|
|
||||||
**English** | [简体中文](README.md) | [日本語](README-ja.md)
|
|
||||||
|
|
||||||
Android device root solution based on [KernelSU](https://github.com/tiann/KernelSU)
|
|
||||||
|
|
||||||
**Experimental! Use at your own risk!** This solution is based on [KernelSU](https://github.com/tiann/KernelSU) and is experimental!
|
|
||||||
|
|
||||||
> This is an unofficial fork. All rights are reserved to [@tiann](https://github.com/tiann)
|
|
||||||
>
|
|
||||||
> However, we will be a separately maintained branch of KSU in the future
|
|
||||||
|
|
||||||
- Fully adapted for non-GKI devices (susfs-dev and unsusfs-patched dev branches only)
|
|
||||||
|
|
||||||
## How to add
|
|
||||||
|
|
||||||
Use the susfs-stable or susfs-dev branch (integrated susfs with support for non-GKI devices)
|
|
||||||
```
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the main branch
|
|
||||||
```
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/KernelSU/main/kernel/setup.sh" | bash -s main
|
|
||||||
```
|
|
||||||
|
|
||||||
## How to use integrated susfs
|
|
||||||
|
|
||||||
1. Use the susfs-dev branch directly without any patching
|
|
||||||
|
|
||||||
## KPM support
|
|
||||||
|
|
||||||
- We have removed duplicate KSU functions based on KernelPatch and retained KPM support.
|
|
||||||
- We will introduce more APatch-compatible functions to ensure the integrity of KPM functionality.
|
|
||||||
|
|
||||||
Open source address: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
|
|
||||||
|
|
||||||
KPM template address: https://github.com/udochina/KPM-Build-Anywhere
|
|
||||||
|
|
||||||
## More links
|
|
||||||
|
|
||||||
Projects compiled based on Sukisu and susfs
|
|
||||||
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
|
||||||
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
|
||||||
|
|
||||||
## Hook method
|
|
||||||
- This method references the hook method from (https://github.com/rsuntk/KernelSU)
|
|
||||||
|
|
||||||
1. **KPROBES hook:**
|
|
||||||
- Also used for Loadable Kernel Module (LKM)
|
|
||||||
- Default hook method on GKI kernels.
|
|
||||||
- Need `CONFIG_KPROBES=y`
|
|
||||||
|
|
||||||
2. **Manual hook:**
|
|
||||||
- Standard KernelSU hook: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
|
||||||
- backslashxx's syscall manual hook: https://github.com/backslashxx/KernelSU/issues/5
|
|
||||||
- Default hook method on Non-GKI kernels.
|
|
||||||
- Need `CONFIG_KSU_MANUAL_HOOK=y`
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### GKI
|
|
||||||
|
|
||||||
Please follow this guide.
|
|
||||||
|
|
||||||
https://kernelsu.org/guide/installation.html
|
|
||||||
|
|
||||||
|
|
||||||
### OnePlus
|
|
||||||
|
|
||||||
1. Use the link mentioned in the 'More Links' section to create a customized build with your device information, and then flash the zip file with the AnyKernel3 suffix.
|
|
||||||
|
|
||||||
> [!Note]
|
|
||||||
> - You only need to fill in the first two parts of kernel versions, such as 5.10, 5.15, 6.1, or 6.6.
|
|
||||||
> - Please search for the processor codename by yourself, usually it is all English without numbers.
|
|
||||||
> - You can find the branch and configuration files from the OnePlus open-source kernel repository.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
1. Kernel-based `su` and root access management.
|
|
||||||
2. Not based on [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) module system, but based on [Magic Mount](https://github.com/5ec1cff/KernelSU) from 5ec1cff
|
|
||||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock root privileges in a cage.
|
|
||||||
4. Bringing back non-GKI/GKI 1.0 support
|
|
||||||
5. More customization
|
|
||||||
6. Support for KPM kernel modules
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
- The file in the “kernel” directory is under [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) license.
|
|
||||||
- All other parts except the “kernel” directory are under [GPL-3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html) license.
|
|
||||||
|
|
||||||
## Sponsorship list
|
|
||||||
|
|
||||||
- [Ktouls](https://github.com/Ktouls) Thanks so much for bringing me support
|
|
||||||
- [zaoqi123](https://github.com/zaoqi123) It's not a bad idea to buy me a milk tea
|
|
||||||
- [wswzgdg](https://github.com/wswzgdg) Many thanks for supporting this project
|
|
||||||
- [yspbwx2010](https://github.com/yspbwx2010) Many thanks
|
|
||||||
- [DARKWWEE](https://github.com/DARKWWEE) Thanks for the 100 USDT Lao
|
|
||||||
|
|
||||||
If the above list does not have your name, I will update it as soon as possible, and thanks again for your support!
|
|
||||||
|
|
||||||
## Contributions
|
|
||||||
|
|
||||||
- [KernelSU](https://github.com/tiann/KernelSU): original project
|
|
||||||
- [MKSU](https://github.com/5ec1cff/KernelSU): Used project
|
|
||||||
- [RKSU](https://github.com/rsuntk/KernelsU): Reintroduced the support of non-GKI devices using the kernel of this project
|
|
||||||
- [susfs](https://gitlab.com/simonpunk/susfs4ksu):Used susfs file system
|
|
||||||
- [KernelSU](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU conceptualization
|
|
||||||
- [Magisk](https://github.com/topjohnwu/Magisk): Powerful root utility
|
|
||||||
- [genuine](https://github.com/brevent/genuine/): APK v2 Signature Verification
|
|
||||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Some rootkit utilities.
|
|
||||||
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch is a key part of the APatch implementation of the kernel module
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
# SukiSU Ultra
|
|
||||||
|
|
||||||
**日本語** | [简体中文](README.md) | [English](README-en.md)
|
|
||||||
|
|
||||||
[KernelSU](https://github.com/tiann/KernelSU) をベースとした Android デバイスの root ソリューション
|
|
||||||
|
|
||||||
**試験中なビルドです!自己責任で使用してください!**<br>
|
|
||||||
このソリューションは [KernelSU](https://github.com/tiann/KernelSU) に基づいていますが、試験中なビルドです。
|
|
||||||
|
|
||||||
> これは非公式なフォークです。すべての権利は [@tiann](https://github.com/tiann) に帰属します。
|
|
||||||
>
|
|
||||||
> ただし、将来的には KSU とは別に管理されるブランチとなる予定です。
|
|
||||||
|
|
||||||
- GKI 非対応なデバイスに完全に適応 (susfs-dev と unsusfs-patched dev ブランチのみ)
|
|
||||||
|
|
||||||
## 追加方法
|
|
||||||
|
|
||||||
susfs-stable または susfs-dev ブランチ (GKI 非対応デバイスに対応する統合された susfs) 使用してください。
|
|
||||||
```
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
|
||||||
```
|
|
||||||
|
|
||||||
メインブランチを使用する場合
|
|
||||||
```
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/KernelSU/main/kernel/setup.sh" | bash -s main
|
|
||||||
```
|
|
||||||
## 統合された susfs の使い方
|
|
||||||
|
|
||||||
1. パッチを当てずに susfs-dev ブランチを直接使用してください。
|
|
||||||
|
|
||||||
## KPM に対応
|
|
||||||
|
|
||||||
- KernelPatch に基づいて重複した KSU の機能を削除、KPM の対応を維持させています。
|
|
||||||
- KPM 機能の整合性を確保するために、APatch の互換機能を更に向上させる予定です。
|
|
||||||
|
|
||||||
オープンソースアドレス: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
|
|
||||||
|
|
||||||
KPM テンプレートのアドレス: https://github.com/udochina/KPM-Build-Anywhere
|
|
||||||
|
|
||||||
## その他のリンク
|
|
||||||
|
|
||||||
SukiSU と susfs をベースにコンパイルされたプロジェクトです。
|
|
||||||
|
|
||||||
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
|
||||||
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
|
||||||
|
|
||||||
## フックの方式
|
|
||||||
|
|
||||||
- この方式は (https://github.com/rsuntk/KernelSU) のフック方式を参照してください。
|
|
||||||
|
|
||||||
1. **KPROBES フック:**
|
|
||||||
- 読み込み可能なカーネルモジュールの場合 (LKM)
|
|
||||||
- GKI カーネルのデフォルトとなるフック方式
|
|
||||||
- `CONFIG_KPROBES=y` が必要です
|
|
||||||
|
|
||||||
2. **手動でフック:**
|
|
||||||
- 標準の KernelSU フック: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
|
||||||
- backslashxx syscall フック: https://github.com/backslashxx/KernelSU/issues/5
|
|
||||||
- 非 GKI カーネル用のデフォルトフッキングメソッド
|
|
||||||
- `CONFIG_KSU_MANUAL_HOOK=y` が必要です
|
|
||||||
|
|
||||||
## 使い方
|
|
||||||
|
|
||||||
### GKI
|
|
||||||
|
|
||||||
このガイドに従ってください。
|
|
||||||
|
|
||||||
https://kernelsu.org/ja_JP/guide/installation.html
|
|
||||||
|
|
||||||
### OnePlus
|
|
||||||
|
|
||||||
1. `その他のリンク`の項目に記載されているリンクを開き、デバイス情報を使用してカスタマイズされたカーネルをビルドし、AnyKernel3 の接頭辞を持つ .zip ファイルをフラッシュします。
|
|
||||||
|
|
||||||
> [!Note]
|
|
||||||
> - 5.10、5.15、6.1、6.6 などのカーネルバージョンの最初の 2 文字のみを入力する必要があります。
|
|
||||||
> - SoC のコードネームは自分で検索してください。通常は、数字がなく英語表記のみです。
|
|
||||||
> - ブランチと構成ファイルは、OnePlus オープンソースカーネルリポジトリから見つけることができます。
|
|
||||||
|
|
||||||
## 機能
|
|
||||||
|
|
||||||
1. カーネルベースな `su` および root アクセスの管理。
|
|
||||||
2. [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) モジュールシステムではなく、 5ec1cff 氏の [Magic Mount](https://github.com/5ec1cff/KernelSU) に基づいています。
|
|
||||||
3. [アプリプロファイル](https://kernelsu.org/guide/app-profile.html): root 権限をケージ内にロックします。
|
|
||||||
4. 非 GKI / GKI 1.0 の対応を復活
|
|
||||||
5. その他のカスタマイズ
|
|
||||||
6. KPM カーネルモジュールに対応
|
|
||||||
|
|
||||||
## ライセンス
|
|
||||||
|
|
||||||
- “kernel” ディレクトリ内のファイルは [GPL-2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0.ja.html) のみライセンス下にあります。
|
|
||||||
- “kernel” ディレクトリを除くその他すべての部分は [GPL-3.0 またはそれ以降](https://www.gnu.org/licenses/gpl-3.0.html) のライセンス下にあります。
|
|
||||||
|
|
||||||
## スポンサーシップの一覧
|
|
||||||
|
|
||||||
- [Ktouls](https://github.com/Ktouls) 応援をしてくれたことに感謝。
|
|
||||||
- [zaoqi123](https://github.com/zaoqi123) ミルクティーを買ってあげるのも良い考えですね。
|
|
||||||
- [wswzgdg](https://github.com/wswzgdg) このプロジェクトを支援していただき、ありがとうございます。
|
|
||||||
- [yspbwx2010](https://github.com/yspbwx2010) どうもありがとう。
|
|
||||||
- [DARKWWEE](https://github.com/DARKWWEE) ラオウ100USDTありがとう!
|
|
||||||
|
|
||||||
上記の一覧にあなたの名前がない場合は、できるだけ早急に更新しますので再度ご支援をお願いします。
|
|
||||||
|
|
||||||
## 貢献者
|
|
||||||
|
|
||||||
- [KernelSU](https://github.com/tiann/KernelSU): オリジナルのプロジェクトです。
|
|
||||||
- [MKSU](https://github.com/5ec1cff/KernelSU): 使用しているプロジェクトです。
|
|
||||||
- [RKSU](https://github.com/rsuntk/KernelsU): このプロジェクトのカーネルを使用して非 GKI デバイスのサポートを追加しています。
|
|
||||||
- [susfs](https://gitlab.com/simonpunk/susfs4ksu):使用している susfs ファイルシステムです。
|
|
||||||
- [KernelSU](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU について。
|
|
||||||
- [Magisk](https://github.com/topjohnwu/Magisk): パワフルな root ユーティリティです。
|
|
||||||
- [genuine](https://github.com/brevent/genuine/): APK v2 署名認証で使用しています。
|
|
||||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): いくつかの rootkit ユーティリティを使用しています。
|
|
||||||
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch はカーネルモジュールの APatch 実装での重要な部分となります。
|
|
||||||
182
docs/README.md
@@ -1,116 +1,102 @@
|
|||||||
# SukiSU Ultra
|
# SukiSU Ultra
|
||||||
|
<img align='right' src='SukiSU-mini.svg' width='220px' alt="sukisu logo">
|
||||||
|
|
||||||
**简体中文** | [English](README-en.md) | [日本語](README-ja.md)
|
|
||||||
|
|
||||||
基于 [KernelSU](https://github.com/tiann/KernelSU) 的安卓设备 root 解决方案
|
**English** | [简体中文](./zh/README.md) | [日本語](./ja/README.md) | [Türkçe](./tr/README.md)
|
||||||
|
|
||||||
**实验性! 使用风险自负!**
|
A kernel-based root solution for Android devices, forked from [`tiann/KernelSU`](https://github.com/tiann/KernelSU), and added some interesting changes.
|
||||||
|
|
||||||
> 这是非官方分支,保留所有权利 [@tiann](https://github.com/tiann)
|
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||||
|
[](https://t.me/Sukiksu)
|
||||||
|
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||||
|
[](/LICENSE)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
1. Kernel-based `su` and root access management
|
||||||
|
2. Module system based on [Magic Mount](https://github.com/5ec1cff/KernelSU)
|
||||||
|
> **Note:** SukiSU now delegates all module mounting to the installed *metamodule*; the core no longer handles mount operations.
|
||||||
|
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock up the root power in a cage
|
||||||
|
4. Support non-GKI and GKI 1.0
|
||||||
|
5. KPM Support
|
||||||
|
6. Tweaks to the manager theme and the built-in susfs management tool.
|
||||||
|
|
||||||
|
## Compatibility Status
|
||||||
|
|
||||||
|
- KernelSU (before v1.0.0) officially supports Android GKI 2.0 devices (kernel 5.10+).
|
||||||
|
|
||||||
|
- Older kernels (4.4+) are also compatible, but the kernel will have to be built manually.
|
||||||
|
|
||||||
|
- With more backports, KernelSU can supports 3.x kernel (3.4-3.18).
|
||||||
|
|
||||||
|
- Currently, only `arm64-v8a`, `armeabi-v7a (bare)` and `X86_64`(some) are supported.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
See [`guide/installation.md`](guide/installation.md)
|
||||||
|
|
||||||
|
## Integration
|
||||||
|
|
||||||
|
See [`guide/how-to-integrate.md`](guide/how-to-integrate.md)
|
||||||
|
|
||||||
|
## Translation
|
||||||
|
|
||||||
|
If you need to submit a translation for the manager, please go to [Crowdin](https://crowdin.com/project/SukiSU-Ultra).
|
||||||
|
|
||||||
|
## KPM Support
|
||||||
|
|
||||||
|
- Based on KernelPatch, we removed features redundant with KSU and retained only KPM support.
|
||||||
|
- Work in Progress: Expanding APatch compatibility by integrating additional functions to ensure compatibility across different implementations.
|
||||||
|
|
||||||
|
**Open-source repository**: [https://github.com/ShirkNeko/SukiSU_KernelPatch_patch](https://github.com/ShirkNeko/SukiSU_KernelPatch_patch)
|
||||||
|
|
||||||
|
**KPM template**: [https://github.com/udochina/KPM-Build-Anywhere](https://github.com/udochina/KPM-Build-Anywhere)
|
||||||
|
|
||||||
|
> [!Note]
|
||||||
>
|
>
|
||||||
> 但是,我们将会在未来成为一个单独维护的 KSU 分支
|
> 1. Requires `CONFIG_KPM=y`
|
||||||
|
> 2. Non-GKI devices requires `CONFIG_KALLSYMS=y` and `CONFIG_KALLSYMS_ALL=y`
|
||||||
|
> 3. For kernels below `4.19`, backporting from `set_memory.h` from `4.19` is required.
|
||||||
|
|
||||||
## 如何添加
|
## Troubleshooting
|
||||||
|
|
||||||
在内核源码的根目录下执行以下命令:
|
1. Device stuck upon manager app uninstallation?
|
||||||
|
Uninstall _com.sony.playmemories.mobile_
|
||||||
|
|
||||||
使用 susfs-dev 分支(已集成 susfs,带非 GKI 设备的支持)
|
## Sponsor
|
||||||
```
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
|
||||||
```
|
|
||||||
|
|
||||||
使用 main 分支
|
- [ShirkNeko](https://afdian.com/a/shirkneko) (maintainer of SukiSU)
|
||||||
```
|
- [weishu](https://github.com/sponsors/tiann) (author of KernelSU)
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
|
||||||
```
|
|
||||||
|
|
||||||
## 如何集成 susfs
|
## ShirkNeko's sponsorship list
|
||||||
|
|
||||||
1. 直接使用 susfs-stable 或者 susfs-dev 分支,不需要再集成 susfs
|
- [Ktouls](https://github.com/Ktouls) Thanks so much for bringing me support.
|
||||||
|
- [zaoqi123](https://github.com/zaoqi123) Thanks for the milk tea.
|
||||||
|
- [wswzgdg](https://github.com/wswzgdg) Many thanks for supporting this project.
|
||||||
|
- [yspbwx2010](https://github.com/yspbwx2010) Many thanks.
|
||||||
|
- [DARKWWEE](https://github.com/DARKWWEE) 100 USDT
|
||||||
|
- [Saksham Singla](https://github.com/TypeFlu) Provide and maintain the website
|
||||||
|
- [OukaroMF](https://github.com/OukaroMF) Donation of website domain name
|
||||||
|
|
||||||
## 钩子方法
|
## License
|
||||||
|
|
||||||
- 此部分引用自 [rsuntk 的钩子方法](https://github.com/rsuntk/KernelSU)
|
- The file in the “kernel” directory is under [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) license.
|
||||||
|
- The images of the files `ic_launcher(?!.*alt.*).*` with anime character sticker are copyrighted by [怡子曰曰](https://space.bilibili.com/10545509), the Brand Intellectual Property in the images is owned by [明风 OuO](https://space.bilibili.com/274939213), and the vectorization is done by @MiRinChan. Before using these files, in addition to complying with [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt), you also need to comply with the authorization of the two authors to use these artistic contents.
|
||||||
|
- Except for the files or directories mentioned above, all other parts are under [GPL-3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html) license.
|
||||||
|
|
||||||
1. **KPROBES 钩子:**
|
## Credit
|
||||||
- 用于可加载内核模块 (LKM)
|
|
||||||
- GKI 2.0 内核的默认钩子方法
|
|
||||||
- 需要 `CONFIG_KPROBES=y`
|
|
||||||
|
|
||||||
2. **手动钩子:**
|
- [KernelSU](https://github.com/tiann/KernelSU): upstream
|
||||||
- 标准的 KernelSU 钩子:https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
- [MKSU](https://github.com/5ec1cff/KernelSU): Magic Mount
|
||||||
- backslashxx 的 syscall 手动钩子:https://github.com/backslashxx/KernelSU/issues/5
|
- [RKSU](https://github.com/rsuntk/KernelsU): support non-GKI
|
||||||
- 非 GKI 内核的默认挂钩方法
|
- [susfs](https://gitlab.com/simonpunk/susfs4ksu): An addon root hiding kernel patches and userspace module for KernelSU.
|
||||||
- 需要 `CONFIG_KSU_MANUAL_HOOK=y`
|
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch is a key part of the APatch implementation of the kernel module
|
||||||
|
|
||||||
## KPM 支持
|
<details>
|
||||||
|
<summary>KernelSU's credit</summary>
|
||||||
|
|
||||||
- 我们基于 KernelPatch 去掉了和 KSU 重复的功能,仅保留了 KPM 支持
|
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): The KernelSU idea.
|
||||||
- 我们将会引入更多的兼容 APatch 的函数来确保 KPM 功能的完整性
|
- [Magisk](https://github.com/topjohnwu/Magisk): The powerful root tool.
|
||||||
|
- [genuine](https://github.com/brevent/genuine/): APK v2 signature validation.
|
||||||
开源地址: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
|
- [Diamorphine](https://github.com/m0nad/Diamorphine): Some rootkit skills.
|
||||||
|
</details>
|
||||||
KPM 模板地址: https://github.com/udochina/KPM-Build-Anywhere
|
|
||||||
|
|
||||||
## 更多链接
|
|
||||||
|
|
||||||
基于 SukiSU 和 susfs 编译的项目
|
|
||||||
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
|
||||||
- [一加](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
|
||||||
|
|
||||||
## 使用方法
|
|
||||||
|
|
||||||
### 普适的 GKI
|
|
||||||
|
|
||||||
请**全部**参考 https://kernelsu.org/zh_CN/guide/installation.html
|
|
||||||
|
|
||||||
> [!Note]
|
|
||||||
> 1. 适用于如小米、红米、三星等的 GKI 2.0 的设备 (不包含魔改内核的厂商如魅族、一加、真我和 oppo)
|
|
||||||
> 2. 找到[更多链接](#%E6%9B%B4%E5%A4%9A%E9%93%BE%E6%8E%A5)里的 GKI 构建的项目。找到设备内核版本。然后下载下来,用TWRP或者内核刷写工具刷入带 AnyKernel3 后缀的压缩包即可
|
|
||||||
> 3. 一般不带后缀的 .zip 压缩包是未压缩的,gz 后缀的为天玑机型所使用的压缩方式
|
|
||||||
|
|
||||||
|
|
||||||
### 一加
|
|
||||||
|
|
||||||
1.找到更多链接里的一加项目进行自行填写,然后云编译构建,最后刷入带 AnyKernel3 后缀的压缩包即可
|
|
||||||
|
|
||||||
> [!Note]
|
|
||||||
> - 内核版本只需要填写前两位即可,如 5.10,5.15,6.1,6.6
|
|
||||||
> - 处理器代号请自行搜索,一般为全英文不带数字的代号
|
|
||||||
> - 分支和配置文件请自行到一加内核开源地址进行填写
|
|
||||||
|
|
||||||
## 特点
|
|
||||||
|
|
||||||
1. 基于内核的 `su` 和 root 访问管理
|
|
||||||
2. 基于 5ec1cff 的 [Magic Mount](https://github.com/5ec1cff/KernelSU) 的模块系统
|
|
||||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html):将 root 权限锁在笼子里
|
|
||||||
4. 恢复对非 GKI 2.0 内核的支持
|
|
||||||
5. 更多自定义功能
|
|
||||||
6. 对 KPM 内核模块的支持
|
|
||||||
|
|
||||||
## 许可证
|
|
||||||
|
|
||||||
- `kernel` 目录下的文件是 [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)。
|
|
||||||
- 除 `kernel` 目录外,所有其他部分均为 [GPL-3.0 或更高版本](https://www.gnu.org/licenses/gpl-3.0.html)。
|
|
||||||
|
|
||||||
## 赞助名单
|
|
||||||
|
|
||||||
- [Ktouls](https://github.com/Ktouls) 非常感谢你给我带来的支持
|
|
||||||
- [zaoqi123](https://github.com/zaoqi123) 请我喝奶茶也不错
|
|
||||||
- [wswzgdg](https://github.com/wswzgdg) 非常感谢对此项目的支持
|
|
||||||
- [yspbwx2010](https://github.com/yspbwx2010) 非常感谢
|
|
||||||
- [DARKWWEE](https://github.com/DARKWWEE) 感谢老哥的 100 USDT
|
|
||||||
|
|
||||||
如果以上名单没有你的名称,我会及时更新,再次感谢大家的支持
|
|
||||||
|
|
||||||
## 贡献
|
|
||||||
|
|
||||||
- [KernelSU](https://github.com/tiann/KernelSU):原始项目
|
|
||||||
- [MKSU](https://github.com/5ec1cff/KernelSU):使用的项目
|
|
||||||
- [RKSU](https://github.com/rsuntk/KernelsU):使用该项目的 kernel 对非GKI设备重新进行支持
|
|
||||||
- [susfs4ksu](https://gitlab.com/simonpunk/susfs4ksu):使用的 susfs 文件系统
|
|
||||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/):KernelSU 的构想
|
|
||||||
- [Magisk](https://github.com/topjohnwu/Magisk):强大的 root 工具
|
|
||||||
- [genuine](https://github.com/brevent/genuine/):APK v2 签名验证
|
|
||||||
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技能
|
|
||||||
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch 是 APatch 实现内核模块的关键部分
|
|
||||||
|
|||||||
183
docs/SukiSU-mini.svg
Normal file
|
After Width: | Height: | Size: 185 KiB |
188
docs/SukiSU.svg
Normal file
|
After Width: | Height: | Size: 200 KiB |
97
docs/guide/how-to-integrate.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# Integrate
|
||||||
|
|
||||||
|
SukiSU can be integrated into both _GKI_ and _non-GKI_ kernels and has been backported to _4.14_.
|
||||||
|
|
||||||
|
<!-- It should be 3.4, but backslashxx's syscall manual hook cannot use in SukiSU-->
|
||||||
|
|
||||||
|
Some OEMs' customization could result in as much as 50% of kernel code being out-of-tree code and not from upstream Linux kernels or ACKs. Due to this, the custom nature of _non-GKI_ kernels resulted in significant kernel fragmentation, and we lacked a universal method for building them. Therefore, we cannot provide boot images of _non-GKI_ kernels.
|
||||||
|
|
||||||
|
Prerequisites: open source bootable kernel.
|
||||||
|
|
||||||
|
### Hook method
|
||||||
|
|
||||||
|
1. **KPROBES hook:**
|
||||||
|
|
||||||
|
- Default hook method on GKI kernels.
|
||||||
|
- Requires `# CONFIG_KSU_MANUAL_HOOK is not set` & `CONFIG_KPROBES=y`
|
||||||
|
- Used for Loadable Kernel Module (LKM).
|
||||||
|
|
||||||
|
2. **Manual hook:**
|
||||||
|
|
||||||
|
<!-- - backslashxx's syscall manual hook: https://github.com/backslashxx/KernelSU/issues/5 (v1.5 version is not available at the moment, if you want to use it, please use v1.4 version, or standard KernelSU hooks)-->
|
||||||
|
|
||||||
|
- Requires `CONFIG_KSU_MANUAL_HOOK=y`
|
||||||
|
- Requires [`guide/how-to-integrate.md`](guide/how-to-integrate.md)
|
||||||
|
- Requires [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source)
|
||||||
|
|
||||||
|
3. **Tracepoint Hook:**
|
||||||
|
|
||||||
|
- Hook method introduced since SukiSU commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124)
|
||||||
|
- Requires `CONFIG_KSU_TRACEPOINT_HOOK=y`
|
||||||
|
- Requires [`guide/tracepoint-hook.md`](tracepoint-hook.md)
|
||||||
|
|
||||||
|
<!-- This part refer to [rsuntk/KernelSU](https://github.com/rsuntk/KernelSU). -->
|
||||||
|
|
||||||
|
If you're able to build a bootable kernel, there are two ways to integrate KernelSU into the kernel source code:
|
||||||
|
|
||||||
|
1. Automatically with `kprobe`
|
||||||
|
2. Manually
|
||||||
|
|
||||||
|
## Integrate with kprobe
|
||||||
|
|
||||||
|
Applicable:
|
||||||
|
|
||||||
|
- _GKI_ kernel
|
||||||
|
|
||||||
|
Not applicable:
|
||||||
|
|
||||||
|
- _non-GKI_ kernel
|
||||||
|
|
||||||
|
KernelSU uses kprobe to do kernel hooks. If kprobe runs well in your kernel, it's recommended to use it this way.
|
||||||
|
|
||||||
|
Please refer to this document [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#integrate-with-kprobe). Although it is titled “for _non-GKI_,” it only applies to _GKI_.
|
||||||
|
|
||||||
|
The execution command for the step that adds KernelSU to your kernel source tree is replaced with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manually modify the kernel source
|
||||||
|
|
||||||
|
Applicable:
|
||||||
|
|
||||||
|
- GKI kernel
|
||||||
|
- non-GKI kernel
|
||||||
|
|
||||||
|
Please refer to this document [https://github.com/~ (Integrate for non-GKI)](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source) and [https://github.com/~ (Build for GKI)](https://kernelsu.org/zh_CN/guide/how-to-build.html) to integrate manually, although first link is titled “for non-GKI,” it also applies to GKI. It can work on them both.
|
||||||
|
|
||||||
|
There is another way to integrate but still work in the process.
|
||||||
|
|
||||||
|
<!-- It is backslashxx's syscall manual hook, but it cannot be used now. -->
|
||||||
|
|
||||||
|
Run command for the step that adds KernelSU(SukiSU) to your kernel source tree is replaced with:
|
||||||
|
|
||||||
|
### GKI kernel
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||||
|
```
|
||||||
|
|
||||||
|
### non-GKI kernel
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||||
|
```
|
||||||
|
|
||||||
|
### GKI / non-GKI kernel with susfs (experiment)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-{{branch}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Branch:
|
||||||
|
|
||||||
|
- `main` (susfs-main)
|
||||||
|
- `test` (susfs-test)
|
||||||
|
- version (for example: susfs-1.5.7, you should check the [branches](https://github.com/SukiSU-Ultra/SukiSU-Ultra/branches))
|
||||||
34
docs/guide/installation.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Installation
|
||||||
|
|
||||||
|
You can go to [KernelSU Documentation - Installation](https://kernelsu.org/guide/installation.html) for a reference on how to install it, here are just additional instructions.
|
||||||
|
|
||||||
|
## Installation by loading the Loadable Kernel Module(LKM)
|
||||||
|
|
||||||
|
See [KernelSU Documentation - LKM Installation](https://kernelsu.org/guide/installation.html#lkm-installation)
|
||||||
|
|
||||||
|
Beginning with **Android™** (trademark meaning licensed Google Mobile Services) 12, devices shipping with kernel version 5.10 or higher must ship with the GKI kernel. You may be able to use LKM mode.
|
||||||
|
|
||||||
|
## Installation by installing the kernel
|
||||||
|
|
||||||
|
See [KernelSU Documentation - GKI mode Installation](https://kernelsu.org/guide/installation.html#gki-mode-installation)
|
||||||
|
|
||||||
|
We provide pre-built kernels for you to use:
|
||||||
|
|
||||||
|
- [ShirkNeko flavor kernel](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS) (add ZRAM compression algorithm patch, susfs, KPM. Works on many devices.)
|
||||||
|
- [MiRinFork flavored kernel](https://github.com/MiRinFork/GKI_SukiSU_SUSFS) (adds susfs, KPM. Closest kernel to GKI, works on most devices.)
|
||||||
|
|
||||||
|
Although some devices can be installed using LKM mode, they cannot be installed on the device by using the GKI kernel; therefore, the kernel needs to be modified manually to compile it. For example:
|
||||||
|
|
||||||
|
- OPPO(OnePlus, REALME)
|
||||||
|
- Meizu
|
||||||
|
|
||||||
|
Also, we provide pre-built kernels for your OnePlus device to use:
|
||||||
|
|
||||||
|
- [ShirkNeko/Action_OnePlus_MKSU_SUSFS](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS) (add ZRAM compression algorithm patch, susfs, KPM.)
|
||||||
|
|
||||||
|
Using the link above, Fork into GitHub Action, fill in the build parameters, compile, and finally flush in the zip with the AnyKernel3 suffix.
|
||||||
|
|
||||||
|
> [!Note]
|
||||||
|
>
|
||||||
|
> - You only need to fill in the first two parts of the version number, e.g. `5.10`, `6.1`...
|
||||||
|
> - Make sure you know the processor designation, kernel version, etc. before you use it.
|
||||||
239
docs/guide/tracepoint-hook.md
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
# Tracepoint Hook Integration
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Since commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124), SukiSU has introduced Tracepoint Hook
|
||||||
|
|
||||||
|
This Hook theoretically has lower performance overhead compared to Kprobes Hook, but is inferior to Manual Hook / Syscall Hook
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This tutorial references the syscall hook v1.4 version from [backslashxx/KernelSU#5](https://github.com/backslashxx/KernelSU/issues/5), as well as the original KernelSU's [Manual Hook](https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source)
|
||||||
|
|
||||||
|
## Guide
|
||||||
|
|
||||||
|
### execve Hook (`exec.c`)
|
||||||
|
|
||||||
|
Generally need to modify the `do_execve` and `compat_do_execve` methods in `fs/exec.c`
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/fs/exec.c
|
||||||
|
+++ b/fs/exec.c
|
||||||
|
@@ -78,6 +78,10 @@
|
||||||
|
#include <trace/hooks/sched.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
EXPORT_TRACEPOINT_SYMBOL_GPL(task_rename);
|
||||||
|
|
||||||
|
static int bprm_creds_from_file(struct linux_binprm *bprm);
|
||||||
|
@@ -2037,6 +2041,9 @@ static int do_execve(struct filename *filename,
|
||||||
|
{
|
||||||
|
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||||
|
struct user_arg_ptr envp = { .ptr.native = __envp };
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0);
|
||||||
|
+#endif
|
||||||
|
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -2064,6 +2071,9 @@ static int compat_do_execve(struct filename *filename,
|
||||||
|
.is_compat = true,
|
||||||
|
.ptr.compat = __envp,
|
||||||
|
};
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0); // 32-bit su and 32-on-64 support
|
||||||
|
+#endif
|
||||||
|
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### faccessat Hook (`open.c`)
|
||||||
|
|
||||||
|
Generally need to modify the `do_faccessat` method in `/fs/open.c`
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/fs/open.c
|
||||||
|
+++ b/fs/open.c
|
||||||
|
@@ -37,6 +37,10 @@
|
||||||
|
#include "internal.h"
|
||||||
|
#include <trace/hooks/syscall_check.h>
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
int do_truncate(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||||
|
loff_t length, unsigned int time_attrs, struct file *filp)
|
||||||
|
{
|
||||||
|
@@ -468,6 +472,9 @@ static long do_faccessat(int dfd, const char __user *filename, int mode, int fla
|
||||||
|
|
||||||
|
SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||||
|
{
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
|
||||||
|
+#endif
|
||||||
|
return do_faccessat(dfd, filename, mode, 0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If there's no `do_faccessat` method, you can find the `faccessat` SYSCALL definition (for kernels earlier than 4.17)
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/fs/open.c
|
||||||
|
+++ b/fs/open.c
|
||||||
|
@@ -31,6 +31,9 @@
|
||||||
|
#include <linux/ima.h>
|
||||||
|
#include <linux/dnotify.h>
|
||||||
|
#include <linux/compat.h>
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
@@ -369,6 +372,9 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||||
|
int res;
|
||||||
|
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
|
||||||
|
+#endif
|
||||||
|
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
|
||||||
|
return -EINVAL;
|
||||||
|
```
|
||||||
|
|
||||||
|
### sys_read Hook (`read_write.c`)
|
||||||
|
|
||||||
|
Need to modify the `sys_read` method in `fs/read_write.c` (4.19 and above)
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/fs/read_write.c
|
||||||
|
+++ b/fs/read_write.c
|
||||||
|
@@ -25,6 +25,10 @@
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
const struct file_operations generic_ro_fops = {
|
||||||
|
.llseek = generic_file_llseek,
|
||||||
|
.read_iter = generic_file_read_iter,
|
||||||
|
@@ -630,6 +634,9 @@ ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count)
|
||||||
|
|
||||||
|
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
|
||||||
|
{
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
|
||||||
|
+#endif
|
||||||
|
return ksys_read(fd, buf, count);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or the `read` SYSCALL definition (4.14 and below)
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/fs/read_write.c
|
||||||
|
+++ b/fs/read_write.c
|
||||||
|
@@ -25,6 +25,11 @@
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+
|
||||||
|
const struct file_operations generic_ro_fops = {
|
||||||
|
.llseek = generic_file_llseek,
|
||||||
|
.read_iter = generic_file_read_iter,
|
||||||
|
@@ -575,6 +580,9 @@ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
|
||||||
|
|
||||||
|
if (f.file) {
|
||||||
|
loff_t pos = file_pos_read(f.file);
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
|
||||||
|
+#endif
|
||||||
|
ret = vfs_read(f.file, buf, count, &pos);
|
||||||
|
if (ret >= 0)
|
||||||
|
file_pos_write(f.file, pos);
|
||||||
|
```
|
||||||
|
|
||||||
|
### fstatat Hook (`stat.c`)
|
||||||
|
|
||||||
|
Need to modify the `newfstatat` SYSCALL definition in `stat.c`
|
||||||
|
|
||||||
|
If 32-bit support is needed, also need to modify the `statat64` SYSCALL definition
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/fs/stat.c
|
||||||
|
+++ b/fs/stat.c
|
||||||
|
@@ -24,6 +24,10 @@
|
||||||
|
#include "internal.h"
|
||||||
|
#include "mount.h"
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* generic_fillattr - Fill in the basic attributes from the inode struct
|
||||||
|
* @mnt_userns: user namespace of the mount the inode was found from
|
||||||
|
@@ -408,6 +412,10 @@ SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
|
||||||
|
struct kstat stat;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
error = vfs_fstatat(dfd, filename, &stat, flag);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
@@ -559,6 +567,10 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
|
||||||
|
struct kstat stat;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag); /* 32-bit su support */
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
error = vfs_fstatat(dfd, filename, &stat, flag);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
```
|
||||||
|
|
||||||
|
### input Hook (`input.c`, for entering KSU built-in security mode)
|
||||||
|
|
||||||
|
Need to modify the `input_event` method in `drivers/input/input.c`, not `input_handle_event`
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/drivers/input/input.c
|
||||||
|
+++ b/drivers/input/input.c
|
||||||
|
@@ -26,6 +26,10 @@
|
||||||
|
#include "input-compat.h"
|
||||||
|
#include "input-poller.h"
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
|
||||||
|
MODULE_DESCRIPTION("Input core");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
@@ -451,6 +455,10 @@ void input_event(struct input_dev *dev,
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_input_hook(&type, &code, &value);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
if (is_event_supported(type, dev->evbit, EV_MAX)) {
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dev->event_lock, flags);
|
||||||
|
```
|
||||||
153
docs/ja/README.md
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
# SukiSU Ultra
|
||||||
|
<img align='right' src='SukiSU-mini.svg' width='220px' alt="sukisu logo">
|
||||||
|
|
||||||
|
|
||||||
|
[English](../README.md) | [简体中文](../zh/README.md) | **日本語** | [Türkçe](../tr/README.md)
|
||||||
|
|
||||||
|
[KernelSU](https://github.com/tiann/KernelSU) をベースとした Android デバイスの root ソリューション
|
||||||
|
|
||||||
|
**試験中なビルドです!自己責任で使用してください!**<br>
|
||||||
|
このソリューションは [KernelSU](https://github.com/tiann/KernelSU) に基づいていますが、試験中なビルドです。
|
||||||
|
|
||||||
|
> これは非公式なフォークです。すべての権利は [@tiann](https://github.com/tiann) に帰属します。
|
||||||
|
>
|
||||||
|
> ただし、将来的には KSU とは別に管理されるブランチとなる予定です。
|
||||||
|
|
||||||
|
## 追加する方法
|
||||||
|
|
||||||
|
メインブランチを使用 (非 GKI のデバイスのビルドは非対応) (susfs を手動で統合が必要)
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||||
|
```
|
||||||
|
|
||||||
|
非 GKI のデバイスに対応するブランチを使用 (susfs を手動で統合が必要)
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||||
|
```
|
||||||
|
|
||||||
|
## 統合された susfs の使い方
|
||||||
|
|
||||||
|
1. susfs-main または他の susfs-\* ブランチを直接で使用、susfs の統合は不要 (非 GKI デバイスのビルドに対応)
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-main
|
||||||
|
```
|
||||||
|
|
||||||
|
## フックの方式
|
||||||
|
|
||||||
|
- この方式は (https://github.com/rsuntk/KernelSU) のフック方式を参照してください。
|
||||||
|
|
||||||
|
1. **KPROBES でフック:**
|
||||||
|
|
||||||
|
- 読み込み可能なカーネルモジュールの場合 (LKM)
|
||||||
|
- GKI カーネルのデフォルトとなるフック方式
|
||||||
|
- `CONFIG_KPROBES=y` が必要です
|
||||||
|
|
||||||
|
2. **手動でフック:**
|
||||||
|
- 標準の KernelSU フック: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
||||||
|
- backslashxx syscall フック: https://github.com/backslashxx/KernelSU/issues/5
|
||||||
|
- 非 GKI カーネル用のデフォルトフック方式
|
||||||
|
- `CONFIG_KSU_MANUAL_HOOK=y` が必要です
|
||||||
|
|
||||||
|
## KPM に対応
|
||||||
|
|
||||||
|
- KernelPatch に基づいて重複した KSU の機能を削除、KPM の対応を維持させています。
|
||||||
|
- KPM 機能の整合性を確保するために、APatch の互換機能を更に向上させる予定です。
|
||||||
|
|
||||||
|
オープンソースアドレス: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
|
||||||
|
|
||||||
|
KPM テンプレートのアドレス: https://github.com/udochina/KPM-Build-Anywhere
|
||||||
|
|
||||||
|
> [!Note]
|
||||||
|
>
|
||||||
|
> 1. `CONFIG_KPM=y` が必要です。
|
||||||
|
> 2. 非 GKI デバイスには `CONFIG_KALLSYMS=y` と `CONFIG_KALLSYMS_ALL=y` も必要です。
|
||||||
|
> 3. いくつかのカーネル `4.19` およびそれ以降のソースコードでは、 `4.19` からバックポートされた `set_memory.h` ヘッダーファイルも必要です。
|
||||||
|
|
||||||
|
## ROOT を保持した状態でのシステムアップデートの方法
|
||||||
|
|
||||||
|
- 始めに OTA 後すぐに再起動せずにマネージャーのカーネルのフラッシュ、パッチのインターフェースを開いて`GKI/非 GKI のインストール`を見つけます。フラッシュする AnyKernel3 の zip ファイルを選択し、フラッシュする実行中のスロットと逆のスロットを選択後に再起動をして GKI モードの更新が保持できます (この方法はすべての非 GKI のデバイスが対応している訳ではないので、自分でお試しください。これは非 GKI のデバイスで TWRP を使用する最も安全な方法です)。
|
||||||
|
- または LKM モードを使用して未使用のスロットにインストールします (OTA 後)。
|
||||||
|
|
||||||
|
## 互換性の状態
|
||||||
|
|
||||||
|
- KernelSU (v1.0.0 より前) は Android GKI 2.0 のデバイス (カーネル 5.10 以降) を公式に対応しています。
|
||||||
|
|
||||||
|
- 古いカーネル (4.4 以降) も互換性がありますが、カーネルを手動で再ビルドする必要があります。
|
||||||
|
|
||||||
|
- KernelSU は追加のリバースポートを通じて 3.x カーネル (3.4-3.18) で対応可能です。
|
||||||
|
|
||||||
|
- 現在 `arm64-v8a`, `armeabi-v7a (bare)` および一部の `X86_64` に対応しています。
|
||||||
|
|
||||||
|
## その他のリンク
|
||||||
|
|
||||||
|
**マネージャーの翻訳を行う場合** https://crowdin.com/project/SukiSU-Ultra
|
||||||
|
|
||||||
|
- [その他パッチ済み GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS) ZRAM パッチ、KPM、susfs が含まれています...
|
||||||
|
- [パッチの少ない GKI](https://github.com/MiRinFork/GKI_SukiSU_SUSFS/releases) susfs のみ
|
||||||
|
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
||||||
|
|
||||||
|
## 使い方
|
||||||
|
|
||||||
|
### Universal GKI
|
||||||
|
|
||||||
|
**すべて**参照してください https://kernelsu.org/ja_JP/guide/installation.html
|
||||||
|
|
||||||
|
> [!Note]
|
||||||
|
>
|
||||||
|
> 1. Xiaomi、Redmi、Samsung などの GKI 2.0 を搭載したデバイス向け (Meizu、OnePlus、Zenith、Oppo などカーネルが変更されているメーカーを除く)
|
||||||
|
> 2. GKI のビルドは[その他のリンク](#その他のリンク)から入手できます。デバイスのカーネルバージョンを確認してください。ダウンロード後に TWRP またはカーネルフラッシュツールを使用して AnyKernel3 の接頭辞を持つ zip ファイルをフラッシュしてください。Pixel のユーザーは、パッチの少ない GKI を使用する必要があります。
|
||||||
|
> 3. 接頭辞のない .zip アーカイブは圧縮されていません。.gz の接頭辞は Tenguet モデルで使用される圧縮になります。
|
||||||
|
|
||||||
|
### OnePlus
|
||||||
|
|
||||||
|
1. `その他のリンク`の項目に記載されているリンクを開き、デバイス情報を使用してカスタマイズされたカーネルをビルドし、AnyKernel3 の接頭辞を持つ .zip ファイルをフラッシュします。
|
||||||
|
|
||||||
|
> [!Note]
|
||||||
|
>
|
||||||
|
> - 5.10、5.15、6.1、6.6 などのカーネルバージョンの最初の 2 文字のみを入力する必要があります。
|
||||||
|
> - SoC のコードネームは自分で検索してください。通常は、数字がなく英語表記のみです。
|
||||||
|
> - ブランチと構成ファイルは、OnePlus オープンソースカーネルリポジトリから見つけることができます。
|
||||||
|
|
||||||
|
## 機能
|
||||||
|
|
||||||
|
1. カーネルベースな `su` および root アクセスの管理。
|
||||||
|
2. [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) モジュールシステムではなく、 5ec1cff 氏の [Magic Mount](https://github.com/5ec1cff/KernelSU) に基づいています。
|
||||||
|
3. [アプリプロファイル](https://kernelsu.org/guide/app-profile.html): root 権限をケージ内にロックします。
|
||||||
|
4. 非 GKI / GKI 1.0 の対応を復活
|
||||||
|
5. その他のカスタマイズ
|
||||||
|
6. KPM カーネルモジュールに対応
|
||||||
|
|
||||||
|
## トラブルシューティング
|
||||||
|
|
||||||
|
1. KernelSU Manager のアンインストールが停止してしまう → com.sony.playmemories.mobile のアプリをアンインストールしてください。
|
||||||
|
|
||||||
|
## ライセンス
|
||||||
|
|
||||||
|
- 「kernel」のディレクトリ内のファイルは [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) のライセンスに基づいています。
|
||||||
|
- アニメキャラクター画像とスタンプを含むこれらのファイルの `ic_launcher(?!.*alt.*).*` は[怡子曰曰](https://space.bilibili.com/10545509)によって著作権保護されており、画像の Brand Intellectual Property は[明风 OuO](https://space.bilibili.com/274939213)によって所有され、ベクター化は @MiRinChan によって行われています。 これらのファイルを使用する前に、[Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt)を遵守することに加えて、アートコンテンツを使用するために前の 2 人の作者から許可を得る必要があります。
|
||||||
|
- 上記のファイルまたはディレクトリを除き、その他のすべての部分は[GPL-3.0 以降](https://www.gnu.org/licenses/gpl-3.0.html)です。
|
||||||
|
|
||||||
|
## スポンサーシップの一覧
|
||||||
|
|
||||||
|
- [Ktouls](https://github.com/Ktouls) 応援してくれてありがとう
|
||||||
|
- [zaoqi123](https://github.com/zaoqi123) ミルクティーを買ってあげるのも良い考えですね
|
||||||
|
- [wswzgdg](https://github.com/wswzgdg) このプロジェクトにご支援いただき、ありがとうございます
|
||||||
|
- [yspbwx2010](https://github.com/yspbwx2010) ありがとうございます
|
||||||
|
- [DARKWWEE](https://github.com/DARKWWEE) ラオスから 100 USDT の支援に感謝します
|
||||||
|
- [Saksham Singla](https://github.com/TypeFlu) ウェブサイトの提供とメンテナンス
|
||||||
|
- [OukaroMF](https://github.com/OukaroMF) ウェブサイトのドメインと寄付
|
||||||
|
|
||||||
|
## 貢献者
|
||||||
|
|
||||||
|
- [KernelSU](https://github.com/tiann/KernelSU): オリジナルのプロジェクト
|
||||||
|
- [MKSU](https://github.com/5ec1cff/KernelSU): 使用しているプロジェクト
|
||||||
|
- [RKSU](https://github.com/rsuntk/KernelsU): このプロジェクトのカーネルを使用した非 GKI デバイスのサポートの再導入
|
||||||
|
- [susfs](https://gitlab.com/simonpunk/susfs4ksu): susfs ファイルシステムの使用
|
||||||
|
- [KernelSU](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU の概念化
|
||||||
|
- [Magisk](https://github.com/topjohnwu/Magisk): パワフルな root ユーティリティ
|
||||||
|
- [genuine](https://github.com/brevent/genuine/): APK v2 署名認証
|
||||||
|
- [Diamorphine](https://github.com/m0nad/Diamorphine): いくつかの root キットユーティリティ
|
||||||
|
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch はカーネルモジュールの APatch 実装の重要な部分での活用
|
||||||
183
docs/ja/SukiSU-mini.svg
Normal file
|
After Width: | Height: | Size: 185 KiB |
151
docs/tr/README.md
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
# SukiSU Ultra
|
||||||
|
<img align='right' src='SukiSU-mini.svg' width='250px' alt="sukisu logo">
|
||||||
|
|
||||||
|
|
||||||
|
[English](../README.md) | [简体中文](../zh/README.md) | [日本語](../ja/README.md) | **Türkçe**
|
||||||
|
|
||||||
|
[KernelSU](https://github.com/tiann/KernelSU) tabanlı Android cihaz root çözümü
|
||||||
|
|
||||||
|
**Deneysel! Kullanım riski size aittir!**
|
||||||
|
|
||||||
|
> Bu resmi olmayan bir daldır, tüm hakları saklıdır [@tiann](https://github.com/tiann)
|
||||||
|
>
|
||||||
|
> Ancak, gelecekte ayrı bir KSU dalı olarak devam edeceğiz
|
||||||
|
|
||||||
|
## Nasıl Eklenir
|
||||||
|
|
||||||
|
Çekirdek kaynak kodunun kök dizininde aşağıdaki komutları çalıştırın:
|
||||||
|
|
||||||
|
Ana dalı kullanın (GKI olmayan cihazlar için desteklenmez)
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||||
|
```
|
||||||
|
|
||||||
|
GKI olmayan cihazları destekleyen dalı kullanın
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||||
|
```
|
||||||
|
|
||||||
|
## susfs Nasıl Entegre Edilir
|
||||||
|
|
||||||
|
1. Doğrudan susfs-main veya susfs-\* dalını kullanın, susfs entegrasyonuna gerek yok
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-main
|
||||||
|
```
|
||||||
|
|
||||||
|
## Kanca Yöntemleri
|
||||||
|
|
||||||
|
- Bu bölüm [rsuntk\'nin kanca yöntemlerinden](https://github.com/rsuntk/KernelSU) alıntılanmıştır
|
||||||
|
|
||||||
|
1. **KPROBES Kancası:**
|
||||||
|
|
||||||
|
- Yüklenebilir çekirdek modülleri (LKM) için kullanılır
|
||||||
|
- GKI 2.0 çekirdeğinin varsayılan kanca yöntemi
|
||||||
|
- `CONFIG_KPROBES=y` gerektirir
|
||||||
|
|
||||||
|
2. **Manuel Kanca:**
|
||||||
|
- Standart KernelSU kancası: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
||||||
|
- backslashxx\'nin syscall manuel kancası: https://github.com/backslashxx/KernelSU/issues/5
|
||||||
|
- GKI olmayan çekirdeğin varsayılan kanca yöntemi
|
||||||
|
- `CONFIG_KSU_MANUAL_HOOK=y` gerektirir
|
||||||
|
|
||||||
|
## KPM Desteği
|
||||||
|
|
||||||
|
- KernelPatch tabanlı olarak KSU ile çakışan işlevleri kaldırdık ve yalnızca KPM desteğini koruduk
|
||||||
|
- APatch ile daha fazla uyumlu fonksiyon ekleyerek KPM işlevlerinin bütünlüğünü sağlayacağız
|
||||||
|
|
||||||
|
Kaynak kodu: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
|
||||||
|
|
||||||
|
KPM şablonu: https://github.com/udochina/KPM-Build-Anywhere
|
||||||
|
|
||||||
|
> [!Note]
|
||||||
|
>
|
||||||
|
> 1. `CONFIG_KPM=y` gerektirir
|
||||||
|
> 2. GKI olmayan cihazlar ayrıca `CONFIG_KALLSYMS=y` ve `CONFIG_KALLSYMS_ALL=y` gerektirir
|
||||||
|
> 3. Bazı çekirdek `4.19` altı kaynak kodları, `4.19`dan geri taşınan başlık dosyası `set_memory.h` gerektirir
|
||||||
|
|
||||||
|
## Sistem Güncellemesini Yaparak ROOT\'u Koruma
|
||||||
|
|
||||||
|
- OTA\'dan sonra hemen yeniden başlatmayın, yöneticiye girin ve çekirdek yazma/onarma arayüzüne gidin, `GKI/non_GKI yükleme` seçeneğini bulun ve Anykernel3 çekirdek sıkıştırma dosyasını seçin, şu anda sistemin çalıştığı yuva ile zıt yuvaya yazın ve yeniden başlatın, böylece GKI modu güncellemesini koruyabilirsiniz (şu anda tüm GKI olmayan cihazlar bu yöntemi desteklemiyor, lütfen kendiniz deneyin. GKI olmayan cihazlar için TWRP kullanmak en güvenlidir)
|
||||||
|
- Veya kullanılmayan yuvaya LKM modunu kullanarak yükleyin (OTA\'dan sonra)
|
||||||
|
|
||||||
|
## Uyumluluk Durumu
|
||||||
|
|
||||||
|
- KernelSU (v1.0.0 öncesi sürümler) resmi olarak Android GKI 2.0 cihazlarını destekler (çekirdek 5.10+)
|
||||||
|
|
||||||
|
- Eski çekirdekler (4.4+) de uyumludur, ancak çekirdeği manuel olarak oluşturmanız gerekir
|
||||||
|
|
||||||
|
- Daha fazla geri taşımayla KernelSU, 3.x çekirdeğini (3.4-3.18) destekleyebilir
|
||||||
|
|
||||||
|
- Şu anda `arm64-v8a`, `armeabi-v7a (bare)` ve bazı `X86_64` desteklenmektedir
|
||||||
|
|
||||||
|
## Daha Fazla Bağlantı
|
||||||
|
|
||||||
|
SukiSU ve susfs tabanlı derlenen projeler
|
||||||
|
|
||||||
|
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
||||||
|
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
||||||
|
|
||||||
|
## Kullanım Yöntemi
|
||||||
|
|
||||||
|
### Evrensel GKI
|
||||||
|
|
||||||
|
Lütfen **tümünü** https://kernelsu.org/zh_CN/guide/installation.html adresinden inceleyin
|
||||||
|
|
||||||
|
> [!Note]
|
||||||
|
>
|
||||||
|
> 1. Xiaomi, Redmi, Samsung gibi GKI 2.0 cihazlar için uygundur (Meizu, OnePlus, Realme ve Oppo gibi değiştirilmiş çekirdekli üreticiler hariç)
|
||||||
|
> 2. [Daha fazla bağlantı](#daha-fazla-bağlantı) bölümündeki GKI tabanlı projeleri bulun. Cihaz çekirdek sürümünü bulun. Ardından indirin ve TWRP veya çekirdek yazma aracı kullanarak AnyKernel3 soneki olan sıkıştırılmış paketi yazın
|
||||||
|
> 3. Genellikle sonek olmayan .zip sıkıştırılmış paketler sıkıştırılmamıştır, gz soneki olanlar ise Dimensity modelleri için kullanılan sıkıştırma yöntemidir
|
||||||
|
|
||||||
|
### OnePlus
|
||||||
|
|
||||||
|
1. Daha fazla bağlantı bölümündeki OnePlus projesini bulun ve kendiniz doldurun, ardından bulut derleme yapın ve AnyKernel3 soneki olan sıkıştırılmış paketi yazın
|
||||||
|
|
||||||
|
> [!Note]
|
||||||
|
>
|
||||||
|
> - Çekirdek sürümü için yalnızca ilk iki haneyi doldurmanız yeterlidir, örneğin 5.10, 5.15, 6.1, 6.6
|
||||||
|
> - İşlemci kod adını kendiniz arayın, genellikle tamamen İngilizce ve sayı içermeden oluşur
|
||||||
|
> - Dal ve yapılandırma dosyasını kendiniz OnePlus çekirdek kaynak kodundan doldurun
|
||||||
|
|
||||||
|
## Özellikler
|
||||||
|
|
||||||
|
1. Çekirdek tabanlı `su` ve root erişim yönetimi
|
||||||
|
2. 5ec1cff\'nin [Magic Mount](https://github.com/5ec1cff/KernelSU) tabanlı modül sistemi
|
||||||
|
3. [App Profile](https://kernelsu.org/guide/app-profile.html): root yetkilerini kafeste kilitleyin
|
||||||
|
4. GKI 2.0 olmayan çekirdekler için desteğin geri getirilmesi
|
||||||
|
5. Daha fazla özelleştirme özelliği
|
||||||
|
6. KPM çekirdek modülleri için destek
|
||||||
|
|
||||||
|
## Lisans
|
||||||
|
|
||||||
|
- `kernel` dizinindeki dosyalar [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) lisansı altındadır.
|
||||||
|
- Anime karakter ifadeleri içeren `ic_launcher(?!.*alt.*).*` dosyalarının görüntüleri [怡子曰曰](https://space.bilibili.com/10545509) tarafından telif hakkıyla korunmaktadır, görüntülerdeki Marka Fikri Mülkiyeti [明风 OuO](https://space.bilibili.com/274939213)'ye aittir ve vektörleştirme @MiRinChan tarafından yapılmıştır. Bu dosyaları kullanmadan önce, [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt) ile uyumlu olmanın yanı sıra, bu sanatsal içerikleri kullanmak için iki yazarın yetkilendirmesine de uymanız gerekir.
|
||||||
|
- Yukarıda belirtilen dosyalar veya dizinler hariç, diğer tüm parçalar [GPL-3.0 veya üzeri](https://www.gnu.org/licenses/gpl-3.0.html)'dir.
|
||||||
|
|
||||||
|
## Afdian Bağlantısı
|
||||||
|
|
||||||
|
- https://afdian.com/a/shirkneko
|
||||||
|
|
||||||
|
## Sponsor Listesi
|
||||||
|
|
||||||
|
- [Ktouls](https://github.com/Ktouls) Bana sağladığınız destek için çok teşekkür ederim
|
||||||
|
- [zaoqi123](https://github.com/zaoqi123) Bana sütlü çay ısmarlamanız da güzel
|
||||||
|
- [wswzgdg](https://github.com/wswzgdg) Bu projeye olan desteğiniz için çok teşekkür ederim
|
||||||
|
- [yspbwx2010](https://github.com/yspbwx2010) Çok teşekkür ederim
|
||||||
|
- [DARKWWEE](https://github.com/DARKWWEE) 100 USDT için teşekkürler
|
||||||
|
|
||||||
|
## Katkıda Bulunanlar
|
||||||
|
|
||||||
|
- [KernelSU](https://github.com/tiann/KernelSU): Orijinal proje
|
||||||
|
- [MKSU](https://github.com/5ec1cff/KernelSU): Kullanılan proje
|
||||||
|
- [RKSU](https://github.com/rsuntk/KernelsU): GKI olmayan cihazlar için destek sağlayan proje
|
||||||
|
- [susfs4ksu](https://gitlab.com/simonpunk/susfs4ksu): Kullanılan susfs dosya sistemi
|
||||||
|
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU fikri
|
||||||
|
- [Magisk](https://github.com/topjohnwu/Magisk): Güçlü root aracı
|
||||||
|
- [genuine](https://github.com/brevent/genuine/): APK v2 imza doğrulama
|
||||||
|
- [Diamorphine](https://github.com/m0nad/Diamorphine): Bazı rootkit becerileri
|
||||||
|
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch, APatch\'in çekirdek modüllerini uygulamak için kritik bir parçadır
|
||||||
183
docs/tr/SukiSU-mini.svg
Normal file
|
After Width: | Height: | Size: 185 KiB |
65
docs/zakomonochrome-128.svg
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
viewBox="0 0 128 128"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||||
|
sodipodi:docname="zakomonochrome-128.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#999999"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:zoom="2.6185048"
|
||||||
|
inkscape:cx="59.957881"
|
||||||
|
inkscape:cy="71.032903"
|
||||||
|
inkscape:window-width="1280"
|
||||||
|
inkscape:window-height="696"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<g
|
||||||
|
inkscape:label="图层 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;paint-order:fill markers stroke;fill-opacity:1"
|
||||||
|
id="rect1"
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
rx="7.772471"
|
||||||
|
ry="7.772471" />
|
||||||
|
<path
|
||||||
|
id="path101"
|
||||||
|
style="fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:4.27504;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||||
|
d="m 42.510282,81.796052 c 0,0 -7.224141,-5.638356 -10.043315,-9.338525 M 14.847106,81.97224 25.41902,71.576535 m 0.17619,-6.695549 2.819179,19.910444 M 11.675534,73.338532 38.281518,71.047931 M 43.567475,62.7666 34.40515,62.942814 M 34.22896,62.590425 33.524162,48.494537 m -18.500855,1.58577 17.972249,-1.409582 m -11.8053,-5.462154 0.352397,18.853251"
|
||||||
|
inkscape:label="杂" />
|
||||||
|
<path
|
||||||
|
id="path111"
|
||||||
|
style="fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:3.94824;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||||
|
d="M 55.912937,82.876745 79.671596,81.412163 M 59.330273,75.391135 74.952411,74.089291 m -9.43837,-14.157553 1.139102,14.645756 m -8.299247,-7.160159 16.273048,-1.464569 m 0.650926,8.136525 0.325472,-14.808482 m -0.162747,0.162739 -17.900363,0.976379 m 0,-0.162738 1.952774,14.645756 m 12.042061,-21.154974 1.464576,-6.346492 m 0,-0.650928 -12.042063,0.650928 m -0.650918,6.509218 0.325459,-8.787441"
|
||||||
|
inkscape:label="鱼" />
|
||||||
|
<path
|
||||||
|
d="m 95.08569,51.121163 c -1.90515,0.116064 -3.64694,0.97349 -4.86738,2.391307 -1.34538,1.56738 -1.91476,3.733159 -1.59523,6.070852 0.40842,2.982962 2.1502,6.17135 5.13887,9.411078 0.63424,0.68546 1.08109,1.129773 1.98202,1.967071 1.58321,1.469144 3.01507,2.634638 4.9875,4.052454 0.70392,0.50905 2.09253,1.453525 2.61627,1.781734 l 0.15133,0.09594 0.22103,-0.140663 c 0.80481,-0.515755 2.23909,-1.504852 3.08956,-2.130057 3.21689,-2.364488 5.79232,-4.737902 7.70228,-7.100167 3.09676,-3.831409 4.4133,-7.562359 3.80549,-10.773058 -0.42043,-2.210414 -1.82588,-4.039057 -3.81992,-4.967887 -0.85767,-0.399664 -1.69132,-0.607312 -2.6355,-0.656431 -1.22285,-0.0647 -2.42648,0.178619 -3.57485,0.721182 -1.95561,0.922124 -3.58927,2.719503 -4.61752,5.081755 -0.072,0.165235 -0.1394,0.310355 -0.14895,0.319295 -0.0312,0.02902 -0.0648,-0.02679 -0.19458,-0.330457 -0.30752,-0.714476 -0.91055,-1.752718 -1.38382,-2.377871 -0.4853,-0.645282 -1.2661,-1.431214 -1.84749,-1.862143 -1.50155,-1.114153 -3.26013,-1.658924 -5.00914,-1.553996 z"
|
||||||
|
id="path1-4"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.00231605" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.9 KiB |
101
docs/zh/README.md
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# SukiSU Ultra
|
||||||
|
<img align='right' src='SukiSU-mini.svg' width='220px' alt="sukisu logo">
|
||||||
|
|
||||||
|
|
||||||
|
[English](../README.md) | **简体中文** | [日本語](../ja/README.md) | [Türkçe](../tr/README.md)
|
||||||
|
|
||||||
|
一个 Android 上基于内核的 root 方案,由 [`tiann/KernelSU`](https://github.com/tiann/KernelSU) 分叉而来,添加了一些有趣的变更。
|
||||||
|
|
||||||
|
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||||
|
[](https://t.me/Sukiksu)
|
||||||
|
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||||
|
[](/LICENSE)
|
||||||
|
|
||||||
|
## 特性
|
||||||
|
|
||||||
|
1. 基于内核的 `su` 和权限管理。
|
||||||
|
2. 基于 [Magic Mount](https://github.com/5ec1cff/KernelSU) 的模块系统。
|
||||||
|
3. [App Profile](https://kernelsu.org/zh_CN/guide/app-profile.html): 把 Root 权限关进笼子里。
|
||||||
|
4. 支持 non-GKI 与 GKI 1.0。
|
||||||
|
5. KPM 支持
|
||||||
|
6. 可调整管理器外观,可自定义 susfs 配置。
|
||||||
|
|
||||||
|
## 兼容状态
|
||||||
|
|
||||||
|
- KernelSU 官方支持 GKI 2.0 的设备(内核版本 5.10 以上)。
|
||||||
|
|
||||||
|
- 旧内核也是兼容的(最低 4.14+),不过需要自己编译内核。
|
||||||
|
|
||||||
|
- 通过更多的反向移植,KernelSU 可以支持 3.x 内核(3.4-3.18)。
|
||||||
|
|
||||||
|
- 目前支持架构 : `arm64-v8a`、`armeabi-v7a (bare)`、`X86_64`。
|
||||||
|
|
||||||
|
## 安装指导
|
||||||
|
|
||||||
|
查看 [`guide/installation.md`](guide/installation.md)
|
||||||
|
|
||||||
|
## 集成指导
|
||||||
|
|
||||||
|
查看 [`guide/how-to-integrate.md`](guide/how-to-integrate.md)
|
||||||
|
|
||||||
|
## 参与翻译
|
||||||
|
|
||||||
|
要将 SukiSU 翻译成您的语言,或完善现有的翻译,请使用 [Crowdin](https://crowdin.com/project/SukiSU-Ultra).
|
||||||
|
|
||||||
|
## KPM 支持
|
||||||
|
|
||||||
|
- 基于 KernelPatch 开发,移除了与 KernelSU 重复的功能。
|
||||||
|
- 正在进行(WIP):通过集成附加功能来扩展 APatch 兼容性,以确保跨不同实现的兼容性。
|
||||||
|
|
||||||
|
**开源仓库**: [https://github.com/ShirkNeko/SukiSU_KernelPatch_patch](https://github.com/ShirkNeko/SukiSU_KernelPatch_patch)
|
||||||
|
|
||||||
|
**KPM 模板**: [https://github.com/udochina/KPM-Build-Anywhere](https://github.com/udochina/KPM-Build-Anywhere)
|
||||||
|
|
||||||
|
> [!Note]
|
||||||
|
>
|
||||||
|
> 1. 需要 `CONFIG_KPM=y`
|
||||||
|
> 2. Non-GKI 设备需要 `CONFIG_KALLSYMS=y` and `CONFIG_KALLSYMS_ALL=y`
|
||||||
|
> 3. 对于低于 `4.19` 的内核,需要从 `4.19` 的 `set_memory.h` 进行反向移植。
|
||||||
|
|
||||||
|
## 故障排除
|
||||||
|
|
||||||
|
1. 卸载管理器后系统卡住?
|
||||||
|
卸载 _com.sony.playmemories.mobile_
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
- 目录 `kernel` 下所有文件为 [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)。
|
||||||
|
- 有动漫人物图片表情包的这些文件 `ic_launcher(?!.*alt.*).*` 的图像版权为[怡子曰曰](https://space.bilibili.com/10545509)所有,图像中的知识产权由[明风 OuO](https://space.bilibili.com/274939213)所有,矢量化由 @MiRinChan 完成,在使用这些文件之前,除了必须遵守 [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt) 以外,还需要遵守向前两者索要使用这些艺术内容的授权。
|
||||||
|
- 除上述文件及目录的其他部分均为 [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html)。
|
||||||
|
|
||||||
|
## 赞助
|
||||||
|
|
||||||
|
- [ShirkNeko](https://afdian.com/a/shirkneko) (SukiSU 主要维护者)
|
||||||
|
- [weishu](https://github.com/sponsors/tiann) (KernelSU 作者)
|
||||||
|
|
||||||
|
## ShirkNeko 的赞助列表
|
||||||
|
|
||||||
|
- [Ktouls](https://github.com/Ktouls) 非常感谢你给我带来的支持
|
||||||
|
- [zaoqi123](https://github.com/zaoqi123) 请我喝奶茶也不错
|
||||||
|
- [wswzgdg](https://github.com/wswzgdg) 非常感谢对此项目的支持
|
||||||
|
- [yspbwx2010](https://github.com/yspbwx2010) 非常感谢
|
||||||
|
- [DARKWWEE](https://github.com/DARKWWEE) 感谢老哥的 100 USDT
|
||||||
|
- [Saksham Singla](https://github.com/TypeFlu) 网站的提供以及维护
|
||||||
|
- [OukaroMF](https://github.com/OukaroMF) 网站域名捐赠
|
||||||
|
|
||||||
|
## 鸣谢
|
||||||
|
|
||||||
|
- [KernelSU](https://github.com/tiann/KernelSU): 上游
|
||||||
|
- [MKSU](https://github.com/5ec1cff/KernelSU): 魔法坐骑支持
|
||||||
|
- [RKSU](https://github.com/rsuntk/KernelsU): non-GKI 支持
|
||||||
|
- [susfs](https://gitlab.com/simonpunk/susfs4ksu): 隐藏内核补丁以及用户空间模组的 KernelSU 附件
|
||||||
|
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch 是内核模块 APatch 实现的关键部分
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>KernelSU 的鸣谢</summary>
|
||||||
|
|
||||||
|
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/):KernelSU 的灵感。
|
||||||
|
- [Magisk](https://github.com/topjohnwu/Magisk):强大的 root 工具箱。
|
||||||
|
- [genuine](https://github.com/brevent/genuine/):apk v2 签名验证。
|
||||||
|
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技巧。
|
||||||
|
</details>
|
||||||
183
docs/zh/SukiSU-mini.svg
Normal file
|
After Width: | Height: | Size: 185 KiB |
188
docs/zh/SukiSU.svg
Normal file
|
After Width: | Height: | Size: 200 KiB |
97
docs/zh/guide/how-to-integrate.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# 集成指导
|
||||||
|
|
||||||
|
SukiSU 可以集成到 GKI 和 non-GKI 内核中,并且已反向移植到 4.14 版本。
|
||||||
|
|
||||||
|
<!-- 应该是 3.4 版本,但 backslashxx 的 syscall manual hook 无法在 SukiSU 中使用-->
|
||||||
|
|
||||||
|
有些 OEM 定制可能导致多达 50% 的内核代码超出内核树代码,而非来自上游 Linux 内核或 ACK。因此,non-GKI 内核的定制特性导致了严重的内核碎片化,而且我们缺乏构建它们的通用方法。因此,我们无法提供 non-GKI 内核的启动映像。
|
||||||
|
|
||||||
|
前提条件:开源的、可启动的内核。
|
||||||
|
|
||||||
|
## Hook 方法
|
||||||
|
|
||||||
|
1. **KPROBES hook:**
|
||||||
|
|
||||||
|
- GKI kernels 的默认 hook 方法。
|
||||||
|
- 需要 `# CONFIG_KSU_MANUAL_HOOK is not set`(未设定) & `CONFIG_KPROBES=y`
|
||||||
|
- 用作可加载的内核模块 (LKM).
|
||||||
|
|
||||||
|
2. **Manual hook:**
|
||||||
|
|
||||||
|
<!-- - backslashxx's syscall manual hook: https://github.com/backslashxx/KernelSU/issues/5 (v1.5 version is not available at the moment, if you want to use it, please use v1.4 version, or standard KernelSU hooks)-->
|
||||||
|
|
||||||
|
- 需要 `CONFIG_KSU_MANUAL_HOOK=y`
|
||||||
|
- 需要 [`guide/how-to-integrate.md`](how-to-integrate.md)
|
||||||
|
- 需要 [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source)
|
||||||
|
|
||||||
|
3. **Tracepoint Hook:**
|
||||||
|
|
||||||
|
- 自 SukiSU commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124) 引入的 hook 方法
|
||||||
|
- 需要 `CONFIG_KSU_TRACEPOINT_HOOK=y`
|
||||||
|
- 需要 [`guide/tracepoint-hook.md`](tracepoint-hook.md)
|
||||||
|
|
||||||
|
<!-- This part refer to [rsuntk/KernelSU](https://github.com/rsuntk/KernelSU). -->
|
||||||
|
|
||||||
|
如果您能够构建可启动内核,有两种方法可以将 KernelSU 集成到内核源代码中:
|
||||||
|
|
||||||
|
1. 使用 `kprobe` 自动集成
|
||||||
|
2. 手动集成
|
||||||
|
|
||||||
|
## 与 kprobe 集成
|
||||||
|
|
||||||
|
适用:
|
||||||
|
|
||||||
|
- GKI 内核
|
||||||
|
|
||||||
|
不适用:
|
||||||
|
|
||||||
|
- non-GKI 内核
|
||||||
|
|
||||||
|
KernelSU 使用 kprobe 机制来做内核的相关 hook,如果 _kprobe_ 可以在你编译的内核中正常运行,那么推荐用这个方法来集成。
|
||||||
|
|
||||||
|
请参阅此文档 [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#integrate-with-kprobe)。虽然标题为“适用于 non-GKI”,但仅适用于 GKI。
|
||||||
|
|
||||||
|
替换 KernelSU 添加到内核源代码树的步骤的执行命令为:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||||
|
```
|
||||||
|
|
||||||
|
## 手动修改内核源代码
|
||||||
|
|
||||||
|
适用:
|
||||||
|
|
||||||
|
- GKI 内核
|
||||||
|
- non-GKI 内核
|
||||||
|
|
||||||
|
请参考此文档 [https://github.com/~ (non-GKI 内核集成)](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source) 和 [https://github.com/~ (GKI 内核构建)](https://kernelsu.org/zh_CN/guide/how-to-build.html) 进行手动集成。虽然第一个链接的标题是“适用于 non-GKI”,但它也适用于 GKI。两者都可以正常工作。
|
||||||
|
|
||||||
|
还有另一种集成方法,但是仍在开发中。
|
||||||
|
|
||||||
|
<!-- 这是 backslashxx 的syscall manual hook,但目前无法使用。 -->
|
||||||
|
|
||||||
|
将 KernelSU(SukiSU)添加到内核源代码树的步骤的运行命令将被替换为:
|
||||||
|
|
||||||
|
### GKI 内核
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||||
|
```
|
||||||
|
|
||||||
|
### non-GKI 内核
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||||
|
```
|
||||||
|
|
||||||
|
### 带有 susfs 的 GKI / non-GKI 内核(实验)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-{{branch}}
|
||||||
|
```
|
||||||
|
|
||||||
|
分支:
|
||||||
|
|
||||||
|
- `main` (susfs-main)
|
||||||
|
- `test` (susfs-test)
|
||||||
|
- 版本号 (例如: susfs-1.5.7, 你需要在 [分支](https://github.com/SukiSU-Ultra/SukiSU-Ultra/branches) 里找到它)
|
||||||
34
docs/zh/guide/installation.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# 安装指导
|
||||||
|
|
||||||
|
您可以前往 [KernelSU 文档 - 安装](https://kernelsu.org/guide/installation.html) 获取有关如何安装的参考,这里只是额外的说明。
|
||||||
|
|
||||||
|
## 通过加载可加载内核模块 (LKM) 进行安装
|
||||||
|
|
||||||
|
请参阅 [KernelSU 文档 - LKM 安装](https://kernelsu.org/guide/installation.html#lkm-installation)
|
||||||
|
|
||||||
|
从 **Android™**(商标,意为获得 Google 移动服务的许可)12 开始,搭载内核版本 5.10 或更高版本的设备必须搭载 GKI 内核。因此你或许可以使用 LKM 模式。
|
||||||
|
|
||||||
|
## 通过安装内核进行安装
|
||||||
|
|
||||||
|
请参阅 [KernelSU 文档 - GKI 模式安装](https://kernelsu.org/guide/installation.html#gki-mode-installation)
|
||||||
|
|
||||||
|
我们提供预编译的内核供您使用:
|
||||||
|
|
||||||
|
- [ShirkNeko 内核](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)(添加了 ZRAM 压缩算法补丁、susfs 文件和 KPM 文件。适用于很多设备。)
|
||||||
|
- [MiRinFork 内核](https://github.com/MiRinFork/GKI_SukiSU_SUSFS)(添加了 susfs 文件和 KPM 文件。最接近 GKI 的内核,适用于大多数设备。)
|
||||||
|
|
||||||
|
虽然某些设备可以使用 LKM 模式安装,但无法使用 GKI 内核将其安装到设备上;因此,需要手动修改内核进行编译。例如:
|
||||||
|
|
||||||
|
- 欧珀(一加、真我)
|
||||||
|
- 魅族
|
||||||
|
|
||||||
|
此外,我们还为您的 OnePlus 设备提供预编译的内核:
|
||||||
|
|
||||||
|
- [ShirkNeko/Action_OnePlus_MKSU_SUSFS](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)(添加 ZRAM 压缩算法补丁、susfs 和 KPM。)
|
||||||
|
|
||||||
|
使用上面的链接,Fork 到 GitHub Action,填写构建参数,进行编译,最后将 zip 文件以 AnyKernel3 后缀上传到 GitHub Action。
|
||||||
|
|
||||||
|
> [!Note]
|
||||||
|
>
|
||||||
|
> - 使用时,您只需填写版本号的前两部分,例如 `5.10`、`6.1`...
|
||||||
|
> - 使用前请确保您了解处理器名称、内核版本等信息。
|
||||||
239
docs/zh/guide/tracepoint-hook.md
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
# Tracepoint Hook 集成
|
||||||
|
|
||||||
|
## 介绍
|
||||||
|
|
||||||
|
自 commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124) 起,SukiSU 引入了 Tracepoint Hook
|
||||||
|
|
||||||
|
该 Hook 理论上相比于 Kprobes Hook,性能开销更小,但次于 Manual Hook / Syscall Hook
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> 本教程参考了 [backslashxx/KernelSU#5](https://github.com/backslashxx/KernelSU/issues/5) 的 syscall hook v1.4 版本钩子,以及原版 KernelSU 的 [Manual Hook](https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source)
|
||||||
|
|
||||||
|
## Guide
|
||||||
|
|
||||||
|
### execve 钩子(`exec.c`)
|
||||||
|
|
||||||
|
一般需要修改 `fs/exec.c` 的 `do_execve` 和 `compat_do_execve` 方法
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/fs/exec.c
|
||||||
|
+++ b/fs/exec.c
|
||||||
|
@@ -78,6 +78,10 @@
|
||||||
|
#include <trace/hooks/sched.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
EXPORT_TRACEPOINT_SYMBOL_GPL(task_rename);
|
||||||
|
|
||||||
|
static int bprm_creds_from_file(struct linux_binprm *bprm);
|
||||||
|
@@ -2037,6 +2041,9 @@ static int do_execve(struct filename *filename,
|
||||||
|
{
|
||||||
|
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||||
|
struct user_arg_ptr envp = { .ptr.native = __envp };
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0);
|
||||||
|
+#endif
|
||||||
|
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -2064,6 +2071,9 @@ static int compat_do_execve(struct filename *filename,
|
||||||
|
.is_compat = true,
|
||||||
|
.ptr.compat = __envp,
|
||||||
|
};
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0)); // 32-bit su and 32-on-64 support
|
||||||
|
+#endif
|
||||||
|
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### faccessat 钩子 (`open.c`)
|
||||||
|
|
||||||
|
一般需要修改 `/fs/open.c` 的 `do_faccessat` 方法
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/fs/open.c
|
||||||
|
+++ b/fs/open.c
|
||||||
|
@@ -37,6 +37,10 @@
|
||||||
|
#include "internal.h"
|
||||||
|
#include <trace/hooks/syscall_check.h>
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
int do_truncate(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||||
|
loff_t length, unsigned int time_attrs, struct file *filp)
|
||||||
|
{
|
||||||
|
@@ -468,6 +472,9 @@ static long do_faccessat(int dfd, const char __user *filename, int mode, int fla
|
||||||
|
|
||||||
|
SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||||
|
{
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
|
||||||
|
+#endif
|
||||||
|
return do_faccessat(dfd, filename, mode, 0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
如果没有 `do_faccessat` 方法,可以找 `faccessat` 的 SYSCALL 定义(对于早于 4.17 的内核)
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/fs/open.c
|
||||||
|
+++ b/fs/open.c
|
||||||
|
@@ -31,6 +31,9 @@
|
||||||
|
#include <linux/ima.h>
|
||||||
|
#include <linux/dnotify.h>
|
||||||
|
#include <linux/compat.h>
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
@@ -369,6 +372,9 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||||
|
int res;
|
||||||
|
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
|
||||||
|
+#endif
|
||||||
|
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
|
||||||
|
return -EINVAL;
|
||||||
|
```
|
||||||
|
|
||||||
|
### sys_read 钩子 ( `read_write.c` )
|
||||||
|
|
||||||
|
需要修改 `fs/read_write.c` 的 `sys_read` 方法(4.19 及以上)
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/fs/read_write.c
|
||||||
|
+++ b/fs/read_write.c
|
||||||
|
@@ -25,6 +25,10 @@
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
const struct file_operations generic_ro_fops = {
|
||||||
|
.llseek = generic_file_llseek,
|
||||||
|
.read_iter = generic_file_read_iter,
|
||||||
|
@@ -630,6 +634,9 @@ ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count)
|
||||||
|
|
||||||
|
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
|
||||||
|
{
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
|
||||||
|
+#endif
|
||||||
|
return ksys_read(fd, buf, count);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
或者是 `read` 的 SYSCALL 定义(4.14 及以下)
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/fs/read_write.c
|
||||||
|
+++ b/fs/read_write.c
|
||||||
|
@@ -25,6 +25,11 @@
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+
|
||||||
|
const struct file_operations generic_ro_fops = {
|
||||||
|
.llseek = generic_file_llseek,
|
||||||
|
.read_iter = generic_file_read_iter,
|
||||||
|
@@ -575,6 +580,9 @@ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
|
||||||
|
|
||||||
|
if (f.file) {
|
||||||
|
loff_t pos = file_pos_read(f.file);
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
|
||||||
|
+#endif
|
||||||
|
ret = vfs_read(f.file, buf, count, &pos);
|
||||||
|
if (ret >= 0)
|
||||||
|
file_pos_write(f.file, pos);
|
||||||
|
```
|
||||||
|
|
||||||
|
### fstatat 钩子 ( `stat.c` )
|
||||||
|
|
||||||
|
需要修改 `stat.c` 的 `newfstatat` SYSCALL 定义
|
||||||
|
|
||||||
|
如果需要 32 位支持,还需要修改 `statat64` SYSCALL 定义
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/fs/stat.c
|
||||||
|
+++ b/fs/stat.c
|
||||||
|
@@ -24,6 +24,10 @@
|
||||||
|
#include "internal.h"
|
||||||
|
#include "mount.h"
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* generic_fillattr - Fill in the basic attributes from the inode struct
|
||||||
|
* @mnt_userns: user namespace of the mount the inode was found from
|
||||||
|
@@ -408,6 +412,10 @@ SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
|
||||||
|
struct kstat stat;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
error = vfs_fstatat(dfd, filename, &stat, flag);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
@@ -559,6 +567,10 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
|
||||||
|
struct kstat stat;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag); /* 32-bit su support */
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
error = vfs_fstatat(dfd, filename, &stat, flag);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
```
|
||||||
|
|
||||||
|
### input 钩子 (`input.c` ,用于进入KSU系的内置安全模式)
|
||||||
|
|
||||||
|
需要修改 `drivers/input/input.c` 的 `input_event` 方法,而不是 `input_handle_event`
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/drivers/input/input.c
|
||||||
|
+++ b/drivers/input/input.c
|
||||||
|
@@ -26,6 +26,10 @@
|
||||||
|
#include "input-compat.h"
|
||||||
|
#include "input-poller.h"
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+#include <../../drivers/kernelsu/ksu_trace.h>
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
|
||||||
|
MODULE_DESCRIPTION("Input core");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
@@ -451,6 +455,10 @@ void input_event(struct input_dev *dev,
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
|
+ trace_ksu_trace_input_hook(&type, &code, &value);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
if (is_event_supported(type, dev->evbit, EV_MAX)) {
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dev->event_lock, flags);
|
||||||
|
```
|
||||||
@@ -56,8 +56,8 @@ ColumnLimit: 80
|
|||||||
CommentPragmas: '^ IWYU pragma:'
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
#CompactNamespaces: false # Unknown to clang-format-4.0
|
#CompactNamespaces: false # Unknown to clang-format-4.0
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
ConstructorInitializerIndentWidth: 8
|
ConstructorInitializerIndentWidth: 4
|
||||||
ContinuationIndentWidth: 8
|
ContinuationIndentWidth: 4
|
||||||
Cpp11BracedListStyle: false
|
Cpp11BracedListStyle: false
|
||||||
DerivePointerAlignment: false
|
DerivePointerAlignment: false
|
||||||
DisableFormat: false
|
DisableFormat: false
|
||||||
@@ -501,7 +501,7 @@ IncludeCategories:
|
|||||||
IncludeIsMainRegex: '(Test)?$'
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
IndentCaseLabels: false
|
IndentCaseLabels: false
|
||||||
#IndentPPDirectives: None # Unknown to clang-format-5.0
|
#IndentPPDirectives: None # Unknown to clang-format-5.0
|
||||||
IndentWidth: 8
|
IndentWidth: 4
|
||||||
IndentWrappedFunctionNames: false
|
IndentWrappedFunctionNames: false
|
||||||
JavaScriptQuotes: Leave
|
JavaScriptQuotes: Leave
|
||||||
JavaScriptWrapImports: true
|
JavaScriptWrapImports: true
|
||||||
@@ -511,7 +511,7 @@ MacroBlockEnd: ''
|
|||||||
MaxEmptyLinesToKeep: 1
|
MaxEmptyLinesToKeep: 1
|
||||||
NamespaceIndentation: None
|
NamespaceIndentation: None
|
||||||
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
|
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
|
||||||
ObjCBlockIndentWidth: 8
|
ObjCBlockIndentWidth: 4
|
||||||
ObjCSpaceAfterProperty: true
|
ObjCSpaceAfterProperty: true
|
||||||
ObjCSpaceBeforeProtocolList: true
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
|
||||||
@@ -543,6 +543,6 @@ SpacesInCStyleCastParentheses: false
|
|||||||
SpacesInParentheses: false
|
SpacesInParentheses: false
|
||||||
SpacesInSquareBrackets: false
|
SpacesInSquareBrackets: false
|
||||||
Standard: Cpp03
|
Standard: Cpp03
|
||||||
TabWidth: 8
|
TabWidth: 4
|
||||||
UseTab: Always
|
UseTab: Never
|
||||||
...
|
...
|
||||||
|
|||||||
22
kernel/.gitignore
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
.cache/
|
||||||
|
.thinlto-cache/
|
||||||
|
compile_commands.json
|
||||||
|
*.ko
|
||||||
|
*.o
|
||||||
|
*.mod
|
||||||
|
*.lds
|
||||||
|
*.mod.o
|
||||||
|
.*.o*
|
||||||
|
.*.mod*
|
||||||
|
*.ko*
|
||||||
|
*.mod.c
|
||||||
|
*.symvers*
|
||||||
|
*.order
|
||||||
|
.*.ko.cmd
|
||||||
|
.tmp_versions/
|
||||||
|
libs/
|
||||||
|
obj/
|
||||||
|
|
||||||
|
CLAUDE.md
|
||||||
|
.ddk-version
|
||||||
|
.vscode/settings.json
|
||||||
11
kernel/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Linux",
|
||||||
|
"cStandard": "c11",
|
||||||
|
"intelliSenseMode": "gcc-arm64",
|
||||||
|
"compileCommands": "${workspaceFolder}/compile_commands.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
||||||
92
kernel/.vscode/generate_compdb.py
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from __future__ import print_function, division
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import fnmatch
|
||||||
|
import functools
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import multiprocessing
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
CMD_VAR_RE = re.compile(r'^\s*(?:saved)?cmd_(\S+)\s*:=\s*(.+)\s*$', re.MULTILINE)
|
||||||
|
SOURCE_VAR_RE = re.compile(r'^\s*source_(\S+)\s*:=\s*(.+)\s*$', re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
def print_progress_bar(progress):
|
||||||
|
progress_bar = '[' + '|' * int(50 * progress) + '-' * int(50 * (1.0 - progress)) + ']'
|
||||||
|
print('\r', progress_bar, "{0:.1%}".format(progress), end='\r', file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_cmd_file(out_dir, cmdfile_path):
|
||||||
|
with open(cmdfile_path, 'r') as cmdfile:
|
||||||
|
cmdfile_content = cmdfile.read()
|
||||||
|
|
||||||
|
commands = { match.group(1): match.group(2) for match in CMD_VAR_RE.finditer(cmdfile_content) }
|
||||||
|
sources = { match.group(1): match.group(2) for match in SOURCE_VAR_RE.finditer(cmdfile_content) }
|
||||||
|
|
||||||
|
return [{
|
||||||
|
'directory': out_dir,
|
||||||
|
'command': commands[o_file_name],
|
||||||
|
'file': source,
|
||||||
|
'output': o_file_name
|
||||||
|
} for o_file_name, source in sources.items()]
|
||||||
|
|
||||||
|
|
||||||
|
def gen_compile_commands(cmd_file_search_path, out_dir):
|
||||||
|
print("Building *.o.cmd file list...", file=sys.stderr)
|
||||||
|
|
||||||
|
out_dir = os.path.abspath(out_dir)
|
||||||
|
|
||||||
|
if not cmd_file_search_path:
|
||||||
|
cmd_file_search_path = [out_dir]
|
||||||
|
|
||||||
|
cmd_files = []
|
||||||
|
for search_path in cmd_file_search_path:
|
||||||
|
if (os.path.isdir(search_path)):
|
||||||
|
for cur_dir, subdir, files in os.walk(search_path):
|
||||||
|
cmd_files.extend(os.path.join(cur_dir, cmdfile_name) for cmdfile_name in fnmatch.filter(files, '*.o.cmd'))
|
||||||
|
else:
|
||||||
|
cmd_files.extend(search_path)
|
||||||
|
|
||||||
|
if not cmd_files:
|
||||||
|
print("No *.o.cmd files found in", ", ".join(cmd_file_search_path), file=sys.stderr)
|
||||||
|
return
|
||||||
|
|
||||||
|
print("Parsing *.o.cmd files...", file=sys.stderr)
|
||||||
|
|
||||||
|
n_processed = 0
|
||||||
|
print_progress_bar(0)
|
||||||
|
|
||||||
|
compdb = []
|
||||||
|
pool = multiprocessing.Pool()
|
||||||
|
try:
|
||||||
|
for compdb_chunk in pool.imap_unordered(functools.partial(parse_cmd_file, out_dir), cmd_files, chunksize=int(math.sqrt(len(cmd_files)))):
|
||||||
|
compdb.extend(compdb_chunk)
|
||||||
|
n_processed += 1
|
||||||
|
print_progress_bar(n_processed / len(cmd_files))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
pool.terminate()
|
||||||
|
pool.join()
|
||||||
|
|
||||||
|
print(file=sys.stderr)
|
||||||
|
print("Writing compile_commands.json...", file=sys.stderr)
|
||||||
|
|
||||||
|
with open('compile_commands.json', 'w') as compdb_file:
|
||||||
|
json.dump(compdb, compdb_file, indent=1)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
cmd_parser = argparse.ArgumentParser()
|
||||||
|
cmd_parser.add_argument('-O', '--out-dir', type=str, default=os.getcwd(), help="Build output directory")
|
||||||
|
cmd_parser.add_argument('cmd_file_search_path', nargs='*', help="*.cmd file search path")
|
||||||
|
gen_compile_commands(**vars(cmd_parser.parse_args()))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
16
kernel/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Generate compile_commands.json",
|
||||||
|
"type": "process",
|
||||||
|
"command": "python",
|
||||||
|
"args": [
|
||||||
|
"${workspaceRoot}/.vscode/generate_compdb.py"
|
||||||
|
],
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,35 +1,42 @@
|
|||||||
menu "KernelSU"
|
menu "KernelSU"
|
||||||
|
|
||||||
config KSU
|
config KSU
|
||||||
tristate "KernelSU function support"
|
tristate "KernelSU function support"
|
||||||
depends on OVERLAY_FS
|
default y
|
||||||
default y
|
help
|
||||||
help
|
Enable kernel-level root privileges on Android System.
|
||||||
Enable kernel-level root privileges on Android System.
|
To compile as a module, choose M here: the
|
||||||
To compile as a module, choose M here: the
|
module will be called kernelsu.
|
||||||
module will be called kernelsu.
|
|
||||||
|
|
||||||
config KSU_DEBUG
|
config KSU_DEBUG
|
||||||
bool "KernelSU debug mode"
|
bool "KernelSU debug mode"
|
||||||
depends on KSU
|
depends on KSU
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enable KernelSU debug mode.
|
Enable KernelSU debug mode.
|
||||||
|
|
||||||
config KSU_HOOK
|
config KSU_MANUAL_SU
|
||||||
bool "Enable KernelSU Hook"
|
bool "Use manual su"
|
||||||
default n
|
depends on KSU
|
||||||
help
|
default y
|
||||||
This option enables the KernelSU Hook feature. If enabled, it will
|
help
|
||||||
override the kernel version check and enable the hook functionality.
|
Use manual su and authorize the corresponding command line and application via prctl
|
||||||
|
|
||||||
config KPM
|
config KPM
|
||||||
bool "Enable SukiSU KPM"
|
bool "Enable SukiSU KPM"
|
||||||
default n
|
depends on KSU && 64BIT
|
||||||
help
|
default n
|
||||||
Enabling this option will activate the KPM feature of SukiSU.
|
help
|
||||||
This option is suitable for scenarios where you need to force KPM to be enabled.
|
Enabling this option will activate the KPM feature of SukiSU.
|
||||||
but it may affect system stability.
|
This option is suitable for scenarios where you need to force KPM to be enabled.
|
||||||
|
but it may affect system stability.
|
||||||
|
select KALLSYMS
|
||||||
|
select KALLSYMS_ALL
|
||||||
|
|
||||||
|
config KSU_MANUAL_HOOK
|
||||||
|
bool "Hook KernelSU manually"
|
||||||
|
depends on KSU != m
|
||||||
|
help
|
||||||
|
If enabled, Hook required KernelSU syscalls with manually-patched function.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|||||||
147
kernel/Makefile
@@ -1,12 +1,29 @@
|
|||||||
kernelsu-objs := ksu.o
|
kernelsu-objs := ksu.o
|
||||||
kernelsu-objs += allowlist.o
|
kernelsu-objs += allowlist.o
|
||||||
|
kernelsu-objs += app_profile.o
|
||||||
|
kernelsu-objs += dynamic_manager.o
|
||||||
kernelsu-objs += apk_sign.o
|
kernelsu-objs += apk_sign.o
|
||||||
kernelsu-objs += sucompat.o
|
kernelsu-objs += sucompat.o
|
||||||
|
kernelsu-objs += syscall_hook_manager.o
|
||||||
kernelsu-objs += throne_tracker.o
|
kernelsu-objs += throne_tracker.o
|
||||||
kernelsu-objs += core_hook.o
|
kernelsu-objs += pkg_observer.o
|
||||||
|
kernelsu-objs += throne_tracker.o
|
||||||
|
kernelsu-objs += umount_manager.o
|
||||||
|
kernelsu-objs += setuid_hook.o
|
||||||
|
kernelsu-objs += kernel_umount.o
|
||||||
|
kernelsu-objs += supercalls.o
|
||||||
|
kernelsu-objs += feature.o
|
||||||
kernelsu-objs += ksud.o
|
kernelsu-objs += ksud.o
|
||||||
kernelsu-objs += embed_ksud.o
|
kernelsu-objs += embed_ksud.o
|
||||||
kernelsu-objs += kernel_compat.o
|
kernelsu-objs += seccomp_cache.o
|
||||||
|
kernelsu-objs += file_wrapper.o
|
||||||
|
kernelsu-objs += throne_comm.o
|
||||||
|
kernelsu-objs += sulog.o
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_KSU_MANUAL_SU), y)
|
||||||
|
ccflags-y += -DCONFIG_KSU_MANUAL_SU
|
||||||
|
kernelsu-objs += manual_su.o
|
||||||
|
endif
|
||||||
|
|
||||||
kernelsu-objs += selinux/selinux.o
|
kernelsu-objs += selinux/selinux.o
|
||||||
kernelsu-objs += selinux/sepolicy.o
|
kernelsu-objs += selinux/sepolicy.o
|
||||||
@@ -18,26 +35,84 @@ obj-$(CONFIG_KSU) += kernelsu.o
|
|||||||
|
|
||||||
obj-$(CONFIG_KPM) += kpm/
|
obj-$(CONFIG_KPM) += kpm/
|
||||||
|
|
||||||
|
REPO_OWNER := SukiSU-Ultra
|
||||||
|
REPO_NAME := SukiSU-Ultra
|
||||||
|
REPO_BRANCH := main
|
||||||
|
KSU_VERSION_API := 4.0.0
|
||||||
|
|
||||||
|
GIT_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git
|
||||||
|
CURL_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin curl
|
||||||
|
|
||||||
|
KDIR := $(KDIR)
|
||||||
|
MDIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
|
||||||
|
|
||||||
|
ifneq ($(KDIR),)
|
||||||
|
$(info -- KDIR: $(KDIR))
|
||||||
|
$(info -- MDIR: $(MDIR))
|
||||||
|
endif
|
||||||
|
|
||||||
|
KSU_GITHUB_VERSION := $(shell $(CURL_BIN) -s "https://api.github.com/repos/$(REPO_OWNER)/$(REPO_NAME)/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
|
||||||
|
KSU_GITHUB_VERSION_COMMIT := $(shell $(CURL_BIN) -sI "https://api.github.com/repos/$(REPO_OWNER)/$(REPO_NAME)/commits?sha=$(REPO_BRANCH)&per_page=1" | grep -i "link:" | sed -n 's/.*page=\([0-9]*\)>; rel="last".*/\1/p')
|
||||||
|
|
||||||
|
ifeq ($(findstring $(srctree),$(src)),$(srctree))
|
||||||
|
KSU_SRC := $(src)
|
||||||
|
else
|
||||||
|
KSU_SRC := $(srctree)/$(src)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(shell test -e $(KSU_SRC)/../.git && echo "in-tree"),in-tree)
|
||||||
|
KSU_SRC := $(MDIR)
|
||||||
|
endif
|
||||||
|
|
||||||
|
LOCAL_GIT_EXISTS := $(shell test -e $(KSU_SRC)/../.git && echo 1 || echo 0)
|
||||||
|
|
||||||
|
define get_ksu_version_full
|
||||||
|
v$1-$(shell cd $(KSU_SRC); $(GIT_BIN) rev-parse --short=8 HEAD)@$(shell cd $(KSU_SRC); $(GIT_BIN) rev-parse --abbrev-ref HEAD)
|
||||||
|
endef
|
||||||
|
|
||||||
|
ifeq ($(KSU_GITHUB_VERSION_COMMIT),)
|
||||||
|
ifeq ($(LOCAL_GIT_EXISTS),1)
|
||||||
|
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||||
|
KSU_LOCAL_VERSION := $(shell cd $(KSU_SRC); $(GIT_BIN) rev-list --count $(REPO_BRANCH))
|
||||||
|
KSU_VERSION := $(shell expr 40000 + $(KSU_LOCAL_VERSION) - 2815)
|
||||||
|
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION))
|
||||||
|
else
|
||||||
|
KSU_VERSION := 13000
|
||||||
|
$(warning -- Could not fetch version online or via local .git! Using fallback version: $(KSU_VERSION))
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
KSU_VERSION := $(shell expr 40000 + $(KSU_GITHUB_VERSION_COMMIT) - 2815)
|
||||||
|
$(info -- $(REPO_NAME) version (GitHub): $(KSU_VERSION))
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(KSU_GITHUB_VERSION),)
|
||||||
|
ifeq ($(LOCAL_GIT_EXISTS),1)
|
||||||
|
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||||
|
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_VERSION_API))
|
||||||
|
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION_FULL))
|
||||||
|
$(info -- $(REPO_NAME) Formatted version (local .git): $(KSU_VERSION))
|
||||||
|
else
|
||||||
|
KSU_VERSION_FULL := v$(KSU_VERSION_API)-$(REPO_NAME)-unknown@unknown
|
||||||
|
$(warning -- $(REPO_NAME) version: $(KSU_VERSION_FULL))
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||||
|
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_GITHUB_VERSION))
|
||||||
|
$(info -- $(REPO_NAME) version (Github): $(KSU_VERSION_FULL))
|
||||||
|
endif
|
||||||
|
|
||||||
# .git is a text file while the module is imported by 'git submodule add'.
|
|
||||||
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
|
||||||
$(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin [ -f ../.git/shallow ] && git fetch --unshallow)
|
|
||||||
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count main)
|
|
||||||
# ksu_version: major * 10000 + git version + 606 for historical reasons
|
|
||||||
$(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 606))
|
|
||||||
$(info -- KernelSU version: $(KSU_VERSION))
|
|
||||||
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
||||||
else # If there is no .git file, the default version will be passed.
|
ccflags-y += -DKSU_VERSION_FULL=\"$(KSU_VERSION_FULL)\"
|
||||||
$(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git submodule!")
|
|
||||||
ccflags-y += -DKSU_VERSION=16
|
# Custom Signs
|
||||||
|
ifdef KSU_EXPECTED_SIZE
|
||||||
|
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
||||||
|
$(info -- Custom KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifndef KSU_EXPECTED_SIZE
|
ifdef KSU_EXPECTED_HASH
|
||||||
KSU_EXPECTED_SIZE := 0x35c
|
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
|
||||||
endif
|
$(info -- Custom KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
|
||||||
|
|
||||||
ifndef KSU_EXPECTED_HASH
|
|
||||||
KSU_EXPECTED_HASH := 947ae944f3de4ed4c21a7e4f7953ecf351bfa2b36239da37a34111ad29993eef
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef KSU_MANAGER_PACKAGE
|
ifdef KSU_MANAGER_PACKAGE
|
||||||
@@ -45,9 +120,15 @@ ccflags-y += -DKSU_MANAGER_PACKAGE=\"$(KSU_MANAGER_PACKAGE)\"
|
|||||||
$(info -- SukiSU Manager package name: $(KSU_MANAGER_PACKAGE))
|
$(info -- SukiSU Manager package name: $(KSU_MANAGER_PACKAGE))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(info -- SukiSU Manager signature size: $(KSU_EXPECTED_SIZE))
|
ifeq ($(CONFIG_KSU_MANUAL_HOOK), y)
|
||||||
$(info -- SukiSU Manager signature hash: $(KSU_EXPECTED_HASH))
|
ccflags-y += -DKSU_MANUAL_HOOK
|
||||||
$(info -- Supported Unofficial Manager: 5ec1cff (GKI) ShirkNeko udochina (GKI and KPM))
|
$(info -- SukiSU: KSU_MANUAL_HOOK Temporarily discontinued))
|
||||||
|
else
|
||||||
|
ccflags-y += -DKSU_KPROBES_HOOK
|
||||||
|
ccflags-y += -DKSU_TP_HOOK
|
||||||
|
$(info -- SukiSU: KSU_TRACEPOINT_HOOK)
|
||||||
|
endif
|
||||||
|
|
||||||
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
|
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
|
||||||
KERNEL_TYPE := Non-GKI
|
KERNEL_TYPE := Non-GKI
|
||||||
# Check for GKI 2.0 (5.10+ or 6.x+)
|
# Check for GKI 2.0 (5.10+ or 6.x+)
|
||||||
@@ -62,18 +143,30 @@ endif
|
|||||||
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
|
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
|
||||||
$(info -- KERNEL_TYPE: $(KERNEL_TYPE))
|
$(info -- KERNEL_TYPE: $(KERNEL_TYPE))
|
||||||
|
|
||||||
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
|
ifeq ($(CONFIG_KPM), y)
|
||||||
ifeq ($(CONFIG_KPM),y)
|
|
||||||
$(info -- KPM is enabled)
|
$(info -- KPM is enabled)
|
||||||
else
|
else
|
||||||
$(info -- KPM is disabled)
|
$(info -- KPM is disabled)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Check new vfs_getattr()
|
||||||
|
ifeq ($(shell grep -A1 "^int vfs_getattr" $(srctree)/fs/stat.c | grep -q "query_flags" ; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_HAS_NEW_VFS_GETATTR
|
||||||
|
endif
|
||||||
|
|
||||||
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
# Function proc_ops check
|
||||||
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
|
ifeq ($(shell grep -q "struct proc_ops " $(srctree)/include/linux/proc_fs.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_COMPAT_HAS_PROC_OPS
|
||||||
|
endif
|
||||||
|
|
||||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
ccflags-y += -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat -Wno-missing-prototypes
|
||||||
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function -Wno-unused-variable
|
||||||
|
|
||||||
|
all:
|
||||||
|
make -C $(KDIR) M=$(MDIR) modules
|
||||||
|
compdb:
|
||||||
|
python3 $(MDIR)/.vscode/generate_compdb.py -O $(KDIR) $(MDIR)
|
||||||
|
clean:
|
||||||
|
make -C $(KDIR) M=$(MDIR) clean
|
||||||
|
|
||||||
# Keep a new line here!! Because someone may append config
|
# Keep a new line here!! Because someone may append config
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
#include <linux/capability.h>
|
#include <linux/capability.h>
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
@@ -8,14 +10,16 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||||
#include <linux/compiler_types.h>
|
#include <linux/compiler_types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "ksu.h"
|
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "ksud.h"
|
||||||
#include "selinux/selinux.h"
|
#include "selinux/selinux.h"
|
||||||
#include "kernel_compat.h"
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
|
||||||
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
|
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
|
||||||
#define FILE_FORMAT_VERSION 3 // u32
|
#define FILE_FORMAT_VERSION 3 // u32
|
||||||
@@ -29,58 +33,61 @@ static DEFINE_MUTEX(allowlist_mutex);
|
|||||||
static struct root_profile default_root_profile;
|
static struct root_profile default_root_profile;
|
||||||
static struct non_root_profile default_non_root_profile;
|
static struct non_root_profile default_non_root_profile;
|
||||||
|
|
||||||
static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly __aligned(PAGE_SIZE);
|
void persistent_allow_list(void);
|
||||||
|
|
||||||
|
static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly
|
||||||
|
__aligned(PAGE_SIZE);
|
||||||
static int allow_list_pointer __read_mostly = 0;
|
static int allow_list_pointer __read_mostly = 0;
|
||||||
|
|
||||||
static void remove_uid_from_arr(uid_t uid)
|
static void remove_uid_from_arr(uid_t uid)
|
||||||
{
|
{
|
||||||
int *temp_arr;
|
int *temp_arr;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
if (allow_list_pointer == 0)
|
if (allow_list_pointer == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
temp_arr = kzalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
||||||
if (temp_arr == NULL) {
|
if (temp_arr == NULL) {
|
||||||
pr_err("%s: unable to allocate memory\n", __func__);
|
pr_err("%s: unable to allocate memory\n", __func__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = j = 0; i < allow_list_pointer; i++) {
|
for (i = j = 0; i < allow_list_pointer; i++) {
|
||||||
if (allow_list_arr[i] == uid)
|
if (allow_list_arr[i] == uid)
|
||||||
continue;
|
continue;
|
||||||
temp_arr[j++] = allow_list_arr[i];
|
temp_arr[j++] = allow_list_arr[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
allow_list_pointer = j;
|
allow_list_pointer = j;
|
||||||
|
|
||||||
for (; j < ARRAY_SIZE(allow_list_arr); j++)
|
for (; j < ARRAY_SIZE(allow_list_arr); j++)
|
||||||
temp_arr[j] = -1;
|
temp_arr[j] = -1;
|
||||||
|
|
||||||
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
|
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
|
||||||
kfree(temp_arr);
|
kfree(temp_arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init_default_profiles()
|
static void init_default_profiles(void)
|
||||||
{
|
{
|
||||||
kernel_cap_t full_cap = CAP_FULL_SET;
|
kernel_cap_t full_cap = CAP_FULL_SET;
|
||||||
|
|
||||||
default_root_profile.uid = 0;
|
default_root_profile.uid = 0;
|
||||||
default_root_profile.gid = 0;
|
default_root_profile.gid = 0;
|
||||||
default_root_profile.groups_count = 1;
|
default_root_profile.groups_count = 1;
|
||||||
default_root_profile.groups[0] = 0;
|
default_root_profile.groups[0] = 0;
|
||||||
memcpy(&default_root_profile.capabilities.effective, &full_cap,
|
memcpy(&default_root_profile.capabilities.effective, &full_cap,
|
||||||
sizeof(default_root_profile.capabilities.effective));
|
sizeof(default_root_profile.capabilities.effective));
|
||||||
default_root_profile.namespaces = 0;
|
default_root_profile.namespaces = 0;
|
||||||
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||||
|
|
||||||
// This means that we will umount modules by default!
|
// This means that we will umount modules by default!
|
||||||
default_non_root_profile.umount_modules = true;
|
default_non_root_profile.umount_modules = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct perm_data {
|
struct perm_data {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct app_profile profile;
|
struct app_profile profile;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct list_head allow_list;
|
static struct list_head allow_list;
|
||||||
@@ -90,437 +97,535 @@ static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
|
|||||||
|
|
||||||
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
|
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
|
||||||
|
|
||||||
static struct work_struct ksu_save_work;
|
|
||||||
static struct work_struct ksu_load_work;
|
|
||||||
|
|
||||||
bool persistent_allow_list(void);
|
|
||||||
|
|
||||||
void ksu_show_allow_list(void)
|
void ksu_show_allow_list(void)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
pr_info("ksu_show_allow_list\n");
|
pr_info("ksu_show_allow_list\n");
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
|
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
|
||||||
p->profile.allow_su);
|
p->profile.allow_su);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
static void ksu_grant_root_to_shell()
|
static void ksu_grant_root_to_shell(void)
|
||||||
{
|
{
|
||||||
struct app_profile profile = {
|
struct app_profile profile = {
|
||||||
.version = KSU_APP_PROFILE_VER,
|
.version = KSU_APP_PROFILE_VER,
|
||||||
.allow_su = true,
|
.allow_su = true,
|
||||||
.current_uid = 2000,
|
.current_uid = 2000,
|
||||||
};
|
};
|
||||||
strcpy(profile.key, "com.android.shell");
|
strcpy(profile.key, "com.android.shell");
|
||||||
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
strcpy(profile.rp_config.profile.selinux_domain,
|
||||||
ksu_set_app_profile(&profile, false);
|
KSU_DEFAULT_SELINUX_DOMAIN);
|
||||||
|
ksu_set_app_profile(&profile, false);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool ksu_get_app_profile(struct app_profile *profile)
|
bool ksu_get_app_profile(struct app_profile *profile)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
bool uid_match = profile->current_uid == p->profile.current_uid;
|
bool uid_match = profile->current_uid == p->profile.current_uid;
|
||||||
if (uid_match) {
|
if (uid_match) {
|
||||||
// found it, override it with ours
|
// found it, override it with ours
|
||||||
memcpy(profile, &p->profile, sizeof(*profile));
|
memcpy(profile, &p->profile, sizeof(*profile));
|
||||||
found = true;
|
found = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool forbid_system_uid(uid_t uid) {
|
static inline bool forbid_system_uid(uid_t uid)
|
||||||
#define SHELL_UID 2000
|
{
|
||||||
#define SYSTEM_UID 1000
|
#define SHELL_UID 2000
|
||||||
return uid < SHELL_UID && uid != SYSTEM_UID;
|
#define SYSTEM_UID 1000
|
||||||
|
return uid < SHELL_UID && uid != SYSTEM_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool profile_valid(struct app_profile *profile)
|
static bool profile_valid(struct app_profile *profile)
|
||||||
{
|
{
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (profile->version < KSU_APP_PROFILE_VER) {
|
if (profile->version < KSU_APP_PROFILE_VER) {
|
||||||
pr_info("Unsupported profile version: %d\n", profile->version);
|
pr_info("Unsupported profile version: %d\n", profile->version);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (profile->allow_su) {
|
if (profile->allow_su) {
|
||||||
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
|
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
|
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
if (!profile_valid(profile)) {
|
if (!profile_valid(profile)) {
|
||||||
pr_err("Failed to set app profile: invalid profile!\n");
|
pr_err("Failed to set app profile: invalid profile!\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
// both uid and package must match, otherwise it will break multiple package with different user id
|
// both uid and package must match, otherwise it will break multiple package with different user id
|
||||||
if (profile->current_uid == p->profile.current_uid &&
|
if (profile->current_uid == p->profile.current_uid &&
|
||||||
!strcmp(profile->key, p->profile.key)) {
|
!strcmp(profile->key, p->profile.key)) {
|
||||||
// found it, just override it all!
|
// found it, just override it all!
|
||||||
memcpy(&p->profile, profile, sizeof(*profile));
|
memcpy(&p->profile, profile, sizeof(*profile));
|
||||||
result = true;
|
result = true;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// not found, alloc a new node!
|
// not found, alloc a new node!
|
||||||
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
|
p = (struct perm_data *)kzalloc(sizeof(struct perm_data), GFP_KERNEL);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
pr_err("ksu_set_app_profile alloc failed\n");
|
pr_err("ksu_set_app_profile alloc failed\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&p->profile, profile, sizeof(*profile));
|
memcpy(&p->profile, profile, sizeof(*profile));
|
||||||
if (profile->allow_su) {
|
if (profile->allow_su) {
|
||||||
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
|
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
|
||||||
profile->key, profile->current_uid,
|
profile->key, profile->current_uid,
|
||||||
profile->rp_config.profile.gid,
|
profile->rp_config.profile.gid,
|
||||||
profile->rp_config.profile.selinux_domain);
|
profile->rp_config.profile.selinux_domain);
|
||||||
} else {
|
} else {
|
||||||
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
|
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
|
||||||
profile->key, profile->current_uid,
|
profile->key, profile->current_uid,
|
||||||
profile->nrp_config.profile.umount_modules);
|
profile->nrp_config.profile.umount_modules);
|
||||||
}
|
}
|
||||||
list_add_tail(&p->list, &allow_list);
|
list_add_tail(&p->list, &allow_list);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (profile->current_uid <= BITMAP_UID_MAX) {
|
if (profile->current_uid <= BITMAP_UID_MAX) {
|
||||||
if (profile->allow_su)
|
if (profile->allow_su)
|
||||||
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= 1 << (profile->current_uid % BITS_PER_BYTE);
|
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |=
|
||||||
else
|
1 << (profile->current_uid % BITS_PER_BYTE);
|
||||||
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= ~(1 << (profile->current_uid % BITS_PER_BYTE));
|
else
|
||||||
} else {
|
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &=
|
||||||
if (profile->allow_su) {
|
~(1 << (profile->current_uid % BITS_PER_BYTE));
|
||||||
/*
|
} else {
|
||||||
* 1024 apps with uid higher than BITMAP_UID_MAX
|
if (profile->allow_su) {
|
||||||
* registered to request superuser?
|
/*
|
||||||
*/
|
* 1024 apps with uid higher than BITMAP_UID_MAX
|
||||||
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
|
* registered to request superuser?
|
||||||
pr_err("too many apps registered\n");
|
*/
|
||||||
WARN_ON(1);
|
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
|
||||||
return false;
|
pr_err("too many apps registered\n");
|
||||||
}
|
WARN_ON(1);
|
||||||
allow_list_arr[allow_list_pointer++] = profile->current_uid;
|
return false;
|
||||||
} else {
|
}
|
||||||
remove_uid_from_arr(profile->current_uid);
|
allow_list_arr[allow_list_pointer++] = profile->current_uid;
|
||||||
}
|
} else {
|
||||||
}
|
remove_uid_from_arr(profile->current_uid);
|
||||||
result = true;
|
}
|
||||||
|
}
|
||||||
|
result = true;
|
||||||
|
|
||||||
// check if the default profiles is changed, cache it to a single struct to accelerate access.
|
// check if the default profiles is changed, cache it to a single struct to accelerate access.
|
||||||
if (unlikely(!strcmp(profile->key, "$"))) {
|
if (unlikely(!strcmp(profile->key, "$"))) {
|
||||||
// set default non root profile
|
// set default non root profile
|
||||||
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
|
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
|
||||||
sizeof(default_non_root_profile));
|
sizeof(default_non_root_profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(!strcmp(profile->key, "#"))) {
|
if (unlikely(!strcmp(profile->key, "#"))) {
|
||||||
// set default root profile
|
// set default root profile
|
||||||
memcpy(&default_root_profile, &profile->rp_config.profile,
|
memcpy(&default_root_profile, &profile->rp_config.profile,
|
||||||
sizeof(default_root_profile));
|
sizeof(default_root_profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (persist)
|
if (persist) {
|
||||||
persistent_allow_list();
|
persistent_allow_list();
|
||||||
|
// FIXME: use a new flag
|
||||||
|
ksu_mark_running_process();
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool __ksu_is_allow_uid(uid_t uid)
|
bool __ksu_is_allow_uid(uid_t uid)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (unlikely(uid == 0)) {
|
if (forbid_system_uid(uid)) {
|
||||||
// already root, but only allow our domain.
|
// do not bother going through the list if it's system
|
||||||
return is_ksu_domain();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forbid_system_uid(uid)) {
|
if (likely(ksu_is_manager_uid_valid()) &&
|
||||||
// do not bother going through the list if it's system
|
unlikely(ksu_get_manager_uid() == uid)) {
|
||||||
return false;
|
// manager is always allowed!
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||||
// manager is always allowed!
|
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] &
|
||||||
return true;
|
(1 << (uid % BITS_PER_BYTE)));
|
||||||
}
|
} else {
|
||||||
|
for (i = 0; i < allow_list_pointer; i++) {
|
||||||
|
if (allow_list_arr[i] == uid)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (likely(uid <= BITMAP_UID_MAX)) {
|
return false;
|
||||||
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
|
}
|
||||||
} else {
|
|
||||||
for (i = 0; i < allow_list_pointer; i++) {
|
|
||||||
if (allow_list_arr[i] == uid)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
bool __ksu_is_allow_uid_for_current(uid_t uid)
|
||||||
|
{
|
||||||
|
if (unlikely(uid == 0)) {
|
||||||
|
// already root, but only allow our domain.
|
||||||
|
return is_ksu_domain();
|
||||||
|
}
|
||||||
|
return __ksu_is_allow_uid(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ksu_uid_should_umount(uid_t uid)
|
bool ksu_uid_should_umount(uid_t uid)
|
||||||
{
|
{
|
||||||
struct app_profile profile = { .current_uid = uid };
|
struct app_profile profile = { .current_uid = uid };
|
||||||
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
if (likely(ksu_is_manager_uid_valid()) &&
|
||||||
// we should not umount on manager!
|
unlikely(ksu_get_manager_uid() == uid)) {
|
||||||
return false;
|
// we should not umount on manager!
|
||||||
}
|
return false;
|
||||||
bool found = ksu_get_app_profile(&profile);
|
}
|
||||||
if (!found) {
|
bool found = ksu_get_app_profile(&profile);
|
||||||
// no app profile found, it must be non root app
|
if (!found) {
|
||||||
return default_non_root_profile.umount_modules;
|
// no app profile found, it must be non root app
|
||||||
}
|
return default_non_root_profile.umount_modules;
|
||||||
if (profile.allow_su) {
|
}
|
||||||
// if found and it is granted to su, we shouldn't umount for it
|
if (profile.allow_su) {
|
||||||
return false;
|
// if found and it is granted to su, we shouldn't umount for it
|
||||||
} else {
|
return false;
|
||||||
// found an app profile
|
} else {
|
||||||
if (profile.nrp_config.use_default) {
|
// found an app profile
|
||||||
return default_non_root_profile.umount_modules;
|
if (profile.nrp_config.use_default) {
|
||||||
} else {
|
return default_non_root_profile.umount_modules;
|
||||||
return profile.nrp_config.profile.umount_modules;
|
} else {
|
||||||
}
|
return profile.nrp_config.profile.umount_modules;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct root_profile *ksu_get_root_profile(uid_t uid)
|
struct root_profile *ksu_get_root_profile(uid_t uid)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
|
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
if (uid == p->profile.current_uid && p->profile.allow_su) {
|
if (uid == p->profile.current_uid && p->profile.allow_su) {
|
||||||
if (!p->profile.rp_config.use_default) {
|
if (!p->profile.rp_config.use_default) {
|
||||||
return &p->profile.rp_config.profile;
|
return &p->profile.rp_config.profile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// use default profile
|
// use default profile
|
||||||
return &default_root_profile;
|
return &default_root_profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ksu_get_allow_list(int *array, int *length, bool allow)
|
bool ksu_get_allow_list(int *array, int *length, bool allow)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, 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->profile.allow_su == allow) {
|
if (p->profile.allow_su == allow) {
|
||||||
array[i++] = p->profile.current_uid;
|
array[i++] = p->profile.current_uid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*length = i;
|
*length = i;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_save_allow_list(struct work_struct *work)
|
static void do_persistent_allow_list(struct callback_head *_cb)
|
||||||
{
|
{
|
||||||
u32 magic = FILE_MAGIC;
|
u32 magic = FILE_MAGIC;
|
||||||
u32 version = FILE_FORMAT_VERSION;
|
u32 version = FILE_FORMAT_VERSION;
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
loff_t off = 0;
|
loff_t off = 0;
|
||||||
|
|
||||||
struct file *fp =
|
mutex_lock(&allowlist_mutex);
|
||||||
ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
struct file *fp =
|
||||||
if (IS_ERR(fp)) {
|
filp_open(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
|
if (IS_ERR(fp)) {
|
||||||
return;
|
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
|
||||||
}
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
// store magic and version
|
// store magic and version
|
||||||
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
|
if (kernel_write(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
|
||||||
sizeof(magic)) {
|
pr_err("save_allow_list write magic failed.\n");
|
||||||
pr_err("save_allow_list write magic failed.\n");
|
goto close_file;
|
||||||
goto exit;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
|
if (kernel_write(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
||||||
sizeof(version)) {
|
pr_err("save_allow_list write version failed.\n");
|
||||||
pr_err("save_allow_list write version failed.\n");
|
goto close_file;
|
||||||
goto exit;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
pr_info("save allow list, name: %s uid :%d, allow: %d\n",
|
pr_info("save allow list, name: %s uid :%d, allow: %d\n",
|
||||||
p->profile.key, p->profile.current_uid,
|
p->profile.key, p->profile.current_uid, p->profile.allow_su);
|
||||||
p->profile.allow_su);
|
|
||||||
|
|
||||||
ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile),
|
kernel_write(fp, &p->profile, sizeof(p->profile), &off);
|
||||||
&off);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
exit:
|
close_file:
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&allowlist_mutex);
|
||||||
|
kfree(_cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_load_allow_list(struct work_struct *work)
|
void persistent_allow_list()
|
||||||
{
|
{
|
||||||
loff_t off = 0;
|
struct task_struct *tsk;
|
||||||
ssize_t ret = 0;
|
|
||||||
struct file *fp = NULL;
|
tsk = get_pid_task(find_vpid(1), PIDTYPE_PID);
|
||||||
u32 magic;
|
if (!tsk) {
|
||||||
u32 version;
|
pr_err("save_allow_list find init task err\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct callback_head *cb =
|
||||||
|
kzalloc(sizeof(struct callback_head), GFP_KERNEL);
|
||||||
|
if (!cb) {
|
||||||
|
pr_err("save_allow_list alloc cb err\b");
|
||||||
|
goto put_task;
|
||||||
|
}
|
||||||
|
cb->func = do_persistent_allow_list;
|
||||||
|
task_work_add(tsk, cb, TWA_RESUME);
|
||||||
|
|
||||||
|
put_task:
|
||||||
|
put_task_struct(tsk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_load_allow_list()
|
||||||
|
{
|
||||||
|
loff_t off = 0;
|
||||||
|
ssize_t ret = 0;
|
||||||
|
struct file *fp = NULL;
|
||||||
|
u32 magic;
|
||||||
|
u32 version;
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
// always allow adb shell by default
|
// always allow adb shell by default
|
||||||
ksu_grant_root_to_shell();
|
ksu_grant_root_to_shell();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// load allowlist now!
|
// load allowlist now!
|
||||||
fp = ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
|
fp = filp_open(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
|
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify magic
|
// verify magic
|
||||||
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) !=
|
if (kernel_read(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
|
||||||
sizeof(magic) ||
|
magic != FILE_MAGIC) {
|
||||||
magic != FILE_MAGIC) {
|
pr_err("allowlist file invalid: %d!\n", magic);
|
||||||
pr_err("allowlist file invalid: %d!\n", magic);
|
goto exit;
|
||||||
goto exit;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) !=
|
if (kernel_read(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
||||||
sizeof(version)) {
|
pr_err("allowlist read version: %d failed\n", version);
|
||||||
pr_err("allowlist read version: %d failed\n", version);
|
goto exit;
|
||||||
goto exit;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("allowlist version: %d\n", version);
|
pr_info("allowlist version: %d\n", version);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
struct app_profile profile;
|
struct app_profile profile;
|
||||||
|
|
||||||
ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
|
ret = kernel_read(fp, &profile, sizeof(profile), &off);
|
||||||
&off);
|
|
||||||
|
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
pr_info("load_allow_list read err: %zd\n", ret);
|
pr_info("load_allow_list read err: %zd\n", ret);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n",
|
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n", profile.key,
|
||||||
profile.key, profile.current_uid, profile.allow_su);
|
profile.current_uid, profile.allow_su);
|
||||||
ksu_set_app_profile(&profile, false);
|
ksu_set_app_profile(&profile, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
ksu_show_allow_list();
|
ksu_show_allow_list();
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), void *data)
|
void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *),
|
||||||
|
void *data)
|
||||||
{
|
{
|
||||||
struct perm_data *np = NULL;
|
struct perm_data *np = NULL;
|
||||||
struct perm_data *n = NULL;
|
struct perm_data *n = NULL;
|
||||||
|
|
||||||
bool modified = false;
|
if (!ksu_boot_completed) {
|
||||||
// TODO: use RCU!
|
pr_info("boot not completed, skip prune\n");
|
||||||
mutex_lock(&allowlist_mutex);
|
return;
|
||||||
list_for_each_entry_safe (np, n, &allow_list, list) {
|
}
|
||||||
uid_t uid = np->profile.current_uid;
|
|
||||||
char *package = np->profile.key;
|
|
||||||
// we use this uid for special cases, don't prune it!
|
|
||||||
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
|
|
||||||
if (!is_preserved_uid && !is_uid_valid(uid, package, data)) {
|
|
||||||
modified = true;
|
|
||||||
pr_info("prune uid: %d, package: %s\n", uid, package);
|
|
||||||
list_del(&np->list);
|
|
||||||
if (likely(uid <= BITMAP_UID_MAX)) {
|
|
||||||
allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE));
|
|
||||||
}
|
|
||||||
remove_uid_from_arr(uid);
|
|
||||||
smp_mb();
|
|
||||||
kfree(np);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&allowlist_mutex);
|
|
||||||
|
|
||||||
if (modified) {
|
bool modified = false;
|
||||||
persistent_allow_list();
|
// TODO: use RCU!
|
||||||
}
|
mutex_lock(&allowlist_mutex);
|
||||||
}
|
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||||
|
uid_t uid = np->profile.current_uid;
|
||||||
|
char *package = np->profile.key;
|
||||||
|
// we use this uid for special cases, don't prune it!
|
||||||
|
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
|
||||||
|
if (!is_preserved_uid && !is_uid_valid(uid, package, data)) {
|
||||||
|
modified = true;
|
||||||
|
pr_info("prune uid: %d, package: %s\n", uid, package);
|
||||||
|
list_del(&np->list);
|
||||||
|
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||||
|
allow_list_bitmap[uid / BITS_PER_BYTE] &=
|
||||||
|
~(1 << (uid % BITS_PER_BYTE));
|
||||||
|
}
|
||||||
|
remove_uid_from_arr(uid);
|
||||||
|
smp_mb();
|
||||||
|
kfree(np);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&allowlist_mutex);
|
||||||
|
|
||||||
// make sure allow list works cross boot
|
if (modified) {
|
||||||
bool persistent_allow_list(void)
|
persistent_allow_list();
|
||||||
{
|
}
|
||||||
return ksu_queue_work(&ksu_save_work);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ksu_load_allow_list(void)
|
|
||||||
{
|
|
||||||
return ksu_queue_work(&ksu_load_work);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_allowlist_init(void)
|
void ksu_allowlist_init(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
|
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
|
||||||
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
|
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
|
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
|
||||||
allow_list_arr[i] = -1;
|
allow_list_arr[i] = -1;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&allow_list);
|
INIT_LIST_HEAD(&allow_list);
|
||||||
|
|
||||||
INIT_WORK(&ksu_save_work, do_save_allow_list);
|
init_default_profiles();
|
||||||
INIT_WORK(&ksu_load_work, do_load_allow_list);
|
|
||||||
|
|
||||||
init_default_profiles();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_allowlist_exit(void)
|
void ksu_allowlist_exit(void)
|
||||||
{
|
{
|
||||||
struct perm_data *np = NULL;
|
struct perm_data *np = NULL;
|
||||||
struct perm_data *n = NULL;
|
struct perm_data *n = NULL;
|
||||||
|
|
||||||
do_save_allow_list(NULL);
|
// free allowlist
|
||||||
|
mutex_lock(&allowlist_mutex);
|
||||||
// free allowlist
|
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||||
mutex_lock(&allowlist_mutex);
|
list_del(&np->list);
|
||||||
list_for_each_entry_safe (np, n, &allow_list, list) {
|
kfree(np);
|
||||||
list_del(&np->list);
|
}
|
||||||
kfree(np);
|
mutex_unlock(&allowlist_mutex);
|
||||||
}
|
|
||||||
mutex_unlock(&allowlist_mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
|
bool ksu_temp_grant_root_once(uid_t uid)
|
||||||
|
{
|
||||||
|
struct app_profile profile = {
|
||||||
|
.version = KSU_APP_PROFILE_VER,
|
||||||
|
.allow_su = true,
|
||||||
|
.current_uid = uid,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *default_key = "com.temp.once";
|
||||||
|
|
||||||
|
struct perm_data *p = NULL;
|
||||||
|
struct list_head *pos = NULL;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
list_for_each (pos, &allow_list) {
|
||||||
|
p = list_entry(pos, struct perm_data, list);
|
||||||
|
if (p->profile.current_uid == uid) {
|
||||||
|
strcpy(profile.key, p->profile.key);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
strcpy(profile.key, default_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.rp_config.profile.uid = default_root_profile.uid;
|
||||||
|
profile.rp_config.profile.gid = default_root_profile.gid;
|
||||||
|
profile.rp_config.profile.groups_count = default_root_profile.groups_count;
|
||||||
|
memcpy(profile.rp_config.profile.groups, default_root_profile.groups, sizeof(default_root_profile.groups));
|
||||||
|
memcpy(&profile.rp_config.profile.capabilities, &default_root_profile.capabilities, sizeof(default_root_profile.capabilities));
|
||||||
|
profile.rp_config.profile.namespaces = default_root_profile.namespaces;
|
||||||
|
strcpy(profile.rp_config.profile.selinux_domain, default_root_profile.selinux_domain);
|
||||||
|
|
||||||
|
bool ok = ksu_set_app_profile(&profile, false);
|
||||||
|
if (ok)
|
||||||
|
pr_info("pending_root: UID=%d granted and persisted\n", uid);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_temp_revoke_root_once(uid_t uid)
|
||||||
|
{
|
||||||
|
struct app_profile profile = {
|
||||||
|
.version = KSU_APP_PROFILE_VER,
|
||||||
|
.allow_su = false,
|
||||||
|
.current_uid = uid,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *default_key = "com.temp.once";
|
||||||
|
|
||||||
|
struct perm_data *p = NULL;
|
||||||
|
struct list_head *pos = NULL;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
list_for_each (pos, &allow_list) {
|
||||||
|
p = list_entry(pos, struct perm_data, list);
|
||||||
|
if (p->profile.current_uid == uid) {
|
||||||
|
strcpy(profile.key, p->profile.key);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
strcpy(profile.key, default_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.nrp_config.profile.umount_modules = default_non_root_profile.umount_modules;
|
||||||
|
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||||
|
|
||||||
|
ksu_set_app_profile(&profile, false);
|
||||||
|
persistent_allow_list();
|
||||||
|
pr_info("pending_root: UID=%d removed and persist updated\n", uid);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -2,19 +2,31 @@
|
|||||||
#define __KSU_H_ALLOWLIST
|
#define __KSU_H_ALLOWLIST
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include "ksu.h"
|
#include <linux/uidgid.h>
|
||||||
|
#include "app_profile.h"
|
||||||
|
|
||||||
|
#define PER_USER_RANGE 100000
|
||||||
|
#define FIRST_APPLICATION_UID 10000
|
||||||
|
#define LAST_APPLICATION_UID 19999
|
||||||
|
#define FIRST_ISOLATED_UID 99000
|
||||||
|
#define LAST_ISOLATED_UID 99999
|
||||||
|
|
||||||
void ksu_allowlist_init(void);
|
void ksu_allowlist_init(void);
|
||||||
|
|
||||||
void ksu_allowlist_exit(void);
|
void ksu_allowlist_exit(void);
|
||||||
|
|
||||||
bool ksu_load_allow_list(void);
|
void ksu_load_allow_list(void);
|
||||||
|
|
||||||
void ksu_show_allow_list(void);
|
void ksu_show_allow_list(void);
|
||||||
|
|
||||||
|
// Check if the uid is in allow list
|
||||||
bool __ksu_is_allow_uid(uid_t uid);
|
bool __ksu_is_allow_uid(uid_t uid);
|
||||||
#define ksu_is_allow_uid(uid) unlikely(__ksu_is_allow_uid(uid))
|
#define ksu_is_allow_uid(uid) unlikely(__ksu_is_allow_uid(uid))
|
||||||
|
|
||||||
|
// Check if the uid is in allow list, or current is ksu domain root
|
||||||
|
bool __ksu_is_allow_uid_for_current(uid_t uid);
|
||||||
|
#define ksu_is_allow_uid_for_current(uid) unlikely(__ksu_is_allow_uid_for_current(uid))
|
||||||
|
|
||||||
bool ksu_get_allow_list(int *array, int *length, bool allow);
|
bool ksu_get_allow_list(int *array, int *length, bool allow);
|
||||||
|
|
||||||
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, char *, void *), void *data);
|
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, char *, void *), void *data);
|
||||||
@@ -24,4 +36,22 @@ bool ksu_set_app_profile(struct app_profile *, bool persist);
|
|||||||
|
|
||||||
bool ksu_uid_should_umount(uid_t uid);
|
bool ksu_uid_should_umount(uid_t uid);
|
||||||
struct root_profile *ksu_get_root_profile(uid_t uid);
|
struct root_profile *ksu_get_root_profile(uid_t uid);
|
||||||
|
|
||||||
|
static inline bool is_appuid(uid_t uid)
|
||||||
|
{
|
||||||
|
uid_t appid = uid % PER_USER_RANGE;
|
||||||
|
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool is_isolated_process(uid_t uid)
|
||||||
|
{
|
||||||
|
uid_t appid = uid % PER_USER_RANGE;
|
||||||
|
return appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
|
bool ksu_temp_grant_root_once(uid_t uid);
|
||||||
|
void ksu_temp_revoke_root_once(uid_t uid);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -15,279 +15,375 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "apk_sign.h"
|
#include "apk_sign.h"
|
||||||
|
#include "dynamic_manager.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "kernel_compat.h"
|
#include "manager_sign.h"
|
||||||
|
|
||||||
|
|
||||||
struct sdesc {
|
struct sdesc {
|
||||||
struct shash_desc shash;
|
struct shash_desc shash;
|
||||||
char ctx[];
|
char ctx[];
|
||||||
|
};
|
||||||
|
|
||||||
|
static apk_sign_key_t apk_sign_keys[] = {
|
||||||
|
{EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // ShirkNeko/SukiSU
|
||||||
|
#ifdef EXPECTED_SIZE
|
||||||
|
{EXPECTED_SIZE, EXPECTED_HASH}, // Custom
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sdesc *init_sdesc(struct crypto_shash *alg)
|
static struct sdesc *init_sdesc(struct crypto_shash *alg)
|
||||||
{
|
{
|
||||||
struct sdesc *sdesc;
|
struct sdesc *sdesc;
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
|
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
|
||||||
sdesc = kmalloc(size, GFP_KERNEL);
|
sdesc = kzalloc(size, GFP_KERNEL);
|
||||||
if (!sdesc)
|
if (!sdesc)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
sdesc->shash.tfm = alg;
|
sdesc->shash.tfm = alg;
|
||||||
return sdesc;
|
return sdesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int calc_hash(struct crypto_shash *alg, const unsigned char *data,
|
static int calc_hash(struct crypto_shash *alg, const unsigned char *data,
|
||||||
unsigned int datalen, unsigned char *digest)
|
unsigned int datalen, unsigned char *digest)
|
||||||
{
|
{
|
||||||
struct sdesc *sdesc;
|
struct sdesc *sdesc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
sdesc = init_sdesc(alg);
|
sdesc = init_sdesc(alg);
|
||||||
if (IS_ERR(sdesc)) {
|
if (IS_ERR(sdesc)) {
|
||||||
pr_info("can't alloc sdesc\n");
|
pr_info("can't alloc sdesc\n");
|
||||||
return PTR_ERR(sdesc);
|
return PTR_ERR(sdesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
|
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
|
||||||
kfree(sdesc);
|
kfree(sdesc);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ksu_sha256(const unsigned char *data, unsigned int datalen,
|
static int ksu_sha256(const unsigned char *data, unsigned int datalen,
|
||||||
unsigned char *digest)
|
unsigned char *digest)
|
||||||
{
|
{
|
||||||
struct crypto_shash *alg;
|
struct crypto_shash *alg;
|
||||||
char *hash_alg_name = "sha256";
|
char *hash_alg_name = "sha256";
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
alg = crypto_alloc_shash(hash_alg_name, 0, 0);
|
alg = crypto_alloc_shash(hash_alg_name, 0, 0);
|
||||||
if (IS_ERR(alg)) {
|
if (IS_ERR(alg)) {
|
||||||
pr_info("can't alloc alg %s\n", hash_alg_name);
|
pr_info("can't alloc alg %s\n", hash_alg_name);
|
||||||
return PTR_ERR(alg);
|
return PTR_ERR(alg);
|
||||||
}
|
}
|
||||||
ret = calc_hash(alg, data, datalen, digest);
|
ret = calc_hash(alg, data, datalen, digest);
|
||||||
crypto_free_shash(alg);
|
crypto_free_shash(alg);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
|
|
||||||
unsigned expected_size, const char *expected_sha256)
|
static struct dynamic_sign_key dynamic_sign = DYNAMIC_SIGN_DEFAULT_CONFIG;
|
||||||
|
|
||||||
|
static bool check_dynamic_sign(struct file *fp, u32 size4, loff_t *pos, int *matched_index)
|
||||||
{
|
{
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
|
struct dynamic_sign_key current_dynamic_key = dynamic_sign;
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
|
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
|
if (ksu_get_dynamic_manager_config(¤t_dynamic_key.size, ¤t_dynamic_key.hash)) {
|
||||||
|
pr_debug("Using dynamic manager config: size=0x%x, hash=%.16s...\n",
|
||||||
*offset += 0x4 * 3;
|
current_dynamic_key.size, current_dynamic_key.hash);
|
||||||
|
}
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length
|
|
||||||
|
if (size4 != current_dynamic_key.size) {
|
||||||
*pos += *size4;
|
return false;
|
||||||
*offset += 0x4 + *size4;
|
}
|
||||||
|
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length
|
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
|
|
||||||
*offset += 0x4 * 2;
|
|
||||||
|
|
||||||
if (*size4 == expected_size) {
|
|
||||||
*offset += *size4;
|
|
||||||
|
|
||||||
#define CERT_MAX_LENGTH 1024
|
#define CERT_MAX_LENGTH 1024
|
||||||
char cert[CERT_MAX_LENGTH];
|
char cert[CERT_MAX_LENGTH];
|
||||||
if (*size4 > CERT_MAX_LENGTH) {
|
if (size4 > CERT_MAX_LENGTH) {
|
||||||
pr_info("cert length overlimit\n");
|
pr_info("cert length overlimit\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ksu_kernel_read_compat(fp, cert, *size4, pos);
|
|
||||||
unsigned char digest[SHA256_DIGEST_SIZE];
|
kernel_read(fp, cert, size4, pos);
|
||||||
if (IS_ERR(ksu_sha256(cert, *size4, digest))) {
|
|
||||||
pr_info("sha256 error\n");
|
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||||
return false;
|
if (ksu_sha256(cert, size4, digest) < 0) {
|
||||||
}
|
pr_info("sha256 error\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
||||||
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
||||||
|
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
||||||
|
|
||||||
|
pr_info("sha256: %s, expected: %s, index: dynamic\n", hash_str, current_dynamic_key.hash);
|
||||||
|
|
||||||
|
if (strcmp(current_dynamic_key.hash, hash_str) == 0) {
|
||||||
|
if (matched_index) {
|
||||||
|
*matched_index = DYNAMIC_SIGN_INDEX;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, int *matched_index)
|
||||||
pr_info("sha256: %s, expected: %s\n", hash_str,
|
{
|
||||||
expected_sha256);
|
int i;
|
||||||
if (strcmp(expected_sha256, hash_str) == 0) {
|
apk_sign_key_t sign_key;
|
||||||
return true;
|
bool signature_valid = false;
|
||||||
}
|
|
||||||
}
|
kernel_read(fp, size4, 0x4, pos); // signer-sequence length
|
||||||
return false;
|
kernel_read(fp, size4, 0x4, pos); // signer length
|
||||||
|
kernel_read(fp, size4, 0x4, pos); // signed data length
|
||||||
|
|
||||||
|
*offset += 0x4 * 3;
|
||||||
|
|
||||||
|
kernel_read(fp, size4, 0x4, pos); // digests-sequence length
|
||||||
|
|
||||||
|
*pos += *size4;
|
||||||
|
*offset += 0x4 + *size4;
|
||||||
|
|
||||||
|
kernel_read(fp, size4, 0x4, pos); // certificates length
|
||||||
|
kernel_read(fp, size4, 0x4, pos); // certificate length
|
||||||
|
*offset += 0x4 * 2;
|
||||||
|
|
||||||
|
if (ksu_is_dynamic_manager_enabled()) {
|
||||||
|
loff_t temp_pos = *pos;
|
||||||
|
if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) {
|
||||||
|
*pos = temp_pos;
|
||||||
|
*offset += *size4;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
|
||||||
|
sign_key = apk_sign_keys[i];
|
||||||
|
|
||||||
|
if (*size4 != sign_key.size)
|
||||||
|
continue;
|
||||||
|
*offset += *size4;
|
||||||
|
|
||||||
|
#define CERT_MAX_LENGTH 1024
|
||||||
|
char cert[CERT_MAX_LENGTH];
|
||||||
|
if (*size4 > CERT_MAX_LENGTH) {
|
||||||
|
pr_info("cert length overlimit\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
kernel_read(fp, cert, *size4, pos);
|
||||||
|
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||||
|
if (ksu_sha256(cert, *size4, digest) < 0 ) {
|
||||||
|
pr_info("sha256 error\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
||||||
|
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
||||||
|
|
||||||
|
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
||||||
|
pr_info("sha256: %s, expected: %s, index: %d\n", hash_str, sign_key.sha256, i);
|
||||||
|
|
||||||
|
if (strcmp(sign_key.sha256, hash_str) == 0) {
|
||||||
|
signature_valid = true;
|
||||||
|
if (matched_index) {
|
||||||
|
*matched_index = i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return signature_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct zip_entry_header {
|
struct zip_entry_header {
|
||||||
uint32_t signature;
|
uint32_t signature;
|
||||||
uint16_t version;
|
uint16_t version;
|
||||||
uint16_t flags;
|
uint16_t flags;
|
||||||
uint16_t compression;
|
uint16_t compression;
|
||||||
uint16_t mod_time;
|
uint16_t mod_time;
|
||||||
uint16_t mod_date;
|
uint16_t mod_date;
|
||||||
uint32_t crc32;
|
uint32_t crc32;
|
||||||
uint32_t compressed_size;
|
uint32_t compressed_size;
|
||||||
uint32_t uncompressed_size;
|
uint32_t uncompressed_size;
|
||||||
uint16_t file_name_length;
|
uint16_t file_name_length;
|
||||||
uint16_t extra_field_length;
|
uint16_t extra_field_length;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
// This is a necessary but not sufficient condition, but it is enough for us
|
// This is a necessary but not sufficient condition, but it is enough for us
|
||||||
static bool has_v1_signature_file(struct file *fp)
|
static bool has_v1_signature_file(struct file *fp)
|
||||||
{
|
{
|
||||||
struct zip_entry_header header;
|
struct zip_entry_header header;
|
||||||
const char MANIFEST[] = "META-INF/MANIFEST.MF";
|
const char MANIFEST[] = "META-INF/MANIFEST.MF";
|
||||||
|
|
||||||
loff_t pos = 0;
|
loff_t pos = 0;
|
||||||
|
|
||||||
while (ksu_kernel_read_compat(fp, &header,
|
while (kernel_read(fp, &header,
|
||||||
sizeof(struct zip_entry_header), &pos) ==
|
sizeof(struct zip_entry_header), &pos) ==
|
||||||
sizeof(struct zip_entry_header)) {
|
sizeof(struct zip_entry_header)) {
|
||||||
if (header.signature != 0x04034b50) {
|
if (header.signature != 0x04034b50) {
|
||||||
// ZIP magic: 'PK'
|
// ZIP magic: 'PK'
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Read the entry file name
|
// Read the entry file name
|
||||||
if (header.file_name_length == sizeof(MANIFEST) - 1) {
|
if (header.file_name_length == sizeof(MANIFEST) - 1) {
|
||||||
char fileName[sizeof(MANIFEST)];
|
char fileName[sizeof(MANIFEST)];
|
||||||
ksu_kernel_read_compat(fp, fileName,
|
kernel_read(fp, fileName,
|
||||||
header.file_name_length, &pos);
|
header.file_name_length, &pos);
|
||||||
fileName[header.file_name_length] = '\0';
|
fileName[header.file_name_length] = '\0';
|
||||||
|
|
||||||
// Check if the entry matches META-INF/MANIFEST.MF
|
// Check if the entry matches META-INF/MANIFEST.MF
|
||||||
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) ==
|
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) ==
|
||||||
0) {
|
0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Skip the entry file name
|
// Skip the entry file name
|
||||||
pos += header.file_name_length;
|
pos += header.file_name_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip to the next entry
|
// Skip to the next entry
|
||||||
pos += header.extra_field_length + header.compressed_size;
|
pos += header.extra_field_length + header.compressed_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline bool check_v2_signature(char *path,
|
static __always_inline bool check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
|
||||||
unsigned expected_size,
|
|
||||||
const char *expected_sha256)
|
|
||||||
{
|
{
|
||||||
unsigned char buffer[0x11] = { 0 };
|
unsigned char buffer[0x11] = { 0 };
|
||||||
u32 size4;
|
u32 size4;
|
||||||
u64 size8, size_of_block;
|
u64 size8, size_of_block;
|
||||||
|
|
||||||
loff_t pos;
|
loff_t pos;
|
||||||
|
|
||||||
bool v2_signing_valid = false;
|
bool v2_signing_valid = false;
|
||||||
int v2_signing_blocks = 0;
|
int v2_signing_blocks = 0;
|
||||||
bool v3_signing_exist = false;
|
bool v3_signing_exist = false;
|
||||||
bool v3_1_signing_exist = false;
|
bool v3_1_signing_exist = false;
|
||||||
|
int matched_index = -1;
|
||||||
|
int i;
|
||||||
|
struct file *fp = filp_open(path, O_RDONLY, 0);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("open %s error.\n", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int i;
|
// If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
|
||||||
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
|
if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
|
||||||
if (IS_ERR(fp)) {
|
filp_close(fp, 0);
|
||||||
pr_err("open %s error.\n", path);
|
return 0;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// disable inotify for this file
|
// disable inotify for this file
|
||||||
fp->f_mode |= FMODE_NONOTIFY;
|
fp->f_mode |= FMODE_NONOTIFY;
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
|
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
|
||||||
for (i = 0;; ++i) {
|
for (i = 0;; ++i) {
|
||||||
unsigned short n;
|
unsigned short n;
|
||||||
pos = generic_file_llseek(fp, -i - 2, SEEK_END);
|
pos = generic_file_llseek(fp, -i - 2, SEEK_END);
|
||||||
ksu_kernel_read_compat(fp, &n, 2, &pos);
|
kernel_read(fp, &n, 2, &pos);
|
||||||
if (n == i) {
|
if (n == i) {
|
||||||
pos -= 22;
|
pos -= 22;
|
||||||
ksu_kernel_read_compat(fp, &size4, 4, &pos);
|
kernel_read(fp, &size4, 4, &pos);
|
||||||
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
|
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i == 0xffff) {
|
if (i == 0xffff) {
|
||||||
pr_info("error: cannot find eocd\n");
|
pr_info("error: cannot find eocd\n");
|
||||||
goto clean;
|
goto clean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pos += 12;
|
pos += 12;
|
||||||
// offset
|
// offset
|
||||||
ksu_kernel_read_compat(fp, &size4, 0x4, &pos);
|
kernel_read(fp, &size4, 0x4, &pos);
|
||||||
pos = size4 - 0x18;
|
pos = size4 - 0x18;
|
||||||
|
|
||||||
ksu_kernel_read_compat(fp, &size8, 0x8, &pos);
|
kernel_read(fp, &size8, 0x8, &pos);
|
||||||
ksu_kernel_read_compat(fp, buffer, 0x10, &pos);
|
kernel_read(fp, buffer, 0x10, &pos);
|
||||||
if (strcmp((char *)buffer, "APK Sig Block 42")) {
|
if (strcmp((char *)buffer, "APK Sig Block 42")) {
|
||||||
goto clean;
|
goto clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = size4 - (size8 + 0x8);
|
pos = size4 - (size8 + 0x8);
|
||||||
ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos);
|
kernel_read(fp, &size_of_block, 0x8, &pos);
|
||||||
if (size_of_block != size8) {
|
if (size_of_block != size8) {
|
||||||
goto clean;
|
goto clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
int loop_count = 0;
|
int loop_count = 0;
|
||||||
while (loop_count++ < 10) {
|
while (loop_count++ < 10) {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
ksu_kernel_read_compat(fp, &size8, 0x8,
|
kernel_read(fp, &size8, 0x8,
|
||||||
&pos); // sequence length
|
&pos); // sequence length
|
||||||
if (size8 == size_of_block) {
|
if (size8 == size_of_block) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id
|
kernel_read(fp, &id, 0x4, &pos); // id
|
||||||
offset = 4;
|
offset = 4;
|
||||||
if (id == 0x7109871au) {
|
if (id == 0x7109871au) {
|
||||||
v2_signing_blocks++;
|
v2_signing_blocks++;
|
||||||
v2_signing_valid =
|
bool result = check_block(fp, &size4, &pos, &offset, &matched_index);
|
||||||
check_block(fp, &size4, &pos, &offset,
|
if (result) {
|
||||||
expected_size, expected_sha256);
|
v2_signing_valid = true;
|
||||||
} else if (id == 0xf05368c0u) {
|
}
|
||||||
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
|
} else if (id == 0xf05368c0u) {
|
||||||
v3_signing_exist = true;
|
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
|
||||||
} else if (id == 0x1b93ad61u) {
|
v3_signing_exist = true;
|
||||||
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
|
} else if (id == 0x1b93ad61u) {
|
||||||
v3_1_signing_exist = true;
|
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
|
||||||
} else {
|
v3_1_signing_exist = true;
|
||||||
|
} else {
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_info("Unknown id: 0x%08x\n", id);
|
pr_info("Unknown id: 0x%08x\n", id);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
pos += (size8 - offset);
|
pos += (size8 - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2_signing_blocks != 1) {
|
if (v2_signing_blocks != 1) {
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_err("Unexpected v2 signature count: %d\n",
|
pr_err("Unexpected v2 signature count: %d\n",
|
||||||
v2_signing_blocks);
|
v2_signing_blocks);
|
||||||
#endif
|
#endif
|
||||||
v2_signing_valid = false;
|
v2_signing_valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2_signing_valid) {
|
if (v2_signing_valid) {
|
||||||
int has_v1_signing = has_v1_signature_file(fp);
|
int has_v1_signing = has_v1_signature_file(fp);
|
||||||
if (has_v1_signing) {
|
if (has_v1_signing) {
|
||||||
pr_err("Unexpected v1 signature scheme found!\n");
|
pr_err("Unexpected v1 signature scheme found!\n");
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clean:
|
clean:
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
|
|
||||||
if (v3_signing_exist || v3_1_signing_exist) {
|
if (v3_signing_exist || v3_1_signing_exist) {
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_err("Unexpected v3 signature scheme found!\n");
|
pr_err("Unexpected v3 signature scheme found!\n");
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return v2_signing_valid;
|
if (v2_signing_valid) {
|
||||||
|
if (signature_index) {
|
||||||
|
*signature_index = matched_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_multi_manager) {
|
||||||
|
// 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign
|
||||||
|
if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) {
|
||||||
|
pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// Common manager check: any valid signature will do
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
@@ -298,23 +394,28 @@ int ksu_debug_manager_uid = -1;
|
|||||||
|
|
||||||
static int set_expected_size(const char *val, const struct kernel_param *kp)
|
static int set_expected_size(const char *val, const struct kernel_param *kp)
|
||||||
{
|
{
|
||||||
int rv = param_set_uint(val, kp);
|
int rv = param_set_uint(val, kp);
|
||||||
ksu_set_manager_uid(ksu_debug_manager_uid);
|
ksu_set_manager_uid(ksu_debug_manager_uid);
|
||||||
pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid);
|
pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct kernel_param_ops expected_size_ops = {
|
static struct kernel_param_ops expected_size_ops = {
|
||||||
.set = set_expected_size,
|
.set = set_expected_size,
|
||||||
.get = param_get_uint,
|
.get = param_get_uint,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
||||||
&ksu_debug_manager_uid, S_IRUSR | S_IWUSR);
|
&ksu_debug_manager_uid, S_IRUSR | S_IWUSR);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool is_manager_apk(char *path)
|
bool is_manager_apk(char *path)
|
||||||
{
|
{
|
||||||
return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH);
|
return check_v2_signature(path, false, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_dynamic_manager_apk(char *path, int *signature_index)
|
||||||
|
{
|
||||||
|
return check_v2_signature(path, true, signature_index);
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,10 @@
|
|||||||
#define __KSU_H_APK_V2_SIGN
|
#define __KSU_H_APK_V2_SIGN
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
#include "ksu.h"
|
||||||
|
|
||||||
bool is_manager_apk(char *path);
|
bool is_manager_apk(char *path);
|
||||||
|
|
||||||
|
bool is_dynamic_manager_apk(char *path, int *signature_index);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
303
kernel/app_profile.c
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/sched/signal.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
#include <linux/uidgid.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include "objsec.h"
|
||||||
|
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "app_profile.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
#include "sucompat.h"
|
||||||
|
|
||||||
|
#include "sulog.h"
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION (6, 7, 0)
|
||||||
|
static struct group_info root_groups = { .usage = REFCOUNT_INIT(2), };
|
||||||
|
#else
|
||||||
|
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void setup_groups(struct root_profile *profile, struct cred *cred)
|
||||||
|
{
|
||||||
|
if (profile->groups_count > KSU_MAX_GROUPS) {
|
||||||
|
pr_warn("Failed to setgroups, too large group: %d!\n",
|
||||||
|
profile->uid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->groups_count == 1 && profile->groups[0] == 0) {
|
||||||
|
// setgroup to root and return early.
|
||||||
|
if (cred->group_info)
|
||||||
|
put_group_info(cred->group_info);
|
||||||
|
cred->group_info = get_group_info(&root_groups);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ngroups = profile->groups_count;
|
||||||
|
struct group_info *group_info = groups_alloc(ngroups);
|
||||||
|
if (!group_info) {
|
||||||
|
pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < ngroups; i++) {
|
||||||
|
gid_t gid = profile->groups[i];
|
||||||
|
kgid_t kgid = make_kgid(current_user_ns(), gid);
|
||||||
|
if (!gid_valid(kgid)) {
|
||||||
|
pr_warn("Failed to setgroups, invalid gid: %d\n", gid);
|
||||||
|
put_group_info(group_info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
group_info->gid[i] = kgid;
|
||||||
|
}
|
||||||
|
|
||||||
|
groups_sort(group_info);
|
||||||
|
set_groups(cred, group_info);
|
||||||
|
put_group_info(group_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable_seccomp(void)
|
||||||
|
{
|
||||||
|
assert_spin_locked(¤t->sighand->siglock);
|
||||||
|
// disable seccomp
|
||||||
|
#if defined(CONFIG_GENERIC_ENTRY) && \
|
||||||
|
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
clear_syscall_work(SECCOMP);
|
||||||
|
#else
|
||||||
|
clear_thread_flag(TIF_SECCOMP);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SECCOMP
|
||||||
|
current->seccomp.mode = 0;
|
||||||
|
current->seccomp.filter = NULL;
|
||||||
|
atomic_set(¤t->seccomp.filter_count, 0);
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void escape_with_root_profile(void)
|
||||||
|
{
|
||||||
|
struct cred *cred;
|
||||||
|
struct task_struct *p = current;
|
||||||
|
struct task_struct *t;
|
||||||
|
|
||||||
|
cred = prepare_creds();
|
||||||
|
if (!cred) {
|
||||||
|
pr_warn("prepare_creds failed!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cred->euid.val == 0) {
|
||||||
|
pr_warn("Already root, don't escape!\n");
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root_failed");
|
||||||
|
#endif
|
||||||
|
abort_creds(cred);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
|
||||||
|
|
||||||
|
cred->uid.val = profile->uid;
|
||||||
|
cred->suid.val = profile->uid;
|
||||||
|
cred->euid.val = profile->uid;
|
||||||
|
cred->fsuid.val = profile->uid;
|
||||||
|
|
||||||
|
cred->gid.val = profile->gid;
|
||||||
|
cred->fsgid.val = profile->gid;
|
||||||
|
cred->sgid.val = profile->gid;
|
||||||
|
cred->egid.val = profile->gid;
|
||||||
|
cred->securebits = 0;
|
||||||
|
|
||||||
|
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
|
||||||
|
sizeof(kernel_cap_t));
|
||||||
|
|
||||||
|
// setup capabilities
|
||||||
|
// we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process
|
||||||
|
// we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec!
|
||||||
|
u64 cap_for_ksud =
|
||||||
|
profile->capabilities.effective | CAP_DAC_READ_SEARCH;
|
||||||
|
memcpy(&cred->cap_effective, &cap_for_ksud,
|
||||||
|
sizeof(cred->cap_effective));
|
||||||
|
memcpy(&cred->cap_permitted, &profile->capabilities.effective,
|
||||||
|
sizeof(cred->cap_permitted));
|
||||||
|
memcpy(&cred->cap_bset, &profile->capabilities.effective,
|
||||||
|
sizeof(cred->cap_bset));
|
||||||
|
|
||||||
|
setup_groups(profile, cred);
|
||||||
|
|
||||||
|
commit_creds(cred);
|
||||||
|
|
||||||
|
// Refer to kernel/seccomp.c: seccomp_set_mode_strict
|
||||||
|
// When disabling Seccomp, ensure that current->sighand->siglock is held during the operation.
|
||||||
|
spin_lock_irq(¤t->sighand->siglock);
|
||||||
|
disable_seccomp();
|
||||||
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
|
||||||
|
setup_selinux(profile->selinux_domain);
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for_each_thread (p, t) {
|
||||||
|
ksu_set_task_tracepoint_flag(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
|
|
||||||
|
#include "ksud.h"
|
||||||
|
|
||||||
|
#ifndef DEVPTS_SUPER_MAGIC
|
||||||
|
#define DEVPTS_SUPER_MAGIC 0x1cd1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int __manual_su_handle_devpts(struct inode *inode)
|
||||||
|
{
|
||||||
|
if (!current->mm) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uid_t uid = current_uid().val;
|
||||||
|
if (uid % 100000 < 10000) {
|
||||||
|
// not untrusted_app, ignore it
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (likely(!ksu_is_allow_uid_for_current(uid)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || defined(KSU_OPTIONAL_SELINUX_INODE)
|
||||||
|
struct inode_security_struct *sec = selinux_inode(inode);
|
||||||
|
#else
|
||||||
|
struct inode_security_struct *sec =
|
||||||
|
(struct inode_security_struct *)inode->i_security;
|
||||||
|
#endif
|
||||||
|
if (ksu_file_sid && sec)
|
||||||
|
sec->sid = ksu_file_sid;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void disable_seccomp_for_task(struct task_struct *tsk)
|
||||||
|
{
|
||||||
|
assert_spin_locked(&tsk->sighand->siglock);
|
||||||
|
#ifdef CONFIG_SECCOMP
|
||||||
|
if (tsk->seccomp.mode == SECCOMP_MODE_DISABLED && !tsk->seccomp.filter)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
clear_tsk_thread_flag(tsk, TIF_SECCOMP);
|
||||||
|
#ifdef CONFIG_SECCOMP
|
||||||
|
tsk->seccomp.mode = SECCOMP_MODE_DISABLED;
|
||||||
|
if (tsk->seccomp.filter) {
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
|
seccomp_filter_release(tsk);
|
||||||
|
#else
|
||||||
|
put_seccomp_filter(tsk);
|
||||||
|
tsk->seccomp.filter = NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid)
|
||||||
|
{
|
||||||
|
struct cred *newcreds;
|
||||||
|
struct task_struct *target_task;
|
||||||
|
unsigned long flags;
|
||||||
|
struct task_struct *p = current;
|
||||||
|
struct task_struct *t;
|
||||||
|
|
||||||
|
pr_info("cmd_su: escape_to_root_for_cmd_su called for UID: %d, PID: %d\n", target_uid, target_pid);
|
||||||
|
|
||||||
|
// Find target task by PID
|
||||||
|
rcu_read_lock();
|
||||||
|
target_task = pid_task(find_vpid(target_pid), PIDTYPE_PID);
|
||||||
|
if (!target_task) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
pr_err("cmd_su: target task not found for PID: %d\n", target_pid);
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_su_grant(target_uid, "cmd_su", "target_not_found");
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
get_task_struct(target_task);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
if (task_uid(target_task).val == 0) {
|
||||||
|
pr_warn("cmd_su: target task is already root, PID: %d\n", target_pid);
|
||||||
|
put_task_struct(target_task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
newcreds = prepare_kernel_cred(target_task);
|
||||||
|
if (newcreds == NULL) {
|
||||||
|
pr_err("cmd_su: failed to allocate new cred for PID: %d\n", target_pid);
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_su_grant(target_uid, "cmd_su", "cred_alloc_failed");
|
||||||
|
#endif
|
||||||
|
put_task_struct(target_task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct root_profile *profile = ksu_get_root_profile(target_uid);
|
||||||
|
|
||||||
|
newcreds->uid.val = profile->uid;
|
||||||
|
newcreds->suid.val = profile->uid;
|
||||||
|
newcreds->euid.val = profile->uid;
|
||||||
|
newcreds->fsuid.val = profile->uid;
|
||||||
|
|
||||||
|
newcreds->gid.val = profile->gid;
|
||||||
|
newcreds->fsgid.val = profile->gid;
|
||||||
|
newcreds->sgid.val = profile->gid;
|
||||||
|
newcreds->egid.val = profile->gid;
|
||||||
|
newcreds->securebits = 0;
|
||||||
|
|
||||||
|
u64 cap_for_cmd_su = profile->capabilities.effective | CAP_DAC_READ_SEARCH | CAP_SETUID | CAP_SETGID;
|
||||||
|
memcpy(&newcreds->cap_effective, &cap_for_cmd_su, sizeof(newcreds->cap_effective));
|
||||||
|
memcpy(&newcreds->cap_permitted, &profile->capabilities.effective, sizeof(newcreds->cap_permitted));
|
||||||
|
memcpy(&newcreds->cap_bset, &profile->capabilities.effective, sizeof(newcreds->cap_bset));
|
||||||
|
|
||||||
|
setup_groups(profile, newcreds);
|
||||||
|
task_lock(target_task);
|
||||||
|
|
||||||
|
const struct cred *old_creds = get_task_cred(target_task);
|
||||||
|
|
||||||
|
rcu_assign_pointer(target_task->real_cred, newcreds);
|
||||||
|
rcu_assign_pointer(target_task->cred, get_cred(newcreds));
|
||||||
|
task_unlock(target_task);
|
||||||
|
|
||||||
|
if (target_task->sighand) {
|
||||||
|
spin_lock_irqsave(&target_task->sighand->siglock, flags);
|
||||||
|
disable_seccomp_for_task(target_task);
|
||||||
|
spin_unlock_irqrestore(&target_task->sighand->siglock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_selinux(profile->selinux_domain);
|
||||||
|
put_cred(old_creds);
|
||||||
|
wake_up_process(target_task);
|
||||||
|
|
||||||
|
if (target_task->signal->tty) {
|
||||||
|
struct inode *inode = target_task->signal->tty->driver_data;
|
||||||
|
if (inode && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) {
|
||||||
|
__manual_su_handle_devpts(inode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
put_task_struct(target_task);
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_su_grant(target_uid, "cmd_su", "manual_escalation");
|
||||||
|
#endif
|
||||||
|
for_each_thread (p, t) {
|
||||||
|
ksu_set_task_tracepoint_flag(t);
|
||||||
|
}
|
||||||
|
pr_info("cmd_su: privilege escalation completed for UID: %d, PID: %d\n", target_uid, target_pid);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
70
kernel/app_profile.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#ifndef __KSU_H_APP_PROFILE
|
||||||
|
#define __KSU_H_APP_PROFILE
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
struct cred;
|
||||||
|
|
||||||
|
#define KSU_APP_PROFILE_VER 2
|
||||||
|
#define KSU_MAX_PACKAGE_NAME 256
|
||||||
|
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
||||||
|
#define KSU_MAX_GROUPS 32
|
||||||
|
#define KSU_SELINUX_DOMAIN 64
|
||||||
|
|
||||||
|
struct root_profile {
|
||||||
|
int32_t uid;
|
||||||
|
int32_t gid;
|
||||||
|
|
||||||
|
int32_t groups_count;
|
||||||
|
int32_t groups[KSU_MAX_GROUPS];
|
||||||
|
|
||||||
|
// kernel_cap_t is u32[2] for capabilities v3
|
||||||
|
struct {
|
||||||
|
u64 effective;
|
||||||
|
u64 permitted;
|
||||||
|
u64 inheritable;
|
||||||
|
} capabilities;
|
||||||
|
|
||||||
|
char selinux_domain[KSU_SELINUX_DOMAIN];
|
||||||
|
|
||||||
|
int32_t namespaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct non_root_profile {
|
||||||
|
bool umount_modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct app_profile {
|
||||||
|
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
|
||||||
|
u32 version;
|
||||||
|
|
||||||
|
// this is usually the package of the app, but can be other value for special apps
|
||||||
|
char key[KSU_MAX_PACKAGE_NAME];
|
||||||
|
int32_t current_uid;
|
||||||
|
bool allow_su;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
bool use_default;
|
||||||
|
char template_name[KSU_MAX_PACKAGE_NAME];
|
||||||
|
|
||||||
|
struct root_profile profile;
|
||||||
|
} rp_config;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool use_default;
|
||||||
|
|
||||||
|
struct non_root_profile profile;
|
||||||
|
} nrp_config;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Escalate current process to root with the appropriate profile
|
||||||
|
void escape_with_root_profile(void);
|
||||||
|
|
||||||
|
void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid);
|
||||||
|
|
||||||
|
void disable_seccomp(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -18,10 +18,8 @@
|
|||||||
#define __PT_SP_REG sp
|
#define __PT_SP_REG sp
|
||||||
#define __PT_IP_REG pc
|
#define __PT_IP_REG pc
|
||||||
|
|
||||||
#define PRCTL_SYMBOL "__arm64_sys_prctl"
|
#define REBOOT_SYMBOL "__arm64_sys_reboot"
|
||||||
#define SYS_READ_SYMBOL "__arm64_sys_read"
|
#define SYS_READ_SYMBOL "__arm64_sys_read"
|
||||||
#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat"
|
|
||||||
#define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat"
|
|
||||||
#define SYS_EXECVE_SYMBOL "__arm64_sys_execve"
|
#define SYS_EXECVE_SYMBOL "__arm64_sys_execve"
|
||||||
|
|
||||||
#elif defined(__x86_64__)
|
#elif defined(__x86_64__)
|
||||||
@@ -39,10 +37,8 @@
|
|||||||
#define __PT_RC_REG ax
|
#define __PT_RC_REG ax
|
||||||
#define __PT_SP_REG sp
|
#define __PT_SP_REG sp
|
||||||
#define __PT_IP_REG ip
|
#define __PT_IP_REG ip
|
||||||
#define PRCTL_SYMBOL "__x64_sys_prctl"
|
#define REBOOT_SYMBOL "__x64_sys_reboot"
|
||||||
#define SYS_READ_SYMBOL "__x64_sys_read"
|
#define SYS_READ_SYMBOL "__x64_sys_read"
|
||||||
#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat"
|
|
||||||
#define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat"
|
|
||||||
#define SYS_EXECVE_SYMBOL "__x64_sys_execve"
|
#define SYS_EXECVE_SYMBOL "__x64_sys_execve"
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -1,907 +0,0 @@
|
|||||||
#include <linux/capability.h>
|
|
||||||
#include <linux/cred.h>
|
|
||||||
#include <linux/dcache.h>
|
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/init_task.h>
|
|
||||||
#include <linux/kallsyms.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/kprobes.h>
|
|
||||||
#include <linux/lsm_hooks.h>
|
|
||||||
#include <linux/mm.h>
|
|
||||||
#include <linux/nsproxy.h>
|
|
||||||
#include <linux/path.h>
|
|
||||||
#include <linux/printk.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/security.h>
|
|
||||||
#include <linux/stddef.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/types.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>
|
|
||||||
|
|
||||||
#ifdef MODULE
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/irqflags.h>
|
|
||||||
#include <linux/mm_types.h>
|
|
||||||
#include <linux/rcupdate.h>
|
|
||||||
#include <linux/vmalloc.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#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 "throne_tracker.h"
|
|
||||||
#include "throne_tracker.h"
|
|
||||||
#include "kernel_compat.h"
|
|
||||||
|
|
||||||
#include "kpm/kpm.h"
|
|
||||||
|
|
||||||
static bool ksu_module_mounted = false;
|
|
||||||
|
|
||||||
extern int handle_sepolicy(unsigned long arg3, void __user *arg4);
|
|
||||||
|
|
||||||
static bool ksu_su_compat_enabled = true;
|
|
||||||
extern void ksu_sucompat_init();
|
|
||||||
extern void ksu_sucompat_exit();
|
|
||||||
|
|
||||||
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_unsupported_uid(uid_t uid)
|
|
||||||
{
|
|
||||||
#define LAST_APPLICATION_UID 19999
|
|
||||||
uid_t appid = uid % 100000;
|
|
||||||
return appid > LAST_APPLICATION_UID;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
|
|
||||||
|
|
||||||
static void setup_groups(struct root_profile *profile, struct cred *cred)
|
|
||||||
{
|
|
||||||
if (profile->groups_count > KSU_MAX_GROUPS) {
|
|
||||||
pr_warn("Failed to setgroups, too large group: %d!\n",
|
|
||||||
profile->uid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (profile->groups_count == 1 && profile->groups[0] == 0) {
|
|
||||||
// setgroup to root and return early.
|
|
||||||
if (cred->group_info)
|
|
||||||
put_group_info(cred->group_info);
|
|
||||||
cred->group_info = get_group_info(&root_groups);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 ngroups = profile->groups_count;
|
|
||||||
struct group_info *group_info = groups_alloc(ngroups);
|
|
||||||
if (!group_info) {
|
|
||||||
pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < ngroups; i++) {
|
|
||||||
gid_t gid = profile->groups[i];
|
|
||||||
kgid_t kgid = make_kgid(current_user_ns(), gid);
|
|
||||||
if (!gid_valid(kgid)) {
|
|
||||||
pr_warn("Failed to setgroups, invalid gid: %d\n", gid);
|
|
||||||
put_group_info(group_info);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
group_info->gid[i] = kgid;
|
|
||||||
}
|
|
||||||
|
|
||||||
groups_sort(group_info);
|
|
||||||
set_groups(cred, group_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void disable_seccomp()
|
|
||||||
{
|
|
||||||
assert_spin_locked(¤t->sighand->siglock);
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
void escape_to_root(void)
|
|
||||||
{
|
|
||||||
struct cred *cred;
|
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
|
|
||||||
do {
|
|
||||||
cred = (struct cred *)__task_cred((current));
|
|
||||||
BUG_ON(!cred);
|
|
||||||
} while (!get_cred_rcu(cred));
|
|
||||||
|
|
||||||
if (cred->euid.val == 0) {
|
|
||||||
pr_warn("Already root, don't escape!\n");
|
|
||||||
rcu_read_unlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
|
|
||||||
|
|
||||||
cred->uid.val = profile->uid;
|
|
||||||
cred->suid.val = profile->uid;
|
|
||||||
cred->euid.val = profile->uid;
|
|
||||||
cred->fsuid.val = profile->uid;
|
|
||||||
|
|
||||||
cred->gid.val = profile->gid;
|
|
||||||
cred->fsgid.val = profile->gid;
|
|
||||||
cred->sgid.val = profile->gid;
|
|
||||||
cred->egid.val = profile->gid;
|
|
||||||
cred->securebits = 0;
|
|
||||||
|
|
||||||
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
|
|
||||||
sizeof(kernel_cap_t));
|
|
||||||
|
|
||||||
// setup capabilities
|
|
||||||
// we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process
|
|
||||||
// we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec!
|
|
||||||
u64 cap_for_ksud =
|
|
||||||
profile->capabilities.effective | CAP_DAC_READ_SEARCH;
|
|
||||||
memcpy(&cred->cap_effective, &cap_for_ksud,
|
|
||||||
sizeof(cred->cap_effective));
|
|
||||||
memcpy(&cred->cap_permitted, &profile->capabilities.effective,
|
|
||||||
sizeof(cred->cap_permitted));
|
|
||||||
memcpy(&cred->cap_bset, &profile->capabilities.effective,
|
|
||||||
sizeof(cred->cap_bset));
|
|
||||||
|
|
||||||
setup_groups(profile, cred);
|
|
||||||
|
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
// Refer to kernel/seccomp.c: seccomp_set_mode_strict
|
|
||||||
// When disabling Seccomp, ensure that current->sighand->siglock is held during the operation.
|
|
||||||
spin_lock_irq(¤t->sighand->siglock);
|
|
||||||
disable_seccomp();
|
|
||||||
spin_unlock_irq(¤t->sighand->siglock);
|
|
||||||
|
|
||||||
setup_selinux(profile->selinux_domain);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (!strstr(buf, "/system/packages.list")) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
pr_info("renameat: %s -> %s, new path: %s\n", old_dentry->d_iname,
|
|
||||||
new_dentry->d_iname, buf);
|
|
||||||
|
|
||||||
track_throne();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nuke_ext4_sysfs() {
|
|
||||||
struct path path;
|
|
||||||
int err = kern_path("/data/adb/modules", 0, &path);
|
|
||||||
if (err) {
|
|
||||||
pr_err("nuke path err: %d\n", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct super_block* sb = path.dentry->d_inode->i_sb;
|
|
||||||
const char* name = sb->s_type->name;
|
|
||||||
if (strcmp(name, "ext4") != 0) {
|
|
||||||
pr_info("nuke but module aren't mounted\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ext4_unregister_sysfs(sb);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: find it in throne tracker!
|
|
||||||
uid_t current_uid_val = current_uid().val;
|
|
||||||
uid_t manager_uid = ksu_get_manager_uid();
|
|
||||||
if (current_uid_val != manager_uid &&
|
|
||||||
current_uid_val % 100000 == manager_uid) {
|
|
||||||
ksu_set_manager_uid(current_uid_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool from_root = 0 == current_uid().val;
|
|
||||||
bool from_manager = is_manager();
|
|
||||||
|
|
||||||
if (!from_root && !from_manager) {
|
|
||||||
// only root or manager can access this interface
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_info("option: 0x%x, cmd: %ld\n", option, arg2);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (arg2 == CMD_BECOME_MANAGER) {
|
|
||||||
if (from_manager) {
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("become_manager: prctl reply error\n");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_GRANT_ROOT) {
|
|
||||||
if (is_allow_su()) {
|
|
||||||
pr_info("allow root for: %d\n", current_uid().val);
|
|
||||||
escape_to_root();
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("grant_root: prctl reply error\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both root manager and root processes should be allowed to get version
|
|
||||||
if (arg2 == CMD_GET_VERSION) {
|
|
||||||
u32 version = KERNEL_SU_VERSION;
|
|
||||||
if (copy_to_user(arg3, &version, sizeof(version))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
u32 version_flags = 0;
|
|
||||||
#ifdef MODULE
|
|
||||||
version_flags |= 0x1;
|
|
||||||
#endif
|
|
||||||
if (arg4 &&
|
|
||||||
copy_to_user(arg4, &version_flags, sizeof(version_flags))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_REPORT_EVENT) {
|
|
||||||
if (!from_root) {
|
|
||||||
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\n");
|
|
||||||
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\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EVENT_MODULE_MOUNTED: {
|
|
||||||
ksu_module_mounted = true;
|
|
||||||
pr_info("module mounted!\n");
|
|
||||||
nuke_ext4_sysfs();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_SET_SEPOLICY) {
|
|
||||||
if (!from_root) {
|
|
||||||
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 (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) {
|
|
||||||
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: %lu\n",
|
|
||||||
arg2);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pr_err("prctl copy allowlist error\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_UID_GRANTED_ROOT || arg2 == CMD_UID_SHOULD_UMOUNT) {
|
|
||||||
uid_t target_uid = (uid_t)arg3;
|
|
||||||
bool allow = false;
|
|
||||||
if (arg2 == CMD_UID_GRANTED_ROOT) {
|
|
||||||
allow = ksu_is_allow_uid(target_uid);
|
|
||||||
} else if (arg2 == CMD_UID_SHOULD_UMOUNT) {
|
|
||||||
allow = ksu_uid_should_umount(target_uid);
|
|
||||||
} else {
|
|
||||||
pr_err("unknown cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
if (!copy_to_user(arg4, &allow, sizeof(allow))) {
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pr_err("prctl copy err, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_KPM
|
|
||||||
// ADD: 添加KPM模块控制
|
|
||||||
if(sukisu_is_kpm_control_code(arg2)) {
|
|
||||||
int res;
|
|
||||||
|
|
||||||
pr_info("KPM: calling before arg2=%d\n", (int) arg2);
|
|
||||||
|
|
||||||
res = sukisu_handle_kpm(arg2, arg3, arg4, arg5);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// all other cmds are for 'root manager'
|
|
||||||
if (!from_manager) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we are already manager
|
|
||||||
if (arg2 == CMD_GET_APP_PROFILE) {
|
|
||||||
struct app_profile profile;
|
|
||||||
if (copy_from_user(&profile, arg3, sizeof(profile))) {
|
|
||||||
pr_err("copy profile failed\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = ksu_get_app_profile(&profile);
|
|
||||||
if (success) {
|
|
||||||
if (copy_to_user(arg3, &profile, sizeof(profile))) {
|
|
||||||
pr_err("copy profile failed\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_SET_APP_PROFILE) {
|
|
||||||
struct app_profile profile;
|
|
||||||
if (copy_from_user(&profile, arg3, sizeof(profile))) {
|
|
||||||
pr_err("copy profile failed\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: validate the params
|
|
||||||
if (ksu_set_app_profile(&profile, true)) {
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_IS_SU_ENABLED) {
|
|
||||||
if (copy_to_user(arg3, &ksu_su_compat_enabled,
|
|
||||||
sizeof(ksu_su_compat_enabled))) {
|
|
||||||
pr_err("copy su compat failed\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_ENABLE_SU) {
|
|
||||||
bool enabled = (arg3 != 0);
|
|
||||||
if (enabled == ksu_su_compat_enabled) {
|
|
||||||
pr_info("cmd enable su but no need to change.\n");
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {// return the reply_ok directly
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enabled) {
|
|
||||||
ksu_sucompat_init();
|
|
||||||
} else {
|
|
||||||
ksu_sucompat_exit();
|
|
||||||
}
|
|
||||||
ksu_su_compat_enabled = enabled;
|
|
||||||
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
|
|
||||||
pr_info("ignore global mnt namespace process: %d\n",
|
|
||||||
current_uid().val);
|
|
||||||
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 ksu_umount_mnt(struct path *path, int flags)
|
|
||||||
{
|
|
||||||
int err = path_umount(path, flags);
|
|
||||||
if (err) {
|
|
||||||
pr_info("umount %s failed: %d\n", path->dentry->d_iname, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void try_umount(const char *mnt, bool check_mnt, int flags)
|
|
||||||
{
|
|
||||||
struct path path;
|
|
||||||
int err = kern_path(mnt, 0, &path);
|
|
||||||
if (err) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.dentry != path.mnt->mnt_root) {
|
|
||||||
// it is not root mountpoint, maybe umounted by others already.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we are only interest in some specific mounts
|
|
||||||
if (check_mnt && !should_umount(&path)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ksu_umount_mnt(&path, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
|
||||||
{
|
|
||||||
// this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it!
|
|
||||||
if (!ksu_module_mounted) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_appuid(new_uid) || is_unsupported_uid(new_uid.val)) {
|
|
||||||
// pr_info("handle setuid ignore non application or isolated 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ksu_uid_should_umount(new_uid.val)) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_info("uid: %d should not umount!\n", current_uid().val);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// check old process's selinux context, if it is not zygote, ignore it!
|
|
||||||
// because some su apps may setuid to untrusted_app but they are in global mount namespace
|
|
||||||
// when we umount for such process, that is a disaster!
|
|
||||||
bool is_zygote_child = is_zygote(old->security);
|
|
||||||
if (!is_zygote_child) {
|
|
||||||
pr_info("handle umount ignore non zygote child: %d\n",
|
|
||||||
current->pid);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
// umount the target mnt
|
|
||||||
pr_info("handle umount for uid: %d, pid: %d\n", new_uid.val,
|
|
||||||
current->pid);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
|
||||||
// filter the mountpoint whose target is `/data/adb`
|
|
||||||
try_umount("/system", true, 0);
|
|
||||||
try_umount("/vendor", true, 0);
|
|
||||||
try_umount("/product", true, 0);
|
|
||||||
try_umount("/system_ext", true, 0);
|
|
||||||
try_umount("/data/adb/modules", false, MNT_DETACH);
|
|
||||||
|
|
||||||
// try umount ksu temp path
|
|
||||||
try_umount("/debug_ramdisk", false, MNT_DETACH);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init functons
|
|
||||||
|
|
||||||
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(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);
|
|
||||||
// PRCTL_SYMBOL is the arch-specificed one, which receive raw pt_regs from syscall
|
|
||||||
unsigned long arg4 = (unsigned long)PT_REGS_SYSCALL_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_CCALL_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef MODULE
|
|
||||||
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),
|
|
||||||
};
|
|
||||||
|
|
||||||
void __init ksu_lsm_hook_init(void)
|
|
||||||
{
|
|
||||||
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu");
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
static int override_security_head(void *head, const void *new_head, size_t len)
|
|
||||||
{
|
|
||||||
unsigned long base = (unsigned long)head & PAGE_MASK;
|
|
||||||
unsigned long offset = offset_in_page(head);
|
|
||||||
|
|
||||||
// this is impossible for our case because the page alignment
|
|
||||||
// but be careful for other cases!
|
|
||||||
BUG_ON(offset + len > PAGE_SIZE);
|
|
||||||
struct page *page = phys_to_page(__pa(base));
|
|
||||||
if (!page) {
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *addr = vmap(&page, 1, VM_MAP, PAGE_KERNEL);
|
|
||||||
if (!addr) {
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
local_irq_disable();
|
|
||||||
memcpy(addr + offset, new_head, len);
|
|
||||||
local_irq_enable();
|
|
||||||
vunmap(addr);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_security_hook_list(struct hlist_head *head)
|
|
||||||
{
|
|
||||||
struct hlist_node *temp;
|
|
||||||
struct security_hook_list *entry;
|
|
||||||
|
|
||||||
if (!head)
|
|
||||||
return;
|
|
||||||
|
|
||||||
hlist_for_each_entry_safe (entry, temp, head, list) {
|
|
||||||
hlist_del(&entry->list);
|
|
||||||
kfree(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
kfree(head);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct hlist_head *copy_security_hlist(struct hlist_head *orig)
|
|
||||||
{
|
|
||||||
struct hlist_head *new_head = kmalloc(sizeof(*new_head), GFP_KERNEL);
|
|
||||||
if (!new_head)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
INIT_HLIST_HEAD(new_head);
|
|
||||||
|
|
||||||
struct security_hook_list *entry;
|
|
||||||
struct security_hook_list *new_entry;
|
|
||||||
|
|
||||||
hlist_for_each_entry (entry, orig, list) {
|
|
||||||
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
|
|
||||||
if (!new_entry) {
|
|
||||||
free_security_hook_list(new_head);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*new_entry = *entry;
|
|
||||||
|
|
||||||
hlist_add_tail_rcu(&new_entry->list, new_head);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new_head;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LSM_SEARCH_MAX 180 // This should be enough to iterate
|
|
||||||
static void *find_head_addr(void *security_ptr, int *index)
|
|
||||||
{
|
|
||||||
if (!security_ptr) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
struct hlist_head *head_start =
|
|
||||||
(struct hlist_head *)&security_hook_heads;
|
|
||||||
|
|
||||||
for (int i = 0; i < LSM_SEARCH_MAX; i++) {
|
|
||||||
struct hlist_head *head = head_start + i;
|
|
||||||
struct security_hook_list *pos;
|
|
||||||
hlist_for_each_entry (pos, head, list) {
|
|
||||||
if (pos->hook.capget == security_ptr) {
|
|
||||||
if (index) {
|
|
||||||
*index = i;
|
|
||||||
}
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define GET_SYMBOL_ADDR(sym) \
|
|
||||||
({ \
|
|
||||||
void *addr = kallsyms_lookup_name(#sym ".cfi_jt"); \
|
|
||||||
if (!addr) { \
|
|
||||||
addr = kallsyms_lookup_name(#sym); \
|
|
||||||
} \
|
|
||||||
addr; \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define KSU_LSM_HOOK_HACK_INIT(head_ptr, name, func) \
|
|
||||||
do { \
|
|
||||||
static struct security_hook_list hook = { \
|
|
||||||
.hook = { .name = func } \
|
|
||||||
}; \
|
|
||||||
hook.head = head_ptr; \
|
|
||||||
hook.lsm = "ksu"; \
|
|
||||||
struct hlist_head *new_head = copy_security_hlist(hook.head); \
|
|
||||||
if (!new_head) { \
|
|
||||||
pr_err("Failed to copy security list: %s\n", #name); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
hlist_add_tail_rcu(&hook.list, new_head); \
|
|
||||||
if (override_security_head(hook.head, new_head, \
|
|
||||||
sizeof(*new_head))) { \
|
|
||||||
free_security_hook_list(new_head); \
|
|
||||||
pr_err("Failed to hack lsm for: %s\n", #name); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
void __init ksu_lsm_hook_init(void)
|
|
||||||
{
|
|
||||||
void *cap_prctl = GET_SYMBOL_ADDR(cap_task_prctl);
|
|
||||||
void *prctl_head = find_head_addr(cap_prctl, NULL);
|
|
||||||
if (prctl_head) {
|
|
||||||
if (prctl_head != &security_hook_heads.task_prctl) {
|
|
||||||
pr_warn("prctl's address has shifted!\n");
|
|
||||||
}
|
|
||||||
KSU_LSM_HOOK_HACK_INIT(prctl_head, task_prctl, ksu_task_prctl);
|
|
||||||
} else {
|
|
||||||
pr_warn("Failed to find task_prctl!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int inode_killpriv_index = -1;
|
|
||||||
void *cap_killpriv = GET_SYMBOL_ADDR(cap_inode_killpriv);
|
|
||||||
find_head_addr(cap_killpriv, &inode_killpriv_index);
|
|
||||||
if (inode_killpriv_index < 0) {
|
|
||||||
pr_warn("Failed to find inode_rename, use kprobe instead!\n");
|
|
||||||
register_kprobe(&renameat_kp);
|
|
||||||
} else {
|
|
||||||
int inode_rename_index = inode_killpriv_index +
|
|
||||||
&security_hook_heads.inode_rename -
|
|
||||||
&security_hook_heads.inode_killpriv;
|
|
||||||
struct hlist_head *head_start =
|
|
||||||
(struct hlist_head *)&security_hook_heads;
|
|
||||||
void *inode_rename_head = head_start + inode_rename_index;
|
|
||||||
if (inode_rename_head != &security_hook_heads.inode_rename) {
|
|
||||||
pr_warn("inode_rename's address has shifted!\n");
|
|
||||||
}
|
|
||||||
KSU_LSM_HOOK_HACK_INIT(inode_rename_head, inode_rename,
|
|
||||||
ksu_inode_rename);
|
|
||||||
}
|
|
||||||
void *cap_setuid = GET_SYMBOL_ADDR(cap_task_fix_setuid);
|
|
||||||
void *setuid_head = find_head_addr(cap_setuid, NULL);
|
|
||||||
if (setuid_head) {
|
|
||||||
if (setuid_head != &security_hook_heads.task_fix_setuid) {
|
|
||||||
pr_warn("setuid's address has shifted!\n");
|
|
||||||
}
|
|
||||||
KSU_LSM_HOOK_HACK_INIT(setuid_head, task_fix_setuid,
|
|
||||||
ksu_task_fix_setuid);
|
|
||||||
} else {
|
|
||||||
pr_warn("Failed to find task_fix_setuid!\n");
|
|
||||||
}
|
|
||||||
smp_mb();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void __init ksu_core_init(void)
|
|
||||||
{
|
|
||||||
ksu_lsm_hook_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ksu_core_exit(void)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_KPROBE
|
|
||||||
pr_info("ksu_core_kprobe_exit\n");
|
|
||||||
// we dont use this now
|
|
||||||
// ksu_kprobe_exit();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#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
|
|
||||||
504
kernel/dynamic_manager.c
Normal file
@@ -0,0 +1,504 @@
|
|||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/gfp.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
#include <linux/moduleparam.h>
|
||||||
|
#endif
|
||||||
|
#include <crypto/hash.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
#include <crypto/sha2.h>
|
||||||
|
#else
|
||||||
|
#include <crypto/sha.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "dynamic_manager.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "manager.h"
|
||||||
|
|
||||||
|
#define MAX_MANAGERS 2
|
||||||
|
|
||||||
|
// Dynamic sign configuration
|
||||||
|
static struct dynamic_manager_config dynamic_manager = {
|
||||||
|
.size = 0x300,
|
||||||
|
.hash = "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
.is_set = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Multi-manager state
|
||||||
|
static struct manager_info active_managers[MAX_MANAGERS];
|
||||||
|
static DEFINE_SPINLOCK(managers_lock);
|
||||||
|
static DEFINE_SPINLOCK(dynamic_manager_lock);
|
||||||
|
|
||||||
|
// Work queues for persistent storage
|
||||||
|
static struct work_struct save_dynamic_manager_work;
|
||||||
|
static struct work_struct load_dynamic_manager_work;
|
||||||
|
static struct work_struct clear_dynamic_manager_work;
|
||||||
|
|
||||||
|
bool ksu_is_dynamic_manager_enabled(void)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
bool enabled;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
enabled = dynamic_manager.is_set;
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_add_manager(uid_t uid, int signature_index)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!ksu_is_dynamic_manager_enabled()) {
|
||||||
|
pr_info("Dynamic sign not enabled, skipping multi-manager add\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&managers_lock, flags);
|
||||||
|
|
||||||
|
// Check if manager already exists and update
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
if (active_managers[i].is_active && active_managers[i].uid == uid) {
|
||||||
|
active_managers[i].signature_index = signature_index;
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
pr_info("Updated manager uid=%d, signature_index=%d\n", uid, signature_index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find free slot for new manager
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
if (!active_managers[i].is_active) {
|
||||||
|
active_managers[i].uid = uid;
|
||||||
|
active_managers[i].signature_index = signature_index;
|
||||||
|
active_managers[i].is_active = true;
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
pr_info("Added manager uid=%d, signature_index=%d\n", uid, signature_index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
pr_warn("Failed to add manager, no free slots\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_remove_manager(uid_t uid)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!ksu_is_dynamic_manager_enabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&managers_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
if (active_managers[i].is_active && active_managers[i].uid == uid) {
|
||||||
|
active_managers[i].is_active = false;
|
||||||
|
pr_info("Removed manager uid=%d\n", uid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ksu_is_any_manager(uid_t uid)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
bool is_manager = false;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!ksu_is_dynamic_manager_enabled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&managers_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
if (active_managers[i].is_active && active_managers[i].uid == uid) {
|
||||||
|
is_manager = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
return is_manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_get_manager_signature_index(uid_t uid)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int signature_index = -1;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Check traditional manager first
|
||||||
|
if (ksu_manager_uid != KSU_INVALID_UID && uid == ksu_manager_uid) {
|
||||||
|
return DYNAMIC_SIGN_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_is_dynamic_manager_enabled()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&managers_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
if (active_managers[i].is_active && active_managers[i].uid == uid) {
|
||||||
|
signature_index = active_managers[i].signature_index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
return signature_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clear_dynamic_manager(void)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&managers_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
if (active_managers[i].is_active) {
|
||||||
|
pr_info("Clearing dynamic manager uid=%d (signature_index=%d) for rescan\n",
|
||||||
|
active_managers[i].uid, active_managers[i].signature_index);
|
||||||
|
active_managers[i].is_active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_get_active_managers(struct manager_list_info *info)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int i, count = 0;
|
||||||
|
|
||||||
|
if (!info) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add traditional manager first
|
||||||
|
if (ksu_manager_uid != KSU_INVALID_UID && count < 2) {
|
||||||
|
info->managers[count].uid = ksu_manager_uid;
|
||||||
|
info->managers[count].signature_index = 0;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add dynamic managers
|
||||||
|
if (ksu_is_dynamic_manager_enabled()) {
|
||||||
|
spin_lock_irqsave(&managers_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_MANAGERS && count < 2; i++) {
|
||||||
|
if (active_managers[i].is_active) {
|
||||||
|
info->managers[count].uid = active_managers[i].uid;
|
||||||
|
info->managers[count].signature_index = active_managers[i].signature_index;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
info->count = count;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_save_dynamic_manager(struct work_struct *work)
|
||||||
|
{
|
||||||
|
u32 magic = DYNAMIC_MANAGER_FILE_MAGIC;
|
||||||
|
u32 version = DYNAMIC_MANAGER_FILE_VERSION;
|
||||||
|
struct dynamic_manager_config config_to_save;
|
||||||
|
loff_t off = 0;
|
||||||
|
unsigned long flags;
|
||||||
|
struct file *fp;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
config_to_save = dynamic_manager;
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
|
||||||
|
if (!config_to_save.is_set) {
|
||||||
|
pr_info("Dynamic sign config not set, skipping save\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = filp_open(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("save_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kernel_write(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
|
||||||
|
pr_err("save_dynamic_manager write magic failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kernel_write(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
||||||
|
pr_err("save_dynamic_manager write version failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kernel_write(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) {
|
||||||
|
pr_err("save_dynamic_manager write config failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("Dynamic sign config saved successfully\n");
|
||||||
|
|
||||||
|
exit:
|
||||||
|
filp_close(fp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_load_dynamic_manager(struct work_struct *work)
|
||||||
|
{
|
||||||
|
loff_t off = 0;
|
||||||
|
ssize_t ret = 0;
|
||||||
|
struct file *fp = NULL;
|
||||||
|
u32 magic;
|
||||||
|
u32 version;
|
||||||
|
struct dynamic_manager_config loaded_config;
|
||||||
|
unsigned long flags;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fp = filp_open(KERNEL_SU_DYNAMIC_MANAGER, O_RDONLY, 0);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
if (PTR_ERR(fp) == -ENOENT) {
|
||||||
|
pr_info("No saved dynamic manager config found\n");
|
||||||
|
} else {
|
||||||
|
pr_err("load_dynamic_manager open file failed: %ld\n", PTR_ERR(fp));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kernel_read(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
|
||||||
|
magic != DYNAMIC_MANAGER_FILE_MAGIC) {
|
||||||
|
pr_err("dynamic manager file invalid magic: %x!\n", magic);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kernel_read(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
||||||
|
pr_err("dynamic manager read version failed\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("dynamic manager file version: %d\n", version);
|
||||||
|
|
||||||
|
ret = kernel_read(fp, &loaded_config, sizeof(loaded_config), &off);
|
||||||
|
if (ret <= 0) {
|
||||||
|
pr_info("load_dynamic_manager read err: %zd\n", ret);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != sizeof(loaded_config)) {
|
||||||
|
pr_err("load_dynamic_manager read incomplete config: %zd/%zu\n", ret, sizeof(loaded_config));
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loaded_config.size < 0x100 || loaded_config.size > 0x1000) {
|
||||||
|
pr_err("Invalid saved config size: 0x%x\n", loaded_config.size);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(loaded_config.hash) != 64) {
|
||||||
|
pr_err("Invalid saved config hash length: %zu\n", strlen(loaded_config.hash));
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate hash format
|
||||||
|
for (i = 0; i < 64; i++) {
|
||||||
|
char c = loaded_config.hash[i];
|
||||||
|
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
|
||||||
|
pr_err("Invalid saved config hash character at position %d: %c\n", i, c);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
dynamic_manager = loaded_config;
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
|
||||||
|
pr_info("Dynamic sign config loaded: size=0x%x, hash=%.16s...\n",
|
||||||
|
loaded_config.size, loaded_config.hash);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
filp_close(fp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool persistent_dynamic_manager(void)
|
||||||
|
{
|
||||||
|
return ksu_queue_work(&save_dynamic_manager_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_clear_dynamic_manager(struct work_struct *work)
|
||||||
|
{
|
||||||
|
loff_t off = 0;
|
||||||
|
struct file *fp;
|
||||||
|
char zero_buffer[512];
|
||||||
|
|
||||||
|
memset(zero_buffer, 0, sizeof(zero_buffer));
|
||||||
|
|
||||||
|
fp = filp_open(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("clear_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write null bytes to overwrite the file content
|
||||||
|
if (kernel_write(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) {
|
||||||
|
pr_err("clear_dynamic_manager write null bytes failed.\n");
|
||||||
|
} else {
|
||||||
|
pr_info("Dynamic sign config file cleared successfully\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
filp_close(fp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool clear_dynamic_manager_file(void)
|
||||||
|
{
|
||||||
|
return ksu_queue_work(&clear_dynamic_manager_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_dynamic_manager(struct dynamic_manager_user_config *config)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int ret = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (config->operation) {
|
||||||
|
case DYNAMIC_MANAGER_OP_SET:
|
||||||
|
if (config->size < 0x100 || config->size > 0x1000) {
|
||||||
|
pr_err("invalid size: 0x%x\n", config->size);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(config->hash) != 64) {
|
||||||
|
pr_err("invalid hash length: %zu\n", strlen(config->hash));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate hash format
|
||||||
|
for (i = 0; i < 64; i++) {
|
||||||
|
char c = config->hash[i];
|
||||||
|
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
|
||||||
|
pr_err("invalid hash character at position %d: %c\n", i, c);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
dynamic_manager.size = config->size;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
|
strscpy(dynamic_manager.hash, config->hash, sizeof(dynamic_manager.hash));
|
||||||
|
#else
|
||||||
|
strlcpy(dynamic_manager.hash, config->hash, sizeof(dynamic_manager.hash));
|
||||||
|
#endif
|
||||||
|
dynamic_manager.is_set = 1;
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
|
||||||
|
persistent_dynamic_manager();
|
||||||
|
pr_info("dynamic manager updated: size=0x%x, hash=%.16s... (multi-manager enabled)\n",
|
||||||
|
config->size, config->hash);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DYNAMIC_MANAGER_OP_GET:
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
if (dynamic_manager.is_set) {
|
||||||
|
config->size = dynamic_manager.size;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
|
strscpy(config->hash, dynamic_manager.hash, sizeof(config->hash));
|
||||||
|
#else
|
||||||
|
strlcpy(config->hash, dynamic_manager.hash, sizeof(config->hash));
|
||||||
|
#endif
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
ret = -ENODATA;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DYNAMIC_MANAGER_OP_CLEAR:
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
dynamic_manager.size = 0x300;
|
||||||
|
strcpy(dynamic_manager.hash, "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
dynamic_manager.is_set = 0;
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
|
||||||
|
// Clear only dynamic managers, preserve default manager
|
||||||
|
clear_dynamic_manager();
|
||||||
|
|
||||||
|
// Clear file using the same method as save
|
||||||
|
clear_dynamic_manager_file();
|
||||||
|
|
||||||
|
pr_info("Dynamic sign config cleared (multi-manager disabled)\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
pr_err("Invalid dynamic manager operation: %d\n", config->operation);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ksu_load_dynamic_manager(void)
|
||||||
|
{
|
||||||
|
return ksu_queue_work(&load_dynamic_manager_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_dynamic_manager_init(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
INIT_WORK(&save_dynamic_manager_work, do_save_dynamic_manager);
|
||||||
|
INIT_WORK(&load_dynamic_manager_work, do_load_dynamic_manager);
|
||||||
|
INIT_WORK(&clear_dynamic_manager_work, do_clear_dynamic_manager);
|
||||||
|
|
||||||
|
// Initialize manager slots
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
active_managers[i].is_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ksu_load_dynamic_manager();
|
||||||
|
|
||||||
|
pr_info("Dynamic sign initialized with conditional multi-manager support\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_dynamic_manager_exit(void)
|
||||||
|
{
|
||||||
|
clear_dynamic_manager();
|
||||||
|
|
||||||
|
// Save current config before exit
|
||||||
|
do_save_dynamic_manager(NULL);
|
||||||
|
pr_info("Dynamic sign exited with persistent storage\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get dynamic manager configuration for signature verification
|
||||||
|
bool ksu_get_dynamic_manager_config(unsigned int *size, const char **hash)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
bool valid = false;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
if (dynamic_manager.is_set) {
|
||||||
|
if (size) *size = dynamic_manager.size;
|
||||||
|
if (hash) *hash = dynamic_manager.hash;
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
51
kernel/dynamic_manager.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#ifndef __KSU_H_DYNAMIC_MANAGER
|
||||||
|
#define __KSU_H_DYNAMIC_MANAGER
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include "ksu.h"
|
||||||
|
|
||||||
|
#define DYNAMIC_MANAGER_FILE_MAGIC 0x7f445347 // 'DSG', u32
|
||||||
|
#define DYNAMIC_MANAGER_FILE_VERSION 1 // u32
|
||||||
|
#define KERNEL_SU_DYNAMIC_MANAGER "/data/adb/ksu/.dynamic_manager"
|
||||||
|
#define DYNAMIC_SIGN_INDEX 100
|
||||||
|
|
||||||
|
struct dynamic_sign_key {
|
||||||
|
unsigned int size;
|
||||||
|
const char *hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DYNAMIC_SIGN_DEFAULT_CONFIG { \
|
||||||
|
.size = 0x300, \
|
||||||
|
.hash = "0000000000000000000000000000000000000000000000000000000000000000" \
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dynamic_manager_config {
|
||||||
|
unsigned int size;
|
||||||
|
char hash[65];
|
||||||
|
int is_set;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct manager_info {
|
||||||
|
uid_t uid;
|
||||||
|
int signature_index;
|
||||||
|
bool is_active;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dynamic sign operations
|
||||||
|
void ksu_dynamic_manager_init(void);
|
||||||
|
void ksu_dynamic_manager_exit(void);
|
||||||
|
int ksu_handle_dynamic_manager(struct dynamic_manager_user_config *config);
|
||||||
|
bool ksu_load_dynamic_manager(void);
|
||||||
|
bool ksu_is_dynamic_manager_enabled(void);
|
||||||
|
|
||||||
|
// Multi-manager operations
|
||||||
|
void ksu_add_manager(uid_t uid, int signature_index);
|
||||||
|
void ksu_remove_manager(uid_t uid);
|
||||||
|
bool ksu_is_any_manager(uid_t uid);
|
||||||
|
int ksu_get_manager_signature_index(uid_t uid);
|
||||||
|
int ksu_get_active_managers(struct manager_list_info *info);
|
||||||
|
|
||||||
|
// Configuration access for signature verification
|
||||||
|
bool ksu_get_dynamic_manager_config(unsigned int *size, const char **hash);
|
||||||
|
|
||||||
|
#endif
|
||||||
173
kernel/feature.c
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
#include "feature.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
|
||||||
|
static const struct ksu_feature_handler *feature_handlers[KSU_FEATURE_MAX];
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(feature_mutex);
|
||||||
|
|
||||||
|
int ksu_register_feature_handler(const struct ksu_feature_handler *handler)
|
||||||
|
{
|
||||||
|
if (!handler) {
|
||||||
|
pr_err("feature: register handler is NULL\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler->feature_id >= KSU_FEATURE_MAX) {
|
||||||
|
pr_err("feature: invalid feature_id %u\n", handler->feature_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handler->get_handler && !handler->set_handler) {
|
||||||
|
pr_err("feature: no handler provided for feature %u\n", handler->feature_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&feature_mutex);
|
||||||
|
|
||||||
|
if (feature_handlers[handler->feature_id]) {
|
||||||
|
pr_warn("feature: handler for %u already registered, overwriting\n",
|
||||||
|
handler->feature_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
feature_handlers[handler->feature_id] = handler;
|
||||||
|
|
||||||
|
pr_info("feature: registered handler for %s (id=%u)\n",
|
||||||
|
handler->name ? handler->name : "unknown", handler->feature_id);
|
||||||
|
|
||||||
|
mutex_unlock(&feature_mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_unregister_feature_handler(u32 feature_id)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (feature_id >= KSU_FEATURE_MAX) {
|
||||||
|
pr_err("feature: invalid feature_id %u\n", feature_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&feature_mutex);
|
||||||
|
|
||||||
|
if (!feature_handlers[feature_id]) {
|
||||||
|
pr_warn("feature: no handler registered for %u\n", feature_id);
|
||||||
|
ret = -ENOENT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
feature_handlers[feature_id] = NULL;
|
||||||
|
|
||||||
|
pr_info("feature: unregistered handler for id=%u\n", feature_id);
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&feature_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_get_feature(u32 feature_id, u64 *value, bool *supported)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
const struct ksu_feature_handler *handler;
|
||||||
|
|
||||||
|
if (feature_id >= KSU_FEATURE_MAX) {
|
||||||
|
pr_err("feature: invalid feature_id %u\n", feature_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value || !supported) {
|
||||||
|
pr_err("feature: invalid parameters\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&feature_mutex);
|
||||||
|
|
||||||
|
handler = feature_handlers[feature_id];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
*supported = false;
|
||||||
|
*value = 0;
|
||||||
|
pr_debug("feature: feature %u not supported\n", feature_id);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
*supported = true;
|
||||||
|
|
||||||
|
if (!handler->get_handler) {
|
||||||
|
pr_warn("feature: no get_handler for feature %u\n", feature_id);
|
||||||
|
ret = -EOPNOTSUPP;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = handler->get_handler(value);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("feature: get_handler for %u failed: %d\n", feature_id, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&feature_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_set_feature(u32 feature_id, u64 value)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
const struct ksu_feature_handler *handler;
|
||||||
|
|
||||||
|
if (feature_id >= KSU_FEATURE_MAX) {
|
||||||
|
pr_err("feature: invalid feature_id %u\n", feature_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&feature_mutex);
|
||||||
|
|
||||||
|
handler = feature_handlers[feature_id];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
pr_err("feature: feature %u not registered\n", feature_id);
|
||||||
|
ret = -EOPNOTSUPP;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handler->set_handler) {
|
||||||
|
pr_warn("feature: no set_handler for feature %u\n", feature_id);
|
||||||
|
ret = -EOPNOTSUPP;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = handler->set_handler(value);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("feature: set_handler for %u failed: %d\n", feature_id, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&feature_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_feature_init(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < KSU_FEATURE_MAX; i++) {
|
||||||
|
feature_handlers[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("feature: feature management initialized\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_feature_exit(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mutex_lock(&feature_mutex);
|
||||||
|
|
||||||
|
for (i = 0; i < KSU_FEATURE_MAX; i++) {
|
||||||
|
feature_handlers[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&feature_mutex);
|
||||||
|
|
||||||
|
pr_info("feature: feature management cleaned up\n");
|
||||||
|
}
|
||||||
37
kernel/feature.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef __KSU_H_FEATURE
|
||||||
|
#define __KSU_H_FEATURE
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
enum ksu_feature_id {
|
||||||
|
KSU_FEATURE_SU_COMPAT = 0,
|
||||||
|
KSU_FEATURE_KERNEL_UMOUNT = 1,
|
||||||
|
KSU_FEATURE_ENHANCED_SECURITY = 2,
|
||||||
|
KSU_FEATURE_SULOG = 3,
|
||||||
|
|
||||||
|
KSU_FEATURE_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int (*ksu_feature_get_t)(u64 *value);
|
||||||
|
typedef int (*ksu_feature_set_t)(u64 value);
|
||||||
|
|
||||||
|
struct ksu_feature_handler {
|
||||||
|
u32 feature_id;
|
||||||
|
const char *name;
|
||||||
|
ksu_feature_get_t get_handler;
|
||||||
|
ksu_feature_set_t set_handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
int ksu_register_feature_handler(const struct ksu_feature_handler *handler);
|
||||||
|
|
||||||
|
int ksu_unregister_feature_handler(u32 feature_id);
|
||||||
|
|
||||||
|
int ksu_get_feature(u32 feature_id, u64 *value, bool *supported);
|
||||||
|
|
||||||
|
int ksu_set_feature(u32 feature_id, u64 value);
|
||||||
|
|
||||||
|
void ksu_feature_init(void);
|
||||||
|
|
||||||
|
void ksu_feature_exit(void);
|
||||||
|
|
||||||
|
#endif // __KSU_H_FEATURE
|
||||||
341
kernel/file_wrapper.c
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/anon_inodes.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
|
||||||
|
#include "file_wrapper.h"
|
||||||
|
|
||||||
|
static loff_t ksu_wrapper_llseek(struct file *fp, loff_t off, int flags) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->llseek(data->orig, off, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_read(struct file *fp, char __user *ptr, size_t sz, loff_t *off) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->read(orig, ptr, sz, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_write(struct file *fp, const char __user *ptr, size_t sz, loff_t *off) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->write(orig, ptr, sz, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_read_iter(struct kiocb *iocb, struct iov_iter *iovi) {
|
||||||
|
struct ksu_file_wrapper* data = iocb->ki_filp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
iocb->ki_filp = orig;
|
||||||
|
return orig->f_op->read_iter(iocb, iovi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_write_iter(struct kiocb *iocb, struct iov_iter *iovi) {
|
||||||
|
struct ksu_file_wrapper* data = iocb->ki_filp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
iocb->ki_filp = orig;
|
||||||
|
return orig->f_op->write_iter(iocb, iovi);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||||
|
static int ksu_wrapper_iopoll(struct kiocb *kiocb, struct io_comp_batch* icb, unsigned int v) {
|
||||||
|
struct ksu_file_wrapper* data = kiocb->ki_filp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
kiocb->ki_filp = orig;
|
||||||
|
return orig->f_op->iopoll(kiocb, icb, v);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int ksu_wrapper_iopoll(struct kiocb *kiocb, bool spin) {
|
||||||
|
struct ksu_file_wrapper* data = kiocb->ki_filp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
kiocb->ki_filp = orig;
|
||||||
|
return orig->f_op->iopoll(kiocb, spin);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||||
|
static int ksu_wrapper_iterate (struct file *fp, struct dir_context *dc) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->iterate(orig, dc);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int ksu_wrapper_iterate_shared(struct file *fp, struct dir_context *dc) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->iterate_shared(orig, dc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __poll_t ksu_wrapper_poll(struct file *fp, struct poll_table_struct *pts) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->poll(orig, pts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ksu_wrapper_unlocked_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->unlocked_ioctl(orig, cmd, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ksu_wrapper_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->compat_ioctl(orig, cmd, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ksu_wrapper_mmap(struct file *fp, struct vm_area_struct * vma) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->mmap(orig, vma);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static unsigned long mmap_supported_flags {}
|
||||||
|
|
||||||
|
static int ksu_wrapper_open(struct inode *ino, struct file *fp) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
struct inode *orig_ino = file_inode(orig);
|
||||||
|
return orig->f_op->open(orig_ino, orig);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ksu_wrapper_flush(struct file *fp, fl_owner_t id) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->flush(orig, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ksu_wrapper_fsync(struct file *fp, loff_t off1, loff_t off2, int datasync) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->fsync(orig, off1, off2, datasync);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ksu_wrapper_fasync(int arg, struct file *fp, int arg2) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->fasync(arg, orig, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ksu_wrapper_lock(struct file *fp, int arg1, struct file_lock *fl) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->lock(orig, arg1, fl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||||
|
static ssize_t ksu_wrapper_sendpage(struct file *fp, struct page *pg, int arg1, size_t sz, loff_t *off, int arg2) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->sendpage) {
|
||||||
|
return orig->f_op->sendpage(orig, pg, arg1, sz, off, arg2);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static unsigned long ksu_wrapper_get_unmapped_area(struct file *fp, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->get_unmapped_area) {
|
||||||
|
return orig->f_op->get_unmapped_area(orig, arg1, arg2, arg3, arg4);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static int ksu_wrapper_check_flags(int arg) {}
|
||||||
|
|
||||||
|
static int ksu_wrapper_flock(struct file *fp, int arg1, struct file_lock *fl) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->flock) {
|
||||||
|
return orig->f_op->flock(orig, arg1, fl);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_splice_write(struct pipe_inode_info * pii, struct file *fp, loff_t *off, size_t sz, unsigned int arg1) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->splice_write) {
|
||||||
|
return orig->f_op->splice_write(pii, orig, off, sz, arg1);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_splice_read(struct file *fp, loff_t *off, struct pipe_inode_info *pii, size_t sz, unsigned int arg1) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->splice_read) {
|
||||||
|
return orig->f_op->splice_read(orig, off, pii, sz, arg1);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||||
|
void ksu_wrapper_splice_eof(struct file *fp) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->splice_eof) {
|
||||||
|
return orig->f_op->splice_eof(orig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||||
|
static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lease **fl, void **p) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->setlease) {
|
||||||
|
return orig->f_op->setlease(orig, arg1, fl, p);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||||
|
static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lock **fl, void **p) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->setlease) {
|
||||||
|
return orig->f_op->setlease(orig, arg1, fl, p);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int ksu_wrapper_setlease(struct file *fp, long arg1, struct file_lock **fl, void **p) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->setlease) {
|
||||||
|
return orig->f_op->setlease(orig, arg1, fl, p);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static long ksu_wrapper_fallocate(struct file *fp, int mode, loff_t offset, loff_t len) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->fallocate) {
|
||||||
|
return orig->f_op->fallocate(orig, mode, offset, len);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) {
|
||||||
|
struct ksu_file_wrapper* data = f->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->show_fdinfo) {
|
||||||
|
orig->f_op->show_fdinfo(m, orig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_copy_file_range(struct file *f1, loff_t off1, struct file *f2,
|
||||||
|
loff_t off2, size_t sz, unsigned int flags) {
|
||||||
|
// TODO: determine which file to use
|
||||||
|
struct ksu_file_wrapper* data = f1->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->copy_file_range) {
|
||||||
|
return orig->f_op->copy_file_range(orig, off1, f2, off2, sz, flags);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static loff_t ksu_wrapper_remap_file_range(struct file *file_in, loff_t pos_in,
|
||||||
|
struct file *file_out, loff_t pos_out,
|
||||||
|
loff_t len, unsigned int remap_flags) {
|
||||||
|
// TODO: determine which file to use
|
||||||
|
struct ksu_file_wrapper* data = file_in->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->remap_file_range) {
|
||||||
|
return orig->f_op->remap_file_range(orig, pos_in, file_out, pos_out, len, remap_flags);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ksu_wrapper_fadvise(struct file *fp, loff_t off1, loff_t off2, int flags) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->fadvise) {
|
||||||
|
return orig->f_op->fadvise(orig, off1, off2, flags);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ksu_wrapper_release(struct inode *inode, struct file *filp) {
|
||||||
|
ksu_delete_file_wrapper(filp->private_data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ksu_file_wrapper* ksu_create_file_wrapper(struct file* fp) {
|
||||||
|
struct ksu_file_wrapper* p = kcalloc(sizeof(struct ksu_file_wrapper), 1, GFP_KERNEL);
|
||||||
|
if (!p) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_file(fp);
|
||||||
|
|
||||||
|
p->orig = fp;
|
||||||
|
p->ops.owner = THIS_MODULE;
|
||||||
|
p->ops.llseek = fp->f_op->llseek ? ksu_wrapper_llseek : NULL;
|
||||||
|
p->ops.read = fp->f_op->read ? ksu_wrapper_read : NULL;
|
||||||
|
p->ops.write = fp->f_op->write ? ksu_wrapper_write : NULL;
|
||||||
|
p->ops.read_iter = fp->f_op->read_iter ? ksu_wrapper_read_iter : NULL;
|
||||||
|
p->ops.write_iter = fp->f_op->write_iter ? ksu_wrapper_write_iter : NULL;
|
||||||
|
p->ops.iopoll = fp->f_op->iopoll ? ksu_wrapper_iopoll : NULL;
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||||
|
p->ops.iterate = fp->f_op->iterate ? ksu_wrapper_iterate : NULL;
|
||||||
|
#endif
|
||||||
|
p->ops.iterate_shared = fp->f_op->iterate_shared ? ksu_wrapper_iterate_shared : NULL;
|
||||||
|
p->ops.poll = fp->f_op->poll ? ksu_wrapper_poll : NULL;
|
||||||
|
p->ops.unlocked_ioctl = fp->f_op->unlocked_ioctl ? ksu_wrapper_unlocked_ioctl : NULL;
|
||||||
|
p->ops.compat_ioctl = fp->f_op->compat_ioctl ? ksu_wrapper_compat_ioctl : NULL;
|
||||||
|
p->ops.mmap = fp->f_op->mmap ? ksu_wrapper_mmap : NULL;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||||
|
p->ops.fop_flags = fp->f_op->fop_flags;
|
||||||
|
#else
|
||||||
|
p->ops.mmap_supported_flags = fp->f_op->mmap_supported_flags;
|
||||||
|
#endif
|
||||||
|
p->ops.open = fp->f_op->open ? ksu_wrapper_open : NULL;
|
||||||
|
p->ops.flush = fp->f_op->flush ? ksu_wrapper_flush : NULL;
|
||||||
|
p->ops.release = ksu_wrapper_release;
|
||||||
|
p->ops.fsync = fp->f_op->fsync ? ksu_wrapper_fsync : NULL;
|
||||||
|
p->ops.fasync = fp->f_op->fasync ? ksu_wrapper_fasync : NULL;
|
||||||
|
p->ops.lock = fp->f_op->lock ? ksu_wrapper_lock : NULL;
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||||
|
p->ops.sendpage = fp->f_op->sendpage ? ksu_wrapper_sendpage : NULL;
|
||||||
|
#endif
|
||||||
|
p->ops.get_unmapped_area = fp->f_op->get_unmapped_area ? ksu_wrapper_get_unmapped_area : NULL;
|
||||||
|
p->ops.check_flags = fp->f_op->check_flags;
|
||||||
|
p->ops.flock = fp->f_op->flock ? ksu_wrapper_flock : NULL;
|
||||||
|
p->ops.splice_write = fp->f_op->splice_write ? ksu_wrapper_splice_write : NULL;
|
||||||
|
p->ops.splice_read = fp->f_op->splice_read ? ksu_wrapper_splice_read : NULL;
|
||||||
|
p->ops.setlease = fp->f_op->setlease ? ksu_wrapper_setlease : NULL;
|
||||||
|
p->ops.fallocate = fp->f_op->fallocate ? ksu_wrapper_fallocate : NULL;
|
||||||
|
p->ops.show_fdinfo = fp->f_op->show_fdinfo ? ksu_wrapper_show_fdinfo : NULL;
|
||||||
|
p->ops.copy_file_range = fp->f_op->copy_file_range ? ksu_wrapper_copy_file_range : NULL;
|
||||||
|
p->ops.remap_file_range = fp->f_op->remap_file_range ? ksu_wrapper_remap_file_range : NULL;
|
||||||
|
p->ops.fadvise = fp->f_op->fadvise ? ksu_wrapper_fadvise : NULL;
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||||
|
p->ops.splice_eof = fp->f_op->splice_eof ? ksu_wrapper_splice_eof : NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_delete_file_wrapper(struct ksu_file_wrapper* data) {
|
||||||
|
fput((struct file*) data->orig);
|
||||||
|
kfree(data);
|
||||||
|
}
|
||||||
14
kernel/file_wrapper.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef KSU_FILE_WRAPPER_H
|
||||||
|
#define KSU_FILE_WRAPPER_H
|
||||||
|
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
|
||||||
|
struct ksu_file_wrapper {
|
||||||
|
struct file* orig;
|
||||||
|
struct file_operations ops;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_file_wrapper* ksu_create_file_wrapper(struct file* fp);
|
||||||
|
void ksu_delete_file_wrapper(struct ksu_file_wrapper* data);
|
||||||
|
#endif // KSU_FILE_WRAPPER_H
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#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
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
#include <linux/version.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/nsproxy.h>
|
|
||||||
#include <linux/sched/task.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include "klog.h" // IWYU pragma: keep
|
|
||||||
#include "kernel_compat.h"
|
|
||||||
|
|
||||||
extern struct task_struct init_task;
|
|
||||||
|
|
||||||
// mnt_ns context switch for environment that android_init->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns, such as WSA
|
|
||||||
struct ksu_ns_fs_saved {
|
|
||||||
struct nsproxy *ns;
|
|
||||||
struct fs_struct *fs;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void ksu_save_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
|
||||||
{
|
|
||||||
ns_fs_saved->ns = current->nsproxy;
|
|
||||||
ns_fs_saved->fs = current->fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ksu_load_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
|
||||||
{
|
|
||||||
current->nsproxy = ns_fs_saved->ns;
|
|
||||||
current->fs = ns_fs_saved->fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool android_context_saved_checked = false;
|
|
||||||
static bool android_context_saved_enabled = false;
|
|
||||||
static struct ksu_ns_fs_saved android_context_saved;
|
|
||||||
|
|
||||||
void ksu_android_ns_fs_check()
|
|
||||||
{
|
|
||||||
if (android_context_saved_checked)
|
|
||||||
return;
|
|
||||||
android_context_saved_checked = true;
|
|
||||||
task_lock(current);
|
|
||||||
if (current->nsproxy && current->fs &&
|
|
||||||
current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) {
|
|
||||||
android_context_saved_enabled = true;
|
|
||||||
pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n",
|
|
||||||
current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns);
|
|
||||||
ksu_save_ns_fs(&android_context_saved);
|
|
||||||
} else {
|
|
||||||
pr_info("android context saved disabled\n");
|
|
||||||
}
|
|
||||||
task_unlock(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
|
||||||
{
|
|
||||||
// switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns
|
|
||||||
struct ksu_ns_fs_saved saved;
|
|
||||||
if (android_context_saved_enabled) {
|
|
||||||
pr_info("start switch current nsproxy and fs to android context\n");
|
|
||||||
task_lock(current);
|
|
||||||
ksu_save_ns_fs(&saved);
|
|
||||||
ksu_load_ns_fs(&android_context_saved);
|
|
||||||
task_unlock(current);
|
|
||||||
}
|
|
||||||
struct file *fp = filp_open(filename, flags, mode);
|
|
||||||
if (android_context_saved_enabled) {
|
|
||||||
task_lock(current);
|
|
||||||
ksu_load_ns_fs(&saved);
|
|
||||||
task_unlock(current);
|
|
||||||
pr_info("switch current nsproxy and fs back to saved successfully\n");
|
|
||||||
}
|
|
||||||
return fp;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
|
||||||
loff_t *pos)
|
|
||||||
{
|
|
||||||
return kernel_read(p, buf, count, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
|
|
||||||
loff_t *pos)
|
|
||||||
{
|
|
||||||
return kernel_write(p, buf, count, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
|
||||||
long count)
|
|
||||||
{
|
|
||||||
return strncpy_from_user_nofault(dst, unsafe_addr, count);
|
|
||||||
}
|
|
||||||
@@ -3,33 +3,22 @@
|
|||||||
|
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include "ss/policydb.h"
|
|
||||||
#include "linux/key.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adapt to Huawei HISI kernel without affecting other kernels ,
|
* ksu_copy_from_user_retry
|
||||||
* Huawei Hisi Kernel EBITMAP Enable or Disable Flag ,
|
* try nofault copy first, if it fails, try with plain
|
||||||
* From ss/ebitmap.h
|
* paramters are the same as copy_from_user
|
||||||
|
* 0 = success
|
||||||
*/
|
*/
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \
|
static long ksu_copy_from_user_retry(void *to,
|
||||||
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \
|
const void __user *from, unsigned long count)
|
||||||
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \
|
{
|
||||||
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
long ret = copy_from_user_nofault(to, from, count);
|
||||||
#ifdef HISI_SELINUX_EBITMAP_RO
|
if (likely(!ret))
|
||||||
#define CONFIG_IS_HW_HISI
|
return ret;
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern long ksu_strncpy_from_user_nofault(char *dst,
|
// we faulted! fallback to slow path
|
||||||
const void __user *unsafe_addr,
|
return copy_from_user(to, from, count);
|
||||||
long count);
|
}
|
||||||
|
|
||||||
extern void ksu_android_ns_fs_check();
|
|
||||||
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
|
|
||||||
umode_t mode);
|
|
||||||
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);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
181
kernel/kernel_umount.c
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
|
#include <linux/nsproxy.h>
|
||||||
|
#include <linux/path.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include "kernel_umount.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "feature.h"
|
||||||
|
#include "ksud.h"
|
||||||
|
|
||||||
|
#include "umount_manager.h"
|
||||||
|
#include "sulog.h"
|
||||||
|
|
||||||
|
static bool ksu_kernel_umount_enabled = true;
|
||||||
|
|
||||||
|
static int kernel_umount_feature_get(u64 *value)
|
||||||
|
{
|
||||||
|
*value = ksu_kernel_umount_enabled ? 1 : 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kernel_umount_feature_set(u64 value)
|
||||||
|
{
|
||||||
|
bool enable = value != 0;
|
||||||
|
ksu_kernel_umount_enabled = enable;
|
||||||
|
pr_info("kernel_umount: set to %d\n", enable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ksu_feature_handler kernel_umount_handler = {
|
||||||
|
.feature_id = KSU_FEATURE_KERNEL_UMOUNT,
|
||||||
|
.name = "kernel_umount",
|
||||||
|
.get_handler = kernel_umount_feature_get,
|
||||||
|
.set_handler = kernel_umount_feature_set,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int path_umount(struct path *path, int flags);
|
||||||
|
|
||||||
|
static void ksu_umount_mnt(struct path *path, int flags)
|
||||||
|
{
|
||||||
|
int err = path_umount(path, flags);
|
||||||
|
if (err) {
|
||||||
|
pr_info("umount %s failed: %d\n", path->dentry->d_iname, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void try_umount(const char *mnt, int flags)
|
||||||
|
{
|
||||||
|
struct path path;
|
||||||
|
int err = kern_path(mnt, 0, &path);
|
||||||
|
if (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.dentry != path.mnt->mnt_root) {
|
||||||
|
// it is not root mountpoint, maybe umounted by others already.
|
||||||
|
path_put(&path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ksu_umount_mnt(&path, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct umount_tw {
|
||||||
|
struct callback_head cb;
|
||||||
|
const struct cred *old_cred;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void umount_tw_func(struct callback_head *cb)
|
||||||
|
{
|
||||||
|
struct umount_tw *tw = container_of(cb, struct umount_tw, cb);
|
||||||
|
const struct cred *saved = NULL;
|
||||||
|
if (tw->old_cred) {
|
||||||
|
saved = override_creds(tw->old_cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mount_entry *entry;
|
||||||
|
down_read(&mount_list_lock);
|
||||||
|
list_for_each_entry(entry, &mount_list, list) {
|
||||||
|
pr_info("%s: unmounting: %s flags 0x%x\n", __func__, entry->umountable, entry->flags);
|
||||||
|
try_umount(entry->umountable, entry->flags);
|
||||||
|
}
|
||||||
|
up_read(&mount_list_lock);
|
||||||
|
|
||||||
|
ksu_umount_manager_execute_all(tw->old_cred);
|
||||||
|
|
||||||
|
if (saved)
|
||||||
|
revert_creds(saved);
|
||||||
|
|
||||||
|
if (tw->old_cred)
|
||||||
|
put_cred(tw->old_cred);
|
||||||
|
|
||||||
|
kfree(tw);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
|
||||||
|
{
|
||||||
|
struct umount_tw *tw;
|
||||||
|
|
||||||
|
// if there isn't any module mounted, just ignore it!
|
||||||
|
if (!ksu_module_mounted) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_kernel_umount_enabled) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are 5 scenarios:
|
||||||
|
// 1. Normal app: zygote -> appuid
|
||||||
|
// 2. Isolated process forked from zygote: zygote -> isolated_process
|
||||||
|
// 3. App zygote forked from zygote: zygote -> appuid
|
||||||
|
// 4. Isolated process froked from app zygote: appuid -> isolated_process (already handled by 3)
|
||||||
|
// 5. Isolated process froked from webview zygote (no need to handle, app cannot run custom code)
|
||||||
|
if (!is_appuid(new_uid) && !is_isolated_process(new_uid)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_uid_should_umount(new_uid) && !is_isolated_process(new_uid)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check old process's selinux context, if it is not zygote, ignore it!
|
||||||
|
// because some su apps may setuid to untrusted_app but they are in global mount namespace
|
||||||
|
// when we umount for such process, that is a disaster!
|
||||||
|
// also handle case 4 and 5
|
||||||
|
bool is_zygote_child = is_zygote(get_current_cred());
|
||||||
|
if (!is_zygote_child) {
|
||||||
|
pr_info("handle umount ignore non zygote child: %d\n", current->pid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_syscall(new_uid, NULL, "setuid", NULL);
|
||||||
|
#endif
|
||||||
|
// umount the target mnt
|
||||||
|
pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid);
|
||||||
|
|
||||||
|
tw = kzalloc(sizeof(*tw), GFP_ATOMIC);
|
||||||
|
if (!tw)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tw->old_cred = get_current_cred();
|
||||||
|
tw->cb.func = umount_tw_func;
|
||||||
|
|
||||||
|
int err = task_work_add(current, &tw->cb, TWA_RESUME);
|
||||||
|
if (err) {
|
||||||
|
if (tw->old_cred) {
|
||||||
|
put_cred(tw->old_cred);
|
||||||
|
}
|
||||||
|
kfree(tw);
|
||||||
|
pr_warn("unmount add task_work failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_kernel_umount_init(void)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
rc = ksu_umount_manager_init();
|
||||||
|
if (rc) {
|
||||||
|
pr_err("Failed to initialize umount manager: %d\n", rc);
|
||||||
|
}
|
||||||
|
if (ksu_register_feature_handler(&kernel_umount_handler)) {
|
||||||
|
pr_err("Failed to register kernel_umount feature handler\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_kernel_umount_exit(void)
|
||||||
|
{
|
||||||
|
ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT);
|
||||||
|
}
|
||||||
25
kernel/kernel_umount.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef __KSU_H_KERNEL_UMOUNT
|
||||||
|
#define __KSU_H_KERNEL_UMOUNT
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/rwsem.h>
|
||||||
|
|
||||||
|
void ksu_kernel_umount_init(void);
|
||||||
|
void ksu_kernel_umount_exit(void);
|
||||||
|
|
||||||
|
void try_umount(const char *mnt, int flags);
|
||||||
|
|
||||||
|
// Handler function to be called from setresuid hook
|
||||||
|
int ksu_handle_umount(uid_t old_uid, uid_t new_uid);
|
||||||
|
|
||||||
|
// for the umount list
|
||||||
|
struct mount_entry {
|
||||||
|
char *umountable;
|
||||||
|
unsigned int flags;
|
||||||
|
struct list_head list;
|
||||||
|
};
|
||||||
|
extern struct list_head mount_list;
|
||||||
|
extern struct rw_semaphore mount_list_lock;
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -3,4 +3,4 @@ obj-y += compact.o
|
|||||||
obj-y += super_access.o
|
obj-y += super_access.o
|
||||||
|
|
||||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||||
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
|
#include <asm/elf.h>
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
@@ -29,74 +29,72 @@
|
|||||||
#include "../allowlist.h"
|
#include "../allowlist.h"
|
||||||
#include "../manager.h"
|
#include "../manager.h"
|
||||||
|
|
||||||
unsigned long sukisu_compact_find_symbol(const char* name);
|
static int sukisu_is_su_allow_uid(uid_t uid)
|
||||||
|
{
|
||||||
// ======================================================================
|
return ksu_is_allow_uid_for_current(uid) ? 1 : 0;
|
||||||
// 兼容函数 for KPM
|
|
||||||
|
|
||||||
static
|
|
||||||
int sukisu_is_su_allow_uid(uid_t uid) {
|
|
||||||
return ksu_is_allow_uid(uid) ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static int sukisu_get_ap_mod_exclude(uid_t uid)
|
||||||
int sukisu_get_ap_mod_exclude(uid_t uid) {
|
{
|
||||||
// Not supported
|
return 0; /* Not supported */
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static int sukisu_is_uid_should_umount(uid_t uid)
|
||||||
int sukisu_is_uid_should_umount(uid_t uid) {
|
{
|
||||||
return ksu_uid_should_umount(uid) ? 1 : 0;
|
return ksu_uid_should_umount(uid) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static int sukisu_is_current_uid_manager(void)
|
||||||
int sukisu_is_current_uid_manager() {
|
{
|
||||||
return is_manager();
|
return is_manager();
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static uid_t sukisu_get_manager_uid(void)
|
||||||
uid_t sukisu_get_manager_uid() {
|
{
|
||||||
return ksu_manager_uid;
|
return ksu_manager_uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================================================================
|
static void sukisu_set_manager_uid(uid_t uid, int force)
|
||||||
|
{
|
||||||
|
if (force || ksu_manager_uid == -1)
|
||||||
|
ksu_manager_uid = uid;
|
||||||
|
}
|
||||||
|
|
||||||
struct CompactAddressSymbol {
|
struct CompactAddressSymbol {
|
||||||
const char* symbol_name;
|
const char *symbol_name;
|
||||||
void* addr;
|
void *addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct CompactAddressSymbol address_symbol [] = {
|
unsigned long sukisu_compact_find_symbol(const char *name);
|
||||||
|
|
||||||
|
static struct CompactAddressSymbol address_symbol[] = {
|
||||||
{ "kallsyms_lookup_name", &kallsyms_lookup_name },
|
{ "kallsyms_lookup_name", &kallsyms_lookup_name },
|
||||||
{ "compact_find_symbol", &sukisu_compact_find_symbol },
|
{ "compact_find_symbol", &sukisu_compact_find_symbol },
|
||||||
{ "is_run_in_sukisu_ultra", (void*)1 },
|
{ "is_run_in_sukisu_ultra", (void *)1 },
|
||||||
{ "is_su_allow_uid", &sukisu_is_su_allow_uid },
|
{ "is_su_allow_uid", &sukisu_is_su_allow_uid },
|
||||||
{ "get_ap_mod_exclude", &sukisu_get_ap_mod_exclude },
|
{ "get_ap_mod_exclude", &sukisu_get_ap_mod_exclude },
|
||||||
{ "is_uid_should_umount", &sukisu_is_uid_should_umount },
|
{ "is_uid_should_umount", &sukisu_is_uid_should_umount },
|
||||||
{ "is_current_uid_manager", &sukisu_is_current_uid_manager },
|
{ "is_current_uid_manager", &sukisu_is_current_uid_manager },
|
||||||
{ "get_manager_uid", &sukisu_get_manager_uid }
|
{ "get_manager_uid", &sukisu_get_manager_uid },
|
||||||
|
{ "sukisu_set_manager_uid", &sukisu_set_manager_uid }
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned long sukisu_compact_find_symbol(const char* name) {
|
unsigned long sukisu_compact_find_symbol(const char* name)
|
||||||
|
{
|
||||||
int i;
|
int i;
|
||||||
unsigned long addr;
|
unsigned long addr;
|
||||||
|
|
||||||
// 先自己在地址表部分查出来
|
for (i = 0; i < (sizeof(address_symbol) / sizeof(struct CompactAddressSymbol)); i++) {
|
||||||
for(i = 0; i < (sizeof(address_symbol) / sizeof(struct CompactAddressSymbol)); i++) {
|
struct CompactAddressSymbol *symbol = &address_symbol[i];
|
||||||
struct CompactAddressSymbol* symbol = &address_symbol[i];
|
|
||||||
if(strcmp(name, symbol->symbol_name) == 0) {
|
if (strcmp(name, symbol->symbol_name) == 0)
|
||||||
return (unsigned long) symbol->addr;
|
return (unsigned long)symbol->addr;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过内核来查
|
|
||||||
addr = kallsyms_lookup_name(name);
|
addr = kallsyms_lookup_name(name);
|
||||||
if(addr) {
|
if (addr)
|
||||||
return addr;
|
return addr;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(sukisu_compact_find_symbol);
|
EXPORT_SYMBOL(sukisu_compact_find_symbol);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#ifndef ___SUKISU_KPM_COMPACT_H
|
#ifndef __SUKISU_KPM_COMPACT_H
|
||||||
#define ___SUKISU_KPM_COMPACT_H
|
#define __SUKISU_KPM_COMPACT_H
|
||||||
|
|
||||||
unsigned long sukisu_compact_find_symbol(const char* name);
|
extern unsigned long sukisu_compact_find_symbol(const char *name);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
322
kernel/kpm/kpm.c
@@ -8,13 +8,11 @@
|
|||||||
* 集成了 ELF 解析、内存布局、符号处理、重定位(支持 ARM64 重定位类型)
|
* 集成了 ELF 解析、内存布局、符号处理、重定位(支持 ARM64 重定位类型)
|
||||||
* 并参照KernelPatch的标准KPM格式实现加载和控制
|
* 并参照KernelPatch的标准KPM格式实现加载和控制
|
||||||
*/
|
*/
|
||||||
#include <linux/export.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/kernfs.h>
|
#include <linux/kernfs.h>
|
||||||
#include <linux/file.h>
|
#include <linux/file.h>
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/elf.h>
|
#include <linux/elf.h>
|
||||||
@@ -23,27 +21,26 @@
|
|||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
|
#include <asm/elf.h>
|
||||||
#include <linux/vmalloc.h>
|
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/vmalloc.h>
|
|
||||||
#include <linux/set_memory.h>
|
#include <linux/set_memory.h>
|
||||||
#include <linux/version.h>
|
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <asm/insn.h>
|
#include <asm/insn.h>
|
||||||
#include <linux/kprobes.h>
|
#include <linux/kprobes.h>
|
||||||
#include <linux/stacktrace.h>
|
#include <linux/stacktrace.h>
|
||||||
#include <linux/kallsyms.h>
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) && defined(CONFIG_MODULES)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) && defined(CONFIG_MODULES)
|
||||||
#include <linux/moduleloader.h> // 需要启用 CONFIG_MODULES
|
#include <linux/moduleloader.h>
|
||||||
#endif
|
#endif
|
||||||
#include "kpm.h"
|
#include "kpm.h"
|
||||||
#include "compact.h"
|
#include "compact.h"
|
||||||
|
|
||||||
|
#define KPM_NAME_LEN 32
|
||||||
|
#define KPM_ARGS_LEN 1024
|
||||||
|
|
||||||
#ifndef NO_OPTIMIZE
|
#ifndef NO_OPTIMIZE
|
||||||
#if defined(__GNUC__) && !defined(__clang__)
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
#define NO_OPTIMIZE __attribute__((optimize("O0")))
|
#define NO_OPTIMIZE __attribute__((optimize("O0")))
|
||||||
@@ -54,131 +51,232 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ============================================================================================
|
noinline NO_OPTIMIZE void sukisu_kpm_load_module_path(const char *path,
|
||||||
|
const char *args, void *ptr, int *result)
|
||||||
|
{
|
||||||
|
pr_info("kpm: Stub function called (sukisu_kpm_load_module_path). "
|
||||||
|
"path=%s args=%s ptr=%p\n", path, args, ptr);
|
||||||
|
|
||||||
noinline
|
__asm__ volatile("nop");
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_load_module_path(const char* path, const char* args, void* ptr, void __user* result) {
|
|
||||||
// This is a KPM module stub.
|
|
||||||
int res = -1;
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_load_module_path). path=%s args=%s ptr=%p\n", path, args, ptr);
|
|
||||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
|
||||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
noinline
|
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_unload_module(const char* name, void* ptr, void __user* result) {
|
|
||||||
// This is a KPM module stub.
|
|
||||||
int res = -1;
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_unload_module). name=%s ptr=%p\n", name, ptr);
|
|
||||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
|
||||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
|
||||||
}
|
|
||||||
|
|
||||||
noinline
|
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_num(void __user* result) {
|
|
||||||
// This is a KPM module stub.
|
|
||||||
int res = 0;
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_num).\n");
|
|
||||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
|
||||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
|
||||||
}
|
|
||||||
|
|
||||||
noinline
|
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_info(const char* name, void __user* out, void __user* result) {
|
|
||||||
// This is a KPM module stub.
|
|
||||||
int res = -1;
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_info). name=%s buffer=%p\n", name, out);
|
|
||||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
|
||||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
|
||||||
}
|
|
||||||
|
|
||||||
noinline
|
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_list(void __user* out, unsigned int bufferSize, void __user* result) {
|
|
||||||
// This is a KPM module stub.
|
|
||||||
int res = -1;
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_list). buffer=%p size=%d\n", out, bufferSize);
|
|
||||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
|
||||||
}
|
|
||||||
|
|
||||||
noinline
|
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_control(void __user* name, void __user* args, void __user* result) {
|
|
||||||
// This is a KPM module stub.
|
|
||||||
int res = -1;
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_control). name=%p args=%p\n", name, args);
|
|
||||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
|
||||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
|
||||||
}
|
|
||||||
|
|
||||||
noinline
|
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_version(void __user* out, unsigned int bufferSize, void __user* result) {
|
|
||||||
int res = -1;
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_version). buffer=%p size=%d\n", out, bufferSize);
|
|
||||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(sukisu_kpm_load_module_path);
|
EXPORT_SYMBOL(sukisu_kpm_load_module_path);
|
||||||
|
|
||||||
|
noinline NO_OPTIMIZE void sukisu_kpm_unload_module(const char *name,
|
||||||
|
void *ptr, int *result)
|
||||||
|
{
|
||||||
|
pr_info("kpm: Stub function called (sukisu_kpm_unload_module). "
|
||||||
|
"name=%s ptr=%p\n", name, ptr);
|
||||||
|
|
||||||
|
__asm__ volatile("nop");
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_unload_module);
|
EXPORT_SYMBOL(sukisu_kpm_unload_module);
|
||||||
|
|
||||||
|
noinline NO_OPTIMIZE void sukisu_kpm_num(int *result)
|
||||||
|
{
|
||||||
|
pr_info("kpm: Stub function called (sukisu_kpm_num).\n");
|
||||||
|
|
||||||
|
__asm__ volatile("nop");
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_num);
|
EXPORT_SYMBOL(sukisu_kpm_num);
|
||||||
|
|
||||||
|
noinline NO_OPTIMIZE void sukisu_kpm_info(const char *name, char *buf, int bufferSize,
|
||||||
|
int *size)
|
||||||
|
{
|
||||||
|
pr_info("kpm: Stub function called (sukisu_kpm_info). "
|
||||||
|
"name=%s buffer=%p\n", name, buf);
|
||||||
|
|
||||||
|
__asm__ volatile("nop");
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_info);
|
EXPORT_SYMBOL(sukisu_kpm_info);
|
||||||
|
|
||||||
|
noinline NO_OPTIMIZE void sukisu_kpm_list(void *out, int bufferSize,
|
||||||
|
int *result)
|
||||||
|
{
|
||||||
|
pr_info("kpm: Stub function called (sukisu_kpm_list). "
|
||||||
|
"buffer=%p size=%d\n", out, bufferSize);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_list);
|
EXPORT_SYMBOL(sukisu_kpm_list);
|
||||||
EXPORT_SYMBOL(sukisu_kpm_version);
|
|
||||||
|
noinline NO_OPTIMIZE void sukisu_kpm_control(const char *name, const char *args, long arg_len,
|
||||||
|
int *result)
|
||||||
|
{
|
||||||
|
pr_info("kpm: Stub function called (sukisu_kpm_control). "
|
||||||
|
"name=%p args=%p arg_len=%ld\n", name, args, arg_len);
|
||||||
|
|
||||||
|
__asm__ volatile("nop");
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_control);
|
EXPORT_SYMBOL(sukisu_kpm_control);
|
||||||
|
|
||||||
noinline
|
noinline NO_OPTIMIZE void sukisu_kpm_version(char *buf, int bufferSize)
|
||||||
int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)
|
|
||||||
{
|
{
|
||||||
if(arg2 == SUKISU_KPM_LOAD) {
|
pr_info("kpm: Stub function called (sukisu_kpm_version). "
|
||||||
char kernel_load_path[256] = { 0 };
|
"buffer=%p\n", buf);
|
||||||
char kernel_args_buffer[256] = { 0 };
|
}
|
||||||
|
EXPORT_SYMBOL(sukisu_kpm_version);
|
||||||
|
|
||||||
if(arg3 == 0) {
|
noinline int sukisu_handle_kpm(unsigned long control_code, unsigned long arg1, unsigned long arg2,
|
||||||
return -1;
|
unsigned long result_code)
|
||||||
|
{
|
||||||
|
int res = -1;
|
||||||
|
if (control_code == SUKISU_KPM_LOAD) {
|
||||||
|
char kernel_load_path[256];
|
||||||
|
char kernel_args_buffer[256];
|
||||||
|
|
||||||
|
if (arg1 == 0) {
|
||||||
|
res = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_ok(arg1, 255)) {
|
||||||
|
goto invalid_arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy_from_user((char*)&kernel_load_path, (const char __user *)arg3, 255);
|
strncpy_from_user((char *)&kernel_load_path, (const char *)arg1, 255);
|
||||||
if(arg4 != 0) {
|
|
||||||
strncpy_from_user((char*)&kernel_args_buffer, (const char __user *)arg4, 255);
|
if (arg2 != 0) {
|
||||||
}
|
if (!access_ok(arg2, 255)) {
|
||||||
sukisu_kpm_load_module_path((const char*)&kernel_load_path, (const char*) &kernel_args_buffer, NULL, (void __user*) arg5);
|
goto invalid_arg;
|
||||||
} else if(arg2 == SUKISU_KPM_UNLOAD) {
|
}
|
||||||
char kernel_name_buffer[256] = { 0 };
|
|
||||||
|
|
||||||
if(arg3 == 0) {
|
strncpy_from_user((char *)&kernel_args_buffer, (const char *)arg2, 255);
|
||||||
return -1;
|
}
|
||||||
|
|
||||||
|
sukisu_kpm_load_module_path((const char *)&kernel_load_path,
|
||||||
|
(const char *)&kernel_args_buffer, NULL, &res);
|
||||||
|
} else if (control_code == SUKISU_KPM_UNLOAD) {
|
||||||
|
char kernel_name_buffer[256];
|
||||||
|
|
||||||
|
if (arg1 == 0) {
|
||||||
|
res = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_ok(arg1, sizeof(kernel_name_buffer))) {
|
||||||
|
goto invalid_arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy_from_user((char*)&kernel_name_buffer, (const char __user *)arg3, 255);
|
strncpy_from_user((char *)&kernel_name_buffer, (const char *)arg1, sizeof(kernel_name_buffer));
|
||||||
sukisu_kpm_unload_module((const char*) &kernel_name_buffer, NULL, (void __user*) arg5);
|
|
||||||
} else if(arg2 == SUKISU_KPM_NUM) {
|
sukisu_kpm_unload_module((const char *)&kernel_name_buffer, NULL, &res);
|
||||||
sukisu_kpm_num((void __user*) arg5);
|
} else if (control_code == SUKISU_KPM_NUM) {
|
||||||
} else if(arg2 == SUKISU_KPM_INFO) {
|
sukisu_kpm_num(&res);
|
||||||
char kernel_name_buffer[256] = { 0 };
|
} else if (control_code == SUKISU_KPM_INFO) {
|
||||||
|
char kernel_name_buffer[256];
|
||||||
|
char buf[256];
|
||||||
|
int size;
|
||||||
|
|
||||||
if(arg3 == 0 || arg4 == 0) {
|
if (arg1 == 0 || arg2 == 0) {
|
||||||
return -1;
|
res = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_ok(arg1, sizeof(kernel_name_buffer))) {
|
||||||
|
goto invalid_arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy_from_user((char*)&kernel_name_buffer, (const char __user *)arg3, 255);
|
strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg1, sizeof(kernel_name_buffer));
|
||||||
sukisu_kpm_info((const char*) &kernel_name_buffer, (char __user*) arg4, (void __user*) arg5);
|
|
||||||
} else if(arg2 == SUKISU_KPM_LIST) {
|
sukisu_kpm_info((const char *)&kernel_name_buffer, (char *)&buf, sizeof(buf), &size);
|
||||||
sukisu_kpm_list((char __user*) arg3, (unsigned int) arg4, (void __user*) arg5);
|
|
||||||
} else if(arg2 == SUKISU_KPM_VERSION) {
|
if (!access_ok(arg2, size)) {
|
||||||
sukisu_kpm_version((char __user*) arg3, (unsigned int) arg4, (void __user*) arg5);
|
goto invalid_arg;
|
||||||
} else if(arg2 == SUKISU_KPM_CONTROL) {
|
}
|
||||||
sukisu_kpm_control((char __user*) arg3, (char __user*) arg4, (void __user*) arg5);
|
|
||||||
|
res = copy_to_user(arg2, &buf, size);
|
||||||
|
|
||||||
|
} else if (control_code == SUKISU_KPM_LIST) {
|
||||||
|
char buf[1024];
|
||||||
|
int len = (int) arg2;
|
||||||
|
|
||||||
|
if (len <= 0) {
|
||||||
|
res = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_ok(arg2, len)) {
|
||||||
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
sukisu_kpm_list((char *)&buf, sizeof(buf), &res);
|
||||||
|
|
||||||
|
if (res > len) {
|
||||||
|
res = -ENOBUFS;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy_to_user(arg1, &buf, len) != 0)
|
||||||
|
pr_info("kpm: Copy to user failed.");
|
||||||
|
|
||||||
|
} else if (control_code == SUKISU_KPM_CONTROL) {
|
||||||
|
char kpm_name[KPM_NAME_LEN] = { 0 };
|
||||||
|
char kpm_args[KPM_ARGS_LEN] = { 0 };
|
||||||
|
|
||||||
|
if (!access_ok(arg1, sizeof(kpm_name))) {
|
||||||
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_ok(arg2, sizeof(kpm_args))) {
|
||||||
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
long name_len = strncpy_from_user((char *)&kpm_name, (const char __user *)arg1, sizeof(kpm_name));
|
||||||
|
if (name_len <= 0) {
|
||||||
|
res = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
long arg_len = strncpy_from_user((char *)&kpm_args, (const char __user *)arg2, sizeof(kpm_args));
|
||||||
|
|
||||||
|
sukisu_kpm_control((const char *)&kpm_name, (const char *)&kpm_args, arg_len, &res);
|
||||||
|
|
||||||
|
} else if (control_code == SUKISU_KPM_VERSION) {
|
||||||
|
char buffer[256] = {0};
|
||||||
|
|
||||||
|
sukisu_kpm_version((char*) &buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
unsigned int outlen = (unsigned int) arg2;
|
||||||
|
int len = strlen(buffer);
|
||||||
|
if (len >= outlen) len = outlen - 1;
|
||||||
|
|
||||||
|
res = copy_to_user(arg1, &buffer, len + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (copy_to_user(result_code, &res, sizeof(res)) != 0)
|
||||||
|
pr_info("kpm: Copy to user failed.");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
invalid_arg:
|
||||||
|
pr_err("kpm: invalid pointer detected! arg1: %px arg2: %px\n", (void *)arg1, (void *)arg2);
|
||||||
|
res = -EFAULT;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(sukisu_handle_kpm);
|
||||||
|
|
||||||
|
int sukisu_is_kpm_control_code(unsigned long control_code) {
|
||||||
|
return (control_code >= CMD_KPM_CONTROL &&
|
||||||
|
control_code <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sukisu_is_kpm_control_code(unsigned long arg2) {
|
int do_kpm(void __user *arg)
|
||||||
return (arg2 >= CMD_KPM_CONTROL && arg2 <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
|
{
|
||||||
|
struct ksu_kpm_cmd cmd;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
pr_err("kpm: copy_from_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_ok(cmd.control_code, sizeof(int))) {
|
||||||
|
pr_err("kpm: invalid control_code pointer %px\n", (void *)cmd.control_code);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_ok(cmd.result_code, sizeof(int))) {
|
||||||
|
pr_err("kpm: invalid result_code pointer %px\n", (void *)cmd.result_code);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sukisu_handle_kpm(cmd.control_code, cmd.arg1, cmd.arg2, cmd.result_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(sukisu_handle_kpm);
|
|
||||||
@@ -1,44 +1,70 @@
|
|||||||
#ifndef ___SUKISU_KPM_H
|
#ifndef __SUKISU_KPM_H
|
||||||
#define ___SUKISU_KPM_H
|
#define __SUKISU_KPM_H
|
||||||
|
|
||||||
int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
|
#include <linux/types.h>
|
||||||
int sukisu_is_kpm_control_code(unsigned long arg2);
|
#include <linux/ioctl.h>
|
||||||
|
|
||||||
// KPM控制代码
|
struct ksu_kpm_cmd {
|
||||||
#define CMD_KPM_CONTROL 28
|
__aligned_u64 __user control_code;
|
||||||
#define CMD_KPM_CONTROL_MAX 35
|
__aligned_u64 __user arg1;
|
||||||
|
__aligned_u64 __user arg2;
|
||||||
|
__aligned_u64 __user result_code;
|
||||||
|
};
|
||||||
|
|
||||||
// 控制代码
|
int sukisu_handle_kpm(unsigned long control_code, unsigned long arg3, unsigned long arg4, unsigned long result_code);
|
||||||
|
int sukisu_is_kpm_control_code(unsigned long control_code);
|
||||||
|
int do_kpm(void __user *arg);
|
||||||
|
|
||||||
// prctl(xxx, 28, "PATH", "ARGS")
|
#define KSU_IOCTL_KPM _IOC(_IOC_READ|_IOC_WRITE, 'K', 200, 0)
|
||||||
// success return 0, error return -N
|
|
||||||
#define SUKISU_KPM_LOAD 28
|
|
||||||
|
|
||||||
// prctl(xxx, 29, "NAME")
|
/* KPM Control Code */
|
||||||
// success return 0, error return -N
|
#define CMD_KPM_CONTROL 1
|
||||||
#define SUKISU_KPM_UNLOAD 29
|
#define CMD_KPM_CONTROL_MAX 10
|
||||||
|
|
||||||
// num = prctl(xxx, 30)
|
/* Control Code */
|
||||||
// error return -N
|
/*
|
||||||
// success return +num or 0
|
* prctl(xxx, 1, "PATH", "ARGS")
|
||||||
#define SUKISU_KPM_NUM 30
|
* success return 0, error return -N
|
||||||
|
*/
|
||||||
|
#define SUKISU_KPM_LOAD 1
|
||||||
|
|
||||||
// prctl(xxx, 31, Buffer, BufferSize)
|
/*
|
||||||
// success return +out, error return -N
|
* prctl(xxx, 2, "NAME")
|
||||||
#define SUKISU_KPM_LIST 31
|
* success return 0, error return -N
|
||||||
|
*/
|
||||||
|
#define SUKISU_KPM_UNLOAD 2
|
||||||
|
|
||||||
// prctl(xxx, 32, "NAME", Buffer[256])
|
/*
|
||||||
// success return +out, error return -N
|
* num = prctl(xxx, 3)
|
||||||
#define SUKISU_KPM_INFO 32
|
* error return -N
|
||||||
|
* success return +num or 0
|
||||||
|
*/
|
||||||
|
#define SUKISU_KPM_NUM 3
|
||||||
|
|
||||||
// prctl(xxx, 33, "NAME", "ARGS")
|
/*
|
||||||
// success return KPM's result value
|
* prctl(xxx, 4, Buffer, BufferSize)
|
||||||
// error return -N
|
* success return +out, error return -N
|
||||||
#define SUKISU_KPM_CONTROL 33
|
*/
|
||||||
|
#define SUKISU_KPM_LIST 4
|
||||||
|
|
||||||
// prctl(xxx, 34, buffer, bufferSize)
|
/*
|
||||||
// success return KPM's result value
|
* prctl(xxx, 5, "NAME", Buffer[256])
|
||||||
// error return -N
|
* success return +out, error return -N
|
||||||
#define SUKISU_KPM_VERSION 34
|
*/
|
||||||
|
#define SUKISU_KPM_INFO 5
|
||||||
|
|
||||||
#endif
|
/*
|
||||||
|
* prctl(xxx, 6, "NAME", "ARGS")
|
||||||
|
* success return KPM's result value
|
||||||
|
* error return -N
|
||||||
|
*/
|
||||||
|
#define SUKISU_KPM_CONTROL 6
|
||||||
|
|
||||||
|
/*
|
||||||
|
* prctl(xxx, 7, buffer, bufferSize)
|
||||||
|
* success return KPM's result value
|
||||||
|
* error return -N
|
||||||
|
*/
|
||||||
|
#define SUKISU_KPM_VERSION 7
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
|
#include <asm/elf.h>
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
@@ -24,34 +24,37 @@
|
|||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include "kpm.h"
|
|
||||||
#include "compact.h"
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/stddef.h>
|
#include <linux/stddef.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
#include <linux/mm_types.h>
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <../fs/mount.h>
|
||||||
|
#include "kpm.h"
|
||||||
|
#include "compact.h"
|
||||||
|
|
||||||
// 结构体成员元数据
|
|
||||||
struct DynamicStructMember {
|
struct DynamicStructMember {
|
||||||
const char* name;
|
const char *name;
|
||||||
size_t size;
|
size_t size;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 结构体元数据(包含总大小)
|
|
||||||
struct DynamicStructInfo {
|
struct DynamicStructInfo {
|
||||||
const char* name;
|
const char *name;
|
||||||
size_t count;
|
size_t count;
|
||||||
size_t total_size;
|
size_t total_size;
|
||||||
struct DynamicStructMember* members;
|
struct DynamicStructMember *members;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 定义结构体元数据的宏(直接使用 struct 名称)
|
|
||||||
#define DYNAMIC_STRUCT_BEGIN(struct_name) \
|
#define DYNAMIC_STRUCT_BEGIN(struct_name) \
|
||||||
static struct DynamicStructMember struct_name##_members[] = {
|
static struct DynamicStructMember struct_name##_members[] = {
|
||||||
|
|
||||||
#define DEFINE_MEMBER(struct_name, member) \
|
#define DEFINE_MEMBER(struct_name, member) \
|
||||||
{ \
|
{ \
|
||||||
.name = #member, \
|
.name = #member, \
|
||||||
.size = sizeof(((struct struct_name*)0)->member), \
|
.size = sizeof(((struct struct_name *)0)->member), \
|
||||||
.offset = offsetof(struct struct_name, member) \
|
.offset = offsetof(struct struct_name, member) \
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -64,17 +67,6 @@ struct DynamicStructInfo {
|
|||||||
.members = struct_name##_members \
|
.members = struct_name##_members \
|
||||||
};
|
};
|
||||||
|
|
||||||
// ==================================================================================
|
|
||||||
|
|
||||||
#include <linux/version.h>
|
|
||||||
|
|
||||||
#define KERNEL_VERSION_6_1 KERNEL_VERSION(6, 1, 0)
|
|
||||||
#define KERNEL_VERSION_5_15 KERNEL_VERSION(5, 15, 0)
|
|
||||||
|
|
||||||
#include <../fs/mount.h>
|
|
||||||
#include <linux/mount.h>
|
|
||||||
|
|
||||||
// 定义元数据
|
|
||||||
DYNAMIC_STRUCT_BEGIN(mount)
|
DYNAMIC_STRUCT_BEGIN(mount)
|
||||||
DEFINE_MEMBER(mount, mnt_parent)
|
DEFINE_MEMBER(mount, mnt_parent)
|
||||||
DEFINE_MEMBER(mount, mnt)
|
DEFINE_MEMBER(mount, mnt)
|
||||||
@@ -96,13 +88,11 @@ DYNAMIC_STRUCT_BEGIN(mnt_namespace)
|
|||||||
DEFINE_MEMBER(mnt_namespace, root)
|
DEFINE_MEMBER(mnt_namespace, root)
|
||||||
DEFINE_MEMBER(mnt_namespace, seq)
|
DEFINE_MEMBER(mnt_namespace, seq)
|
||||||
DEFINE_MEMBER(mnt_namespace, mounts)
|
DEFINE_MEMBER(mnt_namespace, mounts)
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)
|
||||||
DEFINE_MEMBER(mnt_namespace, count)
|
DEFINE_MEMBER(mnt_namespace, count)
|
||||||
#endif
|
#endif
|
||||||
DYNAMIC_STRUCT_END(mnt_namespace)
|
DYNAMIC_STRUCT_END(mnt_namespace)
|
||||||
|
|
||||||
#include <linux/kprobes.h>
|
|
||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef CONFIG_KPROBES
|
||||||
DYNAMIC_STRUCT_BEGIN(kprobe)
|
DYNAMIC_STRUCT_BEGIN(kprobe)
|
||||||
DEFINE_MEMBER(kprobe, addr)
|
DEFINE_MEMBER(kprobe, addr)
|
||||||
@@ -110,16 +100,13 @@ DYNAMIC_STRUCT_BEGIN(kprobe)
|
|||||||
DEFINE_MEMBER(kprobe, offset)
|
DEFINE_MEMBER(kprobe, offset)
|
||||||
DEFINE_MEMBER(kprobe, pre_handler)
|
DEFINE_MEMBER(kprobe, pre_handler)
|
||||||
DEFINE_MEMBER(kprobe, post_handler)
|
DEFINE_MEMBER(kprobe, post_handler)
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)
|
||||||
DEFINE_MEMBER(kprobe, fault_handler)
|
DEFINE_MEMBER(kprobe, fault_handler)
|
||||||
#endif
|
#endif
|
||||||
DEFINE_MEMBER(kprobe, flags)
|
DEFINE_MEMBER(kprobe, flags)
|
||||||
DYNAMIC_STRUCT_END(kprobe)
|
DYNAMIC_STRUCT_END(kprobe)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <linux/mm.h>
|
|
||||||
#include <linux/mm_types.h>
|
|
||||||
|
|
||||||
DYNAMIC_STRUCT_BEGIN(vm_area_struct)
|
DYNAMIC_STRUCT_BEGIN(vm_area_struct)
|
||||||
DEFINE_MEMBER(vm_area_struct,vm_start)
|
DEFINE_MEMBER(vm_area_struct,vm_start)
|
||||||
DEFINE_MEMBER(vm_area_struct,vm_end)
|
DEFINE_MEMBER(vm_area_struct,vm_end)
|
||||||
@@ -128,9 +115,9 @@ DYNAMIC_STRUCT_BEGIN(vm_area_struct)
|
|||||||
DEFINE_MEMBER(vm_area_struct,vm_pgoff)
|
DEFINE_MEMBER(vm_area_struct,vm_pgoff)
|
||||||
DEFINE_MEMBER(vm_area_struct,vm_file)
|
DEFINE_MEMBER(vm_area_struct,vm_file)
|
||||||
DEFINE_MEMBER(vm_area_struct,vm_private_data)
|
DEFINE_MEMBER(vm_area_struct,vm_private_data)
|
||||||
#ifdef CONFIG_ANON_VMA_NAME
|
#ifdef CONFIG_ANON_VMA_NAME
|
||||||
DEFINE_MEMBER(vm_area_struct, anon_name)
|
DEFINE_MEMBER(vm_area_struct, anon_name)
|
||||||
#endif
|
#endif
|
||||||
DEFINE_MEMBER(vm_area_struct, vm_ops)
|
DEFINE_MEMBER(vm_area_struct, vm_ops)
|
||||||
DYNAMIC_STRUCT_END(vm_area_struct)
|
DYNAMIC_STRUCT_END(vm_area_struct)
|
||||||
|
|
||||||
@@ -141,8 +128,6 @@ DYNAMIC_STRUCT_BEGIN(vm_operations_struct)
|
|||||||
DEFINE_MEMBER(vm_operations_struct, access)
|
DEFINE_MEMBER(vm_operations_struct, access)
|
||||||
DYNAMIC_STRUCT_END(vm_operations_struct)
|
DYNAMIC_STRUCT_END(vm_operations_struct)
|
||||||
|
|
||||||
#include <linux/netlink.h>
|
|
||||||
|
|
||||||
DYNAMIC_STRUCT_BEGIN(netlink_kernel_cfg)
|
DYNAMIC_STRUCT_BEGIN(netlink_kernel_cfg)
|
||||||
DEFINE_MEMBER(netlink_kernel_cfg, groups)
|
DEFINE_MEMBER(netlink_kernel_cfg, groups)
|
||||||
DEFINE_MEMBER(netlink_kernel_cfg, flags)
|
DEFINE_MEMBER(netlink_kernel_cfg, flags)
|
||||||
@@ -150,13 +135,11 @@ DYNAMIC_STRUCT_BEGIN(netlink_kernel_cfg)
|
|||||||
DEFINE_MEMBER(netlink_kernel_cfg, cb_mutex)
|
DEFINE_MEMBER(netlink_kernel_cfg, cb_mutex)
|
||||||
DEFINE_MEMBER(netlink_kernel_cfg, bind)
|
DEFINE_MEMBER(netlink_kernel_cfg, bind)
|
||||||
DEFINE_MEMBER(netlink_kernel_cfg, unbind)
|
DEFINE_MEMBER(netlink_kernel_cfg, unbind)
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION_6_1
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
|
||||||
DEFINE_MEMBER(netlink_kernel_cfg, compare)
|
DEFINE_MEMBER(netlink_kernel_cfg, compare)
|
||||||
#endif
|
#endif
|
||||||
DYNAMIC_STRUCT_END(netlink_kernel_cfg)
|
DYNAMIC_STRUCT_END(netlink_kernel_cfg)
|
||||||
|
|
||||||
|
|
||||||
#include <linux/sched.h>
|
|
||||||
DYNAMIC_STRUCT_BEGIN(task_struct)
|
DYNAMIC_STRUCT_BEGIN(task_struct)
|
||||||
DEFINE_MEMBER(task_struct, pid)
|
DEFINE_MEMBER(task_struct, pid)
|
||||||
DEFINE_MEMBER(task_struct, tgid)
|
DEFINE_MEMBER(task_struct, tgid)
|
||||||
@@ -167,7 +150,11 @@ DYNAMIC_STRUCT_BEGIN(task_struct)
|
|||||||
DEFINE_MEMBER(task_struct, group_leader)
|
DEFINE_MEMBER(task_struct, group_leader)
|
||||||
DEFINE_MEMBER(task_struct, mm)
|
DEFINE_MEMBER(task_struct, mm)
|
||||||
DEFINE_MEMBER(task_struct, active_mm)
|
DEFINE_MEMBER(task_struct, active_mm)
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
|
||||||
|
DEFINE_MEMBER(task_struct, pids[PIDTYPE_PID].pid)
|
||||||
|
#else
|
||||||
DEFINE_MEMBER(task_struct, thread_pid)
|
DEFINE_MEMBER(task_struct, thread_pid)
|
||||||
|
#endif
|
||||||
DEFINE_MEMBER(task_struct, files)
|
DEFINE_MEMBER(task_struct, files)
|
||||||
DEFINE_MEMBER(task_struct, seccomp)
|
DEFINE_MEMBER(task_struct, seccomp)
|
||||||
#ifdef CONFIG_THREAD_INFO_IN_TASK
|
#ifdef CONFIG_THREAD_INFO_IN_TASK
|
||||||
@@ -182,104 +169,110 @@ DYNAMIC_STRUCT_BEGIN(task_struct)
|
|||||||
DEFINE_MEMBER(task_struct, thread)
|
DEFINE_MEMBER(task_struct, thread)
|
||||||
DYNAMIC_STRUCT_END(task_struct)
|
DYNAMIC_STRUCT_END(task_struct)
|
||||||
|
|
||||||
// =====================================================================================================================
|
|
||||||
|
|
||||||
#define STRUCT_INFO(name) &(name##_info)
|
#define STRUCT_INFO(name) &(name##_info)
|
||||||
|
|
||||||
static
|
static struct DynamicStructInfo *dynamic_struct_infos[] = {
|
||||||
struct DynamicStructInfo* dynamic_struct_infos[] = {
|
|
||||||
STRUCT_INFO(mount),
|
STRUCT_INFO(mount),
|
||||||
STRUCT_INFO(vfsmount),
|
STRUCT_INFO(vfsmount),
|
||||||
STRUCT_INFO(mnt_namespace),
|
STRUCT_INFO(mnt_namespace),
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef CONFIG_KPROBES
|
||||||
STRUCT_INFO(kprobe),
|
STRUCT_INFO(kprobe),
|
||||||
#endif
|
#endif
|
||||||
STRUCT_INFO(vm_area_struct),
|
STRUCT_INFO(vm_area_struct),
|
||||||
STRUCT_INFO(vm_operations_struct),
|
STRUCT_INFO(vm_operations_struct),
|
||||||
STRUCT_INFO(netlink_kernel_cfg),
|
STRUCT_INFO(netlink_kernel_cfg),
|
||||||
STRUCT_INFO(task_struct)
|
STRUCT_INFO(task_struct)
|
||||||
};
|
};
|
||||||
|
|
||||||
// return 0 if successful
|
/*
|
||||||
// return -1 if struct not defined
|
* return 0 if successful
|
||||||
int sukisu_super_find_struct(
|
* return -1 if struct not defined
|
||||||
const char* struct_name,
|
*/
|
||||||
size_t* out_size,
|
int sukisu_super_find_struct(const char *struct_name, size_t *out_size, int *out_members)
|
||||||
int* out_members
|
{
|
||||||
) {
|
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
||||||
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
struct DynamicStructInfo *info = dynamic_struct_infos[i];
|
||||||
struct DynamicStructInfo* info = dynamic_struct_infos[i];
|
|
||||||
if(strcmp(struct_name, info->name) == 0) {
|
if (strcmp(struct_name, info->name) == 0) {
|
||||||
if(out_size)
|
if (out_size)
|
||||||
*out_size = info->total_size;
|
*out_size = info->total_size;
|
||||||
if(out_members)
|
|
||||||
|
if (out_members)
|
||||||
*out_members = info->count;
|
*out_members = info->count;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_super_find_struct);
|
EXPORT_SYMBOL(sukisu_super_find_struct);
|
||||||
|
|
||||||
// Dynamic access struct
|
/*
|
||||||
// return 0 if successful
|
* Dynamic access struct
|
||||||
// return -1 if struct not defined
|
* return 0 if successful
|
||||||
// return -2 if member not defined
|
* return -1 if struct not defined
|
||||||
int sukisu_super_access (
|
* return -2 if member not defined
|
||||||
const char* struct_name,
|
*/
|
||||||
const char* member_name,
|
int sukisu_super_access(const char *struct_name, const char *member_name, size_t *out_offset,
|
||||||
size_t* out_offset,
|
size_t *out_size)
|
||||||
size_t* out_size
|
{
|
||||||
) {
|
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
||||||
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
struct DynamicStructInfo *info = dynamic_struct_infos[i];
|
||||||
struct DynamicStructInfo* info = dynamic_struct_infos[i];
|
|
||||||
if(strcmp(struct_name, info->name) == 0) {
|
if (strcmp(struct_name, info->name) == 0) {
|
||||||
for (size_t i1 = 0; i1 < info->count; i1++) {
|
for (size_t i1 = 0; i1 < info->count; i1++) {
|
||||||
if (strcmp(info->members[i1].name, member_name) == 0) {
|
if (strcmp(info->members[i1].name, member_name) == 0) {
|
||||||
if(out_offset)
|
if (out_offset)
|
||||||
*out_offset = info->members[i].offset;
|
*out_offset = info->members[i].offset;
|
||||||
if(out_size)
|
|
||||||
|
if (out_size)
|
||||||
*out_size = info->members[i].size;
|
*out_size = info->members[i].size;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_super_access);
|
EXPORT_SYMBOL(sukisu_super_access);
|
||||||
|
|
||||||
// 动态 container_of 宏
|
|
||||||
#define DYNAMIC_CONTAINER_OF(offset, member_ptr) ({ \
|
#define DYNAMIC_CONTAINER_OF(offset, member_ptr) ({ \
|
||||||
(offset != (size_t)-1) ? (void*)((char*)(member_ptr) - offset) : NULL; \
|
(offset != (size_t)-1) ? (void*)((char*)(member_ptr) - offset) : NULL; \
|
||||||
})
|
})
|
||||||
|
|
||||||
// Dynamic container_of
|
/*
|
||||||
// return 0 if success
|
* Dynamic container_of
|
||||||
// return -1 if current struct not defined
|
* return 0 if success
|
||||||
// return -2 if target member not defined
|
* return -1 if current struct not defined
|
||||||
int sukisu_super_container_of(
|
* return -2 if target member not defined
|
||||||
const char* struct_name,
|
*/
|
||||||
const char* member_name,
|
int sukisu_super_container_of(const char *struct_name, const char *member_name, void *ptr,
|
||||||
void* ptr,
|
void **out_ptr)
|
||||||
void** out_ptr
|
{
|
||||||
) {
|
if (ptr == NULL)
|
||||||
if(ptr == NULL) {
|
|
||||||
return -3;
|
return -3;
|
||||||
}
|
|
||||||
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
||||||
struct DynamicStructInfo* info = dynamic_struct_infos[i];
|
struct DynamicStructInfo *info = dynamic_struct_infos[i];
|
||||||
if(strcmp(struct_name, info->name) == 0) {
|
|
||||||
|
if (strcmp(struct_name, info->name) == 0) {
|
||||||
for (size_t i1 = 0; i1 < info->count; i1++) {
|
for (size_t i1 = 0; i1 < info->count; i1++) {
|
||||||
if (strcmp(info->members[i1].name, member_name) == 0) {
|
if (strcmp(info->members[i1].name, member_name) == 0) {
|
||||||
*out_ptr = (void*) DYNAMIC_CONTAINER_OF(info->members[i1].offset, ptr);
|
*out_ptr = (void *)DYNAMIC_CONTAINER_OF(info->members[i1].offset, ptr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_super_container_of);
|
EXPORT_SYMBOL(sukisu_super_container_of);
|
||||||
|
|||||||
@@ -6,34 +6,10 @@
|
|||||||
#include "kpm.h"
|
#include "kpm.h"
|
||||||
#include "compact.h"
|
#include "compact.h"
|
||||||
|
|
||||||
// return 0 if successful
|
extern int sukisu_super_find_struct(const char *struct_name, size_t *out_size, int *out_members);
|
||||||
// return -1 if struct not defined
|
extern int sukisu_super_access(const char *struct_name, const char *member_name, size_t *out_offset,
|
||||||
int sukisu_super_find_struct(
|
size_t *out_size);
|
||||||
const char* struct_name,
|
extern int sukisu_super_container_of(const char *struct_name, const char *member_name, void *ptr,
|
||||||
size_t* out_size,
|
void **out_ptr);
|
||||||
int* out_members
|
|
||||||
);
|
|
||||||
|
|
||||||
// Dynamic access struct
|
|
||||||
// return 0 if successful
|
|
||||||
// return -1 if struct not defined
|
|
||||||
// return -2 if member not defined
|
|
||||||
int sukisu_super_access (
|
|
||||||
const char* struct_name,
|
|
||||||
const char* member_name,
|
|
||||||
size_t* out_offset,
|
|
||||||
size_t* out_size
|
|
||||||
);
|
|
||||||
|
|
||||||
// Dynamic container_of
|
|
||||||
// return 0 if success
|
|
||||||
// return -1 if current struct not defined
|
|
||||||
// return -2 if target member not defined
|
|
||||||
int sukisu_super_container_of(
|
|
||||||
const char* struct_name,
|
|
||||||
const char* member_name,
|
|
||||||
void* ptr,
|
|
||||||
void** out_ptr
|
|
||||||
);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
106
kernel/ksu.c
@@ -3,88 +3,103 @@
|
|||||||
#include <linux/kobject.h>
|
#include <linux/kobject.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "feature.h"
|
||||||
#include "core_hook.h"
|
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksu.h"
|
|
||||||
#include "throne_tracker.h"
|
#include "throne_tracker.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
#include "ksud.h"
|
||||||
|
#include "supercalls.h"
|
||||||
|
|
||||||
|
#include "sulog.h"
|
||||||
|
#include "throne_comm.h"
|
||||||
|
#include "dynamic_manager.h"
|
||||||
|
|
||||||
static struct workqueue_struct *ksu_workqueue;
|
static struct workqueue_struct *ksu_workqueue;
|
||||||
|
|
||||||
bool ksu_queue_work(struct work_struct *work)
|
bool ksu_queue_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
return queue_work(ksu_workqueue, work);
|
return queue_work(ksu_workqueue, work);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
void sukisu_custom_config_init(void)
|
||||||
void *argv, void *envp, int *flags);
|
|
||||||
|
|
||||||
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
|
||||||
void *argv, void *envp, int *flags);
|
|
||||||
|
|
||||||
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_sucompat_init();
|
void sukisu_custom_config_exit(void)
|
||||||
extern void ksu_sucompat_exit();
|
{
|
||||||
extern void ksu_ksud_init();
|
ksu_uid_exit();
|
||||||
extern void ksu_ksud_exit();
|
ksu_throne_comm_exit();
|
||||||
|
ksu_dynamic_manager_exit();
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_exit();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int __init kernelsu_init(void)
|
int __init kernelsu_init(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_alert("*************************************************************");
|
pr_alert("*************************************************************");
|
||||||
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||||
pr_alert("** **");
|
pr_alert("** **");
|
||||||
pr_alert("** You are running KernelSU in DEBUG mode **");
|
pr_alert("** You are running KernelSU in DEBUG mode **");
|
||||||
pr_alert("** **");
|
pr_alert("** **");
|
||||||
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||||
pr_alert("*************************************************************");
|
pr_alert("*************************************************************");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ksu_core_init();
|
ksu_feature_init();
|
||||||
|
|
||||||
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
ksu_supercalls_init();
|
||||||
|
|
||||||
ksu_allowlist_init();
|
sukisu_custom_config_init();
|
||||||
|
|
||||||
ksu_throne_tracker_init();
|
ksu_syscall_hook_manager_init();
|
||||||
#ifdef CONFIG_KPROBES
|
|
||||||
ksu_sucompat_init();
|
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
||||||
ksu_ksud_init();
|
|
||||||
|
ksu_allowlist_init();
|
||||||
|
|
||||||
|
ksu_throne_tracker_init();
|
||||||
|
|
||||||
|
#ifdef KSU_KPROBES_HOOK
|
||||||
|
ksu_ksud_init();
|
||||||
#else
|
#else
|
||||||
pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html");
|
pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef MODULE
|
#ifdef MODULE
|
||||||
#ifndef CONFIG_KSU_DEBUG
|
#ifndef CONFIG_KSU_DEBUG
|
||||||
kobject_del(&THIS_MODULE->mkobj.kobj);
|
kobject_del(&THIS_MODULE->mkobj.kobj);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void ksu_observer_exit(void);
|
||||||
void kernelsu_exit(void)
|
void kernelsu_exit(void)
|
||||||
{
|
{
|
||||||
ksu_allowlist_exit();
|
ksu_allowlist_exit();
|
||||||
|
|
||||||
ksu_throne_tracker_exit();
|
ksu_observer_exit();
|
||||||
|
|
||||||
destroy_workqueue(ksu_workqueue);
|
ksu_throne_tracker_exit();
|
||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
destroy_workqueue(ksu_workqueue);
|
||||||
ksu_ksud_exit();
|
|
||||||
ksu_sucompat_exit();
|
#ifdef KSU_KPROBES_HOOK
|
||||||
|
ksu_ksud_exit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ksu_core_exit();
|
ksu_syscall_hook_manager_exit();
|
||||||
|
|
||||||
|
sukisu_custom_config_exit();
|
||||||
|
|
||||||
|
ksu_supercalls_exit();
|
||||||
|
|
||||||
|
ksu_feature_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(kernelsu_init);
|
module_init(kernelsu_init);
|
||||||
@@ -93,4 +108,9 @@ module_exit(kernelsu_exit);
|
|||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("weishu");
|
MODULE_AUTHOR("weishu");
|
||||||
MODULE_DESCRIPTION("Android KernelSU");
|
MODULE_DESCRIPTION("Android KernelSU");
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0)
|
||||||
|
MODULE_IMPORT_NS("VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver");
|
||||||
|
#else
|
||||||
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
|
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
|
||||||
|
#endif
|
||||||
|
|||||||
99
kernel/ksu.h
@@ -7,95 +7,56 @@
|
|||||||
#define KERNEL_SU_VERSION KSU_VERSION
|
#define KERNEL_SU_VERSION KSU_VERSION
|
||||||
#define KERNEL_SU_OPTION 0xDEADBEEF
|
#define KERNEL_SU_OPTION 0xDEADBEEF
|
||||||
|
|
||||||
#define CMD_GRANT_ROOT 0
|
extern bool ksu_uid_scanner_enabled;
|
||||||
#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 CMD_GET_APP_PROFILE 10
|
|
||||||
#define CMD_SET_APP_PROFILE 11
|
|
||||||
#define CMD_UID_GRANTED_ROOT 12
|
|
||||||
#define CMD_UID_SHOULD_UMOUNT 13
|
|
||||||
#define CMD_IS_SU_ENABLED 14
|
|
||||||
#define CMD_ENABLE_SU 15
|
|
||||||
|
|
||||||
#define EVENT_POST_FS_DATA 1
|
#define EVENT_POST_FS_DATA 1
|
||||||
#define EVENT_BOOT_COMPLETED 2
|
#define EVENT_BOOT_COMPLETED 2
|
||||||
#define EVENT_MODULE_MOUNTED 3
|
#define EVENT_MODULE_MOUNTED 3
|
||||||
|
|
||||||
#define KSU_APP_PROFILE_VER 2
|
// SukiSU Ultra kernel su version full strings
|
||||||
#define KSU_MAX_PACKAGE_NAME 256
|
#ifndef KSU_VERSION_FULL
|
||||||
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
#define KSU_VERSION_FULL "v3.x-00000000@unknown"
|
||||||
#define KSU_MAX_GROUPS 32
|
#endif
|
||||||
#define KSU_SELINUX_DOMAIN 64
|
#define KSU_FULL_VERSION_STRING 255
|
||||||
|
|
||||||
struct root_profile {
|
#define DYNAMIC_MANAGER_OP_SET 0
|
||||||
int32_t uid;
|
#define DYNAMIC_MANAGER_OP_GET 1
|
||||||
int32_t gid;
|
#define DYNAMIC_MANAGER_OP_CLEAR 2
|
||||||
|
|
||||||
int32_t groups_count;
|
#define UID_SCANNER_OP_GET_STATUS 0
|
||||||
int32_t groups[KSU_MAX_GROUPS];
|
#define UID_SCANNER_OP_TOGGLE 1
|
||||||
|
#define UID_SCANNER_OP_CLEAR_ENV 2
|
||||||
|
|
||||||
// kernel_cap_t is u32[2] for capabilities v3
|
struct dynamic_manager_user_config {
|
||||||
struct {
|
unsigned int operation;
|
||||||
u64 effective;
|
unsigned int size;
|
||||||
u64 permitted;
|
char hash[65];
|
||||||
u64 inheritable;
|
|
||||||
} capabilities;
|
|
||||||
|
|
||||||
char selinux_domain[KSU_SELINUX_DOMAIN];
|
|
||||||
|
|
||||||
int32_t namespaces;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct non_root_profile {
|
struct manager_list_info {
|
||||||
bool umount_modules;
|
int count;
|
||||||
};
|
struct {
|
||||||
|
uid_t uid;
|
||||||
struct app_profile {
|
int signature_index;
|
||||||
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
|
} managers[2];
|
||||||
u32 version;
|
|
||||||
|
|
||||||
// this is usually the package of the app, but can be other value for special apps
|
|
||||||
char key[KSU_MAX_PACKAGE_NAME];
|
|
||||||
int32_t current_uid;
|
|
||||||
bool allow_su;
|
|
||||||
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
bool use_default;
|
|
||||||
char template_name[KSU_MAX_PACKAGE_NAME];
|
|
||||||
|
|
||||||
struct root_profile profile;
|
|
||||||
} rp_config;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool use_default;
|
|
||||||
|
|
||||||
struct non_root_profile profile;
|
|
||||||
} nrp_config;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ksu_queue_work(struct work_struct *work);
|
bool ksu_queue_work(struct work_struct *work);
|
||||||
|
|
||||||
|
#if 0
|
||||||
static inline int startswith(char *s, char *prefix)
|
static inline int startswith(char *s, char *prefix)
|
||||||
{
|
{
|
||||||
return strncmp(s, prefix, strlen(prefix));
|
return strncmp(s, prefix, strlen(prefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int endswith(const char *s, const char *t)
|
static inline int endswith(const char *s, const char *t)
|
||||||
{
|
{
|
||||||
size_t slen = strlen(s);
|
size_t slen = strlen(s);
|
||||||
size_t tlen = strlen(t);
|
size_t tlen = strlen(t);
|
||||||
if (tlen > slen)
|
if (tlen > slen)
|
||||||
return 1;
|
return 1;
|
||||||
return strcmp(s + slen - tlen, t);
|
return strcmp(s + slen - tlen, t);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
824
kernel/ksud.c
@@ -3,9 +3,19 @@
|
|||||||
|
|
||||||
#define KSUD_PATH "/data/adb/ksud"
|
#define KSUD_PATH "/data/adb/ksud"
|
||||||
|
|
||||||
|
void ksu_ksud_init();
|
||||||
|
void ksu_ksud_exit();
|
||||||
|
|
||||||
void on_post_fs_data(void);
|
void on_post_fs_data(void);
|
||||||
|
void on_module_mounted(void);
|
||||||
|
void on_boot_completed(void);
|
||||||
|
|
||||||
bool ksu_is_safe_mode(void);
|
bool ksu_is_safe_mode(void);
|
||||||
|
|
||||||
extern u32 ksu_devpts_sid;
|
int nuke_ext4_sysfs(const char* mnt);
|
||||||
|
|
||||||
|
extern u32 ksu_file_sid;
|
||||||
|
extern bool ksu_module_mounted;
|
||||||
|
extern bool ksu_boot_completed;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -8,29 +8,36 @@
|
|||||||
|
|
||||||
extern uid_t ksu_manager_uid; // DO NOT DIRECT USE
|
extern uid_t ksu_manager_uid; // DO NOT DIRECT USE
|
||||||
|
|
||||||
static inline bool ksu_is_manager_uid_valid()
|
extern bool ksu_is_any_manager(uid_t uid);
|
||||||
|
extern void ksu_add_manager(uid_t uid, int signature_index);
|
||||||
|
extern void ksu_remove_manager(uid_t uid);
|
||||||
|
extern int ksu_get_manager_signature_index(uid_t uid);
|
||||||
|
|
||||||
|
static inline bool ksu_is_manager_uid_valid(void)
|
||||||
{
|
{
|
||||||
return ksu_manager_uid != KSU_INVALID_UID;
|
return ksu_manager_uid != KSU_INVALID_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool is_manager()
|
static inline bool is_manager(void)
|
||||||
{
|
{
|
||||||
return unlikely(ksu_manager_uid == current_uid().val);
|
return unlikely(ksu_is_any_manager(current_uid().val) ||
|
||||||
|
(ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uid_t ksu_get_manager_uid()
|
static inline uid_t ksu_get_manager_uid(void)
|
||||||
{
|
{
|
||||||
return ksu_manager_uid;
|
return ksu_manager_uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ksu_set_manager_uid(uid_t uid)
|
static inline void ksu_set_manager_uid(uid_t uid)
|
||||||
{
|
{
|
||||||
ksu_manager_uid = uid;
|
ksu_manager_uid = uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ksu_invalidate_manager_uid()
|
static inline void ksu_invalidate_manager_uid(void)
|
||||||
{
|
{
|
||||||
ksu_manager_uid = KSU_INVALID_UID;
|
ksu_manager_uid = KSU_INVALID_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ksu_observer_init(void);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
17
kernel/manager_sign.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef MANAGER_SIGN_H
|
||||||
|
#define MANAGER_SIGN_H
|
||||||
|
|
||||||
|
// ShirkNeko/SukiSU
|
||||||
|
#define EXPECTED_SIZE_SHIRKNEKO 0x35c
|
||||||
|
#define EXPECTED_HASH_SHIRKNEKO "947ae944f3de4ed4c21a7e4f7953ecf351bfa2b36239da37a34111ad29993eef"
|
||||||
|
|
||||||
|
// Dynamic Sign
|
||||||
|
#define EXPECTED_SIZE_OTHER 0x300
|
||||||
|
#define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned size;
|
||||||
|
const char *sha256;
|
||||||
|
} apk_sign_key_t;
|
||||||
|
|
||||||
|
#endif /* MANAGER_SIGN_H */
|
||||||
357
kernel/manual_su.c
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/binfmts.h>
|
||||||
|
|
||||||
|
#include "manual_su.h"
|
||||||
|
#include "ksu.h"
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "manager.h"
|
||||||
|
#include "app_profile.h"
|
||||||
|
|
||||||
|
static bool current_verified = false;
|
||||||
|
static void ksu_cleanup_expired_tokens(void);
|
||||||
|
static bool is_current_verified(void);
|
||||||
|
static void add_pending_root(uid_t uid);
|
||||||
|
|
||||||
|
static struct pending_uid pending_uids[MAX_PENDING] = {0};
|
||||||
|
static int pending_cnt = 0;
|
||||||
|
static struct ksu_token_entry auth_tokens[MAX_TOKENS] = {0};
|
||||||
|
static int token_count = 0;
|
||||||
|
static DEFINE_SPINLOCK(token_lock);
|
||||||
|
|
||||||
|
static char* get_token_from_envp(void)
|
||||||
|
{
|
||||||
|
struct mm_struct *mm;
|
||||||
|
char *envp_start, *envp_end;
|
||||||
|
char *env_ptr, *token = NULL;
|
||||||
|
unsigned long env_len;
|
||||||
|
char *env_copy = NULL;
|
||||||
|
|
||||||
|
if (!current->mm)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
mm = current->mm;
|
||||||
|
|
||||||
|
down_read(&mm->mmap_lock);
|
||||||
|
|
||||||
|
envp_start = (char *)mm->env_start;
|
||||||
|
envp_end = (char *)mm->env_end;
|
||||||
|
env_len = envp_end - envp_start;
|
||||||
|
|
||||||
|
if (env_len <= 0 || env_len > PAGE_SIZE * 32) {
|
||||||
|
up_read(&mm->mmap_lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
env_copy = kzalloc(env_len + 1, GFP_KERNEL);
|
||||||
|
if (!env_copy) {
|
||||||
|
up_read(&mm->mmap_lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy_from_user(env_copy, envp_start, env_len)) {
|
||||||
|
kfree(env_copy);
|
||||||
|
up_read(&mm->mmap_lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
up_read(&mm->mmap_lock);
|
||||||
|
|
||||||
|
env_copy[env_len] = '\0';
|
||||||
|
env_ptr = env_copy;
|
||||||
|
|
||||||
|
while (env_ptr < env_copy + env_len) {
|
||||||
|
if (strncmp(env_ptr, KSU_TOKEN_ENV_NAME "=", strlen(KSU_TOKEN_ENV_NAME) + 1) == 0) {
|
||||||
|
char *token_start = env_ptr + strlen(KSU_TOKEN_ENV_NAME) + 1;
|
||||||
|
char *token_end = strchr(token_start, '\0');
|
||||||
|
|
||||||
|
if (token_end && (token_end - token_start) == KSU_TOKEN_LENGTH) {
|
||||||
|
token = kzalloc(KSU_TOKEN_LENGTH + 1, GFP_KERNEL);
|
||||||
|
if (token) {
|
||||||
|
memcpy(token, token_start, KSU_TOKEN_LENGTH);
|
||||||
|
token[KSU_TOKEN_LENGTH] = '\0';
|
||||||
|
pr_info("manual_su: found auth token in environment\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
env_ptr += strlen(env_ptr) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(env_copy);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* ksu_generate_auth_token(void)
|
||||||
|
{
|
||||||
|
static char token_buffer[KSU_TOKEN_LENGTH + 1];
|
||||||
|
unsigned long flags;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ksu_cleanup_expired_tokens();
|
||||||
|
|
||||||
|
spin_lock_irqsave(&token_lock, flags);
|
||||||
|
|
||||||
|
if (token_count >= MAX_TOKENS) {
|
||||||
|
for (i = 0; i < MAX_TOKENS - 1; i++) {
|
||||||
|
auth_tokens[i] = auth_tokens[i + 1];
|
||||||
|
}
|
||||||
|
token_count = MAX_TOKENS - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < KSU_TOKEN_LENGTH; i++) {
|
||||||
|
u8 rand_byte;
|
||||||
|
get_random_bytes(&rand_byte, 1);
|
||||||
|
int char_type = rand_byte % 3;
|
||||||
|
if (char_type == 0) {
|
||||||
|
token_buffer[i] = 'A' + (rand_byte % 26);
|
||||||
|
} else if (char_type == 1) {
|
||||||
|
token_buffer[i] = 'a' + (rand_byte % 26);
|
||||||
|
} else {
|
||||||
|
token_buffer[i] = '0' + (rand_byte % 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
|
strscpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
|
||||||
|
#else
|
||||||
|
strlcpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
|
||||||
|
#endif
|
||||||
|
auth_tokens[token_count].expire_time = jiffies + KSU_TOKEN_EXPIRE_TIME * HZ;
|
||||||
|
auth_tokens[token_count].used = false;
|
||||||
|
token_count++;
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&token_lock, flags);
|
||||||
|
|
||||||
|
pr_info("manual_su: generated new auth token (expires in %d seconds)\n", KSU_TOKEN_EXPIRE_TIME);
|
||||||
|
return token_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ksu_verify_auth_token(const char *token)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
bool valid = false;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!token || strlen(token) != KSU_TOKEN_LENGTH) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&token_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < token_count; i++) {
|
||||||
|
if (!auth_tokens[i].used &&
|
||||||
|
time_before(jiffies, auth_tokens[i].expire_time) &&
|
||||||
|
strcmp(auth_tokens[i].token, token) == 0) {
|
||||||
|
|
||||||
|
auth_tokens[i].used = true;
|
||||||
|
valid = true;
|
||||||
|
pr_info("manual_su: auth token verified successfully\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&token_lock, flags);
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
pr_warn("manual_su: invalid or expired auth token\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ksu_cleanup_expired_tokens(void)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&token_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < token_count; ) {
|
||||||
|
if (time_after(jiffies, auth_tokens[i].expire_time) || auth_tokens[i].used) {
|
||||||
|
for (j = i; j < token_count - 1; j++) {
|
||||||
|
auth_tokens[j] = auth_tokens[j + 1];
|
||||||
|
}
|
||||||
|
token_count--;
|
||||||
|
pr_debug("manual_su: cleaned up expired/used token\n");
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&token_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_token_generation(struct manual_su_request *request)
|
||||||
|
{
|
||||||
|
if (current_uid().val > 2000) {
|
||||||
|
pr_warn("manual_su: token generation denied for app UID %d\n", current_uid().val);
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *new_token = ksu_generate_auth_token();
|
||||||
|
if (!new_token) {
|
||||||
|
pr_err("manual_su: failed to generate token\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
|
strscpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
|
||||||
|
#else
|
||||||
|
strlcpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pr_info("manual_su: auth token generated successfully\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_escalation_request(struct manual_su_request *request)
|
||||||
|
{
|
||||||
|
uid_t target_uid = request->target_uid;
|
||||||
|
pid_t target_pid = request->target_pid;
|
||||||
|
struct task_struct *tsk;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
tsk = pid_task(find_vpid(target_pid), PIDTYPE_PID);
|
||||||
|
if (!tsk || ksu_task_is_dead(tsk)) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
pr_err("cmd_su: PID %d is invalid or dead\n", target_pid);
|
||||||
|
return -ESRCH;
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
if (current_uid().val == 0 || is_manager() || ksu_is_allow_uid_for_current(current_uid().val))
|
||||||
|
goto allowed;
|
||||||
|
|
||||||
|
char *env_token = get_token_from_envp();
|
||||||
|
if (!env_token) {
|
||||||
|
pr_warn("manual_su: no auth token found in environment\n");
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool token_valid = ksu_verify_auth_token(env_token);
|
||||||
|
kfree(env_token);
|
||||||
|
|
||||||
|
if (!token_valid) {
|
||||||
|
pr_warn("manual_su: token verification failed\n");
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
allowed:
|
||||||
|
current_verified = true;
|
||||||
|
escape_to_root_for_cmd_su(target_uid, target_pid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_add_pending_request(struct manual_su_request *request)
|
||||||
|
{
|
||||||
|
uid_t target_uid = request->target_uid;
|
||||||
|
|
||||||
|
if (!is_current_verified()) {
|
||||||
|
pr_warn("manual_su: add_pending denied, not verified\n");
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_pending_root(target_uid);
|
||||||
|
current_verified = false;
|
||||||
|
pr_info("manual_su: pending root added for UID %d\n", target_uid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_manual_su_request(int option, struct manual_su_request *request)
|
||||||
|
{
|
||||||
|
if (!request) {
|
||||||
|
pr_err("manual_su: invalid request pointer\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
case MANUAL_SU_OP_GENERATE_TOKEN:
|
||||||
|
pr_info("manual_su: handling token generation request\n");
|
||||||
|
return handle_token_generation(request);
|
||||||
|
|
||||||
|
case MANUAL_SU_OP_ESCALATE:
|
||||||
|
pr_info("manual_su: handling escalation request for UID %d, PID %d\n",
|
||||||
|
request->target_uid, request->target_pid);
|
||||||
|
return handle_escalation_request(request);
|
||||||
|
|
||||||
|
case MANUAL_SU_OP_ADD_PENDING:
|
||||||
|
pr_info("manual_su: handling add pending request for UID %d\n", request->target_uid);
|
||||||
|
return handle_add_pending_request(request);
|
||||||
|
|
||||||
|
default:
|
||||||
|
pr_err("manual_su: unknown option %d\n", option);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_current_verified(void)
|
||||||
|
{
|
||||||
|
return current_verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_pending_root(uid_t uid)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < pending_cnt; i++) {
|
||||||
|
if (pending_uids[i].uid == uid) {
|
||||||
|
pending_uids[i].use_count++;
|
||||||
|
pending_uids[i].remove_calls++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_pending_root(uid_t uid)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < pending_cnt; i++) {
|
||||||
|
if (pending_uids[i].uid == uid) {
|
||||||
|
pending_uids[i].remove_calls++;
|
||||||
|
|
||||||
|
if (pending_uids[i].remove_calls >= REMOVE_DELAY_CALLS) {
|
||||||
|
pending_uids[i] = pending_uids[--pending_cnt];
|
||||||
|
pr_info("pending_root: removed UID %d after %d calls\n", uid, REMOVE_DELAY_CALLS);
|
||||||
|
ksu_temp_revoke_root_once(uid);
|
||||||
|
} else {
|
||||||
|
pr_info("pending_root: UID %d remove_call=%d (<%d)\n",
|
||||||
|
uid, pending_uids[i].remove_calls, REMOVE_DELAY_CALLS);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_pending_root(uid_t uid)
|
||||||
|
{
|
||||||
|
if (pending_cnt >= MAX_PENDING) {
|
||||||
|
pr_warn("pending_root: cache full\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < pending_cnt; i++) {
|
||||||
|
if (pending_uids[i].uid == uid) {
|
||||||
|
pending_uids[i].use_count = 0;
|
||||||
|
pending_uids[i].remove_calls = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pending_uids[pending_cnt++] = (struct pending_uid){uid, 0};
|
||||||
|
ksu_temp_grant_root_once(uid);
|
||||||
|
pr_info("pending_root: cached UID %d\n", uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_try_escalate_for_uid(uid_t uid)
|
||||||
|
{
|
||||||
|
if (!is_pending_root(uid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
pr_info("pending_root: UID=%d temporarily allowed\n", uid);
|
||||||
|
remove_pending_root(uid);
|
||||||
|
}
|
||||||
49
kernel/manual_su.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#ifndef __KSU_MANUAL_SU_H
|
||||||
|
#define __KSU_MANUAL_SU_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0)
|
||||||
|
#define mmap_lock mmap_sem
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ksu_task_is_dead(t) ((t)->exit_state != 0)
|
||||||
|
|
||||||
|
#define MAX_PENDING 16
|
||||||
|
#define REMOVE_DELAY_CALLS 150
|
||||||
|
#define MAX_TOKENS 10
|
||||||
|
|
||||||
|
#define KSU_SU_VERIFIED_BIT (1UL << 0)
|
||||||
|
#define KSU_TOKEN_LENGTH 32
|
||||||
|
#define KSU_TOKEN_ENV_NAME "KSU_AUTH_TOKEN"
|
||||||
|
#define KSU_TOKEN_EXPIRE_TIME 150
|
||||||
|
|
||||||
|
#define MANUAL_SU_OP_GENERATE_TOKEN 0
|
||||||
|
#define MANUAL_SU_OP_ESCALATE 1
|
||||||
|
#define MANUAL_SU_OP_ADD_PENDING 2
|
||||||
|
|
||||||
|
struct pending_uid {
|
||||||
|
uid_t uid;
|
||||||
|
int use_count;
|
||||||
|
int remove_calls;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct manual_su_request {
|
||||||
|
uid_t target_uid;
|
||||||
|
pid_t target_pid;
|
||||||
|
char token_buffer[KSU_TOKEN_LENGTH + 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_token_entry {
|
||||||
|
char token[KSU_TOKEN_LENGTH + 1];
|
||||||
|
unsigned long expire_time;
|
||||||
|
bool used;
|
||||||
|
};
|
||||||
|
|
||||||
|
int ksu_handle_manual_su_request(int option, struct manual_su_request *request);
|
||||||
|
bool is_pending_root(uid_t uid);
|
||||||
|
void remove_pending_root(uid_t uid);
|
||||||
|
void ksu_try_escalate_for_uid(uid_t uid);
|
||||||
|
#endif
|
||||||
133
kernel/pkg_observer.c
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
|
#include <linux/fsnotify_backend.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/rculist.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "ksu.h"
|
||||||
|
#include "throne_tracker.h"
|
||||||
|
#include "throne_comm.h"
|
||||||
|
|
||||||
|
#define MASK_SYSTEM (FS_CREATE | FS_MOVE | FS_EVENT_ON_CHILD)
|
||||||
|
|
||||||
|
struct watch_dir {
|
||||||
|
const char *path;
|
||||||
|
u32 mask;
|
||||||
|
struct path kpath;
|
||||||
|
struct inode *inode;
|
||||||
|
struct fsnotify_mark *mark;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct fsnotify_group *g;
|
||||||
|
|
||||||
|
static int ksu_handle_inode_event(struct fsnotify_mark *mark, u32 mask,
|
||||||
|
struct inode *inode, struct inode *dir,
|
||||||
|
const struct qstr *file_name, u32 cookie)
|
||||||
|
{
|
||||||
|
if (!file_name)
|
||||||
|
return 0;
|
||||||
|
if (mask & FS_ISDIR)
|
||||||
|
return 0;
|
||||||
|
if (file_name->len == 13 &&
|
||||||
|
!memcmp(file_name->name, "packages.list", 13)) {
|
||||||
|
pr_info("packages.list detected: %d\n", mask);
|
||||||
|
if (ksu_uid_scanner_enabled) {
|
||||||
|
ksu_request_userspace_scan();
|
||||||
|
}
|
||||||
|
track_throne(false);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct fsnotify_ops ksu_ops = {
|
||||||
|
.handle_inode_event = ksu_handle_inode_event,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int add_mark_on_inode(struct inode *inode, u32 mask,
|
||||||
|
struct fsnotify_mark **out)
|
||||||
|
{
|
||||||
|
struct fsnotify_mark *m;
|
||||||
|
|
||||||
|
m = kzalloc(sizeof(*m), GFP_KERNEL);
|
||||||
|
if (!m)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
fsnotify_init_mark(m, g);
|
||||||
|
m->mask = mask;
|
||||||
|
|
||||||
|
if (fsnotify_add_inode_mark(m, inode, 0)) {
|
||||||
|
fsnotify_put_mark(m);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
*out = m;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int watch_one_dir(struct watch_dir *wd)
|
||||||
|
{
|
||||||
|
int ret = kern_path(wd->path, LOOKUP_FOLLOW, &wd->kpath);
|
||||||
|
if (ret) {
|
||||||
|
pr_info("path not ready: %s (%d)\n", wd->path, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
wd->inode = d_inode(wd->kpath.dentry);
|
||||||
|
ihold(wd->inode);
|
||||||
|
|
||||||
|
ret = add_mark_on_inode(wd->inode, wd->mask, &wd->mark);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("Add mark failed for %s (%d)\n", wd->path, ret);
|
||||||
|
path_put(&wd->kpath);
|
||||||
|
iput(wd->inode);
|
||||||
|
wd->inode = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
pr_info("watching %s\n", wd->path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unwatch_one_dir(struct watch_dir *wd)
|
||||||
|
{
|
||||||
|
if (wd->mark) {
|
||||||
|
fsnotify_destroy_mark(wd->mark, g);
|
||||||
|
fsnotify_put_mark(wd->mark);
|
||||||
|
wd->mark = NULL;
|
||||||
|
}
|
||||||
|
if (wd->inode) {
|
||||||
|
iput(wd->inode);
|
||||||
|
wd->inode = NULL;
|
||||||
|
}
|
||||||
|
if (wd->kpath.dentry) {
|
||||||
|
path_put(&wd->kpath);
|
||||||
|
memset(&wd->kpath, 0, sizeof(wd->kpath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct watch_dir g_watch = { .path = "/data/system",
|
||||||
|
.mask = MASK_SYSTEM };
|
||||||
|
|
||||||
|
int ksu_observer_init(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
|
||||||
|
g = fsnotify_alloc_group(&ksu_ops, 0);
|
||||||
|
#else
|
||||||
|
g = fsnotify_alloc_group(&ksu_ops);
|
||||||
|
#endif
|
||||||
|
if (IS_ERR(g))
|
||||||
|
return PTR_ERR(g);
|
||||||
|
|
||||||
|
ret = watch_one_dir(&g_watch);
|
||||||
|
pr_info("observer init done\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_observer_exit(void)
|
||||||
|
{
|
||||||
|
unwatch_one_dir(&g_watch);
|
||||||
|
fsnotify_put_group(g);
|
||||||
|
pr_info("observer exit done\n");
|
||||||
|
}
|
||||||
69
kernel/seccomp_cache.c
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/nsproxy.h>
|
||||||
|
#include <linux/sched/task.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/filter.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "seccomp_cache.h"
|
||||||
|
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 2) // Android backport this feature in 5.10.2
|
||||||
|
struct action_cache {
|
||||||
|
DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR);
|
||||||
|
#ifdef SECCOMP_ARCH_COMPAT
|
||||||
|
DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct seccomp_filter {
|
||||||
|
refcount_t refs;
|
||||||
|
refcount_t users;
|
||||||
|
bool log;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||||
|
bool wait_killable_recv;
|
||||||
|
#endif
|
||||||
|
struct action_cache cache;
|
||||||
|
struct seccomp_filter *prev;
|
||||||
|
struct bpf_prog *prog;
|
||||||
|
struct notification *notif;
|
||||||
|
struct mutex notify_lock;
|
||||||
|
wait_queue_head_t wqh;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr)
|
||||||
|
{
|
||||||
|
if (!filter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
|
||||||
|
clear_bit(nr, filter->cache.allow_native);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SECCOMP_ARCH_COMPAT
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
|
||||||
|
clear_bit(nr, filter->cache.allow_compat);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr)
|
||||||
|
{
|
||||||
|
if (!filter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
|
||||||
|
set_bit(nr, filter->cache.allow_native);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SECCOMP_ARCH_COMPAT
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
|
||||||
|
set_bit(nr, filter->cache.allow_compat);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
12
kernel/seccomp_cache.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef __KSU_H_SECCOMP_CACHE
|
||||||
|
#define __KSU_H_SECCOMP_CACHE
|
||||||
|
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 2) // Android backport this feature in 5.10.2
|
||||||
|
extern void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr);
|
||||||
|
extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -2,15 +2,7 @@ obj-y += selinux.o
|
|||||||
obj-y += sepolicy.o
|
obj-y += sepolicy.o
|
||||||
obj-y += rules.o
|
obj-y += rules.o
|
||||||
|
|
||||||
ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
ccflags-y += -Wno-strict-prototypes -Wno-int-conversion
|
||||||
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-declaration-after-statement -Wno-unused-function
|
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
||||||
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
|
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
|
||||||
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include "selinux.h"
|
#include "selinux.h"
|
||||||
#include "sepolicy.h"
|
#include "sepolicy.h"
|
||||||
#include "ss/services.h"
|
#include "ss/services.h"
|
||||||
#include "linux/lsm_audit.h"
|
#include "linux/lsm_audit.h" // IWYU pragma: keep
|
||||||
#include "xfrm.h"
|
#include "xfrm.h"
|
||||||
|
|
||||||
#define SELINUX_POLICY_INSTEAD_SELINUX_SS
|
#define SELINUX_POLICY_INSTEAD_SELINUX_SS
|
||||||
@@ -18,111 +18,119 @@
|
|||||||
|
|
||||||
static struct policydb *get_policydb(void)
|
static struct policydb *get_policydb(void)
|
||||||
{
|
{
|
||||||
struct policydb *db;
|
struct policydb *db;
|
||||||
struct selinux_policy *policy = rcu_dereference(selinux_state.policy);
|
struct selinux_policy *policy = selinux_state.policy;
|
||||||
db = &policy->policydb;
|
db = &policy->policydb;
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(ksu_rules);
|
||||||
|
|
||||||
void apply_kernelsu_rules()
|
void apply_kernelsu_rules()
|
||||||
{
|
{
|
||||||
if (!getenforce()) {
|
struct policydb *db;
|
||||||
pr_info("SELinux permissive or disabled, apply rules!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
rcu_read_lock();
|
if (!getenforce()) {
|
||||||
struct policydb *db = get_policydb();
|
pr_info("SELinux permissive or disabled, apply rules!\n");
|
||||||
|
}
|
||||||
|
|
||||||
ksu_permissive(db, KERNEL_SU_DOMAIN);
|
mutex_lock(&ksu_rules);
|
||||||
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
|
db = get_policydb();
|
||||||
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!
|
ksu_permissive(db, KERNEL_SU_DOMAIN);
|
||||||
ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL);
|
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
|
||||||
|
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain");
|
||||||
|
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain");
|
||||||
|
|
||||||
// allow us do any ioctl
|
// Create unconstrained file type
|
||||||
if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
|
ksu_type(db, KERNEL_SU_FILE, "file_type");
|
||||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "blk_file", ALL);
|
ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject");
|
||||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "fifo_file", ALL);
|
ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL);
|
||||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
|
|
||||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "file", ALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we need to save allowlist in /data/adb/ksu
|
// allow all!
|
||||||
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
|
ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL);
|
||||||
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
|
|
||||||
// we need to search /data/app
|
|
||||||
ksu_allow(db, "kernel", "apk_data_file", "file", "open");
|
|
||||||
ksu_allow(db, "kernel", "apk_data_file", "dir", "open");
|
|
||||||
ksu_allow(db, "kernel", "apk_data_file", "dir", "read");
|
|
||||||
ksu_allow(db, "kernel", "apk_data_file", "dir", "search");
|
|
||||||
// we may need to do mount on shell
|
|
||||||
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
|
|
||||||
ksu_allow(db, "init", "adb_data_file", "file", ALL);
|
|
||||||
ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289
|
|
||||||
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
|
|
||||||
// we need to umount modules in zygote
|
|
||||||
ksu_allow(db, "zygote", "adb_data_file", "dir", "search");
|
|
||||||
|
|
||||||
// copied from Magisk rules
|
// allow us do any ioctl
|
||||||
// suRights
|
if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "blk_file", ALL);
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "read");
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "fifo_file", ALL);
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "file", ALL);
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr");
|
}
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "process", "sigchld");
|
|
||||||
|
|
||||||
// allowLog
|
// we need to save allowlist in /data/adb/ksu
|
||||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "dir", "search");
|
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
|
||||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "read");
|
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
|
||||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "open");
|
// we need to search /data/app
|
||||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr");
|
ksu_allow(db, "kernel", "apk_data_file", "file", "open");
|
||||||
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "open");
|
||||||
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "read");
|
||||||
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "search");
|
||||||
|
// we may need to do mount on shell
|
||||||
|
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
|
||||||
|
ksu_allow(db, "init", "adb_data_file", "file", ALL);
|
||||||
|
ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289
|
||||||
|
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
|
||||||
|
// we need to umount modules in zygote
|
||||||
|
ksu_allow(db, "zygote", "adb_data_file", "dir", "search");
|
||||||
|
|
||||||
// dumpsys
|
// copied from Magisk rules
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fd", "use");
|
// suRights
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "write");
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "read");
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "read");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "open");
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "getattr");
|
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");
|
||||||
|
|
||||||
// bootctl
|
// allowLog
|
||||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "dir", "search");
|
||||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "read");
|
||||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "open");
|
||||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process",
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr");
|
||||||
"getattr");
|
|
||||||
|
|
||||||
// For mounting loop devices, mirrors, tmpfs
|
// dumpsys
|
||||||
ksu_allow(db, "kernel", ALL, "file", "read");
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fd", "use");
|
||||||
ksu_allow(db, "kernel", ALL, "file", "write");
|
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");
|
||||||
|
|
||||||
// Allow all binder transactions
|
// bootctl
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
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");
|
||||||
|
|
||||||
|
// For mounting loop devices, mirrors, tmpfs
|
||||||
|
ksu_allow(db, "kernel", ALL, "file", "read");
|
||||||
|
ksu_allow(db, "kernel", ALL, "file", "write");
|
||||||
|
|
||||||
|
// Allow all binder transactions
|
||||||
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
||||||
|
|
||||||
// Allow system server kill su process
|
// Allow system server kill su process
|
||||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
||||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
||||||
|
|
||||||
rcu_read_unlock();
|
// https://android-review.googlesource.com/c/platform/system/logging/+/3725346
|
||||||
|
ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");
|
||||||
|
|
||||||
|
mutex_unlock(&ksu_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_SEPOL_LEN 128
|
#define MAX_SEPOL_LEN 128
|
||||||
@@ -138,326 +146,332 @@ void apply_kernelsu_rules()
|
|||||||
#define CMD_GENFSCON 9
|
#define CMD_GENFSCON 9
|
||||||
|
|
||||||
struct sepol_data {
|
struct sepol_data {
|
||||||
u32 cmd;
|
u32 cmd;
|
||||||
u32 subcmd;
|
u32 subcmd;
|
||||||
char __user *sepol1;
|
char __user *sepol1;
|
||||||
char __user *sepol2;
|
char __user *sepol2;
|
||||||
char __user *sepol3;
|
char __user *sepol3;
|
||||||
char __user *sepol4;
|
char __user *sepol4;
|
||||||
char __user *sepol5;
|
char __user *sepol5;
|
||||||
char __user *sepol6;
|
char __user *sepol6;
|
||||||
char __user *sepol7;
|
char __user *sepol7;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
||||||
char **object)
|
char **object)
|
||||||
{
|
{
|
||||||
if (!user_object) {
|
if (!user_object) {
|
||||||
*object = ALL;
|
*object = ALL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
|
if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
|
||||||
return -1;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*object = buf;
|
*object = buf;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
||||||
|
extern int avc_ss_reset(u32 seqno);
|
||||||
|
#else
|
||||||
|
extern int avc_ss_reset(struct selinux_avc *avc, u32 seqno);
|
||||||
|
#endif
|
||||||
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
||||||
static void reset_avc_cache()
|
static void reset_avc_cache()
|
||||||
{
|
{
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
||||||
avc_ss_reset(0);
|
avc_ss_reset(0);
|
||||||
selnl_notify_policyload(0);
|
selnl_notify_policyload(0);
|
||||||
selinux_status_update_policyload(0);
|
selinux_status_update_policyload(0);
|
||||||
#else
|
#else
|
||||||
struct selinux_avc *avc = selinux_state.avc;
|
struct selinux_avc *avc = selinux_state.avc;
|
||||||
avc_ss_reset(avc, 0);
|
avc_ss_reset(avc, 0);
|
||||||
selnl_notify_policyload(0);
|
selnl_notify_policyload(0);
|
||||||
selinux_status_update_policyload(&selinux_state, 0);
|
selinux_status_update_policyload(&selinux_state, 0);
|
||||||
#endif
|
#endif
|
||||||
selinux_xfrm_notify_policyload();
|
selinux_xfrm_notify_policyload();
|
||||||
}
|
}
|
||||||
|
|
||||||
int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||||
{
|
{
|
||||||
if (!arg4) {
|
struct policydb *db;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!getenforce()) {
|
if (!arg4) {
|
||||||
pr_info("SELinux permissive or disabled when handle policy!\n");
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sepol_data data;
|
if (!getenforce()) {
|
||||||
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
pr_info("SELinux permissive or disabled when handle policy!\n");
|
||||||
pr_err("sepol: copy sepol_data failed.\n");
|
}
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 cmd = data.cmd;
|
struct sepol_data data;
|
||||||
u32 subcmd = data.subcmd;
|
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
||||||
|
pr_err("sepol: copy sepol_data failed.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
rcu_read_lock();
|
u32 cmd = data.cmd;
|
||||||
|
u32 subcmd = data.subcmd;
|
||||||
|
|
||||||
struct policydb *db = get_policydb();
|
mutex_lock(&ksu_rules);
|
||||||
|
|
||||||
int ret = -1;
|
db = get_policydb();
|
||||||
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;
|
int ret = -EINVAL;
|
||||||
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
if (cmd == CMD_NORMAL_PERM) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
char src_buf[MAX_SEPOL_LEN];
|
||||||
goto exit;
|
char tgt_buf[MAX_SEPOL_LEN];
|
||||||
}
|
char cls_buf[MAX_SEPOL_LEN];
|
||||||
|
char perm_buf[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
|
char *s, *t, *c, *p;
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
||||||
goto exit;
|
pr_err("sepol: copy src failed.\n");
|
||||||
}
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
|
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_object(perm_buf, data.sepol4, sizeof(perm_buf), &p) <
|
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
|
||||||
0) {
|
pr_err("sepol: copy cls failed.\n");
|
||||||
pr_err("sepol: copy perm failed.\n");
|
goto exit;
|
||||||
goto exit;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool success = false;
|
if (get_object(perm_buf, data.sepol4, sizeof(perm_buf), &p) <
|
||||||
if (subcmd == 1) {
|
0) {
|
||||||
success = ksu_allow(db, s, t, c, p);
|
pr_err("sepol: copy perm failed.\n");
|
||||||
} else if (subcmd == 2) {
|
goto exit;
|
||||||
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\n", subcmd);
|
|
||||||
}
|
|
||||||
ret = success ? 0 : -1;
|
|
||||||
|
|
||||||
} else if (cmd == CMD_XPERM) {
|
bool success = false;
|
||||||
char src_buf[MAX_SEPOL_LEN];
|
if (subcmd == 1) {
|
||||||
char tgt_buf[MAX_SEPOL_LEN];
|
success = ksu_allow(db, s, t, c, p);
|
||||||
char cls_buf[MAX_SEPOL_LEN];
|
} 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\n", subcmd);
|
||||||
|
}
|
||||||
|
ret = success ? 0 : -EINVAL;
|
||||||
|
|
||||||
char __maybe_unused
|
} else if (cmd == CMD_XPERM) {
|
||||||
operation[MAX_SEPOL_LEN]; // it is always ioctl now!
|
char src_buf[MAX_SEPOL_LEN];
|
||||||
char perm_set[MAX_SEPOL_LEN];
|
char tgt_buf[MAX_SEPOL_LEN];
|
||||||
|
char cls_buf[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
char *s, *t, *c;
|
char __maybe_unused
|
||||||
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
operation[MAX_SEPOL_LEN]; // it is always ioctl now!
|
||||||
pr_err("sepol: copy src failed.\n");
|
char perm_set[MAX_SEPOL_LEN];
|
||||||
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;
|
char *s, *t, *c;
|
||||||
if (subcmd == 1) {
|
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
||||||
success = ksu_allowxperm(db, s, t, c, perm_set);
|
pr_err("sepol: copy src failed.\n");
|
||||||
} else if (subcmd == 2) {
|
goto exit;
|
||||||
success = ksu_auditallowxperm(db, s, t, c, perm_set);
|
}
|
||||||
} else if (subcmd == 3) {
|
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||||
success = ksu_dontauditxperm(db, s, t, c, perm_set);
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
} else {
|
goto exit;
|
||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
}
|
||||||
}
|
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
|
||||||
ret = success ? 0 : -1;
|
pr_err("sepol: copy cls failed.\n");
|
||||||
} else if (cmd == CMD_TYPE_STATE) {
|
goto exit;
|
||||||
char src[MAX_SEPOL_LEN];
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
bool success = false;
|
||||||
pr_err("sepol: copy src failed.\n");
|
if (subcmd == 1) {
|
||||||
goto exit;
|
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\n", subcmd);
|
||||||
|
}
|
||||||
|
ret = success ? 0 : -EINVAL;
|
||||||
|
} else if (cmd == CMD_TYPE_STATE) {
|
||||||
|
char src[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
bool success = false;
|
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||||
if (subcmd == 1) {
|
pr_err("sepol: copy src failed.\n");
|
||||||
success = ksu_permissive(db, src);
|
goto exit;
|
||||||
} else if (subcmd == 2) {
|
}
|
||||||
success = ksu_enforce(db, src);
|
|
||||||
} else {
|
|
||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
|
||||||
}
|
|
||||||
if (success)
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
|
bool success = false;
|
||||||
char type[MAX_SEPOL_LEN];
|
if (subcmd == 1) {
|
||||||
char attr[MAX_SEPOL_LEN];
|
success = ksu_permissive(db, src);
|
||||||
|
} else if (subcmd == 2) {
|
||||||
|
success = ksu_enforce(db, src);
|
||||||
|
} else {
|
||||||
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
|
}
|
||||||
|
if (success)
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
if (strncpy_from_user(type, data.sepol1, sizeof(type)) < 0) {
|
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
|
||||||
pr_err("sepol: copy type failed.\n");
|
char type[MAX_SEPOL_LEN];
|
||||||
goto exit;
|
char attr[MAX_SEPOL_LEN];
|
||||||
}
|
|
||||||
if (strncpy_from_user(attr, data.sepol2, sizeof(attr)) < 0) {
|
|
||||||
pr_err("sepol: copy attr failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = false;
|
if (strncpy_from_user(type, data.sepol1, sizeof(type)) < 0) {
|
||||||
if (cmd == CMD_TYPE) {
|
pr_err("sepol: copy type failed.\n");
|
||||||
success = ksu_type(db, type, attr);
|
goto exit;
|
||||||
} else {
|
}
|
||||||
success = ksu_typeattribute(db, type, attr);
|
if (strncpy_from_user(attr, data.sepol2, sizeof(attr)) < 0) {
|
||||||
}
|
pr_err("sepol: copy attr failed.\n");
|
||||||
if (!success) {
|
goto exit;
|
||||||
pr_err("sepol: %d failed.\n", cmd);
|
}
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
} else if (cmd == CMD_ATTR) {
|
bool success = false;
|
||||||
char attr[MAX_SEPOL_LEN];
|
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;
|
||||||
|
|
||||||
if (strncpy_from_user(attr, data.sepol1, sizeof(attr)) < 0) {
|
} else if (cmd == CMD_ATTR) {
|
||||||
pr_err("sepol: copy attr failed.\n");
|
char attr[MAX_SEPOL_LEN];
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (!ksu_attribute(db, attr)) {
|
|
||||||
pr_err("sepol: %d failed.\n", cmd);
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
} else if (cmd == CMD_TYPE_TRANSITION) {
|
if (strncpy_from_user(attr, data.sepol1, sizeof(attr)) < 0) {
|
||||||
char src[MAX_SEPOL_LEN];
|
pr_err("sepol: copy attr failed.\n");
|
||||||
char tgt[MAX_SEPOL_LEN];
|
goto exit;
|
||||||
char cls[MAX_SEPOL_LEN];
|
}
|
||||||
char default_type[MAX_SEPOL_LEN];
|
if (!ksu_attribute(db, attr)) {
|
||||||
char object[MAX_SEPOL_LEN];
|
pr_err("sepol: %d failed.\n", cmd);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
} else if (cmd == CMD_TYPE_TRANSITION) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
char src[MAX_SEPOL_LEN];
|
||||||
goto exit;
|
char tgt[MAX_SEPOL_LEN];
|
||||||
}
|
char cls[MAX_SEPOL_LEN];
|
||||||
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
|
char default_type[MAX_SEPOL_LEN];
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
char object[MAX_SEPOL_LEN];
|
||||||
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,
|
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||||
default_type, real_object);
|
pr_err("sepol: copy src failed.\n");
|
||||||
if (success)
|
goto exit;
|
||||||
ret = 0;
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (cmd == CMD_TYPE_CHANGE) {
|
bool success = ksu_type_transition(db, src, tgt, cls,
|
||||||
char src[MAX_SEPOL_LEN];
|
default_type, real_object);
|
||||||
char tgt[MAX_SEPOL_LEN];
|
if (success)
|
||||||
char cls[MAX_SEPOL_LEN];
|
ret = 0;
|
||||||
char default_type[MAX_SEPOL_LEN];
|
|
||||||
|
|
||||||
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
} else if (cmd == CMD_TYPE_CHANGE) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
char src[MAX_SEPOL_LEN];
|
||||||
goto exit;
|
char tgt[MAX_SEPOL_LEN];
|
||||||
}
|
char cls[MAX_SEPOL_LEN];
|
||||||
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
|
char default_type[MAX_SEPOL_LEN];
|
||||||
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\n", 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)) {
|
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||||
pr_err("sepol: %d failed.\n", cmd);
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = 0;
|
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
|
||||||
} else {
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
pr_err("sepol: unknown cmd: %d\n", cmd);
|
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\n", 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:
|
exit:
|
||||||
rcu_read_unlock();
|
mutex_unlock(&ksu_rules);
|
||||||
|
|
||||||
// only allow and xallow needs to reset avc cache, but we cannot do that because
|
// 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.
|
// we are in atomic context. so we just reset it every time.
|
||||||
reset_avc_cache();
|
reset_avc_cache();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
#include "selinux.h"
|
#include "selinux.h"
|
||||||
|
#include "linux/cred.h"
|
||||||
|
#include "linux/sched.h"
|
||||||
#include "objsec.h"
|
#include "objsec.h"
|
||||||
#include "linux/version.h"
|
#include "linux/version.h"
|
||||||
#include "../klog.h" // IWYU pragma: keep
|
#include "../klog.h" // IWYU pragma: keep
|
||||||
@@ -7,124 +9,146 @@
|
|||||||
|
|
||||||
static int transive_to_domain(const char *domain)
|
static int transive_to_domain(const char *domain)
|
||||||
{
|
{
|
||||||
struct cred *cred;
|
struct cred *cred;
|
||||||
struct task_security_struct *tsec;
|
struct task_security_struct *tsec;
|
||||||
u32 sid;
|
u32 sid;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
cred = (struct cred *)__task_cred(current);
|
cred = (struct cred *)__task_cred(current);
|
||||||
|
|
||||||
tsec = cred->security;
|
tsec = cred->security;
|
||||||
if (!tsec) {
|
if (!tsec) {
|
||||||
pr_err("tsec == NULL!\n");
|
pr_err("tsec == NULL!\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = security_secctx_to_secid(domain, strlen(domain), &sid);
|
error = security_secctx_to_secid(domain, strlen(domain), &sid);
|
||||||
if (error) {
|
if (error) {
|
||||||
pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n",
|
pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n",
|
||||||
domain, sid, error);
|
domain, sid, error);
|
||||||
}
|
}
|
||||||
if (!error) {
|
if (!error) {
|
||||||
tsec->sid = sid;
|
tsec->sid = sid;
|
||||||
tsec->create_sid = 0;
|
tsec->create_sid = 0;
|
||||||
tsec->keycreate_sid = 0;
|
tsec->keycreate_sid = 0;
|
||||||
tsec->sockcreate_sid = 0;
|
tsec->sockcreate_sid = 0;
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_selinux(const char *domain)
|
void setup_selinux(const char *domain)
|
||||||
{
|
{
|
||||||
if (transive_to_domain(domain)) {
|
if (transive_to_domain(domain)) {
|
||||||
pr_err("transive domain failed.\n");
|
pr_err("transive domain failed.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setenforce(bool enforce)
|
void setenforce(bool enforce)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||||
selinux_state.enforcing = enforce;
|
selinux_state.enforcing = enforce;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getenforce()
|
bool getenforce()
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
||||||
if (selinux_state.disabled) {
|
if (selinux_state.disabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||||
return selinux_state.enforcing;
|
return selinux_state.enforcing;
|
||||||
#else
|
#else
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 14, 0)
|
||||||
!defined(KSU_COMPAT_HAS_CURRENT_SID)
|
struct lsm_context {
|
||||||
/*
|
char *context;
|
||||||
* get the subjective security ID of the current task
|
u32 len;
|
||||||
*/
|
};
|
||||||
static inline u32 current_sid(void)
|
|
||||||
|
static int __security_secid_to_secctx(u32 secid, struct lsm_context *cp)
|
||||||
{
|
{
|
||||||
const struct task_security_struct *tsec = current_security();
|
return security_secid_to_secctx(secid, &cp->context, &cp->len);
|
||||||
|
|
||||||
return tsec->sid;
|
|
||||||
}
|
}
|
||||||
|
static void __security_release_secctx(struct lsm_context *cp)
|
||||||
|
{
|
||||||
|
return security_release_secctx(cp->context, cp->len);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define __security_secid_to_secctx security_secid_to_secctx
|
||||||
|
#define __security_release_secctx security_release_secctx
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool is_task_ksu_domain(const struct cred* cred)
|
||||||
|
{
|
||||||
|
struct lsm_context ctx;
|
||||||
|
bool result;
|
||||||
|
if (!cred) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const struct task_security_struct *tsec = selinux_cred(cred);
|
||||||
|
if (!tsec) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int err = __security_secid_to_secctx(tsec->sid, &ctx);
|
||||||
|
if (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0;
|
||||||
|
__security_release_secctx(&ctx);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool is_ksu_domain()
|
bool is_ksu_domain()
|
||||||
{
|
{
|
||||||
char *domain;
|
current_sid();
|
||||||
u32 seclen;
|
return is_task_ksu_domain(current_cred());
|
||||||
bool result;
|
|
||||||
int err = security_secid_to_secctx(current_sid(), &domain, &seclen);
|
|
||||||
if (err) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
result = strncmp(KERNEL_SU_DOMAIN, domain, seclen) == 0;
|
|
||||||
security_release_secctx(domain, seclen);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_zygote(void *sec)
|
bool is_context(const struct cred* cred, const char* context)
|
||||||
{
|
{
|
||||||
struct task_security_struct *tsec = (struct task_security_struct *)sec;
|
if (!cred) {
|
||||||
if (!tsec) {
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
const struct task_security_struct * tsec = selinux_cred(cred);
|
||||||
char *domain;
|
if (!tsec) {
|
||||||
u32 seclen;
|
return false;
|
||||||
bool result;
|
}
|
||||||
int err = security_secid_to_secctx(tsec->sid, &domain, &seclen);
|
struct lsm_context ctx;
|
||||||
if (err) {
|
bool result;
|
||||||
return false;
|
int err = __security_secid_to_secctx(tsec->sid, &ctx);
|
||||||
}
|
if (err) {
|
||||||
result = strncmp("u:r:zygote:s0", domain, seclen) == 0;
|
return false;
|
||||||
security_release_secctx(domain, seclen);
|
}
|
||||||
return result;
|
result = strncmp(context, ctx.context, ctx.len) == 0;
|
||||||
|
__security_release_secctx(&ctx);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DEVPTS_DOMAIN "u:object_r:ksu_file:s0"
|
bool is_zygote(const struct cred* cred)
|
||||||
|
|
||||||
u32 ksu_get_devpts_sid()
|
|
||||||
{
|
{
|
||||||
u32 devpts_sid = 0;
|
return is_context(cred, "u:r:zygote:s0");
|
||||||
int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN),
|
}
|
||||||
&devpts_sid);
|
|
||||||
if (err) {
|
bool is_init(const struct cred* cred) {
|
||||||
pr_info("get devpts sid err %d\n", err);
|
return is_context(cred, "u:r:init:s0");
|
||||||
}
|
}
|
||||||
return devpts_sid;
|
|
||||||
|
#define KSU_FILE_DOMAIN "u:object_r:ksu_file:s0"
|
||||||
|
|
||||||
|
u32 ksu_get_ksu_file_sid()
|
||||||
|
{
|
||||||
|
u32 ksu_file_sid = 0;
|
||||||
|
int err = security_secctx_to_secid(KSU_FILE_DOMAIN, strlen(KSU_FILE_DOMAIN),
|
||||||
|
&ksu_file_sid);
|
||||||
|
if (err) {
|
||||||
|
pr_info("get ksufile sid err %d\n", err);
|
||||||
|
}
|
||||||
|
return ksu_file_sid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "linux/types.h"
|
#include "linux/types.h"
|
||||||
#include "linux/version.h"
|
#include "linux/version.h"
|
||||||
|
#include "linux/cred.h"
|
||||||
|
|
||||||
void setup_selinux(const char *);
|
void setup_selinux(const char *);
|
||||||
|
|
||||||
@@ -10,12 +11,18 @@ void setenforce(bool);
|
|||||||
|
|
||||||
bool getenforce();
|
bool getenforce();
|
||||||
|
|
||||||
|
bool is_task_ksu_domain(const struct cred* cred);
|
||||||
|
|
||||||
bool is_ksu_domain();
|
bool is_ksu_domain();
|
||||||
|
|
||||||
bool is_zygote(void *cred);
|
bool is_zygote(const struct cred* cred);
|
||||||
|
|
||||||
|
bool is_init(const struct cred* cred);
|
||||||
|
|
||||||
void apply_kernelsu_rules();
|
void apply_kernelsu_rules();
|
||||||
|
|
||||||
u32 ksu_get_devpts_sid();
|
u32 ksu_get_ksu_file_sid();
|
||||||
|
|
||||||
|
int handle_sepolicy(unsigned long arg3, void __user *arg4);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -15,32 +15,32 @@ bool ksu_exists(struct policydb *db, const char *type);
|
|||||||
|
|
||||||
// Access vector rules
|
// Access vector rules
|
||||||
bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *perm);
|
const char *cls, const char *perm);
|
||||||
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *perm);
|
const char *cls, const char *perm);
|
||||||
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *perm);
|
const char *cls, const char *perm);
|
||||||
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *perm);
|
const char *cls, const char *perm);
|
||||||
|
|
||||||
// Extended permissions access vector rules
|
// Extended permissions access vector rules
|
||||||
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *range);
|
const char *cls, const char *range);
|
||||||
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *range);
|
const char *cls, const char *range);
|
||||||
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *range);
|
const char *cls, const char *range);
|
||||||
|
|
||||||
// Type rules
|
// Type rules
|
||||||
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *def, const char *obj);
|
const char *cls, const char *def, const char *obj);
|
||||||
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *def);
|
const char *cls, const char *def);
|
||||||
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *def);
|
const char *cls, const char *def);
|
||||||
|
|
||||||
// File system labeling
|
// File system labeling
|
||||||
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
|
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
|
||||||
const char *ctx);
|
const char *ctx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
171
kernel/setuid_hook.c
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#include <linux/compiler.h>
|
||||||
|
#include <linux/sched/signal.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
|
#include <linux/bpf.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/dcache.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/init_task.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
|
#include <linux/nsproxy.h>
|
||||||
|
#include <linux/path.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/security.h>
|
||||||
|
#include <linux/stddef.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/uidgid.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/binfmts.h>
|
||||||
|
#include <linux/tty.h>
|
||||||
|
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "setuid_hook.h"
|
||||||
|
#include "feature.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "manager.h"
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "seccomp_cache.h"
|
||||||
|
#include "supercalls.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
#include "kernel_umount.h"
|
||||||
|
#include "app_profile.h"
|
||||||
|
|
||||||
|
static bool ksu_enhanced_security_enabled = false;
|
||||||
|
|
||||||
|
static int enhanced_security_feature_get(u64 *value)
|
||||||
|
{
|
||||||
|
*value = ksu_enhanced_security_enabled ? 1 : 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int enhanced_security_feature_set(u64 value)
|
||||||
|
{
|
||||||
|
bool enable = value != 0;
|
||||||
|
ksu_enhanced_security_enabled = enable;
|
||||||
|
pr_info("enhanced_security: set to %d\n", enable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ksu_feature_handler enhanced_security_handler = {
|
||||||
|
.feature_id = KSU_FEATURE_ENHANCED_SECURITY,
|
||||||
|
.name = "enhanced_security",
|
||||||
|
.get_handler = enhanced_security_feature_get,
|
||||||
|
.set_handler = enhanced_security_feature_set,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool is_allow_su()
|
||||||
|
{
|
||||||
|
if (is_manager()) {
|
||||||
|
// we are manager, allow!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return ksu_is_allow_uid_for_current(current_uid().val);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid)
|
||||||
|
{
|
||||||
|
uid_t new_uid = ruid;
|
||||||
|
uid_t old_uid = current_uid().val;
|
||||||
|
|
||||||
|
pr_info("handle_setresuid from %d to %d\n", old_uid, new_uid);
|
||||||
|
|
||||||
|
// if old process is root, ignore it.
|
||||||
|
if (old_uid != 0 && ksu_enhanced_security_enabled) {
|
||||||
|
// disallow any non-ksu domain escalation from non-root to root!
|
||||||
|
// euid is what we care about here as it controls permission
|
||||||
|
if (unlikely(euid == 0)) {
|
||||||
|
if (!is_ksu_domain()) {
|
||||||
|
pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
|
||||||
|
current->pid, current->comm, old_uid, new_uid);
|
||||||
|
force_sig(SIGKILL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// disallow appuid decrease to any other uid if it is not allowed to su
|
||||||
|
if (is_appuid(old_uid)) {
|
||||||
|
if (euid < current_euid().val && !ksu_is_allow_uid_for_current(old_uid)) {
|
||||||
|
pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
|
||||||
|
current->pid, current->comm, old_uid, new_uid);
|
||||||
|
force_sig(SIGKILL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if on private space, see if its possibly the manager
|
||||||
|
if (new_uid > PER_USER_RANGE && new_uid % PER_USER_RANGE == ksu_get_manager_uid()) {
|
||||||
|
ksu_set_manager_uid(new_uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
|
if (ksu_get_manager_uid() == new_uid) {
|
||||||
|
pr_info("install fd for manager: %d\n", new_uid);
|
||||||
|
ksu_install_fd();
|
||||||
|
spin_lock_irq(¤t->sighand->siglock);
|
||||||
|
ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot);
|
||||||
|
ksu_set_task_tracepoint_flag(current);
|
||||||
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ksu_is_allow_uid_for_current(new_uid)) {
|
||||||
|
if (current->seccomp.mode == SECCOMP_MODE_FILTER &&
|
||||||
|
current->seccomp.filter) {
|
||||||
|
spin_lock_irq(¤t->sighand->siglock);
|
||||||
|
ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot);
|
||||||
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
}
|
||||||
|
ksu_set_task_tracepoint_flag(current);
|
||||||
|
} else {
|
||||||
|
ksu_clear_task_tracepoint_flag_if_needed(current);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (ksu_is_allow_uid_for_current(new_uid)) {
|
||||||
|
spin_lock_irq(¤t->sighand->siglock);
|
||||||
|
disable_seccomp();
|
||||||
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
|
||||||
|
if (ksu_get_manager_uid() == new_uid) {
|
||||||
|
pr_info("install fd for ksu manager(uid=%d)\n",
|
||||||
|
new_uid);
|
||||||
|
ksu_install_fd();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Handle kernel umount
|
||||||
|
ksu_handle_umount(old_uid, new_uid);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_setuid_hook_init(void)
|
||||||
|
{
|
||||||
|
ksu_kernel_umount_init();
|
||||||
|
if (ksu_register_feature_handler(&enhanced_security_handler)) {
|
||||||
|
pr_err("Failed to register enhanced security feature handler\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_setuid_hook_exit(void)
|
||||||
|
{
|
||||||
|
pr_info("ksu_core_exit\n");
|
||||||
|
ksu_kernel_umount_exit();
|
||||||
|
ksu_unregister_feature_handler(KSU_FEATURE_ENHANCED_SECURITY);
|
||||||
|
}
|
||||||
14
kernel/setuid_hook.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __KSU_H_KSU_CORE
|
||||||
|
#define __KSU_H_KSU_CORE
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include "apk_sign.h"
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
|
||||||
|
void ksu_setuid_hook_init(void);
|
||||||
|
void ksu_setuid_hook_exit(void);
|
||||||
|
|
||||||
|
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
GKI_ROOT=$(pwd)
|
KERNEL_ROOT=$(pwd)
|
||||||
|
|
||||||
display_usage() {
|
display_usage() {
|
||||||
echo "Usage: $0 [--cleanup | <commit-or-tag>]"
|
echo "Usage: $0 [--cleanup | <commit-or-tag>]"
|
||||||
@@ -12,10 +12,10 @@ display_usage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialize_variables() {
|
initialize_variables() {
|
||||||
if test -d "$GKI_ROOT/common/drivers"; then
|
if test -d "$KERNEL_ROOT/common/drivers"; then
|
||||||
DRIVER_DIR="$GKI_ROOT/common/drivers"
|
DRIVER_DIR="$KERNEL_ROOT/common/drivers"
|
||||||
elif test -d "$GKI_ROOT/drivers"; then
|
elif test -d "$KERNEL_ROOT/drivers"; then
|
||||||
DRIVER_DIR="$GKI_ROOT/drivers"
|
DRIVER_DIR="$KERNEL_ROOT/drivers"
|
||||||
else
|
else
|
||||||
echo '[ERROR] "drivers/" directory not found.'
|
echo '[ERROR] "drivers/" directory not found.'
|
||||||
exit 127
|
exit 127
|
||||||
@@ -30,22 +30,21 @@ perform_cleanup() {
|
|||||||
echo "[+] Cleaning up..."
|
echo "[+] Cleaning up..."
|
||||||
[ -L "$DRIVER_DIR/kernelsu" ] && rm "$DRIVER_DIR/kernelsu" && echo "[-] Symlink removed."
|
[ -L "$DRIVER_DIR/kernelsu" ] && rm "$DRIVER_DIR/kernelsu" && echo "[-] Symlink removed."
|
||||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" && sed -i '/kernelsu/d' "$DRIVER_MAKEFILE" && echo "[-] Makefile reverted."
|
grep -q "kernelsu" "$DRIVER_MAKEFILE" && sed -i '/kernelsu/d' "$DRIVER_MAKEFILE" && echo "[-] Makefile reverted."
|
||||||
grep -q "drivers/kernelsu/Kconfig" "$DRIVER_KCONFIG" && sed -i '/drivers\/kernelsu\/Kconfig/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted."
|
grep -q "kernelsu" "$DRIVER_KCONFIG" && sed -i '/kernelsu/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted."
|
||||||
if [ -d "$GKI_ROOT/KernelSU" ]; then
|
if [ -d "$KERNEL_ROOT/KernelSU" ]; then
|
||||||
rm -rf "$GKI_ROOT/KernelSU" && echo "[-] KernelSU directory deleted."
|
rm -rf "$KERNEL_ROOT/KernelSU" && echo "[-] KernelSU directory deleted."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Sets up or update KernelSU environment
|
# Sets up or update KernelSU environment
|
||||||
setup_kernelsu() {
|
setup_kernelsu() {
|
||||||
echo "[+] Setting up KernelSU..."
|
echo "[+] Setting up KernelSU..."
|
||||||
# Clone the repository and rename it to KernelSU
|
# Clone the repository
|
||||||
if [ ! -d "$GKI_ROOT/KernelSU" ]; then
|
if [ ! -d "$KERNEL_ROOT/KernelSU" ]; then
|
||||||
git clone https://github.com/ShirkNeko/SukiSU-Ultra SukiSU-Ultra
|
git clone https://github.com/SukiSU-Ultra/SukiSU-Ultra KernelSU
|
||||||
mv SukiSU-Ultra KernelSU
|
echo "[+] Repository cloned."
|
||||||
echo "[+] Repository cloned and renamed to KernelSU."
|
|
||||||
fi
|
fi
|
||||||
cd "$GKI_ROOT/KernelSU"
|
cd "$KERNEL_ROOT/KernelSU"
|
||||||
git stash && echo "[-] Stashed current changes."
|
git stash && echo "[-] Stashed current changes."
|
||||||
if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then
|
if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then
|
||||||
git checkout main && echo "[-] Switched to main branch."
|
git checkout main && echo "[-] Switched to main branch."
|
||||||
@@ -57,11 +56,11 @@ setup_kernelsu() {
|
|||||||
git checkout "$1" && echo "[-] Checked out $1." || echo "[-] Checkout default branch"
|
git checkout "$1" && echo "[-] Checked out $1." || echo "[-] Checkout default branch"
|
||||||
fi
|
fi
|
||||||
cd "$DRIVER_DIR"
|
cd "$DRIVER_DIR"
|
||||||
ln -sf "$(realpath --relative-to="$DRIVER_DIR" "$GKI_ROOT/KernelSU/kernel")" "kernelsu" && echo "[+] Symlink created."
|
ln -sf "$(realpath --relative-to="$DRIVER_DIR" "$KERNEL_ROOT/KernelSU/kernel")" "kernelsu" && echo "[+] Symlink created."
|
||||||
|
|
||||||
# Add entries in Makefile and Kconfig if not already existing
|
# Add entries in Makefile and Kconfig if not already existing
|
||||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" && echo "[+] Modified Makefile."
|
grep -q "kernelsu" "$DRIVER_MAKEFILE" || echo 'obj-$(CONFIG_KSU) += kernelsu/' >> "$DRIVER_MAKEFILE" && echo "[+] Modified Makefile."
|
||||||
grep -q "source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" && echo "[+] Modified Kconfig."
|
grep -q 'source "drivers/kernelsu/Kconfig"' "$DRIVER_KCONFIG" || sed -i '/endmenu/i\source "drivers/kernelsu/Kconfig"' "$DRIVER_KCONFIG" && echo "[+] Modified Kconfig."
|
||||||
echo '[+] Done.'
|
echo '[+] Done.'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,4 +76,4 @@ elif [ "$1" = "--cleanup" ]; then
|
|||||||
else
|
else
|
||||||
initialize_variables
|
initialize_variables
|
||||||
setup_kernelsu "$@"
|
setup_kernelsu "$@"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,250 +1,172 @@
|
|||||||
#include <linux/dcache.h>
|
#include "linux/compiler.h"
|
||||||
#include <linux/security.h>
|
#include "linux/printk.h"
|
||||||
#include <asm/current.h>
|
#include <asm/current.h>
|
||||||
#include <linux/cred.h>
|
#include <linux/cred.h>
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/kprobes.h>
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include <linux/sched/task_stack.h>
|
#include <linux/sched/task_stack.h>
|
||||||
|
#include <linux/ptrace.h>
|
||||||
|
|
||||||
#include "objsec.h"
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "feature.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksud.h"
|
#include "ksud.h"
|
||||||
#include "kernel_compat.h"
|
#include "sucompat.h"
|
||||||
|
#include "app_profile.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
|
||||||
|
#include "sulog.h"
|
||||||
|
|
||||||
#define SU_PATH "/system/bin/su"
|
#define SU_PATH "/system/bin/su"
|
||||||
#define SH_PATH "/system/bin/sh"
|
#define SH_PATH "/system/bin/sh"
|
||||||
|
|
||||||
extern void escape_to_root();
|
bool ksu_su_compat_enabled __read_mostly = true;
|
||||||
|
|
||||||
|
static int su_compat_feature_get(u64 *value)
|
||||||
|
{
|
||||||
|
*value = ksu_su_compat_enabled ? 1 : 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int su_compat_feature_set(u64 value)
|
||||||
|
{
|
||||||
|
bool enable = value != 0;
|
||||||
|
ksu_su_compat_enabled = enable;
|
||||||
|
pr_info("su_compat: set to %d\n", enable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ksu_feature_handler su_compat_handler = {
|
||||||
|
.feature_id = KSU_FEATURE_SU_COMPAT,
|
||||||
|
.name = "su_compat",
|
||||||
|
.get_handler = su_compat_feature_get,
|
||||||
|
.set_handler = su_compat_feature_set,
|
||||||
|
};
|
||||||
|
|
||||||
static void __user *userspace_stack_buffer(const void *d, size_t len)
|
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
|
// To avoid having to mmap a page in userspace, just write below the stack
|
||||||
* pointer. */
|
// pointer.
|
||||||
char __user *p = (void __user *)current_user_stack_pointer() - len;
|
char __user *p = (void __user *)current_user_stack_pointer() - len;
|
||||||
|
|
||||||
return copy_to_user(p, d, len) ? NULL : p;
|
return copy_to_user(p, d, len) ? NULL : p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char __user *sh_user_path(void)
|
static char __user *sh_user_path(void)
|
||||||
{
|
{
|
||||||
static const char sh_path[] = "/system/bin/sh";
|
static const char sh_path[] = "/system/bin/sh";
|
||||||
|
|
||||||
return userspace_stack_buffer(sh_path, sizeof(sh_path));
|
return userspace_stack_buffer(sh_path, sizeof(sh_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
static char __user *ksud_user_path(void)
|
static char __user *ksud_user_path(void)
|
||||||
{
|
{
|
||||||
static const char ksud_path[] = KSUD_PATH;
|
static const char ksud_path[] = KSUD_PATH;
|
||||||
|
|
||||||
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
|
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||||
int *__unused_flags)
|
int *__unused_flags)
|
||||||
{
|
{
|
||||||
const char su[] = SU_PATH;
|
const char su[] = SU_PATH;
|
||||||
|
|
||||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
if (!ksu_is_allow_uid_for_current(current_uid().val)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char path[sizeof(su) + 1];
|
char path[sizeof(su) + 1];
|
||||||
memset(path, 0, sizeof(path));
|
memset(path, 0, sizeof(path));
|
||||||
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
||||||
pr_info("faccessat su->sh!\n");
|
#if __SULOG_GATE
|
||||||
*filename_user = sh_user_path();
|
ksu_sulog_report_syscall(current_uid().val, NULL, "faccessat", path);
|
||||||
}
|
#endif
|
||||||
|
pr_info("faccessat su->sh!\n");
|
||||||
|
*filename_user = sh_user_path();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
||||||
{
|
{
|
||||||
// const char sh[] = SH_PATH;
|
// const char sh[] = SH_PATH;
|
||||||
const char su[] = SU_PATH;
|
const char su[] = SU_PATH;
|
||||||
|
|
||||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
if (!ksu_is_allow_uid_for_current(current_uid().val)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(!filename_user)) {
|
if (unlikely(!filename_user)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char path[sizeof(su) + 1];
|
char path[sizeof(su) + 1];
|
||||||
memset(path, 0, sizeof(path));
|
memset(path, 0, sizeof(path));
|
||||||
// Remove this later!! we use syscall hook, so this will never happen!!!!!
|
strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0
|
|
||||||
// it becomes a `struct filename *` after 5.18
|
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
||||||
// https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216
|
#if __SULOG_GATE
|
||||||
const char sh[] = SH_PATH;
|
ksu_sulog_report_syscall(current_uid().val, NULL, "newfstatat", path);
|
||||||
struct filename *filename = *((struct filename **)filename_user);
|
#endif
|
||||||
if (IS_ERR(filename)) {
|
pr_info("newfstatat su->sh!\n");
|
||||||
return 0;
|
*filename_user = sh_user_path();
|
||||||
}
|
}
|
||||||
if (likely(memcmp(filename->name, su, sizeof(su))))
|
|
||||||
return 0;
|
return 0;
|
||||||
pr_info("vfs_statx su->sh!\n");
|
}
|
||||||
memcpy((void *)filename->name, sh, sizeof(sh));
|
|
||||||
|
int ksu_handle_execve_sucompat(const char __user **filename_user,
|
||||||
|
void *__never_use_argv, void *__never_use_envp,
|
||||||
|
int *__never_use_flags)
|
||||||
|
{
|
||||||
|
const char su[] = SU_PATH;
|
||||||
|
char path[sizeof(su) + 1];
|
||||||
|
|
||||||
|
if (unlikely(!filename_user))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
|
if (likely(memcmp(path, su, sizeof(su))))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#if __SULOG_GATE
|
||||||
|
bool is_allowed = ksu_is_allow_uid_for_current(current_uid().val);
|
||||||
|
ksu_sulog_report_syscall(current_uid().val, NULL, "execve", path);
|
||||||
|
|
||||||
|
if (!is_allowed)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ksu_sulog_report_su_attempt(current_uid().val, NULL, path, is_allowed);
|
||||||
#else
|
#else
|
||||||
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
if (!ksu_is_allow_uid_for_current(current_uid().val)) {
|
||||||
|
return 0;
|
||||||
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
}
|
||||||
pr_info("newfstatat su->sh!\n");
|
|
||||||
*filename_user = sh_user_path();
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
pr_info("sys_execve su found\n");
|
||||||
|
*filename_user = ksud_user_path();
|
||||||
|
|
||||||
|
escape_with_root_profile();
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code
|
// sucompat: permitted process can execute 'su' to gain root access.
|
||||||
int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
|
||||||
void *__never_use_argv, void *__never_use_envp,
|
|
||||||
int *__never_use_flags)
|
|
||||||
{
|
|
||||||
struct filename *filename;
|
|
||||||
const char sh[] = KSUD_PATH;
|
|
||||||
const char su[] = SU_PATH;
|
|
||||||
|
|
||||||
if (unlikely(!filename_ptr))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
filename = *filename_ptr;
|
|
||||||
if (IS_ERR(filename)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (likely(memcmp(filename->name, su, sizeof(su))))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!ksu_is_allow_uid(current_uid().val))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
pr_info("do_execveat_common su found\n");
|
|
||||||
memcpy((void *)filename->name, sh, sizeof(sh));
|
|
||||||
|
|
||||||
escape_to_root();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
|
||||||
void *__never_use_argv, void *__never_use_envp,
|
|
||||||
int *__never_use_flags)
|
|
||||||
{
|
|
||||||
const char su[] = SU_PATH;
|
|
||||||
char path[sizeof(su) + 1];
|
|
||||||
|
|
||||||
if (unlikely(!filename_user))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
memset(path, 0, sizeof(path));
|
|
||||||
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
|
||||||
|
|
||||||
if (likely(memcmp(path, su, sizeof(su))))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!ksu_is_allow_uid(current_uid().val))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
pr_info("sys_execve su found\n");
|
|
||||||
*filename_user = ksud_user_path();
|
|
||||||
|
|
||||||
escape_to_root();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
|
||||||
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
|
||||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
|
||||||
const char __user **filename_user =
|
|
||||||
(const char **)&PT_REGS_PARM2(real_regs);
|
|
||||||
int *mode = (int *)&PT_REGS_PARM3(real_regs);
|
|
||||||
|
|
||||||
return ksu_handle_faccessat(dfd, filename_user, mode, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
|
||||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
|
||||||
const char __user **filename_user =
|
|
||||||
(const char **)&PT_REGS_PARM2(real_regs);
|
|
||||||
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs);
|
|
||||||
|
|
||||||
return ksu_handle_stat(dfd, filename_user, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
|
||||||
const char __user **filename_user =
|
|
||||||
(const char **)&PT_REGS_PARM1(real_regs);
|
|
||||||
|
|
||||||
return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kprobe *init_kprobe(const char *name,
|
|
||||||
kprobe_pre_handler_t handler)
|
|
||||||
{
|
|
||||||
struct kprobe *kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
|
|
||||||
if (!kp)
|
|
||||||
return NULL;
|
|
||||||
kp->symbol_name = name;
|
|
||||||
kp->pre_handler = handler;
|
|
||||||
|
|
||||||
int ret = register_kprobe(kp);
|
|
||||||
pr_info("sucompat: register_%s kprobe: %d\n", name, ret);
|
|
||||||
if (ret) {
|
|
||||||
kfree(kp);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return kp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroy_kprobe(struct kprobe **kp_ptr)
|
|
||||||
{
|
|
||||||
struct kprobe *kp = *kp_ptr;
|
|
||||||
if (!kp)
|
|
||||||
return;
|
|
||||||
unregister_kprobe(kp);
|
|
||||||
synchronize_rcu();
|
|
||||||
kfree(kp);
|
|
||||||
*kp_ptr = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kprobe *su_kps[3];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// sucompat: permited process can execute 'su' to gain root access.
|
|
||||||
void ksu_sucompat_init()
|
void ksu_sucompat_init()
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KPROBES
|
if (ksu_register_feature_handler(&su_compat_handler)) {
|
||||||
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
pr_err("Failed to register su_compat feature handler\n");
|
||||||
su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
|
}
|
||||||
su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_sucompat_exit()
|
void ksu_sucompat_exit()
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KPROBES
|
ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT);
|
||||||
for (int i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
}
|
||||||
destroy_kprobe(&su_kps[i]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
18
kernel/sucompat.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef __KSU_H_SUCOMPAT
|
||||||
|
#define __KSU_H_SUCOMPAT
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
extern bool ksu_su_compat_enabled;
|
||||||
|
|
||||||
|
void ksu_sucompat_init(void);
|
||||||
|
void ksu_sucompat_exit(void);
|
||||||
|
|
||||||
|
// Handler functions exported for hook_manager
|
||||||
|
int ksu_handle_faccessat(int *dfd, const char __user **filename_user,
|
||||||
|
int *mode, int *__unused_flags);
|
||||||
|
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
||||||
|
int ksu_handle_execve_sucompat(const char __user **filename_user,
|
||||||
|
void *__never_use_argv, void *__never_use_envp,
|
||||||
|
int *__never_use_flags);
|
||||||
|
|
||||||
|
#endif
|
||||||
369
kernel/sulog.c
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/time.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
#include "klog.h"
|
||||||
|
|
||||||
|
#include "sulog.h"
|
||||||
|
#include "ksu.h"
|
||||||
|
#include "feature.h"
|
||||||
|
|
||||||
|
#if __SULOG_GATE
|
||||||
|
|
||||||
|
struct dedup_entry dedup_tbl[SULOG_COMM_LEN];
|
||||||
|
static DEFINE_SPINLOCK(dedup_lock);
|
||||||
|
static LIST_HEAD(sulog_queue);
|
||||||
|
static struct workqueue_struct *sulog_workqueue;
|
||||||
|
static struct work_struct sulog_work;
|
||||||
|
static bool sulog_enabled __read_mostly = true;
|
||||||
|
|
||||||
|
static int sulog_feature_get(u64 *value)
|
||||||
|
{
|
||||||
|
*value = sulog_enabled ? 1 : 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sulog_feature_set(u64 value)
|
||||||
|
{
|
||||||
|
bool enable = value != 0;
|
||||||
|
sulog_enabled = enable;
|
||||||
|
pr_info("sulog: set to %d\n", enable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ksu_feature_handler sulog_handler = {
|
||||||
|
.feature_id = KSU_FEATURE_SULOG,
|
||||||
|
.name = "sulog",
|
||||||
|
.get_handler = sulog_feature_get,
|
||||||
|
.set_handler = sulog_feature_set,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void get_timestamp(char *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct timespec64 ts;
|
||||||
|
struct tm tm;
|
||||||
|
|
||||||
|
ktime_get_real_ts64(&ts);
|
||||||
|
time64_to_tm(ts.tv_sec - sys_tz.tz_minuteswest * 60, 0, &tm);
|
||||||
|
|
||||||
|
snprintf(buf, len, "%04ld-%02d-%02d %02d:%02d:%02d",
|
||||||
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
||||||
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ksu_get_cmdline(char *full_comm, const char *comm, size_t buf_len)
|
||||||
|
{
|
||||||
|
if (!full_comm || buf_len <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (comm && strlen(comm) > 0) {
|
||||||
|
KSU_STRSCPY(full_comm, comm, buf_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_atomic() || in_interrupt() || irqs_disabled()) {
|
||||||
|
KSU_STRSCPY(full_comm, current->comm, buf_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current->mm) {
|
||||||
|
KSU_STRSCPY(full_comm, current->comm, buf_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = get_cmdline(current, full_comm, buf_len);
|
||||||
|
if (n <= 0) {
|
||||||
|
KSU_STRSCPY(full_comm, current->comm, buf_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < n && i < buf_len - 1; i++) {
|
||||||
|
if (full_comm[i] == '\0')
|
||||||
|
full_comm[i] = ' ';
|
||||||
|
}
|
||||||
|
full_comm[n < buf_len ? n : buf_len - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sanitize_string(char *str, size_t len)
|
||||||
|
{
|
||||||
|
if (!str || len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size_t read_pos = 0, write_pos = 0;
|
||||||
|
|
||||||
|
while (read_pos < len && str[read_pos] != '\0') {
|
||||||
|
char c = str[read_pos];
|
||||||
|
|
||||||
|
if (c == '\n' || c == '\r') {
|
||||||
|
read_pos++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == ' ' && write_pos > 0 && str[write_pos - 1] == ' ') {
|
||||||
|
read_pos++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
str[write_pos++] = c;
|
||||||
|
read_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
str[write_pos] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool dedup_should_print(uid_t uid, u8 type, const char *content, size_t len)
|
||||||
|
{
|
||||||
|
struct dedup_key key = {
|
||||||
|
.crc = dedup_calc_hash(content, len),
|
||||||
|
.uid = uid,
|
||||||
|
.type = type,
|
||||||
|
};
|
||||||
|
u64 now = ktime_get_ns();
|
||||||
|
u64 delta_ns = DEDUP_SECS * NSEC_PER_SEC;
|
||||||
|
|
||||||
|
u32 idx = key.crc & (SULOG_COMM_LEN - 1);
|
||||||
|
spin_lock(&dedup_lock);
|
||||||
|
|
||||||
|
struct dedup_entry *e = &dedup_tbl[idx];
|
||||||
|
if (e->key.crc == key.crc &&
|
||||||
|
e->key.uid == key.uid &&
|
||||||
|
e->key.type == key.type &&
|
||||||
|
(now - e->ts_ns) < delta_ns) {
|
||||||
|
spin_unlock(&dedup_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
e->key = key;
|
||||||
|
e->ts_ns = now;
|
||||||
|
spin_unlock(&dedup_lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sulog_work_handler(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct file *fp;
|
||||||
|
struct sulog_entry *entry, *tmp;
|
||||||
|
LIST_HEAD(local_queue);
|
||||||
|
loff_t pos = 0;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dedup_lock, flags);
|
||||||
|
list_splice_init(&sulog_queue, &local_queue);
|
||||||
|
spin_unlock_irqrestore(&dedup_lock, flags);
|
||||||
|
|
||||||
|
if (list_empty(&local_queue))
|
||||||
|
return;
|
||||||
|
|
||||||
|
fp = filp_open(SULOG_PATH, O_WRONLY | O_CREAT | O_APPEND, 0640);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("sulog: failed to open log file: %ld\n", PTR_ERR(fp));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fp->f_inode->i_size > SULOG_MAX_SIZE) {
|
||||||
|
if (vfs_truncate(&fp->f_path, 0))
|
||||||
|
pr_err("sulog: failed to truncate log file\n");
|
||||||
|
pos = 0;
|
||||||
|
} else {
|
||||||
|
pos = fp->f_inode->i_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &local_queue, list)
|
||||||
|
kernel_write(fp, entry->content, strlen(entry->content), &pos);
|
||||||
|
|
||||||
|
vfs_fsync(fp, 0);
|
||||||
|
filp_close(fp, 0);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
list_for_each_entry_safe(entry, tmp, &local_queue, list) {
|
||||||
|
list_del(&entry->list);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sulog_add_entry(char *log_buf, size_t len, uid_t uid, u8 dedup_type)
|
||||||
|
{
|
||||||
|
struct sulog_entry *entry;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (!sulog_enabled || !log_buf || len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!dedup_should_print(uid, dedup_type, log_buf, len))
|
||||||
|
return;
|
||||||
|
|
||||||
|
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
|
||||||
|
if (!entry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
KSU_STRSCPY(entry->content, log_buf, SULOG_ENTRY_MAX_LEN);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dedup_lock, flags);
|
||||||
|
list_add_tail(&entry->list, &sulog_queue);
|
||||||
|
spin_unlock_irqrestore(&dedup_lock, flags);
|
||||||
|
|
||||||
|
if (sulog_workqueue)
|
||||||
|
queue_work(sulog_workqueue, &sulog_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method)
|
||||||
|
{
|
||||||
|
char log_buf[SULOG_ENTRY_MAX_LEN];
|
||||||
|
char timestamp[32];
|
||||||
|
char full_comm[SULOG_COMM_LEN];
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
get_timestamp(timestamp, sizeof(timestamp));
|
||||||
|
ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
sanitize_string(full_comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
snprintf(log_buf, sizeof(log_buf),
|
||||||
|
"[%s] SU_GRANT: UID=%d COMM=%s METHOD=%s PID=%d\n",
|
||||||
|
timestamp, uid, full_comm, method ? method : "unknown", current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SU_GRANT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success)
|
||||||
|
{
|
||||||
|
char log_buf[SULOG_ENTRY_MAX_LEN];
|
||||||
|
char timestamp[32];
|
||||||
|
char full_comm[SULOG_COMM_LEN];
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
get_timestamp(timestamp, sizeof(timestamp));
|
||||||
|
ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
sanitize_string(full_comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
snprintf(log_buf, sizeof(log_buf),
|
||||||
|
"[%s] SU_EXEC: UID=%d COMM=%s TARGET=%s RESULT=%s PID=%d\n",
|
||||||
|
timestamp, uid, full_comm, target_path ? target_path : "unknown",
|
||||||
|
success ? "SUCCESS" : "DENIED", current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SU_ATTEMPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed)
|
||||||
|
{
|
||||||
|
char log_buf[SULOG_ENTRY_MAX_LEN];
|
||||||
|
char timestamp[32];
|
||||||
|
char full_comm[SULOG_COMM_LEN];
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
get_timestamp(timestamp, sizeof(timestamp));
|
||||||
|
ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
sanitize_string(full_comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
snprintf(log_buf, sizeof(log_buf),
|
||||||
|
"[%s] PERM_CHECK: UID=%d COMM=%s RESULT=%s PID=%d\n",
|
||||||
|
timestamp, uid, full_comm, allowed ? "ALLOWED" : "DENIED", current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_PERM_CHECK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid)
|
||||||
|
{
|
||||||
|
char log_buf[SULOG_ENTRY_MAX_LEN];
|
||||||
|
char timestamp[32];
|
||||||
|
char full_comm[SULOG_COMM_LEN];
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
get_timestamp(timestamp, sizeof(timestamp));
|
||||||
|
ksu_get_cmdline(full_comm, NULL, sizeof(full_comm));
|
||||||
|
|
||||||
|
sanitize_string(full_comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
snprintf(log_buf, sizeof(log_buf),
|
||||||
|
"[%s] MANAGER_OP: OP=%s MANAGER_UID=%d TARGET_UID=%d COMM=%s PID=%d\n",
|
||||||
|
timestamp, operation ? operation : "unknown", manager_uid, target_uid, full_comm, current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf, strlen(log_buf), manager_uid, DEDUP_MANAGER_OP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_syscall(uid_t uid, const char *comm, const char *syscall, const char *args)
|
||||||
|
{
|
||||||
|
char log_buf[SULOG_ENTRY_MAX_LEN];
|
||||||
|
char timestamp[32];
|
||||||
|
char full_comm[SULOG_COMM_LEN];
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
get_timestamp(timestamp, sizeof(timestamp));
|
||||||
|
ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
sanitize_string(full_comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
snprintf(log_buf, sizeof(log_buf),
|
||||||
|
"[%s] SYSCALL: UID=%d COMM=%s SYSCALL=%s ARGS=%s PID=%d\n",
|
||||||
|
timestamp, uid, full_comm, syscall ? syscall : "unknown",
|
||||||
|
args ? args : "none", current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SYSCALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_sulog_init(void)
|
||||||
|
{
|
||||||
|
if (ksu_register_feature_handler(&sulog_handler)) {
|
||||||
|
pr_err("Failed to register sulog feature handler\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
sulog_workqueue = alloc_workqueue("ksu_sulog", WQ_UNBOUND | WQ_HIGHPRI, 1);
|
||||||
|
if (!sulog_workqueue) {
|
||||||
|
pr_err("sulog: failed to create workqueue\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_WORK(&sulog_work, sulog_work_handler);
|
||||||
|
pr_info("sulog: initialized successfully\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_exit(void)
|
||||||
|
{
|
||||||
|
struct sulog_entry *entry, *tmp;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
ksu_unregister_feature_handler(KSU_FEATURE_SULOG);
|
||||||
|
|
||||||
|
sulog_enabled = false;
|
||||||
|
|
||||||
|
if (sulog_workqueue) {
|
||||||
|
flush_workqueue(sulog_workqueue);
|
||||||
|
destroy_workqueue(sulog_workqueue);
|
||||||
|
sulog_workqueue = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dedup_lock, flags);
|
||||||
|
list_for_each_entry_safe(entry, tmp, &sulog_queue, list) {
|
||||||
|
list_del(&entry->list);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&dedup_lock, flags);
|
||||||
|
|
||||||
|
pr_info("sulog: cleaned up successfully\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __SULOG_GATE
|
||||||