Repository: qiang/Riru-ModuleFridaGadget Branch: master Commit: f1484a762d56 Files: 30 Total size: 41.1 KB Directory structure: gitextract_8ya0o3m1/ ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── module/ │ ├── .gitignore │ ├── build.gradle │ ├── libs/ │ │ └── riru-10.aar │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── cpp/ │ ├── CMakeLists.txt │ ├── logging.h │ ├── main.cpp │ └── main.h ├── module.example.gradle ├── module.gradle ├── settings.gradle └── template/ └── magisk_module/ ├── .gitattributes ├── META-INF/ │ └── com/ │ └── google/ │ └── android/ │ ├── update-binary │ └── updater-script ├── README.md ├── customize.sh ├── post-fs-data.sh ├── riru.sh ├── uninstall.sh └── verify.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ *.prop text eol=lf *.sh text eol=lf ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties .idea /.idea/caches/build_file_checksums.ser /.idea/libraries /.idea/modules.xml /.idea/workspace.xml .DS_Store /build /captures /out .externalNativeBuild .cxx git-email.sh ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 Rikka 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. ================================================ FILE: README.md ================================================ #### 1、说明 firda gadget 模式支持如下四种模式: - Listen(部分支持,不支持 `"on_load": "wait"`) - Connect (?) - Script (完整支持) - ScriptDirectory(完整支持) 我没有全部测试,根据使用目的不同,我现在只需要最后一种,主要用于大规模手机部署hook功能,为了把 libgadget.so 注入到进程,所以选择了 magisk + riru 的模式,通过自定义riru模块在riru的回调里面加载 libgadget.so [Riru-ModuleTemplate](https://github.com/RikkaApps/Riru-ModuleTemplate) 本项目使用的是 [API 10 (Riru v23)](https://github.com/RikkaApps/Riru-ModuleTemplate#api-10-riru-v23) 来编译的 #### 2、 目的 & 功能 - frida 持久化 - frida 代码能够hook同一个应用的不同进程 - 应用白名单(避免和其他hook框架冲突) - 为了用于生产环境而不是调试环境 #### 3、适配Android版本 Android 9,Android 10 ,? #### 4、安装 - 通过 twrp 刷入 magisk v21 - v22.1(或者官网推荐方式) - 通过 magisk 刷入 riru ,目前测试过 v23.9 ~ v25.4.4 - 通过 magisk 刷入 riru-FridaGadgetRiruMoudle-v14.2.12.9.zip - 记得重启手机 #### 5、编译 gradle assembleRelease #### 6、配置 ##### 6.1、白名单 `/data/local/tmp/_white_list.config` 我写的这个白名单是控制进程是不是需要加载 gadget 的so的,目的是为了在手机上同时兼容xposed,要不然一个进程启动的时候同时加载 xposed 的so和 gadget.so 这时候进程会卡死。 这个配置文件格式很简单,就是把app进程名(包名)用逗号隔开,比如: ```txt com.twitter.twitter,com.github.testapp1,com.github.testapp2 ``` ##### 6.2、gadget scriptdirectory 配置 这个配置不同于上面的白名单配置,这个配置是用了控制当前已经加载了 gadget.so 的进程,是不是要加载和当前配置文件同名的js文件的。 https://frida.re/docs/gadget/#scriptdirectory 我把 gadget 的这个识别的目录硬编码指定在了 `/data/local/tmp/frida_scripts` 如果有需要,可以修改后自己编译当前项目。 那么以twitter 手机目录结构如下为例: ``` /data/local/tmp/_white_list.config /data/local/tmp/frida_scripts/twitter.js /data/local/tmp/frida_scripts/twitter.config ``` twitter.config 配置文件的目的是为了指定是否应该为某个 app 加载 twitter.js hook 脚本。twitter.js 就是普通的frida hook 脚本,twitter.config 文件格式大概如下: ```json { "filter": { "executables": ["com.twitter.twitter"], "bundles": [], "objc_classes": [] } } ``` #### 7、TODO 1、构建用于调试的工具 2、开发一个图形界面用于配置配置文件和传输js脚本 3、app 图形界面控制gadget的动态库可选 4、整理当前ts 脚本,分离改机和系统函数监控 ================================================ FILE: build.gradle ================================================ apply plugin: 'idea' idea.module { excludeDirs += file('out') resourceDirs += file('template') resourceDirs += file('scripts') } buildscript { repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:4.1.1' } } allprojects { repositories { google() jcenter() } } task clean(type: Delete) { delete rootProject.buildDir } ext { minSdkVersion = 23 targetSdkVersion = 30 } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Fri Oct 09 23:12:33 CST 2020 distributionBase=GRADLE_USER_HOME distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME ================================================ FILE: gradle.properties ================================================ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true # AndroidX package structure to make it clearer which packages are bundled with the # Android operating system, and which are packaged with your app's APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true # https://github.com/google/prefab/issues/122 android.prefabVersion=1.1.2 ================================================ FILE: gradlew ================================================ #!/usr/bin/env sh ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn () { echo "$*" } die () { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=$(save "$@") # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then cd "$(dirname "$0")" fi exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: module/.gitignore ================================================ /.externalNativeBuild /build /release ================================================ FILE: module/build.gradle ================================================ apply plugin: 'com.android.library' apply from: file(rootProject.file('module.gradle')) android { compileSdkVersion rootProject.ext.targetSdkVersion defaultConfig { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion externalNativeBuild { cmake { arguments "-DMODULE_NAME:STRING=riru_$moduleId", "-DRIRU_MODULE_API_VERSION=$moduleRiruApiVersion", "-DRIRU_MODULE_VERSION=$moduleVersionCode", "-DRIRU_MODULE_VERSION_NAME:STRING=\"$moduleVersion\"" } } } buildFeatures { prefab true } externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" version "3.10.2" } } ndkVersion '22.0.6917172 rc1' } repositories { mavenLocal() jcenter() maven { url 'https://dl.bintray.com/rikkaw/Libraries' } flatDir { dirs 'libs' } } dependencies { // This is prefab aar which contains "riru.h" // If you want to use older versions of AGP, // you can copy this file from https://github.com/RikkaApps/Riru/blob/master/riru/src/main/cpp/include_riru/riru.h // The default version of prefab in AGP has problem to process header only package, // you may have to add android.prefabVersion=1.1.2 in your gradle.properties. // See https://github.com/google/prefab/issues/122 // implementation 'rikka.ndk:riru:10' implementation (name:'riru-10', ext:'aar') } def outDir = file("$rootDir/out") def magiskDir = file("$outDir/magisk_module") def zipName = "${magiskModuleProp['id'].replace('_', '-')}-${magiskModuleProp['version']}.zip" def riruDir = "$magiskDir/riru" import org.apache.tools.ant.filters.FixCrLfFilter import java.nio.file.Files import java.security.MessageDigest static def calcSha256(file) { def md = MessageDigest.getInstance("SHA-256") file.eachByte 4096, { bytes, size -> md.update(bytes, 0, size); } return md.digest().encodeHex() } static def renameOrFail(File fromFile, File toFile) { if (!fromFile.renameTo(toFile)) { throw new IOException("Unable reanme file $fromFile to $toFile") } } android.libraryVariants.all { variant -> def task = variant.assembleProvider.get() task.doLast { // clear delete { delete magiskDir } // copy from template copy { from "$rootDir/template/magisk_module" into magiskDir.path exclude 'riru.sh' } // copy riru.sh copy { from "$rootDir/template/magisk_module" into magiskDir.path include 'riru.sh' filter { line -> line.replaceAll('%%%RIRU_MODULE_ID%%%', moduleId) .replaceAll('%%%RIRU_MODULE_API_VERSION%%%', moduleRiruApiVersion.toString()) .replaceAll('%%%RIRU_MODULE_MIN_API_VERSION%%%', moduleMinRiruApiVersion.toString()) .replaceAll('%%%RIRU_MODULE_MIN_RIRU_VERSION_NAME%%%', moduleMinRiruVersionName) } filter(FixCrLfFilter.class, eol: FixCrLfFilter.CrLf.newInstance("lf")) } // copy .git files manually since gradle exclude it by default Files.copy(file("$rootDir/template/magisk_module/.gitattributes").toPath(), file("${magiskDir.path}/.gitattributes").toPath()) // generate module.prop def modulePropText = "" magiskModuleProp.each { k, v -> modulePropText += "$k=$v\n" } modulePropText = modulePropText.trim() file("$magiskDir/module.prop").text = modulePropText // generate module.prop for Riru def riruModulePropText = "" moduleProp.each { k, v -> riruModulePropText += "$k=$v\n" } riruModulePropText = riruModulePropText.trim() file(riruDir).mkdirs() // module.prop.new will be renamed to module.prop in post-fs-data.sh file("$riruDir/module.prop.new").text = riruModulePropText // copy native files def nativeOutDir = file("build/intermediates/cmake/$variant.name/obj") file("$magiskDir/system").mkdirs() file("$magiskDir/system_x86").mkdirs() if (file("$magiskDir/system/lib64").exists()) { copy { from "$nativeOutDir/arm64-v8a" into "$magiskDir/system/lib64" include '*.so' include '*.sha256sum' } } else { renameOrFail(file("$nativeOutDir/arm64-v8a"), file("$magiskDir/system/lib64")) } if (file("$magiskDir/system/lib").exists()) { copy { from "$nativeOutDir/armeabi-v7a" into "$magiskDir/system/lib" include '*.so' } } else { renameOrFail(file("$nativeOutDir/armeabi-v7a"), file("$magiskDir/system/lib")) } renameOrFail(file("$nativeOutDir/x86_64"), file("$magiskDir/system_x86/lib64")) renameOrFail(file("$nativeOutDir/x86"), file("$magiskDir/system_x86/lib")) // generate sha1sum fileTree("$magiskDir").matching { exclude "README.md", "META-INF" }.visit { f -> if (f.directory) return file(f.file.path + ".sha256sum").text = calcSha256(f.file) } } task.finalizedBy zipMagiskMoudle } task zipMagiskMoudle(type: Zip) { from magiskDir archiveName zipName destinationDir outDir } ================================================ FILE: module/src/main/AndroidManifest.xml ================================================ ================================================ FILE: module/src/main/cpp/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.4.1) if (NOT DEFINED MODULE_NAME) message(FATAL_ERROR "MODULE_NAME is not set") endif () add_definitions(-DRIRU_MODULE) add_definitions(-DRIRU_MODULE_API_VERSION=${RIRU_MODULE_API_VERSION}) add_definitions(-DRIRU_MODULE_VERSION=${RIRU_MODULE_VERSION}) add_definitions(-DRIRU_MODULE_VERSION_NAME=${RIRU_MODULE_VERSION_NAME}) message("Build type: ${CMAKE_BUILD_TYPE}") set(CMAKE_CXX_STANDARD 11) set(LINKER_FLAGS "-ffixed-x18 -Wl,--hash-style=both") set(C_FLAGS "-Werror=format -fdata-sections -ffunction-sections") if (CMAKE_BUILD_TYPE STREQUAL "Release") set(C_FLAGS "${C_FLAGS} -O2 -fvisibility=hidden -fvisibility-inlines-hidden") set(LINKER_FLAGS "${LINKER_FLAGS} -Wl,-exclude-libs,ALL -Wl,--gc-sections") else () set(C_FLAGS "${C_FLAGS} -O0") endif () set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${C_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}") find_package(riru REQUIRED CONFIG) add_library(${MODULE_NAME} SHARED main.cpp) target_link_libraries(${MODULE_NAME} log riru::riru) set_target_properties(${MODULE_NAME} PROPERTIES LINK_FLAGS_RELEASE -s) ================================================ FILE: module/src/main/cpp/logging.h ================================================ #ifndef _LOGGING_H #define _LOGGING_H #include #include "android/log.h" #ifndef LOG_TAG #define LOG_TAG "Riru-ModuleFridaGadget" #endif #ifdef DEBUG #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #else #define LOGD(...) #endif #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) #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 PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)) #endif // _LOGGING_H ================================================ FILE: module/src/main/cpp/main.cpp ================================================ #include #include #include #include #include #include #include #include "logging.h" #include "main.h" static char nice_process_name[256] = {0}; static char package_name[256] = {0}; static jint my_uid = 0; static bool isApp(int uid) { if (uid < 0) { return false; } int appId = uid % 100000; // limit only regular app, or strange situation will happen, such as zygote process not start (dead for no reason and leave no clues?) // https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r8/core/java/android/os/UserHandle.java#151 return appId >= 10000 && appId <= 19999; } static void my_forkAndSpecializePre(JNIEnv *env, jint *uid, jstring *niceName, jstring *appDataDir) { //LOGI("Q_M xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx %s", "forkAndSpecializePre"); my_uid = *uid; if (!isApp(my_uid)) { return; } const char *tablePath = (env->GetStringUTFChars(*niceName, 0)); sprintf(nice_process_name, "%s", tablePath); delete tablePath; if (!appDataDir) { LOGI("Q_M forkAndSpecializePre appDataDir null"); return; } const char *app_data_dir = env->GetStringUTFChars(*appDataDir, NULL); if (app_data_dir == nullptr) { return; } //LOGI("Q_M xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx app_data_dir %s",app_data_dir); int user = 0; if (sscanf(app_data_dir, "/data/%*[^/]/%d/%s", &user, package_name) != 2) { if (sscanf(app_data_dir, "/data/%*[^/]/%s", package_name) != 1) { package_name[0] = '\0'; LOGI("Q_M can't parse %s", app_data_dir); } } env->ReleaseStringUTFChars(*appDataDir, app_data_dir); } static void my_forkAndSpecializePost(JNIEnv *env) { if (!isApp(my_uid)) { return; } // if (!strstr(nice_process_name, "com.smile.gifmaker") // && !strstr(nice_process_name, "com.ss.android.ugc.aweme") // && !strstr(nice_process_name, "com.xingin.xhs") // ) { // return; // } //http://www.cplusplus.com/reference/cstdio/fread/ 读取整个文件 char *white_list; //白名单的pkgName 最好以逗号或者分好分割开来 const char *filepath = "/data/local/tmp/_white_list.config"; FILE *fp = nullptr; fp = fopen(filepath, "r"); if (fp != nullptr) { fseek(fp, 0, SEEK_END); int fileLen = ftell(fp); white_list = (char *) malloc(sizeof(char) * (fileLen + 1)); fseek(fp, 0, SEEK_SET); // size_t count = fread(white_list, fileLen, sizeof(char), fp); size_t count = fread(white_list, 1, fileLen, fp); // LOGI("Q_M xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 白名单长度 %zu", count); white_list[count] = '\0'; fclose(fp); LOGI("Q_M xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 白名单:%s", white_list); } else { white_list = ""; } if (!strstr(white_list, package_name)) { return; } LOGI("Q_M xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx nice_process_name=%s, pkg=%s,uid=%d, isApp= %d", nice_process_name, package_name, my_uid, isApp(my_uid)); //添加这种机制,就可以提前设置进程名, 从而让frida 的gadget 能够识别到 jclass java_Process = env->FindClass("android/os/Process"); if (java_Process != nullptr && isApp(my_uid)) { jmethodID mtd_setArgV0 = env->GetStaticMethodID(java_Process, "setArgV0", "(Ljava/lang/String;)V"); jstring name = env->NewStringUTF(nice_process_name); env->CallStaticVoidMethod(java_Process, mtd_setArgV0, name); void *handle = dlopen(nextLoadSo, RTLD_LAZY); if (!handle) { // LOGE("%s",dlerror()); LOGE("Q_M %s loaded in libgadget 出错 %s", nice_process_name, dlerror()); } else { LOGI("Q_M xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-> %s 加载 ' %s ' 成功 ", nice_process_name, nextLoadSo); } } } static void forkAndSpecializePre( JNIEnv *env, jclass clazz, 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) { my_forkAndSpecializePre(env, uid, niceName, appDataDir); } //很遗憾,执行这行代码的时候,还没有设置进程名字,导致,这个方法里面加载 frida gadget 的动态库获取不到进程名 // 只有执行完 Zygote.forkAndSpecialize 会在 handleChildProc 里面设置进程名,Process.setArgV0(parsedArgs.niceName); // libandroid_runtime.so //https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/jni/android_util_Process.cpp;l=593?q=setSwappiness&ss=android%2Fplatform%2Fsuperproject static void forkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) { if (res == 0) { my_forkAndSpecializePost(env); } else { // in zygote process, res is child pid // don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66 } } static void specializeAppProcessPre( JNIEnv *env, jclass clazz, 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) { // added from Android 10, but disabled at least in Google Pixel devices my_forkAndSpecializePre(env, uid, niceName, appDataDir); } static void specializeAppProcessPost( JNIEnv *env, jclass clazz) { // added from Android 10, but disabled at least in Google Pixel devices my_forkAndSpecializePost(env); } static void forkSystemServerPre( JNIEnv *env, jclass clazz, uid_t *uid, gid_t *gid, jintArray *gids, jint *runtimeFlags, jobjectArray *rlimits, jlong *permittedCapabilities, jlong *effectiveCapabilities) { } static void forkSystemServerPost(JNIEnv *env, jclass clazz, jint res) { if (res == 0) { // in system server process } else { // in zygote process, res is child pid // don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66 } } static int shouldSkipUid(int uid) { // By default (the module does not provide this function in init), Riru will only call // module functions in "normal app processes" (10000 <= uid % 100000 <= 19999) // Provide this function so that the module can control if a specific uid should be skipped return false; } static void onModuleLoaded() { // called when the shared library of Riru core is loaded } extern "C" { int riru_api_version; RiruApiV9 *riru_api_v9; /* * Init will be called three times. * * The first time: * Returns the highest version number supported by both Riru and the module. * * arg: (int *) Riru's API version * returns: (int *) the highest possible API version * * The second time: * Returns the RiruModuleX struct created by the module. * (X is the return of the first call) * * arg: (RiruApiVX *) RiruApi strcut, this pointer can be saved for further use * returns: (RiruModuleX *) RiruModule strcut * * The third time: * Let the module to cleanup (such as RiruModuleX struct created before). * * arg: null * returns: (ignored) * */ void *init(void *arg) { static int step = 0; step += 1; LOGI("Q_M Riru-ModuleFridaGadget xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx %s", "init"); static void *_module; switch (step) { case 1: { auto core_max_api_version = *(int *) arg; riru_api_version = core_max_api_version <= RIRU_MODULE_API_VERSION ? core_max_api_version : RIRU_MODULE_API_VERSION; return &riru_api_version; } case 2: { switch (riru_api_version) { // RiruApiV10 and RiruModuleInfoV10 are equal to V9 case 10: case 9: { riru_api_v9 = (RiruApiV9 *) arg; auto module = (RiruModuleInfoV9 *) malloc(sizeof(RiruModuleInfoV9)); memset(module, 0, sizeof(RiruModuleInfoV9)); _module = module; module->supportHide = true; module->version = RIRU_MODULE_VERSION; module->versionName = RIRU_MODULE_VERSION_NAME; module->onModuleLoaded = onModuleLoaded; module->shouldSkipUid = shouldSkipUid; module->forkAndSpecializePre = forkAndSpecializePre; module->forkAndSpecializePost = forkAndSpecializePost; module->specializeAppProcessPre = specializeAppProcessPre; module->specializeAppProcessPost = specializeAppProcessPost; module->forkSystemServerPre = forkSystemServerPre; module->forkSystemServerPost = forkSystemServerPost; return module; } default: { return nullptr; } } } case 3: { free(_module); return nullptr; } default: { return nullptr; } } } } ================================================ FILE: module/src/main/cpp/main.h ================================================ // // Created by canyie on 2020/8/12. // #ifndef NBINJECTION_MAIN_H #define NBINJECTION_MAIN_H #ifdef __LP64__ constexpr const char* kZygoteNiceName = "zygote64"; constexpr const char* nextLoadSo = "/system/lib64/libgadget.so"; #else constexpr const char* kZygoteNiceName = "zygote"; constexpr const char* nextLoadSo = "/system/lib/libgadget.so"; #endif #endif //NBINJECTION_MAIN_H ================================================ FILE: module.example.gradle ================================================ ext { // FIXME replace with yours moduleId = "template" moduleName = "Template" moduleAuthor = "Template" moduleDescription = "Riru module template." moduleVersion = "v1.1" moduleVersionCode = 1 moduleMinRiruApiVersion = 9 moduleMinRiruVersionName = "v22.0" moduleRiruApiVersion = 10 moduleProp = [ name : moduleName, version : moduleVersion, versionCode: moduleVersionCode.toString(), author : moduleAuthor, description: moduleDescription, minApi : moduleMinRiruApiVersion ] magiskModuleProp = [ id : "riru-${moduleId.replace('_', '-')}", name : "Riru - ${moduleProp['name']}", version : moduleProp['version'], versionCode: moduleProp['versionCode'], author : moduleProp['author'], description: moduleProp['description'] ] } ================================================ FILE: module.gradle ================================================ ext { moduleId = "FridaGadgetRiruMoudle" moduleName = "Frida-Gadget-Riru-Module" moduleAuthor = "BayBayMax" moduleDescription = "一个加载 FridaGadget 动态库的magisk 插件,依赖riru 框架" moduleVersion = "v14.2.12.9" moduleVersionCode = 14_2_12_9 moduleMinRiruApiVersion = 9 moduleMinRiruVersionName = "v22.0" moduleRiruApiVersion = 10 moduleProp = [ name : moduleName, version : moduleVersion, versionCode: moduleVersionCode.toString(), author : moduleAuthor, description: moduleDescription, minApi : moduleMinRiruApiVersion ] magiskModuleProp = [ id : "riru-${moduleId.replace('_', '-')}", name : "Riru - ${moduleProp['name']}", version : moduleProp['version'], versionCode: moduleProp['versionCode'], author : moduleProp['author'], description: moduleProp['description'] ] } ================================================ FILE: settings.gradle ================================================ include ':module' ================================================ FILE: template/magisk_module/.gitattributes ================================================ # Declare files that will always have LF line endings on checkout. META-INF/** text eol=lf *.prop text eol=lf *.sh text eol=lf *.md text eol=lf sepolicy.rule text eol=lf # Denote all files that are truly binary and should not be modified. system/** binary system_x86/** binary ================================================ FILE: template/magisk_module/META-INF/com/google/android/update-binary ================================================ #!/sbin/sh ################# # Initialization ################# umask 022 # Global vars TMPDIR=/dev/tmp PERSISTDIR=/sbin/.magisk/mirror/persist rm -rf $TMPDIR 2>/dev/null mkdir -p $TMPDIR # echo before loading util_functions ui_print() { echo "$1"; } require_new_magisk() { ui_print "*******************************" ui_print " Please install Magisk v19.0+! " ui_print "*******************************" exit 1 } is_legacy_script() { unzip -l "$ZIPFILE" install.sh | grep -q install.sh return $? } print_modname() { local len len=`echo -n $MODNAME | wc -c` len=$((len + 2)) local pounds=`printf "%${len}s" | tr ' ' '*'` ui_print "$pounds" ui_print " $MODNAME " ui_print "$pounds" ui_print "*******************" ui_print " Powered by Magisk " ui_print "*******************" } ############## # Environment ############## OUTFD=$2 ZIPFILE=$3 mount /data 2>/dev/null # Load utility functions [ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk . /data/adb/magisk/util_functions.sh [ $MAGISK_VER_CODE -gt 18100 ] || require_new_magisk # Preperation for flashable zips setup_flashable # Mount partitions mount_partitions # Detect version and architecture api_level_arch_detect # Setup busybox and binaries $BOOTMODE && boot_actions || recovery_actions ############## # Preparation ############## # Extract prop file unzip -o "$ZIPFILE" module.prop -d $TMPDIR >&2 [ ! -f $TMPDIR/module.prop ] && abort "! Unable to extract zip file!" $BOOTMODE && MODDIRNAME=modules_update || MODDIRNAME=modules MODULEROOT=$NVBASE/$MODDIRNAME MODID=`grep_prop id $TMPDIR/module.prop` MODPATH=$MODULEROOT/$MODID MODNAME=`grep_prop name $TMPDIR/module.prop` # Create mod paths rm -rf $MODPATH 2>/dev/null mkdir -p $MODPATH ########## # Install ########## if is_legacy_script; then unzip -oj "$ZIPFILE" module.prop install.sh uninstall.sh 'common/*' -d $TMPDIR >&2 # Load install script . $TMPDIR/install.sh # Callbacks print_modname on_install # Custom uninstaller [ -f $TMPDIR/uninstall.sh ] && cp -af $TMPDIR/uninstall.sh $MODPATH/uninstall.sh # Skip mount $SKIPMOUNT && touch $MODPATH/skip_mount # prop file $PROPFILE && cp -af $TMPDIR/system.prop $MODPATH/system.prop # Module info cp -af $TMPDIR/module.prop $MODPATH/module.prop # post-fs-data scripts $POSTFSDATA && cp -af $TMPDIR/post-fs-data.sh $MODPATH/post-fs-data.sh # service scripts $LATESTARTSERVICE && cp -af $TMPDIR/service.sh $MODPATH/service.sh ui_print "- Setting permissions" set_permissions else print_modname unzip -o "$ZIPFILE" customize.sh -d $MODPATH >&2 if ! grep -q '^SKIPUNZIP=1$' $MODPATH/customize.sh 2>/dev/null; then ui_print "- Extracting module files" unzip -o "$ZIPFILE" -x 'META-INF/*' -d $MODPATH >&2 # Default permissions set_perm_recursive $MODPATH 0 0 0755 0644 fi # Load customization script [ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh fi # Handle replace folders for TARGET in $REPLACE; do ui_print "- Replace target: $TARGET" mktouch $MODPATH$TARGET/.replace done if $BOOTMODE; then # Update info for Magisk Manager mktouch $NVBASE/modules/$MODID/update cp -af $MODPATH/module.prop $NVBASE/modules/$MODID/module.prop fi # Copy over custom sepolicy rules if [ -f $MODPATH/sepolicy.rule -a -e $PERSISTDIR ]; then ui_print "- Installing custom sepolicy patch" PERSISTMOD=$PERSISTDIR/magisk/$MODID mkdir -p $PERSISTMOD cp -af $MODPATH/sepolicy.rule $PERSISTMOD/sepolicy.rule fi # Remove stuffs that don't belong to modules rm -rf \ $MODPATH/system/placeholder $MODPATH/customize.sh \ $MODPATH/README.md $MODPATH/.git* 2>/dev/null ############## # Finalizing ############## cd / $BOOTMODE || recovery_cleanup rm -rf $TMPDIR ui_print "- Done" exit 0 ================================================ FILE: template/magisk_module/META-INF/com/google/android/updater-script ================================================ #MAGISK ================================================ FILE: template/magisk_module/README.md ================================================ # Riru - Template ================================================ FILE: template/magisk_module/customize.sh ================================================ SKIPUNZIP=1 # check_architecture if [ "$ARCH" != "arm" ] && [ "$ARCH" != "arm64" ] && [ "$ARCH" != "x86" ] && [ "$ARCH" != "x64" ]; then abort "! Unsupported platform: $ARCH" else ui_print "- Device platform: $ARCH" fi # extract verify.sh ui_print "- Extracting verify.sh" unzip -o "$ZIPFILE" 'verify.sh' -d "$TMPDIR" >&2 if [ ! -f "$TMPDIR/verify.sh" ]; then ui_print "*********************************************************" ui_print "! Unable to extract verify.sh!" ui_print "! This zip may be corrupted, please try downloading again" abort "*********************************************************" fi . $TMPDIR/verify.sh # extract riru.sh extract "$ZIPFILE" 'riru.sh' "$MODPATH" . $MODPATH/riru.sh check_riru_version # extract libs ui_print "- Extracting module files" extract "$ZIPFILE" 'module.prop' "$MODPATH" extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH" extract "$ZIPFILE" 'uninstall.sh' "$MODPATH" if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then ui_print "- Extracting x86 libraries" extract "$ZIPFILE" "system_x86/lib/libriru_$RIRU_MODULE_ID.so" "$MODPATH" mv "$MODPATH/system_x86" "$MODPATH/system" if [ "$IS64BIT" = true ]; then ui_print "- Extracting x64 libraries" extract "$ZIPFILE" "system_x86/lib64/libriru_$RIRU_MODULE_ID.so" "$MODPATH" mv "$MODPATH/system_x86/lib64" "$MODPATH/system/lib64" fi else ui_print "- Extracting arm libraries" REPLACEMENT_DIR="system/*" # 一劳永逸。完全提取。在某些一加手机上这个方法不能提取 system/lib/ 里面的数据,为啥?是unzip这个命令工具的问题。 # 注意加上引号 unzip -o "$ZIPFILE" "$REPLACEMENT_DIR" -d "$MODPATH" >&2 || abort "! Can't extract system/: $?" # extract "$ZIPFILE" "system/lib/libgadget.so" "$MODPATH" # extract "$ZIPFILE" "system/lib/libgadget.config.so" "$MODPATH" # extract "$ZIPFILE" "system/lib/libriru_FridaGadgetRiruMoudle.so" "$MODPATH" # # extract "$ZIPFILE" "system/lib64/libgadget.so" "$MODPATH" # extract "$ZIPFILE" "system/lib64/libgadget.config.so" "$MODPATH" # extract "$ZIPFILE" "system/lib64/libriru_FridaGadgetRiruMoudle.so" "$MODPATH" fi set_perm_recursive "$MODPATH" 0 0 0755 0644 # extract Riru files ui_print "- Extracting extra files" [ -d "$RIRU_MODULE_PATH" ] || mkdir -p "$RIRU_MODULE_PATH" || abort "! Can't create $RIRU_MODULE_PATH" rm -f "$RIRU_MODULE_PATH/module.prop.new" extract "$ZIPFILE" 'riru/module.prop.new' "$RIRU_MODULE_PATH" true set_perm "$RIRU_MODULE_PATH/module.prop.new" 0 0 0600 $RIRU_SECONTEXT ================================================ FILE: template/magisk_module/post-fs-data.sh ================================================ #!/system/bin/sh MODDIR=${0%/*} [ ! -f "$MODDIR/riru.sh" ] && exit 1 . $MODDIR/riru.sh # Rename module.prop.new if [ -f "$RIRU_MODULE_PATH/module.prop.new" ]; then rm "$RIRU_MODULE_PATH/module.prop" mv "$RIRU_MODULE_PATH/module.prop.new" "$RIRU_MODULE_PATH/module.prop" fi ================================================ FILE: template/magisk_module/riru.sh ================================================ #!/sbin/sh RIRU_PATH="/data/adb/riru" RIRU_MODULE_ID="%%%RIRU_MODULE_ID%%%" RIRU_MODULE_PATH="$RIRU_PATH/modules/$RIRU_MODULE_ID" RIRU_SECONTEXT="u:object_r:magisk_file:s0" # used by /data/adb/riru/util_functions.sh RIRU_MODULE_API_VERSION=%%%RIRU_MODULE_API_VERSION%%% RIRU_MODULE_MIN_API_VERSION=%%%RIRU_MODULE_MIN_API_VERSION%%% RIRU_MODULE_MIN_RIRU_VERSION_NAME="%%%RIRU_MODULE_MIN_RIRU_VERSION_NAME%%%" # this function will be used when /data/adb/riru/util_functions.sh not exits check_riru_version() { if [ ! -f "$RIRU_PATH/api_version" ] && [ ! -f "$RIRU_PATH/api_version.new" ]; then ui_print "*********************************************************" ui_print "! Riru $RIRU_MIN_VERSION_NAME or above is required" ui_print "! Please install Riru from Magisk Manager or https://github.com/RikkaApps/Riru/releases" abort "*********************************************************" fi local_api_version=$(cat "$RIRU_PATH/api_version.new") || local_api_version=$(cat "$RIRU_PATH/api_version") || local_api_version=0 [ "$local_api_version" -eq "$local_api_version" ] || local_api_version=0 ui_print "- Riru API version: $local_api_version" if [ "$local_api_version" -lt $RIRU_MODULE_MIN_API_VERSION ]; then ui_print "*********************************************************" ui_print "! Riru $RIRU_MIN_VERSION_NAME or above is required" ui_print "! Please upgrade Riru from Magisk Manager or https://github.com/RikkaApps/Riru/releases" abort "*********************************************************" fi } if [ -f /data/adb/riru/util_functions.sh ]; then ui_print "- Load /data/adb/riru/util_functions.sh" . /data/adb/riru/util_functions.sh else ui_print "- Can't find /data/adb/riru/util_functions.sh" fi ================================================ FILE: template/magisk_module/uninstall.sh ================================================ #!/sbin/sh MODDIR=${0%/*} [ ! -f "$MODDIR/riru.sh" ] && exit 1 . $MODDIR/riru.sh rm -rf "$RIRU_MODULE_PATH" ================================================ FILE: template/magisk_module/verify.sh ================================================ TMPDIR_FOR_VERIFY="$TMPDIR/.vunzip" mkdir "$TMPDIR_FOR_VERIFY" abort_verify() { ui_print "*********************************************************" ui_print "! $1" ui_print "! This zip may be corrupted, please try downloading again" abort "*********************************************************" } # extract extract() { zip=$1 file=$2 dir=$3 junk_paths=$4 [ -z "$junk_paths" ] && junk_paths=false opts="-o" [ $junk_paths = true ] && opts="-oj" file_path="" hash_path="" if [ $junk_paths = true ]; then file_path="$dir/$(basename "$file")" hash_path="$TMPDIR_FOR_VERIFY/$(basename "$file").sha256sum" else file_path="$dir/$file" hash_path="$TMPDIR_FOR_VERIFY/$file.sha256sum" fi unzip $opts "$zip" "$file" -d "$dir" >&2 [ -f "$file_path" ] || abort_verify "$file not exists" unzip $opts "$zip" "$file.sha256sum" -d "$TMPDIR_FOR_VERIFY" >&2 [ -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 }