diff --git a/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPApplicationService.aidl b/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPApplicationService.aidl index e1e63af3..18451ad0 100644 --- a/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPApplicationService.aidl +++ b/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPApplicationService.aidl @@ -3,7 +3,9 @@ package io.github.lsposed.lspd.service; interface ILSPApplicationService { void registerHeartBeat(IBinder handle) = 1; - IBinder requestModuleBinder() = 2; + int getVariant() = 2; - IBinder requestManagerBinder() = 3; + IBinder requestModuleBinder() = 3; + + IBinder requestManagerBinder() = 4; } diff --git a/core/src/main/cpp/main/include/JNIHelper.h b/core/src/main/cpp/main/include/JNIHelper.h index 1669ff6f..65bdf7a4 100644 --- a/core/src/main/cpp/main/include/JNIHelper.h +++ b/core/src/main/cpp/main/include/JNIHelper.h @@ -87,6 +87,10 @@ ALWAYS_INLINE static int ClearException(JNIEnv *env) { env->CallVoidMethod(obj, __VA_ARGS__); \ if (ClearException(env)) LOGE("CallVoidMethod " #obj " " #__VA_ARGS__) +#define JNI_CallBooleanMethod(env, obj, ...) \ + env->CallBooleanMethod(obj, __VA_ARGS__); \ + if (ClearException(env)) LOGE("CallVoidMethod " #obj " " #__VA_ARGS__) + #define JNI_GetStaticFieldID(env, class, name, sig) \ env->GetStaticFieldID(class, name, sig); \ if (ClearException(env)) LOGE("GetStaticFieldID " #name " " #sig) diff --git a/core/src/main/cpp/main/src/context.cpp b/core/src/main/cpp/main/src/context.cpp index 10795b91..6fa3cece 100644 --- a/core/src/main/cpp/main/src/context.cpp +++ b/core/src/main/cpp/main/src/context.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include "JNIHelper.h" #include "jni/config_manager.h" #include "jni/art_class_linker.h" #include "jni/yahfa.h" @@ -133,23 +133,23 @@ namespace lspd { RegisterPendingHooks(env); RegisterNativeAPI(env); - variant_ = Variant(ConfigManager::GetInstance()->GetVariant()); - LOGI("LSP Variant: %d", variant_); - - if (variant_ == SANDHOOK) { - //for SandHook variant - ScopedLocalRef sandhook_class(env, FindClassFromCurrentLoader(env, kSandHookClassName)); - ScopedLocalRef nevercall_class(env, - FindClassFromCurrentLoader(env, - kSandHookNeverCallClassName)); - if (sandhook_class == nullptr || nevercall_class == nullptr) { // fail-fast - return; - } - if (!JNI_Load_Ex(env, sandhook_class.get(), nevercall_class.get())) { - LOGE("SandHook: HookEntry class error. %d", getpid()); - } - - } +// variant_ = Variant(ConfigManager::GetInstance()->GetVariant()); +// LOGI("LSP Variant: %d", variant_); +// +// if (variant_ == SANDHOOK) { +// //for SandHook variant +// ScopedLocalRef sandhook_class(env, FindClassFromCurrentLoader(env, kSandHookClassName)); +// ScopedLocalRef nevercall_class(env, +// FindClassFromCurrentLoader(env, +// kSandHookNeverCallClassName)); +// if (sandhook_class == nullptr || nevercall_class == nullptr) { // fail-fast +// return; +// } +// if (!JNI_Load_Ex(env, sandhook_class.get(), nevercall_class.get())) { +// LOGE("SandHook: HookEntry class error. %d", getpid()); +// } +// +// } } jclass @@ -202,108 +202,33 @@ namespace lspd { [[maybe_unused]] jobjectArray rlimits, [[maybe_unused]] jlong permitted_capabilities, [[maybe_unused]] jlong effective_capabilities) { - ConfigManager::SetCurrentUser(0u); - app_modules_list_ = ConfigManager::GetInstance()->GetAppModuleList( - "android"); // I don't think we need this, but anyway - skip_ = false; - if (!ConfigManager::GetInstance()->IsInitialized()) { - LOGE("skip injecting into android because configurations are not loaded properly"); - } - if (!skip_ && app_modules_list_.empty()) { - skip_ = true; - LOGD("skip injecting into android because no module hooks it"); - } + Service::instance()->InitService(env); PreLoadDex(ConfigManager::GetInjectDexPath()); - ConfigManager::GetInstance()->EnsurePermission("android", 1000); + skip_ = false; } - int - Context::OnNativeForkSystemServerPost(JNIEnv *env, [[maybe_unused]] jclass clazz, jint res) { - if (res == 0) { - if (!skip_) { - if (void *buf = mmap(nullptr, 1, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_ANONYMOUS | MAP_PRIVATE, -1, - 0); - buf == MAP_FAILED) { - skip_ = true; - LOGE("skip injecting into android because sepolicy was not loaded properly"); - } else { - munmap(buf, 1); - } + void + Context::OnNativeForkSystemServerPost(JNIEnv *env, jint res) { + if (res != 0) return; + auto binder = Service::instance()->RequestBinder(env); + if (binder) { + if (void *buf = mmap(nullptr, 1, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_ANONYMOUS | MAP_PRIVATE, -1, + 0); + buf == MAP_FAILED) { + skip_ = true; + LOGE("skip injecting into android because sepolicy was not loaded properly"); + } else { + munmap(buf, 1); } - LoadDex(env); - if (!skip_) { - InstallInlineHooks(); - Init(env); - // only do work in child since FindAndCall would print log - FindAndCall(env, "forkSystemServerPost", "(II)V", res, - variant_); - } - InitService(*this, env); - } else { - // in zygote process, res is child zygote pid - // don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66 } - return 0; - } - - std::tuple - Context::GetAppInfoFromDir(JNIEnv *env, jstring dir, jstring nice_name) { - uid_t uid = 0; - JUTFString app_data_dir(env, dir); - JUTFString name(env, nice_name); - if (!app_data_dir) return {false, 0, name.get()}; - fs::path path(app_data_dir.get()); - std::vector splits(path.begin(), path.end()); - if (splits.size() < 5u) { - LOGE("can't parse %s", path.c_str()); - return {false, 0, name.get()}; + LoadDex(env); + if (!skip_ && binder) { + InstallInlineHooks(); + Init(env); + FindAndCall(env, "forkSystemServerPost", "(Landroid/os/IBinder;)V", binder); } - const auto &uid_str = splits[3]; - const auto &package_name = splits[4]; - try { - uid = stol(uid_str); - } catch (const std::invalid_argument &ignored) { - LOGE("can't parse %s", app_data_dir.get()); - return {false, uid, {}}; - } - return {true, uid, package_name}; - } - - bool Context::ShouldSkipInject(const std::string &package_name, uid_t user, uid_t uid, - bool info_res, - const std::function &empty_list, - bool is_child_zygote) { - const auto app_id = uid % PER_USER_RANGE; - bool skip = false; - if (!ConfigManager::GetInstance()->IsInitialized()) { - LOGE("skip injecting into %s because configurations are not loaded properly", - package_name.c_str()); - skip = true; - } - if (!skip && !info_res) { - LOGD("skip injecting into %s because it has no data dir", package_name.c_str()); - skip = true; - } - if (!skip && is_child_zygote) { - skip = true; - LOGD("skip injecting into %s because it's a child zygote", package_name.c_str()); - } - - if (!skip && ((app_id >= FIRST_ISOLATED_UID && app_id <= LAST_ISOLATED_UID) || - (app_id >= FIRST_APP_ZYGOTE_ISOLATED_UID && - app_id <= LAST_APP_ZYGOTE_ISOLATED_UID) || - app_id == SHARED_RELRO_UID)) { - skip = true; - LOGI("skip injecting into %s because it's isolated", package_name.c_str()); - } - - if (!skip && empty_list() && !ConfigManager::GetInstance()->IsInstaller(package_name)) { - skip = true; - LOGD("skip injecting xposed into %s because no module hooks it", - package_name.c_str()); - } - return skip; + Service::instance()->HookBridge(*this, env); } void Context::OnNativeForkAndSpecializePre(JNIEnv *env, jclass clazz, @@ -319,50 +244,50 @@ namespace lspd { jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) { - const auto&[res, user, package_name] = GetAppInfoFromDir(env, app_data_dir, nice_name); - app_data_dir_ = app_data_dir; - nice_name_ = nice_name; - ConfigManager::SetCurrentUser(user); - skip_ = ShouldSkipInject(package_name, user, uid, res, - // Only obtains when needed - [this, &package_name = package_name]() { - app_modules_list_ = ConfigManager::GetInstance()->GetAppModuleList( - package_name); - return app_modules_list_.empty(); - }, is_child_zygote); - if (!skip_) { - ConfigManager::GetInstance()->EnsurePermission(package_name, uid); - PreLoadDex(ConfigManager::GetInjectDexPath()); + PreLoadDex(ConfigManager::GetInjectDexPath()); + Service::instance()->InitService(env); + this->uid = uid; + const auto app_id = uid % PER_USER_RANGE; + skip_ = false; + if (!skip_ && !app_data_dir) { + LOGD("skip injecting into %d because it has no data dir", uid); + skip_ = true; + } + if (!skip_ && is_child_zygote) { + skip_ = true; + LOGD("skip injecting into %d because it's a child zygote", uid); + } + + if (!skip_ && ((app_id >= FIRST_ISOLATED_UID && app_id <= LAST_ISOLATED_UID) || + (app_id >= FIRST_APP_ZYGOTE_ISOLATED_UID && + app_id <= LAST_APP_ZYGOTE_ISOLATED_UID) || + app_id == SHARED_RELRO_UID)) { + skip_ = true; + LOGI("skip injecting into %d because it's isolated", uid); } } - int - Context::OnNativeForkAndSpecializePost(JNIEnv *env, [[maybe_unused]]jclass clazz, jint res) { - if (res == 0) { - const JUTFString process_name(env, nice_name_); - if (!skip_) { - LoadDex(env); - InstallInlineHooks(); - Init(env); - LOGD("Done prepare"); - FindAndCall(env, "forkAndSpecializePost", - "(ILjava/lang/String;Ljava/lang/String;I)V", - res, app_data_dir_, nice_name_, - variant_); - LOGD("injected xposed into %s", process_name.get()); - } else { - [[maybe_unused]] auto config_manager = ConfigManager::ReleaseInstances(); - auto context = Context::ReleaseInstance(); - LOGD("skipped %s", process_name.get()); - } + void + Context::OnNativeForkAndSpecializePost(JNIEnv *env) { + const JUTFString process_name(env, nice_name_); + auto binder = skip_? nullptr : Service::instance()->RequestBinder(env); + if (binder) { + LoadDex(env); + InstallInlineHooks(); + Init(env); + LOGD("Done prepare"); + // TODO: cache this method + FindAndCall(env, "forkAndSpecializePost", + "(Ljava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V", + app_data_dir_, nice_name_, + binder); + LOGD("injected xposed into %s", process_name.get()); } else { - // in zygote process, res is child zygote pid - // don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66 + auto context = Context::ReleaseInstance(); + auto service = Service::ReleaseInstance(); + LOGD("skipped %s", process_name.get()); } - return 0; } - - } #pragma clang diagnostic pop \ No newline at end of file diff --git a/core/src/main/cpp/main/src/context.h b/core/src/main/cpp/main/src/context.h index 5f603525..82baf21b 100644 --- a/core/src/main/cpp/main/src/context.h +++ b/core/src/main/cpp/main/src/context.h @@ -39,9 +39,6 @@ namespace lspd { public: inline static Context *GetInstance() { - if (!instance_) { - instance_ = std::make_unique(); - } return instance_.get(); } @@ -55,18 +52,6 @@ namespace lspd { void FindAndCall(JNIEnv *env, const char *method_name, const char *method_sig, ...) const; - inline auto *GetJavaVM() const { return vm_; } - - inline void SetAppDataDir(jstring app_data_dir) { app_data_dir_ = app_data_dir; } - - inline void SetNiceName(jstring nice_name) { nice_name_ = nice_name; } - - inline auto GetAppDataDir() const { return app_data_dir_; } - - inline auto GetNiceName() const { return nice_name_; } - - inline auto GetAppModulesList() const { return app_modules_list_; } - inline jclass FindClassFromCurrentLoader(JNIEnv *env, std::string_view className) const { return FindClassFromLoader(env, GetCurrentClassLoader(), className); }; @@ -78,20 +63,18 @@ namespace lspd { jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir); - int OnNativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res); + void OnNativeForkAndSpecializePost(JNIEnv *env); - int OnNativeForkSystemServerPost(JNIEnv *env, jclass clazz, jint res); + void OnNativeForkSystemServerPost(JNIEnv *env, jint res); void OnNativeForkSystemServerPre(JNIEnv *env, jclass clazz, uid_t uid, gid_t gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities); - inline auto GetVariant() const { return variant_; }; private: - inline static std::unique_ptr instance_; - Variant variant_ = NONE; + inline static std::unique_ptr instance_ = std::make_unique(); jobject inject_class_loader_ = nullptr; jclass entry_class_ = nullptr; jstring app_data_dir_ = nullptr; @@ -99,9 +82,9 @@ namespace lspd { JavaVM *vm_ = nullptr; jclass class_linker_class_ = nullptr; jmethodID post_fixup_static_mid_ = nullptr; + jint uid = -1; bool skip_ = false; std::vector dex{}; - std::vector app_modules_list_{}; Context() {} @@ -114,14 +97,6 @@ namespace lspd { static jclass FindClassFromLoader(JNIEnv *env, jobject class_loader, std::string_view class_name); - static bool - ShouldSkipInject(const std::string &package_name, uid_t user, uid_t uid, bool res, - const std::function &empty_list, - bool is_child_zygote); - - static std::tuple - GetAppInfoFromDir(JNIEnv *env, jstring dir, jstring nice_name); - friend std::unique_ptr std::make_unique(); }; diff --git a/core/src/main/cpp/main/src/jni/config_manager.cpp b/core/src/main/cpp/main/src/jni/config_manager.cpp index 484e9951..590e58ae 100644 --- a/core/src/main/cpp/main/src/jni/config_manager.cpp +++ b/core/src/main/cpp/main/src/jni/config_manager.cpp @@ -74,13 +74,7 @@ namespace lspd { } LSP_DEF_NATIVE_METHOD(jstring, ConfigManager, getModulesList) { - auto module_list = Context::GetInstance()->GetAppModulesList(); - std::ostringstream join; - std::copy(module_list.begin(), module_list.end(), - std::ostream_iterator(join, "\n")); - const auto &list = join.str(); - LOGD("module list: %s", list.c_str()); - return env->NewStringUTF(list.c_str()); + return nullptr; } LSP_DEF_NATIVE_METHOD(jboolean, ConfigManager, isPermissive) { diff --git a/core/src/main/cpp/main/src/main.cpp b/core/src/main/cpp/main/src/main.cpp index f63f79cd..9f236522 100644 --- a/core/src/main/cpp/main/src/main.cpp +++ b/core/src/main/cpp/main/src/main.cpp @@ -29,9 +29,6 @@ #include "config_manager.h" #include "symbol_cache.h" -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-value" - namespace lspd { static void onModuleLoaded() { LOGI("onModuleLoaded: welcome to LSPosed!"); @@ -63,7 +60,7 @@ namespace lspd { } static void nativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) { - Context::GetInstance()->OnNativeForkAndSpecializePost(env, clazz, res); + Context::GetInstance()->OnNativeForkAndSpecializePost(env); } static void nativeForkSystemServerPre(JNIEnv *env, jclass clazz, uid_t *uid, gid_t *gid, @@ -77,8 +74,8 @@ namespace lspd { ); } - static void nativeForkSystemServerPost(JNIEnv *env, jclass clazz, jint res) { - Context::GetInstance()->OnNativeForkSystemServerPost(env, clazz, res); + static void nativeForkSystemServerPost(JNIEnv *env, [[maybe_unused]] jclass clazz, jint res) { + Context::GetInstance()->OnNativeForkSystemServerPost(env, res); } /* method added in Android Q */ @@ -97,13 +94,12 @@ namespace lspd { *app_data_dir); } - static void specializeAppProcessPost(JNIEnv *env, jclass clazz) { - Context::GetInstance()->OnNativeForkAndSpecializePost(env, clazz, 0); + static void specializeAppProcessPost(JNIEnv *env, [[maybe_unused]] jclass clazz) { + Context::GetInstance()->OnNativeForkAndSpecializePost(env); } } int riru_api_version; -RiruApiV10 *riru_api_v10; RIRU_EXPORT void *init(void *arg) { static int step = 0; @@ -122,8 +118,6 @@ RIRU_EXPORT void *init(void *arg) { case 10: [[fallthrough]]; case 9: { - riru_api_v10 = (RiruApiV10 *) arg; - auto module = (RiruModuleInfoV10 *) malloc(sizeof(RiruModuleInfoV10)); memset(module, 0, sizeof(RiruModuleInfoV10)); _module = module; @@ -156,5 +150,3 @@ RIRU_EXPORT void *init(void *arg) { } } } - -#pragma clang diagnostic pop \ No newline at end of file diff --git a/core/src/main/cpp/main/src/service.cpp b/core/src/main/cpp/main/src/service.cpp index d093613c..678a1feb 100644 --- a/core/src/main/cpp/main/src/service.cpp +++ b/core/src/main/cpp/main/src/service.cpp @@ -3,27 +3,14 @@ // #include -#include #include +#include "base/object.h" #include "service.h" #include "context.h" +#include "JNIHelper.h" namespace lspd { - - namespace { - constexpr uint32_t BRIDGE_TRANSACTION_CODE = 1598837584; - - JNINativeInterface native_interface_replace{}; - jmethodID exec_transact_backup_methodID = nullptr; - - jboolean (*call_boolean_method_va_backup)(JNIEnv *env, jobject obj, jmethodID methodId, - va_list args) = nullptr; - - jclass bridge_service_class = nullptr; - jmethodID exec_transact_replace_methodID = nullptr; - } - - static bool exec_transact_replace(jboolean *res, JNIEnv *env, jobject obj, va_list args) { + jboolean Service::exec_transact_replace(jboolean *res, JNIEnv *env, [[maybe_unused]] jobject obj, va_list args) { jint code; va_list copy; @@ -32,8 +19,8 @@ namespace lspd { va_end(copy); if (UNLIKELY(code == BRIDGE_TRANSACTION_CODE)) { - *res = env->CallStaticBooleanMethodV(bridge_service_class, - exec_transact_replace_methodID, + *res = env->CallStaticBooleanMethodV(instance()->bridge_service_class_, + instance()->exec_transact_replace_methodID_, args); return true; } @@ -41,50 +28,139 @@ namespace lspd { return false; } - static jboolean - call_boolean_method_va_replace(JNIEnv *env, jobject obj, jmethodID methodId, va_list args) { - if (UNLIKELY(methodId == exec_transact_backup_methodID)) { + jboolean + Service::call_boolean_method_va_replace(JNIEnv *env, jobject obj, jmethodID methodId, + va_list args) { + if (UNLIKELY(methodId == instance()->exec_transact_backup_methodID_)) { jboolean res = false; if (LIKELY(exec_transact_replace(&res, env, obj, args))) return res; // else fallback to backup } - return call_boolean_method_va_backup(env, obj, methodId, args); + return instance()->call_boolean_method_va_backup_(env, obj, methodId, args); } - void InitService(const Context &context, JNIEnv *env) { - bridge_service_class = context.FindClassFromCurrentLoader(env, kBridgeServiceClassName); - if (!bridge_service_class) { + void Service::InitService(JNIEnv *env) { + if (LIKELY(initialized_)) return; + initialized_ = true; + + // ServiceManager + serviceManagerClass_ = env->FindClass("android/os/ServiceManager"); + if (serviceManagerClass_) { + serviceManagerClass_ = (jclass) env->NewGlobalRef(serviceManagerClass_); + } else { + env->ExceptionClear(); + return; + } + getServiceMethod_ = env->GetStaticMethodID(serviceManagerClass_, "getService", + "(Ljava/lang/String;)Landroid/os/IBinder;"); + if (!getServiceMethod_) { + env->ExceptionClear(); + return; + } + + // IBinder + jclass iBinderClass = env->FindClass("android/os/IBinder"); + transactMethod_ = env->GetMethodID(iBinderClass, "transact", + "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z"); + + // Parcel + parcelClass_ = env->FindClass("android/os/Parcel"); + if (parcelClass_) parcelClass_ = (jclass) env->NewGlobalRef(parcelClass_); + obtainMethod_ = env->GetStaticMethodID(parcelClass_, "obtain", "()Landroid/os/Parcel;"); + recycleMethod_ = env->GetMethodID(parcelClass_, "recycle", "()V"); + writeInterfaceTokenMethod_ = env->GetMethodID(parcelClass_, "writeInterfaceToken", + "(Ljava/lang/String;)V"); + writeIntMethod_ = env->GetMethodID(parcelClass_, "writeInt", "(I)V"); + writeStringMethod_ = env->GetMethodID(parcelClass_, "writeString", "(Ljava/lang/String;)V"); + readExceptionMethod_ = env->GetMethodID(parcelClass_, "readException", "()V"); + readStrongBinderMethod_ = env->GetMethodID(parcelClass_, "readStrongBinder", + "()Landroid/os/IBinder;"); + createStringArray_ = env->GetMethodID(parcelClass_, "createStringArray", + "()[Ljava/lang/String;"); + + deadObjectExceptionClass_ = env->FindClass("android/os/DeadObjectException"); + if (deadObjectExceptionClass_) + deadObjectExceptionClass_ = (jclass) env->NewGlobalRef(deadObjectExceptionClass_); + } + + void Service::HookBridge(const Context &context, JNIEnv *env) { + static bool hooked = false; + // This should only be ran once, so unlikely + if (UNLIKELY(hooked)) return; + hooked = true; + bridge_service_class_ = context.FindClassFromCurrentLoader(env, kBridgeServiceClassName); + if (!bridge_service_class_) { LOGE("server class not found"); return; } - bridge_service_class = (jclass) env->NewGlobalRef(bridge_service_class); - exec_transact_replace_methodID = env->GetStaticMethodID(bridge_service_class, - "execTransact", - "(IJJI)Z"); - if (!exec_transact_replace_methodID) { + bridge_service_class_ = (jclass) env->NewGlobalRef(bridge_service_class_); + exec_transact_replace_methodID_ = env->GetStaticMethodID(bridge_service_class_, + "execTransact", + "(IJJI)Z"); + if (!exec_transact_replace_methodID_) { LOGE("execTransact class not found"); return; } ScopedLocalRef binderClass(env, env->FindClass("android/os/Binder")); - exec_transact_backup_methodID = env->GetMethodID(binderClass.get(), "execTransact", - "(IJJI)Z"); + exec_transact_backup_methodID_ = env->GetMethodID(binderClass.get(), "execTransact", + "(IJJI)Z"); auto set_table_override = reinterpret_cast(DobbySymbolResolver(nullptr, "_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface")); if (!set_table_override) { LOGE("set table override not found"); } - memcpy(&native_interface_replace, env->functions, sizeof(JNINativeInterface)); + memcpy(&native_interface_replace_, env->functions, sizeof(JNINativeInterface)); - call_boolean_method_va_backup = env->functions->CallBooleanMethodV; - native_interface_replace.CallBooleanMethodV = &call_boolean_method_va_replace; + call_boolean_method_va_backup_ = env->functions->CallBooleanMethodV; + native_interface_replace_.CallBooleanMethodV = &call_boolean_method_va_replace; if (set_table_override != nullptr) { - set_table_override(&native_interface_replace); + set_table_override(&native_interface_replace_); } LOGD("Done InitService"); } + jobject Service::RequestBinder(JNIEnv *env) { + if (UNLIKELY(!initialized_)) { + LOGE("Service not initialized"); + return nullptr; + } + + + auto bridgeServiceName = env->NewStringUTF(BRIDGE_SERVICE_NAME.data()); + auto bridgeService = JNI_CallStaticObjectMethod(env, serviceManagerClass_, + getServiceMethod_, bridgeServiceName); + if (!bridgeService) { + LOGD("can't get %s", BRIDGE_SERVICE_NAME.data()); + return nullptr; + } + + auto data = JNI_CallStaticObjectMethod(env, parcelClass_, obtainMethod_); + auto reply = JNI_CallStaticObjectMethod(env, parcelClass_, obtainMethod_); + + auto descriptor = env->NewStringUTF(BRIDGE_SERVICE_DESCRIPTOR.data()); + JNI_CallVoidMethod(env, data, writeInterfaceTokenMethod_, descriptor); + JNI_CallVoidMethod(env, data, writeIntMethod_, BRIDGE_ACTION_GET_BINDER); + + auto res = JNI_CallBooleanMethod(env, bridgeService, transactMethod_, + BRIDGE_TRANSACTION_CODE, + data, + reply, 0); + + jobject service = nullptr; + if (res) { + JNI_CallVoidMethod(env, reply, readExceptionMethod_); + service = JNI_CallObjectMethod(env, reply, readStrongBinderMethod_); + } else { + LOGD("no reply"); + } + + JNI_CallVoidMethod(env, data, recycleMethod_); + JNI_CallObjectMethod(env, reply, recycleMethod_); + + return service; + } } \ No newline at end of file diff --git a/core/src/main/cpp/main/src/service.h b/core/src/main/cpp/main/src/service.h index 2c731ead..b7447847 100644 --- a/core/src/main/cpp/main/src/service.h +++ b/core/src/main/cpp/main/src/service.h @@ -8,8 +8,72 @@ #include #include "context.h" +using namespace std::literals::string_view_literals; + namespace lspd { - void InitService(const Context & context, JNIEnv *env); + class Service { + constexpr static jint BRIDGE_TRANSACTION_CODE = 1598837584; + constexpr static auto BRIDGE_SERVICE_DESCRIPTOR = "android.app.IActivityManager"sv; + constexpr static auto BRIDGE_SERVICE_NAME = "activity"sv; + constexpr static jint BRIDGE_ACTION_GET_BINDER = 2; + + public: + inline static Service* instance() { + return instance_.get(); + } + + inline static std::unique_ptr ReleaseInstance() { + return std::move(instance_); + } + + void InitService(JNIEnv *env); + + void HookBridge(const Context& context, JNIEnv *env); + + jobject RequestBinder(JNIEnv *env); + + private: + inline static std::unique_ptr instance_ = std::make_unique(); + bool initialized_ = false; + + Service() { + + } + + static jboolean + call_boolean_method_va_replace(JNIEnv *env, jobject obj, jmethodID methodId, va_list args); + + static jboolean exec_transact_replace(jboolean *res, JNIEnv *env, jobject obj, va_list args); + + JNINativeInterface native_interface_replace_{}; + jmethodID exec_transact_backup_methodID_ = nullptr; + + jboolean (*call_boolean_method_va_backup_)(JNIEnv *env, jobject obj, jmethodID methodId, + va_list args) = nullptr; + + jclass bridge_service_class_ = nullptr; + jmethodID exec_transact_replace_methodID_ = nullptr; + + jclass serviceManagerClass_ = nullptr; + jmethodID getServiceMethod_ = nullptr; + + jmethodID transactMethod_ = nullptr; + + jclass parcelClass_ = nullptr; + jmethodID obtainMethod_ = nullptr; + jmethodID recycleMethod_ = nullptr; + jmethodID writeInterfaceTokenMethod_ = nullptr; + jmethodID writeIntMethod_ = nullptr; + jmethodID writeStringMethod_ = nullptr; + jmethodID readExceptionMethod_ = nullptr; + jmethodID readStrongBinderMethod_ = nullptr; + jmethodID createStringArray_ = nullptr; + + jclass deadObjectExceptionClass_ = nullptr; + + friend std::unique_ptr std::make_unique(); + + }; } diff --git a/core/src/main/java/io/github/lsposed/lspd/core/Main.java b/core/src/main/java/io/github/lsposed/lspd/core/Main.java index ab7ff5b7..a4d1c413 100644 --- a/core/src/main/java/io/github/lsposed/lspd/core/Main.java +++ b/core/src/main/java/io/github/lsposed/lspd/core/Main.java @@ -22,10 +22,14 @@ package io.github.lsposed.lspd.core; import android.annotation.SuppressLint; import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; import android.util.Log; import android.ddm.DdmHandleAppName; import io.github.lsposed.common.KeepAll; +import io.github.lsposed.lspd.service.ILSPApplicationService; import io.github.lsposed.lspd.service.ServiceManager; import io.github.lsposed.lspd.util.Utils; @@ -36,43 +40,41 @@ import static io.github.lsposed.lspd.service.ServiceManager.TAG; @SuppressLint("DefaultLocale") public class Main implements KeepAll { private static final AtomicReference lspdImplRef = new AtomicReference<>(null); + private static final Binder heartBeatBinder = new Binder(); - /////////////////////////////////////////////////////////////////////////////////////////////// - // entry points - /////////////////////////////////////////////////////////////////////////////////////////////// - - public static void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags, - int[][] rlimits, int mountExternal, String seInfo, - String niceName, int[] fdsToClose, int[] fdsToIgnore, - boolean startChildZygote, String instructionSet, - String appDataDir) { - // won't be loaded - } - - public static void forkAndSpecializePost(int pid, String appDataDir, String niceName, int variant) { + public static void forkAndSpecializePost(String appDataDir, String niceName, IBinder binder) { + ILSPApplicationService service = ILSPApplicationService.Stub.asInterface(binder); + final int variant; + try { + service.registerHeartBeat(heartBeatBinder); + variant = service.getVariant(); + } catch (RemoteException e) { + Utils.logW("Register fail", e); + return; + } EdxpImpl lspd = getEdxpImpl(variant); if (lspd == null || !lspd.isInitialized()) { Utils.logE("Not started up"); return; } - if (pid == 0) { - lspd.getNormalProxy().forkAndSpecializePost(pid, appDataDir, niceName); + lspd.getNormalProxy().forkAndSpecializePost(appDataDir, niceName); + } + + public static void forkSystemServerPost(IBinder binder) { + ILSPApplicationService service = ILSPApplicationService.Stub.asInterface(binder); + final int variant; + try { + service.registerHeartBeat(heartBeatBinder); + variant = service.getVariant(); + } catch (RemoteException e) { + Utils.logW("Register fail", e); + return; } - } - - public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, - long permittedCapabilities, long effectiveCapabilities) { - // Won't load - } - - public static void forkSystemServerPost(int pid, int variant) { EdxpImpl lspd = getEdxpImpl(variant); if (lspd == null || !lspd.isInitialized()) { return; } - if (pid == 0) { - lspd.getNormalProxy().forkSystemServerPost(pid); - } + lspd.getNormalProxy().forkSystemServerPost(); } public static synchronized boolean setEdxpImpl(EdxpImpl lspd) { diff --git a/core/src/main/java/io/github/lsposed/lspd/core/Proxy.java b/core/src/main/java/io/github/lsposed/lspd/core/Proxy.java index 37ee6e17..9337ba6f 100644 --- a/core/src/main/java/io/github/lsposed/lspd/core/Proxy.java +++ b/core/src/main/java/io/github/lsposed/lspd/core/Proxy.java @@ -26,16 +26,6 @@ public interface Proxy extends KeepAll { boolean init(); - void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags, - int[][] rlimits, int mountExternal, String seInfo, - String niceName, int[] fdsToClose, int[] fdsToIgnore, - boolean startChildZygote, String instructionSet, - String appDataDir); - - void forkAndSpecializePost(int pid, String appDataDir, String niceName); - - void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, - long permittedCapabilities, long effectiveCapabilities); - - void forkSystemServerPost(int pid); + void forkAndSpecializePost(String appDataDir, String niceName); + void forkSystemServerPost(); } diff --git a/core/src/main/java/io/github/lsposed/lspd/proxy/NormalProxy.java b/core/src/main/java/io/github/lsposed/lspd/proxy/NormalProxy.java index d44039a8..ddf91bf2 100644 --- a/core/src/main/java/io/github/lsposed/lspd/proxy/NormalProxy.java +++ b/core/src/main/java/io/github/lsposed/lspd/proxy/NormalProxy.java @@ -35,28 +35,17 @@ public class NormalProxy extends BaseProxy { super(router); } - public void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags, - int[][] rlimits, int mountExternal, String seInfo, - String niceName, int[] fdsToClose, int[] fdsToIgnore, - boolean startChildZygote, String instructionSet, - String appDataDir) { + public void forkAndSpecializePost(String appDataDir, String niceName) { + forkPostCommon(false, appDataDir, niceName); } - public void forkAndSpecializePost(int pid, String appDataDir, String niceName) { - forkPostCommon(pid, false, appDataDir, niceName); - } - - public void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, - long permittedCapabilities, long effectiveCapabilities) { - } - - public void forkSystemServerPost(int pid) { - forkPostCommon(pid, true, + public void forkSystemServerPost() { + forkPostCommon(true, getDataPathPrefix() + "android", "system_server"); } - private void forkPostCommon(int pid, boolean isSystem, String appDataDir, String niceName) { + private void forkPostCommon(boolean isSystem, String appDataDir, String niceName) { SELinuxHelper.initOnce(); mRouter.initResourcesHook(); mRouter.prepare(isSystem); diff --git a/core/src/main/java/io/github/lsposed/lspd/service/LSPApplicationService.java b/core/src/main/java/io/github/lsposed/lspd/service/LSPApplicationService.java index 2f60945d..069f3d4d 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/LSPApplicationService.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/LSPApplicationService.java @@ -29,23 +29,32 @@ public class LSPApplicationService extends ILSPApplicationService.Stub { }, 0); } + @Override + public int getVariant() throws RemoteException { + ensureRegistered(); + return ConfigManager.getInstance().variant(); + } + // TODO: check if module @Override - public IBinder requestModuleBinder() { - if (!hasRegister(Binder.getCallingUid(), Binder.getCallingPid())) - return null; + public IBinder requestModuleBinder() throws RemoteException { + ensureRegistered(); return ServiceManager.getModuleService(); } // TODO: check if manager @Override - public IBinder requestManagerBinder() { - if (!hasRegister(Binder.getCallingUid(), Binder.getCallingPid())) - return null; + public IBinder requestManagerBinder() throws RemoteException { + ensureRegistered(); return ServiceManager.getManagerService(); } public boolean hasRegister(int uid, int pid) { return cache.contains(new Pair<>(uid, pid)); } + + private void ensureRegistered() throws RemoteException { + if (!hasRegister(Binder.getCallingUid(), Binder.getCallingPid())) + throw new RemoteException("Not registered"); + } }