[core] Native request binder

This commit is contained in:
LoveSy 2021-02-16 03:18:15 +08:00 committed by tehcneko
parent c65af4c4b5
commit 0ec8959235
12 changed files with 322 additions and 300 deletions

View File

@ -3,7 +3,9 @@ package io.github.lsposed.lspd.service;
interface ILSPApplicationService { interface ILSPApplicationService {
void registerHeartBeat(IBinder handle) = 1; void registerHeartBeat(IBinder handle) = 1;
IBinder requestModuleBinder() = 2; int getVariant() = 2;
IBinder requestManagerBinder() = 3; IBinder requestModuleBinder() = 3;
IBinder requestManagerBinder() = 4;
} }

View File

@ -87,6 +87,10 @@ ALWAYS_INLINE static int ClearException(JNIEnv *env) {
env->CallVoidMethod(obj, __VA_ARGS__); \ env->CallVoidMethod(obj, __VA_ARGS__); \
if (ClearException(env)) LOGE("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) \ #define JNI_GetStaticFieldID(env, class, name, sig) \
env->GetStaticFieldID(class, name, sig); \ env->GetStaticFieldID(class, name, sig); \
if (ClearException(env)) LOGE("GetStaticFieldID " #name " " #sig) if (ClearException(env)) LOGE("GetStaticFieldID " #name " " #sig)

View File

@ -20,7 +20,7 @@
#include <jni.h> #include <jni.h>
#include <android-base/macros.h> #include <android-base/macros.h>
#include <JNIHelper.h> #include "JNIHelper.h"
#include "jni/config_manager.h" #include "jni/config_manager.h"
#include "jni/art_class_linker.h" #include "jni/art_class_linker.h"
#include "jni/yahfa.h" #include "jni/yahfa.h"
@ -133,23 +133,23 @@ namespace lspd {
RegisterPendingHooks(env); RegisterPendingHooks(env);
RegisterNativeAPI(env); RegisterNativeAPI(env);
variant_ = Variant(ConfigManager::GetInstance()->GetVariant()); // variant_ = Variant(ConfigManager::GetInstance()->GetVariant());
LOGI("LSP Variant: %d", variant_); // LOGI("LSP Variant: %d", variant_);
//
if (variant_ == SANDHOOK) { // if (variant_ == SANDHOOK) {
//for SandHook variant // //for SandHook variant
ScopedLocalRef sandhook_class(env, FindClassFromCurrentLoader(env, kSandHookClassName)); // ScopedLocalRef sandhook_class(env, FindClassFromCurrentLoader(env, kSandHookClassName));
ScopedLocalRef nevercall_class(env, // ScopedLocalRef nevercall_class(env,
FindClassFromCurrentLoader(env, // FindClassFromCurrentLoader(env,
kSandHookNeverCallClassName)); // kSandHookNeverCallClassName));
if (sandhook_class == nullptr || nevercall_class == nullptr) { // fail-fast // if (sandhook_class == nullptr || nevercall_class == nullptr) { // fail-fast
return; // return;
} // }
if (!JNI_Load_Ex(env, sandhook_class.get(), nevercall_class.get())) { // if (!JNI_Load_Ex(env, sandhook_class.get(), nevercall_class.get())) {
LOGE("SandHook: HookEntry class error. %d", getpid()); // LOGE("SandHook: HookEntry class error. %d", getpid());
} // }
//
} // }
} }
jclass jclass
@ -202,108 +202,33 @@ namespace lspd {
[[maybe_unused]] jobjectArray rlimits, [[maybe_unused]] jobjectArray rlimits,
[[maybe_unused]] jlong permitted_capabilities, [[maybe_unused]] jlong permitted_capabilities,
[[maybe_unused]] jlong effective_capabilities) { [[maybe_unused]] jlong effective_capabilities) {
ConfigManager::SetCurrentUser(0u); Service::instance()->InitService(env);
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");
}
PreLoadDex(ConfigManager::GetInjectDexPath()); PreLoadDex(ConfigManager::GetInjectDexPath());
ConfigManager::GetInstance()->EnsurePermission("android", 1000); skip_ = false;
} }
int void
Context::OnNativeForkSystemServerPost(JNIEnv *env, [[maybe_unused]] jclass clazz, jint res) { Context::OnNativeForkSystemServerPost(JNIEnv *env, jint res) {
if (res == 0) { if (res != 0) return;
if (!skip_) { auto binder = Service::instance()->RequestBinder(env);
if (void *buf = mmap(nullptr, 1, PROT_READ | PROT_WRITE | PROT_EXEC, if (binder) {
MAP_ANONYMOUS | MAP_PRIVATE, -1, if (void *buf = mmap(nullptr, 1, PROT_READ | PROT_WRITE | PROT_EXEC,
0); MAP_ANONYMOUS | MAP_PRIVATE, -1,
buf == MAP_FAILED) { 0);
skip_ = true; buf == MAP_FAILED) {
LOGE("skip injecting into android because sepolicy was not loaded properly"); skip_ = true;
} else { LOGE("skip injecting into android because sepolicy was not loaded properly");
munmap(buf, 1); } 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; LoadDex(env);
} if (!skip_ && binder) {
InstallInlineHooks();
std::tuple<bool, uid_t, std::string> Init(env);
Context::GetAppInfoFromDir(JNIEnv *env, jstring dir, jstring nice_name) { FindAndCall(env, "forkSystemServerPost", "(Landroid/os/IBinder;)V", binder);
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<std::string> splits(path.begin(), path.end());
if (splits.size() < 5u) {
LOGE("can't parse %s", path.c_str());
return {false, 0, name.get()};
} }
const auto &uid_str = splits[3]; Service::instance()->HookBridge(*this, env);
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<bool()> &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;
} }
void Context::OnNativeForkAndSpecializePre(JNIEnv *env, jclass clazz, void Context::OnNativeForkAndSpecializePre(JNIEnv *env, jclass clazz,
@ -319,50 +244,50 @@ namespace lspd {
jboolean is_child_zygote, jboolean is_child_zygote,
jstring instruction_set, jstring instruction_set,
jstring app_data_dir) { jstring app_data_dir) {
const auto&[res, user, package_name] = GetAppInfoFromDir(env, app_data_dir, nice_name); PreLoadDex(ConfigManager::GetInjectDexPath());
app_data_dir_ = app_data_dir; Service::instance()->InitService(env);
nice_name_ = nice_name; this->uid = uid;
ConfigManager::SetCurrentUser(user); const auto app_id = uid % PER_USER_RANGE;
skip_ = ShouldSkipInject(package_name, user, uid, res, skip_ = false;
// Only obtains when needed if (!skip_ && !app_data_dir) {
[this, &package_name = package_name]() { LOGD("skip injecting into %d because it has no data dir", uid);
app_modules_list_ = ConfigManager::GetInstance()->GetAppModuleList( skip_ = true;
package_name); }
return app_modules_list_.empty(); if (!skip_ && is_child_zygote) {
}, is_child_zygote); skip_ = true;
if (!skip_) { LOGD("skip injecting into %d because it's a child zygote", uid);
ConfigManager::GetInstance()->EnsurePermission(package_name, uid); }
PreLoadDex(ConfigManager::GetInjectDexPath());
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 void
Context::OnNativeForkAndSpecializePost(JNIEnv *env, [[maybe_unused]]jclass clazz, jint res) { Context::OnNativeForkAndSpecializePost(JNIEnv *env) {
if (res == 0) { const JUTFString process_name(env, nice_name_);
const JUTFString process_name(env, nice_name_); auto binder = skip_? nullptr : Service::instance()->RequestBinder(env);
if (!skip_) { if (binder) {
LoadDex(env); LoadDex(env);
InstallInlineHooks(); InstallInlineHooks();
Init(env); Init(env);
LOGD("Done prepare"); LOGD("Done prepare");
FindAndCall(env, "forkAndSpecializePost", // TODO: cache this method
"(ILjava/lang/String;Ljava/lang/String;I)V", FindAndCall(env, "forkAndSpecializePost",
res, app_data_dir_, nice_name_, "(Ljava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V",
variant_); app_data_dir_, nice_name_,
LOGD("injected xposed into %s", process_name.get()); binder);
} else { LOGD("injected xposed into %s", process_name.get());
[[maybe_unused]] auto config_manager = ConfigManager::ReleaseInstances();
auto context = Context::ReleaseInstance();
LOGD("skipped %s", process_name.get());
}
} else { } else {
// in zygote process, res is child zygote pid auto context = Context::ReleaseInstance();
// don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66 auto service = Service::ReleaseInstance();
LOGD("skipped %s", process_name.get());
} }
return 0;
} }
} }
#pragma clang diagnostic pop #pragma clang diagnostic pop

View File

@ -39,9 +39,6 @@ namespace lspd {
public: public:
inline static Context *GetInstance() { inline static Context *GetInstance() {
if (!instance_) {
instance_ = std::make_unique<Context>();
}
return instance_.get(); return instance_.get();
} }
@ -55,18 +52,6 @@ namespace lspd {
void FindAndCall(JNIEnv *env, const char *method_name, const char *method_sig, ...) const; 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 { inline jclass FindClassFromCurrentLoader(JNIEnv *env, std::string_view className) const {
return FindClassFromLoader(env, GetCurrentClassLoader(), className); return FindClassFromLoader(env, GetCurrentClassLoader(), className);
}; };
@ -78,20 +63,18 @@ namespace lspd {
jintArray fds_to_ignore, jboolean is_child_zygote, jintArray fds_to_ignore, jboolean is_child_zygote,
jstring instruction_set, jstring app_data_dir); 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, void OnNativeForkSystemServerPre(JNIEnv *env, jclass clazz, uid_t uid, gid_t gid,
jintArray gids, jint runtime_flags, jobjectArray rlimits, jintArray gids, jint runtime_flags, jobjectArray rlimits,
jlong permitted_capabilities, jlong permitted_capabilities,
jlong effective_capabilities); jlong effective_capabilities);
inline auto GetVariant() const { return variant_; };
private: private:
inline static std::unique_ptr<Context> instance_; inline static std::unique_ptr<Context> instance_ = std::make_unique<Context>();
Variant variant_ = NONE;
jobject inject_class_loader_ = nullptr; jobject inject_class_loader_ = nullptr;
jclass entry_class_ = nullptr; jclass entry_class_ = nullptr;
jstring app_data_dir_ = nullptr; jstring app_data_dir_ = nullptr;
@ -99,9 +82,9 @@ namespace lspd {
JavaVM *vm_ = nullptr; JavaVM *vm_ = nullptr;
jclass class_linker_class_ = nullptr; jclass class_linker_class_ = nullptr;
jmethodID post_fixup_static_mid_ = nullptr; jmethodID post_fixup_static_mid_ = nullptr;
jint uid = -1;
bool skip_ = false; bool skip_ = false;
std::vector<signed char> dex{}; std::vector<signed char> dex{};
std::vector<std::string> app_modules_list_{};
Context() {} Context() {}
@ -114,14 +97,6 @@ namespace lspd {
static jclass FindClassFromLoader(JNIEnv *env, jobject class_loader, static jclass FindClassFromLoader(JNIEnv *env, jobject class_loader,
std::string_view class_name); std::string_view class_name);
static bool
ShouldSkipInject(const std::string &package_name, uid_t user, uid_t uid, bool res,
const std::function<bool()> &empty_list,
bool is_child_zygote);
static std::tuple<bool, uid_t, std::string>
GetAppInfoFromDir(JNIEnv *env, jstring dir, jstring nice_name);
friend std::unique_ptr<Context> std::make_unique<Context>(); friend std::unique_ptr<Context> std::make_unique<Context>();
}; };

View File

@ -74,13 +74,7 @@ namespace lspd {
} }
LSP_DEF_NATIVE_METHOD(jstring, ConfigManager, getModulesList) { LSP_DEF_NATIVE_METHOD(jstring, ConfigManager, getModulesList) {
auto module_list = Context::GetInstance()->GetAppModulesList(); return nullptr;
std::ostringstream join;
std::copy(module_list.begin(), module_list.end(),
std::ostream_iterator<std::string>(join, "\n"));
const auto &list = join.str();
LOGD("module list: %s", list.c_str());
return env->NewStringUTF(list.c_str());
} }
LSP_DEF_NATIVE_METHOD(jboolean, ConfigManager, isPermissive) { LSP_DEF_NATIVE_METHOD(jboolean, ConfigManager, isPermissive) {

View File

@ -29,9 +29,6 @@
#include "config_manager.h" #include "config_manager.h"
#include "symbol_cache.h" #include "symbol_cache.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-value"
namespace lspd { namespace lspd {
static void onModuleLoaded() { static void onModuleLoaded() {
LOGI("onModuleLoaded: welcome to LSPosed!"); LOGI("onModuleLoaded: welcome to LSPosed!");
@ -63,7 +60,7 @@ namespace lspd {
} }
static void nativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) { 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, 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) { static void nativeForkSystemServerPost(JNIEnv *env, [[maybe_unused]] jclass clazz, jint res) {
Context::GetInstance()->OnNativeForkSystemServerPost(env, clazz, res); Context::GetInstance()->OnNativeForkSystemServerPost(env, res);
} }
/* method added in Android Q */ /* method added in Android Q */
@ -97,13 +94,12 @@ namespace lspd {
*app_data_dir); *app_data_dir);
} }
static void specializeAppProcessPost(JNIEnv *env, jclass clazz) { static void specializeAppProcessPost(JNIEnv *env, [[maybe_unused]] jclass clazz) {
Context::GetInstance()->OnNativeForkAndSpecializePost(env, clazz, 0); Context::GetInstance()->OnNativeForkAndSpecializePost(env);
} }
} }
int riru_api_version; int riru_api_version;
RiruApiV10 *riru_api_v10;
RIRU_EXPORT void *init(void *arg) { RIRU_EXPORT void *init(void *arg) {
static int step = 0; static int step = 0;
@ -122,8 +118,6 @@ RIRU_EXPORT void *init(void *arg) {
case 10: case 10:
[[fallthrough]]; [[fallthrough]];
case 9: { case 9: {
riru_api_v10 = (RiruApiV10 *) arg;
auto module = (RiruModuleInfoV10 *) malloc(sizeof(RiruModuleInfoV10)); auto module = (RiruModuleInfoV10 *) malloc(sizeof(RiruModuleInfoV10));
memset(module, 0, sizeof(RiruModuleInfoV10)); memset(module, 0, sizeof(RiruModuleInfoV10));
_module = module; _module = module;
@ -156,5 +150,3 @@ RIRU_EXPORT void *init(void *arg) {
} }
} }
} }
#pragma clang diagnostic pop

View File

@ -3,27 +3,14 @@
// //
#include <nativehelper/scoped_local_ref.h> #include <nativehelper/scoped_local_ref.h>
#include <base/object.h>
#include <dobby.h> #include <dobby.h>
#include "base/object.h"
#include "service.h" #include "service.h"
#include "context.h" #include "context.h"
#include "JNIHelper.h"
namespace lspd { namespace lspd {
jboolean Service::exec_transact_replace(jboolean *res, JNIEnv *env, [[maybe_unused]] jobject obj, va_list args) {
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) {
jint code; jint code;
va_list copy; va_list copy;
@ -32,8 +19,8 @@ namespace lspd {
va_end(copy); va_end(copy);
if (UNLIKELY(code == BRIDGE_TRANSACTION_CODE)) { if (UNLIKELY(code == BRIDGE_TRANSACTION_CODE)) {
*res = env->CallStaticBooleanMethodV(bridge_service_class, *res = env->CallStaticBooleanMethodV(instance()->bridge_service_class_,
exec_transact_replace_methodID, instance()->exec_transact_replace_methodID_,
args); args);
return true; return true;
} }
@ -41,50 +28,139 @@ namespace lspd {
return false; return false;
} }
static jboolean jboolean
call_boolean_method_va_replace(JNIEnv *env, jobject obj, jmethodID methodId, va_list args) { Service::call_boolean_method_va_replace(JNIEnv *env, jobject obj, jmethodID methodId,
if (UNLIKELY(methodId == exec_transact_backup_methodID)) { va_list args) {
if (UNLIKELY(methodId == instance()->exec_transact_backup_methodID_)) {
jboolean res = false; jboolean res = false;
if (LIKELY(exec_transact_replace(&res, env, obj, args))) return res; if (LIKELY(exec_transact_replace(&res, env, obj, args))) return res;
// else fallback to backup // 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) { void Service::InitService(JNIEnv *env) {
bridge_service_class = context.FindClassFromCurrentLoader(env, kBridgeServiceClassName); if (LIKELY(initialized_)) return;
if (!bridge_service_class) { 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"); LOGE("server class not found");
return; return;
} }
bridge_service_class = (jclass) env->NewGlobalRef(bridge_service_class); bridge_service_class_ = (jclass) env->NewGlobalRef(bridge_service_class_);
exec_transact_replace_methodID = env->GetStaticMethodID(bridge_service_class, exec_transact_replace_methodID_ = env->GetStaticMethodID(bridge_service_class_,
"execTransact", "execTransact",
"(IJJI)Z"); "(IJJI)Z");
if (!exec_transact_replace_methodID) { if (!exec_transact_replace_methodID_) {
LOGE("execTransact class not found"); LOGE("execTransact class not found");
return; return;
} }
ScopedLocalRef<jclass> binderClass(env, env->FindClass("android/os/Binder")); ScopedLocalRef<jclass> binderClass(env, env->FindClass("android/os/Binder"));
exec_transact_backup_methodID = env->GetMethodID(binderClass.get(), "execTransact", exec_transact_backup_methodID_ = env->GetMethodID(binderClass.get(), "execTransact",
"(IJJI)Z"); "(IJJI)Z");
auto set_table_override = reinterpret_cast<void (*)( auto set_table_override = reinterpret_cast<void (*)(
JNINativeInterface *)>(DobbySymbolResolver(nullptr, JNINativeInterface *)>(DobbySymbolResolver(nullptr,
"_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface")); "_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface"));
if (!set_table_override) { if (!set_table_override) {
LOGE("set table override not found"); 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; call_boolean_method_va_backup_ = env->functions->CallBooleanMethodV;
native_interface_replace.CallBooleanMethodV = &call_boolean_method_va_replace; native_interface_replace_.CallBooleanMethodV = &call_boolean_method_va_replace;
if (set_table_override != nullptr) { if (set_table_override != nullptr) {
set_table_override(&native_interface_replace); set_table_override(&native_interface_replace_);
} }
LOGD("Done InitService"); 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;
}
} }

View File

@ -8,8 +8,72 @@
#include <jni.h> #include <jni.h>
#include "context.h" #include "context.h"
using namespace std::literals::string_view_literals;
namespace lspd { 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<Service> 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<Service> instance_ = std::make_unique<Service>();
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<Service> std::make_unique<Service>();
};
} }

View File

@ -22,10 +22,14 @@ package io.github.lsposed.lspd.core;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import android.ddm.DdmHandleAppName; import android.ddm.DdmHandleAppName;
import io.github.lsposed.common.KeepAll; 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.service.ServiceManager;
import io.github.lsposed.lspd.util.Utils; import io.github.lsposed.lspd.util.Utils;
@ -36,43 +40,41 @@ import static io.github.lsposed.lspd.service.ServiceManager.TAG;
@SuppressLint("DefaultLocale") @SuppressLint("DefaultLocale")
public class Main implements KeepAll { public class Main implements KeepAll {
private static final AtomicReference<EdxpImpl> lspdImplRef = new AtomicReference<>(null); private static final AtomicReference<EdxpImpl> lspdImplRef = new AtomicReference<>(null);
private static final Binder heartBeatBinder = new Binder();
/////////////////////////////////////////////////////////////////////////////////////////////// public static void forkAndSpecializePost(String appDataDir, String niceName, IBinder binder) {
// entry points ILSPApplicationService service = ILSPApplicationService.Stub.asInterface(binder);
/////////////////////////////////////////////////////////////////////////////////////////////// final int variant;
try {
public static void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags, service.registerHeartBeat(heartBeatBinder);
int[][] rlimits, int mountExternal, String seInfo, variant = service.getVariant();
String niceName, int[] fdsToClose, int[] fdsToIgnore, } catch (RemoteException e) {
boolean startChildZygote, String instructionSet, Utils.logW("Register fail", e);
String appDataDir) { return;
// won't be loaded }
}
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName, int variant) {
EdxpImpl lspd = getEdxpImpl(variant); EdxpImpl lspd = getEdxpImpl(variant);
if (lspd == null || !lspd.isInitialized()) { if (lspd == null || !lspd.isInitialized()) {
Utils.logE("Not started up"); Utils.logE("Not started up");
return; return;
} }
if (pid == 0) { lspd.getNormalProxy().forkAndSpecializePost(appDataDir, niceName);
lspd.getNormalProxy().forkAndSpecializePost(pid, 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); EdxpImpl lspd = getEdxpImpl(variant);
if (lspd == null || !lspd.isInitialized()) { if (lspd == null || !lspd.isInitialized()) {
return; return;
} }
if (pid == 0) { lspd.getNormalProxy().forkSystemServerPost();
lspd.getNormalProxy().forkSystemServerPost(pid);
}
} }
public static synchronized boolean setEdxpImpl(EdxpImpl lspd) { public static synchronized boolean setEdxpImpl(EdxpImpl lspd) {

View File

@ -26,16 +26,6 @@ public interface Proxy extends KeepAll {
boolean init(); boolean init();
void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags, void forkAndSpecializePost(String appDataDir, String niceName);
int[][] rlimits, int mountExternal, String seInfo, void forkSystemServerPost();
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);
} }

View File

@ -35,28 +35,17 @@ public class NormalProxy extends BaseProxy {
super(router); super(router);
} }
public void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags, public void forkAndSpecializePost(String appDataDir, String niceName) {
int[][] rlimits, int mountExternal, String seInfo, forkPostCommon(false, appDataDir, niceName);
String niceName, int[] fdsToClose, int[] fdsToIgnore,
boolean startChildZygote, String instructionSet,
String appDataDir) {
} }
public void forkAndSpecializePost(int pid, String appDataDir, String niceName) { public void forkSystemServerPost() {
forkPostCommon(pid, false, appDataDir, niceName); forkPostCommon(true,
}
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,
getDataPathPrefix() + "android", "system_server"); 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(); SELinuxHelper.initOnce();
mRouter.initResourcesHook(); mRouter.initResourcesHook();
mRouter.prepare(isSystem); mRouter.prepare(isSystem);

View File

@ -29,23 +29,32 @@ public class LSPApplicationService extends ILSPApplicationService.Stub {
}, 0); }, 0);
} }
@Override
public int getVariant() throws RemoteException {
ensureRegistered();
return ConfigManager.getInstance().variant();
}
// TODO: check if module // TODO: check if module
@Override @Override
public IBinder requestModuleBinder() { public IBinder requestModuleBinder() throws RemoteException {
if (!hasRegister(Binder.getCallingUid(), Binder.getCallingPid())) ensureRegistered();
return null;
return ServiceManager.getModuleService(); return ServiceManager.getModuleService();
} }
// TODO: check if manager // TODO: check if manager
@Override @Override
public IBinder requestManagerBinder() { public IBinder requestManagerBinder() throws RemoteException {
if (!hasRegister(Binder.getCallingUid(), Binder.getCallingPid())) ensureRegistered();
return null;
return ServiceManager.getManagerService(); return ServiceManager.getManagerService();
} }
public boolean hasRegister(int uid, int pid) { public boolean hasRegister(int uid, int pid) {
return cache.contains(new Pair<>(uid, pid)); return cache.contains(new Pair<>(uid, pid));
} }
private void ensureRegistered() throws RemoteException {
if (!hasRegister(Binder.getCallingUid(), Binder.getCallingPid()))
throw new RemoteException("Not registered");
}
} }