From 13f631c4b36d556600d2d7b3ae125f56c8b21395 Mon Sep 17 00:00:00 2001 From: changhuapeng Date: Mon, 23 Dec 2024 18:19:46 +0800 Subject: [PATCH] susfs: Add susfs lib --- .github/workflows/build-manager.yml | 41 +- .github/workflows/susfs.yml | 27 + userspace/susfs/.gitignore | 2 + userspace/susfs/jni/Android.mk | 8 + userspace/susfs/jni/Application.mk | 3 + userspace/susfs/jni/main.c | 786 ++++++++++++++++++++++++++++ 6 files changed, 862 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/susfs.yml create mode 100644 userspace/susfs/.gitignore create mode 100644 userspace/susfs/jni/Android.mk create mode 100644 userspace/susfs/jni/Application.mk create mode 100644 userspace/susfs/jni/main.c diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml index f3305651e1b8..767f3fcbf159 100644 --- a/.github/workflows/build-manager.yml +++ b/.github/workflows/build-manager.yml @@ -2,12 +2,13 @@ name: Build Manager on: push: - branches: [ "legacy", "ci", "susfs-*" ] + branches: [ "legacy", "ci", "susfs-*", "libsusfs" ] paths: - '.github/workflows/build-manager.yml' - 'manager/**' - 'kernel/**' - 'userspace/ksud/**' + - 'userspace/susfs/**' pull_request: branches: [ "legacy" ] paths: @@ -119,8 +120,19 @@ jobs: os: ${{ matrix.os }} pack_lkm: false + build-susfs: + needs: update-version + strategy: + matrix: + include: + - target: arm64-v8a + - target: x86_64 + uses: ./.github/workflows/susfs.yml + with: + target: ${{ matrix.target }} + build-manager: - needs: build-ksud + needs: [build-ksud, build-susfs] runs-on: ubuntu-latest defaults: run: @@ -143,7 +155,7 @@ jobs: fi - name: Write key - if: ${{ ( github.event_name != 'pull_request' && ( github.ref == 'refs/heads/legacy' || startsWith(github.ref, 'refs/heads/susfs-') )) || github.ref_type == 'tag' }} + if: ${{ ( github.event_name != 'pull_request' && ( github.ref == 'refs/heads/libsusfs' || github.ref == 'refs/heads/legacy' || startsWith(github.ref, 'refs/heads/susfs-') )) || github.ref_type == 'tag' }} run: | if [ ! -z "${{ secrets.KEYSTORE }}" ]; then { @@ -186,6 +198,25 @@ jobs: cp -f ../aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud.so cp -f ../x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud.so + - name: Download arm64 susfs + uses: actions/download-artifact@v4 + with: + name: susfs-arm64-v8a + path: ./arm64-v8a + + - name: Download x86_64 susfs + uses: actions/download-artifact@v4 + with: + name: susfs-x86_64 + path: ./x86_64 + + - name: Copy susfs to app jniLibs + run: | + mkdir -p app/src/main/jniLibs/arm64-v8a + mkdir -p app/src/main/jniLibs/x86_64 + cp -f ../arm64-v8a/susfs ../manager/app/src/main/jniLibs/arm64-v8a/libsusfs.so + cp -f ../x86_64/susfs ../manager/app/src/main/jniLibs/x86_64/libsusfs.so + - name: Build with Gradle run: | { @@ -199,14 +230,14 @@ jobs: - name: Upload build artifact uses: actions/upload-artifact@v4 - if: ${{ ( github.event_name != 'pull_request' && ( github.ref == 'refs/heads/legacy' || startsWith(github.ref, 'refs/heads/susfs-') )) || github.ref_type == 'tag' }} + if: ${{ ( github.event_name != 'pull_request' && ( github.ref == 'refs/heads/libsusfs' || github.ref == 'refs/heads/legacy' || startsWith(github.ref, 'refs/heads/susfs-') )) || 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/legacy' || startsWith(github.ref, 'refs/heads/susfs-') )) || github.ref_type == 'tag' }} + if: ${{ ( github.event_name != 'pull_request' && ( github.ref == 'refs/heads/libsusfs' || github.ref == 'refs/heads/legacy' || startsWith(github.ref, 'refs/heads/susfs-') )) || github.ref_type == 'tag' }} with: name: "mappings" path: "manager/app/build/outputs/mapping/release/" diff --git a/.github/workflows/susfs.yml b/.github/workflows/susfs.yml new file mode 100644 index 000000000000..412ace8a31cf --- /dev/null +++ b/.github/workflows/susfs.yml @@ -0,0 +1,27 @@ +name: Build susfs +on: + workflow_call: + inputs: + target: + required: true + type: string +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download artifacts + uses: actions/download-artifact@v4 + + - name: Build susfs + working-directory: ./userspace/susfs + run: $ANDROID_NDK/ndk-build + + - name: Upload susfs artifact + uses: actions/upload-artifact@v4 + with: + name: susfs-${{ inputs.target }} + path: ./userspace/susfs/libs/${{ inputs.target }}/susfs diff --git a/userspace/susfs/.gitignore b/userspace/susfs/.gitignore new file mode 100644 index 000000000000..720289cc670e --- /dev/null +++ b/userspace/susfs/.gitignore @@ -0,0 +1,2 @@ +/obj +/libs diff --git a/userspace/susfs/jni/Android.mk b/userspace/susfs/jni/Android.mk new file mode 100644 index 000000000000..2225510401a8 --- /dev/null +++ b/userspace/susfs/jni/Android.mk @@ -0,0 +1,8 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := susfs +LOCAL_SRC_FILES := main.c + +include $(BUILD_EXECUTABLE) diff --git a/userspace/susfs/jni/Application.mk b/userspace/susfs/jni/Application.mk new file mode 100644 index 000000000000..8c2407c5e24d --- /dev/null +++ b/userspace/susfs/jni/Application.mk @@ -0,0 +1,3 @@ +APP_ABI := arm64-v8a armeabi-v7a x86 x86_64 +APP_PLATFORM := android-24 +APP_STL := none diff --git a/userspace/susfs/jni/main.c b/userspace/susfs/jni/main.c new file mode 100644 index 000000000000..bb484c51d94a --- /dev/null +++ b/userspace/susfs/jni/main.c @@ -0,0 +1,786 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/************************* + ** Define Const Values ** + *************************/ +#define TAG "ksu_susfs" +#define KERNEL_SU_OPTION 0xDEADBEEF + +#define CMD_SUSFS_ADD_SUS_PATH 0x55550 +#define CMD_SUSFS_ADD_SUS_MOUNT 0x55560 +#define CMD_SUSFS_ADD_SUS_KSTAT 0x55570 +#define CMD_SUSFS_UPDATE_SUS_KSTAT 0x55571 +#define CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY 0x55572 +#define CMD_SUSFS_ADD_TRY_UMOUNT 0x55580 +#define CMD_SUSFS_SET_UNAME 0x55590 +#define CMD_SUSFS_ENABLE_LOG 0x555a0 +#define CMD_SUSFS_SET_PROC_CMDLINE 0x555b0 +#define CMD_SUSFS_ADD_OPEN_REDIRECT 0x555c0 +#define CMD_SUSFS_RUN_UMOUNT_FOR_CURRENT_MNT_NS 0x555d0 +#define CMD_SUSFS_SHOW_VERSION 0x555e1 +#define CMD_SUSFS_SHOW_ENABLED_FEATURES 0x555e2 +#define CMD_SUSFS_SHOW_VARIANT 0x555e3 +#define CMD_SUSFS_SHOW_SUS_SU_WORKING_MODE 0x555e4 +#define CMD_SUSFS_IS_SUS_SU_READY 0x555f0 +#define CMD_SUSFS_SUS_SU 0x60000 + +#define SUSFS_MAX_LEN_PATHNAME 256 +#define SUSFS_MAX_LEN_MOUNT_TYPE_NAME 32 + +#ifndef __NEW_UTS_LEN +#define __NEW_UTS_LEN 64 +#endif + +#define SUS_SU_BIN_PATH "/data/adb/ksu/bin/sus_su" +#define SUS_SU_CONF_FILE_PATH "/data/adb/ksu/bin/sus_su_drv_path" +#define SUS_SU_DISABLED 0 +#define SUS_SU_WITH_OVERLAY 1 /* deprecated */ +#define SUS_SU_WITH_HOOKS 2 + +/* VM flags from linux kernel */ +#define VM_NONE 0x00000000 +#define VM_READ 0x00000001 /* currently active flags */ +#define VM_WRITE 0x00000002 +#define VM_EXEC 0x00000004 +#define VM_SHARED 0x00000008 +/* mprotect() hardcodes VM_MAYREAD >> 4 == VM_READ, and so for r/w/x bits. */ +#define VM_MAYREAD 0x00000010 /* limits for mprotect() etc */ +#define VM_MAYWRITE 0x00000020 +#define VM_MAYEXEC 0x00000040 +#define VM_MAYSHARE 0x00000080 + +/****************** + ** Define Macro ** + ******************/ +#define log(fmt, msg...) printf(TAG ":" fmt, ##msg); +#define PRT_MSG_IF_OPERATION_NOT_SUPPORTED(x, cmd) if (x == -1) log("[-] CMD: '0x%x', SUSFS operation not supported, please enable it in kernel\n", cmd) + +/******************* + ** Define Struct ** + *******************/ +struct st_susfs_sus_path { + unsigned long target_ino; + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; +}; + +struct st_susfs_sus_mount { + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + unsigned long target_dev; +}; + +struct st_susfs_sus_kstat { + bool is_statically; + unsigned long target_ino; // the ino after bind mounted or overlayed + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + unsigned long spoofed_ino; + unsigned long spoofed_dev; + unsigned int spoofed_nlink; + long long spoofed_size; + long spoofed_atime_tv_sec; + long spoofed_mtime_tv_sec; + long spoofed_ctime_tv_sec; + long spoofed_atime_tv_nsec; + long spoofed_mtime_tv_nsec; + long spoofed_ctime_tv_nsec; + unsigned long spoofed_blksize; + unsigned long long spoofed_blocks; +}; + +struct st_susfs_try_umount { + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + int mnt_mode; +}; + +struct st_susfs_uname { + char release[__NEW_UTS_LEN+1]; + char version[__NEW_UTS_LEN+1]; +}; + +struct st_susfs_open_redirect { + unsigned long target_ino; + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + char redirected_pathname[SUSFS_MAX_LEN_PATHNAME]; +}; + +struct st_sus_su { + int mode; +}; + +/********************** + ** Define Functions ** + **********************/ +void pre_check() { + if (getuid() != 0) { + log("[-] Must run as root\n"); + exit(1); + } +} + +int isNumeric(char* str) { + // Check if the string is empty + if (str[0] == '\0') { + return 0; + } + + // Check each character in the string + for (int i = 0; str[i] != '\0'; i++) { + // If any character is not a digit, return false + if (!isdigit(str[i])) { + return 0; + } + } + + // All characters are digits, return true + return 1; +} + +int get_file_stat(char *pathname, struct stat* sb) { + if (stat(pathname, sb) != 0) { + return 1; + } + return 0; +} + +void copy_stat_to_sus_kstat(struct st_susfs_sus_kstat* info, struct stat* sb) { + info->spoofed_ino = sb->st_ino; + info->spoofed_dev = sb->st_dev; + info->spoofed_nlink = sb->st_nlink; + info->spoofed_size = sb->st_size; + info->spoofed_atime_tv_sec = sb->st_atime; + info->spoofed_mtime_tv_sec = sb->st_mtime; + info->spoofed_ctime_tv_sec = sb->st_ctime; + info->spoofed_atime_tv_nsec = sb->st_atime_nsec; + info->spoofed_mtime_tv_nsec = sb->st_mtime_nsec; + info->spoofed_ctime_tv_nsec = sb->st_ctime_nsec; + info->spoofed_blksize = sb->st_blksize; + info->spoofed_blocks = sb->st_blocks; +} + +int enable_sus_su(int last_working_mode, int target_working_mode) { + struct st_sus_su info; + int error = -1; + + if (target_working_mode == SUS_SU_WITH_HOOKS) { + info.mode = SUS_SU_WITH_HOOKS; + prctl(KERNEL_SU_OPTION, CMD_SUSFS_SUS_SU, &info, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_SUS_SU); + if (error) { + if (error == 1) { + log("[-] current sus_su mode is already %d\n", SUS_SU_WITH_HOOKS); + } else if (error == 2) { + log("[-] please make sure the current sus_su mode is %d first\n", SUS_SU_DISABLED); + } + return error; + } + log("[+] sus_su mode 2 is enabled\n"); + } else if (target_working_mode == SUS_SU_DISABLED) { + info.mode = SUS_SU_DISABLED; + prctl(KERNEL_SU_OPTION, CMD_SUSFS_SUS_SU, &info, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_SUS_SU); + if (error) { + if (error == 1) { + log("[-] current sus_su mode is already %d\n", SUS_SU_DISABLED); + } + return error; + } + log("[+] sus_su mode 0 is enabled\n"); + } else { + return 1; + } + return 0; +} + +static void print_help(void) { + log(" usage: %s [CMD options]\n", TAG); + log(" :\n"); + log(" add_sus_path \n"); + log(" |--> Added path and all its sub-paths will be hidden from several syscalls\n"); + log(" |--> Please be reminded that the target path must be added after the bind mount or overlay operation, otherwise it won't be effective\n"); + log("\n"); + log(" add_sus_mount \n"); + log(" |--> Added mounted path will be hidden from /proc/self/[mounts|mountinfo|mountstats]\n"); + log(" |--> Please be reminded that the target path must be added after the bind mount or overlay operation, otherwise it won't be effective\n"); + log("\n"); + log(" add_sus_kstat_statically \\\n"); + log(" \n"); + log(" \n"); + log(" |--> Use 'stat' tool to find the format:\n"); + log(" ino -> %%i, dev -> %%d, nlink -> %%h, atime -> %%X, mtime -> %%Y, ctime -> %%Z\n"); + log(" size -> %%s, blocks -> %%b, blksize -> %%B\n"); + log(" |--> e.g., %s add_sus_kstat_statically '/system/addon.d' '1234' '1234' '2' '223344'\\\n", TAG); + log(" '1712592355' '0' '1712592355' '0' '1712592355' '0' '1712592355' '0'\\\n"); + log(" '16' '512'\n"); + log(" |--> Or pass 'default' to use its original value:\n"); + log(" |--> e.g., %s add_sus_kstat_statically '/system/addon.d' 'default' 'default' 'default' 'default'\\\n", TAG); + log(" '1712592355' 'default' '1712592355' 'default' '1712592355' 'default'\\\n"); + log(" 'default' 'default'\n"); + log("\n"); + log(" add_sus_kstat \n"); + log(" |--> Add the desired path BEFORE it gets bind mounted or overlayed, this is used for storing original stat info in kernel memory\n"); + log(" |--> This command must be completed with later after the added path is bind mounted or overlayed\n"); + log("\n"); + log(" update_sus_kstat \n"); + log(" |--> Add the desired path you have added before via to complete the kstat spoofing procedure\n"); + log(" |--> This updates the target ino, but size and blocks are remained the same as current stat\n"); + log("\n"); + log(" update_sus_kstat_full_clone \n"); + log(" |--> Add the desired path you have added before via to complete the kstat spoofing procedure\n"); + log(" |--> This updates the target ino only, other stat members are remained the same as the original stat\n"); + log("\n"); + log(" add_try_umount \n"); + log(" |--> Added path will be umounted from KSU for all UIDs that are NOT su allowed, and profile template configured with umount\n"); + log(" |--> : 0 -> umount with no flags, 1 -> umount with MNT_DETACH\n"); + log(" |--> NOTE: susfs umount takes precedence of ksu umount\n"); + log("\n"); + log(" run_try_umount\n"); + log(" |--> Make all sus mounts to be private and umount them one by one in kernel for the mount namespace of current process\n"); + log("\n"); + log(" set_uname \n"); + log(" |--> NOTE: Only 'release' and are spoofed as others are no longer needed\n"); + log(" |--> Spoof uname for all processes, set string to 'default' to imply the function to use original string\n"); + log(" |--> e.g., set_uname '4.9.337-g3291538446b7' 'default'\n"); + log("\n"); + log(" enable_log <0|1>\n"); + log(" |--> 0: disable susfs log in kernel, 1: enable susfs log in kernel\n"); + log("\n"); + log(" set_proc_cmdline \n"); + log(" |--> Spoof the output of /proc/proc_cmdline from a text file\n"); + log("\n"); + log(" add_open_redirect \n"); + log(" |--> Redirect the target path to be opened with user defined path\n"); + log("\n"); + log(" show \n"); + log(" |--> version: show the current susfs version implemented in kernel\n"); + log(" |--> enabled_features: show the current implemented susfs features in kernel\n"); + log(" |--> variant: show the current variant: GKI or NON-GKI\n"); + log("\n"); + log(" sus_su <0|1|2|show_working_mode>\n"); + log(" |--> NOTE-1:\n"); + log(" - For mode 1: (deprecated) It disables kprobe hooks made by ksu, and instead,\n"); + log(" a sus_su character device driver with random name will be created, and user\n"); + log(" need to use a tool named 'sus_su' together with a path file in same current directory\n"); + log(" named '" SUS_SU_CONF_FILE_PATH "' to get a root shell from the sus_su driver.'\n"); + log(" ** sus_su userspace tool and an overlay mount is required **'\n"); + log(" - For mode 2: It disables kprobe hooks made by ksu, and instead,\n"); + log(" the non-kprobe inline hooks will be enbaled, just the same implementation for non-gki kernel without kprobe supported)\n"); + log(" ** Needs no extra userspace tools and mounts **\n"); + log(" |--> NOTE-2:\n"); + log(" Please see the service.sh template from ksu_module_susfs for the usage\n"); + log(" |--> 0: enable core ksu kprobe hooks and disable sus_su driver\n"); + log(" |--> 1: (deprecated), disable the core ksu kprobe hooks and enable sus_su fifo driver\n"); + log(" |--> 2: disable the core ksu kprobe hooks and enable sus_su just with non-kprobe hooks\n"); + log(" |--> show_working_mode: show the current sus_su working mode, [0,1,2]\n"); + log("\n"); +} + +/******************* + ** Main Function ** + *******************/ +int main(int argc, char *argv[]) { + int error = -1; + + pre_check(); + // add_sus_path + if (argc == 3 && !strcmp(argv[1], "add_sus_path")) { + struct st_susfs_sus_path info = {0}; + struct stat sb; + + if (get_file_stat(argv[2], &sb)) { + log("%s not found, skip adding its ino\n", info.target_pathname); + return 1; + } + info.target_ino = sb.st_ino; + strncpy(info.target_pathname, argv[2], SUSFS_MAX_LEN_PATHNAME-1); + prctl(KERNEL_SU_OPTION, CMD_SUSFS_ADD_SUS_PATH, &info, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_ADD_SUS_PATH); + return error; + // add_sus_mount + } else if (argc == 3 && !strcmp(argv[1], "add_sus_mount")) { + struct st_susfs_sus_mount info; + struct stat sb; + + strncpy(info.target_pathname, argv[2], SUSFS_MAX_LEN_PATHNAME-1); + if (get_file_stat(argv[2], &sb)) { + log("[-] Failed to get stat from path: '%s'\n", argv[2]); + return 1; + } + info.target_dev = sb.st_dev; + prctl(KERNEL_SU_OPTION, CMD_SUSFS_ADD_SUS_MOUNT, &info, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_ADD_SUS_MOUNT); + return error; + // add_sus_kstat_statically + } else if (argc == 15 && !strcmp(argv[1], "add_sus_kstat_statically")) { + struct st_susfs_sus_kstat info; + struct stat sb; + char* endptr; + unsigned long ino, dev, nlink, size, atime, atime_nsec, mtime, mtime_nsec, ctime, ctime_nsec, blksize; + long blocks; + + if (get_file_stat(argv[2], &sb)) { + log("[-] Failed to get stat from path: '%s'\n", argv[2]); + return 1; + } + + info.is_statically = true; + /* ino */ + if (strcmp(argv[3], "default")) { + ino = strtoul(argv[3], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + info.target_ino = sb.st_ino; + sb.st_ino = ino; + } else { + info.target_ino = sb.st_ino; + } + /* dev */ + if (strcmp(argv[4], "default")) { + dev = strtoul(argv[4], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + sb.st_dev = dev; + } + /* nlink */ + if (strcmp(argv[5], "default")) { + nlink = strtoul(argv[5], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + sb.st_nlink = nlink; + } + /* size */ + if (strcmp(argv[6], "default")) { + size = strtoul(argv[6], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + sb.st_size = size; + } + /* atime */ + if (strcmp(argv[7], "default")) { + atime = strtol(argv[7], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + sb.st_atime = atime; + } + /* atime_nsec */ + if (strcmp(argv[8], "default")) { + atime_nsec = strtoul(argv[8], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + sb.st_atimensec = atime_nsec; + } + /* mtime */ + if (strcmp(argv[9], "default")) { + mtime = strtol(argv[9], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + sb.st_mtime = mtime; + } + /* mtime_nsec */ + if (strcmp(argv[10], "default")) { + mtime_nsec = strtoul(argv[10], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + sb.st_mtimensec = mtime_nsec; + } + /* ctime */ + if (strcmp(argv[11], "default")) { + ctime = strtol(argv[11], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + sb.st_ctime = ctime; + } + /* ctime_nsec */ + if (strcmp(argv[12], "default")) { + ctime_nsec = strtoul(argv[12], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + sb.st_ctimensec = ctime_nsec; + } + /* blocks */ + if (strcmp(argv[13], "default")) { + blocks = strtoul(argv[13], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + sb.st_blocks = blocks; + } + /* blksize */ + if (strcmp(argv[14], "default")) { + blksize = strtoul(argv[14], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + sb.st_blksize = blksize; + } + strncpy(info.target_pathname, argv[2], SUSFS_MAX_LEN_PATHNAME-1); + copy_stat_to_sus_kstat(&info, &sb); + prctl(KERNEL_SU_OPTION, CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY, &info, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY); + return error; + // add_sus_kstat + } else if (argc == 3 && !strcmp(argv[1], "add_sus_kstat")) { + struct st_susfs_sus_kstat info; + struct stat sb; + + if (get_file_stat(argv[2], &sb)) { + log("[-] Failed to get stat from path: '%s'\n", argv[2]); + return 1; + } + strncpy(info.target_pathname, argv[2], SUSFS_MAX_LEN_PATHNAME-1); + info.is_statically = false; + info.target_ino = sb.st_ino; + copy_stat_to_sus_kstat(&info, &sb); + prctl(KERNEL_SU_OPTION, CMD_SUSFS_ADD_SUS_KSTAT, &info, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_ADD_SUS_KSTAT); + return error; + // update_sus_kstat + } else if (argc == 3 && !strcmp(argv[1], "update_sus_kstat")) { + struct st_susfs_sus_kstat info = {0}; + struct stat sb; + + if (get_file_stat(argv[2], &sb)) { + log("[-] Failed to get stat from path: '%s'\n", argv[2]); + return 1; + } + strncpy(info.target_pathname, argv[2], SUSFS_MAX_LEN_PATHNAME-1); + info.is_statically = false; + info.target_ino = sb.st_ino; + info.spoofed_size = sb.st_size; // use the current size, not the spoofed one + info.spoofed_blocks = sb.st_blocks; // use the current blocks, not the spoofed one + prctl(KERNEL_SU_OPTION, CMD_SUSFS_UPDATE_SUS_KSTAT, &info, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_UPDATE_SUS_KSTAT); + return error; + // update_sus_kstat_full_clone + } else if (argc == 3 && !strcmp(argv[1], "update_sus_kstat_full_clone")) { + struct st_susfs_sus_kstat info = {0}; + struct stat sb; + + if (get_file_stat(argv[2], &sb)) { + log("[-] Failed to get stat from path: '%s'\n", argv[2]); + return 1; + } + strncpy(info.target_pathname, argv[2], SUSFS_MAX_LEN_PATHNAME-1); + info.is_statically = false; + info.target_ino = sb.st_ino; + prctl(KERNEL_SU_OPTION, CMD_SUSFS_UPDATE_SUS_KSTAT, &info, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_UPDATE_SUS_KSTAT); + return error; + // add_try_umount + } else if (argc == 4 && !strcmp(argv[1], "add_try_umount")) { + struct st_susfs_try_umount info; + char* endptr; + char abs_path[PATH_MAX], *p_abs_path; + + strncpy(info.target_pathname, argv[2], SUSFS_MAX_LEN_PATHNAME-1); + p_abs_path = realpath(info.target_pathname, abs_path); + if (p_abs_path == NULL) { + perror("realpath"); + return 1; + } + if (!strcmp(p_abs_path, "/system") || + !strcmp(p_abs_path, "/vendor") || + !strcmp(p_abs_path, "/product") || + !strcmp(p_abs_path, "/data/adb/modules") || + !strcmp(p_abs_path, "/debug_ramdisk") || + !strcmp(p_abs_path, "/sbin")) { + log("[-] %s cannot be added to try_umount, because it will be umounted by ksu lastly\n", p_abs_path); + return 1; + } + if (strcmp(argv[3], "0") && strcmp(argv[3], "1")) { + print_help(); + return 1; + } + info.mnt_mode = strtol(argv[3], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + prctl(KERNEL_SU_OPTION, CMD_SUSFS_ADD_TRY_UMOUNT, &info, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_ADD_TRY_UMOUNT); + return error; + // run_try_umount + } else if (argc == 2 && !strcmp(argv[1], "run_try_umount")) { + prctl(KERNEL_SU_OPTION, CMD_SUSFS_RUN_UMOUNT_FOR_CURRENT_MNT_NS, NULL, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_RUN_UMOUNT_FOR_CURRENT_MNT_NS); + return error; + // set_uname + } else if (argc == 4 && !strcmp(argv[1], "set_uname")) { + struct st_susfs_uname info; + + strncpy(info.release, argv[2], __NEW_UTS_LEN); + strncpy(info.version, argv[3], __NEW_UTS_LEN); + prctl(KERNEL_SU_OPTION, CMD_SUSFS_SET_UNAME, &info, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_SET_UNAME); + return error; + // enable_log + } else if (argc == 3 && !strcmp(argv[1], "enable_log")) { + if (strcmp(argv[2], "0") && strcmp(argv[2], "1")) { + print_help(); + return 1; + } + prctl(KERNEL_SU_OPTION, CMD_SUSFS_ENABLE_LOG, atoi(argv[2]), NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_ENABLE_LOG); + return error; + // set_proc_cmdline + } else if (argc == 3 && !strcmp(argv[1], "set_proc_cmdline")) { + char abs_path[PATH_MAX], *p_abs_path, *buffer; + FILE *file; + long file_size; + size_t result; + + p_abs_path = realpath(argv[2], abs_path); + if (p_abs_path == NULL) { + perror("realpath"); + return 1; + } + file = fopen(abs_path, "rb"); + if (file == NULL) { + perror("Error opening file"); + return 1; + } + fseek(file, 0, SEEK_END); + file_size = ftell(file); + rewind(file); + buffer = (char *)malloc(sizeof(char) * (file_size + 1)); + if (buffer == NULL) { + perror("No enough memory"); + fclose(file); + return 1; + } + result = fread(buffer, 1, file_size, file); + if (result != file_size) { + perror("Reading error"); + fclose(file); + free(buffer); + return 1; + } + buffer[file_size] = '\0'; + fclose(file); + prctl(KERNEL_SU_OPTION, CMD_SUSFS_SET_PROC_CMDLINE, buffer, NULL, &error); + free(buffer); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_SET_PROC_CMDLINE); + return error; + // add_open_redirect + } else if (argc == 4 && !strcmp(argv[1], "add_open_redirect")) { + struct st_susfs_open_redirect info; + struct stat sb; + char target_pathname[PATH_MAX], *p_abs_target_pathname; + char redirected_pathname[PATH_MAX], *p_abs_redirected_pathname; + + p_abs_target_pathname = realpath(argv[2], target_pathname); + if (p_abs_target_pathname == NULL) { + perror("realpath"); + return 1; + } + strncpy(info.target_pathname, target_pathname, SUSFS_MAX_LEN_PATHNAME-1); + p_abs_redirected_pathname = realpath(argv[3], redirected_pathname); + if (p_abs_redirected_pathname == NULL) { + perror("realpath"); + return 1; + } + strncpy(info.redirected_pathname, redirected_pathname, SUSFS_MAX_LEN_PATHNAME-1); + if (get_file_stat(info.target_pathname, &sb)) { + log("[-] Failed to get stat from path: '%s'\n", info.target_pathname); + return 1; + } + info.target_ino = sb.st_ino; + prctl(KERNEL_SU_OPTION, CMD_SUSFS_ADD_OPEN_REDIRECT, &info, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_ADD_OPEN_REDIRECT); + return error; + // show + } else if (argc == 3 && !strcmp(argv[1], "show")) { + if (!strcmp(argv[2], "version")) { + char version[16]; + prctl(KERNEL_SU_OPTION, CMD_SUSFS_SHOW_VERSION, version, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_SHOW_VERSION); + if (!error) + printf("%s\n", version); + } else if (!strcmp(argv[2], "enabled_features")) { + char *enabled_features_buf = malloc(getpagesize()*2); + char *ptr_buf; + unsigned long enabled_features; + int str_len; + if (!enabled_features_buf) { + perror("malloc"); + return -ENOMEM; + } + ptr_buf = enabled_features_buf; + prctl(KERNEL_SU_OPTION, CMD_SUSFS_SHOW_ENABLED_FEATURES, &enabled_features, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_SHOW_ENABLED_FEATURES); + if (!error) { + if (enabled_features & (1 << 0)) { + str_len = strlen("CONFIG_KSU_SUSFS_SUS_PATH\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SUS_PATH\n", str_len); + ptr_buf += str_len; + } + if (enabled_features & (1 << 1)) { + str_len = strlen("CONFIG_KSU_SUSFS_SUS_MOUNT\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SUS_MOUNT\n", str_len); + ptr_buf += str_len; + } + if (enabled_features & (1 << 2)) { + str_len = strlen("CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT\n", str_len); + ptr_buf += str_len; + } + if (enabled_features & (1 << 3)) { + str_len = strlen("CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT\n", str_len); + ptr_buf += str_len; + } + if (enabled_features & (1 << 4)) { + str_len = strlen("CONFIG_KSU_SUSFS_SUS_KSTAT\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SUS_KSTAT\n", str_len); + ptr_buf += str_len; + } + if (enabled_features & (1 << 5)) { + str_len = strlen("CONFIG_KSU_SUSFS_SUS_OVERLAYFS\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SUS_OVERLAYFS\n", str_len); + ptr_buf += str_len; + } + if (enabled_features & (1 << 6)) { + str_len = strlen("CONFIG_KSU_SUSFS_TRY_UMOUNT\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_TRY_UMOUNT\n", str_len); + ptr_buf += str_len; + } + if (enabled_features & (1 << 7)) { + str_len = strlen("CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT\n", str_len); + ptr_buf += str_len; + } + if (enabled_features & (1 << 8)) { + str_len = strlen("CONFIG_KSU_SUSFS_SPOOF_UNAME\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SPOOF_UNAME\n", str_len); + ptr_buf += str_len; + } + if (enabled_features & (1 << 9)) { + str_len = strlen("CONFIG_KSU_SUSFS_ENABLE_LOG\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_ENABLE_LOG\n", str_len); + ptr_buf += str_len; + } + if (enabled_features & (1 << 10)) { + str_len = strlen("CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS\n", str_len); + ptr_buf += str_len; + } + if (enabled_features & (1 << 11)) { + str_len = strlen("CONFIG_KSU_SUSFS_SPOOF_PROC_CMDLINE\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SPOOF_PROC_CMDLINE\n", str_len); + ptr_buf += str_len; + } + if (enabled_features & (1 << 12)) { + str_len = strlen("CONFIG_KSU_SUSFS_OPEN_REDIRECT\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_OPEN_REDIRECT\n", str_len); + ptr_buf += str_len; + } + if (enabled_features & (1 << 13)) { + str_len = strlen("CONFIG_KSU_SUSFS_SUS_SU\n"); + strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SUS_SU\n", str_len); + ptr_buf += str_len; + } + printf("%s", enabled_features_buf); + free(enabled_features_buf); + } + } else if (!strcmp(argv[2], "variant")) { + char variant[16]; + prctl(KERNEL_SU_OPTION, CMD_SUSFS_SHOW_VARIANT, variant, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_SHOW_VARIANT); + if (!error) + printf("%s\n", variant); + } + return error; + // sus_su + } else if (argc == 3 && !strcmp(argv[1], "sus_su")) { + int last_working_mode = 0; + int target_working_mode; + char* endptr; + + prctl(KERNEL_SU_OPTION, CMD_SUSFS_SHOW_SUS_SU_WORKING_MODE, &last_working_mode, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_SHOW_SUS_SU_WORKING_MODE); + if (error) + return error; + if (!strcmp(argv[2], "show_working_mode")) { + printf("%d\n", last_working_mode); + return 0; + } + target_working_mode = strtol(argv[2], &endptr, 10); + if (*endptr != '\0') { + print_help(); + return 1; + } + if (target_working_mode == SUS_SU_WITH_HOOKS) { + bool is_sus_su_ready; + prctl(KERNEL_SU_OPTION, CMD_SUSFS_IS_SUS_SU_READY, &is_sus_su_ready, NULL, &error); + PRT_MSG_IF_OPERATION_NOT_SUPPORTED(error, CMD_SUSFS_IS_SUS_SU_READY); + if (error) + return error; + if (!is_sus_su_ready) { + log("[-] sus_su mode %d has to be run during or after service stage\n", SUS_SU_WITH_HOOKS); + return 1; + } + if (last_working_mode == SUS_SU_DISABLED) { + error = enable_sus_su(last_working_mode, SUS_SU_WITH_HOOKS); + } else if (last_working_mode == SUS_SU_WITH_HOOKS) { + log("[-] sus_su is already in mode %d\n", last_working_mode); + return 1; + } else { + error = enable_sus_su(last_working_mode, SUS_SU_DISABLED); + if (!error) + error = enable_sus_su(last_working_mode, SUS_SU_WITH_HOOKS); + } + } else if (target_working_mode == SUS_SU_DISABLED) { + if (last_working_mode == SUS_SU_DISABLED) { + log("[-] sus_su is already in mode %d\n", last_working_mode); + return 1; + } + error = enable_sus_su(last_working_mode, SUS_SU_DISABLED); + } else if (target_working_mode == SUS_SU_WITH_OVERLAY) { + log("[-] sus_su mode %d is deprecated\n", SUS_SU_WITH_OVERLAY); + return 1; + } else { + print_help(); + return 1; + } + return error; + } else { + print_help(); + } +out: + return 0; +} +