297 lines
7.8 KiB
Rust
297 lines
7.8 KiB
Rust
use std::fs;
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
use std::os::fd::RawFd;
|
|
use std::sync::OnceLock;
|
|
|
|
// Event constants
|
|
const EVENT_POST_FS_DATA: u32 = 1;
|
|
const EVENT_BOOT_COMPLETED: u32 = 2;
|
|
const EVENT_MODULE_MOUNTED: u32 = 3;
|
|
|
|
const KSU_IOCTL_GRANT_ROOT: u32 = 0x00004b01; // _IOC(_IOC_NONE, 'K', 1, 0)
|
|
const KSU_IOCTL_GET_INFO: u32 = 0x80004b02; // _IOC(_IOC_READ, 'K', 2, 0)
|
|
const KSU_IOCTL_REPORT_EVENT: u32 = 0x40004b03; // _IOC(_IOC_WRITE, 'K', 3, 0)
|
|
const KSU_IOCTL_SET_SEPOLICY: u32 = 0xc0004b04; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 4, 0)
|
|
const KSU_IOCTL_CHECK_SAFEMODE: u32 = 0x80004b05; // _IOC(_IOC_READ, 'K', 5, 0)
|
|
const KSU_IOCTL_GET_FEATURE: u32 = 0xc0004b0d; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0)
|
|
const KSU_IOCTL_SET_FEATURE: u32 = 0x40004b0e; // _IOC(_IOC_WRITE, 'K', 14, 0)
|
|
const KSU_IOCTL_PROXY_FILE: u32 = 0x00004b0f; // _IOC(_IOC_NONE, 'K', 15, 0)
|
|
#[allow(dead_code)]
|
|
const KSU_IOCTL_KPM: u32 = 0xc0004bc8; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 200, 0)
|
|
#[allow(dead_code)]
|
|
const KSU_IOCTL_UMOUNT_MANAGER: u32 = 0xc0004b6b; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 107, 0)
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Default)]
|
|
struct GetInfoCmd {
|
|
version: u32,
|
|
flags: u32,
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct ReportEventCmd {
|
|
event: u32,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Default)]
|
|
pub struct SetSepolicyCmd {
|
|
pub cmd: u64,
|
|
pub arg: u64,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Default)]
|
|
struct CheckSafemodeCmd {
|
|
in_safe_mode: u8,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Default)]
|
|
struct GetFeatureCmd {
|
|
feature_id: u32,
|
|
value: u64,
|
|
supported: u8,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Default)]
|
|
struct SetFeatureCmd {
|
|
feature_id: u32,
|
|
value: u64,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Default)]
|
|
struct ProxyFileCmd {
|
|
fd: i32,
|
|
flags: u32,
|
|
}
|
|
|
|
// Global driver fd cache
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
static DRIVER_FD: OnceLock<RawFd> = OnceLock::new();
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
static INFO_CACHE: OnceLock<GetInfoCmd> = OnceLock::new();
|
|
|
|
const KSU_INSTALL_MAGIC1: u32 = 0xDEADBEEF;
|
|
const KSU_INSTALL_MAGIC2: u32 = 0xCAFEBABE;
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
fn scan_driver_fd() -> Option<RawFd> {
|
|
let fd_dir = fs::read_dir("/proc/self/fd").ok()?;
|
|
|
|
for entry in fd_dir.flatten() {
|
|
if let Ok(fd_num) = entry.file_name().to_string_lossy().parse::<i32>() {
|
|
let link_path = format!("/proc/self/fd/{}", fd_num);
|
|
if let Ok(target) = fs::read_link(&link_path) {
|
|
let target_str = target.to_string_lossy();
|
|
if target_str.contains("[ksu_driver]") {
|
|
return Some(fd_num);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
// Get cached driver fd
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
fn init_driver_fd() -> Option<RawFd> {
|
|
let fd = scan_driver_fd();
|
|
if fd.is_none() {
|
|
let mut fd = -1;
|
|
unsafe {
|
|
libc::syscall(
|
|
libc::SYS_reboot,
|
|
KSU_INSTALL_MAGIC1,
|
|
KSU_INSTALL_MAGIC2,
|
|
0,
|
|
&mut fd,
|
|
);
|
|
};
|
|
if fd >= 0 { Some(fd) } else { None }
|
|
} else {
|
|
fd
|
|
}
|
|
}
|
|
|
|
// ioctl wrapper using libc
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
fn ksuctl<T>(request: u32, arg: *mut T) -> std::io::Result<i32> {
|
|
use std::io;
|
|
|
|
let fd = *DRIVER_FD.get_or_init(|| init_driver_fd().unwrap_or(-1));
|
|
unsafe {
|
|
#[cfg(not(target_env = "gnu"))]
|
|
let ret = libc::ioctl(fd as libc::c_int, request as i32, arg);
|
|
#[cfg(target_env = "gnu")]
|
|
let ret = libc::ioctl(fd as libc::c_int, request as u64, arg);
|
|
if ret < 0 {
|
|
Err(io::Error::last_os_error())
|
|
} else {
|
|
Ok(ret)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
|
fn ksuctl<T>(_request: u32, _arg: *mut T) -> std::io::Result<i32> {
|
|
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
|
}
|
|
|
|
// API implementations
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
fn get_info() -> GetInfoCmd {
|
|
*INFO_CACHE.get_or_init(|| {
|
|
let mut cmd = GetInfoCmd {
|
|
version: 0,
|
|
flags: 0,
|
|
};
|
|
let _ = ksuctl(KSU_IOCTL_GET_INFO, &mut cmd as *mut _);
|
|
cmd
|
|
})
|
|
}
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
pub fn get_version() -> i32 {
|
|
get_info().version as i32
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
|
pub fn get_version() -> i32 {
|
|
0
|
|
}
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
pub fn grant_root() -> std::io::Result<()> {
|
|
ksuctl(KSU_IOCTL_GRANT_ROOT, std::ptr::null_mut::<u8>())?;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
|
pub fn grant_root() -> std::io::Result<()> {
|
|
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
|
}
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
fn report_event(event: u32) {
|
|
let mut cmd = ReportEventCmd { event };
|
|
let _ = ksuctl(KSU_IOCTL_REPORT_EVENT, &mut cmd as *mut _);
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
|
fn report_event(_event: u32) {}
|
|
|
|
pub fn report_post_fs_data() {
|
|
report_event(EVENT_POST_FS_DATA);
|
|
}
|
|
|
|
pub fn report_boot_complete() {
|
|
report_event(EVENT_BOOT_COMPLETED);
|
|
}
|
|
|
|
pub fn report_module_mounted() {
|
|
report_event(EVENT_MODULE_MOUNTED);
|
|
}
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
pub fn check_kernel_safemode() -> bool {
|
|
let mut cmd = CheckSafemodeCmd { in_safe_mode: 0 };
|
|
let _ = ksuctl(KSU_IOCTL_CHECK_SAFEMODE, &mut cmd as *mut _);
|
|
cmd.in_safe_mode != 0
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
|
pub fn check_kernel_safemode() -> bool {
|
|
false
|
|
}
|
|
|
|
pub fn set_sepolicy(cmd: &SetSepolicyCmd) -> std::io::Result<()> {
|
|
let mut ioctl_cmd = *cmd;
|
|
ksuctl(KSU_IOCTL_SET_SEPOLICY, &mut ioctl_cmd as *mut _)?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Get feature value and support status from kernel
|
|
/// Returns (value, supported)
|
|
pub fn get_feature(feature_id: u32) -> std::io::Result<(u64, bool)> {
|
|
let mut cmd = GetFeatureCmd {
|
|
feature_id,
|
|
value: 0,
|
|
supported: 0,
|
|
};
|
|
ksuctl(KSU_IOCTL_GET_FEATURE, &mut cmd as *mut _)?;
|
|
Ok((cmd.value, cmd.supported != 0))
|
|
}
|
|
|
|
/// Set feature value in kernel
|
|
pub fn set_feature(feature_id: u32, value: u64) -> std::io::Result<()> {
|
|
let mut cmd = SetFeatureCmd { feature_id, value };
|
|
ksuctl(KSU_IOCTL_SET_FEATURE, &mut cmd as *mut _)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn proxy_file(fd: RawFd) -> std::io::Result<RawFd> {
|
|
let mut cmd = ProxyFileCmd { fd, flags: 0 };
|
|
let result = ksuctl(KSU_IOCTL_PROXY_FILE, &mut cmd as *mut _)?;
|
|
Ok(result)
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Default)]
|
|
#[allow(dead_code)]
|
|
pub struct KsuKpmCmd {
|
|
pub arg2: u64,
|
|
pub arg3: u64,
|
|
pub arg4: u64,
|
|
pub arg5: u64,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn kpm_ioctl(cmd: &mut KsuKpmCmd) -> std::io::Result<()> {
|
|
ksuctl(KSU_IOCTL_KPM, cmd as *mut _)?;
|
|
Ok(())
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy)]
|
|
#[allow(dead_code)]
|
|
pub struct UmountManagerCmd {
|
|
pub operation: u32,
|
|
pub path: [u8; 256],
|
|
pub check_mnt: u8,
|
|
pub flags: i32,
|
|
pub count: u32,
|
|
pub entries_ptr: u64,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl Default for UmountManagerCmd {
|
|
fn default() -> Self {
|
|
UmountManagerCmd {
|
|
operation: 0,
|
|
path: [0; 256],
|
|
check_mnt: 0,
|
|
flags: 0,
|
|
count: 0,
|
|
entries_ptr: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
#[allow(dead_code)]
|
|
pub fn umount_manager_ioctl(cmd: &UmountManagerCmd) -> std::io::Result<()> {
|
|
let mut ioctl_cmd = *cmd;
|
|
ksuctl(KSU_IOCTL_UMOUNT_MANAGER, &mut ioctl_cmd as *mut _)?;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
|
#[allow(dead_code)]
|
|
pub fn umount_manager_ioctl(_cmd: &UmountManagerCmd) -> std::io::Result<()> {
|
|
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
|
}
|