Full Code of qiang/Riru-ModuleFridaGadget for AI

master f1484a762d56 cached
30 files
41.1 KB
12.7k tokens
11 symbols
1 requests
Download .txt
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
================================================
<manifest package="riru.template" />


================================================
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 <errno.h>
#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 <jni.h>
#include <sys/types.h>
#include <riru.h>
#include <malloc.h>
#include <vector>

#include <cstring>
#include <dlfcn.h>
#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 <zip> <file> <target dir> <junk paths>
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
}
Download .txt
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
Download .txt
SYMBOL INDEX (11 symbols across 1 files)

FILE: module/src/main/cpp/main.cpp
  function isApp (line 17) | static bool isApp(int uid) {
  function my_forkAndSpecializePre (line 28) | static void
  function my_forkAndSpecializePost (line 62) | static void my_forkAndSpecializePost(JNIEnv *env) {
  function forkAndSpecializePre (line 125) | static void forkAndSpecializePre(
  function forkAndSpecializePost (line 140) | static void forkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) {
  function specializeAppProcessPre (line 149) | static void specializeAppProcessPre(
  function specializeAppProcessPost (line 160) | static void specializeAppProcessPost(
  function forkSystemServerPre (line 166) | static void forkSystemServerPre(
  function forkSystemServerPost (line 172) | static void forkSystemServerPost(JNIEnv *env, jclass clazz, jint res) {
  function shouldSkipUid (line 181) | static int shouldSkipUid(int uid) {
  function onModuleLoaded (line 189) | static void onModuleLoaded() {
Condensed preview — 30 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (47K chars).
[
  {
    "path": ".gitattributes",
    "chars": 35,
    "preview": "*.prop text eol=lf\n*.sh text eol=lf"
  },
  {
    "path": ".gitignore",
    "chars": 205,
    "preview": "*.iml\n.gradle\n/local.properties\n.idea\n/.idea/caches/build_file_checksums.ser\n/.idea/libraries\n/.idea/modules.xml\n/.idea/"
  },
  {
    "path": "LICENSE",
    "chars": 1062,
    "preview": "MIT License\n\nCopyright (c) 2020 Rikka\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof t"
  },
  {
    "path": "README.md",
    "chars": 1803,
    "preview": "#### 1、说明\n\nfirda gadget 模式支持如下四种模式:\n\n   - Listen(部分支持,不支持 `\"on_load\": \"wait\"`)\n   - Connect (?)\n   - Script (完整支持)\n   - "
  },
  {
    "path": "build.gradle",
    "chars": 498,
    "preview": "apply plugin: 'idea'\n\nidea.module {\n    excludeDirs += file('out')\n    resourceDirs += file('template')\n    resourceDirs"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 232,
    "preview": "#Fri Oct 09 23:12:33 CST 2020\ndistributionBase=GRADLE_USER_HOME\ndistributionUrl=https\\://services.gradle.org/distributio"
  },
  {
    "path": "gradle.properties",
    "chars": 1145,
    "preview": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will ov"
  },
  {
    "path": "gradlew",
    "chars": 5296,
    "preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up"
  },
  {
    "path": "gradlew.bat",
    "chars": 2176,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
  },
  {
    "path": "module/.gitignore",
    "chars": 37,
    "preview": "/.externalNativeBuild\n/build\n/release"
  },
  {
    "path": "module/build.gradle",
    "chars": 5577,
    "preview": "apply plugin: 'com.android.library'\napply from: file(rootProject.file('module.gradle'))\n\nandroid {\n    compileSdkVersion"
  },
  {
    "path": "module/src/main/AndroidManifest.xml",
    "chars": 37,
    "preview": "<manifest package=\"riru.template\" />\n"
  },
  {
    "path": "module/src/main/cpp/CMakeLists.txt",
    "chars": 1276,
    "preview": "cmake_minimum_required(VERSION 3.4.1)\n\nif (NOT DEFINED MODULE_NAME)\n    message(FATAL_ERROR \"MODULE_NAME is not set\")\nen"
  },
  {
    "path": "module/src/main/cpp/logging.h",
    "chars": 709,
    "preview": "#ifndef _LOGGING_H\n#define _LOGGING_H\n\n#include <errno.h>\n#include \"android/log.h\"\n\n#ifndef LOG_TAG\n#define LOG_TAG    \""
  },
  {
    "path": "module/src/main/cpp/main.cpp",
    "chars": 9802,
    "preview": "#include <jni.h>\n#include <sys/types.h>\n#include <riru.h>\n#include <malloc.h>\n#include <vector>\n\n#include <cstring>\n#inc"
  },
  {
    "path": "module/src/main/cpp/main.h",
    "chars": 386,
    "preview": "//\n// Created by canyie on 2020/8/12.\n//\n\n#ifndef NBINJECTION_MAIN_H\n#define NBINJECTION_MAIN_H\n\n\n#ifdef __LP64__\nconste"
  },
  {
    "path": "module.example.gradle",
    "chars": 966,
    "preview": "ext {\n    // FIXME replace with yours\n    moduleId = \"template\"\n    moduleName = \"Template\"\n    moduleAuthor = \"Template"
  },
  {
    "path": "module.gradle",
    "chars": 998,
    "preview": "ext {\n    moduleId = \"FridaGadgetRiruMoudle\"\n    moduleName = \"Frida-Gadget-Riru-Module\"\n    moduleAuthor = \"BayBayMax\"\n"
  },
  {
    "path": "settings.gradle",
    "chars": 18,
    "preview": "include ':module'\n"
  },
  {
    "path": "template/magisk_module/.gitattributes",
    "chars": 277,
    "preview": "# Declare files that will always have LF line endings on checkout.\nMETA-INF/** text eol=lf\n*.prop text eol=lf\n*.sh text "
  },
  {
    "path": "template/magisk_module/META-INF/com/google/android/update-binary",
    "chars": 3793,
    "preview": "#!/sbin/sh\n\n#################\n# Initialization\n#################\n\numask 022\n\n# Global vars\nTMPDIR=/dev/tmp\nPERSISTDIR=/s"
  },
  {
    "path": "template/magisk_module/META-INF/com/google/android/updater-script",
    "chars": 8,
    "preview": "#MAGISK\n"
  },
  {
    "path": "template/magisk_module/README.md",
    "chars": 17,
    "preview": "# Riru - Template"
  },
  {
    "path": "template/magisk_module/customize.sh",
    "chars": 2435,
    "preview": "SKIPUNZIP=1\n\n# check_architecture\nif [ \"$ARCH\" != \"arm\" ] && [ \"$ARCH\" != \"arm64\" ] && [ \"$ARCH\" != \"x86\" ] && [ \"$ARCH\""
  },
  {
    "path": "template/magisk_module/post-fs-data.sh",
    "chars": 281,
    "preview": "#!/system/bin/sh\nMODDIR=${0%/*}\n[ ! -f \"$MODDIR/riru.sh\" ] && exit 1\n. $MODDIR/riru.sh\n\n# Rename module.prop.new\nif [ -f"
  },
  {
    "path": "template/magisk_module/riru.sh",
    "chars": 1766,
    "preview": "#!/sbin/sh\nRIRU_PATH=\"/data/adb/riru\"\nRIRU_MODULE_ID=\"%%%RIRU_MODULE_ID%%%\"\nRIRU_MODULE_PATH=\"$RIRU_PATH/modules/$RIRU_M"
  },
  {
    "path": "template/magisk_module/uninstall.sh",
    "chars": 108,
    "preview": "#!/sbin/sh\nMODDIR=${0%/*}\n[ ! -f \"$MODDIR/riru.sh\" ] && exit 1\n. $MODDIR/riru.sh\n\nrm -rf \"$RIRU_MODULE_PATH\""
  },
  {
    "path": "template/magisk_module/verify.sh",
    "chars": 1151,
    "preview": "TMPDIR_FOR_VERIFY=\"$TMPDIR/.vunzip\"\nmkdir \"$TMPDIR_FOR_VERIFY\"\n\nabort_verify() {\n  ui_print \"***************************"
  }
]

// ... and 2 more files (download for full content)

About this extraction

This page contains the full source code of the qiang/Riru-ModuleFridaGadget GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 30 files (41.1 KB), approximately 12.7k tokens, and a symbol index with 11 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!