riru: Initial module implementation

This commit is contained in:
Danny Lin 2021-09-06 01:09:49 -07:00
parent 1282960dbf
commit 8d50d8fe3c
No known key found for this signature in database
GPG Key ID: 1988FAA1797EE5AC
9 changed files with 201 additions and 96 deletions

View File

@ -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"

View File

@ -27,3 +27,30 @@
-keepclassmembers class dev.kdrag0n.safetynetriru.proxy.ProxyKeyStoreSpi {
public <init>(...);
}
# 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);
}

View File

@ -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
}

View File

@ -4,6 +4,115 @@
#include <malloc.h>
#include <cstring>
#include <config.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <android/log.h>
#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, "<init>", "(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) {

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2021 Danny Lin <danny@kdrag0n.dev>
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.

View File

@ -1 +0,0 @@
# Riru - Template

View File

@ -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

View File

@ -1,2 +0,0 @@
#!/sbin/sh
MODDIR=${0%/*}

View File

@ -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
}