├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── colors.xml │ │ │ └── themes.xml │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-anydpi │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── xml │ │ │ ├── backup_rules.xml │ │ │ └── data_extraction_rules.xml │ │ ├── values-night │ │ │ └── themes.xml │ │ └── drawable │ │ │ ├── ic_launcher_foreground.xml │ │ │ └── ic_launcher_background.xml │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── module ├── .gitignore ├── consumer-rules.pro ├── magisk_module │ ├── common │ │ ├── install.sh │ │ ├── addon │ │ │ └── placeholder │ │ └── functions.sh │ ├── .gitignore │ ├── META-INF │ │ └── com │ │ │ └── google │ │ │ └── android │ │ │ ├── updater-script │ │ │ └── update-binary │ ├── module.prop │ ├── update.json │ ├── .gitattributes │ ├── uninstall.sh │ └── customize.sh ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── cpp │ │ ├── third │ │ ├── log2file │ │ │ ├── CMakeLists.txt │ │ │ ├── app_file_writer.cpp │ │ │ └── app_file_writer.h │ │ ├── CMakeLists.txt │ │ └── utils │ │ │ ├── java_vm.cpp │ │ │ ├── wait_group.h │ │ │ ├── map_helper.h │ │ │ ├── stack.asm │ │ │ ├── meminfo.h │ │ │ ├── log.cpp │ │ │ ├── macro_helper.h │ │ │ ├── thread_poool.h │ │ │ ├── meminfo.cpp │ │ │ ├── linux_helper.h │ │ │ ├── utils.h │ │ │ ├── log.h │ │ │ ├── jni_helper.cpp │ │ │ ├── java_vm.h │ │ │ ├── utils.cpp │ │ │ ├── linux_helper.cpp │ │ │ └── jni_helper.hpp │ │ ├── CMakeLists.txt │ │ ├── example.cpp │ │ └── zygisk.hpp ├── proguard-rules.pro ├── build.gradle └── make_plug.gradle ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── README.md ├── .gitignore ├── settings.gradle ├── gradle.properties ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /module/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /module/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /module/magisk_module/common/install.sh: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /module/magisk_module/common/addon/placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /module/magisk_module/.gitignore: -------------------------------------------------------------------------------- 1 | __MACOSX 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /module/magisk_module/META-INF/com/google/android/updater-script: -------------------------------------------------------------------------------- 1 | #MAGISK 2 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | jni_trace 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xbyl1234/jni_trace/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xbyl1234/jni_trace/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xbyl1234/jni_trace/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xbyl1234/jni_trace/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xbyl1234/jni_trace/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xbyl1234/jni_trace/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xbyl1234/jni_trace/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xbyl1234/jni_trace/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xbyl1234/jni_trace/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xbyl1234/jni_trace/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xbyl1234/jni_trace/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /module/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /module/magisk_module/module.prop: -------------------------------------------------------------------------------- 1 | id=jni_trace 2 | name=jni_trace 3 | version=v3.7 4 | versionCode=19 5 | author=jni_trace 6 | description=jni_trace 7 | updateJson=https://raw.githubusercontent.com/Zackptg5/MMT-Extended/master/update.json 8 | -------------------------------------------------------------------------------- /module/magisk_module/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v3.6", 3 | "versionCode": 18, 4 | "zipUrl": "https://raw.githubusercontent.com/Zackptg5/MMT-Extended/install.zip", 5 | "changelog": "https://raw.githubusercontent.com/Zackptg5/MMT-Extended/changelog.md" 6 | } -------------------------------------------------------------------------------- /module/src/main/cpp/third/log2file/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.28.0+) 2 | project("log2file") 3 | 4 | file(GLOB log2file_src 5 | "*.h" 6 | "*.cpp" 7 | "*.c") 8 | 9 | add_library(log2file 10 | STATIC 11 | ${log2file_src}) -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 20 15:19:49 CST 2025 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /module/magisk_module/.gitattributes: -------------------------------------------------------------------------------- 1 | # Declare files that will always have LF line endings on checkout. 2 | *.sh text eol=lf 3 | *.prop text eol=lf 4 | *.md text eol=lf 5 | *.xml text eol=lf 6 | META-INF/** text eol=lf 7 | 8 | # Denote all files that are truly binary and should not be modified. 9 | common/addon/**/tools/** binary 10 | -------------------------------------------------------------------------------- /module/src/main/cpp/third/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.28.0+) 2 | project("third") 3 | enable_language(ASM) 4 | 5 | file(GLOB utils_src 6 | "utils/*.h" 7 | "utils/*.cpp" 8 | "utils/*.c" 9 | "utils/*.asm" 10 | ) 11 | 12 | add_library(libutils 13 | STATIC 14 | ${utils_src}) 15 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 代码 6 | 没有 7 | 同步 8 | 最新 9 | 代码 10 | 查看 11 | android_analysis 12 | 仓库 13 | 自行 14 | 同步 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 直接编译, 模块生成在out目录 24 | 需要删除postAppSpecialize(const AppSpecializeArgs *args)中我调试其他app的代码 25 | 需要在jni_trace.cpp 26 | JNIEXPORT jboolean JNICALL init(JNIEnv *env, jclass frida_helper)函数中配置你目标so 27 | 28 | 疯狂星期四 v我50 29 | 我是秦始皇 v我50封你为大牛马 30 | 31 | 哦对了 需要一个frida_helper.dex 在我另一个项目里面 android_analysis 编译安装给root就写到对应目录了 32 | -------------------------------------------------------------------------------- /module/magisk_module/uninstall.sh: -------------------------------------------------------------------------------- 1 | # Don't modify anything after this 2 | if [ -f $INFO ]; then 3 | while read LINE; do 4 | if [ "$(echo -n $LINE | tail -c 1)" == "~" ]; then 5 | continue 6 | elif [ -f "$LINE~" ]; then 7 | mv -f $LINE~ $LINE 8 | else 9 | rm -f $LINE 10 | while true; do 11 | LINE=$(dirname $LINE) 12 | [ "$(ls -A $LINE 2>/dev/null)" ] && break 1 || rm -rf $LINE 13 | done 14 | fi 15 | done < $INFO 16 | rm -f $INFO 17 | fi -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | /.idea/.gitignore 17 | /.idea/compiler.xml 18 | /.idea/deploymentTargetSelector.xml 19 | /.idea/gradle.xml 20 | /.idea/migrations.xml 21 | /.idea/misc.xml 22 | /.idea/runConfigurations.xml 23 | /.idea/vcs.xml 24 | /module/libs/arm64-v8a/libanalyse.so 25 | /module/magisk_module/zygisk/arm64-v8a.so 26 | /module/out/output.zip 27 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google { 4 | content { 5 | includeGroupByRegex("com\\.android.*") 6 | includeGroupByRegex("com\\.google.*") 7 | includeGroupByRegex("androidx.*") 8 | } 9 | } 10 | mavenCentral() 11 | gradlePluginPortal() 12 | } 13 | } 14 | dependencyResolutionManagement { 15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 16 | repositories { 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | rootProject.name = "jni_trace" 23 | include ':app' 24 | include ':module' 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 16 | -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/java_vm.cpp: -------------------------------------------------------------------------------- 1 | //#include 2 | //#include "../dlfc/dlfcn_nougat.h" 3 | // 4 | //JNIEnv *g_tls_jnienv = nullptr; 5 | // 6 | //JNIEnv *get_jni_env() { 7 | // if (!g_tls_jnienv) { 8 | // elf_info *dlctx = fake_dlopen("libandroid_runtime.so", 0); 9 | // if (dlctx) { 10 | // typedef JNIEnv *(*getJNIEnv_t)(); 11 | // getJNIEnv_t getJNIEnv = (getJNIEnv_t) fake_dlsym(dlctx, 12 | // "_ZN7android14AndroidRuntime9getJNIEnvEv"); 13 | // if (getJNIEnv) 14 | // g_tls_jnienv = getJNIEnv(); 15 | // fake_dlclose(dlctx); 16 | // } 17 | // } 18 | // return g_tls_jnienv; 19 | //} -------------------------------------------------------------------------------- /module/magisk_module/META-INF/com/google/android/update-binary: -------------------------------------------------------------------------------- 1 | #!/sbin/sh 2 | 3 | ################# 4 | # Initialization 5 | ################# 6 | 7 | umask 022 8 | 9 | # echo before loading util_functions 10 | ui_print() { echo "$1"; } 11 | 12 | require_new_magisk() { 13 | ui_print "*******************************" 14 | ui_print " Please install Magisk v20.4+! " 15 | ui_print "*******************************" 16 | exit 1 17 | } 18 | 19 | ######################### 20 | # Load util_functions.sh 21 | ######################### 22 | 23 | OUTFD=$2 24 | ZIPFILE=$3 25 | 26 | mount /data 2>/dev/null 27 | 28 | [ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk 29 | . /data/adb/magisk/util_functions.sh 30 | [ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk 31 | 32 | install_module 33 | exit 0 -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /module/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/wait_group.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using std::atomic; 9 | using std::mutex; 10 | using std::shared_ptr; 11 | using std::condition_variable; 12 | 13 | class _WaitGroup { 14 | public: 15 | _WaitGroup() : count(0) {} 16 | 17 | void add(int n = 1) { 18 | count += n; 19 | } 20 | 21 | void done() { 22 | --count; 23 | if (count == 0) { cv.notify_all(); } 24 | } 25 | 26 | void wait() { 27 | std::unique_lock lock(mtx); 28 | while (count > 0) { 29 | cv.wait(lock); 30 | } 31 | } 32 | 33 | private: 34 | std::atomic count; 35 | std::mutex mtx; 36 | std::condition_variable cv; 37 | }; 38 | 39 | using WaitGroup = shared_ptr<_WaitGroup>; -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /module/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.28.0+) 2 | project("jni_trace") 3 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/${ANDROID_ABI}") 4 | set(CXX_FLAGS "${CXX_FLAGS} -std=c++20 -fno-exceptions -fno-rtti -fvisibility=hidden -fvisibility-inlines-hidden") 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_FLAGS}") 6 | 7 | enable_language(ASM) 8 | 9 | find_library(log-lib log) 10 | find_package(cxx REQUIRED CONFIG) 11 | link_libraries(cxx::cxx) 12 | 13 | add_subdirectory(third/log2file) 14 | add_subdirectory(./third) 15 | 16 | file(GLOB analyse_src 17 | "*.h" 18 | "*.hpp" 19 | "*.cpp" 20 | ) 21 | 22 | add_library( 23 | analyse 24 | SHARED 25 | ${analyse_src} 26 | ) 27 | 28 | target_link_libraries( 29 | analyse 30 | libutils 31 | log2file 32 | ${log-lib} 33 | ) -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | agp = "8.8.2" 3 | junit = "4.13.2" 4 | junitVersion = "1.2.1" 5 | espressoCore = "3.6.1" 6 | appcompat = "1.7.1" 7 | material = "1.12.0" 8 | 9 | [libraries] 10 | junit = { group = "junit", name = "junit", version.ref = "junit" } 11 | ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } 12 | espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } 13 | appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } 14 | material = { group = "com.google.android.material", name = "material", version.ref = "material" } 15 | 16 | cxx = { module = "org.lsposed.libcxx:libcxx", version = "27.0.12077973" } 17 | 18 | [plugins] 19 | android-application = { id = "com.android.application", version.ref = "agp" } 20 | android-library = { id = "com.android.library", version.ref = "agp" } 21 | 22 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.android.application) 3 | } 4 | 5 | android { 6 | namespace 'com.jni_trace' 7 | compileSdk 35 8 | 9 | defaultConfig { 10 | applicationId "com.jni_trace" 11 | minSdk 27 12 | targetSdk 35 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_11 27 | targetCompatibility JavaVersion.VERSION_11 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation libs.appcompat 33 | implementation libs.material 34 | implementation project(path: ':module') 35 | } -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/map_helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class FileMapHelper { 13 | int fd; 14 | struct stat sb; 15 | void *mapped; 16 | 17 | void *MapFile(const std::string &filePath) { 18 | if ((fd = open(filePath.c_str(), O_RDWR)) < 0) { 19 | return nullptr; 20 | } 21 | 22 | if ((fstat(fd, &sb)) == -1) { 23 | } 24 | 25 | mapped = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 26 | if (mapped == (void *) -1) { 27 | } 28 | return mapped; 29 | } 30 | 31 | bool FlushMap() { 32 | return msync((void *) mapped, sb.st_size, MS_SYNC) == -1; 33 | } 34 | 35 | bool Close() { 36 | return munmap((void *) mapped, sb.st_size)) == -1 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/stack.asm: -------------------------------------------------------------------------------- 1 | .text 2 | .arch arm64v8a 3 | .global get_call_stack 4 | .type get_call_stack,%function 5 | get_call_stack: 6 | stp x29, x30, [sp, #-0x10]! // 保存帧指针和返回地址 7 | stp x19, x20, [sp, #-0x10]! // 保存被调用者寄存器 8 | stp x21, x22, [sp, #-0x10]! // 保存被调用者寄存器 9 | 10 | mov x19, x0 // X19 保存数组地址 11 | mov x20, #0 // 初始化计数器 12 | mov x21, x29 // 当前帧指针 13 | mov x22, x30 // 当前返回地址 14 | 15 | loop: 16 | cmp x20, #10 17 | bge done // 达到 10 层,退出 18 | 19 | mov x0, x21 20 | bl check_stack 21 | cbz x0, done // 内存不可读,退出 22 | 23 | xpaclri // 清除 PAC 24 | 25 | str x22, [x19, x20, lsl #3] // 保存返回地址到 stack[i] 26 | ldr x21, [x21] // 获取上一帧指针 27 | ldr x22, [x21, #8] // 获取上一帧返回地址 28 | 29 | add x20, x20, #1 30 | b loop 31 | 32 | done: 33 | mov x0, x20 34 | ldp x21, x22, [sp], 0x10 35 | ldp x19, x20, [sp], 0x10 36 | ldp x29, x30, [sp], 0x10 37 | ret 38 | .end -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/meminfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct MemInfo { 6 | int MemTotal; 7 | int MemFree; 8 | int MemAvailable; 9 | int Buffers; 10 | int Cached; 11 | int SwapCached; 12 | int Active; 13 | int Inactive; 14 | int Active_anon; 15 | int Inactive_anon; 16 | int Active_file; 17 | int Inactive_file; 18 | int Unevictable; 19 | int Mlocked; 20 | int SwapTotal; 21 | int SwapFree; 22 | int Dirty; 23 | int Writeback; 24 | int AnonPages; 25 | int Mapped; 26 | int Shmem; 27 | int Slab; 28 | int SReclaimable; 29 | int SUnreclaim; 30 | int KernelStack; 31 | int PageTables; 32 | int NFS_Unstable; 33 | int Bounce; 34 | int WritebackTmp; 35 | int CommitLimit; 36 | int Committed_AS; 37 | int VmallocTotal; 38 | int VmallocUsed; 39 | int VmallocChunk; 40 | int CmaTotal; 41 | int CmaFree; 42 | }; 43 | 44 | bool GetMemInfo(const std::string &path, MemInfo *memInfo); 45 | 46 | bool GetMemInfo(const std::string &path, MemInfo *memInfo, int maxLine); 47 | 48 | 49 | -------------------------------------------------------------------------------- /module/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | apply from: "./make_plug.gradle" 6 | 7 | android { 8 | namespace 'com.jni_trace' 9 | compileSdk 34 10 | ndkVersion '27.0.12077973' 11 | defaultConfig { 12 | minSdk 21 13 | targetSdk 35 14 | externalNativeBuild { 15 | cmake { 16 | cppFlags '-std=c++20' 17 | arguments '-DANDROID_STL=none' 18 | } 19 | } 20 | ndk { 21 | // abiFilters "armeabi-v7a" 22 | abiFilters "arm64-v8a" 23 | // abiFilters "x86_64" 24 | } 25 | } 26 | externalNativeBuild { 27 | cmake { 28 | path file('src/main/cpp/CMakeLists.txt') 29 | version "3.28.0+" 30 | } 31 | } 32 | buildFeatures { 33 | prefab true 34 | } 35 | tasks.configureEach { task -> 36 | if (task.name == 'copyDebugJniLibsProjectOnly' || task.name == 'assembleRelease') { 37 | task.doLast { 38 | project.BuildMagiskPlug() 39 | } 40 | } 41 | } 42 | } 43 | 44 | dependencies { 45 | implementation(libs.cxx) 46 | } -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/log.cpp: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | #include "utils.h" 3 | 4 | namespace xbyl { 5 | log defaultLog; 6 | 7 | void init_log(const string &tag, adapter *adapt) { 8 | if (adapt == nullptr) { 9 | defaultLog.enabled = false; 10 | return; 11 | } 12 | defaultLog.adapters.clear(); 13 | defaultLog.set_adapt(adapt); 14 | defaultLog.setTag(tag); 15 | } 16 | 17 | // void init_log(const string &tag, vector> &adapt) { 18 | // defaultLog.adapters.clear(); 19 | // defaultLog.adapters = std::move(adapt); 20 | // defaultLog.setTag(tag); 21 | // } 22 | 23 | void init_log(const string &tag) { 24 | defaultLog.setTag(tag); 25 | } 26 | 27 | void disable_adb_log() { 28 | for (const std::unique_ptr &item: defaultLog.adapters) { 29 | if (item->type == log_adapt::use_adb) { 30 | item->enabled = false; 31 | } 32 | } 33 | } 34 | 35 | void enable_adb_log() { 36 | for (const std::unique_ptr &item: defaultLog.adapters) { 37 | if (item->type == log_adapt::use_adb) { 38 | item->enabled = true; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. For more details, visit 12 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Enables namespacing of each library's R class so that its R class includes only the 19 | # resources declared in the library itself and none from the library's dependencies, 20 | # thereby reducing the size of the R class for that library 21 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/macro_helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define Symbol(lib, x) (decltype(lib##_##x))DobbySymbolResolver(#lib".so",#x) 4 | #define Symbol2(type, lib, name) (decltype(type))DobbySymbolResolver(lib,name) 5 | #define IsBit64 __x86_64__ || __arm64__ || __aarch64__ 6 | #define IsBit32 __i386__ || __arm__ 7 | #define IsArm64 __arm64__ || __aarch64__ 8 | #define IsArm32 __arm__ 9 | #define IsX86_64 __x86_64__ 10 | #define IsX86_32 __i386__ 11 | #define IsX86 __x86_64__||__i386__ 12 | #define IsArm __arm__||__arm64__||__aarch64__ 13 | 14 | #define InlineHook(lib, name, exportName) { \ 15 | void* tmp_##name = DobbySymbolResolver(lib,#exportName); \ 16 | if(tmp_##name == nullptr){ \ 17 | loge("DobbySymbolResolver %s -> %s failed!!!",lib,#name); \ 18 | } \ 19 | int tmp2_##name = DobbyHook(tmp_##name,(dobby_dummy_func_t) Hook_##name,(dobby_dummy_func_t*) &p##name); \ 20 | if(tmp2_##name== -1) { \ 21 | loge("DobbyHook %s -> %s failed!!!",lib,#name); \ 22 | } \ 23 | } -------------------------------------------------------------------------------- /module/src/main/cpp/third/log2file/app_file_writer.cpp: -------------------------------------------------------------------------------- 1 | #include "app_file_writer.h" 2 | 3 | app_file_writer logWriter; 4 | 5 | string getPkgName(); 6 | 7 | string get_extern_data_path() { 8 | return "/sdcard/Android/data/" + getPkgName(); 9 | } 10 | 11 | string get_data_path() { 12 | return "/data/data/" + getPkgName(); 13 | } 14 | 15 | void log2file(const string &log) { 16 | if (!logWriter.is_open()) { 17 | logWriter.open(get_data_path(), "log"); 18 | } 19 | logWriter.write2file(log); 20 | } 21 | 22 | void log2file(const char *fmt, ...) { 23 | va_list ap; 24 | va_start(ap, fmt); 25 | string result = xbyl::format_string(fmt, ap); 26 | va_end(ap); 27 | log2file(result); 28 | } 29 | 30 | extern "C" 31 | JNIEXPORT void JNICALL 32 | Java_com_android_analyse_hook_Native_nativeLog(JNIEnv *env, jclass clazz, jint level, jstring msg) { 33 | log2file(string(lsplant::JUTFString(env, msg).get())); 34 | } 35 | 36 | extern "C" 37 | JNIEXPORT jlong JNICALL 38 | Java_com_android_analyse_hook_Native_nativeOpenFile(JNIEnv *env, jclass clazz, jstring name) { 39 | return (int64_t) new app_file_writer(get_extern_data_path(), lsplant::JUTFString(env, name)); 40 | } 41 | 42 | extern "C" 43 | JNIEXPORT void JNICALL 44 | Java_com_android_analyse_hook_Native_nativeWrite__JLjava_lang_String_2(JNIEnv *env, jclass clazz, 45 | jlong handle, jstring data) { 46 | auto obj = (app_file_writer *) handle; 47 | obj->write2file(lsplant::JUTFString(env, data)); 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /module/src/main/cpp/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "third/utils/utils.h" 6 | #include "third/utils/log.h" 7 | #include "third/utils/linux_helper.h" 8 | #include "zygisk.hpp" 9 | 10 | using namespace std; 11 | using zygisk::Api; 12 | using zygisk::AppSpecializeArgs; 13 | using zygisk::ServerSpecializeArgs; 14 | 15 | 16 | class MyModule : public zygisk::ModuleBase { 17 | public: 18 | void onLoad(Api *api, JNIEnv *env) override { 19 | this->api = api; 20 | this->env = env; 21 | } 22 | 23 | void preAppSpecialize(AppSpecializeArgs *args) override { 24 | const char *process = env->GetStringUTFChars(args->nice_name, nullptr); 25 | this->process = process; 26 | env->ReleaseStringUTFChars(args->nice_name, process); 27 | } 28 | 29 | void postAppSpecialize(const AppSpecializeArgs *args) { 30 | allowPkg = ReadFile("/data/pkg"); 31 | if (allowPkg.empty()) { 32 | loge("pkg empty %d", errno); 33 | return; 34 | } 35 | allowPkg = replace_all(allowPkg, "\n", ""); 36 | allowPkg = replace_all(allowPkg, "\r", ""); 37 | if (process.find(allowPkg) == -1) { 38 | return; 39 | } 40 | auto handle = dlopen("/data/libanalyse.so", RTLD_NOW); 41 | if (handle != nullptr) { 42 | logi("inject to %s", process.c_str()); 43 | } else { 44 | loge("inject to %s error %d", process.c_str(), errno); 45 | return; 46 | } 47 | void *inject_entry = dlsym(handle, "inject_entry"); 48 | if (!inject_entry) { 49 | loge("inject_entry error %d", errno); 50 | return; 51 | } 52 | ((void (*)(JNIEnv *, const char *)) inject_entry)(env, this->process.c_str()); 53 | } 54 | 55 | private: 56 | Api *api; 57 | JNIEnv *env; 58 | string process; 59 | string allowPkg; 60 | }; 61 | 62 | static void companion_handler(int i) { 63 | } 64 | 65 | // Register our module class and the companion handler function 66 | REGISTER_ZYGISK_MODULE(MyModule) 67 | 68 | REGISTER_ZYGISK_COMPANION(companion_handler) -------------------------------------------------------------------------------- /module/make_plug.gradle: -------------------------------------------------------------------------------- 1 | boolean deleteFile(File file) { 2 | if (file == null || !file.exists()) { 3 | return false 4 | } 5 | File[] files = file.listFiles(); 6 | for (File f : files) { 7 | if (f.isDirectory()) { 8 | deleteFile(f) 9 | } else { 10 | f.delete() 11 | } 12 | } 13 | file.delete() 14 | return true 15 | } 16 | 17 | def copyFile(src, des, name) { 18 | File srcFile = file(src) 19 | File destDir = file(des) 20 | if (!srcFile.exists()) { 21 | println("$srcFile not exists!") 22 | return 23 | } 24 | if (destDir.exists() && !destDir.isDirectory()) { 25 | throw new GradleException("$destDir not is a directory!") 26 | } 27 | 28 | copy { 29 | from srcFile 30 | into destDir 31 | rename { String filename -> 32 | return name == null ? filename : name 33 | } 34 | } 35 | } 36 | 37 | def copyFile(src, des) { 38 | copyFile(src, des, null) 39 | } 40 | 41 | void CpyPlugFile() { 42 | String base32Path = "${projectDir}/libs/armeabi-v7a/" 43 | String base64Path = "${projectDir}/libs/arm64-v8a/" 44 | String so32Name = "armeabi-v7a.so" 45 | String so64Name = "arm64-v8a.so" 46 | 47 | copyFile(base64Path + "libanalyse.so", "${projectDir}/magisk_module/zygisk", so64Name) 48 | copyFile(base32Path + "libanalyse.so", "${projectDir}/magisk_module/zygisk", so32Name) 49 | } 50 | 51 | tasks.register('ZipPlug', Zip) { 52 | archiveFileName = "output.zip" 53 | destinationDirectory = file("${projectDir}/out/") 54 | from file("${projectDir}/magisk_module/") 55 | } 56 | 57 | void executeTask(Task task) { 58 | task.taskDependencies.getDependencies(task).each { 59 | subTask -> executeTask(subTask) 60 | } 61 | task.actions.each { it.execute(task) } 62 | } 63 | 64 | void BuildMagiskPlug() { 65 | try { 66 | println "start make magisk plug" 67 | file("${projectDir}/out").mkdirs() 68 | CpyPlugFile() 69 | executeTask(ZipPlug) 70 | println "make magisk plug finish" 71 | } catch (Throwable e) { 72 | println("make plug error: " + e) 73 | e.printStackTrace() 74 | } 75 | } 76 | 77 | ext { 78 | BuildMagiskPlug = this.&BuildMagiskPlug 79 | } 80 | -------------------------------------------------------------------------------- /module/src/main/cpp/third/log2file/app_file_writer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../utils/jni_helper.hpp" 10 | #include "../utils/log.h" 11 | 12 | using namespace std; 13 | 14 | void log2file(const string &log); 15 | 16 | void log2file(const char *fmt, ...); 17 | 18 | class app_file_writer { 19 | public: 20 | app_file_writer(const string &dataPath, const string &name) { 21 | open(dataPath, name); 22 | } 23 | 24 | app_file_writer() = default; 25 | 26 | ~app_file_writer() { 27 | if (file == nullptr) { 28 | return; 29 | } 30 | fclose(file); 31 | } 32 | 33 | bool is_open() { 34 | return file != nullptr; 35 | } 36 | 37 | bool open(const string &dataPath, const string &name) { 38 | lock_guard guard(fileLock); 39 | if (is_open()) { 40 | return true; 41 | } 42 | this->appDataPath = dataPath; 43 | this->fileName = name; 44 | this->myPid = getpid(); 45 | file = open_file(dataPath, name); 46 | return file != nullptr; 47 | } 48 | 49 | void write2file(const char *data, int len) { 50 | lock_guard guard(fileLock); 51 | check_process(); 52 | if (len > 0 && file != nullptr) { 53 | fwrite(data, 1, len, file); 54 | fflush(file); 55 | } 56 | } 57 | 58 | void write2file(const string &data) { 59 | lock_guard guard(fileLock); 60 | check_process(); 61 | if (data.size() > 0 && file != nullptr) { 62 | fwrite(data.c_str(), 1, data.size(), file); 63 | fwrite("\n", 1, 1, file); 64 | fflush(file); 65 | } 66 | } 67 | 68 | private: 69 | int myPid{}; 70 | string appDataPath; 71 | string fileName; 72 | FILE *file{}; 73 | mutex fileLock; 74 | 75 | void check_process() { 76 | if (myPid == getpid()) { 77 | return; 78 | } 79 | LOGI("log pid change: %d -> %d", myPid, getpid()); 80 | myPid = getpid(); 81 | file = open_file(this->appDataPath, this->fileName); 82 | } 83 | 84 | public: 85 | static FILE *open_file(const string &appDataPath, const string &name) { 86 | srandom(::time(nullptr) + getpid()); 87 | char path[256]; 88 | snprintf(path, sizeof(path), "%s/%s_%d_%ld", appDataPath.c_str(), name.c_str(), getpid(), random()); 89 | FILE *file = fopen(path, "wb"); 90 | if (!file) { 91 | LOGI("analyse open log file %s error: %d", path, errno); 92 | return nullptr; 93 | } 94 | return file; 95 | } 96 | }; -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/thread_poool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class ThreadPool { 15 | public: 16 | ThreadPool(size_t); 17 | 18 | template 19 | auto enqueue(F &&f, Args &&... args) 20 | -> std::future::type>; 21 | 22 | ~ThreadPool(); 23 | 24 | size_t size() const { return workers.size(); } 25 | 26 | private: 27 | // need to keep track of threads so we can join them 28 | std::vector workers; 29 | // the task queue 30 | std::queue > tasks; 31 | 32 | // synchronization 33 | std::mutex queue_mutex; 34 | std::condition_variable condition; 35 | bool stop; 36 | }; 37 | 38 | // the constructor just launches some amount of workers 39 | inline ThreadPool::ThreadPool(size_t threads) 40 | : stop(false) { 41 | for (size_t i = 0; i < threads; ++i) 42 | workers.emplace_back( 43 | [this] { 44 | for (;;) { 45 | std::function task; 46 | { 47 | std::unique_lock lock(this->queue_mutex); 48 | this->condition.wait(lock, 49 | [this] { 50 | return this->stop || !this->tasks.empty(); 51 | }); 52 | if (this->stop && this->tasks.empty()) 53 | return; 54 | task = std::move(this->tasks.front()); 55 | this->tasks.pop(); 56 | } 57 | task(); 58 | } 59 | } 60 | ); 61 | } 62 | 63 | // add new work item to the pool 64 | template 65 | auto ThreadPool::enqueue(F &&f, Args &&... args) 66 | -> std::future::type> { 67 | using return_type = typename std::result_of::type; 68 | 69 | auto task = std::make_shared >( 70 | std::bind(std::forward(f), std::forward(args)...) 71 | ); 72 | 73 | std::future res = task->get_future(); 74 | { 75 | std::unique_lock lock(queue_mutex); 76 | 77 | // don't allow enqueueing after stopping the pool 78 | assert(stop == false); 79 | 80 | tasks.emplace([task]() { (*task)(); }); 81 | } 82 | condition.notify_one(); 83 | return res; 84 | } 85 | 86 | // the destructor joins all threads 87 | inline ThreadPool::~ThreadPool() { 88 | { 89 | std::unique_lock lock(queue_mutex); 90 | stop = true; 91 | } 92 | condition.notify_all(); 93 | for (std::thread &worker: workers) 94 | worker.join(); 95 | } -------------------------------------------------------------------------------- /module/magisk_module/customize.sh: -------------------------------------------------------------------------------- 1 | ########################################################################################## 2 | # 3 | # MMT Extended Config Script 4 | # 5 | ########################################################################################## 6 | 7 | ########################################################################################## 8 | # Config Flags 9 | ########################################################################################## 10 | 11 | # Uncomment and change 'MINAPI' and 'MAXAPI' to the minimum and maximum android version for your mod 12 | # Uncomment DYNLIB if you want libs installed to vendor for oreo+ and system for anything older 13 | # Uncomment PARTOVER if you have a workaround in place for extra partitions in regular magisk install (can mount them yourself - you will need to do this each boot as well). If unsure, keep commented 14 | # Uncomment PARTITIONS and list additional partitions you will be modifying (other than system and vendor), for example: PARTITIONS="/odm /product /system_ext" 15 | #MINAPI=21 16 | #MAXAPI=25 17 | #DYNLIB=true 18 | #PARTOVER=true 19 | #PARTITIONS="" 20 | 21 | ########################################################################################## 22 | # Replace list 23 | ########################################################################################## 24 | 25 | # List all directories you want to directly replace in the system 26 | # Check the documentations for more info why you would need this 27 | 28 | # Construct your list in the following format 29 | # This is an example 30 | REPLACE_EXAMPLE=" 31 | /system/app/Youtube 32 | /system/priv-app/SystemUI 33 | /system/priv-app/Settings 34 | /system/framework 35 | " 36 | 37 | # Construct your own list here 38 | REPLACE=" 39 | " 40 | 41 | ########################################################################################## 42 | # Permissions 43 | ########################################################################################## 44 | 45 | set_permissions() { 46 | : # Remove this if adding to this function 47 | 48 | # Note that all files/folders in magisk module directory have the $MODPATH prefix - keep this prefix on all of your files/folders 49 | # Some examples: 50 | 51 | # For directories (includes files in them): 52 | # set_perm_recursive (default: u:object_r:system_file:s0) 53 | 54 | # set_perm_recursive $MODPATH/system/lib 0 0 0755 0644 55 | # set_perm_recursive $MODPATH/system/vendor/lib/soundfx 0 0 0755 0644 56 | 57 | # For files (not in directories taken care of above) 58 | # set_perm (default: u:object_r:system_file:s0) 59 | 60 | # set_perm $MODPATH/system/lib/libart.so 0 0 0644 61 | # set_perm /data/local/tmp/file.txt 0 0 644 62 | } 63 | 64 | ########################################################################################## 65 | # MMT Extended Logic - Don't modify anything after this 66 | ########################################################################################## 67 | 68 | SKIPUNZIP=1 69 | unzip -qjo "$ZIPFILE" 'common/functions.sh' -d $TMPDIR >&2 70 | . $TMPDIR/functions.sh 71 | -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/meminfo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "meminfo.h" 4 | 5 | using namespace std; 6 | 7 | enum MemInfoKeyIndex { 8 | MemTotal = 0, 9 | MemFree, 10 | MemAvailable, 11 | Buffers, 12 | Cached, 13 | SwapCached, 14 | Active, 15 | Inactive, 16 | Active_anon, 17 | Inactive_anon, 18 | Active_file, 19 | Inactive_file, 20 | Unevictable, 21 | Mlocked, 22 | SwapTotal, 23 | SwapFree, 24 | Dirty, 25 | Writeback, 26 | AnonPages, 27 | Mapped, 28 | Shmem, 29 | Slab, 30 | SReclaimable, 31 | SUnreclaim, 32 | KernelStack, 33 | PageTables, 34 | NFS_Unstable, 35 | Bounce, 36 | WritebackTmp, 37 | CommitLimit, 38 | Committed_AS, 39 | VmallocTotal, 40 | VmallocUsed, 41 | VmallocChunk, 42 | CmaTotal, 43 | CmaFree, 44 | }; 45 | 46 | map MemInfoOffset = { 47 | {"MemTotal:", MemTotal}, 48 | {"MemFree:", MemFree}, 49 | {"MemAvailable:", MemAvailable}, 50 | {"Buffers:", Buffers}, 51 | {"Cached:", Cached}, 52 | {"SwapCached:", SwapCached}, 53 | {"Active:", Active}, 54 | {"Inactive:", Inactive}, 55 | {"Active(anon):", Active_anon}, 56 | {"Inactive(anon):", Inactive_anon}, 57 | {"Active(file):", Active_file}, 58 | {"Inactive(file):", Inactive_file}, 59 | {"Unevictable:", Unevictable}, 60 | {"Mlocked:", Mlocked}, 61 | {"SwapTotal:", SwapTotal}, 62 | {"SwapFree:", SwapFree}, 63 | {"Dirty:", Dirty}, 64 | {"Writeback:", Writeback}, 65 | {"AnonPages:", AnonPages}, 66 | {"Mapped:", Mapped}, 67 | {"Shmem:", Shmem}, 68 | {"Slab:", Slab}, 69 | {"SReclaimable:", SReclaimable}, 70 | {"SUnreclaim:", SUnreclaim}, 71 | {"KernelStack:", KernelStack}, 72 | {"PageTables:", PageTables}, 73 | {"NFS_Unstable:", NFS_Unstable}, 74 | {"Bounce:", Bounce}, 75 | {"WritebackTmp:", WritebackTmp}, 76 | {"CommitLimit:", CommitLimit}, 77 | {"Committed_AS:", Committed_AS}, 78 | {"VmallocTotal:", VmallocTotal}, 79 | {"VmallocUsed:", VmallocUsed}, 80 | {"VmallocChunk:", VmallocChunk}, 81 | {"CmaTotal:", CmaTotal}, 82 | {"CmaFree:", CmaFree}, 83 | }; 84 | 85 | bool GetMemInfo(const string &path, MemInfo *memInfo) { 86 | return GetMemInfo(path, memInfo, -1); 87 | } 88 | 89 | bool GetMemInfo(const string &path, MemInfo *memInfo, int maxLine) { 90 | memset(memInfo, 0, sizeof(MemInfo)); 91 | FILE *fp; 92 | char buf[128]; 93 | fp = fopen(path.c_str(), "r"); 94 | if (fp == NULL) { 95 | return false; 96 | } 97 | if (maxLine == -1) { 98 | maxLine = 99999; 99 | } 100 | char name[20]; 101 | int value; 102 | while (NULL != fgets(buf, sizeof(buf), fp) && maxLine > 0) { 103 | maxLine--; 104 | sscanf(buf, "%s%d", name, &value); 105 | const auto &item = MemInfoOffset.find(name); 106 | if (item == MemInfoOffset.end()) { 107 | continue; 108 | } 109 | *((int *) memInfo + (int) item->second) = value; 110 | } 111 | fclose(fp); 112 | return true; 113 | } -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/linux_helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "utils.h" 9 | 10 | int pid_by_process_name(char *task_name); 11 | 12 | std::string Fd2Path(int fd); 13 | 14 | bool Mount(const std::string &fakePath, const std::string &targetPath); 15 | 16 | bool UnMount(const std::string &targetPath); 17 | 18 | std::string GetCmdLine(pid_t pid); 19 | 20 | std::string RunCmd(const std::string &strCmd); 21 | 22 | struct MountInfo { 23 | std::string mnt_fsname; 24 | std::string mnt_dir; 25 | std::string mnt_type; 26 | std::string mnt_opts; 27 | int mnt_freq; 28 | int mnt_passno; 29 | }; 30 | 31 | bool get_mount_list(const char *path, std::vector &ret); 32 | 33 | bool copy_file(const std::string &from, const std::string &to); 34 | 35 | struct FilePerm { 36 | std::string ctx; 37 | mode_t st_mode; 38 | gid_t st_gid; 39 | uid_t st_uid; 40 | }; 41 | 42 | bool 43 | SetFilePerm(const std::string &path, const std::string &context, mode_t st_mode, gid_t st_gid, 44 | uid_t st_uid); 45 | 46 | bool GetFilePerm(const std::string &path, FilePerm &perm); 47 | 48 | bool GetFileSelinuxCtx(const std::string &path, std::string &ctx); 49 | 50 | bool remove_dir(const std::string &dir_name); 51 | 52 | unsigned long get_file_size(const std::string &path); 53 | 54 | bool remove_files(const std::string &path); 55 | 56 | bool remove_dir(const string &rootPath, const vector &white, bool deleteRoot); 57 | 58 | bool UnMount2(const char *target, int flags); 59 | 60 | bool UnMount(const char *target); 61 | 62 | bool Mount(const char *source, const char *target, const char *fs_type, unsigned long flags, 63 | const void *data); 64 | 65 | bool mkdir_recursive(const std::string &path, const mode_t mode); 66 | 67 | enum class FileType { 68 | file, 69 | dir, 70 | link2file, 71 | link2dir, 72 | unknown, 73 | }; 74 | 75 | FileType get_path_type(const string &path, struct stat *statFile, struct stat *statLink); 76 | 77 | bool 78 | traverse_path(const std::string &rootPath, 79 | const std::function &callback, 81 | const vector &whitePath = vector()); 82 | 83 | template 84 | bool singleCase(const string &filePath, bool *isSingle, Func &&callback, Args &&... args) { 85 | int lockfile = open(filePath.c_str(), O_CREAT | O_RDWR, 0666); 86 | if (lockfile == -1) { 87 | if (isSingle) { 88 | *isSingle = false; 89 | } 90 | return false; 91 | } 92 | defer([&]() { 93 | close(lockfile); 94 | }); 95 | flock fl{}; 96 | fl.l_type = F_WRLCK; 97 | fl.l_start = 0; 98 | fl.l_whence = SEEK_SET; 99 | fl.l_len = 0; 100 | if (fcntl(lockfile, F_SETLK, &fl) == -1) { 101 | if (isSingle) { 102 | *isSingle = false; 103 | } 104 | return false; 105 | } 106 | defer([&]() { 107 | fl.l_type = F_UNLCK; 108 | fcntl(lockfile, F_SETLK, &fl); 109 | }); 110 | if (isSingle) { 111 | *isSingle = true; 112 | } 113 | callback(std::forward(args)...); 114 | return true; 115 | } 116 | 117 | struct MapsInfo { 118 | void *region_start; 119 | void *region_end; 120 | void *region_offset; 121 | std::string permissions; 122 | std::string path; 123 | }; 124 | 125 | class MapsHelper { 126 | public: 127 | vector mapsInfo; 128 | 129 | int refresh(const string &libPath = "", const string &wantPerm = ""); 130 | 131 | int refresh_reg(const string &libPath = "", const string &wantPerm = ""); 132 | 133 | void *get_module_base(const string &libPath); 134 | void *get_module_base_reg(const string &libPath); 135 | 136 | void *get_module_end(const string &libPath); 137 | void *get_module_end_reg(const string &libPath); 138 | private: 139 | void *get_module_base(const string &libPath, bool is_reg); 140 | void *get_module_end(const string &libPath, bool is_reg); 141 | 142 | bool get_process_maps(bool is_reg, const string &libPath = "", const string &wantPerm = ""); 143 | }; 144 | -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using std::string; 15 | using std::mutex; 16 | using std::vector; 17 | 18 | #define XbylLogTag "analyse_log" 19 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, XbylLogTag, __VA_ARGS__) 20 | 21 | struct auto_lock { 22 | mutex *lock; 23 | 24 | auto_lock(mutex *lock) { 25 | this->lock = lock; 26 | this->lock->lock(); 27 | } 28 | 29 | ~auto_lock() { 30 | this->lock->unlock(); 31 | } 32 | }; 33 | 34 | #define defer_name(name, count) name##count##private 35 | #define defer_link(class, count) defer_name(class, count) 36 | #define defer(expr) AutoDefer defer_link(AutoDefer, __COUNTER__){ expr} 37 | 38 | 39 | template 40 | struct AutoDefer { 41 | private: 42 | T _func = nullptr; 43 | public: 44 | AutoDefer(T &&func) : _func(std::move(func)) {} 45 | 46 | AutoDefer(T &func) : _func(func) {} 47 | 48 | ~AutoDefer() { 49 | _func(); 50 | } 51 | }; 52 | 53 | struct AutoClose { 54 | int _fd = -1; 55 | FILE *file = nullptr; 56 | private: 57 | AutoClose() {}; 58 | public: 59 | AutoClose(int fd) { 60 | _fd = fd; 61 | } 62 | 63 | AutoClose(FILE *fd) { 64 | file = fd; 65 | } 66 | 67 | ~AutoClose() { 68 | if (_fd != -1) 69 | close(_fd); 70 | if (file != nullptr) 71 | fclose(file); 72 | } 73 | }; 74 | 75 | 76 | char *str2hex(const char *str, int str_len, char *hex, int buf_len); 77 | 78 | char *hex2str(const char *hex, int hex_len, char *str, int buf_len); 79 | 80 | vector string_split(const string &str, const string &pattern); 81 | 82 | string replace_all(const string &str, const string &old_value, const string &new_value); 83 | 84 | string get_uuid(); 85 | 86 | int64_t get_time(); 87 | 88 | string time_to_string(int64_t tick); 89 | 90 | int64_t string_to_time(const string &time_str, const string &fmt); 91 | 92 | bool WritFile(const char *path, const char *buf, int len); 93 | 94 | int gen_number(int min, int max); 95 | 96 | float gen_double(float min, float max); 97 | 98 | std::string gen_hexstr(int len); 99 | 100 | std::string gen_uuid(); 101 | 102 | extern const char *CharSet_123; 103 | extern const char *CharSet_ABC; 104 | extern const char *CharSet_abc; 105 | extern const char *CharSet_hex; 106 | extern const char *CharSet_all; 107 | 108 | std::string gen_strabc(int len); 109 | 110 | std::string gen_strABC(int len); 111 | 112 | std::string gen_strABC123(int len); 113 | 114 | std::string gen_str123(int len); 115 | 116 | string gen_strall(int len); 117 | 118 | string gen_str(const char *charSet, int len); 119 | 120 | std::string to_upper(const string &str); 121 | 122 | std::string to_lower(const std::string &str); 123 | 124 | std::string ReadFile(const std::string &path); 125 | 126 | bool ReadFile(const std::string &path, char **data, int *len); 127 | 128 | namespace xbyl { 129 | std::string format_string(const string fmt, ...); 130 | 131 | std::string format_string(const char *fmt, va_list ap); 132 | } 133 | 134 | long get_system_time_nanosecond(); // 纳秒 135 | long get_system_time_microsecond(); // 微秒 136 | long get_system_time_millisecond(); // 毫秒 137 | 138 | std::string mid_string(const std::string &src, const std::string &start, const std::string &end); 139 | 140 | int ReadPkgGid(const string &pkgName); 141 | 142 | bool ReadAllPackagesGid(vector &gids); 143 | 144 | string ReadPkgDirSelinuxCtx(const string &pkgName); 145 | 146 | int 147 | TraversePackagesList(std::function &)> callback); 148 | 149 | bool check_memory_readable(void *addr); 150 | 151 | void gen_hex(int len, char *result); 152 | 153 | string get_packet_name(); 154 | 155 | struct Stack { 156 | std::string name; 157 | void *offset; 158 | }; 159 | 160 | vector GetStackInfo(int num, ...); 161 | 162 | //max 10 163 | extern "C" int get_call_stack(void *p); 164 | 165 | bool check_mem(void *p); 166 | 167 | extern "C" bool check_stack(void *p); 168 | 169 | string stack2str(const vector &stack); 170 | 171 | 172 | 173 | extern inline std::vector GetStackInfo() __attribute__((always_inline)) { 174 | std::vector frame; 175 | void *p[10] = {0}; 176 | int count = get_call_stack(p); 177 | for (int i = 0; i < count; i++) { 178 | Dl_info info{}; 179 | void *addr = p[i]; 180 | if (dladdr(addr, &info) != 0) { 181 | frame.push_back({ 182 | info.dli_fname, 183 | (void *) ((uint64_t) addr - (uint64_t) info.dli_fbase) 184 | }); 185 | } else { 186 | frame.push_back({ 187 | "unknow", 188 | (void *) ((uint64_t) addr - (uint64_t) info.dli_fbase) 189 | }); 190 | } 191 | } 192 | return frame; 193 | } 194 | -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "utils.h" 15 | 16 | using std::string; 17 | 18 | 19 | #if defined(__arm64__) || defined(__aarch64__) 20 | #define XbylLogTagArch "Arm64" 21 | #elif defined(__arm__ ) 22 | #define XbylLogTagArch "Arm32" 23 | #elif defined(__i386__ ) 24 | #define XbylLogTagArch "X86_32" 25 | #elif defined(__x86_64__ ) 26 | #define XbylLogTagArch "X86_64" 27 | #endif 28 | 29 | #define logd(fmt, ...) xbyl::defaultLog.do_log(xbyl::log_level::DEBUG ,fmt, ##__VA_ARGS__) 30 | #define logi(fmt, ...) xbyl::defaultLog.do_log(xbyl::log_level::INFO ,fmt, ##__VA_ARGS__) 31 | #define logw(fmt, ...) xbyl::defaultLog.do_log(xbyl::log_level::WARN ,fmt, ##__VA_ARGS__) 32 | #define loge(fmt, ...) xbyl::defaultLog.do_log(xbyl::log_level::ERROR ,fmt, ##__VA_ARGS__) 33 | 34 | #define XbylLogTag "analyse_log" 35 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, XbylLogTag, __VA_ARGS__) 36 | 37 | namespace xbyl { 38 | using func_log = void (*)(const string &msg); 39 | 40 | using std::mutex; 41 | 42 | static string level_string[]{ 43 | "Unknown", 44 | "Default", 45 | "Verbose", 46 | "Debug", 47 | "Info", 48 | "Warn", 49 | "Error", 50 | "Fatal", 51 | "Silent", 52 | }; 53 | 54 | enum class log_adapt { 55 | use_file, 56 | use_printf, 57 | use_custom_func, 58 | use_adb, 59 | use_none, 60 | use_client, 61 | }; 62 | 63 | enum class log_level { 64 | UNKNOWN = ANDROID_LOG_UNKNOWN, 65 | DEFAULT = ANDROID_LOG_DEFAULT, 66 | VERBOSE = ANDROID_LOG_VERBOSE, 67 | DEBUG = ANDROID_LOG_DEBUG, 68 | INFO = ANDROID_LOG_INFO, 69 | WARN = ANDROID_LOG_WARN, 70 | ERROR = ANDROID_LOG_ERROR, 71 | FATAL = ANDROID_LOG_FATAL, 72 | SILENT = ANDROID_LOG_SILENT, 73 | }; 74 | 75 | 76 | class adapter { 77 | public: 78 | std::mutex lock; 79 | log_adapt type; 80 | bool enabled = true; 81 | 82 | virtual void output_log(log_level level, const string &buf) = 0; 83 | }; 84 | 85 | class adapter_file : public adapter { 86 | FILE *log_file; 87 | 88 | public: 89 | adapter_file(const string &path) { 90 | type = log_adapt::use_file; 91 | log_file = fopen(path.c_str(), "w+"); 92 | if (log_file == nullptr) { 93 | LOGI("log open file %s error: %d", path.c_str(), errno); 94 | } 95 | } 96 | 97 | ~adapter_file() { 98 | if (log_file) { 99 | fclose(log_file); 100 | } 101 | log_file = nullptr; 102 | } 103 | 104 | virtual void output_log(log_level, const string &buf) { 105 | lock.lock(); 106 | fwrite(buf.c_str(), buf.size(), 1, log_file); 107 | fflush(log_file); 108 | lock.unlock(); 109 | } 110 | }; 111 | 112 | class adapter_printf : public adapter { 113 | public: 114 | adapter_printf() { 115 | type = log_adapt::use_printf; 116 | } 117 | 118 | virtual void output_log(log_level, const string &buf) { 119 | printf("%s\n", buf.c_str()); 120 | } 121 | }; 122 | 123 | class adapter_adb : public adapter { 124 | public: 125 | adapter_adb() { 126 | type = log_adapt::use_adb; 127 | } 128 | 129 | virtual void output_log(log_level level, const string &buf) { 130 | __android_log_write((int) level, XbylLogTag, buf.c_str()); 131 | } 132 | }; 133 | 134 | class adapter_custom : public adapter { 135 | func_log flog; 136 | public: 137 | adapter_custom() { 138 | type = log_adapt::use_client; 139 | } 140 | 141 | virtual void output_log(log_level level, const string &buf) { 142 | 143 | } 144 | }; 145 | 146 | class log { 147 | public: 148 | vector> adapters; 149 | string tag = XbylLogTag "-Native" XbylLogTagArch "-"; 150 | bool enabled = true; 151 | 152 | log() { 153 | adapters.push_back(std::make_unique()); 154 | } 155 | 156 | void setTag(const string &_tag) { 157 | tag = XbylLogTag "-Native" XbylLogTagArch "-"; 158 | tag += _tag; 159 | } 160 | 161 | ~log() { 162 | } 163 | 164 | bool set_adapt(adapter *adapt) { 165 | adapters.push_back(std::unique_ptr(adapt)); 166 | return true; 167 | } 168 | 169 | void do_log(log_level level, const char *fmt, ...) { 170 | if (adapters.size() == 0 || !enabled) { 171 | return; 172 | } 173 | 174 | string head; 175 | head += level_string[(int) level]; 176 | head += "\t"; 177 | head += tag; 178 | head += ":\t\t\t"; 179 | 180 | va_list ap; 181 | va_start(ap, fmt); 182 | string result = xbyl::format_string((head + fmt).c_str(), ap); 183 | va_end(ap); 184 | for (const std::unique_ptr &item: adapters) { 185 | if (item->enabled) { 186 | item->output_log(level, result); 187 | } 188 | } 189 | } 190 | }; 191 | 192 | extern log defaultLog; 193 | 194 | void enable_adb_log(); 195 | 196 | void disable_adb_log(); 197 | 198 | void init_log(const string &tag); 199 | 200 | void init_log(const string &tag, adapter *adapt); 201 | 202 | // void init_log(const string &tag, vector> &adapt); 203 | } 204 | 205 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/jni_helper.cpp: -------------------------------------------------------------------------------- 1 | #include "jni_helper.hpp" 2 | 3 | jstring char2jstring(JNIEnv *env, const char *pat) { 4 | //定义java String类 strClass 5 | jclass strClass = (env)->FindClass("java/lang/String"); 6 | //获取String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String 7 | jmethodID ctorID = (env)->GetMethodID(strClass, "", "([BLjava/lang/String;)V"); 8 | //建立byte数组 9 | jbyteArray bytes = (env)->NewByteArray(strlen(pat)); 10 | //将char* 转换为byte数组 11 | (env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte *) pat); 12 | // 设置String, 保存语言类型,用于byte数组转换至String时的参数 13 | jstring encoding = (env)->NewStringUTF("GB2312"); 14 | //将byte数组转换为java String,并输出 15 | return (jstring) (env)->NewObject(strClass, ctorID, bytes, encoding); 16 | } 17 | 18 | jstring string2jstring(JNIEnv *env, const string &str) { 19 | return char2jstring(env, str.c_str()); 20 | } 21 | 22 | string jstring2string(JNIEnv *env, jstring jstr) { 23 | char *rtn = NULL; 24 | jclass clsstring = env->FindClass("java/lang/String"); 25 | jstring strencode = env->NewStringUTF("utf-8"); 26 | jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); 27 | auto barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode); 28 | jsize alen = env->GetArrayLength(barr); 29 | jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE); 30 | if (alen == 0) { 31 | env->ReleaseByteArrayElements(barr, ba, 0); 32 | return string(); 33 | } 34 | rtn = (char *) malloc(alen + 1); 35 | memcpy(rtn, ba, alen); 36 | rtn[alen] = 0; 37 | env->ReleaseByteArrayElements(barr, ba, 0); 38 | std::string stemp(rtn); 39 | free(rtn); 40 | return stemp; 41 | } 42 | 43 | vector get_java_list(JNIEnv *env, jobject obj_list) { 44 | jclass list_cls = env->GetObjectClass(obj_list); 45 | jmethodID list_get = env->GetMethodID(list_cls, "get", "(I)Ljava/lang/Object;"); 46 | jmethodID list_size = env->GetMethodID(list_cls, "size", "()I"); 47 | int len = env->CallIntMethod(obj_list, list_size); 48 | vector ret; 49 | for (int i = 0; i < len; i++) { 50 | auto element = (jstring) (env->CallObjectMethod(obj_list, list_get, i)); 51 | if (element == nullptr) { 52 | continue; 53 | } 54 | ret.push_back(element); 55 | env->DeleteLocalRef(element); 56 | } 57 | return ret; 58 | } 59 | 60 | 61 | 62 | 63 | //struct auto_init_jni_helper { 64 | // auto_init_jni_helper() { 65 | // auto handle = fake_dlopen("libart.so", 0); 66 | // if (handle == nullptr) { 67 | // loge("fake_dlopen libart.so is null!", ""); 68 | // return; 69 | // } 70 | // _ZN3art9ArtMethod12JniShortNameEv = (func_type1) fake_dlsym(handle, 71 | // "_ZN3art9ArtMethod12JniShortNameEv"); 72 | // if (_ZN3art9ArtMethod12JniShortNameEv == nullptr) { 73 | // loge("fake_dlsym _ZN3art9ArtMethod12JniShortNameEv is null!", ""); 74 | // } 75 | // _ZN3art9ArtMethod11JniLongNameEv = (func_type1) fake_dlsym(handle, 76 | // "_ZN3art9ArtMethod11JniLongNameEv"); 77 | // if (_ZN3art9ArtMethod11JniLongNameEv == nullptr) { 78 | // loge("fake_dlsym _ZN3art9ArtMethod11JniLongNameEv is null!", ""); 79 | // } 80 | // _ZN3art9ArtMethod12PrettyMethodEb = (func_type2) fake_dlsym(handle, 81 | // "_ZN3art9ArtMethod12PrettyMethodEb"); 82 | // if (_ZN3art9ArtMethod12PrettyMethodEb == nullptr) { 83 | // loge("fake_dlsym _ZN3art9ArtMethod12PrettyMethodEb is null!", ""); 84 | // } 85 | // fake_dlclose(handle); 86 | // } 87 | //} init_jni_help; 88 | 89 | 90 | 91 | string get_object_class_name(JNIEnv *env, jobject obj) { 92 | //"class [[I" 93 | jclass jc = env->GetObjectClass(obj); 94 | jmethodID toString = env->GetMethodID(jc, "toString", 95 | "()Ljava/lang/String;"); 96 | // if (env->ExceptionCheck()) { 97 | // env->ExceptionDescribe(); 98 | // env->ExceptionClear(); 99 | // } 100 | string ret = jstring2string(env, (jstring) env->CallObjectMethod(jc, toString)); 101 | return ret.substr(ret.find(" ") + 1); 102 | } 103 | 104 | jthrowable clean_exception(JNIEnv *env) { 105 | if (env->ExceptionCheck()) { 106 | jthrowable t; 107 | t = env->ExceptionOccurred(); 108 | env->ExceptionDescribe(); 109 | env->ExceptionClear(); 110 | return t; 111 | } 112 | return nullptr; 113 | } 114 | 115 | jobject get_application(JNIEnv *env) { 116 | jobject application = NULL; 117 | jclass activity_thread_clz = env->FindClass("android/app/ActivityThread"); 118 | if (activity_thread_clz != NULL) { 119 | jmethodID get_Application = env->GetStaticMethodID(activity_thread_clz, 120 | "currentActivityThread", 121 | "()Landroid/app/ActivityThread;"); 122 | if (get_Application != NULL) { 123 | jobject currentActivityThread = env->CallStaticObjectMethod(activity_thread_clz, 124 | get_Application); 125 | jmethodID getal = env->GetMethodID(activity_thread_clz, "getApplication", 126 | "()Landroid/app/Application;"); 127 | application = env->CallObjectMethod(currentActivityThread, getal); 128 | } 129 | return application; 130 | } 131 | return application; 132 | } 133 | 134 | jstring get_package_name(JNIEnv *env) { 135 | jobject context = get_application(env); 136 | if (context == NULL) { 137 | return NULL; 138 | } 139 | jclass activity = env->GetObjectClass(context); 140 | jmethodID methodId_pack = env->GetMethodID(activity, "getPackageName", "()Ljava/lang/String;"); 141 | jstring name_str = static_cast( env->CallObjectMethod(context, methodId_pack)); 142 | return name_str; 143 | } 144 | 145 | int get_sdk_int() { 146 | char buff[256] = {0}; 147 | __system_property_get("ro.build.version.sdk", buff); 148 | return atoi(buff); 149 | } 150 | 151 | string prop_get_string(const string &key) { 152 | char buff[256] = {0}; 153 | __system_property_get(key.c_str(), buff); 154 | return buff; 155 | } 156 | 157 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/java_vm.h: -------------------------------------------------------------------------------- 1 | //#pragma once 2 | // 3 | //#include 4 | // 5 | // 6 | //JNIEnv *get_jni_env(); 7 | // 8 | // 9 | //// 10 | ////#ifdef __cplusplus 11 | ////extern "C" { 12 | ////#endif 13 | ////#include 14 | //// 15 | //// JNIEnv* get_java_env(); 16 | //// 17 | //// int create_java_env(JavaVM** jvm, JNIEnv** env); 18 | //// 19 | //// void delete_java_env(JavaVM* jvm); 20 | //// 21 | //// int init_java(); 22 | //// 23 | //// void uninit_java(); 24 | //// 25 | ////#ifdef __cplusplus 26 | ////} 27 | ////#endif 28 | //// 29 | //// 30 | ////#define JAVA_ENV get_java_env() 31 | ////#define JAVA_RENV (*get_java_env()) 32 | //// 33 | ////#ifdef __cplusplus 34 | ////#include 35 | ////#include 36 | ////#include 37 | ////#include 38 | ////#include 39 | ////using std::string; 40 | ////using std::vector; 41 | ////using std::set; 42 | ////using std::map; 43 | ////using std::shared_ptr; 44 | //// 45 | ////string jstring2str(JNIEnv* env, jstring jstr); 46 | //// 47 | ////class java_hook; 48 | ////class jclass_hook; 49 | //// 50 | ////class jmethod_hook { 51 | //// friend class java_hook; 52 | //// friend class jclass_hook; 53 | ////protected: 54 | //// string name; 55 | //// string sig; 56 | //// jmethodID jmethod; 57 | //// void* hook = NULL; 58 | //// 59 | //// template 60 | //// R on_event(JNIEnv*, jobject obj, jmethodID, Args&...args) { 61 | //// using function = R(*)(jobject obj, Args&...); 62 | //// if (hook != NULL) 63 | //// { 64 | //// return ((function)hook)(obj, args...); 65 | //// } 66 | //// else { 67 | //// //err 68 | //// } 69 | //// } 70 | //// 71 | ////public: 72 | //// 73 | //// template 74 | //// using function = R(*)(jobject obj, Args&...); 75 | //// 76 | //// template 77 | //// bool set_hook(function pfun) { 78 | //// hook = pfun; 79 | //// } 80 | ////}; 81 | //// 82 | ////class java_hook { 83 | //// static map> hoook_list; 84 | //// static map< int, void*> old_fun_ptr; 85 | ////private: 86 | //// java_hook() { 87 | //// init_hook(); 88 | //// } 89 | ////public: 90 | //// static java_hook* get_java_hook() { 91 | //// static java_hook* ptr = NULL; 92 | //// if (ptr == NULL) 93 | //// { 94 | //// ptr = new java_hook(); 95 | //// } 96 | //// 97 | //// return ptr; 98 | //// } 99 | //// 100 | //// static void set_hook(shared_ptr hook_info) { 101 | //// hoook_list[hook_info->jmethod] = hook_info; 102 | //// } 103 | //// 104 | //// template 105 | //// static R on_call_method(JNIEnv* env, jobject obj, jmethodID jmethod, ...) { 106 | //// using function_v = R(*)(JNIEnv* env, jobject obj, jmethodID, va_list); 107 | //// auto item = hoook_list.find(jmethod); 108 | //// R ret; 109 | //// 110 | //// va_list args; 111 | //// va_start(args, jmethod); 112 | //// 113 | //// if (item == hoook_list.end()) 114 | //// ret = ((function_v)(old_fun_ptr.find(index + 2)->second))(env, obj, jmethod, args); 115 | //// else 116 | //// ret = ((function_v)(item->second->hook))(env, obj, jmethod, args); 117 | //// 118 | //// va_end(args); 119 | //// return ret; 120 | //// } 121 | //// 122 | //// template 123 | //// static R on_call_method_(JNIEnv* env, jobject obj, jmethodID jmethod, V v) { 124 | //// using function_v = R(*)(JNIEnv* env, jobject obj, jmethodID, V); 125 | //// auto item = hoook_list.find(jmethod); 126 | //// R ret; 127 | //// 128 | //// if (item == hoook_list.end()) 129 | //// ret = ((function_v)(old_fun_ptr.find(index + 2)->second))(env, obj, jmethod, v); 130 | //// else 131 | //// ret = ((function_v)(item->second->hook))(env, obj, jmethod, v); 132 | //// return ret; 133 | //// } 134 | //// 135 | ////#define decl_call_method_hook(method) {const int index =__COUNTER__ ;\ 136 | //// new_fun = (void*)(&java_hook::on_call_method); \ 137 | //// *((void**)(&env->method)) = new_fun; \ 138 | //// old_fun_ptr[index]= (void*)env->method ;}; \ 139 | //// {const int index =__COUNTER__;\ 140 | //// new_fun = (void*)(&java_hook::on_call_method_); \ 141 | //// *((void**)(&env->method##A)) = new_fun; \ 142 | //// old_fun_ptr[index]= (void*)env->method##A;}; \ 143 | //// {const int index =__COUNTER__;\ 144 | //// new_fun = (void*)(&java_hook::on_call_method_); \ 145 | //// *((void**)(&env->method##V)) = new_fun; \ 146 | //// old_fun_ptr[index]= (void*)env->method##V;}; 147 | //// 148 | //// static bool init_hook() { 149 | //// void* new_fun; 150 | //// JNINativeInterface_* env = (JNINativeInterface_*)JAVA_RENV; 151 | //// 152 | //// decl_call_method_hook(CallObjectMethod); 153 | //// decl_call_method_hook(CallBooleanMethod); 154 | //// decl_call_method_hook(CallByteMethod); 155 | //// decl_call_method_hook(CallCharMethod); 156 | //// decl_call_method_hook(CallShortMethod); 157 | //// decl_call_method_hook(CallIntMethod); 158 | //// decl_call_method_hook(CallLongMethod); 159 | //// decl_call_method_hook(CallFloatMethod); 160 | //// decl_call_method_hook(CallDoubleMethod); 161 | //// decl_call_method_hook(CallVoidMethod); 162 | //// } 163 | ////}; 164 | //// 165 | ////class jclass_hook { 166 | //// 167 | //// string name; 168 | //// jclass pclass; 169 | //// vector > hoook_list; 170 | ////public: 171 | //// jclass_hook() { 172 | //// } 173 | //// 174 | //// jclass_hook(string class_name) { 175 | //// use_class(class_name); 176 | //// } 177 | //// 178 | //// static jclass_hook create_jclass_hook_info(string class_name) { 179 | //// return jclass_hook(class_name); 180 | //// } 181 | //// 182 | //// bool use_class(string class_name) { 183 | //// jclass pclass = JAVA_RENV->FindClass(JAVA_ENV, class_name.c_str()); 184 | //// if (pclass == NULL) 185 | //// { 186 | //// return false; 187 | //// } 188 | //// 189 | //// this->name = class_name; 190 | //// this->pclass = pclass; 191 | //// } 192 | //// 193 | //// shared_ptr use_method(const char* name, const char* sig) { 194 | //// jmethodID jmethod = JAVA_RENV->GetMethodID(JAVA_ENV, pclass, name, sig); 195 | //// if (jmethod == NULL) 196 | //// { 197 | //// return NULL; 198 | //// } 199 | //// shared_ptr ptr(new jmethod_hook); 200 | //// ptr->name = name; 201 | //// ptr->sig = sig; 202 | //// ptr->jmethod = jmethod; 203 | //// hoook_list.push_back(ptr); 204 | //// java_hook::set_hook(ptr); 205 | //// return ptr; 206 | //// } 207 | ////}; 208 | //// 209 | ////#endif 210 | //// 211 | //// 212 | -------------------------------------------------------------------------------- /module/magisk_module/common/functions.sh: -------------------------------------------------------------------------------- 1 | ########################################################################################## 2 | # 3 | # MMT Extended Utility Functions 4 | # 5 | ########################################################################################## 6 | 7 | require_new_ksu() { 8 | ui_print "**********************************" 9 | ui_print " Please install KernelSU v0.6.6+! " 10 | ui_print "**********************************" 11 | exit 1 12 | } 13 | 14 | umount_mirrors() { 15 | [ -d $ORIGDIR ] || return 0 16 | for i in $ORIGDIR/*; do 17 | umount -l $i 2>/dev/null 18 | done 19 | rm -rf $ORIGDIR 2>/dev/null 20 | $KSU && mount -o ro,remount $MAGISKTMP 21 | } 22 | 23 | cleanup() { 24 | if $KSU || [ $MAGISK_VER_CODE -ge 27000 ]; then umount_mirrors; fi 25 | rm -rf $MODPATH/common $MODPATH/install.zip 2>/dev/null 26 | } 27 | 28 | abort() { 29 | ui_print "$1" 30 | rm -rf $MODPATH 2>/dev/null 31 | cleanup 32 | rm -rf $TMPDIR 2>/dev/null 33 | exit 1 34 | } 35 | 36 | device_check() { 37 | local opt=`getopt -o dm -- "$@"` type=device 38 | eval set -- "$opt" 39 | while true; do 40 | case "$1" in 41 | -d) local type=device; shift;; 42 | -m) local type=manufacturer; shift;; 43 | --) shift; break;; 44 | *) abort "Invalid device_check argument $1! Aborting!";; 45 | esac 46 | done 47 | local prop=$(echo "$1" | tr '[:upper:]' '[:lower:]') 48 | for i in /system /vendor /odm /product; do 49 | if [ -f $i/build.prop ]; then 50 | for j in "ro.product.$type" "ro.build.$type" "ro.product.vendor.$type" "ro.vendor.product.$type"; do 51 | [ "$(sed -n "s/^$j=//p" $i/build.prop 2>/dev/null | head -n 1 | tr '[:upper:]' '[:lower:]')" == "$prop" ] && return 0 52 | done 53 | [ "$type" == "device" ] && [ "$(sed -n "s/^"ro.build.product"=//p" $i/build.prop 2>/dev/null | head -n 1 | tr '[:upper:]' '[:lower:]')" == "$prop" ] && return 0 54 | fi 55 | done 56 | return 1 57 | } 58 | 59 | cp_ch() { 60 | local opt=`getopt -o nr -- "$@"` BAK=true UBAK=true FOL=false 61 | eval set -- "$opt" 62 | while true; do 63 | case "$1" in 64 | -n) UBAK=false; shift;; 65 | -r) FOL=true; shift;; 66 | --) shift; break;; 67 | *) abort "Invalid cp_ch argument $1! Aborting!";; 68 | esac 69 | done 70 | local SRC="$1" DEST="$2" OFILES="$1" 71 | $FOL && local OFILES=$(find $SRC -type f 2>/dev/null) 72 | [ -z $3 ] && PERM=0644 || PERM=$3 73 | case "$DEST" in 74 | $TMPDIR/*|$MODULEROOT/*|$NVBASE/modules/$MODID/*) BAK=false;; 75 | esac 76 | for OFILE in ${OFILES}; do 77 | if $FOL; then 78 | if [ "$(basename $SRC)" == "$(basename $DEST)" ]; then 79 | local FILE=$(echo $OFILE | sed "s|$SRC|$DEST|") 80 | else 81 | local FILE=$(echo $OFILE | sed "s|$SRC|$DEST/$(basename $SRC)|") 82 | fi 83 | else 84 | [ -d "$DEST" ] && local FILE="$DEST/$(basename $SRC)" || local FILE="$DEST" 85 | fi 86 | if $BAK && $UBAK; then 87 | [ ! "$(grep "$FILE$" $INFO 2>/dev/null)" ] && echo "$FILE" >> $INFO 88 | [ -f "$FILE" -a ! -f "$FILE~" ] && { mv -f $FILE $FILE~; echo "$FILE~" >> $INFO; } 89 | elif $BAK; then 90 | [ ! "$(grep "$FILE$" $INFO 2>/dev/null)" ] && echo "$FILE" >> $INFO 91 | fi 92 | install -D -m $PERM "$OFILE" "$FILE" 93 | done 94 | } 95 | 96 | install_script() { 97 | case "$1" in 98 | -b) shift; 99 | if $KSU; then 100 | local INPATH=$NVBASE/boot-completed.d 101 | else 102 | local INPATH=$SERVICED 103 | sed -i -e '1i (\nwhile [ "$(getprop sys.boot_completed)" != "1" ]; do\n sleep 1\ndone\nsleep 3\n' -e '$a)&' $1 104 | fi;; 105 | -l) shift; local INPATH=$SERVICED;; 106 | -p) shift; local INPATH=$POSTFSDATAD;; 107 | *) local INPATH=$SERVICED;; 108 | esac 109 | [ "$(grep "#!/system/bin/sh" $1)" ] || sed -i "1i #!/system/bin/sh" $1 110 | local i; for i in "MODPATH" "LIBDIR" "MODID" "INFO" "MODDIR"; do 111 | case $i in 112 | "MODPATH") sed -i "1a $i=$NVBASE/modules/$MODID" $1;; 113 | "MODDIR") sed -i "1a $i=\${0%/*}" $1;; 114 | *) sed -i "1a $i=$(eval echo \$$i)" $1;; 115 | esac 116 | done 117 | case $1 in 118 | "$MODPATH/post-fs-data.sh"|"$MODPATH/service.sh"|"$MODPATH/uninstall.sh") sed -i "s|^MODPATH=.*|MODPATH=\$MODDIR|" $1;; # MODPATH=MODDIR for these scripts (located in module directory) 119 | "$MODPATH/boot-completed.sh") $KSU && sed -i "s|^MODPATH=.*|MODPATH=\$MODDIR|" $1 || { cp_ch -n $1 $INPATH/$MODID-$(basename $1) 0755; rm -f $MODPATH/boot-completed.sh; };; 120 | *) cp_ch -n $1 $INPATH/$(basename $1) 0755;; 121 | esac 122 | } 123 | 124 | prop_process() { 125 | sed -i -e "/^#/d" -e "/^ *$/d" $1 126 | [ -f $MODPATH/system.prop ] || mktouch $MODPATH/system.prop 127 | while read LINE; do 128 | echo "$LINE" >> $MODPATH/system.prop 129 | done < $1 130 | } 131 | 132 | mount_mirrors() { 133 | $KSU && mount -o rw,remount $MAGISKTMP 134 | mkdir -p $ORIGDIR/system 135 | if $SYSTEM_ROOT; then 136 | mkdir -p $ORIGDIR/system_root 137 | mount -o ro / $ORIGDIR/system_root 138 | mount -o bind $ORIGDIR/system_root/system $ORIGDIR/system 139 | else 140 | mount -o ro /system $ORIGDIR/system 141 | fi 142 | for i in /vendor $PARTITIONS; do 143 | [ ! -d $i -o -d $ORIGDIR$i ] && continue 144 | mkdir -p $ORIGDIR$i 145 | mount -o ro $i $ORIGDIR$i 146 | done 147 | } 148 | 149 | # Credits 150 | ui_print "**************************************" 151 | ui_print "* MMT Extended by Zackptg5 @ XDA *" 152 | ui_print "**************************************" 153 | ui_print " " 154 | 155 | # Check for min/max api version 156 | [ -z $MINAPI ] || { [ $API -lt $MINAPI ] && abort "! Your system API of $API is less than the minimum api of $MINAPI! Aborting!"; } 157 | [ -z $MAXAPI ] || { [ $API -gt $MAXAPI ] && abort "! Your system API of $API is greater than the maximum api of $MAXAPI! Aborting!"; } 158 | 159 | # Min KSU v0.6.6 160 | [ -z $KSU ] && KSU=false 161 | $KSU && { [ $KSU_VER_CODE -lt 11184 ] && require_new_ksu; } 162 | # APatch is fork of KSU, treat same 163 | [ -z $APATCH ] && APATCH=false 164 | [ "$APATCH" == "true" ] && KSU=true 165 | 166 | # Start debug 167 | set -x 168 | 169 | # Set variables 170 | [ -z $ARCH32 ] && ARCH32="$(echo $ABI32 | cut -c-3)" 171 | [ $API -lt 26 ] && DYNLIB=false 172 | [ -z $DYNLIB ] && DYNLIB=false 173 | [ -z $PARTOVER ] && PARTOVER=false 174 | [ -z $SYSTEM_ROOT ] && SYSTEM_ROOT=$SYSTEM_AS_ROOT # renamed in magisk v26.3 175 | [ -z $SERVICED ] && SERVICED=$NVBASE/service.d # removed in magisk v26.2 176 | [ -z $POSTFSDATAD ] && POSTFSDATAD=$NVBASE/post-fs-data.d # removed in magisk v26.2 177 | INFO=$NVBASE/modules/.$MODID-files 178 | if $KSU; then 179 | MAGISKTMP="/mnt" 180 | ORIGDIR="$MAGISKTMP/mirror" 181 | mount_mirrors 182 | elif [ "$(magisk --path 2>/dev/null)" ]; then 183 | if [ $MAGISK_VER_CODE -ge 27000 ]; then # Atomic Mount 184 | if [ -z $MAGISKTMP ]; then 185 | [ -d /sbin ] && MAGISKTMP=/sbin || MAGISKTMP=/debug_ramdisk 186 | fi 187 | ORIGDIR="$MAGISKTMP/mirror" 188 | mount_mirrors 189 | else 190 | ORIGDIR="$(magisk --path 2>/dev/null)/.magisk/mirror" 191 | fi 192 | elif [ "$(echo $MAGISKTMP | awk -F/ '{ print $NF}')" == ".magisk" ]; then 193 | ORIGDIR="$MAGISKTMP/mirror" 194 | else 195 | ORIGDIR="$MAGISKTMP/.magisk/mirror" 196 | fi 197 | if $DYNLIB; then 198 | LIBPATCH="\/vendor" 199 | LIBDIR=/system/vendor 200 | else 201 | LIBPATCH="\/system" 202 | LIBDIR=/system 203 | fi 204 | # Detect extra partition compatibility (KernelSU or Magisk Delta/Kitsune) 205 | EXTRAPART=false 206 | if $KSU || [ "$(echo $MAGISK_VER | awk -F- '{ print $NF}')" == "delta" ] || [ "$(echo $MAGISK_VER | awk -F- '{ print $NF}')" == "kitsune" ]; then 207 | EXTRAPART=true 208 | elif ! $PARTOVER; then 209 | unset PARTITIONS 210 | fi 211 | 212 | if ! $BOOTMODE; then 213 | ui_print "- Only uninstall is supported in recovery" 214 | ui_print " Uninstalling!" 215 | touch $MODPATH/remove 216 | [ -s $INFO ] && install_script $MODPATH/uninstall.sh || rm -f $INFO $MODPATH/uninstall.sh 217 | recovery_cleanup 218 | cleanup 219 | rm -rf $NVBASE/modules_update/$MODID $TMPDIR 2>/dev/null 220 | exit 0 221 | fi 222 | 223 | # Extract files 224 | ui_print "- Extracting module files" 225 | unzip -o "$ZIPFILE" -x 'META-INF/*' 'common/functions.sh' -d $MODPATH >&2 226 | [ -f "$MODPATH/common/addon.tar.xz" ] && tar -xf $MODPATH/common/addon.tar.xz -C $MODPATH/common 2>/dev/null 227 | 228 | # Run addons 229 | if [ "$(ls -A $MODPATH/common/addon/*/install.sh 2>/dev/null)" ]; then 230 | ui_print " "; ui_print "- Running Addons -" 231 | for i in $MODPATH/common/addon/*/install.sh; do 232 | ui_print " Running $(echo $i | sed -r "s|$MODPATH/common/addon/(.*)/install.sh|\1|")..." 233 | . $i 234 | done 235 | fi 236 | 237 | # Remove files outside of module directory 238 | ui_print "- Removing old files" 239 | 240 | if [ -f $INFO ]; then 241 | while read LINE; do 242 | if [ "$(echo -n $LINE | tail -c 1)" == "~" ]; then 243 | continue 244 | elif [ -f "$LINE~" ]; then 245 | mv -f $LINE~ $LINE 246 | else 247 | rm -f $LINE 248 | while true; do 249 | LINE=$(dirname $LINE) 250 | [ "$(ls -A $LINE 2>/dev/null)" ] && break 1 || rm -rf $LINE 251 | done 252 | fi 253 | done < $INFO 254 | rm -f $INFO 255 | fi 256 | 257 | ### Install 258 | ui_print "- Installing" 259 | 260 | [ -f "$MODPATH/common/install.sh" ] && . $MODPATH/common/install.sh 261 | 262 | ui_print " Installing for $ARCH SDK $API device..." 263 | # Remove comments from files and place them, add blank line to end if not already present 264 | for i in $(find $MODPATH -type f -name "*.sh" -o -name "*.prop" -o -name "*.rule"); do 265 | [ -f $i ] && { sed -i -e "/^#/d" -e "/^ *$/d" $i; [ "$(tail -1 $i)" ] && echo "" >> $i; } || continue 266 | case $i in 267 | "$MODPATH/boot-completed.sh") install_script -b $i;; 268 | "$MODPATH/service.sh") install_script -l $i;; 269 | "$MODPATH/post-fs-data.sh") install_script -p $i;; 270 | "$MODPATH/uninstall.sh") if [ -s $INFO ] || [ "$(head -n1 $MODPATH/uninstall.sh)" != "# Don't modify anything after this" ]; then 271 | cp -f $MODPATH/uninstall.sh $MODPATH/$MODID-uninstall.sh # Fallback script in case module manually deleted 272 | sed -i "1i[ -d \"\$MODPATH\" ] && exit 0" $MODPATH/$MODID-uninstall.sh 273 | echo 'rm -f $0' >> $MODPATH/$MODID-uninstall.sh 274 | install_script -l $MODPATH/$MODID-uninstall.sh 275 | rm -f $MODPATH/$MODID-uninstall.sh 276 | install_script $MODPATH/uninstall.sh 277 | else 278 | rm -f $INFO $MODPATH/uninstall.sh 279 | fi;; 280 | esac 281 | done 282 | 283 | $IS64BIT || for i in $(find $MODPATH/system -type d -name "lib64"); do rm -rf $i 2>/dev/null; done 284 | [ -d "/system/priv-app" ] || mv -f $MODPATH/system/priv-app $MODPATH/system/app 2>/dev/null 285 | [ -d "/system/xbin" ] || mv -f $MODPATH/system/xbin $MODPATH/system/bin 2>/dev/null 286 | if $DYNLIB; then 287 | for FILE in $(find $MODPATH/system/lib* -type f 2>/dev/null | sed "s|$MODPATH/system/||"); do 288 | [ -s $MODPATH/system/$FILE ] || continue 289 | case $FILE in 290 | lib*/modules/*) continue;; 291 | esac 292 | mkdir -p $(dirname $MODPATH/system/vendor/$FILE) 293 | mv -f $MODPATH/system/$FILE $MODPATH/system/vendor/$FILE 294 | [ "$(ls -A `dirname $MODPATH/system/$FILE`)" ] || rm -rf `dirname $MODPATH/system/$FILE` 295 | done 296 | # Delete empty lib folders (busybox find doesn't have this capability) 297 | toybox find $MODPATH/system/lib* -type d -empty -delete >/dev/null 2>&1 298 | fi 299 | 300 | # Set permissions 301 | ui_print " " 302 | ui_print "- Setting Permissions" 303 | set_perm_recursive $MODPATH 0 0 0755 0644 304 | for i in /system/vendor /vendor /system/vendor/app /vendor/app /system/vendor/etc /vendor/etc /system/odm/etc /odm/etc /system/vendor/odm/etc /vendor/odm/etc /system/vendor/overlay /vendor/overlay; do 305 | if [ -d "$MODPATH$i" ] && [ ! -L "$MODPATH$i" ]; then 306 | case $i in 307 | *"/vendor") set_perm_recursive $MODPATH$i 0 0 0755 0644 u:object_r:vendor_file:s0;; 308 | *"/app") set_perm_recursive $MODPATH$i 0 0 0755 0644 u:object_r:vendor_app_file:s0;; 309 | *"/overlay") set_perm_recursive $MODPATH$i 0 0 0755 0644 u:object_r:vendor_overlay_file:s0;; 310 | *"/etc") set_perm_recursive $MODPATH$i 0 2000 0755 0644 u:object_r:vendor_configs_file:s0;; 311 | esac 312 | fi 313 | done 314 | for i in $(find $MODPATH/system/vendor $MODPATH/vendor -type f -name *".apk" 2>/dev/null); do 315 | chcon u:object_r:vendor_app_file:s0 $i 316 | done 317 | set_permissions 318 | 319 | # Complete install 320 | cleanup 321 | -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "utils.h" 15 | #include "linux_helper.h" 16 | 17 | string get_packet_name() { 18 | string cmdLine = GetCmdLine(getpid()); 19 | int p = cmdLine.find(":"); 20 | if (p >= 0) { 21 | return cmdLine.substr(0, p); 22 | } 23 | return cmdLine; 24 | } 25 | 26 | string mid_string(const string &src, const string &start, const string &end) { 27 | int p = src.find(start); 28 | if (p == -1) { 29 | return ""; 30 | } 31 | int pe = src.find(end, p + start.size()); 32 | if (pe == -1) { 33 | return ""; 34 | } 35 | return src.substr(p + start.size(), pe - p - start.size()); 36 | } 37 | 38 | string get_uuid() { 39 | char uuid[37] = {0}; 40 | int fd = open("/proc/sys/kernel/random/uuid", O_RDONLY); 41 | if (fd >= 0) { 42 | read(fd, uuid + 1, 36); 43 | } 44 | close(fd); 45 | return uuid; 46 | } 47 | 48 | int64_t get_time() { 49 | time_t timep; 50 | time(&timep); 51 | return timep; 52 | } 53 | 54 | string time_to_string(int64_t tick) { 55 | tm tm{}; 56 | #ifdef _WIN32 57 | localtime_s(&tm, &tick); 58 | #else 59 | localtime_r((time_t *) &tick, &tm); 60 | #endif 61 | 62 | char buffer[32]; 63 | strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm); 64 | return string(buffer); 65 | } 66 | 67 | int64_t string_to_time(const string &time_str, const string &fmt = "%Y-%m-%d %H:%M:%S") { 68 | tm tm{}; 69 | strptime(time_str.c_str(), fmt.c_str(), &tm); 70 | return mktime(&tm); 71 | } 72 | 73 | bool WritFile(const char *path, const char *buf, int len) { 74 | FILE *file = fopen(path, "wb"); 75 | if (!file) { 76 | return false; 77 | } 78 | size_t written = fwrite(buf, 1, len, file); 79 | fflush(file); 80 | fclose(file); 81 | return written == len; 82 | } 83 | 84 | char *hex2str(const char *hex, int hex_len, char *str, int buf_len) { 85 | const char *cHex = "0123456789ABCDEF"; 86 | int i = 0; 87 | if (hex_len * 2 >= buf_len) { 88 | hex_len = buf_len / 2 - 1; 89 | } 90 | if (hex_len <= 0) { 91 | str[0] = 0; 92 | return nullptr; 93 | } 94 | for (int j = 0; j < hex_len; j++) { 95 | auto a = (unsigned int) hex[j]; 96 | str[i++] = cHex[(a & 0xf0) >> 4]; 97 | str[i++] = cHex[(a & 0x0f)]; 98 | // if ((j + 1) % 16 == 0) { 99 | // str[i++] = '\n'; 100 | // } else { 101 | // str[i++] = ' '; 102 | // } 103 | } 104 | str[i] = '\0'; 105 | return str; 106 | } 107 | 108 | int hex2int(char c) { 109 | if (c >= '0' && c <= '9') { 110 | return c - '0'; 111 | } else if (c >= 'A' && c <= 'F') { 112 | return c - 'A' + 10; 113 | } else if (c >= 'a' && c <= 'f') { 114 | return c - 'a' + 10; 115 | } 116 | return -1; 117 | } 118 | 119 | char *str2hex(const char *str, int str_len, char *hex, int buf_len) { 120 | if (str_len / 2 > buf_len) { 121 | str_len = buf_len * 2; 122 | } 123 | for (int i = 0; i < str_len; i += 2) { 124 | int high = hex2int(str[i]); 125 | int low = hex2int(str[i + 1]); 126 | hex[i / 2] = (high << 4) | low; 127 | } 128 | return hex; 129 | } 130 | 131 | //字符串分割函数 132 | vector string_split(const string &str, const string &pattern) { 133 | string::size_type pos; 134 | vector result; 135 | int size = str.size(); 136 | 137 | for (int i = 0; i < size; i++) { 138 | pos = str.find(pattern, i); 139 | if (pos == string::npos) { 140 | string s = str.substr(i); 141 | result.push_back(s); 142 | break; 143 | } 144 | if (pos < size) { 145 | string s = str.substr(i, pos - i); 146 | result.push_back(s); 147 | i = pos + pattern.size() - 1; 148 | } 149 | } 150 | return result; 151 | } 152 | 153 | //vector string_split(const string &str, const string &pattern) { 154 | // vector ret; 155 | // if (pattern.empty()) return ret; 156 | // size_t start = 0, index = str.find_first_of(pattern, 0); 157 | // while (index != std::string::npos) { 158 | // if (start != index) 159 | // ret.push_back(str.substr(start, index - start)); 160 | // start = index + 1; 161 | // index = str.find_first_of(pattern, start); 162 | // } 163 | // if (!str.substr(start).empty()) 164 | // ret.push_back(str.substr(start)); 165 | // return ret; 166 | //} 167 | 168 | string replace_all(const string &str, const string &old_value, const string &new_value) { 169 | string cp = str; 170 | while (true) { 171 | string::size_type pos(0); 172 | if ((pos = cp.find(old_value)) != string::npos) 173 | cp.replace(pos, old_value.length(), new_value); 174 | else break; 175 | } 176 | return cp; 177 | } 178 | 179 | struct AutoInitRand { 180 | AutoInitRand() { 181 | srand(time(NULL)); 182 | } 183 | } _; 184 | 185 | int gen_number(int min, int max) { 186 | return (rand() % (max - min + 1)) + min; 187 | } 188 | 189 | float gen_double(float min, float max) { 190 | return (rand() / (float) RAND_MAX) * (max - min) + min; 191 | } 192 | 193 | 194 | const char *CharSet_123 = "0123456789"; 195 | const char *CharSet_ABC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 196 | const char *CharSet_abc = "abcdefghijklmnopqrstuvwxyz"; 197 | const char *CharSet_hex = "0123456789abcdef"; 198 | const char *CharSet_ABC123 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 199 | const char *CharSet_all = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 200 | 201 | void gen_hex(int len, char *result) { 202 | for (int i = 0; i < len; ++i) { 203 | result[i] = gen_number(0, 0xff); 204 | } 205 | } 206 | 207 | string gen_str(const char *charSet, int maxIdx, int len) { 208 | string buff; 209 | buff.reserve(len + 1); 210 | for (int i = 0; i < len; ++i) { 211 | buff += charSet[gen_number(0, maxIdx)]; 212 | } 213 | return buff; 214 | } 215 | 216 | string gen_strABC123(int len) { 217 | return gen_str(CharSet_ABC123, 35, len); 218 | } 219 | 220 | string gen_hexstr(int len) { 221 | return gen_str(CharSet_hex, 15, len); 222 | } 223 | 224 | string gen_strabc(int len) { 225 | return gen_str(CharSet_abc, 25, len); 226 | } 227 | 228 | string gen_strABC(int len) { 229 | return gen_str(CharSet_ABC, 25, len); 230 | } 231 | 232 | string gen_str123(int len) { 233 | return gen_str(CharSet_123, 9, len); 234 | } 235 | 236 | string gen_strall(int len) { 237 | return gen_str(CharSet_all, 61, len); 238 | } 239 | 240 | string gen_str(const char *charSet, int len) { 241 | return gen_str(charSet, strlen(charSet) - 1, len); 242 | } 243 | 244 | string gen_uuid() { 245 | return gen_hexstr(8) + "-" + 246 | gen_hexstr(4) + "-" + 247 | gen_hexstr(4) + "-" + 248 | gen_hexstr(4) + "-" + 249 | gen_hexstr(12); 250 | } 251 | 252 | string to_lower(const string &str) { 253 | string cp = str; 254 | transform(cp.begin(), cp.end(), cp.begin(), 255 | [](unsigned char c) { 256 | return std::tolower(c); 257 | }); 258 | return cp; 259 | } 260 | 261 | string to_upper(const string &str) { 262 | string cp = str; 263 | transform(cp.begin(), cp.end(), cp.begin(), 264 | [](unsigned char c) { 265 | return std::toupper(c); 266 | }); 267 | return cp; 268 | } 269 | 270 | bool ReadFile(const string &path, char **data, int *len) { 271 | if (access(path.c_str(), R_OK) != 0) { 272 | return false; 273 | } 274 | FILE *fp = fopen(path.c_str(), "rb"); 275 | if (!fp) { 276 | return false; 277 | } 278 | fseek(fp, 0, SEEK_END); 279 | long fos = ftell(fp); 280 | fseek(fp, 0, SEEK_SET); 281 | char *buff = new char[fos]; 282 | fread(buff, 1, fos, fp); 283 | fclose(fp); 284 | *data = buff; 285 | *len = fos; 286 | return true; 287 | } 288 | 289 | string ReadFile(const string &path) { 290 | if (access(path.c_str(), R_OK) != 0) { 291 | return ""; 292 | } 293 | FILE *fp = fopen(path.c_str(), "r"); 294 | if (!fp) { 295 | return ""; 296 | } 297 | fseek(fp, 0, SEEK_END); 298 | long size = ftell(fp); 299 | fseek(fp, 0, SEEK_SET); 300 | char *buffer = new char[size + 1]; 301 | fread(buffer, 1, size, fp); 302 | buffer[size] = '\0'; 303 | fclose(fp); 304 | string result(buffer); 305 | delete[] buffer; 306 | return result; 307 | } 308 | 309 | 310 | void StringAppendV(std::string *dst, const char *format, va_list ap) { 311 | // First try with a small fixed size buffer 312 | char *space = new char[1024]{}; 313 | defer([&]() { 314 | delete[]space; 315 | }); 316 | // It's possible for methods that use a va_list to invalidate 317 | // the data in it upon use. The fix is to make a copy 318 | // of the structure before using it and use that copy instead. 319 | va_list backup_ap; 320 | va_copy(backup_ap, ap); 321 | int result = vsnprintf(space, 1024, format, backup_ap); 322 | va_end(backup_ap); 323 | 324 | if (result < static_cast(sizeof(space))) { 325 | if (result >= 0) { 326 | // Normal case -- everything fit. 327 | dst->append(space, result); 328 | return; 329 | } 330 | 331 | if (result < 0) { 332 | // Just an error. 333 | return; 334 | } 335 | } 336 | 337 | // Increase the buffer size to the size requested by vsnprintf, 338 | // plus one for the closing \0. 339 | int length = result + 1; 340 | char *buf = new char[length]; 341 | 342 | // Restore the va_list before we use it again 343 | va_copy(backup_ap, ap); 344 | result = vsnprintf(buf, length, format, backup_ap); 345 | va_end(backup_ap); 346 | 347 | if (result >= 0 && result < length) { 348 | // It fit 349 | dst->append(buf, result); 350 | } 351 | delete[] buf; 352 | } 353 | 354 | 355 | namespace xbyl { 356 | std::string format_string(const char *fmt, va_list ap) { 357 | std::string result; 358 | StringAppendV(&result, fmt, ap); 359 | return result; 360 | } 361 | 362 | std::string format_string(const string fmt, ...) { 363 | va_list ap; 364 | va_start(ap, fmt); 365 | std::string result; 366 | StringAppendV(&result, fmt.c_str(), ap); 367 | va_end(ap); 368 | return result; 369 | } 370 | } 371 | 372 | std::string StringPrintf(const char *fmt, ...) { 373 | va_list ap; 374 | va_start(ap, fmt); 375 | std::string result; 376 | StringAppendV(&result, fmt, ap); 377 | va_end(ap); 378 | return result; 379 | } 380 | 381 | long get_system_time_nanosecond() { 382 | struct timespec timestamp = {}; 383 | if (0 == clock_gettime(CLOCK_REALTIME, ×tamp)) 384 | return timestamp.tv_sec * 1000000000 + timestamp.tv_nsec; 385 | else 386 | return 0; 387 | } 388 | 389 | long get_system_time_microsecond() { 390 | struct timeval timestamp = {}; 391 | if (0 == gettimeofday(×tamp, NULL)) 392 | return timestamp.tv_sec * 1000000 + timestamp.tv_usec; 393 | else 394 | return 0; 395 | } 396 | 397 | long get_system_time_millisecond() { 398 | struct timeval timestamp = {}; 399 | if (0 == gettimeofday(×tamp, NULL)) 400 | return timestamp.tv_sec * 1000 + timestamp.tv_usec / 1000; 401 | else 402 | return 0; 403 | } 404 | 405 | int 406 | TraversePackagesList(std::function &)> callback) { 407 | const string &data = ReadFile("/data/system/packages.list"); 408 | if (data.empty()) { 409 | return -1; 410 | } 411 | 412 | const auto &lines = string_split(data, "\n"); 413 | for (const auto &line: lines) { 414 | const auto &sp = string_split(line, " "); 415 | if (!callback(sp)) { 416 | return 1; 417 | } 418 | } 419 | return 0; 420 | } 421 | 422 | bool ReadAllPackagesGid(vector &gids) { 423 | return TraversePackagesList([&](const vector &line_sp) -> bool { 424 | if (line_sp.size() >= 2) { 425 | int gid = stoi(line_sp[1]); 426 | if (gid >= 10000) { 427 | gids.push_back(gid); 428 | } 429 | } 430 | return true; 431 | }) != -1; 432 | } 433 | 434 | 435 | int ReadPkgGid(const string &pkgName) { 436 | int ret = -1; 437 | TraversePackagesList([&](const vector &line_sp) -> bool { 438 | if (line_sp.size() < 5) { 439 | return true; 440 | } 441 | if (line_sp[0] == pkgName) { 442 | ret = stoi(line_sp[1]); 443 | return false; 444 | } 445 | return true; 446 | }); 447 | return ret; 448 | } 449 | 450 | string ReadPkgDirSelinuxCtx(const string &pkgName) { 451 | string ret = ""; 452 | TraversePackagesList([&](const vector &line_sp) -> bool { 453 | if (line_sp.size() < 5) { 454 | return true; 455 | } 456 | if (line_sp[0] == pkgName) { 457 | ret = line_sp[4].empty() ? "" : line_sp[4] + ":complete"; 458 | return false; 459 | } 460 | return true; 461 | }); 462 | return ret; 463 | } 464 | 465 | static int mem_validate_pipe[2] = {0, 0}; 466 | mutex testMemoryLock; 467 | 468 | static void do_pipe2(int pipefd[2]) { 469 | pipe2(pipefd, O_CLOEXEC | O_NONBLOCK); 470 | } 471 | 472 | static void open_pipe() { 473 | if (mem_validate_pipe[0] != -1) 474 | close(mem_validate_pipe[0]); 475 | if (mem_validate_pipe[1] != -1) 476 | close(mem_validate_pipe[1]); 477 | do_pipe2(mem_validate_pipe); 478 | } 479 | 480 | bool check_memory_readable(void *addr) { 481 | testMemoryLock.lock(); 482 | defer([&] { 483 | testMemoryLock.unlock(); 484 | }); 485 | int ret = -1; 486 | ssize_t bytes = 0; 487 | do { 488 | char buf; 489 | bytes = read(mem_validate_pipe[0], &buf, 1); 490 | } while (errno == EINTR); 491 | if (!(bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK)) { 492 | open_pipe(); 493 | } 494 | do { 495 | ret = write(mem_validate_pipe[1], addr, 1); 496 | } while (errno == EINTR); 497 | return ret > 0; 498 | } 499 | 500 | vector GetStackInfo(int num, ...) { 501 | vector frame; 502 | va_list args; 503 | va_start(args, num); 504 | for (int i = 0; i < num; ++i) { 505 | void *addr = va_arg(args, void*); 506 | Dl_info info{}; 507 | 508 | if (dladdr(addr, &info) != 0) { 509 | frame.push_back({ 510 | info.dli_fname, 511 | (void *) ((uint64_t) addr - (uint64_t) info.dli_fbase) 512 | }); 513 | } else { 514 | frame.push_back({ 515 | "unknow", 516 | (void *) ((uint64_t) addr - (uint64_t) info.dli_fbase) 517 | }); 518 | } 519 | } 520 | va_end(args); 521 | return frame; 522 | } 523 | 524 | bool check_mem(void *p) { 525 | int pageSize = getpagesize(); 526 | unsigned char vec = 0; 527 | uint64_t start = ((uint64_t) p) & (~(pageSize - 1)); 528 | int result = mincore((void *) start, pageSize, &vec); 529 | return result == 0 && vec == 1; 530 | } 531 | 532 | extern "C" bool check_stack(void *p) { 533 | if (!check_mem(p)) { 534 | return false; 535 | } 536 | if (!check_mem((void *) *((uint64_t *) p))) { 537 | return false; 538 | } 539 | if (!check_mem((void *) (((uint64_t) p) + 8))) { 540 | return false; 541 | } 542 | if (!check_mem((void *) *((uint64_t *) (((uint64_t) p) + 8)))) { 543 | return false; 544 | } 545 | return true; 546 | } 547 | 548 | string stack2str(const vector &stack) { 549 | string result; 550 | for (const Stack &item: stack) { 551 | result += xbyl::format_string("%s:%p,", item.name.c_str(), item.offset); 552 | } 553 | return result; 554 | } 555 | -------------------------------------------------------------------------------- /module/src/main/cpp/zygisk.hpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2022-2023 John "topjohnwu" Wu 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 7 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 8 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 9 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 10 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 11 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 12 | * PERFORMANCE OF THIS SOFTWARE. 13 | */ 14 | 15 | // This is the public API for Zygisk modules. 16 | // DO NOT MODIFY ANY CODE IN THIS HEADER. 17 | 18 | #pragma once 19 | 20 | #include 21 | 22 | #define ZYGISK_API_VERSION 4 23 | 24 | /* 25 | 26 | *************** 27 | * Introduction 28 | *************** 29 | 30 | On Android, all app processes are forked from a special daemon called "Zygote". 31 | For each new app process, zygote will fork a new process and perform "specialization". 32 | This specialization operation enforces the Android security sandbox on the newly forked 33 | process to make sure that 3rd party application code is only loaded after it is being 34 | restricted within a sandbox. 35 | 36 | On Android, there is also this special process called "system_server". This single 37 | process hosts a significant portion of system services, which controls how the 38 | Android operating system and apps interact with each other. 39 | 40 | The Zygisk framework provides a way to allow developers to build modules and run custom 41 | code before and after system_server and any app processes' specialization. 42 | This enable developers to inject code and alter the behavior of system_server and app processes. 43 | 44 | Please note that modules will only be loaded after zygote has forked the child process. 45 | THIS MEANS ALL OF YOUR CODE RUNS IN THE APP/SYSTEM_SERVER PROCESS, NOT THE ZYGOTE DAEMON! 46 | 47 | ********************* 48 | * Development Guide 49 | ********************* 50 | 51 | Define a class and inherit zygisk::ModuleBase to implement the functionality of your module. 52 | Use the macro REGISTER_ZYGISK_MODULE(className) to register that class to Zygisk. 53 | 54 | Example code: 55 | 56 | static jint (*orig_logger_entry_max)(JNIEnv *env); 57 | static jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); } 58 | 59 | class ExampleModule : public zygisk::ModuleBase { 60 | public: 61 | void onLoad(zygisk::Api *api, JNIEnv *env) override { 62 | this->api = api; 63 | this->env = env; 64 | } 65 | void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { 66 | JNINativeMethod methods[] = { 67 | { "logger_entry_max_payload_native", "()I", (void*) my_logger_entry_max }, 68 | }; 69 | api->hookJniNativeMethods(env, "android/util/Log", methods, 1); 70 | *(void **) &orig_logger_entry_max = methods[0].fnPtr; 71 | } 72 | private: 73 | zygisk::Api *api; 74 | JNIEnv *env; 75 | }; 76 | 77 | REGISTER_ZYGISK_MODULE(ExampleModule) 78 | 79 | ----------------------------------------------------------------------------------------- 80 | 81 | Since your module class's code runs with either Zygote's privilege in pre[XXX]Specialize, 82 | or runs in the sandbox of the target process in post[XXX]Specialize, the code in your class 83 | never runs in a true superuser environment. 84 | 85 | If your module require access to superuser permissions, you can create and register 86 | a root companion handler function. This function runs in a separate root companion 87 | daemon process, and an Unix domain socket is provided to allow you to perform IPC between 88 | your target process and the root companion process. 89 | 90 | Example code: 91 | 92 | static void example_handler(int socket) { ... } 93 | 94 | REGISTER_ZYGISK_COMPANION(example_handler) 95 | 96 | */ 97 | 98 | namespace zygisk { 99 | 100 | struct Api; 101 | struct AppSpecializeArgs; 102 | struct ServerSpecializeArgs; 103 | 104 | class ModuleBase { 105 | public: 106 | 107 | // This method is called as soon as the module is loaded into the target process. 108 | // A Zygisk API handle will be passed as an argument. 109 | virtual void onLoad([[maybe_unused]] Api *api, [[maybe_unused]] JNIEnv *env) {} 110 | 111 | // This method is called before the app process is specialized. 112 | // At this point, the process just got forked from zygote, but no app specific specialization 113 | // is applied. This means that the process does not have any sandbox restrictions and 114 | // still runs with the same privilege of zygote. 115 | // 116 | // All the arguments that will be sent and used for app specialization is passed as a single 117 | // AppSpecializeArgs object. You can read and overwrite these arguments to change how the app 118 | // process will be specialized. 119 | // 120 | // If you need to run some operations as superuser, you can call Api::connectCompanion() to 121 | // get a socket to do IPC calls with a root companion process. 122 | // See Api::connectCompanion() for more info. 123 | virtual void preAppSpecialize([[maybe_unused]] AppSpecializeArgs *args) {} 124 | 125 | // This method is called after the app process is specialized. 126 | // At this point, the process has all sandbox restrictions enabled for this application. 127 | // This means that this method runs with the same privilege of the app's own code. 128 | virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {} 129 | 130 | // This method is called before the system server process is specialized. 131 | // See preAppSpecialize(args) for more info. 132 | virtual void preServerSpecialize([[maybe_unused]] ServerSpecializeArgs *args) {} 133 | 134 | // This method is called after the system server process is specialized. 135 | // At this point, the process runs with the privilege of system_server. 136 | virtual void postServerSpecialize([[maybe_unused]] const ServerSpecializeArgs *args) {} 137 | }; 138 | 139 | struct AppSpecializeArgs { 140 | // Required arguments. These arguments are guaranteed to exist on all Android versions. 141 | jint &uid; 142 | jint &gid; 143 | jintArray &gids; 144 | jint &runtime_flags; 145 | jobjectArray &rlimits; 146 | jint &mount_external; 147 | jstring &se_info; 148 | jstring &nice_name; 149 | jstring &instruction_set; 150 | jstring &app_data_dir; 151 | 152 | // Optional arguments. Please check whether the pointer is null before de-referencing 153 | jintArray *const fds_to_ignore; 154 | jboolean *const is_child_zygote; 155 | jboolean *const is_top_app; 156 | jobjectArray *const pkg_data_info_list; 157 | jobjectArray *const whitelisted_data_info_list; 158 | jboolean *const mount_data_dirs; 159 | jboolean *const mount_storage_dirs; 160 | 161 | AppSpecializeArgs() = delete; 162 | }; 163 | 164 | struct ServerSpecializeArgs { 165 | jint &uid; 166 | jint &gid; 167 | jintArray &gids; 168 | jint &runtime_flags; 169 | jlong &permitted_capabilities; 170 | jlong &effective_capabilities; 171 | 172 | ServerSpecializeArgs() = delete; 173 | }; 174 | 175 | namespace internal { 176 | struct api_table; 177 | template void entry_impl(api_table *, JNIEnv *); 178 | } 179 | 180 | // These values are used in Api::setOption(Option) 181 | enum Option : int { 182 | // Force Magisk's denylist unmount routines to run on this process. 183 | // 184 | // Setting this option only makes sense in preAppSpecialize. 185 | // The actual unmounting happens during app process specialization. 186 | // 187 | // Set this option to force all Magisk and modules' files to be unmounted from the 188 | // mount namespace of the process, regardless of the denylist enforcement status. 189 | FORCE_DENYLIST_UNMOUNT = 0, 190 | 191 | // When this option is set, your module's library will be dlclose-ed after post[XXX]Specialize. 192 | // Be aware that after dlclose-ing your module, all of your code will be unmapped from memory. 193 | // YOU MUST NOT ENABLE THIS OPTION AFTER HOOKING ANY FUNCTIONS IN THE PROCESS. 194 | DLCLOSE_MODULE_LIBRARY = 1, 195 | }; 196 | 197 | // Bit masks of the return value of Api::getFlags() 198 | enum StateFlag : uint32_t { 199 | // The user has granted root access to the current process 200 | PROCESS_GRANTED_ROOT = (1u << 0), 201 | 202 | // The current process was added on the denylist 203 | PROCESS_ON_DENYLIST = (1u << 1), 204 | }; 205 | 206 | // All API methods will stop working after post[XXX]Specialize as Zygisk will be unloaded 207 | // from the specialized process afterwards. 208 | struct Api { 209 | 210 | // Connect to a root companion process and get a Unix domain socket for IPC. 211 | // 212 | // This API only works in the pre[XXX]Specialize methods due to SELinux restrictions. 213 | // 214 | // The pre[XXX]Specialize methods run with the same privilege of zygote. 215 | // If you would like to do some operations with superuser permissions, register a handler 216 | // function that would be called in the root process with REGISTER_ZYGISK_COMPANION(func). 217 | // Another good use case for a companion process is that if you want to share some resources 218 | // across multiple processes, hold the resources in the companion process and pass it over. 219 | // 220 | // The root companion process is ABI aware; that is, when calling this method from a 32-bit 221 | // process, you will be connected to a 32-bit companion process, and vice versa for 64-bit. 222 | // 223 | // Returns a file descriptor to a socket that is connected to the socket passed to your 224 | // module's companion request handler. Returns -1 if the connection attempt failed. 225 | int connectCompanion(); 226 | 227 | // Get the file descriptor of the root folder of the current module. 228 | // 229 | // This API only works in the pre[XXX]Specialize methods. 230 | // Accessing the directory returned is only possible in the pre[XXX]Specialize methods 231 | // or in the root companion process (assuming that you sent the fd over the socket). 232 | // Both restrictions are due to SELinux and UID. 233 | // 234 | // Returns -1 if errors occurred. 235 | int getModuleDir(); 236 | 237 | // Set various options for your module. 238 | // Please note that this method accepts one single option at a time. 239 | // Check zygisk::Option for the full list of options available. 240 | void setOption(Option opt); 241 | 242 | // Get information about the current process. 243 | // Returns bitwise-or'd zygisk::StateFlag values. 244 | uint32_t getFlags(); 245 | 246 | // Exempt the provided file descriptor from being automatically closed. 247 | // 248 | // This API only make sense in preAppSpecialize; calling this method in any other situation 249 | // is either a no-op (returns true) or an error (returns false). 250 | // 251 | // When false is returned, the provided file descriptor will eventually be closed by zygote. 252 | bool exemptFd(int fd); 253 | 254 | // Hook JNI native methods for a class 255 | // 256 | // Lookup all registered JNI native methods and replace it with your own methods. 257 | // The original function pointer will be saved in each JNINativeMethod's fnPtr. 258 | // If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr 259 | // will be set to nullptr. 260 | void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods); 261 | 262 | // Hook functions in the PLT (Procedure Linkage Table) of ELFs loaded in memory. 263 | // 264 | // Parsing /proc/[PID]/maps will give you the memory map of a process. As an example: 265 | // 266 | //
267 | // 56b4346000-56b4347000 r-xp 00002000 fe:00 235 /system/bin/app_process64 268 | // (More details: https://man7.org/linux/man-pages/man5/proc.5.html) 269 | // 270 | // The `dev` and `inode` pair uniquely identifies a file being mapped into memory. 271 | // For matching ELFs loaded in memory, replace function `symbol` with `newFunc`. 272 | // If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`. 273 | void pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc); 274 | 275 | // Commit all the hooks that was previously registered. 276 | // Returns false if an error occurred. 277 | bool pltHookCommit(); 278 | 279 | private: 280 | internal::api_table *tbl; 281 | template friend void internal::entry_impl(internal::api_table *, JNIEnv *); 282 | }; 283 | 284 | // Register a class as a Zygisk module 285 | 286 | #define REGISTER_ZYGISK_MODULE(clazz) \ 287 | void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) { \ 288 | zygisk::internal::entry_impl(table, env); \ 289 | } 290 | 291 | // Register a root companion request handler function for your module 292 | // 293 | // The function runs in a superuser daemon process and handles a root companion request from 294 | // your module running in a target process. The function has to accept an integer value, 295 | // which is a Unix domain socket that is connected to the target process. 296 | // See Api::connectCompanion() for more info. 297 | // 298 | // NOTE: the function can run concurrently on multiple threads. 299 | // Be aware of race conditions if you have globally shared resources. 300 | 301 | #define REGISTER_ZYGISK_COMPANION(func) \ 302 | void zygisk_companion_entry(int client) { func(client); } 303 | 304 | /********************************************************* 305 | * The following is internal ABI implementation detail. 306 | * You do not have to understand what it is doing. 307 | *********************************************************/ 308 | 309 | namespace internal { 310 | 311 | struct module_abi { 312 | long api_version; 313 | ModuleBase *impl; 314 | 315 | void (*preAppSpecialize)(ModuleBase *, AppSpecializeArgs *); 316 | void (*postAppSpecialize)(ModuleBase *, const AppSpecializeArgs *); 317 | void (*preServerSpecialize)(ModuleBase *, ServerSpecializeArgs *); 318 | void (*postServerSpecialize)(ModuleBase *, const ServerSpecializeArgs *); 319 | 320 | module_abi(ModuleBase *module) : api_version(ZYGISK_API_VERSION), impl(module) { 321 | preAppSpecialize = [](auto m, auto args) { m->preAppSpecialize(args); }; 322 | postAppSpecialize = [](auto m, auto args) { m->postAppSpecialize(args); }; 323 | preServerSpecialize = [](auto m, auto args) { m->preServerSpecialize(args); }; 324 | postServerSpecialize = [](auto m, auto args) { m->postServerSpecialize(args); }; 325 | } 326 | }; 327 | 328 | struct api_table { 329 | // Base 330 | void *impl; 331 | bool (*registerModule)(api_table *, module_abi *); 332 | 333 | void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int); 334 | void (*pltHookRegister)(dev_t, ino_t, const char *, void *, void **); 335 | bool (*exemptFd)(int); 336 | bool (*pltHookCommit)(); 337 | int (*connectCompanion)(void * /* impl */); 338 | void (*setOption)(void * /* impl */, Option); 339 | int (*getModuleDir)(void * /* impl */); 340 | uint32_t (*getFlags)(void * /* impl */); 341 | }; 342 | 343 | template 344 | void entry_impl(api_table *table, JNIEnv *env) { 345 | static Api api; 346 | api.tbl = table; 347 | static T module; 348 | ModuleBase *m = &module; 349 | static module_abi abi(m); 350 | if (!table->registerModule(table, &abi)) return; 351 | m->onLoad(&api, env); 352 | } 353 | 354 | } // namespace internal 355 | 356 | inline int Api::connectCompanion() { 357 | return tbl->connectCompanion ? tbl->connectCompanion(tbl->impl) : -1; 358 | } 359 | inline int Api::getModuleDir() { 360 | return tbl->getModuleDir ? tbl->getModuleDir(tbl->impl) : -1; 361 | } 362 | inline void Api::setOption(Option opt) { 363 | if (tbl->setOption) tbl->setOption(tbl->impl, opt); 364 | } 365 | inline uint32_t Api::getFlags() { 366 | return tbl->getFlags ? tbl->getFlags(tbl->impl) : 0; 367 | } 368 | inline bool Api::exemptFd(int fd) { 369 | return tbl->exemptFd != nullptr && tbl->exemptFd(fd); 370 | } 371 | inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) { 372 | if (tbl->hookJniNativeMethods) tbl->hookJniNativeMethods(env, className, methods, numMethods); 373 | } 374 | inline void Api::pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc) { 375 | if (tbl->pltHookRegister) tbl->pltHookRegister(dev, inode, symbol, newFunc, oldFunc); 376 | } 377 | inline bool Api::pltHookCommit() { 378 | return tbl->pltHookCommit != nullptr && tbl->pltHookCommit(); 379 | } 380 | 381 | } // namespace zygisk 382 | 383 | extern "C" { 384 | 385 | [[gnu::visibility("default"), maybe_unused]] 386 | void zygisk_module_entry(zygisk::internal::api_table *, JNIEnv *); 387 | 388 | [[gnu::visibility("default"), maybe_unused]] 389 | void zygisk_companion_entry(int); 390 | 391 | } // extern "C" 392 | -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/linux_helper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "log.h" 13 | #include "linux_helper.h" 14 | 15 | using namespace std; 16 | 17 | #define BUF_SIZE 1024 18 | 19 | int pid_by_process_name(char *task_name) { 20 | DIR *dir; 21 | dirent *ptr; 22 | FILE *fp; 23 | char filepath[50]; 24 | char cur_task_name[50]; 25 | char buf[BUF_SIZE]; 26 | pid_t pid = 0; 27 | 28 | dir = opendir("/proc"); 29 | if (NULL != dir) { 30 | while ((ptr = readdir(dir)) != NULL) { 31 | if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) 32 | continue; 33 | if (DT_DIR != ptr->d_type) 34 | continue; 35 | sprintf(filepath, "/proc/%s/status", ptr->d_name); 36 | fp = fopen(filepath, "r"); 37 | if (NULL != fp) { 38 | if (fgets(buf, BUF_SIZE - 1, fp) == NULL) { 39 | fclose(fp); 40 | continue; 41 | } 42 | sscanf(buf, "%*s %s", cur_task_name); 43 | if (!strcmp(task_name, cur_task_name)) { 44 | sscanf(ptr->d_name, "%d", &pid); 45 | fclose(fp); 46 | break; 47 | } 48 | fclose(fp); 49 | } 50 | } 51 | closedir(dir); 52 | } 53 | return pid; 54 | } 55 | 56 | //string process_name_by_pid(pid_t pid) { 57 | // char proc_pid_path[BUF_SIZE]; 58 | // char buf[BUF_SIZE]; 59 | // 60 | // sprintf(proc_pid_path, "/proc/%d/status", pid); 61 | // FILE *fp = fopen(proc_pid_path, "r"); 62 | // if (NULL != fp) { 63 | // if (fgets(buf, BUF_SIZE - 1, fp) == NULL) { 64 | // fclose(fp); 65 | // } 66 | // fclose(fp); 67 | // sscanf(buf, "%*s %s", task_name); 68 | // } 69 | //} 70 | 71 | bool Mount(const string &fakePath, const string &targetPath) { 72 | logi("mount %s to %s", fakePath.c_str(), targetPath.c_str()); 73 | return TEMP_FAILURE_RETRY(mount(fakePath.c_str(), targetPath.c_str(), nullptr, 74 | MS_BIND | MS_REC, nullptr)) != -1; 75 | } 76 | 77 | bool UnMount(const string &targetPath) { 78 | logi("umount %s", targetPath.c_str()); 79 | return umount(targetPath.c_str()) == 0; 80 | } 81 | 82 | bool Mount(const char *source, const char *target, const char *fs_type, unsigned long flags, 83 | const void *data) { 84 | logi("mount %s to %s", source, target); 85 | return TEMP_FAILURE_RETRY(mount(source, target, fs_type, flags, data)) != -1; 86 | } 87 | 88 | bool UnMount(const char *target) { 89 | logi("umount %s", target); 90 | return umount(target) == 0; 91 | } 92 | 93 | bool UnMount2(const char *target, int flags) { 94 | logi("umount2 %s", target); 95 | return umount2(target, flags) == 0; 96 | } 97 | 98 | string Fd2Path(int fd) { 99 | char filePath[MAXPATHLEN]; 100 | string fdPath = "/proc/self/fd/" + to_string(fd); 101 | int rdLen = readlink(fdPath.c_str(), filePath, MAXPATHLEN); 102 | if (rdLen == -1) { 103 | loge("readlink %s error: %d", fdPath.c_str(), errno); 104 | return ""; 105 | } 106 | filePath[rdLen] = 0; 107 | return filePath; 108 | } 109 | 110 | string GetCmdLine(pid_t pid) { 111 | char buff[256]{}; 112 | sprintf(buff, "/proc/%d/cmdline", pid); 113 | FILE *file = fopen(buff, "r"); 114 | if (file == nullptr) { 115 | return ""; 116 | } 117 | if (!fgets(buff, 255, file)) { 118 | fclose(file); 119 | return ""; 120 | } 121 | fclose(file); 122 | return string(buff); 123 | } 124 | 125 | string RunCmd(const string &strCmd) { 126 | char buf[1024] = {0}; 127 | FILE *pf = NULL; 128 | if ((pf = popen(strCmd.c_str(), "r")) == NULL) { 129 | return "cant popen file " + strCmd; 130 | } 131 | string strResult; 132 | while (fgets(buf, sizeof buf, pf)) { 133 | strResult += buf; 134 | } 135 | pclose(pf); 136 | return strResult; 137 | } 138 | 139 | bool get_mount_list(const char *path, vector &ret) { 140 | if (path == nullptr) { 141 | path = "/proc/mounts"; 142 | } 143 | FILE *fp = fopen(path, "r"); 144 | if (!fp) { 145 | return false; 146 | } 147 | char buf[1024]{0}; 148 | while (fgets(buf, sizeof(buf), fp) != NULL) { 149 | MountInfo info; 150 | int fsname0, fsname1, dir0, dir1, type0, type1, opts0, opts1; 151 | if (sscanf(buf, " %n%*s%n %n%*s%n %n%*s%n %n%*s%n %d %d", 152 | &fsname0, &fsname1, &dir0, &dir1, &type0, &type1, &opts0, &opts1, 153 | &info.mnt_freq, &info.mnt_passno) == 2) { 154 | buf[fsname1] = '\0'; 155 | info.mnt_fsname = &buf[fsname0]; 156 | buf[dir1] = '\0'; 157 | info.mnt_dir = &buf[dir0]; 158 | buf[type1] = '\0'; 159 | info.mnt_type = &buf[type0]; 160 | buf[opts1] = '\0'; 161 | info.mnt_opts = &buf[opts0]; 162 | ret.push_back(move(info)); 163 | } 164 | } 165 | fclose(fp); 166 | return true; 167 | } 168 | 169 | bool copy_file(const string &from, const string &to) { 170 | FILE *in, *out; 171 | in = fopen(from.c_str(), "rb"); 172 | if (in == nullptr) { 173 | loge("copy file can not open %s, error: %d", from.c_str(), errno); 174 | return false; 175 | } 176 | out = fopen(to.c_str(), "wb"); 177 | if (out == nullptr) { 178 | loge("copy file can not open %s, error: %d", to.c_str(), errno); 179 | fclose(in); 180 | return false; 181 | } 182 | 183 | char buff[4096]; 184 | size_t len = 0; 185 | while ((len = fread(buff, 1, sizeof(buff), in))) { 186 | fwrite(buff, 1, len, out); 187 | } 188 | 189 | fclose(in); 190 | fclose(out); 191 | return true; 192 | } 193 | 194 | bool 195 | SetFilePerm(const string &path, const string &context, mode_t st_mode, gid_t st_gid, uid_t st_uid) { 196 | bool ret = true; 197 | if (chmod(path.c_str(), st_mode) != 0) { 198 | loge("SetFilePerm chmod %s error: %d ", path.c_str(), errno); 199 | ret = false; 200 | } 201 | if (chown(path.c_str(), st_uid, st_gid) != 0) { 202 | loge("SetFilePerm chown %s error: %d ", path.c_str(), errno); 203 | ret = false; 204 | } 205 | // if (setfilecon(path.c_str(), context.c_str()) != 0) { 206 | // loge("SetFilePerm setfilecon %s error: %d ", path.c_str(), errno); 207 | // ret = false; 208 | // } 209 | return ret; 210 | } 211 | 212 | bool GetFileSelinuxCtx(const string &path, string &ctx) { 213 | char *bufctx = nullptr; 214 | defer([&]() { 215 | if (bufctx) 216 | free(bufctx); 217 | }); 218 | // if (!lgetfilecon(path.c_str(), &bufctx)) { 219 | // loge("get path %s selinux ctx error: %d", path.c_str(), errno); 220 | // return false; 221 | // } 222 | ctx = bufctx; 223 | return true; 224 | } 225 | 226 | bool GetFilePerm(const string &path, FilePerm &perm) { 227 | char *bufctx = nullptr; 228 | defer([&]() { 229 | if (bufctx) 230 | free(bufctx); 231 | }); 232 | // if (!lgetfilecon(path.c_str(), &bufctx)) { 233 | // loge("get path %s selinux ctx error: %d", path.c_str(), errno); 234 | // return false; 235 | // } 236 | struct stat buf; 237 | if (stat(path.c_str(), &buf) != 0) { 238 | loge("get path %s stat error: %d", path.c_str(), errno); 239 | return false; 240 | } 241 | 242 | perm.ctx = bufctx; 243 | perm.st_uid = buf.st_uid; 244 | perm.st_gid = buf.st_gid; 245 | perm.st_mode = buf.st_mode; 246 | return true; 247 | } 248 | 249 | bool remove_files(const string &path) { 250 | struct stat s_buf; 251 | if (stat(path.c_str(), &s_buf) != 0) { 252 | return false; 253 | } 254 | if (!S_ISDIR(s_buf.st_mode)) { 255 | return unlink(path.c_str()) == 0; 256 | } else { 257 | return remove_dir(path); 258 | } 259 | } 260 | 261 | bool remove_dir(const string &dir_name) { 262 | DIR *dir; 263 | struct dirent *entry; 264 | 265 | if (!(dir = opendir(dir_name.c_str()))) 266 | return false; 267 | while ((entry = readdir(dir)) != NULL) { 268 | if (entry->d_type == DT_DIR) { 269 | if (strcmp(entry->d_name, ".") == 0 || 270 | strcmp(entry->d_name, "..") == 0) 271 | continue; 272 | string path = dir_name + "/" + entry->d_name; 273 | if (!remove_dir(path)) { 274 | return false; 275 | } 276 | } else { 277 | string path = dir_name + "/" + entry->d_name; 278 | if (unlink(path.c_str()) != 0) { 279 | return false; 280 | } 281 | } 282 | } 283 | closedir(dir); 284 | return rmdir(dir_name.c_str()) == 0; 285 | } 286 | 287 | bool isInVector(const vector &v, const string &data) { 288 | for (const string &item: v) { 289 | if (item == data) { 290 | return true; 291 | } 292 | } 293 | return false; 294 | } 295 | 296 | bool remove_dir(const string &rootPath, const vector &white, bool deleteRoot) { 297 | DIR *dir; 298 | struct dirent *entry; 299 | 300 | if (!(dir = opendir(rootPath.c_str()))) 301 | return false; 302 | while ((entry = readdir(dir)) != NULL) { 303 | string path = rootPath + "/" + entry->d_name; 304 | if (isInVector(white, path)) { 305 | continue; 306 | } 307 | if (entry->d_type == DT_DIR) { 308 | if (strcmp(entry->d_name, ".") == 0 || 309 | strcmp(entry->d_name, "..") == 0) 310 | continue; 311 | 312 | if (!remove_dir(path)) { 313 | return false; 314 | } 315 | } else { 316 | if (unlink(path.c_str()) != 0) { 317 | return false; 318 | } 319 | } 320 | } 321 | closedir(dir); 322 | if (deleteRoot) { 323 | return rmdir(rootPath.c_str()) == 0; 324 | } 325 | return true; 326 | } 327 | 328 | unsigned long get_file_size(const string &path) { 329 | struct stat buff; 330 | if (stat(path.c_str(), &buff) >= 0) { 331 | return buff.st_size; 332 | } 333 | return -1; 334 | } 335 | 336 | bool mkdir_recursive(const std::string &path, const mode_t mode) { 337 | std::string::size_type pos = 0; 338 | std::string::size_type prev = 0; 339 | while ((pos = path.find('/', prev)) != std::string::npos) { 340 | std::string sub = path.substr(0, pos); 341 | prev = pos + 1; 342 | if (sub.empty()) { 343 | continue; 344 | } 345 | if (mkdir(sub.c_str(), mode) != 0 && errno != EEXIST) { 346 | return false; 347 | } 348 | } 349 | if (mkdir(path.c_str(), mode) != 0 && errno != EEXIST) { 350 | return false; 351 | } 352 | return true; 353 | } 354 | 355 | FileType get_path_type(const string &path, struct stat *statFile, struct stat *statLink) { 356 | FileType fileType; 357 | if (lstat(path.c_str(), statFile) == 0) { 358 | if (S_ISLNK(statFile->st_mode)) { 359 | if (stat(path.c_str(), statLink) == 0) { 360 | if (S_ISDIR(statLink->st_mode)) { 361 | fileType = FileType::link2dir; 362 | } else { 363 | fileType = FileType::link2file; 364 | } 365 | } else { 366 | fileType = FileType::unknown; 367 | } 368 | } else if (S_ISDIR(statFile->st_mode)) { 369 | fileType = FileType::dir; 370 | } else if (S_ISREG(statFile->st_mode)) { 371 | fileType = FileType::file; 372 | } else { 373 | fileType = FileType::unknown; 374 | } 375 | } else { 376 | fileType = FileType::unknown; 377 | } 378 | return fileType; 379 | } 380 | 381 | bool 382 | traverse_path(const std::string &rootPath, 383 | const function &callback, 385 | const vector &whitePath) { 386 | auto isInWhite = [&](const string &path) { 387 | for (const auto &child: whitePath) { 388 | if (child == path) { 389 | return true; 390 | } 391 | } 392 | return false; 393 | }; 394 | struct stat sb{}; 395 | std::stack pathStack; 396 | if (lstat(rootPath.c_str(), &sb) < 0) { 397 | return false; 398 | } 399 | if (!S_ISDIR(sb.st_mode)) { 400 | return false; 401 | } 402 | pathStack.push(rootPath); 403 | while (!pathStack.empty()) { 404 | string top = pathStack.top(); 405 | pathStack.pop(); 406 | DIR *dp = nullptr; 407 | if ((dp = opendir(top.c_str())) == nullptr) { 408 | return false; 409 | } 410 | struct dirent *dirp = nullptr; 411 | while ((dirp = readdir(dp)) != nullptr) { 412 | if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0) { 413 | continue; 414 | } 415 | string path = top + "/" + dirp->d_name; 416 | if (isInWhite(path)) { 417 | continue; 418 | } 419 | 420 | struct stat statFile; 421 | struct stat statLink; 422 | FileType fileType = get_path_type(path, &statFile, &statLink); 423 | if (!callback(path, dirp->d_name, dirp->d_type)) { 424 | return false; 425 | } 426 | 427 | if (fileType == FileType::dir) { 428 | pathStack.push(path); 429 | } 430 | } 431 | closedir(dp); 432 | } 433 | return true; 434 | } 435 | 436 | 437 | #define LINE_MAX 2048 438 | 439 | #ifdef __LP64__ 440 | #define __PRI_64_prefix "l" 441 | #define __PRI_PTR_prefix "l" 442 | #else 443 | #define __PRI_64_prefix "ll" 444 | #define __PRI_PTR_prefix 445 | #endif 446 | #define PRIxPTR __PRI_PTR_prefix "x" /* uintptr_t */ 447 | 448 | 449 | int MapsHelper::refresh_reg(const string &libPath, const string &wantPerm) { 450 | if (!get_process_maps(true, libPath, wantPerm)) { 451 | return -1; 452 | } 453 | return mapsInfo.size(); 454 | } 455 | 456 | int MapsHelper::refresh(const string &libPath, const string &wantPerm) { 457 | if (!get_process_maps(false, libPath, wantPerm)) { 458 | return -1; 459 | } 460 | return mapsInfo.size(); 461 | } 462 | 463 | void *MapsHelper::get_module_end(const string &libPath) { 464 | return get_module_end(libPath, false); 465 | } 466 | 467 | void *MapsHelper::get_module_end_reg(const string &libPath) { 468 | return get_module_end(libPath, true); 469 | } 470 | 471 | void *MapsHelper::get_module_end(const string &libPath, bool is_reg) { 472 | void *start = nullptr; 473 | for (const auto &item: mapsInfo) { 474 | if (item.path.find(libPath) == -1) { 475 | continue; 476 | } 477 | if ((uint64_t) item.region_start > (uint64_t) start) { 478 | start = item.region_start; 479 | } 480 | } 481 | return start; 482 | } 483 | 484 | void *MapsHelper::get_module_base(const string &libPath) { 485 | return get_module_base(libPath, false); 486 | } 487 | 488 | void *MapsHelper::get_module_base_reg(const string &libPath) { 489 | return get_module_base(libPath, true); 490 | } 491 | 492 | void *MapsHelper::get_module_base(const string &libPath, bool is_reg) { 493 | void *start = (void *) -1; 494 | for (const auto &item: mapsInfo) { 495 | if (item.path.find(libPath) == -1) { 496 | continue; 497 | } 498 | if ((uint64_t) item.region_start < (uint64_t) start) { 499 | start = item.region_start; 500 | } 501 | } 502 | return start == (void *) -1 ? nullptr : start; 503 | } 504 | 505 | bool MapsHelper::get_process_maps(bool is_reg, const string &libPath, const string &wantPerm) { 506 | mapsInfo.clear(); 507 | 508 | FILE *fp = fopen("/proc/self/maps", "r"); 509 | if (fp == nullptr) { 510 | return false; 511 | } 512 | 513 | while (!feof(fp)) { 514 | char line_buffer[LINE_MAX + 1]; 515 | if (!fgets(line_buffer, LINE_MAX, fp)) { 516 | break; 517 | } 518 | // ignore the rest of characters 519 | if (strlen(line_buffer) == LINE_MAX && line_buffer[LINE_MAX] != '\n') { 520 | // Entry not describing executable data. Skip to end of line to set up 521 | // reading the next entry. 522 | int c; 523 | do { 524 | c = getc(fp); 525 | } while ((c != EOF) && (c != '\n')); 526 | if (c == EOF) 527 | break; 528 | } 529 | 530 | void *region_start; 531 | void *region_end; 532 | void *region_offset; 533 | char permissions[5] = {'\0'}; // Ensure NUL-terminated string. 534 | uint8_t dev_major = 0; 535 | uint8_t dev_minor = 0; 536 | long inode = 0; 537 | int path_index = 0; 538 | // Sample format from man 5 proc: 539 | // 540 | // address perms offset dev inode pathname 541 | // 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm 542 | // 543 | // The final %n term captures the offset in the input string, which is used 544 | // to determine the path name. It *does not* increment the return value. 545 | // Refer to man 3 sscanf for details. 546 | if (sscanf(line_buffer, 547 | "%" PRIxPTR "-%" PRIxPTR " %4c " 548 | "%" PRIxPTR " %hhx:%hhx %ld %n", 549 | ®ion_start, ®ion_end, permissions, ®ion_offset, &dev_major, 550 | &dev_minor, 551 | &inode, 552 | &path_index) < 7) { 553 | fclose(fp); 554 | return false; 555 | } 556 | 557 | string path = &line_buffer[path_index]; 558 | if (path.ends_with("\n")) { 559 | path.pop_back(); 560 | } 561 | if ((!libPath.empty() && path.find(libPath) == -1) || 562 | (!wantPerm.empty() && wantPerm != permissions)) { 563 | continue; 564 | } 565 | 566 | MapsInfo info; 567 | info.region_start = region_start; 568 | info.region_end = region_end; 569 | info.region_offset = region_offset; 570 | info.permissions = permissions; 571 | info.path = path; 572 | mapsInfo.push_back(move(info)); 573 | } 574 | fclose(fp); 575 | 576 | return !mapsInfo.empty(); 577 | } 578 | -------------------------------------------------------------------------------- /module/src/main/cpp/third/utils/jni_helper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using std::string; 10 | using std::vector; 11 | 12 | jstring char2jstring(JNIEnv *env, const char *pat); 13 | 14 | jstring string2jstring(JNIEnv *env, const string &str); 15 | 16 | string jstring2string(JNIEnv *env, jstring jstr); 17 | 18 | vector get_java_list(JNIEnv *env, jobject obj_list); 19 | 20 | 21 | enum class method_name_type { 22 | short_name, 23 | long_name, 24 | pretty_name 25 | }; 26 | 27 | 28 | string get_object_class_name(JNIEnv *env, jobject obj); 29 | 30 | jthrowable clean_exception(JNIEnv *env); 31 | 32 | jstring get_package_name(JNIEnv *env); 33 | 34 | jobject get_application(JNIEnv *env); 35 | 36 | int get_sdk_int(); 37 | 38 | string prop_get_string(const string &key); 39 | 40 | #pragma clang diagnostic push 41 | #pragma clang diagnostic ignored "-Winvalid-partial-specialization" 42 | #pragma clang diagnostic ignored "-Wunknown-pragmas" 43 | #pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" 44 | 45 | #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ 46 | TypeName(const TypeName &) = delete; \ 47 | void operator=(const TypeName &) = delete 48 | 49 | namespace lsplant { 50 | template class> 51 | struct is_instance : public std::false_type {}; 52 | 53 | template class U> 54 | struct is_instance, U> : public std::true_type {}; 55 | 56 | template class U> 57 | inline constexpr bool is_instance_v = is_instance::value; 58 | 59 | template 60 | concept JObject = std::is_base_of_v, std::remove_pointer_t>; 61 | 62 | template 63 | class ScopedLocalRef { 64 | public: 65 | using BaseType [[maybe_unused]] = T; 66 | 67 | ScopedLocalRef(JNIEnv *env, T local_ref) : env_(env), local_ref_(nullptr) { reset(local_ref); } 68 | 69 | ScopedLocalRef(ScopedLocalRef &&s) noexcept : ScopedLocalRef(s.env_, s.release()) {} 70 | 71 | template 72 | ScopedLocalRef(ScopedLocalRef &&s) noexcept : ScopedLocalRef(s.env_, (T)s.release()) {} 73 | 74 | explicit ScopedLocalRef(JNIEnv *env) noexcept : ScopedLocalRef(env, T{nullptr}) {} 75 | explicit ScopedLocalRef() noexcept : ScopedLocalRef(nullptr, T{nullptr}) {} 76 | 77 | ~ScopedLocalRef() { reset(); } 78 | 79 | void reset(T ptr = nullptr) { 80 | if (ptr != local_ref_) { 81 | if (local_ref_ != nullptr) { 82 | env_->DeleteLocalRef(local_ref_); 83 | } 84 | local_ref_ = ptr; 85 | } 86 | } 87 | 88 | [[nodiscard]] T release() { 89 | T localRef = local_ref_; 90 | local_ref_ = nullptr; 91 | return localRef; 92 | } 93 | 94 | T get() const { return local_ref_; } 95 | 96 | operator T() const { return local_ref_; } 97 | 98 | // We do not expose an empty constructor as it can easily lead to errors 99 | // using common idioms, e.g.: 100 | // ScopedLocalRef<...> ref; 101 | // ref.reset(...); 102 | // Move assignment operator. 103 | ScopedLocalRef &operator=(ScopedLocalRef &&s) noexcept { 104 | reset(s.release()); 105 | env_ = s.env_; 106 | return *this; 107 | } 108 | 109 | operator bool() const { return local_ref_; } 110 | 111 | template 112 | friend class ScopedLocalRef; 113 | 114 | friend class JUTFString; 115 | 116 | private: 117 | JNIEnv *env_; 118 | T local_ref_; 119 | DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef); 120 | }; 121 | 122 | template 123 | concept JArray = std::is_base_of_v, std::remove_pointer_t>; 124 | 125 | template 126 | class ScopedLocalRef; 127 | 128 | class JNIScopeFrame { 129 | JNIEnv *env_; 130 | 131 | DISALLOW_COPY_AND_ASSIGN(JNIScopeFrame); 132 | 133 | public: 134 | JNIScopeFrame(JNIEnv *env, jint size) : env_(env) { env_->PushLocalFrame(size); } 135 | 136 | ~JNIScopeFrame() { env_->PopLocalFrame(nullptr); } 137 | }; 138 | 139 | class JNIMonitor { 140 | JNIEnv *env_; 141 | jobject obj_; 142 | 143 | DISALLOW_COPY_AND_ASSIGN(JNIMonitor); 144 | 145 | public: 146 | JNIMonitor(JNIEnv *env, jobject obj) : env_(env), obj_(obj) { env_->MonitorEnter(obj_); } 147 | 148 | ~JNIMonitor() { env_->MonitorExit(obj_); } 149 | }; 150 | 151 | template 152 | concept ScopeOrRaw = std::is_convertible_v || 153 | (is_instance_v, ScopedLocalRef> 154 | &&std::is_convertible_v::BaseType, U>); 155 | 156 | template 157 | concept ScopeOrClass = ScopeOrRaw; 158 | 159 | template 160 | concept ScopeOrObject = ScopeOrRaw; 161 | 162 | inline ScopedLocalRef ClearException(JNIEnv *env) { 163 | if (auto exception = env->ExceptionOccurred()) { 164 | env->ExceptionClear(); 165 | static jclass log = (jclass)env->NewGlobalRef(env->FindClass("android/util/Log")); 166 | static jmethodID toString = env->GetStaticMethodID( 167 | log, "getStackTraceString", "(Ljava/lang/Throwable;)Ljava/lang/String;"); 168 | auto str = (jstring)env->CallStaticObjectMethod(log, toString, exception); 169 | env->DeleteLocalRef(exception); 170 | return {env, str}; 171 | } 172 | return {env, nullptr}; 173 | } 174 | 175 | template 176 | [[maybe_unused]] inline auto UnwrapScope(T &&x) { 177 | if constexpr (std::is_same_v, std::string_view>) 178 | return x.data(); 179 | else if constexpr (is_instance_v, ScopedLocalRef>) 180 | return x.get(); 181 | else 182 | return std::forward(x); 183 | } 184 | 185 | template 186 | [[maybe_unused]] inline auto WrapScope(JNIEnv *env, T &&x) { 187 | if constexpr (std::is_convertible_v) { 188 | return ScopedLocalRef(env, std::forward(x)); 189 | } else 190 | return x; 191 | } 192 | 193 | template 194 | [[maybe_unused]] inline auto WrapScope(JNIEnv *env, std::tuple &&x, 195 | std::index_sequence) { 196 | return std::make_tuple(WrapScope(env, std::forward(std::get(x)))...); 197 | } 198 | 199 | template 200 | [[maybe_unused]] inline auto WrapScope(JNIEnv *env, std::tuple &&x) { 201 | return WrapScope(env, std::forward>(x), 202 | std::make_index_sequence()); 203 | } 204 | 205 | inline auto JNI_NewStringUTF(JNIEnv *env, std::string_view sv) { 206 | return ScopedLocalRef(env, env->NewStringUTF(sv.data())); 207 | } 208 | 209 | class JUTFString { 210 | public: 211 | inline JUTFString(JNIEnv *env, jstring jstr) : JUTFString(env, jstr, nullptr) {} 212 | inline JUTFString(JNIEnv *env, jobject jstr) : JUTFString(env, (jstring)jstr, nullptr) {} 213 | 214 | inline JUTFString(const ScopedLocalRef &jstr) 215 | : JUTFString(jstr.env_, jstr.local_ref_, nullptr) {} 216 | 217 | inline JUTFString(JNIEnv *env, jstring jstr, const char *default_cstr) 218 | : env_(env), jstr_(jstr) { 219 | if (env_ && jstr_) 220 | cstr_ = env_->GetStringUTFChars(jstr, nullptr); 221 | else 222 | cstr_ = default_cstr; 223 | } 224 | 225 | inline operator const char *() const { return cstr_; } 226 | 227 | inline operator const std::string() const { return cstr_; } 228 | 229 | inline operator const bool() const { return cstr_ != nullptr; } 230 | 231 | inline auto get() const { return cstr_; } 232 | 233 | inline ~JUTFString() { 234 | if (env_ && jstr_) env_->ReleaseStringUTFChars(jstr_, cstr_); 235 | } 236 | 237 | JUTFString(JUTFString &&other) 238 | : env_(std::move(other.env_)), 239 | jstr_(std::move(other.jstr_)), 240 | cstr_(std::move(other.cstr_)) { 241 | other.cstr_ = nullptr; 242 | } 243 | 244 | JUTFString &operator=(JUTFString &&other) { 245 | if (&other != this) { 246 | env_ = std::move(other.env_); 247 | jstr_ = std::move(other.jstr_); 248 | cstr_ = std::move(other.cstr_); 249 | other.cstr_ = nullptr; 250 | } 251 | return *this; 252 | } 253 | 254 | private: 255 | JNIEnv *env_; 256 | jstring jstr_; 257 | const char *cstr_; 258 | 259 | JUTFString(const JUTFString &) = delete; 260 | 261 | JUTFString &operator=(const JUTFString &) = delete; 262 | }; 263 | 264 | template 265 | requires(std::is_function_v) 266 | [[maybe_unused]] inline auto JNI_SafeInvoke(JNIEnv *env, Func JNIEnv::*f, Args &&...args) { 267 | struct finally { 268 | finally(JNIEnv *env) : env_(env) {} 269 | 270 | ~finally() { 271 | if (auto exception = ClearException(env_)) { 272 | __android_log_print(ANDROID_LOG_ERROR, 273 | #ifdef LOG_TAG 274 | LOG_TAG, 275 | #else 276 | "JNIHelper", 277 | #endif 278 | "%s", JUTFString(env_, exception.get()).get()); 279 | } 280 | } 281 | 282 | JNIEnv *env_; 283 | } _(env); 284 | 285 | if constexpr (!std::is_same_v(args)))...>>) 288 | return WrapScope(env, (env->*f)(UnwrapScope(std::forward(args))...)); 289 | else 290 | (env->*f)(UnwrapScope(std::forward(args))...); 291 | } 292 | 293 | // functions to class 294 | 295 | [[maybe_unused]] inline auto JNI_FindClass(JNIEnv *env, std::string_view name) { 296 | return JNI_SafeInvoke(env, &JNIEnv::FindClass, name); 297 | } 298 | 299 | template 300 | [[maybe_unused]] inline auto JNI_GetObjectClass(JNIEnv *env, const Object &obj) { 301 | return JNI_SafeInvoke(env, &JNIEnv::GetObjectClass, obj); 302 | } 303 | 304 | // functions to field 305 | 306 | template 307 | [[maybe_unused]] inline auto JNI_GetFieldID(JNIEnv *env, Class &&clazz, std::string_view name, 308 | std::string_view sig) { 309 | return JNI_SafeInvoke(env, &JNIEnv::GetFieldID, std::forward(clazz), name, sig); 310 | } 311 | 312 | // getters 313 | 314 | template 315 | [[maybe_unused]] inline auto JNI_GetObjectField(JNIEnv *env, Object &&obj, jfieldID fieldId) { 316 | return JNI_SafeInvoke(env, &JNIEnv::GetObjectField, std::forward(obj), fieldId); 317 | } 318 | 319 | template 320 | [[maybe_unused]] inline auto JNI_GetBooleanField(JNIEnv *env, Object &&obj, jfieldID fieldId) { 321 | return JNI_SafeInvoke(env, &JNIEnv::GetBooleanField, std::forward(obj), fieldId); 322 | } 323 | 324 | template 325 | [[maybe_unused]] inline auto JNI_GetByteField(JNIEnv *env, Object &&obj, jfieldID fieldId) { 326 | return JNI_SafeInvoke(env, &JNIEnv::GetByteField, std::forward(obj), fieldId); 327 | } 328 | 329 | template 330 | [[maybe_unused]] inline auto JNI_GetCharField(JNIEnv *env, Object &&obj, jfieldID fieldId) { 331 | return JNI_SafeInvoke(env, &JNIEnv::GetCharField, std::forward(obj), fieldId); 332 | } 333 | 334 | template 335 | [[maybe_unused]] inline auto JNI_GetShortField(JNIEnv *env, Object &&obj, jfieldID fieldId) { 336 | return JNI_SafeInvoke(env, &JNIEnv::GetShortField, std::forward(obj), fieldId); 337 | } 338 | 339 | template 340 | [[maybe_unused]] inline auto JNI_GetIntField(JNIEnv *env, Object &&obj, jfieldID fieldId) { 341 | return JNI_SafeInvoke(env, &JNIEnv::GetIntField, std::forward(obj), fieldId); 342 | } 343 | 344 | template 345 | [[maybe_unused]] inline auto JNI_GetLongField(JNIEnv *env, Object &&obj, jfieldID fieldId) { 346 | return JNI_SafeInvoke(env, &JNIEnv::GetLongField, std::forward(obj), fieldId); 347 | } 348 | 349 | template 350 | [[maybe_unused]] inline auto JNI_GetFloatField(JNIEnv *env, Object &&obj, jfieldID fieldId) { 351 | return JNI_SafeInvoke(env, &JNIEnv::GetFloatField, std::forward(obj), fieldId); 352 | } 353 | 354 | template 355 | [[maybe_unused]] inline auto JNI_GetDoubleField(JNIEnv *env, Object &&obj, jfieldID fieldId) { 356 | return JNI_SafeInvoke(env, &JNIEnv::GetDoubleField, std::forward(obj), fieldId); 357 | } 358 | 359 | // setters 360 | 361 | template 362 | [[maybe_unused]] inline auto JNI_SetObjectField(JNIEnv *env, Object &&obj, jfieldID fieldId, 363 | const Value &value) { 364 | return JNI_SafeInvoke(env, &JNIEnv::SetObjectField, std::forward(obj), fieldId, value); 365 | } 366 | 367 | template 368 | [[maybe_unused]] inline auto JNI_SetBooleanField(JNIEnv *env, Object &&obj, jfieldID fieldId, 369 | jboolean value) { 370 | return JNI_SafeInvoke(env, &JNIEnv::SetBooleanField, std::forward(obj), fieldId, value); 371 | } 372 | 373 | template 374 | [[maybe_unused]] inline auto JNI_SetByteField(JNIEnv *env, Object &&obj, jfieldID fieldId, 375 | jbyte value) { 376 | return JNI_SafeInvoke(env, &JNIEnv::SetByteField, std::forward(obj), fieldId, value); 377 | } 378 | 379 | template 380 | [[maybe_unused]] inline auto JNI_SetCharField(JNIEnv *env, Object &&obj, jfieldID fieldId, 381 | jchar value) { 382 | return JNI_SafeInvoke(env, &JNIEnv::SetCharField, std::forward(obj), fieldId, value); 383 | } 384 | 385 | template 386 | [[maybe_unused]] inline auto JNI_SetShortField(JNIEnv *env, Object &&obj, jfieldID fieldId, 387 | jshort value) { 388 | return JNI_SafeInvoke(env, &JNIEnv::SetShortField, std::forward(obj), fieldId, value); 389 | } 390 | 391 | template 392 | [[maybe_unused]] inline auto JNI_SetIntField(JNIEnv *env, Object &&obj, jfieldID fieldId, 393 | jint value) { 394 | return JNI_SafeInvoke(env, &JNIEnv::SetIntField, std::forward(obj), fieldId, value); 395 | } 396 | 397 | template 398 | [[maybe_unused]] inline auto JNI_SetLongField(JNIEnv *env, Object &&obj, jfieldID fieldId, 399 | jlong value) { 400 | return JNI_SafeInvoke(env, &JNIEnv::SetLongField, std::forward(obj), fieldId, value); 401 | } 402 | 403 | template 404 | [[maybe_unused]] inline auto JNI_SetFloatField(JNIEnv *env, Object &&obj, jfieldID fieldId, 405 | jfloat value) { 406 | return JNI_SafeInvoke(env, &JNIEnv::SetFloatField, std::forward(obj), fieldId, value); 407 | } 408 | 409 | template 410 | [[maybe_unused]] inline auto JNI_SetDoubleField(JNIEnv *env, Object &&obj, jfieldID fieldId, 411 | jdouble value) { 412 | return JNI_SafeInvoke(env, &JNIEnv::SetDoubleField, std::forward(obj), fieldId, value); 413 | } 414 | 415 | // functions to static field 416 | 417 | template 418 | [[maybe_unused]] inline auto JNI_GetStaticFieldID(JNIEnv *env, Class &&clazz, std::string_view name, 419 | std::string_view sig) { 420 | return JNI_SafeInvoke(env, &JNIEnv::GetStaticFieldID, std::forward(clazz), name, sig); 421 | } 422 | 423 | // getters 424 | 425 | template 426 | [[maybe_unused]] inline auto JNI_GetStaticObjectField(JNIEnv *env, Class &&clazz, 427 | jfieldID fieldId) { 428 | return JNI_SafeInvoke(env, &JNIEnv::GetStaticObjectField, std::forward(clazz), fieldId); 429 | } 430 | 431 | template 432 | [[maybe_unused]] inline auto JNI_GetStaticBooleanField(JNIEnv *env, Class &&clazz, 433 | jfieldID fieldId) { 434 | return JNI_SafeInvoke(env, &JNIEnv::GetStaticBooleanField, std::forward(clazz), fieldId); 435 | } 436 | 437 | template 438 | [[maybe_unused]] inline auto JNI_GetStaticByteField(JNIEnv *env, Class &&clazz, jfieldID fieldId) { 439 | return JNI_SafeInvoke(env, &JNIEnv::GetStaticByteField, std::forward(clazz), fieldId); 440 | } 441 | 442 | template 443 | [[maybe_unused]] inline auto JNI_GetStaticCharField(JNIEnv *env, Class &&clazz, jfieldID fieldId) { 444 | return JNI_SafeInvoke(env, &JNIEnv::GetStaticCharField, std::forward(clazz), fieldId); 445 | } 446 | 447 | template 448 | [[maybe_unused]] inline auto JNI_GetStaticShortField(JNIEnv *env, Class &&clazz, jfieldID fieldId) { 449 | return JNI_SafeInvoke(env, &JNIEnv::GetStaticShortField, std::forward(clazz), fieldId); 450 | } 451 | 452 | template 453 | [[maybe_unused]] inline auto JNI_GetStaticIntField(JNIEnv *env, Class &&clazz, jfieldID fieldId) { 454 | return JNI_SafeInvoke(env, &JNIEnv::GetStaticIntField, std::forward(clazz), fieldId); 455 | } 456 | 457 | template 458 | [[maybe_unused]] inline auto JNI_GetStaticLongField(JNIEnv *env, Class &&clazz, jfieldID fieldId) { 459 | return JNI_SafeInvoke(env, &JNIEnv::GetStaticLongField, std::forward(clazz), fieldId); 460 | } 461 | 462 | template 463 | [[maybe_unused]] inline auto JNI_GetStaticFloatField(JNIEnv *env, Class &&clazz, jfieldID fieldId) { 464 | return JNI_SafeInvoke(env, &JNIEnv::GetStaticFloatField, std::forward(clazz), fieldId); 465 | } 466 | 467 | template 468 | [[maybe_unused]] inline auto JNI_GetStaticDoubleField(JNIEnv *env, Class &&clazz, 469 | jfieldID fieldId) { 470 | return JNI_SafeInvoke(env, &JNIEnv::GetStaticDoubleField, std::forward(clazz), fieldId); 471 | } 472 | 473 | // setters 474 | 475 | template 476 | [[maybe_unused]] inline auto JNI_SetStaticObjectField(JNIEnv *env, Class &&clazz, jfieldID fieldId, 477 | const Object &value) { 478 | return JNI_SafeInvoke(env, &JNIEnv::SetStaticObjectField, std::forward(clazz), fieldId, 479 | value); 480 | } 481 | 482 | template 483 | [[maybe_unused]] inline auto JNI_SetStaticBooleanField(JNIEnv *env, Class &&clazz, jfieldID fieldId, 484 | jboolean value) { 485 | return JNI_SafeInvoke(env, &JNIEnv::SetStaticBooleanField, std::forward(clazz), fieldId, 486 | value); 487 | } 488 | 489 | template 490 | [[maybe_unused]] inline auto JNI_SetStaticByteField(JNIEnv *env, Class &&clazz, jfieldID fieldId, 491 | jbyte value) { 492 | return JNI_SafeInvoke(env, &JNIEnv::SetStaticByteField, std::forward(clazz), fieldId, 493 | value); 494 | } 495 | 496 | template 497 | [[maybe_unused]] inline auto JNI_SetStaticCharField(JNIEnv *env, Class &&clazz, jfieldID fieldId, 498 | jchar value) { 499 | return JNI_SafeInvoke(env, &JNIEnv::SetStaticCharField, std::forward(clazz), fieldId, 500 | value); 501 | } 502 | 503 | template 504 | [[maybe_unused]] inline auto JNI_SetStaticShortField(JNIEnv *env, Class &&clazz, jfieldID fieldId, 505 | jshort value) { 506 | return JNI_SafeInvoke(env, &JNIEnv::SetStaticShortField, std::forward(clazz), fieldId, 507 | value); 508 | } 509 | 510 | template 511 | [[maybe_unused]] inline auto JNI_SetStaticIntField(JNIEnv *env, Class &&clazz, jfieldID fieldId, 512 | jint value) { 513 | return JNI_SafeInvoke(env, &JNIEnv::SetStaticIntField, std::forward(clazz), fieldId, 514 | value); 515 | } 516 | 517 | template 518 | [[maybe_unused]] inline auto JNI_SetStaticLongField(JNIEnv *env, Class &&clazz, jfieldID fieldId, 519 | jlong value) { 520 | return JNI_SafeInvoke(env, &JNIEnv::SetStaticLongField, std::forward(clazz), fieldId, 521 | value); 522 | } 523 | 524 | template 525 | [[maybe_unused]] inline auto JNI_SetStaticFloatField(JNIEnv *env, Class &&clazz, jfieldID fieldId, 526 | jfloat value) { 527 | return JNI_SafeInvoke(env, &JNIEnv::SetStaticFloatField, std::forward(clazz), fieldId, 528 | value); 529 | } 530 | 531 | template 532 | [[maybe_unused]] inline auto JNI_SetStaticDoubleField(JNIEnv *env, Class &&clazz, jfieldID fieldId, 533 | jdouble value) { 534 | return JNI_SafeInvoke(env, &JNIEnv::SetStaticDoubleField, std::forward(clazz), fieldId, 535 | value); 536 | } 537 | 538 | template 539 | [[maybe_unused]] inline auto JNI_ToReflectedMethod(JNIEnv *env, Class &&clazz, jmethodID method, 540 | jboolean isStatic = JNI_FALSE) { 541 | return JNI_SafeInvoke(env, &JNIEnv::ToReflectedMethod, std::forward(clazz), method, 542 | isStatic); 543 | } 544 | 545 | // functions to method 546 | 547 | // virtual methods 548 | 549 | template 550 | [[maybe_unused]] inline auto JNI_GetMethodID(JNIEnv *env, Class &&clazz, std::string_view name, 551 | std::string_view sig) { 552 | return JNI_SafeInvoke(env, &JNIEnv::GetMethodID, std::forward(clazz), name, sig); 553 | } 554 | 555 | template 556 | [[maybe_unused]] inline auto JNI_CallVoidMethod(JNIEnv *env, Object &&obj, jmethodID method, 557 | Args &&...args) { 558 | return JNI_SafeInvoke(env, &JNIEnv::CallVoidMethod, std::forward(obj), method, 559 | std::forward(args)...); 560 | } 561 | 562 | template 563 | [[maybe_unused]] inline auto JNI_CallObjectMethod(JNIEnv *env, Object &&obj, jmethodID method, 564 | Args &&...args) { 565 | return JNI_SafeInvoke(env, &JNIEnv::CallObjectMethod, std::forward(obj), method, 566 | std::forward(args)...); 567 | } 568 | 569 | template 570 | [[maybe_unused]] inline auto JNI_CallBooleanMethod(JNIEnv *env, Object &&obj, jmethodID method, 571 | Args &&...args) { 572 | return JNI_SafeInvoke(env, &JNIEnv::CallBooleanMethod, std::forward(obj), method, 573 | std::forward(args)...); 574 | } 575 | 576 | template 577 | [[maybe_unused]] inline auto JNI_CallByteMethod(JNIEnv *env, Object &&obj, jmethodID method, 578 | Args &&...args) { 579 | return JNI_SafeInvoke(env, &JNIEnv::CallByteMethod, std::forward(obj), method, 580 | std::forward(args)...); 581 | } 582 | 583 | template 584 | [[maybe_unused]] inline auto JNI_CallCharMethod(JNIEnv *env, Object &&obj, jmethodID method, 585 | Args &&...args) { 586 | return JNI_SafeInvoke(env, &JNIEnv::CallCharMethod, std::forward(obj), method, 587 | std::forward(args)...); 588 | } 589 | 590 | template 591 | [[maybe_unused]] inline auto JNI_CallShortMethod(JNIEnv *env, Object &&obj, jmethodID method, 592 | Args &&...args) { 593 | return JNI_SafeInvoke(env, &JNIEnv::CallShortMethod, std::forward(obj), method, 594 | std::forward(args)...); 595 | } 596 | 597 | template 598 | [[maybe_unused]] inline auto JNI_CallIntMethod(JNIEnv *env, Object &&obj, jmethodID method, 599 | Args &&...args) { 600 | return JNI_SafeInvoke(env, &JNIEnv::CallIntMethod, std::forward(obj), method, 601 | std::forward(args)...); 602 | } 603 | 604 | template 605 | [[maybe_unused]] inline auto JNI_CallLongMethod(JNIEnv *env, Object &&obj, jmethodID method, 606 | Args &&...args) { 607 | return JNI_SafeInvoke(env, &JNIEnv::CallLongMethod, std::forward(obj), method, 608 | std::forward(args)...); 609 | } 610 | 611 | template 612 | [[maybe_unused]] inline auto JNI_CallFloatMethod(JNIEnv *env, Object &&obj, jmethodID method, 613 | Args &&...args) { 614 | return JNI_SafeInvoke(env, &JNIEnv::CallFloatMethod, std::forward(obj), method, 615 | std::forward(args)...); 616 | } 617 | 618 | template 619 | [[maybe_unused]] inline auto JNI_CallDoubleMethod(JNIEnv *env, Object &&obj, jmethodID method, 620 | Args &&...args) { 621 | return JNI_SafeInvoke(env, &JNIEnv::CallDoubleMethod, std::forward(obj), method, 622 | std::forward(args)...); 623 | } 624 | 625 | // static methods 626 | 627 | template 628 | [[maybe_unused]] inline auto JNI_GetStaticMethodID(JNIEnv *env, Class &&clazz, 629 | std::string_view name, std::string_view sig) { 630 | return JNI_SafeInvoke(env, &JNIEnv::GetStaticMethodID, std::forward(clazz), name, sig); 631 | } 632 | 633 | template 634 | [[maybe_unused]] inline auto JNI_CallStaticVoidMethod(JNIEnv *env, Class &&clazz, jmethodID method, 635 | Args &&...args) { 636 | return JNI_SafeInvoke(env, &JNIEnv::CallStaticVoidMethod, std::forward(clazz), method, 637 | std::forward(args)...); 638 | } 639 | 640 | template 641 | [[maybe_unused]] inline auto JNI_CallStaticObjectMethod(JNIEnv *env, Class &&clazz, 642 | jmethodID method, Args &&...args) { 643 | return JNI_SafeInvoke(env, &JNIEnv::CallStaticObjectMethod, std::forward(clazz), method, 644 | std::forward(args)...); 645 | } 646 | 647 | template 648 | [[maybe_unused]] inline auto JNI_CallStaticBooleanMethod(JNIEnv *env, Class &&clazz, 649 | jmethodID method, Args &&...args) { 650 | return JNI_SafeInvoke(env, &JNIEnv::CallStaticBooleanMethod, std::forward(clazz), method, 651 | std::forward(args)...); 652 | } 653 | 654 | template 655 | [[maybe_unused]] inline auto JNI_CallStaticByteMethod(JNIEnv *env, Class &&clazz, jmethodID method, 656 | Args &&...args) { 657 | return JNI_SafeInvoke(env, &JNIEnv::CallStaticByteMethod, std::forward(clazz), method, 658 | std::forward(args)...); 659 | } 660 | 661 | template 662 | [[maybe_unused]] inline auto JNI_CallStaticCharMethod(JNIEnv *env, Class &&clazz, jmethodID method, 663 | Args &&...args) { 664 | return JNI_SafeInvoke(env, &JNIEnv::CallStaticCharMethod, std::forward(clazz), method, 665 | std::forward(args)...); 666 | } 667 | 668 | template 669 | [[maybe_unused]] inline auto JNI_CallStaticShortMethod(JNIEnv *env, Class &&clazz, jmethodID method, 670 | Args &&...args) { 671 | return JNI_SafeInvoke(env, &JNIEnv::CallStaticShortMethod, std::forward(clazz), method, 672 | std::forward(args)...); 673 | } 674 | 675 | template 676 | [[maybe_unused]] inline auto JNI_CallStaticIntMethod(JNIEnv *env, Class &&clazz, jmethodID method, 677 | Args &&...args) { 678 | return JNI_SafeInvoke(env, &JNIEnv::CallStaticIntMethod, std::forward(clazz), method, 679 | std::forward(args)...); 680 | } 681 | 682 | template 683 | [[maybe_unused]] inline auto JNI_CallStaticLongMethod(JNIEnv *env, Class &&clazz, jmethodID method, 684 | Args &&...args) { 685 | return JNI_SafeInvoke(env, &JNIEnv::CallStaticLongMethod, std::forward(clazz), method, 686 | std::forward(args)...); 687 | } 688 | 689 | template 690 | [[maybe_unused]] inline auto JNI_CallStaticFloatMethod(JNIEnv *env, Class &&clazz, jmethodID method, 691 | Args &&...args) { 692 | return JNI_SafeInvoke(env, &JNIEnv::CallStaticFloatMethod, std::forward(clazz), method, 693 | std::forward(args)...); 694 | } 695 | 696 | template 697 | [[maybe_unused]] inline auto JNI_CallStaticDoubleMethod(JNIEnv *env, Class &&clazz, 698 | jmethodID method, Args &&...args) { 699 | return JNI_SafeInvoke(env, &JNIEnv::CallStaticDoubleMethod, std::forward(clazz), method, 700 | std::forward(args)...); 701 | } 702 | 703 | // non-vritual methods 704 | 705 | template 706 | [[maybe_unused]] inline auto JNI_CallCallNonvirtualVoidMethod(JNIEnv *env, Object &&obj, 707 | Class &&clazz, jmethodID method, 708 | Args &&...args) { 709 | return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualVoidMethod, std::forward(obj), 710 | std::forward(clazz), method, std::forward(args)...); 711 | } 712 | 713 | template 714 | [[maybe_unused]] inline auto JNI_CallCallNonvirtualObjectMethod(JNIEnv *env, Object &&obj, 715 | Class &&clazz, jmethodID method, 716 | Args &&...args) { 717 | return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualObjectMethod, std::forward(obj), 718 | std::forward(clazz), method, std::forward(args)...); 719 | } 720 | 721 | template 722 | [[maybe_unused]] inline auto JNI_CallCallNonvirtualBooleanMethod(JNIEnv *env, Object &&obj, 723 | Class &&clazz, jmethodID method, 724 | Args &&...args) { 725 | return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualBooleanMethod, std::forward(obj), 726 | std::forward(clazz), method, std::forward(args)...); 727 | } 728 | 729 | template 730 | [[maybe_unused]] inline auto JNI_CallCallNonvirtualByteMethod(JNIEnv *env, Object &&obj, 731 | Class &&clazz, jmethodID method, 732 | Args &&...args) { 733 | return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualByteMethod, std::forward(obj), 734 | std::forward(clazz), method, std::forward(args)...); 735 | } 736 | 737 | template 738 | [[maybe_unused]] inline auto JNI_CallCallNonvirtualCharMethod(JNIEnv *env, Object &&obj, 739 | Class &&clazz, jmethodID method, 740 | Args &&...args) { 741 | return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualCharMethod, std::forward(obj), 742 | std::forward(clazz), method, std::forward(args)...); 743 | } 744 | 745 | template 746 | [[maybe_unused]] inline auto JNI_CallCallNonvirtualShortMethod(JNIEnv *env, Object &&obj, 747 | Class &&clazz, jmethodID method, 748 | Args &&...args) { 749 | return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualShortMethod, std::forward(obj), 750 | std::forward(clazz), method, std::forward(args)...); 751 | } 752 | 753 | template 754 | [[maybe_unused]] inline auto JNI_CallCallNonvirtualIntMethod(JNIEnv *env, Object &&obj, 755 | Class &&clazz, jmethodID method, 756 | Args &&...args) { 757 | return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualIntMethod, std::forward(obj), 758 | std::forward(clazz), method, std::forward(args)...); 759 | } 760 | 761 | template 762 | [[maybe_unused]] inline auto JNI_CallCallNonvirtualLongMethod(JNIEnv *env, Object &&obj, 763 | Class &&clazz, jmethodID method, 764 | Args &&...args) { 765 | return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualLongMethod, std::forward(obj), 766 | std::forward(clazz), method, std::forward(args)...); 767 | } 768 | 769 | template 770 | [[maybe_unused]] inline auto JNI_CallCallNonvirtualFloatMethod(JNIEnv *env, Object &&obj, 771 | Class &&clazz, jmethodID method, 772 | Args &&...args) { 773 | return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualFloatMethod, std::forward(obj), 774 | std::forward(clazz), method, std::forward(args)...); 775 | } 776 | 777 | template 778 | [[maybe_unused]] inline auto JNI_CallCallNonvirtualDoubleMethod(JNIEnv *env, Object &&obj, 779 | Class &&clazz, jmethodID method, 780 | Args &&...args) { 781 | return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualDoubleMethod, std::forward(obj), 782 | std::forward(clazz), method, std::forward(args)...); 783 | } 784 | 785 | template 786 | [[maybe_unused]] inline auto JNI_NewObject(JNIEnv *env, Class &&clazz, jmethodID method, 787 | Args &&...args) { 788 | return JNI_SafeInvoke(env, &JNIEnv::NewObject, std::forward(clazz), method, 789 | std::forward(args)...); 790 | } 791 | 792 | template 793 | [[maybe_unused]] inline auto JNI_NewDirectByteBuffer(JNIEnv *env, Args &&...args) { 794 | return JNI_SafeInvoke(env, &JNIEnv::NewDirectByteBuffer, std::forward(args)...); 795 | } 796 | 797 | template 798 | [[maybe_unused]] inline auto JNI_RegisterNatives(JNIEnv *env, Class &&clazz, 799 | const JNINativeMethod *methods, jint size) { 800 | return JNI_SafeInvoke(env, &JNIEnv::RegisterNatives, std::forward(clazz), methods, size); 801 | } 802 | 803 | template 804 | [[maybe_unused]] inline auto JNI_IsInstanceOf(JNIEnv *env, Object &&obj, Class &&clazz) { 805 | return JNI_SafeInvoke(env, &JNIEnv::IsInstanceOf, std::forward(obj), 806 | std::forward(clazz)); 807 | } 808 | 809 | template 810 | [[maybe_unused]] inline auto JNI_NewGlobalRef(JNIEnv *env, Object &&x) { 811 | return (decltype(UnwrapScope(std::forward(x))))env->NewGlobalRef( 812 | UnwrapScope(std::forward(x))); 813 | } 814 | 815 | template 816 | [[maybe_unused]] inline auto JNI_Cast(ScopedLocalRef &&x) requires( 817 | std::is_convertible_v) { 818 | return ScopedLocalRef(std::move(x)); 819 | } 820 | 821 | [[maybe_unused]] inline auto JNI_NewDirectByteBuffer(JNIEnv *env, void *address, jlong capacity) { 822 | return JNI_SafeInvoke(env, &JNIEnv::NewDirectByteBuffer, address, capacity); 823 | } 824 | 825 | template 826 | struct JArrayUnderlyingTypeHelper; 827 | 828 | template <> 829 | struct JArrayUnderlyingTypeHelper { 830 | using Type = ScopedLocalRef; 831 | }; 832 | 833 | template <> 834 | struct JArrayUnderlyingTypeHelper { 835 | using Type = jboolean; 836 | }; 837 | 838 | template <> 839 | struct JArrayUnderlyingTypeHelper { 840 | using Type = jbyte; 841 | }; 842 | 843 | template <> 844 | struct JArrayUnderlyingTypeHelper { 845 | using Type = jchar; 846 | }; 847 | 848 | template <> 849 | struct JArrayUnderlyingTypeHelper { 850 | using Type = jshort; 851 | }; 852 | 853 | template <> 854 | struct JArrayUnderlyingTypeHelper { 855 | using Type = jint; 856 | }; 857 | 858 | template <> 859 | struct JArrayUnderlyingTypeHelper { 860 | using Type = jlong; 861 | }; 862 | 863 | template <> 864 | struct JArrayUnderlyingTypeHelper { 865 | using Type = jfloat; 866 | }; 867 | 868 | template <> 869 | struct JArrayUnderlyingTypeHelper { 870 | using Type = jdouble; 871 | }; 872 | 873 | template 874 | using JArrayUnderlyingType = typename JArrayUnderlyingTypeHelper::Type; 875 | 876 | template 877 | class ScopedLocalRef { 878 | ScopedLocalRef(JNIEnv *env, T local_ref, size_t size, JArrayUnderlyingType *elements, 879 | bool modified) noexcept 880 | : env_(env), local_ref_(local_ref), size_(size), elements_(elements), modified_(modified) {} 881 | 882 | public: 883 | class Iterator { 884 | friend class ScopedLocalRef; 885 | Iterator(JArrayUnderlyingType *e) : e_(e) {} 886 | JArrayUnderlyingType *e_; 887 | 888 | public: 889 | auto &operator*() { return *e_; } 890 | auto *operator->() { return e_; } 891 | Iterator &operator++() { return ++e_, *this; } 892 | Iterator &operator--() { return --e_, *this; } 893 | Iterator operator++(int) { return Iterator(e_++); } 894 | Iterator operator--(int) { return Iterator(e_--); } 895 | bool operator==(const Iterator &other) const { return other.e_ == e_; } 896 | bool operator!=(const Iterator &other) const { return other.e_ != e_; } 897 | }; 898 | 899 | class ConstIterator { 900 | friend class ScopedLocalRef; 901 | ConstIterator(const JArrayUnderlyingType *e) : e_(e) {} 902 | const JArrayUnderlyingType *e_; 903 | 904 | public: 905 | const auto &operator*() { return *e_; } 906 | const auto *operator->() { return e_; } 907 | ConstIterator &operator++() { return ++e_, *this; } 908 | ConstIterator &operator--() { return --e_, *this; } 909 | ConstIterator operator++(int) { return ConstIterator(e_++); } 910 | ConstIterator operator--(int) { return ConstIterator(e_--); } 911 | bool operator==(const ConstIterator &other) const { return other.e_ == e_; } 912 | bool operator!=(const ConstIterator &other) const { return other.e_ != e_; } 913 | }; 914 | 915 | auto begin() { 916 | modified_ = true; 917 | return Iterator(elements_); 918 | } 919 | 920 | auto end() { 921 | modified_ = true; 922 | return Iterator(elements_ + size_); 923 | } 924 | 925 | const auto begin() const { return ConstIterator(elements_); } 926 | 927 | auto end() const { return ConstIterator(elements_ + size_); } 928 | 929 | const auto cbegin() const { return ConstIterator(elements_); } 930 | 931 | auto cend() const { return ConstIterator(elements_ + size_); } 932 | 933 | using BaseType [[maybe_unused]] = T; 934 | 935 | ScopedLocalRef(JNIEnv *env, T local_ref) noexcept : env_(env), local_ref_(nullptr) { 936 | reset(local_ref); 937 | } 938 | 939 | ScopedLocalRef(ScopedLocalRef &&s) noexcept 940 | : ScopedLocalRef(s.env_, s.local_ref_, s.size_, s.elements_, s.modified_) { 941 | s.local_ref_ = nullptr; 942 | s.size_ = 0; 943 | s.elements_ = nullptr; 944 | s.modified_ = false; 945 | } 946 | 947 | template 948 | ScopedLocalRef(ScopedLocalRef &&s) noexcept : ScopedLocalRef(s.env_, (T)s.release()) {} 949 | 950 | explicit ScopedLocalRef(JNIEnv *env) noexcept : ScopedLocalRef(env, T{nullptr}) {} 951 | 952 | ~ScopedLocalRef() { env_->DeleteLocalRef(release()); } 953 | 954 | void reset(T ptr = nullptr) { 955 | if (ptr != local_ref_) { 956 | if (local_ref_ != nullptr) { 957 | ReleaseElements(modified_ ? 0 : JNI_ABORT); 958 | env_->DeleteLocalRef(local_ref_); 959 | if constexpr (std::is_same_v) { 960 | for (size_t i = 0; i < size_; ++i) { 961 | elements_[i].~ScopedLocalRef(); 962 | } 963 | operator delete[](elements_); 964 | } 965 | elements_ = nullptr; 966 | } 967 | local_ref_ = ptr; 968 | size_ = local_ref_ ? env_->GetArrayLength(local_ref_) : 0; 969 | if (!local_ref_) return; 970 | if constexpr (std::is_same_v) { 971 | elements_ = static_cast *>(operator new[]( 972 | sizeof(ScopedLocalRef) * size_)); 973 | for (size_t i = 0; i < size_; ++i) { 974 | new (&elements_[i]) ScopedLocalRef( 975 | JNI_SafeInvoke(env_, &JNIEnv::GetObjectArrayElement, local_ref_, i)); 976 | } 977 | } else if constexpr (std::is_same_v) { 978 | elements_ = env_->GetBooleanArrayElements(local_ref_, nullptr); 979 | } else if constexpr (std::is_same_v) { 980 | elements_ = env_->GetByteArrayElements(local_ref_, nullptr); 981 | } else if constexpr (std::is_same_v) { 982 | elements_ = env_->GetCharArrayElements(local_ref_, nullptr); 983 | } else if constexpr (std::is_same_v) { 984 | elements_ = env_->GetShortArrayElements(local_ref_, nullptr); 985 | } else if constexpr (std::is_same_v) { 986 | elements_ = env_->GetIntArrayElements(local_ref_, nullptr); 987 | } else if constexpr (std::is_same_v) { 988 | elements_ = env_->GetLongArrayElements(local_ref_, nullptr); 989 | } else if constexpr (std::is_same_v) { 990 | elements_ = env_->GetFloatArrayElements(local_ref_, nullptr); 991 | } else if constexpr (std::is_same_v) { 992 | elements_ = env_->GetDoubleArrayElements(local_ref_, nullptr); 993 | } 994 | } 995 | } 996 | 997 | [[nodiscard]] T release() { 998 | T localRef = local_ref_; 999 | size_ = 0; 1000 | local_ref_ = nullptr; 1001 | ReleaseElements(modified_ ? 0 : JNI_ABORT); 1002 | if constexpr (std::is_same_v) { 1003 | for (size_t i = 0; i < size_; ++i) { 1004 | elements_[i].~ScopedLocalRef(); 1005 | } 1006 | operator delete[](elements_); 1007 | } 1008 | elements_ = nullptr; 1009 | return localRef; 1010 | } 1011 | 1012 | T get() const { return local_ref_; } 1013 | 1014 | explicit operator T() const { return local_ref_; } 1015 | 1016 | JArrayUnderlyingType &operator[](size_t index) { 1017 | modified_ = true; 1018 | return elements_[index]; 1019 | } 1020 | 1021 | const JArrayUnderlyingType &operator[](size_t index) const { return elements_[index]; } 1022 | 1023 | void commit() { 1024 | ReleaseElements(JNI_COMMIT); 1025 | modified_ = false; 1026 | } 1027 | 1028 | // We do not expose an empty constructor as it can easily lead to errors 1029 | // using common idioms, e.g.: 1030 | // ScopedLocalRef<...> ref; 1031 | // ref.reset(...); 1032 | // Move assignment operator. 1033 | ScopedLocalRef &operator=(ScopedLocalRef &&s) noexcept { 1034 | env_ = s.env_; 1035 | local_ref_ = s.local_ref_; 1036 | size_ = s.size_; 1037 | elements_ = s.elements_; 1038 | modified_ = s.modified_; 1039 | s.elements_ = nullptr; 1040 | s.size_ = 0; 1041 | s.modified_ = false; 1042 | s.local_ref_ = nullptr; 1043 | return *this; 1044 | } 1045 | 1046 | size_t size() const { return size_; } 1047 | 1048 | operator bool() const { return local_ref_; } 1049 | 1050 | template 1051 | friend class ScopedLocalRef; 1052 | 1053 | friend class JUTFString; 1054 | 1055 | private: 1056 | void ReleaseElements(jint mode) { 1057 | if (!local_ref_ || !elements_) return; 1058 | if constexpr (std::is_same_v) { 1059 | for (size_t i = 0; i < size_; ++i) { 1060 | JNI_SafeInvoke(env_, &JNIEnv::SetObjectArrayElement, local_ref_, i, elements_[i]); 1061 | } 1062 | } else if constexpr (std::is_same_v) { 1063 | env_->ReleaseBooleanArrayElements(local_ref_, elements_, mode); 1064 | } else if constexpr (std::is_same_v) { 1065 | env_->ReleaseByteArrayElements(local_ref_, elements_, mode); 1066 | } else if constexpr (std::is_same_v) { 1067 | env_->ReleaseCharArrayElements(local_ref_, elements_, mode); 1068 | } else if constexpr (std::is_same_v) { 1069 | env_->ReleaseShortArrayElements(local_ref_, elements_, mode); 1070 | } else if constexpr (std::is_same_v) { 1071 | env_->ReleaseIntArrayElements(local_ref_, elements_, mode); 1072 | } else if constexpr (std::is_same_v) { 1073 | env_->ReleaseLongArrayElements(local_ref_, elements_, mode); 1074 | } else if constexpr (std::is_same_v) { 1075 | env_->ReleaseFloatArrayElements(local_ref_, elements_, mode); 1076 | } else if constexpr (std::is_same_v) { 1077 | env_->ReleaseDoubleArrayElements(local_ref_, elements_, mode); 1078 | } 1079 | } 1080 | 1081 | JNIEnv *env_; 1082 | T local_ref_; 1083 | size_t size_; 1084 | JArrayUnderlyingType *elements_{nullptr}; 1085 | bool modified_ = false; 1086 | DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef); 1087 | }; 1088 | 1089 | // functions to array 1090 | 1091 | template Array> 1092 | [[maybe_unused]] inline auto JNI_GetArrayLength(JNIEnv *env, const Array &array) { 1093 | return JNI_SafeInvoke(env, &JNIEnv::GetArrayLength, array); 1094 | } 1095 | 1096 | // newers 1097 | 1098 | template 1099 | [[maybe_unused]] inline auto JNI_NewObjectArray(JNIEnv *env, jsize len, Class &&clazz, 1100 | const Object &init) { 1101 | return JNI_SafeInvoke(env, &JNIEnv::NewObjectArray, len, std::forward(clazz), init); 1102 | } 1103 | 1104 | [[maybe_unused]] inline auto JNI_NewBooleanArray(JNIEnv *env, jsize len) { 1105 | return JNI_SafeInvoke(env, &JNIEnv::NewBooleanArray, len); 1106 | } 1107 | 1108 | [[maybe_unused]] inline auto JNI_NewByteArray(JNIEnv *env, jsize len) { 1109 | return JNI_SafeInvoke(env, &JNIEnv::NewByteArray, len); 1110 | } 1111 | 1112 | [[maybe_unused]] inline auto JNI_NewCharArray(JNIEnv *env, jsize len) { 1113 | return JNI_SafeInvoke(env, &JNIEnv::NewCharArray, len); 1114 | } 1115 | 1116 | [[maybe_unused]] inline auto JNI_NewShortArray(JNIEnv *env, jsize len) { 1117 | return JNI_SafeInvoke(env, &JNIEnv::NewShortArray, len); 1118 | } 1119 | 1120 | [[maybe_unused]] inline auto JNI_NewIntArray(JNIEnv *env, jsize len) { 1121 | return JNI_SafeInvoke(env, &JNIEnv::NewIntArray, len); 1122 | } 1123 | 1124 | [[maybe_unused]] inline auto JNI_NewLongArray(JNIEnv *env, jsize len) { 1125 | return JNI_SafeInvoke(env, &JNIEnv::NewLongArray, len); 1126 | } 1127 | 1128 | [[maybe_unused]] inline auto JNI_NewFloatArray(JNIEnv *env, jsize len) { 1129 | return JNI_SafeInvoke(env, &JNIEnv::NewFloatArray, len); 1130 | } 1131 | 1132 | [[maybe_unused]] inline auto JNI_NewDoubleArray(JNIEnv *env, jsize len) { 1133 | return JNI_SafeInvoke(env, &JNIEnv::NewDoubleArray, len); 1134 | } 1135 | 1136 | template 1137 | [[maybe_unused]] inline auto JNI_GetObjectFieldOf(JNIEnv *env, Object &&object, 1138 | std::string_view field_name, 1139 | std::string_view field_class) { 1140 | auto &&o = std::forward(object); 1141 | return JNI_GetObjectField( 1142 | env, o, JNI_GetFieldID(env, JNI_GetObjectClass(env, o), field_name, field_class)); 1143 | } 1144 | 1145 | } // namespace lsplant 1146 | 1147 | #undef DISALLOW_COPY_AND_ASSIGN 1148 | 1149 | #pragma clang diagnostic pop 1150 | --------------------------------------------------------------------------------