300 Commits

Author SHA1 Message Date
ShirkNeko
5ba4f73eeb manager: Update to version 3.1.8 and rename the relevant version checking functions 2025-07-21 21:22:07 +08:00
ShirkNeko
139899d05d [skip ci]manager: Add SUS loop path function 2025-07-21 21:14:09 +08:00
awkoo
d2ab325e18 优化setup.sh (#288) 2025-07-21 20:23:08 +08:00
ShirkNeko
be393ddb7c manager: Updating susfs binaries 2025-07-21 16:28:50 +08:00
WenHao2130
467d6e0838 ksud: Add KSU_SUKISU to env
This will help module authors determine which root implementation the module is running on.

Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
2025-07-20 23:31:29 +08:00
米凛MiRin
46fcf97be3 readme: add chinese translation (#290) 2025-07-20 12:56:03 +05:30
Juno Bủh
72ed1bc4a2 Update Vietnamese Translation (#284) 2025-07-19 17:41:34 +08:00
ShirkNeko
b8544b4f53 manager: Adding a separate status to the update module
fix duplicate installations of update modules due to status confusion. fix #287

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-07-19 16:59:36 +08:00
Cloud_Yun
60f0a721ce script: Simplify repository cloning logic (#283)
Rather than cloning to SukiSU-Ultra and renaming it KernelSU, it would be better to clone it directly as KernelSU.

Signed-off-by: Cloud_Yun <1770669041@qq.com>
2025-07-17 19:37:31 +09:00
ShirkNeko
c62676d643 New Crowdin updates (#280)
* New translations strings.xml (Japanese)

* New translations strings.xml (Russian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Vietnamese)
2025-07-17 14:32:35 +08:00
米凛MiRin
dd2b2e995a docs: fix wrong infomation (#282)
* docs: fix wrong infomation

* docs: fix wrong infomation
2025-07-17 14:32:20 +08:00
MiRinChan
86456a4d95 README & docs: Revise the README and documentation. Thanks to lama's help. 2025-07-16 17:18:31 +05:30
Prslc
11e9e37f43 manager: fix typo in hide_zygisk_implement string and summary (#279)
Signed-off-by: prslc <prslc113@gmail.com>
2025-07-16 18:16:34 +08:00
Prslc
a8a2aef4b5 manager: Display Zygisk implementation method with rezygisk support (#278)
* manager: Display Zygisk implementation method with rezygisk support

Signed-off-by: prslc <prslc113@gmail.com>

* manager: Fix incorrect display of Zygisk implementation when module is disabled

Signed-off-by: prslc <prslc113@gmail.com>

---------

Signed-off-by: prslc <prslc113@gmail.com>
2025-07-16 15:08:20 +08:00
Prslc
77ac0d70fd [skip ci]manager: Add switch to hide Zygisk implementation status information (#277)
Signed-off-by: prslc <prslc113@gmail.com>
2025-07-16 15:08:08 +08:00
wxxsfxyzm
2a00ef96c2 UPSTREAM: manager: improve predictive back animations
This pull request introduces custom screen transition animations to
enhance the overall user experience during navigation.

The key change is the implementation of a custom slide/fade effect for
navigating from main screens (i.e., screens hosted in the bottom
navigation bar) to detail screens. Transitions between the bottom
navigation bar tabs themselves retain a simple, clean cross-fade effect
to ensure a fast and smooth user interaction.

This PR also addresses the root cause of an issue where custom
animations were being overridden by the navigation library's defaults.

During implementation, it was discovered that custom transition
animations defined in the `defaultTransitions` parameter of the
`DestinationsNavHost` in `MainActivity` were not being applied. Instead,
a default fade-in/fade-out animation was always present.

The root cause was traced to the `compose-destinations` KSP (Kotlin
Symbol Processing) code generator. By default, the generator creates a
`NavGraphSpec` (e.g., `RootNavGraph.kt`) that includes its own
`defaultTransitions` property. This property, defined at compile-time
within the generated graph object, has a higher precedence than the
`defaultTransitions` parameter supplied to the `DestinationsNavHost`
composable at runtime.

As a result, our intended custom animations were being ignored and
overridden by the generated default.

To resolve this precedence issue permanently, this PR adopts the
official configuration method recommended by the `compose-destinations`
library.

- The following KSP argument has been added to the
`app/build.gradle.kts` file:

```kotlin
ksp {
    arg("compose-destinations.defaultTransitions", "none")
}
```

- This argument instructs the code generator to omit the
`defaultTransitions` property from the generated `NavGraphSpec`.

- By removing the higher-priority, generated default, the
`defaultTransitions` parameter on `DestinationsNavHost` now functions as
the effective default, allowing our custom animation logic to execute as
intended.

The new animation logic is conditional and defined within
`MainActivity`. It distinguishes between two primary navigation types:

- Main Screen → Detail Screen:

   - Enter: The new detail screen slides in from the right.

   - Exit: The old main screen slides out to the left while fading out.

- Detail Screen → Main Screen (on Pop):

- Pop Enter: The main screen slides back in from the left while fading
in.

   - Pop Exit: The detail screen slides out to the right.

- Between Bottom Navigation Tabs:

- A simple cross-fade (`fadeIn`/`fadeOut`) is maintained for these
transitions to provide a quick and non-disruptive experience when
switching between primary sections of the app.
2025-07-15 09:02:06 +08:00
YangQi0408
39ee1cc41d kernel: core_hook: add support for KernelNoSU (#270)
reorder ksu_handle_prctl checks a bit to allow non-manager to use CMD 15
this allows us to piggyback a small su to KernelSU's permission system after
disabling kernel sucompat

from:
Relax prctl perm check
- 95125c32f9
Allow prctl only for root or manager or su binary
- fa7af67d94
Refine prctl access check, allow /product/bin/su
- dd466dc1b6
Refine prctl check a little bit more
- e7c5b24efa

Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
Co-authored-by: nampud <nampud@users.noreply.github.com>
2025-07-14 15:24:48 +08:00
米凛MiRin
51556d1253 strings: add copyright infomation (#275)
* copyright: Correct copyright owner information

* string: add copyright infomation
2025-07-14 15:21:48 +08:00
米凛MiRin
94df64f234 copyright: Correct copyright owner information (#273) 2025-07-14 00:08:00 +08:00
WenHao2130
02f545b3fb [skip ci] Revert "Redirect the Feature Requests issue template tab (#1788)"
This reverts commit 3695435c3f.

Allow feature requests

Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
2025-07-13 10:50:28 +08:00
ShirkNeko
62c7aac75b Merge branch 'main' of https://github.com/SukiSU-Ultra/SukiSU-Ultra 2025-07-12 11:43:23 +08:00
ShirkNeko
755d454960 manager:Add flag bits to module installations to prevent repeated installations
- Modify the time and location of the susfs self-startup scripts
2025-07-12 11:41:58 +08:00
Marat Budkevich
9717fa0de6 fix(manager): add extraspace after "UID:" for Home screen (#268) 2025-07-11 19:18:03 +08:00
ShirkNeko
bd6eb7fddd manager: Update agp version
- Fix bug caused by SuSFS configuration screen returning too quickly
2025-07-11 10:56:30 +08:00
ShirkNeko
9f706873f2 manager: Fixed susfs binary naming scheme 2025-07-09 18:54:27 +08:00
ShirkNeko
c424d5bab4 kernel: simplified signature verification 2025-07-09 12:12:12 +08:00
WenHao2130
e795387c30 manager: Reformat zh-rCN strings
Change-Id: I9880ac9c4e6f74d30110cccfbd774ddfebf89ac0
Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
2025-07-09 11:07:54 +08:00
ShirkNeko
044dd9471f manager: Refactor the way information is fetched in the home screen to avoid null pointers
- add an auto-refresh cache feature
2025-07-09 01:33:00 +08:00
YangQi0408
213a15cdb6 manager: 显示 Zygisk 实现方式 (#264)
* manager: Add display of Zygisk Implement feature and formatting code

* manager: Change the method of detecting Zygisk Implement
Signed-off-by: YangQi0408 <a123er3643@163.com>
Co-authored-by: WenHao2130 <WenHao2130@outlook.com>
2025-07-08 22:34:34 +08:00
ShirkNeko
a255ea9d56 [skip ci]New Crowdin updates (#263)
* New translations strings.xml (Russian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Vietnamese)
2025-07-07 17:49:24 +08:00
ShirkNeko
6cbe13dafc Assets: Add susfs executable binary v1.5.9 2025-07-06 14:49:54 +08:00
ShirkNeko
fb5c7c2f9d manager: Updated dependency library version
- Add JNI macros to simplify field setup and object creation
2025-07-06 14:44:36 +08:00
ShirkNeko
b8f2d15bd1 manager: Modify the separator for information cards from a semicolon to a vertical line 2025-07-06 00:56:15 +08:00
ShirkNeko
2bd6929d24 manager: Add the ability to get a list of active managers 2025-07-06 00:26:42 +08:00
ShirkNeko
42b883240e kernel: add multi-manager support 2025-07-05 22:27:39 +08:00
ShirkNeko
4840540038 manager: Modify Dynamic Signature Configuration Input Verification
- Allow sizes beginning with 0x
2025-07-05 17:39:24 +08:00
Juno Bủh
7bfb37a11e [skip ci]Update Vietnamese Translation (#257)
* Update Vietnamese Translation

* Update strings.xml

* Update strings.xml
2025-07-05 16:37:48 +08:00
ShirkNeko
dd6d695020 kernel:Add Dynamic Signature Configuration 2025-07-05 16:21:29 +08:00
ShirkNeko
6a60b72e21 manager: Reorganizing the backup and restore functionality of SuSFS configurations
- Add checking for the existence of a data catalog for applications in SUS Path

- Modify the loading and caching mechanism of the application information class to avoid repeated refreshes,Finish loading with SuperUser.
2025-07-04 17:57:54 +08:00
ShirkNeko
ea3a0cf73b manager: Update the name in the webui description to “SukiSU-Ultra” 2025-07-03 21:09:37 +08:00
ShirkNeko
1816d15ce8 manager: Refactor show SuSFS features
- need to update the kernel
2025-07-03 19:25:52 +08:00
ShirkNeko
79c8f7a709 New Crowdin updates (#247)
* New translations strings.xml (Russian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Japanese)

* New translations strings.xml (Turkish)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Russian)

* New translations strings.xml (Vietnamese)
2025-07-03 14:00:45 +08:00
ShirkNeko
b0cc0e6f6d kernel: Update CMD_GET_FULL_VERSION to a new constant value 2025-07-03 00:08:54 +08:00
ShirkNeko
eccc70c0c9 manager: Improve loading and empty state handling in SuperUserScreen 2025-07-03 00:03:59 +08:00
Juno Bủh
fc828ff3aa [skip ci]: Update Vietnamese Translation (#248)
* Update Vietnamese Translation

* Update Vietnamese Translation
2025-07-02 23:34:48 +08:00
ShirkNeko
acb7cfff1b Revert: Rollback some of the changes in "Add option to use WebUI X" and refactor the KsuService
- Solve the problem that SuperUser is not available when opening SU compatibility.
2025-07-02 01:37:37 +08:00
nanLianQing
3729c22dd0 fix: Fix application crashes caused by improper handling of coroutine exceptions (#249) 2025-07-01 23:26:36 +08:00
ShirkNeko
a84cf70730 manager:Fixed the issue of not getting the corresponding app name and icon in SuSFS paths 2025-07-01 20:00:26 +08:00
ShirkNeko
2278fe49d2 manager: Adding optional additions to SUS paths applies functionality corresponding to the package name as well as categorization 2025-07-01 17:29:45 +08:00
ShirkNeko
be14da387e manager: replace Runtime.exec with getRootShell for command execution 2025-07-01 02:48:09 +08:00
ShirkNeko
1fd13d9d8d docs: Updated documentation to introduce SuSFS Configuration Manager and introduction of advanced features 2025-06-30 16:57:56 +08:00
NkBe
4205db6870 [skip ci]Update Traditional Chinese (Taiwan) (#242) 2025-06-30 16:43:32 +08:00
ShirkNeko
70f03081a4 [skip ci]New Crowdin updates (#240)
* New translations strings.xml (Japanese)

* New translations strings.xml (Russian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Chinese Traditional, Hong Kong)
2025-06-30 15:51:32 +08:00
Kitty
5ccb779b6a [skip ci] Update Ukrainian translation (#236) 2025-06-30 15:35:34 +08:00
Juno Bủh
b07bc408ce [skip ci] Update Vietnamese Translation (#238)
* Update Vietnamese Translation

* Update Vietnamese Translation

* Update Vietnam
2025-06-30 15:35:12 +08:00
ShirkNeko
7ee1fd63f1 manager: Add Zygote isolation service uninstallation to susfs feature 2025-06-30 15:32:47 +08:00
ShirkNeko
3551441e42 manager: Provides re-editable functionality for all SuSFS path configurations 2025-06-29 20:36:45 +08:00
ShirkNeko
4a1ab76322 manager: Optimize the logical order of Susfs self boot scripts 2025-06-29 19:06:20 +08:00
ShirkNeko
2fedb051b8 manager: Add Hide Bootloader Scripts and Cleanup Tool Residue feature and switch 2025-06-29 17:50:39 +08:00
Juno Bủh
10c35f4baa Update Vietnamese Translation (#229)
* Update Vietnamese Translation

* Update Vietnamese Translation
2025-06-29 12:32:05 +08:00
科家
4f82eda003 manager: Only display slotinformation on AB devices (#233)
还是有人在用 A-Only 分区设备的
2025-06-29 12:31:50 +08:00
Prslc
80f89c0241 [skip ci] manager: Fix unintended LKM dialog when selecting AnyKernel3 install method (#234)
This fixes a logic issue where the KMI selection dialog would still appear
even when the user selected the AnyKernel3 install method.

Signed-off-by: prslc <prslc113@gmail.com>
2025-06-29 12:30:34 +08:00
ShirkNeko
8399f14fad manager: Add SuSFS configuration backup and restore feature
- Optimize susfs self-boot scripts
- Solve some invalid issues where startup duration does not match or is too fast.

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-28 23:19:18 +08:00
YC酱luyancib
c49a66d1af Update Kconfig (#224)
make sure kpm config requrement was enabled
2025-06-28 10:19:53 +05:30
Juno Bủh
d66b390361 Update Vietnamese Translation (#223) 2025-06-27 18:47:02 +08:00
ShirkNeko
9c290a8080 [skip ci]: New Crowdin updates (#222)
* New translations strings.xml (Japanese)

* New translations strings.xml (Russian)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Chinese Traditional, Hong Kong)
2025-06-27 18:46:45 +08:00
Saksham Singla
48efc28e8f Update Funding.yml (#221)
* Update FUNDING.yml

* add sukisu donation

---------

Co-authored-by: weishu <twsxtd@gmail.com>
2025-06-27 16:55:22 +08:00
ShirkNeko
634adad15c ksud: fmt 2025-06-27 11:15:19 +08:00
古塵
4532bab230 kernel: refactor CMD_GET_FULL_VERSION to safely initialize version string (#220)
Use strscpy()/strlcpy() to populate the version buffer in CMD_GET_FULL_VERSION
instead of relying on uninitialized memory. This ensures the returned string
is null-terminated and avoids exposing garbage data to user space.

Signed-off-by: schqiushui <orochi9999@gmail.com>
2025-06-27 10:42:38 +08:00
ShirkNeko
d3c9b6e739 ksud: handle errors and non compatible ramdisk
Co-authored-by: rifsxd <rifat.44.azad.rifs@gmail.com>
2025-06-26 14:22:42 +08:00
ShirkNeko
8e4f980db0 ksud: third test properly check if vendor is already patched or not for lkm restoration and also handle magisk patched vendor boot
Co-authored-by: rifsxd <rifat.44.azad.rifs@gmail.com>
2025-06-26 14:15:49 +08:00
ShirkNeko
cfee357ed1 manager: Optimize susfs management,
- solve some problems caused by new versions not taking effect
2025-06-26 14:10:50 +08:00
ShirkNeko
9393459b27 manager: Improvements to susfs functionality status fetch exceptions
- Show false if unable to fetch
2025-06-26 00:24:33 +08:00
ShirkNeko
60af173a7e manager:Optimize the path configuration to automatically configure the corresponding value as long as the self-boot is enabled 2025-06-25 23:39:04 +08:00
NkBe
23e2377f87 [skip ci]Update README.md (#218)
就单纯怎么看怎么怪,所以手欠改一改
2025-06-25 20:13:06 +08:00
NkBe
d45ba31849 manger: fix lkm detection (#217)
* manger: Rollback crowdin Update (Traditional Chinese Taiwan)

* manger: Try to modify the lkm detection

改了下判斷方式,現在只要核心返回的 version_flags最後一位是 1,is_lkm就會被正確地設成 true
2025-06-25 19:26:54 +08:00
Saksham Singla
c5705c2d5d Bump agp and Gradle to latest (#216)
* Bump AGP

* Bump Gradle

* update gradle to 14.2
2025-06-25 19:15:17 +08:00
ShirkNeko
dfae83cf58 New Crowdin updates (#215)
* New translations strings.xml (Russian)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Chinese Traditional, Hong Kong)
2025-06-25 16:33:07 +08:00
NkBe
cd5ba3ac3c [skip ci] manger: Update Traditional Chinese (Taiwan) (#214) 2025-06-25 16:26:24 +08:00
ShirkNeko
2c2698f6bc kernel: Rewrite kernle version code management
Co-authored-by: lamadaemon <i@lama.icu>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-24 23:16:16 +08:00
NkBe
f57fe79c5d manager: Update LKM select file prompt, optimize “init_boot / vendor_boot” text (#211)
* Update Install.kt

* Update Install.kt

* Update strings.xml

* Update strings-rCN.xml

* Update strings-rTW.xml

* manger: Add a space to ensure pangu

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-24 15:50:48 +08:00
ShirkNeko
91ae4c9650 manager: Update LKM select file prompt, support “init_boot / vendor_boot” option 2025-06-24 15:03:14 +08:00
ShirkNeko
01f44dc1d9 ksud: Adding LKM patch support for vendor_boot images
- And improve the OTA update mechanism
- Updating the magiskboot library

Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: rifsxd <rifat.44.azad.rifs@gmail.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-24 12:52:22 +08:00
Prslc
6e35b88041 Kernel: use main branch commit count for consistent versioning (#209) 2025-06-23 18:59:30 +08:00
Re*Index. (ot_inc)
c9c122d79b skip ci :Update & Fix Japanese (#208)
* Update README-ja.md

* Update README-ja.md

* Update README-ja.md

* Update strings.xml

* Update strings.xml

* Update strings.xml
2025-06-23 18:58:03 +08:00
ShirkNeko
4bec5ae7b1 ksud: Update version code calculations to adjust historical version offsets 2025-06-22 18:45:58 +08:00
ShirkNeko
f9b3478dbb Fix text parameter formatting in KstatConfigItemCard, remove redundant “parameter:” prefixes 2025-06-22 18:33:50 +08:00
ShirkNeko
561c82de0a manager: Refactoring the SuSFS configuration layout and simplifying functions 2025-06-22 18:31:11 +08:00
Amicia De Rune
e96ceb84c9 Update Indonesia Translate (#204)
* Update Indonesia Translate

* Update manager/app/src/main/res/values-in/strings.xml

<string name="show_kpm_info">Sembunyikan fungsi KPM</string>

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update manager/app/src/main/res/values-in/strings.xml

<string name="show_kpm_info_summary">Sembunyikan fungsi informasi KPM dan menu KPM di bilah navigasi</string>

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update manager/app/src/main/res/values-in/strings.xml

<string name="home_ContributionCard_kernelsu">Antusias Untuk SukiSU Ultra</string>

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update manager/app/src/main/res/values-in/strings.xml

<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra akan menjadi cabang KSU yang relatif independen di masa mendatang, tetapi kami tetap menghargai KernelSU dan MKSU resmi dan sebagainya atas kontribusi mereka!</string>

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update manager/app/src/main/res/values-in/strings.xml

<string name="engine_force_ksu">Penggunaan wajib KSU WebUI</string>

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update manager/app/src/main/res/values-in/strings.xml

<string name="language_changed">Bahasa dirubah, mulai ulang aplikasi untuk menerapkan</string>

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update manager/app/src/main/res/values-in/strings.xml

<string name="icon_switch_summary">Ubah ikon peluncur aplikasi ke ikon KernelSU</string>

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update manager/app/src/main/res/values-in/strings.xml

<string name="show_more_module_info_summary">Pajang info modul tambahan seperti URL pembaruan JSON</string>

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-21 23:40:11 +08:00
ShirkNeko
ddbbeafc64 manager: Added the SUS Mount Hide Control feature added in SuSFS version 1.5.8 2025-06-21 23:39:19 +08:00
ShirkNeko
285478a778 manager: Remove deprecated OverlayFS checks for automatic kernel stats support.
- Updating the LKM build

- New Crowdin updates
2025-06-21 18:34:37 +08:00
Rifat Azad
00ffa86705 kernel/Makefile: check kernelsu driver version from online git repo first, if fails then check local .git and if that also fails then use hardcoded fallback 2025-06-21 17:09:16 +08:00
ShirkNeko
74ec20745c Uniformly use surfaceContainerHigh as the background color of the popup window 2025-06-21 16:34:38 +08:00
ShirkNeko
b7b995bf73 Updating the Crowdin Workflow Configuration 2025-06-21 15:37:33 +08:00
ShirkNeko
29b7f9e0ad New Crowdin updates (#200)
* New translations strings.xml (Japanese)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Russian)
2025-06-21 14:51:13 +08:00
ShirkNeko
00a4c69227 Remove unused strings 2025-06-21 14:18:51 +08:00
愛莉希雅
9c204496c3 完善補充正體中文-香港 (#3) (#194) 2025-06-21 12:41:41 +08:00
ShirkNeko
519401cf39 Fixing SUS Path Execution Errors
- Simplify SuSFS configuration management
2025-06-21 00:21:14 +08:00
愛莉希雅
f69eb5c115 完善補充正體中文-香港#2 (#191) 2025-06-20 21:23:29 +08:00
NkBe
82e96f4394 manger: 完善補充正體中文 (#189)
* manger: Pangu Format

Pangu Format maintained for new text.

* manger: 完善正體中文
2025-06-20 19:11:30 +08:00
Prslc
8e3db00b9b README: Sync README_EN with README (#190) 2025-06-20 19:11:20 +08:00
ShirkNeko
adf299d9f3 manager: Update the feature definition
- remove the unused FEATURE_SUS_OVERLAYFS
- add FEATURE_SUSFS_HAS_MAGIC_MOUNT
2025-06-20 01:52:00 +08:00
rsuntk
483a39c7ac kernel: core_hook: fix refcount leaks on try_umount (#2635)
Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
Signed-off-by: rsuntk <rsuntk@yukiprjkt.my.id>
Co-authored-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
2025-06-19 18:28:21 +08:00
ShirkNeko
c83baad6d5 manager: Add SuSFS Kstat-related configuration 2025-06-19 11:26:15 +08:00
ShirkNeko
2ff3b5ee06 manager: Fix the character processing logic in the clear command of FlashScreen. 2025-06-18 15:52:48 +08:00
ShirkNeko
b537b51034 manager:Add SuSFS to obtain slot uname and build time information 2025-06-18 15:41:08 +08:00
ShirkNeko
bfb6ea3613 manager:Adjust the text display of setting items to allow multi-line text descriptions 2025-06-18 00:12:27 +08:00
ShirkNeko
edf7685e9a strings: Updates the string resources at the current execution location and modifies the associated text display 2025-06-17 23:58:42 +08:00
ShirkNeko
f65f62360a strings: Update path setting related strings and comments 2025-06-17 23:38:37 +08:00
ShirkNeko
af97488d58 manager: Add pseudo kernel and build time execution location settings 2025-06-17 23:30:29 +08:00
ShirkNeko
6b1f73aa3d manager: Small increase in app acquisition speed 2025-06-17 22:34:47 +08:00
Re*Index. (ot_inc)
4eeece9559 Fix Japanese (#186) 2025-06-17 10:52:16 +08:00
ShirkNeko
4d7d5547ac New Crowdin updates (#184)
* New translations strings.xml (Turkish)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)
2025-06-17 00:56:15 +08:00
NkBe
7b74e70f97 manger: 更新正體中文 (#183)
* manger: 更新正體中文

* 保证 pangu 格式
2025-06-17 00:25:12 +08:00
ShirkNeko
d92f8fc8fd New Crowdin updates (#181)
* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)
2025-06-16 21:55:31 +08:00
ShirkNeko
55f9de2fa9 manager: split susfs module creation scripts
- Add a small number of scripts from the susfs4ksu module that lack a weak BL.
2025-06-16 21:50:51 +08:00
ShirkNeko
a12b14ef46 manager: Refactoring get the status of SUSFS functions
- More precise status cases
- Requires a recompile of your kernel to use it.
2025-06-16 20:16:25 +08:00
ShirkNeko
4ce6ff6286 manager: add ability to read and parse /proc/config.gz file, optimize function status checking 2025-06-15 22:48:56 +08:00
ShirkNeko
ce3566640c Add the ability to display more module information, support copying the update configuration address to the clipboard 2025-06-15 22:21:14 +08:00
ShirkNeko
a0a9fb01f4 manager: Add the function of hiding module label rows, optimize the module lagging problem 2025-06-15 21:50:09 +08:00
ShirkNeko
e1bd16d94f manager: Contrasting scroll to top and bottom functions 2025-06-15 20:05:40 +08:00
ShirkNeko
776ae8744c manager: add folder size labels for module items to optimize the display of module information 2025-06-15 19:27:26 +08:00
ShirkNeko
9285945e8b manager: Optimize the animation of floating action buttons, add zoom and transparency animation 2025-06-15 18:47:51 +08:00
ShirkNeko
75e56038ec manager: Add visibility state management for floating action buttons
- Get change button style from Apatch
2025-06-15 18:32:17 +08:00
ShirkNeko
730d58f18b dec++-ify jni part
- Refactoring to c

Co-authored-by: lamadaemon <i@lama.icu>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-15 17:17:19 +08:00
ShirkNeko
67a05e8813 strings: Modifying SuSFS Configuration to Optimize Boot Self-Start Description 2025-06-15 16:05:04 +08:00
ShirkNeko
e95a469bdb manager: Add a default tab item to the AppItem
- Maybe make the spacing consistent? I'm not sure.

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-15 15:52:43 +08:00
HSSkyBoy
2ff122e235 manger: 更新正體中文 (#169)
* manger: 更新正體中文

翻譯已更新內容,優化原本翻譯

* Update strings.xml

* 保证 Pangu 格式

* 補全新內容
2025-06-15 15:08:51 +08:00
ShirkNeko
2319452306 manager: Refactor the bootstrap script to automatically create a KSU module to execute instead
- Add Shamiko's Hidden Weakness Bl Scripts

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-15 14:41:14 +08:00
ShirkNeko
a0752d10c7 Merge branch 'main' of https://github.com/SukiSU-Ultra/SukiSU-Ultra 2025-06-15 03:39:13 +08:00
ShirkNeko
9110d89d61 manager: Add configuration for SuSFS logging feature, support enable/disable logging 2025-06-15 03:38:51 +08:00
米凛MiRin
39d6962320 modified: docs/README-en.md (#170)
modified:   docs/README.md
2025-06-15 01:35:25 +08:00
ShirkNeko
7b314116e9 manager: Refactored SuSFS pop-up window and changed to interface to optimize visual experience 2025-06-15 01:32:38 +08:00
ShirkNeko
ef4101cbf9 manager: Optimizing SuSFS Feature Status Display
- Use labels instead of Text

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-14 22:35:36 +08:00
ShirkNeko
85f5459c1d manager: Add support for automatic selection of corresponding SuSFS version and build time artifacts 2025-06-14 22:04:40 +08:00
ShirkNeko
97e367aa92 manager: Update and add SuSFS related settings and functions. 2025-06-14 20:00:16 +08:00
ShirkNeko
7097986cf5 manager: Add loading animation and empty state component
- Improve user experience when app has no content

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-14 15:39:06 +08:00
ShirkNeko
d6c8ef3737 manager: Add configure susfs uname value in more settings
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-14 01:10:40 +08:00
ShirkNeko
d7a5e80d34 manager: Modifying the getHookType function to return a string type
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: rifsxd <rifat.44.azad.rifs@gmail.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-13 15:36:15 +08:00
ShirkNeko
2d9783e3d4 manager: Add the ability to get the hook type 2025-06-13 14:39:16 +08:00
backslashxx
9f407a94e3 kernel: throne_tracker: avoid cross-fs traversal using s_magic check (#2633)
Skip directories that does NOT have the same magic as /data/app.
This is to avoid scanning incfs and any other stacked filesystems.

While this is way dumber, it's way cheaper.
no kern_path(), no missable path_put(), no ref handling.

This supercedes
`throne_tracker: avoid cross fs access
(https://github.com/tiann/KernelSU/pull/2626)`
- upstream
0b6998b474

Signed-off-by: backslashxx
<118538522+backslashxx@users.noreply.github.com>
2025-06-13 02:38:02 +08:00
ShirkNeko
99726a2c4e [skip ci] docs: Updated README file to reflect instructions for the susfs-main branch
kernel: Added sukisu_set_manager_uid function to set the management UID
2025-06-13 02:36:50 +08:00
ShirkNeko
f3675e7f6e Revert "throne_tracker: avoid cross fs access 2025-06-11 18:55:25 +08:00
ShirkNeko
b84d528d99 [skip ci] manager: update values-tr/strings.xml (#161)
Co-authored-by: cvnertnc <148134890+cvnertnc@users.noreply.github.com>
2025-06-11 18:48:08 +08:00
ShirkNeko
0aab0c1d6b manager: Optimize the layout of PullToRefreshBox in SuperUserScreen to remove redundant padding 2025-06-11 00:47:03 +08:00
ShirkNeko
ab2367f7fa manager: Refactor AppItem layout and improve label display 2025-06-11 00:21:13 +08:00
Wang Han
1bac30930f Switch to prepare_creds/commit_creds
Update API as per kernel doc recommends, also fix setup_groups refcount
leak while at it.
2025-06-10 23:39:08 +08:00
ShirkNeko
6a9186300b [skip ci]New Crowdin updates (#156)
* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)

* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)

* New translations strings.xml (Japanese)

* New translations strings.xml (Russian)

* New translations strings.xml (Vietnamese)
2025-06-10 23:33:35 +08:00
WenHao2130
e6dea3c29e [skip ci]manager: Drop QQ group invitation link (#158)
Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
2025-06-10 23:33:04 +08:00
ShirkNeko
c873ff74cb manager: Add status tracking to ensure that brush-write operations are performed correctly 2025-06-09 02:25:02 +08:00
ShirkNeko
7b6f451cfb manager: Optimize the function of app classification and sorting method 2025-06-09 01:27:33 +08:00
ShirkNeko
73dea0b8e7 Adjusting component spacing and interaction effects 2025-06-08 23:13:41 +08:00
米凛MiRin
f71d617cb3 manager: fix color (#154) 2025-06-08 22:07:29 +08:00
WenHao2130
f0d8e42026 manager: Fix get Google device model issues (#153)
- Inconsistent case of BRAND and MANUFACTURER information on Google devices results in unintended behavior (e.g. Google Google Pixel 9 XL)

Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
2025-06-08 21:08:05 +08:00
ShirkNeko
5bbd95e821 manager: Updated superuser interface, added sidebar categories 2025-06-08 20:28:05 +08:00
WenHao2130
fa060dca58 manager: Refactoring the get device name logic (#152)
* manager: Import `getDeviceInfo()` from bmax/APatch

Co-authored-by: GarfieldHan <2652609017@qq.com>
Signed-off-by: WenHao2130 <wenhao2130@outlook.com>

* manager: Use `getDeviceInfo()` instead `Build.DEVICE`

Signed-off-by: WenHao2130 <wenhao2130@outlook.com>

---------

Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
Co-authored-by: GarfieldHan <2652609017@qq.com>
2025-06-08 15:47:48 +08:00
ShirkNeko
9c7ba5b998 [skip ci]: docs: Updating the README file to add a description of support for non-GKI device builds
完整正體中文本地化

對缺失內容的完整翻譯,對現有內容參考 SukiSU-Ultra 簡體中文優化

New Crowdin updates (#140)

Co-authored-by: HSSkyBoy <HSSkyBoy@outlook.com>
Co-authored-by: cvnertnc <148134890+cvnertnc@users.noreply.github.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-07 22:23:49 +08:00
ShirkNeko
061136900a [skip ci]: New Crowdin updates (#138)
* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)
2025-06-03 17:16:15 +08:00
ShirkNeko
6375bf4b7c manager: Simplify state management of components for installation method selection
Remove unnecessary LKM upload logic
2025-06-03 16:57:41 +08:00
ShirkNeko
17288c086a manager: Simplify Home data initialization logic and remove unnecessary caching time 2025-06-03 16:29:41 +08:00
ShirkNeko
15747ceaa5 manager: Simplify KPM display judgment in Home messages 2025-06-03 15:48:21 +08:00
ShirkNeko
675bb20f52 manager: Modify the Show KPM function to hidden and update the related settings. 2025-06-03 15:26:03 +08:00
ShirkNeko
ec0b26a174 manager: Optimize slot selection dialog to remove unnecessary information 2025-06-03 15:15:48 +08:00
ShirkNeko
92f6f2f51e manager: Add card shadow effect control 2025-06-03 14:04:55 +08:00
5ec1cff
587e73b449 manager: persist show system app settings 2025-06-03 13:53:45 +08:00
ShirkNeko
07c9cce4b9 manager: Fixed flickering on activity refresh using a clever method.
- Add Activity lifecycle callback and method to refresh current Activity
2025-06-03 01:43:31 +08:00
ShirkNeko
1d34ea4995 Rename files and update package structure
Add tool classes related to displaying and refreshing data
2025-06-03 01:02:08 +08:00
5ec1cff
d58ec6952c throne_tracker: avoid cross fs access 2025-06-03 00:03:16 +08:00
ShirkNeko
50631aade6 Manager: Refactoring of hidden messages and display of KPM settings status management 2025-06-03 00:00:39 +08:00
ShirkNeko
6df8f6f5d4 refactor: simplify handling and add app restart utility 2025-06-02 22:47:30 +08:00
米凛MiRin
4aee26b48e manager: auto restart after Display KPM Function and Hide other info (#136) 2025-06-02 22:24:17 +08:00
ShirkNeko
3bbe415c7e Merge branch 'main' of https://github.com/SukiSU-Ultra/SukiSU-Ultra 2025-06-02 21:33:55 +08:00
ShirkNeko
892fa9040f docs: add troubleshooting section for KernelSU Manager uninstallation issues 2025-06-02 21:33:48 +08:00
ShirkNeko
cadc123eab [skip ci] New Crowdin updates (#135)
* New translations strings.xml (Romanian)

* New translations strings.xml (Romanian)

* New translations strings.xml (Russian)

* New translations strings.xml (Vietnamese)
2025-06-02 21:30:13 +08:00
ShirkNeko
3a27537648 [skip ci] strings: update the description of the hidden additional information 2025-06-02 21:06:08 +08:00
ShirkNeko
6fa1a5c8b8 Optimize the logic of badge display in the bottom navigation bar
Add support for settings to hide other information

README: add notice about stuck device solution (#134)

Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: =?UTF-8?q?=E7=B1=B3=E5=87=9BMiRin?= <148533509+MiRinChan@users.noreply.github.com>

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-02 20:46:49 +08:00
ShirkNeko
b772c8ece1 manager: Refactoring data refresh management
New AppData object to optimize data fetching and state management.
2025-06-02 17:49:34 +08:00
ShirkNeko
c0e839dd8e manager: Implement module count refresh
- update count data periodically and optimize the bottom bar display
2025-06-02 17:24:34 +08:00
ShirkNeko
a6ed7befdc manager: Add count icon to bottom bar
- Remove count icon from Home
- Add put back button for more settings
2025-06-02 16:43:31 +08:00
米凛MiRin
c210b00d54 manager: accessibility optimization (#131)
manager: style optimization
2025-06-02 14:47:59 +08:00
ShirkNeko
13b5290598 [skip ci]New Crowdin updates (#133)
* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)

* New translations strings.xml (Japanese)

* New translations strings.xml (Chinese Simplified)
2025-06-02 14:34:41 +08:00
ShirkNeko
b99516da69 [skip ci]: New Crowdin updates (#130)
* Update source file strings.xml

* New translations strings.xml (Romanian)

* New translations strings.xml (Danish)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)
2025-06-02 14:03:10 +08:00
lshwjgpt25
fe8b5f2135 [skip ci]: Align superuser app spacing with module spacing (#132) 2025-06-02 13:31:41 +08:00
ShirkNeko
04e1b9bf77 manager: Update back navigation logic in FlashScreen for module flashing 2025-06-02 04:10:41 +08:00
ShirkNeko
b8aaf918fe Merge branch 'main' of https://github.com/SukiSU-Ultra/SukiSU-Ultra 2025-06-02 02:11:40 +08:00
ShirkNeko
54925188e8 Optimize interface element spacing and styles 2025-06-02 02:09:46 +08:00
yycgit1
3443e48ef1 manager: Add alternate app icon toggle (#129)
Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
Co-authored-by: MiRinChan <148533509+MiRinChan@users.noreply.github.com>
2025-06-01 22:43:08 +08:00
ShirkNeko
53b3e84890 Optimize button styles in SuperUser 2025-06-01 22:36:23 +08:00
ShirkNeko
a5b85bfdad manager: Update theme color display in MoreSettings screen and adjust title style 2025-06-01 21:56:56 +08:00
ShirkNeko
2817583e3c Optimize icon handling for settings cards
Allow icons to be optional and remove redundant group title displays
2025-06-01 21:24:21 +08:00
ShirkNeko
8a6116b4ec manager: Update surface colors to use cardAlpha for improved theme consistency 2025-06-01 20:58:28 +08:00
cvnertnc
6a4270787a [skip ci]Manager: update values-tr/strings.xml (#126) 2025-06-01 20:29:03 +08:00
ShirkNeko
5457a4772b manager: Refactor card elevation handling and improve theme support 2025-06-01 20:17:22 +08:00
ShirkNeko
ee4c3bb03b manager: Optimize color schemes for themes and module screens
- Remove unnecessary background color settings
2025-06-01 14:04:10 +08:00
ShirkNeko
dd1d17d2cf Optimize the display logic of reboot drop down menu items 2025-06-01 02:27:55 +08:00
WenHao2130
3c353e8f88 manager: Modify the display logic of safe_mode (#125)
Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
2025-06-01 01:30:16 +08:00
ShirkNeko
d743073309 docs: Add instructions for manually integrating susfs 2025-06-01 00:20:38 +08:00
ShirkNeko
a636911612 [skip ci]: New Crowdin updates (#123)
* New translations strings.xml (Russian)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Chinese Traditional, Hong Kong)
2025-06-01 00:16:11 +08:00
ShirkNeko
7a62f91752 manager: Using SwitchItem instead of ListItem 2025-06-01 00:07:17 +08:00
米凛MiRin
b551a54c8f manager: convert bitmap image to vector by hand. document: add copyright infomation. (#121) 2025-05-31 23:30:37 +08:00
ShirkNeko
26d86aa2fe manager: Optimize Home data refresh logic 2025-05-31 23:29:34 +08:00
ShirkNeko
6ee9246650 Fixes the problem of not refreshing automatically 2025-05-31 20:59:47 +08:00
ShirkNeko
1cd96fbdbf Optimize data preloading 2025-05-31 20:40:55 +08:00
ShirkNeko
a030a026b1 Manager: optimizing Home's data caching logic 2025-05-31 20:26:23 +08:00
ShirkNeko
8bf9cd0bee manager: Add initialization to optimize loading of SuperUser and Home data 2025-05-31 19:17:43 +08:00
ShirkNeko
13b1aad4b8 manager: Optimizing Home Performance
- Reorganize Home structure using MVVM architecture pattern to separate UI and data logic
2025-05-31 17:39:24 +08:00
ShirkNeko
916d956ce2 [skip ci]: New Crowdin updates (#120)
* Update source file strings.xml

* New translations strings.xml (Chinese Traditional, Hong Kong)
2025-05-31 13:28:54 +08:00
ShirkNeko
87a7650d26 New Crowdin updates (#119)
* Update source file strings.xml

* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)

* Update source file strings.xml
2025-05-31 13:19:14 +08:00
ShirkNeko
3484e187da manager: Refactoring the settings interface
- Merge Web Debugging and Web X Eruda switches
2025-05-31 12:49:58 +08:00
ShirkNeko
0835f330e2 manager: Modifying the WebUI Engine Global Options 2025-05-31 04:41:16 +08:00
ShirkNeko
8064472477 manager: better handle webui engine select
- Optimize the flashback problem caused by null pointer

Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: Der_Googler <54764558+DerGoogler@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-31 03:03:22 +08:00
ShirkNeko
2281012e33 manager: Fallback allows developers to override user preferences for the selected WebUI engine
- Because the WebUI can cause some problems, the automatic fetching of the
2025-05-31 02:14:25 +08:00
ShirkNeko
83eaeab1ba New Crowdin updates (#115)
* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)

* New translations strings.xml (Russian)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Chinese Traditional, Hong Kong)
2025-05-30 20:17:37 +08:00
ShirkNeko
6405764df3 Adjust settings and optimize theme styles 2025-05-30 20:00:33 +08:00
ShirkNeko
253276a27b Remove border styles from labels 2025-05-29 20:25:17 +08:00
ShirkNeko
855a71ac56 Adjust the Dark Mode Card Transparency setting to the default value of 1 2025-05-29 18:48:43 +08:00
ShirkNeko
96dc53977f manager: Refactoring kernel flash features and styles 2025-05-29 18:25:45 +08:00
ShirkNeko
31111e68eb [skip ci]: New translations strings.xml (Russian) (#113) 2025-05-29 15:21:14 +08:00
ShirkNeko
ac0de29872 Remove cards from Home 2025-05-29 15:00:05 +08:00
ShirkNeko
9e2b722491 manager: Adding Vacancies to WeiUI Configuration and Implementing Asynchronous Loading
- Upgrade agp version to 8.10.1
2025-05-29 14:44:24 +08:00
ShirkNeko
59627e6fe2 manager: update Crowdin workflow to include resource paths 2025-05-28 13:53:58 +08:00
ShirkNeko
cd0b5fb378 New Crowdin updates (#111)
* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (German)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Russian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Kannada)

* New translations strings.xml (Spanish)

* New translations strings.xml (Danish)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Bosnian)
2025-05-28 13:47:41 +08:00
ShirkNeko
48a3c64c7c New translations strings.xml (Vietnamese) (#109) 2025-05-28 12:22:58 +08:00
米凛MiRin
62da804518 manager: ksuEngine as default WebUI engine (#110) 2025-05-28 12:22:38 +08:00
ShirkNeko
439b99cc4a manager: refactor label item in superuser list
* manager: Improvements

* manager: bump mmrl

* manager: use ktx ext Str.toUri

* manager: add "webui-engine" from config.json

This allows the developer to override the user preference of the selected WebUI engine.

Supported engines are:

- `wx` for WebUI X
- `ksu` for the KernelSU WebUI

All not named strings will default to `wx`

R.string.use_webuix_summary needs proper translations

* manager: add support for multilingual module meta

Co-authored-by: Der_Googler <54764558+DerGoogler@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-27 16:57:54 +08:00
ShirkNeko
64f0efc2c0 manager: use myUserId as fallback
Co-authored-by: Der_Googler <54764558+DerGoogler@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-27 16:33:58 +08:00
ShirkNeko
f196bf5b76 manager: Updated Kpm and version info icons 2025-05-27 16:02:36 +08:00
ShirkNeko
790968be6a manager: Change icon 2025-05-27 15:47:24 +08:00
ShirkNeko
83f0f9537f New Crowdin updates (#107)
* New translations strings.xml (French)

* New translations strings.xml (Arabic)

* New translations strings.xml (German)

* New translations strings.xml (Russian)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Thai)

* New translations strings.xml (Romanian)

* New translations strings.xml (Spanish)

* New translations strings.xml (Danish)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)
2025-05-27 01:33:53 +08:00
ShirkNeko
68ebfec918 manager: Optimize the logic of displaying the Machine Architecture tab of the home status card
Fix the problem of displaying text in safe mode
2025-05-27 01:07:24 +08:00
ShirkNeko
8be4dea081 manager: Update interface card color to surfaceContainerLow to optimize visual effect. 2025-05-24 22:00:52 +08:00
Rifat Azad
cfdbba45c3 manager: make action execution screen have the same behavior as Magisk
based on pr https://github.com/tiann/KernelSU/pull/2321

* Magisk's behavior: Hide Bottom Navbar, Show close button if failed or success
and removed automatic exit when module execution success.
2025-05-24 17:03:20 +08:00
WenHao2130
d408c9f4bf manager: Modify Module page icon (#104)
Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
2025-05-24 15:33:50 +08:00
ShirkNeko
8f4c58c4c3 [skip ci]: kernel: simplify KPM enabled check in ksu_handle_prctl 2025-05-24 15:25:54 +08:00
rsuntk
7e88e9648f kernel: guard nuke_ext4_sysfs
Rather than using depends on / select,
i just prefer this way, although, yes, it is
an ifdef hell.

Signed-off-by: rsuntk <rsuntk@yukiprjkt.my.id>
2025-05-24 15:05:44 +08:00
ShirkNeko
4516d136a4 Merge branch 'main' of https://github.com/SukiSU-Ultra/SukiSU-Ultra 2025-05-24 14:11:35 +08:00
ShirkNeko
1b85dfbed1 manager: Modify the text padding in the ElevatedCard
- Adding Formatting Characters

Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
2025-05-24 14:11:14 +08:00
ShirkNeko
807ffb419a [skip ci] : Update source file strings.xml (#101) 2025-05-24 13:55:43 +08:00
ShirkNeko
e826f43aed Optimize KPM checking logic
- Simplify code and ensure KPM information is displayed under supported versions
2025-05-24 04:38:54 +08:00
ShirkNeko
d619f5fafc Refactoring KPM support to check KPM status using CMD_ENABLE_KPM 2025-05-24 03:28:28 +08:00
ShirkNeko
b3e2f9b7ff manager: Updated colors and styles
- Adapted from reference style

Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: Der_Googler <54764558+DerGoogler@users.noreply.github.com>
Co-authored-by: rifsxd <rifat.44.azad.rifs@gmail.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-24 00:47:15 +08:00
ShirkNeko
99a39c6f52 New Crowdin updates (#100)
* Update source file strings.xml

* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)

* Update source file strings.xml
2025-05-23 17:06:24 +08:00
ShirkNeko
22991e8740 Merge branch 'main' of https://github.com/SukiSU-Ultra/SukiSU-Ultra 2025-05-23 16:46:31 +08:00
ShirkNeko
7646ecb6f7 manager: Update theme color scheme, fix style
* Remove redundant strings
* Bump MMRL

Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: liaowenxuan <jby13147208050@163.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-23 16:45:17 +08:00
ShirkNeko
204db674bb [skip ci]: New Crowdin updates (#99)
* New translations strings.xml (Spanish)

* New translations strings.xml (Portuguese, Brazilian)
2025-05-23 16:38:20 +08:00
ShirkNeko
99fe6623de manager: possible fix
- Possible fix a bug where IUserManager.getUsers(ZZZ) are not defined in the framework.jar
- Refactored WebUI X to meet the new WXInterface
- Only fetch the app from the current user and not all users

* manager: remove unused AIDL interfaces

Signed-off-by: Der_Googler <54764558+DerGoogler@users.noreply.github.com>
2025-05-23 16:31:13 +08:00
ShirkNeko
f1f78d2485 Add force refresh to get module list after installing a module 2025-05-22 18:12:13 +08:00
ShirkNeko
b2ae20b796 manager: Enhance and simplify module name capture
- Add the use of incoming module name to load the corresponding installation list when the file cannot be retrieved, using utf-8 encoding and formatting characters by default.
2025-05-20 22:23:45 +08:00
ShirkNeko
83bd4e9642 New translations strings.xml (French) (#95) 2025-05-20 19:34:08 +08:00
ShirkNeko
767349798a docs: Added a link to submit translations to the Crowdin project page in the documentation 2025-05-20 19:31:44 +08:00
Jiu
ae38f4709b [skip ci]: 删掉本地配置sdk (#94)
* feat: Update string resources for clarity and consistency; simplify build manager workflow

* 使用gki-kernel-local.yml

* build-lkm-local.yml

* 修改为谷歌源

* 修改为清华源

* 修改max-size为16G

* Updating Vietnamese strings

* kernel: kpm: add compatibility for kernel 4.14 and lower (#76)

`thread_pid` is not defined in kernel 4.14 and lower, leading to compilation issue.
To fix this, use `pids[PIDTYPE_PID].pid` for kernel versions 4.14 and lower.
Else use `thread_pid` for kernel versions 4.19 and higher.

Reference: 107717913b/tracee/tracee.bpf.c (L354)

* 1

* 1

* 1

---------

Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: KernelSUBot <bot@kernelsu.org>
Co-authored-by: sidex15 <24408329+sidex15@users.noreply.github.com>
Co-authored-by: build <123456@cnm.com>
2025-05-20 19:26:49 +08:00
ShirkNeko
fc7001a11a New Crowdin updates (#93)
* Update source file strings.xml

* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)

* Update source file strings.xml

* New translations strings.xml (Japanese)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Vietnamese)
2025-05-20 13:22:44 +08:00
ShirkNeko
9924809bdb [skip ci]: Update the GitHub repository links in the documentation to ensure that they point to the correct SukiSU-Ultra repositories 2025-05-20 12:48:06 +08:00
ShirkNeko
58a4ff94e4 Add module download error alerts and optimize update checking logic
- Add a formatting string for the update list
- Fix module update failures caused by spaces and other non Linux readable characters.

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-20 12:12:22 +08:00
ShirkNeko
29033e9b80 [skip ci]: New Crowdin updates (#90)
* New translations strings.xml (French)

* New translations strings.xml (Japanese)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Romanian)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)
2025-05-19 23:44:53 +08:00
ShirkNeko
ea24daf37c Update Crowdin configuration file 2025-05-19 21:32:29 +08:00
ShirkNeko
ebc16583fb [skip ci]:kernel: kpm: add compatibility for kernel 4.14 and lower (#76)
manger: Fix and simplify back gesture

`thread_pid` is not defined in kernel 4.14 and lower, leading to compilation issue.
To fix this, use `pids[PIDTYPE_PID].pid` for kernel versions 4.14 and lower.
Else use `thread_pid` for kernel versions 4.19 and higher.

Reference: 107717913b/tracee/tracee.bpf.c (L354)

Co-authored-by: sidex15 <24408329+sidex15@users.noreply.github.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-19 21:30:13 +08:00
ShirkNeko
2a10b41781 [skip ci]: Adding Flash related string resources 2025-05-19 17:17:42 +08:00
ShirkNeko
d5946047a1 manger: update flash style
add instructions to make it easier to understand
2025-05-19 16:49:37 +08:00
ShirkNeko
4ff46a4911 manager: Enhance Flash module handling
- Module screen for batch installation

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-19 14:07:40 +08:00
ShirkNeko
b587216b5e Updating Vietnamese strings 2025-05-18 21:21:21 +08:00
ShirkNeko
245fce167e manager: Optimize device model and KPM configuration checking
Add caching mechanism to improve performance
2025-05-18 20:16:04 +08:00
ShirkNeko
de9b82ffd5 [skip ci]: feat: Update string resources for clarity and consistency; simplify build manager workflow 2025-05-18 19:35:05 +08:00
ShirkNeko
e570f402e4 feat: Add a GitHub workflow for building LKM locally 2025-05-18 17:01:28 +08:00
ShirkNeko
9c761b13fa feat: Adding a GitHub workflow with a manual build manager 2025-05-18 16:43:17 +08:00
cvnertnc
cc4b135d20 Manager: update values-tr/strings.xml Docs: added README-tr.md (#83)
Manager: update values-tr/strings.xml
Docs: added README-tr.md
2025-05-18 11:38:51 +08:00
ShirkNeko
ec5395c787 Remove unnecessary patches 2025-05-18 04:07:53 +08:00
ShirkNeko
6d60e54a7d feat: Enhance KPM configuration checking,
- remove unused imports, update mmrl versions
2025-05-18 04:06:36 +08:00
ShirkNeko
28aa34c0b6 Updating the KPM configuration
- We don't know if KPM can run on arm32-bit devices, so to avoid some problems, add a dependency on 64-bit architectures

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-17 22:22:13 +08:00
ShirkNeko
0701967bab [skip ci]: ci: update kmi versions 2025-05-17 21:15:07 +08:00
Wang Han
a76b1eece4 Fix fallback option for createRootShell() (#2593) 2025-05-17 21:10:52 +08:00
ShirkNeko
8e791c680e docs: 添加爱发电链接至 README.md 2025-05-17 17:54:22 +08:00
ShirkNeko
fc9f2ccf25 Add Icon Patch 2025-05-17 17:48:55 +08:00
ShirkNeko
d4682fb06e manager: Update secondary interface status and optimize WebView interface 2025-05-17 17:09:47 +08:00
ShirkNeko
377ea183a7 Updated Vietnamese Translation 2025-05-16 22:01:04 +08:00
ShirkNeko
72361ab8bf manager: Modify the batch selection ui on the superuser page
- Add more convenient buttons for it
2025-05-16 16:00:51 +08:00
ShirkNeko
f708e583c3 docs: updated to reflect changes to support for non-GKI devices.
- Adjusted branch usage instructions and KPM support information
2025-05-15 22:55:18 +08:00
ShirkNeko
d753e1dc48 [ship ci]: Updated Vietnamese Translation 2025-05-15 22:06:49 +08:00
ShirkNeko
315a8a3805 Normalize kernel related constants to restore 2025-05-15 20:59:44 +08:00
ShirkNeko
129fed9c9f manager: simplify kernel arch
Previously:

Kernel
4.19.331-Rissu

Kernel Arch
armv8l

This changes:

Kernel
4.19.331-Rissu (armv8l)

Suggested-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
Signed-off-by: rsuntk <90097027+rsuntk@users.noreply.github.com>
2025-05-15 20:40:50 +08:00
ShirkNeko
0baccb7621 Add ksud support for the armeabi-v7a architecture
Co-authored-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
Co-authored-by: SChernykh <15806605+SChernykh@users.noreply.github.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-15 20:00:51 +08:00
backslashxx
842a8aa45a kernel/selinux: fix pointer mismatch with 32-bit ksud on 64-bit kernels
Since KernelSU Manager can now be built for 32-bit, theres this problematic
setup where userspace is 32-bit (armeabi-v7a) and kernel is 64bit (aarch64).

On 64-bit kernels with CONFIG_COMPAT=y, 32-bit userspace passes 32-bit pointers.
These values are interpreted as 64-bit pointers without proper casting and that
results in invalid or near-null memory access.

This patch adds proper compat-mode handling with the ff changes:
- introduce a dedicated struct (`sepol_compat_data`) using u32 fields
- use `compat_ptr()` to safely convert 32-bit user pointers to kernel pointers
- adding a runtime `ksu_is_compat` flag to dynamically select between struct layouts

This prevents a near-null pointer dereference when handling SELinux
policy updates from 32-bit ksud in a 64-bit kernel.

Truth table:

kernel 32 + ksud 32, struct is u32, no compat_ptr
kernel 64 + ksud 32, struct is u32, yes compat_ptr
kernel 64 + ksud 64, struct is u64, no compat_ptr

Preprocessor check

64BIT=y COMPAT=y: define both structs, select dynamically
64BIT=y COMPAT=n: struct u64
64BIT=n: struct u32

Tested-by: ...
Tested-by: ...
Tested-by: ...
Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
2025-05-15 17:39:41 +08:00
backslashxx
d17843479c kernel/throne_tracker: we just uninstalled the manager, stop looking for it
When the manager UID disappears from packages.list, we correctly
invalidate it — good. But, in the very next breath, we start scanning
/data/app hoping to find it again?

This event is just unnecessary I/O, exactly when we should be doing less.
Apparently this causes hangups and stuckups which is REALLY noticeable
on Ultra-Legacy devices.

Skip the scan — we’ll catch the reinstall next time packages.list updates.

Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
2025-05-15 17:39:41 +08:00
backslashxx
0d70cc8e58 kernel: sucompat: sucompat toggle support for non-kp (tiann#2506)
This is done like how vfs_read_hook, input_hook and execve_hook is disabled.
While this is not exactly the same thing, this CAN achieve the same results.
The complete disabling of all KernelSU hooks.

While this is likely unneeded, It keeps feature parity to non-kprobe builds.

adapted from upstream:
	kernel: Allow to re-enable sucompat - 4593ae81c7

Rejected: https://github.com/tiann/KernelSU/pull/2506

Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
2025-05-15 17:39:41 +08:00
Re*Index. (ot_inc)
4e6cacb206 [skip ci]: Update Japanese. (#74)
* Update strings.xml

* fix typo & change Japanese text.
2025-05-15 16:41:38 +08:00
ShirkNeko
52514ba35b [skip ci]: Move the language selection into the card 2025-05-15 16:40:27 +08:00
ShirkNeko
4d59ce435e Add card darkness adjustment function
- Updated some string translations
2025-05-14 19:55:11 +08:00
ShirkNeko
b3b7fa6f4d [skip ci]: Update language options
- Add Vietnamese support (from bro in the group)
2025-05-14 18:33:31 +08:00
ShirkNeko
c057c16391 Stand alone theme configuration for webuiX
- Add secondary color interface: isSecondaryPage (bool)
2025-05-14 16:29:27 +08:00
ShirkNeko
dee7cc6f2b Add language options
- Fix some icon color issues
2025-05-14 15:01:59 +08:00
ShirkNeko
3d0d87cb0c Add application DPI setting function
- Allow users to customize the display density of the current application

- Fix some popup color errors
2025-05-13 23:56:18 +08:00
ShirkNeko
6b66d9b3f8 Remove redundant definitions of KPM strings
Clean up unused code in build scripts
2025-05-13 21:57:42 +08:00
ShirkNeko
a301d94858 [skip ci]: Fix missing brackets in KPM feature information summary 2025-05-13 21:51:21 +08:00
ShirkNeko
01199470f2 [manager]: Add KPM function display options and related settings
- Eruda injection web UI X will not be displayed when the modification is not enabled.

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-13 21:44:42 +08:00
ShirkNeko
9e7ea19567 [skip ci]:Update some descriptions 2025-05-13 18:30:39 +08:00
Der_Googler
cdc6a6cb4a Add option to use WebUI X
- Added WebUI X from MMRL

Co-authored-by:Der_Googler <54764558+DerGoogler@users.noreply.github.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-13 15:44:20 +08:00
ShirkNeko
bb2d8fd7e0 Refactoring the KsuIsValid Check Logic
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: rsuntk <rsuntk@yukiprjkt.my.id>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: Rifat Azad <33044977+rifsxd@users.noreply.github.com>
2025-05-12 00:06:17 +08:00
ShirkNeko
2b6d418fe6 [skip ci]:Removing unused LocalDensity variables 2025-05-11 23:36:09 +08:00
ShirkNeko
d8b1126b96 [skip ci]:Modify comments 2025-05-11 23:32:46 +08:00
ShirkNeko
2eeddcfa80 Modify UI layout
- Adjust the maximum number of SwitchItem rows to optimize the layout and spacing of interface buttons
2025-05-11 23:30:48 +08:00
458 changed files with 71961 additions and 6862 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.bat eol=crlf

4
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
github: tiann
patreon: weishu
open_collective: sukisu-ultra

33
.github/ISSUE_TEMPLATE/add_device.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Contribute to Unofficially Supported Device
description: Add your device kernel source to KernelSU's Unofficially Supported Device List
title: "[Add Device]: "
labels: ["add-device"]
body:
- type: markdown
attributes:
value: |
Thanks for supporting KernelSU!
- type: input
id: repo-url
attributes:
label: Repository URL
description: Your repository URL
placeholder: https://github.com/tiann/KernelSU
validations:
required: true
- type: input
id: device
attributes:
label: Device
description: Please describe the device maintained by you.
placeholder: GKI 2.0 Device
validations:
required: true
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you should be the maintainer of the repository.
options:
- label: I'm the maintainer of this repository
required: true

72
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,72 @@
name: Bug report
description: Create a report to help us improve KernelSU
labels: [Bug]
body:
- type: checkboxes
attributes:
label: Please check before submitting an issue
options:
- label: I have searched the issues and haven't found anything relevant
required: true
- label: I will upload bugreport file in KernelSU Manager - Settings - Report log
required: true
- label: I know how to reproduce the issue which may not be specific to my device
required: false
- type: textarea
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is
validations:
required: true
- type: textarea
attributes:
label: To Reproduce
description: Steps to reproduce the behaviour
placeholder: |
- 1. Go to '...'
- 2. Click on '....'
- 3. Scroll down to '....'
- 4. See error
- type: textarea
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
- type: textarea
attributes:
label: Screenshots
description: If applicable, add screenshots to help explain your problem.
- type: textarea
attributes:
label: Logs
description: If applicable, add crash or any other logs to help us figure out the problem.
- type: textarea
attributes:
label: Device info
value: |
- Device:
- OS Version:
- KernelSU Version:
- Kernel Version:
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: Add any other context about the problem here.

11
.github/ISSUE_TEMPLATE/custom.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
name: Custom issue template
description: WARNING! If you are reporting a bug but use this template, the issue will be closed directly.
title: '[Custom]'
body:
- type: textarea
id: description
attributes:
label: "Describe your problem."
validations:
required: true

View 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."

64
.github/scripts/build_a12.sh vendored Normal file
View File

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

43
.github/scripts/build_a13.sh vendored Normal file
View File

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

60
.github/workflows/add-device.yml vendored Normal file
View File

@@ -0,0 +1,60 @@
name: handle-add-device-issue
on:
issues:
types: [labeled]
jobs:
handle-add-device:
if: github.event.label.name == 'add-device'
runs-on: ubuntu-latest
env:
ISSUE_CONTENT: ${{ github.event.issue.body }}
steps:
- uses: actions/checkout@v4
- name: Parse issue body
id: handle-add-device
run: |
python3 scripts/add_device_handler.py website/docs/repos.json || true
- name: Commit
if: steps.handle-add-device.outputs.success == 'true'
run: |
git config --local user.name "GitHub Actions"
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add website/docs/repos.json
git commit -m "add device: ${{ steps.handle-add-device.outputs.device }}"
- name: Make pull request
if: steps.handle-add-device.outputs.success == 'true'
id: cpr
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "[add device]: ${{ steps.handle-add-device.outputs.device }}"
title: "[add device]: ${{ steps.handle-add-device.outputs.device }}"
body: |
${{ steps.handle-add-device.outputs.device }} has been added to the website.
Related issue: ${{ github.event.issue.html_url }}
branch: "add-device-${{ github.event.issue.number }}"
labels: add-device
delete-branch: true
sign-commits: true
- name: Check outputs
if: ${{ steps.cpr.outputs.pull-request-number }}
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
- uses: Kernel-SU/actions-comment-on-issue@master
if: ${{ steps.cpr.outputs.pull-request-number }}
with:
message: "Automatically created pull request: ${{ steps.cpr.outputs.pull-request-url }}"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: Kernel-SU/actions-comment-on-issue@master
if: steps.handle-add-device.outputs.success != 'true'
with:
message: "Cannot create pull request. Please check the issue content. Or you can create a pull request manually."
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: close issue
uses: peter-evans/close-issue@v3
with:
issue-number: ${{ github.event.issue.number }}
token: ${{ secrets.GITHUB_TOKEN }}

137
.github/workflows/avd-kernel.yml vendored Normal file
View File

@@ -0,0 +1,137 @@
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 }}"

74
.github/workflows/build-lkm-local.yml vendored Normal file
View File

@@ -0,0 +1,74 @@
name: Build LKM for KernelSU Local
on:
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:
inputs:
upload:
required: true
type: boolean
default: true
description: "Whether to upload to branch"
jobs:
build-lkm:
strategy:
matrix:
include:
- version: "android12-5.10"
sub_level: 236
os_patch_level: 2025-05
- version: "android13-5.10"
sub_level: 234
os_patch_level: 2025-03
- version: "android13-5.15"
sub_level: 178
os_patch_level: 2025-03
- version: "android14-5.15"
sub_level: 178
os_patch_level: 2025-03
- version: "android14-6.1"
sub_level: 134
os_patch_level: 2025-05
- version: "android15-6.6"
sub_level: 87
os_patch_level: 2025-05
# uses: ./.github/workflows/gki-kernel-mock.yml when debugging
uses: ./.github/workflows/gki-kernel-local.yml
with:
version: ${{ matrix.version }}
version_name: ${{ matrix.version }}.${{ matrix.sub_level }}
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: self-hosted
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

74
.github/workflows/build-lkm.yml vendored Normal file
View File

@@ -0,0 +1,74 @@
name: Build LKM for KernelSU
on:
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:
inputs:
upload:
required: true
type: boolean
default: true
description: "Whether to upload to branch"
jobs:
build-lkm:
strategy:
matrix:
include:
- version: "android12-5.10"
sub_level: 237
os_patch_level: 2025-06
- version: "android13-5.10"
sub_level: 236
os_patch_level: 2025-05
- version: "android13-5.15"
sub_level: 180
os_patch_level: 2025-05
- version: "android14-5.15"
sub_level: 180
os_patch_level: 2025-05
- version: "android14-6.1"
sub_level: 138
os_patch_level: 2025-06
- version: "android15-6.6"
sub_level: 89
os_patch_level: 2025-06
# uses: ./.github/workflows/gki-kernel-mock.yml when debugging
uses: ./.github/workflows/gki-kernel.yml
with:
version: ${{ matrix.version }}
version_name: ${{ matrix.version }}.${{ matrix.sub_level }}
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

View File

@@ -0,0 +1,252 @@
name: Build Manager Manual
on:
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: self-hosted
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-local.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"
build-lkm:
needs: check-build-lkm
uses: ./.github/workflows/build-lkm-local.yml
if: ${{ needs.check-build-lkm.outputs.build_lkm == 'true' }}
with:
upload: ${{ needs.check-build-lkm.outputs.upload_lkm == 'true' }}
secrets: inherit
build-susfs:
if: ${{ always() }}
needs: [ check-build-lkm, build-lkm ]
strategy:
matrix:
include:
- target: aarch64-linux-android
os: ubuntu-latest
uses: ./.github/workflows/susfs.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:
target: ${{ matrix.target }}
os: ${{ matrix.os }}
build-ksud:
if: ${{ always() }}
needs: [ check-build-lkm, build-lkm ]
strategy:
matrix:
include:
- target: aarch64-linux-android
os: ubuntu-latest
- target: x86_64-linux-android
os: ubuntu-latest
- target: armv7-linux-androideabi
os: ubuntu-latest
uses: ./.github/workflows/ksud.yml
with:
target: ${{ matrix.target }}
os: ${{ matrix.os }}
pack_lkm: true
pull_lkm: ${{ needs.check-build-lkm.outputs.build_lkm != 'true' }}
build-manager:
if: ${{ always() }}
needs: build-ksud
runs-on: self-hosted
defaults:
run:
working-directory: ./manager
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup need_upload
id: need_upload
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Write key
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref == 'refs/heads/susfs' || github.ref_type == 'tag' }}
run: |
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
{
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}'
echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}'
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}'
echo KEYSTORE_FILE='key.jks'
} >> gradle.properties
echo "${{ secrets.KEYSTORE }}" | base64 -d > key.jks
fi
- name: Download arm64 susfs
uses: actions/download-artifact@v4
with:
name: susfs-aarch64-linux-android
path: .
- name: Download arm64 kpmmgr
uses: actions/download-artifact@v4
with:
name: kpmmgr-aarch64-linux-android
path: .
- name: Download arm64 ksud
uses: actions/download-artifact@v4
with:
name: ksud-aarch64-linux-android
path: .
- name: Download x86_64 ksud
uses: actions/download-artifact@v4
with:
name: ksud-x86_64-linux-android
path: .
- name: Download arm ksud
uses: actions/download-artifact@v4
with:
name: ksud-armv7-linux-androideabi
path: .
- name: Copy ksud to app jniLibs
run: |
mkdir -p app/src/main/jniLibs/arm64-v8a
mkdir -p app/src/main/jniLibs/x86_64
mkdir -p app/src/main/jniLibs/armeabi-v7a
cp -f ../aarch64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozako.so
cp -f ../x86_64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/x86_64/libzakozako.so
cp -f ../armv7-linux-androideabi/release/zakozako ../manager/app/src/main/jniLibs/armeabi-v7a/libzakozako.so
- name: Copy kpmmgr to app jniLibs
run: |
mkdir -p app/src/main/jniLibs/arm64-v8a
cp -f ../arm64-v8a/kpmmgr ../manager/app/src/main/jniLibs/arm64-v8a/libkpmmgr.so
- name: Copy susfs to app jniLibs
run: |
mkdir -p app/src/main/jniLibs/arm64-v8a
cp -f ../arm64-v8a/zakozakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozakozako.so
- name: Build with Gradle
run: |
export ANDROID_HOME=/root/.android/sdk
export PATH=$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$PATH
{
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
uses: actions/upload-artifact@v4
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
with:
name: manager
path: manager/app/build/outputs/apk/release/*.apk
- name: Upload mappings
uses: actions/upload-artifact@v4
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
with:
name: "mappings"
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
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
env:
CHAT_ID: ${{ vars.CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ vars.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
TITLE: Manager
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
export VERSION=$(git rev-list --count HEAD)
APK=$(find ./app/build/outputs/apk/release -name "*.apk")
python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK
fi

276
.github/workflows/build-manager.yml vendored Normal file
View File

@@ -0,0 +1,276 @@
name: Build Manager
on:
push:
branches: [ "main", "ci" ]
paths:
- '.github/workflows/build-manager.yml'
- 'manager/**'
- 'kernel/**'
- 'userspace/ksud/**'
- 'userspace/susfs/**'
- 'userspace/kpmmgr/**'
pull_request:
branches: [ "main" ]
paths:
- 'manager/**'
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"
build-lkm:
needs: check-build-lkm
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
build-susfs:
if: ${{ always() }}
needs: [ check-build-lkm, build-lkm ]
strategy:
matrix:
include:
- target: aarch64-linux-android
os: ubuntu-latest
uses: ./.github/workflows/susfs.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:
target: ${{ matrix.target }}
os: ${{ matrix.os }}
build-ksud:
if: ${{ always() }}
needs: [ check-build-lkm, build-lkm ]
strategy:
matrix:
include:
- target: aarch64-linux-android
os: ubuntu-latest
- target: x86_64-linux-android
os: ubuntu-latest
- target: armv7-linux-androideabi
os: ubuntu-latest
uses: ./.github/workflows/ksud.yml
with:
target: ${{ matrix.target }}
os: ${{ matrix.os }}
pack_lkm: true
pull_lkm: ${{ needs.check-build-lkm.outputs.build_lkm != 'true' }}
build-manager:
if: ${{ always() }}
needs: build-ksud
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./manager
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup need_upload
id: need_upload
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Write key
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref == 'refs/heads/susfs' || github.ref_type == 'tag' }}
run: |
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
{
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}'
echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}'
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}'
echo KEYSTORE_FILE='key.jks'
} >> gradle.properties
echo "${{ secrets.KEYSTORE }}" | base64 -d > key.jks
fi
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Download arm64 susfs
uses: actions/download-artifact@v4
with:
name: susfs-aarch64-linux-android
path: .
- name: Download arm64 kpmmgr
uses: actions/download-artifact@v4
with:
name: kpmmgr-aarch64-linux-android
path: .
- name: Download arm64 ksud
uses: actions/download-artifact@v4
with:
name: ksud-aarch64-linux-android
path: .
- name: Download x86_64 ksud
uses: actions/download-artifact@v4
with:
name: ksud-x86_64-linux-android
path: .
- name: Download arm ksud
uses: actions/download-artifact@v4
with:
name: ksud-armv7-linux-androideabi
path: .
- name: Copy ksud to app jniLibs
run: |
mkdir -p app/src/main/jniLibs/arm64-v8a
mkdir -p app/src/main/jniLibs/x86_64
mkdir -p app/src/main/jniLibs/armeabi-v7a
cp -f ../aarch64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozako.so
cp -f ../x86_64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/x86_64/libzakozako.so
cp -f ../armv7-linux-androideabi/release/zakozako ../manager/app/src/main/jniLibs/armeabi-v7a/libzakozako.so
- name: Copy kpmmgr to app jniLibs
run: |
mkdir -p app/src/main/jniLibs/arm64-v8a
cp -f ../arm64-v8a/kpmmgr ../manager/app/src/main/jniLibs/arm64-v8a/libkpmmgr.so
- name: Copy susfs to app jniLibs
run: |
mkdir -p app/src/main/jniLibs/arm64-v8a
cp -f ../arm64-v8a/zakozakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozakozako.so
- name: Build with Gradle
run: |
{
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
uses: actions/upload-artifact@v4
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
with:
name: manager
path: manager/app/build/outputs/apk/release/*.apk
- name: Upload mappings
uses: actions/upload-artifact@v4
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
with:
name: "mappings"
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
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
env:
CHAT_ID: ${{ vars.CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ vars.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
TITLE: Manager
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
export VERSION=$(git rev-list --count HEAD)
APK=$(find ./app/build/outputs/apk/release -name "*.apk")
pip3 install telethon
python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK
fi

36
.github/workflows/build-su.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Build SU
on:
push:
branches: [ "main", "ci" ]
paths:
- '.github/workflows/build-su.yml'
- 'userspace/su/**'
- 'scripts/ksubot.py'
pull_request:
branches: [ "main" ]
paths:
- 'userspace/su/**'
jobs:
build-su:
name: Build userspace su
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup need_upload
id: need_upload
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Build su
working-directory: ./userspace/su
run: $ANDROID_NDK/ndk-build
- name: Upload a Build Artifact
uses: actions/upload-artifact@v4
with:
name: su
path: ./userspace/su/libs

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

@@ -0,0 +1,37 @@
name: Clippy check
on:
push:
branches:
- main
paths:
- '.github/workflows/clippy.yml'
- 'userspace/ksud/**'
pull_request:
branches:
- main
paths:
- '.github/workflows/clippy.yml'
- 'userspace/ksud/**'
env:
RUSTFLAGS: '-Dwarnings'
jobs:
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: rustup update stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: userspace/ksud
- name: Install cross
run: |
RUSTFLAGS="" cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1
- name: Run clippy
run: |
cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android --release
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android --release

40
.github/workflows/crowdin.yml vendored Normal file
View 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 }}

67
.github/workflows/deploy-website.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: Deploy Website
on:
push:
branches:
- main
- website
paths:
- '.github/workflows/deploy-website.yml'
- 'website/**'
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: pages
cancel-in-progress: false
jobs:
# Build job
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./website
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Not needed if lastUpdated is not enabled
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: latest
cache: yarn # or pnpm / yarn
cache-dependency-path: website/yarn.lock
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build with VitePress
run: |
yarn docs:build
touch docs/.vitepress/dist/.nojekyll
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: website/docs/.vitepress/dist
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: Deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

252
.github/workflows/gki-kernel-local.yml vendored Normal file
View File

@@ -0,0 +1,252 @@
name: GKI Kernel Build Local
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:
build:
name: Build ${{ inputs.version_name }}
runs-on: self-hosted
env:
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
CCACHE_NOHASHDIR: "true"
CCACHE_HARDLINK: "true"
steps:
- 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
export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'
mkdir android-kernel && cd android-kernel
repo init --depth=1 --u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.35
REMOTE_BRANCH=$(git ls-remote https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/kernel/common ${{ inputs.tag }})
DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
if grep -q deprecated <<< $REMOTE_BRANCH; then
echo "Found deprecated branch: ${{ inputs.tag }}"
sed -i 's/"${{ inputs.tag }}"/"deprecated\/${{ inputs.tag }}"/g' $DEFAULT_MANIFEST_PATH
cat $DEFAULT_MANIFEST_PATH
fi
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 Compilation Patches"
if [ ! -e build/build.sh ]; then
GLIBC_VERSION=$(ldd --version 2>/dev/null | head -n 1 | awk '{print $NF}')
echo "GLIBC_VERSION: $GLIBC_VERSION"
if [ "$(printf '%s\n' "2.38" "$GLIBC_VERSION" | sort -V | head -n1)" = "2.38" ]; then
echo "Patching resolve_btfids/Makefile"
cd $GKI_ROOT/common/ && sed -i '/\$(Q)\$(MAKE) -C \$(SUBCMD_SRC) OUTPUT=\$(abspath \$(dir \$@))\/ \$(abspath \$@)/s//$(Q)$(MAKE) -C $(SUBCMD_SRC) EXTRA_CFLAGS="$(CFLAGS)" OUTPUT=$(abspath $(dir $@))\/ $(abspath $@)/' tools/bpf/resolve_btfids/Makefile || echo "No patch needed."
fi
fi
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."
- name: Symbol magic
run: |
echo "[+] Export all symbol from abi_gki_aarch64.xml"
COMMON_ROOT=$GITHUB_WORKSPACE/android-kernel/common
KSU_ROOT=$GITHUB_WORKSPACE/KernelSU
ABI_XML=$COMMON_ROOT/android/abi_gki_aarch64.xml
SYMBOL_LIST=$COMMON_ROOT/android/abi_gki_aarch64
# python3 $KSU_ROOT/scripts/abi_gki_all.py $ABI_XML > $SYMBOL_LIST
echo "[+] Add KernelSU symbols"
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
- name: Setup ccache
if: inputs.use_cache == true
uses: hendrikmuhs/ccache-action@v1
with:
key: gki-kernel-aarch64-${{ inputs.version_name }}
max-size: 2G
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
- name: Setup for LKM
if: ${{ inputs.build_lkm == true }}
working-directory: android-kernel
run: |
pip install ast-grep-cli
sudo apt-get install llvm-15 -y
ast-grep -U -p '$$$ check_exports($$$) {$$$}' -r '' common/scripts/mod/modpost.c
ast-grep -U -p 'check_exports($$$);' -r '' common/scripts/mod/modpost.c
sed -i '/config KSU/,/help/{s/default y/default m/}' common/drivers/kernelsu/Kconfig
echo "drivers/kernelsu/kernelsu.ko" >> common/android/gki_aarch64_modules
# bazel build, android14-5.15, android14-6.1 use bazel
if [ ! -e build/build.sh ]; then
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
if [ -e common/modules.bzl ]; then
sed -i 's/_COMMON_GKI_MODULES_LIST = \[/_COMMON_GKI_MODULES_LIST = \[ "drivers\/kernelsu\/kernelsu.ko",/g' common/modules.bzl
fi
else
TARGET_FILE="build/kernel/build.sh"
if [ ! -e "$TARGET_FILE" ]; then
TARGET_FILE="build/build.sh"
fi
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' $TARGET_FILE || echo "No unknown symbol in $TARGET_FILE"
sed -i 's/if ! diff -u "\${KERNEL_DIR}\/\${MODULES_ORDER}" "\${OUT_DIR}\/modules\.order"; then/if false; then/g' $TARGET_FILE
sed -i 's@${ROOT_DIR}/build/abi/compare_to_symbol_list@echo@g' $TARGET_FILE
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
fi
- name: Make working directory clean to avoid dirty
working-directory: android-kernel
run: |
if [ -e common/BUILD.bazel ]; then
sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' common/BUILD.bazel
fi
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/LKM
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
if [ -e build/build.sh ]; then
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang"
else
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist
fi
- name: Prepare artifacts
id: prepareArtifacts
run: |
OUTDIR=android-kernel/out/${{ inputs.version }}/dist
if [ ! -e $OUTDIR ]; then
OUTDIR=android-kernel/dist
fi
mkdir output
if [ "${{ inputs.build_lkm}}" = "true" ]; then
llvm-strip-15 -d $OUTDIR/kernelsu.ko
mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko
else
cp $OUTDIR/Image ./output/
cp $OUTDIR/Image.lz4 ./output/
git clone https://github.com/Kernel-SU/AnyKernel3
rm -rf ./AnyKernel3/.git
cp $OUTDIR/Image ./AnyKernel3/
fi
- name: Upload Image and Image.gz
uses: actions/upload-artifact@v4
if: ${{ inputs.build_lkm == false }}
with:
name: Image-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
path: ./output/*
- name: Upload AnyKernel3
if: ${{ inputs.build_lkm == false }}
uses: actions/upload-artifact@v4
with:
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
path: ./AnyKernel3/*
- name: Upload LKM
uses: actions/upload-artifact@v4
if: ${{ inputs.build_lkm == true }}
with:
name: ${{ inputs.version }}-lkm
path: ./output/*_kernelsu.ko

79
.github/workflows/gki-kernel-mock.yml vendored Normal file
View File

@@ -0,0 +1,79 @@
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

261
.github/workflows/gki-kernel.yml vendored Normal file
View File

@@ -0,0 +1,261 @@
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:
build:
name: Build ${{ inputs.version_name }}
runs-on: ubuntu-latest
env:
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
CCACHE_NOHASHDIR: "true"
CCACHE_HARDLINK: "true"
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 -b common-${{ inputs.tag }} --repo-rev=v2.35
REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/kernel/common ${{ inputs.tag }})
DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
if grep -q deprecated <<< $REMOTE_BRANCH; then
echo "Found deprecated branch: ${{ inputs.tag }}"
sed -i 's/"${{ inputs.tag }}"/"deprecated\/${{ inputs.tag }}"/g' $DEFAULT_MANIFEST_PATH
cat $DEFAULT_MANIFEST_PATH
fi
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 Compilation Patches"
if [ ! -e build/build.sh ]; then
GLIBC_VERSION=$(ldd --version 2>/dev/null | head -n 1 | awk '{print $NF}')
echo "GLIBC_VERSION: $GLIBC_VERSION"
if [ "$(printf '%s\n' "2.38" "$GLIBC_VERSION" | sort -V | head -n1)" = "2.38" ]; then
echo "Patching resolve_btfids/Makefile"
cd $GKI_ROOT/common/ && sed -i '/\$(Q)\$(MAKE) -C \$(SUBCMD_SRC) OUTPUT=\$(abspath \$(dir \$@))\/ \$(abspath \$@)/s//$(Q)$(MAKE) -C $(SUBCMD_SRC) EXTRA_CFLAGS="$(CFLAGS)" OUTPUT=$(abspath $(dir $@))\/ $(abspath $@)/' tools/bpf/resolve_btfids/Makefile || echo "No patch needed."
fi
fi
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."
- name: Symbol magic
run: |
echo "[+] Export all symbol from abi_gki_aarch64.xml"
COMMON_ROOT=$GITHUB_WORKSPACE/android-kernel/common
KSU_ROOT=$GITHUB_WORKSPACE/KernelSU
ABI_XML=$COMMON_ROOT/android/abi_gki_aarch64.xml
SYMBOL_LIST=$COMMON_ROOT/android/abi_gki_aarch64
# python3 $KSU_ROOT/scripts/abi_gki_all.py $ABI_XML > $SYMBOL_LIST
echo "[+] Add KernelSU symbols"
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
- name: Setup ccache
if: inputs.use_cache == true
uses: hendrikmuhs/ccache-action@v1
with:
key: gki-kernel-aarch64-${{ inputs.version_name }}
max-size: 2G
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
- name: Setup for LKM
if: ${{ inputs.build_lkm == true }}
working-directory: android-kernel
run: |
pip install ast-grep-cli
sudo apt-get install llvm-15 -y
ast-grep -U -p '$$$ check_exports($$$) {$$$}' -r '' common/scripts/mod/modpost.c
ast-grep -U -p 'check_exports($$$);' -r '' common/scripts/mod/modpost.c
sed -i '/config KSU/,/help/{s/default y/default m/}' common/drivers/kernelsu/Kconfig
echo "drivers/kernelsu/kernelsu.ko" >> common/android/gki_aarch64_modules
# bazel build, android14-5.15, android14-6.1 use bazel
if [ ! -e build/build.sh ]; then
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
if [ -e common/modules.bzl ]; then
sed -i 's/_COMMON_GKI_MODULES_LIST = \[/_COMMON_GKI_MODULES_LIST = \[ "drivers\/kernelsu\/kernelsu.ko",/g' common/modules.bzl
fi
else
TARGET_FILE="build/kernel/build.sh"
if [ ! -e "$TARGET_FILE" ]; then
TARGET_FILE="build/build.sh"
fi
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' $TARGET_FILE || echo "No unknown symbol in $TARGET_FILE"
sed -i 's/if ! diff -u "\${KERNEL_DIR}\/\${MODULES_ORDER}" "\${OUT_DIR}\/modules\.order"; then/if false; then/g' $TARGET_FILE
sed -i 's@${ROOT_DIR}/build/abi/compare_to_symbol_list@echo@g' $TARGET_FILE
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
fi
- name: Make working directory clean to avoid dirty
working-directory: android-kernel
run: |
if [ -e common/BUILD.bazel ]; then
sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' common/BUILD.bazel
fi
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/LKM
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
if [ -e build/build.sh ]; then
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang"
else
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist
fi
- name: Prepare artifacts
id: prepareArtifacts
run: |
OUTDIR=android-kernel/out/${{ inputs.version }}/dist
if [ ! -e $OUTDIR ]; then
OUTDIR=android-kernel/dist
fi
mkdir output
if [ "${{ inputs.build_lkm}}" = "true" ]; then
llvm-strip-15 -d $OUTDIR/kernelsu.ko
mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko
else
cp $OUTDIR/Image ./output/
cp $OUTDIR/Image.lz4 ./output/
git clone https://github.com/Kernel-SU/AnyKernel3
rm -rf ./AnyKernel3/.git
cp $OUTDIR/Image ./AnyKernel3/
fi
- name: Upload Image and Image.gz
uses: actions/upload-artifact@v4
if: ${{ inputs.build_lkm == false }}
with:
name: Image-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
path: ./output/*
- name: Upload AnyKernel3
if: ${{ inputs.build_lkm == false }}
uses: actions/upload-artifact@v4
with:
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
path: ./AnyKernel3/*
- name: Upload LKM
uses: actions/upload-artifact@v4
if: ${{ inputs.build_lkm == true }}
with:
name: ${{ inputs.version }}-lkm
path: ./output/*_kernelsu.ko

40
.github/workflows/kpmmgr.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
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

74
.github/workflows/ksud.yml vendored Normal file
View File

@@ -0,0 +1,74 @@
name: Build ksud
on:
workflow_call:
inputs:
target:
required: true
type: string
os:
required: false
type: string
default: ubuntu-latest
pull_lkm:
required: false
type: boolean
default: true
pack_lkm:
required: false
type: boolean
default: true
use_cache:
required: false
type: boolean
default: true
jobs:
build:
runs-on: ${{ inputs.os }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Pull lkms from branch
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
- name: Prepare LKM files
if: ${{ inputs.pack_lkm && inputs.pull_lkm }}
run: |
cp lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
- name: Prepare LKM files
if: ${{ inputs.pack_lkm && !inputs.pull_lkm }}
run: |
cp android*-lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
- name: Setup rustup
run: |
rustup update stable
rustup target add x86_64-apple-darwin
rustup target add aarch64-apple-darwin
- uses: Swatinem/rust-cache@v2
with:
workspaces: userspace/ksud
cache-targets: false
- name: Install cross
run: |
RUSTFLAGS="" cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1
- name: Build ksud
run: CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud/Cargo.toml
- name: Upload ksud artifact
uses: actions/upload-artifact@v4
with:
name: ksud-${{ inputs.target }}
path: userspace/ksud/target/**/release/zakozako*

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

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

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

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

40
.github/workflows/susfs.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: Build susfs
on:
push:
branches: [ "mian" ]
paths:
- '.github/workflows/susfs.yml'
- 'userspace/susfs/**'
workflow_dispatch:
workflow_call:
inputs:
target:
required: true
type: string
os:
required: false
type: string
default: self-hosted
jobs:
build-susfs:
name: Build userspace susfs
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build susfs
working-directory: ./userspace/susfs
run: |
$ANDROID_NDK_HOME/ndk-build
- name: Upload a Build Artifact
uses: actions/upload-artifact@v4
with:
name: susfs-aarch64-linux-android
path: ./userspace/susfs/libs

4
.gitignore vendored
View File

@@ -1 +1,3 @@
manager
.idea
.vscode
.DS_Store

7
SECURITY.md Normal file
View File

@@ -0,0 +1,7 @@
# Reporting Security Issues
The KernelSU team and community take security bugs in KernelSU seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/tiann/KernelSU/security/advisories/new) tab, or you can mailto [weishu](mailto:twsxtd@gmail.com) directly.
The KernelSU team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.

6
crowdin.yml Normal file
View 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

101
docs/README.md Normal file
View File

@@ -0,0 +1,101 @@
<img align='right' src='zakomonochrome-128.svg' width='100px' alt="logo">
# SukiSU Ultra
**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.
[![Latest release](https://img.shields.io/github/v/release/SukiSU-Ultra/SukiSU-Ultra?label=Release&logo=github)](https://github.com/tiann/KernelSU/releases/latest)
[![Channel](https://img.shields.io/badge/Follow-Telegram-blue.svg?logo=telegram)](https://t.me/Sukiksu)
[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
[![GitHub License](https://img.shields.io/github/license/tiann/KernelSU?logo=gnu)](/LICENSE)
## Features
1. Kernel-based `su` and root access management
2. Module system based on [Magic Mount](https://github.com/5ec1cff/KernelSU)
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]
>
> 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_
## Sponsor
- [ShirkNeko](https://afdian.com/a/shirkneko) (maintainer of SukiSU)
- [weishu](https://github.com/sponsors/tiann) (author of KernelSU)
## ShirkNeko's sponsorship list
- [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
- 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.
## Credit
- [KernelSU](https://github.com/tiann/KernelSU): upstream
- [MKSU](https://github.com/5ec1cff/KernelSU): Magic Mount
- [RKSU](https://github.com/rsuntk/KernelsU): support non-GKI
- [susfs](https://gitlab.com/simonpunk/susfs4ksu): An addon root hiding kernel patches and userspace module for KernelSU.
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch is a key part of the APatch implementation of the kernel module
<details>
<summary>KernelSU's credit</summary>
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): The KernelSU idea.
- [Magisk](https://github.com/topjohnwu/Magisk): The powerful root tool.
- [genuine](https://github.com/brevent/genuine/): APK v2 signature validation.
- [Diamorphine](https://github.com/m0nad/Diamorphine): Some rootkit skills.
</details>

View File

@@ -0,0 +1,91 @@
# 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)
<!-- 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))

View 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.

151
docs/ja/README.md Normal file
View File

@@ -0,0 +1,151 @@
# SukiSU Ultra
[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 実装の重要な部分での活用

149
docs/tr/README.md Normal file
View File

@@ -0,0 +1,149 @@
# SukiSU Ultra
[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

View 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
View File

@@ -0,0 +1,101 @@
<img align='right' src='zakomonochrome-128.svg' width='100px' alt="logo">
# SukiSU Ultra
[English](../README.md) | **简体中文** | [日本語](../ja/README.md) | [Türkçe](../tr/README.md)
一个 Android 上基于内核的 root 方案,由 [`tiann/KernelSU`](https://github.com/tiann/KernelSU) 分叉而来,添加了一些有趣的变更。
[![最新发行](https://img.shields.io/github/v/release/SukiSU-Ultra/SukiSU-Ultra?label=Release&logo=github)](https://github.com/tiann/KernelSU/releases/latest)
[![频道](https://img.shields.io/badge/Follow-Telegram-blue.svg?logo=telegram)](https://t.me/Sukiksu)
[![协议: GPL v2](https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
[![GitHub 协议](https://img.shields.io/github/license/tiann/KernelSU?logo=gnu)](/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>

View File

@@ -0,0 +1,91 @@
# 集成指导
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`](guide/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)
<!-- 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但目前无法使用。 -->
将 KernelSUSukiSU添加到内核源代码树的步骤的运行命令将被替换为
### 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) 里找到它)

View 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`...
> - 使用前请确保您了解处理器名称、内核版本等信息。

View 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

121
js/README.md Normal file
View File

@@ -0,0 +1,121 @@
# Library for KernelSU's module WebUI
## Install
```sh
yarn add kernelsu
```
## API
### exec
Spawns a **root** shell and runs a command within that shell, returning a Promise that resolves with the `stdout` and `stderr` outputs upon completion.
- `command` `<string>` The command to run, with space-separated arguments.
- `options` `<Object>`
- `cwd` - Current working directory of the child process.
- `env` - Environment key-value pairs.
```javascript
import { exec } from 'kernelsu';
const { errno, stdout, stderr } = await exec('ls -l', { cwd: '/tmp' });
if (errno === 0) {
// success
console.log(stdout);
}
```
### spawn
Spawns a new process using the given `command` in **root** shell, with command-line arguments in `args`. If omitted, `args` defaults to an empty array.
Returns a `ChildProcess` instance. Instances of `ChildProcess` represent spawned child processes.
- `command` `<string>` The command to run.
- `args` `<string[]>` List of string arguments.
- `options` `<Object>`:
- `cwd` `<string>` - Current working directory of the child process.
- `env` `<Object>` - Environment key-value pairs.
Example of running `ls -lh /data`, capturing `stdout`, `stderr`, and the exit code:
```javascript
import { spawn } from 'kernelsu';
const ls = spawn('ls', ['-lh', '/data']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
ls.on('exit', (code) => {
console.log(`child process exited with code ${code}`);
});
```
#### ChildProcess
##### Event 'exit'
- `code` `<number>` The exit code if the child process exited on its own.
The `'exit'` event is emitted when the child process ends. If the process exits, `code` contains the final exit code; otherwise, it is null.
##### Event 'error'
- `err` `<Error>` The error.
The `'error'` event is emitted whenever:
- The process could not be spawned.
- The process could not be killed.
##### `stdout`
A `Readable Stream` that represents the child process's `stdout`.
```javascript
const subprocess = spawn('ls');
subprocess.stdout.on('data', (data) => {
console.log(`Received chunk ${data}`);
});
```
#### `stderr`
A `Readable Stream` that represents the child process's `stderr`.
### fullScreen
Request the WebView enter/exit full screen.
```javascript
import { fullScreen } from 'kernelsu';
fullScreen(true);
```
### toast
Show a toast message.
```javascript
import { toast } from 'kernelsu';
toast('Hello, world!');
```
### moduleInfo
Get module info.
```javascript
import { moduleInfo } from 'kernelsu';
// print moduleId in console
console.log(moduleInfo());
```

48
js/index.d.ts vendored Normal file
View File

@@ -0,0 +1,48 @@
interface ExecOptions {
cwd?: string,
env?: { [key: string]: string }
}
interface ExecResults {
errno: number,
stdout: string,
stderr: string
}
declare function exec(command: string): Promise<ExecResults>;
declare function exec(command: string, options: ExecOptions): Promise<ExecResults>;
interface SpawnOptions {
cwd?: string,
env?: { [key: string]: string }
}
interface Stdio {
on(event: 'data', callback: (data: string) => void)
}
interface ChildProcess {
stdout: Stdio,
stderr: Stdio,
on(event: 'exit', callback: (code: number) => void)
on(event: 'error', callback: (err: any) => void)
}
declare function spawn(command: string): ChildProcess;
declare function spawn(command: string, args: string[]): ChildProcess;
declare function spawn(command: string, options: SpawnOptions): ChildProcess;
declare function spawn(command: string, args: string[], options: SpawnOptions): ChildProcess;
declare function fullScreen(isFullScreen: boolean);
declare function toast(message: string);
declare function moduleInfo(): string;
export {
exec,
spawn,
fullScreen,
toast,
moduleInfo
}

119
js/index.js Normal file
View File

@@ -0,0 +1,119 @@
let callbackCounter = 0;
function getUniqueCallbackName(prefix) {
return `${prefix}_callback_${Date.now()}_${callbackCounter++}`;
}
export function exec(command, options) {
if (typeof options === "undefined") {
options = {};
}
return new Promise((resolve, reject) => {
// Generate a unique callback function name
const callbackFuncName = getUniqueCallbackName("exec");
// Define the success callback function
window[callbackFuncName] = (errno, stdout, stderr) => {
resolve({ errno, stdout, stderr });
cleanup(callbackFuncName);
};
function cleanup(successName) {
delete window[successName];
}
try {
ksu.exec(command, JSON.stringify(options), callbackFuncName);
} catch (error) {
reject(error);
cleanup(callbackFuncName);
}
});
}
function Stdio() {
this.listeners = {};
}
Stdio.prototype.on = function (event, listener) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(listener);
};
Stdio.prototype.emit = function (event, ...args) {
if (this.listeners[event]) {
this.listeners[event].forEach((listener) => listener(...args));
}
};
function ChildProcess() {
this.listeners = {};
this.stdin = new Stdio();
this.stdout = new Stdio();
this.stderr = new Stdio();
}
ChildProcess.prototype.on = function (event, listener) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(listener);
};
ChildProcess.prototype.emit = function (event, ...args) {
if (this.listeners[event]) {
this.listeners[event].forEach((listener) => listener(...args));
}
};
export function spawn(command, args, options) {
if (typeof args === "undefined") {
args = [];
} else if (!(args instanceof Array)) {
// allow for (command, options) signature
options = args;
}
if (typeof options === "undefined") {
options = {};
}
const child = new ChildProcess();
const childCallbackName = getUniqueCallbackName("spawn");
window[childCallbackName] = child;
function cleanup(name) {
delete window[name];
}
child.on("exit", code => {
cleanup(childCallbackName);
});
try {
ksu.spawn(
command,
JSON.stringify(args),
JSON.stringify(options),
childCallbackName
);
} catch (error) {
child.emit("error", error);
cleanup(childCallbackName);
}
return child;
}
export function fullScreen(isFullScreen) {
ksu.fullScreen(isFullScreen);
}
export function toast(message) {
ksu.toast(message);
}
export function moduleInfo() {
return ksu.moduleInfo();
}

26
js/package.json Normal file
View File

@@ -0,0 +1,26 @@
{
"name": "kernelsu",
"version": "1.0.7",
"description": "Library for KernelSU's module WebUI",
"main": "index.js",
"types": "index.d.ts",
"scripts": {
"test": "npm run test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tiann/KernelSU.git"
},
"keywords": [
"su",
"kernelsu",
"module",
"webui"
],
"author": "weishu",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/tiann/KernelSU/issues"
},
"homepage": "https://github.com/tiann/KernelSU#readme"
}

14
justfile Normal file
View File

@@ -0,0 +1,14 @@
alias bk := build_ksud
alias bm := build_manager
build_ksud:
cross build --target aarch64-linux-android --release --manifest-path ./userspace/ksud/Cargo.toml
build_manager: build_ksud
cp userspace/ksud/target/aarch64-linux-android/release/ksud manager/app/src/main/jniLibs/arm64-v8a/libksud.so
cd manager && ./gradlew aDebug
clippy:
cargo fmt --manifest-path ./userspace/ksud/Cargo.toml
cross clippy --target x86_64-pc-windows-gnu --release --manifest-path ./userspace/ksud/Cargo.toml
cross clippy --target aarch64-linux-android --release --manifest-path ./userspace/ksud/Cargo.toml

22
kernel/.gitignore vendored
View File

@@ -1,22 +0,0 @@
.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

View File

@@ -2,17 +2,13 @@ menu "KernelSU"
config KSU
tristate "KernelSU function support"
depends on OVERLAY_FS
default y
help
Enable kernel-level root privileges on Android System.
To compile as a module, choose M here: the
module will be called kernelsu.
# For easier extern ifdef handling
config RKSU
bool "RKSU compat, do not modify"
default y
config KSU_DEBUG
bool "KernelSU debug mode"
depends on KSU
@@ -20,13 +16,6 @@ config KSU_DEBUG
help
Enable KernelSU debug mode.
config KSU_ALLOWLIST_WORKAROUND
bool "KernelSU allowlist workaround"
depends on KSU
default n
help
Enable workaround for broken allowlist save
config KPM
bool "Enable SukiSU KPM"
depends on KSU && 64BIT
@@ -37,23 +26,4 @@ config KPM
but it may affect system stability.
select KALLSYMS
select KALLSYMS_ALL
config KSU_MANUAL_HOOK
bool "KernelSU manual hook mode."
depends on KSU && KSU != m
default y if !KPROBES
default n
help
Enable manual hook support.
config KSU_SHOULD_USE_NEW_TP
bool "KernelSU tracepoint+kretprobe hook"
depends on KSU && !KSU_MANUAL_HOOK
depends on KRETPROBES && KPROBES && HAVE_SYSCALL_TRACEPOINTS
default y if KPROBES && KRETPROBES && HAVE_SYSCALL_TRACEPOINTS
default n
help
Enable KPROBES, KRETPROBES and TRACEPOINT hook for KernelSU core.
This should not be used on kernel below 5.10.
endmenu

View File

@@ -1,41 +1,28 @@
kernelsu-objs := ksu.o
kernelsu-objs += allowlist.o
kernelsu-objs += dynamic_manager.o
kernelsu-objs += app_profile.o
kernelsu-objs += apk_sign.o
kernelsu-objs += sucompat.o
kernelsu-objs += syscall_hook_manager.o
kernelsu-objs += throne_tracker.o
kernelsu-objs += pkg_observer.o
kernelsu-objs += setuid_hook.o
kernelsu-objs += lsm_hooks.o
kernelsu-objs += kernel_compat.o
kernelsu-objs += kernel_umount.o
kernelsu-objs += supercalls.o
kernelsu-objs += feature.o
kernelsu-objs += throne_tracker.o
kernelsu-objs += core_hook.o
kernelsu-objs += ksud.o
kernelsu-objs += embed_ksud.o
kernelsu-objs += seccomp_cache.o
kernelsu-objs += file_wrapper.o
kernelsu-objs += throne_comm.o
kernelsu-objs += kernel_compat.o
kernelsu-objs += selinux/selinux.o
kernelsu-objs += selinux/sepolicy.o
kernelsu-objs += selinux/rules.o
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
obj-$(CONFIG_KSU) += kernelsu.o
obj-$(CONFIG_KSU_TRACEPOINT_HOOK) += ksu_trace_export.o
obj-$(CONFIG_KPM) += kpm/
REPO_OWNER := SukiSU-Ultra
REPO_NAME := SukiSU-Ultra
REPO_BRANCH := main
KSU_VERSION_API := 4.0.0
KSU_VERSION_API := 3.1.8
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
@@ -43,36 +30,30 @@ CURL_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin curl
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
LOCAL_GIT_EXISTS := $(shell test -e $(KSU_SRC)/../.git && echo 1 || echo 0)
LOCAL_GIT_EXISTS := $(shell test -e $(srctree)/$(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)
v$1-$(shell cd $(srctree)/$(src); $(GIT_BIN) rev-parse --short=8 HEAD)@$(shell cd $(srctree)/$(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)
$(shell cd $(srctree)/$(src); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
KSU_LOCAL_VERSION := $(shell cd $(srctree)/$(src); $(GIT_BIN) rev-list --count $(REPO_BRANCH))
KSU_VERSION := $(shell expr 10000 + $(KSU_LOCAL_VERSION) + 700)
$(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)
KSU_VERSION := $(shell expr 10000 + $(KSU_GITHUB_VERSION_COMMIT) + 700)
$(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)
$(shell cd $(srctree)/$(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))
@@ -81,7 +62,7 @@ ifeq ($(KSU_GITHUB_VERSION),)
$(warning -- $(REPO_NAME) version: $(KSU_VERSION_FULL))
endif
else
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
$(shell cd $(srctree)/$(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
@@ -89,97 +70,12 @@ endif
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
ccflags-y += -DKSU_VERSION_FULL=\"$(KSU_VERSION_FULL)\"
# RKSU: checks for available hook
## Logic flipped for HAVE_KSU_HOOK: 0 is success, 1 is failure, but not with KSU_DRY_RUN
HAVE_KSU_HOOK ?= 1
KSU_DRY_RUN ?= 0
# Checks hooks state
ifeq ($(CONFIG_KSU_SHOULD_USE_NEW_TP), y)
$(info -- KernelSU: SHOULD_USE_NEW_TP)
ccflags-y += -DKSU_SHOULD_USE_NEW_TP
# Let's make it 0, so it would pass.
HAVE_KSU_HOOK := 0
ifndef KSU_EXPECTED_SIZE
KSU_EXPECTED_SIZE := 0x35c
endif
ifeq ($(CONFIG_KSU_MANUAL_HOOK), y)
HAVE_KSU_HOOK := $(shell grep -q "ksu_handle_faccessat" $(srctree)/fs/open.c; echo $$?)
ifeq ($(HAVE_KSU_HOOK),0)
$(info -- KernelSU: CONFIG_KSU_MANUAL_HOOK)
endif
endif
ifeq ($(KSU_DRY_RUN),0)
ifneq ($(HAVE_KSU_HOOK),0)
$(error -- KernelSU: No hooks were defined, bail!)
endif
else
$(info -- KernelSU in dry run mode, skip hook checks)
endif
# SELinux drivers check
ifeq ($(shell grep -q "current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID
endif
ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0)
ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE
endif
# Handle optional backports
ifeq ($(shell grep -q "strncpy_from_user_nofault" $(srctree)/include/linux/uaccess.h; echo $$?),0)
ccflags-y += -DKSU_OPTIONAL_STRNCPY
endif
ifeq ($(shell grep -q "ssize_t kernel_read" $(srctree)/fs/read_write.c; echo $$?),0)
ccflags-y += -DKSU_OPTIONAL_KERNEL_READ
endif
ifeq ($(shell grep "ssize_t kernel_write" $(srctree)/fs/read_write.c | grep -q "const void" ; echo $$?),0)
ccflags-y += -DKSU_OPTIONAL_KERNEL_WRITE
endif
ifeq ($(shell grep -q "int\s\+path_umount" $(srctree)/fs/namespace.c; echo $$?),0)
ccflags-y += -DKSU_HAS_PATH_UMOUNT
endif
ifeq ($(shell grep -q "inode_security_struct\s\+\*selinux_inode" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
ccflags-y += -DKSU_OPTIONAL_SELINUX_INODE
endif
ifeq ($(shell grep -q "task_security_struct\s\+\*selinux_cred" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
ccflags-y += -DKSU_OPTIONAL_SELINUX_CRED
endif
# seccomp_types.h were added on 6.7
ifeq ($(shell grep -q "atomic_t\s\+filter_count" $(srctree)/include/linux/seccomp.h $(srctree)/include/linux/seccomp_types.h; echo $$?),0)
ccflags-y += -DKSU_OPTIONAL_SECCOMP_FILTER_CNT
endif
# some old kernel backport this, let's check if put_seccomp_filter still exist
ifneq ($(shell grep -wq "put_seccomp_filter" $(srctree)/kernel/seccomp.c $(srctree)/include/linux/seccomp.h; echo $$?),0)
ccflags-y += -DKSU_OPTIONAL_SECCOMP_FILTER_RELEASE
endif
ifeq ($(shell grep -q "anon_inode_getfd_secure" $(srctree)/fs/anon_inodes.c; echo $$?),0)
ccflags-y += -DKSU_HAS_GETFD_SECURE
endif
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
# Checks Samsung
ifeq ($(shell grep -q "CONFIG_KDP_CRED" $(srctree)/kernel/cred.c; echo $$?),0)
ccflags-y += -DSAMSUNG_UH_DRIVER_EXIST
endif
ifeq ($(shell grep -q "SEC_SELINUX_PORTING_COMMON" $(srctree)/security/selinux/avc.c; echo $$?),0)
ccflags-y += -DSAMSUNG_SELINUX_PORTING
endif
# Function proc_ops check
ifeq ($(shell grep -q "struct proc_ops " $(srctree)/include/linux/proc_fs.h; echo $$?),0)
ccflags-y += -DKSU_COMPAT_HAS_PROC_OPS
endif
# Custom Signs
ifdef KSU_EXPECTED_SIZE
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
$(info -- Custom KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
endif
ifdef KSU_EXPECTED_HASH
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
$(info -- Custom KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
ifndef KSU_EXPECTED_HASH
KSU_EXPECTED_HASH := 947ae944f3de4ed4c21a7e4f7953ecf351bfa2b36239da37a34111ad29993eef
endif
ifdef KSU_MANAGER_PACKAGE
@@ -187,8 +83,9 @@ ccflags-y += -DKSU_MANAGER_PACKAGE=\"$(KSU_MANAGER_PACKAGE)\"
$(info -- SukiSU Manager package name: $(KSU_MANAGER_PACKAGE))
endif
$(info -- SukiSU Manager signature size: $(KSU_EXPECTED_SIZE))
$(info -- SukiSU Manager signature hash: $(KSU_EXPECTED_HASH))
$(info -- Supported Unofficial Manager: 5ec1cff (GKI) ShirkNeko udochina (GKI and KPM))
# Kernel version check
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
KERNEL_TYPE := Non-GKI
# Check for GKI 2.0 (5.10+ or 6.x+)
@@ -204,13 +101,17 @@ $(info -- KERNEL_VERSION: $(KERNEL_VERSION))
$(info -- KERNEL_TYPE: $(KERNEL_TYPE))
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
ifeq ($(CONFIG_KPM), y)
ifeq ($(CONFIG_KPM),y)
$(info -- KPM is enabled)
else
$(info -- KPM is disabled)
endif
ccflags-y += -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function -Wno-missing-prototypes
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
# Keep a new line here!! Because someone may append config

View File

@@ -1,5 +1,3 @@
#include <linux/mutex.h>
#include <linux/task_work.h>
#include <linux/capability.h>
#include <linux/compiler.h>
#include <linux/fs.h>
@@ -10,22 +8,14 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/task.h>
#else
#include <linux/sched.h>
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
#include <linux/compiler_types.h>
#endif
#include "ksu.h"
#include "klog.h" // IWYU pragma: keep
#include "ksud.h"
#include "selinux/selinux.h"
#include "kernel_compat.h"
#include "allowlist.h"
#include "manager.h"
#include "kernel_compat.h"
#include "syscall_hook_manager.h"
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
#define FILE_FORMAT_VERSION 3 // u32
@@ -39,8 +29,7 @@ static DEFINE_MUTEX(allowlist_mutex);
static struct root_profile default_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);
static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly __aligned(PAGE_SIZE);
static int allow_list_pointer __read_mostly = 0;
static void remove_uid_from_arr(uid_t uid)
@@ -51,7 +40,7 @@ static void remove_uid_from_arr(uid_t uid)
if (allow_list_pointer == 0)
return;
temp_arr = kzalloc(sizeof(allow_list_arr), GFP_KERNEL);
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
if (temp_arr == NULL) {
pr_err("%s: unable to allocate memory\n", __func__);
return;
@@ -72,7 +61,7 @@ static void remove_uid_from_arr(uid_t uid)
kfree(temp_arr);
}
static void init_default_profiles(void)
static void init_default_profiles()
{
kernel_cap_t full_cap = CAP_FULL_SET;
@@ -101,7 +90,10 @@ static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
void persistent_allow_list(void);
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)
{
@@ -116,7 +108,7 @@ void ksu_show_allow_list(void)
}
#ifdef CONFIG_KSU_DEBUG
static void ksu_grant_root_to_shell(void)
static void ksu_grant_root_to_shell()
{
struct app_profile profile = {
.version = KSU_APP_PROFILE_VER,
@@ -124,8 +116,7 @@ static void ksu_grant_root_to_shell(void)
.current_uid = 2000,
};
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_DEFAULT_SELINUX_DOMAIN);
ksu_set_app_profile(&profile, false);
}
#endif
@@ -151,10 +142,9 @@ exit:
return found;
}
static inline bool forbid_system_uid(uid_t uid)
{
#define SHELL_UID 2000
#define SYSTEM_UID 1000
static inline bool forbid_system_uid(uid_t uid) {
#define SHELL_UID 2000
#define SYSTEM_UID 1000
return uid < SHELL_UID && uid != SYSTEM_UID;
}
@@ -206,7 +196,7 @@ bool ksu_set_app_profile(struct app_profile *profile, bool persist)
}
// not found, alloc a new node!
p = (struct perm_data *)kzalloc(sizeof(struct perm_data), GFP_KERNEL);
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
if (!p) {
pr_err("ksu_set_app_profile alloc failed\n");
return false;
@@ -228,11 +218,9 @@ bool ksu_set_app_profile(struct app_profile *profile, bool persist)
out:
if (profile->current_uid <= BITMAP_UID_MAX) {
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] |= 1 << (profile->current_uid % BITS_PER_BYTE);
else
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] &= ~(1 << (profile->current_uid % BITS_PER_BYTE));
} else {
if (profile->allow_su) {
/*
@@ -244,8 +232,7 @@ out:
WARN_ON(1);
return false;
}
allow_list_arr[allow_list_pointer++] =
profile->current_uid;
allow_list_arr[allow_list_pointer++] = profile->current_uid;
} else {
remove_uid_from_arr(profile->current_uid);
}
@@ -265,11 +252,8 @@ out:
sizeof(default_root_profile));
}
if (persist) {
if (persist)
persistent_allow_list();
// FIXME: use a new flag
ksu_mark_running_process();
}
return result;
}
@@ -278,20 +262,23 @@ bool __ksu_is_allow_uid(uid_t uid)
{
int i;
if (unlikely(uid == 0)) {
// already root, but only allow our domain.
return is_ksu_domain();
}
if (forbid_system_uid(uid)) {
// do not bother going through the list if it's system
return false;
}
if (likely(ksu_is_manager_uid_valid()) &&
unlikely(ksu_get_manager_uid() == uid)) {
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
// manager is always allowed!
return true;
}
if (likely(uid <= BITMAP_UID_MAX)) {
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] &
(1 << (uid % BITS_PER_BYTE)));
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)
@@ -302,20 +289,10 @@ bool __ksu_is_allow_uid(uid_t uid)
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)
{
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()) && unlikely(ksu_get_manager_uid() == uid)) {
// we should not umount on manager!
return false;
}
@@ -372,7 +349,7 @@ bool ksu_get_allow_list(int *array, int *length, bool allow)
return true;
}
static void do_persistent_allow_list(struct callback_head *_cb)
void do_save_allow_list(struct work_struct *work)
{
u32 magic = FILE_MAGIC;
u32 version = FILE_FORMAT_VERSION;
@@ -380,26 +357,24 @@ static void do_persistent_allow_list(struct callback_head *_cb)
struct list_head *pos = NULL;
loff_t off = 0;
mutex_lock(&allowlist_mutex);
struct file *fp = ksu_filp_open_compat(
KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
struct file *fp =
ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) {
pr_err("save_allow_list create file failed: %ld\n",
PTR_ERR(fp));
goto unlock;
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
return;
}
// store magic and version
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
sizeof(magic)) {
pr_err("save_allow_list write magic failed.\n");
goto close_file;
goto exit;
}
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
sizeof(version)) {
pr_err("save_allow_list write version failed.\n");
goto close_file;
goto exit;
}
list_for_each (pos, &allow_list) {
@@ -412,37 +387,11 @@ static void do_persistent_allow_list(struct callback_head *_cb)
&off);
}
close_file:
exit:
filp_close(fp, 0);
unlock:
mutex_unlock(&allowlist_mutex);
kfree(_cb);
}
void persistent_allow_list(void)
{
struct task_struct *tsk;
tsk = get_pid_task(find_vpid(1), PIDTYPE_PID);
if (!tsk) {
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(void)
void do_load_allow_list(struct work_struct *work)
{
loff_t off = 0;
ssize_t ret = 0;
@@ -499,17 +448,11 @@ exit:
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 *n = NULL;
if (!ksu_boot_completed) {
pr_info("boot not completed, skip prune\n");
return;
}
bool modified = false;
// TODO: use RCU!
mutex_lock(&allowlist_mutex);
@@ -523,8 +466,7 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *),
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));
allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE));
}
remove_uid_from_arr(uid);
smp_mb();
@@ -538,6 +480,17 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *),
}
}
// make sure allow list works cross boot
bool persistent_allow_list(void)
{
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)
{
int i;
@@ -550,6 +503,9 @@ void ksu_allowlist_init(void)
INIT_LIST_HEAD(&allow_list);
INIT_WORK(&ksu_save_work, do_save_allow_list);
INIT_WORK(&ksu_load_work, do_load_allow_list);
init_default_profiles();
}
@@ -558,6 +514,8 @@ void ksu_allowlist_exit(void)
struct perm_data *np = NULL;
struct perm_data *n = NULL;
do_save_allow_list(NULL);
// free allowlist
mutex_lock(&allowlist_mutex);
list_for_each_entry_safe (np, n, &allow_list, list) {

View File

@@ -2,44 +2,26 @@
#define __KSU_H_ALLOWLIST
#include <linux/types.h>
#include <linux/uidgid.h>
#include "app_profile.h"
#define PER_USER_RANGE 100000
#define FIRST_APPLICATION_UID 10000
#define LAST_APPLICATION_UID 19999
#include "ksu.h"
void ksu_allowlist_init(void);
void ksu_allowlist_exit(void);
void ksu_load_allow_list(void);
bool ksu_load_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);
#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);
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);
bool ksu_get_app_profile(struct app_profile *);
bool ksu_set_app_profile(struct app_profile *, bool persist);
bool ksu_uid_should_umount(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;
}
#endif

View File

@@ -4,6 +4,7 @@
#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
@@ -15,21 +16,483 @@
#endif
#include "apk_sign.h"
#include "dynamic_manager.h"
#include "klog.h" // IWYU pragma: keep
#include "kernel_compat.h"
#include "manager_sign.h"
// Expected sizes and hashes for various APK signatures
#define DYNAMIC_SIGN_FILE_MAGIC 0x7f445347 // 'DSG', u32
#define DYNAMIC_SIGN_FILE_VERSION 1 // u32
#define KERNEL_SU_DYNAMIC_SIGN "/data/adb/ksu/.dynamic_sign"
#define MAX_MANAGERS 2
static struct manager_info active_managers[MAX_MANAGERS];
static DEFINE_SPINLOCK(managers_lock);
static struct dynamic_sign_config dynamic_sign = {
.size = 0x300,
.hash = "0000000000000000000000000000000000000000000000000000000000000000",
.is_set = 0
};
static DEFINE_SPINLOCK(dynamic_sign_lock);
static struct work_struct ksu_save_dynamic_sign_work;
static struct work_struct ksu_load_dynamic_sign_work;
static struct work_struct ksu_clear_dynamic_sign_work;
static inline bool is_dynamic_sign_enabled(void)
{
unsigned long flags;
bool enabled;
spin_lock_irqsave(&dynamic_sign_lock, flags);
enabled = dynamic_sign.is_set;
spin_unlock_irqrestore(&dynamic_sign_lock, flags);
return enabled;
}
void ksu_add_manager(uid_t uid, int signature_index)
{
unsigned long flags;
int i;
if (!is_dynamic_sign_enabled()) {
pr_info("Dynamic sign not enabled, skipping multi-manager add\n");
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].signature_index = signature_index;
spin_unlock_irqrestore(&managers_lock, flags);
pr_info("Updated manager uid=%d, signature_index=%d\n", uid, signature_index);
return;
}
}
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 (!is_dynamic_sign_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 (!is_dynamic_sign_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;
if (ksu_manager_uid != KSU_INVALID_UID && uid == ksu_manager_uid) {
return 1;
}
if (!is_dynamic_sign_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_all_managers(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 manager uid=%d due to dynamic_sign disable\n",
active_managers[i].uid);
active_managers[i].is_active = false;
}
}
spin_unlock_irqrestore(&managers_lock, flags);
}
static void do_save_dynamic_sign(struct work_struct *work)
{
u32 magic = DYNAMIC_SIGN_FILE_MAGIC;
u32 version = DYNAMIC_SIGN_FILE_VERSION;
struct dynamic_sign_config config_to_save;
loff_t off = 0;
unsigned long flags;
struct file *fp;
spin_lock_irqsave(&dynamic_sign_lock, flags);
config_to_save = dynamic_sign;
spin_unlock_irqrestore(&dynamic_sign_lock, flags);
if (!config_to_save.is_set) {
pr_info("Dynamic sign config not set, skipping save\n");
return;
}
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_SIGN, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) {
pr_err("save_dynamic_sign create file failed: %ld\n", PTR_ERR(fp));
return;
}
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
pr_err("save_dynamic_sign write magic failed.\n");
goto exit;
}
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
pr_err("save_dynamic_sign write version failed.\n");
goto exit;
}
if (ksu_kernel_write_compat(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) {
pr_err("save_dynamic_sign write config failed.\n");
goto exit;
}
pr_info("Dynamic sign config saved successfully\n");
exit:
filp_close(fp, 0);
}
// Loading dynamic signatures from persistent storage
static void do_load_dynamic_sign(struct work_struct *work)
{
loff_t off = 0;
ssize_t ret = 0;
struct file *fp = NULL;
u32 magic;
u32 version;
struct dynamic_sign_config loaded_config;
unsigned long flags;
int i;
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_SIGN, O_RDONLY, 0);
if (IS_ERR(fp)) {
if (PTR_ERR(fp) == -ENOENT) {
pr_info("No saved dynamic sign config found\n");
} else {
pr_err("load_dynamic_sign open file failed: %ld\n", PTR_ERR(fp));
}
return;
}
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
magic != DYNAMIC_SIGN_FILE_MAGIC) {
pr_err("dynamic sign file invalid magic: %x!\n", magic);
goto exit;
}
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
pr_err("dynamic sign read version failed\n");
goto exit;
}
pr_info("dynamic sign file version: %d\n", version);
ret = ksu_kernel_read_compat(fp, &loaded_config, sizeof(loaded_config), &off);
if (ret <= 0) {
pr_info("load_dynamic_sign read err: %zd\n", ret);
goto exit;
}
if (ret != sizeof(loaded_config)) {
pr_err("load_dynamic_sign 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;
}
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_sign_lock, flags);
dynamic_sign = loaded_config;
spin_unlock_irqrestore(&dynamic_sign_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_sign(void)
{
return ksu_queue_work(&ksu_save_dynamic_sign_work);
}
// Clear dynamic sign config file using the same method as do_save_dynamic_sign
static void do_clear_dynamic_sign_file(struct work_struct *work)
{
loff_t off = 0;
struct file *fp;
char zero_buffer[512];
memset(zero_buffer, 0, sizeof(zero_buffer));
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_SIGN, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) {
pr_err("clear_dynamic_sign create file failed: %ld\n", PTR_ERR(fp));
return;
}
// Write null bytes to overwrite the file content
if (ksu_kernel_write_compat(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) {
pr_err("clear_dynamic_sign write null bytes failed.\n");
} else {
pr_info("Dynamic sign config file cleared successfully\n");
}
filp_close(fp, 0);
}
static bool clear_dynamic_sign_file(void)
{
return ksu_queue_work(&ksu_clear_dynamic_sign_work);
}
int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config)
{
unsigned long flags;
int ret = 0;
int i;
if (!config) {
return -EINVAL;
}
switch (config->operation) {
case DYNAMIC_SIGN_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;
}
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_sign_lock, flags);
dynamic_sign.size = config->size;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
strscpy(dynamic_sign.hash, config->hash, sizeof(dynamic_sign.hash));
#else
strlcpy(dynamic_sign.hash, config->hash, sizeof(dynamic_sign.hash));
#endif
dynamic_sign.is_set = 1;
spin_unlock_irqrestore(&dynamic_sign_lock, flags);
persistent_dynamic_sign();
pr_info("dynamic sign updated: size=0x%x, hash=%.16s... (multi-manager enabled)\n",
config->size, config->hash);
break;
case DYNAMIC_SIGN_OP_GET:
// Getting Dynamic Signatures
spin_lock_irqsave(&dynamic_sign_lock, flags);
if (dynamic_sign.is_set) {
config->size = dynamic_sign.size;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
strscpy(config->hash, dynamic_sign.hash, sizeof(config->hash));
#else
strlcpy(config->hash, dynamic_sign.hash, sizeof(config->hash));
#endif
ret = 0;
} else {
ret = -ENODATA;
}
spin_unlock_irqrestore(&dynamic_sign_lock, flags);
break;
case DYNAMIC_SIGN_OP_CLEAR:
// Clearing dynamic signatures
spin_lock_irqsave(&dynamic_sign_lock, flags);
dynamic_sign.size = 0x300;
strcpy(dynamic_sign.hash, "0000000000000000000000000000000000000000000000000000000000000000");
dynamic_sign.is_set = 0;
spin_unlock_irqrestore(&dynamic_sign_lock, flags);
clear_all_managers();
// Clear file using the same method as save
clear_dynamic_sign_file();
pr_info("Dynamic sign config cleared (multi-manager disabled)\n");
break;
default:
pr_err("Invalid dynamic sign operation: %d\n", config->operation);
return -EINVAL;
}
return ret;
}
bool ksu_load_dynamic_sign(void)
{
return ksu_queue_work(&ksu_load_dynamic_sign_work);
}
void ksu_dynamic_sign_init(void)
{
int i;
INIT_WORK(&ksu_save_dynamic_sign_work, do_save_dynamic_sign);
INIT_WORK(&ksu_load_dynamic_sign_work, do_load_dynamic_sign);
INIT_WORK(&ksu_clear_dynamic_sign_work, do_clear_dynamic_sign_file);
for (i = 0; i < MAX_MANAGERS; i++) {
active_managers[i].is_active = false;
}
pr_info("Dynamic sign initialized with conditional multi-manager support\n");
}
void ksu_dynamic_sign_exit(void)
{
clear_all_managers();
do_save_dynamic_sign(NULL);
pr_info("Dynamic sign exited with persistent storage\n");
}
// Get active managers for multi-manager APKs
int ksu_get_active_managers(struct manager_list_info *info)
{
unsigned long flags;
int i, count = 0;
if (!info) {
return -EINVAL;
}
if (ksu_manager_uid != KSU_INVALID_UID && count < 2) {
info->managers[count].uid = ksu_manager_uid;
info->managers[count].signature_index = 1;
count++;
}
if (is_dynamic_sign_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;
}
struct sdesc {
struct shash_desc shash;
char ctx[];
};
static apk_sign_key_t apk_sign_keys[] = {
{ EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO }, // SukiSU
#ifdef EXPECTED_SIZE
{ EXPECTED_SIZE, EXPECTED_HASH }, // Custom
#endif
static struct apk_sign_key {
unsigned size;
const char *sha256;
} apk_sign_keys[] = {
{EXPECTED_SIZE, EXPECTED_HASH},
{EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // ShirkNeko/SukiSU
{EXPECTED_SIZE_OTHER, EXPECTED_HASH_OTHER}, // Dynamic Sign
};
static struct sdesc *init_sdesc(struct crypto_shash *alg)
@@ -38,7 +501,7 @@ static struct sdesc *init_sdesc(struct crypto_shash *alg)
int size;
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
sdesc = kzalloc(size, GFP_KERNEL);
sdesc = kmalloc(size, GFP_KERNEL);
if (!sdesc)
return ERR_PTR(-ENOMEM);
sdesc->shash.tfm = alg;
@@ -79,121 +542,6 @@ static int ksu_sha256(const unsigned char *data, unsigned int datalen,
return ret;
}
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)
{
struct dynamic_sign_key current_dynamic_key = dynamic_sign;
if (ksu_get_dynamic_manager_config(&current_dynamic_key.size, &current_dynamic_key.hash)) {
pr_debug("Using dynamic manager config: size=0x%x, hash=%.16s...\n",
current_dynamic_key.size, current_dynamic_key.hash);
}
if (size4 != current_dynamic_key.size) {
return false;
}
#define CERT_MAX_LENGTH 1024
char cert[CERT_MAX_LENGTH];
if (size4 > CERT_MAX_LENGTH) {
pr_info("cert length overlimit\n");
return false;
}
ksu_kernel_read_compat(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: 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;
}
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
int *matched_index)
{
int i;
apk_sign_key_t sign_key;
bool signature_valid = false;
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
*offset += 0x4 * 3;
ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length
*pos += *size4;
*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 (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;
}
ksu_kernel_read_compat(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 {
uint32_t signature;
uint16_t version;
@@ -247,15 +595,87 @@ static bool has_v1_signature_file(struct file *fp)
return false;
}
static __always_inline bool
check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
// Generic Signature Block Verification
static int verify_signature_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, int *matched_index)
{
int i;
struct apk_sign_key sign_key;
bool signature_valid = false;
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
*offset += 0x4 * 3;
ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length
*pos += *size4;
*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;
for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
sign_key = apk_sign_keys[i];
if (i == 2) {
unsigned long flags;
spin_lock_irqsave(&dynamic_sign_lock, flags);
if (dynamic_sign.is_set) {
sign_key.size = dynamic_sign.size;
sign_key.sha256 = dynamic_sign.hash;
}
spin_unlock_irqrestore(&dynamic_sign_lock, flags);
}
if (*size4 != sign_key.size)
continue;
#define CERT_MAX_LENGTH 1024
char cert[CERT_MAX_LENGTH];
if (*size4 > CERT_MAX_LENGTH) {
pr_info("cert length overlimit\n");
continue;
}
loff_t cert_pos = *pos;
ksu_kernel_read_compat(fp, cert, *size4, &cert_pos);
unsigned char digest[SHA256_DIGEST_SIZE];
if (IS_ERR(ksu_sha256(cert, *size4, digest))) {
pr_info("sha256 error\n");
continue;
}
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;
}
}
*offset += *size4;
*pos += *size4;
return signature_valid ? 1 : 0;
}
// Generic APK signature parsing
static int parse_apk_signature(char *path, bool check_multi_manager, int *signature_index)
{
unsigned char buffer[0x11] = { 0 };
u32 size4;
u64 size8, size_of_block;
loff_t pos;
bool v2_signing_valid = false;
int v2_signing_blocks = 0;
bool v3_signing_exist = false;
@@ -265,11 +685,11 @@ check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
if (IS_ERR(fp)) {
pr_err("open %s error.\n", path);
return false;
return -1;
}
// If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
// If you want to check for multi-manager APK signing, but dynamic signing is not enabled, skip the
if (check_multi_manager && !is_dynamic_sign_enabled()) {
filp_close(fp, 0);
return 0;
}
@@ -312,12 +732,12 @@ check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
goto clean;
}
// Parsing the signature block
int loop_count = 0;
while (loop_count++ < 10) {
uint32_t id;
uint32_t offset;
ksu_kernel_read_compat(fp, &size8, 0x8,
&pos); // sequence length
ksu_kernel_read_compat(fp, &size8, 0x8, &pos); // sequence length
if (size8 == size_of_block) {
break;
}
@@ -325,9 +745,8 @@ check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
offset = 4;
if (id == 0x7109871au) {
v2_signing_blocks++;
bool result = check_block(fp, &size4, &pos, &offset,
&matched_index);
if (result) {
int result = verify_signature_block(fp, &size4, &pos, &offset, &matched_index);
if (result == 1) {
v2_signing_valid = true;
}
} else if (id == 0xf05368c0u) {
@@ -346,20 +765,21 @@ check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
if (v2_signing_blocks != 1) {
#ifdef CONFIG_KSU_DEBUG
pr_err("Unexpected v2 signature count: %d\n",
v2_signing_blocks);
pr_err("Unexpected v2 signature count: %d\n", v2_signing_blocks);
#endif
v2_signing_valid = false;
}
// Check v1 signatures
if (v2_signing_valid) {
int has_v1_signing = has_v1_signature_file(fp);
bool has_v1_signing = has_v1_signature_file(fp);
if (has_v1_signing) {
pr_err("Unexpected v1 signature scheme found!\n");
filp_close(fp, 0);
return false;
return -1;
}
}
clean:
filp_close(fp, 0);
@@ -367,7 +787,7 @@ clean:
#ifdef CONFIG_KSU_DEBUG
pr_err("Unexpected v3 signature scheme found!\n");
#endif
return false;
return -1;
}
if (v2_signing_valid) {
@@ -376,19 +796,37 @@ clean:
}
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;
// 1: ShirkNeko/SukiSU, 2: Dynamic Sign
if (matched_index == 1 || matched_index == 2) {
pr_info("Multi-manager APK detected (dynamic_sign enabled): signature_index=%d\n", matched_index);
return 1;
}
return false;
return 0;
} else {
// Common manager check: any valid signature will do
return true;
return 1;
}
}
return false;
return 0;
}
bool ksu_is_multi_manager_apk(char *path, int *signature_index)
{
int result = parse_apk_signature(path, true, signature_index);
return result == 1;
}
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset)
{
int result = verify_signature_block(fp, size4, pos, offset, NULL);
return result == 1;
}
static __always_inline bool check_v2_signature(char *path)
{
int result = parse_apk_signature(path, false, NULL);
return result == 1;
}
#ifdef CONFIG_KSU_DEBUG
@@ -417,10 +855,5 @@ module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
bool is_manager_apk(char *path)
{
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);
return check_v2_signature(path);
}

View File

@@ -3,9 +3,32 @@
#include <linux/types.h>
#include "ksu.h"
#include "manager.h"
bool is_manager_apk(char *path);
bool is_dynamic_manager_apk(char *path, int *signature_index);
struct dynamic_sign_config {
unsigned int size;
char hash[65];
int is_set;
};
struct manager_info {
uid_t uid;
int signature_index;
bool is_active;
};
bool ksu_is_multi_manager_apk(char *path, int *signature_index);
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);
int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config);
void ksu_dynamic_sign_init(void);
void ksu_dynamic_sign_exit(void);
bool ksu_load_dynamic_sign(void);
#endif

View File

@@ -1,313 +0,0 @@
#include <linux/version.h>
#include <linux/capability.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/fdtable.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/proc_ns.h>
#include <linux/pid.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
#include <linux/sched/signal.h> // signal_struct
#include <linux/sched/task.h>
#endif
#include <linux/sched.h>
#include <linux/seccomp.h>
#include <linux/thread_info.h>
#include <linux/uidgid.h>
#include <linux/syscalls.h>
#include "allowlist.h"
#include "app_profile.h"
#include "arch.h"
#include "kernel_compat.h"
#include "klog.h" // IWYU pragma: keep
#include "selinux/selinux.h"
#include "syscall_hook_manager.h"
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
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;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
group_info->gid[i] = kgid;
#else
GROUP_AT(group_info, i) = kgid;
#endif
}
groups_sort(group_info);
set_groups(cred, group_info);
put_group_info(group_info);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
extern long SYS_SETNS_SYMBOL(const struct pt_regs *regs);
static long ksu_sys_setns(int fd, int flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
PT_REGS_PARM1(&regs) = fd;
PT_REGS_PARM2(&regs) = flags;
// TODO: arm support
#if (defined(__aarch64__) || defined(__x86_64__))
return SYS_SETNS_SYMBOL(&regs);
#else
return -ENOSYS;
#endif
}
#else
static long ksu_sys_setns(int fd, int flags)
{
return sys_setns(fd, flags);
}
__weak int ksys_unshare(unsigned long unshare_flags)
{
return sys_unshare(unshare_flags);
}
#endif
static void setup_mount_namespace(int32_t ns_mode)
{
pr_info("setup mount namespace for pid: %d\n", current->pid);
if (ns_mode == 0) {
pr_info("mount namespace mode: inherit\n");
// do nothing
return;
}
if (ns_mode > 2) {
pr_warn("unknown mount namespace mode: %d\n", ns_mode);
return;
}
const struct cred *old_cred = NULL;
struct cred *new_cred = NULL;
if (!(capable(CAP_SYS_ADMIN) && capable(CAP_SYS_CHROOT))) {
pr_info("process dont have CAP_SYS_ADMIN or CAP_SYS_CHROOT, adding it temporarily.\n");
new_cred = prepare_creds();
if (!new_cred) {
pr_warn("failed to prepare new credentials\n");
return;
}
cap_raise(new_cred->cap_effective, CAP_SYS_ADMIN);
cap_raise(new_cred->cap_effective, CAP_SYS_CHROOT);
old_cred = override_creds(new_cred);
}
if (ns_mode == 1) {
pr_info("mount namespace mode: global\n");
struct file *ns_file;
struct path ns_path;
struct task_struct *pid1_task = NULL;
struct pid *pid_struct = NULL;
rcu_read_lock();
// find init
pid_struct = find_pid_ns(1, &init_pid_ns);
if (unlikely(!pid_struct)) {
rcu_read_unlock();
pr_warn("failed to find pid_struct for PID 1\n");
goto try_drop_caps;
}
pid1_task = get_pid_task(pid_struct, PIDTYPE_PID);
rcu_read_unlock();
if (unlikely(!pid1_task)) {
pr_warn("failed to get task_struct for PID 1\n");
goto try_drop_caps;
}
// mabe you can use &init_task for first stage init?
long ret = ns_get_path(&ns_path, pid1_task, &mntns_operations);
put_task_struct(pid1_task);
if (ret) {
pr_warn("failed to get path for init's mount namespace: %ld\n",
ret);
goto try_drop_caps;
}
ns_file = dentry_open(&ns_path, O_RDONLY, current_cred());
path_put(&ns_path);
if (IS_ERR(ns_file)) {
pr_warn("failed to open file for init's mount namespace: %ld\n",
PTR_ERR(ns_file));
goto try_drop_caps;
}
int fd = get_unused_fd_flags(O_CLOEXEC);
if (fd < 0) {
pr_warn("failed to get an unused fd: %d\n", fd);
fput(ns_file);
goto try_drop_caps;
}
fd_install(fd, ns_file);
pr_info("calling sys_setns with fd : %d\n", fd);
ret = ksu_sys_setns(fd, CLONE_NEWNS);
if (ret) {
pr_warn("sys_setns failed: %ld\n", ret);
}
do_close_fd(fd);
}
if (ns_mode == 2) {
long ret;
pr_info("mount namespace mode: independent\n");
ret = ksys_unshare(CLONE_NEWNS);
if (ret) {
pr_warn("call ksys_unshare failed: %ld\n", ret);
}
}
try_drop_caps:
if (old_cred) {
pr_info("dropping temporarily capability.\n");
revert_creds(old_cred);
put_cred(new_cred);
}
return;
}
// RKSU: Use it wisely, not static.
void disable_seccomp(struct task_struct *tsk)
{
if (unlikely(!tsk))
return;
assert_spin_locked(&tsk->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
tsk->seccomp.mode = 0;
if (tsk->seccomp.filter) {
// 5.9+ have filter_count, but optional.
#ifdef KSU_OPTIONAL_SECCOMP_FILTER_CNT
atomic_set(&tsk->seccomp.filter_count, 0);
#endif
// some old kernel backport seccomp_filter_release..
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) && \
defined(KSU_OPTIONAL_SECCOMP_FILTER_RELEASE)
seccomp_filter_release(tsk);
#else
// never, ever call seccomp_filter_release on 6.10+ (no effect)
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) && \
LINUX_VERSION_CODE < KERNEL_VERSION(6, 10, 0))
seccomp_filter_release(tsk);
#else
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
put_seccomp_filter(tsk);
#endif
tsk->seccomp.filter = NULL;
#endif
#endif
}
#endif
}
void escape_with_root_profile(void)
{
struct cred *cred;
// a bit useless, but we just want less ifdefs
struct task_struct *p = current;
if (current_euid().val == 0) {
pr_warn("Already root, don't escape!\n");
return;
}
cred = prepare_creds();
if (!cred) {
pr_warn("prepare_creds failed!\n");
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(&p->sighand->siglock);
disable_seccomp(p);
spin_unlock_irq(&p->sighand->siglock);
setup_selinux(profile->selinux_domain);
setup_mount_namespace(profile->namespaces);
#ifdef KSU_SHOULD_USE_NEW_TP
struct task_struct *t;
for_each_thread (p, t) {
ksu_set_task_tracepoint_flag(t);
}
#endif
}

View File

@@ -1,66 +0,0 @@
#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);
#endif

View File

@@ -18,17 +18,11 @@
#define __PT_SP_REG sp
#define __PT_IP_REG pc
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
#define REBOOT_SYMBOL "__arm64_sys_reboot"
#define PRCTL_SYMBOL "__arm64_sys_prctl"
#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_SETNS_SYMBOL __arm64_sys_setns
#else
#define REBOOT_SYMBOL "sys_reboot"
#define SYS_READ_SYMBOL "sys_read"
#define SYS_EXECVE_SYMBOL "sys_execve"
#define SYS_SETNS_SYMBOL sys_setns
#endif
#elif defined(__x86_64__)
@@ -45,24 +39,15 @@
#define __PT_RC_REG ax
#define __PT_SP_REG sp
#define __PT_IP_REG ip
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
#define REBOOT_SYMBOL "__x64_sys_reboot"
#define PRCTL_SYMBOL "__x64_sys_prctl"
#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_SETNS_SYMBOL __x64_sys_setns
#else
#define REBOOT_SYMBOL "sys_reboot"
#define SYS_READ_SYMBOL "sys_read"
#define SYS_EXECVE_SYMBOL "sys_execve"
#define SYS_SETNS_SYMBOL sys_setns
#endif
#else
#ifdef KSU_SHOULD_USE_NEW_TP
#error "Unsupported arch"
#endif
#endif
/* allow some architecutres to override `struct pt_regs` */
#ifndef __PT_REGS_CAST
@@ -82,10 +67,6 @@
#define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG)
#define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
#define PT_REAL_REGS(regs) ((struct pt_regs *)PT_REGS_PARM1(regs))
#else
#define PT_REAL_REGS(regs) ((regs))
#endif
#endif

1003
kernel/core_hook.c Normal file

File diff suppressed because it is too large Load Diff

10
kernel/core_hook.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef __KSU_H_KSU_CORE
#define __KSU_H_KSU_CORE
#include <linux/init.h>
#include "apk_sign.h"
void __init ksu_core_init(void);
void ksu_core_exit(void);
#endif

View File

@@ -1,505 +0,0 @@
#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 "kernel_compat.h"
#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 = ksu_filp_open_compat(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 (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
pr_err("save_dynamic_manager write magic failed.\n");
goto exit;
}
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
pr_err("save_dynamic_manager write version failed.\n");
goto exit;
}
if (ksu_kernel_write_compat(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 = ksu_filp_open_compat(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 (ksu_kernel_read_compat(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 (ksu_kernel_read_compat(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 = ksu_kernel_read_compat(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 = ksu_filp_open_compat(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 (ksu_kernel_write_compat(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;
}

View File

@@ -1,51 +0,0 @@
#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

View File

@@ -1,176 +0,0 @@
#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");
}

View File

@@ -1,36 +0,0 @@
#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_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

View File

@@ -1,402 +0,0 @@
#include <linux/export.h>
#include <linux/anon_inodes.h>
#include <linux/aio.h> // kernel 3.18
#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);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
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);
}
#endif
#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);
}
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
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) && (LINUX_VERSION_CODE > KERNEL_VERSION(3, 11, 0) || defined(KSU_HAS_ITERATE_DIR))
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
// int (*readdir) (struct file *, void *, filldir_t);
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) && !defined(KSU_HAS_ITERATE_DIR)
static int ksu_wrapper_readdir(struct file *fp, void *ptr, filldir_t filler) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->readdir(orig, ptr, filler);
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
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);
}
#endif
// typedef unsigned __bitwise __poll_t;
static unsigned __bitwise 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;
}
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) // int (*setlease)(struct file *, long, struct file_lock **, void **);
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;
}
#else // int (*setlease)(struct file *, long, struct file_lock **);
static int ksu_wrapper_setlease(struct file *fp, long arg1, struct file_lock **fl) {
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);
}
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;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
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);
}
}
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
static int 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);
}
return -EINVAL;
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/read_write.c;l=1593-1606;drc=398da7defe218d3e51b0f3bdff75147e28125b60
static ssize_t ksu_wrapper_copy_file_range(struct file *file_in, loff_t pos_in, struct file *file_out,
loff_t pos_out, size_t len, unsigned int flags) {
struct ksu_file_wrapper* data = file_out->private_data;
struct file* orig = data->orig;
return orig->f_op->copy_file_range(file_in, pos_in, orig, pos_out, len, flags);
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
// no REMAP_FILE_DEDUP: use file_in
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/read_write.c;l=1598-1599;drc=398da7defe218d3e51b0f3bdff75147e28125b60
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/remap_range.c;l=403-404;drc=398da7defe218d3e51b0f3bdff75147e28125b60
// REMAP_FILE_DEDUP: use file_out
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/remap_range.c;l=483-484;drc=398da7defe218d3e51b0f3bdff75147e28125b60
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) {
if (remap_flags & REMAP_FILE_DEDUP) {
struct ksu_file_wrapper* data = file_out->private_data;
struct file* orig = data->orig;
return orig->f_op->remap_file_range(file_in, pos_in, orig, pos_out, len, remap_flags);
} else {
struct ksu_file_wrapper* data = file_in->private_data;
struct file* orig = data->orig;
return orig->f_op->remap_file_range(orig, pos_in, file_out, pos_out, len, remap_flags);
}
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
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;
}
#endif
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;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
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;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
p->ops.iopoll = fp->f_op->iopoll ? ksu_wrapper_iopoll : NULL;
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) && (LINUX_VERSION_CODE > KERNEL_VERSION(3, 11, 0) || defined(KSU_HAS_ITERATE_DIR))
p->ops.iterate = fp->f_op->iterate ? ksu_wrapper_iterate : NULL;
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) && !defined(KSU_HAS_ITERATE_DIR)
p->ops.readdir = fp->f_op->readdir ? ksu_wrapper_readdir : NULL;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
p->ops.iterate_shared = fp->f_op->iterate_shared ? ksu_wrapper_iterate_shared : NULL;
#endif
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;
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
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;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
p->ops.show_fdinfo = fp->f_op->show_fdinfo ? ksu_wrapper_show_fdinfo : NULL;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
p->ops.copy_file_range = fp->f_op->copy_file_range ? ksu_wrapper_copy_file_range : NULL;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
p->ops.remap_file_range = fp->f_op->remap_file_range ? ksu_wrapper_remap_file_range : NULL;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
p->ops.fadvise = fp->f_op->fadvise ? ksu_wrapper_fadvise : NULL;
#endif
#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);
}

View File

@@ -1,15 +0,0 @@
#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

28
kernel/include/ksu_hook.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef __KSU_H_KSHOOK
#define __KSU_H_KSHOOK
#include <linux/fs.h>
#include <linux/types.h>
// For sucompat
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
int *flags);
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
// For ksud
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
size_t *count_ptr, loff_t **pos);
// For ksud and sucompat
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
void *envp, int *flags);
// For volume button
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
int *value);
#endif

View File

@@ -1,149 +1,88 @@
#include <linux/version.h>
#include <linux/fs.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
#include <linux/nsproxy.h>
#include <linux/sched/task.h>
#else
#include <linux/sched.h>
#endif
#include <linux/uaccess.h>
#include "klog.h" // IWYU pragma: keep
#include "kernel_compat.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
#include <linux/key.h>
#include <linux/errno.h>
#include <linux/cred.h>
#include <linux/lsm_hooks.h>
extern struct task_struct init_task;
extern int install_session_keyring_to_cred(struct cred *, struct key *);
struct key *init_session_keyring = NULL;
// 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 int install_session_keyring(struct key *keyring)
static void ksu_save_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
{
struct cred *new;
int ret;
new = prepare_creds();
if (!new)
return -ENOMEM;
ret = install_session_keyring_to_cred(new, keyring);
if (ret < 0) {
abort_creds(new);
return ret;
}
return commit_creds(new);
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);
}
#endif
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
if (init_session_keyring != NULL && !current_cred()->session_keyring &&
(current->flags & PF_WQ_WORKER)) {
pr_info("installing init session keyring for older kernel\n");
install_session_keyring(init_session_keyring);
// 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);
}
#endif
return filp_open(filename, flags, mode);
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)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \
defined(KSU_OPTIONAL_KERNEL_READ)
return kernel_read(p, buf, count, pos);
#else
loff_t offset = pos ? *pos : 0;
ssize_t result = kernel_read(p, offset, (char *)buf, count);
if (pos && result > 0) {
*pos = offset + result;
}
return result;
#endif
}
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
loff_t *pos)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \
defined(KSU_OPTIONAL_KERNEL_WRITE)
return kernel_write(p, buf, count, pos);
#else
loff_t offset = pos ? *pos : 0;
ssize_t result = kernel_write(p, buf, count, offset);
if (pos && result > 0) {
*pos = offset + result;
}
return result;
#endif
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || \
defined(KSU_OPTIONAL_STRNCPY)
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
long count)
{
return strncpy_from_user_nofault(dst, unsafe_addr, count);
}
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
long count)
{
return strncpy_from_unsafe_user(dst, unsafe_addr, count);
}
#else
// Copied from: https://elixir.bootlin.com/linux/v4.9.337/source/mm/maccess.c#L201
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
long count)
{
mm_segment_t old_fs = get_fs();
long ret;
if (unlikely(count <= 0))
return 0;
set_fs(USER_DS);
pagefault_disable();
ret = strncpy_from_user(dst, unsafe_addr, count);
pagefault_enable();
set_fs(old_fs);
if (ret >= count) {
ret = count;
dst[ret - 1] = '\0';
} else if (ret > 0) {
ret++;
}
return ret;
}
#endif
long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
return copy_from_user_nofault(dst, src, size);
#else
// https://elixir.bootlin.com/linux/v5.8/source/mm/maccess.c#L205
long ret = -EFAULT;
mm_segment_t old_fs = get_fs();
set_fs(USER_DS);
// tweaked to use ksu_access_ok
if (ksu_access_ok(src, size)) {
pagefault_disable();
ret = __copy_from_user_inatomic(dst, src, size);
pagefault_enable();
}
set_fs(old_fs);
if (ret)
return -EFAULT;
return 0;
#endif
}

View File

@@ -3,8 +3,6 @@
#include <linux/fs.h>
#include <linux/version.h>
#include <linux/task_work.h>
#include <linux/fdtable.h>
#include "ss/policydb.h"
#include "linux/key.h"
@@ -22,69 +20,16 @@
#endif
#endif
// Checks for UH, KDP and RKP
#ifdef SAMSUNG_UH_DRIVER_EXIST
#if defined(CONFIG_UH) || defined(CONFIG_KDP) || defined(CONFIG_RKP)
#error "CONFIG_UH, CONFIG_KDP and CONFIG_RKP is enabled! Please disable or remove it before compile a kernel with KernelSU!"
#endif
#endif
extern long ksu_strncpy_from_user_nofault(char *dst,
const void __user *unsafe_addr,
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);
extern long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size);
/*
* ksu_copy_from_user_retry
* try nofault copy first, if it fails, try with plain
* paramters are the same as copy_from_user
* 0 = success
*/
static long ksu_copy_from_user_retry(void *to,
const void __user *from, unsigned long count)
{
long ret = ksu_copy_from_user_nofault(to, from, count);
if (likely(!ret))
return ret;
// we faulted! fallback to slow path
return copy_from_user(to, from, count);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
extern struct key *init_session_keyring;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
#define ksu_access_ok(addr, size) access_ok(addr, size)
#else
#define ksu_access_ok(addr, size) access_ok(VERIFY_READ, addr, size)
#endif
// Linux >= 5.7
// task_work_add (struct, struct, enum)
// Linux pre-5.7
// task_work_add (struct, struct, bool)
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0)
#ifndef TWA_RESUME
#define TWA_RESUME true
#endif
#endif
static inline int do_close_fd(unsigned int fd)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
return close_fd(fd);
#else
return __close_fd(current->files, fd);
#endif
}
#endif

View File

@@ -1,206 +0,0 @@
#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>
#ifndef KSU_HAS_PATH_UMOUNT
#include <linux/syscalls.h>
#endif
#include "kernel_umount.h"
#include "klog.h" // IWYU pragma: keep
#include "allowlist.h"
#include "kernel_compat.h"
#include "selinux/selinux.h"
#include "feature.h"
#include "ksud.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,
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || \
defined(KSU_HAS_PATH_UMOUNT)
extern int path_umount(struct path *path, int flags);
static void ksu_umount_mnt(const char *__never_use_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);
}
}
#else
static void ksu_sys_umount(const char *mnt, int flags)
{
char __user *usermnt = (char __user *)mnt;
mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
ksys_umount(usermnt, flags);
#else
sys_umount(usermnt, flags); // cuz asmlinkage long sys##name
#endif
set_fs(old_fs);
}
#define ksu_umount_mnt(mnt, __unused, flags) \
({ \
path_put(__unused); \
ksu_sys_umount(mnt, flags); \
})
#endif
static 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(mnt, &path, flags);
}
static inline void do_umount_work(void)
{
struct mount_entry *entry;
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);
}
}
#ifdef KSU_SHOULD_USE_NEW_TP
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);
}
down_read(&mount_list_lock);
do_umount_work();
up_read(&mount_list_lock);
if (saved)
revert_creds(saved);
if (tw->old_cred)
put_cred(tw->old_cred);
kfree(tw);
}
#endif
int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
{
// 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 (!ksu_kernel_umount_enabled) {
return 0;
}
// FIXME: isolated process which directly forks from zygote is not handled
if (!is_appuid(new_uid)) {
return 0;
}
if (!ksu_uid_should_umount(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!
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;
}
// umount the target mnt
pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid);
#ifdef KSU_SHOULD_USE_NEW_TP
struct umount_tw *tw;
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");
}
#else
// Using task work for non-kp context is expansive?
down_read(&mount_list_lock);
do_umount_work();
up_read(&mount_list_lock);
#endif
return 0;
}
void ksu_kernel_umount_init(void)
{
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);
}

View File

@@ -1,23 +0,0 @@
#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);
// 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

View File

@@ -4,6 +4,3 @@ obj-y += super_access.o
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 += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h

View File

@@ -29,73 +29,78 @@
#include "../allowlist.h"
#include "../manager.h"
unsigned long sukisu_compact_find_symbol(const char *name);
unsigned long sukisu_compact_find_symbol(const char* name);
// ======================================================================
// 兼容函数 for KPM
static int sukisu_is_su_allow_uid(uid_t uid)
{
static
int sukisu_is_su_allow_uid(uid_t uid) {
return ksu_is_allow_uid(uid) ? 1 : 0;
}
static int sukisu_get_ap_mod_exclude(uid_t uid)
{
static
int sukisu_get_ap_mod_exclude(uid_t uid) {
// Not supported
return 0;
}
static int sukisu_is_uid_should_umount(uid_t uid)
{
static
int sukisu_is_uid_should_umount(uid_t uid) {
return ksu_uid_should_umount(uid) ? 1 : 0;
}
static int sukisu_is_current_uid_manager()
{
static
int sukisu_is_current_uid_manager() {
return is_manager();
}
static uid_t sukisu_get_manager_uid()
{
static
uid_t sukisu_get_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 {
const char *symbol_name;
void *addr;
const char* symbol_name;
void* addr;
};
static struct CompactAddressSymbol address_symbol[] = {
static struct CompactAddressSymbol address_symbol [] = {
{ "kallsyms_lookup_name", &kallsyms_lookup_name },
{ "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 },
{ "get_ap_mod_exclude", &sukisu_get_ap_mod_exclude },
{ "is_uid_should_umount", &sukisu_is_uid_should_umount },
{ "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;
unsigned long addr;
// 先自己在地址表部分查出来
for (i = 0;
i < (sizeof(address_symbol) / sizeof(struct CompactAddressSymbol));
i++) {
struct CompactAddressSymbol *symbol = &address_symbol[i];
if (strcmp(name, symbol->symbol_name) == 0) {
return (unsigned long)symbol->addr;
for(i = 0; i < (sizeof(address_symbol) / sizeof(struct CompactAddressSymbol)); i++) {
struct CompactAddressSymbol* symbol = &address_symbol[i];
if(strcmp(name, symbol->symbol_name) == 0) {
return (unsigned long) symbol->addr;
}
}
// 通过内核来查
addr = kallsyms_lookup_name(name);
if (addr) {
if(addr) {
return addr;
}

View File

@@ -1,6 +1,6 @@
#ifndef ___SUKISU_KPM_COMPACT_H
#define ___SUKISU_KPM_COMPACT_H
unsigned long sukisu_compact_find_symbol(const char *name);
unsigned long sukisu_compact_find_symbol(const char* name);
#endif

View File

@@ -8,11 +8,13 @@
* 集成了 ELF 解析、内存布局、符号处理、重定位(支持 ARM64 重定位类型)
* 并参照KernelPatch的标准KPM格式实现加载和控制
*/
#include <linux/export.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/kernfs.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
#include <linux/elf.h>
@@ -21,26 +23,26 @@
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <asm/elf.h>
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/cacheflush.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/set_memory.h>
#include <linux/version.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <asm/insn.h>
#include <linux/kprobes.h>
#include <linux/stacktrace.h>
#include <linux/kallsyms.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) && defined(CONFIG_MODULES)
#include <linux/moduleloader.h>
#include <linux/moduleloader.h> // 需要启用 CONFIG_MODULES
#endif
#include "kpm.h"
#include "compact.h"
#include "../kernel_compat.h"
#define KPM_NAME_LEN 32
#define KPM_ARGS_LEN 1024
#ifndef NO_OPTIMIZE
#if defined(__GNUC__) && !defined(__clang__)
@@ -52,231 +54,131 @@
#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);
// ============================================================================================
__asm__ volatile("nop");
noinline
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);
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);
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);
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);
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);
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_version);
EXPORT_SYMBOL(sukisu_kpm_control);
noinline NO_OPTIMIZE void sukisu_kpm_version(char *buf, int bufferSize)
noinline
int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)
{
pr_info("kpm: Stub function called (sukisu_kpm_version). "
"buffer=%p\n", buf);
}
EXPORT_SYMBOL(sukisu_kpm_version);
if(arg2 == SUKISU_KPM_LOAD) {
char kernel_load_path[256] = { 0 };
char kernel_args_buffer[256] = { 0 };
noinline int sukisu_handle_kpm(unsigned long control_code, unsigned long arg1, unsigned long arg2,
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(arg3 == 0) {
return -1;
}
if (!ksu_access_ok(arg1, sizeof(kernel_load_path))) {
goto invalid_arg;
strncpy_from_user((char*)&kernel_load_path, (const char __user *)arg3, 255);
if(arg4 != 0) {
strncpy_from_user((char*)&kernel_args_buffer, (const char __user *)arg4, 255);
}
sukisu_kpm_load_module_path((const char*)&kernel_load_path, (const char*) &kernel_args_buffer, NULL, (void __user*) arg5);
} else if(arg2 == SUKISU_KPM_UNLOAD) {
char kernel_name_buffer[256] = { 0 };
if(arg3 == 0) {
return -1;
}
strncpy_from_user((char *)&kernel_load_path, (const char *)arg1, sizeof(kernel_load_path));
strncpy_from_user((char*)&kernel_name_buffer, (const char __user *)arg3, 255);
sukisu_kpm_unload_module((const char*) &kernel_name_buffer, NULL, (void __user*) arg5);
} else if(arg2 == SUKISU_KPM_NUM) {
sukisu_kpm_num((void __user*) arg5);
} else if(arg2 == SUKISU_KPM_INFO) {
char kernel_name_buffer[256] = { 0 };
if (arg2 != 0) {
if (!ksu_access_ok(arg2, sizeof(kernel_args_buffer))) {
goto invalid_arg;
if(arg3 == 0 || arg4 == 0) {
return -1;
}
strncpy_from_user((char *)&kernel_args_buffer, (const char *)arg2, sizeof(kernel_args_buffer));
strncpy_from_user((char*)&kernel_name_buffer, (const char __user *)arg3, 255);
sukisu_kpm_info((const char*) &kernel_name_buffer, (char __user*) arg4, (void __user*) arg5);
} else if(arg2 == SUKISU_KPM_LIST) {
sukisu_kpm_list((char __user*) arg3, (unsigned int) arg4, (void __user*) arg5);
} else if(arg2 == SUKISU_KPM_VERSION) {
sukisu_kpm_version((char __user*) arg3, (unsigned int) arg4, (void __user*) arg5);
} else if(arg2 == SUKISU_KPM_CONTROL) {
sukisu_kpm_control((char __user*) arg3, (char __user*) arg4, (void __user*) arg5);
}
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 (!ksu_access_ok(arg1, sizeof(kernel_name_buffer))) {
goto invalid_arg;
}
strncpy_from_user((char *)&kernel_name_buffer, (const char *)arg1, sizeof(kernel_name_buffer));
sukisu_kpm_unload_module((const char *)&kernel_name_buffer, NULL, &res);
} else if (control_code == SUKISU_KPM_NUM) {
sukisu_kpm_num(&res);
} else if (control_code == SUKISU_KPM_INFO) {
char kernel_name_buffer[256];
char buf[256];
int size;
if (arg1 == 0 || arg2 == 0) {
res = -EINVAL;
goto exit;
}
if (!ksu_access_ok(arg1, sizeof(kernel_name_buffer))) {
goto invalid_arg;
}
strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg1, sizeof(kernel_name_buffer));
sukisu_kpm_info((const char *)&kernel_name_buffer, (char *)&buf, sizeof(buf), &size);
if (!ksu_access_ok(arg2, size)) {
goto invalid_arg;
}
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 (!ksu_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 (!ksu_access_ok(arg1, sizeof(kpm_name))) {
goto invalid_arg;
}
if (!ksu_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;
invalid_arg:
pr_err("kpm: invalid pointer detected! arg1: %px arg2: %px\n", (void *)arg1, (void *)arg2);
res = -EFAULT;
goto exit;
}
int sukisu_is_kpm_control_code(unsigned long arg2) {
return (arg2 >= CMD_KPM_CONTROL && arg2 <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
}
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 do_kpm(void __user *arg)
{
struct ksu_kpm_cmd cmd;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("kpm: copy_from_user failed\n");
return -EFAULT;
}
if (!ksu_access_ok(cmd.control_code, sizeof(int))) {
pr_err("kpm: invalid control_code pointer %px\n", (void *)cmd.control_code);
return -EFAULT;
}
if (!ksu_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);
}

View File

@@ -1,70 +1,44 @@
#ifndef __SUKISU_KPM_H
#define __SUKISU_KPM_H
#ifndef ___SUKISU_KPM_H
#define ___SUKISU_KPM_H
#include <linux/types.h>
#include <linux/ioctl.h>
int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
int sukisu_is_kpm_control_code(unsigned long arg2);
struct ksu_kpm_cmd {
__aligned_u64 __user control_code;
__aligned_u64 __user arg1;
__aligned_u64 __user arg2;
__aligned_u64 __user result_code;
};
// KPM控制代码
#define CMD_KPM_CONTROL 28
#define CMD_KPM_CONTROL_MAX 35
int sukisu_handle_kpm(unsigned long control_code, unsigned long arg1, unsigned long arg2, unsigned long result_code);
int sukisu_is_kpm_control_code(unsigned long control_code);
int do_kpm(void __user *arg);
// 控制代码
#define KSU_IOCTL_KPM _IOC(_IOC_READ|_IOC_WRITE, 'K', 200, 0)
// prctl(xxx, 28, "PATH", "ARGS")
// success return 0, error return -N
#define SUKISU_KPM_LOAD 28
/* KPM Control Code */
#define CMD_KPM_CONTROL 1
#define CMD_KPM_CONTROL_MAX 10
// prctl(xxx, 29, "NAME")
// success return 0, error return -N
#define SUKISU_KPM_UNLOAD 29
/* Control Code */
/*
* prctl(xxx, 1, "PATH", "ARGS")
* success return 0, error return -N
*/
#define SUKISU_KPM_LOAD 1
// num = prctl(xxx, 30)
// error return -N
// success return +num or 0
#define SUKISU_KPM_NUM 30
/*
* prctl(xxx, 2, "NAME")
* success return 0, error return -N
*/
#define SUKISU_KPM_UNLOAD 2
// prctl(xxx, 31, Buffer, BufferSize)
// success return +out, error return -N
#define SUKISU_KPM_LIST 31
/*
* num = prctl(xxx, 3)
* error return -N
* success return +num or 0
*/
#define SUKISU_KPM_NUM 3
// prctl(xxx, 32, "NAME", Buffer[256])
// success return +out, error return -N
#define SUKISU_KPM_INFO 32
/*
* prctl(xxx, 4, Buffer, BufferSize)
* success return +out, error return -N
*/
#define SUKISU_KPM_LIST 4
// prctl(xxx, 33, "NAME", "ARGS")
// success return KPM's result value
// error return -N
#define SUKISU_KPM_CONTROL 33
/*
* prctl(xxx, 5, "NAME", Buffer[256])
* success return +out, error return -N
*/
#define SUKISU_KPM_INFO 5
/*
* 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
// prctl(xxx, 34, buffer, bufferSize)
// success return KPM's result value
// error return -N
#define SUKISU_KPM_VERSION 34
#endif

View File

@@ -31,34 +31,35 @@
// 结构体成员元数据
struct DynamicStructMember {
const char *name;
const char* name;
size_t size;
size_t offset;
};
// 结构体元数据(包含总大小)
struct DynamicStructInfo {
const char *name;
const char* name;
size_t count;
size_t total_size;
struct DynamicStructMember *members;
struct DynamicStructMember* members;
};
// 定义结构体元数据的宏(直接使用 struct 名称)
#define DYNAMIC_STRUCT_BEGIN(struct_name) \
static struct DynamicStructMember struct_name##_members[] = {
#define DEFINE_MEMBER(struct_name, member) \
{ .name = #member, \
.size = sizeof(((struct struct_name *)0)->member), \
.offset = offsetof(struct struct_name, member) },
{ \
.name = #member, \
.size = sizeof(((struct struct_name*)0)->member), \
.offset = offsetof(struct struct_name, member) \
},
#define DYNAMIC_STRUCT_END(struct_name) \
} \
; \
}; \
static struct DynamicStructInfo struct_name##_info = { \
.name = #struct_name, \
.count = sizeof(struct_name##_members) / \
sizeof(struct DynamicStructMember), \
.count = sizeof(struct_name##_members) / sizeof(struct DynamicStructMember), \
.total_size = sizeof(struct struct_name), \
.members = struct_name##_members \
};
@@ -75,28 +76,28 @@ struct DynamicStructInfo {
// 定义元数据
DYNAMIC_STRUCT_BEGIN(mount)
DEFINE_MEMBER(mount, mnt_parent)
DEFINE_MEMBER(mount, mnt)
DEFINE_MEMBER(mount, mnt_id)
DEFINE_MEMBER(mount, mnt_group_id)
DEFINE_MEMBER(mount, mnt_expiry_mark)
DEFINE_MEMBER(mount, mnt_master)
DEFINE_MEMBER(mount, mnt_devname)
DEFINE_MEMBER(mount, mnt_parent)
DEFINE_MEMBER(mount, mnt)
DEFINE_MEMBER(mount, mnt_id)
DEFINE_MEMBER(mount, mnt_group_id)
DEFINE_MEMBER(mount, mnt_expiry_mark)
DEFINE_MEMBER(mount, mnt_master)
DEFINE_MEMBER(mount, mnt_devname)
DYNAMIC_STRUCT_END(mount)
DYNAMIC_STRUCT_BEGIN(vfsmount)
DEFINE_MEMBER(vfsmount, mnt_root)
DEFINE_MEMBER(vfsmount, mnt_sb)
DEFINE_MEMBER(vfsmount, mnt_flags)
DEFINE_MEMBER(vfsmount, mnt_root)
DEFINE_MEMBER(vfsmount, mnt_sb)
DEFINE_MEMBER(vfsmount, mnt_flags)
DYNAMIC_STRUCT_END(vfsmount)
DYNAMIC_STRUCT_BEGIN(mnt_namespace)
DEFINE_MEMBER(mnt_namespace, ns)
DEFINE_MEMBER(mnt_namespace, root)
DEFINE_MEMBER(mnt_namespace, seq)
DEFINE_MEMBER(mnt_namespace, mounts)
DEFINE_MEMBER(mnt_namespace, ns)
DEFINE_MEMBER(mnt_namespace, root)
DEFINE_MEMBER(mnt_namespace, seq)
DEFINE_MEMBER(mnt_namespace, mounts)
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
DEFINE_MEMBER(mnt_namespace, count)
DEFINE_MEMBER(mnt_namespace, count)
#endif
DYNAMIC_STRUCT_END(mnt_namespace)
@@ -104,15 +105,15 @@ DYNAMIC_STRUCT_END(mnt_namespace)
#ifdef CONFIG_KPROBES
DYNAMIC_STRUCT_BEGIN(kprobe)
DEFINE_MEMBER(kprobe, addr)
DEFINE_MEMBER(kprobe, symbol_name)
DEFINE_MEMBER(kprobe, offset)
DEFINE_MEMBER(kprobe, pre_handler)
DEFINE_MEMBER(kprobe, post_handler)
DEFINE_MEMBER(kprobe, addr)
DEFINE_MEMBER(kprobe, symbol_name)
DEFINE_MEMBER(kprobe, offset)
DEFINE_MEMBER(kprobe, pre_handler)
DEFINE_MEMBER(kprobe, post_handler)
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
DEFINE_MEMBER(kprobe, fault_handler)
DEFINE_MEMBER(kprobe, fault_handler)
#endif
DEFINE_MEMBER(kprobe, flags)
DEFINE_MEMBER(kprobe, flags)
DYNAMIC_STRUCT_END(kprobe)
#endif
@@ -120,89 +121,83 @@ DYNAMIC_STRUCT_END(kprobe)
#include <linux/mm_types.h>
DYNAMIC_STRUCT_BEGIN(vm_area_struct)
DEFINE_MEMBER(vm_area_struct, vm_start)
DEFINE_MEMBER(vm_area_struct, vm_end)
DEFINE_MEMBER(vm_area_struct, vm_flags)
DEFINE_MEMBER(vm_area_struct, anon_vma)
DEFINE_MEMBER(vm_area_struct, vm_pgoff)
DEFINE_MEMBER(vm_area_struct, vm_file)
DEFINE_MEMBER(vm_area_struct, vm_private_data)
#ifdef CONFIG_ANON_VMA_NAME
DEFINE_MEMBER(vm_area_struct, anon_name)
#endif
DEFINE_MEMBER(vm_area_struct, vm_ops)
DEFINE_MEMBER(vm_area_struct,vm_start)
DEFINE_MEMBER(vm_area_struct,vm_end)
DEFINE_MEMBER(vm_area_struct,vm_flags)
DEFINE_MEMBER(vm_area_struct,anon_vma)
DEFINE_MEMBER(vm_area_struct,vm_pgoff)
DEFINE_MEMBER(vm_area_struct,vm_file)
DEFINE_MEMBER(vm_area_struct,vm_private_data)
#ifdef CONFIG_ANON_VMA_NAME
DEFINE_MEMBER(vm_area_struct, anon_name)
#endif
DEFINE_MEMBER(vm_area_struct, vm_ops)
DYNAMIC_STRUCT_END(vm_area_struct)
DYNAMIC_STRUCT_BEGIN(vm_operations_struct)
DEFINE_MEMBER(vm_operations_struct, open)
DEFINE_MEMBER(vm_operations_struct, close)
DEFINE_MEMBER(vm_operations_struct, name)
DEFINE_MEMBER(vm_operations_struct, access)
DEFINE_MEMBER(vm_operations_struct, open)
DEFINE_MEMBER(vm_operations_struct, close)
DEFINE_MEMBER(vm_operations_struct, name)
DEFINE_MEMBER(vm_operations_struct, access)
DYNAMIC_STRUCT_END(vm_operations_struct)
#include <linux/netlink.h>
DYNAMIC_STRUCT_BEGIN(netlink_kernel_cfg)
DEFINE_MEMBER(netlink_kernel_cfg, groups)
DEFINE_MEMBER(netlink_kernel_cfg, flags)
DEFINE_MEMBER(netlink_kernel_cfg, input)
DEFINE_MEMBER(netlink_kernel_cfg, cb_mutex)
DEFINE_MEMBER(netlink_kernel_cfg, bind)
DEFINE_MEMBER(netlink_kernel_cfg, unbind)
DEFINE_MEMBER(netlink_kernel_cfg, groups)
DEFINE_MEMBER(netlink_kernel_cfg, flags)
DEFINE_MEMBER(netlink_kernel_cfg, input)
DEFINE_MEMBER(netlink_kernel_cfg, cb_mutex)
DEFINE_MEMBER(netlink_kernel_cfg, bind)
DEFINE_MEMBER(netlink_kernel_cfg, unbind)
#if LINUX_VERSION_CODE < KERNEL_VERSION_6_1
DEFINE_MEMBER(netlink_kernel_cfg, compare)
DEFINE_MEMBER(netlink_kernel_cfg, compare)
#endif
DYNAMIC_STRUCT_END(netlink_kernel_cfg)
#include <linux/sched.h>
DYNAMIC_STRUCT_BEGIN(task_struct)
DEFINE_MEMBER(task_struct, pid)
DEFINE_MEMBER(task_struct, tgid)
DEFINE_MEMBER(task_struct, cred)
DEFINE_MEMBER(task_struct, real_cred)
DEFINE_MEMBER(task_struct, comm)
DEFINE_MEMBER(task_struct, parent)
DEFINE_MEMBER(task_struct, group_leader)
DEFINE_MEMBER(task_struct, mm)
DEFINE_MEMBER(task_struct, active_mm)
DEFINE_MEMBER(task_struct, pid)
DEFINE_MEMBER(task_struct, tgid)
DEFINE_MEMBER(task_struct, cred)
DEFINE_MEMBER(task_struct, real_cred)
DEFINE_MEMBER(task_struct, comm)
DEFINE_MEMBER(task_struct, parent)
DEFINE_MEMBER(task_struct, group_leader)
DEFINE_MEMBER(task_struct, mm)
DEFINE_MEMBER(task_struct, active_mm)
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
DEFINE_MEMBER(task_struct, pids[PIDTYPE_PID].pid)
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, seccomp)
DEFINE_MEMBER(task_struct, files)
DEFINE_MEMBER(task_struct, seccomp)
#ifdef CONFIG_THREAD_INFO_IN_TASK
DEFINE_MEMBER(task_struct, thread_info)
DEFINE_MEMBER(task_struct, thread_info)
#endif
#ifdef CONFIG_CGROUPS
DEFINE_MEMBER(task_struct, cgroups)
DEFINE_MEMBER(task_struct, cgroups)
#endif
#ifdef CONFIG_SECURITY
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0))
DEFINE_MEMBER(task_struct, security)
#else
DEFINE_MEMBER(task_struct, cred)
#endif
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0))
DEFINE_MEMBER(task_struct, thread)
#else
DEFINE_MEMBER(task_struct, thread_info)
DEFINE_MEMBER(task_struct, security)
#endif
DEFINE_MEMBER(task_struct, thread)
DYNAMIC_STRUCT_END(task_struct)
// =====================================================================================================================
#define STRUCT_INFO(name) &(name##_info)
static struct DynamicStructInfo *dynamic_struct_infos[] = {
static
struct DynamicStructInfo* dynamic_struct_infos[] = {
STRUCT_INFO(mount),
STRUCT_INFO(vfsmount),
STRUCT_INFO(mnt_namespace),
#ifdef CONFIG_KPROBES
#ifdef CONFIG_KPROBES
STRUCT_INFO(kprobe),
#endif
#endif
STRUCT_INFO(vm_area_struct),
STRUCT_INFO(vm_operations_struct),
STRUCT_INFO(netlink_kernel_cfg),
@@ -211,17 +206,17 @@ static struct DynamicStructInfo *dynamic_struct_infos[] = {
// return 0 if successful
// return -1 if struct not defined
int sukisu_super_find_struct(const char *struct_name, size_t *out_size,
int *out_members)
{
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) /
sizeof(dynamic_struct_infos[0]));
i++) {
struct DynamicStructInfo *info = dynamic_struct_infos[i];
if (strcmp(struct_name, info->name) == 0) {
if (out_size)
int sukisu_super_find_struct(
const char* struct_name,
size_t* out_size,
int* out_members
) {
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
struct DynamicStructInfo* info = dynamic_struct_infos[i];
if(strcmp(struct_name, info->name) == 0) {
if(out_size)
*out_size = info->total_size;
if (out_members)
if(out_members)
*out_members = info->count;
return 0;
}
@@ -234,23 +229,21 @@ EXPORT_SYMBOL(sukisu_super_find_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)
{
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) /
sizeof(dynamic_struct_infos[0]));
i++) {
struct DynamicStructInfo *info = dynamic_struct_infos[i];
if (strcmp(struct_name, info->name) == 0) {
int sukisu_super_access (
const char* struct_name,
const char* member_name,
size_t* out_offset,
size_t* out_size
) {
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
struct DynamicStructInfo* info = dynamic_struct_infos[i];
if(strcmp(struct_name, info->name) == 0) {
for (size_t i1 = 0; i1 < info->count; i1++) {
if (strcmp(info->members[i1].name,
member_name) == 0) {
if (out_offset)
*out_offset =
info->members[i].offset;
if (out_size)
*out_size =
info->members[i].size;
if (strcmp(info->members[i1].name, member_name) == 0) {
if(out_offset)
*out_offset = info->members[i].offset;
if(out_size)
*out_size = info->members[i].size;
return 0;
}
}
@@ -262,33 +255,29 @@ int sukisu_super_access(const char *struct_name, const char *member_name,
EXPORT_SYMBOL(sukisu_super_access);
// 动态 container_of 宏
#define DYNAMIC_CONTAINER_OF(offset, member_ptr) \
({ \
(offset != (size_t)-1) ? \
(void *)((char *)(member_ptr) - offset) : \
NULL; \
})
#define DYNAMIC_CONTAINER_OF(offset, member_ptr) ({ \
(offset != (size_t)-1) ? (void*)((char*)(member_ptr) - offset) : NULL; \
})
// 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)
{
if (ptr == NULL) {
int sukisu_super_container_of(
const char* struct_name,
const char* member_name,
void* ptr,
void** out_ptr
) {
if(ptr == NULL) {
return -3;
}
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) /
sizeof(dynamic_struct_infos[0]));
i++) {
struct DynamicStructInfo *info = dynamic_struct_infos[i];
if (strcmp(struct_name, info->name) == 0) {
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
struct DynamicStructInfo* info = dynamic_struct_infos[i];
if(strcmp(struct_name, info->name) == 0) {
for (size_t i1 = 0; i1 < info->count; i1++) {
if (strcmp(info->members[i1].name,
member_name) == 0) {
*out_ptr = (void *)DYNAMIC_CONTAINER_OF(
info->members[i1].offset, ptr);
if (strcmp(info->members[i1].name, member_name) == 0) {
*out_ptr = (void*) DYNAMIC_CONTAINER_OF(info->members[i1].offset, ptr);
return 0;
}
}

View File

@@ -8,21 +8,32 @@
// return 0 if successful
// return -1 if struct not defined
int sukisu_super_find_struct(const char *struct_name, size_t *out_size,
int *out_members);
int sukisu_super_find_struct(
const char* struct_name,
size_t* out_size,
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);
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);
int sukisu_super_container_of(
const char* struct_name,
const char* member_name,
void* ptr,
void** out_ptr
);
#endif

View File

@@ -1,25 +1,15 @@
#include <linux/export.h>
#include <linux/fs.h>
#include <linux/printk.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <generated/utsrelease.h>
#include <generated/compile.h>
#include <linux/version.h> /* LINUX_VERSION_CODE, KERNEL_VERSION macros */
#include "allowlist.h"
#include "arch.h"
#include "feature.h"
#include "core_hook.h"
#include "klog.h" // IWYU pragma: keep
#include "ksu.h"
#include "throne_tracker.h"
#include "syscall_hook_manager.h"
#include "ksud.h"
#include "supercalls.h"
#include "throne_comm.h"
#include "dynamic_manager.h"
static struct workqueue_struct *ksu_workqueue;
@@ -28,21 +18,12 @@ bool ksu_queue_work(struct work_struct *work)
return queue_work(ksu_workqueue, work);
}
void sukisu_custom_config_init(void)
{
}
void sukisu_custom_config_exit(void)
{
ksu_uid_exit();
ksu_throne_comm_exit();
ksu_dynamic_manager_exit();
}
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
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)
{
@@ -51,13 +32,13 @@ int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
flags);
}
extern void ksu_sucompat_init();
extern void ksu_sucompat_exit();
extern void ksu_ksud_init();
extern void ksu_ksud_exit();
int __init kernelsu_init(void)
{
#ifndef DDK_ENV
pr_info("Initialized on: %s (%s) with driver version: %u\n",
UTS_RELEASE, UTS_MACHINE, KSU_VERSION);
#endif
#ifdef CONFIG_KSU_DEBUG
pr_alert("*************************************************************");
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
@@ -68,21 +49,19 @@ int __init kernelsu_init(void)
pr_alert("*************************************************************");
#endif
ksu_feature_init();
ksu_supercalls_init();
sukisu_custom_config_init();
ksu_syscall_hook_manager_init();
ksu_core_init();
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
ksu_allowlist_init();
ksu_throne_tracker_init();
#ifdef CONFIG_KPROBES
ksu_sucompat_init();
ksu_ksud_init();
#else
pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html");
#endif
#ifdef MODULE
#ifndef CONFIG_KSU_DEBUG
@@ -92,26 +71,20 @@ int __init kernelsu_init(void)
return 0;
}
extern void ksu_observer_exit(void);
void kernelsu_exit(void)
{
ksu_allowlist_exit();
ksu_throne_tracker_exit();
ksu_observer_exit();
destroy_workqueue(ksu_workqueue);
#ifdef CONFIG_KPROBES
ksu_ksud_exit();
ksu_sucompat_exit();
#endif
ksu_syscall_hook_manager_exit();
sukisu_custom_config_exit();
ksu_supercalls_exit();
ksu_feature_exit();
ksu_core_exit();
}
module_init(kernelsu_init);
@@ -120,11 +93,4 @@ module_exit(kernelsu_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("weishu");
MODULE_DESCRIPTION("Android KernelSU");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
#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);
#endif
#endif

View File

@@ -7,27 +7,50 @@
#define KERNEL_SU_VERSION KSU_VERSION
#define KERNEL_SU_OPTION 0xDEADBEEF
extern bool ksu_uid_scanner_enabled;
#define CMD_GRANT_ROOT 0
#define CMD_BECOME_MANAGER 1
#define CMD_GET_VERSION 2
#define CMD_ALLOW_SU 3
#define CMD_DENY_SU 4
#define CMD_GET_ALLOW_LIST 5
#define CMD_GET_DENY_LIST 6
#define CMD_REPORT_EVENT 7
#define CMD_SET_SEPOLICY 8
#define CMD_CHECK_SAFEMODE 9
#define 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 CMD_GET_FULL_VERSION 0xC0FFEE1A
#define CMD_ENABLE_KPM 100
#define CMD_DYNAMIC_SIGN 103
#define CMD_GET_MANAGERS 104
#define EVENT_POST_FS_DATA 1
#define EVENT_BOOT_COMPLETED 2
#define EVENT_MODULE_MOUNTED 3
#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
// SukiSU Ultra kernel su version full strings
#ifndef KSU_VERSION_FULL
#define KSU_VERSION_FULL "v3.x-00000000@unknown"
#endif
#define KSU_FULL_VERSION_STRING 255
#define DYNAMIC_MANAGER_OP_SET 0
#define DYNAMIC_MANAGER_OP_GET 1
#define DYNAMIC_MANAGER_OP_CLEAR 2
#define DYNAMIC_SIGN_OP_SET 0
#define DYNAMIC_SIGN_OP_GET 1
#define DYNAMIC_SIGN_OP_CLEAR 2
#define UID_SCANNER_OP_GET_STATUS 0
#define UID_SCANNER_OP_TOGGLE 1
#define UID_SCANNER_OP_CLEAR_ENV 2
struct dynamic_manager_user_config {
struct dynamic_sign_user_config {
unsigned int operation;
unsigned int size;
char hash[65];
@@ -41,9 +64,56 @@ struct manager_list_info {
} managers[2];
};
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;
};
};
bool ksu_queue_work(struct work_struct *work);
#if 0
static inline int startswith(char *s, char *prefix)
{
return strncmp(s, prefix, strlen(prefix));
@@ -57,6 +127,5 @@ static inline int endswith(const char *s, const char *t)
return 1;
return strcmp(s + slen - tlen, t);
}
#endif
#endif

View File

@@ -1,6 +1,3 @@
#include <linux/rcupdate.h>
#include <linux/slab.h>
#include <linux/task_work.h>
#include <asm/current.h>
#include <linux/compat.h>
#include <linux/cred.h>
@@ -9,37 +6,20 @@
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
#include <linux/input-event-codes.h>
#else
#include <uapi/linux/input.h>
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
#include <linux/aio.h>
#endif
#include <linux/kprobes.h>
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/namei.h>
#include <linux/workqueue.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/signal.h>
#else
#include <linux/sched.h>
#endif
#include "manager.h"
#include "allowlist.h"
#include "arch.h"
#include "kernel_compat.h"
#include "klog.h" // IWYU pragma: keep
#include "ksud.h"
#include "kernel_compat.h"
#include "selinux/selinux.h"
#include "throne_tracker.h"
bool ksu_module_mounted __read_mostly = false;
bool ksu_boot_completed __read_mostly = false;
static const char KERNEL_SU_RC[] =
"\n"
@@ -64,25 +44,29 @@ static const char KERNEL_SU_RC[] =
"\n";
static void stop_vfs_read_hook(void);
static void stop_execve_hook(void);
static void stop_input_hook(void);
static void stop_vfs_read_hook();
static void stop_execve_hook();
static void stop_input_hook();
#ifdef KSU_SHOULD_USE_NEW_TP
#ifdef CONFIG_KPROBES
static struct work_struct stop_vfs_read_work;
static struct work_struct stop_execve_hook_work;
static struct work_struct stop_input_hook_work;
#else
bool ksu_vfs_read_hook __read_mostly = true;
bool ksu_execveat_hook __read_mostly = true;
bool ksu_input_hook __read_mostly = true;
#endif
bool ksu_execveat_hook __read_mostly = true;
u32 ksu_file_sid;
u32 ksu_devpts_sid;
// Detect whether it is on or not
static bool is_boot_phase = true;
#ifdef CONFIG_COMPAT
bool ksu_is_compat __read_mostly = false;
#endif
void on_post_fs_data(void)
{
static bool done = false;
@@ -93,51 +77,14 @@ void on_post_fs_data(void)
done = true;
pr_info("on_post_fs_data!\n");
ksu_load_allow_list();
ksu_observer_init();
// sanity check, this may influence the performance
stop_input_hook();
ksu_devpts_sid = ksu_get_devpts_sid();
pr_info("devpts sid: %d\n", ksu_devpts_sid);
// End of boot state
is_boot_phase = false;
ksu_file_sid = ksu_get_ksu_file_sid();
pr_info("devpts sid: %d\n", ksu_file_sid);
}
extern void ext4_unregister_sysfs(struct super_block *sb);
int nuke_ext4_sysfs(const char* mnt)
{
struct path path;
int err = kern_path(mnt, 0, &path);
if (err) {
pr_err("nuke path err: %d\n", err);
return err;
}
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");
path_put(&path);
return -EINVAL;
}
ext4_unregister_sysfs(sb);
path_put(&path);
return 0;
}
void on_module_mounted(void)
{
pr_info("on_module_mounted!\n");
ksu_module_mounted = true;
}
void on_boot_completed(void)
{
ksu_boot_completed = true;
pr_info("on_boot_completed!\n");
track_throne(true);
}
#define MAX_ARG_STRINGS 0x7FFFFFFF
@@ -153,20 +100,75 @@ struct user_arg_ptr {
} ptr;
};
static void on_post_fs_data_cbfun(struct callback_head *cb)
static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
{
on_post_fs_data();
const char __user *native;
#ifdef CONFIG_COMPAT
if (unlikely(argv.is_compat)) {
compat_uptr_t compat;
if (get_user(compat, argv.ptr.compat + nr))
return ERR_PTR(-EFAULT);
ksu_is_compat = true;
return compat_ptr(compat);
}
#endif
if (get_user(native, argv.ptr.native + nr))
return ERR_PTR(-EFAULT);
return native;
}
static struct callback_head on_post_fs_data_cb = {
.func = on_post_fs_data_cbfun
};
/*
* count() counts the number of strings in array ARGV.
*/
// since _ksud handler only uses argv and envp for comparisons
// this can probably work
// adapted from ksu_handle_execveat_ksud
static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const char *envp, size_t envp_len)
/*
* Make sure old GCC compiler can use __maybe_unused,
* Test passed in 4.4.x ~ 4.9.x when use GCC.
*/
static int __maybe_unused count(struct user_arg_ptr argv, int max)
{
int i = 0;
if (argv.ptr.native != NULL) {
for (;;) {
const char __user *p = get_user_arg_ptr(argv, i);
if (!p)
break;
if (IS_ERR(p))
return -EFAULT;
if (i >= max)
return -E2BIG;
++i;
if (fatal_signal_pending(current))
return -ERESTARTNOHAND;
cond_resched();
}
}
return i;
}
// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
struct user_arg_ptr *argv,
struct user_arg_ptr *envp, int *flags)
{
#ifndef CONFIG_KPROBES
if (!ksu_execveat_hook) {
return 0;
}
#endif
struct filename *filename;
static const char app_process[] = "/system/bin/app_process";
static bool first_app_process = true;
@@ -176,157 +178,113 @@ static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const c
static const char old_system_init[] = "/init";
static bool init_second_stage_executed = false;
// return early when disabled
if (!ksu_execveat_hook)
if (!filename_ptr)
return 0;
if (!filename)
filename = *filename_ptr;
if (IS_ERR(filename)) {
return 0;
}
// debug! remove me!
pr_info("%s: filename: %s argv1: %s envp_len: %zu\n", __func__, filename, argv1, envp_len);
#ifdef CONFIG_KSU_DEBUG
const char *envp_n = envp;
unsigned int envc = 1;
do {
pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n);
envp_n += strlen(envp_n) + 1;
envc++;
} while (envp_n < envp + 256);
#endif
if (init_second_stage_executed)
goto first_app_process;
// /system/bin/init with argv1
if (!init_second_stage_executed
&& (!memcmp(filename, system_bin_init, sizeof(system_bin_init) - 1))) {
if (argv1 && !strcmp(argv1, "second_stage")) {
pr_info("%s: /system/bin/init second_stage executed\n", __func__);
if (unlikely(!memcmp(filename->name, system_bin_init,
sizeof(system_bin_init) - 1) &&
argv)) {
// /system/bin/init executed
int argc = count(*argv, MAX_ARG_STRINGS);
pr_info("/system/bin/init argc: %d\n", argc);
if (argc > 1 && !init_second_stage_executed) {
const char __user *p = get_user_arg_ptr(*argv, 1);
if (p && !IS_ERR(p)) {
char first_arg[16];
ksu_strncpy_from_user_nofault(
first_arg, p, sizeof(first_arg));
pr_info("/system/bin/init first arg: %s\n",
first_arg);
if (!strcmp(first_arg, "second_stage")) {
pr_info("/system/bin/init second_stage executed\n");
apply_kernelsu_rules();
init_second_stage_executed = true;
ksu_android_ns_fs_check();
}
} else {
pr_err("/system/bin/init parse args err!\n");
}
}
// /init with argv1
if (!init_second_stage_executed
&& (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) {
if (argv1 && !strcmp(argv1, "--second-stage")) {
pr_info("%s: /init --second-stage executed\n", __func__);
} else if (unlikely(!memcmp(filename->name, old_system_init,
sizeof(old_system_init) - 1) &&
argv)) {
// /init executed
int argc = count(*argv, MAX_ARG_STRINGS);
pr_info("/init argc: %d\n", argc);
if (argc > 1 && !init_second_stage_executed) {
/* This applies to versions between Android 6 ~ 7 */
const char __user *p = get_user_arg_ptr(*argv, 1);
if (p && !IS_ERR(p)) {
char first_arg[16];
ksu_strncpy_from_user_nofault(
first_arg, p, sizeof(first_arg));
pr_info("/init first arg: %s\n", first_arg);
if (!strcmp(first_arg, "--second-stage")) {
pr_info("/init second_stage executed\n");
apply_kernelsu_rules();
init_second_stage_executed = true;
ksu_android_ns_fs_check();
}
} else {
pr_err("/init parse args err!\n");
}
if (!envp || !envp_len)
goto first_app_process;
// /init without argv1/useless-argv1 but usable envp
// untested! TODO: test and debug me!
if (!init_second_stage_executed && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) {
// we hunt for "INIT_SECOND_STAGE"
const char *envp_n = envp;
unsigned int envc = 1;
do {
if (strstarts(envp_n, "INIT_SECOND_STAGE"))
break;
envp_n += strlen(envp_n) + 1;
envc++;
} while (envp_n < envp + envp_len);
pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n);
if (!strcmp(envp_n, "INIT_SECOND_STAGE=1")
|| !strcmp(envp_n, "INIT_SECOND_STAGE=true") ) {
pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__);
} else if (argc == 1 && !init_second_stage_executed && envp) {
/* This applies to versions between Android 8 ~ 9 */
int envc = count(*envp, MAX_ARG_STRINGS);
if (envc > 0) {
int n;
for (n = 1; n <= envc; n++) {
const char __user *p =
get_user_arg_ptr(*envp, n);
if (!p || IS_ERR(p)) {
continue;
}
char env[256];
// Reading environment variable strings from user space
if (ksu_strncpy_from_user_nofault(
env, p, sizeof(env)) < 0)
continue;
// Parsing environment variable names and values
char *env_name = env;
char *env_value = strchr(env, '=');
if (env_value == NULL)
continue;
// Replace equal sign with string terminator
*env_value = '\0';
env_value++;
// Check if the environment variable name and value are matching
if (!strcmp(env_name,
"INIT_SECOND_STAGE") &&
(!strcmp(env_value, "1") ||
!strcmp(env_value, "true"))) {
pr_info("/init second_stage executed\n");
apply_kernelsu_rules();
init_second_stage_executed = true;
init_second_stage_executed =
true;
ksu_android_ns_fs_check();
}
}
}
}
}
first_app_process:
if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) {
if (unlikely(first_app_process && !memcmp(filename->name, app_process,
sizeof(app_process) - 1))) {
first_app_process = false;
pr_info("exec app_process, /data prepared, second_stage: %d\n",
init_second_stage_executed);
struct task_struct *init_task;
rcu_read_lock();
init_task = rcu_dereference(current->real_parent);
if (init_task) {
task_work_add(init_task, &on_post_fs_data_cb,
TWA_RESUME);
}
rcu_read_unlock();
on_post_fs_data(); // we keep this for old ksud
stop_execve_hook();
}
return 0;
}
int ksu_handle_pre_ksud(const char *filename)
{
if (likely(!ksu_execveat_hook))
return 0;
// not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy)
// return 0;
if (likely(strcmp(filename, "/system/bin/init") && strcmp(filename, "/init")
&& !strstarts(filename, "/system/bin/app_process") ))
return 0;
if (!current || !current->mm)
return 0;
// https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429
// unsigned long arg_start, arg_end, env_start, env_end;
unsigned long arg_start = current->mm->arg_start;
unsigned long arg_end = current->mm->arg_end;
unsigned long env_start = current->mm->env_start;
unsigned long env_end = current->mm->env_end;
size_t arg_len = arg_end - arg_start;
size_t envp_len = env_end - env_start;
if (arg_len <= 0 || envp_len <= 0) // this wont make sense, filter it
return 0;
#define ARGV_MAX 32 // this is enough for argv1
#define ENVP_MAX 256 // this is enough for INIT_SECOND_STAGE
char args[ARGV_MAX];
size_t argv_copy_len = (arg_len > ARGV_MAX) ? ARGV_MAX : arg_len;
char envp[ENVP_MAX];
size_t envp_copy_len = (envp_len > ENVP_MAX) ? ENVP_MAX : envp_len;
// we cant use strncpy on here, else it will truncate once it sees \0
if (ksu_copy_from_user_retry(args, (void __user *)arg_start, argv_copy_len))
return 0;
if (ksu_copy_from_user_retry(envp, (void __user *)env_start, envp_copy_len))
return 0;
args[ARGV_MAX - 1] = '\0';
envp[ENVP_MAX - 1] = '\0';
// we only need argv1 !
// abuse strlen here since it only gets length up to \0
char *argv1 = args + strlen(args) + 1;
if (argv1 >= args + argv_copy_len) // out of bounds!
argv1 = "";
return ksu_handle_bprm_ksud(filename, argv1, envp, envp_copy_len);
}
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
struct user_arg_ptr *argv, struct user_arg_ptr *envp,
int *flags)
{
// this is now handled via security_bprm_check
// we only keep this for the sake of old hooks.
return 0;
}
static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *);
static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *);
static struct file_operations fops_proxy;
@@ -360,7 +318,7 @@ static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to)
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
size_t *count_ptr, loff_t **pos)
{
#ifndef KSU_SHOULD_USE_NEW_TP
#ifndef CONFIG_KPROBES
if (!ksu_vfs_read_hook) {
return 0;
}
@@ -473,7 +431,7 @@ static bool is_volumedown_enough(unsigned int count)
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
int *value)
{
#ifndef KSU_SHOULD_USE_NEW_TP
#ifndef CONFIG_KPROBES
if (!ksu_input_hook) {
return 0;
}
@@ -493,7 +451,7 @@ int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
return 0;
}
bool ksu_is_safe_mode(void)
bool ksu_is_safe_mode()
{
static bool safe_mode = false;
if (safe_mode) {
@@ -515,125 +473,28 @@ bool ksu_is_safe_mode(void)
return false;
}
#ifdef KSU_SHOULD_USE_NEW_TP
#ifdef CONFIG_KPROBES
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
/*
asmlinkage int sys_execve(const char __user *filenamei,
const char __user *const __user *argv,
const char __user *const __user *envp, struct pt_regs *regs)
*/
struct pt_regs *real_regs = PT_REAL_REGS(regs);
const char __user *filename_user = (const char __user *)PT_REGS_PARM1(real_regs);
const char __user *const __user *__argv = (const char __user *const __user *)PT_REGS_PARM2(real_regs);
const char __user *const __user *__envp = (const char __user *const __user *)PT_REGS_PARM3(real_regs);
const char __user **filename_user =
(const char **)&PT_REGS_PARM1(real_regs);
const char __user *const __user *__argv =
(const char __user *const __user *)PT_REGS_PARM2(real_regs);
struct user_arg_ptr argv = { .ptr.native = __argv };
struct filename filename_in, *filename_p;
char path[32];
if (!filename_user)
return 0;
// filename stage
if (ksu_copy_from_user_retry(path, filename_user, sizeof(path)))
return 0;
memset(path, 0, sizeof(path));
ksu_strncpy_from_user_nofault(path, *filename_user, 32);
filename_in.name = path;
path[sizeof(path) - 1] = '\0';
// not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy)
// we dont care !!
if (likely(strcmp(path, "/system/bin/init") && strcmp(path, "/init")
&& !strstarts(path, "/system/bin/app_process") ))
return 0;
// argv stage
char argv1[32] = {0};
// memzero_explicit(argv1, 32);
if (__argv) {
const char __user *arg1_user = NULL;
// grab argv[1] pointer
// this looks like
/*
* 0x1000 ./program << this is __argv
* 0x1001 -o
* 0x1002 arg
*/
if (ksu_copy_from_user_retry(&arg1_user, __argv + 1, sizeof(arg1_user)))
goto no_argv1; // copy argv[1] pointer fail, probably no argv1 !!
if (arg1_user)
ksu_copy_from_user_retry(argv1, arg1_user, sizeof(argv1));
}
no_argv1:
argv1[sizeof(argv1) - 1] = '\0';
// envp stage
#define ENVP_MAX 256
char envp[ENVP_MAX] = {0};
char *dst = envp;
size_t envp_len = 0;
int i = 0; // to track user pointer offset from __envp
// memzero_explicit(envp, ENVP_MAX);
if (__envp) {
do {
const char __user *env_entry_user = NULL;
// this is also like argv above
/*
* 0x1001 PATH=/bin
* 0x1002 VARIABLE=value
* 0x1002 some_more_env_var=1
*/
// check if pointer exists
if (ksu_copy_from_user_retry(&env_entry_user, __envp + i, sizeof(env_entry_user)))
break;
// check if no more env entry
if (!env_entry_user)
break;
// probably redundant to while condition but ok
if (envp_len >= ENVP_MAX - 1)
break;
// copy strings from env_entry_user pointer that we collected
// also break if failed
if (ksu_copy_from_user_retry(dst, env_entry_user, ENVP_MAX - envp_len))
break;
// get the length of that new copy above
// get lngth of dst as far as ENVP_MAX - current collected envp_len
size_t len = strnlen(dst, ENVP_MAX - envp_len);
if (envp_len + len + 1 > ENVP_MAX)
break; // if more than 255 bytes, bail
dst[len] = '\0';
// collect total number of copied strings
envp_len = envp_len + len + 1;
// increment dst address since we need to put something on next iter
dst = dst + len + 1;
// pointer walk, __envp + i
i++;
} while (envp_len < ENVP_MAX);
}
/*
at this point, we shoul've collected envp from
* 0x1001 PATH=/bin
* 0x1002 VARIABLE=value
* 0x1002 some_more_env_var=1
to
* 0x1234 PATH=/bin\0VARIABLE=value\0some_more_env_var=1\0\0\0\0
*/
envp[ENVP_MAX - 1] = '\0';
#ifdef CONFIG_KSU_DEBUG
pr_info("%s: filename: %s argv[1]:%s envp_len: %zu\n", __func__, path, argv1, envp_len);
#endif
return ksu_handle_bprm_ksud(path, argv1, envp, envp_len);
filename_p = &filename_in;
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL,
NULL);
}
static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
@@ -684,58 +545,11 @@ static void do_stop_input_hook(struct work_struct *work)
{
unregister_kprobe(&input_event_kp);
}
#else
static int ksu_execve_ksud_common(const char __user *filename_user,
struct user_arg_ptr *argv)
{
struct filename filename_in, *filename_p;
char path[32];
long len;
// return early if disabled.
if (!ksu_execveat_hook) {
return 0;
}
if (!filename_user)
return 0;
len = ksu_strncpy_from_user_nofault(path, filename_user, 32);
if (len <= 0)
return 0;
path[sizeof(path) - 1] = '\0';
// this is because ksu_handle_execveat_ksud calls it filename->name
filename_in.name = path;
filename_p = &filename_in;
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, argv, NULL,
NULL);
}
int __maybe_unused
ksu_handle_execve_ksud(const char __user *filename_user,
const char __user *const __user *__argv)
{
struct user_arg_ptr argv = { .ptr.native = __argv };
return ksu_execve_ksud_common(filename_user, &argv);
}
#if defined(CONFIG_COMPAT) && defined(CONFIG_64BIT)
int __maybe_unused ksu_handle_compat_execve_ksud(
const char __user *filename_user, const compat_uptr_t __user *__argv)
{
struct user_arg_ptr argv = { .ptr.compat = __argv };
return ksu_execve_ksud_common(filename_user, &argv);
}
#endif /* COMPAT & 64BIT */
#endif
static void stop_vfs_read_hook(void)
static void stop_vfs_read_hook()
{
#ifdef KSU_SHOULD_USE_NEW_TP
#ifdef CONFIG_KPROBES
bool ret = schedule_work(&stop_vfs_read_work);
pr_info("unregister vfs_read kprobe: %d!\n", ret);
#else
@@ -744,40 +558,37 @@ static void stop_vfs_read_hook(void)
#endif
}
static void stop_execve_hook(void)
static void stop_execve_hook()
{
#ifdef KSU_SHOULD_USE_NEW_TP
#ifdef CONFIG_KPROBES
bool ret = schedule_work(&stop_execve_hook_work);
pr_info("unregister execve kprobe: %d!\n", ret);
#else
pr_info("stop execve_hook\n");
ksu_execveat_hook = false;
pr_info("stop execve_hook\n");
#endif
}
static void stop_input_hook(void)
static void stop_input_hook()
{
#ifdef KSU_SHOULD_USE_NEW_TP
static bool input_hook_stopped = false;
if (input_hook_stopped) {
return;
}
input_hook_stopped = true;
#ifdef CONFIG_KPROBES
bool ret = schedule_work(&stop_input_hook_work);
pr_info("unregister input kprobe: %d!\n", ret);
#else
if (!ksu_input_hook) {
return;
}
ksu_input_hook = false;
pr_info("stop input_hook\n");
#endif
}
// ksud: module support
void ksu_ksud_init(void)
void ksu_ksud_init()
{
#ifdef KSU_SHOULD_USE_NEW_TP
#ifdef CONFIG_KPROBES
int ret;
ret = register_kprobe(&execve_kp);
@@ -795,9 +606,9 @@ void ksu_ksud_init(void)
#endif
}
void ksu_ksud_exit(void)
void ksu_ksud_exit()
{
#ifdef KSU_SHOULD_USE_NEW_TP
#ifdef CONFIG_KPROBES
unregister_kprobe(&execve_kp);
// this should be done before unregister vfs_read_kp
// unregister_kprobe(&vfs_read_kp);

View File

@@ -1,26 +1,11 @@
#ifndef __KSU_H_KSUD
#define __KSU_H_KSUD
#include <linux/types.h>
#define KSUD_PATH "/data/adb/ksud"
void ksu_ksud_init(void);
void ksu_ksud_exit(void);
void on_post_fs_data(void);
void on_module_mounted(void);
void on_boot_completed(void);
bool ksu_is_safe_mode(void);
int nuke_ext4_sysfs(const char* mnt);
extern u32 ksu_file_sid;
extern bool ksu_module_mounted;
extern bool ksu_boot_completed;
extern bool ksu_execveat_hook __read_mostly;
extern int ksu_handle_pre_ksud(const char *filename);
extern u32 ksu_devpts_sid;
#endif

View File

@@ -1,123 +0,0 @@
#include <linux/lsm_hooks.h>
#include <linux/uidgid.h>
#include <linux/version.h>
#include <linux/binfmts.h>
#include <linux/err.h>
#include "klog.h" // IWYU pragma: keep
#include "ksud.h"
#include "kernel_compat.h"
#include "setuid_hook.h"
#ifndef KSU_SHOULD_USE_NEW_TP
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
unsigned perm)
{
if (init_session_keyring != NULL) {
return 0;
}
if (strcmp(current->comm, "init")) {
// we are only interested in `init` process
return 0;
}
init_session_keyring = cred->session_keyring;
pr_info("kernel_compat: got init_session_keyring\n");
return 0;
}
#endif
static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
int flags)
{
kuid_t new_uid = new->uid;
kuid_t new_euid = new->euid;
return ksu_handle_setresuid((uid_t)new_uid.val, (uid_t)new_euid.val,
(uid_t)new_uid.val);
}
#ifndef DEVPTS_SUPER_MAGIC
#define DEVPTS_SUPER_MAGIC 0x1cd1
#endif
extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c
#ifdef CONFIG_COMPAT
bool ksu_is_compat __read_mostly = false;
#endif
int ksu_inode_permission(struct inode *inode, int mask)
{
if (inode && inode->i_sb
&& unlikely(inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) {
//pr_info("%s: handling devpts for: %s \n", __func__, current->comm);
__ksu_handle_devpts(inode);
}
return 0;
}
int ksu_bprm_check(struct linux_binprm *bprm)
{
char *filename = (char *)bprm->filename;
if (likely(!ksu_execveat_hook))
return 0;
#ifdef CONFIG_COMPAT
static bool compat_check_done __read_mostly = false;
if ( unlikely(!compat_check_done) && unlikely(!strcmp(filename, "/data/adb/ksud"))
&& !memcmp(bprm->buf, "\x7f\x45\x4c\x46", 4) ) {
if (bprm->buf[4] == 0x01 )
ksu_is_compat = true;
pr_info("%s: %s ELF magic found! ksu_is_compat: %d \n", __func__, filename, ksu_is_compat);
compat_check_done = true;
}
#endif
ksu_handle_pre_ksud(filename);
return 0;
}
static struct security_hook_list ksu_hooks[] = {
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
LSM_HOOK_INIT(key_permission, ksu_key_permission),
#endif
LSM_HOOK_INIT(inode_permission, ksu_inode_permission),
#ifndef KSU_SHOULD_USE_NEW_TP
LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check),
#endif
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid)
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
static const struct lsm_id ksu_lsmid = {
.name = "ksu",
.id = 912,
};
#endif
void __init ksu_lsm_hook_init(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), &ksu_lsmid);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu");
#else
// https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks));
#endif
pr_info("LSM hooks initialized.\n");
}
#else
void ksu_lsm_hook_init(void)
{
return;
}
#endif

View File

@@ -13,18 +13,17 @@ 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)
static inline bool ksu_is_manager_uid_valid()
{
return ksu_manager_uid != KSU_INVALID_UID;
}
static inline bool is_manager(void)
static inline bool is_manager()
{
return unlikely(ksu_is_any_manager(current_uid().val) ||
(ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val));
return unlikely(ksu_is_any_manager(current_uid().val) || ksu_manager_uid == current_uid().val);
}
static inline uid_t ksu_get_manager_uid(void)
static inline uid_t ksu_get_manager_uid()
{
return ksu_manager_uid;
}
@@ -34,10 +33,9 @@ static inline void ksu_set_manager_uid(uid_t uid)
ksu_manager_uid = uid;
}
static inline void ksu_invalidate_manager_uid(void)
static inline void ksu_invalidate_manager_uid()
{
ksu_manager_uid = KSU_INVALID_UID;
}
int ksu_observer_init(void);
#endif

View File

@@ -1,19 +1,13 @@
#ifndef MANAGER_SIGN_H
#define MANAGER_SIGN_H
// ShirkNeko/KernelSU
// ShirkNeko/SukiSU
#define EXPECTED_SIZE_SHIRKNEKO 0x35c
#define EXPECTED_HASH_SHIRKNEKO \
"947ae944f3de4ed4c21a7e4f7953ecf351bfa2b36239da37a34111ad29993eef"
#define EXPECTED_HASH_SHIRKNEKO "947ae944f3de4ed4c21a7e4f7953ecf351bfa2b36239da37a34111ad29993eef"
// Dynamic Sign
#define EXPECTED_SIZE_OTHER 0x300
#define EXPECTED_HASH_OTHER \
"0000000000000000000000000000000000000000000000000000000000000000"
#define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000"
typedef struct {
unsigned size;
const char *sha256;
} apk_sign_key_t;
#endif /* MANAGER_SIGN_H */

View File

@@ -1,151 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/fsnotify.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/rculist.h>
#include <linux/version.h>
#include "klog.h" // IWYU pragma: keep
#include "throne_tracker.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;
#include "pkg_observer_defs.h" // KSU_DECL_FSNOTIFY_OPS
static KSU_DECL_FSNOTIFY_OPS(ksu_handle_generic_event)
{
if (!file_name || (mask & FS_ISDIR))
return 0;
if (ksu_fname_len(file_name) == 13 &&
!memcmp(ksu_fname_arg(file_name), "packages.list", 13)) {
pr_info("packages.list detected (mask=%d)\n", mask);
track_throne(false);
}
return 0;
}
static const struct fsnotify_ops ksu_ops = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
.handle_inode_event = ksu_handle_generic_event,
#else
.handle_event = ksu_handle_generic_event,
#endif
};
static void __maybe_unused m_free(struct fsnotify_mark *m)
{
if (m) {
kfree(m);
}
}
static int add_mark_on_inode(struct inode *inode, u32 mask,
struct fsnotify_mark **out)
{
struct fsnotify_mark *m;
int ret;
m = kzalloc(sizeof(*m), GFP_KERNEL);
if (!m)
return -ENOMEM;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
fsnotify_init_mark(m, m_free);
m->mask = mask;
ret = fsnotify_add_mark(m, g, inode, NULL, 0);
#else
fsnotify_init_mark(m, g);
m->mask = mask;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
ret = fsnotify_add_inode_mark(m, inode, 0);
#else
ret = fsnotify_add_mark(m, inode, NULL, 0);
#endif
#endif
if (ret < 0) {
fsnotify_put_mark(m);
return ret;
}
*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("%s: done.\n", __func__);
return 0;
}
void ksu_observer_exit(void)
{
unwatch_one_dir(&g_watch);
fsnotify_put_group(g);
pr_info("%s: done.\n", __func__);
}

View File

@@ -1,42 +0,0 @@
// This header should not be used outside of pkg_observer.c!
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
typedef const struct qstr *ksu_fname_t;
#define ksu_fname_len(f) ((f)->len)
#define ksu_fname_arg(f) ((f)->name)
#else
typedef const unsigned char *ksu_fname_t;
#define ksu_fname_len(f) (strlen(f))
#define ksu_fname_arg(f) (f)
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
#define KSU_DECL_FSNOTIFY_OPS(name) \
int name(struct fsnotify_mark *mark, u32 mask, struct inode *inode, \
struct inode *dir, const struct qstr *file_name, u32 cookie)
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
#define KSU_DECL_FSNOTIFY_OPS(name) \
int name(struct fsnotify_group *group, struct inode *inode, u32 mask, \
const void *data, int data_type, ksu_fname_t file_name, \
u32 cookie, struct fsnotify_iter_info *iter_info)
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
#define KSU_DECL_FSNOTIFY_OPS(name) \
int name(struct fsnotify_group *group, struct inode *inode, u32 mask, \
const void *data, int data_type, ksu_fname_t file_name, \
u32 cookie, struct fsnotify_iter_info *iter_info)
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
#define KSU_DECL_FSNOTIFY_OPS(name) \
int name(struct fsnotify_group *group, struct inode *inode, \
struct fsnotify_mark *inode_mark, \
struct fsnotify_mark *vfsmount_mark, u32 mask, \
const void *data, int data_type, ksu_fname_t file_name, \
u32 cookie, struct fsnotify_iter_info *iter_info)
#else
#define KSU_DECL_FSNOTIFY_OPS(name) \
int name(struct fsnotify_group *group, struct inode *inode, \
struct fsnotify_mark *inode_mark, \
struct fsnotify_mark *vfsmount_mark, u32 mask, void *data, \
int data_type, ksu_fname_t file_name, u32 cookie)
#endif

View File

@@ -1,67 +0,0 @@
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
#include <linux/fs.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"
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

View File

@@ -1,12 +0,0 @@
#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, 0)
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

16
kernel/selinux/Makefile Normal file
View File

@@ -0,0 +1,16 @@
obj-y += selinux.o
obj-y += sepolicy.o
obj-y += rules.o
ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID
endif
ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0)
ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE
endif
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
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

View File

@@ -6,12 +6,10 @@
#include "selinux.h"
#include "sepolicy.h"
#include "ss/services.h"
#include "linux/lsm_audit.h" // IWYU pragma: keep
#include "linux/lsm_audit.h"
#include "xfrm.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
#define SELINUX_POLICY_INSTEAD_SELINUX_SS
#endif
#define KERNEL_SU_DOMAIN "su"
#define KERNEL_SU_FILE "ksu_file"
@@ -21,34 +19,19 @@
static struct policydb *get_policydb(void)
{
struct policydb *db;
// selinux_state does not exists before 4.19
#ifdef KSU_COMPAT_USE_SELINUX_STATE
#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS
struct selinux_policy *policy = selinux_state.policy;
struct selinux_policy *policy = rcu_dereference(selinux_state.policy);
db = &policy->policydb;
#else
struct selinux_ss *ss = selinux_state.ss;
db = &ss->policydb;
#endif
#else
db = &policydb;
#endif
return db;
}
static DEFINE_MUTEX(ksu_rules);
void apply_kernelsu_rules(void)
void apply_kernelsu_rules()
{
struct policydb *db;
if (!getenforce()) {
pr_info("SELinux permissive or disabled, apply rules!\n");
}
mutex_lock(&ksu_rules);
db = get_policydb();
rcu_read_lock();
struct policydb *db = get_policydb();
ksu_permissive(db, KERNEL_SU_DOMAIN);
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
@@ -139,7 +122,7 @@ void apply_kernelsu_rules(void)
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
mutex_unlock(&ksu_rules);
rcu_read_unlock();
}
#define MAX_SEPOL_LEN 128
@@ -154,17 +137,45 @@ void apply_kernelsu_rules(void)
#define CMD_TYPE_CHANGE 8
#define CMD_GENFSCON 9
#ifdef CONFIG_64BIT
struct sepol_data {
u32 cmd;
u32 subcmd;
u64 sepol1;
u64 sepol2;
u64 sepol3;
u64 sepol4;
u64 sepol5;
u64 sepol6;
u64 sepol7;
u64 field_sepol1;
u64 field_sepol2;
u64 field_sepol3;
u64 field_sepol4;
u64 field_sepol5;
u64 field_sepol6;
u64 field_sepol7;
};
#ifdef CONFIG_COMPAT
extern bool ksu_is_compat __read_mostly;
struct sepol_compat_data {
u32 cmd;
u32 subcmd;
u32 field_sepol1;
u32 field_sepol2;
u32 field_sepol3;
u32 field_sepol4;
u32 field_sepol5;
u32 field_sepol6;
u32 field_sepol7;
};
#endif // CONFIG_COMPAT
#else
struct sepol_data {
u32 cmd;
u32 subcmd;
u32 field_sepol1;
u32 field_sepol2;
u32 field_sepol3;
u32 field_sepol4;
u32 field_sepol5;
u32 field_sepol6;
u32 field_sepol7;
};
#endif // CONFIG_64BIT
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
char **object)
@@ -175,24 +186,18 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz,
}
if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
return -EINVAL;
return -1;
}
*object = buf;
return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \
!defined(KSU_COMPAT_USE_SELINUX_STATE)
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
static void reset_avc_cache(void)
static void reset_avc_cache()
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \
!defined(KSU_COMPAT_USE_SELINUX_STATE)
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
avc_ss_reset(0);
selnl_notify_policyload(0);
selinux_status_update_policyload(0);
@@ -207,64 +212,101 @@ static void reset_avc_cache(void)
int handle_sepolicy(unsigned long arg3, void __user *arg4)
{
struct policydb *db;
if (!arg4) {
return -EINVAL;
return -1;
}
if (!getenforce()) {
pr_info("SELinux permissive or disabled when handle policy!\n");
}
u32 cmd, subcmd;
char __user *sepol1, *sepol2, *sepol3, *sepol4, *sepol5, *sepol6, *sepol7;
#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
if (unlikely(ksu_is_compat)) {
struct sepol_compat_data compat_data;
if (copy_from_user(&compat_data, arg4, sizeof(struct sepol_compat_data))) {
pr_err("sepol: copy sepol_data failed.\n");
return -1;
}
sepol1 = compat_ptr(compat_data.field_sepol1);
sepol2 = compat_ptr(compat_data.field_sepol2);
sepol3 = compat_ptr(compat_data.field_sepol3);
sepol4 = compat_ptr(compat_data.field_sepol4);
sepol5 = compat_ptr(compat_data.field_sepol5);
sepol6 = compat_ptr(compat_data.field_sepol6);
sepol7 = compat_ptr(compat_data.field_sepol7);
cmd = compat_data.cmd;
subcmd = compat_data.subcmd;
} else {
struct sepol_data data;
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
pr_err("sepol: copy sepol_data failed.\n");
return -EINVAL;
return -1;
}
sepol1 = data.field_sepol1;
sepol2 = data.field_sepol2;
sepol3 = data.field_sepol3;
sepol4 = data.field_sepol4;
sepol5 = data.field_sepol5;
sepol6 = data.field_sepol6;
sepol7 = data.field_sepol7;
cmd = data.cmd;
subcmd = data.subcmd;
}
#else
// basically for full native, say (64BIT=y COMPAT=n) || (64BIT=n)
struct sepol_data data;
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
pr_err("sepol: copy sepol_data failed.\n");
return -1;
}
sepol1 = data.field_sepol1;
sepol2 = data.field_sepol2;
sepol3 = data.field_sepol3;
sepol4 = data.field_sepol4;
sepol5 = data.field_sepol5;
sepol6 = data.field_sepol6;
sepol7 = data.field_sepol7;
cmd = data.cmd;
subcmd = data.subcmd;
#endif
u32 cmd = data.cmd;
u32 subcmd = data.subcmd;
rcu_read_lock();
mutex_lock(&ksu_rules);
struct policydb *db = get_policydb();
db = get_policydb();
int ret = -EINVAL;
switch (cmd) {
case CMD_NORMAL_PERM: {
int ret = -1;
if (cmd == CMD_NORMAL_PERM) {
char src_buf[MAX_SEPOL_LEN];
char tgt_buf[MAX_SEPOL_LEN];
char cls_buf[MAX_SEPOL_LEN];
char perm_buf[MAX_SEPOL_LEN];
char *s, *t, *c, *p;
if (get_object(src_buf, (void __user *)data.sepol1,
sizeof(src_buf), &s) < 0) {
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (get_object(tgt_buf, (void __user *)data.sepol2,
sizeof(tgt_buf), &t) < 0) {
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (get_object(cls_buf, (void __user *)data.sepol3,
sizeof(cls_buf), &c) < 0) {
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (get_object(perm_buf, (void __user *)data.sepol4,
sizeof(perm_buf), &p) < 0) {
if (get_object(perm_buf, sepol4, sizeof(perm_buf), &p) <
0) {
pr_err("sepol: copy perm failed.\n");
goto exit;
}
bool success = false;
if (subcmd == 1) {
success = ksu_allow(db, s, t, c, p);
} else if (subcmd == 2) {
@@ -276,10 +318,9 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
} else {
pr_err("sepol: unknown subcmd: %d\n", subcmd);
}
ret = success ? 0 : -EINVAL;
break;
}
case CMD_XPERM: {
ret = success ? 0 : -1;
} else if (cmd == CMD_XPERM) {
char src_buf[MAX_SEPOL_LEN];
char tgt_buf[MAX_SEPOL_LEN];
char cls_buf[MAX_SEPOL_LEN];
@@ -289,28 +330,25 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
char perm_set[MAX_SEPOL_LEN];
char *s, *t, *c;
if (get_object(src_buf, (void __user *)data.sepol1,
sizeof(src_buf), &s) < 0) {
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (get_object(tgt_buf, (void __user *)data.sepol2,
sizeof(tgt_buf), &t) < 0) {
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (get_object(cls_buf, (void __user *)data.sepol3,
sizeof(cls_buf), &c) < 0) {
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (strncpy_from_user(operation, (void __user *)data.sepol4,
if (strncpy_from_user(operation, sepol4,
sizeof(operation)) < 0) {
pr_err("sepol: copy operation failed.\n");
goto exit;
}
if (strncpy_from_user(perm_set, (void __user *)data.sepol5,
sizeof(perm_set)) < 0) {
if (strncpy_from_user(perm_set, sepol5, sizeof(perm_set)) <
0) {
pr_err("sepol: copy perm_set failed.\n");
goto exit;
}
@@ -325,14 +363,11 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
} else {
pr_err("sepol: unknown subcmd: %d\n", subcmd);
}
ret = success ? 0 : -EINVAL;
break;
}
case CMD_TYPE_STATE: {
ret = success ? 0 : -1;
} else if (cmd == CMD_TYPE_STATE) {
char src[MAX_SEPOL_LEN];
if (strncpy_from_user(src, (void __user *)data.sepol1,
sizeof(src)) < 0) {
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
@@ -347,20 +382,16 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
}
if (success)
ret = 0;
break;
}
case CMD_TYPE:
case CMD_TYPE_ATTR: {
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
char type[MAX_SEPOL_LEN];
char attr[MAX_SEPOL_LEN];
if (strncpy_from_user(type, (void __user *)data.sepol1,
sizeof(type)) < 0) {
if (strncpy_from_user(type, sepol1, sizeof(type)) < 0) {
pr_err("sepol: copy type failed.\n");
goto exit;
}
if (strncpy_from_user(attr, (void __user *)data.sepol2,
sizeof(attr)) < 0) {
if (strncpy_from_user(attr, sepol2, sizeof(attr)) < 0) {
pr_err("sepol: copy attr failed.\n");
goto exit;
}
@@ -376,13 +407,11 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
goto exit;
}
ret = 0;
break;
}
case CMD_ATTR: {
} else if (cmd == CMD_ATTR) {
char attr[MAX_SEPOL_LEN];
if (strncpy_from_user(attr, (void __user *)data.sepol1,
sizeof(attr)) < 0) {
if (strncpy_from_user(attr, sepol1, sizeof(attr)) < 0) {
pr_err("sepol: copy attr failed.\n");
goto exit;
}
@@ -391,41 +420,36 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
goto exit;
}
ret = 0;
break;
}
case CMD_TYPE_TRANSITION: {
} else if (cmd == CMD_TYPE_TRANSITION) {
char src[MAX_SEPOL_LEN];
char tgt[MAX_SEPOL_LEN];
char cls[MAX_SEPOL_LEN];
char default_type[MAX_SEPOL_LEN];
char object[MAX_SEPOL_LEN];
if (strncpy_from_user(src, (void __user *)data.sepol1,
sizeof(src)) < 0) {
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (strncpy_from_user(tgt, (void __user *)data.sepol2,
sizeof(tgt)) < 0) {
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (strncpy_from_user(cls, (void __user *)data.sepol3,
sizeof(cls)) < 0) {
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (strncpy_from_user(default_type, (void __user *)data.sepol4,
if (strncpy_from_user(default_type, sepol4,
sizeof(default_type)) < 0) {
pr_err("sepol: copy default_type failed.\n");
goto exit;
}
char *real_object;
if ((void __user *)data.sepol5 == NULL) {
if (sepol5 == NULL) {
real_object = NULL;
} else {
if (strncpy_from_user(object,
(void __user *)data.sepol5,
if (strncpy_from_user(object, sepol5,
sizeof(object)) < 0) {
pr_err("sepol: copy object failed.\n");
goto exit;
@@ -437,30 +461,26 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
default_type, real_object);
if (success)
ret = 0;
break;
}
case CMD_TYPE_CHANGE: {
} else if (cmd == CMD_TYPE_CHANGE) {
char src[MAX_SEPOL_LEN];
char tgt[MAX_SEPOL_LEN];
char cls[MAX_SEPOL_LEN];
char default_type[MAX_SEPOL_LEN];
if (strncpy_from_user(src, (void __user *)data.sepol1,
sizeof(src)) < 0) {
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (strncpy_from_user(tgt, (void __user *)data.sepol2,
sizeof(tgt)) < 0) {
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (strncpy_from_user(cls, (void __user *)data.sepol3,
sizeof(cls)) < 0) {
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (strncpy_from_user(default_type, (void __user *)data.sepol4,
if (strncpy_from_user(default_type, sepol4,
sizeof(default_type)) < 0) {
pr_err("sepol: copy default_type failed.\n");
goto exit;
@@ -477,24 +497,20 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
}
if (success)
ret = 0;
break;
}
case CMD_GENFSCON: {
} 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, (void __user *)data.sepol1,
sizeof(name)) < 0) {
if (strncpy_from_user(name, sepol1, sizeof(name)) < 0) {
pr_err("sepol: copy name failed.\n");
goto exit;
}
if (strncpy_from_user(path, (void __user *)data.sepol2,
sizeof(path)) < 0) {
if (strncpy_from_user(path, sepol2, sizeof(path)) < 0) {
pr_err("sepol: copy path failed.\n");
goto exit;
}
if (strncpy_from_user(context, (void __user *)data.sepol3,
sizeof(context)) < 0) {
if (strncpy_from_user(context, sepol3, sizeof(context)) <
0) {
pr_err("sepol: copy context failed.\n");
goto exit;
}
@@ -504,16 +520,12 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
goto exit;
}
ret = 0;
break;
}
default: {
} else {
pr_err("sepol: unknown cmd: %d\n", cmd);
break;
}
}
exit:
mutex_unlock(&ksu_rules);
rcu_read_unlock();
// only allow and xallow needs to reset avc cache, but we cannot do that because
// we are in atomic context. so we just reset it every time.

View File

@@ -1,8 +1,6 @@
#include "linux/cred.h"
#include "linux/sched.h"
#include "linux/security.h"
#include "selinux.h"
#include "objsec.h"
#include "linux/version.h"
#include "selinux_defs.h"
#include "../klog.h" // IWYU pragma: keep
#define KERNEL_SU_DOMAIN "u:r:su:s0"
@@ -36,50 +34,41 @@ static int transive_to_domain(const char *domain)
return error;
}
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 19, 0)
bool __maybe_unused
is_ksu_transition(const struct task_security_struct *old_tsec,
const struct task_security_struct *new_tsec)
{
static u32 ksu_sid;
char *secdata;
u32 seclen;
bool allowed = false;
if (!ksu_sid)
security_secctx_to_secid(KERNEL_SU_DOMAIN,
strlen(KERNEL_SU_DOMAIN), &ksu_sid);
if (security_secid_to_secctx(old_tsec->sid, &secdata, &seclen))
return false;
allowed = (!strcmp("u:r:init:s0", secdata) && new_tsec->sid == ksu_sid);
security_release_secctx(secdata, seclen);
return allowed;
}
#endif
void setup_selinux(const char *domain)
{
if (transive_to_domain(domain)) {
pr_err("transive domain failed.\n");
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)
{
__setenforce(enforce);
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
selinux_state.enforcing = enforce;
#endif
}
bool getenforce(void)
bool getenforce()
{
if (is_selinux_disabled()) {
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
if (selinux_state.disabled) {
return false;
}
#endif
return __is_selinux_enforcing();
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
return selinux_state.enforcing;
#else
return true;
#endif
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
@@ -95,90 +84,47 @@ static inline u32 current_sid(void)
}
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 14, 0)
struct lsm_context {
char *context;
u32 len;
};
static int __security_secid_to_secctx(u32 secid, struct lsm_context *cp)
bool is_ksu_domain()
{
return security_secid_to_secctx(secid, &cp->context, &cp->len);
}
static void __security_release_secctx(struct lsm_context *cp)
{
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
bool is_task_ksu_domain(const struct cred *cred)
{
struct lsm_context ctx;
char *domain;
u32 seclen;
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);
int err = security_secid_to_secctx(current_sid(), &domain, &seclen);
if (err) {
return false;
}
result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0;
__security_release_secctx(&ctx);
result = strncmp(KERNEL_SU_DOMAIN, domain, seclen) == 0;
security_release_secctx(domain, seclen);
return result;
}
bool is_ksu_domain(void)
bool is_zygote(void *sec)
{
current_sid();
return is_task_ksu_domain(current_cred());
}
bool is_context(const struct cred *cred, const char *context)
{
if (!cred) {
return false;
}
const struct task_security_struct *tsec = __selinux_cred(cred);
struct task_security_struct *tsec = (struct task_security_struct *)sec;
if (!tsec) {
return false;
}
struct lsm_context ctx;
char *domain;
u32 seclen;
bool result;
int err = __security_secid_to_secctx(tsec->sid, &ctx);
int err = security_secid_to_secctx(tsec->sid, &domain, &seclen);
if (err) {
return false;
}
result = strncmp(context, ctx.context, ctx.len) == 0;
__security_release_secctx(&ctx);
result = strncmp("u:r:zygote:s0", domain, seclen) == 0;
security_release_secctx(domain, seclen);
return result;
}
bool is_zygote(const struct cred *cred)
{
return is_context(cred, "u:r:zygote:s0");
}
#define DEVPTS_DOMAIN "u:object_r:ksu_file:s0"
bool is_init(const struct cred *cred)
u32 ksu_get_devpts_sid()
{
return is_context(cred, "u:r:init:s0");
}
#define KSU_FILE_DOMAIN "u:object_r:ksu_file:s0"
u32 ksu_get_ksu_file_sid(void)
{
u32 ksu_file_sid = 0;
int err = security_secctx_to_secid(
KSU_FILE_DOMAIN, strlen(KSU_FILE_DOMAIN), &ksu_file_sid);
u32 devpts_sid = 0;
int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN),
&devpts_sid);
if (err) {
pr_info("get ksufile sid err %d\n", err);
pr_info("get devpts sid err %d\n", err);
}
return ksu_file_sid;
return devpts_sid;
}

View File

@@ -3,31 +3,19 @@
#include "linux/types.h"
#include "linux/version.h"
#include "linux/cred.h"
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || \
defined(KSU_COMPAT_HAS_SELINUX_STATE)
#define KSU_COMPAT_USE_SELINUX_STATE
#endif
void setup_selinux(const char *);
void setenforce(bool);
bool getenforce(void);
bool getenforce();
bool is_task_ksu_domain(const struct cred *cred);
bool is_ksu_domain();
bool is_ksu_domain(void);
bool is_zygote(void *cred);
bool is_zygote(const struct cred *cred);
void apply_kernelsu_rules();
bool is_init(const struct cred *cred);
void apply_kernelsu_rules(void);
u32 ksu_get_ksu_file_sid(void);
int handle_sepolicy(unsigned long arg3, void __user *arg4);
u32 ksu_get_devpts_sid();
#endif

View File

@@ -1,42 +0,0 @@
#ifndef __KSU_H_SELINUX_DEFS
#define __KSU_H_SELINUX_DEFS
#include "selinux.h"
#include "objsec.h"
#ifdef SAMSUNG_SELINUX_PORTING
#include "security.h" // Samsung SELinux Porting
#endif
#ifndef KSU_COMPAT_USE_SELINUX_STATE
#include "avc.h"
#endif
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
#ifdef KSU_COMPAT_USE_SELINUX_STATE
#define is_selinux_disabled() (selinux_state.disabled)
#else
#define is_selinux_disabled() (selinux_disabled)
#endif
#else
#define is_selinux_disabled() (0)
#endif
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
#ifdef KSU_COMPAT_USE_SELINUX_STATE
#define __is_selinux_enforcing() (selinux_state.enforcing)
#define __setenforce(val) selinux_state.enforcing = val
#elif defined(SAMSUNG_SELINUX_PORTING) || !defined(KSU_COMPAT_USE_SELINUX_STATE)
#define __is_selinux_enforcing() (selinux_enforcing)
#define __setenforce(val) selinux_enforcing = val
#endif
#else
#define __is_selinux_enforcing() (1)
#define __setenforce(val)
#endif
#ifdef KSU_OPTIONAL_SELINUX_CRED
#define __selinux_cred(cred) (selinux_cred(cred))
#else
#define __selinux_cred(cred) (cred->security)
#endif
#endif

View File

@@ -355,7 +355,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
if (datum->u.xperms == NULL) {
datum->u.xperms =
(struct avtab_extended_perms *)(kzalloc(
(struct avtab_extended_perms *)(kmalloc(
sizeof(xperms), GFP_KERNEL));
if (!datum->u.xperms) {
pr_err("alloc xperms failed\n");
@@ -524,7 +524,6 @@ static bool add_filename_trans(struct policydb *db, const char *s,
return false;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
struct filename_trans_key key;
key.ttype = tgt->value;
key.tclass = cls->value;
@@ -532,13 +531,8 @@ static bool add_filename_trans(struct policydb *db, const char *s,
struct filename_trans_datum *last = NULL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
struct filename_trans_datum *trans =
policydb_filenametr_search(db, &key);
#else
struct filename_trans_datum *trans =
hashtab_search(&db->filename_trans, &key);
#endif
while (trans) {
if (ebitmap_get_bit(&trans->stypes, src->value - 1)) {
// Duplicate, overwrite existing data and return
@@ -555,7 +549,7 @@ static bool add_filename_trans(struct policydb *db, const char *s,
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
1, GFP_ATOMIC);
struct filename_trans_key *new_key =
(struct filename_trans_key *)kzalloc(sizeof(*new_key),
(struct filename_trans_key *)kmalloc(sizeof(*new_key),
GFP_ATOMIC);
*new_key = key;
new_key->name = kstrdup(key.name, GFP_ATOMIC);
@@ -567,39 +561,6 @@ static bool add_filename_trans(struct policydb *db, const char *s,
db->compat_filename_trans_count++;
return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0;
#else // < 5.7.0, has no filename_trans_key, but struct filename_trans
struct filename_trans key;
key.ttype = tgt->value;
key.tclass = cls->value;
key.name = (char *)o;
struct filename_trans_datum *trans =
hashtab_search(db->filename_trans, &key);
if (trans == NULL) {
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
1, GFP_ATOMIC);
if (!trans) {
pr_err("add_filename_trans: Failed to alloc datum\n");
return false;
}
struct filename_trans *new_key =
(struct filename_trans *)kzalloc(sizeof(*new_key),
GFP_ATOMIC);
if (!new_key) {
pr_err("add_filename_trans: Failed to alloc new_key\n");
return false;
}
*new_key = key;
new_key->name = kstrdup(key.name, GFP_ATOMIC);
trans->otype = def->value;
hashtab_insert(db->filename_trans, new_key, trans);
}
return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) ==
0;
#endif
}
static bool add_genfscon(struct policydb *db, const char *fs_name,
@@ -626,7 +587,6 @@ static void *ksu_realloc(void *old, size_t new_size, size_t old_size)
static bool add_type(struct policydb *db, const char *type_name, bool attr)
{
#ifdef KSU_SUPPORT_ADD_TYPE
struct type_datum *type = symtab_search(&db->p_types, type_name);
if (type) {
pr_warn("Type %s already exists\n", type_name);
@@ -656,7 +616,6 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
return false;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
struct ebitmap *new_type_attr_map_array =
ksu_realloc(db->type_attr_map_array,
value * sizeof(struct ebitmap),
@@ -703,171 +662,6 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
}
return true;
#elif defined(CONFIG_IS_HW_HISI)
/*
* Huawei use type_attr_map and type_val_to_struct.
* And use ebitmap not flex_array.
*/
size_t new_size = sizeof(struct ebitmap) * db->p_types.nprim;
struct ebitmap *new_type_attr_map =
(krealloc(db->type_attr_map, new_size, GFP_ATOMIC));
struct type_datum **new_type_val_to_struct =
krealloc(db->type_val_to_struct,
sizeof(*db->type_val_to_struct) * db->p_types.nprim,
GFP_ATOMIC);
if (!new_type_attr_map) {
pr_err("add_type: alloc type_attr_map failed\n");
return false;
}
if (!new_type_val_to_struct) {
pr_err("add_type: alloc type_val_to_struct failed\n");
return false;
}
char **new_val_to_name_types =
krealloc(db->sym_val_to_name[SYM_TYPES],
sizeof(char *) * db->symtab[SYM_TYPES].nprim,
GFP_KERNEL);
if (!new_val_to_name_types) {
pr_err("add_type: alloc val_to_name failed\n");
return false;
}
db->type_attr_map = new_type_attr_map;
ebitmap_init(&db->type_attr_map[value - 1], HISI_SELINUX_EBITMAP_RO);
ebitmap_set_bit(&db->type_attr_map[value - 1], value - 1, 1);
db->type_val_to_struct = new_type_val_to_struct;
db->type_val_to_struct[value - 1] = type;
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
db->sym_val_to_name[SYM_TYPES][value - 1] = key;
int i;
for (i = 0; i < db->p_roles.nprim; ++i) {
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
1);
}
return true;
#else
// flex_array is not extensible, we need to create a new bigger one instead
struct flex_array *new_type_attr_map_array =
flex_array_alloc(sizeof(struct ebitmap), db->p_types.nprim,
GFP_ATOMIC | __GFP_ZERO);
struct flex_array *new_type_val_to_struct =
flex_array_alloc(sizeof(struct type_datum *), db->p_types.nprim,
GFP_ATOMIC | __GFP_ZERO);
struct flex_array *new_val_to_name_types =
flex_array_alloc(sizeof(char *), db->symtab[SYM_TYPES].nprim,
GFP_ATOMIC | __GFP_ZERO);
if (!new_type_attr_map_array) {
pr_err("add_type: alloc type_attr_map_array failed\n");
return false;
}
if (!new_type_val_to_struct) {
pr_err("add_type: alloc type_val_to_struct failed\n");
return false;
}
if (!new_val_to_name_types) {
pr_err("add_type: alloc val_to_name failed\n");
return false;
}
// preallocate so we don't have to worry about the put ever failing
if (flex_array_prealloc(new_type_attr_map_array, 0, db->p_types.nprim,
GFP_ATOMIC | __GFP_ZERO)) {
pr_err("add_type: prealloc type_attr_map_array failed\n");
return false;
}
if (flex_array_prealloc(new_type_val_to_struct, 0, db->p_types.nprim,
GFP_ATOMIC | __GFP_ZERO)) {
pr_err("add_type: prealloc type_val_to_struct_array failed\n");
return false;
}
if (flex_array_prealloc(new_val_to_name_types, 0,
db->symtab[SYM_TYPES].nprim,
GFP_ATOMIC | __GFP_ZERO)) {
pr_err("add_type: prealloc val_to_name_types failed\n");
return false;
}
int j;
void *old_elem;
// copy the old data or pointers to new flex arrays
for (j = 0; j < db->type_attr_map_array->total_nr_elements; j++) {
old_elem = flex_array_get(db->type_attr_map_array, j);
if (old_elem)
flex_array_put(new_type_attr_map_array, j, old_elem,
GFP_ATOMIC | __GFP_ZERO);
}
for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) {
old_elem = flex_array_get_ptr(db->type_val_to_struct_array, j);
if (old_elem)
flex_array_put_ptr(new_type_val_to_struct, j, old_elem,
GFP_ATOMIC | __GFP_ZERO);
}
for (j = 0; j < db->symtab[SYM_TYPES].nprim; j++) {
old_elem =
flex_array_get_ptr(db->sym_val_to_name[SYM_TYPES], j);
if (old_elem)
flex_array_put_ptr(new_val_to_name_types, j, old_elem,
GFP_ATOMIC | __GFP_ZERO);
}
// store the pointer of old flex arrays first, when assigning new ones we
// should free it
struct flex_array *old_fa;
old_fa = db->type_attr_map_array;
db->type_attr_map_array = new_type_attr_map_array;
if (old_fa) {
flex_array_free(old_fa);
}
ebitmap_init(flex_array_get(db->type_attr_map_array, value - 1));
ebitmap_set_bit(flex_array_get(db->type_attr_map_array, value - 1),
value - 1, 1);
old_fa = db->type_val_to_struct_array;
db->type_val_to_struct_array = new_type_val_to_struct;
if (old_fa) {
flex_array_free(old_fa);
}
flex_array_put_ptr(db->type_val_to_struct_array, value - 1, type,
GFP_ATOMIC | __GFP_ZERO);
old_fa = db->sym_val_to_name[SYM_TYPES];
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
if (old_fa) {
flex_array_free(old_fa);
}
flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1, key,
GFP_ATOMIC | __GFP_ZERO);
int i;
for (i = 0; i < db->p_roles.nprim; ++i) {
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
1);
}
return true;
#endif
#else
return false;
#endif
}
static bool set_type_state(struct policydb *db, const char *type_name,
@@ -902,18 +696,7 @@ static bool set_type_state(struct policydb *db, const char *type_name,
static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
struct type_datum *attr)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1];
#elif defined(CONFIG_IS_HW_HISI)
/*
* HISI_SELINUX_EBITMAP_RO is Huawei's unique features.
*/
struct ebitmap *sattr = &db->type_attr_map[type->value - 1],
HISI_SELINUX_EBITMAP_RO;
#else
struct ebitmap *sattr =
flex_array_get(db->type_attr_map_array, type->value - 1);
#endif
ebitmap_set_bit(sattr, attr->value - 1, 1);
struct hashtab_node *node;

View File

@@ -1,186 +0,0 @@
#include <linux/compiler.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
#include <linux/sched/signal.h>
#endif
#include <linux/slab.h>
#include <linux/task_work.h>
#include <linux/thread_info.h>
#include <linux/seccomp.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/stddef.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/uidgid.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"
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(void)
{
if (is_manager()) {
// we are manager, allow!
return true;
}
return ksu_is_allow_uid_for_current(current_uid().val);
}
// force_sig kcompat, TODO: move it out of core_hook.c
// https://elixir.bootlin.com/linux/v5.3-rc1/source/kernel/signal.c#L1613
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
#define __force_sig(sig) force_sig(sig)
#else
#define __force_sig(sig) force_sig(sig, current)
#endif
extern void disable_seccomp(struct task_struct *tsk);
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid)
{
// we rely on the fact that zygote always call setresuid(3) with same uids
uid_t new_uid = ruid;
uid_t old_uid = current_uid().val;
if (old_uid != new_uid)
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 ksu manager(uid=%d)\n", new_uid);
ksu_install_fd();
spin_lock_irq(&current->sighand->siglock);
ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot);
ksu_set_task_tracepoint_flag(current);
spin_unlock_irq(&current->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(&current->sighand->siglock);
ksu_seccomp_allow_cache(current->seccomp.filter,
__NR_reboot);
spin_unlock_irq(&current->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(&current->sighand->siglock);
disable_seccomp(current);
spin_unlock_irq(&current->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;
}
extern void ksu_lsm_hook_init(void);
void ksu_setuid_hook_init(void)
{
ksu_kernel_umount_init();
ksu_lsm_hook_init(); // <4.11
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);
}

View File

@@ -1,13 +0,0 @@
#ifndef __KSU_H_KSU_SETUID_HOOK
#define __KSU_H_KSU_SETUID_HOOK
#include <linux/init.h>
#include <linux/types.h>
void ksu_setuid_hook_init(void);
void ksu_setuid_hook_exit(void);
// Handler functions for hook_manager
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid);
#endif

View File

@@ -1,7 +1,7 @@
#!/bin/sh
set -eu
GKI_ROOT=$(pwd)
KERNEL_ROOT=$(pwd)
display_usage() {
echo "Usage: $0 [--cleanup | <commit-or-tag>]"
@@ -12,10 +12,10 @@ display_usage() {
}
initialize_variables() {
if test -d "$GKI_ROOT/common/drivers"; then
DRIVER_DIR="$GKI_ROOT/common/drivers"
elif test -d "$GKI_ROOT/drivers"; then
DRIVER_DIR="$GKI_ROOT/drivers"
if test -d "$KERNEL_ROOT/common/drivers"; then
DRIVER_DIR="$KERNEL_ROOT/common/drivers"
elif test -d "$KERNEL_ROOT/drivers"; then
DRIVER_DIR="$KERNEL_ROOT/drivers"
else
echo '[ERROR] "drivers/" directory not found.'
exit 127
@@ -30,21 +30,21 @@ perform_cleanup() {
echo "[+] Cleaning up..."
[ -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 "drivers/kernelsu/Kconfig" "$DRIVER_KCONFIG" && sed -i '/drivers\/kernelsu\/Kconfig/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted."
if [ -d "$GKI_ROOT/KernelSU" ]; then
rm -rf "$GKI_ROOT/KernelSU" && echo "[-] KernelSU directory deleted."
grep -q "kernelsu" "$DRIVER_KCONFIG" && sed -i '/kernelsu/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted."
if [ -d "$KERNEL_ROOT/KernelSU" ]; then
rm -rf "$KERNEL_ROOT/KernelSU" && echo "[-] KernelSU directory deleted."
fi
}
# Sets up or update KernelSU environment
setup_kernelsu() {
echo "[+] Setting up KernelSU..."
# Clone the repository and rename it to KernelSU
if [ ! -d "$GKI_ROOT/KernelSU" ]; then
# Clone the repository
if [ ! -d "$KERNEL_ROOT/KernelSU" ]; then
git clone https://github.com/SukiSU-Ultra/SukiSU-Ultra KernelSU
echo "[+] Repository cloned and renamed to KernelSU."
echo "[+] Repository cloned."
fi
cd "$GKI_ROOT/KernelSU"
cd "$KERNEL_ROOT/KernelSU"
git stash && echo "[-] Stashed current changes."
if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then
git checkout main && echo "[-] Switched to main branch."
@@ -56,11 +56,11 @@ setup_kernelsu() {
git checkout "$1" && echo "[-] Checked out $1." || echo "[-] Checkout default branch"
fi
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
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$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 "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."
echo '[+] Done.'
}

View File

@@ -1,63 +1,35 @@
#include <linux/dcache.h>
#include <linux/security.h>
#include <asm/current.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/kprobes.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <linux/ptrace.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
#include <linux/compiler.h>
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/task_stack.h>
#else
#include <linux/sched.h>
#endif
#include "objsec.h"
#include "allowlist.h"
#include "feature.h"
#include "arch.h"
#include "klog.h" // IWYU pragma: keep
#include "ksud.h"
#include "sucompat.h"
#include "app_profile.h"
#include "syscall_hook_manager.h"
#include "kernel_compat.h"
#define SU_PATH "/system/bin/su"
#define SH_PATH "/system/bin/sh"
bool ksu_su_compat_enabled __read_mostly = true;
extern void escape_to_root();
static const char su[] = SU_PATH;
static const char ksud_path[] = KSUD_PATH;
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,
};
#ifndef CONFIG_KPROBES
static bool ksu_sucompat_non_kp __read_mostly = true;
#endif
static void __user *userspace_stack_buffer(const void *d, size_t len)
{
// To avoid having to mmap a page in userspace, just write below the stack
// pointer.
/* To avoid having to mmap a page in userspace, just write below the stack
* pointer. */
char __user *p = (void __user *)current_user_stack_pointer() - len;
return copy_to_user(p, d, len) ? NULL : p;
@@ -72,140 +44,238 @@ static char __user *sh_user_path(void)
static char __user *ksud_user_path(void)
{
static const char ksud_path[] = KSUD_PATH;
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
}
static inline bool __is_su_allowed(const void *ptr_to_check)
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
int *__unused_flags)
{
#ifndef KSU_SHOULD_USE_NEW_TP
if (!ksu_su_compat_enabled)
return false;
const char su[] = SU_PATH;
#ifndef CONFIG_KPROBES
if (!ksu_sucompat_non_kp) {
return 0;
}
#endif
if (!ksu_is_allow_uid_for_current(current_uid().val))
return false;
if (unlikely(!ptr_to_check))
return false;
if (!ksu_is_allow_uid(current_uid().val)) {
return 0;
}
return true;
}
#define is_su_allowed(ptr) __is_su_allowed((const void *)ptr)
static int ksu_sucompat_user_common(const char __user **filename_user,
const char *syscall_name,
const bool escalate)
{
char path[sizeof(su)]; // sizeof includes nullterm already!
char path[sizeof(su) + 1];
memset(path, 0, sizeof(path));
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (memcmp(path, su, sizeof(su)))
return 0;
if (escalate) {
pr_info("%s su found\n", syscall_name);
*filename_user = ksud_user_path();
escape_with_root_profile(); // escalate !!
} else {
pr_info("%s su->sh!\n", syscall_name);
if (unlikely(!memcmp(path, su, sizeof(su)))) {
pr_info("faccessat su->sh!\n");
*filename_user = sh_user_path();
}
return 0;
}
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
int *__unused_flags)
{
if (!is_su_allowed(filename_user))
return 0;
return ksu_sucompat_user_common(filename_user, "faccessat", false);
}
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
{
if (!is_su_allowed(filename_user))
// const char sh[] = SH_PATH;
const char su[] = SU_PATH;
#ifndef CONFIG_KPROBES
if (!ksu_sucompat_non_kp) {
return 0;
}
#endif
if (!ksu_is_allow_uid(current_uid().val)) {
return 0;
}
if (unlikely(!filename_user)) {
return 0;
}
char path[sizeof(su) + 1];
memset(path, 0, sizeof(path));
// Remove this later!! we use syscall hook, so this will never happen!!!!!
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0
// it becomes a `struct filename *` after 5.18
// https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216
const char sh[] = SH_PATH;
struct filename *filename = *((struct filename **)filename_user);
if (IS_ERR(filename)) {
return 0;
}
if (likely(memcmp(filename->name, su, sizeof(su))))
return 0;
pr_info("vfs_statx su->sh!\n");
memcpy((void *)filename->name, sh, sizeof(sh));
#else
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (unlikely(!memcmp(path, su, sizeof(su)))) {
pr_info("newfstatat su->sh!\n");
*filename_user = sh_user_path();
}
#endif
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
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;
#ifndef CONFIG_KPROBES
if (!ksu_sucompat_non_kp) {
return 0;
}
#endif
if (unlikely(!filename_ptr))
return 0;
return ksu_sucompat_user_common(filename_user, "newfstatat", false);
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)
{
if (!is_su_allowed(filename_user))
const char su[] = SU_PATH;
char path[sizeof(su) + 1];
#ifndef CONFIG_KPROBES
if (!ksu_sucompat_non_kp){
return 0;
}
#endif
if (unlikely(!filename_user))
return 0;
return ksu_sucompat_user_common(filename_user, "sys_execve", true);
}
memset(path, 0, sizeof(path));
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
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;
if (!is_su_allowed(filename_ptr))
if (likely(memcmp(path, su, sizeof(su))))
return 0;
filename = *filename_ptr;
if (IS_ERR(filename))
if (!ksu_is_allow_uid(current_uid().val))
return 0;
if (likely(memcmp(filename->name, su, sizeof(su))))
return 0;
pr_info("sys_execve su found\n");
*filename_user = ksud_user_path();
pr_info("do_execveat_common su found\n");
memcpy((void *)filename->name, ksud_path, sizeof(ksud_path));
escape_with_root_profile();
escape_to_root();
return 0;
}
int __ksu_handle_devpts(struct inode *inode)
#ifdef CONFIG_KPROBES
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
#ifndef KSU_SHOULD_USE_NEW_TP
if (!ksu_su_compat_enabled)
return 0;
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
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(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);
// sucompat: permited process can execute 'su' to gain root access.
void ksu_sucompat_init()
{
#ifdef CONFIG_KPROBES
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
#else
struct inode_security_struct *sec = (struct inode_security_struct *)inode->i_security;
ksu_sucompat_non_kp = true;
pr_info("ksu_sucompat_init: hooks enabled: execve/execveat_su, faccessat, stat\n");
#endif
if (ksu_file_sid && sec)
sec->sid = ksu_file_sid;
return 0;
}
// sucompat: permitted process can execute 'su' to gain root access.
void ksu_sucompat_init(void)
void ksu_sucompat_exit()
{
if (ksu_register_feature_handler(&su_compat_handler)) {
pr_err("Failed to register su_compat feature handler\n");
#ifdef CONFIG_KPROBES
for (int i = 0; i < ARRAY_SIZE(su_kps); i++) {
destroy_kprobe(&su_kps[i]);
}
}
void ksu_sucompat_exit(void)
{
ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT);
#else
ksu_sucompat_non_kp = false;
pr_info("ksu_sucompat_exit: hooks disabled: execve/execveat_su, faccessat, stat\n");
#endif
}

View File

@@ -1,18 +0,0 @@
#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(int *fd, const char __user **filename_user,
void *__never_use_argv, void *__never_use_envp,
int *__never_use_flags);
#endif

View File

@@ -1,993 +0,0 @@
#include <linux/anon_inodes.h>
#include <linux/capability.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/fdtable.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/kprobes.h>
#include <linux/syscalls.h>
#include <linux/task_work.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include "supercalls.h"
#include "arch.h"
#include "allowlist.h"
#include "feature.h"
#include "klog.h" // IWYU pragma: keep
#include "ksud.h"
#include "kernel_compat.h"
#include "kernel_umount.h"
#include "manager.h"
#include "selinux/selinux.h"
#include "objsec.h"
#include "file_wrapper.h"
#include "syscall_hook_manager.h"
#include "throne_comm.h"
#include "dynamic_manager.h"
bool ksu_uid_scanner_enabled = false;
// Permission check functions
bool only_manager(void)
{
return is_manager();
}
bool only_root(void)
{
return current_uid().val == 0;
}
bool manager_or_root(void)
{
return current_uid().val == 0 || is_manager();
}
bool always_allow(void)
{
return true; // No permission check
}
bool allowed_for_su(void)
{
bool is_allowed = is_manager() || ksu_is_allow_uid_for_current(current_uid().val);
return is_allowed;
}
static void init_uid_scanner(void)
{
ksu_uid_init();
do_load_throne_state(NULL);
if (ksu_uid_scanner_enabled) {
int ret = ksu_throne_comm_init();
if (ret != 0) {
pr_err("Failed to initialize throne communication: %d\n", ret);
}
}
}
static int do_grant_root(void __user *arg)
{
// we already check uid above on allowed_for_su()
pr_info("allow root for: %d\n", current_uid().val);
escape_with_root_profile();
return 0;
}
static int do_get_info(void __user *arg)
{
struct ksu_get_info_cmd cmd = {.version = KERNEL_SU_VERSION, .flags = 0};
#ifdef MODULE
cmd.flags |= 0x1;
#endif
if (is_manager()) {
cmd.flags |= 0x2;
}
cmd.features = KSU_FEATURE_MAX;
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("get_version: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
static int do_report_event(void __user *arg)
{
struct ksu_report_event_cmd cmd;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
return -EFAULT;
}
switch (cmd.event) {
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();
init_uid_scanner();
ksu_dynamic_manager_init();
}
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");
on_boot_completed();
}
break;
}
case EVENT_MODULE_MOUNTED: {
pr_info("module mounted!\n");
on_module_mounted();
break;
}
default:
break;
}
return 0;
}
static int do_set_sepolicy(void __user *arg)
{
struct ksu_set_sepolicy_cmd cmd;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
return -EFAULT;
}
return handle_sepolicy(cmd.cmd, (void __user *)cmd.arg);
}
static int do_check_safemode(void __user *arg)
{
struct ksu_check_safemode_cmd cmd;
cmd.in_safe_mode = ksu_is_safe_mode();
if (cmd.in_safe_mode) {
pr_warn("safemode enabled!\n");
}
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("check_safemode: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
static int do_get_allow_list(void __user *arg)
{
struct ksu_get_allow_list_cmd cmd;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
return -EFAULT;
}
bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, true);
if (!success) {
return -EFAULT;
}
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("get_allow_list: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
static int do_get_deny_list(void __user *arg)
{
struct ksu_get_allow_list_cmd cmd;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
return -EFAULT;
}
bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, false);
if (!success) {
return -EFAULT;
}
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("get_deny_list: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
static int do_uid_granted_root(void __user *arg)
{
struct ksu_uid_granted_root_cmd cmd;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
return -EFAULT;
}
cmd.granted = ksu_is_allow_uid_for_current(cmd.uid);
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("uid_granted_root: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
static int do_uid_should_umount(void __user *arg)
{
struct ksu_uid_should_umount_cmd cmd;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
return -EFAULT;
}
cmd.should_umount = ksu_uid_should_umount(cmd.uid);
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("uid_should_umount: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
static int do_get_manager_uid(void __user *arg)
{
struct ksu_get_manager_uid_cmd cmd;
cmd.uid = ksu_get_manager_uid();
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("get_manager_uid: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
static int do_get_app_profile(void __user *arg)
{
struct ksu_get_app_profile_cmd cmd;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("get_app_profile: copy_from_user failed\n");
return -EFAULT;
}
if (!ksu_get_app_profile(&cmd.profile)) {
return -ENOENT;
}
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("get_app_profile: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
static int do_set_app_profile(void __user *arg)
{
struct ksu_set_app_profile_cmd cmd;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("set_app_profile: copy_from_user failed\n");
return -EFAULT;
}
if (!ksu_set_app_profile(&cmd.profile, true)) {
return -EFAULT;
}
return 0;
}
static int do_get_feature(void __user *arg)
{
struct ksu_get_feature_cmd cmd;
bool supported;
int ret;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("get_feature: copy_from_user failed\n");
return -EFAULT;
}
ret = ksu_get_feature(cmd.feature_id, &cmd.value, &supported);
cmd.supported = supported ? 1 : 0;
if (ret && supported) {
pr_err("get_feature: failed for feature %u: %d\n", cmd.feature_id, ret);
return ret;
}
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("get_feature: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
static int do_set_feature(void __user *arg)
{
struct ksu_set_feature_cmd cmd;
int ret;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("set_feature: copy_from_user failed\n");
return -EFAULT;
}
ret = ksu_set_feature(cmd.feature_id, cmd.value);
if (ret) {
pr_err("set_feature: failed for feature %u: %d\n", cmd.feature_id, ret);
return ret;
}
return 0;
}
static int do_get_wrapper_fd(void __user *arg) {
if (!ksu_file_sid) {
return -EINVAL;
}
struct ksu_get_wrapper_fd_cmd cmd;
int ret;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("get_wrapper_fd: copy_from_user failed\n");
return -EFAULT;
}
struct file* f = fget(cmd.fd);
if (!f) {
return -EBADF;
}
struct ksu_file_wrapper *data = ksu_create_file_wrapper(f);
if (data == NULL) {
ret = -ENOMEM;
goto put_orig_file;
}
// kcompat for older kernel
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
#define getfd_secure anon_inode_create_getfd
#elif defined(KSU_HAS_GETFD_SECURE)
#define getfd_secure anon_inode_getfd_secure
#else
// technically not a secure inode, but, this is the only way so.
#define getfd_secure(name, ops, data, flags, __unused) \
anon_inode_getfd(name, ops, data, flags)
#endif
ret = getfd_secure("[ksu_fdwrapper]", &data->ops, data, f->f_flags, NULL);
if (ret < 0) {
pr_err("ksu_fdwrapper: getfd failed: %d\n", ret);
goto put_wrapper_data;
}
struct file* pf = fget(ret);
struct inode* wrapper_inode = file_inode(pf);
// copy original inode mode
wrapper_inode->i_mode = file_inode(f)->i_mode;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || defined(KSU_OPTIONAL_SELINUX_INODE)
struct inode_security_struct *sec = selinux_inode(wrapper_inode);
#else
struct inode_security_struct *sec = (struct inode_security_struct *)wrapper_inode->i_security;
#endif
if (sec) {
sec->sid = ksu_file_sid;
}
fput(pf);
goto put_orig_file;
put_wrapper_data:
ksu_delete_file_wrapper(data);
put_orig_file:
fput(f);
return ret;
}
static int do_manage_mark(void __user *arg)
{
struct ksu_manage_mark_cmd cmd;
int ret = 0;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("manage_mark: copy_from_user failed\n");
return -EFAULT;
}
switch (cmd.operation) {
case KSU_MARK_GET: {
// Get task mark status
ret = ksu_get_task_mark(cmd.pid);
if (ret < 0) {
pr_err("manage_mark: get failed for pid %d: %d\n", cmd.pid, ret);
return ret;
}
cmd.result = (u32)ret;
break;
}
case KSU_MARK_MARK: {
if (cmd.pid == 0) {
ksu_mark_all_process();
} else {
ret = ksu_set_task_mark(cmd.pid, true);
if (ret < 0) {
pr_err("manage_mark: set_mark failed for pid %d: %d\n", cmd.pid,
ret);
return ret;
}
}
break;
}
case KSU_MARK_UNMARK: {
if (cmd.pid == 0) {
ksu_unmark_all_process();
} else {
ret = ksu_set_task_mark(cmd.pid, false);
if (ret < 0) {
pr_err("manage_mark: set_unmark failed for pid %d: %d\n",
cmd.pid, ret);
return ret;
}
}
break;
}
case KSU_MARK_REFRESH: {
ksu_mark_running_process();
pr_info("manage_mark: refreshed running processes\n");
break;
}
default: {
pr_err("manage_mark: invalid operation %u\n", cmd.operation);
return -EINVAL;
}
}
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("manage_mark: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
struct list_head mount_list = LIST_HEAD_INIT(mount_list);
DECLARE_RWSEM(mount_list_lock);
static int add_try_umount(void __user *arg)
{
struct mount_entry *new_entry, *entry, *tmp;
struct ksu_add_try_umount_cmd cmd;
char buf[256] = { 0 };
if (copy_from_user(&cmd, arg, sizeof cmd))
return -EFAULT;
switch (cmd.mode) {
case KSU_UMOUNT_WIPE: {
struct mount_entry *entry, *tmp;
down_write(&mount_list_lock);
list_for_each_entry_safe (entry, tmp, &mount_list, list) {
pr_info("wipe_umount_list: removing entry: %s\n",
entry->umountable);
list_del(&entry->list);
kfree(entry->umountable);
kfree(entry);
}
up_write(&mount_list_lock);
return 0;
}
case KSU_UMOUNT_ADD: {
long len = strncpy_from_user(buf, (const char __user *)cmd.arg,
256);
if (len <= 0)
return -EFAULT;
buf[sizeof(buf) - 1] = '\0';
new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
if (!new_entry)
return -ENOMEM;
new_entry->umountable = kstrdup(buf, GFP_KERNEL);
if (!new_entry->umountable) {
kfree(new_entry);
return -1;
}
down_write(&mount_list_lock);
// disallow dupes
// if this gets too many, we can consider moving this whole task to a kthread
list_for_each_entry (entry, &mount_list, list) {
if (!strcmp(entry->umountable, buf)) {
pr_info("cmd_add_try_umount: %s is already here!\n",
buf);
up_write(&mount_list_lock);
kfree(new_entry->umountable);
kfree(new_entry);
return -1;
}
}
// now check flags and add
// this also serves as a null check
if (cmd.flags)
new_entry->flags = cmd.flags;
else
new_entry->flags = 0;
// debug
list_add(&new_entry->list, &mount_list);
up_write(&mount_list_lock);
pr_info("cmd_add_try_umount: %s added!\n", buf);
return 0;
}
// this is just strcmp'd wipe anyway
case KSU_UMOUNT_DEL: {
long len = strncpy_from_user(buf, (const char __user *)cmd.arg,
sizeof(buf) - 1);
if (len <= 0)
return -EFAULT;
buf[sizeof(buf) - 1] = '\0';
down_write(&mount_list_lock);
list_for_each_entry_safe (entry, tmp, &mount_list, list) {
if (!strcmp(entry->umountable, buf)) {
pr_info("cmd_add_try_umount: entry removed: %s\n",
entry->umountable);
list_del(&entry->list);
kfree(entry->umountable);
kfree(entry);
}
}
up_write(&mount_list_lock);
return 0;
}
default: {
pr_err("cmd_add_try_umount: invalid operation %u\n", cmd.mode);
return -EINVAL;
}
} // switch(cmd.mode)
return 0;
}
static int do_nuke_ext4_sysfs(void __user *arg)
{
struct ksu_nuke_ext4_sysfs_cmd cmd;
char mnt[256];
long ret;
if (copy_from_user(&cmd, arg, sizeof(cmd)))
return -EFAULT;
if (!cmd.arg)
return -EINVAL;
memset(mnt, 0, sizeof(mnt));
ret = strncpy_from_user(mnt, cmd.arg, sizeof(mnt));
if (ret < 0) {
pr_err("nuke ext4 copy mnt failed: %ld\\n", ret);
return -EFAULT; // 或者 return ret;
}
if (ret == sizeof(mnt)) {
pr_err("nuke ext4 mnt path too long\\n");
return -ENAMETOOLONG;
}
pr_info("do_nuke_ext4_sysfs: %s\n", mnt);
return nuke_ext4_sysfs(mnt);
}
// 100. GET_FULL_VERSION - Get full version string
static int do_get_full_version(void __user *arg)
{
struct ksu_get_full_version_cmd cmd = {0};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
strscpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full));
#else
strlcpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full));
#endif
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("get_full_version: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
// 101. HOOK_TYPE - Get hook type
static int do_get_hook_type(void __user *arg)
{
struct ksu_hook_type_cmd cmd = {0};
const char *type = "Tracepoint";
#if defined(CONFIG_KSU_MANUAL_HOOK)
type = "Manual";
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
strscpy(cmd.hook_type, type, sizeof(cmd.hook_type));
#else
strlcpy(cmd.hook_type, type, sizeof(cmd.hook_type));
#endif
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("get_hook_type: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
// 102. ENABLE_KPM - Check if KPM is enabled
static int do_enable_kpm(void __user *arg)
{
struct ksu_enable_kpm_cmd cmd;
cmd.enabled = IS_ENABLED(CONFIG_KPM);
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("enable_kpm: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
static int do_dynamic_manager(void __user *arg)
{
struct ksu_dynamic_manager_cmd cmd;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("dynamic_manager: copy_from_user failed\n");
return -EFAULT;
}
int ret = ksu_handle_dynamic_manager(&cmd.config);
if (ret)
return ret;
if (cmd.config.operation == DYNAMIC_MANAGER_OP_GET &&
copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("dynamic_manager: copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
static int do_get_managers(void __user *arg)
{
struct ksu_get_managers_cmd cmd;
int ret = ksu_get_active_managers(&cmd.manager_info);
if (ret)
return ret;
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("get_managers: copy_from_user failed\n");
return -EFAULT;
}
return 0;
}
static int do_enable_uid_scanner(void __user *arg)
{
struct ksu_enable_uid_scanner_cmd cmd;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("enable_uid_scanner: copy_from_user failed\n");
return -EFAULT;
}
switch (cmd.operation) {
case UID_SCANNER_OP_GET_STATUS: {
bool status = ksu_uid_scanner_enabled;
if (copy_to_user((void __user *)cmd.status_ptr, &status, sizeof(status))) {
pr_err("enable_uid_scanner: copy status failed\n");
return -EFAULT;
}
break;
}
case UID_SCANNER_OP_TOGGLE: {
bool enabled = cmd.enabled;
if (enabled == ksu_uid_scanner_enabled) {
pr_info("enable_uid_scanner: no need to change, already %s\n",
enabled ? "enabled" : "disabled");
break;
}
if (enabled) {
// Enable UID scanner
int ret = ksu_throne_comm_init();
if (ret != 0) {
pr_err("enable_uid_scanner: failed to initialize: %d\n", ret);
return -EFAULT;
}
pr_info("enable_uid_scanner: enabled\n");
} else {
// Disable UID scanner
ksu_throne_comm_exit();
pr_info("enable_uid_scanner: disabled\n");
}
ksu_uid_scanner_enabled = enabled;
ksu_throne_comm_save_state();
break;
}
case UID_SCANNER_OP_CLEAR_ENV: {
// Clear environment (force exit)
ksu_throne_comm_exit();
ksu_uid_scanner_enabled = false;
ksu_throne_comm_save_state();
pr_info("enable_uid_scanner: environment cleared\n");
break;
}
default:
pr_err("enable_uid_scanner: invalid operation\n");
return -EINVAL;
}
return 0;
}
// IOCTL handlers mapping table
static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
{ .cmd = KSU_IOCTL_GRANT_ROOT, .name = "GRANT_ROOT", .handler = do_grant_root, .perm_check = allowed_for_su },
{ .cmd = KSU_IOCTL_GET_INFO, .name = "GET_INFO", .handler = do_get_info, .perm_check = always_allow },
{ .cmd = KSU_IOCTL_REPORT_EVENT, .name = "REPORT_EVENT", .handler = do_report_event, .perm_check = only_root },
{ .cmd = KSU_IOCTL_SET_SEPOLICY, .name = "SET_SEPOLICY", .handler = do_set_sepolicy, .perm_check = only_root },
{ .cmd = KSU_IOCTL_CHECK_SAFEMODE, .name = "CHECK_SAFEMODE", .handler = do_check_safemode, .perm_check = always_allow },
{ .cmd = KSU_IOCTL_GET_ALLOW_LIST, .name = "GET_ALLOW_LIST", .handler = do_get_allow_list, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_GET_DENY_LIST, .name = "GET_DENY_LIST", .handler = do_get_deny_list, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_UID_GRANTED_ROOT, .name = "UID_GRANTED_ROOT", .handler = do_uid_granted_root, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_UID_SHOULD_UMOUNT, .name = "UID_SHOULD_UMOUNT", .handler = do_uid_should_umount, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_GET_MANAGER_UID, .name = "GET_MANAGER_UID", .handler = do_get_manager_uid, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_GET_APP_PROFILE, .name = "GET_APP_PROFILE", .handler = do_get_app_profile, .perm_check = only_manager },
{ .cmd = KSU_IOCTL_SET_APP_PROFILE, .name = "SET_APP_PROFILE", .handler = do_set_app_profile, .perm_check = only_manager },
{ .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_GET_WRAPPER_FD, .name = "GET_WRAPPER_FD", .handler = do_get_wrapper_fd, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_MANAGE_MARK, .name = "MANAGE_MARK", .handler = do_manage_mark, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_NUKE_EXT4_SYSFS, .name = "NUKE_EXT4_SYSFS", .handler = do_nuke_ext4_sysfs, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_ADD_TRY_UMOUNT, .name = "ADD_TRY_UMOUNT", .handler = add_try_umount, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow},
{ .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root},
{ .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root},
{ .cmd = KSU_IOCTL_DYNAMIC_MANAGER, .name = "SET_DYNAMIC_MANAGER", .handler = do_dynamic_manager, .perm_check = manager_or_root},
{ .cmd = KSU_IOCTL_GET_MANAGERS, .name = "GET_MANAGERS", .handler = do_get_managers, .perm_check = manager_or_root},
{ .cmd = KSU_IOCTL_ENABLE_UID_SCANNER, .name = "SET_ENABLE_UID_SCANNER", .handler = do_enable_uid_scanner, .perm_check = manager_or_root},
#ifdef CONFIG_KPM
{ .cmd = KSU_IOCTL_KPM, .name = "KPM_OPERATION", .handler = do_kpm, .perm_check = manager_or_root},
#endif
{ .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL} // Sentine
};
#ifdef KSU_SHOULD_USE_NEW_TP
struct ksu_install_fd_tw {
struct callback_head cb;
int __user *outp;
};
static void ksu_install_fd_tw_func(struct callback_head *cb)
{
struct ksu_install_fd_tw *tw =
container_of(cb, struct ksu_install_fd_tw, cb);
int fd = ksu_install_fd();
pr_info("[%d] install ksu fd: %d\n", current->pid, fd);
if (copy_to_user(tw->outp, &fd, sizeof(fd))) {
pr_err("install ksu fd reply err\n");
do_close_fd(fd);
}
kfree(tw);
}
static int reboot_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
struct pt_regs *real_regs = PT_REAL_REGS(regs);
int magic1 = (int)PT_REGS_PARM1(real_regs);
int magic2 = (int)PT_REGS_PARM2(real_regs);
unsigned long arg4;
// Check if this is a request to install KSU fd
if (magic1 == KSU_INSTALL_MAGIC1 && magic2 == KSU_INSTALL_MAGIC2) {
struct ksu_install_fd_tw *tw;
arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs);
tw = kzalloc(sizeof(*tw), GFP_ATOMIC);
if (!tw)
return 0;
tw->outp = (int __user *)arg4;
tw->cb.func = ksu_install_fd_tw_func;
if (task_work_add(current, &tw->cb, TWA_RESUME)) {
kfree(tw);
pr_warn("install fd add task_work failed\n");
}
}
return 0;
}
static struct kprobe reboot_kp = {
.symbol_name = REBOOT_SYMBOL,
.pre_handler = reboot_handler_pre,
};
#else
int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd,
void __user **arg)
{
if (magic1 != KSU_INSTALL_MAGIC1)
return 0;
#ifdef CONFIG_KSU_DEBUG
pr_info("sys_reboot: intercepted call! magic: 0x%x id: %d\n", magic1,
magic2);
#endif
// Check if this is a request to install KSU fd
if (magic2 == KSU_INSTALL_MAGIC2) {
int fd = ksu_install_fd();
// downstream: dereference all arg usage!
if (copy_to_user((void __user *)*arg, &fd, sizeof(fd))) {
pr_err("install ksu fd reply err\n");
do_close_fd(fd);
}
return 0;
}
return 0;
}
#endif
void ksu_supercalls_init(void)
{
int i;
pr_info("KernelSU IOCTL Commands:\n");
for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
pr_info(" %-18s = 0x%08x\n", ksu_ioctl_handlers[i].name,
ksu_ioctl_handlers[i].cmd);
}
#ifdef KSU_SHOULD_USE_NEW_TP
int rc = register_kprobe(&reboot_kp);
if (rc) {
pr_err("reboot kprobe failed: %d\n", rc);
} else {
pr_info("reboot kprobe registered successfully\n");
}
#endif
}
void ksu_supercalls_exit(void)
{
#ifdef KSU_SHOULD_USE_NEW_TP
unregister_kprobe(&reboot_kp);
#endif
}
// IOCTL dispatcher
static long anon_ksu_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
void __user *argp = (void __user *)arg;
int i;
#ifdef CONFIG_KSU_DEBUG
pr_info("ksu ioctl: cmd=0x%x from uid=%d\n", cmd, current_uid().val);
#endif
for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
if (cmd == ksu_ioctl_handlers[i].cmd) {
// Check permission first
if (ksu_ioctl_handlers[i].perm_check &&
!ksu_ioctl_handlers[i].perm_check()) {
pr_warn("ksu ioctl: permission denied for cmd=0x%x uid=%d\n",
cmd, current_uid().val);
return -EPERM;
}
// Execute handler
return ksu_ioctl_handlers[i].handler(argp);
}
}
pr_warn("ksu ioctl: unsupported command 0x%x\n", cmd);
return -ENOTTY;
}
// File release handler
static int anon_ksu_release(struct inode *inode, struct file *filp)
{
#ifdef CONFIG_KSU_DEBUG
pr_info("ksu fd released\n");
#endif
return 0;
}
// File operations structure
static const struct file_operations anon_ksu_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = anon_ksu_ioctl,
.compat_ioctl = anon_ksu_ioctl,
.release = anon_ksu_release,
};
// Install KSU fd to current process
int ksu_install_fd(void)
{
struct file *filp;
int fd;
// Get unused fd
fd = get_unused_fd_flags(O_CLOEXEC);
if (fd < 0) {
pr_err("ksu_install_fd: failed to get unused fd\n");
return fd;
}
// Create anonymous inode file
filp = anon_inode_getfile("[ksu_driver]", &anon_ksu_fops, NULL,
O_RDWR | O_CLOEXEC);
if (IS_ERR(filp)) {
pr_err("ksu_install_fd: failed to create anon inode file\n");
put_unused_fd(fd);
return PTR_ERR(filp);
}
// Install fd
fd_install(fd, filp);
#ifdef CONFIG_KSU_DEBUG
pr_info("ksu fd[%d] installed for %s/%d\n", fd, current->comm,
current->pid);
#endif
return fd;
}

View File

@@ -1,182 +0,0 @@
#ifndef __KSU_H_SUPERCALLS
#define __KSU_H_SUPERCALLS
#include <linux/types.h>
#include <linux/ioctl.h>
#include "app_profile.h"
#ifdef CONFIG_KPM
#include "kpm/kpm.h"
#endif
#include "ksu.h"
// Magic numbers for reboot hook to install fd
#define KSU_INSTALL_MAGIC1 0xDEADBEEF
#define KSU_INSTALL_MAGIC2 0xCAFEBABE
// Command structures for ioctl
struct ksu_become_daemon_cmd {
__u8 token[65]; // Input: daemon token (null-terminated)
};
struct ksu_get_info_cmd {
__u32 version; // Output: KERNEL_SU_VERSION
__u32 flags; // Output: flags (bit 0: MODULE mode)
__u32 features; // Output: max feature ID supported
};
struct ksu_report_event_cmd {
__u32 event; // Input: EVENT_POST_FS_DATA, EVENT_BOOT_COMPLETED, etc.
};
struct ksu_set_sepolicy_cmd {
__u64 cmd; // Input: sepolicy command
__aligned_u64 arg; // Input: sepolicy argument pointer
};
struct ksu_check_safemode_cmd {
__u8 in_safe_mode; // Output: true if in safe mode, false otherwise
};
struct ksu_get_allow_list_cmd {
__u32 uids[128]; // Output: array of allowed/denied UIDs
__u32 count; // Output: number of UIDs in array
__u8 allow; // Input: true for allow list, false for deny list
};
struct ksu_uid_granted_root_cmd {
__u32 uid; // Input: target UID to check
__u8 granted; // Output: true if granted, false otherwise
};
struct ksu_uid_should_umount_cmd {
__u32 uid; // Input: target UID to check
__u8 should_umount; // Output: true if should umount, false otherwise
};
struct ksu_get_manager_uid_cmd {
__u32 uid; // Output: manager UID
};
struct ksu_get_app_profile_cmd {
struct app_profile profile; // Input/Output: app profile structure
};
struct ksu_set_app_profile_cmd {
struct app_profile profile; // Input: app profile structure
};
struct ksu_get_feature_cmd {
__u32 feature_id; // Input: feature ID (enum ksu_feature_id)
__u64 value; // Output: feature value/state
__u8 supported; // Output: true if feature is supported, false otherwise
};
struct ksu_set_feature_cmd {
__u32 feature_id; // Input: feature ID (enum ksu_feature_id)
__u64 value; // Input: feature value/state to set
};
struct ksu_nuke_ext4_sysfs_cmd {
__aligned_u64 arg; // Input: mnt pointer
};
// Other command structures
struct ksu_get_full_version_cmd {
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string
};
struct ksu_hook_type_cmd {
char hook_type[32]; // Output: hook type string
};
struct ksu_enable_kpm_cmd {
__u8 enabled; // Output: true if KPM is enabled
};
struct ksu_dynamic_manager_cmd {
struct dynamic_manager_user_config config; // Input/Output: dynamic manager config
};
struct ksu_get_managers_cmd {
struct manager_list_info manager_info; // Output: manager list information
};
struct ksu_enable_uid_scanner_cmd {
__u32 operation; // Input: operation type (UID_SCANNER_OP_GET_STATUS, UID_SCANNER_OP_TOGGLE, UID_SCANNER_OP_CLEAR_ENV)
__u32 enabled; // Input: enable or disable (for UID_SCANNER_OP_TOGGLE)
void __user *status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS)
};
struct ksu_get_wrapper_fd_cmd {
__u32 fd; // Input: userspace fd
__u32 flags; // Input: flags of userspace fd
};
struct ksu_manage_mark_cmd {
__u32 operation; // Input: KSU_MARK_*
__s32 pid; // Input: target pid (0 for all processes)
__u32 result; // Output: for get operation - mark status or reg_count
};
#define KSU_MARK_GET 1
#define KSU_MARK_MARK 2
#define KSU_MARK_UNMARK 3
#define KSU_MARK_REFRESH 4
struct ksu_add_try_umount_cmd {
__aligned_u64 arg; // char ptr, this is the mountpoint
__u32 flags; // this is the flag we use for it
__u8 mode; // denotes what to do with it 0:wipe_list 1:add_to_list 2:delete_entry
};
#define KSU_UMOUNT_WIPE 0 // ignore everything and wipe list
#define KSU_UMOUNT_ADD 1 // add entry (path + flags)
#define KSU_UMOUNT_DEL 2 // delete entry, strcmp
// IOCTL command definitions
#define KSU_IOCTL_GRANT_ROOT _IOC(_IOC_NONE, 'K', 1, 0)
#define KSU_IOCTL_GET_INFO _IOC(_IOC_READ, 'K', 2, 0)
#define KSU_IOCTL_REPORT_EVENT _IOC(_IOC_WRITE, 'K', 3, 0)
#define KSU_IOCTL_SET_SEPOLICY _IOC(_IOC_READ|_IOC_WRITE, 'K', 4, 0)
#define KSU_IOCTL_CHECK_SAFEMODE _IOC(_IOC_READ, 'K', 5, 0)
#define KSU_IOCTL_GET_ALLOW_LIST _IOC(_IOC_READ|_IOC_WRITE, 'K', 6, 0)
#define KSU_IOCTL_GET_DENY_LIST _IOC(_IOC_READ|_IOC_WRITE, 'K', 7, 0)
#define KSU_IOCTL_UID_GRANTED_ROOT _IOC(_IOC_READ|_IOC_WRITE, 'K', 8, 0)
#define KSU_IOCTL_UID_SHOULD_UMOUNT _IOC(_IOC_READ|_IOC_WRITE, 'K', 9, 0)
#define KSU_IOCTL_GET_MANAGER_UID _IOC(_IOC_READ, 'K', 10, 0)
#define KSU_IOCTL_GET_APP_PROFILE _IOC(_IOC_READ|_IOC_WRITE, 'K', 11, 0)
#define KSU_IOCTL_SET_APP_PROFILE _IOC(_IOC_WRITE, 'K', 12, 0)
#define KSU_IOCTL_GET_FEATURE _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0)
#define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0)
#define KSU_IOCTL_GET_WRAPPER_FD _IOC(_IOC_WRITE, 'K', 15, 0)
#define KSU_IOCTL_MANAGE_MARK _IOC(_IOC_READ | _IOC_WRITE, 'K', 16, 0)
#define KSU_IOCTL_NUKE_EXT4_SYSFS _IOC(_IOC_WRITE, 'K', 17, 0)
#define KSU_IOCTL_ADD_TRY_UMOUNT _IOC(_IOC_WRITE, 'K', 18, 0)
// Other IOCTL command definitions
#define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0)
#define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0)
#define KSU_IOCTL_ENABLE_KPM _IOC(_IOC_READ, 'K', 102, 0)
#define KSU_IOCTL_DYNAMIC_MANAGER _IOC(_IOC_READ|_IOC_WRITE, 'K', 103, 0)
#define KSU_IOCTL_GET_MANAGERS _IOC(_IOC_READ|_IOC_WRITE, 'K', 104, 0)
#define KSU_IOCTL_ENABLE_UID_SCANNER _IOC(_IOC_READ|_IOC_WRITE, 'K', 105, 0)
// IOCTL handler types
typedef int (*ksu_ioctl_handler_t)(void __user *arg);
typedef bool (*ksu_perm_check_t)(void);
// IOCTL command mapping
struct ksu_ioctl_cmd_map {
unsigned int cmd;
const char *name;
ksu_ioctl_handler_t handler;
ksu_perm_check_t perm_check; // Permission check function
};
// Install KSU fd to current process
int ksu_install_fd(void);
void ksu_supercalls_init(void);
void ksu_supercalls_exit(void);
#endif // __KSU_H_SUPERCALLS

View File

@@ -1,381 +0,0 @@
#ifdef KSU_SHOULD_USE_NEW_TP
#include "linux/compiler.h"
#include "linux/cred.h"
#include "linux/printk.h"
#include "selinux/selinux.h"
#include <linux/spinlock.h>
#include <linux/kprobes.h>
#include <linux/tracepoint.h>
#include <asm/syscall.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <trace/events/syscalls.h>
#include "allowlist.h"
#include "arch.h"
#include "klog.h" // IWYU pragma: keep
#include "syscall_hook_manager.h"
#include "sucompat.h"
#include "setuid_hook.h"
#include "selinux/selinux.h"
// Tracepoint registration count management
// == 1: just us
// > 1: someone else is also using syscall tracepoint e.g. ftrace
static int tracepoint_reg_count = 0;
static DEFINE_SPINLOCK(tracepoint_reg_lock);
void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t)
{
unsigned long flags;
spin_lock_irqsave(&tracepoint_reg_lock, flags);
if (tracepoint_reg_count <= 1) {
ksu_clear_task_tracepoint_flag(t);
}
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
}
// Process marking management
static void handle_process_mark(bool mark)
{
struct task_struct *p, *t;
read_lock(&tasklist_lock);
for_each_process_thread (p, t) {
if (mark)
ksu_set_task_tracepoint_flag(t);
else
ksu_clear_task_tracepoint_flag(t);
}
read_unlock(&tasklist_lock);
}
void ksu_mark_all_process(void)
{
handle_process_mark(true);
pr_info("hook_manager: mark all user process done!\n");
}
void ksu_unmark_all_process(void)
{
handle_process_mark(false);
pr_info("hook_manager: unmark all user process done!\n");
}
static void ksu_mark_running_process_locked()
{
struct task_struct *p, *t;
read_lock(&tasklist_lock);
for_each_process_thread (p, t) {
if (!t->mm) { // only user processes
continue;
}
int uid = task_uid(t).val;
const struct cred *cred = get_task_cred(t);
bool ksu_root_process = uid == 0 && is_task_ksu_domain(cred);
bool is_zygote_process = is_zygote(cred);
bool is_shell = uid == 2000;
// before boot completed, we shall mark init for marking zygote
bool is_init = t->pid == 1;
if (ksu_root_process || is_zygote_process || is_shell ||
is_init || ksu_is_allow_uid(uid)) {
ksu_set_task_tracepoint_flag(t);
pr_info("hook_manager: mark process: pid:%d, uid: %d, comm:%s\n",
t->pid, uid, t->comm);
} else {
ksu_clear_task_tracepoint_flag(t);
pr_info("hook_manager: unmark process: pid:%d, uid: %d, comm:%s\n",
t->pid, uid, t->comm);
}
put_cred(cred);
}
read_unlock(&tasklist_lock);
}
void ksu_mark_running_process()
{
unsigned long flags;
spin_lock_irqsave(&tracepoint_reg_lock, flags);
if (tracepoint_reg_count <= 1) {
ksu_mark_running_process_locked();
} else {
pr_info("hook_manager: not mark running process since syscall tracepoint is in use\n");
}
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
}
// Get task mark status
// Returns: 1 if marked, 0 if not marked, -ESRCH if task not found
int ksu_get_task_mark(pid_t pid)
{
struct task_struct *task;
int marked = -ESRCH;
rcu_read_lock();
task = find_task_by_vpid(pid);
if (task) {
get_task_struct(task);
rcu_read_unlock();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
marked = test_task_syscall_work(task, SYSCALL_TRACEPOINT) ? 1 :
0;
#else
marked = test_tsk_thread_flag(task, TIF_SYSCALL_TRACEPOINT) ?
1 :
0;
#endif
put_task_struct(task);
} else {
rcu_read_unlock();
}
return marked;
}
// Set task mark status
// Returns: 0 on success, -ESRCH if task not found
int ksu_set_task_mark(pid_t pid, bool mark)
{
struct task_struct *task;
int ret = -ESRCH;
rcu_read_lock();
task = find_task_by_vpid(pid);
if (task) {
get_task_struct(task);
rcu_read_unlock();
if (mark) {
ksu_set_task_tracepoint_flag(task);
pr_info("hook_manager: marked task pid=%d comm=%s\n",
pid, task->comm);
} else {
ksu_clear_task_tracepoint_flag(task);
pr_info("hook_manager: unmarked task pid=%d comm=%s\n",
pid, task->comm);
}
put_task_struct(task);
ret = 0;
} else {
rcu_read_unlock();
}
return ret;
}
static struct kretprobe *init_kretprobe(const char *name,
kretprobe_handler_t handler)
{
struct kretprobe *rp = kzalloc(sizeof(struct kretprobe), GFP_KERNEL);
if (!rp)
return NULL;
rp->kp.symbol_name = name;
rp->handler = handler;
rp->data_size = 0;
rp->maxactive = 0;
int ret = register_kretprobe(rp);
pr_info("hook_manager: register_%s kretprobe: %d\n", name, ret);
if (ret) {
kfree(rp);
return NULL;
}
return rp;
}
static void destroy_kretprobe(struct kretprobe **rp_ptr)
{
struct kretprobe *rp = *rp_ptr;
if (!rp)
return;
unregister_kretprobe(rp);
synchronize_rcu();
kfree(rp);
*rp_ptr = NULL;
}
static int syscall_regfunc_handler(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
unsigned long flags;
spin_lock_irqsave(&tracepoint_reg_lock, flags);
if (tracepoint_reg_count < 1) {
// while install our tracepoint, mark our processes
ksu_mark_running_process_locked();
} else if (tracepoint_reg_count == 1) {
// while other tracepoint first added, mark all processes
ksu_mark_all_process();
}
tracepoint_reg_count++;
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
return 0;
}
static int syscall_unregfunc_handler(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
unsigned long flags;
spin_lock_irqsave(&tracepoint_reg_lock, flags);
tracepoint_reg_count--;
if (tracepoint_reg_count <= 0) {
// while no tracepoint left, unmark all processes
ksu_unmark_all_process();
} else if (tracepoint_reg_count == 1) {
// while just our tracepoint left, unmark disallowed processes
ksu_mark_running_process_locked();
}
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
return 0;
}
static struct kretprobe *syscall_regfunc_rp = NULL;
static struct kretprobe *syscall_unregfunc_rp = NULL;
static inline bool check_syscall_fastpath(int nr)
{
switch (nr) {
case __NR_newfstatat:
case __NR_faccessat:
case __NR_execve:
case __NR_setresuid:
return true;
default:
return false;
}
}
// Unmark init's child that are not zygote, adbd or ksud
int ksu_handle_init_mark_tracker(const char __user **filename_user)
{
char path[64];
if (unlikely(!filename_user))
return 0;
memset(path, 0, sizeof(path));
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (likely(strstr(path, "/app_process") == NULL &&
strstr(path, "/adbd") == NULL &&
strstr(path, "/ksud") == NULL)) {
pr_info("hook_manager: unmark %d exec %s", current->pid, path);
ksu_clear_task_tracepoint_flag_if_needed(current);
}
return 0;
}
// Generic sys_enter handler that dispatches to specific handlers
static void ksu_sys_enter_handler(void *data, struct pt_regs *regs, long id)
{
if (unlikely(check_syscall_fastpath(id))) {
if (ksu_su_compat_enabled) {
// Handle newfstatat
if (id == __NR_newfstatat) {
int *dfd = (int *)&PT_REGS_PARM1(regs);
const char __user **filename_user =
(const char __user **)&PT_REGS_PARM2(
regs);
int *flags =
(int *)&PT_REGS_SYSCALL_PARM4(regs);
ksu_handle_stat(dfd, filename_user, flags);
return;
}
// Handle faccessat
if (id == __NR_faccessat) {
int *dfd = (int *)&PT_REGS_PARM1(regs);
const char __user **filename_user =
(const char __user **)&PT_REGS_PARM2(
regs);
int *mode = (int *)&PT_REGS_PARM3(regs);
ksu_handle_faccessat(dfd, filename_user, mode,
NULL);
return;
}
// Handle execve
if (id == __NR_execve) {
const char __user **filename_user =
(const char __user **)&PT_REGS_PARM1(
regs);
if (current->pid != 1 &&
is_init(get_current_cred())) {
ksu_handle_init_mark_tracker(
filename_user);
} else {
ksu_handle_execve_sucompat(
NULL, filename_user, NULL,
NULL, NULL);
}
return;
}
}
// Handle setresuid
if (id == __NR_setresuid) {
uid_t ruid = (uid_t)PT_REGS_PARM1(regs);
uid_t euid = (uid_t)PT_REGS_PARM2(regs);
uid_t suid = (uid_t)PT_REGS_PARM3(regs);
ksu_handle_setresuid(ruid, euid, suid);
return;
}
}
}
void ksu_syscall_hook_manager_init(void)
{
int ret;
pr_info("hook_manager: ksu_hook_manager_init called\n");
// Register kretprobe for syscall_regfunc
syscall_regfunc_rp =
init_kretprobe("syscall_regfunc", syscall_regfunc_handler);
// Register kretprobe for syscall_unregfunc
syscall_unregfunc_rp =
init_kretprobe("syscall_unregfunc", syscall_unregfunc_handler);
ret = register_trace_sys_enter(ksu_sys_enter_handler, NULL);
if (ret) {
pr_err("hook_manager: failed to register sys_enter tracepoint: %d\n",
ret);
} else {
pr_info("hook_manager: sys_enter tracepoint registered\n");
}
ksu_setuid_hook_init();
ksu_sucompat_init();
}
void ksu_syscall_hook_manager_exit(void)
{
pr_info("hook_manager: ksu_hook_manager_exit called\n");
unregister_trace_sys_enter(ksu_sys_enter_handler, NULL);
tracepoint_synchronize_unregister();
pr_info("hook_manager: sys_enter tracepoint unregistered\n");
destroy_kretprobe(&syscall_regfunc_rp);
destroy_kretprobe(&syscall_unregfunc_rp);
ksu_sucompat_exit();
ksu_setuid_hook_exit();
}
#else
#include "klog.h" // IWYU pragma: keep
#include "syscall_hook_manager.h"
#include "sucompat.h"
#include "setuid_hook.h"
void ksu_syscall_hook_manager_init(void)
{
pr_info("hook_manager: initializing..\n");
ksu_setuid_hook_init();
ksu_sucompat_init();
}
void ksu_syscall_hook_manager_exit(void)
{
pr_info("hook_manager: exiting..\n");
ksu_sucompat_exit();
ksu_setuid_hook_exit();
}
#endif

View File

@@ -1,85 +0,0 @@
#ifndef __KSU_H_HOOK_MANAGER
#define __KSU_H_HOOK_MANAGER
#include <linux/version.h>
#include <linux/sched.h>
#include <linux/thread_info.h>
#include "kernel_compat.h"
// Hook manager initialization and cleanup
void ksu_syscall_hook_manager_init(void);
void ksu_syscall_hook_manager_exit(void);
#ifdef KSU_SHOULD_USE_NEW_TP
// Process marking for tracepoint
void ksu_mark_all_process(void);
void ksu_unmark_all_process(void);
void ksu_mark_running_process(void);
// Per-task mark operations
int ksu_get_task_mark(pid_t pid);
int ksu_set_task_mark(pid_t pid, bool mark);
static inline void ksu_set_task_tracepoint_flag(struct task_struct *t)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
set_task_syscall_work(t, SYSCALL_TRACEPOINT);
#else
set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
#endif
}
static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
clear_task_syscall_work(t, SYSCALL_TRACEPOINT);
#else
clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
#endif
}
void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t);
#else
// Process marking for tracepoint
static inline void ksu_mark_all_process(void)
{
return;
}
static inline void ksu_unmark_all_process(void)
{
return;
}
static inline void ksu_mark_running_process(void)
{
return;
}
// Per-task mark operations
static inline int ksu_get_task_mark(pid_t pid)
{
return 0;
}
static inline int ksu_set_task_mark(pid_t pid, bool mark)
{
return 0;
}
static inline void ksu_set_task_tracepoint_flag(struct task_struct *t)
{
return;
}
static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t)
{
return;
}
static inline void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t)
{
return;
}
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More