Fix hook may not work on debuggable runtime (#1892)

Co-authored-by: Wang Han <wanghan1995315@gmail.com>
This commit is contained in:
LoveSy 2022-04-27 06:21:47 +08:00 committed by GitHub
parent 59913258f9
commit 177c2cd0c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 84 additions and 315 deletions

View File

@ -31,14 +31,17 @@ import org.lsposed.lspd.deopt.PrebuiltMethodsDeopter;
import org.lsposed.lspd.hooker.CrashDumpHooker;
import org.lsposed.lspd.hooker.HandleSystemServerProcessHooker;
import org.lsposed.lspd.hooker.LoadedApkCtorHooker;
import org.lsposed.lspd.hooker.OpenDexFileHooker;
import org.lsposed.lspd.service.ILSPApplicationService;
import org.lsposed.lspd.util.Utils;
import dalvik.system.DexFile;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.XposedInit;
public class Startup {
@SuppressWarnings("deprecation")
private static void startBootstrapHook(boolean isSystem) {
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
XposedHelpers.findAndHookMethod(Thread.class, "dispatchUncaughtException",
@ -46,6 +49,10 @@ public class Startup {
if (isSystem) {
XposedBridge.hookAllMethods(ZygoteInit.class,
"handleSystemServerProcess", new HandleSystemServerProcessHooker());
} else {
var hooker = new OpenDexFileHooker();
XposedBridge.hookAllMethods(DexFile.class, "openDexFile", hooker);
XposedBridge.hookAllMethods(DexFile.class, "openInMemoryDexFile", hooker);
}
XposedHelpers.findAndHookConstructor(LoadedApk.class,
ActivityThread.class, ApplicationInfo.class, CompatibilityInfo.class,

View File

@ -0,0 +1,31 @@
package org.lsposed.lspd.hooker;
import android.os.Build;
import org.lsposed.lspd.nativebridge.HookBridge;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
public class OpenDexFileHooker extends XC_MethodHook {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
ClassLoader classLoader = null;
for (var arg : param.args) {
if (arg instanceof ClassLoader) {
classLoader = (ClassLoader) arg;
}
}
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P && classLoader == null) {
classLoader = XposedHelpers.class.getClassLoader();
}
while (classLoader != null) {
if (classLoader == XposedHelpers.class.getClassLoader()) {
HookBridge.setTrusted(param.getResult());
return;
} else {
classLoader = classLoader.getParent();
}
}
}
}

View File

@ -15,4 +15,7 @@ public class HookBridge {
@FastNative
public static native boolean instanceOf(Object obj, Class<?> clazz);
@FastNative
public static native boolean setTrusted(Object cookie);
}

View File

@ -39,6 +39,7 @@ public:
virtual obfuscation_map_t& obfuscation_map() = 0;
virtual void obfuscation_map(obfuscation_map_t) = 0;
virtual ~ConfigBridge() = default;
protected:
inline static std::unique_ptr<ConfigBridge> instance_ = nullptr;
};

View File

@ -1,148 +0,0 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 EdXposed Contributors
* Copyright (C) 2021 LSPosed Contributors
*/
#pragma once
#include "symbol_cache.h"
#include "utils/hook_helper.hpp"
#include "context.h"
#include "runtime.h"
namespace art {
namespace hidden_api {
inline static bool is_debuggable = false;
using lsplant::operator""_tstr;
CREATE_FUNC_SYMBOL_ENTRY(void, DexFile_setTrusted, JNIEnv *env, jclass clazz,
jobject j_cookie) {
if (DexFile_setTrustedSym != nullptr) [[likely]] {
if (!is_debuggable) Runtime::Current()->SetJavaDebuggable(true);
DexFile_setTrustedSym(env, clazz, j_cookie);
if (!is_debuggable) Runtime::Current()->SetJavaDebuggable(false);
}
}
inline void
maybeSetTrusted(JNIEnv *env, jclass clazz, jobject class_loader, jobject j_cookie) {
if (env->ExceptionCheck()) return;
static auto get_parent = env->GetMethodID(env->FindClass("java/lang/ClassLoader"),
"getParent", "()Ljava/lang/ClassLoader;");
for (auto current = lspd::Context::GetInstance()->GetCurrentClassLoader();
class_loader != nullptr;
class_loader = env->CallObjectMethod(class_loader, get_parent)) {
if (!current || env->IsSameObject(class_loader, current)) {
DexFile_setTrusted(env, clazz, j_cookie);
LOGD("Set classloader as trusted");
return;
}
}
}
CREATE_HOOK_STUB_ENTRY(
"_ZN3artL25DexFile_openDexFileNativeEP7_JNIEnvP7_jclassP8_jstringS5_iP8_jobjectP13_jobjectArray",
jobject, DexFile_openDexFileNative, (JNIEnv * env,
jclass clazz,
jstring javaSourceName,
jstring javaOutputName,
jint flags,
jobject class_loader,
jobjectArray dex_elements), {
auto j_cookie = backup(env, clazz, javaSourceName, javaOutputName, flags,
class_loader,
dex_elements);
maybeSetTrusted(env, clazz, class_loader, j_cookie);
return j_cookie;
}
);
CREATE_HOOK_STUB_ENTRY(
"_ZN3artL34DexFile_openInMemoryDexFilesNativeEP7_JNIEnvP7_jclassP13_jobjectArrayS5_P10_jintArrayS7_P8_jobjectS5_",
jobject, DexFile_openInMemoryDexFilesNative, (JNIEnv * env,
jclass clazz,
jobjectArray buffers,
jobjectArray arrays,
jintArray jstarts,
jintArray jends,
jobject class_loader,
jobjectArray dex_elements), {
auto j_cookie = backup(env, clazz, buffers, arrays, jstarts, jends,
class_loader,
dex_elements);
maybeSetTrusted(env, clazz, class_loader, j_cookie);
return j_cookie;
}
);
CREATE_HOOK_STUB_ENTRY(
"_ZN3artL29DexFile_createCookieWithArrayEP7_JNIEnvP7_jclassP11_jbyteArrayii",
jobject, DexFile_createCookieWithArray, (JNIEnv * env,
jclass clazz,
jbyteArray buffer,
jint start,
jint end), {
auto j_cookie = backup(env, clazz, buffer, start, end);
DexFile_setTrusted(env, clazz, j_cookie);
return j_cookie;
}
);
CREATE_HOOK_STUB_ENTRY(
"_ZN3artL36DexFile_createCookieWithDirectBufferEP7_JNIEnvP7_jclassP8_jobjectii",
jobject, DexFile_createCookieWithDirectBuffer, (JNIEnv * env,
jclass clazz,
jobject buffer,
jint start,
jint end), {
auto j_cookie = backup(env, clazz, buffer, start, end);
DexFile_setTrusted(env, clazz, j_cookie);
return j_cookie;
}
);
inline void DisableHiddenApi(JNIEnv* env, const lsplant::HookHandler &handler) {
const int api_level = lspd::GetAndroidApiLevel();
if (api_level < __ANDROID_API_P__) {
return;
}
auto runtime_class = lsplant::JNI_FindClass(env, "dalvik/system/VMRuntime");
auto get_runtime_method = lsplant::JNI_GetStaticMethodID(env, runtime_class, "getRuntime", "()Ldalvik/system/VMRuntime;");
auto is_debuggable_method = lsplant::JNI_GetMethodID(env, runtime_class, "isJavaDebuggable", "()Z");
auto runtime = lsplant::JNI_CallStaticObjectMethod(env, runtime_class, get_runtime_method);
is_debuggable = lsplant::JNI_CallBooleanMethod(env, runtime, is_debuggable_method);
LOGD("java runtime debuggable %s", is_debuggable ? "true" : "false");
DexFile_setTrustedSym = reinterpret_cast<decltype(DexFile_setTrustedSym)>(lspd::symbol_cache->setTrusted);
lsplant::HookSymNoHandle(handler, lspd::symbol_cache->openDexFileNative, DexFile_openDexFileNative);
lsplant::HookSymNoHandle(handler, lspd::symbol_cache->openInMemoryDexFilesNative,
DexFile_openInMemoryDexFilesNative);
if (api_level == __ANDROID_API_P__) {
lsplant::HookSymNoHandle(handler, lspd::symbol_cache->createCookieWithArray,
DexFile_createCookieWithArray);
lsplant::HookSymNoHandle(handler, lspd::symbol_cache->createCookieWithDirectBuffer,
DexFile_createCookieWithDirectBuffer);
}
};
}
}

View File

@ -1,56 +0,0 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 EdXposed Contributors
* Copyright (C) 2021 LSPosed Contributors
*/
#pragma once
#include "utils/hook_helper.hpp"
namespace art {
class Runtime {
private:
inline static Runtime *instance_;
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, SetJavaDebuggable, void *thiz, bool value) {
if (SetJavaDebuggableSym) [[likely]] {
SetJavaDebuggableSym(thiz, value);
}
}
public:
inline static Runtime *Current() {
return instance_;
}
void SetJavaDebuggable(bool value) {
SetJavaDebuggable(this, value);
}
// @ApiSensitive(Level.LOW)
inline static void Setup(const lsplant::HookHandler &handler) {
void **instance = nullptr;
RETRIEVE_FIELD_SYMBOL(instance, "_ZN3art7Runtime9instance_E");
RETRIEVE_MEM_FUNC_SYMBOL(SetJavaDebuggable, "_ZN3art7Runtime17SetJavaDebuggableEb");
void *thiz = *instance;
LOGD("_ZN3art7Runtime9instance_E = {}", thiz);
instance_ = reinterpret_cast<Runtime*>(thiz);
}
};
}

View File

@ -1,28 +0,0 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 EdXposed Contributors
* Copyright (C) 2021 LSPosed Contributors
*/
#pragma once
#include "utils/hook_helper.hpp"
namespace lspd {
void InstallInlineHooks(JNIEnv* env, const lsplant::HookHandler &handler);
}

View File

@ -109,7 +109,7 @@ inline int UnhookFunction(void *original) {
return DobbyDestroy(original);
}
static std::string GetNativeBridgeSignature() {
inline std::string GetNativeBridgeSignature() {
const auto &obfs_map = ConfigBridge::GetInstance()->obfuscation_map();
static auto signature = obfs_map.at("org.lsposed.lspd.nativebridge.");
return signature;

View File

@ -35,22 +35,12 @@ namespace lspd {
struct SymbolCache {
std::atomic_flag initialized{};
void *do_dlopen;
void *openInMemoryDexFilesNative;
void *createCookieWithArray;
void *createCookieWithDirectBuffer;
void *openDexFileNative;
void *setTrusted;
void *setTableOverride;
SymbolCache() = default;
SymbolCache(const SymbolCache &other) :
do_dlopen(other.do_dlopen),
openInMemoryDexFilesNative(other.openInMemoryDexFilesNative),
createCookieWithArray(other.createCookieWithArray),
createCookieWithDirectBuffer(other.createCookieWithDirectBuffer),
openDexFileNative(other.openDexFileNative),
setTrusted(other.setTrusted),
setTableOverride(other.setTableOverride) {}
SymbolCache &operator=(const SymbolCache &other) {

View File

@ -22,7 +22,6 @@
#include "config.h"
#include "context.h"
#include "native_hook.h"
#include "native_util.h"
#include "jni/hook_bridge.h"
#include "jni/native_api.h"
@ -48,11 +47,40 @@ namespace lspd {
if (*this) munmap(addr_, size_);
}
void Context::InitHooks(JNIEnv *env, const lsplant::InitInfo& initInfo) {
void Context::InitHooks(JNIEnv *env, const lsplant::InitInfo &initInfo) {
if (!lsplant::Init(env, initInfo)) {
LOGE("Failed to init lsplant");
return;
}
auto path_list = JNI_GetObjectFieldOf(env, inject_class_loader_, "pathList",
"Ldalvik/system/DexPathList;");
if (!path_list) {
LOGE("Failed to get path list");
return;
}
const auto elements = JNI_Cast<jobjectArray>(
JNI_GetObjectFieldOf(env, path_list, "dexElements",
"[Ldalvik/system/DexPathList$Element;"));
if (!elements) {
LOGE("Failed to get elements");
return;
}
for (const auto &element: elements) {
if (!element)
continue;
auto java_dex_file = JNI_GetObjectFieldOf(env, element, "dexFile",
"Ldalvik/system/DexFile;");
if (!java_dex_file) {
LOGE("Failed to get java dex file");
return;
}
auto cookie = JNI_GetObjectFieldOf(env, java_dex_file, "mCookie", "Ljava/lang/Object;");
if (!cookie) {
LOGE("Failed to get cookie");
return;
}
lsplant::MakeDexFileTrusted(env, cookie);
}
RegisterResourcesHook(env);
RegisterHookBridge(env);
RegisterNativeAPI(env);

View File

@ -173,12 +173,17 @@ LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, instanceOf, jobject object, jclass e
return env->IsInstanceOf(object, expected_class);
}
LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, setTrusted, jobject cookie) {
return lsplant::MakeDexFileTrusted(env, cookie);
}
static JNINativeMethod gMethods[] = {
LSP_NATIVE_METHOD(HookBridge, hookMethod, "(Ljava/lang/reflect/Executable;Ljava/lang/Class;ILjava/lang/Object;)Z"),
LSP_NATIVE_METHOD(HookBridge, unhookMethod, "(Ljava/lang/reflect/Executable;Ljava/lang/Object;)Z"),
LSP_NATIVE_METHOD(HookBridge, deoptimizeMethod, "(Ljava/lang/reflect/Executable;)Z"),
LSP_NATIVE_METHOD(HookBridge, invokeOriginalMethod, "(Ljava/lang/reflect/Executable;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"),
LSP_NATIVE_METHOD(HookBridge, instanceOf, "(Ljava/lang/Object;Ljava/lang/Class;)Z"),
LSP_NATIVE_METHOD(HookBridge, setTrusted, "(Ljava/lang/Object;)Z"),
};
void RegisterHookBridge(JNIEnv *env) {

View File

@ -1,45 +0,0 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 EdXposed Contributors
* Copyright (C) 2021 LSPosed Contributors
*/
#include <dlfcn.h>
#include <string>
#include <vector>
#include <dobby.h>
#include "symbol_cache.h"
#include "logging.h"
#include "native_api.h"
#include "native_hook.h"
#include "art/runtime/hidden_api.h"
namespace lspd {
static std::atomic_bool installed = false;
void InstallInlineHooks(JNIEnv* env, const lsplant::HookHandler& handler) {
if (installed.exchange(true)) [[unlikely]] {
LOGD("Inline hooks have been installed, skip");
return;
}
LOGD("Start to install inline hooks");
art::Runtime::Setup(handler);
art::hidden_api::DisableHiddenApi(env, handler);
LOGD("Inline hooks installed");
}
} // namespace lspd

View File

@ -47,24 +47,8 @@ namespace lspd {
bool FindLibArt() {
auto &art = GetArt();
if (!art->isValid()) return false;
auto api_level = GetAndroidApiLevel();
return (symbol_cache->setTableOverride = art->getSymbAddress<void *>(
"_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface")) != nullptr
&& (api_level < __ANDROID_API_P__ || (
(symbol_cache->openDexFileNative = art->getSymbAddress<void *>(
"_ZN3artL25DexFile_openDexFileNativeEP7_JNIEnvP7_jclassP8_jstringS5_iP8_jobjectP13_jobjectArray")) &&
(
(symbol_cache->openInMemoryDexFilesNative = art->getSymbAddress<void *>(
"_ZN3artL34DexFile_openInMemoryDexFilesNativeEP7_JNIEnvP7_jclassP13_jobjectArrayS5_P10_jintArrayS7_P8_jobjectS5_")) ||
(
(symbol_cache->createCookieWithArray = art->getSymbAddress<void *>(
"_ZN3artL29DexFile_createCookieWithArrayEP7_JNIEnvP7_jclassP11_jbyteArrayii")) &&
(symbol_cache->createCookieWithDirectBuffer = art->getSymbAddress<void *>(
"_ZN3artL36DexFile_createCookieWithDirectBufferEP7_JNIEnvP7_jclassP8_jobjectii"))
)
) &&
(symbol_cache->setTrusted = art->getSymbAddress<void *>(
"_ZN3artL18DexFile_setTrustedEP7_JNIEnvP7_jclassP8_jobject"))));
return symbol_cache->setTableOverride = art->getSymbAddress<void *>(
"_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface");
}
void InitSymbolCache(SymbolCache *other) {

2
external/lsplant vendored

@ -1 +1 @@
Subproject commit 20413748f0bac79ca480909cdce48723f600ff90
Subproject commit 9c63dcecf38f0ae34c4a67c4f1fb3bd8bd377f0f

View File

@ -22,4 +22,4 @@ MODDIR=${0%/*}
rm -f "/data/local/tmp/daemon.apk"
cd "$MODDIR"
unshare -m sh -c "$MODDIR/daemon &"
unshare -m sh -c "$MODDIR/daemon $@&"

View File

@ -20,4 +20,4 @@
MODDIR=${0%/*}
cd "$MODDIR"
# post-fs-data.sh may be blocked by other modules. retry to start this
unshare -m "$MODDIR/daemon" --from-service "$@"&
unshare -m sh -c "$MODDIR/daemon --from-service $@&"

View File

@ -26,7 +26,6 @@
#include "elf_util.h"
#include "loader.h"
#include "magisk_loader.h"
#include "native_hook.h"
#include "native_util.h"
#include "service.h"
#include "symbol_cache.h"
@ -132,7 +131,6 @@ namespace lspd {
return GetArt()->getSymbAddress<void*>(symbol);
},
};
InstallInlineHooks(env, initInfo);
InitHooks(env, initInfo);
SetupEntryClass(env);
FindAndCall(env, "forkCommon",
@ -204,7 +202,6 @@ namespace lspd {
return GetArt()->getSymbAddress<void*>(symbol);
},
};
InstallInlineHooks(env, initInfo);
auto [dex_fd, size] = instance->RequestLSPDex(env, binder);
auto obfs_map = instance->RequestObfuscationMap(env, binder);
ConfigBridge::GetInstance()->obfuscation_map(std::move(obfs_map));