source: removed all SuSFS code

This commit is contained in:
Rifat Azad
2025-01-04 17:33:03 +06:00
parent 6ae0737fba
commit ace36326cd
15 changed files with 7 additions and 731 deletions
-327
View File
@@ -1,327 +0,0 @@
use anyhow::{anyhow, bail, Ok, Result};
#[cfg(any(target_os = "linux", target_os = "android"))]
use anyhow::Context;
#[cfg(any(target_os = "linux", target_os = "android"))]
use rustix::{fd::AsFd, fs::CWD, mount::*};
use crate::defs::KSU_OVERLAY_SOURCE;
use log::{info, warn};
#[cfg(any(target_os = "linux", target_os = "android"))]
use procfs::process::Process;
use std::path::Path;
use std::path::PathBuf;
pub struct AutoMountExt4 {
target: String,
auto_umount: bool,
}
impl AutoMountExt4 {
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn try_new(source: &str, target: &str, auto_umount: bool) -> Result<Self> {
mount_ext4(source, target)?;
Ok(Self {
target: target.to_string(),
auto_umount,
})
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn try_new(_src: &str, _mnt: &str, _auto_umount: bool) -> Result<Self> {
unimplemented!()
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn umount(&self) -> Result<()> {
unmount(self.target.as_str(), UnmountFlags::DETACH)?;
Ok(())
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
impl Drop for AutoMountExt4 {
fn drop(&mut self) {
log::info!(
"AutoMountExt4 drop: {}, auto_umount: {}",
self.target,
self.auto_umount
);
if self.auto_umount {
let _ = self.umount();
}
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn mount_ext4(source: impl AsRef<Path>, target: impl AsRef<Path>) -> Result<()> {
let new_loopback = loopdev::LoopControl::open()?
.next_free()
.with_context(|| "Failed to alloc loop")?;
new_loopback
.with()
.attach(source)
.with_context(|| "Failed to attach loop")?;
let lo = new_loopback.path().ok_or(anyhow!("no loop"))?;
if let Result::Ok(fs) = fsopen("ext4", FsOpenFlags::FSOPEN_CLOEXEC) {
let fs = fs.as_fd();
fsconfig_set_string(fs, "source", lo)?;
fsconfig_create(fs)?;
let mount = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?;
move_mount(
mount.as_fd(),
"",
CWD,
target.as_ref(),
MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
)?;
} else {
mount(lo, target.as_ref(), "ext4", MountFlags::empty(), "")?;
}
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn umount_dir(src: impl AsRef<Path>) -> Result<()> {
unmount(src.as_ref(), UnmountFlags::empty())
.with_context(|| format!("Failed to umount {}", src.as_ref().display()))?;
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn mount_overlayfs(
lower_dirs: &[String],
lowest: &str,
upperdir: Option<PathBuf>,
workdir: Option<PathBuf>,
dest: impl AsRef<Path>,
) -> Result<()> {
let lowerdir_config = lower_dirs
.iter()
.map(|s| s.as_ref())
.chain(std::iter::once(lowest))
.collect::<Vec<_>>()
.join(":");
info!(
"mount overlayfs on {:?}, lowerdir={}, upperdir={:?}, workdir={:?}",
dest.as_ref(),
lowerdir_config,
upperdir,
workdir
);
let upperdir = upperdir
.filter(|up| up.exists())
.map(|e| e.display().to_string());
let workdir = workdir
.filter(|wd| wd.exists())
.map(|e| e.display().to_string());
let result = (|| {
let fs = fsopen("overlay", FsOpenFlags::FSOPEN_CLOEXEC)?;
let fs = fs.as_fd();
fsconfig_set_string(fs, "lowerdir", &lowerdir_config)?;
if let (Some(upperdir), Some(workdir)) = (&upperdir, &workdir) {
fsconfig_set_string(fs, "upperdir", upperdir)?;
fsconfig_set_string(fs, "workdir", workdir)?;
}
fsconfig_set_string(fs, "source", KSU_OVERLAY_SOURCE)?;
fsconfig_create(fs)?;
let mount = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?;
move_mount(
mount.as_fd(),
"",
CWD,
dest.as_ref(),
MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
)
})();
if let Err(e) = result {
warn!("fsopen mount failed: {:#}, fallback to mount", e);
let mut data = format!("lowerdir={lowerdir_config}");
if let (Some(upperdir), Some(workdir)) = (upperdir, workdir) {
data = format!("{data},upperdir={upperdir},workdir={workdir}");
}
mount(
KSU_OVERLAY_SOURCE,
dest.as_ref(),
"overlay",
MountFlags::empty(),
data,
)?;
}
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn mount_tmpfs(dest: impl AsRef<Path>) -> Result<()> {
info!("mount tmpfs on {}", dest.as_ref().display());
if let Result::Ok(fs) = fsopen("tmpfs", FsOpenFlags::FSOPEN_CLOEXEC) {
let fs = fs.as_fd();
fsconfig_set_string(fs, "source", KSU_OVERLAY_SOURCE)?;
fsconfig_create(fs)?;
let mount = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?;
move_mount(
mount.as_fd(),
"",
CWD,
dest.as_ref(),
MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
)?;
} else {
mount(
KSU_OVERLAY_SOURCE,
dest.as_ref(),
"tmpfs",
MountFlags::empty(),
"",
)?;
}
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn bind_mount(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
info!(
"bind mount {} -> {}",
from.as_ref().display(),
to.as_ref().display()
);
if let Result::Ok(tree) = open_tree(
CWD,
from.as_ref(),
OpenTreeFlags::OPEN_TREE_CLOEXEC
| OpenTreeFlags::OPEN_TREE_CLONE
| OpenTreeFlags::AT_RECURSIVE,
) {
move_mount(
tree.as_fd(),
"",
CWD,
to.as_ref(),
MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
)?;
} else {
mount(
from.as_ref(),
to.as_ref(),
"",
MountFlags::BIND | MountFlags::REC,
"",
)?;
}
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn mount_overlay_child(
mount_point: &str,
relative: &String,
module_roots: &Vec<String>,
stock_root: &String,
) -> Result<()> {
if !module_roots
.iter()
.any(|lower| Path::new(&format!("{lower}{relative}")).exists())
{
return bind_mount(stock_root, mount_point);
}
if !Path::new(&stock_root).is_dir() {
return Ok(());
}
let mut lower_dirs: Vec<String> = vec![];
for lower in module_roots {
let lower_dir = format!("{lower}{relative}");
let path = Path::new(&lower_dir);
if path.is_dir() {
lower_dirs.push(lower_dir);
} else if path.exists() {
// stock root has been blocked by this file
return Ok(());
}
}
if lower_dirs.is_empty() {
return Ok(());
}
// merge modules and stock
if let Err(e) = mount_overlayfs(&lower_dirs, stock_root, None, None, mount_point) {
warn!("failed: {:#}, fallback to bind mount", e);
bind_mount(stock_root, mount_point)?;
}
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn mount_overlay(
root: &String,
module_roots: &Vec<String>,
workdir: Option<PathBuf>,
upperdir: Option<PathBuf>,
) -> Result<()> {
info!("mount overlay for {}", root);
std::env::set_current_dir(root).with_context(|| format!("failed to chdir to {root}"))?;
let stock_root = ".";
// collect child mounts before mounting the root
let mounts = Process::myself()?
.mountinfo()
.with_context(|| "get mountinfo")?;
let mut mount_seq = mounts
.0
.iter()
.filter(|m| {
m.mount_point.starts_with(root) && !Path::new(&root).starts_with(&m.mount_point)
})
.map(|m| m.mount_point.to_str())
.collect::<Vec<_>>();
mount_seq.sort();
mount_seq.dedup();
mount_overlayfs(module_roots, root, upperdir, workdir, root)
.with_context(|| "mount overlayfs for root failed")?;
for mount_point in mount_seq.iter() {
let Some(mount_point) = mount_point else {
continue;
};
let relative = mount_point.replacen(root, "", 1);
let stock_root: String = format!("{stock_root}{relative}");
if !Path::new(&stock_root).exists() {
continue;
}
if let Err(e) = mount_overlay_child(mount_point, &relative, module_roots, &stock_root) {
warn!(
"failed to mount overlay for child {}: {:#}, revert",
mount_point, e
);
umount_dir(root).with_context(|| format!("failed to revert {root}"))?;
bail!(e);
}
}
Ok(())
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn mount_ext4(_src: &str, _target: &str, _autodrop: bool) -> Result<()> {
unimplemented!()
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn umount_dir(_src: &str) -> Result<()> {
unimplemented!()
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn mount_overlay(
_root: &String,
_module_roots: &Vec<String>,
_workdir: Option<PathBuf>,
_upperdir: Option<PathBuf>,
) -> Result<()> {
unimplemented!()
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn mount_tmpfs(_dest: impl AsRef<Path>) -> Result<()> {
unimplemented!()
}
-2
View File
@@ -1,2 +0,0 @@
/obj
/libs
-6
View File
@@ -1,6 +0,0 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := susfsd
LOCAL_SRC_FILES := susfsd.c
include $(BUILD_EXECUTABLE)
-3
View File
@@ -1,3 +0,0 @@
APP_ABI := arm64-v8a
APP_PLATFORM := android-24
APP_STL := none
-239
View File
@@ -1,239 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <stdbool.h>
#include <errno.h>
#include <unistd.h>
#define KERNEL_SU_OPTION 0xDEADBEEF
#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 SUS_SU_DISABLED 0
#define SUS_SU_WITH_HOOKS 2
struct st_sus_su {
int mode;
};
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);
if (error) {
if (error == 1) {
} else if (error == 2) {
}
return error;
}
printf("[+] 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);
if (error) {
if (error == 1) {
}
return error;
}
printf("[+] sus_su mode 0 is enabled\n");
} else {
return 1;
}
return 0;
}
int main(int argc, char *argv[]) {
int error = -1;
char support[16];
char version[16];
char variant[16];
// Check for arguments
if (argc < 2) {
fprintf(stderr, "Usage: %s <support|version|variant|features|sus_su <0|2|mode>|>\n", argv[0]);
return 1;
}
if (strcmp(argv[1], "version") == 0) {
prctl(KERNEL_SU_OPTION, CMD_SUSFS_SHOW_VERSION, version, NULL, &error);
if (!error) {
printf("%s\n", version);
} else {
printf("Invalid\n");
}
} else if (strcmp(argv[1], "variant") == 0) {
prctl(KERNEL_SU_OPTION, CMD_SUSFS_SHOW_VARIANT, variant, NULL, &error);
if (!error) {
printf("%s\n", variant);
} else {
printf("Invalid\n");
}
} else if (strcmp(argv[1], "features") == 0) {
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);
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_BOOTCONFIG\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SPOOF_BOOTCONFIG\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 {
printf("Invalid\n");
}
} else if (strcmp(argv[1], "support") == 0) {
unsigned long enabled_features;
int any_feature_enabled = 0;
prctl(KERNEL_SU_OPTION, CMD_SUSFS_SHOW_ENABLED_FEATURES, &enabled_features, NULL, &error);
if (!error) {
if (enabled_features & ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) |
(1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) |
(1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) |
(1 << 12) | (1 << 13))) {
any_feature_enabled = 1;
}
if (any_feature_enabled) {
printf("Supported\n");
} else {
printf("Unsupported\n");
}
} else {
printf("Unsupported\n");
}
} 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);
if (error)
return error;
if (!strcmp(argv[2], "mode")) {
printf("%d\n", last_working_mode);
return 0;
}
target_working_mode = strtol(argv[2], &endptr, 10);
if (*endptr != '\0') {
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);
if (error)
return error;
if (!is_sus_su_ready) {
printf("[-] 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) {
printf("[-] 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) {
printf("[-] sus_su is already in mode %d\n", last_working_mode);
return 1;
}
error = enable_sus_su(last_working_mode, SUS_SU_DISABLED);
}
} else {
fprintf(stderr, "Invalid argument: %s\n", argv[1]);
return 1;
}
return 0;
}