diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index 009cae93..14f99f89 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -46,6 +46,7 @@ jobs: with: path: | ~/.gradle/caches/build-cache-* + ~/.gradle/buildOutputCleanup/cache.properties key: gradle-builds-core-${{ github.sha }} restore-keys: | gradle-builds @@ -66,8 +67,6 @@ jobs: ccache -o compiler_check='%compiler% -dumpmachine; %compiler% -dumpversion' ccache -zp - name: Build with Gradle - env: - NDK_CCACHE: ccache run: | [ $(du -s ~/.gradle/wrapper | awk '{ print $1 }') -gt 250000 ] && rm -rf ~/.gradle/wrapper/* || true find ~/.gradle/caches -exec touch -d "2 days ago" {} + || true @@ -117,6 +116,13 @@ jobs: path: | core/build/outputs/mapping app/build/outputs/mapping + - name: Upload symbols + uses: actions/upload-artifact@v2 + with: + name: symbols + path: | + core/build/symbols + daemon/build/symbols - name: Post to channel if: ${{ github.event_name != 'pull_request' && success() && github.ref == 'refs/heads/master' }} env: diff --git a/.gitmodules b/.gitmodules index c7181000..741313d3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,12 @@ [submodule "service"] path = service url = https://github.com/libxposed/XposedService.git -[submodule "core/src/main/cpp/external/DexBuilder"] - path = core/src/main/cpp/external/DexBuilder - url = https://github.com/LSPosed/DexBuilder.git +[submodule "external/cxx"] + path = external/cxx + url = https://github.com/LSPosed/libcxx.git +[submodule "external/lsplant"] + path = external/lsplant + url = https://github.com/LSPosed/LSPlant.git +[submodule "external/dobby"] + path = external/dobby + url = https://github.com/LSPosed/Dobby.git diff --git a/core/.gitignore b/core/.gitignore index 00ac22a3..99ef122d 100644 --- a/core/.gitignore +++ b/core/.gitignore @@ -1,3 +1,4 @@ /build /release -/src/main/cpp/main/api/config.cpp +/src/main/cpp/api/config.cpp +/.cxx diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 46c9f0b4..9b8f7c51 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -73,11 +73,32 @@ android { multiDexEnabled = false externalNativeBuild { - ndkBuild { - arguments += "INJECTED_AID=$injectedPackageUid" - arguments += "VERSION_CODE=$verCode" - arguments += "VERSION_NAME=$verName" - arguments += "-j${Runtime.getRuntime().availableProcessors()}" + cmake { + arguments += "-DEXTERNAL_ROOT=${File(rootDir.absolutePath, "external")}" + abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64") + val flags = arrayOf( + "-Wall", + "-Qunused-arguments", + "-Wno-gnu-string-literal-operator-template", + "-fno-rtti", + "-fvisibility=hidden", + "-fvisibility-inlines-hidden", + "-fno-exceptions", + "-fno-stack-protector", + "-fomit-frame-pointer", + "-Wno-builtin-macro-redefined", + "-Wno-unused-value", + "-D__FILE__=__FILE_NAME__", + "-DINJECTED_AID=$injectedPackageUid", + ) + cppFlags("-std=c++20", *flags) + cFlags("-std=c18", *flags) + arguments( + "-DANDROID_STL=none", + "-DVERSION_CODE=$verCode", + "-DVERSION_NAME=$verName", + ) + targets("lspd") } } @@ -97,14 +118,58 @@ android { } buildTypes { + debug { + externalNativeBuild { + cmake { + arguments.addAll( + arrayOf( + "-DCMAKE_CXX_FLAGS_DEBUG=-Og", + "-DCMAKE_C_FLAGS_DEBUG=-Og", + ) + ) + } + } + } release { isMinifyEnabled = true proguardFiles("proguard-rules.pro") + + externalNativeBuild { + cmake { + val flags = arrayOf( + "-Wl,--exclude-libs,ALL", + "-ffunction-sections", + "-fdata-sections", + "-Wl,--gc-sections", + "-fno-unwind-tables", + "-fno-asynchronous-unwind-tables", + "-flto=thin", + "-Wl,--thinlto-cache-policy,cache_size_bytes=300m", + "-Wl,--thinlto-cache-dir=${buildDir.absolutePath}/.lto-cache", + ) + cppFlags.addAll(flags) + cFlags.addAll(flags) + val configFlags = arrayOf( + "-Oz", + "-DNDEBUG" + ).joinToString(" ") + arguments.addAll( + arrayOf( + "-DCMAKE_CXX_FLAGS_RELEASE=$configFlags", + "-DCMAKE_CXX_FLAGS_RELWITHDEBINFO=$configFlags", + "-DCMAKE_C_FLAGS_RELEASE=$configFlags", + "-DCMAKE_C_FLAGS_RELWITHDEBINFO=$configFlags", + "-DDEBUG_SYMBOLS_PATH=${buildDir.absolutePath}/symbols", + ) + ) + } + } } } + externalNativeBuild { - ndkBuild { - path("src/main/cpp/Android.mk") + cmake { + path("src/main/jni/CMakeLists.txt") } } @@ -113,22 +178,12 @@ android { sourceCompatibility(androidSourceCompatibility) } - buildTypes { - all { - externalNativeBuild { - ndkBuild { - arguments += "NDK_OUT=${File(buildDir, ".cxx/$name").absolutePath}" - } - } - } - } - productFlavors { all { externalNativeBuild { - ndkBuild { - arguments += "MODULE_NAME=${name.toLowerCase()}_$moduleBaseId" - arguments += "API=${name.toLowerCase()}" + cmake { + arguments += "-DMODULE_NAME=${name.toLowerCase()}_$moduleBaseId" + arguments += "-DAPI=${name.toLowerCase()}" } } buildConfigField("String", "API", """"$name"""") @@ -137,8 +192,8 @@ android { create("Riru") { dimension = "api" externalNativeBuild { - ndkBuild { - arguments += "API_VERSION=$moduleMaxRiruApiVersion" + cmake { + arguments += "-DAPI_VERSION=$moduleMaxRiruApiVersion" } } } @@ -146,8 +201,8 @@ android { create("Zygisk") { dimension = "api" externalNativeBuild { - ndkBuild { - arguments += "API_VERSION=1" + cmake { + arguments += "-DAPI_VERSION=1" } } } @@ -157,10 +212,6 @@ android { dependencies { - // keep this dep since it affects ccache - implementation("dev.rikka.ndk:riru:26.0.0") - implementation("dev.rikka.ndk.thirdparty:cxx:1.2.0") - implementation("io.github.vvb2060.ndk:dobby:1.2") implementation("org.apache.commons:commons-lang3:3.12.0") implementation("de.upb.cs.swt:axml:2.1.2") compileOnly("androidx.annotation:annotation:1.3.0") @@ -247,8 +298,12 @@ fun afterEval() = android.applicationVariants.forEach { variant -> rename(".*\\.apk", "daemon.apk") } into("lib") { - from("${buildDir}/intermediates/stripped_native_libs/$variantCapped/out/lib") - from("${project(":daemon").buildDir}/intermediates/ndkBuild/$buildTypeLowered/obj/local") + from("${buildDir}/intermediates/cmake/$variantCapped/obj") { + include("**/liblspd.so") + } + from("${project(":daemon").buildDir}/intermediates/cmake/$buildTypeLowered/obj") { + include("**/libdaemon.so") + } } val dexOutPath = if (buildTypeLowered == "release") "$buildDir/intermediates/dex/$variantCapped/minify${variantCapped}WithR8" else diff --git a/core/proguard-rules.pro b/core/proguard-rules.pro index fc283da2..f92b4bc8 100644 --- a/core/proguard-rules.pro +++ b/core/proguard-rules.pro @@ -7,9 +7,6 @@ -keepclasseswithmembers,includedescriptorclasses class * { native ; } --keepclasseswithmembers class org.lsposed.lspd.nativebridge.ClassLinker { - public static void onPostFixupStaticTrampolines(java.lang.Class); -} -keepclasseswithmembers class org.lsposed.lspd.service.BridgeService { public static boolean *(android.os.IBinder, int, long, long, int); } diff --git a/core/src/main/cpp/.clang-tidy b/core/src/main/cpp/.clang-tidy deleted file mode 100644 index 9734c7e7..00000000 --- a/core/src/main/cpp/.clang-tidy +++ /dev/null @@ -1,60 +0,0 @@ ---- -Checks: > - -*, - bugprone-*, - google-*, - misc-*, - modernize-*, - performance-*, - portability-*, - readability-*, - clang-analyzer-*, - llvm-include-order, - -modernize-use-trailing-return-type, - -readability-implicit-bool-conversion, - -CheckOptions: - - key: readability-identifier-naming.ClassCase - value: CamelCase - - key: readability-identifier-naming.ClassMemberCase - value: lower_case - - key: readability-identifier-naming.EnumCase - value: CamelCase - - key: readability-identifier-naming.EnumConstantCase - value: CamelCase - - key: readability-identifier-naming.EnumConstantPrefix - value: k - - key: readability-identifier-naming.FunctionCase - value: CamelCase - - key: readability-identifier-naming.GlobalConstantCase - value: CamelCase - - key: readability-identifier-naming.GlobalConstantPrefix - value: k - - key: readability-identifier-naming.StaticConstantCase - value: CamelCase - - key: readability-identifier-naming.StaticConstantPrefix - value: k - - key: readability-identifier-naming.StaticVariableCase - value: CamelCase - - key: readability-identifier-naming.StaticVariablePrefix - value: k - - key: readability-identifier-naming.MacroDefinitionCase - value: UPPER_CASE - - key: readability-identifier-naming.MemberCase - value: lower_case - - key: readability-identifier-naming.MemberSuffix - value: _ - - key: readability-identifier-naming.NamespaceCase - value: lower_case - - key: readability-identifier-naming.ParameterCase - value: lower_case - - key: readability-identifier-naming.TypeAliasCase - value: CamelCase - - key: readability-identifier-naming.TypedefCase - value: CamelCase - - key: readability-identifier-naming.VariableCase - value: lower_case - - key: readability-identifier-naming.IgnoreMainLikeFunctions - value: 1 - - key: readability-braces-around-statements.ShortStatementLines - value: 1 diff --git a/core/src/main/cpp/Android.mk b/core/src/main/cpp/Android.mk deleted file mode 100644 index cba3d605..00000000 --- a/core/src/main/cpp/Android.mk +++ /dev/null @@ -1,7 +0,0 @@ -include src/main/cpp/external/DexBuilder/Android.mk -include src/main/cpp/external/yahfa/Android.mk -include src/main/cpp/main/Android.mk - -$(call import-module,prefab/cxx) -$(call import-module,prefab/riru) -$(call import-module,prefab/dobby) diff --git a/core/src/main/cpp/Application.mk b/core/src/main/cpp/Application.mk deleted file mode 100644 index acaee188..00000000 --- a/core/src/main/cpp/Application.mk +++ /dev/null @@ -1,15 +0,0 @@ -APP_CFLAGS := -Wall -Wextra -APP_CFLAGS += -fno-stack-protector -fomit-frame-pointer -APP_CFLAGS += -Wno-builtin-macro-redefined -D__FILE__=__FILE_NAME__ -APP_CPPFLAGS := -std=c++20 -APP_CONLYFLAGS := -std=c18 -APP_LDFLAGS := -Wl,--exclude-libs,ALL -APP_STL := none - -ifneq ($(NDK_DEBUG),1) -APP_CFLAGS += -Oz -flto=thin -APP_CFLAGS += -Wno-unused -Wno-unused-parameter -Werror -APP_CFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden -APP_CFLAGS += -fno-unwind-tables -fno-asynchronous-unwind-tables -APP_LDFLAGS += -flto=thin -Wl,--thinlto-cache-policy,cache_size_bytes=300m -Wl,--thinlto-cache-dir=build/.lto-cache -Wl,--gc-sections -Wl,--strip-all -endif diff --git a/core/src/main/cpp/external/DexBuilder b/core/src/main/cpp/external/DexBuilder deleted file mode 160000 index 6273ec71..00000000 --- a/core/src/main/cpp/external/DexBuilder +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6273ec71c0c4ded162f1569c94eac098c5f87e02 diff --git a/core/src/main/cpp/external/yahfa/Android.mk b/core/src/main/cpp/external/yahfa/Android.mk deleted file mode 100644 index 100bb99e..00000000 --- a/core/src/main/cpp/external/yahfa/Android.mk +++ /dev/null @@ -1,10 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := yahfa -LOCAL_C_INCLUDES := $(LOCAL_PATH)/include -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include -LOCAL_SRC_FILES := src/HookMain.cpp src/trampoline.cpp -LOCAL_EXPORT_LDLIBS := -llog -LOCAL_STATIC_LIBRARIES := libcxx -include $(BUILD_STATIC_LIBRARY) diff --git a/core/src/main/cpp/external/yahfa/include/HookMain.h b/core/src/main/cpp/external/yahfa/include/HookMain.h deleted file mode 100644 index dece1c80..00000000 --- a/core/src/main/cpp/external/yahfa/include/HookMain.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef HOOK_MAIN_H -#define HOOK_MAIN_H - -#include - -namespace yahfa { - constexpr uint32_t kAccPublic = 0x0001; // class, field, method, ic - constexpr uint32_t kAccPrivate = 0x0002; // field, method, ic - constexpr uint32_t kAccProtected = 0x0004; // field, method, ic - constexpr uint32_t kAccStatic = 0x0008; // field, method, ic - constexpr uint32_t kAccNative = 0x0100; // method - - void init(JNIEnv *env, jclass clazz, jint sdkVersion); - - jobject findMethodNative(JNIEnv *env, jclass clazz, - jclass targetClass, jstring methodName, - jstring methodSig); - - jboolean backupAndHookNative(JNIEnv *env, jclass clazz, - jobject target, jobject hook, - jobject backup); - - void *getArtMethod(JNIEnv *env, jobject jmethod); - - uint32_t getAccessFlags(void* art_method); - - void setAccessFlags(void* art_method, uint32_t access_flags); - - void* getEntryPoint(void* art_method); - - void setEntryPoint(void* art_method, void* entry_point); -} - -#endif // HOOK_MAIN_H diff --git a/core/src/main/cpp/external/yahfa/include/trampoline.h b/core/src/main/cpp/external/yahfa/include/trampoline.h deleted file mode 100644 index a79ac2a2..00000000 --- a/core/src/main/cpp/external/yahfa/include/trampoline.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// Created by liuruikai756 on 05/07/2017. -// - -#ifndef YAHFA_TAMPOLINE_H -#define YAHFA_TAMPOLINE_H - -namespace yahfa { - void setupTrampoline(); - - void *genTrampoline(void *hookMethod); -} - -#endif //YAHFA_TAMPOLINE_H diff --git a/core/src/main/cpp/external/yahfa/src/HookMain.cpp b/core/src/main/cpp/external/yahfa/src/HookMain.cpp deleted file mode 100644 index 0fa6d3f7..00000000 --- a/core/src/main/cpp/external/yahfa/src/HookMain.cpp +++ /dev/null @@ -1,213 +0,0 @@ -#include "jni.h" -#include -#include -#include - -#include "common.h" -#include "trampoline.h" -#include "HookMain.h" - - -namespace yahfa { - size_t OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod; - int SDKVersion; - - namespace { - constexpr size_t OFFSET_access_flags_in_ArtMethod = 4; - constexpr uint32_t kAccCompileDontBother = 0x02000000; - constexpr uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000; - - size_t ArtMethodSize; - uint32_t kAccPreCompiled = 0x00200000; - - jfieldID fieldArtMethod = nullptr; - - constexpr inline uint32_t read32(void *addr) { - return *((uint32_t *) addr); - } - - constexpr inline void write32(void *addr, uint32_t value) { - *((uint32_t *) addr) = value; - } - - constexpr inline void *readAddr(void *addr) { - return *((void **) addr); - } - - constexpr inline void writeAddr(void *addr, void *value) { - *((void **) addr) = value; - } - - void setNonCompilable(void *method) { - uint32_t access_flags = getAccessFlags(method); - LOGI("setNonCompilable: access flags is 0x%x", access_flags); - access_flags |= kAccCompileDontBother; - if (SDKVersion >= __ANDROID_API_R__) - access_flags &= ~kAccPreCompiled; - setAccessFlags(method, access_flags); - } - - void setPrivate(void *method) { - uint32_t access_flags = getAccessFlags(method); - if (!(access_flags & kAccStatic)) { - LOGI("setPrivate: access flags is 0x%x", access_flags); - access_flags |= kAccPrivate; - access_flags &= ~kAccProtected; - access_flags &= ~kAccPublic; - setAccessFlags(method, access_flags); - } - } - - int doBackupAndHook(void *targetMethod, void *hookMethod, void *backupMethod) { - LOGI("target method is at %p, hook method is at %p, backup method is at %p", - targetMethod, hookMethod, backupMethod); - - - // set kAccCompileDontBother for a method we do not want the compiler to compile - // so that we don't need to worry about hotness_count_ - setNonCompilable(targetMethod); - setNonCompilable(hookMethod); - - if (backupMethod) {// do method backup - // have to copy the whole target ArtMethod here - // if the target method calls other methods which are to be resolved - // then ToDexPC would be invoked for the caller(origin method) - // in which case ToDexPC would use the entrypoint as a base for mapping pc to dex offset - // so any changes to the target method's entrypoint would result in a wrong dex offset - // and artQuickResolutionTrampoline would fail for methods called by the origin method - memcpy(backupMethod, targetMethod, ArtMethodSize); - setPrivate(backupMethod); - } - - // replace entry point - void *newEntrypoint = genTrampoline(hookMethod); - LOGI("origin ep is %p, new ep is %p", - getEntryPoint(targetMethod), - newEntrypoint - ); - if (newEntrypoint) { - setEntryPoint(targetMethod, newEntrypoint); - } else { - LOGE("failed to allocate space for trampoline of target method"); - return 1; - } - - if (SDKVersion >= __ANDROID_API_Q__) { - uint32_t access_flags = getAccessFlags(targetMethod); - // On API 29 whether to use the fast path or not is cached in the ART method structure - access_flags &= ~kAccFastInterpreterToInterpreterInvoke; - setAccessFlags(targetMethod, access_flags); - } - - LOGI("hook and backup done"); - return 0; - } - - } - - void init(JNIEnv *env, [[maybe_unused]] jclass clazz, jint sdkVersion) { - SDKVersion = sdkVersion; - jclass classExecutable = env->FindClass("java/lang/reflect/Executable"); - fieldArtMethod = env->GetFieldID(classExecutable, "artMethod", "J"); - env->DeleteLocalRef(classExecutable); - LOGI("init to SDK %d", sdkVersion); - switch (sdkVersion) { - default: - LOGE("not compatible with SDK %d", sdkVersion); - case __ANDROID_API_FUTURE__: - // fallthrough - case __ANDROID_API_T__: - case 32: // stupid Google forgot to add 12L - case __ANDROID_API_S__: - OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = - roundUpToPtrSize(4 * 3 + 2 * 2) + pointer_size; - ArtMethodSize = roundUpToPtrSize(4 * 3 + 2 * 2) + pointer_size * 2; - kAccPreCompiled = 0x00800000; - break; - case __ANDROID_API_R__: - case __ANDROID_API_Q__: - case __ANDROID_API_P__: - OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = - roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size; - ArtMethodSize = roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 2; - break; - case __ANDROID_API_O_MR1__: - OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = - roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 2; - ArtMethodSize = roundUpToPtrSize(4 * 4 + 2 * 2) + pointer_size * 3; - break; - } - - setupTrampoline(); - } - - void *getArtMethod(JNIEnv *env, jobject jmethod) { - if (jmethod == nullptr || fieldArtMethod == nullptr) { - return nullptr; - } else { - return (void *) env->GetLongField(jmethod, fieldArtMethod); - } - } - - uint32_t getAccessFlags(void *art_method) { - return read32((char *) art_method + OFFSET_access_flags_in_ArtMethod); - } - - void setAccessFlags(void *art_method, uint32_t access_flags) { - write32((char *) art_method + OFFSET_access_flags_in_ArtMethod, access_flags); - } - - void *getEntryPoint(void *art_method) { - return readAddr( - (char *) art_method + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod); - } - - void setEntryPoint(void *art_method, void *entry_point) { - writeAddr((char *) art_method + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod, - entry_point); - } - - jobject findMethodNative(JNIEnv *env, [[maybe_unused]] jclass clazz, - jclass targetClass, - jstring methodName, - jstring methodSig) { - const char *c_methodName = env->GetStringUTFChars(methodName, nullptr); - const char *c_methodSig = env->GetStringUTFChars(methodSig, nullptr); - jobject ret = nullptr; - - - //Try both GetMethodID and GetStaticMethodID -- Whatever works :) - jmethodID method = env->GetMethodID(targetClass, c_methodName, c_methodSig); - if (!env->ExceptionCheck()) { - ret = env->ToReflectedMethod(targetClass, method, JNI_FALSE); - } else { - env->ExceptionClear(); - method = env->GetStaticMethodID(targetClass, c_methodName, c_methodSig); - if (!env->ExceptionCheck()) { - ret = env->ToReflectedMethod(targetClass, method, JNI_TRUE); - } else { - env->ExceptionClear(); - } - } - - env->ReleaseStringUTFChars(methodName, c_methodName); - env->ReleaseStringUTFChars(methodSig, c_methodSig); - return ret; - } - - jboolean backupAndHookNative(JNIEnv *env, [[maybe_unused]] jclass clazz, - jobject target, jobject hook, - jobject backup) { - - if (!doBackupAndHook(getArtMethod(env, target), - getArtMethod(env, hook), - getArtMethod(env, backup) - )) { - env->NewGlobalRef(hook); // keep a global ref so that the hook method would not be GCed - if (backup) env->NewGlobalRef(backup); - return JNI_TRUE; - } - return JNI_FALSE; - - } -} diff --git a/core/src/main/cpp/external/yahfa/src/common.h b/core/src/main/cpp/external/yahfa/src/common.h deleted file mode 100644 index 1ada70c8..00000000 --- a/core/src/main/cpp/external/yahfa/src/common.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// Created by liuruikai756 on 05/07/2017. -// -#include - -#ifndef YAHFA_COMMON_H -#define YAHFA_COMMON_H - -//#define DEBUG -//#define LOG_DISABLED - -#ifdef LOG_DISABLED -#define LOGI(...) -#define LOGW(...) -#define LOGE(...) -#else -#define LOG_TAG "LSPosed" -#ifndef NDEBUG -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) -#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) -#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) -#else -#define LOGI(...) -#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) -#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) -#endif // DEBUG -#endif // LOG_DISABLED - -#define pointer_size sizeof(void*) -#define roundUpToPtrSize(v) (v + pointer_size - 1 - ((v + pointer_size - 1) & (pointer_size - 1))) - -#endif //YAHFA_COMMON_H diff --git a/core/src/main/cpp/external/yahfa/src/trampoline.cpp b/core/src/main/cpp/external/yahfa/src/trampoline.cpp deleted file mode 100644 index 98d29f00..00000000 --- a/core/src/main/cpp/external/yahfa/src/trampoline.cpp +++ /dev/null @@ -1,147 +0,0 @@ -// -// Created by liuruikai756 on 05/07/2017. -// -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "trampoline.h" - -static_assert(std::endian::native == std::endian::little, "Unsupported architecture"); - -namespace yahfa { - extern size_t OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod; - - union Trampoline { - uintptr_t addr; - unsigned count: 12; - }; - - static_assert(sizeof(Trampoline) == sizeof(uintptr_t), "Unsupported architecture"); - static_assert(std::atomic_uintptr_t::is_always_lock_free, "Unsupported architecture"); - -// trampoline: -// 1. set eax/r0/x0 to the hook ArtMethod addr -// 2. jump into its entry point -#if defined(__i386__) -// b8 78 56 34 12 ; mov eax, 0x12345678 (addr of the hook method) -// ff 70 20 ; push DWORD PTR [eax + 0x20] -// c3 ; ret - unsigned char trampoline[] = "\xb8\x78\x56\x34\x12\xff\x70\x20\xc3"; -#elif defined(__x86_64__) - // 48 bf 78 56 34 12 78 56 34 12 ; movabs rdi, 0x1234567812345678 - // ff 77 20 ; push QWORD PTR [rdi + 0x20] - // c3 ; ret - unsigned char trampoline[] = "\x48\xbf\x78\x56\x34\x12\x78\x56\x34\x12\xff\x77\x20\xc3"; - -#elif defined(__arm__) - // 00 00 9F E5 ; ldr r0, [pc, #0] - // 20 F0 90 E5 ; ldr pc, [r0, 0x20] - // 78 56 34 12 ; 0x12345678 (addr of the hook method) - unsigned char trampoline[] = "\x00\x00\x9f\xe5\x20\xf0\x90\xe5\x78\x56\x34\x12"; - -#elif defined(__aarch64__) - // 60 00 00 58 ; ldr x0, 12 - // 10 00 40 F8 ; ldr x16, [x0, #0x00] - // 00 02 1f d6 ; br x16 - // 78 56 34 12 - // 78 56 34 12 ; 0x1234567812345678 (addr of the hook method) - unsigned char trampoline[] = "\x60\x00\x00\x58\x10\x00\x40\xf8\x00\x02\x1f\xd6\x78\x56\x34\x12\x78\x56\x34\x12"; -#endif - static std::atomic_uintptr_t trampoline_pool{0}; - static std::atomic_flag trampoline_lock{false}; - static constexpr size_t trampolineSize = roundUpToPtrSize(sizeof(trampoline)); - static constexpr size_t pageSize = 4096; - static constexpr size_t trampolineNumPerPage = pageSize / trampolineSize; - - static inline void FlushCache(void *addr, size_t size) { - __builtin___clear_cache((char *) addr, (char *) ((uintptr_t) addr + size)); - } - - void *genTrampoline(void *hookMethod) { - unsigned count; - uintptr_t addr; - while (true) { - auto tl = Trampoline{.addr = trampoline_pool.fetch_add(1, std::memory_order_release)}; - count = tl.count; - addr = tl.addr & ~uintptr_t(0xfff); - if (addr == 0 || count >= trampolineNumPerPage) { - if (trampoline_lock.test_and_set(std::memory_order_acq_rel)) { - trampoline_lock.wait(true, std::memory_order_acquire); - continue; - } else { - addr = reinterpret_cast(mmap(nullptr, pageSize, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); - if (addr == reinterpret_cast(MAP_FAILED)) { - LOGE("mmap failed, errno = %s", strerror(errno)); - trampoline_lock.clear(std::memory_order_release); - trampoline_lock.notify_all(); - return nullptr; - } - count = 0; - tl.addr = addr; - tl.count = count + 1; - trampoline_pool.store(tl.addr, std::memory_order_release); - trampoline_lock.clear(std::memory_order_release); - trampoline_lock.notify_all(); - } - } - LOGI("trampoline: count = %u, addr = %zx, target = %zx", count, addr, - addr + count * trampolineSize); - addr = addr + count * trampolineSize; - break; - } - unsigned char *targetAddr = reinterpret_cast(addr); - memcpy(targetAddr, trampoline, - sizeof(trampoline)); // do not use trampolineSize since it's a rounded size - - // replace with the actual ArtMethod addr -#if defined(__i386__) - memcpy(targetAddr + 1, &hookMethod, pointer_size); - -#elif defined(__x86_64__) - memcpy((char*)targetAddr + 2, &hookMethod, pointer_size); - -#elif defined(__arm__) - memcpy(targetAddr+8, &hookMethod, pointer_size); - -#elif defined(__aarch64__) - memcpy(targetAddr + 12, &hookMethod, pointer_size); - -#else -#error Unsupported architecture -#endif - FlushCache(targetAddr, sizeof(trampoline)); - - return targetAddr; - } - - void setupTrampoline() { -#if defined(__i386__) - trampoline[7] = (unsigned char) OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod; -#elif defined(__x86_64__) - trampoline[12] = (unsigned char)OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod; -#elif defined(__arm__) - trampoline[4] = (unsigned char)OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod; -#elif defined(__aarch64__) - trampoline[5] |= - ((unsigned char) OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod) << 4; - trampoline[6] |= - ((unsigned char) OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod) >> 4; -#else -#error Unsupported architecture -#endif - } - -} diff --git a/core/src/main/cpp/main/Android.mk b/core/src/main/cpp/main/Android.mk deleted file mode 100644 index bf47e448..00000000 --- a/core/src/main/cpp/main/Android.mk +++ /dev/null @@ -1,28 +0,0 @@ -LOCAL_PATH := $(call my-dir) -define walk - $(wildcard $(1)) $(foreach e, $(wildcard $(1)/*), $(call walk, $(e))) -endef - -include $(CLEAR_VARS) -LOCAL_MODULE := lspd -LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/src -FILE_LIST := $(filter %.cpp, $(call walk, $(LOCAL_PATH)/src)) -LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%) api/config.cpp -LOCAL_STATIC_LIBRARIES := cxx riru yahfa dobby dex_builder -ifeq ($(API), riru) -LOCAL_SRC_FILES += api/riru_main.cpp -else ifeq ($(API), zygisk) -LOCAL_SRC_FILES += api/zygisk_main.cpp -endif -LOCAL_CFLAGS += -DINJECTED_AID=${INJECTED_AID} -LOCAL_LDLIBS := -llog -include $(BUILD_SHARED_LIBRARY) - -$(LOCAL_PATH)/api/config.cpp : FORCE - $(file > $@,namespace lspd {) - $(file >> $@,extern const int versionCode = ${VERSION_CODE};) - $(file >> $@,extern const int apiVersion = ${API_VERSION};) - $(file >> $@,extern const char* const versionName = "${VERSION_NAME}";) - $(file >> $@,extern const char* const moduleName = "${MODULE_NAME}";) - $(file >> $@,}) -FORCE: ; diff --git a/core/src/main/cpp/main/include/art/runtime/art_method.h b/core/src/main/cpp/main/include/art/runtime/art_method.h deleted file mode 100644 index 93fc1fa7..00000000 --- a/core/src/main/cpp/main/include/art/runtime/art_method.h +++ /dev/null @@ -1,50 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -// -// Created by εŒθ‰ι…Έι…― on 12/19/20. -// - -#ifndef LSPOSED_ART_METHOD_H -#define LSPOSED_ART_METHOD_H - -#include - -namespace art { - namespace art_method { - CREATE_MEM_FUNC_SYMBOL_ENTRY(std::string, PrettyMethod, void *thiz, bool with_signature) { - if (thiz == nullptr) [[unlikely]] - return "null"; - if (PrettyMethodSym) [[likely]] - return PrettyMethodSym(thiz, with_signature); - else return "null sym"; - } - - inline std::string PrettyMethod(void *thiz) { - return PrettyMethod(thiz, true); - } - - inline void Setup(const SandHook::ElfImg &handle) { - RETRIEVE_MEM_FUNC_SYMBOL(PrettyMethod, "_ZN3art9ArtMethod12PrettyMethodEb"); - } - } -} - -#endif //LSPOSED_ART_METHOD_H diff --git a/core/src/main/cpp/main/include/art/runtime/class_linker.h b/core/src/main/cpp/main/include/art/runtime/class_linker.h deleted file mode 100644 index a50518b5..00000000 --- a/core/src/main/cpp/main/include/art/runtime/class_linker.h +++ /dev/null @@ -1,149 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -#pragma once - -#include -#include -#include -#include "runtime.h" -#include "config.h" -#include "jni_env_ext.h" -#include "context.h" -#include "jni/yahfa.h" -#include "utils.h" -#include "HookMain.h" - -namespace art { - - class ClassLinker : public lspd::HookedObject { - private: - [[gnu::always_inline]] - static auto MaybeDelayHook(void *clazz_ptr) { - std::vector> out; - art::mirror::Class mirror_class(clazz_ptr); - auto class_def = mirror_class.GetClassDef(); - if (!class_def) return out; - auto set = lspd::isClassHooked(class_def); - if (!set.empty()) [[unlikely]] { - LOGD("Pending hook for %p (%s)", clazz_ptr, - art::mirror::Class(clazz_ptr).GetDescriptor().c_str()); - for (auto art_method : set) { - out.emplace_back(art_method, yahfa::getEntryPoint(art_method)); - } - } - return out; - } - - [[gnu::always_inline]] - static void FixTrampoline(const std::vector>& methods) { - for (const auto &[art_method, old_trampoline] : methods) { - auto *new_trampoline = yahfa::getEntryPoint(art_method); - auto *backup = lspd::isHooked(art_method); - if (backup && new_trampoline != old_trampoline) { - yahfa::setEntryPoint(backup, new_trampoline); - yahfa::setEntryPoint(art_method, old_trampoline); - } - } - } - - CREATE_MEM_HOOK_STUB_ENTRIES( - "_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE", - void, FixupStaticTrampolines, (void * thiz, void * clazz_ptr), { - auto b = MaybeDelayHook(clazz_ptr); - backup(thiz, clazz_ptr); - FixTrampoline(b); - }); - - CREATE_MEM_HOOK_STUB_ENTRIES( - "_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6ThreadENS_6ObjPtrINS_6mirror5ClassEEE", - void, FixupStaticTrampolinesWithThread, - (void * thiz, void * self, void * clazz_ptr), { - auto b = MaybeDelayHook(clazz_ptr); - backup(thiz, self, clazz_ptr); - FixTrampoline(b); - }); - - CREATE_MEM_FUNC_SYMBOL_ENTRY(void, MakeInitializedClassesVisiblyInitialized, void *thiz, - void *self, bool wait) { - if (MakeInitializedClassesVisiblyInitializedSym) [[likely]] - MakeInitializedClassesVisiblyInitializedSym(thiz, self, wait); - } - - CREATE_MEM_FUNC_SYMBOL_ENTRY(void, SetEntryPointsToInterpreter, void *thiz, - void *art_method) { - if (SetEntryPointsToInterpreterSym) [[likely]] - SetEntryPointsToInterpreterSym(thiz, art_method); - } - - CREATE_HOOK_STUB_ENTRIES( - "_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv", - bool, ShouldUseInterpreterEntrypoint, (void * art_method, - const void *quick_code), { - if (quick_code != nullptr && lspd::isHooked(art_method)) [[unlikely]] { - return false; - } - return backup(art_method, quick_code); - }); - - CREATE_FUNC_SYMBOL_ENTRY(void, art_quick_to_interpreter_bridge, void*) {} - CREATE_FUNC_SYMBOL_ENTRY(void, art_quick_generic_jni_trampoline, void*) {} - - CREATE_HOOK_STUB_ENTRIES("_ZN3art11interpreter29ShouldStayInSwitchInterpreterEPNS_9ArtMethodE", - bool, ShouldStayInSwitchInterpreter ,(void* art_method), { - if (lspd::isHooked(art_method)) [[unlikely]] { - return false; - } - return backup(art_method); - }); - - public: - // @ApiSensitive(Level.MIDDLE) - inline static void Setup(const SandHook::ElfImg &handle) { - RETRIEVE_MEM_FUNC_SYMBOL(SetEntryPointsToInterpreter, - "_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE"); - - lspd::HookSyms(handle, ShouldUseInterpreterEntrypoint, ShouldStayInSwitchInterpreter); - - lspd::HookSyms(handle, FixupStaticTrampolinesWithThread, FixupStaticTrampolines); - - RETRIEVE_FUNC_SYMBOL(art_quick_to_interpreter_bridge, "art_quick_to_interpreter_bridge"); - RETRIEVE_FUNC_SYMBOL(art_quick_generic_jni_trampoline, "art_quick_generic_jni_trampoline"); - - LOGD("art_quick_to_interpreter_bridge = %p", art_quick_to_interpreter_bridgeSym); - LOGD("art_quick_generic_jni_trampoline = %p", art_quick_generic_jni_trampolineSym); - } - - [[gnu::always_inline]] - static void SetEntryPointsToInterpreter(void *art_method) { - if (art_quick_to_interpreter_bridgeSym && art_quick_generic_jni_trampolineSym) [[likely]] { - if (yahfa::getAccessFlags(art_method) & yahfa::kAccNative) [[unlikely]] { - yahfa::setEntryPoint(art_method, - reinterpret_cast(art_quick_generic_jni_trampolineSym)); - } else { - yahfa::setEntryPoint(art_method, - reinterpret_cast(art_quick_to_interpreter_bridgeSym)); - } - } - SetEntryPointsToInterpreter(nullptr, art_method); - } - - }; -} diff --git a/core/src/main/cpp/main/include/art/runtime/gc/collector_type.h b/core/src/main/cpp/main/include/art/runtime/gc/collector_type.h deleted file mode 100644 index fd7cc9be..00000000 --- a/core/src/main/cpp/main/include/art/runtime/gc/collector_type.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_GC_COLLECTOR_GC_TYPE_H_ -#define ART_RUNTIME_GC_COLLECTOR_GC_TYPE_H_ - -#include - -namespace art { - namespace gc { -// Which types of collections are able to be performed. - enum CollectorType { - // No collector selected. - kCollectorTypeNone, - // Non concurrent mark-sweep. - kCollectorTypeMS, - // Concurrent mark-sweep. - kCollectorTypeCMS, - // Semi-space / mark-sweep hybrid, enables compaction. - kCollectorTypeSS, - // Heap trimming collector, doesn't do any actual collecting. - kCollectorTypeHeapTrim, - // A (mostly) concurrent copying collector. - kCollectorTypeCC, - // The background compaction of the concurrent copying collector. - kCollectorTypeCCBackground, - // Instrumentation critical section fake collector. - kCollectorTypeInstrumentation, - // Fake collector for adding or removing application image spaces. - kCollectorTypeAddRemoveAppImageSpace, - // Fake collector used to implement exclusion between GC and debugger. - kCollectorTypeDebugger, - // A homogeneous space compaction collector used in background transition - // when both foreground and background collector are CMS. - kCollectorTypeHomogeneousSpaceCompact, - // Class linker fake collector. - kCollectorTypeClassLinker, - // JIT Code cache fake collector. - kCollectorTypeJitCodeCache, - // Hprof fake collector. - kCollectorTypeHprof, - // Fake collector for installing/removing a system-weak holder. - kCollectorTypeAddRemoveSystemWeakHolder, - // Fake collector type for GetObjectsAllocated - kCollectorTypeGetObjectsAllocated, - // Fake collector type for ScopedGCCriticalSection - kCollectorTypeCriticalSection, - }; - } // namespace gc -} // namespace art -#endif // ART_RUNTIME_GC_COLLECTOR_GC_TYPE_H_ \ No newline at end of file diff --git a/core/src/main/cpp/main/include/art/runtime/gc/gc_cause.h b/core/src/main/cpp/main/include/art/runtime/gc/gc_cause.h deleted file mode 100644 index 5ba97b0e..00000000 --- a/core/src/main/cpp/main/include/art/runtime/gc/gc_cause.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_GC_GC_CAUSE_H_ -#define ART_RUNTIME_GC_GC_CAUSE_H_ - -#include - -namespace art { - namespace gc { -// What caused the GC? - enum GcCause { - // Invalid GC cause used as a placeholder. - kGcCauseNone, - // GC triggered by a failed allocation. Thread doing allocation is blocked waiting for GC before - // retrying allocation. - kGcCauseForAlloc, - // A background GC trying to ensure there is free memory ahead of allocations. - kGcCauseBackground, - // An explicit System.gc() call. - kGcCauseExplicit, - // GC triggered for a native allocation when NativeAllocationGcWatermark is exceeded. - // (This may be a blocking GC depending on whether we run a non-concurrent collector). - kGcCauseForNativeAlloc, - // GC triggered for a collector transition. - kGcCauseCollectorTransition, - // Not a real GC cause, used when we disable moving GC (currently for GetPrimitiveArrayCritical). - kGcCauseDisableMovingGc, - // Not a real GC cause, used when we trim the heap. - kGcCauseTrim, - // Not a real GC cause, used to implement exclusion between GC and instrumentation. - kGcCauseInstrumentation, - // Not a real GC cause, used to add or remove app image spaces. - kGcCauseAddRemoveAppImageSpace, - // Not a real GC cause, used to implement exclusion between GC and debugger. - kGcCauseDebugger, - // GC triggered for background transition when both foreground and background collector are CMS. - kGcCauseHomogeneousSpaceCompact, - // Class linker cause, used to guard filling art methods with special values. - kGcCauseClassLinker, - // Not a real GC cause, used to implement exclusion between code cache metadata and GC. - kGcCauseJitCodeCache, - // Not a real GC cause, used to add or remove system-weak holders. - kGcCauseAddRemoveSystemWeakHolder, - // Not a real GC cause, used to prevent hprof running in the middle of GC. - kGcCauseHprof, - // Not a real GC cause, used to prevent GetObjectsAllocated running in the middle of GC. - kGcCauseGetObjectsAllocated, - // GC cause for the profile saver. - kGcCauseProfileSaver, - }; - } // namespace gc -} // namespace art -#endif // ART_RUNTIME_GC_GC_CAUSE_H_ \ No newline at end of file diff --git a/core/src/main/cpp/main/include/art/runtime/gc/scoped_gc_critical_section.h b/core/src/main/cpp/main/include/art/runtime/gc/scoped_gc_critical_section.h deleted file mode 100644 index fab430c2..00000000 --- a/core/src/main/cpp/main/include/art/runtime/gc/scoped_gc_critical_section.h +++ /dev/null @@ -1,65 +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 . - * - * Copyright (C) 2021 LSPosed Contributors - */ - -#ifndef LSPOSED_SCOPED_GC_CRITICAL_SECTION_H -#define LSPOSED_SCOPED_GC_CRITICAL_SECTION_H - -#include "gc_cause.h" -#include "collector_type.h" - -namespace art { - namespace gc { - - class GCCriticalSection { - private: - [[maybe_unused]] void* self_; - [[maybe_unused]] const char* section_name_; - }; - - class ScopedGCCriticalSection { - CREATE_MEM_FUNC_SYMBOL_ENTRY(void, constructor, void *thiz, void* self, GcCause cause, CollectorType collector_type) { - if (thiz == nullptr) [[unlikely]] return; - if (constructorSym) [[likely]] - return constructorSym(thiz, self, cause, collector_type); - } - CREATE_MEM_FUNC_SYMBOL_ENTRY(void, destructor, void *thiz) { - if (thiz == nullptr) [[unlikely]] return; - if (destructorSym) [[likely]] - return destructorSym(thiz); - } - public: - ScopedGCCriticalSection(void *self, GcCause cause, CollectorType collector_type) { - constructor(this, self, cause, collector_type); - } - ~ScopedGCCriticalSection() { - destructor(this); - } - - inline static void Setup(const SandHook::ElfImg &handle) { - RETRIEVE_MEM_FUNC_SYMBOL(constructor, "_ZN3art2gc23ScopedGCCriticalSectionC2EPNS_6ThreadENS0_7GcCauseENS0_13CollectorTypeE"); - RETRIEVE_MEM_FUNC_SYMBOL(destructor, "_ZN3art2gc23ScopedGCCriticalSectionD2Ev"); - } - private: - [[maybe_unused]] GCCriticalSection critical_section_; - [[maybe_unused]] const char* old_no_suspend_reason_; - }; - } -} - -#endif //LSPOSED_SCOPED_GC_CRITICAL_SECTION_H diff --git a/core/src/main/cpp/main/include/art/runtime/jit/jit_code_cache.h b/core/src/main/cpp/main/include/art/runtime/jit/jit_code_cache.h deleted file mode 100644 index 73f2ec10..00000000 --- a/core/src/main/cpp/main/include/art/runtime/jit/jit_code_cache.h +++ /dev/null @@ -1,52 +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 . - * - * Copyright (C) 2021 LSPosed Contributors - */ - -#pragma once - -#include -#include -#include "jni/yahfa.h" - -namespace art { - namespace jit { - namespace jit_code_cache { - CREATE_MEM_FUNC_SYMBOL_ENTRY(void, MoveObsoleteMethod, void *thiz, - void *old_method, void *new_method) { - if (MoveObsoleteMethodSym) - [[likely]] MoveObsoleteMethodSym(thiz, old_method, new_method); - } - - CREATE_MEM_HOOK_STUB_ENTRIES( - "_ZN3art3jit12JitCodeCache19GarbageCollectCacheEPNS_6ThreadE", - void, GarbageCollectCache, (void * thiz, void * self), { - LOGD("Before jit cache gc, moving hooked methods"); - for (auto[target, backup] : lspd::getJitMovements()) { - MoveObsoleteMethod(thiz, target, backup); - } - backup(thiz, self); - }); - - inline void Setup(const SandHook::ElfImg &handle) { - RETRIEVE_MEM_FUNC_SYMBOL(MoveObsoleteMethod, - "_ZN3art3jit12JitCodeCache18MoveObsoleteMethodEPNS_9ArtMethodES3_"); - lspd::HookSyms(handle, GarbageCollectCache); - } - } - } -} diff --git a/core/src/main/cpp/main/include/art/runtime/jni_env_ext.h b/core/src/main/cpp/main/include/art/runtime/jni_env_ext.h deleted file mode 100644 index 57b9a241..00000000 --- a/core/src/main/cpp/main/include/art/runtime/jni_env_ext.h +++ /dev/null @@ -1,58 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -#pragma once - -#include "jni.h" -#include "base/object.h" - -namespace art { - - class JNIEnvExt : lspd::HookedObject { - - private: - CREATE_MEM_FUNC_SYMBOL_ENTRY(jobject, NewLocalRef, void *thiz, void *mirror_ptr) { - return NewLocalRefSym(thiz, mirror_ptr); - } - - CREATE_MEM_FUNC_SYMBOL_ENTRY(void, DeleteLocalRef, void *thiz, jobject obj) { - return DeleteLocalRefSym(thiz, obj); - } - - public: - JNIEnvExt(void *thiz) : HookedObject(thiz) {} - - // @ApiSensitive(Level.MIDDLE) - inline static void Setup(const SandHook::ElfImg &handle) { - RETRIEVE_MEM_FUNC_SYMBOL(NewLocalRef, "_ZN3art9JNIEnvExt11NewLocalRefEPNS_6mirror6ObjectE"); - RETRIEVE_MEM_FUNC_SYMBOL(DeleteLocalRef, "_ZN3art9JNIEnvExt14DeleteLocalRefEP8_jobject"); - } - - jobject NewLocalRefer(void *mirror_ptr) { - return NewLocalRef(thiz_, mirror_ptr); - } - - void DeleteLocalRef(jobject obj) { - DeleteLocalRef(thiz_, obj); - } - }; - - -} diff --git a/core/src/main/cpp/main/include/art/runtime/mirror/class.h b/core/src/main/cpp/main/include/art/runtime/mirror/class.h deleted file mode 100644 index da58a3c7..00000000 --- a/core/src/main/cpp/main/include/art/runtime/mirror/class.h +++ /dev/null @@ -1,80 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -#pragma once - -#include -#include "base/object.h" - -namespace art { - namespace mirror { - - using namespace std; - using namespace lspd; - - class Class : public HookedObject { - - private: - - CREATE_MEM_FUNC_SYMBOL_ENTRY(const char *, GetDescriptor, void *thiz, - std::string *storage) { - if (GetDescriptorSym) - return GetDescriptorSym(thiz, storage); - else - return ""; - } - - CREATE_MEM_FUNC_SYMBOL_ENTRY(void*, GetClassDef, void* thiz) { - if (GetClassDefSym) [[likely]] - return GetClassDefSym(thiz); - return nullptr; - } - - public: - Class(void *thiz) : HookedObject(thiz) {} - - // @ApiSensitive(Level.MIDDLE) - inline static void Setup(const SandHook::ElfImg &handle) { - RETRIEVE_MEM_FUNC_SYMBOL(GetDescriptor, "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112" - "basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE"); - RETRIEVE_MEM_FUNC_SYMBOL(GetClassDef, "_ZN3art6mirror5Class11GetClassDefEv"); - } - - const char *GetDescriptor(std::string *storage) { - if (thiz_ && GetDescriptorSym) { - return GetDescriptor(thiz_, storage); - } - return ""; - } - - std::string GetDescriptor() { - std::string storage; - return GetDescriptor(&storage); - } - - void *GetClassDef() { - if(thiz_ && GetClassDefSym) - return GetClassDef(thiz_); - return nullptr; - } - }; - - } // namespace mirror -} // namespace art diff --git a/core/src/main/cpp/main/include/art/runtime/thread.h b/core/src/main/cpp/main/include/art/runtime/thread.h deleted file mode 100644 index 6c8690a1..00000000 --- a/core/src/main/cpp/main/include/art/runtime/thread.h +++ /dev/null @@ -1,64 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -#pragma once - -#include - -namespace art { - - class Thread : public lspd::HookedObject { - - CREATE_MEM_FUNC_SYMBOL_ENTRY(lspd::ObjPtr, DecodeJObject, void *thiz, jobject obj) { - if (DecodeJObjectSym) - return DecodeJObjectSym(thiz, obj); - else - return {.data=nullptr}; - } - - CREATE_FUNC_SYMBOL_ENTRY(void *, CurrentFromGdb) { - if (CurrentFromGdbSym) [[likely]] - return CurrentFromGdbSym(); - else - return nullptr; - } - - public: - Thread(void *thiz) : HookedObject(thiz) {} - - inline static Thread Current() { - return Thread(CurrentFromGdb()); - } - - inline static void Setup(const SandHook::ElfImg &handle) { - RETRIEVE_MEM_FUNC_SYMBOL(DecodeJObject, - "_ZNK3art6Thread13DecodeJObjectEP8_jobject"); - RETRIEVE_FUNC_SYMBOL(CurrentFromGdb, - "_ZN3art6Thread14CurrentFromGdbEv"); - } - - void *DecodeJObject(jobject obj) { - if (thiz_ && DecodeJObjectSym) [[likely]] { - return DecodeJObject(thiz_, obj).data; - } - return nullptr; - } - }; -} diff --git a/core/src/main/cpp/main/include/art/runtime/thread_list.h b/core/src/main/cpp/main/include/art/runtime/thread_list.h deleted file mode 100644 index c21bf800..00000000 --- a/core/src/main/cpp/main/include/art/runtime/thread_list.h +++ /dev/null @@ -1,53 +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 . - * - * Copyright (C) 2021 LSPosed Contributors - */ - -#ifndef LSPOSED_THREAD_LIST_H -#define LSPOSED_THREAD_LIST_H - -namespace art { - namespace thread_list { - - class ScopedSuspendAll { - CREATE_MEM_FUNC_SYMBOL_ENTRY(void, constructor, void *thiz, const char * cause, bool long_suspend) { - if (thiz == nullptr) [[unlikely]] return; - if (constructorSym) [[likely]] - return constructorSym(thiz, cause, long_suspend); - } - CREATE_MEM_FUNC_SYMBOL_ENTRY(void, destructor, void *thiz) { - if (thiz == nullptr) [[unlikely]] return; - if (destructorSym) [[likely]] - return destructorSym(thiz); - } - public: - ScopedSuspendAll(const char * cause, bool long_suspend) { - constructor(this, cause, long_suspend); - } - ~ScopedSuspendAll() { - destructor(this); - } - - inline static void Setup(const SandHook::ElfImg &handle) { - RETRIEVE_MEM_FUNC_SYMBOL(constructor, "_ZN3art16ScopedSuspendAllC2EPKcb"); - RETRIEVE_MEM_FUNC_SYMBOL(destructor, "_ZN3art16ScopedSuspendAllD2Ev"); - } - }; - } -} - -#endif //LSPOSED_THREAD_LIST_H diff --git a/core/src/main/cpp/main/include/base/object.h b/core/src/main/cpp/main/include/base/object.h deleted file mode 100644 index 3975ee10..00000000 --- a/core/src/main/cpp/main/include/base/object.h +++ /dev/null @@ -1,277 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -#pragma once - -#include "macros.h" -#include -#include -#include "config.h" -#include "native_hook.h" -#include "elf_util.h" -#include - -#define _uintval(p) reinterpret_cast(p) -#define _ptr(p) reinterpret_cast(p) -#define _align_up(x, n) (((x) + ((n) - 1)) & ~((n) - 1)) -#define _align_down(x, n) ((x) & -(n)) -#define _page_size 4096 -#define _page_align(n) _align_up(static_cast(n), _page_size) -#define _ptr_align(x) _ptr(_align_down(reinterpret_cast(x), _page_size)) -#define _make_rwx(p, n) ::mprotect(_ptr_align(p), \ - _page_align(_uintval(p) + n) != _page_align(_uintval(p)) ? _page_align(n) + _page_size : _page_align(n), \ - PROT_READ | PROT_WRITE | PROT_EXEC) - -#define CONCATENATE(a, b) a##b - -#define CREATE_HOOK_STUB_ENTRIES(SYM, RET, FUNC, PARAMS, DEF) \ - inline static struct : public lspd::Hooker{ \ - inline static RET replace PARAMS DEF \ - } FUNC - -#define CREATE_MEM_HOOK_STUB_ENTRIES(SYM, RET, FUNC, PARAMS, DEF) \ - inline static struct : public lspd::MemHooker{ \ - inline static RET replace PARAMS DEF \ - } FUNC - -#define RETRIEVE_FUNC_SYMBOL(name, ...) \ - name##Sym = reinterpret_cast( \ - lspd::Dlsym(handle, __VA_ARGS__)) - -#define RETRIEVE_MEM_FUNC_SYMBOL(name, ...) \ - name##Sym = reinterpret_cast( \ - lspd::Dlsym(handle, __VA_ARGS__)) - -#define RETRIEVE_FIELD_SYMBOL(name, ...) \ - void *name = lspd::Dlsym(handle, __VA_ARGS__) - -#define CREATE_FUNC_SYMBOL_ENTRY(ret, func, ...) \ - typedef ret (*func##Type)(__VA_ARGS__); \ - inline static ret (*func##Sym)(__VA_ARGS__); \ - inline static ret func(__VA_ARGS__) - -#define CREATE_MEM_FUNC_SYMBOL_ENTRY(ret, func, thiz, ...) \ - using func##Type = lspd::MemberFunction; \ - inline static func##Type func##Sym; \ - inline static ret func(thiz, ## __VA_ARGS__) - -namespace lspd { - - class ShadowObject { - - public: - ShadowObject(void *thiz) : thiz_(thiz) { - } - - [[gnu::always_inline]] - inline void *Get() { - return thiz_; - } - - [[gnu::always_inline]] - inline void Reset(void *thiz) { - thiz_ = thiz; - } - - [[gnu::always_inline]] - inline operator bool() const { - return thiz_ != nullptr; - } - - protected: - void *thiz_; - }; - - class HookedObject : public ShadowObject { - - public: - - HookedObject(void *thiz) : ShadowObject(thiz) {} - }; - - struct ObjPtr { - void *data; - }; - - [[gnu::always_inline]] - inline void *Dlsym(void *handle, const char *name) { - return dlsym(handle, name); - } - - [[gnu::always_inline]] - inline void *Dlsym(const SandHook::ElfImg &handle, const char *name) { - return handle.getSymbAddress(name); - } - - template - inline void *Dlsym(H &&handle, T first, Args... last) { - auto ret = Dlsym(std::forward(handle), first); - if (ret) { - return ret; - } - return Dlsym(std::forward(handle), last...); - } - - inline int HookFunction(void *original, void *replace, void **backup) { - _make_rwx(original, _page_size); - if constexpr (isDebug) { - Dl_info info; - if (dladdr(original, &info)) - LOGD("Hooking %s (%p) from %s (%p)", - info.dli_sname ? info.dli_sname : "(unknown symbol)", info.dli_saddr, - info.dli_fname ? info.dli_fname : "(unknown file)", info.dli_fbase); - } - return DobbyHook(original, replace, backup); - } - - inline int UnhookFunction(void *original) { - if constexpr (isDebug) { - Dl_info info; - if (dladdr(original, &info)) - LOGD("Unhooking %s (%p) from %s (%p)", - info.dli_sname ? info.dli_sname : "(unknown symbol)", info.dli_saddr, - info.dli_fname ? info.dli_fname : "(unknown file)", info.dli_fbase); - } - return DobbyDestroy(original); - } - - template class> - struct is_instance : public std::false_type { - }; - - template class U> - struct is_instance, U> : public std::true_type { - }; - - template - requires (std::is_same_v || std::is_same_v) - inline static auto memfun_cast(Return (*func)(T *, Args...)) { - union { - Return (Class::*f)(Args...); - - struct { - decltype(func) p; - std::ptrdiff_t adj; - } data; - } u{.data = {func, 0}}; - static_assert(sizeof(u.f) == sizeof(u.data), "Try different T"); - return u.f; - } - - template T, typename Return, typename... Args> - inline auto memfun_cast(Return (*func)(T *, Args...)) { - return memfun_cast(func); - } - - template - class MemberFunction; - - template - class MemberFunction { - using SelfType = MemberFunction; - using ThisType = std::conditional_t, SelfType, This>; - using MemFunType = Return(ThisType::*)(Args...); - public: - using FunType = Return (*)(This *, Args...); - private: - MemFunType f_ = nullptr; - public: - MemberFunction() = default; - - MemberFunction(FunType f) : f_(memfun_cast(f)) {} - - MemberFunction(MemFunType f) : f_(f) {} - - Return operator()(This *thiz, Args... args) { - return (reinterpret_cast(thiz)->*f_)(std::forward(args)...); - } - - inline operator bool() { - return f_ != nullptr; - } - }; - - // deduction guide - template - MemberFunction(Return(*f)(This *, Args...)) -> MemberFunction; - - template - MemberFunction(Return(This::*f)(Args...)) -> MemberFunction; - - template - struct Hooker; - - template - struct Hooker> { - inline static Ret (*backup)(Args...) = nullptr; - - inline static constexpr const char *sym = tstring::c_str(); - }; - - template - struct MemHooker; - template - struct MemHooker> { - inline static MemberFunction backup; - inline static constexpr const char *sym = tstring::c_str(); - }; - - template - concept HookerType = requires(T a) { - a.backup; - a.replace; - }; - - template - inline static bool HookSymNoHandle(void *original, T &arg) { - if (original) { - if constexpr(is_instance::value) { - void *backup; - HookFunction(original, reinterpret_cast(arg.replace), &backup); - arg.backup = reinterpret_cast(backup); - } else { - HookFunction(original, reinterpret_cast(arg.replace), - reinterpret_cast(&arg.backup)); - } - return true; - } else { - return false; - } - } - - template - inline static bool HookSym(H &&handle, T &arg) { - auto original = Dlsym(std::forward(handle), arg.sym); - return HookSymNoHandle(original, arg); - } - - template - inline static bool HookSyms(H &&handle, T &first, Args &...rest) { - if (!(HookSym(std::forward(handle), first) || ... || HookSym(std::forward(handle), - rest))) { - LOGW("Hook Fails: %s", first.sym); - return false; - } - return true; - } - -} // namespace lspd - -using lspd::operator ""_tstr; diff --git a/core/src/main/cpp/main/include/jni_helper.h b/core/src/main/cpp/main/include/jni_helper.h deleted file mode 100644 index 7eea74c2..00000000 --- a/core/src/main/cpp/main/include/jni_helper.h +++ /dev/null @@ -1,363 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 - 2022 LSPosed Contributors - */ - -#pragma once - -#include -#include "macros.h" -#include -#include "logging.h" - -#define JNI_START JNIEnv *env, [[maybe_unused]] jclass clazz - -namespace JNIHelper { - template class> - struct is_instance : public std::false_type { - }; - - template class U> - struct is_instance, U> : public std::true_type { - }; -} - -class JUTFString { -public: - inline JUTFString(JNIEnv *env, jstring jstr) : JUTFString(env, jstr, nullptr) { - } - - inline JUTFString(JNIEnv *env, jstring jstr, const char *default_cstr) : env_(env), - jstr_(jstr) { - if (env_ && jstr_) cstr_ = env_->GetStringUTFChars(jstr, nullptr); - else cstr_ = default_cstr; - } - - inline operator const char *() const { return cstr_; } - - inline operator const std::string() const { return cstr_; } - - inline operator const bool() const { return cstr_ != nullptr; } - - inline auto get() const { return cstr_; } - - inline ~JUTFString() { - if (env_ && jstr_) env_->ReleaseStringUTFChars(jstr_, cstr_); - } - - JUTFString(JUTFString &&other) - : env_(std::move(other.env_)), jstr_(std::move(other.jstr_)), - cstr_(std::move(other.cstr_)) { - other.cstr_ = nullptr; - } - - JUTFString & - operator=(JUTFString &&other) { - if (&other != this) { - env_ = std::move(other.env_); - jstr_ = std::move(other.jstr_); - cstr_ = std::move(other.cstr_); - other.cstr_ = nullptr; - } - return *this; - } - -private: - JNIEnv *env_; - jstring jstr_; - const char *cstr_; - - JUTFString(const JUTFString &) = delete; - - JUTFString &operator=(const JUTFString &) = delete; -}; - -template -concept JObject = std::is_base_of_v, std::remove_pointer_t>; - -template -class ScopedLocalRef { -public: - ScopedLocalRef(JNIEnv *env, T localRef) : mEnv(env), mLocalRef(localRef) { - } - - ScopedLocalRef(ScopedLocalRef &&s) noexcept: mEnv(s.mEnv), mLocalRef(s.release()) { - } - - template - ScopedLocalRef(ScopedLocalRef &&s) noexcept: mEnv(s.mEnv), mLocalRef((T) s.release()) { - } - - explicit ScopedLocalRef(JNIEnv *env) : mEnv(env), mLocalRef(nullptr) { - } - - ~ScopedLocalRef() { - reset(); - } - - void reset(T ptr = nullptr) { - if (ptr != mLocalRef) { - if (mLocalRef != nullptr) { - mEnv->DeleteLocalRef(mLocalRef); - } - mLocalRef = ptr; - } - } - - [[nodiscard]] T release() { - T localRef = mLocalRef; - mLocalRef = nullptr; - return localRef; - } - - T get() const { - return mLocalRef; - } - - // We do not expose an empty constructor as it can easily lead to errors - // using common idioms, e.g.: - // ScopedLocalRef<...> ref; - // ref.reset(...); - // Move assignment operator. - ScopedLocalRef &operator=(ScopedLocalRef &&s) noexcept { - reset(s.release()); - mEnv = s.mEnv; - return *this; - } - - operator bool() const { - return mLocalRef; - } - - template - friend - class ScopedLocalRef; - -private: - JNIEnv *mEnv; - T mLocalRef; - DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef); -}; - - -template -concept ScopeOrRaw = std::is_same_v || std::is_same_v, U>; -template -concept ScopeOrClass = ScopeOrRaw; -template -concept ScopeOrObject = ScopeOrRaw; - -inline ScopedLocalRef ClearException(JNIEnv *env) { - if (auto exception = env->ExceptionOccurred()) { - env->ExceptionClear(); - static jclass log = (jclass) env->NewGlobalRef(env->FindClass("android/util/Log")); - static jmethodID toString = env->GetStaticMethodID(log, "getStackTraceString", - "(Ljava/lang/Throwable;)Ljava/lang/String;"); - auto str = (jstring) env->CallStaticObjectMethod(log, toString, exception); - env->DeleteLocalRef(exception); - return {env, str}; - } - return {env, nullptr}; -} - -template -[[maybe_unused]] -inline auto unwrap_scope(T &&x) { - if constexpr (std::is_same_v, std::string_view>) return x.data(); - else if constexpr (JNIHelper::is_instance, ScopedLocalRef>::value) return x.get(); - else return std::forward(x); -} - -template -[[maybe_unused]] -inline auto wrap_scope(JNIEnv *env, T &&x) { - if constexpr (std::is_convertible_v) { - return ScopedLocalRef(env, std::forward(x)); - } else return x; -} - -inline auto JNI_NewStringUTF(JNIEnv *env, std::string_view sv) { - return ScopedLocalRef(env, env->NewStringUTF(sv.data())); -} - -template -requires(std::is_function_v) -[[maybe_unused]] -inline auto JNI_SafeInvoke(JNIEnv *env, Func JNIEnv::*f, Args &&... args) { - struct finally { - finally(JNIEnv *env) : env_(env) {} - - ~finally() { - if (auto exception = ClearException(env_)) { - LOGE("%s", JUTFString(env_, exception.get()).get()); - } - } - - JNIEnv *env_; - } _(env); - - if constexpr (!std::is_same_v(args)))...>>) - return wrap_scope(env, (env->*f)(unwrap_scope(std::forward(args))...)); - else (env->*f)(unwrap_scope(std::forward(args))...); -} - -[[maybe_unused]] -inline auto JNI_FindClass(JNIEnv *env, std::string_view name) { - return JNI_SafeInvoke(env, &JNIEnv::FindClass, name); -} - -template -[[maybe_unused]] -inline auto JNI_GetObjectClass(JNIEnv *env, const Object &obj) { - return JNI_SafeInvoke(env, &JNIEnv::GetObjectClass, obj); -} - -template -[[maybe_unused]] -inline auto -JNI_GetFieldID(JNIEnv *env, const Class &clazz, std::string_view name, std::string_view sig) { - return JNI_SafeInvoke(env, &JNIEnv::GetFieldID, clazz, name, sig); -} - -template -[[maybe_unused]] -inline auto JNI_GetObjectField(JNIEnv *env, const Class &clazz, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetObjectField, clazz, fieldId); -} - -template -[[maybe_unused]] -inline auto -JNI_GetMethodID(JNIEnv *env, const Class &clazz, std::string_view name, std::string_view sig) { - return JNI_SafeInvoke(env, &JNIEnv::GetMethodID, clazz, name, sig); -} - -template -[[maybe_unused]] -inline auto JNI_CallObjectMethod(JNIEnv *env, const Object &obj, Args &&... args) { - return JNI_SafeInvoke(env, &JNIEnv::CallObjectMethod, obj, std::forward(args)...); -} - -template -[[maybe_unused]] -inline auto JNI_CallIntMethod(JNIEnv *env, const Object &obj, Args &&... args) { - return JNI_SafeInvoke(env, &JNIEnv::CallIntMethod, obj, std::forward(args)...); -} - -template -[[maybe_unused]] -inline auto JNI_CallLongMethod(JNIEnv *env, const Object &obj, Args &&... args) { - return JNI_SafeInvoke(env, &JNIEnv::CallLongMethod, obj, std::forward(args)...); -} - -template -[[maybe_unused]] -inline auto JNI_CallVoidMethod(JNIEnv *env, const Object &obj, Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallVoidMethod, obj, std::forward(args)...); -} - -template -[[maybe_unused]] -inline auto JNI_CallBooleanMethod(JNIEnv *env, const Object &obj, Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallBooleanMethod, obj, std::forward(args)...); -} - -template -[[maybe_unused]] -inline auto -JNI_GetStaticFieldID(JNIEnv *env, const Class &clazz, std::string_view name, std::string_view sig) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticFieldID, clazz, name, sig); -} - -template -[[maybe_unused]] -inline auto JNI_GetStaticObjectField(JNIEnv *env, const Class &clazz, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticObjectField, clazz, fieldId); -} - -template -[[maybe_unused]] -inline auto JNI_GetStaticIntField(JNIEnv *env, const Class &clazz, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticIntField, clazz, fieldId); -} - -template -[[maybe_unused]] -inline auto -JNI_GetStaticMethodID(JNIEnv *env, const Class &clazz, std::string_view name, - std::string_view sig) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticMethodID, clazz, name, sig); -} - -template -[[maybe_unused]] -inline auto JNI_CallStaticVoidMethod(JNIEnv *env, const Class &clazz, Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticVoidMethod, clazz, std::forward(args)...); -} - -template -[[maybe_unused]] -inline auto JNI_CallStaticObjectMethod(JNIEnv *env, const Class &clazz, Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticObjectMethod, clazz, std::forward(args)...); -} - -template -[[maybe_unused]] -inline auto JNI_CallStaticIntMethod(JNIEnv *env, const Class &clazz, Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticIntMethod, clazz, std::forward(args)...); -} - -template -[[maybe_unused]] -inline auto JNI_CallStaticBooleanMethod(JNIEnv *env, const Class &clazz, Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticBooleanMethod, clazz, std::forward(args)...); -} - -template Array> -[[maybe_unused]] -inline auto JNI_GetArrayLength(JNIEnv *env, const Array &array) { - return JNI_SafeInvoke(env, &JNIEnv::GetArrayLength, array); -} - -template -[[maybe_unused]] -inline auto JNI_NewObject(JNIEnv *env, const Class &clazz, Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::NewObject, clazz, std::forward(args)...); -} - -template -[[maybe_unused]] -inline auto -JNI_RegisterNatives(JNIEnv *env, const Class &clazz, const JNINativeMethod *methods, jint size) { - return JNI_SafeInvoke(env, &JNIEnv::RegisterNatives, clazz, methods, size); -} - -template -[[maybe_unused]] -inline auto JNI_NewGlobalRef(JNIEnv *env, T &&x) requires(std::is_convertible_v){ - return (T) env->NewGlobalRef(std::forward(x)); -} - -template -[[maybe_unused]] -inline auto -JNI_NewGlobalRef(JNIEnv *env, const ScopedLocalRef &x) requires( - std::is_convertible_v){ - return (T) env->NewGlobalRef(x.get()); -} diff --git a/core/src/main/cpp/main/include/macros.h b/core/src/main/cpp/main/include/macros.h deleted file mode 100644 index 5e47256a..00000000 --- a/core/src/main/cpp/main/include/macros.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include // for size_t -#include // for TEMP_FAILURE_RETRY -#include -// A macro to disallow the copy constructor and operator= functions -// This must be placed in the private: declarations for a class. -// -// For disallowing only assign or copy, delete the relevant operator or -// constructor, for example: -// void operator=(const TypeName&) = delete; -// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken -// semantically, one should either use disallow both or neither. Try to -// avoid these in new code. -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&) = delete; \ - void operator=(const TypeName&) = delete -// The arraysize(arr) macro returns the # of elements in an array arr. -// The expression is a compile-time constant, and therefore can be -// used in defining new arrays, for example. If you use arraysize on -// a pointer by mistake, you will get a compile-time error. -template -[[gnu::always_inline]] constexpr inline size_t arraysize(T(&)[N]) { - return N; -} -// Current ABI string -#if defined(__arm__) -#define ABI_STRING "arm" -#elif defined(__aarch64__) -#define ABI_STRING "arm64" -#elif defined(__i386__) -#define ABI_STRING "x86" -#elif defined(__x86_64__) -#define ABI_STRING "x86_64" -#elif defined(__mips__) && !defined(__LP64__) -#define ABI_STRING "mips" -#elif defined(__mips__) && defined(__LP64__) -#define ABI_STRING "mips64" -#endif -// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private: -// declarations in a class. -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&) = delete; \ - void operator=(const TypeName&) = delete diff --git a/core/src/main/cpp/main/include/native_util.h b/core/src/main/cpp/main/include/native_util.h deleted file mode 100644 index 6e7880cf..00000000 --- a/core/src/main/cpp/main/include/native_util.h +++ /dev/null @@ -1,73 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-value" -#pragma once - -#include -#include "macros.h" -#include "jni_helper.h" -#include "logging.h" -#include - -namespace lspd { - - [[gnu::always_inline]] - inline bool RegisterNativeMethodsInternal(JNIEnv *env, - const char *class_name, - const JNINativeMethod *methods, - jint method_count) { - - auto clazz = Context::GetInstance()->FindClassFromCurrentLoader(env, class_name); - if (clazz.get() == nullptr) { - LOGF("Couldn't find class: %s", class_name); - return false; - } - return JNI_RegisterNatives(env, clazz, methods, method_count); - } - -#if defined(__cplusplus) -#define _NATIVEHELPER_JNI_MACRO_CAST(to) \ - reinterpret_cast -#else -#define _NATIVEHELPER_JNI_MACRO_CAST(to) \ - (to) -#endif - -#ifndef LSP_NATIVE_METHOD -#define LSP_NATIVE_METHOD(className, functionName, signature) \ - { #functionName, \ - signature, \ - _NATIVEHELPER_JNI_MACRO_CAST(void*) (Java_org_lsposed_lspd_nativebridge_## className ## _ ## functionName) \ - } -#endif - -#ifndef LSP_DEF_NATIVE_METHOD -#define LSP_DEF_NATIVE_METHOD(ret, className, functionName, ...) \ - extern "C" ret Java_org_lsposed_lspd_nativebridge_## className ## _ ## functionName (JNI_START, ## __VA_ARGS__) -#endif - -#define REGISTER_LSP_NATIVE_METHODS(class_name) \ - RegisterNativeMethodsInternal(env, "org.lsposed.lspd.nativebridge." #class_name, gMethods, arraysize(gMethods)) - -} // namespace lspd - -#pragma clang diagnostic pop diff --git a/core/src/main/cpp/main/src/jni/art_class_linker.cpp b/core/src/main/cpp/main/src/jni/art_class_linker.cpp deleted file mode 100644 index 3e911115..00000000 --- a/core/src/main/cpp/main/src/jni/art_class_linker.cpp +++ /dev/null @@ -1,46 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -#include -#include -#include -#include -#include -#include -#include "art_class_linker.h" - -namespace lspd { - LSP_DEF_NATIVE_METHOD(void, ClassLinker, setEntryPointsToInterpreter, jobject method) { - void *reflected_method = yahfa::getArtMethod(env, method); - LOGD("deoptimizing method: %p", reflected_method); - art::ClassLinker::SetEntryPointsToInterpreter(reflected_method); - LOGD("method deoptimized: %p", reflected_method); - } - - static JNINativeMethod gMethods[] = { - LSP_NATIVE_METHOD(ClassLinker, setEntryPointsToInterpreter, - "(Ljava/lang/reflect/Executable;)V") - }; - - void RegisterArtClassLinker(JNIEnv *env) { - REGISTER_LSP_NATIVE_METHODS(ClassLinker); - } - -} // namespace lspd diff --git a/core/src/main/cpp/main/src/jni/art_class_linker.h b/core/src/main/cpp/main/src/jni/art_class_linker.h deleted file mode 100644 index 4706daa9..00000000 --- a/core/src/main/cpp/main/src/jni/art_class_linker.h +++ /dev/null @@ -1,27 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -#pragma once - -namespace lspd { - - void RegisterArtClassLinker(JNIEnv *); - -} // namespace lspd diff --git a/core/src/main/cpp/main/src/jni/yahfa.cpp b/core/src/main/cpp/main/src/jni/yahfa.cpp deleted file mode 100644 index 73647167..00000000 --- a/core/src/main/cpp/main/src/jni/yahfa.cpp +++ /dev/null @@ -1,241 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 - 2022 LSPosed Contributors - */ - -#include "yahfa.h" -#include "HookMain.h" -#include "native_util.h" -#include "art/runtime/class_linker.h" -#include "art/runtime/thread_list.h" -#include "art/runtime/thread.h" -#include "art/runtime/gc/scoped_gc_critical_section.h" -#include -#include -#include - - -namespace lspd { - namespace { - std::unordered_map hooked_methods_; - std::shared_mutex hooked_methods_lock_; - - std::vector> jit_movements_; - std::shared_mutex jit_movements_lock_; - - std::unordered_map> hooked_classes_; - std::shared_mutex hooked_classes_lock_; - } - - void* isHooked(void *art_method) { - std::shared_lock lk(hooked_methods_lock_); - if (auto found = hooked_methods_.find(art_method); found != hooked_methods_.end()) { - return found->second; - } - return nullptr; - } - - void recordHooked(void *art_method, void *backup) { - { - std::unique_lock lk(hooked_methods_lock_); - hooked_methods_.emplace(art_method, backup); - } - auto clazz = art::mirror::Class(reinterpret_cast(*reinterpret_cast(art_method))); - { - std::unique_lock lk(hooked_classes_lock_); - hooked_classes_[clazz.GetClassDef()].emplace(art_method); - } - } - - const std::unordered_set &isClassHooked(void *clazz) { - static std::unordered_set empty; - std::shared_lock lk(hooked_classes_lock_); - if (auto found = hooked_classes_.find(clazz); found != hooked_classes_.end()) { - return found->second; - } - return empty; - } - - - void recordJitMovement(void *target, void *backup) { - std::unique_lock lk(jit_movements_lock_); - jit_movements_.emplace_back(target, backup); - } - - std::vector> getJitMovements() { - std::unique_lock lk(jit_movements_lock_); - return std::move(jit_movements_); - } - - using namespace startop::dex; - - LSP_DEF_NATIVE_METHOD(void, Yahfa, init, jint sdkVersion) { - yahfa::init(env, clazz, sdkVersion); - } - - LSP_DEF_NATIVE_METHOD(jobject, Yahfa, findMethodNative, jclass targetClass, - jstring methodName, jstring methodSig) { - return yahfa::findMethodNative(env, clazz, targetClass, methodName, - methodSig); - } - - LSP_DEF_NATIVE_METHOD(jboolean, Yahfa, backupAndHookNative, jobject target, - jobject hook, jobject backup, jboolean is_proxy) { - art::gc::ScopedGCCriticalSection section(art::Thread::Current().Get(), - art::gc::kGcCauseDebugger, - art::gc::kCollectorTypeDebugger); - art::thread_list::ScopedSuspendAll suspend("Yahfa Hook", false); - if (yahfa::backupAndHookNative(env, clazz, target, hook, backup)) { - auto *target_method = yahfa::getArtMethod(env, target); - auto *backup_method = yahfa::getArtMethod(env, backup); - recordHooked(target_method, backup_method); - if (!is_proxy) [[likely]] recordJitMovement(target_method, backup_method); - return JNI_TRUE; - } - return JNI_FALSE; - } - - LSP_DEF_NATIVE_METHOD(jboolean, Yahfa, isHooked, jobject member) { - return lspd::isHooked(yahfa::getArtMethod(env, member)) != nullptr; - } - - LSP_DEF_NATIVE_METHOD(jclass, Yahfa, buildHooker, jobject app_class_loader, jchar return_class, - jcharArray classes, jstring method_name, jstring hooker_name) { - static auto *kInMemoryClassloader = JNI_NewGlobalRef(env, JNI_FindClass(env, - "dalvik/system/InMemoryDexClassLoader")); - static jmethodID kInitMid = JNI_GetMethodID(env, kInMemoryClassloader, "", - "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); - DexBuilder dex_file; - - auto parameter_length = env->GetArrayLength(classes); - auto parameter_types = std::vector(); - parameter_types.reserve(parameter_length); - std::string storage; - auto return_type = - return_class == 'L' ? TypeDescriptor::Object : TypeDescriptor::FromDescriptor( - (char) return_class); - auto *params = env->GetCharArrayElements(classes, nullptr); - for (int i = 0; i < parameter_length; ++i) { - parameter_types.push_back( - params[i] == 'L' ? TypeDescriptor::Object : TypeDescriptor::FromDescriptor( - (char) params[i])); - } - - ClassBuilder cbuilder{dex_file.MakeClass("LspHooker_")}; - cbuilder.set_source_file("LSP"); - - auto hooker_type = - TypeDescriptor::FromClassname(JUTFString(env, hooker_name).get()); - - auto *hooker_field = cbuilder.CreateField("hooker", hooker_type) - .access_flags(dex::kAccStatic) - .Encode(); - - auto hook_builder{cbuilder.CreateMethod( - JUTFString(env, method_name), Prototype{return_type, parameter_types})}; - // allocate tmp frist because of wide - auto tmp{hook_builder.AllocRegister()}; - hook_builder.BuildConst(tmp, parameter_types.size()); - auto hook_params_array{hook_builder.AllocRegister()}; - hook_builder.BuildNewArray(hook_params_array, TypeDescriptor::Object, tmp); - for (size_t i = 0U, j = 0U; i < parameter_types.size(); ++i, ++j) { - hook_builder.BuildBoxIfPrimitive(Value::Parameter(j), parameter_types[i], - Value::Parameter(j)); - hook_builder.BuildConst(tmp, i); - hook_builder.BuildAput(Instruction::Op::kAputObject, hook_params_array, - Value::Parameter(j), tmp); - if (parameter_types[i].is_wide()) ++j; - } - auto handle_hook_method{dex_file.GetOrDeclareMethod( - hooker_type, "handleHookedMethod", - Prototype{TypeDescriptor::Object, TypeDescriptor::Object.ToArray()})}; - hook_builder.AddInstruction( - Instruction::GetStaticObjectField(hooker_field->decl->orig_index, tmp)); - hook_builder.AddInstruction(Instruction::InvokeVirtualObject( - handle_hook_method.id, tmp, tmp, hook_params_array)); - if (return_type == TypeDescriptor::Void) { - hook_builder.BuildReturn(); - } else if (return_type.is_primitive()) { - auto box_type{return_type.ToBoxType()}; - const ir::Type *type_def = dex_file.GetOrAddType(box_type); - hook_builder.AddInstruction( - Instruction::Cast(tmp, Value::Type(type_def->orig_index))); - hook_builder.BuildUnBoxIfPrimitive(tmp, box_type, tmp); - hook_builder.BuildReturn(tmp, false, return_type.is_wide()); - } else { - const ir::Type *type_def = dex_file.GetOrAddType(return_type); - hook_builder.AddInstruction( - Instruction::Cast(tmp, Value::Type(type_def->orig_index))); - hook_builder.BuildReturn(tmp, true); - } - [[maybe_unused]] auto *hook_method = hook_builder.Encode(); - - auto backup_builder{ - cbuilder.CreateMethod("backup", Prototype{return_type, parameter_types})}; - if (return_type == TypeDescriptor::Void) { - backup_builder.BuildReturn(); - } else if (return_type.is_wide()) { - LiveRegister zero = backup_builder.AllocRegister(); - LiveRegister zero_wide = backup_builder.AllocRegister(); - backup_builder.BuildConstWide(zero, 0); - backup_builder.BuildReturn(zero, /*is_object=*/false, true); - } else { - LiveRegister zero = backup_builder.AllocRegister(); - LiveRegister zero_wide = backup_builder.AllocRegister(); - backup_builder.BuildConst(zero, 0); - backup_builder.BuildReturn(zero, /*is_object=*/!return_type.is_primitive(), false); - } - [[maybe_unused]] auto *back_method = backup_builder.Encode(); - - slicer::MemView image{dex_file.CreateImage()}; - - auto *dex_buffer = env->NewDirectByteBuffer(const_cast(image.ptr()), image.size()); - auto my_cl = JNI_NewObject(env, kInMemoryClassloader, kInitMid, - dex_buffer, app_class_loader); - env->DeleteLocalRef(dex_buffer); - - static jmethodID kMid = JNI_GetMethodID(env, kInMemoryClassloader, "loadClass", - "(Ljava/lang/String;)Ljava/lang/Class;"); - if (!kMid) { - kMid = JNI_GetMethodID(env, kInMemoryClassloader, "findClass", - "(Ljava/lang/String;)Ljava/lang/Class;"); - } - if (my_cl) { - auto target = JNI_CallObjectMethod(env, my_cl, kMid, - JNI_NewStringUTF(env, "LspHooker_")); - if (target) return (jclass) target.release(); - } - return nullptr; - } - - static JNINativeMethod gMethods[] = { - LSP_NATIVE_METHOD(Yahfa, init, "(I)V"), - LSP_NATIVE_METHOD(Yahfa, findMethodNative, - "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Executable;"), - LSP_NATIVE_METHOD(Yahfa, backupAndHookNative, - "(Ljava/lang/reflect/Executable;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Z)Z"), - LSP_NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Executable;)Z"), - LSP_NATIVE_METHOD(Yahfa, buildHooker, - "(Ljava/lang/ClassLoader;C[CLjava/lang/String;Ljava/lang/String;)Ljava/lang/Class;"), - }; - - void RegisterYahfa(JNIEnv *env) { - REGISTER_LSP_NATIVE_METHODS(Yahfa); - } - -} // namespace lspd diff --git a/core/src/main/cpp/main/src/native_hook.cpp b/core/src/main/cpp/main/src/native_hook.cpp deleted file mode 100644 index aeb15c86..00000000 --- a/core/src/main/cpp/main/src/native_hook.cpp +++ /dev/null @@ -1,67 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -#include -#include -#include -#include -#include -#include -#include "symbol_cache.h" -#include "logging.h" -#include "native_api.h" -#include "native_hook.h" -#include "art/runtime/mirror/class.h" -#include "art/runtime/art_method.h" -#include "art/runtime/class_linker.h" -#include "art/runtime/thread.h" -#include "art/runtime/hidden_api.h" -#include "art/runtime/thread_list.h" -#include "art/runtime/gc/scoped_gc_critical_section.h" -#include "art/runtime/jit/jit_code_cache.h" - -namespace lspd { - static std::atomic_bool installed = false; - - void InstallInlineHooks() { - if (installed.exchange(true)) [[unlikely]] { - LOGD("Inline hooks have been installed, skip"); - return; - } - LOGD("Start to install inline hooks"); - const auto &handle_libart = *GetArt(); - if (!handle_libart.isValid()) { - LOGE("Failed to fetch libart.so"); - } - art::Runtime::Setup(handle_libart); - art::hidden_api::DisableHiddenApi(handle_libart); - art::art_method::Setup(handle_libart); - art::Thread::Setup(handle_libart); - art::ClassLinker::Setup(handle_libart); - art::mirror::Class::Setup(handle_libart); - art::JNIEnvExt::Setup(handle_libart); - art::thread_list::ScopedSuspendAll::Setup(handle_libart); - art::gc::ScopedGCCriticalSection::Setup(handle_libart); - art::jit::jit_code_cache::Setup(handle_libart); - GetArt(true); - LOGD("Inline hooks installed"); - } -} // namespace lspd - diff --git a/core/src/main/java/de/robv/android/xposed/IModuleContext.java b/core/src/main/java/de/robv/android/xposed/IModuleContext.java deleted file mode 100644 index 9f322c5a..00000000 --- a/core/src/main/java/de/robv/android/xposed/IModuleContext.java +++ /dev/null @@ -1,26 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -package de.robv.android.xposed; - -public interface IModuleContext { - - String getApkPath(); -} diff --git a/core/src/main/java/de/robv/android/xposed/IXposedHookInitPackageResources.java b/core/src/main/java/de/robv/android/xposed/IXposedHookInitPackageResources.java index 9b00ccea..ec4476c3 100644 --- a/core/src/main/java/de/robv/android/xposed/IXposedHookInitPackageResources.java +++ b/core/src/main/java/de/robv/android/xposed/IXposedHookInitPackageResources.java @@ -1,23 +1,3 @@ -/* - * 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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - package de.robv.android.xposed; import android.content.res.XResources; @@ -42,26 +22,15 @@ public interface IXposedHookInitPackageResources extends IXposedMod { */ void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable; - /** - * @hide - */ + /** @hide */ final class Wrapper extends XC_InitPackageResources { private final IXposedHookInitPackageResources instance; - private final String apkPath; - - public Wrapper(IXposedHookInitPackageResources instance, String apkPath) { + public Wrapper(IXposedHookInitPackageResources instance) { this.instance = instance; - this.apkPath = apkPath; } - @Override public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable { instance.handleInitPackageResources(resparam); } - - @Override - public String getApkPath() { - return apkPath; - } } } diff --git a/core/src/main/java/de/robv/android/xposed/IXposedHookLoadPackage.java b/core/src/main/java/de/robv/android/xposed/IXposedHookLoadPackage.java index 7be79f5f..5f1bcdd8 100644 --- a/core/src/main/java/de/robv/android/xposed/IXposedHookLoadPackage.java +++ b/core/src/main/java/de/robv/android/xposed/IXposedHookLoadPackage.java @@ -1,23 +1,3 @@ -/* - * 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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - package de.robv.android.xposed; import android.app.Application; @@ -43,26 +23,15 @@ public interface IXposedHookLoadPackage extends IXposedMod { */ void handleLoadPackage(LoadPackageParam lpparam) throws Throwable; - /** - * @hide - */ + /** @hide */ final class Wrapper extends XC_LoadPackage { private final IXposedHookLoadPackage instance; - private final String apkPath; - - public Wrapper(IXposedHookLoadPackage instance, String apkPath) { + public Wrapper(IXposedHookLoadPackage instance) { this.instance = instance; - this.apkPath = apkPath; } - @Override public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable { instance.handleLoadPackage(lpparam); } - - @Override - public String getApkPath() { - return apkPath; - } } } diff --git a/core/src/main/java/de/robv/android/xposed/IXposedHookZygoteInit.java b/core/src/main/java/de/robv/android/xposed/IXposedHookZygoteInit.java index 5e6d5c3a..fd4f95bc 100644 --- a/core/src/main/java/de/robv/android/xposed/IXposedHookZygoteInit.java +++ b/core/src/main/java/de/robv/android/xposed/IXposedHookZygoteInit.java @@ -1,28 +1,5 @@ -/* - * 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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - package de.robv.android.xposed; -import de.robv.android.xposed.callbacks.XC_InitZygote; -import de.robv.android.xposed.callbacks.XCallback; - /** * Hook the initialization of Zygote process(es), from which all the apps are forked. * @@ -37,30 +14,16 @@ import de.robv.android.xposed.callbacks.XCallback; public interface IXposedHookZygoteInit extends IXposedMod { /** * Called very early during startup of Zygote. - * * @param startupParam Details about the module itself and the started process. * @throws Throwable everything is caught, but will prevent further initialization of the module. */ void initZygote(StartupParam startupParam) throws Throwable; - /** - * Data holder for {@link #initZygote}. - */ - final class StartupParam extends XCallback.Param { - /*package*/ StartupParam() { - } + /** Data holder for {@link #initZygote}. */ + final class StartupParam { + /*package*/ StartupParam() {} - /** - * @param callbacks - * @hide - */ - public StartupParam(XposedBridge.CopyOnWriteSortedSet callbacks) { - super(callbacks); - } - - /** - * The path to the module's APK. - */ + /** The path to the module's APK. */ public String modulePath; /** @@ -69,29 +32,4 @@ public interface IXposedHookZygoteInit extends IXposedMod { */ public boolean startsSystemServer; } - - /** - * @hide - */ - final class Wrapper extends XC_InitZygote { - private final IXposedHookZygoteInit instance; - private final StartupParam startupParam; - - public Wrapper(IXposedHookZygoteInit instance, StartupParam startupParam) { - this.instance = instance; - this.startupParam = startupParam; - } - - @Override - public void initZygote(StartupParam startupParam) throws Throwable { - // NOTE: parameter startupParam not used - // cause startupParam info is generated and saved along with instance here - instance.initZygote(this.startupParam); - } - - @Override - public String getApkPath() { - return startupParam.modulePath; - } - } } diff --git a/core/src/main/java/de/robv/android/xposed/LspHooker.java b/core/src/main/java/de/robv/android/xposed/LspHooker.java deleted file mode 100644 index a8c2044c..00000000 --- a/core/src/main/java/de/robv/android/xposed/LspHooker.java +++ /dev/null @@ -1,132 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -package de.robv.android.xposed; - -import java.lang.reflect.Executable; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - -public class LspHooker { - public final XposedBridge.AdditionalHookInfo additionalInfo; - private final Executable method; - private final Method backup; - - public LspHooker(XposedBridge.AdditionalHookInfo info, Executable origin, Method backup) { - this.additionalInfo = info; - this.method = origin; - this.backup = backup; - this.backup.setAccessible(true); - } - - public Object invokeOriginalMethod(Object thisObject, Object[] args) throws InvocationTargetException, IllegalAccessException { - return backup.invoke(thisObject, args); - } - - @SuppressWarnings({"unused", "RedundantSuppression"}) - public Object handleHookedMethod(Object[] args) throws Throwable { - XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam(); - - param.method = method; - - if (Modifier.isStatic(method.getModifiers())) { - param.thisObject = null; - param.args = args; - } else { - param.thisObject = args[0]; - param.args = new Object[args.length - 1]; - System.arraycopy(args, 1, param.args, 0, args.length - 1); - } - - Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot(); - final int callbacksLength = callbacksSnapshot.length; - if (callbacksLength == 0) { - try { - return backup.invoke(param.thisObject, param.args); - } catch (InvocationTargetException ite) { - throw ite.getCause(); - } - } - - // call "before method" callbacks - int beforeIdx = 0; - do { - try { - ((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param); - } catch (Throwable t) { - XposedBridge.log(t); - - // reset result (ignoring what the unexpectedly exiting callback did) - param.setResult(null); - param.returnEarly = false; - continue; - } - - if (param.returnEarly) { - // skip remaining "before" callbacks and corresponding "after" callbacks - beforeIdx++; - break; - } - } while (++beforeIdx < callbacksLength); - - // call original method if not requested otherwise - if (!param.returnEarly) { - try { - param.setResult(backup.invoke(param.thisObject, param.args)); - } catch (InvocationTargetException e) { - param.setThrowable(e.getCause()); - } - } - - // call "after method" callbacks - int afterIdx = beforeIdx - 1; - do { - Object lastResult = param.getResult(); - Throwable lastThrowable = param.getThrowable(); - - try { - ((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param); - } catch (Throwable t) { - XposedBridge.log(t); - - // reset to last result (ignoring what the unexpectedly exiting callback did) - if (lastThrowable == null) - param.setResult(lastResult); - else - param.setThrowable(lastThrowable); - } - } while (--afterIdx >= 0); - - // return - if (param.hasThrowable()) - throw param.getThrowable(); - else { - var result = param.getResult(); - if (method instanceof Method) { - var returnType = ((Method) method).getReturnType(); - if (!returnType.isPrimitive()) - return returnType.cast(result); - } - return result; - } - } - -} diff --git a/core/src/main/java/de/robv/android/xposed/XposedBridge.java b/core/src/main/java/de/robv/android/xposed/XposedBridge.java index 7278b08b..723000af 100644 --- a/core/src/main/java/de/robv/android/xposed/XposedBridge.java +++ b/core/src/main/java/de/robv/android/xposed/XposedBridge.java @@ -28,8 +28,8 @@ import android.content.res.TypedArray; import android.util.Log; import org.lsposed.lspd.BuildConfig; +import org.lsposed.lspd.nativebridge.HookBridge; import org.lsposed.lspd.nativebridge.ResourcesHook; -import org.lsposed.lspd.yahfa.hooker.YahfaHooker; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Executable; @@ -39,15 +39,11 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; import de.robv.android.xposed.callbacks.XC_InitPackageResources; -import de.robv.android.xposed.callbacks.XC_InitZygote; import de.robv.android.xposed.callbacks.XC_LoadPackage; /** @@ -78,10 +74,8 @@ public final class XposedBridge { private static final Object[] EMPTY_ARRAY = new Object[0]; // built-in handlers - private static final Map> sHookedMethodCallbacks = new NoValuesHashMap<>(); - public static final CopyOnWriteSortedSet sLoadedPackageCallbacks = new CopyOnWriteSortedSet<>(); - /*package*/ static final CopyOnWriteSortedSet sInitPackageResourcesCallbacks = new CopyOnWriteSortedSet<>(); - /*package*/ static final CopyOnWriteSortedSet sInitZygoteCallbacks = new CopyOnWriteSortedSet<>(); + public static final CopyOnWriteArraySet sLoadedPackageCallbacks = new CopyOnWriteArraySet<>(); + /*package*/ static final CopyOnWriteArraySet sInitPackageResourcesCallbacks = new CopyOnWriteArraySet<>(); private XposedBridge() { } @@ -124,7 +118,7 @@ public final class XposedBridge { ResourcesHook.makeInheritable(resClass, resClass.getDeclaredConstructors()); ResourcesHook.makeInheritable(taClass, taClass.getDeclaredConstructors()); ClassLoader myCL = XposedBridge.class.getClassLoader(); - dummyClassLoader = ResourcesHook.buildDummyClassLoader(myCL.getParent(), resClass, taClass); + dummyClassLoader = ResourcesHook.buildDummyClassLoader(myCL.getParent(), resClass.getName(), taClass.getName()); dummyClassLoader.loadClass("xposed.dummy.XResourcesSuperClass"); dummyClassLoader.loadClass("xposed.dummy.XTypedArraySuperClass"); setObjectField(myCL, "parent", dummyClassLoader); @@ -180,7 +174,7 @@ public final class XposedBridge { } else if (Proxy.isProxyClass(deoptimizedMethod.getDeclaringClass())) { throw new IllegalArgumentException("Cannot deoptimize methods from proxy class: " + deoptimizedMethod); } - YahfaHooker.deoptMethodNative((Executable) deoptimizedMethod); + HookBridge.deoptimizeMethod((Executable) deoptimizedMethod); } /** @@ -211,28 +205,11 @@ public final class XposedBridge { throw new IllegalArgumentException("Do not allow hooking inner methods"); } - Executable targetMethod = (Executable) hookMethod; - if (callback == null) { throw new IllegalArgumentException("callback should not be null!"); } - boolean newMethod = false; - CopyOnWriteSortedSet callbacks; - synchronized (sHookedMethodCallbacks) { - callbacks = sHookedMethodCallbacks.get(targetMethod); - if (callbacks == null) { - callbacks = new CopyOnWriteSortedSet<>(); - sHookedMethodCallbacks.put(targetMethod, callbacks); - newMethod = true; - } - } - callbacks.add(callback); - - if (newMethod) { - AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks); - YahfaHooker.hookMethod(targetMethod, additionalInfo); - } + HookBridge.hookMethod((Executable) hookMethod, AdditionalHookInfo.class, callback.priority, callback); return callback.new Unhook(hookMethod); } @@ -247,13 +224,9 @@ public final class XposedBridge { */ @Deprecated public static void unhookMethod(Member hookMethod, XC_MethodHook callback) { - CopyOnWriteSortedSet callbacks; - synchronized (sHookedMethodCallbacks) { - callbacks = sHookedMethodCallbacks.get(hookMethod); - if (callbacks == null) - return; + if (hookMethod instanceof Executable) { + HookBridge.unhookMethod((Executable) hookMethod, callback); } - callbacks.remove(callback); } /** @@ -305,12 +278,6 @@ public final class XposedBridge { } } - public static void clearLoadedPackages() { - synchronized (sLoadedPackageCallbacks) { - sLoadedPackageCallbacks.clear(); - } - } - /** * Adds a callback to be executed when the resources for an app are initialized. * @@ -326,12 +293,6 @@ public final class XposedBridge { } } - public static void hookInitZygote(XC_InitZygote callback) { - synchronized (sInitZygoteCallbacks) { - sInitZygoteCallbacks.add(callback); - } - } - /** * Basically the same as {@link Method#invoke}, but calls the original method * as it was before the interception by Xposed. Also, access permissions are not checked. @@ -363,39 +324,7 @@ public final class XposedBridge { throw new IllegalArgumentException("method must be of type Method or Constructor"); } - return YahfaHooker.invokeOriginalMethod((Executable) method, thisObject, args); - } - - private static class NoValuesHashMap extends HashMap { - @Override - public Collection values() { - return Collections.EMPTY_LIST; - } - - @Override - public void clear() { - - } - - @Override - public Set keySet() { - return Collections.EMPTY_SET; - } - - @Override - public Set> entrySet() { - return Collections.EMPTY_SET; - } - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return true; - } + return HookBridge.invokeOriginalMethod((Executable) method, thisObject, args); } /** @@ -449,10 +378,99 @@ public final class XposedBridge { } public static class AdditionalHookInfo { - public final CopyOnWriteSortedSet callbacks; + final Executable method; + final Object[][] callbacks; - private AdditionalHookInfo(CopyOnWriteSortedSet callbacks) { + private AdditionalHookInfo(Executable method, Object[][] callbacks) { + this.method = method; this.callbacks = callbacks; } + + public Object callback(Object[] args) throws Throwable { + XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam(); + + param.method = method; + + if (Modifier.isStatic(method.getModifiers())) { + param.thisObject = null; + param.args = args; + } else { + param.thisObject = args[0]; + param.args = new Object[args.length - 1]; + System.arraycopy(args, 1, param.args, 0, args.length - 1); + } + + Object[] callbacksSnapshot = callbacks[0]; + final int callbacksLength = callbacksSnapshot.length; + if (callbacksLength == 0) { + try { + return HookBridge.invokeOriginalMethod(method, param.thisObject, param.args); + } catch (InvocationTargetException ite) { + throw ite.getCause(); + } + } + + // call "before method" callbacks + int beforeIdx = 0; + do { + try { + ((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param); + } catch (Throwable t) { + XposedBridge.log(t); + + // reset result (ignoring what the unexpectedly exiting callback did) + param.setResult(null); + param.returnEarly = false; + continue; + } + + if (param.returnEarly) { + // skip remaining "before" callbacks and corresponding "after" callbacks + beforeIdx++; + break; + } + } while (++beforeIdx < callbacksLength); + + // call original method if not requested otherwise + if (!param.returnEarly) { + try { + param.setResult(HookBridge.invokeOriginalMethod(method, param.thisObject, param.args)); + } catch (InvocationTargetException e) { + param.setThrowable(e.getCause()); + } + } + + // call "after method" callbacks + int afterIdx = beforeIdx - 1; + do { + Object lastResult = param.getResult(); + Throwable lastThrowable = param.getThrowable(); + + try { + ((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param); + } catch (Throwable t) { + XposedBridge.log(t); + + // reset to last result (ignoring what the unexpectedly exiting callback did) + if (lastThrowable == null) + param.setResult(lastResult); + else + param.setThrowable(lastThrowable); + } + } while (--afterIdx >= 0); + + // return + if (param.hasThrowable()) + throw param.getThrowable(); + else { + var result = param.getResult(); + if (method instanceof Method) { + var returnType = ((Method) method).getReturnType(); + if (!returnType.isPrimitive()) + return returnType.cast(result); + } + return result; + } + } } } diff --git a/core/src/main/java/de/robv/android/xposed/XposedInit.java b/core/src/main/java/de/robv/android/xposed/XposedInit.java index d76c2b92..f8d550fa 100644 --- a/core/src/main/java/de/robv/android/xposed/XposedInit.java +++ b/core/src/main/java/de/robv/android/xposed/XposedInit.java @@ -23,9 +23,6 @@ package de.robv.android.xposed; import static org.lsposed.lspd.config.LSPApplicationServiceClient.serviceClient; import static org.lsposed.lspd.deopt.PrebuiltMethodsDeopter.deoptResourceMethods; import static de.robv.android.xposed.XposedBridge.hookAllMethods; -import static de.robv.android.xposed.XposedBridge.sInitPackageResourcesCallbacks; -import static de.robv.android.xposed.XposedBridge.sInitZygoteCallbacks; -import static de.robv.android.xposed.XposedBridge.sLoadedPackageCallbacks; import static de.robv.android.xposed.XposedHelpers.callMethod; import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; import static de.robv.android.xposed.XposedHelpers.getObjectField; @@ -58,8 +55,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import de.robv.android.xposed.callbacks.XC_InitPackageResources; -import de.robv.android.xposed.callbacks.XC_InitZygote; -import de.robv.android.xposed.callbacks.XC_LoadPackage; import de.robv.android.xposed.callbacks.XCallback; import hidden.HiddenApiBridge; @@ -219,79 +214,22 @@ public final class XposedInit { return newRes; } - /** - * Try to load all modules defined in INSTALLER_DATA_BASE_DIR/conf/modules.list - */ - private static final AtomicBoolean modulesLoaded = new AtomicBoolean(false); - private static final Object moduleLoadLock = new Object(); - // @GuardedBy("moduleLoadLock") private static final ArraySet loadedModules = new ArraySet<>(); - public static ArraySet getLoadedModules() { - synchronized (moduleLoadLock) { - return loadedModules; - } + synchronized public static ArraySet getLoadedModules() { + return loadedModules; } - public static void loadModules() { - boolean hasLoaded = !modulesLoaded.compareAndSet(false, true); - if (hasLoaded) { - return; - } - synchronized (moduleLoadLock) { - var moduleList = serviceClient.getModulesList(); - var newLoadedApk = new ArraySet(); - moduleList.forEach(module -> { - var apk = module.apkPath; - var name = module.packageName; - var file = module.file; - if (loadedModules.contains(apk)) { - newLoadedApk.add(apk); - } else { - loadedModules.add(apk); // temporarily add it for XSharedPreference - boolean loadSuccess = loadModule(name, apk, file); - if (loadSuccess) { - newLoadedApk.add(apk); - } - } - - loadedModules.clear(); - loadedModules.addAll(newLoadedApk); - - // refresh callback according to current loaded module list - pruneCallbacks(); - }); - } - } - - // remove deactivated or outdated module callbacks - private static void pruneCallbacks() { - synchronized (moduleLoadLock) { - Object[] loadedPkgSnapshot = sLoadedPackageCallbacks.getSnapshot(); - Object[] initPkgResSnapshot = sInitPackageResourcesCallbacks.getSnapshot(); - Object[] initZygoteSnapshot = sInitZygoteCallbacks.getSnapshot(); - for (Object loadedPkg : loadedPkgSnapshot) { - if (loadedPkg instanceof IModuleContext) { - if (!loadedModules.contains(((IModuleContext) loadedPkg).getApkPath())) { - sLoadedPackageCallbacks.remove((XC_LoadPackage) loadedPkg); - } - } + synchronized public static void loadModules() { + var moduleList = serviceClient.getModulesList(); + moduleList.forEach(module -> { + var apk = module.apkPath; + var name = module.packageName; + var file = module.file; + if (loadModule(name, apk, file)) { + loadedModules.add(apk); // temporarily add it for XSharedPreference } - for (Object initPkgRes : initPkgResSnapshot) { - if (initPkgRes instanceof IModuleContext) { - if (!loadedModules.contains(((IModuleContext) initPkgRes).getApkPath())) { - sInitPackageResourcesCallbacks.remove((XC_InitPackageResources) initPkgRes); - } - } - } - for (Object initZygote : initZygoteSnapshot) { - if (initZygote instanceof IModuleContext) { - if (!loadedModules.contains(((IModuleContext) initZygote).getApkPath())) { - sInitZygoteCallbacks.remove((XC_InitZygote) initZygote); - } - } - } - } + }); } /** @@ -321,23 +259,18 @@ public final class XposedInit { IXposedHookZygoteInit.StartupParam param = new IXposedHookZygoteInit.StartupParam(); param.modulePath = apk; param.startsSystemServer = startsSystemServer; - - XposedBridge.hookInitZygote(new IXposedHookZygoteInit.Wrapper( - (IXposedHookZygoteInit) moduleInstance, param)); ((IXposedHookZygoteInit) moduleInstance).initZygote(param); count++; } if (moduleInstance instanceof IXposedHookLoadPackage) { - XposedBridge.hookLoadPackage(new IXposedHookLoadPackage.Wrapper( - (IXposedHookLoadPackage) moduleInstance, apk)); + XposedBridge.hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance)); count++; } if (moduleInstance instanceof IXposedHookInitPackageResources) { hookResources(); - XposedBridge.hookInitPackageResources(new IXposedHookInitPackageResources.Wrapper( - (IXposedHookInitPackageResources) moduleInstance, apk)); + XposedBridge.hookInitPackageResources(new IXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources) moduleInstance)); count++; } } catch (Throwable t) { diff --git a/core/src/main/java/de/robv/android/xposed/callbacks/XC_InitPackageResources.java b/core/src/main/java/de/robv/android/xposed/callbacks/XC_InitPackageResources.java index c46917c6..dd8e42d7 100644 --- a/core/src/main/java/de/robv/android/xposed/callbacks/XC_InitPackageResources.java +++ b/core/src/main/java/de/robv/android/xposed/callbacks/XC_InitPackageResources.java @@ -22,8 +22,9 @@ package de.robv.android.xposed.callbacks; import android.content.res.XResources; +import java.util.concurrent.CopyOnWriteArraySet; + import de.robv.android.xposed.IXposedHookInitPackageResources; -import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet; /** * This class is only used for internal purposes, except for the {@link InitPackageResourcesParam} @@ -57,8 +58,8 @@ public abstract class XC_InitPackageResources extends XCallback implements IXpos /** * @hide */ - public InitPackageResourcesParam(CopyOnWriteSortedSet callbacks) { - super(callbacks); + public InitPackageResourcesParam(CopyOnWriteArraySet callbacks) { + super(callbacks.toArray(new XCallback[0])); } /** diff --git a/core/src/main/java/de/robv/android/xposed/callbacks/XC_InitZygote.java b/core/src/main/java/de/robv/android/xposed/callbacks/XC_InitZygote.java deleted file mode 100644 index a04f7255..00000000 --- a/core/src/main/java/de/robv/android/xposed/callbacks/XC_InitZygote.java +++ /dev/null @@ -1,59 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -package de.robv.android.xposed.callbacks; - -import de.robv.android.xposed.IXposedHookZygoteInit; - -/** - * This class is only used for internal purposes, except for the {@link StartupParam} - * subclass. - */ -public abstract class XC_InitZygote extends XCallback implements IXposedHookZygoteInit { - - /** - * Creates a new callback with default priority. - * - * @hide - */ - @SuppressWarnings("deprecation") - public XC_InitZygote() { - super(); - } - - /** - * Creates a new callback with a specific priority. - * - * @param priority See {@link XCallback#priority}. - * @hide - */ - public XC_InitZygote(int priority) { - super(priority); - } - - /** - * @hide - */ - @Override - protected void call(Param param) throws Throwable { - if (param instanceof StartupParam) - initZygote((StartupParam) param); - } -} diff --git a/core/src/main/java/de/robv/android/xposed/callbacks/XC_LayoutInflated.java b/core/src/main/java/de/robv/android/xposed/callbacks/XC_LayoutInflated.java index 62026fea..fd75f71f 100644 --- a/core/src/main/java/de/robv/android/xposed/callbacks/XC_LayoutInflated.java +++ b/core/src/main/java/de/robv/android/xposed/callbacks/XC_LayoutInflated.java @@ -56,7 +56,7 @@ public abstract class XC_LayoutInflated extends XCallback { * @hide */ public LayoutInflatedParam(CopyOnWriteSortedSet callbacks) { - super(callbacks); + super((XCallback[]) callbacks.getSnapshot()); } /** diff --git a/core/src/main/java/de/robv/android/xposed/callbacks/XC_LoadPackage.java b/core/src/main/java/de/robv/android/xposed/callbacks/XC_LoadPackage.java index 5cd3a1a4..d33429fa 100644 --- a/core/src/main/java/de/robv/android/xposed/callbacks/XC_LoadPackage.java +++ b/core/src/main/java/de/robv/android/xposed/callbacks/XC_LoadPackage.java @@ -22,6 +22,8 @@ package de.robv.android.xposed.callbacks; import android.content.pm.ApplicationInfo; +import java.util.concurrent.CopyOnWriteArraySet; + import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet; @@ -57,8 +59,8 @@ public abstract class XC_LoadPackage extends XCallback implements IXposedHookLoa /** * @hide */ - public LoadPackageParam(CopyOnWriteSortedSet callbacks) { - super(callbacks); + public LoadPackageParam(CopyOnWriteArraySet callbacks) { + super(callbacks.toArray(new XCallback[0])); } /** diff --git a/core/src/main/java/de/robv/android/xposed/callbacks/XCallback.java b/core/src/main/java/de/robv/android/xposed/callbacks/XCallback.java index 839a8f38..13958a65 100644 --- a/core/src/main/java/de/robv/android/xposed/callbacks/XCallback.java +++ b/core/src/main/java/de/robv/android/xposed/callbacks/XCallback.java @@ -26,9 +26,7 @@ import org.lsposed.lspd.deopt.PrebuiltMethodsDeopter; import java.io.Serializable; -import de.robv.android.xposed.IModuleContext; import de.robv.android.xposed.XposedBridge; -import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet; /** * Base class for Xposed callbacks. @@ -36,7 +34,7 @@ import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet; * This class only keeps a priority for ordering multiple callbacks. * The actual (abstract) callback methods are added by subclasses. */ -public abstract class XCallback implements Comparable, IModuleContext { +abstract public class XCallback { /** * Callback priority, higher number means earlier execution. * @@ -69,7 +67,7 @@ public abstract class XCallback implements Comparable, IModuleContext /** * @hide */ - public final Object[] callbacks; + public final XCallback[] callbacks; private Bundle extra; /** @@ -83,8 +81,8 @@ public abstract class XCallback implements Comparable, IModuleContext /** * @hide */ - protected Param(CopyOnWriteSortedSet callbacks) { - this.callbacks = callbacks.getSnapshot(); + protected Param(XCallback[] callbacks) { + this.callbacks = callbacks; } /** @@ -147,7 +145,7 @@ public abstract class XCallback implements Comparable, IModuleContext for (int i = 0; i < param.callbacks.length; i++) { try { - ((XCallback) param.callbacks[i]).call(param); + param.callbacks[i].call(param); } catch (Throwable t) { XposedBridge.log(t); } @@ -160,28 +158,6 @@ public abstract class XCallback implements Comparable, IModuleContext protected void call(Param param) throws Throwable { } - @Override - public String getApkPath() { - return ""; - } - - /** - * @hide - */ - @Override - public int compareTo(XCallback other) { - if (this == other) - return 0; - - // order descending by priority - if (other.priority != this.priority) - return other.priority - this.priority; - // then randomly - else if (System.identityHashCode(this) < System.identityHashCode(other)) - return -1; - else - return 1; - } /** * The default priority, see {@link #priority}. diff --git a/core/src/main/java/org/lsposed/lspd/core/Main.java b/core/src/main/java/org/lsposed/lspd/core/Main.java index aaecb606..4fcc3c05 100644 --- a/core/src/main/java/org/lsposed/lspd/core/Main.java +++ b/core/src/main/java/org/lsposed/lspd/core/Main.java @@ -39,7 +39,6 @@ import org.lsposed.lspd.hooker.LoadedApkCstrHooker; import org.lsposed.lspd.hooker.HandleSystemServerProcessHooker; import org.lsposed.lspd.util.ParasiticManagerHooker; import org.lsposed.lspd.util.Utils; -import org.lsposed.lspd.yahfa.hooker.YahfaHooker; import java.io.File; @@ -77,7 +76,6 @@ public class Main { public static void forkPostCommon(boolean isSystem, String appDataDir, String niceName) { // init logger - YahfaHooker.init(); XposedBridge.initXResources(); XposedInit.startsSystemServer = isSystem; PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote diff --git a/core/src/main/java/org/lsposed/lspd/core/yahfa/HookMain.java b/core/src/main/java/org/lsposed/lspd/core/yahfa/HookMain.java deleted file mode 100644 index b31694e9..00000000 --- a/core/src/main/java/org/lsposed/lspd/core/yahfa/HookMain.java +++ /dev/null @@ -1,103 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -package org.lsposed.lspd.core.yahfa; - -import org.lsposed.lspd.nativebridge.Yahfa; -import org.lsposed.lspd.util.Utils; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.Arrays; - -public class HookMain { - public static void backupAndHook(Executable target, Method hook, Method backup) { - Utils.logD(String.format("target=%s, hook=%s, backup=%s", target, hook, backup)); - if (target == null) { - throw new IllegalArgumentException("null target method"); - } - if (hook == null) { - throw new IllegalArgumentException("null hook method"); - } - - if (!Modifier.isStatic(hook.getModifiers())) { - throw new IllegalArgumentException("Hook must be a static method: " + hook); - } - checkCompatibleMethods(target, hook, "Hook"); - if (backup != null) { - if (!Modifier.isStatic(backup.getModifiers())) { - throw new IllegalArgumentException("Backup must be a static method: " + backup); - } - // backup is just a placeholder and the constraint could be less strict - checkCompatibleMethods(target, backup, "Backup"); - } - if (!Yahfa.backupAndHookNative(target, hook, backup, Proxy.isProxyClass(target.getDeclaringClass()))) { - throw new RuntimeException("Failed to hook " + target + " with " + hook); - } - } - - private static void checkCompatibleMethods(Executable original, Method replacement, String replacementName) { - ArrayList> originalParams; - if (original instanceof Method) { - originalParams = new ArrayList<>(Arrays.asList(original.getParameterTypes())); - } else if (original instanceof Constructor) { - originalParams = new ArrayList<>(Arrays.asList(original.getParameterTypes())); - } else { - throw new IllegalArgumentException("Type of target method is wrong"); - } - - ArrayList> replacementParams = new ArrayList<>(Arrays.asList(replacement.getParameterTypes())); - - if (original instanceof Method - && !Modifier.isStatic(original.getModifiers())) { - originalParams.add(0, original.getDeclaringClass()); - } else if (original instanceof Constructor) { - originalParams.add(0, ((Constructor) original).getDeclaringClass()); - } - - - if (!Modifier.isStatic(replacement.getModifiers())) { - replacementParams.add(0, replacement.getDeclaringClass()); - } - - if (original instanceof Method - && !replacement.getReturnType().isAssignableFrom(((Method) original).getReturnType())) { - throw new IllegalArgumentException("Incompatible return types. " + "Original" + ": " + ((Method) original).getReturnType() + ", " + replacementName + ": " + replacement.getReturnType()); - } else if (original instanceof Constructor) { - if (replacement.getReturnType().equals(Void.class)) { - throw new IllegalArgumentException("Incompatible return types. " + "" + ": " + "V" + ", " + replacementName + ": " + replacement.getReturnType()); - } - } - - if (originalParams.size() != replacementParams.size()) { - throw new IllegalArgumentException("Number of arguments don't match. " + "Original" + ": " + originalParams.size() + ", " + replacementName + ": " + replacementParams.size()); - } - - for (int i = 0; i < originalParams.size(); i++) { - if (!replacementParams.get(i).isAssignableFrom(originalParams.get(i))) { - throw new IllegalArgumentException("Incompatible argument #" + i + ": " + "Original" + ": " + originalParams.get(i) + ", " + replacementName + ": " + replacementParams.get(i)); - } - } - } -} diff --git a/core/src/main/java/org/lsposed/lspd/deopt/PrebuiltMethodsDeopter.java b/core/src/main/java/org/lsposed/lspd/deopt/PrebuiltMethodsDeopter.java index 23baa0cd..d7ea240b 100644 --- a/core/src/main/java/org/lsposed/lspd/deopt/PrebuiltMethodsDeopter.java +++ b/core/src/main/java/org/lsposed/lspd/deopt/PrebuiltMethodsDeopter.java @@ -24,9 +24,8 @@ import static org.lsposed.lspd.deopt.InlinedMethodCallers.KEY_BOOT_IMAGE; import static org.lsposed.lspd.deopt.InlinedMethodCallers.KEY_BOOT_IMAGE_MIUI_RES; import static org.lsposed.lspd.deopt.InlinedMethodCallers.KEY_SYSTEM_SERVER; -import org.lsposed.lspd.nativebridge.Yahfa; +import org.lsposed.lspd.nativebridge.HookBridge; import org.lsposed.lspd.util.Utils; -import org.lsposed.lspd.yahfa.hooker.YahfaHooker; import java.lang.reflect.Executable; import java.util.Arrays; @@ -46,9 +45,9 @@ public class PrebuiltMethodsDeopter { if (clazz == null) { continue; } - Executable method = Yahfa.findMethodNative(clazz, caller[1], caller[2]); + Executable method = null; if (method != null) { - YahfaHooker.deoptMethodNative(method); + HookBridge.deoptimizeMethod(method); } } catch (Throwable throwable) { Utils.logE("error when deopting method: " + Arrays.toString(caller), throwable); diff --git a/core/src/main/java/org/lsposed/lspd/nativebridge/ClassLinker.java b/core/src/main/java/org/lsposed/lspd/nativebridge/ClassLinker.java deleted file mode 100644 index f0cb8ecb..00000000 --- a/core/src/main/java/org/lsposed/lspd/nativebridge/ClassLinker.java +++ /dev/null @@ -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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -package org.lsposed.lspd.nativebridge; - -import java.lang.reflect.Executable; - -public class ClassLinker { - - public static native void setEntryPointsToInterpreter(Executable method); -} diff --git a/core/src/main/java/org/lsposed/lspd/nativebridge/HookBridge.java b/core/src/main/java/org/lsposed/lspd/nativebridge/HookBridge.java new file mode 100644 index 00000000..118e78ed --- /dev/null +++ b/core/src/main/java/org/lsposed/lspd/nativebridge/HookBridge.java @@ -0,0 +1,13 @@ +package org.lsposed.lspd.nativebridge; + +import java.lang.reflect.Executable; +import java.lang.reflect.InvocationTargetException; + +public class HookBridge { + public static native boolean hookMethod(Executable hookMethod, Class hooker, int priority, Object callback); + public static native boolean unhookMethod(Executable hookMethod, Object callback); + + public static native boolean deoptimizeMethod(Executable method); + + public static native Object invokeOriginalMethod(Executable method, Object thisObject, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException; +} diff --git a/core/src/main/java/org/lsposed/lspd/nativebridge/ResourcesHook.java b/core/src/main/java/org/lsposed/lspd/nativebridge/ResourcesHook.java index ac444e5a..cd1c007c 100644 --- a/core/src/main/java/org/lsposed/lspd/nativebridge/ResourcesHook.java +++ b/core/src/main/java/org/lsposed/lspd/nativebridge/ResourcesHook.java @@ -31,7 +31,7 @@ public class ResourcesHook { public static native boolean makeInheritable(Class clazz, Constructor[] constructors); - public static native ClassLoader buildDummyClassLoader(ClassLoader parent, Class resourceSuperClass, Class typedArraySuperClass); + public static native ClassLoader buildDummyClassLoader(ClassLoader parent, String resourceSuperClass, String typedArraySuperClass); public static native void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes); } diff --git a/core/src/main/java/org/lsposed/lspd/nativebridge/Yahfa.java b/core/src/main/java/org/lsposed/lspd/nativebridge/Yahfa.java deleted file mode 100644 index 05a5086d..00000000 --- a/core/src/main/java/org/lsposed/lspd/nativebridge/Yahfa.java +++ /dev/null @@ -1,38 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -package org.lsposed.lspd.nativebridge; - -import java.lang.reflect.Executable; -import java.lang.reflect.Method; - -public class Yahfa { - - public static native boolean backupAndHookNative(Executable target, Method hook, Method backup, boolean isProxy); - - // JNI.ToReflectedMethod() could return either Method or Constructor - public static native Executable findMethodNative(Class targetClass, String methodName, String methodSig); - - public static native void init(int sdkVersion); - - public static native boolean isHooked(Executable member); - - public static native Class buildHooker(ClassLoader appClassLoader, char returnType, char[] params, String methodName, String hookerName); -} diff --git a/core/src/main/java/org/lsposed/lspd/yahfa/dexmaker/DynamicBridge.java b/core/src/main/java/org/lsposed/lspd/yahfa/dexmaker/DynamicBridge.java deleted file mode 100644 index e0d58b5e..00000000 --- a/core/src/main/java/org/lsposed/lspd/yahfa/dexmaker/DynamicBridge.java +++ /dev/null @@ -1,73 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -package org.lsposed.lspd.yahfa.dexmaker; - -import org.lsposed.lspd.util.Utils; - -import java.lang.reflect.Executable; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Member; -import java.util.concurrent.ConcurrentHashMap; - -import de.robv.android.xposed.LspHooker; -import de.robv.android.xposed.XC_MethodHook; -import de.robv.android.xposed.XposedBridge; - -public final class DynamicBridge { - private static final ConcurrentHashMap hookedInfo = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap lockers = new ConcurrentHashMap<>(); - - public static void hookMethod(Executable hookMethod, XposedBridge.AdditionalHookInfo additionalHookInfo) { - Utils.logD("hooking " + hookMethod); - - synchronized (lockers.computeIfAbsent(hookMethod, (m) -> new Object())) { - var hooker = hookedInfo.getOrDefault(hookMethod, null); - if (hooker == null) { - Utils.logD("start to generate class for: " + hookMethod); - try { - final HookerDexMaker dexMaker = new HookerDexMaker(); - dexMaker.start(hookMethod, additionalHookInfo); - hookedInfo.put(hookMethod, dexMaker.getHooker()); - } catch (Throwable e) { - Utils.logE("error occur when generating dex.", e); - } - } else { - for (var callback : additionalHookInfo.callbacks.getSnapshot()) - hooker.additionalInfo.callbacks.add((XC_MethodHook) callback); - } - } - } - - public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) - throws InvocationTargetException, IllegalAccessException { - if (!(method instanceof Executable)) { - throw new IllegalArgumentException("Only methods or constructors can be invoked."); - } - LspHooker hooker = hookedInfo.getOrDefault(method, null); - if (hooker == null) { - throw new IllegalStateException("method not hooked, cannot call original method."); - } - if (args == null) { - args = new Object[0]; - } - return hooker.invokeOriginalMethod(thisObject, args); - } -} diff --git a/core/src/main/java/org/lsposed/lspd/yahfa/dexmaker/HookerDexMaker.java b/core/src/main/java/org/lsposed/lspd/yahfa/dexmaker/HookerDexMaker.java deleted file mode 100644 index 7c92e2e2..00000000 --- a/core/src/main/java/org/lsposed/lspd/yahfa/dexmaker/HookerDexMaker.java +++ /dev/null @@ -1,123 +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 . - * - * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors - */ - -package org.lsposed.lspd.yahfa.dexmaker; - -import org.lsposed.lspd.core.yahfa.HookMain; -import org.lsposed.lspd.nativebridge.Yahfa; -import org.lsposed.lspd.util.Utils; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.HashMap; - -import de.robv.android.xposed.LspHooker; -import de.robv.android.xposed.XposedBridge; - -@SuppressWarnings("rawtypes") -public class HookerDexMaker { - - public static final String METHOD_NAME_BACKUP = "backup"; - public static final String FIELD_NAME_HOOKER = "hooker"; - private static final HashMap, Character> descriptors = new HashMap<>() {{ - put(int.class, 'I'); - put(boolean.class, 'Z'); - put(char.class, 'C'); - put(long.class, 'J'); - put(short.class, 'S'); - put(float.class, 'F'); - put(double.class, 'D'); - put(byte.class, 'B'); - put(void.class, 'V'); - put(Object.class, 'L'); - }}; - - private Class mReturnType; - private Class[] mActualParameterTypes; - - private Executable mMember; - private XposedBridge.AdditionalHookInfo mHookInfo; - private LspHooker mHooker; - - private static Class[] getParameterTypes(Executable method, boolean isStatic) { - Class[] parameterTypes = method.getParameterTypes(); - for (int i = 0; i < parameterTypes.length; ++i) { - parameterTypes[i] = parameterTypes[i].isPrimitive() ? parameterTypes[i] : Object.class; - } - if (isStatic) { - return parameterTypes; - } - int parameterSize = parameterTypes.length; - Class[] newParameterTypes = new Class[parameterSize + 1]; - newParameterTypes[0] = Object.class; - System.arraycopy(parameterTypes, 0, newParameterTypes, 1, parameterSize); - return newParameterTypes; - } - - private static char getDescriptor(Class clazz) { - return descriptors.getOrDefault(clazz, 'L'); - } - - private static char[] getDescriptors(Class[] classes) { - var descriptors = new char[classes.length]; - for (int i = 0; i < classes.length; ++i) { - descriptors[i] = getDescriptor(classes[i]); - } - return descriptors; - } - - public void start(Executable member, XposedBridge.AdditionalHookInfo hookInfo) throws Exception { - if (member instanceof Method) { - Method method = (Method) member; - mReturnType = method.getReturnType(); - mActualParameterTypes = getParameterTypes(method, Modifier.isStatic(method.getModifiers())); - } else if (member instanceof Constructor) { - Constructor constructor = (Constructor) member; - mReturnType = void.class; - mActualParameterTypes = getParameterTypes(constructor, false); - } - mMember = member; - mHookInfo = hookInfo; - - long startTime = System.nanoTime(); - doMake(member instanceof Constructor ? "constructor" : member.getName()); - long endTime = System.nanoTime(); - Utils.logD("Hook time: " + (endTime - startTime) / 1e6 + "ms"); - } - - private void doMake(String methodName) throws Exception { - Class hookClass = Yahfa.buildHooker(LspHooker.class.getClassLoader(), getDescriptor(mReturnType), getDescriptors(mActualParameterTypes), methodName, LspHooker.class.getCanonicalName()); - if (hookClass == null) throw new IllegalStateException("Failed to hook " + methodName); - // Execute our newly-generated code in-process. - Method backupMethod = hookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes); - mHooker = new LspHooker(mHookInfo, mMember, backupMethod); - var hooker = hookClass.getDeclaredField(FIELD_NAME_HOOKER); - hooker.setAccessible(true); - hooker.set(null, mHooker); - Method hookMethod = hookClass.getMethod(methodName, mActualParameterTypes); - HookMain.backupAndHook(mMember, hookMethod, backupMethod); - } - - public LspHooker getHooker() { - return mHooker; - } -} diff --git a/core/src/main/java/org/lsposed/lspd/yahfa/hooker/YahfaHooker.java b/core/src/main/java/org/lsposed/lspd/yahfa/hooker/YahfaHooker.java deleted file mode 100644 index afea0f21..00000000 --- a/core/src/main/java/org/lsposed/lspd/yahfa/hooker/YahfaHooker.java +++ /dev/null @@ -1,52 +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 . - * - * Copyright (C) 2021 LSPosed Contributors - */ - -package org.lsposed.lspd.yahfa.hooker; - -import android.os.Build; - -import org.lsposed.lspd.nativebridge.ClassLinker; -import org.lsposed.lspd.nativebridge.Yahfa; -import org.lsposed.lspd.yahfa.dexmaker.DynamicBridge; - -import java.lang.reflect.Executable; - -import de.robv.android.xposed.XposedBridge.AdditionalHookInfo; - -public class YahfaHooker { - public static void init() { - int sdkVersion = Build.VERSION.SDK_INT; - if (Build.VERSION.PREVIEW_SDK_INT != 0) { - sdkVersion += 1; - } - Yahfa.init(sdkVersion); - } - - public static void hookMethod(Executable method, AdditionalHookInfo additionalInfo) { - DynamicBridge.hookMethod(method, additionalInfo); - } - - public static Object invokeOriginalMethod(Executable method, Object thisObject, Object[] args) throws Throwable { - return DynamicBridge.invokeOriginalMethod(method, thisObject, args); - } - - public static void deoptMethodNative(Executable method) { - ClassLinker.setEntryPointsToInterpreter(method); - } -} diff --git a/core/src/main/jni/CMakeLists.txt b/core/src/main/jni/CMakeLists.txt new file mode 100644 index 00000000..30c2202a --- /dev/null +++ b/core/src/main/jni/CMakeLists.txt @@ -0,0 +1,33 @@ +project(lspd) +cmake_minimum_required(VERSION 3.4.1) + +add_subdirectory(${EXTERNAL_ROOT} external) + +configure_file(template/config.cpp src/config.cpp) + +aux_source_directory(src SRC_LIST) +aux_source_directory(src/jni SRC_LIST) +if (${API} STREQUAL "riru") + set(SRC_LIST ${SRC_LIST} api/riru_main.cpp) +elseif (${API} STREQUAL "zygisk") + set(SRC_LIST ${SRC_LIST} api/zygisk_main.cpp) +endif() + +add_library(${PROJECT_NAME} SHARED ${SRC_LIST} ${CMAKE_CURRENT_BINARY_DIR}/src/config.cpp) + +target_include_directories(${PROJECT_NAME} PUBLIC include) +target_include_directories(${PROJECT_NAME} PRIVATE src) + +target_link_libraries(${PROJECT_NAME} dobby dex_builder_static log lsplant_static) + +if (DEFINED DEBUG_SYMBOLS_PATH) + set(DEBUG_SYMBOLS_PATH ${DEBUG_SYMBOLS_PATH}/${API}) + message(STATUS "Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI} + COMMAND ${CMAKE_OBJCOPY} --only-keep-debug $ + ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug + COMMAND ${CMAKE_STRIP} --strip-all $ + COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug + $) +endif() diff --git a/core/src/main/jni/api/riru.h b/core/src/main/jni/api/riru.h new file mode 100644 index 00000000..e3f04c20 --- /dev/null +++ b/core/src/main/jni/api/riru.h @@ -0,0 +1,125 @@ +#ifndef RIRU_H +#define RIRU_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +// --------------------------------------------------------- + +typedef void(onModuleLoaded_v9)(); + +#ifndef RIRU_MODULE +typedef int(shouldSkipUid_v9)(int uid); +#endif + +typedef void(nativeForkAndSpecializePre_v9)( + JNIEnv *env, jclass cls, jint *uid, jint *gid, jintArray *gids, jint *runtimeFlags, + jobjectArray *rlimits, jint *mountExternal, jstring *seInfo, jstring *niceName, + jintArray *fdsToClose, jintArray *fdsToIgnore, jboolean *is_child_zygote, + jstring *instructionSet, jstring *appDataDir, jboolean *isTopApp, + jobjectArray *pkgDataInfoList, + jobjectArray *whitelistedDataInfoList, jboolean *bindMountAppDataDirs, + jboolean *bindMountAppStorageDirs); + +typedef void(nativeForkAndSpecializePost_v9)(JNIEnv *env, jclass cls, jint res); + +typedef void(nativeForkSystemServerPre_v9)( + JNIEnv *env, jclass cls, uid_t *uid, gid_t *gid, jintArray *gids, jint *runtimeFlags, + jobjectArray *rlimits, jlong *permittedCapabilities, jlong *effectiveCapabilities); + +typedef void(nativeForkSystemServerPost_v9)(JNIEnv *env, jclass cls, jint res); + +typedef void(nativeSpecializeAppProcessPre_v9)( + JNIEnv *env, jclass cls, jint *uid, jint *gid, jintArray *gids, jint *runtimeFlags, + jobjectArray *rlimits, jint *mountExternal, jstring *seInfo, jstring *niceName, + jboolean *startChildZygote, jstring *instructionSet, jstring *appDataDir, + jboolean *isTopApp, jobjectArray *pkgDataInfoList, jobjectArray *whitelistedDataInfoList, + jboolean *bindMountAppDataDirs, jboolean *bindMountAppStorageDirs); + +typedef void(nativeSpecializeAppProcessPost_v9)(JNIEnv *env, jclass cls); + +typedef struct { + int supportHide; + int version; + const char *versionName; + onModuleLoaded_v9 *onModuleLoaded; +#ifndef RIRU_MODULE + shouldSkipUid_v9 *shouldSkipUid; +#else + void *unused; +#endif + nativeForkAndSpecializePre_v9 *forkAndSpecializePre; + nativeForkAndSpecializePost_v9 *forkAndSpecializePost; + nativeForkSystemServerPre_v9 *forkSystemServerPre; + nativeForkSystemServerPost_v9 *forkSystemServerPost; + nativeSpecializeAppProcessPre_v9 *specializeAppProcessPre; + nativeSpecializeAppProcessPost_v9 *specializeAppProcessPost; +} RiruModuleInfo; + +typedef struct { + int moduleApiVersion; + RiruModuleInfo moduleInfo; +} RiruVersionedModuleInfo; + +// --------------------------------------------------------- + +typedef struct { + int riruApiVersion; + void *unused; + const char *magiskModulePath; + int *allowUnload; +} Riru; + +typedef RiruVersionedModuleInfo *(RiruInit_t)(Riru *); + +#ifdef RIRU_MODULE +#define RIRUD_ADDRESS "rirud" + + +#if __cplusplus < 201103L +#define RIRU_EXPORT __attribute__((visibility("default"))) __attribute__((used)) +#else +#define RIRU_EXPORT [[gnu::visibility("default")]] [[gnu::used]] +#endif + +RIRU_EXPORT RiruVersionedModuleInfo *init(Riru *riru) ; + +extern int riru_api_version; +extern const char *riru_magisk_module_path; +extern int *riru_allow_unload; + +#if !__cplusplus && __STDC_VERSION__ < 199409L +#define RIRU_INLINE __attribute__((weak)) __inline__ +#elif !__cplusplus +#define RIRU_INLINE __attribute__((weak)) inline extern +#else +#define RIRU_INLINE inline +#endif + +RIRU_INLINE const char *riru_get_magisk_module_path() { + if (riru_api_version >= 24) { + return riru_magisk_module_path; + } + return NULL; +} + +RIRU_INLINE void riru_set_unload_allowed(int allowed) { + if (riru_api_version >= 25 && riru_allow_unload) { + *riru_allow_unload = allowed; + } +} +#undef RIRU_INLINE + +#endif + +#ifdef __cplusplus +} +#endif + +#endif //RIRU_H diff --git a/core/src/main/cpp/main/api/riru_main.cpp b/core/src/main/jni/api/riru_main.cpp similarity index 99% rename from core/src/main/cpp/main/api/riru_main.cpp rename to core/src/main/jni/api/riru_main.cpp index 00c2499b..22c36708 100644 --- a/core/src/main/cpp/main/api/riru_main.cpp +++ b/core/src/main/jni/api/riru_main.cpp @@ -28,7 +28,7 @@ #include "symbol_cache.h" #define RIRU_MODULE -#include +#include "riru.h" namespace lspd { int *allowUnload = nullptr; diff --git a/core/src/main/cpp/main/src/jni/zygisk.h b/core/src/main/jni/api/zygisk.h similarity index 100% rename from core/src/main/cpp/main/src/jni/zygisk.h rename to core/src/main/jni/api/zygisk.h diff --git a/core/src/main/cpp/main/api/zygisk_main.cpp b/core/src/main/jni/api/zygisk_main.cpp similarity index 98% rename from core/src/main/cpp/main/api/zygisk_main.cpp rename to core/src/main/jni/api/zygisk_main.cpp index 7e3755dc..3914d2ce 100644 --- a/core/src/main/cpp/main/api/zygisk_main.cpp +++ b/core/src/main/jni/api/zygisk_main.cpp @@ -22,7 +22,7 @@ #include #include -#include "jni/zygisk.h" +#include "zygisk.h" #include "logging.h" #include "context.h" #include "config.h" @@ -328,7 +328,10 @@ namespace lspd { auto *process = env_->FindClass("android/os/Process"); auto *set_argv0 = env_->GetStaticMethodID(process, "setArgV0", "(Ljava/lang/String;)V"); - JNI_CallStaticVoidMethod(env_, process, set_argv0, JNI_NewStringUTF(env_, "system_server")); + auto *name = env_->NewStringUTF("system_server"); + env_->CallStaticVoidMethod(process, set_argv0, name); + env_->DeleteLocalRef(name); + env_->DeleteLocalRef(process); } Context::GetInstance()->OnNativeForkSystemServerPost(env_); if (*allowUnload) api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); diff --git a/core/src/main/cpp/main/include/art/runtime/hidden_api.h b/core/src/main/jni/include/art/runtime/hidden_api.h similarity index 88% rename from core/src/main/cpp/main/include/art/runtime/hidden_api.h rename to core/src/main/jni/include/art/runtime/hidden_api.h index 8d345d40..813878d8 100644 --- a/core/src/main/cpp/main/include/art/runtime/hidden_api.h +++ b/core/src/main/jni/include/art/runtime/hidden_api.h @@ -21,14 +21,14 @@ #pragma once #include "symbol_cache.h" -#include "base/object.h" +#include "utils/hook_helper.hpp" #include "context.h" #include "runtime.h" namespace art { namespace hidden_api { - + using lsplant::operator""_tstr; CREATE_FUNC_SYMBOL_ENTRY(void, DexFile_setTrusted, JNIEnv *env, jclass clazz, jobject j_cookie) { if (DexFile_setTrustedSym != nullptr) [[likely]] { @@ -36,7 +36,7 @@ namespace art { DexFile_setTrustedSym(env, clazz, j_cookie); Runtime::Current()->SetJavaDebuggable(false); } - }; + } inline void maybeSetTrusted(JNIEnv *env, jclass clazz, jobject class_loader, jobject j_cookie) { @@ -54,7 +54,7 @@ namespace art { } } - CREATE_HOOK_STUB_ENTRIES( + CREATE_HOOK_STUB_ENTRY( "_ZN3artL25DexFile_openDexFileNativeEP7_JNIEnvP7_jclassP8_jstringS5_iP8_jobjectP13_jobjectArray", jobject, DexFile_openDexFileNative, (JNIEnv * env, jclass clazz, @@ -71,7 +71,7 @@ namespace art { } ); - CREATE_HOOK_STUB_ENTRIES( + CREATE_HOOK_STUB_ENTRY( "_ZN3artL34DexFile_openInMemoryDexFilesNativeEP7_JNIEnvP7_jclassP13_jobjectArrayS5_P10_jintArrayS7_P8_jobjectS5_", jobject, DexFile_openInMemoryDexFilesNative, (JNIEnv * env, jclass clazz, @@ -89,7 +89,7 @@ namespace art { } ); - CREATE_HOOK_STUB_ENTRIES( + CREATE_HOOK_STUB_ENTRY( "_ZN3artL29DexFile_createCookieWithArrayEP7_JNIEnvP7_jclassP11_jbyteArrayii", jobject, DexFile_createCookieWithArray, (JNIEnv * env, jclass clazz, @@ -102,7 +102,7 @@ namespace art { } ); - CREATE_HOOK_STUB_ENTRIES( + CREATE_HOOK_STUB_ENTRY( "_ZN3artL36DexFile_createCookieWithDirectBufferEP7_JNIEnvP7_jclassP8_jobjectii", jobject, DexFile_createCookieWithDirectBuffer, (JNIEnv * env, jclass clazz, @@ -115,20 +115,20 @@ namespace art { } ); - inline void DisableHiddenApi([[maybe_unused]] const SandHook::ElfImg &handle) { + inline void DisableHiddenApi(const lsplant::HookHandler &handler) { const int api_level = lspd::GetAndroidApiLevel(); if (api_level < __ANDROID_API_P__) { return; } DexFile_setTrustedSym = reinterpret_cast(lspd::symbol_cache->setTrusted); - lspd::HookSymNoHandle(lspd::symbol_cache->openDexFileNative, DexFile_openDexFileNative); - lspd::HookSymNoHandle(lspd::symbol_cache->openInMemoryDexFilesNative, + lsplant::HookSymNoHandle(handler, lspd::symbol_cache->openDexFileNative, DexFile_openDexFileNative); + lsplant::HookSymNoHandle(handler, lspd::symbol_cache->openInMemoryDexFilesNative, DexFile_openInMemoryDexFilesNative); if (api_level == __ANDROID_API_P__) { - lspd::HookSymNoHandle(lspd::symbol_cache->createCookieWithArray, + lsplant::HookSymNoHandle(handler, lspd::symbol_cache->createCookieWithArray, DexFile_createCookieWithArray); - lspd::HookSymNoHandle(lspd::symbol_cache->createCookieWithDirectBuffer, + lsplant::HookSymNoHandle(handler, lspd::symbol_cache->createCookieWithDirectBuffer, DexFile_createCookieWithDirectBuffer); } }; diff --git a/core/src/main/cpp/main/include/art/runtime/runtime.h b/core/src/main/jni/include/art/runtime/runtime.h similarity index 81% rename from core/src/main/cpp/main/include/art/runtime/runtime.h rename to core/src/main/jni/include/art/runtime/runtime.h index 8e5a12ff..c84647d4 100644 --- a/core/src/main/cpp/main/include/art/runtime/runtime.h +++ b/core/src/main/jni/include/art/runtime/runtime.h @@ -20,11 +20,11 @@ #pragma once -#include +#include "utils/hook_helper.hpp" namespace art { - class Runtime : public lspd::HookedObject { + class Runtime { private: inline static Runtime *instance_; CREATE_MEM_FUNC_SYMBOL_ENTRY(void, SetJavaDebuggable, void *thiz, bool value) { @@ -34,23 +34,22 @@ namespace art { } public: - Runtime(void *thiz) : HookedObject(thiz) {} - inline static Runtime *Current() { return instance_; } void SetJavaDebuggable(bool value) { - SetJavaDebuggable(thiz_, value); + SetJavaDebuggable(this, value); } // @ApiSensitive(Level.LOW) - inline static void Setup(const SandHook::ElfImg &handle) { + 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 = *reinterpret_cast(instance); + void *thiz = *instance; LOGD("_ZN3art7Runtime9instance_E = %p", thiz); - instance_ = new Runtime(thiz); + instance_ = reinterpret_cast(thiz); } }; diff --git a/core/src/main/cpp/main/include/config.h b/core/src/main/jni/include/config.h similarity index 91% rename from core/src/main/cpp/main/include/config.h rename to core/src/main/jni/include/config.h index e5baf050..5ab07278 100644 --- a/core/src/main/cpp/main/include/config.h +++ b/core/src/main/jni/include/config.h @@ -25,12 +25,13 @@ #include #include "macros.h" #include "utils.h" +#include "utils/hook_helper.hpp" namespace lspd { //#define LOG_DISABLED //#define DEBUG - + using lsplant::operator""_tstr; inline bool constexpr Is64() { #if defined(__LP64__) @@ -59,9 +60,7 @@ namespace lspd { #endif inline static constexpr auto kEntryClassName = "org.lsposed.lspd.core.Main"_tstr; - inline static constexpr auto kClassLinkerClassName = "org.lsposed.lspd.nativebridge.ClassLinker"_tstr; inline static constexpr auto kBridgeServiceClassName = "org.lsposed.lspd.service.BridgeService"_tstr; - inline static constexpr auto kDexPath = "framework/lspd.dex"_tstr; inline static constexpr auto kLibArtName = "libart.so"_tstr; inline static constexpr auto kLibFwName = "libandroidfw.so"_tstr; diff --git a/core/src/main/cpp/main/include/framework/androidfw/resource_types.h b/core/src/main/jni/include/framework/androidfw/resource_types.h similarity index 99% rename from core/src/main/cpp/main/include/framework/androidfw/resource_types.h rename to core/src/main/jni/include/framework/androidfw/resource_types.h index b02b71a9..4d6346ce 100644 --- a/core/src/main/cpp/main/include/framework/androidfw/resource_types.h +++ b/core/src/main/jni/include/framework/androidfw/resource_types.h @@ -22,6 +22,7 @@ #include #include +#include "utils/hook_helper.hpp" // @ApiSensitive(Level.MIDDLE) namespace android { @@ -173,7 +174,7 @@ namespace android { return {nullptr, 0u}; } - static bool setup(const SandHook::ElfImg &handle) { + static bool setup(const lsplant::HookHandler &handler) { RETRIEVE_MEM_FUNC_SYMBOL(stringAt, LP_SELECT("_ZNK7android13ResStringPool8stringAtEjPj", "_ZNK7android13ResStringPool8stringAtEmPm")); RETRIEVE_MEM_FUNC_SYMBOL(stringAtS, LP_SELECT("_ZNK7android13ResStringPool8stringAtEj", "_ZNK7android13ResStringPool8stringAtEm")); return !stringAtSym || !stringAtSSym; diff --git a/core/src/main/cpp/main/include/logging.h b/core/src/main/jni/include/logging.h similarity index 100% rename from core/src/main/cpp/main/include/logging.h rename to core/src/main/jni/include/logging.h diff --git a/core/src/main/jni/include/macros.h b/core/src/main/jni/include/macros.h new file mode 100644 index 00000000..e8b96ded --- /dev/null +++ b/core/src/main/jni/include/macros.h @@ -0,0 +1,13 @@ +#pragma once + +#include // for size_t +#include // for TEMP_FAILURE_RETRY +#include +// The arraysize(arr) macro returns the # of elements in an array arr. +// The expression is a compile-time constant, and therefore can be +// used in defining new arrays, for example. If you use arraysize on +// a pointer by mistake, you will get a compile-time error. +template +[[gnu::always_inline]] constexpr inline size_t arraysize(T(&)[N]) { + return N; +} diff --git a/core/src/main/jni/include/native_util.h b/core/src/main/jni/include/native_util.h new file mode 100644 index 00000000..c8a80f95 --- /dev/null +++ b/core/src/main/jni/include/native_util.h @@ -0,0 +1,113 @@ +/* + * 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 . + * + * Copyright (C) 2020 EdXposed Contributors + * Copyright (C) 2021 LSPosed Contributors + */ + +#include +#include "dobby.h" +#include +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-value" +#pragma once + +#include +#include "macros.h" +#include "utils/jni_helper.hpp" +#include "logging.h" +#include "config.h" +#include + +#define _uintval(p) reinterpret_cast(p) +#define _ptr(p) reinterpret_cast(p) +#define _align_up(x, n) (((x) + ((n) - 1)) & ~((n) - 1)) +#define _align_down(x, n) ((x) & -(n)) +#define _page_size 4096 +#define _page_align(n) _align_up(static_cast(n), _page_size) +#define _ptr_align(x) _ptr(_align_down(reinterpret_cast(x), _page_size)) +#define _make_rwx(p, n) ::mprotect(_ptr_align(p), \ + _page_align(_uintval(p) + n) != _page_align(_uintval(p)) ? _page_align(n) + _page_size : _page_align(n), \ + PROT_READ | PROT_WRITE | PROT_EXEC) + +namespace lspd { + +[[gnu::always_inline]] +inline bool RegisterNativeMethodsInternal(JNIEnv *env, + const char *class_name, + const JNINativeMethod *methods, + jint method_count) { + + auto clazz = Context::GetInstance()->FindClassFromCurrentLoader(env, class_name); + if (clazz.get() == nullptr) { + LOGF("Couldn't find class: %s", class_name); + return false; + } + return JNI_RegisterNatives(env, clazz, methods, method_count); +} + +#if defined(__cplusplus) +#define _NATIVEHELPER_JNI_MACRO_CAST(to) \ + reinterpret_cast +#else +#define _NATIVEHELPER_JNI_MACRO_CAST(to) \ + (to) +#endif + +#ifndef LSP_NATIVE_METHOD +#define LSP_NATIVE_METHOD(className, functionName, signature) \ + { #functionName, \ + signature, \ + _NATIVEHELPER_JNI_MACRO_CAST(void*) (Java_org_lsposed_lspd_nativebridge_## className ## _ ## functionName) \ + } +#endif + +#define JNI_START [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz + +#ifndef LSP_DEF_NATIVE_METHOD +#define LSP_DEF_NATIVE_METHOD(ret, className, functionName, ...) \ + extern "C" ret Java_org_lsposed_lspd_nativebridge_## className ## _ ## functionName (JNI_START, ## __VA_ARGS__) +#endif + +#define REGISTER_LSP_NATIVE_METHODS(class_name) \ + RegisterNativeMethodsInternal(env, "org.lsposed.lspd.nativebridge." #class_name, gMethods, arraysize(gMethods)) + +inline int HookFunction(void *original, void *replace, void **backup) { + _make_rwx(original, _page_size); + if constexpr (isDebug) { + Dl_info info; + if (dladdr(original, &info)) + LOGD("Hooking %s (%p) from %s (%p)", + info.dli_sname ? info.dli_sname : "(unknown symbol)", info.dli_saddr, + info.dli_fname ? info.dli_fname : "(unknown file)", info.dli_fbase); + } + return DobbyHook(original, replace, backup); +} + +inline int UnhookFunction(void *original) { + if constexpr (isDebug) { + Dl_info info; + if (dladdr(original, &info)) + LOGD("Unhooking %s (%p) from %s (%p)", + info.dli_sname ? info.dli_sname : "(unknown symbol)", info.dli_saddr, + info.dli_fname ? info.dli_fname : "(unknown file)", info.dli_fbase); + } + return DobbyDestroy(original); +} + +} // namespace lspd + +#pragma clang diagnostic pop diff --git a/core/src/main/cpp/main/include/utils.h b/core/src/main/jni/include/utils.h similarity index 60% rename from core/src/main/cpp/main/include/utils.h rename to core/src/main/jni/include/utils.h index 64fcdcf6..8d28ddd9 100644 --- a/core/src/main/cpp/main/include/utils.h +++ b/core/src/main/jni/include/utils.h @@ -43,45 +43,6 @@ namespace lspd { }(); return api_level; } - - - template - struct tstring : public std::integer_sequence { - inline constexpr static const char *c_str() { - return str_; - } - - inline constexpr operator std::string_view() const { - return c_str(); - } - - private: - constexpr static char str_[]{chars..., '\0'}; - }; - - template - inline constexpr tstring operator ""_tstr() { - return {}; - } - - - template - inline constexpr tstring - operator+(const tstring &, const tstring &) { - return {}; - } - - template - inline constexpr auto operator+(const std::string &a, const tstring &) { - char b[]{as..., '\0'}; - return a + b; - } - - template - inline constexpr auto operator+(const tstring &, const std::string &b) { - char a[]{as..., '\0'}; - return a + b; - } } #pragma clang diagnostic pop diff --git a/core/src/main/cpp/main/src/context.cpp b/core/src/main/jni/src/context.cpp similarity index 85% rename from core/src/main/cpp/main/src/context.cpp rename to core/src/main/jni/src/context.cpp index 278d43a4..1c4b47c5 100644 --- a/core/src/main/cpp/main/src/context.cpp +++ b/core/src/main/jni/src/context.cpp @@ -19,19 +19,23 @@ */ #include -#include "jni_helper.h" -#include "jni/art_class_linker.h" -#include "jni/yahfa.h" +#include "config.h" +#include "utils/jni_helper.hpp" #include "jni/resources_hook.h" -#include #include "context.h" #include "native_hook.h" +#include "elf_util.h" +#include "jni/hook_bridge.h" #include "jni/native_api.h" #include "service.h" +#include #include "symbol_cache.h" #include #include +#include + +using namespace lsplant; static_assert(FS_IOC_SETFLAGS == LP_SELECT(0x40046602, 0x40086602)); @@ -96,15 +100,17 @@ namespace lspd { void Context::Init() { } - void Context::Init(JNIEnv *env) { + void Context::Init(JNIEnv *env, const lsplant::InitInfo& initInfo) { + if (!lsplant::Init(env, initInfo)) { + return; + } if (auto entry_class = FindClassFromLoader(env, GetCurrentClassLoader(), kEntryClassName)) { entry_class_ = JNI_NewGlobalRef(env, entry_class); } RegisterResourcesHook(env); - RegisterArtClassLinker(env); - RegisterYahfa(env); + RegisterHookBridge(env); RegisterNativeAPI(env); } @@ -179,9 +185,22 @@ namespace lspd { instance->HookBridge(*this, env); if (application_binder) { - InstallInlineHooks(); - Init(env); + 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); + }, + }; + InstallInlineHooks(initInfo); + Init(env, initInfo); FindAndCall(env, "forkSystemServerPost", "(Landroid/os/IBinder;)V", application_binder); + GetArt(true); } else { LOGI("skipped system server"); GetArt(true); @@ -236,11 +255,23 @@ namespace lspd { auto binder = skip_ ? ScopedLocalRef{env, nullptr} : instance->RequestBinder(env, nice_name); if (binder) { - InstallInlineHooks(); + 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); + }, + }; + InstallInlineHooks(initInfo); auto [dex_fd, size] = instance->RequestLSPDex(env, binder); LoadDex(env, dex_fd, size); close(dex_fd); - Init(env); + Init(env, initInfo); LOGD("Done prepare"); FindAndCall(env, "forkAndSpecializePost", "(Ljava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V", @@ -248,6 +279,7 @@ namespace lspd { binder); LOGD("injected xposed into %s", process_name.get()); setAllowUnload(false); + GetArt(true); } else { auto context = Context::ReleaseInstance(); auto service = Service::ReleaseInstance(); diff --git a/core/src/main/cpp/main/src/context.h b/core/src/main/jni/src/context.h similarity index 93% rename from core/src/main/cpp/main/src/context.h rename to core/src/main/jni/src/context.h index 1247a6f5..2e66db3c 100644 --- a/core/src/main/cpp/main/src/context.h +++ b/core/src/main/jni/src/context.h @@ -26,8 +26,9 @@ #include #include #include +#include #include "utils.h" -#include "jni_helper.h" +#include "utils/jni_helper.hpp" namespace lspd { class Context { @@ -43,7 +44,7 @@ namespace lspd { inline jobject GetCurrentClassLoader() const { return inject_class_loader_; } - inline ScopedLocalRef + inline lsplant::ScopedLocalRef FindClassFromCurrentLoader(JNIEnv *env, std::string_view className) const { return FindClassFromLoader(env, GetCurrentClassLoader(), className); }; @@ -108,9 +109,9 @@ namespace lspd { void LoadDex(JNIEnv *env, int fd, size_t size); - void Init(JNIEnv *env); + void Init(JNIEnv *env, const lsplant::InitInfo& initInfo); - static ScopedLocalRef FindClassFromLoader(JNIEnv *env, jobject class_loader, + static lsplant::ScopedLocalRef FindClassFromLoader(JNIEnv *env, jobject class_loader, std::string_view class_name); static void setAllowUnload(bool unload); diff --git a/core/src/main/cpp/main/src/elf_util.cpp b/core/src/main/jni/src/elf_util.cpp similarity index 100% rename from core/src/main/cpp/main/src/elf_util.cpp rename to core/src/main/jni/src/elf_util.cpp diff --git a/core/src/main/cpp/main/src/elf_util.h b/core/src/main/jni/src/elf_util.h similarity index 100% rename from core/src/main/cpp/main/src/elf_util.h rename to core/src/main/jni/src/elf_util.h diff --git a/core/src/main/jni/src/jni/hook_bridge.cpp b/core/src/main/jni/src/jni/hook_bridge.cpp new file mode 100644 index 00000000..20516ab5 --- /dev/null +++ b/core/src/main/jni/src/jni/hook_bridge.cpp @@ -0,0 +1,188 @@ +/* + * 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 . + * + * Copyright (C) 2020 EdXposed Contributors + * Copyright (C) 2021 LSPosed Contributors + */ + +#include "hook_bridge.h" +#include "native_util.h" +#include "lsplant.hpp" +#include "unordered_map" +#include +#include + +using namespace lsplant; + +namespace { + +struct HookItem { + jobject backup {nullptr}; + jobjectArray callbacks {nullptr}; + std::multiset priorities {}; +}; + +std::shared_mutex hooked_lock; +// Rehashing invalidates iterators, changes ordering between elements, and changes which buckets elements appear in, but does not invalidate pointers or references to elements. +std::unordered_map hooked_methods; + +jmethodID invoke = nullptr; +} + +namespace lspd { +LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, hookMethod, jobject hookMethod, + jclass hooker, jint priority, jobject callback) { + bool newHook = false; +#ifndef NDEBUG + struct finally { + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + bool &newHook; + ~finally() { + auto finish = std::chrono::steady_clock::now(); + if (newHook) { + LOGV("New hook took %lldus", + std::chrono::duration_cast(finish - start).count()); + } + } + } finally { + .newHook = newHook + }; +#endif + auto target = env->FromReflectedMethod(hookMethod); + HookItem * hook_item = nullptr; + { + std::shared_lock lk(hooked_lock); + if (auto found = hooked_methods.find(target); found != hooked_methods.end()) { + hook_item = &found->second; + } + } + if (!hook_item) { + std::unique_lock lk(hooked_lock); + hook_item = &hooked_methods[target]; + if (!hook_item->callbacks) { + newHook = true; + hook_item->callbacks = (jobjectArray) env->NewGlobalRef( + env->NewObjectArray(1, env->FindClass("[Ljava/lang/Object;"), + env->NewObjectArray(0, JNI_FindClass(env, "java/lang/Object"), nullptr))); + } + } + if (newHook) { + auto init = env->GetMethodID(hooker, "", "(Ljava/lang/reflect/Executable;[[Ljava/lang/Object;)V"); + auto callback_method = env->ToReflectedMethod(hooker, env->GetMethodID(hooker, "callback", + "([Ljava/lang/Object;)Ljava/lang/Object;"), + false); + auto hooker_object = env->NewObject(hooker, init, hookMethod, hook_item->callbacks); + hook_item->backup = lsplant::Hook(env, hookMethod, hooker_object, callback_method); + env->DeleteLocalRef(hooker_object); + } + env->MonitorEnter(hook_item->callbacks); + auto insert_point = hook_item->priorities.emplace(priority); + auto old_array = (jobjectArray) env->GetObjectArrayElement(hook_item->callbacks, 0); + auto new_array = env->NewObjectArray(static_cast(hook_item->priorities.size()), env->FindClass("java/lang/Object"), nullptr); + for (auto [i, current, passed] = std::make_tuple(0, hook_item->priorities.begin(), false); current != hook_item->priorities.end(); ++current, ++i) { + if (current == insert_point) { + env->SetObjectArrayElement(new_array, i, callback); + passed = true; + } else { + auto element = env->GetObjectArrayElement(old_array, i - passed); + env->SetObjectArrayElement(new_array, i, element); + env->DeleteLocalRef(element); + } + } + env->SetObjectArrayElement(hook_item->callbacks, 0, new_array); + env->DeleteLocalRef(old_array); + env->DeleteLocalRef(new_array); + env->MonitorExit(hook_item->callbacks); + return hook_item->backup ? JNI_TRUE : JNI_FALSE; +} + +LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, unhookMethod, jobject hookMethod, jobject callback) { + auto target = env->FromReflectedMethod(hookMethod); + HookItem * hook_item = nullptr; + { + std::shared_lock lk(hooked_lock); + if (auto found = hooked_methods.find(target); found != hooked_methods.end()) { + hook_item = &found->second; + } + } + if (!hook_item) return JNI_FALSE; + env->MonitorEnter(hook_item->callbacks); + auto old_array = (jobjectArray) env->GetObjectArrayElement(hook_item->callbacks, 0); + auto new_array = env->NewObjectArray(static_cast(hook_item->priorities.size() - 1), env->FindClass("java/lang/Object"), nullptr); + auto to_remove = hook_item->priorities.end(); + for (auto [i, current, passed] = std::make_tuple(0, hook_item->priorities.begin(), false); current != hook_item->priorities.end(); ++current, ++i) { + auto element = env->GetObjectArrayElement(old_array, i); + if (env->IsSameObject(element, callback)) { + to_remove = current; + passed = true; + } else { + if (i - passed >= hook_item->priorities.size() - 1) { + return JNI_FALSE; + } + env->SetObjectArrayElement(new_array, i - passed, element); + } + env->DeleteLocalRef(element); + } + bool removed = false; + if (to_remove != hook_item->priorities.end()) { + hook_item->priorities.erase(to_remove); + env->SetObjectArrayElement(hook_item->callbacks, 0, new_array); + removed = true; + } + env->DeleteLocalRef(old_array); + env->DeleteLocalRef(new_array); + env->MonitorExit(hook_item->callbacks); + return removed ? JNI_TRUE : JNI_FALSE; +} + +LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, deoptimizeMethod, jobject hookMethod, + jclass hooker, jint priority, jobject callback) { + return lsplant::Deoptimize(env, hookMethod); +} + +LSP_DEF_NATIVE_METHOD(jobject, HookBridge, invokeOriginalMethod, jobject hookMethod, + jobject thiz, jobjectArray args) { + auto target = env->FromReflectedMethod(hookMethod); + HookItem * hook_item = nullptr; + { + std::shared_lock lk(hooked_lock); + if (auto found = hooked_methods.find(target); found != hooked_methods.end()) { + hook_item = &found->second; + } + } + jobject to_call = hookMethod; + if (hook_item && hook_item->backup) { + to_call = hook_item->backup; + } + return env->CallObjectMethod(to_call, invoke, thiz, args); +} + +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;"), +}; + +void RegisterHookBridge(JNIEnv *env) { + auto method = env->FindClass("java/lang/reflect/Method"); + invoke = env->GetMethodID( + method, "invoke", + "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); + env->DeleteLocalRef(method); + REGISTER_LSP_NATIVE_METHODS(HookBridge); +} +} // namespace lspd diff --git a/core/src/main/jni/src/jni/hook_bridge.h b/core/src/main/jni/src/jni/hook_bridge.h new file mode 100644 index 00000000..035b4e87 --- /dev/null +++ b/core/src/main/jni/src/jni/hook_bridge.h @@ -0,0 +1,11 @@ +// +// Created by loves on 3/13/2022. +// + +#pragma once + +#include + +namespace lspd { +void RegisterHookBridge(JNIEnv* env); +} diff --git a/core/src/main/cpp/main/src/jni/native_api.cpp b/core/src/main/jni/src/jni/native_api.cpp similarity index 92% rename from core/src/main/cpp/main/src/jni/native_api.cpp rename to core/src/main/jni/src/jni/native_api.cpp index e564ee27..8cb82101 100644 --- a/core/src/main/cpp/main/src/jni/native_api.cpp +++ b/core/src/main/jni/src/jni/native_api.cpp @@ -23,12 +23,14 @@ // #include "native_api.h" #include "native_util.h" -#include "jni_helper.h" +#include "utils/jni_helper.hpp" #include "../native_api.h" +using namespace lsplant; + namespace lspd { LSP_DEF_NATIVE_METHOD(void, NativeAPI, recordNativeEntrypoint, jstring jstr) { - JUTFString str(env, jstr); + lsplant::JUTFString str(env, jstr); RegisterNativeLib(str); } diff --git a/core/src/main/cpp/main/src/jni/native_api.h b/core/src/main/jni/src/jni/native_api.h similarity index 100% rename from core/src/main/cpp/main/src/jni/native_api.h rename to core/src/main/jni/src/jni/native_api.h diff --git a/core/src/main/cpp/main/src/jni/resources_hook.cpp b/core/src/main/jni/src/jni/resources_hook.cpp similarity index 80% rename from core/src/main/cpp/main/src/jni/resources_hook.cpp rename to core/src/main/jni/src/jni/resources_hook.cpp index 82379255..605a4841 100644 --- a/core/src/main/cpp/main/src/jni/resources_hook.cpp +++ b/core/src/main/jni/src/jni/resources_hook.cpp @@ -19,25 +19,24 @@ */ #include -#include -#include -#include -#include -#include -#include +#include "dex_builder.h" +#include "framework/androidfw/resource_types.h" +#include "elf_util.h" #include "native_util.h" #include "resources_hook.h" +using namespace lsplant; + namespace lspd { static constexpr const char *kXResourcesClassName = "android/content/res/XResources"; - typedef int32_t (*TYPE_GET_ATTR_NAME_ID)(void *, int); + using TYPE_GET_ATTR_NAME_ID = int32_t (*)(void *, int); - typedef char16_t *(*TYPE_STRING_AT)(const void *, int32_t, size_t *); + using TYPE_STRING_AT = char16_t *(*)(const void *, int32_t, size_t *); - typedef void(*TYPE_RESTART)(void *); + using TYPE_RESTART = void (*)(void *); - typedef int32_t (*TYPE_NEXT)(void *); + using TYPE_NEXT = int32_t (*)(void *); static jclass classXResources; static jmethodID methodXResourcesTranslateAttrId; @@ -65,7 +64,11 @@ namespace lspd { "_ZNK7android12ResXMLParser18getAttributeNameIDEm")))) { return false; } - return android::ResStringPool::setup(fw); + return android::ResStringPool::setup(HookHandler{ + .art_symbol_resolver = [&](auto s) { + return fw.template getSymbAddress(s); + } + }); } LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, initXResourcesNative) { @@ -97,29 +100,14 @@ namespace lspd { // @ApiSensitive(Level.MIDDLE) LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, makeInheritable, jclass target_class, jobjectArray constructors) { - if (target_class) { - static auto class_clazz = JNI_NewGlobalRef(env, JNI_FindClass(env, "java/lang/Class")); - static jfieldID java_lang_Class_accessFlags = JNI_GetFieldID( - env, class_clazz, "accessFlags", "I"); - jint access_flags = env->GetIntField(target_class, java_lang_Class_accessFlags); - env->SetIntField(target_class, java_lang_Class_accessFlags, access_flags & ~kAccFinal); - for (auto i = 0; i < env->GetArrayLength(constructors); ++i) { - auto constructor = env->GetObjectArrayElement(constructors, i); - void *method = yahfa::getArtMethod(env, constructor); - uint32_t flags = yahfa::getAccessFlags(method); - if ((flags & yahfa::kAccPublic) == 0 && (flags & yahfa::kAccProtected) == 0) { - flags |= yahfa::kAccProtected; - flags &= ~yahfa::kAccPrivate; - } - yahfa::setAccessFlags(method, flags); - } + if (lsplant::MakeClassInheritable(env, target_class)) { return JNI_TRUE; } return JNI_FALSE; } LSP_DEF_NATIVE_METHOD(jobject, ResourcesHook, buildDummyClassLoader, jobject parent, - jobject resource_super_class, jobject typed_array_super_class) { + jstring resource_super_class, jstring typed_array_super_class) { using namespace startop::dex; static auto in_memory_classloader = JNI_NewGlobalRef(env, JNI_FindClass(env, "dalvik/system/InMemoryDexClassLoader")); @@ -127,17 +115,13 @@ namespace lspd { "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); DexBuilder dex_file; - std::string storage; - auto current_thread = art::Thread::Current(); ClassBuilder xresource_builder{ dex_file.MakeClass("xposed.dummy.XResourcesSuperClass")}; - xresource_builder.setSuperClass(TypeDescriptor::FromDescriptor(art::mirror::Class( - current_thread.DecodeJObject(resource_super_class)).GetDescriptor(&storage))); + xresource_builder.setSuperClass(TypeDescriptor::FromClassname(JUTFString(env, resource_super_class).get())); ClassBuilder xtypearray_builder{ dex_file.MakeClass("xposed.dummy.XTypedArraySuperClass")}; - xtypearray_builder.setSuperClass(TypeDescriptor::FromDescriptor(art::mirror::Class( - current_thread.DecodeJObject(typed_array_super_class)).GetDescriptor(&storage))); + xtypearray_builder.setSuperClass(TypeDescriptor::FromClassname(JUTFString(env, typed_array_super_class).get())); slicer::MemView image{dex_file.CreateImage()}; @@ -222,7 +206,7 @@ namespace lspd { LSP_NATIVE_METHOD(ResourcesHook, makeInheritable, "(Ljava/lang/Class;[Ljava/lang/reflect/Constructor;)Z"), LSP_NATIVE_METHOD(ResourcesHook, buildDummyClassLoader, - "(Ljava/lang/ClassLoader;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/ClassLoader;"), + "(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/ClassLoader;"), LSP_NATIVE_METHOD(ResourcesHook, rewriteXmlReferencesNative, "(JLandroid/content/res/XResources;Landroid/content/res/Resources;)V") }; diff --git a/core/src/main/cpp/main/src/jni/resources_hook.h b/core/src/main/jni/src/jni/resources_hook.h similarity index 100% rename from core/src/main/cpp/main/src/jni/resources_hook.h rename to core/src/main/jni/src/jni/resources_hook.h diff --git a/core/src/main/cpp/main/src/native_api.cpp b/core/src/main/jni/src/native_api.cpp similarity index 84% rename from core/src/main/cpp/main/src/native_api.cpp rename to core/src/main/jni/src/native_api.cpp index e673f196..bf9f0702 100644 --- a/core/src/main/cpp/main/src/native_api.cpp +++ b/core/src/main/jni/src/native_api.cpp @@ -23,10 +23,15 @@ // #include "native_api.h" +#include "logging.h" #include "symbol_cache.h" +#include "utils/hook_helper.hpp" +#include #include #include -#include +#include +#include "native_util.h" + /* * Module: define xposed_native file in /assets, each line is a .so file name @@ -44,26 +49,33 @@ */ namespace lspd { + + using lsplant::operator""_tstr; std::list moduleLoadedCallbacks; std::list moduleNativeLibs; std::unique_ptr> protected_page( - mmap(nullptr, _page_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0), - [](void *ptr) { munmap(ptr, _page_size); }); + mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0), + [](void *ptr) { munmap(ptr, 4096); }); const auto[entries] = []() { auto *entries = new(protected_page.get()) NativeAPIEntries{ .version = 2, - .hookFunc = HookFunction, - .unhookFunc = UnhookFunction + .hookFunc = &HookFunction, + .unhookFunc = &UnhookFunction, }; - mprotect(protected_page.get(), _page_size, PROT_READ); + mprotect(protected_page.get(), 4096, PROT_READ); return std::make_tuple(entries); }(); void RegisterNativeLib(const std::string &library_name) { static bool initialized = []() { - return InstallNativeAPI(); + return InstallNativeAPI({ + .inline_hooker = [](auto t, auto r) { + void* bk = nullptr; + return HookFunction(t, r, &bk) ? bk : nullptr; + }, + }); }(); if (!initialized) [[unlikely]] return; LOGD("native_api: Registered %s", library_name.c_str()); @@ -74,12 +86,11 @@ namespace lspd { if (fullString.length() >= ending.length()) { return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending)); - } else { - return false; } + return false; } - CREATE_HOOK_STUB_ENTRIES( + CREATE_HOOK_STUB_ENTRY( "__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv", void*, do_dlopen, (const char* name, int flags, const void* extinfo, const void* caller_addr), { @@ -121,10 +132,10 @@ namespace lspd { return handle; }); - bool InstallNativeAPI() { + bool InstallNativeAPI(const lsplant::HookHandler & handler) { LOGD("InstallNativeAPI: %p", symbol_cache->do_dlopen); if (symbol_cache->do_dlopen) [[likely]] { - HookSymNoHandle(symbol_cache->do_dlopen, do_dlopen); + HookSymNoHandle(handler, symbol_cache->do_dlopen, do_dlopen); return true; } return false; diff --git a/core/src/main/cpp/main/src/native_api.h b/core/src/main/jni/src/native_api.h similarity index 93% rename from core/src/main/cpp/main/src/native_api.h rename to core/src/main/jni/src/native_api.h index 88ea0a95..8ba4ce1a 100644 --- a/core/src/main/cpp/main/src/native_api.h +++ b/core/src/main/jni/src/native_api.h @@ -27,7 +27,8 @@ #include #include -#include + +#include "utils/hook_helper.hpp" typedef int (*HookFunType)(void *func, void *replace, void **backup); @@ -44,7 +45,7 @@ typedef struct { typedef NativeOnModuleLoaded (*NativeInit)(const NativeAPIEntries *entries); namespace lspd { - bool InstallNativeAPI(); + bool InstallNativeAPI(const lsplant::HookHandler& handler); void RegisterNativeLib(const std::string &library_name); } diff --git a/core/src/main/cpp/main/src/jni/yahfa.h b/core/src/main/jni/src/native_hook.cpp similarity index 56% rename from core/src/main/cpp/main/src/jni/yahfa.h rename to core/src/main/jni/src/native_hook.cpp index c6be13fe..d480f103 100644 --- a/core/src/main/cpp/main/src/jni/yahfa.h +++ b/core/src/main/jni/src/native_hook.cpp @@ -18,21 +18,28 @@ * Copyright (C) 2021 LSPosed Contributors */ -#pragma once - -#include "jni.h" -#include -#include +#include +#include #include +#include +#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* isHooked(void* art_method); + void InstallInlineHooks(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(handler); + LOGD("Inline hooks installed"); + } +} // namespace lspd - std::vector> getJitMovements(); - - const std::unordered_set &isClassHooked(void *clazz); - - void RegisterYahfa(JNIEnv *); - -} // namespace lspd diff --git a/core/src/main/cpp/main/src/native_hook.h b/core/src/main/jni/src/native_hook.h similarity index 89% rename from core/src/main/cpp/main/src/native_hook.h rename to core/src/main/jni/src/native_hook.h index 2e6df53e..9208d517 100644 --- a/core/src/main/cpp/main/src/native_hook.h +++ b/core/src/main/jni/src/native_hook.h @@ -20,9 +20,9 @@ #pragma once -#include +#include "utils/hook_helper.hpp" namespace lspd { - void InstallInlineHooks(); + void InstallInlineHooks(const lsplant::HookHandler &handler); } diff --git a/core/src/main/cpp/main/src/service.cpp b/core/src/main/jni/src/service.cpp similarity index 99% rename from core/src/main/cpp/main/src/service.cpp rename to core/src/main/jni/src/service.cpp index c1da4a44..6e0c2ca8 100644 --- a/core/src/main/cpp/main/src/service.cpp +++ b/core/src/main/jni/src/service.cpp @@ -23,12 +23,14 @@ #include #include -#include "base/object.h" +#include "config.h" #include "service.h" #include "context.h" -#include "jni_helper.h" +#include "utils/jni_helper.hpp" #include "symbol_cache.h" +using namespace lsplant; + namespace lspd { jboolean Service::exec_transact_replace(jboolean *res, JNIEnv *env, [[maybe_unused]] jobject obj, diff --git a/core/src/main/cpp/main/src/service.h b/core/src/main/jni/src/service.h similarity index 91% rename from core/src/main/cpp/main/src/service.h rename to core/src/main/jni/src/service.h index 2e6a7b11..8eb372d0 100644 --- a/core/src/main/cpp/main/src/service.h +++ b/core/src/main/jni/src/service.h @@ -51,13 +51,13 @@ namespace lspd { void InitService(JNIEnv *env); void HookBridge(const Context& context, JNIEnv *env); - ScopedLocalRef RequestBinder(JNIEnv *env, jstring nice_name); + lsplant::ScopedLocalRef RequestBinder(JNIEnv *env, jstring nice_name); - ScopedLocalRef RequestSystemServerBinder(JNIEnv *env); + lsplant::ScopedLocalRef RequestSystemServerBinder(JNIEnv *env); - ScopedLocalRef RequestApplicationBinderFromSystemServer(JNIEnv *env, const ScopedLocalRef &system_server_binder); + lsplant::ScopedLocalRef RequestApplicationBinderFromSystemServer(JNIEnv *env, const lsplant::ScopedLocalRef &system_server_binder); - std::tuple RequestLSPDex(JNIEnv *env, const ScopedLocalRef &binder); + std::tuple RequestLSPDex(JNIEnv *env, const lsplant::ScopedLocalRef &binder); private: inline static std::unique_ptr instance_ = std::make_unique(); diff --git a/core/src/main/cpp/main/src/symbol_cache.cpp b/core/src/main/jni/src/symbol_cache.cpp similarity index 100% rename from core/src/main/cpp/main/src/symbol_cache.cpp rename to core/src/main/jni/src/symbol_cache.cpp diff --git a/core/src/main/cpp/main/src/symbol_cache.h b/core/src/main/jni/src/symbol_cache.h similarity index 100% rename from core/src/main/cpp/main/src/symbol_cache.h rename to core/src/main/jni/src/symbol_cache.h diff --git a/core/src/main/jni/template/config.cpp b/core/src/main/jni/template/config.cpp new file mode 100644 index 00000000..d2fcef6d --- /dev/null +++ b/core/src/main/jni/template/config.cpp @@ -0,0 +1,8 @@ +#include "config.h" + +namespace lspd { +const int versionCode = ${VERSION_CODE}; +const int apiVersion = ${API_VERSION}; +const char* const versionName = "${VERSION_NAME}"; +const char* const moduleName = "${MODULE_NAME}"; +} diff --git a/daemon/.gitignore b/daemon/.gitignore index 42afabfd..9ff2a377 100644 --- a/daemon/.gitignore +++ b/daemon/.gitignore @@ -1 +1,2 @@ -/build \ No newline at end of file +/build +/.cxx diff --git a/daemon/build.gradle.kts b/daemon/build.gradle.kts index 528e3e11..4e29d7c2 100644 --- a/daemon/build.gradle.kts +++ b/daemon/build.gradle.kts @@ -65,12 +65,6 @@ android { versionName = verName multiDexEnabled = false - externalNativeBuild { - ndkBuild { - arguments += "-j${Runtime.getRuntime().availableProcessors()}" - } - } - buildConfigField("int", "API_CODE", "$apiCode") buildConfigField( "String", @@ -87,15 +81,58 @@ android { } buildTypes { + debug { + externalNativeBuild { + cmake { + arguments.addAll( + arrayOf( + "-DCMAKE_CXX_FLAGS_DEBUG=-Og", + "-DCMAKE_C_FLAGS_DEBUG=-Og" + ) + ) + } + } + } release { isMinifyEnabled = true proguardFiles("proguard-rules.pro") + + externalNativeBuild { + cmake { + val flags = arrayOf( + "-Wl,--exclude-libs,ALL", + "-ffunction-sections", + "-fdata-sections", + "-Wl,--gc-sections", + "-fno-unwind-tables", + "-fno-asynchronous-unwind-tables", + "-flto=thin", + "-Wl,--thinlto-cache-policy,cache_size_bytes=300m", + "-Wl,--thinlto-cache-dir=${buildDir.absolutePath}/.lto-cache", + ) + cppFlags.addAll(flags) + cFlags.addAll(flags) + val configFlags = arrayOf( + "-Oz", + "-DNDEBUG" + ).joinToString(" ") + arguments.addAll( + arrayOf( + "-DCMAKE_CXX_FLAGS_RELEASE=$configFlags", + "-DCMAKE_CXX_FLAGS_RELWITHDEBINFO=$configFlags", + "-DCMAKE_C_FLAGS_RELEASE=$configFlags", + "-DCMAKE_C_FLAGS_RELWITHDEBINFO=$configFlags", + "-DDEBUG_SYMBOLS_PATH=${buildDir.absolutePath}/symbols", + ) + ) + } + } } } externalNativeBuild { - ndkBuild { - path("src/main/cpp/Android.mk") + cmake { + path("src/main/jni/CMakeLists.txt") } } @@ -104,12 +141,30 @@ android { sourceCompatibility(androidSourceCompatibility) } - buildTypes { - all { - externalNativeBuild { - ndkBuild { - arguments += "NDK_OUT=${File(buildDir, ".cxx/$name").absolutePath}" - } + defaultConfig { + externalNativeBuild { + cmake { + arguments += "-DEXTERNAL_ROOT=${File(rootDir.absolutePath, "external")}" + abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64") + val flags = arrayOf( + "-Wall", + "-Werror", + "-Qunused-arguments", + "-Wno-gnu-string-literal-operator-template", + "-fno-rtti", + "-fvisibility=hidden", + "-fvisibility-inlines-hidden", + "-fno-exceptions", + "-fno-stack-protector", + "-fomit-frame-pointer", + "-Wno-builtin-macro-redefined", + "-Wno-unused-value", + "-D__FILE__=__FILE_NAME__", + ) + cppFlags("-std=c++20", *flags) + cFlags("-std=c18", *flags) + arguments("-DANDROID_STL=none") + targets("daemon") } } } @@ -165,7 +220,6 @@ afterEvaluate { } dependencies { - implementation("dev.rikka.ndk.thirdparty:cxx:1.2.0") implementation("com.android.tools.build:apksig:$agpVersion") implementation("org.apache.commons:commons-lang3:3.12.0") compileOnly("androidx.annotation:annotation:1.3.0") diff --git a/daemon/src/main/cpp/Android.mk b/daemon/src/main/cpp/Android.mk deleted file mode 100644 index 76403638..00000000 --- a/daemon/src/main/cpp/Android.mk +++ /dev/null @@ -1,13 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := daemon -LOCAL_C_INCLUDES := ../core/src/main/cpp/main/include/ -LOCAL_SRC_FILES := logcat.cpp obfuscation.cpp -LOCAL_STATIC_LIBRARIES := cxx dex_builder -LOCAL_ALLOW_UNDEFINED_SYMBOLS := true -LOCAL_LDLIBS := -llog -landroid -include $(BUILD_SHARED_LIBRARY) - -include ../core/src/main/cpp/external/DexBuilder/Android.mk -$(call import-module,prefab/cxx) diff --git a/daemon/src/main/cpp/Application.mk b/daemon/src/main/cpp/Application.mk deleted file mode 100644 index c9ca3a6e..00000000 --- a/daemon/src/main/cpp/Application.mk +++ /dev/null @@ -1,15 +0,0 @@ -APP_CFLAGS := -Wall -Wextra -APP_CFLAGS += -fno-stack-protector -fomit-frame-pointer -APP_CFLAGS += -Wno-builtin-macro-redefined -D__FILE__=__FILE_NAME__ -APP_CPPFLAGS := -std=c++20 -APP_CONLYFLAGS := -std=c18 -APP_LDFLAGS := -Wl,--exclude-libs,ALL -APP_STL := none - -ifneq ($(NDK_DEBUG),1) -APP_CFLAGS += -Oz -flto=thin -APP_CFLAGS += -Wno-unused -Wno-unused-parameter -Werror -APP_CFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden -APP_CFLAGS += -fno-unwind-tables -fno-asynchronous-unwind-tables -APP_LDFLAGS += -flto=thin -Wl,--thinlto-cache-policy,cache_size_bytes=10m -Wl,--thinlto-cache-dir=build/.lto-cache -Wl,--gc-sections -Wl,--strip-all -endif diff --git a/daemon/src/main/jni/CMakeLists.txt b/daemon/src/main/jni/CMakeLists.txt new file mode 100644 index 00000000..a40bf5ec --- /dev/null +++ b/daemon/src/main/jni/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.4.1) +project(daemon) + +add_subdirectory(${EXTERNAL_ROOT} external) + +set(SOURCES + logcat.cpp + obfuscation.cpp + ) + +add_library(${PROJECT_NAME} SHARED ${SOURCES}) + +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(${PROJECT_NAME} PRIVATE lsplant_static dex_builder_static android log) + +if (DEFINED DEBUG_SYMBOLS_PATH) + message(STATUS "Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI} + COMMAND ${CMAKE_OBJCOPY} --only-keep-debug $ + ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug + COMMAND ${CMAKE_STRIP} --strip-all $ + COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug + $) +endif() diff --git a/daemon/src/main/cpp/logcat.cpp b/daemon/src/main/jni/logcat.cpp similarity index 99% rename from daemon/src/main/cpp/logcat.cpp rename to daemon/src/main/jni/logcat.cpp index 1343e99d..410ffe37 100644 --- a/daemon/src/main/cpp/logcat.cpp +++ b/daemon/src/main/jni/logcat.cpp @@ -224,7 +224,7 @@ void Logcat::ProcessBuffer(struct log_msg *buf) { shortcut = true; } if (verbose_ && (shortcut || buf->id() == log_id::LOG_ID_CRASH || - entry.pid == my_pid_ || tag == "Magisk"sv || + entry.pid == my_pid_ || tag == "Magisk"sv || tag == "Dobby"sv || tag.starts_with("Riru"sv) || tag.starts_with("zygisk"sv) || tag.starts_with("LSPosed"sv))) [[unlikely]] { verbose_print_count_ += PrintLogLine(entry, verbose_file_.get()); diff --git a/daemon/src/main/cpp/logcat.h b/daemon/src/main/jni/logcat.h similarity index 66% rename from daemon/src/main/cpp/logcat.h rename to daemon/src/main/jni/logcat.h index cc39b58a..80099450 100644 --- a/daemon/src/main/cpp/logcat.h +++ b/daemon/src/main/jni/logcat.h @@ -51,13 +51,13 @@ struct log_msg { struct logger; struct logger_list; -long android_logger_get_log_size(struct logger* logger); -int android_logger_set_log_size(struct logger *logger, unsigned long size); -struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid); -void android_logger_list_free(struct logger_list *logger_list); -int android_logger_list_read(struct logger_list *logger_list, struct log_msg *log_msg); -struct logger *android_logger_open(struct logger_list *logger_list, log_id_t id); -int android_log_processLogBuffer(struct logger_entry *buf, AndroidLogEntry *entry); +[[gnu::weak]] long android_logger_get_log_size(struct logger* logger); +[[gnu::weak]] int android_logger_set_log_size(struct logger *logger, unsigned long size); +[[gnu::weak]] struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid); +[[gnu::weak]] void android_logger_list_free(struct logger_list *logger_list); +[[gnu::weak]] int android_logger_list_read(struct logger_list *logger_list, struct log_msg *log_msg); +[[gnu::weak]] struct logger *android_logger_open(struct logger_list *logger_list, log_id_t id); +[[gnu::weak]] int android_log_processLogBuffer(struct logger_entry *buf, AndroidLogEntry *entry); #ifdef __cplusplus } #endif diff --git a/daemon/src/main/jni/logging.h b/daemon/src/main/jni/logging.h new file mode 100644 index 00000000..6d4be69e --- /dev/null +++ b/daemon/src/main/jni/logging.h @@ -0,0 +1,51 @@ +/* + * 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 . + * + * Copyright (C) 2020 EdXposed Contributors + * Copyright (C) 2021 LSPosed Contributors + */ + +#ifndef _LOGGING_H +#define _LOGGING_H + +#include + +#ifndef LOG_TAG +#define LOG_TAG "LSPosed" +#endif + +#ifdef LOG_DISABLED +#define LOGD(...) +#define LOGV(...) +#define LOGI(...) +#define LOGW(...) +#define LOGE(...) +#else +#ifndef NDEBUG +#define LOGD(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "%s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__) +#define LOGV(fmt, ...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "%s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__) +#else +#define LOGD(...) +#define LOGV(...) +#endif +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__) +#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)) +#endif + +#endif // _LOGGING_H diff --git a/daemon/src/main/cpp/obfuscation.cpp b/daemon/src/main/jni/obfuscation.cpp similarity index 99% rename from daemon/src/main/cpp/obfuscation.cpp rename to daemon/src/main/jni/obfuscation.cpp index 5890009e..cb38ce17 100644 --- a/daemon/src/main/cpp/obfuscation.cpp +++ b/daemon/src/main/jni/obfuscation.cpp @@ -34,7 +34,9 @@ #include "slicer/reader.h" #include "slicer/writer.h" #include "obfuscation.h" +#include "logging.h" +using namespace lsplant; namespace { std::mutex init_lock{}; std::string obfuscated_signature; diff --git a/daemon/src/main/cpp/obfuscation.h b/daemon/src/main/jni/obfuscation.h similarity index 97% rename from daemon/src/main/cpp/obfuscation.h rename to daemon/src/main/jni/obfuscation.h index acb4055a..4d8b16ad 100644 --- a/daemon/src/main/cpp/obfuscation.h +++ b/daemon/src/main/jni/obfuscation.h @@ -19,7 +19,7 @@ #pragma once #include -#include "jni_helper.h" +#include "utils/jni_helper.hpp" class WA: public dex::Writer::Allocator { // addr: {size, fd} diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt new file mode 100644 index 00000000..c19b328d --- /dev/null +++ b/external/CMakeLists.txt @@ -0,0 +1,111 @@ +project(external) + +find_program(CCACHE ccache) + +if (CCACHE) + set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE}) + set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE}) +endif () + +macro(SET_OPTION option value) + set(${option} ${value} CACHE INTERNAL "" FORCE) +endmacro() + +SET_OPTION(DOBBY_GENERATE_SHARED OFF) +if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + SET_OPTION(DOBBY_DEBUG OFF) +endif () + +set(LIBCXX_SOURCES + algorithm.cpp + any.cpp + atomic.cpp + barrier.cpp + bind.cpp + charconv.cpp + chrono.cpp + condition_variable.cpp + condition_variable_destructor.cpp + debug.cpp + exception.cpp + filesystem/directory_iterator.cpp + filesystem/int128_builtins.cpp + filesystem/operations.cpp + functional.cpp + future.cpp + hash.cpp + ios.cpp + iostream.cpp + locale.cpp + memory.cpp + mutex.cpp + mutex_destructor.cpp + new.cpp + optional.cpp + random.cpp + regex.cpp + shared_mutex.cpp + stdexcept.cpp + string.cpp + strstream.cpp + system_error.cpp + thread.cpp + typeinfo.cpp + utility.cpp + valarray.cpp + variant.cpp + vector.cpp + ) + +list(TRANSFORM LIBCXX_SOURCES PREPEND cxx/src/) + +set(LIBCXX_EXPORT_FLAGS) +set(LIBCXX_FLAGS + -fvisibility-global-new-delete-hidden + -fvisibility=hidden + -fvisibility-inlines-hidden + -DLIBCXX_BUILDING_LIBCXXABI + -D_LIBCPP_NO_EXCEPTIONS + -D_LIBCPP_NO_RTTI + -D_LIBCPP_BUILDING_LIBRARY + -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS + -D__STDC_FORMAT_MACROS + ) +set(LIBCXX_EXPORT_INCLUDES cxx/include) +set(LIBCXX_INCLUDES) + +set(LIBCXXABI_SOURCES + abort_message.cpp + cxa_aux_runtime.cpp + cxa_default_handlers.cpp + cxa_exception_storage.cpp + cxa_guard.cpp + cxa_handlers.cpp + cxa_noexception.cpp + cxa_thread_atexit.cpp + cxa_unexpected.cpp + cxa_vector.cpp + cxa_virtual.cpp + stdlib_exception.cpp + stdlib_new_delete.cpp + stdlib_stdexcept.cpp + stdlib_typeinfo.cpp + ) +list(TRANSFORM LIBCXXABI_SOURCES PREPEND cxx/src/abi/) +set(LIBCXXABI_FLAGS + -D_LIBCXXABI_NO_EXCEPTIONS + -Wno-macro-redefined + -Wno-unknown-attributes + -DHAS_THREAD_LOCAL) +set(LIBCXXABI_INCLUDES cxx/include/abi) + +add_library(cxx STATIC ${LIBCXX_SOURCES} ${LIBCXXABI_SOURCES}) +target_compile_options(cxx PUBLIC ${LIBCXX_EXPORT_FLAGS}) +target_compile_options(cxx PRIVATE ${LIBCXX_FLAGS} ${LIBCXXABI_FLAGS} -ffunction-sections -fdata-sections) +target_include_directories(cxx PUBLIC ${LIBCXX_EXPORT_INCLUDES}) +target_include_directories(cxx PRIVATE ${LIBCXX_INCLUDES} ${LIBCXXABI_INCLUDES}) + +link_libraries(cxx) + +add_subdirectory(lsplant/lsplant/src/main/jni) +add_subdirectory(dobby) diff --git a/external/cxx b/external/cxx new file mode 160000 index 00000000..0aa67c3f --- /dev/null +++ b/external/cxx @@ -0,0 +1 @@ +Subproject commit 0aa67c3ffea069bdb58fe3b5e7aad06934afb292 diff --git a/external/dobby b/external/dobby new file mode 160000 index 00000000..f575002d --- /dev/null +++ b/external/dobby @@ -0,0 +1 @@ +Subproject commit f575002d11f10ae9e2d7ad65b63071ba2da8d31b diff --git a/external/lsplant b/external/lsplant new file mode 160000 index 00000000..b0c3e270 --- /dev/null +++ b/external/lsplant @@ -0,0 +1 @@ +Subproject commit b0c3e27061dcc2f3f7e03211fc75acece6c37bce