Riru和Lsp
riru
-
通过修改系统属性ro.dalvik.vm.native.bridge将libriruloader.so注入到zygote进程中
zygote会执行LoadNativeBridge函数, LoadNativeBridge函数会根据属性
ro.dalvik.vm.native.bridge
的值进行dlopen,因此.init_array成为了Hook点 -
libriru的init函数分别调用了PrepareMapsHideLibrary,InstallHooks和 Load
-
PrepareMapsHideLibrary // 隐藏maps
-
InstallHooks // 利用xHook框架hook函数jniRegisterNativeMethods,因此可以拿到nativeForkAndSpecialize, nativeSpecializeAppProcess, nativeForkSystemServer的函数地址
-
Load // 调用LoadModule函数,通过dlopen加载所有的riru模块so,隐藏maps, 加载的riru模块so的onModuleLoaded函数
riru主动调用Loadmodule
static void LoadModule(std::string_view id, std::string_view path, std::string_view magisk_module_path) {
auto *handle = DlopenExt(path.data(), 0);
if (!handle) {
LOGE("dlopen %s failed: %s", path.data(), dlerror());
return;
}
auto init = reinterpret_cast<RiruInit_t *>(dlsym(handle, "init"));
if (!init) {
LOGW("%s does not export init", path.data());
Cleanup(handle);
return;
}
auto allow_unload = std::make_unique<int>();
auto riru = std::make_unique<Riru>(Riru{
.riruApiVersion = riru::apiVersion,
.unused = nullptr,
.magiskModulePath = magisk_module_path.data(),
.allowUnload = allow_unload.get()
});
auto *module_info = init(riru.get()); //主动调用riru模块中的init函数
if (module_info == nullptr) {
LOGE("%s requires higher Riru version (or its broken)", path.data());
Cleanup(handle);
return;
}
auto api_version = module_info->moduleApiVersion;
if (api_version < riru::minApiVersion || api_version > riru::apiVersion) {//校验下版本
LOGW("unsupported API %s: %d", id.data(), api_version);
Cleanup(handle);
return;
}
LOGI("module loaded: %s (api %d)", id.data(), api_version);
modules::Get().emplace_back(id, path, magisk_module_path, api_version, module_info->moduleInfo,
handle,
std::move(allow_unload)); //全局保存
}
LSP中对应返回模块信息代码如下:
// LSPosed/magisk-loader/src/main/jni/api/riru_main.cpp
RIRU_EXPORT RiruVersionedModuleInfo *init(Riru *riru) {
LOGD("using riru {}", riru->riruApiVersion);
LOGD("module path: {}", riru->magiskModulePath);
lspd::magiskPath = riru->magiskModulePath;
if (!lspd::isDebug && lspd::magiskPath.find(lspd::moduleName) == std::string::npos) {
LOGE("who am i");
return nullptr;
}
lspd::allowUnload = riru->allowUnload;
return &lspd::module; //返回lsp模块信息给riru
}
RiruVersionedModuleInfo module{
.moduleApiVersion = apiVersion,
.moduleInfo = RiruModuleInfo{
.supportHide = !isDebug,
.version = versionCode,
.versionName = versionName,
.onModuleLoaded = lspd::onModuleLoaded,
.forkAndSpecializePre = lspd::nativeForkAndSpecializePre,
.forkAndSpecializePost = lspd::nativeForkAndSpecializePost,
.forkSystemServerPre = lspd::nativeForkSystemServerPre,
.forkSystemServerPost = lspd::nativeForkSystemServerPost,
.specializeAppProcessPre = lspd::specializeAppProcessPre,
.specializeAppProcessPost = lspd::specializeAppProcessPost,
}
};
再看OnLoad何时调用, 但Riru.so被加载时候, 调用其init函数,init函数中再调用modules::Load()
中回调Riru模块的onModuleLoaded
方法
__used __attribute__((constructor)) void Constructor() {
...
auto *handle = DlopenExt(riru_path, 0);
if (handle) {
auto init = reinterpret_cast<void (*)(void *, const char *, const RirudSocket &)>(dlsym(
handle, "init"));
if (init) {
init(handle, magisk_path.data(), rirud);
} else {
LOGE("dlsym init %s", dlerror());
}
} else {
LOGE("dlopen riru.so %s", dlerror());
}
...
}
// Riru/riru/src/main/cpp/entry.cpp"
init(void *handle, const char* magisk_path, const RirudSocket& rirud) {
self_handle = handle;
magisk::SetPath(magisk_path);
hide::PrepareMapsHideLibrary();
jni::InstallHooks();
modules::Load(rirud); // 主动调用模块onLoad方法
}
void modules::Load(const RirudSocket &rirud) {
...
for (const auto &module : modules::Get()) {
if (module.hasOnModuleLoaded()) {
LOGV("%s: onModuleLoaded", module.id.data());
module.onModuleLoaded(); // 主动调用模块onModuleLoaded方法
}
}
...
}
LSP
LSP就是一个Riru模块
void onModuleLoaded() {
LOGI("onModuleLoaded: welcome to LSPosed!");
LOGI("onModuleLoaded: version v{} ({})", versionName, versionCode);
MagiskLoader::Init();
ConfigImpl::Init();
}
zygote上的钩子
./LSPosed/magisk-loader/src/main/jni/api/riru_main.cpp
./LSPosed/external/lsplant/lsplant/src/main/jni/art/runtime/art_method.hpp
./LSPosed/external/lsplant/lsplant/src/main/jni/lsplant.cc
./LSPosed/external/lsplant/lsplant/src/main/jni/art/runtime/class_linker.hpp
../LSPosed/core/src/main/jni/src/context.cpp // Context::InitHooks
RiruVersionedModuleInfo module{
.moduleApiVersion = apiVersion,
.moduleInfo = RiruModuleInfo{
.supportHide = !isDebug,
.version = versionCode,
.versionName = versionName,
.onModuleLoaded = lspd::onModuleLoaded,
.forkAndSpecializePre = lspd::nativeForkAndSpecializePre,
.forkAndSpecializePost = lspd::nativeForkAndSpecializePost,
.forkSystemServerPre = lspd::nativeForkSystemServerPre,
.forkSystemServerPost = lspd::nativeForkSystemServerPost,
.specializeAppProcessPre = lspd::specializeAppProcessPre,
.specializeAppProcessPost = lspd::specializeAppProcessPost,
}
};
OnNativeForkAndSpecializePre
这里forkAndSpecializePre
和specializeAppProcessPre
都是调用OnNativeForkAndSpecializePre
,只有forkSystemServerPre
是OnNativeForkSystemServerPre
void MagiskLoader::OnNativeForkAndSpecializePre(JNIEnv *env,
jint uid,
jintArray &gids,
jstring nice_name,
jboolean is_child_zygote,
jstring app_data_dir) {
Service::instance()->InitService(env);
...
}
// ../LSPosed/magisk-loader/src/main/jni/src/service.cpp
void Service::InitService(JNIEnv *env) {
if (initialized_) [[unlikely]] return;
// ServiceManager
if (auto service_manager_class = JNI_FindClass(env, "android/os/ServiceManager")) {
service_manager_class_ = JNI_NewGlobalRef(env, service_manager_class);
} else return;
get_service_method_ = JNI_GetStaticMethodID(env, service_manager_class_, "getService",
"(Ljava/lang/String;)Landroid/os/IBinder;");
if (!get_service_method_) return;
...
}
OnNativeForkAndSpecializePost
void
MagiskLoader::OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name, jstring app_dir) {
const JUTFString process_name(env, nice_name);
auto *instance = Service::instance();
auto binder = skip_ ? ScopedLocalRef<jobject>{env, nullptr}
: instance->RequestBinder(env, nice_name);
if (binder) {
lsplant::InitInfo initInfo{
.inline_hooker = [](auto t, auto r) {
void* bk = nullptr;
return HookFunction(t, r, &bk) == RS_SUCCESS ? bk : nullptr;
},
.inline_unhooker = [](auto t) {
return UnhookFunction(t) == RT_SUCCESS;
},
.art_symbol_resolver = [](auto symbol){
return GetArt()->getSymbAddress(symbol);
},
.art_symbol_prefix_resolver = [](auto symbol) {
return GetArt()->getSymbPrefixFirstAddress(symbol);
},
};
auto [dex_fd, size] = instance->RequestLSPDex(env, binder);
auto obfs_map = instance->RequestObfuscationMap(env, binder);
ConfigBridge::GetInstance()->obfuscation_map(std::move(obfs_map));
LoadDex(env, PreloadedDex(dex_fd, size)); //加载lspd.dex
close(dex_fd);
InitArtHooker(env, initInfo);
InitHooks(env);
SetupEntryClass(env); //lspd.dex的入口类org.lsposed.lspd.core.Main
LOGD("Done prepare");
FindAndCall(env, "forkCommon",
"(ZLjava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V",
JNI_FALSE, nice_name, app_dir, binder); // 启动 xposed Startup.bootstrapXposed
LOGD("injected xposed into {}", process_name.get());
setAllowUnload(false);
GetArt(true);
} else {
auto context = Context::ReleaseInstance();
auto service = Service::ReleaseInstance();
GetArt(true);
LOGD("skipped {}", process_name.get());
setAllowUnload(true);
}
}
-
LoadDex // 将lspd.dex文件从磁盘map到内存中, InMemoryClassLoader加载,而不是pathclassload可能还是怕被风控
-
InitArtHooker
-
InitJNI(env) //通过jni env 获取java 比如 dalvik/system/PathClassLoader
-
InitNative(env, info) //libart 符号hook一些函数, SetEntryPointsToInterpreter等等
-
-
InitHooks
InitNative 基本是一顿Hook,hook了非常多的函数,比如setTrusted允许访问系统级API
CREATE_FUNC_SYMBOL_ENTRY(void, DexFile_setTrusted, JNIEnv* env, jclass clazz,
jobject j_cookie) {
if (DexFile_setTrustedSym != nullptr) [[likely]] {
DexFile_setTrustedSym(env, clazz, j_cookie);
}
}
ClassLinker::Init(const HookHandler &handler) --> SetEntryPointsToInterpreter
// LSPosed/external/lsplant/lsplant/src/main/jni/art/runtime/class_linker.hpp
[[gnu::always_inline]] static bool SetEntryPointsToInterpreter(ArtMethod *art_method) {
if (SetEntryPointsToInterpreterSym) [[likely]] {
SetEntryPointsToInterpreter(nullptr, art_method);
return true;
}
// Android 13
if (art_quick_to_interpreter_bridgeSym && art_quick_generic_jni_trampolineSym) [[likely]] {
if (art_method->GetAccessFlags() & ArtMethod::kAccNative) [[unlikely]] {
LOGV("deoptimize native method %s from %p to %p",
art_method->PrettyMethod(true).data(), art_method->GetEntryPoint(),
art_quick_generic_jni_trampolineSym);
art_method->SetEntryPoint(
reinterpret_cast<void *>(art_quick_generic_jni_trampolineSym));
} else {
LOGV("deoptimize method %s from %p to %p", art_method->PrettyMethod(true).data(),
art_method->GetEntryPoint(), art_quick_to_interpreter_bridgeSym);
art_method->SetEntryPoint(
reinterpret_cast<void *>(art_quick_to_interpreter_bridgeSym));
}
return true;
}
return false;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】