diff --git a/java_module/app/build.gradle b/java_module/app/build.gradle index 757c301..2349d48 100644 --- a/java_module/app/build.gradle +++ b/java_module/app/build.gradle @@ -4,12 +4,12 @@ plugins { } android { - compileSdk 31 + compileSdk 30 defaultConfig { applicationId "dev.kdrag0n.safetynetriru" minSdk 24 - targetSdk 31 + targetSdk 30 versionCode 1 versionName "1.0" diff --git a/java_module/app/proguard-rules.pro b/java_module/app/proguard-rules.pro index 4fd7834..4653987 100644 --- a/java_module/app/proguard-rules.pro +++ b/java_module/app/proguard-rules.pro @@ -27,3 +27,30 @@ -keepclassmembers class dev.kdrag0n.safetynetriru.proxy.ProxyKeyStoreSpi { public (...); } + +# Remove @DebugMetadata annotations to avoid leaking info +# Source: https://proandroiddev.com/kotlin-cleaning-java-bytecode-before-release-9567d4c63911 +-checkdiscard @interface kotlin.coroutines.jvm.internal.DebugMetadata +-assumenosideeffects public class kotlin.coroutines.jvm.internal.BaseContinuationImpl { + private kotlin.coroutines.jvm.internal.DebugMetadata getDebugMetadataAnnotation() return null; + public java.lang.StackTraceElement getStackTraceElement() return null; + public java.lang.String[] getSpilledVariableFieldMapping() return null; +} + +-assumenosideeffects class kotlin.jvm.internal.Intrinsics { + # Remove verbose NPE intrinsics to reduce code size and avoid leaking info + # Source: https://issuetracker.google.com/issues/190489514 + static void checkParameterIsNotNull(java.lang.Object, java.lang.String); + static void checkNotNullParameter(java.lang.Object, java.lang.String); + static void checkFieldIsNotNull(java.lang.Object, java.lang.String); + static void checkFieldIsNotNull(java.lang.Object, java.lang.String, java.lang.String); + static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String); + static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String, java.lang.String); + static void checkNotNullExpressionValue(java.lang.Object, java.lang.String); + static void checkExpressionValueIsNotNull(java.lang.Object, java.lang.String); + static void checkNotNull(java.lang.Object); + static void checkNotNull(java.lang.Object, java.lang.String); + + # Remove remaining stray calls to stringPlus + static java.lang.String stringPlus(java.lang.String, java.lang.Object); +} diff --git a/riru/module.example.gradle b/riru/module.example.gradle deleted file mode 100644 index 6c06e3b..0000000 --- a/riru/module.example.gradle +++ /dev/null @@ -1,30 +0,0 @@ -ext { - /* - This name will be used in the name of the so file ("lib${moduleLibraryName}.so"). - */ - moduleLibraryName = "template" - - /* Minimal supported Riru API version, used in the version check of riru.sh */ - moduleMinRiruApiVersion = 24 - - /* The version name of minimal supported Riru, used in the version check of riru.sh */ - moduleMinRiruVersionName = "v24.0.0" - - /* Maximum supported Riru API version, used in the version check of riru.sh */ - moduleRiruApiVersion = 26 - - /* - Magisk module ID - Since Magisk use it to distinguish different modules, you should never change it. - - Note, the older version of the template uses '-' instead of '_', if your are upgrading from - the older version, please pay attention. - */ - magiskModuleId = "riru_template" - - moduleName = "Template" - moduleAuthor = "Template" - moduleDescription = "Riru module template. Requires Riru $moduleMinRiruVersionName or above." - moduleVersion = "v26.0.0" - moduleVersionCode = 26 -} diff --git a/riru/module/src/main/cpp/main.cpp b/riru/module/src/main/cpp/main.cpp index 1368034..bf89041 100644 --- a/riru/module/src/main/cpp/main.cpp +++ b/riru/module/src/main/cpp/main.cpp @@ -4,6 +4,115 @@ #include #include #include +#include +#include +#include +#include + +#ifndef NDEBUG +#define DEBUG(...) __android_log_write(ANDROID_LOG_DEBUG, "SafetyNetJNI/SARU", __VA_ARGS__) +#else +#define DEBUG(...) +#endif + +static constexpr bool DEBUG = true; + +static void *moduleDex; +static size_t moduleDexSize; + +static constexpr size_t APP_DATA_DIR_SIZE = 128; +static char lastAppDataDir[APP_DATA_DIR_SIZE]; + +static void updateAppDataDir(JNIEnv *env, jstring appDataDir) { + DEBUG("updateAppDataDir"); + if (appDataDir == nullptr) { + DEBUG("dir is null"); + memset(lastAppDataDir, 0, APP_DATA_DIR_SIZE); + } else { + DEBUG("copy dir"); + const char *copy = env->GetStringUTFChars(appDataDir, NULL); + strncpy(lastAppDataDir, copy, APP_DATA_DIR_SIZE); + env->ReleaseStringUTFChars(appDataDir, copy); + DEBUG(lastAppDataDir); + } +} + +static void specializeCommon(JNIEnv *env) { + DEBUG("specializeCommon"); + DEBUG(lastAppDataDir); + if (moduleDex == nullptr) { + DEBUG("dex null"); + } + if (moduleDex == nullptr || strstr(lastAppDataDir, "com.google.android.gms") == nullptr) { + DEBUG("pkg doesn't match"); + riru_set_unload_allowed(true); + return; + } + + DEBUG("get system classloader"); + // First, get the system classloader + jclass clClass = env->FindClass("java/lang/ClassLoader"); + jmethodID getSystemClassLoader = env->GetStaticMethodID(clClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); + jobject systemClassLoader = env->CallStaticObjectMethod(clClass, getSystemClassLoader); + + DEBUG("create buf"); + // Assuming we have a valid mapped module, load it. This is similar to the approach used for + // Dynamite modules in GmsCompat, except we can use InMemoryDexClassLoader directly instead of + // tampering with DelegateLastClassLoader's DexPathList. + jobject buf = env->NewDirectByteBuffer(moduleDex, moduleDexSize); + DEBUG("construct dex cl"); + jclass dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader"); + jmethodID dexClInit = env->GetMethodID(dexClClass, "", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); + jobject dexCl = env->NewObject(dexClClass, dexClInit, buf, systemClassLoader); + + // Run it + DEBUG("load class method lookup"); + jmethodID loadClass = env->GetMethodID(clClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + DEBUG("call load class"); + // Create a Java string. VM crashes with a C string. + jstring entryClassName = env->NewStringUTF("dev.kdrag0n.safetynetriru.EntryPoint"); + jobject entryClassObj = env->CallObjectMethod(dexCl, loadClass, entryClassName); + + // Call init + DEBUG("call init"); + auto entryClass = (jclass) entryClassObj; + jmethodID entryInit = env->GetStaticMethodID(entryClass, "init", "()V"); + env->CallStaticVoidMethod(entryClass, entryInit); + DEBUG("specializeCommon end"); +} + +static void *readFile(char *path, size_t *fileSize) { + int fd = open(path, O_RDONLY, 0); + if (fd < 0) { + DEBUG("open fail"); + return nullptr; + } + // Get size + DEBUG("get size"); + *fileSize = lseek(fd, 0, SEEK_END); + if (*fileSize < 0) { + DEBUG("seek fail"); + return nullptr; + } + lseek(fd, 0, SEEK_SET); + // Map + /* + DEBUG("mmap"); + moduleDex = mmap(nullptr, *fileSize, PROT_READ, MAP_PRIVATE, fd, 0); + if (moduleDex == MAP_FAILED) { + DEBUG("mmap fail"); + }*/ + // Close the fd. This doesn't destroy the mapping. + //DEBUG("close"); + char *data = (char *) malloc(*fileSize); + int bytes = 0; + while (bytes < *fileSize) { + bytes += read(fd, data + bytes, *fileSize - bytes); + } + close(fd); + + return data; +} static void forkAndSpecializePre( JNIEnv *env, jclass clazz, jint *uid, jint *gid, jintArray *gids, jint *runtimeFlags, @@ -11,25 +120,7 @@ static void forkAndSpecializePre( jintArray *fdsToClose, jintArray *fdsToIgnore, jboolean *is_child_zygote, jstring *instructionSet, jstring *appDataDir, jboolean *isTopApp, jobjectArray *pkgDataInfoList, jobjectArray *whitelistedDataInfoList, jboolean *bindMountAppDataDirs, jboolean *bindMountAppStorageDirs) { - // Called "before" com_android_internal_os_Zygote_nativeForkAndSpecialize in frameworks/base/core/jni/com_android_internal_os_Zygote.cpp - // Parameters are pointers, you can change the value of them if you want - // Some parameters are not exist is older Android versions, in this case, they are null or 0 -} - -static void forkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) { - // Called "after" com_android_internal_os_Zygote_nativeForkAndSpecialize in frameworks/base/core/jni/com_android_internal_os_Zygote.cpp - // "res" is the return value of com_android_internal_os_Zygote_nativeForkAndSpecialize - - if (res == 0) { - // In app process - - // When unload allowed is true, the module will be unloaded (dlclose) by Riru - // If this modules has hooks installed, DONOT set it to true, or there will be SIGSEGV - // This value will be automatically reset to false before the "pre" function is called - riru_set_unload_allowed(true); - } else { - // In zygote process - } + updateAppDataDir(env, *appDataDir); } static void specializeAppProcessPre( @@ -38,37 +129,23 @@ static void specializeAppProcessPre( jboolean *startChildZygote, jstring *instructionSet, jstring *appDataDir, jboolean *isTopApp, jobjectArray *pkgDataInfoList, jobjectArray *whitelistedDataInfoList, jboolean *bindMountAppDataDirs, jboolean *bindMountAppStorageDirs) { - // Called "before" com_android_internal_os_Zygote_nativeSpecializeAppProcess in frameworks/base/core/jni/com_android_internal_os_Zygote.cpp - // Parameters are pointers, you can change the value of them if you want - // Some parameters are not exist is older Android versions, in this case, they are null or 0 + updateAppDataDir(env, *appDataDir); +} + +static void forkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) { + // Called "after" com_android_internal_os_Zygote_nativeForkAndSpecialize in frameworks/base/core/jni/com_android_internal_os_Zygote.cpp + // "res" is the return value of com_android_internal_os_Zygote_nativeForkAndSpecialize + + if (res == 0) { + // In app process + specializeCommon(env); + } } static void specializeAppProcessPost( JNIEnv *env, jclass clazz) { // Called "after" com_android_internal_os_Zygote_nativeSpecializeAppProcess in frameworks/base/core/jni/com_android_internal_os_Zygote.cpp - - // When unload allowed is true, the module will be unloaded (dlclose) by Riru - // If this modules has hooks installed, DONOT set it to true, or there will be SIGSEGV - // This value will be automatically reset to false before the "pre" function is called - riru_set_unload_allowed(true); -} - -static void forkSystemServerPre( - JNIEnv *env, jclass clazz, uid_t *uid, gid_t *gid, jintArray *gids, jint *runtimeFlags, - jobjectArray *rlimits, jlong *permittedCapabilities, jlong *effectiveCapabilities) { - // Called "before" com_android_internal_os_Zygote_forkSystemServer in frameworks/base/core/jni/com_android_internal_os_Zygote.cpp - // Parameters are pointers, you can change the value of them if you want - // Some parameters are not exist is older Android versions, in this case, they are null or 0 -} - -static void forkSystemServerPost(JNIEnv *env, jclass clazz, jint res) { - // Called "after" com_android_internal_os_Zygote_forkSystemServer in frameworks/base/core/jni/com_android_internal_os_Zygote.cpp - - if (res == 0) { - // In system server process - } else { - // In zygote process - } + specializeCommon(env); } static void onModuleLoaded() { @@ -77,6 +154,21 @@ static void onModuleLoaded() { // If you want to use threads, start them here rather than the constructors // __attribute__((constructor)) or constructors of static variables, // or the "hide" will cause SIGSEGV + + + // Load + DEBUG("onModuleLoaded, loading file"); + char pathBuf[128]; + snprintf(pathBuf, 128, "%s/%s", riru_magisk_module_path, "classes.dex"); + DEBUG((char*)riru_magisk_module_path); + DEBUG(pathBuf); + + moduleDex = readFile(pathBuf, &moduleDexSize); + if (!moduleDex) { + return; + } + + DEBUG("module loaded"); } extern "C" { @@ -86,19 +178,19 @@ const char *riru_magisk_module_path = nullptr; int *riru_allow_unload = nullptr; static auto module = RiruVersionedModuleInfo{ - .moduleApiVersion = riru::moduleApiVersion, - .moduleInfo= RiruModuleInfo{ - .supportHide = true, - .version = riru::moduleVersionCode, - .versionName = riru::moduleVersionName, - .onModuleLoaded = onModuleLoaded, - .forkAndSpecializePre = forkAndSpecializePre, - .forkAndSpecializePost = forkAndSpecializePost, - .forkSystemServerPre = forkSystemServerPre, - .forkSystemServerPost = forkSystemServerPost, - .specializeAppProcessPre = specializeAppProcessPre, - .specializeAppProcessPost = specializeAppProcessPost - } + .moduleApiVersion = riru::moduleApiVersion, + .moduleInfo = RiruModuleInfo{ + .supportHide = true, + .version = riru::moduleVersionCode, + .versionName = riru::moduleVersionName, + .onModuleLoaded = onModuleLoaded, + .forkAndSpecializePre = forkAndSpecializePre, + .forkAndSpecializePost = forkAndSpecializePost, + .forkSystemServerPre = NULL, + .forkSystemServerPost = NULL, + .specializeAppProcessPre = specializeAppProcessPre, + .specializeAppProcessPost = specializeAppProcessPost, + }, }; RiruVersionedModuleInfo *init(Riru *riru) { diff --git a/riru/template/magisk_module/LICENSE b/riru/template/magisk_module/LICENSE new file mode 100644 index 0000000..0981d09 --- /dev/null +++ b/riru/template/magisk_module/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2021 Danny Lin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/riru/template/magisk_module/README.md b/riru/template/magisk_module/README.md deleted file mode 100644 index c316e8b..0000000 --- a/riru/template/magisk_module/README.md +++ /dev/null @@ -1 +0,0 @@ -# Riru - Template \ No newline at end of file diff --git a/riru/template/magisk_module/customize.sh b/riru/template/magisk_module/customize.sh index 11f86b9..d89666e 100644 --- a/riru/template/magisk_module/customize.sh +++ b/riru/template/magisk_module/customize.sh @@ -1,7 +1,6 @@ SKIPUNZIP=1 # Extract verify.sh -ui_print "- Extracting verify.sh" unzip -o "$ZIPFILE" 'verify.sh' -d "$TMPDIR" >&2 if [ ! -f "$TMPDIR/verify.sh" ]; then ui_print "*********************************************************" @@ -38,7 +37,7 @@ fi ui_print "- Extracting module files" extract "$ZIPFILE" 'module.prop' "$MODPATH" -extract "$ZIPFILE" 'uninstall.sh' "$MODPATH" +extract "$ZIPFILE" 'classes.dex' "$MODPATH" # Riru v24+ load files from the "riru" folder in the Magisk module folder # This "riru" folder is also used to determine if a Magisk module is a Riru module diff --git a/riru/template/magisk_module/uninstall.sh b/riru/template/magisk_module/uninstall.sh deleted file mode 100644 index 3c86bf3..0000000 --- a/riru/template/magisk_module/uninstall.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/sbin/sh -MODDIR=${0%/*} diff --git a/riru/template/magisk_module/verify.sh b/riru/template/magisk_module/verify.sh index fc706b6..cd6a4b6 100644 --- a/riru/template/magisk_module/verify.sh +++ b/riru/template/magisk_module/verify.sh @@ -35,5 +35,4 @@ extract() { [ -f "$hash_path" ] || abort_verify "$file.sha256sum not exists" (echo "$(cat "$hash_path") $file_path" | sha256sum -c -s -) || abort_verify "Failed to verify $file" - ui_print "- Verified $file" >&1 }