diff --git a/loader/src/include/misc.h b/loader/src/include/misc.h index 168b9fb..e7a02a4 100644 --- a/loader/src/include/misc.h +++ b/loader/src/include/misc.h @@ -4,6 +4,9 @@ extern "C" { #endif /* __cplusplus */ +#define IS_ISOLATED_SERVICE(uid) \ + ((uid) >= 90000 && (uid) < 1000000) + /* * Bionic's atoi runs through strtol(). * Use our own implementation for faster conversion. diff --git a/loader/src/injector/hook.cpp b/loader/src/injector/hook.cpp index ccdc688..4695031 100644 --- a/loader/src/injector/hook.cpp +++ b/loader/src/injector/hook.cpp @@ -722,7 +722,48 @@ void ZygiskContext::run_modules_post() { void ZygiskContext::app_specialize_pre() { flags[APP_SPECIALIZE] = true; - info_flags = rezygiskd_get_process_flags(g_ctx->args.app->uid, (const char *const)process); + /* INFO: Isolated services have different UIDs than the main apps. Because + numerous root implementations base themselves in the UID of the + app, we need to ensure that the UID sent to ReZygiskd to search + is the app's and not the isolated service, or else it will be + able to bypass DenyList. + + All apps, and isolated processes, of *third-party* applications will + have their app_data_dir set. The system applications might not have + one, however it is unlikely they will create an isolated process, + and even if so, it should not impact in detections, performance or + any area. + */ + uid_t uid = args.app->uid; + if (IS_ISOLATED_SERVICE(uid) && args.app->app_data_dir) { + /* INFO: If the app is an isolated service, we use the UID of the + app's process data directory, which is the UID of the + app itself, which root implementations actually use. + */ + const char *data_dir = env->GetStringUTFChars(args.app->app_data_dir, NULL); + if (!data_dir) { + LOGE("Failed to get app data directory"); + + return; + } + + struct stat st; + if (stat(data_dir, &st) == -1) { + PLOGE("Failed to stat app data directory [%s]", data_dir); + + env->ReleaseStringUTFChars(args.app->app_data_dir, data_dir); + + return; + } + + uid = st.st_uid; + + LOGD("Isolated service being related to UID %d, app data dir: %s", uid, data_dir); + + env->ReleaseStringUTFChars(args.app->app_data_dir, data_dir); + } + + info_flags = rezygiskd_get_process_flags(uid, (const char *const)process); if (info_flags & PROCESS_IS_FIRST_STARTED) { /* INFO: To ensure we are really using a clean mount namespace, we use the first process it as reference for clean mount namespace, diff --git a/zygiskd/src/root_impl/apatch.c b/zygiskd/src/root_impl/apatch.c index 7b76fb7..786fe99 100644 --- a/zygiskd/src/root_impl/apatch.c +++ b/zygiskd/src/root_impl/apatch.c @@ -168,22 +168,8 @@ bool apatch_uid_should_umount(uid_t uid, const char *const process) { return false; } - /* INFO: Some can take advantage of the UID being different in an app's - isolated service, bypassing this check, so we must check against - process name in case it is an isolated service. This can happen in - all root implementations. */ - size_t targeted_process_length = 0; - if (IS_ISOLATED_SERVICE(uid)) targeted_process_length = strlen(process); - for (size_t i = 0; i < config.size; i++) { - if (IS_ISOLATED_SERVICE(uid)) { - size_t config_process_length = strlen(config.configs[i].process); - size_t smallest_process_length = targeted_process_length < config_process_length ? targeted_process_length : config_process_length; - - if (strncmp(config.configs[i].process, process, smallest_process_length) != 0) continue; - } else { - if (config.configs[i].uid != uid) continue; - } + if (config.configs[i].uid != uid) continue; /* INFO: This allow us to copy the information to avoid use-after-free */ bool umount_needed = config.configs[i].umount_needed; @@ -193,6 +179,29 @@ bool apatch_uid_should_umount(uid_t uid, const char *const process) { return umount_needed; } + /* INFO: Isolated services have different UIDs than the main app, and + while libzygisk.so has code to send the UID of the app related + to the isolated service, we add this so that in case it fails, + this should avoid it pass through as Mounted. + */ + if (IS_ISOLATED_SERVICE(uid)) { + size_t targeted_process_length = strlen(process); + + for (size_t i = 0; i < config.size; i++) { + size_t config_process_length = strlen(config.configs[i].process); + size_t smallest_process_length = targeted_process_length < config_process_length ? targeted_process_length : config_process_length; + + if (strncmp(config.configs[i].process, process, smallest_process_length) != 0) continue; + + /* INFO: This allow us to copy the information to avoid use-after-free */ + bool umount_needed = config.configs[i].umount_needed; + + _apatch_free_package_config(&config); + + return umount_needed; + } + } + _apatch_free_package_config(&config); return false;