From 9ed110c91b16c5ca56b50db2552366f204428205 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 8 Jan 2021 05:25:44 -0800 Subject: [PATCH] Add JNI hooks to critical methods --- native/jni/Android.mk | 3 +- native/jni/inject/entry.cpp | 1 + native/jni/inject/hook.cpp | 79 ++++- native/jni/inject/inject.hpp | 26 ++ native/jni/inject/jni_hooks.cpp | 596 ++++++++++++++++++++++++++++++++ 5 files changed, 700 insertions(+), 5 deletions(-) create mode 100644 native/jni/inject/jni_hooks.cpp diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 732a3b9f8..f7271423b 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -33,7 +33,8 @@ LOCAL_SRC_FILES := \ su/su_daemon.cpp \ inject/entry.cpp \ inject/utils.cpp \ - inject/hook.cpp + inject/hook.cpp \ + inject/jni_hooks.cpp LOCAL_LDLIBS := -llog include $(BUILD_EXECUTABLE) diff --git a/native/jni/inject/entry.cpp b/native/jni/inject/entry.cpp index ffd3fd2f7..caebb2f60 100644 --- a/native/jni/inject/entry.cpp +++ b/native/jni/inject/entry.cpp @@ -42,6 +42,7 @@ static void inject_cleanup() { } void self_unload() { + LOGD("hook: Request to self unload\n"); // If unhook failed, do not unload or else it will cause SIGSEGV if (!unhook_functions()) return; diff --git a/native/jni/inject/hook.cpp b/native/jni/inject/hook.cpp index c86d777d8..fe832e9bb 100644 --- a/native/jni/inject/hook.cpp +++ b/native/jni/inject/hook.cpp @@ -8,20 +8,61 @@ using namespace std; -// Static vector won't work, use a pointer instead +static JavaVM *g_jvm; + +// For some reason static vector won't work, use a pointer instead static vector> *hook_list; #define DEF_HOOK_FUNC(ret, func, ...) \ static ret (*old_##func)(__VA_ARGS__); \ static ret new_##func(__VA_ARGS__) +#define HOOK_JNI(clazz, method) \ +if (newMethods[i].name == #method##sv) { \ + JNI::clazz::method##_orig = new JNINativeMethod(); \ + memcpy(JNI::clazz::method##_orig, &newMethods[i], sizeof(JNINativeMethod)); \ + for (int j = 0; j < JNI::clazz::method##_methods_num; ++j) { \ + if (strcmp(newMethods[i].signature, JNI::clazz::method##_methods[j].signature) == 0) { \ + newMethods[i] = JNI::clazz::method##_methods[j]; \ + LOGI("hook: replaced " #clazz "#" #method "\n"); \ + ++hooked; \ + break; \ + } \ + } \ + continue; \ +} + +#define clone_methods() \ + newMethods = make_unique(numMethods); \ + memcpy(newMethods.get(), methods, sizeof(JNINativeMethod) * numMethods) + DEF_HOOK_FUNC(int, jniRegisterNativeMethods, JNIEnv *env, const char *className, const JNINativeMethod *methods, int numMethods) { LOGD("hook: jniRegisterNativeMethods %s", className); - // TODO: actually do things like replacing JNI native methods + unique_ptr newMethods; + int hooked = 0; - return old_jniRegisterNativeMethods(env, className, methods, numMethods); + if (g_jvm == nullptr) { + // Save for later unhooking + env->GetJavaVM(&g_jvm); + } + + if (className == "com/android/internal/os/Zygote"sv) { + clone_methods(); + for (int i = 0; i < numMethods && hooked < 3; ++i) { + HOOK_JNI(Zygote, nativeForkAndSpecialize); + HOOK_JNI(Zygote, nativeSpecializeAppProcess); + HOOK_JNI(Zygote, nativeForkSystemServer); + } + } else if (className == "android/os/SystemProperties"sv) { + clone_methods(); + for (int i = 0; i < numMethods && hooked < 1; ++i) { + HOOK_JNI(SystemProperties, native_set); + } + } + + return old_jniRegisterNativeMethods(env, className, newMethods.get() ?: methods, numMethods); } static bool hook_refresh() { @@ -58,10 +99,40 @@ void hook_functions() { hook_refresh(); } +#define push_method(clazz, method) \ +if (JNI::clazz::method##_orig) methods.emplace_back(*JNI::clazz::method##_orig) + bool unhook_functions() { + JNIEnv* env; + if (g_jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) + return false; + + vector methods; + + push_method(Zygote, nativeForkAndSpecialize); + push_method(Zygote, nativeSpecializeAppProcess); + push_method(Zygote, nativeForkSystemServer); + + if (!methods.empty() && old_jniRegisterNativeMethods(env, + "com/android/internal/os/Zygote", + methods.data(), methods.size()) != 0) { + LOGE("hook: Failed to register JNI hook for Zygote\n"); + return false; + } + + methods.clear(); + push_method(SystemProperties, native_set); + + if (!methods.empty() && old_jniRegisterNativeMethods(env, + "android/os/SystemProperties", + methods.data(), methods.size()) != 0) { + LOGE("hook: Failed to register JNI hook for SystemProperties\n"); + return false; + } + for (auto &[path, sym, old_func] : *hook_list) { if (xhook_register(path, sym, *old_func, nullptr) != 0) { - LOGE("hook: Failed to register hook \"%s\"\n", sym); \ + LOGE("hook: Failed to register hook \"%s\"\n", sym); return false; } } diff --git a/native/jni/inject/inject.hpp b/native/jni/inject/inject.hpp index 574ad72b2..6e37665ed 100644 --- a/native/jni/inject/inject.hpp +++ b/native/jni/inject/inject.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #define INJECT_LIB_1 "/dev/tmp/magisk.1.so" #define INJECT_LIB_2 "/dev/tmp/magisk.2.so" @@ -19,3 +20,28 @@ uintptr_t get_remote_lib(int pid, const char *lib); void self_unload(); void hook_functions(); bool unhook_functions(); + +// JNI method declarations + +namespace JNI { + namespace Zygote { + extern JNINativeMethod *nativeForkAndSpecialize_orig; + extern JNINativeMethod *nativeSpecializeAppProcess_orig; + extern JNINativeMethod *nativeForkSystemServer_orig; + + extern const JNINativeMethod nativeForkAndSpecialize_methods[]; + extern const int nativeForkAndSpecialize_methods_num; + + extern const JNINativeMethod nativeSpecializeAppProcess_methods[]; + extern const int nativeSpecializeAppProcess_methods_num; + + extern const JNINativeMethod nativeForkSystemServer_methods[]; + extern const int nativeForkSystemServer_methods_num; + } + namespace SystemProperties { + extern JNINativeMethod *native_set_orig; + + extern const JNINativeMethod native_set_methods[]; + constexpr int native_set_methods_num = 1; + } +} diff --git a/native/jni/inject/jni_hooks.cpp b/native/jni/inject/jni_hooks.cpp new file mode 100644 index 000000000..db4840465 --- /dev/null +++ b/native/jni/inject/jni_hooks.cpp @@ -0,0 +1,596 @@ +/* + * Original code: https://github.com/RikkaApps/Riru/blob/master/riru/src/main/cpp/jni_native_method.cpp + * The code is modified and sublicensed to GPLv3 for incorporating into Magisk. + * + * Copyright (c) 2018-2021, RikkaW + * Copyright (c) 2021, John 'topjohnwu' Wu + */ + +#include + +#include + +#include "inject.hpp" + +#define ENABLE_LEGACY_DP 0 // Nobody should use outdated developer preview... + +static void nativeForkAndSpecialize_pre( + JNIEnv *env, jclass clazz, jint &uid, jint &gid, jintArray &gids, jint &runtime_flags, + jobjectArray &rlimits, jint &mount_external, jstring &se_info, jstring &se_name, + jintArray &fdsToClose, jintArray &fdsToIgnore, jboolean &is_child_zygote, + jstring &instructionSet, jstring &appDataDir, jboolean &isTopApp, jobjectArray &pkgDataInfoList, + jobjectArray &whitelistedDataInfoList, jboolean &bindMountAppDataDirs, jboolean &bindMountAppStorageDirs) { + LOGD("hook: %s\n", __FUNCTION__); +} + +static void nativeForkAndSpecialize_post(JNIEnv *env, jclass clazz, jint uid, jint pid) { + LOGD("hook: %s\n", __FUNCTION__); + // Demonstrate self unload in child process + if (pid == 0) + self_unload(); +} + +// ----------------------------------------------------------------- + +static void nativeSpecializeAppProcess_pre( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtimeFlags, + jobjectArray rlimits, jint mountExternal, jstring seInfo, jstring niceName, + jboolean startChildZygote, jstring instructionSet, jstring appDataDir, + jboolean &isTopApp, jobjectArray &pkgDataInfoList, jobjectArray &whitelistedDataInfoList, + jboolean &bindMountAppDataDirs, jboolean &bindMountAppStorageDirs) { + LOGD("hook: %s\n", __FUNCTION__); +} + +static void nativeSpecializeAppProcess_post(JNIEnv *env, jclass clazz) { + LOGD("hook: %s\n", __FUNCTION__); +} + +// ----------------------------------------------------------------- + +static void nativeForkSystemServer_pre( + JNIEnv *env, jclass clazz, uid_t &uid, gid_t &gid, jintArray &gids, jint &debug_flags, + jobjectArray &rlimits, jlong &permittedCapabilities, jlong &effectiveCapabilities) { + LOGD("hook: %s\n", __FUNCTION__); +} + +static void nativeForkSystemServer_post(JNIEnv *env, jclass clazz, jint res) { + LOGD("hook: %s\n", __FUNCTION__); +} + +// ----------------------------------------------------------------- + +#define pre_fork() nativeForkAndSpecialize_pre( \ + env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, \ + se_info, se_name, fdsToClose, fdsToIgnore, is_child_zygote, \ + instructionSet, appDataDir, isTopApp, pkgDataInfoList, whitelistedDataInfoList, \ + bindMountAppDataDirs, bindMountAppStorageDirs) \ + +template +static jint orig_fork(Args && ...args) { + return reinterpret_cast(JNI::Zygote::nativeForkAndSpecialize_orig->fnPtr)(std::forward(args)...); +} + +#define post_fork() nativeForkAndSpecialize_post(env, clazz, uid, pid) + +const static char nativeForkAndSpecialize_m_sig[] = + "(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I"; + +static jint nativeForkAndSpecialize_m( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, + jobjectArray rlimits, jint mount_external, jstring se_info, jstring se_name, + jintArray fdsToClose, jstring instructionSet, jstring appDataDir) { + + jintArray fdsToIgnore = nullptr; + jboolean is_child_zygote = JNI_FALSE; + jboolean isTopApp = JNI_FALSE; + jobjectArray pkgDataInfoList = nullptr; + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + jboolean bindMountAppStorageDirs = JNI_FALSE; + + pre_fork(); + jint pid = orig_fork( + env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, se_name, + fdsToClose, instructionSet, appDataDir); + post_fork(); + return pid; +} + +const static char nativeForkAndSpecialize_o_sig[] = + "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I"; + +static jint nativeForkAndSpecialize_o( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, + jobjectArray rlimits, jint mount_external, jstring se_info, jstring se_name, + jintArray fdsToClose, jintArray fdsToIgnore, jstring instructionSet, jstring appDataDir) { + + jboolean is_child_zygote = JNI_FALSE; + jboolean isTopApp = JNI_FALSE; + jobjectArray pkgDataInfoList = nullptr; + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + jboolean bindMountAppStorageDirs = JNI_FALSE; + + pre_fork(); + jint pid = orig_fork( + env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, se_name, + fdsToClose, fdsToIgnore, instructionSet, appDataDir); + post_fork(); + return pid; +} + +const static char nativeForkAndSpecialize_p_sig[] = + "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I"; + +static jint nativeForkAndSpecialize_p( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, + jobjectArray rlimits, jint mount_external, jstring se_info, jstring se_name, + jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote, + jstring instructionSet, jstring appDataDir) { + + jboolean isTopApp = JNI_FALSE; + jobjectArray pkgDataInfoList = nullptr; + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + jboolean bindMountAppStorageDirs = JNI_FALSE; + + pre_fork(); + jint pid = orig_fork( + env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, se_name, + fdsToClose, fdsToIgnore, is_child_zygote, instructionSet, appDataDir); + post_fork(); + return pid; +} + +const static char nativeForkAndSpecialize_q_alt_sig[] = + "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z)I"; + +static jint nativeForkAndSpecialize_q_alt( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, + jobjectArray rlimits, jint mount_external, jstring se_info, jstring se_name, + jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote, + jstring instructionSet, jstring appDataDir, jboolean isTopApp) { + + jobjectArray pkgDataInfoList = nullptr; + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + jboolean bindMountAppStorageDirs = JNI_FALSE; + + pre_fork(); + jint pid = orig_fork( + env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, se_name, + fdsToClose, fdsToIgnore, is_child_zygote, instructionSet, appDataDir, isTopApp); + post_fork(); + return pid; +} + +#if ENABLE_LEGACY_DP +const static char nativeForkAndSpecialize_r_dp2_sig[] = + "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;)I"; + +static jint nativeForkAndSpecialize_r_dp2( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, + jobjectArray rlimits, jint mount_external, jstring se_info, jstring se_name, + jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote, + jstring instructionSet, jstring appDataDir, jboolean isTopApp, jobjectArray pkgDataInfoList) { + + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + jboolean bindMountAppStorageDirs = JNI_FALSE; + + pre_fork(); + jint pid = orig_fork( + env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, se_name, + fdsToClose, fdsToIgnore, is_child_zygote, instructionSet, appDataDir, isTopApp, + pkgDataInfoList); + post_fork(); + return pid; +} + +const static char nativeForkAndSpecialize_r_dp3_sig[] = + "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;Z)I"; + +static jint nativeForkAndSpecialize_r_dp3( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, + jobjectArray rlimits, jint mount_external, jstring se_info, jstring se_name, + jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote, + jstring instructionSet, jstring appDataDir, jboolean isTopApp, jobjectArray pkgDataInfoList, + jboolean bindMountAppStorageDirs) { + + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + + pre_fork(); + jint pid = orig_fork( + env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, se_name, + fdsToClose, fdsToIgnore, is_child_zygote, instructionSet, appDataDir, isTopApp, + pkgDataInfoList, + bindMountAppStorageDirs); + post_fork(); + return pid; +} +#endif // ENABLE_LEGACY_DP + +const static char *nativeForkAndSpecialize_r_sig = + "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I"; + +static jint nativeForkAndSpecialize_r( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, + jobjectArray rlimits, jint mount_external, jstring se_info, jstring se_name, + jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote, + jstring instructionSet, jstring appDataDir, jboolean isTopApp, jobjectArray pkgDataInfoList, + jobjectArray whitelistedDataInfoList, jboolean bindMountAppDataDirs, jboolean bindMountAppStorageDirs) { + + pre_fork(); + jint pid = orig_fork( + env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, se_name, + fdsToClose, fdsToIgnore, is_child_zygote, instructionSet, appDataDir, isTopApp, + pkgDataInfoList, + whitelistedDataInfoList, bindMountAppDataDirs, bindMountAppStorageDirs); + post_fork(); + return pid; +} + +const static char nativeForkAndSpecialize_samsung_m_sig[] = + "(II[II[[IILjava/lang/String;IILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I"; + +jint nativeForkAndSpecialize_samsung_m( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, + jobjectArray rlimits, jint mount_external, jstring se_info, jint category, jint accessInfo, + jstring se_name, jintArray fdsToClose, jstring instructionSet, jstring appDataDir) { + + jintArray fdsToIgnore = nullptr; + jboolean is_child_zygote = JNI_FALSE; + jboolean isTopApp = JNI_FALSE; + jobjectArray pkgDataInfoList = nullptr; + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + jboolean bindMountAppStorageDirs = JNI_FALSE; + + pre_fork(); + jint pid = orig_fork( + env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, category, + accessInfo, se_name, fdsToClose, instructionSet, appDataDir); + post_fork(); + return pid; +} + +const static char nativeForkAndSpecialize_samsung_n_sig[] = + "(II[II[[IILjava/lang/String;IILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;I)I"; + +static jint nativeForkAndSpecialize_samsung_n( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, + jobjectArray rlimits, jint mount_external, jstring se_info, jint category, jint accessInfo, + jstring se_name, jintArray fdsToClose, jstring instructionSet, jstring appDataDir, + jint a1) { + + jintArray fdsToIgnore = nullptr; + jboolean is_child_zygote = JNI_FALSE; + jboolean isTopApp = JNI_FALSE; + jobjectArray pkgDataInfoList = nullptr; + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + jboolean bindMountAppStorageDirs = JNI_FALSE; + + pre_fork(); + jint pid = orig_fork( + env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, category, + accessInfo, se_name, fdsToClose, instructionSet, appDataDir, a1); + post_fork(); + return pid; +} + +const static char nativeForkAndSpecialize_samsung_o_sig[] = + "(II[II[[IILjava/lang/String;IILjava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I"; + +static jint nativeForkAndSpecialize_samsung_o( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, + jobjectArray rlimits, jint mount_external, jstring se_info, jint category, jint accessInfo, + jstring se_name, jintArray fdsToClose, jintArray fdsToIgnore, jstring instructionSet, + jstring appDataDir) { + + jboolean is_child_zygote = JNI_FALSE; + jboolean isTopApp = JNI_FALSE; + jobjectArray pkgDataInfoList = nullptr; + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + jboolean bindMountAppStorageDirs = JNI_FALSE; + + pre_fork(); + jint pid = orig_fork( + env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, category, + accessInfo, se_name, fdsToClose, fdsToIgnore, instructionSet, appDataDir); + post_fork(); + return pid; +} + +const static char nativeForkAndSpecialize_samsung_p_sig[] = + "(II[II[[IILjava/lang/String;IILjava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I"; + +static jint nativeForkAndSpecialize_samsung_p( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, + jobjectArray rlimits, jint mount_external, jstring se_info, jint category, jint accessInfo, + jstring se_name, jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote, + jstring instructionSet, jstring appDataDir) { + + jboolean isTopApp = JNI_FALSE; + jobjectArray pkgDataInfoList = nullptr; + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + jboolean bindMountAppStorageDirs = JNI_FALSE; + + pre_fork(); + jint pid = orig_fork( + env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, category, + accessInfo, se_name, fdsToClose, fdsToIgnore, is_child_zygote, instructionSet, + appDataDir); + post_fork(); + return pid; +} + +#define DCL_FORK(ver) { \ + "nativeForkAndSpecialize", \ + nativeForkAndSpecialize_##ver##_sig, \ + (void *) &nativeForkAndSpecialize_##ver \ +} + +// ----------------------------------------------------------------- + +#define pre_spec() nativeSpecializeAppProcess_pre( \ + env, clazz, uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, \ + startChildZygote, instructionSet, appDataDir, isTopApp, pkgDataInfoList, \ + whitelistedDataInfoList, bindMountAppDataDirs, bindMountAppStorageDirs) + +template +static void orig_spec(Args && ...args) { + reinterpret_cast(JNI::Zygote::nativeSpecializeAppProcess_orig->fnPtr)(std::forward(args)...); +} + +#define post_spec() nativeSpecializeAppProcess_post(env, clazz) + +const static char nativeSpecializeAppProcess_q_sig[] = + "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V"; + +static void nativeSpecializeAppProcess_q( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtimeFlags, + jobjectArray rlimits, jint mountExternal, jstring seInfo, jstring niceName, + jboolean startChildZygote, jstring instructionSet, jstring appDataDir) { + + jboolean isTopApp = JNI_FALSE; + jobjectArray pkgDataInfoList = nullptr; + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + jboolean bindMountAppStorageDirs = JNI_FALSE; + + pre_spec(); + orig_spec( + env, clazz, uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, + startChildZygote, instructionSet, appDataDir); + post_spec(); +} + +const static char nativeSpecializeAppProcess_q_alt_sig[] = + "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z)V"; + +static void nativeSpecializeAppProcess_q_alt( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtimeFlags, + jobjectArray rlimits, jint mountExternal, jstring seInfo, jstring niceName, + jboolean startChildZygote, jstring instructionSet, jstring appDataDir, + jboolean isTopApp) { + + jobjectArray pkgDataInfoList = nullptr; + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + jboolean bindMountAppStorageDirs = JNI_FALSE; + + pre_spec(); + orig_spec( + env, clazz, uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, + startChildZygote, instructionSet, appDataDir, isTopApp); + post_spec(); +} + +#if ENABLE_LEGACY_DP +const static char nativeSpecializeAppProcess_r_dp2_sig[] = + "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;)V"; + +static void nativeSpecializeAppProcess_r_dp2( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtimeFlags, + jobjectArray rlimits, jint mountExternal, jstring seInfo, jstring niceName, + jboolean startChildZygote, jstring instructionSet, jstring appDataDir, + jboolean isTopApp, jobjectArray pkgDataInfoList) { + + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + jboolean bindMountAppStorageDirs = JNI_FALSE; + + pre_spec(); + orig_spec( + env, clazz, uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, + startChildZygote, instructionSet, appDataDir, isTopApp, pkgDataInfoList); + post_spec(); +} + +const static char nativeSpecializeAppProcess_r_dp3_sig[] = + "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;Z)V"; + +static void nativeSpecializeAppProcess_r_dp3( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtimeFlags, + jobjectArray rlimits, jint mountExternal, jstring seInfo, jstring niceName, + jboolean startChildZygote, jstring instructionSet, jstring appDataDir, + jboolean isTopApp, jobjectArray pkgDataInfoList, jboolean bindMountAppStorageDirs) { + + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + + pre_spec(); + orig_spec( + env, clazz, uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, + startChildZygote, instructionSet, appDataDir, isTopApp, pkgDataInfoList, + bindMountAppStorageDirs); + post_spec(); +} +#endif // ENABLE_LEGACY_DP + +const static char nativeSpecializeAppProcess_r_sig[] = + "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V"; + +static void nativeSpecializeAppProcess_r( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtimeFlags, + jobjectArray rlimits, jint mountExternal, jstring seInfo, jstring niceName, + jboolean startChildZygote, jstring instructionSet, jstring appDataDir, + jboolean isTopApp, jobjectArray pkgDataInfoList, jobjectArray whitelistedDataInfoList, + jboolean bindMountAppDataDirs, jboolean bindMountAppStorageDirs) { + + pre_spec(); + orig_spec( + env, clazz, uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, + startChildZygote, instructionSet, appDataDir, isTopApp, pkgDataInfoList, + whitelistedDataInfoList, bindMountAppDataDirs, bindMountAppStorageDirs); + post_spec(); +} + +const static char nativeSpecializeAppProcess_samsung_q_sig[] = + "(II[II[[IILjava/lang/String;IILjava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V"; + +static void nativeSpecializeAppProcess_samsung_q( + JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtimeFlags, + jobjectArray rlimits, jint mountExternal, jstring seInfo, jint space, jint accessInfo, + jstring niceName, jboolean startChildZygote, jstring instructionSet, jstring appDataDir) { + + jboolean isTopApp = JNI_FALSE; + jobjectArray pkgDataInfoList = nullptr; + jobjectArray whitelistedDataInfoList = nullptr; + jboolean bindMountAppDataDirs = JNI_FALSE; + jboolean bindMountAppStorageDirs = JNI_FALSE; + + pre_spec(); + orig_spec( + env, clazz, uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, space, + accessInfo, niceName, startChildZygote, instructionSet, appDataDir); + post_spec(); +} + +#define DCL_SPEC(ver) { \ + "nativeSpecializeAppProcess", \ + nativeSpecializeAppProcess_##ver##_sig, \ + (void *) &nativeSpecializeAppProcess_##ver \ +} + +// ----------------------------------------------------------------- + +#define pre_server() nativeForkSystemServer_pre( \ + env, clazz, uid, gid, gids, runtimeFlags, rlimits, permittedCapabilities, \ + effectiveCapabilities) + +template +static jint orig_server(Args && ...args) { + return reinterpret_cast(JNI::Zygote::nativeForkSystemServer_orig->fnPtr)(std::forward(args)...); +} + +#define post_server() nativeForkSystemServer_post(env, clazz, pid) + +const static char nativeForkSystemServer_m_sig[] = "(II[II[[IJJ)I"; + +static jint nativeForkSystemServer_m( + JNIEnv *env, jclass clazz, uid_t uid, gid_t gid, jintArray gids, jint runtimeFlags, + jobjectArray rlimits, jlong permittedCapabilities, jlong effectiveCapabilities) { + + pre_server(); + jint pid = orig_server( + env, clazz, uid, gid, gids, runtimeFlags, rlimits, permittedCapabilities, + effectiveCapabilities); + post_server(); + return pid; +} + +const static char nativeForkSystemServer_samsung_q_sig[] = "(II[IIII[[IJJ)I"; + +static jint nativeForkSystemServer_samsung_q( + JNIEnv *env, jclass clazz, uid_t uid, gid_t gid, jintArray gids, jint runtimeFlags, + jint space, jint accessInfo, jobjectArray rlimits, jlong permittedCapabilities, + jlong effectiveCapabilities) { + + pre_server(); + jint pid = orig_server( + env, clazz, uid, gid, gids, runtimeFlags, space, accessInfo, rlimits, + permittedCapabilities, + effectiveCapabilities); + post_server(); + return pid; +} + +#define DCL_SERVER(ver) { \ + "nativeForkSystemServer", \ + nativeForkSystemServer_##ver##_sig, \ + (void *) &nativeForkSystemServer_##ver \ +} + +/* + * On Android 9+, in very rare cases, SystemProperties.set("sys.user." + userId + ".ce_available", "true") + * will throw an exception (no idea if this is caused by hooking) and user data will be wiped. + * Hook it and clear the exception to prevent this problem from happening. + * + * https://cs.android.com/android/platform/superproject/+/android-9.0.0_r34:frameworks/base/services/core/java/com/android/server/pm/UserDataPreparer.java;l=107;bpv=0;bpt=0 + */ +static void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ, jstring valJ) { + const char *key = env->GetStringUTFChars(keyJ, JNI_FALSE); + char user[16]; + bool no_throw = sscanf(key, "sys.user.%[^.].ce_available", user) == 1; + env->ReleaseStringUTFChars(keyJ, key); + + reinterpret_cast + (JNI::SystemProperties::native_set_orig->fnPtr)(env, clazz, keyJ, valJ); + + jthrowable exception = env->ExceptionOccurred(); + if (exception && no_throw) { + LOGW("prevented data destroy"); + + env->ExceptionDescribe(); + env->ExceptionClear(); + } +} + +namespace JNI { + + namespace Zygote { + JNINativeMethod *nativeForkAndSpecialize_orig = nullptr; + JNINativeMethod *nativeSpecializeAppProcess_orig = nullptr; + JNINativeMethod *nativeForkSystemServer_orig = nullptr; + + const JNINativeMethod nativeForkAndSpecialize_methods[] = { + DCL_FORK(m), DCL_FORK(o), DCL_FORK(p), + DCL_FORK(q_alt), DCL_FORK(r), + DCL_FORK(samsung_m), DCL_FORK(samsung_n), + DCL_FORK(samsung_o), DCL_FORK(samsung_p), +#if ENABLE_LEGACY_DP + DCL_FORK(r_dp2), DCL_FORK(r_dp3) +#endif + }; + const int nativeForkAndSpecialize_methods_num = std::size(nativeForkAndSpecialize_methods); + + const JNINativeMethod nativeSpecializeAppProcess_methods[] = { + DCL_SPEC(q), DCL_SPEC(q_alt), + DCL_SPEC(r), DCL_SPEC(samsung_q), +#if ENABLE_LEGACY_DP + DCL_SPEC(r_dp2), DCL_SPEC(r_dp3) +#endif + }; + const int nativeSpecializeAppProcess_methods_num = std::size(nativeSpecializeAppProcess_methods); + + const JNINativeMethod nativeForkSystemServer_methods[] = { + DCL_SERVER(m), DCL_SERVER(samsung_q) + }; + const int nativeForkSystemServer_methods_num = std::size(nativeForkSystemServer_methods); + } + + namespace SystemProperties { + JNINativeMethod *native_set_orig = nullptr; + + const JNINativeMethod native_set_methods[] = {{ + "native_set", + "(Ljava/lang/String;Ljava/lang/String;)V", + (void *) &SystemProperties_set + }}; + } +}