Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d743073309 | ||
|
|
a636911612 | ||
|
|
7a62f91752 | ||
|
|
b551a54c8f | ||
|
|
26d86aa2fe | ||
|
|
6ee9246650 | ||
|
|
1cd96fbdbf | ||
|
|
a030a026b1 | ||
|
|
8bf9cd0bee | ||
|
|
13b1aad4b8 | ||
|
|
916d956ce2 | ||
|
|
87a7650d26 | ||
|
|
3484e187da | ||
|
|
0835f330e2 | ||
|
|
8064472477 | ||
|
|
2281012e33 | ||
|
|
83eaeab1ba | ||
|
|
6405764df3 | ||
|
|
253276a27b | ||
|
|
855a71ac56 | ||
|
|
96dc53977f | ||
|
|
31111e68eb | ||
|
|
ac0de29872 | ||
|
|
9e2b722491 | ||
|
|
59627e6fe2 | ||
|
|
cd0b5fb378 | ||
|
|
48a3c64c7c | ||
|
|
62da804518 | ||
|
|
439b99cc4a | ||
|
|
64f0efc2c0 | ||
|
|
f196bf5b76 | ||
|
|
790968be6a | ||
|
|
83f0f9537f | ||
|
|
68ebfec918 | ||
|
|
8be4dea081 | ||
|
|
cfdbba45c3 | ||
|
|
d408c9f4bf | ||
|
|
8f4c58c4c3 | ||
|
|
7e88e9648f | ||
|
|
4516d136a4 | ||
|
|
1b85dfbed1 | ||
|
|
807ffb419a | ||
|
|
e826f43aed | ||
|
|
d619f5fafc | ||
|
|
b3e2f9b7ff | ||
|
|
99a39c6f52 | ||
|
|
22991e8740 | ||
|
|
7646ecb6f7 | ||
|
|
204db674bb | ||
|
|
99fe6623de | ||
|
|
f1f78d2485 | ||
|
|
b2ae20b796 | ||
|
|
83bd4e9642 | ||
|
|
767349798a | ||
|
|
ae38f4709b | ||
|
|
fc7001a11a | ||
|
|
9924809bdb | ||
|
|
58a4ff94e4 | ||
|
|
29033e9b80 | ||
|
|
ea24daf37c | ||
|
|
ebc16583fb | ||
|
|
2a10b41781 | ||
|
|
d5946047a1 | ||
|
|
4ff46a4911 | ||
|
|
b587216b5e | ||
|
|
245fce167e | ||
|
|
de9b82ffd5 | ||
|
|
e570f402e4 | ||
|
|
9c761b13fa | ||
|
|
cc4b135d20 | ||
|
|
ec5395c787 | ||
|
|
6d60e54a7d | ||
|
|
28aa34c0b6 | ||
|
|
0701967bab | ||
|
|
a76b1eece4 | ||
|
|
8e791c680e | ||
|
|
fc9f2ccf25 | ||
|
|
d4682fb06e | ||
|
|
377ea183a7 | ||
|
|
72361ab8bf | ||
|
|
f708e583c3 | ||
|
|
d753e1dc48 | ||
|
|
315a8a3805 | ||
|
|
129fed9c9f | ||
|
|
0baccb7621 | ||
|
|
842a8aa45a | ||
|
|
d17843479c | ||
|
|
0d70cc8e58 | ||
|
|
4e6cacb206 | ||
|
|
52514ba35b | ||
|
|
4d59ce435e | ||
|
|
b3b7fa6f4d | ||
|
|
c057c16391 | ||
|
|
dee7cc6f2b | ||
|
|
3d0d87cb0c | ||
|
|
6b66d9b3f8 | ||
|
|
a301d94858 | ||
|
|
01199470f2 | ||
|
|
9e7ea19567 | ||
|
|
cdc6a6cb4a | ||
|
|
bb2d8fd7e0 | ||
|
|
2b6d418fe6 | ||
|
|
d8b1126b96 | ||
|
|
2eeddcfa80 |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.bat eol=crlf
|
||||
5
.github/FUNDING.yml
vendored
Normal file
5
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: tiann
|
||||
patreon: weishu
|
||||
custom: https://vxposed.com/donate.html
|
||||
33
.github/ISSUE_TEMPLATE/add_device.yml
vendored
Normal file
33
.github/ISSUE_TEMPLATE/add_device.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Contribute to Unofficially Supported Device
|
||||
description: Add your device kernel source to KernelSU's Unofficially Supported Device List
|
||||
title: "[Add Device]: "
|
||||
labels: ["add-device"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for supporting KernelSU!
|
||||
- type: input
|
||||
id: repo-url
|
||||
attributes:
|
||||
label: Repository URL
|
||||
description: Your repository URL
|
||||
placeholder: https://github.com/tiann/KernelSU
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: device
|
||||
attributes:
|
||||
label: Device
|
||||
description: Please describe the device maintained by you.
|
||||
placeholder: GKI 2.0 Device
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you should be the maintainer of the repository.
|
||||
options:
|
||||
- label: I'm the maintainer of this repository
|
||||
required: true
|
||||
72
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
72
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal 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.
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Feature Request
|
||||
url: https://github.com/tiann/KernelSU/issues/1705
|
||||
about: "We do not accept external Feature Requests, see this link for more details."
|
||||
11
.github/ISSUE_TEMPLATE/custom.yml
vendored
Normal file
11
.github/ISSUE_TEMPLATE/custom.yml
vendored
Normal 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
|
||||
|
||||
64
.github/scripts/build_a12.sh
vendored
Normal file
64
.github/scripts/build_a12.sh
vendored
Normal 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
43
.github/scripts/build_a13.sh
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
build_from_image() {
|
||||
export TITLE
|
||||
TITLE=kernel-aarch64-${1//Image-/}
|
||||
|
||||
echo "[+] title: $TITLE"
|
||||
echo '[+] Building Image.gz'
|
||||
$GZIP -n -k -f -9 Image >Image.gz
|
||||
|
||||
echo '[+] Building boot.img'
|
||||
$MKBOOTIMG --header_version 4 --kernel Image --output boot.img
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
|
||||
echo '[+] Building boot-gz.img'
|
||||
$MKBOOTIMG --header_version 4 --kernel Image.gz --output boot-gz.img
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
|
||||
echo '[+] Building boot-lz4.img'
|
||||
$MKBOOTIMG --header_version 4 --kernel Image.lz4 --output boot-lz4.img
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
|
||||
echo '[+] Compress images'
|
||||
for image in boot*.img; do
|
||||
$GZIP -n -f -9 "$image"
|
||||
mv "$image".gz "${1//Image-/}"-"$image".gz
|
||||
done
|
||||
|
||||
echo '[+] Images to upload'
|
||||
find . -type f -name "*.gz"
|
||||
|
||||
# find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU/scripts/ksubot.py {} +
|
||||
}
|
||||
|
||||
for dir in Image*; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "----- Building $dir -----"
|
||||
cd "$dir"
|
||||
build_from_image "$dir"
|
||||
cd ..
|
||||
fi
|
||||
done
|
||||
60
.github/workflows/add-device.yml
vendored
Normal file
60
.github/workflows/add-device.yml
vendored
Normal 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
137
.github/workflows/avd-kernel.yml
vendored
Normal 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
74
.github/workflows/build-lkm-local.yml
vendored
Normal 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
74
.github/workflows/build-lkm.yml
vendored
Normal 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: 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.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
|
||||
252
.github/workflows/build-manager-manual.yml
vendored
Normal file
252
.github/workflows/build-manager-manual.yml
vendored
Normal 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
276
.github/workflows/build-manager.yml
vendored
Normal 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
36
.github/workflows/build-su.yml
vendored
Normal 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
37
.github/workflows/clippy.yml
vendored
Normal 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
|
||||
38
.github/workflows/crowdin.yml
vendored
Normal file
38
.github/workflows/crowdin.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Crowdin Action
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'manager/app/src/main/res/**'
|
||||
|
||||
jobs:
|
||||
synchronize-with-crowdin:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: crowdin action
|
||||
uses: crowdin/github-action@master
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_translations: true
|
||||
download_translations: true
|
||||
localization_branch_name: "Crowdin"
|
||||
crowdin_branch_name: "main"
|
||||
create_pull_request: true
|
||||
pull_request_title: 'New Crowdin Translations'
|
||||
pull_request_body: 'New Crowdin translations by [Crowdin GH Action](https://github.com/crowdin/github-action)'
|
||||
pull_request_base_branch_name: 'main'
|
||||
skip_untranslated_files: true
|
||||
env:
|
||||
# A classic GitHub Personal Access Token with the 'repo' scope selected (the user should have write access to the repository).
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
# A numeric ID, found at https://crowdin.com/project/<projectName>/tools/api
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
|
||||
# Visit https://crowdin.com/settings#api-key to create this token
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
67
.github/workflows/deploy-website.yml
vendored
Normal file
67
.github/workflows/deploy-website.yml
vendored
Normal 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
252
.github/workflows/gki-kernel-local.yml
vendored
Normal 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
79
.github/workflows/gki-kernel-mock.yml
vendored
Normal 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
261
.github/workflows/gki-kernel.yml
vendored
Normal 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
40
.github/workflows/kpmmgr.yml
vendored
Normal 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
74
.github/workflows/ksud.yml
vendored
Normal 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
33
.github/workflows/rustfmt.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Rustfmt check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- '.github/workflows/rustfmt.yml'
|
||||
- 'userspace/ksud/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- '.github/workflows/rustfmt.yml'
|
||||
- 'userspace/ksud/**'
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@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
27
.github/workflows/shellcheck.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: ShellCheck
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- '.github/workflows/shellcheck.yml'
|
||||
- '**/*.sh'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- '.github/workflows/shellcheck.yml'
|
||||
- '**/*.sh'
|
||||
|
||||
jobs:
|
||||
shellcheck:
|
||||
runs-on: 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
40
.github/workflows/susfs.yml
vendored
Normal 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
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
manager
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
7
SECURITY.md
Normal file
7
SECURITY.md
Normal 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.
|
||||
3
crowdin.yml
Normal file
3
crowdin.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
files:
|
||||
- source: /manager/app/src/main/res/values/strings.xml
|
||||
translation: /manager/app/src/main/res/values-%two_letters_code%/strings.xml
|
||||
141
docs/README-en.md
Normal file
141
docs/README-en.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# SukiSU Ultra
|
||||
|
||||
**English** | [简体中文](README.md) | [日本語](README-ja.md) | [Türkçe](README-tr.md)
|
||||
|
||||
Android device root solution based on [KernelSU](https://github.com/tiann/KernelSU)
|
||||
|
||||
**Experimental! Use at your own risk!** This solution is based on [KernelSU](https://github.com/tiann/KernelSU) and is experimental!
|
||||
|
||||
> This is an unofficial fork. All rights are reserved to [@tiann](https://github.com/tiann)
|
||||
>
|
||||
> However, we will be a separately maintained branch of KSU in the future
|
||||
|
||||
## How to add
|
||||
|
||||
Using main branching (non-GKI device builds are not supported) (requires manual integration of susfs)
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
Using branches that support non-GKI devices (requires manual integration of susfs)
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||
```
|
||||
|
||||
## How to use integrated susfs
|
||||
|
||||
1. Use the susfs-dev branch directly without any patching
|
||||
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
||||
```
|
||||
|
||||
## KPM Support
|
||||
|
||||
- Based on KernelPatch, we have removed duplicates of KSU and kept only KPM support.
|
||||
- We will introduce more APatch-compatible functions to ensure the integrity of KPM functionality.
|
||||
|
||||
We will introduce more APatch-compatible functions to ensure the completeness of KPM functionality.
|
||||
|
||||
KPM templates: https://github.com/udochina/KPM-Build-Anywhere
|
||||
|
||||
> [!Note]
|
||||
> 1. `CONFIG_KPM=y` needs to be added.
|
||||
> 2. Non-GKI devices need to add `CONFIG_KALLSYMS=y` and `CONFIG_KALLSYMS_ALL=y` as well.
|
||||
> 3. Some kernel source code below `4.19` also needs to be backport from `4.19` to the header file `set_memory.h`.
|
||||
|
||||
## How to do a system update to retain ROOT
|
||||
- After OTA, don't reboot first, go to the manager flashing/patching kernel interface, find `GKI/non_GKI install` and select the Anykernel3 kernel zip file that needs to be flashed, select the slot that is opposite to the current running slot of the system for flashing, and then reboot to retain the GKI mode update (This method is not supported for all non-GKI devices, so please try it yourself. It is the safest way to use TWRP for non-GKI devices.)
|
||||
- Or use LKM mode to install to the unused slot (after OTA).
|
||||
|
||||
## Compatibility Status
|
||||
- KernelSU (versions prior to v1.0.0) officially supports Android GKI 2.0 devices (kernel 5.10+)
|
||||
|
||||
- Older kernels (4.4+) are also compatible, but the kernel must be built manually
|
||||
|
||||
- KernelSU can support 3.x kernels (3.4-3.18) through additional reverse ports
|
||||
|
||||
- Currently supports `arm64-v8a`, `armeabi-v7a (bare)` and some `X86_64`
|
||||
|
||||
|
||||
## More links
|
||||
|
||||
**If you need to submit a translation for the manager go to** https://crowdin.com/project/SukiSU-Ultra
|
||||
|
||||
Projects compiled based on Sukisu and susfs
|
||||
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
||||
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
||||
|
||||
## Hook method
|
||||
- This method references the hook method from (https://github.com/rsuntk/KernelSU)
|
||||
|
||||
1. **KPROBES hook:**
|
||||
- Also used for Loadable Kernel Module (LKM)
|
||||
- Default hook method on GKI kernels.
|
||||
- Need `CONFIG_KPROBES=y`
|
||||
|
||||
2. **Manual hook:**
|
||||
- Standard KernelSU hook: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
||||
- backslashxx's syscall manual hook: https://github.com/backslashxx/KernelSU/issues/5
|
||||
- Default hook method on Non-GKI kernels.
|
||||
- Need `CONFIG_KSU_MANUAL_HOOK=y`
|
||||
|
||||
## Usage
|
||||
|
||||
### Universal GKI
|
||||
|
||||
Please **all** refer to https://kernelsu.org/zh_CN/guide/installation.html
|
||||
|
||||
> [!Note]
|
||||
> 1. for devices with GKI 2.0 such as Xiaomi, Redmi, Samsung, etc. (excludes kernel-modified manufacturers such as Meizu, OnePlus, Zenith, and oppo)
|
||||
> 2. Find the GKI build in [more links](#%E6%9B%B4%E5%A4%9A%E9%93%BE%E6%8E%A5). Find the device kernel version. Then download it and use TWRP or kernel flashing tool to flash the zip file with AnyKernel3 suffix.
|
||||
> 3. The .zip archive without suffix is uncompressed, the gz suffix is the compression used by Tenguet models.
|
||||
|
||||
|
||||
### OnePlus
|
||||
|
||||
1. Use the link mentioned in the 'More Links' section to create a customized build with your device information, and then flash the zip file with the AnyKernel3 suffix.
|
||||
|
||||
> [!Note]
|
||||
> - You only need to fill in the first two parts of kernel versions, such as 5.10, 5.15, 6.1, or 6.6.
|
||||
> - Please search for the processor codename by yourself, usually it is all English without numbers.
|
||||
> - You can find the branch and configuration files from the OnePlus open-source kernel repository.
|
||||
|
||||
## Features
|
||||
|
||||
1. Kernel-based `su` and root access management.
|
||||
2. Not based on [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) module system, but based on [Magic Mount](https://github.com/5ec1cff/KernelSU) from 5ec1cff
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock root privileges in a cage.
|
||||
4. Bringing back non-GKI/GKI 1.0 support
|
||||
5. More customization
|
||||
6. Support for KPM kernel modules
|
||||
|
||||
## License
|
||||
|
||||
- The file in the “kernel” directory is under [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) license.
|
||||
|
||||
- The images of the files `ic_launcher*` with anime character emoticons are copyrighted by [五十根大虾仁](https://space.bilibili.com/370927), 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.
|
||||
|
||||
## Sponsorship list
|
||||
|
||||
- [Ktouls](https://github.com/Ktouls) Thanks so much for bringing me support
|
||||
- [zaoqi123](https://github.com/zaoqi123) It's not a bad idea to buy me a milk tea
|
||||
- [wswzgdg](https://github.com/wswzgdg) Many thanks for supporting this project
|
||||
- [yspbwx2010](https://github.com/yspbwx2010) Many thanks
|
||||
- [DARKWWEE](https://github.com/DARKWWEE) Thanks for the 100 USDT Lao
|
||||
|
||||
If the above list does not have your name, I will update it as soon as possible, and thanks again for your support!
|
||||
|
||||
## Contributions
|
||||
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): original project
|
||||
- [MKSU](https://github.com/5ec1cff/KernelSU): Used project
|
||||
- [RKSU](https://github.com/rsuntk/KernelsU): Reintroduced the support of non-GKI devices using the kernel of this project
|
||||
- [susfs](https://gitlab.com/simonpunk/susfs4ksu):Used susfs file system
|
||||
- [KernelSU](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU conceptualization
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): Powerful root utility
|
||||
- [genuine](https://github.com/brevent/genuine/): APK v2 Signature Verification
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Some rootkit utilities.
|
||||
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch is a key part of the APatch implementation of the kernel module
|
||||
137
docs/README-ja.md
Normal file
137
docs/README-ja.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# SukiSU Ultra
|
||||
|
||||
**日本語** | [简体中文](README.md) | [English](README-en.md) | [Türkçe](README-tr.md)
|
||||
|
||||
[KernelSU](https://github.com/tiann/KernelSU) をベースとした Android デバイスの root ソリューション
|
||||
|
||||
**試験中なビルドです!自己責任で使用してください!**<br>
|
||||
このソリューションは [KernelSU](https://github.com/tiann/KernelSU) に基づいていますが、試験中なビルドです。
|
||||
|
||||
> これは非公式なフォークです。すべての権利は [@tiann](https://github.com/tiann) に帰属します。
|
||||
>
|
||||
> ただし、将来的には KSU とは別に管理されるブランチとなる予定です。
|
||||
|
||||
## 追加方法
|
||||
|
||||
メイン分岐の使用(GKI デバイス以外のビルドはサポートされていません。) (手動によるサスフ統合が必要)
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
GKI以外のデバイスをサポートするブランチを使用する (手動によるサスフ統合が必要)
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||
```
|
||||
|
||||
## 統合された susfs の使い方
|
||||
|
||||
1. パッチを当てずに susfs-dev ブランチを直接使用してください。
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
||||
```
|
||||
|
||||
## 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/non_GKI 取り付け`を見つけ、フラッシュする必要があるAnykernel3カーネルzipファイルを選択し、フラッシュするためにシステムの現在の実行スロットと反対のスロットを選択し、GKIモードアップデートを保持するために再起動します(この方法は、現時点ではすべてのnon_GKIデバイスでサポートされていませんので、各自でお試しください。 (この方法は、すべての非GKIデバイスでサポートされていませんので、ご自身でお試しください)。
|
||||
- または、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)
|
||||
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
||||
|
||||
## フックの方式
|
||||
|
||||
- この方式は (https://github.com/rsuntk/KernelSU) のフック方式を参照してください。
|
||||
|
||||
1. **KPROBES フック:**
|
||||
- 読み込み可能なカーネルモジュールの場合 (LKM)
|
||||
- GKI カーネルのデフォルトとなるフック方式
|
||||
- `CONFIG_KPROBES=y` が必要です
|
||||
|
||||
2. **手動でフック:**
|
||||
- 標準の KernelSU フック: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
||||
- backslashxx syscall フック: https://github.com/backslashxx/KernelSU/issues/5
|
||||
- 非 GKI カーネル用のデフォルトフッキングメソッド
|
||||
- `CONFIG_KSU_MANUAL_HOOK=y` が必要です
|
||||
|
||||
## 使い方
|
||||
|
||||
### ユニバーサルGKI
|
||||
|
||||
https://kernelsu.org/zh_CN/guide/installation.html をご参照ください。
|
||||
|
||||
> [!Note]
|
||||
> 1.Xiaomi、Redmi、Samsung などの GKI 2.0 を搭載したデバイス用 (Meizu、Yiga、Zenith、oppo などのマジックカーネルを搭載したメーカーは除く)。
|
||||
> 2. [more links](#%E6%9B%B4%E5%A4%9A%E9%93%BE%E6%8E%A5) で GKI ビルドを検索します。 デバイスのカーネルバージョンを検索します。 次に、それをダウンロードし、TWRPまたはカーネルフラッシングツールを使用して、AnyKernel3の接尾辞が付いたzipファイルをフラッシュします。
|
||||
> 接尾辞なしの.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 カーネルモジュールに対応
|
||||
|
||||
## ライセンス
|
||||
|
||||
- `kernel` ディレクトリ以下のファイルは[GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)です。
|
||||
- アニメキャラクターの絵文字を含むファイル `ic_launcher*` の画像は[五十根大虾仁](https://space.bilibili.com/370927)が著作権を所有しており、画像内のブランド知的財産権は[明风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) ラオウ100USDTありがとう!
|
||||
|
||||
上記の一覧にあなたの名前がない場合は、できるだけ早急に更新しますので再度ご支援をお願いします。
|
||||
|
||||
## 貢献者
|
||||
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): オリジナルのプロジェクトです。
|
||||
- [MKSU](https://github.com/5ec1cff/KernelSU): 使用しているプロジェクトです。
|
||||
- [RKSU](https://github.com/rsuntk/KernelsU): このプロジェクトのカーネルを使用して非 GKI デバイスのサポートを追加しています。
|
||||
- [susfs](https://gitlab.com/simonpunk/susfs4ksu):使用している susfs ファイルシステムです。
|
||||
- [KernelSU](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU について。
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): パワフルな root ユーティリティです。
|
||||
- [genuine](https://github.com/brevent/genuine/): APK v2 署名認証で使用しています。
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): いくつかの rootkit ユーティリティを使用しています。
|
||||
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch はカーネルモジュールの APatch 実装での重要な部分となります。
|
||||
151
docs/README-tr.md
Normal file
151
docs/README-tr.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# SukiSU Ultra
|
||||
|
||||
**Türkçe** | [简体中文](README.md) | [English](README-en.md) | [日本語](README-ja.md)
|
||||
|
||||
[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-stable veya susfs-dev 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-dev
|
||||
```
|
||||
|
||||
## 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*` dosyalarının görüntüleri [五十根大虾仁](https://space.bilibili.com/370927) 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
|
||||
|
||||
Eğer yukarıdaki listede adınız yoksa, zamanında güncelleyeceğim, herkese tekrar teşekkür ederim
|
||||
|
||||
## 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
|
||||
153
docs/README.md
Normal file
153
docs/README.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# SukiSU Ultra
|
||||
|
||||
**简体中文** | [English](README-en.md) | [日本語](README-ja.md) | [Türkçe](README-tr.md)
|
||||
|
||||
基于 [KernelSU](https://github.com/tiann/KernelSU) 的安卓设备 root 解决方案
|
||||
|
||||
**实验性! 使用风险自负!**
|
||||
|
||||
> 这是非官方分支,保留所有权利 [@tiann](https://github.com/tiann)
|
||||
>
|
||||
> 但是,我们将会在未来成为一个单独维护的 KSU 分支
|
||||
|
||||
## 如何添加
|
||||
|
||||
在内核源码的根目录下执行以下命令:
|
||||
|
||||
使用 main 分支 (不支持非 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-stable 或者 susfs-dev 分支,不需要再集成 susfs
|
||||
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
||||
```
|
||||
|
||||
## 钩子方法
|
||||
|
||||
- 此部分引用自 [rsuntk 的钩子方法](https://github.com/rsuntk/KernelSU)
|
||||
|
||||
1. **KPROBES 钩子:**
|
||||
|
||||
- 用于可加载内核模块 (LKM)
|
||||
- GKI 2.0 内核的默认钩子方法
|
||||
- 需要 `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 支持
|
||||
- 我们将会引入更多的兼容 APatch 的函数来确保 KPM 功能的完整性
|
||||
|
||||
开源地址: 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/non_GKI安装` 选择需要刷写的 Anykernel3 内核压缩文件,选择与现在系统运行槽位相反的槽位进行刷写并重启即可保留 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
|
||||
|
||||
基于 SukiSU 和 susfs 编译的项目
|
||||
|
||||
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
||||
- [一加](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 普适的 GKI
|
||||
|
||||
请**全部**参考 https://kernelsu.org/zh_CN/guide/installation.html
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> 1. 适用于如小米、红米、三星等的 GKI 2.0 的设备 (不包含魔改内核的厂商如魅族、一加、真我和 oppo)
|
||||
> 2. 找到[更多链接](#%E6%9B%B4%E5%A4%9A%E9%93%BE%E6%8E%A5)里的 GKI 构建的项目。找到设备内核版本。然后下载下来,用 TWRP 或者内核刷写工具刷入带 AnyKernel3 后缀的压缩包即可
|
||||
> 3. 一般不带后缀的 .zip 压缩包是未压缩的,gz 后缀的为天玑机型所使用的压缩方式
|
||||
|
||||
### 一加
|
||||
|
||||
1.找到更多链接里的一加项目进行自行填写,然后云编译构建,最后刷入带 AnyKernel3 后缀的压缩包即可
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> - 内核版本只需要填写前两位即可,如 5.10,5.15,6.1,6.6
|
||||
> - 处理器代号请自行搜索,一般为全英文不带数字的代号
|
||||
> - 分支和配置文件请自行到一加内核开源地址进行填写
|
||||
|
||||
## 特点
|
||||
|
||||
1. 基于内核的 `su` 和 root 访问管理
|
||||
2. 基于 5ec1cff 的 [Magic Mount](https://github.com/5ec1cff/KernelSU) 的模块系统
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html):将 root 权限锁在笼子里
|
||||
4. 恢复对非 GKI 2.0 内核的支持
|
||||
5. 更多自定义功能
|
||||
6. 对 KPM 内核模块的支持
|
||||
|
||||
## 许可证
|
||||
|
||||
- `kernel` 目录下的文件是 [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)。
|
||||
- 有动漫人物图片表情包的这些文件 `ic_launcher*` 的图像版权为[五十根大虾仁](https://space.bilibili.com/370927)所有,图像中的 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) 以外,还需要遵守向前两者索要使用这些艺术内容的授权。
|
||||
- 除了以上所述的文件或目录外,所有其他部分均为 [GPL-3.0 或更高版本](https://www.gnu.org/licenses/gpl-3.0.html)。
|
||||
|
||||
## 爱发电链接
|
||||
|
||||
- https://afdian.com/a/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
|
||||
|
||||
如果以上名单没有你的名称,我会及时更新,再次感谢大家的支持
|
||||
|
||||
## 贡献
|
||||
|
||||
- [KernelSU](https://github.com/tiann/KernelSU):原始项目
|
||||
- [MKSU](https://github.com/5ec1cff/KernelSU):使用的项目
|
||||
- [RKSU](https://github.com/rsuntk/KernelsU):使用该项目的 kernel 对非 GKI 设备重新进行支持
|
||||
- [susfs4ksu](https://gitlab.com/simonpunk/susfs4ksu):使用的 susfs 文件系统
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/):KernelSU 的构想
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk):强大的 root 工具
|
||||
- [genuine](https://github.com/brevent/genuine/):APK v2 签名验证
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技能
|
||||
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch 是 APatch 实现内核模块的关键部分
|
||||
121
js/README.md
Normal file
121
js/README.md
Normal 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
48
js/index.d.ts
vendored
Normal 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
119
js/index.js
Normal 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
26
js/package.json
Normal 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
14
justfile
Normal 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
22
kernel/.gitignore
vendored
@@ -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
|
||||
@@ -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
|
||||
@@ -35,25 +24,5 @@ config KPM
|
||||
Enabling this option will activate the KPM feature of SukiSU.
|
||||
This option is suitable for scenarios where you need to force KPM to be enabled.
|
||||
but it may affect system stability.
|
||||
select KALLSYMS
|
||||
select KALLSYMS_ALL
|
||||
|
||||
config KSU_MANUAL_HOOK
|
||||
bool "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
|
||||
|
||||
187
kernel/Makefile
187
kernel/Makefile
@@ -1,185 +1,43 @@
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
define get_ksu_version_full
|
||||
v$1-$(shell cd $(KSU_SRC); $(GIT_BIN) rev-parse --short=8 HEAD)@$(shell cd $(KSU_SRC); $(GIT_BIN) rev-parse --abbrev-ref HEAD)
|
||||
endef
|
||||
|
||||
ifeq ($(KSU_GITHUB_VERSION_COMMIT),)
|
||||
ifeq ($(LOCAL_GIT_EXISTS),1)
|
||||
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||
KSU_LOCAL_VERSION := $(shell cd $(KSU_SRC); $(GIT_BIN) rev-list --count $(REPO_BRANCH))
|
||||
KSU_VERSION := $(shell expr 40000 + $(KSU_LOCAL_VERSION) - 2815)
|
||||
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION))
|
||||
else
|
||||
KSU_VERSION := 13000
|
||||
$(warning -- Could not fetch version online or via local .git! Using fallback version: $(KSU_VERSION))
|
||||
endif
|
||||
else
|
||||
KSU_VERSION := $(shell expr 40000 + $(KSU_GITHUB_VERSION_COMMIT) - 2815)
|
||||
$(info -- $(REPO_NAME) version (GitHub): $(KSU_VERSION))
|
||||
endif
|
||||
|
||||
ifeq ($(KSU_GITHUB_VERSION),)
|
||||
ifeq ($(LOCAL_GIT_EXISTS),1)
|
||||
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_VERSION_API))
|
||||
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION_FULL))
|
||||
$(info -- $(REPO_NAME) Formatted version (local .git): $(KSU_VERSION))
|
||||
else
|
||||
KSU_VERSION_FULL := v$(KSU_VERSION_API)-$(REPO_NAME)-unknown@unknown
|
||||
$(warning -- $(REPO_NAME) version: $(KSU_VERSION_FULL))
|
||||
endif
|
||||
else
|
||||
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_GITHUB_VERSION))
|
||||
$(info -- $(REPO_NAME) version (Github): $(KSU_VERSION_FULL))
|
||||
endif
|
||||
|
||||
# .git is a text file while the module is imported by 'git submodule add'.
|
||||
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
||||
$(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin [ -f ../.git/shallow ] && git fetch --unshallow)
|
||||
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count main)
|
||||
# ksu_version: major * 10000 + git version + 606 for historical reasons
|
||||
$(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 606))
|
||||
$(info -- KernelSU version: $(KSU_VERSION))
|
||||
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
||||
ccflags-y += -DKSU_VERSION_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
|
||||
else # If there is no .git file, the default version will be passed.
|
||||
$(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git submodule!")
|
||||
ccflags-y += -DKSU_VERSION=16
|
||||
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
|
||||
ifndef KSU_EXPECTED_SIZE
|
||||
KSU_EXPECTED_SIZE := 0x35c
|
||||
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 +45,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 +63,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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -15,30 +15,22 @@
|
||||
#endif
|
||||
|
||||
#include "apk_sign.h"
|
||||
#include "dynamic_manager.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "kernel_compat.h"
|
||||
#include "manager_sign.h"
|
||||
|
||||
|
||||
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 sdesc *init_sdesc(struct crypto_shash *alg)
|
||||
{
|
||||
struct sdesc *sdesc;
|
||||
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,59 +71,9 @@ 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(¤t_dynamic_key.size, ¤t_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)
|
||||
unsigned expected_size, const char *expected_sha256)
|
||||
{
|
||||
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
|
||||
@@ -147,20 +89,7 @@ static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
|
||||
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;
|
||||
if (*size4 == expected_size) {
|
||||
*offset += *size4;
|
||||
|
||||
#define CERT_MAX_LENGTH 1024
|
||||
@@ -171,7 +100,7 @@ static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
|
||||
}
|
||||
ksu_kernel_read_compat(fp, cert, *size4, pos);
|
||||
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||
if (ksu_sha256(cert, *size4, digest) < 0) {
|
||||
if (IS_ERR(ksu_sha256(cert, *size4, digest))) {
|
||||
pr_info("sha256 error\n");
|
||||
return false;
|
||||
}
|
||||
@@ -180,18 +109,13 @@ static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
|
||||
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;
|
||||
pr_info("sha256: %s, expected: %s\n", hash_str,
|
||||
expected_sha256);
|
||||
if (strcmp(expected_sha256, hash_str) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return signature_valid;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct zip_entry_header {
|
||||
@@ -247,8 +171,9 @@ 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)
|
||||
static __always_inline bool check_v2_signature(char *path,
|
||||
unsigned expected_size,
|
||||
const char *expected_sha256)
|
||||
{
|
||||
unsigned char buffer[0x11] = { 0 };
|
||||
u32 size4;
|
||||
@@ -260,7 +185,7 @@ check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
|
||||
int v2_signing_blocks = 0;
|
||||
bool v3_signing_exist = false;
|
||||
bool v3_1_signing_exist = false;
|
||||
int matched_index = -1;
|
||||
|
||||
int i;
|
||||
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
@@ -268,12 +193,6 @@ check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
filp_close(fp, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// disable inotify for this file
|
||||
fp->f_mode |= FMODE_NONOTIFY;
|
||||
|
||||
@@ -325,11 +244,9 @@ 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) {
|
||||
v2_signing_valid = true;
|
||||
}
|
||||
v2_signing_valid =
|
||||
check_block(fp, &size4, &pos, &offset,
|
||||
expected_size, expected_sha256);
|
||||
} else if (id == 0xf05368c0u) {
|
||||
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
|
||||
v3_signing_exist = true;
|
||||
@@ -370,25 +287,7 @@ clean:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (v2_signing_valid) {
|
||||
if (signature_index) {
|
||||
*signature_index = matched_index;
|
||||
}
|
||||
|
||||
if (check_multi_manager) {
|
||||
// 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX: Dynamic Sign
|
||||
if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) {
|
||||
pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n",
|
||||
matched_index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
// Common manager check: any valid signature will do
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return v2_signing_valid;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
@@ -417,10 +316,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, EXPECTED_SIZE, EXPECTED_HASH);
|
||||
}
|
||||
@@ -2,10 +2,7 @@
|
||||
#define __KSU_H_APK_V2_SIGN
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "ksu.h"
|
||||
|
||||
bool is_manager_apk(char *path);
|
||||
|
||||
bool is_dynamic_manager_apk(char *path, int *signature_index);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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(®s, 0, sizeof(regs));
|
||||
|
||||
PT_REGS_PARM1(®s) = fd;
|
||||
PT_REGS_PARM2(®s) = flags;
|
||||
|
||||
// TODO: arm support
|
||||
#if (defined(__aarch64__) || defined(__x86_64__))
|
||||
return SYS_SETNS_SYMBOL(®s);
|
||||
#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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
918
kernel/core_hook.c
Normal file
918
kernel/core_hook.c
Normal file
@@ -0,0 +1,918 @@
|
||||
#include <linux/capability.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/init_task.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/lsm_hooks.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/nsproxy.h>
|
||||
#include <linux/path.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/uidgid.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/mount.h>
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
#ifdef MODULE
|
||||
#include <linux/list.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#endif
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "arch.h"
|
||||
#include "core_hook.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksu.h"
|
||||
#include "ksud.h"
|
||||
#include "manager.h"
|
||||
#include "selinux/selinux.h"
|
||||
#include "throne_tracker.h"
|
||||
#include "throne_tracker.h"
|
||||
#include "kernel_compat.h"
|
||||
|
||||
#include "kpm/kpm.h"
|
||||
|
||||
static bool ksu_module_mounted = false;
|
||||
|
||||
extern int handle_sepolicy(unsigned long arg3, void __user *arg4);
|
||||
|
||||
static bool ksu_su_compat_enabled = true;
|
||||
extern void ksu_sucompat_init();
|
||||
extern void ksu_sucompat_exit();
|
||||
|
||||
static inline bool is_allow_su()
|
||||
{
|
||||
if (is_manager()) {
|
||||
// we are manager, allow!
|
||||
return true;
|
||||
}
|
||||
return ksu_is_allow_uid(current_uid().val);
|
||||
}
|
||||
|
||||
static inline bool is_unsupported_uid(uid_t uid)
|
||||
{
|
||||
#define LAST_APPLICATION_UID 19999
|
||||
uid_t appid = uid % 100000;
|
||||
return appid > LAST_APPLICATION_UID;
|
||||
}
|
||||
|
||||
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
|
||||
|
||||
static void setup_groups(struct root_profile *profile, struct cred *cred)
|
||||
{
|
||||
if (profile->groups_count > KSU_MAX_GROUPS) {
|
||||
pr_warn("Failed to setgroups, too large group: %d!\n",
|
||||
profile->uid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (profile->groups_count == 1 && profile->groups[0] == 0) {
|
||||
// setgroup to root and return early.
|
||||
if (cred->group_info)
|
||||
put_group_info(cred->group_info);
|
||||
cred->group_info = get_group_info(&root_groups);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 ngroups = profile->groups_count;
|
||||
struct group_info *group_info = groups_alloc(ngroups);
|
||||
if (!group_info) {
|
||||
pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid);
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ngroups; i++) {
|
||||
gid_t gid = profile->groups[i];
|
||||
kgid_t kgid = make_kgid(current_user_ns(), gid);
|
||||
if (!gid_valid(kgid)) {
|
||||
pr_warn("Failed to setgroups, invalid gid: %d\n", gid);
|
||||
put_group_info(group_info);
|
||||
return;
|
||||
}
|
||||
group_info->gid[i] = kgid;
|
||||
}
|
||||
|
||||
groups_sort(group_info);
|
||||
set_groups(cred, group_info);
|
||||
}
|
||||
|
||||
static void disable_seccomp()
|
||||
{
|
||||
assert_spin_locked(¤t->sighand->siglock);
|
||||
// disable seccomp
|
||||
#if defined(CONFIG_GENERIC_ENTRY) && \
|
||||
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||
current_thread_info()->syscall_work &= ~SYSCALL_WORK_SECCOMP;
|
||||
#else
|
||||
current_thread_info()->flags &= ~(TIF_SECCOMP | _TIF_SECCOMP);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECCOMP
|
||||
current->seccomp.mode = 0;
|
||||
current->seccomp.filter = NULL;
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
|
||||
void escape_to_root(void)
|
||||
{
|
||||
struct cred *cred;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
do {
|
||||
cred = (struct cred *)__task_cred((current));
|
||||
BUG_ON(!cred);
|
||||
} while (!get_cred_rcu(cred));
|
||||
|
||||
if (cred->euid.val == 0) {
|
||||
pr_warn("Already root, don't escape!\n");
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
|
||||
|
||||
cred->uid.val = profile->uid;
|
||||
cred->suid.val = profile->uid;
|
||||
cred->euid.val = profile->uid;
|
||||
cred->fsuid.val = profile->uid;
|
||||
|
||||
cred->gid.val = profile->gid;
|
||||
cred->fsgid.val = profile->gid;
|
||||
cred->sgid.val = profile->gid;
|
||||
cred->egid.val = profile->gid;
|
||||
cred->securebits = 0;
|
||||
|
||||
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
|
||||
sizeof(kernel_cap_t));
|
||||
|
||||
// setup capabilities
|
||||
// we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process
|
||||
// we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec!
|
||||
u64 cap_for_ksud =
|
||||
profile->capabilities.effective | CAP_DAC_READ_SEARCH;
|
||||
memcpy(&cred->cap_effective, &cap_for_ksud,
|
||||
sizeof(cred->cap_effective));
|
||||
memcpy(&cred->cap_permitted, &profile->capabilities.effective,
|
||||
sizeof(cred->cap_permitted));
|
||||
memcpy(&cred->cap_bset, &profile->capabilities.effective,
|
||||
sizeof(cred->cap_bset));
|
||||
|
||||
setup_groups(profile, cred);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
// Refer to kernel/seccomp.c: seccomp_set_mode_strict
|
||||
// When disabling Seccomp, ensure that current->sighand->siglock is held during the operation.
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
disable_seccomp();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
setup_selinux(profile->selinux_domain);
|
||||
}
|
||||
|
||||
int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
|
||||
{
|
||||
if (!current->mm) {
|
||||
// skip kernel threads
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (current_uid().val != 1000) {
|
||||
// skip non system uid
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!old_dentry || !new_dentry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// /data/system/packages.list.tmp -> /data/system/packages.list
|
||||
if (strcmp(new_dentry->d_iname, "packages.list")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char path[128];
|
||||
char *buf = dentry_path_raw(new_dentry, path, sizeof(path));
|
||||
if (IS_ERR(buf)) {
|
||||
pr_err("dentry_path_raw failed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strstr(buf, "/system/packages.list")) {
|
||||
return 0;
|
||||
}
|
||||
pr_info("renameat: %s -> %s, new path: %s\n", old_dentry->d_iname,
|
||||
new_dentry->d_iname, buf);
|
||||
|
||||
track_throne();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXT4_FS
|
||||
static void nuke_ext4_sysfs() {
|
||||
struct path path;
|
||||
int err = kern_path("/data/adb/modules", 0, &path);
|
||||
if (err) {
|
||||
pr_err("nuke path err: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
struct super_block* sb = path.dentry->d_inode->i_sb;
|
||||
const char* name = sb->s_type->name;
|
||||
if (strcmp(name, "ext4") != 0) {
|
||||
pr_info("nuke but module aren't mounted\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ext4_unregister_sysfs(sb);
|
||||
}
|
||||
#else
|
||||
static inline void nuke_ext4_sysfs() { }
|
||||
#endif
|
||||
|
||||
int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
// if success, we modify the arg5 as result!
|
||||
u32 *result = (u32 *)arg5;
|
||||
u32 reply_ok = KERNEL_SU_OPTION;
|
||||
|
||||
if (KERNEL_SU_OPTION != option) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: find it in throne tracker!
|
||||
uid_t current_uid_val = current_uid().val;
|
||||
uid_t manager_uid = ksu_get_manager_uid();
|
||||
if (current_uid_val != manager_uid &&
|
||||
current_uid_val % 100000 == manager_uid) {
|
||||
ksu_set_manager_uid(current_uid_val);
|
||||
}
|
||||
|
||||
bool from_root = 0 == current_uid().val;
|
||||
bool from_manager = is_manager();
|
||||
|
||||
if (!from_root && !from_manager) {
|
||||
// only root or manager can access this interface
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("option: 0x%x, cmd: %ld\n", option, arg2);
|
||||
#endif
|
||||
|
||||
if (arg2 == CMD_BECOME_MANAGER) {
|
||||
if (from_manager) {
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("become_manager: prctl reply error\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_GRANT_ROOT) {
|
||||
if (is_allow_su()) {
|
||||
pr_info("allow root for: %d\n", current_uid().val);
|
||||
escape_to_root();
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("grant_root: prctl reply error\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Both root manager and root processes should be allowed to get version
|
||||
if (arg2 == CMD_GET_VERSION) {
|
||||
u32 version = KERNEL_SU_VERSION;
|
||||
if (copy_to_user(arg3, &version, sizeof(version))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
u32 version_flags = 0;
|
||||
#ifdef MODULE
|
||||
version_flags |= 0x1;
|
||||
#endif
|
||||
if (arg4 &&
|
||||
copy_to_user(arg4, &version_flags, sizeof(version_flags))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_REPORT_EVENT) {
|
||||
if (!from_root) {
|
||||
return 0;
|
||||
}
|
||||
switch (arg3) {
|
||||
case EVENT_POST_FS_DATA: {
|
||||
static bool post_fs_data_lock = false;
|
||||
if (!post_fs_data_lock) {
|
||||
post_fs_data_lock = true;
|
||||
pr_info("post-fs-data triggered\n");
|
||||
on_post_fs_data();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EVENT_BOOT_COMPLETED: {
|
||||
static bool boot_complete_lock = false;
|
||||
if (!boot_complete_lock) {
|
||||
boot_complete_lock = true;
|
||||
pr_info("boot_complete triggered\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EVENT_MODULE_MOUNTED: {
|
||||
ksu_module_mounted = true;
|
||||
pr_info("module mounted!\n");
|
||||
nuke_ext4_sysfs();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_SET_SEPOLICY) {
|
||||
if (!from_root) {
|
||||
return 0;
|
||||
}
|
||||
if (!handle_sepolicy(arg3, arg4)) {
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("sepolicy: prctl reply error\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_CHECK_SAFEMODE) {
|
||||
if (ksu_is_safe_mode()) {
|
||||
pr_warn("safemode enabled!\n");
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("safemode: prctl reply error\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) {
|
||||
u32 array[128];
|
||||
u32 array_length;
|
||||
bool success = ksu_get_allow_list(array, &array_length,
|
||||
arg2 == CMD_GET_ALLOW_LIST);
|
||||
if (success) {
|
||||
if (!copy_to_user(arg4, &array_length,
|
||||
sizeof(array_length)) &&
|
||||
!copy_to_user(arg3, array,
|
||||
sizeof(u32) * array_length)) {
|
||||
if (copy_to_user(result, &reply_ok,
|
||||
sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n",
|
||||
arg2);
|
||||
}
|
||||
} else {
|
||||
pr_err("prctl copy allowlist error\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_UID_GRANTED_ROOT || arg2 == CMD_UID_SHOULD_UMOUNT) {
|
||||
uid_t target_uid = (uid_t)arg3;
|
||||
bool allow = false;
|
||||
if (arg2 == CMD_UID_GRANTED_ROOT) {
|
||||
allow = ksu_is_allow_uid(target_uid);
|
||||
} else if (arg2 == CMD_UID_SHOULD_UMOUNT) {
|
||||
allow = ksu_uid_should_umount(target_uid);
|
||||
} else {
|
||||
pr_err("unknown cmd: %lu\n", arg2);
|
||||
}
|
||||
if (!copy_to_user(arg4, &allow, sizeof(allow))) {
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
} else {
|
||||
pr_err("prctl copy err, cmd: %lu\n", arg2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KPM
|
||||
// ADD: 添加KPM模块控制
|
||||
if(sukisu_is_kpm_control_code(arg2)) {
|
||||
int res;
|
||||
|
||||
pr_info("KPM: calling before arg2=%d\n", (int) arg2);
|
||||
|
||||
res = sukisu_handle_kpm(arg2, arg3, arg4, arg5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (arg2 == CMD_ENABLE_KPM) {
|
||||
bool KPM_Enabled = IS_ENABLED(CONFIG_KPM);
|
||||
if (copy_to_user((void __user *)arg3, &KPM_Enabled, sizeof(KPM_Enabled)))
|
||||
pr_info("KPM: copy_to_user() failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// all other cmds are for 'root manager'
|
||||
if (!from_manager) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we are already manager
|
||||
if (arg2 == CMD_GET_APP_PROFILE) {
|
||||
struct app_profile profile;
|
||||
if (copy_from_user(&profile, arg3, sizeof(profile))) {
|
||||
pr_err("copy profile failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool success = ksu_get_app_profile(&profile);
|
||||
if (success) {
|
||||
if (copy_to_user(arg3, &profile, sizeof(profile))) {
|
||||
pr_err("copy profile failed\n");
|
||||
return 0;
|
||||
}
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_SET_APP_PROFILE) {
|
||||
struct app_profile profile;
|
||||
if (copy_from_user(&profile, arg3, sizeof(profile))) {
|
||||
pr_err("copy profile failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// todo: validate the params
|
||||
if (ksu_set_app_profile(&profile, true)) {
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_IS_SU_ENABLED) {
|
||||
if (copy_to_user(arg3, &ksu_su_compat_enabled,
|
||||
sizeof(ksu_su_compat_enabled))) {
|
||||
pr_err("copy su compat failed\n");
|
||||
return 0;
|
||||
}
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_ENABLE_SU) {
|
||||
bool enabled = (arg3 != 0);
|
||||
if (enabled == ksu_su_compat_enabled) {
|
||||
pr_info("cmd enable su but no need to change.\n");
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {// return the reply_ok directly
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
ksu_sucompat_init();
|
||||
} else {
|
||||
ksu_sucompat_exit();
|
||||
}
|
||||
ksu_su_compat_enabled = enabled;
|
||||
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_appuid(kuid_t uid)
|
||||
{
|
||||
#define PER_USER_RANGE 100000
|
||||
#define FIRST_APPLICATION_UID 10000
|
||||
#define LAST_APPLICATION_UID 19999
|
||||
|
||||
uid_t appid = uid.val % PER_USER_RANGE;
|
||||
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
|
||||
}
|
||||
|
||||
static bool should_umount(struct path *path)
|
||||
{
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
|
||||
pr_info("ignore global mnt namespace process: %d\n",
|
||||
current_uid().val);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) {
|
||||
const char *fstype = path->mnt->mnt_sb->s_type->name;
|
||||
return strcmp(fstype, "overlay") == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ksu_umount_mnt(struct path *path, int flags)
|
||||
{
|
||||
int err = path_umount(path, flags);
|
||||
if (err) {
|
||||
pr_info("umount %s failed: %d\n", path->dentry->d_iname, err);
|
||||
}
|
||||
}
|
||||
|
||||
static void try_umount(const char *mnt, bool check_mnt, int flags)
|
||||
{
|
||||
struct path path;
|
||||
int err = kern_path(mnt, 0, &path);
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.dentry != path.mnt->mnt_root) {
|
||||
// it is not root mountpoint, maybe umounted by others already.
|
||||
return;
|
||||
}
|
||||
|
||||
// we are only interest in some specific mounts
|
||||
if (check_mnt && !should_umount(&path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ksu_umount_mnt(&path, flags);
|
||||
}
|
||||
|
||||
int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
{
|
||||
// this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it!
|
||||
if (!ksu_module_mounted) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!new || !old) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
kuid_t new_uid = new->uid;
|
||||
kuid_t old_uid = old->uid;
|
||||
|
||||
if (0 != old_uid.val) {
|
||||
// old process is not root, ignore it.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!is_appuid(new_uid) || is_unsupported_uid(new_uid.val)) {
|
||||
// pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid.val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ksu_is_allow_uid(new_uid.val)) {
|
||||
// pr_info("handle setuid ignore allowed application: %d\n", new_uid.val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ksu_uid_should_umount(new_uid.val)) {
|
||||
return 0;
|
||||
} else {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("uid: %d should not umount!\n", current_uid().val);
|
||||
#endif
|
||||
}
|
||||
|
||||
// check old process's selinux context, if it is not zygote, ignore it!
|
||||
// because some su apps may setuid to untrusted_app but they are in global mount namespace
|
||||
// when we umount for such process, that is a disaster!
|
||||
bool is_zygote_child = is_zygote(old->security);
|
||||
if (!is_zygote_child) {
|
||||
pr_info("handle umount ignore non zygote child: %d\n",
|
||||
current->pid);
|
||||
return 0;
|
||||
}
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
// umount the target mnt
|
||||
pr_info("handle umount for uid: %d, pid: %d\n", new_uid.val,
|
||||
current->pid);
|
||||
#endif
|
||||
|
||||
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
||||
// filter the mountpoint whose target is `/data/adb`
|
||||
try_umount("/system", true, 0);
|
||||
try_umount("/vendor", true, 0);
|
||||
try_umount("/product", true, 0);
|
||||
try_umount("/system_ext", true, 0);
|
||||
try_umount("/data/adb/modules", false, MNT_DETACH);
|
||||
|
||||
// try umount ksu temp path
|
||||
try_umount("/debug_ramdisk", false, MNT_DETACH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Init functons
|
||||
|
||||
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
int option = (int)PT_REGS_PARM1(real_regs);
|
||||
unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs);
|
||||
unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs);
|
||||
// PRCTL_SYMBOL is the arch-specificed one, which receive raw pt_regs from syscall
|
||||
unsigned long arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs);
|
||||
unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs);
|
||||
|
||||
return ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
static struct kprobe prctl_kp = {
|
||||
.symbol_name = PRCTL_SYMBOL,
|
||||
.pre_handler = handler_pre,
|
||||
};
|
||||
|
||||
static int renameat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
// https://elixir.bootlin.com/linux/v5.12-rc1/source/include/linux/fs.h
|
||||
struct renamedata *rd = PT_REGS_PARM1(regs);
|
||||
struct dentry *old_entry = rd->old_dentry;
|
||||
struct dentry *new_entry = rd->new_dentry;
|
||||
#else
|
||||
struct dentry *old_entry = (struct dentry *)PT_REGS_PARM2(regs);
|
||||
struct dentry *new_entry = (struct dentry *)PT_REGS_CCALL_PARM4(regs);
|
||||
#endif
|
||||
|
||||
return ksu_handle_rename(old_entry, new_entry);
|
||||
}
|
||||
|
||||
static struct kprobe renameat_kp = {
|
||||
.symbol_name = "vfs_rename",
|
||||
.pre_handler = renameat_handler_pre,
|
||||
};
|
||||
|
||||
__maybe_unused int ksu_kprobe_init(void)
|
||||
{
|
||||
int rc = 0;
|
||||
rc = register_kprobe(&prctl_kp);
|
||||
|
||||
if (rc) {
|
||||
pr_info("prctl kprobe failed: %d.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = register_kprobe(&renameat_kp);
|
||||
pr_info("renameat kp: %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
__maybe_unused int ksu_kprobe_exit(void)
|
||||
{
|
||||
unregister_kprobe(&prctl_kp);
|
||||
unregister_kprobe(&renameat_kp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
|
||||
struct inode *new_inode, struct dentry *new_dentry)
|
||||
{
|
||||
return ksu_handle_rename(old_dentry, new_dentry);
|
||||
}
|
||||
|
||||
static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
|
||||
int flags)
|
||||
{
|
||||
return ksu_handle_setuid(new, old);
|
||||
}
|
||||
|
||||
#ifndef MODULE
|
||||
static struct security_hook_list ksu_hooks[] = {
|
||||
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
|
||||
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
|
||||
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),
|
||||
};
|
||||
|
||||
void __init ksu_lsm_hook_init(void)
|
||||
{
|
||||
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu");
|
||||
}
|
||||
|
||||
#else
|
||||
static int override_security_head(void *head, const void *new_head, size_t len)
|
||||
{
|
||||
unsigned long base = (unsigned long)head & PAGE_MASK;
|
||||
unsigned long offset = offset_in_page(head);
|
||||
|
||||
// this is impossible for our case because the page alignment
|
||||
// but be careful for other cases!
|
||||
BUG_ON(offset + len > PAGE_SIZE);
|
||||
struct page *page = phys_to_page(__pa(base));
|
||||
if (!page) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
void *addr = vmap(&page, 1, VM_MAP, PAGE_KERNEL);
|
||||
if (!addr) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
local_irq_disable();
|
||||
memcpy(addr + offset, new_head, len);
|
||||
local_irq_enable();
|
||||
vunmap(addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_security_hook_list(struct hlist_head *head)
|
||||
{
|
||||
struct hlist_node *temp;
|
||||
struct security_hook_list *entry;
|
||||
|
||||
if (!head)
|
||||
return;
|
||||
|
||||
hlist_for_each_entry_safe (entry, temp, head, list) {
|
||||
hlist_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
kfree(head);
|
||||
}
|
||||
|
||||
struct hlist_head *copy_security_hlist(struct hlist_head *orig)
|
||||
{
|
||||
struct hlist_head *new_head = kmalloc(sizeof(*new_head), GFP_KERNEL);
|
||||
if (!new_head)
|
||||
return NULL;
|
||||
|
||||
INIT_HLIST_HEAD(new_head);
|
||||
|
||||
struct security_hook_list *entry;
|
||||
struct security_hook_list *new_entry;
|
||||
|
||||
hlist_for_each_entry (entry, orig, list) {
|
||||
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
|
||||
if (!new_entry) {
|
||||
free_security_hook_list(new_head);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*new_entry = *entry;
|
||||
|
||||
hlist_add_tail_rcu(&new_entry->list, new_head);
|
||||
}
|
||||
|
||||
return new_head;
|
||||
}
|
||||
|
||||
#define LSM_SEARCH_MAX 180 // This should be enough to iterate
|
||||
static void *find_head_addr(void *security_ptr, int *index)
|
||||
{
|
||||
if (!security_ptr) {
|
||||
return NULL;
|
||||
}
|
||||
struct hlist_head *head_start =
|
||||
(struct hlist_head *)&security_hook_heads;
|
||||
|
||||
for (int i = 0; i < LSM_SEARCH_MAX; i++) {
|
||||
struct hlist_head *head = head_start + i;
|
||||
struct security_hook_list *pos;
|
||||
hlist_for_each_entry (pos, head, list) {
|
||||
if (pos->hook.capget == security_ptr) {
|
||||
if (index) {
|
||||
*index = i;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define GET_SYMBOL_ADDR(sym) \
|
||||
({ \
|
||||
void *addr = kallsyms_lookup_name(#sym ".cfi_jt"); \
|
||||
if (!addr) { \
|
||||
addr = kallsyms_lookup_name(#sym); \
|
||||
} \
|
||||
addr; \
|
||||
})
|
||||
|
||||
#define KSU_LSM_HOOK_HACK_INIT(head_ptr, name, func) \
|
||||
do { \
|
||||
static struct security_hook_list hook = { \
|
||||
.hook = { .name = func } \
|
||||
}; \
|
||||
hook.head = head_ptr; \
|
||||
hook.lsm = "ksu"; \
|
||||
struct hlist_head *new_head = copy_security_hlist(hook.head); \
|
||||
if (!new_head) { \
|
||||
pr_err("Failed to copy security list: %s\n", #name); \
|
||||
break; \
|
||||
} \
|
||||
hlist_add_tail_rcu(&hook.list, new_head); \
|
||||
if (override_security_head(hook.head, new_head, \
|
||||
sizeof(*new_head))) { \
|
||||
free_security_hook_list(new_head); \
|
||||
pr_err("Failed to hack lsm for: %s\n", #name); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void __init ksu_lsm_hook_init(void)
|
||||
{
|
||||
void *cap_prctl = GET_SYMBOL_ADDR(cap_task_prctl);
|
||||
void *prctl_head = find_head_addr(cap_prctl, NULL);
|
||||
if (prctl_head) {
|
||||
if (prctl_head != &security_hook_heads.task_prctl) {
|
||||
pr_warn("prctl's address has shifted!\n");
|
||||
}
|
||||
KSU_LSM_HOOK_HACK_INIT(prctl_head, task_prctl, ksu_task_prctl);
|
||||
} else {
|
||||
pr_warn("Failed to find task_prctl!\n");
|
||||
}
|
||||
|
||||
int inode_killpriv_index = -1;
|
||||
void *cap_killpriv = GET_SYMBOL_ADDR(cap_inode_killpriv);
|
||||
find_head_addr(cap_killpriv, &inode_killpriv_index);
|
||||
if (inode_killpriv_index < 0) {
|
||||
pr_warn("Failed to find inode_rename, use kprobe instead!\n");
|
||||
register_kprobe(&renameat_kp);
|
||||
} else {
|
||||
int inode_rename_index = inode_killpriv_index +
|
||||
&security_hook_heads.inode_rename -
|
||||
&security_hook_heads.inode_killpriv;
|
||||
struct hlist_head *head_start =
|
||||
(struct hlist_head *)&security_hook_heads;
|
||||
void *inode_rename_head = head_start + inode_rename_index;
|
||||
if (inode_rename_head != &security_hook_heads.inode_rename) {
|
||||
pr_warn("inode_rename's address has shifted!\n");
|
||||
}
|
||||
KSU_LSM_HOOK_HACK_INIT(inode_rename_head, inode_rename,
|
||||
ksu_inode_rename);
|
||||
}
|
||||
void *cap_setuid = GET_SYMBOL_ADDR(cap_task_fix_setuid);
|
||||
void *setuid_head = find_head_addr(cap_setuid, NULL);
|
||||
if (setuid_head) {
|
||||
if (setuid_head != &security_hook_heads.task_fix_setuid) {
|
||||
pr_warn("setuid's address has shifted!\n");
|
||||
}
|
||||
KSU_LSM_HOOK_HACK_INIT(setuid_head, task_fix_setuid,
|
||||
ksu_task_fix_setuid);
|
||||
} else {
|
||||
pr_warn("Failed to find task_fix_setuid!\n");
|
||||
}
|
||||
smp_mb();
|
||||
}
|
||||
#endif
|
||||
|
||||
void __init ksu_core_init(void)
|
||||
{
|
||||
ksu_lsm_hook_init();
|
||||
}
|
||||
|
||||
void ksu_core_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_KPROBE
|
||||
pr_info("ksu_core_kprobe_exit\n");
|
||||
// we dont use this now
|
||||
// ksu_kprobe_exit();
|
||||
#endif
|
||||
}
|
||||
9
kernel/core_hook.h
Normal file
9
kernel/core_hook.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef __KSU_H_KSU_CORE
|
||||
#define __KSU_H_KSU_CORE
|
||||
|
||||
#include <linux/init.h>
|
||||
|
||||
void __init ksu_core_init(void);
|
||||
void ksu_core_exit(void);
|
||||
|
||||
#endif
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
176
kernel/feature.c
176
kernel/feature.c
@@ -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");
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
28
kernel/include/ksu_hook.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef __KSU_H_KSHOOK
|
||||
#define __KSU_H_KSHOOK
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
// For sucompat
|
||||
|
||||
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||
int *flags);
|
||||
|
||||
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
||||
|
||||
// For ksud
|
||||
|
||||
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||
size_t *count_ptr, loff_t **pos);
|
||||
|
||||
// For ksud and sucompat
|
||||
|
||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||
void *envp, int *flags);
|
||||
|
||||
// For volume button
|
||||
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||
int *value);
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -29,48 +29,48 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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 },
|
||||
@@ -78,24 +78,21 @@ static struct CompactAddressSymbol address_symbol[] = {
|
||||
{ "get_manager_uid", &sukisu_get_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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
328
kernel/kpm/kpm.c
328
kernel/kpm/kpm.c
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
70
kernel/ksu.c
70
kernel/ksu.c
@@ -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
|
||||
|
||||
86
kernel/ksu.h
86
kernel/ksu.h
@@ -7,43 +7,84 @@
|
||||
#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_ENABLE_KPM 100
|
||||
|
||||
#define EVENT_POST_FS_DATA 1
|
||||
#define EVENT_BOOT_COMPLETED 2
|
||||
#define EVENT_MODULE_MOUNTED 3
|
||||
|
||||
// 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 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
|
||||
|
||||
#define DYNAMIC_MANAGER_OP_SET 0
|
||||
#define DYNAMIC_MANAGER_OP_GET 1
|
||||
#define DYNAMIC_MANAGER_OP_CLEAR 2
|
||||
struct root_profile {
|
||||
int32_t uid;
|
||||
int32_t gid;
|
||||
|
||||
#define UID_SCANNER_OP_GET_STATUS 0
|
||||
#define UID_SCANNER_OP_TOGGLE 1
|
||||
#define UID_SCANNER_OP_CLEAR_ENV 2
|
||||
int32_t groups_count;
|
||||
int32_t groups[KSU_MAX_GROUPS];
|
||||
|
||||
struct dynamic_manager_user_config {
|
||||
unsigned int operation;
|
||||
unsigned int size;
|
||||
char hash[65];
|
||||
// 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 manager_list_info {
|
||||
int count;
|
||||
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 {
|
||||
uid_t uid;
|
||||
int signature_index;
|
||||
} managers[2];
|
||||
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 +98,5 @@ static inline int endswith(const char *s, const char *t)
|
||||
return 1;
|
||||
return strcmp(s + slen - tlen, t);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
563
kernel/ksud.c
563
kernel/ksud.c
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -8,23 +8,17 @@
|
||||
|
||||
extern uid_t ksu_manager_uid; // DO NOT DIRECT USE
|
||||
|
||||
extern bool ksu_is_any_manager(uid_t uid);
|
||||
extern void ksu_add_manager(uid_t uid, int signature_index);
|
||||
extern void ksu_remove_manager(uid_t uid);
|
||||
extern int ksu_get_manager_signature_index(uid_t uid);
|
||||
|
||||
static inline bool ksu_is_manager_uid_valid(void)
|
||||
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_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 +28,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
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#ifndef MANAGER_SIGN_H
|
||||
#define MANAGER_SIGN_H
|
||||
|
||||
// ShirkNeko/KernelSU
|
||||
#define EXPECTED_SIZE_SHIRKNEKO 0x35c
|
||||
#define EXPECTED_HASH_SHIRKNEKO \
|
||||
"947ae944f3de4ed4c21a7e4f7953ecf351bfa2b36239da37a34111ad29993eef"
|
||||
|
||||
// Dynamic Sign
|
||||
#define EXPECTED_SIZE_OTHER 0x300
|
||||
#define EXPECTED_HASH_OTHER \
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
|
||||
typedef struct {
|
||||
unsigned size;
|
||||
const char *sha256;
|
||||
} apk_sign_key_t;
|
||||
|
||||
#endif /* MANAGER_SIGN_H */
|
||||
@@ -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__);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
16
kernel/selinux/Makefile
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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(¤t->sighand->siglock);
|
||||
ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot);
|
||||
ksu_set_task_tracepoint_flag(current);
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ksu_is_allow_uid_for_current(new_uid)) {
|
||||
if (current->seccomp.mode == SECCOMP_MODE_FILTER &&
|
||||
current->seccomp.filter) {
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
ksu_seccomp_allow_cache(current->seccomp.filter,
|
||||
__NR_reboot);
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
}
|
||||
ksu_set_task_tracepoint_flag(current);
|
||||
} else {
|
||||
ksu_clear_task_tracepoint_flag_if_needed(current);
|
||||
}
|
||||
#else
|
||||
if (ksu_is_allow_uid_for_current(new_uid)) {
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
disable_seccomp(current);
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
if (ksu_get_manager_uid() == new_uid) {
|
||||
pr_info("install fd for ksu manager(uid=%d)\n",
|
||||
new_uid);
|
||||
ksu_install_fd();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Handle kernel umount
|
||||
ksu_handle_umount(old_uid, new_uid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -41,7 +41,8 @@ setup_kernelsu() {
|
||||
echo "[+] Setting up KernelSU..."
|
||||
# Clone the repository and rename it to KernelSU
|
||||
if [ ! -d "$GKI_ROOT/KernelSU" ]; then
|
||||
git clone https://github.com/SukiSU-Ultra/SukiSU-Ultra KernelSU
|
||||
git clone https://github.com/SukiSU-Ultra/SukiSU-Ultra SukiSU-Ultra
|
||||
mv SukiSU-Ultra KernelSU
|
||||
echo "[+] Repository cloned and renamed to KernelSU."
|
||||
fi
|
||||
cd "$GKI_ROOT/KernelSU"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,215 +0,0 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "klog.h"
|
||||
#include "throne_comm.h"
|
||||
#include "kernel_compat.h"
|
||||
#include "ksu.h"
|
||||
|
||||
#define PROC_UID_SCANNER "ksu_uid_scanner"
|
||||
#define UID_SCANNER_STATE_FILE "/data/adb/ksu/.uid_scanner"
|
||||
|
||||
static struct proc_dir_entry *proc_entry = NULL;
|
||||
static struct workqueue_struct *scanner_wq = NULL;
|
||||
static struct work_struct scan_work;
|
||||
static struct work_struct ksu_state_save_work;
|
||||
static struct work_struct ksu_state_load_work;
|
||||
|
||||
|
||||
// Signal userspace to rescan
|
||||
static bool need_rescan = false;
|
||||
|
||||
static void rescan_work_fn(struct work_struct *work)
|
||||
{
|
||||
// Signal userspace through proc interface
|
||||
need_rescan = true;
|
||||
pr_info("requested userspace uid rescan\n");
|
||||
}
|
||||
|
||||
void ksu_request_userspace_scan(void)
|
||||
{
|
||||
if (scanner_wq) {
|
||||
queue_work(scanner_wq, &scan_work);
|
||||
}
|
||||
}
|
||||
|
||||
void ksu_handle_userspace_update(void)
|
||||
{
|
||||
// Called when userspace notifies update complete
|
||||
need_rescan = false;
|
||||
pr_info("userspace uid list updated\n");
|
||||
}
|
||||
|
||||
static void do_save_throne_state(struct work_struct *work)
|
||||
{
|
||||
struct file *fp;
|
||||
char state_char = ksu_uid_scanner_enabled ? '1' : '0';
|
||||
loff_t off = 0;
|
||||
|
||||
fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("save_throne_state create file failed: %ld\n", PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ksu_kernel_write_compat(fp, &state_char, sizeof(state_char), &off) != sizeof(state_char)) {
|
||||
pr_err("save_throne_state write failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
pr_info("throne state saved: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled");
|
||||
|
||||
exit:
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
|
||||
void do_load_throne_state(struct work_struct *work)
|
||||
{
|
||||
struct file *fp;
|
||||
char state_char;
|
||||
loff_t off = 0;
|
||||
ssize_t ret;
|
||||
|
||||
fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_info("throne state file not found, using default: disabled\n");
|
||||
ksu_uid_scanner_enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = ksu_kernel_read_compat(fp, &state_char, sizeof(state_char), &off);
|
||||
if (ret != sizeof(state_char)) {
|
||||
pr_err("load_throne_state read err: %zd\n", ret);
|
||||
ksu_uid_scanner_enabled = false;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ksu_uid_scanner_enabled = (state_char == '1');
|
||||
pr_info("throne state loaded: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled");
|
||||
|
||||
exit:
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
|
||||
bool ksu_throne_comm_load_state(void)
|
||||
{
|
||||
return ksu_queue_work(&ksu_state_load_work);
|
||||
}
|
||||
|
||||
void ksu_throne_comm_save_state(void)
|
||||
{
|
||||
ksu_queue_work(&ksu_state_save_work);
|
||||
}
|
||||
|
||||
static int uid_scanner_show(struct seq_file *m, void *v)
|
||||
{
|
||||
if (need_rescan) {
|
||||
seq_puts(m, "RESCAN\n");
|
||||
} else {
|
||||
seq_puts(m, "OK\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uid_scanner_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, uid_scanner_show, NULL);
|
||||
}
|
||||
|
||||
static ssize_t uid_scanner_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
char cmd[16];
|
||||
|
||||
if (count >= sizeof(cmd))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(cmd, buffer, count))
|
||||
return -EFAULT;
|
||||
|
||||
cmd[count] = '\0';
|
||||
|
||||
// Remove newline if present
|
||||
if (count > 0 && cmd[count-1] == '\n')
|
||||
cmd[count-1] = '\0';
|
||||
|
||||
if (strcmp(cmd, "UPDATED") == 0) {
|
||||
ksu_handle_userspace_update();
|
||||
pr_info("received userspace update notification\n");
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#ifdef KSU_COMPAT_HAS_PROC_OPS
|
||||
static const struct proc_ops uid_scanner_proc_ops = {
|
||||
.proc_open = uid_scanner_open,
|
||||
.proc_read = seq_read,
|
||||
.proc_write = uid_scanner_write,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = single_release,
|
||||
};
|
||||
#else
|
||||
static const struct file_operations uid_scanner_proc_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = uid_scanner_open,
|
||||
.read = seq_read,
|
||||
.write = uid_scanner_write,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
#endif
|
||||
|
||||
int ksu_throne_comm_init(void)
|
||||
{
|
||||
// Create workqueue
|
||||
scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1);
|
||||
if (!scanner_wq) {
|
||||
pr_err("failed to create scanner workqueue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_WORK(&scan_work, rescan_work_fn);
|
||||
|
||||
// Create proc entry
|
||||
proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops);
|
||||
if (!proc_entry) {
|
||||
pr_err("failed to create proc entry\n");
|
||||
destroy_workqueue(scanner_wq);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pr_info("throne communication initialized\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksu_throne_comm_exit(void)
|
||||
{
|
||||
if (proc_entry) {
|
||||
proc_remove(proc_entry);
|
||||
proc_entry = NULL;
|
||||
}
|
||||
|
||||
if (scanner_wq) {
|
||||
destroy_workqueue(scanner_wq);
|
||||
scanner_wq = NULL;
|
||||
}
|
||||
|
||||
pr_info("throne communication cleaned up\n");
|
||||
}
|
||||
|
||||
int ksu_uid_init(void)
|
||||
{
|
||||
INIT_WORK(&ksu_state_save_work, do_save_throne_state);
|
||||
INIT_WORK(&ksu_state_load_work, do_load_throne_state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksu_uid_exit(void)
|
||||
{
|
||||
do_save_throne_state(NULL);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef __KSU_H_THRONE_COMM
|
||||
#define __KSU_H_THRONE_COMM
|
||||
|
||||
void ksu_request_userspace_scan(void);
|
||||
|
||||
void ksu_handle_userspace_update(void);
|
||||
|
||||
int ksu_throne_comm_init(void);
|
||||
|
||||
void ksu_throne_comm_exit(void);
|
||||
|
||||
int ksu_uid_init(void);
|
||||
|
||||
void ksu_uid_exit(void);
|
||||
|
||||
bool ksu_throne_comm_load_state(void);
|
||||
|
||||
void ksu_throne_comm_save_state(void);
|
||||
|
||||
void do_load_throne_state(struct work_struct *work);
|
||||
|
||||
#endif
|
||||
@@ -5,23 +5,17 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksu.h"
|
||||
#include "manager.h"
|
||||
#include "throne_tracker.h"
|
||||
#include "apk_sign.h"
|
||||
#include "dynamic_manager.h"
|
||||
#include "throne_comm.h"
|
||||
#include "kernel_compat.h"
|
||||
|
||||
uid_t ksu_manager_uid = KSU_INVALID_UID;
|
||||
static uid_t locked_manager_uid = KSU_INVALID_UID;
|
||||
static uid_t locked_dynamic_manager_uid = KSU_INVALID_UID;
|
||||
|
||||
#define KSU_UID_LIST_PATH "/data/misc/user_uid/uid_list"
|
||||
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list"
|
||||
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list.tmp"
|
||||
|
||||
struct uid_data {
|
||||
struct list_head list;
|
||||
@@ -29,77 +23,6 @@ struct uid_data {
|
||||
char package[KSU_MAX_PACKAGE_NAME];
|
||||
};
|
||||
|
||||
// Try read /data/misc/user_uid/uid_list
|
||||
static int uid_from_um_list(struct list_head *uid_list)
|
||||
{
|
||||
struct file *fp;
|
||||
char *buf = NULL;
|
||||
loff_t size, pos = 0;
|
||||
ssize_t nr;
|
||||
int cnt = 0;
|
||||
|
||||
fp = filp_open(KSU_UID_LIST_PATH, O_RDONLY, 0);
|
||||
if (IS_ERR(fp))
|
||||
return -ENOENT;
|
||||
|
||||
size = fp->f_inode->i_size;
|
||||
if (size <= 0) {
|
||||
filp_close(fp, NULL);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
buf = kzalloc(size + 1, GFP_ATOMIC);
|
||||
if (!buf) {
|
||||
pr_err("uid_list: OOM %lld B\n", size);
|
||||
filp_close(fp, NULL);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
nr = kernel_read(fp, buf, size, &pos);
|
||||
filp_close(fp, NULL);
|
||||
if (nr != size) {
|
||||
pr_err("uid_list: short read %zd/%lld\n", nr, size);
|
||||
kfree(buf);
|
||||
return -EIO;
|
||||
}
|
||||
buf[size] = '\0';
|
||||
|
||||
for (char *line = buf, *next; line; line = next) {
|
||||
next = strchr(line, '\n');
|
||||
if (next) *next++ = '\0';
|
||||
|
||||
while (*line == ' ' || *line == '\t' || *line == '\r') ++line;
|
||||
if (!*line) continue;
|
||||
|
||||
char *uid_str = strsep(&line, " \t");
|
||||
char *pkg = line;
|
||||
if (!pkg) continue;
|
||||
while (*pkg == ' ' || *pkg == '\t') ++pkg;
|
||||
if (!*pkg) continue;
|
||||
|
||||
u32 uid;
|
||||
if (kstrtou32(uid_str, 10, &uid)) {
|
||||
pr_warn_once("uid_list: bad uid <%s>\n", uid_str);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct uid_data *d = kzalloc(sizeof(*d), GFP_ATOMIC);
|
||||
if (unlikely(!d)) {
|
||||
pr_err("uid_list: OOM uid=%u\n", uid);
|
||||
continue;
|
||||
}
|
||||
|
||||
d->uid = uid;
|
||||
strscpy(d->package, pkg, KSU_MAX_PACKAGE_NAME);
|
||||
list_add_tail(&d->list, uid_list);
|
||||
++cnt;
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
pr_info("uid_list: loaded %d entries\n", cnt);
|
||||
return cnt > 0 ? 0 : -ENODATA;
|
||||
}
|
||||
|
||||
static int get_pkg_from_apk_path(char *pkg, const char *path)
|
||||
{
|
||||
int len = strlen(path);
|
||||
@@ -139,7 +62,7 @@ static int get_pkg_from_apk_path(char *pkg, const char *path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void crown_manager(const char *apk, struct list_head *uid_data, int signature_index)
|
||||
static void crown_manager(const char *apk, struct list_head *uid_data)
|
||||
{
|
||||
char pkg[KSU_MAX_PACKAGE_NAME];
|
||||
if (get_pkg_from_apk_path(pkg, apk) < 0) {
|
||||
@@ -147,7 +70,7 @@ static void crown_manager(const char *apk, struct list_head *uid_data, int signa
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("manager pkg: %s, signature_index: %d\n", pkg, signature_index);
|
||||
pr_info("manager pkg: %s\n", pkg);
|
||||
|
||||
#ifdef KSU_MANAGER_PACKAGE
|
||||
// pkg is `/<real package>`
|
||||
@@ -157,42 +80,13 @@ static void crown_manager(const char *apk, struct list_head *uid_data, int signa
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
struct list_head *list = (struct list_head *)uid_data;
|
||||
struct uid_data *np;
|
||||
|
||||
list_for_each_entry(np, uid_data, list) {
|
||||
list_for_each_entry (np, list, list) {
|
||||
if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) {
|
||||
bool is_dynamic = (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2);
|
||||
|
||||
if (is_dynamic) {
|
||||
if (locked_dynamic_manager_uid != KSU_INVALID_UID && locked_dynamic_manager_uid != np->uid) {
|
||||
pr_info("Unlocking previous dynamic manager UID: %d\n", locked_dynamic_manager_uid);
|
||||
ksu_remove_manager(locked_dynamic_manager_uid);
|
||||
locked_dynamic_manager_uid = KSU_INVALID_UID;
|
||||
}
|
||||
} else {
|
||||
if (locked_manager_uid != KSU_INVALID_UID && locked_manager_uid != np->uid) {
|
||||
pr_info("Unlocking previous manager UID: %d\n", locked_manager_uid);
|
||||
ksu_invalidate_manager_uid(); // unlock old one
|
||||
locked_manager_uid = KSU_INVALID_UID;
|
||||
}
|
||||
}
|
||||
|
||||
pr_info("Crowning %s manager: %s (uid=%d, signature_index=%d)\n",
|
||||
is_dynamic ? "dynamic" : "traditional", pkg, np->uid, signature_index);
|
||||
|
||||
if (is_dynamic) {
|
||||
ksu_add_manager(np->uid, signature_index);
|
||||
locked_dynamic_manager_uid = np->uid;
|
||||
|
||||
// If there is no traditional manager, set it to the current UID
|
||||
if (!ksu_is_manager_uid_valid()) {
|
||||
pr_info("Crowning manager: %s(uid=%d)\n", pkg, np->uid);
|
||||
ksu_set_manager_uid(np->uid);
|
||||
locked_manager_uid = np->uid;
|
||||
}
|
||||
} else {
|
||||
ksu_set_manager_uid(np->uid); // throne new UID
|
||||
locked_manager_uid = np->uid; // store locked UID
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -212,7 +106,7 @@ struct apk_path_hash {
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static struct list_head apk_path_hash_list;
|
||||
static struct list_head apk_path_hash_list = LIST_HEAD_INIT(apk_path_hash_list);
|
||||
|
||||
struct my_dir_context {
|
||||
struct dir_context ctx;
|
||||
@@ -233,6 +127,7 @@ struct my_dir_context {
|
||||
#define FILLDIR_ACTOR_CONTINUE 0
|
||||
#define FILLDIR_ACTOR_STOP -EINVAL
|
||||
#endif
|
||||
|
||||
FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
int namelen, loff_t off, u64 ino,
|
||||
unsigned int d_type)
|
||||
@@ -259,6 +154,7 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
return FILLDIR_ACTOR_CONTINUE; // Skip staging package
|
||||
}
|
||||
|
||||
|
||||
if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir,
|
||||
namelen, name) >= DATA_PATH_LEN) {
|
||||
pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen,
|
||||
@@ -268,7 +164,7 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
|
||||
if (d_type == DT_DIR && my_ctx->depth > 0 &&
|
||||
(my_ctx->stop && !*my_ctx->stop)) {
|
||||
struct data_path *data = kzalloc(sizeof(struct data_path), GFP_ATOMIC);
|
||||
struct data_path *data = kmalloc(sizeof(struct data_path), GFP_ATOMIC);
|
||||
|
||||
if (!data) {
|
||||
pr_err("Failed to allocate memory for %s\n", dirpath);
|
||||
@@ -279,14 +175,9 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
data->depth = my_ctx->depth - 1;
|
||||
list_add_tail(&data->list, my_ctx->data_path_list);
|
||||
} else {
|
||||
if ((namelen == 8) &&
|
||||
(strncmp(name, "base.apk", namelen) == 0)) {
|
||||
struct apk_path_hash *pos;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
|
||||
unsigned int hash = full_name_hash(dirpath, strlen(dirpath));
|
||||
#else
|
||||
if ((namelen == 8) && (strncmp(name, "base.apk", namelen) == 0)) {
|
||||
struct apk_path_hash *pos, *n;
|
||||
unsigned int hash = full_name_hash(NULL, dirpath, strlen(dirpath));
|
||||
#endif
|
||||
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
||||
if (hash == pos->hash) {
|
||||
pos->exists = true;
|
||||
@@ -294,19 +185,23 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
}
|
||||
}
|
||||
|
||||
int signature_index = -1;
|
||||
bool is_multi_manager = is_dynamic_manager_apk(
|
||||
dirpath, &signature_index);
|
||||
|
||||
pr_info("Found new base.apk at path: %s, is_multi_manager: %d, signature_index: %d\n",
|
||||
dirpath, is_multi_manager, signature_index);
|
||||
|
||||
// Check for dynamic sign or multi-manager signatures
|
||||
if (is_multi_manager && (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2)) {
|
||||
crown_manager(dirpath, my_ctx->private_data, signature_index);
|
||||
} else if (is_manager_apk(dirpath)) {
|
||||
crown_manager(dirpath, my_ctx->private_data, 0);
|
||||
bool is_manager = is_manager_apk(dirpath);
|
||||
pr_info("Found new base.apk at path: %s, is_manager: %d\n",
|
||||
dirpath, is_manager);
|
||||
if (is_manager) {
|
||||
crown_manager(dirpath, my_ctx->private_data);
|
||||
*my_ctx->stop = 1;
|
||||
|
||||
// Manager found, clear APK cache list
|
||||
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
|
||||
list_del(&pos->list);
|
||||
kfree(pos);
|
||||
}
|
||||
} else {
|
||||
struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC);
|
||||
apk_data->hash = hash;
|
||||
apk_data->exists = true;
|
||||
list_add_tail(&apk_data->list, &apk_path_hash_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,17 +209,15 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
static void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
{
|
||||
int i, stop = 0;
|
||||
struct list_head data_path_list;
|
||||
INIT_LIST_HEAD(&data_path_list);
|
||||
INIT_LIST_HEAD(&apk_path_hash_list);
|
||||
unsigned long data_app_magic = 0;
|
||||
|
||||
// Initialize APK cache list
|
||||
struct apk_path_hash *pos, *n;
|
||||
list_for_each_entry (pos, &apk_path_hash_list, list) {
|
||||
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
||||
pos->exists = false;
|
||||
}
|
||||
|
||||
@@ -337,7 +230,7 @@ static void search_manager(const char *path, int depth, struct list_head *uid_da
|
||||
for (i = depth; i >= 0; i--) {
|
||||
struct data_path *pos, *n;
|
||||
|
||||
list_for_each_entry_safe (pos, n, &data_path_list, list) {
|
||||
list_for_each_entry_safe(pos, n, &data_path_list, list) {
|
||||
struct my_dir_context ctx = { .ctx.actor = my_actor,
|
||||
.data_path_list = &data_path_list,
|
||||
.parent_dir = pos->dirpath,
|
||||
@@ -347,49 +240,29 @@ static void search_manager(const char *path, int depth, struct list_head *uid_da
|
||||
struct file *file;
|
||||
|
||||
if (!stop) {
|
||||
file = filp_open(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0);
|
||||
file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0);
|
||||
if (IS_ERR(file)) {
|
||||
pr_err("Failed to open directory: %s, err: %ld\n",
|
||||
pos->dirpath, PTR_ERR(file));
|
||||
goto skip_iterate;
|
||||
}
|
||||
|
||||
// grab magic on first folder, which is /data/app
|
||||
if (!data_app_magic) {
|
||||
if (file->f_inode->i_sb->s_magic) {
|
||||
data_app_magic = file->f_inode->i_sb->s_magic;
|
||||
pr_info("%s: dir: %s got magic! 0x%lx\n", __func__,
|
||||
pos->dirpath, data_app_magic);
|
||||
} else {
|
||||
filp_close(file, NULL);
|
||||
goto skip_iterate;
|
||||
}
|
||||
}
|
||||
|
||||
if (file->f_inode->i_sb->s_magic != data_app_magic) {
|
||||
pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n",
|
||||
__func__, pos->dirpath,
|
||||
file->f_inode->i_sb->s_magic, data_app_magic);
|
||||
filp_close(file, NULL);
|
||||
pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file));
|
||||
goto skip_iterate;
|
||||
}
|
||||
|
||||
iterate_dir(file, &ctx.ctx);
|
||||
filp_close(file, NULL);
|
||||
}
|
||||
skip_iterate:
|
||||
skip_iterate:
|
||||
list_del(&pos->list);
|
||||
if (pos != &data)
|
||||
kfree(pos);
|
||||
}
|
||||
}
|
||||
|
||||
// clear apk_path_hash_list unconditionally
|
||||
pr_info("search manager: cleanup!\n");
|
||||
list_for_each_entry_safe (pos, n, &apk_path_hash_list, list) {
|
||||
// Remove stale cached APK entries
|
||||
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
|
||||
if (!pos->exists) {
|
||||
list_del(&pos->list);
|
||||
kfree(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_uid_exist(uid_t uid, char *package, void *data)
|
||||
@@ -408,51 +281,34 @@ static bool is_uid_exist(uid_t uid, char *package, void *data)
|
||||
return exist;
|
||||
}
|
||||
|
||||
void track_throne(bool prune_only)
|
||||
void track_throne()
|
||||
{
|
||||
struct file *fp =
|
||||
ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n",
|
||||
__func__, PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
struct list_head uid_list;
|
||||
struct uid_data *np, *n;
|
||||
struct file *fp;
|
||||
INIT_LIST_HEAD(&uid_list);
|
||||
|
||||
char chr = 0;
|
||||
loff_t pos = 0;
|
||||
loff_t line_start = 0;
|
||||
char buf[KSU_MAX_PACKAGE_NAME];
|
||||
static bool manager_exist = false;
|
||||
static bool dynamic_manager_exist = false;
|
||||
int current_manager_uid = ksu_get_manager_uid() % 100000;
|
||||
|
||||
// init uid list head
|
||||
INIT_LIST_HEAD(&uid_list);
|
||||
|
||||
if (ksu_uid_scanner_enabled) {
|
||||
pr_info("Scanning %s directory..\n", KSU_UID_LIST_PATH);
|
||||
|
||||
if (uid_from_um_list(&uid_list) == 0) {
|
||||
pr_info("Loaded UIDs from %s success\n", KSU_UID_LIST_PATH);
|
||||
goto uid_ready;
|
||||
}
|
||||
|
||||
pr_warn("%s read failed, fallback to %s\n",
|
||||
KSU_UID_LIST_PATH, SYSTEM_PACKAGES_LIST_PATH);
|
||||
}
|
||||
|
||||
{
|
||||
fp = filp_open(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n", __func__, PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
ssize_t count =
|
||||
kernel_read(fp, &chr, sizeof(chr), &pos);
|
||||
ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
|
||||
if (count != sizeof(chr))
|
||||
break;
|
||||
if (chr != '\n')
|
||||
continue;
|
||||
|
||||
count = kernel_read(fp, buf, sizeof(buf),
|
||||
count = ksu_kernel_read_compat(fp, buf, sizeof(buf),
|
||||
&line_start);
|
||||
|
||||
struct uid_data *data =
|
||||
kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
||||
if (!data) {
|
||||
@@ -480,55 +336,33 @@ void track_throne(bool prune_only)
|
||||
// reset line start
|
||||
line_start = pos;
|
||||
}
|
||||
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
|
||||
uid_ready:
|
||||
if (prune_only)
|
||||
goto prune;
|
||||
// now update uid list
|
||||
struct uid_data *np;
|
||||
struct uid_data *n;
|
||||
|
||||
// first, check if manager_uid exist!
|
||||
list_for_each_entry(np, &uid_list, list) {
|
||||
if (np->uid == current_manager_uid) {
|
||||
bool manager_exist = false;
|
||||
list_for_each_entry (np, &uid_list, list) {
|
||||
// if manager is installed in work profile, the uid in packages.list is still equals main profile
|
||||
// don't delete it in this case!
|
||||
int manager_uid = ksu_get_manager_uid() % 100000;
|
||||
if (np->uid == manager_uid) {
|
||||
manager_exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!manager_exist && locked_manager_uid != KSU_INVALID_UID) {
|
||||
pr_info("Manager APK removed, unlock previous UID: %d\n",
|
||||
locked_manager_uid);
|
||||
if (!manager_exist) {
|
||||
if (ksu_is_manager_uid_valid()) {
|
||||
pr_info("manager is uninstalled, invalidate it!\n");
|
||||
ksu_invalidate_manager_uid();
|
||||
locked_manager_uid = KSU_INVALID_UID;
|
||||
goto prune;
|
||||
}
|
||||
|
||||
// Check if the Dynamic Manager exists (only check locked UIDs)
|
||||
if (ksu_is_dynamic_manager_enabled() &&
|
||||
locked_dynamic_manager_uid != KSU_INVALID_UID) {
|
||||
list_for_each_entry(np, &uid_list, list) {
|
||||
if (np->uid == locked_dynamic_manager_uid) {
|
||||
dynamic_manager_exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dynamic_manager_exist) {
|
||||
pr_info("Dynamic manager APK removed, unlock previous UID: %d\n",
|
||||
locked_dynamic_manager_uid);
|
||||
ksu_remove_manager(locked_dynamic_manager_uid);
|
||||
locked_dynamic_manager_uid = KSU_INVALID_UID;
|
||||
}
|
||||
}
|
||||
|
||||
bool need_search = !manager_exist;
|
||||
if (ksu_is_dynamic_manager_enabled() && !dynamic_manager_exist)
|
||||
need_search = true;
|
||||
|
||||
if (need_search) {
|
||||
pr_info("Searching for manager(s)...\n");
|
||||
pr_info("Searching manager...\n");
|
||||
search_manager("/data/app", 2, &uid_list);
|
||||
pr_info("Manager search finished\n");
|
||||
pr_info("Search manager finished\n");
|
||||
}
|
||||
|
||||
prune:
|
||||
@@ -536,18 +370,18 @@ prune:
|
||||
ksu_prune_allowlist(is_uid_exist, &uid_list);
|
||||
out:
|
||||
// free uid_list
|
||||
list_for_each_entry_safe(np, n, &uid_list, list) {
|
||||
list_for_each_entry_safe (np, n, &uid_list, list) {
|
||||
list_del(&np->list);
|
||||
kfree(np);
|
||||
}
|
||||
}
|
||||
|
||||
void ksu_throne_tracker_init(void)
|
||||
void ksu_throne_tracker_init()
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
void ksu_throne_tracker_exit(void)
|
||||
void ksu_throne_tracker_exit()
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
#ifndef __KSU_H_THRONE_TRACKER
|
||||
#define __KSU_H_THRONE_TRACKER
|
||||
#ifndef __KSU_H_UID_OBSERVER
|
||||
#define __KSU_H_UID_OBSERVER
|
||||
|
||||
void ksu_throne_tracker_init(void);
|
||||
void ksu_throne_tracker_init();
|
||||
|
||||
void ksu_throne_tracker_exit(void);
|
||||
void ksu_throne_tracker_exit();
|
||||
|
||||
void track_throne(bool prune_only);
|
||||
void track_throne();
|
||||
|
||||
#endif
|
||||
|
||||
10
manager/.gitignore
vendored
Normal file
10
manager/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
*.iml
|
||||
.gradle
|
||||
.idea
|
||||
.kotlin
|
||||
.DS_Store
|
||||
build
|
||||
captures
|
||||
.cxx
|
||||
local.properties
|
||||
key.jks
|
||||
2
manager/app/.gitignore
vendored
Normal file
2
manager/app/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/build
|
||||
/release/
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user