[core] Native request binder
This commit is contained in:
parent
c65af4c4b5
commit
0ec8959235
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include <jni.h>
|
||||
#include <android-base/macros.h>
|
||||
#include <JNIHelper.h>
|
||||
#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<bool, uid_t, std::string>
|
||||
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<std::string> 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<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;
|
||||
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
|
||||
|
|
@ -39,9 +39,6 @@ namespace lspd {
|
|||
|
||||
public:
|
||||
inline static Context *GetInstance() {
|
||||
if (!instance_) {
|
||||
instance_ = std::make_unique<Context>();
|
||||
}
|
||||
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<Context> instance_;
|
||||
Variant variant_ = NONE;
|
||||
inline static std::unique_ptr<Context> instance_ = std::make_unique<Context>();
|
||||
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<signed char> dex{};
|
||||
std::vector<std::string> 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<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>();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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<std::string>(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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -3,27 +3,14 @@
|
|||
//
|
||||
|
||||
#include <nativehelper/scoped_local_ref.h>
|
||||
#include <base/object.h>
|
||||
#include <dobby.h>
|
||||
#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<jclass> 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<void (*)(
|
||||
JNINativeInterface *)>(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;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,8 +8,72 @@
|
|||
#include <jni.h>
|
||||
#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<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>();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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<EdxpImpl> 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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue