├── module ├── .gitignore ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── cpp │ │ ├── game.h │ │ ├── hack.h │ │ ├── il2cpp_dump.h │ │ ├── log.h │ │ ├── xdl │ │ ├── xdl_linker.h │ │ ├── xdl_lzma.h │ │ ├── xdl_iterate.h │ │ ├── xdl_util.h │ │ ├── xdl_util.c │ │ ├── include │ │ │ └── xdl.h │ │ ├── xdl_lzma.c │ │ ├── xdl_linker.c │ │ ├── xdl_iterate.c │ │ └── xdl.c │ │ ├── CMakeLists.txt │ │ ├── main.cpp │ │ ├── il2cpp-class.h │ │ ├── il2cpp-tabledefs.h │ │ ├── hack.cpp │ │ ├── zygisk.hpp │ │ ├── il2cpp_dump.cpp │ │ └── il2cpp-api-functions.h └── build.gradle ├── .gitattributes ├── template └── magisk_module │ ├── META-INF │ └── com │ │ └── google │ │ └── android │ │ ├── updater-script │ │ └── update-binary │ └── module.prop ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle ├── .gitignore ├── module.gradle ├── README.zh-CN.md ├── .github └── workflows │ └── build.yml ├── README.md ├── LICENSE ├── gradle.properties ├── gradlew.bat └── gradlew /module/.gitignore: -------------------------------------------------------------------------------- 1 | /.externalNativeBuild 2 | /build 3 | /release -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.bat text eol=crlf 4 | *.jar binary -------------------------------------------------------------------------------- /template/magisk_module/META-INF/com/google/android/updater-script: -------------------------------------------------------------------------------- 1 | #MAGISK 2 | -------------------------------------------------------------------------------- /module/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArkanDash/Zygisk-Il2CppDumper/master/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':module' 2 | 3 | import org.apache.tools.ant.DirectoryScanner 4 | 5 | DirectoryScanner.removeDefaultExclude('**/.gitattributes') 6 | -------------------------------------------------------------------------------- /template/magisk_module/module.prop: -------------------------------------------------------------------------------- 1 | id=${id} 2 | name=${name} 3 | version=${version} 4 | versionCode=${versionCode} 5 | author=${author} 6 | description=${description} 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .idea 5 | /.idea/caches/build_file_checksums.ser 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | .DS_Store 10 | /build 11 | /captures 12 | /out 13 | .externalNativeBuild 14 | .cxx -------------------------------------------------------------------------------- /module/src/main/cpp/game.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Perfare on 2020/7/4. 3 | // 4 | 5 | #ifndef ZYGISK_IL2CPPDUMPER_GAME_H 6 | #define ZYGISK_IL2CPPDUMPER_GAME_H 7 | 8 | #define GamePackageName "com.game.packagename" 9 | 10 | #endif //ZYGISK_IL2CPPDUMPER_GAME_H 11 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon May 22 11:22:38 CST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /module.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | moduleLibraryName = "il2cppdumper" 3 | magiskModuleId = "zygisk_il2cppdumper" 4 | moduleName = "Il2CppDumper" 5 | moduleAuthor = "Perfare" 6 | moduleDescription = "Il2CppDumper Zygisk version." 7 | moduleVersion = "v1.2.0" 8 | moduleVersionCode = 1 9 | } 10 | -------------------------------------------------------------------------------- /module/src/main/cpp/hack.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Perfare on 2020/7/4. 3 | // 4 | 5 | #ifndef ZYGISK_IL2CPPDUMPER_HACK_H 6 | #define ZYGISK_IL2CPPDUMPER_HACK_H 7 | 8 | #include 9 | 10 | void hack_prepare(const char *game_data_dir, void *data, size_t length); 11 | 12 | #endif //ZYGISK_IL2CPPDUMPER_HACK_H 13 | -------------------------------------------------------------------------------- /module/src/main/cpp/il2cpp_dump.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Perfare on 2020/7/4. 3 | // 4 | 5 | #ifndef ZYGISK_IL2CPPDUMPER_IL2CPP_DUMP_H 6 | #define ZYGISK_IL2CPPDUMPER_IL2CPP_DUMP_H 7 | 8 | void il2cpp_api_init(void *handle); 9 | 10 | void il2cpp_dump(const char *outDir); 11 | 12 | #endif //ZYGISK_IL2CPPDUMPER_IL2CPP_DUMP_H 13 | -------------------------------------------------------------------------------- /module/src/main/cpp/log.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Perfare on 2020/7/4. 3 | // 4 | 5 | #ifndef ZYGISK_IL2CPPDUMPER_LOG_H 6 | #define ZYGISK_IL2CPPDUMPER_LOG_H 7 | 8 | #include 9 | 10 | #define LOG_TAG "Perfare" 11 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 12 | #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) 13 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 14 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 15 | 16 | #endif //ZYGISK_IL2CPPDUMPER_LOG_H 17 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # Zygisk-Il2CppDumper 2 | Zygisk版Il2CppDumper,在游戏运行时dump il2cpp数据,可以绕过保护,加密以及混淆。 3 | 4 | ## 如何食用 5 | 1. 安装[Magisk](https://github.com/topjohnwu/Magisk) v24以上版本并开启Zygisk 6 | 2. 生成模块 7 | - GitHub Actions 8 | 1. Fork这个项目 9 | 2. 在你fork的项目中选择**Actions**选项卡 10 | 3. 在左边的侧边栏中,单击**Build** 11 | 4. 选择**Run workflow** 12 | 5. 输入游戏包名并点击**Run workflow** 13 | 6. 等待操作完成并下载 14 | - Android Studio 15 | 1. 下载源码 16 | 2. 编辑`game.h`, 修改`GamePackageName`为游戏包名 17 | 3. 使用Android Studio运行gradle任务`:module:assembleRelease`编译,zip包会生成在`out`文件夹下 18 | 3. 在Magisk里安装模块 19 | 4. 启动游戏,会在`/data/data/GamePackageName/files/`目录下生成`dump.cs` -------------------------------------------------------------------------------- /template/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 34 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | package_name: 6 | description: "Package name of the game:" 7 | required: true 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-java@v3 15 | with: 16 | distribution: temurin 17 | java-version: 11 18 | cache: gradle 19 | - run: | 20 | chmod +x ./gradlew 21 | sed -i 's/moduleDescription = "/moduleDescription = "(${{ github.event.inputs.package_name }}) /g' module.gradle 22 | sed -i "s/com.game.packagename/${{ github.event.inputs.package_name }}/g" module/src/main/cpp/game.h 23 | ./gradlew :module:assembleRelease 24 | - uses: actions/upload-artifact@v3 25 | with: 26 | name: zygisk-il2cppdumper 27 | path: out/magisk_module_release/ 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zygisk-Il2CppDumper 2 | Il2CppDumper with Zygisk, dump il2cpp data at runtime, can bypass protection, encryption and obfuscation. 3 | 4 | 中文说明请戳[这里](README.zh-CN.md) 5 | 6 | ## How to use 7 | 1. Install [Magisk](https://github.com/topjohnwu/Magisk) v24 or later and enable Zygisk 8 | 2. Build module 9 | - GitHub Actions 10 | 1. Fork this repo 11 | 2. Go to the **Actions** tab in your forked repo 12 | 3. In the left sidebar, click the **Build** workflow. 13 | 4. Above the list of workflow runs, select **Run workflow** 14 | 5. Input the game package name and click **Run workflow** 15 | 6. Wait for the action to complete and download the artifact 16 | - Android Studio 17 | 1. Download the source code 18 | 2. Edit `game.h`, modify `GamePackageName` to the game package name 19 | 3. Use Android Studio to run the gradle task `:module:assembleRelease` to compile, the zip package will be generated in the `out` folder 20 | 3. Install module in Magisk 21 | 4. Start the game, `dump.cs` will be generated in the `/data/data/GamePackageName/files/` directory -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Rikka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /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. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec: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 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | -------------------------------------------------------------------------------- /module/src/main/cpp/xdl/xdl_linker.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2021-02-21. 23 | 24 | #ifndef IO_HEXHACKING_XDL_LINKER 25 | #define IO_HEXHACKING_XDL_LINKER 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | void xdl_linker_lock(void); 32 | void xdl_linker_unlock(void); 33 | 34 | void *xdl_linker_load(const char *filename); 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /module/src/main/cpp/xdl/xdl_lzma.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-11-08. 23 | 24 | #ifndef IO_HEXHACKING_XDL_LZMA 25 | #define IO_HEXHACKING_XDL_LZMA 26 | 27 | #include 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size); 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /module/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18.1) 2 | 3 | if (NOT DEFINED MODULE_NAME) 4 | message(FATAL_ERROR "MODULE_NAME is not set") 5 | else () 6 | project(${MODULE_NAME}) 7 | endif () 8 | 9 | message("Build type: ${CMAKE_BUILD_TYPE}") 10 | 11 | set(CMAKE_CXX_STANDARD 20) 12 | 13 | set(LINKER_FLAGS "-ffixed-x18 -Wl,--hash-style=both") 14 | set(C_FLAGS "-Werror=format -fdata-sections -ffunction-sections") 15 | set(CXX_FLAGS "${CXX_FLAGS} -fno-exceptions -fno-rtti") 16 | 17 | if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") 18 | set(C_FLAGS "${C_FLAGS} -O2 -fvisibility=hidden -fvisibility-inlines-hidden") 19 | set(LINKER_FLAGS "${LINKER_FLAGS} -Wl,-exclude-libs,ALL -Wl,--gc-sections -Wl,--strip-all") 20 | else () 21 | set(C_FLAGS "${C_FLAGS} -O0") 22 | endif () 23 | 24 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_FLAGS}") 25 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${C_FLAGS} ${CXX_FLAGS}") 26 | 27 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_FLAGS}") 28 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}") 29 | 30 | include_directories( 31 | xdl/include 32 | ) 33 | 34 | aux_source_directory(xdl xdl-src) 35 | 36 | add_library(${MODULE_NAME} SHARED 37 | main.cpp 38 | hack.cpp 39 | il2cpp_dump.cpp 40 | ${xdl-src}) 41 | target_link_libraries(${MODULE_NAME} log) 42 | 43 | if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") 44 | add_custom_command(TARGET ${MODULE_NAME} POST_BUILD 45 | COMMAND ${CMAKE_STRIP} --strip-all --remove-section=.comment "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/lib${MODULE_NAME}.so") 46 | endif () 47 | -------------------------------------------------------------------------------- /module/src/main/cpp/xdl/xdl_iterate.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-04. 23 | 24 | #ifndef IO_HEXHACKING_XDL_ITERATE 25 | #define IO_HEXHACKING_XDL_ITERATE 26 | 27 | #include 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | typedef int (*xdl_iterate_phdr_cb_t)(struct dl_phdr_info *info, size_t size, void *arg); 35 | int xdl_iterate_phdr_impl(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags); 36 | 37 | int xdl_iterate_get_full_pathname(uintptr_t base, char *buf, size_t buf_len); 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /module/src/main/cpp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "hack.h" 10 | #include "zygisk.hpp" 11 | #include "game.h" 12 | #include "log.h" 13 | 14 | using zygisk::Api; 15 | using zygisk::AppSpecializeArgs; 16 | using zygisk::ServerSpecializeArgs; 17 | 18 | class MyModule : public zygisk::ModuleBase { 19 | public: 20 | void onLoad(Api *api, JNIEnv *env) override { 21 | this->api = api; 22 | this->env = env; 23 | } 24 | 25 | void preAppSpecialize(AppSpecializeArgs *args) override { 26 | auto package_name = env->GetStringUTFChars(args->nice_name, nullptr); 27 | auto app_data_dir = env->GetStringUTFChars(args->app_data_dir, nullptr); 28 | preSpecialize(package_name, app_data_dir); 29 | env->ReleaseStringUTFChars(args->nice_name, package_name); 30 | env->ReleaseStringUTFChars(args->app_data_dir, app_data_dir); 31 | } 32 | 33 | void postAppSpecialize(const AppSpecializeArgs *) override { 34 | if (enable_hack) { 35 | std::thread hack_thread(hack_prepare, game_data_dir, data, length); 36 | hack_thread.detach(); 37 | } 38 | } 39 | 40 | private: 41 | Api *api; 42 | JNIEnv *env; 43 | bool enable_hack; 44 | char *game_data_dir; 45 | void *data; 46 | size_t length; 47 | 48 | void preSpecialize(const char *package_name, const char *app_data_dir) { 49 | if (strcmp(package_name, GamePackageName) == 0) { 50 | LOGI("detect game: %s", package_name); 51 | enable_hack = true; 52 | game_data_dir = new char[strlen(app_data_dir) + 1]; 53 | strcpy(game_data_dir, app_data_dir); 54 | 55 | #if defined(__i386__) 56 | auto path = "zygisk/armeabi-v7a.so"; 57 | #endif 58 | #if defined(__x86_64__) 59 | auto path = "zygisk/arm64-v8a.so"; 60 | #endif 61 | #if defined(__i386__) || defined(__x86_64__) 62 | int dirfd = api->getModuleDir(); 63 | int fd = openat(dirfd, path, O_RDONLY); 64 | if (fd != -1) { 65 | struct stat sb{}; 66 | fstat(fd, &sb); 67 | length = sb.st_size; 68 | data = mmap(nullptr, length, PROT_READ, MAP_PRIVATE, fd, 0); 69 | close(fd); 70 | } else { 71 | LOGW("Unable to open arm file"); 72 | } 73 | #endif 74 | } else { 75 | api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY); 76 | } 77 | } 78 | }; 79 | 80 | REGISTER_ZYGISK_MODULE(MyModule) -------------------------------------------------------------------------------- /module/src/main/cpp/xdl/xdl_util.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-04. 23 | 24 | #ifndef IO_HEXHACKING_XDL_UTIL 25 | #define IO_HEXHACKING_XDL_UTIL 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #ifndef __LP64__ 32 | #define XDL_UTIL_LINKER_BASENAME "linker" 33 | #define XDL_UTIL_LINKER_PATHNAME "/system/bin/linker" 34 | #define XDL_UTIL_APP_PROCESS_BASENAME "app_process32" 35 | #define XDL_UTIL_APP_PROCESS_PATHNAME "/system/bin/app_process32" 36 | #define XDL_UTIL_APP_PROCESS_BASENAME_K "app_process" 37 | #define XDL_UTIL_APP_PROCESS_PATHNAME_K "/system/bin/app_process" 38 | #else 39 | #define XDL_UTIL_LINKER_BASENAME "linker64" 40 | #define XDL_UTIL_LINKER_PATHNAME "/system/bin/linker64" 41 | #define XDL_UTIL_APP_PROCESS_BASENAME "app_process64" 42 | #define XDL_UTIL_APP_PROCESS_PATHNAME "/system/bin/app_process64" 43 | #endif 44 | #define XDL_UTIL_VDSO_BASENAME "[vdso]" 45 | 46 | #define XDL_UTIL_TEMP_FAILURE_RETRY(exp) \ 47 | ({ \ 48 | __typeof__(exp) _rc; \ 49 | do { \ 50 | errno = 0; \ 51 | _rc = (exp); \ 52 | } while (_rc == -1 && errno == EINTR); \ 53 | _rc; \ 54 | }) 55 | 56 | #ifdef __cplusplus 57 | extern "C" { 58 | #endif 59 | 60 | bool xdl_util_starts_with(const char *str, const char *start); 61 | bool xdl_util_ends_with(const char *str, const char *ending); 62 | 63 | size_t xdl_util_trim_ending(char *start); 64 | 65 | int xdl_util_get_api_level(void); 66 | 67 | #ifdef __cplusplus 68 | } 69 | #endif 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /module/src/main/cpp/xdl/xdl_util.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-04. 23 | 24 | #include "xdl_util.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | bool xdl_util_starts_with(const char *str, const char *start) { 37 | while (*str && *str == *start) { 38 | str++; 39 | start++; 40 | } 41 | 42 | return '\0' == *start; 43 | } 44 | 45 | bool xdl_util_ends_with(const char *str, const char *ending) { 46 | size_t str_len = strlen(str); 47 | size_t ending_len = strlen(ending); 48 | 49 | if (ending_len > str_len) return false; 50 | 51 | return 0 == strcmp(str + (str_len - ending_len), ending); 52 | } 53 | 54 | size_t xdl_util_trim_ending(char *start) { 55 | char *end = start + strlen(start); 56 | while (start < end && isspace((int)(*(end - 1)))) { 57 | end--; 58 | *end = '\0'; 59 | } 60 | return (size_t)(end - start); 61 | } 62 | 63 | static int xdl_util_get_api_level_from_build_prop(void) { 64 | char buf[128]; 65 | int api_level = -1; 66 | 67 | FILE *fp = fopen("/system/build.prop", "r"); 68 | if (NULL == fp) goto end; 69 | 70 | while (fgets(buf, sizeof(buf), fp)) { 71 | if (xdl_util_starts_with(buf, "ro.build.version.sdk=")) { 72 | api_level = atoi(buf + 21); 73 | break; 74 | } 75 | } 76 | fclose(fp); 77 | 78 | end: 79 | return (api_level > 0) ? api_level : -1; 80 | } 81 | 82 | int xdl_util_get_api_level(void) { 83 | static int xdl_util_api_level = -1; 84 | 85 | if (xdl_util_api_level < 0) { 86 | int api_level = android_get_device_api_level(); 87 | if (api_level < 0) 88 | api_level = xdl_util_get_api_level_from_build_prop(); // compatible with unusual models 89 | if (api_level < __ANDROID_API_J__) api_level = __ANDROID_API_J__; 90 | 91 | __atomic_store_n(&xdl_util_api_level, api_level, __ATOMIC_SEQ_CST); 92 | } 93 | 94 | return xdl_util_api_level; 95 | } 96 | -------------------------------------------------------------------------------- /module/src/main/cpp/xdl/include/xdl.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-04. 23 | 24 | // 25 | // xDL version: 1.2.1 26 | // 27 | // xDL is an enhanced implementation of the Android DL series functions. 28 | // For more information, documentation, and the latest version please check: 29 | // https://github.com/hexhacking/xDL 30 | // 31 | 32 | #ifndef IO_HEXHACKING_XDL 33 | #define IO_HEXHACKING_XDL 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #ifdef __cplusplus 40 | extern "C" { 41 | #endif 42 | 43 | typedef struct { 44 | // same as Dl_info: 45 | const char *dli_fname; // Pathname of shared object that contains address. 46 | void *dli_fbase; // Address at which shared object is loaded. 47 | const char *dli_sname; // Name of nearest symbol with address lower than addr. 48 | void *dli_saddr; // Exact address of symbol named in dli_sname. 49 | // added by xDL: 50 | size_t dli_ssize; // Symbol size of nearest symbol with address lower than addr. 51 | const ElfW(Phdr) *dlpi_phdr; // Pointer to array of ELF program headers for this object. 52 | size_t dlpi_phnum; // Number of items in dlpi_phdr. 53 | } xdl_info_t; 54 | 55 | // 56 | // Default value for flags in both xdl_open() and xdl_iterate_phdr(). 57 | // 58 | #define XDL_DEFAULT 0x00 59 | 60 | // 61 | // Enhanced dlopen() / dlclose() / dlsym(). 62 | // 63 | #define XDL_TRY_FORCE_LOAD 0x01 64 | #define XDL_ALWAYS_FORCE_LOAD 0x02 65 | void *xdl_open(const char *filename, int flags); 66 | void *xdl_close(void *handle); 67 | void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size); 68 | void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size); 69 | 70 | // 71 | // Enhanced dladdr(). 72 | // 73 | int xdl_addr(void *addr, xdl_info_t *info, void **cache); 74 | void xdl_addr_clean(void **cache); 75 | 76 | // 77 | // Enhanced dl_iterate_phdr(). 78 | // 79 | #define XDL_FULL_PATHNAME 0x01 80 | int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags); 81 | 82 | // 83 | // Custom dlinfo(). 84 | // 85 | #define XDL_DI_DLINFO 1 // type of info: xdl_info_t 86 | int xdl_info(void *handle, int request, void *info); 87 | 88 | #ifdef __cplusplus 89 | } 90 | #endif 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /module/build.gradle: -------------------------------------------------------------------------------- 1 | import org.apache.tools.ant.filters.FixCrLfFilter 2 | 3 | import java.nio.file.Paths 4 | import java.nio.file.Files 5 | 6 | apply plugin: 'com.android.library' 7 | apply from: file(rootProject.file('module.gradle')) 8 | 9 | android { 10 | compileSdkVersion rootProject.ext.targetSdkVersion 11 | ndkVersion '25.2.9519653' 12 | defaultConfig { 13 | minSdkVersion rootProject.ext.minSdkVersion 14 | targetSdkVersion rootProject.ext.targetSdkVersion 15 | externalNativeBuild { 16 | cmake { 17 | arguments "-DMODULE_NAME:STRING=$moduleLibraryName" 18 | } 19 | } 20 | } 21 | buildFeatures { 22 | prefab true 23 | } 24 | externalNativeBuild { 25 | cmake { 26 | path "src/main/cpp/CMakeLists.txt" 27 | version "3.22.1" 28 | } 29 | } 30 | } 31 | 32 | repositories { 33 | mavenLocal() 34 | } 35 | 36 | afterEvaluate { 37 | android.libraryVariants.forEach { variant -> 38 | def variantCapped = variant.name.capitalize() 39 | def variantLowered = variant.name.toLowerCase() 40 | 41 | def zipName = "${magiskModuleId.replace('_', '-')}-${moduleVersion}-${variantLowered}.zip" 42 | def magiskDir = file("$outDir/magisk_module_$variantLowered") 43 | 44 | task("prepareMagiskFiles${variantCapped}", type: Sync) { 45 | dependsOn("assemble$variantCapped") 46 | 47 | def templatePath = "$rootDir/template/magisk_module" 48 | 49 | into magiskDir 50 | from(templatePath) { 51 | exclude 'module.prop' 52 | } 53 | from(templatePath) { 54 | include 'module.prop' 55 | expand([ 56 | id : magiskModuleId, 57 | name : moduleName, 58 | version : moduleVersion, 59 | versionCode: moduleVersionCode.toString(), 60 | author : moduleAuthor, 61 | description: moduleDescription, 62 | ]) 63 | filter(FixCrLfFilter.class, 64 | eol: FixCrLfFilter.CrLf.newInstance("lf")) 65 | } 66 | from("$buildDir/intermediates/stripped_native_libs/$variantLowered/out/lib") { 67 | into 'lib' 68 | } 69 | doLast { 70 | file("$magiskDir/zygisk").mkdir() 71 | fileTree("$magiskDir/lib").visit { f -> 72 | if (!f.directory) return 73 | def srcPath = Paths.get("${f.file.absolutePath}/lib${moduleLibraryName}.so") 74 | def dstPath = Paths.get("$magiskDir/zygisk/${f.path}.so") 75 | Files.move(srcPath, dstPath) 76 | } 77 | new File("$magiskDir/lib").deleteDir() 78 | } 79 | } 80 | 81 | task("zip${variantCapped}", type: Zip) { 82 | dependsOn("prepareMagiskFiles${variantCapped}") 83 | from magiskDir 84 | archiveFileName.set(zipName) 85 | destinationDirectory.set(outDir) 86 | } 87 | 88 | task("push${variantCapped}", type: Exec) { 89 | dependsOn("zip${variantCapped}") 90 | workingDir outDir 91 | commandLine android.adbExecutable, "push", zipName, "/data/local/tmp/" 92 | } 93 | 94 | task("flash${variantCapped}", type: Exec) { 95 | dependsOn("push${variantCapped}") 96 | commandLine android.adbExecutable, "shell", "su", "-c", 97 | "magisk --install-module /data/local/tmp/${zipName}" 98 | } 99 | 100 | task("flashAndReboot${variantCapped}", type: Exec) { 101 | dependsOn("flash${variantCapped}") 102 | commandLine android.adbExecutable, "shell", "reboot" 103 | } 104 | 105 | variant.assembleProvider.get().finalizedBy("zip${variantCapped}") 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /module/src/main/cpp/il2cpp-class.h: -------------------------------------------------------------------------------- 1 | typedef uint16_t Il2CppChar; 2 | typedef uintptr_t il2cpp_array_size_t; 3 | typedef int32_t TypeDefinitionIndex; 4 | typedef int32_t GenericParameterIndex; 5 | typedef char Il2CppNativeChar; 6 | 7 | typedef struct Il2CppMemoryCallbacks Il2CppMemoryCallbacks; 8 | typedef struct Il2CppImage Il2CppImage; 9 | typedef struct Il2CppClass Il2CppClass; 10 | typedef struct Il2CppArrayBounds Il2CppArrayBounds; 11 | typedef struct Il2CppAssembly Il2CppAssembly; 12 | typedef struct Il2CppArrayType Il2CppArrayType; 13 | typedef struct Il2CppGenericClass Il2CppGenericClass; 14 | typedef struct Il2CppReflectionType Il2CppReflectionType; 15 | typedef struct MonitorData MonitorData; 16 | typedef Il2CppClass Il2CppVTable; 17 | typedef struct EventInfo EventInfo; 18 | typedef struct FieldInfo FieldInfo; 19 | typedef struct PropertyInfo PropertyInfo; 20 | typedef struct Il2CppDomain Il2CppDomain; 21 | typedef struct Il2CppException Il2CppException; 22 | typedef struct Il2CppObject Il2CppObject; 23 | typedef struct Il2CppReflectionMethod Il2CppReflectionMethod; 24 | typedef struct Il2CppString Il2CppString; 25 | typedef struct Il2CppThread Il2CppThread; 26 | typedef struct Il2CppStackFrameInfo Il2CppStackFrameInfo; 27 | typedef struct Il2CppManagedMemorySnapshot Il2CppManagedMemorySnapshot; 28 | typedef struct Il2CppDebuggerTransport Il2CppDebuggerTransport; 29 | typedef struct Il2CppMethodDebugInfo Il2CppMethodDebugInfo; 30 | typedef struct Il2CppCustomAttrInfo Il2CppCustomAttrInfo; 31 | typedef const struct ___Il2CppMetadataTypeHandle *Il2CppMetadataTypeHandle; 32 | typedef const struct ___Il2CppMetadataGenericParameterHandle *Il2CppMetadataGenericParameterHandle; 33 | 34 | typedef void (*Il2CppMethodPointer)(); 35 | 36 | typedef void (*il2cpp_register_object_callback)(Il2CppObject **arr, int size, void *userdata); 37 | 38 | typedef void *(*il2cpp_liveness_reallocate_callback)(void *ptr, size_t size, void *userdata); 39 | 40 | typedef void (*Il2CppFrameWalkFunc)(const Il2CppStackFrameInfo *info, void *user_data); 41 | 42 | typedef size_t(*Il2CppBacktraceFunc)(Il2CppMethodPointer *buffer, size_t maxSize); 43 | 44 | typedef const Il2CppNativeChar *(*Il2CppSetFindPlugInCallback)(const Il2CppNativeChar *); 45 | 46 | typedef void (*Il2CppLogCallback)(const char *); 47 | 48 | typedef enum { 49 | IL2CPP_UNHANDLED_POLICY_LEGACY, 50 | IL2CPP_UNHANDLED_POLICY_CURRENT 51 | } Il2CppRuntimeUnhandledExceptionPolicy; 52 | 53 | typedef enum { 54 | IL2CPP_GC_MODE_DISABLED = 0, 55 | IL2CPP_GC_MODE_ENABLED = 1, 56 | IL2CPP_GC_MODE_MANUAL = 2 57 | } Il2CppGCMode; 58 | 59 | typedef enum Il2CppStat { 60 | IL2CPP_STAT_NEW_OBJECT_COUNT, 61 | IL2CPP_STAT_INITIALIZED_CLASS_COUNT, 62 | IL2CPP_STAT_METHOD_COUNT, 63 | IL2CPP_STAT_CLASS_STATIC_DATA_SIZE, 64 | IL2CPP_STAT_GENERIC_INSTANCE_COUNT, 65 | IL2CPP_STAT_GENERIC_CLASS_COUNT, 66 | IL2CPP_STAT_INFLATED_METHOD_COUNT, 67 | IL2CPP_STAT_INFLATED_TYPE_COUNT, 68 | } Il2CppStat; 69 | 70 | typedef enum Il2CppTypeEnum { 71 | IL2CPP_TYPE_END = 0x00, 72 | IL2CPP_TYPE_VOID = 0x01, 73 | IL2CPP_TYPE_BOOLEAN = 0x02, 74 | IL2CPP_TYPE_CHAR = 0x03, 75 | IL2CPP_TYPE_I1 = 0x04, 76 | IL2CPP_TYPE_U1 = 0x05, 77 | IL2CPP_TYPE_I2 = 0x06, 78 | IL2CPP_TYPE_U2 = 0x07, 79 | IL2CPP_TYPE_I4 = 0x08, 80 | IL2CPP_TYPE_U4 = 0x09, 81 | IL2CPP_TYPE_I8 = 0x0a, 82 | IL2CPP_TYPE_U8 = 0x0b, 83 | IL2CPP_TYPE_R4 = 0x0c, 84 | IL2CPP_TYPE_R8 = 0x0d, 85 | IL2CPP_TYPE_STRING = 0x0e, 86 | IL2CPP_TYPE_PTR = 0x0f, 87 | IL2CPP_TYPE_BYREF = 0x10, 88 | IL2CPP_TYPE_VALUETYPE = 0x11, 89 | IL2CPP_TYPE_CLASS = 0x12, 90 | IL2CPP_TYPE_VAR = 0x13, 91 | IL2CPP_TYPE_ARRAY = 0x14, 92 | IL2CPP_TYPE_GENERICINST = 0x15, 93 | IL2CPP_TYPE_TYPEDBYREF = 0x16, 94 | IL2CPP_TYPE_I = 0x18, 95 | IL2CPP_TYPE_U = 0x19, 96 | IL2CPP_TYPE_FNPTR = 0x1b, 97 | IL2CPP_TYPE_OBJECT = 0x1c, 98 | IL2CPP_TYPE_SZARRAY = 0x1d, 99 | IL2CPP_TYPE_MVAR = 0x1e, 100 | IL2CPP_TYPE_CMOD_REQD = 0x1f, 101 | IL2CPP_TYPE_CMOD_OPT = 0x20, 102 | IL2CPP_TYPE_INTERNAL = 0x21, 103 | IL2CPP_TYPE_MODIFIER = 0x40, 104 | IL2CPP_TYPE_SENTINEL = 0x41, 105 | IL2CPP_TYPE_PINNED = 0x45, 106 | IL2CPP_TYPE_ENUM = 0x55, 107 | IL2CPP_TYPE_IL2CPP_TYPE_INDEX = 0xff 108 | } Il2CppTypeEnum; 109 | 110 | typedef struct Il2CppType { 111 | union { 112 | void *dummy; 113 | TypeDefinitionIndex klassIndex; 114 | const Il2CppType *type; 115 | Il2CppArrayType *array; 116 | GenericParameterIndex genericParameterIndex; 117 | Il2CppGenericClass *generic_class; 118 | } data; 119 | unsigned int attrs: 16; 120 | Il2CppTypeEnum type: 8; 121 | unsigned int num_mods: 6; 122 | unsigned int byref: 1; 123 | unsigned int pinned: 1; 124 | } Il2CppType; 125 | 126 | typedef struct MethodInfo { 127 | Il2CppMethodPointer methodPointer; 128 | } MethodInfo; 129 | 130 | typedef struct Il2CppObject { 131 | union { 132 | Il2CppClass *klass; 133 | Il2CppVTable *vtable; 134 | }; 135 | MonitorData *monitor; 136 | } Il2CppObject; 137 | 138 | typedef struct Il2CppArray { 139 | Il2CppObject obj; 140 | Il2CppArrayBounds *bounds; 141 | il2cpp_array_size_t max_length; 142 | void *vector[32]; 143 | } Il2CppArray; 144 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /module/src/main/cpp/xdl/xdl_lzma.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-11-08. 23 | 24 | #include "xdl_lzma.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "xdl.h" 36 | #include "xdl_util.h" 37 | 38 | // LZMA library pathname & symbol names 39 | #ifndef __LP64__ 40 | #define XDL_LZMA_PATHNAME "/system/lib/liblzma.so" 41 | #else 42 | #define XDL_LZMA_PATHNAME "/system/lib64/liblzma.so" 43 | #endif 44 | #define XDL_LZMA_SYM_CRCGEN "CrcGenerateTable" 45 | #define XDL_LZMA_SYM_CRC64GEN "Crc64GenerateTable" 46 | #define XDL_LZMA_SYM_CONSTRUCT "XzUnpacker_Construct" 47 | #define XDL_LZMA_SYM_ISFINISHED "XzUnpacker_IsStreamWasFinished" 48 | #define XDL_LZMA_SYM_FREE "XzUnpacker_Free" 49 | #define XDL_LZMA_SYM_CODE "XzUnpacker_Code" 50 | 51 | // LZMA data type definition 52 | #define SZ_OK 0 53 | typedef struct ISzAlloc ISzAlloc; 54 | typedef const ISzAlloc *ISzAllocPtr; 55 | struct ISzAlloc { 56 | void *(*Alloc)(ISzAllocPtr p, size_t size); 57 | void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */ 58 | }; 59 | typedef enum { 60 | CODER_STATUS_NOT_SPECIFIED, /* use main error code instead */ 61 | CODER_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ 62 | CODER_STATUS_NOT_FINISHED, /* stream was not finished */ 63 | CODER_STATUS_NEEDS_MORE_INPUT /* you must provide more input bytes */ 64 | } ECoderStatus; 65 | typedef enum { 66 | CODER_FINISH_ANY, /* finish at any point */ 67 | CODER_FINISH_END /* block must be finished at the end */ 68 | } ECoderFinishMode; 69 | 70 | // LZMA function type definition 71 | typedef void (*xdl_lzma_crcgen_t)(void); 72 | typedef void (*xdl_lzma_crc64gen_t)(void); 73 | typedef void (*xdl_lzma_construct_t)(void *, ISzAllocPtr); 74 | typedef int (*xdl_lzma_isfinished_t)(const void *); 75 | typedef void (*xdl_lzma_free_t)(void *); 76 | typedef int (*xdl_lzma_code_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, ECoderFinishMode, 77 | ECoderStatus *); 78 | typedef int (*xdl_lzma_code_q_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, int, 79 | ECoderFinishMode, ECoderStatus *); 80 | 81 | // LZMA function pointor 82 | static xdl_lzma_construct_t xdl_lzma_construct = NULL; 83 | static xdl_lzma_isfinished_t xdl_lzma_isfinished = NULL; 84 | static xdl_lzma_free_t xdl_lzma_free = NULL; 85 | static void *xdl_lzma_code = NULL; 86 | 87 | // LZMA init 88 | static void xdl_lzma_init() { 89 | void *lzma = xdl_open(XDL_LZMA_PATHNAME, XDL_TRY_FORCE_LOAD); 90 | if (NULL == lzma) return; 91 | 92 | xdl_lzma_crcgen_t crcgen = NULL; 93 | xdl_lzma_crc64gen_t crc64gen = NULL; 94 | if (NULL == (crcgen = (xdl_lzma_crcgen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRCGEN, NULL))) goto end; 95 | if (NULL == (crc64gen = (xdl_lzma_crc64gen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRC64GEN, NULL))) goto end; 96 | if (NULL == (xdl_lzma_construct = (xdl_lzma_construct_t)xdl_sym(lzma, XDL_LZMA_SYM_CONSTRUCT, NULL))) 97 | goto end; 98 | if (NULL == (xdl_lzma_isfinished = (xdl_lzma_isfinished_t)xdl_sym(lzma, XDL_LZMA_SYM_ISFINISHED, NULL))) 99 | goto end; 100 | if (NULL == (xdl_lzma_free = (xdl_lzma_free_t)xdl_sym(lzma, XDL_LZMA_SYM_FREE, NULL))) goto end; 101 | if (NULL == (xdl_lzma_code = xdl_sym(lzma, XDL_LZMA_SYM_CODE, NULL))) goto end; 102 | crcgen(); 103 | crc64gen(); 104 | 105 | end: 106 | xdl_close(lzma); 107 | } 108 | 109 | // LZMA internal alloc / free 110 | static void *xdl_lzma_internal_alloc(ISzAllocPtr p, size_t size) { 111 | (void)p; 112 | return malloc(size); 113 | } 114 | static void xdl_lzma_internal_free(ISzAllocPtr p, void *address) { 115 | (void)p; 116 | free(address); 117 | } 118 | 119 | int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size) { 120 | size_t src_offset = 0; 121 | size_t dst_offset = 0; 122 | size_t src_remaining; 123 | size_t dst_remaining; 124 | ISzAlloc alloc = {.Alloc = xdl_lzma_internal_alloc, .Free = xdl_lzma_internal_free}; 125 | long long state[4096 / sizeof(long long)]; // must be enough, 8-bit aligned 126 | ECoderStatus status; 127 | int api_level = xdl_util_get_api_level(); 128 | 129 | // init and check 130 | static bool inited = false; 131 | if (!inited) { 132 | xdl_lzma_init(); 133 | inited = true; 134 | } 135 | if (NULL == xdl_lzma_code) return -1; 136 | 137 | xdl_lzma_construct(&state, &alloc); 138 | 139 | *dst_size = 2 * src_size; 140 | *dst = NULL; 141 | do { 142 | *dst_size *= 2; 143 | if (NULL == (*dst = realloc(*dst, *dst_size))) { 144 | xdl_lzma_free(&state); 145 | return -1; 146 | } 147 | 148 | src_remaining = src_size - src_offset; 149 | dst_remaining = *dst_size - dst_offset; 150 | 151 | int result; 152 | if (api_level >= __ANDROID_API_Q__) { 153 | xdl_lzma_code_q_t lzma_code_q = (xdl_lzma_code_q_t)xdl_lzma_code; 154 | result = lzma_code_q(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining, 1, 155 | CODER_FINISH_ANY, &status); 156 | } else { 157 | xdl_lzma_code_t lzma_code = (xdl_lzma_code_t)xdl_lzma_code; 158 | result = lzma_code(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining, 159 | CODER_FINISH_ANY, &status); 160 | } 161 | if (SZ_OK != result) { 162 | free(*dst); 163 | xdl_lzma_free(&state); 164 | return -1; 165 | } 166 | 167 | src_offset += src_remaining; 168 | dst_offset += dst_remaining; 169 | } while (status == CODER_STATUS_NOT_FINISHED); 170 | 171 | xdl_lzma_free(&state); 172 | 173 | if (!xdl_lzma_isfinished(&state)) { 174 | free(*dst); 175 | return -1; 176 | } 177 | 178 | *dst_size = dst_offset; 179 | *dst = realloc(*dst, *dst_size); 180 | return 0; 181 | } 182 | -------------------------------------------------------------------------------- /module/src/main/cpp/il2cpp-tabledefs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Field Attributes (21.1.5). 5 | */ 6 | 7 | #define FIELD_ATTRIBUTE_FIELD_ACCESS_MASK 0x0007 8 | #define FIELD_ATTRIBUTE_COMPILER_CONTROLLED 0x0000 9 | #define FIELD_ATTRIBUTE_PRIVATE 0x0001 10 | #define FIELD_ATTRIBUTE_FAM_AND_ASSEM 0x0002 11 | #define FIELD_ATTRIBUTE_ASSEMBLY 0x0003 12 | #define FIELD_ATTRIBUTE_FAMILY 0x0004 13 | #define FIELD_ATTRIBUTE_FAM_OR_ASSEM 0x0005 14 | #define FIELD_ATTRIBUTE_PUBLIC 0x0006 15 | 16 | #define FIELD_ATTRIBUTE_STATIC 0x0010 17 | #define FIELD_ATTRIBUTE_INIT_ONLY 0x0020 18 | #define FIELD_ATTRIBUTE_LITERAL 0x0040 19 | #define FIELD_ATTRIBUTE_NOT_SERIALIZED 0x0080 20 | #define FIELD_ATTRIBUTE_SPECIAL_NAME 0x0200 21 | #define FIELD_ATTRIBUTE_PINVOKE_IMPL 0x2000 22 | 23 | /* For runtime use only */ 24 | #define FIELD_ATTRIBUTE_RESERVED_MASK 0x9500 25 | #define FIELD_ATTRIBUTE_RT_SPECIAL_NAME 0x0400 26 | #define FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL 0x1000 27 | #define FIELD_ATTRIBUTE_HAS_DEFAULT 0x8000 28 | #define FIELD_ATTRIBUTE_HAS_FIELD_RVA 0x0100 29 | 30 | /* 31 | * Method Attributes (22.1.9) 32 | */ 33 | 34 | #define METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK 0x0003 35 | #define METHOD_IMPL_ATTRIBUTE_IL 0x0000 36 | #define METHOD_IMPL_ATTRIBUTE_NATIVE 0x0001 37 | #define METHOD_IMPL_ATTRIBUTE_OPTIL 0x0002 38 | #define METHOD_IMPL_ATTRIBUTE_RUNTIME 0x0003 39 | 40 | #define METHOD_IMPL_ATTRIBUTE_MANAGED_MASK 0x0004 41 | #define METHOD_IMPL_ATTRIBUTE_UNMANAGED 0x0004 42 | #define METHOD_IMPL_ATTRIBUTE_MANAGED 0x0000 43 | 44 | #define METHOD_IMPL_ATTRIBUTE_FORWARD_REF 0x0010 45 | #define METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG 0x0080 46 | #define METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL 0x1000 47 | #define METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED 0x0020 48 | #define METHOD_IMPL_ATTRIBUTE_NOINLINING 0x0008 49 | #define METHOD_IMPL_ATTRIBUTE_MAX_METHOD_IMPL_VAL 0xffff 50 | 51 | #define METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK 0x0007 52 | #define METHOD_ATTRIBUTE_COMPILER_CONTROLLED 0x0000 53 | #define METHOD_ATTRIBUTE_PRIVATE 0x0001 54 | #define METHOD_ATTRIBUTE_FAM_AND_ASSEM 0x0002 55 | #define METHOD_ATTRIBUTE_ASSEM 0x0003 56 | #define METHOD_ATTRIBUTE_FAMILY 0x0004 57 | #define METHOD_ATTRIBUTE_FAM_OR_ASSEM 0x0005 58 | #define METHOD_ATTRIBUTE_PUBLIC 0x0006 59 | 60 | #define METHOD_ATTRIBUTE_STATIC 0x0010 61 | #define METHOD_ATTRIBUTE_FINAL 0x0020 62 | #define METHOD_ATTRIBUTE_VIRTUAL 0x0040 63 | #define METHOD_ATTRIBUTE_HIDE_BY_SIG 0x0080 64 | 65 | #define METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK 0x0100 66 | #define METHOD_ATTRIBUTE_REUSE_SLOT 0x0000 67 | #define METHOD_ATTRIBUTE_NEW_SLOT 0x0100 68 | 69 | #define METHOD_ATTRIBUTE_STRICT 0x0200 70 | #define METHOD_ATTRIBUTE_ABSTRACT 0x0400 71 | #define METHOD_ATTRIBUTE_SPECIAL_NAME 0x0800 72 | 73 | #define METHOD_ATTRIBUTE_PINVOKE_IMPL 0x2000 74 | #define METHOD_ATTRIBUTE_UNMANAGED_EXPORT 0x0008 75 | 76 | /* 77 | * For runtime use only 78 | */ 79 | #define METHOD_ATTRIBUTE_RESERVED_MASK 0xd000 80 | #define METHOD_ATTRIBUTE_RT_SPECIAL_NAME 0x1000 81 | #define METHOD_ATTRIBUTE_HAS_SECURITY 0x4000 82 | #define METHOD_ATTRIBUTE_REQUIRE_SEC_OBJECT 0x8000 83 | 84 | /* 85 | * Type Attributes (21.1.13). 86 | */ 87 | #define TYPE_ATTRIBUTE_VISIBILITY_MASK 0x00000007 88 | #define TYPE_ATTRIBUTE_NOT_PUBLIC 0x00000000 89 | #define TYPE_ATTRIBUTE_PUBLIC 0x00000001 90 | #define TYPE_ATTRIBUTE_NESTED_PUBLIC 0x00000002 91 | #define TYPE_ATTRIBUTE_NESTED_PRIVATE 0x00000003 92 | #define TYPE_ATTRIBUTE_NESTED_FAMILY 0x00000004 93 | #define TYPE_ATTRIBUTE_NESTED_ASSEMBLY 0x00000005 94 | #define TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM 0x00000006 95 | #define TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM 0x00000007 96 | 97 | #define TYPE_ATTRIBUTE_LAYOUT_MASK 0x00000018 98 | #define TYPE_ATTRIBUTE_AUTO_LAYOUT 0x00000000 99 | #define TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT 0x00000008 100 | #define TYPE_ATTRIBUTE_EXPLICIT_LAYOUT 0x00000010 101 | 102 | #define TYPE_ATTRIBUTE_CLASS_SEMANTIC_MASK 0x00000020 103 | #define TYPE_ATTRIBUTE_CLASS 0x00000000 104 | #define TYPE_ATTRIBUTE_INTERFACE 0x00000020 105 | 106 | #define TYPE_ATTRIBUTE_ABSTRACT 0x00000080 107 | #define TYPE_ATTRIBUTE_SEALED 0x00000100 108 | #define TYPE_ATTRIBUTE_SPECIAL_NAME 0x00000400 109 | 110 | #define TYPE_ATTRIBUTE_IMPORT 0x00001000 111 | #define TYPE_ATTRIBUTE_SERIALIZABLE 0x00002000 112 | 113 | #define TYPE_ATTRIBUTE_STRING_FORMAT_MASK 0x00030000 114 | #define TYPE_ATTRIBUTE_ANSI_CLASS 0x00000000 115 | #define TYPE_ATTRIBUTE_UNICODE_CLASS 0x00010000 116 | #define TYPE_ATTRIBUTE_AUTO_CLASS 0x00020000 117 | 118 | #define TYPE_ATTRIBUTE_BEFORE_FIELD_INIT 0x00100000 119 | #define TYPE_ATTRIBUTE_FORWARDER 0x00200000 120 | 121 | #define TYPE_ATTRIBUTE_RESERVED_MASK 0x00040800 122 | #define TYPE_ATTRIBUTE_RT_SPECIAL_NAME 0x00000800 123 | #define TYPE_ATTRIBUTE_HAS_SECURITY 0x00040000 124 | 125 | /* 126 | * Flags for Params (22.1.12) 127 | */ 128 | #define PARAM_ATTRIBUTE_IN 0x0001 129 | #define PARAM_ATTRIBUTE_OUT 0x0002 130 | #define PARAM_ATTRIBUTE_OPTIONAL 0x0010 131 | #define PARAM_ATTRIBUTE_RESERVED_MASK 0xf000 132 | #define PARAM_ATTRIBUTE_HAS_DEFAULT 0x1000 133 | #define PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL 0x2000 134 | #define PARAM_ATTRIBUTE_UNUSED 0xcfe0 135 | 136 | // Flags for Generic Parameters (II.23.1.7) 137 | #define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_NON_VARIANT 0x00 138 | #define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_COVARIANT 0x01 139 | #define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_CONTRAVARIANT 0x02 140 | #define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_VARIANCE_MASK 0x03 141 | #define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT 0x04 142 | #define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_NOT_NULLABLE_VALUE_TYPE_CONSTRAINT 0x08 143 | #define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_DEFAULT_CONSTRUCTOR_CONSTRAINT 0x10 144 | #define IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINT_MASK 0x1C 145 | 146 | /** 147 | * 21.5 AssemblyRefs 148 | */ 149 | #define ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG 0x00000001 150 | #define ASSEMBLYREF_RETARGETABLE_FLAG 0x00000100 151 | #define ASSEMBLYREF_ENABLEJITCOMPILE_TRACKING_FLAG 0x00008000 152 | #define ASSEMBLYREF_DISABLEJITCOMPILE_OPTIMIZER_FLAG 0x00004000 153 | -------------------------------------------------------------------------------- /module/src/main/cpp/xdl/xdl_linker.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2021-02-21. 23 | 24 | #include "xdl_linker.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "xdl.h" 32 | #include "xdl_iterate.h" 33 | #include "xdl_util.h" 34 | 35 | #define XDL_LINKER_SYM_MUTEX "__dl__ZL10g_dl_mutex" 36 | #define XDL_LINKER_SYM_DLOPEN_EXT_N "__dl__ZL10dlopen_extPKciPK17android_dlextinfoPv" 37 | #define XDL_LINKER_SYM_DO_DLOPEN_N "__dl__Z9do_dlopenPKciPK17android_dlextinfoPv" 38 | #define XDL_LINKER_SYM_DLOPEN_O "__dl__Z8__dlopenPKciPKv" 39 | #define XDL_LINKER_SYM_LOADER_DLOPEN_P "__loader_dlopen" 40 | 41 | typedef void *(*xdl_linker_dlopen_n_t)(const char *, int, const void *, void *); 42 | typedef void *(*xdl_linker_dlopen_o_t)(const char *, int, const void *); 43 | 44 | static pthread_mutex_t *xdl_linker_mutex = NULL; 45 | static void *xdl_linker_dlopen = NULL; 46 | 47 | static void *xdl_linker_caller_addr[] = { 48 | NULL, // default 49 | NULL, // art 50 | NULL // vendor 51 | }; 52 | 53 | #ifndef __LP64__ 54 | #define XDL_LINKER_LIB "lib" 55 | #else 56 | #define XDL_LINKER_LIB "lib64" 57 | #endif 58 | static const char *xdl_linker_vendor_path[] = { 59 | // order is important 60 | "/vendor/" XDL_LINKER_LIB "/egl/", "/vendor/" XDL_LINKER_LIB "/hw/", 61 | "/vendor/" XDL_LINKER_LIB "/", "/odm/" XDL_LINKER_LIB "/", 62 | "/vendor/" XDL_LINKER_LIB "/vndk-sp/", "/odm/" XDL_LINKER_LIB "/vndk-sp/"}; 63 | 64 | static void xdl_linker_init(void) { 65 | static bool inited = false; 66 | if (inited) return; 67 | inited = true; 68 | 69 | void *handle = xdl_open(XDL_UTIL_LINKER_BASENAME, XDL_DEFAULT); 70 | if (NULL == handle) return; 71 | 72 | int api_level = xdl_util_get_api_level(); 73 | if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) { 74 | // == Android 5.x 75 | xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL); 76 | } else if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) { 77 | // == Android 7.x 78 | xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_EXT_N, NULL); 79 | if (NULL == xdl_linker_dlopen) { 80 | xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DO_DLOPEN_N, NULL); 81 | xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL); 82 | } 83 | } else if (__ANDROID_API_O__ == api_level || __ANDROID_API_O_MR1__ == api_level) { 84 | // == Android 8.x 85 | xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_O, NULL); 86 | } else if (api_level >= __ANDROID_API_P__) { 87 | // >= Android 9.0 88 | xdl_linker_dlopen = xdl_sym(handle, XDL_LINKER_SYM_LOADER_DLOPEN_P, NULL); 89 | } 90 | 91 | xdl_close(handle); 92 | } 93 | 94 | void xdl_linker_lock(void) { 95 | xdl_linker_init(); 96 | 97 | if (NULL != xdl_linker_mutex) pthread_mutex_lock(xdl_linker_mutex); 98 | } 99 | 100 | void xdl_linker_unlock(void) { 101 | if (NULL != xdl_linker_mutex) pthread_mutex_unlock(xdl_linker_mutex); 102 | } 103 | 104 | static void *xdl_linker_get_caller_addr(struct dl_phdr_info *info) { 105 | for (size_t i = 0; i < info->dlpi_phnum; i++) { 106 | const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]); 107 | if (PT_LOAD == phdr->p_type) { 108 | return (void *)(info->dlpi_addr + phdr->p_vaddr); 109 | } 110 | } 111 | return NULL; 112 | } 113 | 114 | static int xdl_linker_get_caller_addr_cb(struct dl_phdr_info *info, size_t size, void *arg) { 115 | (void)size; 116 | 117 | size_t *vendor_match = (size_t *)arg; 118 | 119 | if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0; // continue 120 | 121 | if (NULL == xdl_linker_caller_addr[0] && xdl_util_ends_with(info->dlpi_name, "/libc.so")) 122 | xdl_linker_caller_addr[0] = xdl_linker_get_caller_addr(info); 123 | 124 | if (NULL == xdl_linker_caller_addr[1] && xdl_util_ends_with(info->dlpi_name, "/libart.so")) 125 | xdl_linker_caller_addr[1] = xdl_linker_get_caller_addr(info); 126 | 127 | if (0 != *vendor_match) { 128 | for (size_t i = 0; i < *vendor_match; i++) { 129 | if (xdl_util_starts_with(info->dlpi_name, xdl_linker_vendor_path[i])) { 130 | void *caller_addr = xdl_linker_get_caller_addr(info); 131 | if (NULL != caller_addr) { 132 | xdl_linker_caller_addr[2] = caller_addr; 133 | *vendor_match = i; 134 | } 135 | } 136 | } 137 | } 138 | 139 | if (NULL != xdl_linker_caller_addr[0] && NULL != xdl_linker_caller_addr[1] && 0 == *vendor_match) { 140 | return 1; // finish 141 | } else { 142 | return 0; // continue 143 | } 144 | } 145 | 146 | static void xdl_linker_load_caller_addr(void) { 147 | if (NULL == xdl_linker_caller_addr[0]) { 148 | size_t vendor_match = sizeof(xdl_linker_vendor_path) / sizeof(xdl_linker_vendor_path[0]); 149 | xdl_iterate_phdr_impl(xdl_linker_get_caller_addr_cb, &vendor_match, XDL_DEFAULT); 150 | } 151 | } 152 | 153 | void *xdl_linker_load(const char *filename) { 154 | int api_level = xdl_util_get_api_level(); 155 | 156 | if (api_level <= __ANDROID_API_M__) { 157 | // <= Android 6.0 158 | return dlopen(filename, RTLD_NOW); 159 | } else { 160 | xdl_linker_init(); 161 | if (NULL == xdl_linker_dlopen) return NULL; 162 | xdl_linker_load_caller_addr(); 163 | 164 | void *handle = NULL; 165 | if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) { 166 | // == Android 7.x 167 | xdl_linker_lock(); 168 | for (size_t i = 0; i < sizeof(xdl_linker_caller_addr) / sizeof(xdl_linker_caller_addr[0]); i++) { 169 | if (NULL != xdl_linker_caller_addr[i]) { 170 | handle = 171 | ((xdl_linker_dlopen_n_t)xdl_linker_dlopen)(filename, RTLD_NOW, NULL, xdl_linker_caller_addr[i]); 172 | if (NULL != handle) break; 173 | } 174 | } 175 | xdl_linker_unlock(); 176 | } else { 177 | // >= Android 8.0 178 | for (size_t i = 0; i < sizeof(xdl_linker_caller_addr) / sizeof(xdl_linker_caller_addr[0]); i++) { 179 | if (NULL != xdl_linker_caller_addr[i]) { 180 | handle = ((xdl_linker_dlopen_o_t)xdl_linker_dlopen)(filename, RTLD_NOW, xdl_linker_caller_addr[i]); 181 | if (NULL != handle) break; 182 | } 183 | } 184 | } 185 | return handle; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /module/src/main/cpp/hack.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Perfare on 2020/7/4. 3 | // 4 | 5 | #include "hack.h" 6 | #include "il2cpp_dump.h" 7 | #include "log.h" 8 | #include "xdl.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | void hack_start(const char *game_data_dir) { 21 | bool load = false; 22 | for (int i = 0; i < 10; i++) { 23 | void *handle = xdl_open("libil2cpp.so", 0); 24 | if (handle) { 25 | load = true; 26 | il2cpp_api_init(handle); 27 | il2cpp_dump(game_data_dir); 28 | break; 29 | } else { 30 | sleep(1); 31 | } 32 | } 33 | if (!load) { 34 | LOGI("libil2cpp.so not found in thread %d", gettid()); 35 | } 36 | } 37 | 38 | std::string GetLibDir(JavaVM *vms) { 39 | JNIEnv *env = nullptr; 40 | vms->AttachCurrentThread(&env, nullptr); 41 | jclass activity_thread_clz = env->FindClass("android/app/ActivityThread"); 42 | if (activity_thread_clz != nullptr) { 43 | jmethodID currentApplicationId = env->GetStaticMethodID(activity_thread_clz, 44 | "currentApplication", 45 | "()Landroid/app/Application;"); 46 | if (currentApplicationId) { 47 | jobject application = env->CallStaticObjectMethod(activity_thread_clz, 48 | currentApplicationId); 49 | jclass application_clazz = env->GetObjectClass(application); 50 | if (application_clazz) { 51 | jmethodID get_application_info = env->GetMethodID(application_clazz, 52 | "getApplicationInfo", 53 | "()Landroid/content/pm/ApplicationInfo;"); 54 | if (get_application_info) { 55 | jobject application_info = env->CallObjectMethod(application, 56 | get_application_info); 57 | jfieldID native_library_dir_id = env->GetFieldID( 58 | env->GetObjectClass(application_info), "nativeLibraryDir", 59 | "Ljava/lang/String;"); 60 | if (native_library_dir_id) { 61 | auto native_library_dir_jstring = (jstring) env->GetObjectField( 62 | application_info, native_library_dir_id); 63 | auto path = env->GetStringUTFChars(native_library_dir_jstring, nullptr); 64 | LOGI("lib dir %s", path); 65 | std::string lib_dir(path); 66 | env->ReleaseStringUTFChars(native_library_dir_jstring, path); 67 | return lib_dir; 68 | } else { 69 | LOGE("nativeLibraryDir not found"); 70 | } 71 | } else { 72 | LOGE("getApplicationInfo not found"); 73 | } 74 | } else { 75 | LOGE("application class not found"); 76 | } 77 | } else { 78 | LOGE("currentApplication not found"); 79 | } 80 | } else { 81 | LOGE("ActivityThread not found"); 82 | } 83 | return {}; 84 | } 85 | 86 | static std::string GetNativeBridgeLibrary() { 87 | auto value = std::array(); 88 | __system_property_get("ro.dalvik.vm.native.bridge", value.data()); 89 | return {value.data()}; 90 | } 91 | 92 | struct NativeBridgeCallbacks { 93 | uint32_t version; 94 | void *initialize; 95 | 96 | void *(*loadLibrary)(const char *libpath, int flag); 97 | 98 | void *(*getTrampoline)(void *handle, const char *name, const char *shorty, uint32_t len); 99 | 100 | void *isSupported; 101 | void *getAppEnv; 102 | void *isCompatibleWith; 103 | void *getSignalHandler; 104 | void *unloadLibrary; 105 | void *getError; 106 | void *isPathSupported; 107 | void *initAnonymousNamespace; 108 | void *createNamespace; 109 | void *linkNamespaces; 110 | 111 | void *(*loadLibraryExt)(const char *libpath, int flag, void *ns); 112 | }; 113 | 114 | bool NativeBridgeLoad(const char *game_data_dir, int api_level, void *data, size_t length) { 115 | //TODO 等待houdini初始化 116 | sleep(5); 117 | 118 | auto libart = dlopen("libart.so", RTLD_NOW); 119 | auto JNI_GetCreatedJavaVMs = (jint (*)(JavaVM **, jsize, jsize *)) dlsym(libart, 120 | "JNI_GetCreatedJavaVMs"); 121 | LOGI("JNI_GetCreatedJavaVMs %p", JNI_GetCreatedJavaVMs); 122 | JavaVM *vms_buf[1]; 123 | JavaVM *vms; 124 | jsize num_vms; 125 | jint status = JNI_GetCreatedJavaVMs(vms_buf, 1, &num_vms); 126 | if (status == JNI_OK && num_vms > 0) { 127 | vms = vms_buf[0]; 128 | } else { 129 | LOGE("GetCreatedJavaVMs error"); 130 | return false; 131 | } 132 | 133 | auto lib_dir = GetLibDir(vms); 134 | if (lib_dir.empty()) { 135 | LOGE("GetLibDir error"); 136 | return false; 137 | } 138 | if (lib_dir.find("/lib/x86") != std::string::npos) { 139 | LOGI("no need NativeBridge"); 140 | munmap(data, length); 141 | return false; 142 | } 143 | 144 | auto nb = dlopen("libhoudini.so", RTLD_NOW); 145 | if (!nb) { 146 | auto native_bridge = GetNativeBridgeLibrary(); 147 | LOGI("native bridge: %s", native_bridge.data()); 148 | nb = dlopen(native_bridge.data(), RTLD_NOW); 149 | } 150 | if (nb) { 151 | LOGI("nb %p", nb); 152 | auto callbacks = (NativeBridgeCallbacks *) dlsym(nb, "NativeBridgeItf"); 153 | if (callbacks) { 154 | LOGI("NativeBridgeLoadLibrary %p", callbacks->loadLibrary); 155 | LOGI("NativeBridgeLoadLibraryExt %p", callbacks->loadLibraryExt); 156 | LOGI("NativeBridgeGetTrampoline %p", callbacks->getTrampoline); 157 | 158 | int fd = syscall(__NR_memfd_create, "anon", MFD_CLOEXEC); 159 | ftruncate(fd, (off_t) length); 160 | void *mem = mmap(nullptr, length, PROT_WRITE, MAP_SHARED, fd, 0); 161 | memcpy(mem, data, length); 162 | munmap(mem, length); 163 | munmap(data, length); 164 | char path[PATH_MAX]; 165 | snprintf(path, PATH_MAX, "/proc/self/fd/%d", fd); 166 | LOGI("arm path %s", path); 167 | 168 | void *arm_handle; 169 | if (api_level >= 26) { 170 | arm_handle = callbacks->loadLibraryExt(path, RTLD_NOW, (void *) 3); 171 | } else { 172 | arm_handle = callbacks->loadLibrary(path, RTLD_NOW); 173 | } 174 | if (arm_handle) { 175 | LOGI("arm handle %p", arm_handle); 176 | auto init = (void (*)(JavaVM *, void *)) callbacks->getTrampoline(arm_handle, 177 | "JNI_OnLoad", 178 | nullptr, 0); 179 | LOGI("JNI_OnLoad %p", init); 180 | init(vms, (void *) game_data_dir); 181 | return true; 182 | } 183 | close(fd); 184 | } 185 | } 186 | return false; 187 | } 188 | 189 | void hack_prepare(const char *game_data_dir, void *data, size_t length) { 190 | LOGI("hack thread: %d", gettid()); 191 | int api_level = android_get_device_api_level(); 192 | LOGI("api level: %d", api_level); 193 | 194 | #if defined(__i386__) || defined(__x86_64__) 195 | if (!NativeBridgeLoad(game_data_dir, api_level, data, length)) { 196 | #endif 197 | hack_start(game_data_dir); 198 | #if defined(__i386__) || defined(__x86_64__) 199 | } 200 | #endif 201 | } 202 | 203 | #if defined(__arm__) || defined(__aarch64__) 204 | 205 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { 206 | auto game_data_dir = (const char *) reserved; 207 | std::thread hack_thread(hack_start, game_data_dir); 208 | hack_thread.detach(); 209 | return JNI_VERSION_1_6; 210 | } 211 | 212 | #endif -------------------------------------------------------------------------------- /module/src/main/cpp/xdl/xdl_iterate.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-04. 23 | 24 | #include "xdl_iterate.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "xdl.h" 40 | #include "xdl_linker.h" 41 | #include "xdl_util.h" 42 | 43 | /* 44 | * ========================================================================================================= 45 | * API-LEVEL ANDROID-VERSION SOLUTION 46 | * ========================================================================================================= 47 | * 16 4.1 /proc/self/maps 48 | * 17 4.2 /proc/self/maps 49 | * 18 4.3 /proc/self/maps 50 | * 19 4.4 /proc/self/maps 51 | * 20 4.4W /proc/self/maps 52 | * --------------------------------------------------------------------------------------------------------- 53 | * 21 5.0 dl_iterate_phdr() + __dl__ZL10g_dl_mutex + linker/linker64 from getauxval(3) 54 | * 22 5.1 dl_iterate_phdr() + __dl__ZL10g_dl_mutex + linker/linker64 from getauxval(3) 55 | * --------------------------------------------------------------------------------------------------------- 56 | * 23 >= 6.0 dl_iterate_phdr() + linker/linker64 from getauxval(3) 57 | * ========================================================================================================= 58 | */ 59 | 60 | extern __attribute((weak)) int dl_iterate_phdr(int (*)(struct dl_phdr_info *, size_t, void *), void *); 61 | extern __attribute((weak)) unsigned long int getauxval(unsigned long int); 62 | 63 | static uintptr_t xdl_iterate_get_min_vaddr(struct dl_phdr_info *info) { 64 | uintptr_t min_vaddr = UINTPTR_MAX; 65 | for (size_t i = 0; i < info->dlpi_phnum; i++) { 66 | const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]); 67 | if (PT_LOAD == phdr->p_type) { 68 | if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr; 69 | } 70 | } 71 | return min_vaddr; 72 | } 73 | 74 | static int xdl_iterate_open_or_rewind_maps(FILE **maps) { 75 | if (NULL == *maps) { 76 | *maps = fopen("/proc/self/maps", "r"); 77 | if (NULL == *maps) return -1; 78 | } else 79 | rewind(*maps); 80 | 81 | return 0; 82 | } 83 | 84 | static int xdl_iterate_get_pathname_from_maps(uintptr_t base, char *buf, size_t buf_len, FILE **maps) { 85 | // open or rewind maps-file 86 | if (0 != xdl_iterate_open_or_rewind_maps(maps)) return -1; // failed 87 | 88 | char line[1024]; 89 | while (fgets(line, sizeof(line), *maps)) { 90 | // check base address 91 | uintptr_t start, end; 92 | if (2 != sscanf(line, "%" SCNxPTR "-%" SCNxPTR " r", &start, &end)) continue; 93 | if (base < start) break; // failed 94 | if (base >= end) continue; 95 | 96 | // get pathname 97 | char *pathname = strchr(line, '/'); 98 | if (NULL == pathname) break; // failed 99 | xdl_util_trim_ending(pathname); 100 | 101 | // found it 102 | strlcpy(buf, pathname, buf_len); 103 | return 0; // OK 104 | } 105 | 106 | return -1; // failed 107 | } 108 | 109 | static int xdl_iterate_by_linker_cb(struct dl_phdr_info *info, size_t size, void *arg) { 110 | uintptr_t *pkg = (uintptr_t *)arg; 111 | xdl_iterate_phdr_cb_t cb = (xdl_iterate_phdr_cb_t)*pkg++; 112 | void *cb_arg = (void *)*pkg++; 113 | FILE **maps = (FILE **)*pkg++; 114 | uintptr_t linker_load_bias = *pkg++; 115 | int flags = (int)*pkg; 116 | 117 | // ignore invalid ELF 118 | if (0 == info->dlpi_addr || NULL == info->dlpi_name || '\0' == info->dlpi_name[0]) return 0; 119 | 120 | // ignore linker if we have returned it already 121 | if (linker_load_bias == info->dlpi_addr) return 0; 122 | 123 | struct dl_phdr_info info_fixed; 124 | info_fixed.dlpi_addr = info->dlpi_addr; 125 | info_fixed.dlpi_name = info->dlpi_name; 126 | info_fixed.dlpi_phdr = info->dlpi_phdr; 127 | info_fixed.dlpi_phnum = info->dlpi_phnum; 128 | info = &info_fixed; 129 | 130 | // fix dlpi_phdr & dlpi_phnum (from memory) 131 | if (NULL == info->dlpi_phdr || 0 == info->dlpi_phnum) { 132 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)info->dlpi_addr; 133 | info->dlpi_phdr = (ElfW(Phdr) *)(info->dlpi_addr + ehdr->e_phoff); 134 | info->dlpi_phnum = ehdr->e_phnum; 135 | } 136 | 137 | // fix dlpi_name (from /proc/self/maps) 138 | if ('/' != info->dlpi_name[0] && '[' != info->dlpi_name[0] && (0 != (flags & XDL_FULL_PATHNAME))) { 139 | // get base address 140 | uintptr_t min_vaddr = xdl_iterate_get_min_vaddr(info); 141 | if (UINTPTR_MAX == min_vaddr) return 0; // ignore this ELF 142 | uintptr_t base = (uintptr_t)(info->dlpi_addr + min_vaddr); 143 | 144 | char buf[1024]; 145 | if (0 != xdl_iterate_get_pathname_from_maps(base, buf, sizeof(buf), maps)) return 0; // ignore this ELF 146 | 147 | info->dlpi_name = (const char *)buf; 148 | } 149 | 150 | // callback 151 | return cb(info, size, cb_arg); 152 | } 153 | 154 | static uintptr_t xdl_iterate_get_linker_base(void) { 155 | if (NULL == getauxval) return 0; 156 | 157 | uintptr_t base = (uintptr_t)getauxval(AT_BASE); 158 | if (0 == base) return 0; 159 | if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) return 0; 160 | 161 | return base; 162 | } 163 | 164 | static int xdl_iterate_do_callback(xdl_iterate_phdr_cb_t cb, void *cb_arg, uintptr_t base, 165 | const char *pathname, uintptr_t *load_bias) { 166 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base; 167 | 168 | struct dl_phdr_info info; 169 | info.dlpi_name = pathname; 170 | info.dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff); 171 | info.dlpi_phnum = ehdr->e_phnum; 172 | 173 | // get load bias 174 | uintptr_t min_vaddr = xdl_iterate_get_min_vaddr(&info); 175 | if (UINTPTR_MAX == min_vaddr) return 0; // ignore invalid ELF 176 | info.dlpi_addr = (ElfW(Addr))(base - min_vaddr); 177 | if (NULL != load_bias) *load_bias = info.dlpi_addr; 178 | 179 | return cb(&info, sizeof(struct dl_phdr_info), cb_arg); 180 | } 181 | 182 | static int xdl_iterate_by_linker(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags) { 183 | if (NULL == dl_iterate_phdr) return 0; 184 | 185 | int api_level = xdl_util_get_api_level(); 186 | FILE *maps = NULL; 187 | int r; 188 | 189 | // dl_iterate_phdr(3) does NOT contain linker/linker64 when Android version < 8.1 (API level 27). 190 | // Here we always try to get linker base address from auxv. 191 | uintptr_t linker_load_bias = 0; 192 | uintptr_t linker_base = xdl_iterate_get_linker_base(); 193 | if (0 != linker_base) { 194 | if (0 != 195 | (r = xdl_iterate_do_callback(cb, cb_arg, linker_base, XDL_UTIL_LINKER_PATHNAME, &linker_load_bias))) 196 | return r; 197 | } 198 | 199 | // for other ELF 200 | uintptr_t pkg[5] = {(uintptr_t)cb, (uintptr_t)cb_arg, (uintptr_t)&maps, linker_load_bias, (uintptr_t)flags}; 201 | if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) xdl_linker_lock(); 202 | r = dl_iterate_phdr(xdl_iterate_by_linker_cb, pkg); 203 | if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) xdl_linker_unlock(); 204 | 205 | if (NULL != maps) fclose(maps); 206 | return r; 207 | } 208 | 209 | #if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__ 210 | static int xdl_iterate_by_maps(xdl_iterate_phdr_cb_t cb, void *cb_arg) { 211 | FILE *maps = fopen("/proc/self/maps", "r"); 212 | if (NULL == maps) return 0; 213 | 214 | int r = 0; 215 | char buf1[1024], buf2[1024]; 216 | char *line = buf1; 217 | uintptr_t prev_base = 0; 218 | bool try_next_line = false; 219 | 220 | while (fgets(line, sizeof(buf1), maps)) { 221 | // Try to find an ELF which loaded by linker. 222 | uintptr_t base, offset; 223 | char exec; 224 | if (3 != sscanf(line, "%" SCNxPTR "-%*" SCNxPTR " r%*c%cp %" SCNxPTR " ", &base, &exec, &offset)) goto clean; 225 | 226 | if ('-' == exec && 0 == offset) { 227 | // r--p 228 | prev_base = base; 229 | line = (line == buf1 ? buf2 : buf1); 230 | try_next_line = true; 231 | continue; 232 | } 233 | else if (exec == 'x') { 234 | // r-xp 235 | char *pathname = NULL; 236 | if (try_next_line && 0 != offset) { 237 | char *prev = (line == buf1 ? buf2 : buf1); 238 | char *prev_pathname = strchr(prev, '/'); 239 | if (NULL == prev_pathname) goto clean; 240 | 241 | pathname = strchr(line, '/'); 242 | if (NULL == pathname) goto clean; 243 | 244 | xdl_util_trim_ending(prev_pathname); 245 | xdl_util_trim_ending(pathname); 246 | if (0 != strcmp(prev_pathname, pathname)) goto clean; 247 | 248 | // we found the line with r-xp in the next line 249 | base = prev_base; 250 | offset = 0; 251 | } 252 | 253 | if (0 != offset) goto clean; 254 | 255 | // get pathname 256 | if (NULL == pathname) { 257 | pathname = strchr(line, '/'); 258 | if (NULL == pathname) goto clean; 259 | xdl_util_trim_ending(pathname); 260 | } 261 | 262 | if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) goto clean; 263 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base; 264 | struct dl_phdr_info info; 265 | info.dlpi_name = pathname; 266 | info.dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff); 267 | info.dlpi_phnum = ehdr->e_phnum; 268 | 269 | // callback 270 | if (0 != (r = xdl_iterate_do_callback(cb, cb_arg, base, pathname, NULL))) break; 271 | } 272 | 273 | clean: 274 | try_next_line = false; 275 | } 276 | 277 | fclose(maps); 278 | return r; 279 | } 280 | #endif 281 | 282 | int xdl_iterate_phdr_impl(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags) { 283 | // iterate by /proc/self/maps in Android 4.x (Android 4.x only supports arm32 and x86) 284 | #if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__ 285 | if (xdl_util_get_api_level() < __ANDROID_API_L__) return xdl_iterate_by_maps(cb, cb_arg); 286 | #endif 287 | 288 | // iterate by dl_iterate_phdr() 289 | return xdl_iterate_by_linker(cb, cb_arg, flags); 290 | } 291 | 292 | int xdl_iterate_get_full_pathname(uintptr_t base, char *buf, size_t buf_len) { 293 | FILE *maps = NULL; 294 | int r = xdl_iterate_get_pathname_from_maps(base, buf, buf_len, &maps); 295 | if (NULL != maps) fclose(maps); 296 | return r; 297 | } 298 | -------------------------------------------------------------------------------- /module/src/main/cpp/zygisk.hpp: -------------------------------------------------------------------------------- 1 | // This is the public API for Zygisk modules. 2 | // DO NOT MODIFY ANY CODE IN THIS HEADER. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #define ZYGISK_API_VERSION 2 9 | 10 | /* 11 | 12 | Define a class and inherit zygisk::ModuleBase to implement the functionality of your module. 13 | Use the macro REGISTER_ZYGISK_MODULE(className) to register that class to Zygisk. 14 | 15 | Please note that modules will only be loaded after zygote has forked the child process. 16 | THIS MEANS ALL OF YOUR CODE RUNS IN THE APP/SYSTEM SERVER PROCESS, NOT THE ZYGOTE DAEMON! 17 | 18 | Example code: 19 | 20 | static jint (*orig_logger_entry_max)(JNIEnv *env); 21 | static jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); } 22 | 23 | static void example_handler(int socket) { ... } 24 | 25 | class ExampleModule : public zygisk::ModuleBase { 26 | public: 27 | void onLoad(zygisk::Api *api, JNIEnv *env) override { 28 | this->api = api; 29 | this->env = env; 30 | } 31 | void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { 32 | JNINativeMethod methods[] = { 33 | { "logger_entry_max_payload_native", "()I", (void*) my_logger_entry_max }, 34 | }; 35 | api->hookJniNativeMethods(env, "android/util/Log", methods, 1); 36 | *(void **) &orig_logger_entry_max = methods[0].fnPtr; 37 | } 38 | private: 39 | zygisk::Api *api; 40 | JNIEnv *env; 41 | }; 42 | 43 | REGISTER_ZYGISK_MODULE(ExampleModule) 44 | 45 | REGISTER_ZYGISK_COMPANION(example_handler) 46 | 47 | */ 48 | 49 | namespace zygisk { 50 | 51 | struct Api; 52 | struct AppSpecializeArgs; 53 | struct ServerSpecializeArgs; 54 | 55 | class ModuleBase { 56 | public: 57 | 58 | // This function is called when the module is loaded into the target process. 59 | // A Zygisk API handle will be sent as an argument; call utility functions or interface 60 | // with Zygisk through this handle. 61 | virtual void onLoad([[maybe_unused]] Api *api, [[maybe_unused]] JNIEnv *env) {} 62 | 63 | // This function is called before the app process is specialized. 64 | // At this point, the process just got forked from zygote, but no app specific specialization 65 | // is applied. This means that the process does not have any sandbox restrictions and 66 | // still runs with the same privilege of zygote. 67 | // 68 | // All the arguments that will be sent and used for app specialization is passed as a single 69 | // AppSpecializeArgs object. You can read and overwrite these arguments to change how the app 70 | // process will be specialized. 71 | // 72 | // If you need to run some operations as superuser, you can call Api::connectCompanion() to 73 | // get a socket to do IPC calls with a root companion process. 74 | // See Api::connectCompanion() for more info. 75 | virtual void preAppSpecialize([[maybe_unused]] AppSpecializeArgs *args) {} 76 | 77 | // This function is called after the app process is specialized. 78 | // At this point, the process has all sandbox restrictions enabled for this application. 79 | // This means that this function runs as the same privilege of the app's own code. 80 | virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {} 81 | 82 | // This function is called before the system server process is specialized. 83 | // See preAppSpecialize(args) for more info. 84 | virtual void preServerSpecialize([[maybe_unused]] ServerSpecializeArgs *args) {} 85 | 86 | // This function is called after the system server process is specialized. 87 | // At this point, the process runs with the privilege of system_server. 88 | virtual void postServerSpecialize([[maybe_unused]] const ServerSpecializeArgs *args) {} 89 | }; 90 | 91 | struct AppSpecializeArgs { 92 | // Required arguments. These arguments are guaranteed to exist on all Android versions. 93 | jint &uid; 94 | jint &gid; 95 | jintArray &gids; 96 | jint &runtime_flags; 97 | jint &mount_external; 98 | jstring &se_info; 99 | jstring &nice_name; 100 | jstring &instruction_set; 101 | jstring &app_data_dir; 102 | 103 | // Optional arguments. Please check whether the pointer is null before de-referencing 104 | jboolean *const is_child_zygote; 105 | jboolean *const is_top_app; 106 | jobjectArray *const pkg_data_info_list; 107 | jobjectArray *const whitelisted_data_info_list; 108 | jboolean *const mount_data_dirs; 109 | jboolean *const mount_storage_dirs; 110 | 111 | AppSpecializeArgs() = delete; 112 | }; 113 | 114 | struct ServerSpecializeArgs { 115 | jint &uid; 116 | jint &gid; 117 | jintArray &gids; 118 | jint &runtime_flags; 119 | jlong &permitted_capabilities; 120 | jlong &effective_capabilities; 121 | 122 | ServerSpecializeArgs() = delete; 123 | }; 124 | 125 | namespace internal { 126 | struct api_table; 127 | template void entry_impl(api_table *, JNIEnv *); 128 | } 129 | 130 | // These values are used in Api::setOption(Option) 131 | enum Option : int { 132 | // Force Magisk's denylist unmount routines to run on this process. 133 | // 134 | // Setting this option only makes sense in preAppSpecialize. 135 | // The actual unmounting happens during app process specialization. 136 | // 137 | // Set this option to force all Magisk and modules' files to be unmounted from the 138 | // mount namespace of the process, regardless of the denylist enforcement status. 139 | FORCE_DENYLIST_UNMOUNT = 0, 140 | 141 | // When this option is set, your module's library will be dlclose-ed after post[XXX]Specialize. 142 | // Be aware that after dlclose-ing your module, all of your code will be unmapped from memory. 143 | // YOU MUST NOT ENABLE THIS OPTION AFTER HOOKING ANY FUNCTIONS IN THE PROCESS. 144 | DLCLOSE_MODULE_LIBRARY = 1, 145 | }; 146 | 147 | // Bit masks of the return value of Api::getFlags() 148 | enum StateFlag : uint32_t { 149 | // The user has granted root access to the current process 150 | PROCESS_GRANTED_ROOT = (1u << 0), 151 | 152 | // The current process was added on the denylist 153 | PROCESS_ON_DENYLIST = (1u << 1), 154 | }; 155 | 156 | // All API functions will stop working after post[XXX]Specialize as Zygisk will be unloaded 157 | // from the specialized process afterwards. 158 | struct Api { 159 | 160 | // Connect to a root companion process and get a Unix domain socket for IPC. 161 | // 162 | // This API only works in the pre[XXX]Specialize functions due to SELinux restrictions. 163 | // 164 | // The pre[XXX]Specialize functions run with the same privilege of zygote. 165 | // If you would like to do some operations with superuser permissions, register a handler 166 | // function that would be called in the root process with REGISTER_ZYGISK_COMPANION(func). 167 | // Another good use case for a companion process is that if you want to share some resources 168 | // across multiple processes, hold the resources in the companion process and pass it over. 169 | // 170 | // The root companion process is ABI aware; that is, when calling this function from a 32-bit 171 | // process, you will be connected to a 32-bit companion process, and vice versa for 64-bit. 172 | // 173 | // Returns a file descriptor to a socket that is connected to the socket passed to your 174 | // module's companion request handler. Returns -1 if the connection attempt failed. 175 | int connectCompanion(); 176 | 177 | // Get the file descriptor of the root folder of the current module. 178 | // 179 | // This API only works in the pre[XXX]Specialize functions. 180 | // Accessing the directory returned is only possible in the pre[XXX]Specialize functions 181 | // or in the root companion process (assuming that you sent the fd over the socket). 182 | // Both restrictions are due to SELinux and UID. 183 | // 184 | // Returns -1 if errors occurred. 185 | int getModuleDir(); 186 | 187 | // Set various options for your module. 188 | // Please note that this function accepts one single option at a time. 189 | // Check zygisk::Option for the full list of options available. 190 | void setOption(Option opt); 191 | 192 | // Get information about the current process. 193 | // Returns bitwise-or'd zygisk::StateFlag values. 194 | uint32_t getFlags(); 195 | 196 | // Hook JNI native methods for a class 197 | // 198 | // Lookup all registered JNI native methods and replace it with your own functions. 199 | // The original function pointer will be saved in each JNINativeMethod's fnPtr. 200 | // If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr 201 | // will be set to nullptr. 202 | void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods); 203 | 204 | // For ELFs loaded in memory matching `regex`, replace function `symbol` with `newFunc`. 205 | // If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`. 206 | void pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc); 207 | 208 | // For ELFs loaded in memory matching `regex`, exclude hooks registered for `symbol`. 209 | // If `symbol` is nullptr, then all symbols will be excluded. 210 | void pltHookExclude(const char *regex, const char *symbol); 211 | 212 | // Commit all the hooks that was previously registered. 213 | // Returns false if an error occurred. 214 | bool pltHookCommit(); 215 | 216 | private: 217 | internal::api_table *impl; 218 | template friend void internal::entry_impl(internal::api_table *, JNIEnv *); 219 | }; 220 | 221 | // Register a class as a Zygisk module 222 | 223 | #define REGISTER_ZYGISK_MODULE(clazz) \ 224 | void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) { \ 225 | zygisk::internal::entry_impl(table, env); \ 226 | } 227 | 228 | // Register a root companion request handler function for your module 229 | // 230 | // The function runs in a superuser daemon process and handles a root companion request from 231 | // your module running in a target process. The function has to accept an integer value, 232 | // which is a socket that is connected to the target process. 233 | // See Api::connectCompanion() for more info. 234 | // 235 | // NOTE: the function can run concurrently on multiple threads. 236 | // Be aware of race conditions if you have a globally shared resource. 237 | 238 | #define REGISTER_ZYGISK_COMPANION(func) \ 239 | void zygisk_companion_entry(int client) { func(client); } 240 | 241 | /************************************************************************************ 242 | * All the code after this point is internal code used to interface with Zygisk 243 | * and guarantee ABI stability. You do not have to understand what it is doing. 244 | ************************************************************************************/ 245 | 246 | namespace internal { 247 | 248 | struct module_abi { 249 | long api_version; 250 | ModuleBase *_this; 251 | 252 | void (*preAppSpecialize)(ModuleBase *, AppSpecializeArgs *); 253 | void (*postAppSpecialize)(ModuleBase *, const AppSpecializeArgs *); 254 | void (*preServerSpecialize)(ModuleBase *, ServerSpecializeArgs *); 255 | void (*postServerSpecialize)(ModuleBase *, const ServerSpecializeArgs *); 256 | 257 | module_abi(ModuleBase *module) : api_version(ZYGISK_API_VERSION), _this(module) { 258 | preAppSpecialize = [](auto self, auto args) { self->preAppSpecialize(args); }; 259 | postAppSpecialize = [](auto self, auto args) { self->postAppSpecialize(args); }; 260 | preServerSpecialize = [](auto self, auto args) { self->preServerSpecialize(args); }; 261 | postServerSpecialize = [](auto self, auto args) { self->postServerSpecialize(args); }; 262 | } 263 | }; 264 | 265 | struct api_table { 266 | // These first 2 entries are permanent, shall never change 267 | void *_this; 268 | bool (*registerModule)(api_table *, module_abi *); 269 | 270 | // Utility functions 271 | void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int); 272 | void (*pltHookRegister)(const char *, const char *, void *, void **); 273 | void (*pltHookExclude)(const char *, const char *); 274 | bool (*pltHookCommit)(); 275 | 276 | // Zygisk functions 277 | int (*connectCompanion)(void * /* _this */); 278 | void (*setOption)(void * /* _this */, Option); 279 | int (*getModuleDir)(void * /* _this */); 280 | uint32_t (*getFlags)(void * /* _this */); 281 | }; 282 | 283 | template 284 | void entry_impl(api_table *table, JNIEnv *env) { 285 | ModuleBase *module = new T(); 286 | if (!table->registerModule(table, new module_abi(module))) 287 | return; 288 | auto api = new Api(); 289 | api->impl = table; 290 | module->onLoad(api, env); 291 | } 292 | 293 | } // namespace internal 294 | 295 | inline int Api::connectCompanion() { 296 | return impl->connectCompanion ? impl->connectCompanion(impl->_this) : -1; 297 | } 298 | inline int Api::getModuleDir() { 299 | return impl->getModuleDir ? impl->getModuleDir(impl->_this) : -1; 300 | } 301 | inline void Api::setOption(Option opt) { 302 | if (impl->setOption) impl->setOption(impl->_this, opt); 303 | } 304 | inline uint32_t Api::getFlags() { 305 | return impl->getFlags ? impl->getFlags(impl->_this) : 0; 306 | } 307 | inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) { 308 | if (impl->hookJniNativeMethods) impl->hookJniNativeMethods(env, className, methods, numMethods); 309 | } 310 | inline void Api::pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc) { 311 | if (impl->pltHookRegister) impl->pltHookRegister(regex, symbol, newFunc, oldFunc); 312 | } 313 | inline void Api::pltHookExclude(const char *regex, const char *symbol) { 314 | if (impl->pltHookExclude) impl->pltHookExclude(regex, symbol); 315 | } 316 | inline bool Api::pltHookCommit() { 317 | return impl->pltHookCommit != nullptr && impl->pltHookCommit(); 318 | } 319 | 320 | } // namespace zygisk 321 | 322 | [[gnu::visibility("default")]] [[gnu::used]] 323 | extern "C" void zygisk_module_entry(zygisk::internal::api_table *, JNIEnv *); 324 | 325 | [[gnu::visibility("default")]] [[gnu::used]] 326 | extern "C" void zygisk_companion_entry(int); 327 | -------------------------------------------------------------------------------- /module/src/main/cpp/il2cpp_dump.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Perfare on 2020/7/4. 3 | // 4 | 5 | #include "il2cpp_dump.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "xdl.h" 16 | #include "log.h" 17 | #include "il2cpp-tabledefs.h" 18 | #include "il2cpp-class.h" 19 | 20 | #define DO_API(r, n, p) r (*n) p 21 | 22 | #include "il2cpp-api-functions.h" 23 | 24 | #undef DO_API 25 | 26 | static uint64_t il2cpp_base = 0; 27 | 28 | void init_il2cpp_api(void *handle) { 29 | #define DO_API(r, n, p) { \ 30 | n = (r (*) p)xdl_sym(handle, #n, nullptr); \ 31 | if(!n) { \ 32 | LOGW("api not found %s", #n); \ 33 | } \ 34 | } 35 | 36 | #include "il2cpp-api-functions.h" 37 | 38 | #undef DO_API 39 | } 40 | 41 | std::string get_method_modifier(uint32_t flags) { 42 | std::stringstream outPut; 43 | auto access = flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK; 44 | switch (access) { 45 | case METHOD_ATTRIBUTE_PRIVATE: 46 | outPut << "private "; 47 | break; 48 | case METHOD_ATTRIBUTE_PUBLIC: 49 | outPut << "public "; 50 | break; 51 | case METHOD_ATTRIBUTE_FAMILY: 52 | outPut << "protected "; 53 | break; 54 | case METHOD_ATTRIBUTE_ASSEM: 55 | case METHOD_ATTRIBUTE_FAM_AND_ASSEM: 56 | outPut << "internal "; 57 | break; 58 | case METHOD_ATTRIBUTE_FAM_OR_ASSEM: 59 | outPut << "protected internal "; 60 | break; 61 | } 62 | if (flags & METHOD_ATTRIBUTE_STATIC) { 63 | outPut << "static "; 64 | } 65 | if (flags & METHOD_ATTRIBUTE_ABSTRACT) { 66 | outPut << "abstract "; 67 | if ((flags & METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK) == METHOD_ATTRIBUTE_REUSE_SLOT) { 68 | outPut << "override "; 69 | } 70 | } else if (flags & METHOD_ATTRIBUTE_FINAL) { 71 | if ((flags & METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK) == METHOD_ATTRIBUTE_REUSE_SLOT) { 72 | outPut << "sealed override "; 73 | } 74 | } else if (flags & METHOD_ATTRIBUTE_VIRTUAL) { 75 | if ((flags & METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK) == METHOD_ATTRIBUTE_NEW_SLOT) { 76 | outPut << "virtual "; 77 | } else { 78 | outPut << "override "; 79 | } 80 | } 81 | if (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) { 82 | outPut << "extern "; 83 | } 84 | return outPut.str(); 85 | } 86 | 87 | bool _il2cpp_type_is_byref(const Il2CppType *type) { 88 | auto byref = type->byref; 89 | if (il2cpp_type_is_byref) { 90 | byref = il2cpp_type_is_byref(type); 91 | } 92 | return byref; 93 | } 94 | 95 | std::string dump_method(Il2CppClass *klass) { 96 | std::stringstream outPut; 97 | outPut << "\n\t// Methods\n"; 98 | void *iter = nullptr; 99 | while (auto method = il2cpp_class_get_methods(klass, &iter)) { 100 | //TODO attribute 101 | if (method->methodPointer) { 102 | outPut << "\t// RVA: 0x"; 103 | outPut << std::hex << (uint64_t) method->methodPointer - il2cpp_base; 104 | outPut << " VA: 0x"; 105 | outPut << std::hex << (uint64_t) method->methodPointer; 106 | } else { 107 | outPut << "\t// RVA: 0x VA: 0x0"; 108 | } 109 | /*if (method->slot != 65535) { 110 | outPut << " Slot: " << std::dec << method->slot; 111 | }*/ 112 | outPut << "\n\t"; 113 | uint32_t iflags = 0; 114 | auto flags = il2cpp_method_get_flags(method, &iflags); 115 | outPut << get_method_modifier(flags); 116 | //TODO genericContainerIndex 117 | auto return_type = il2cpp_method_get_return_type(method); 118 | if (_il2cpp_type_is_byref(return_type)) { 119 | outPut << "ref "; 120 | } 121 | auto return_class = il2cpp_class_from_type(return_type); 122 | outPut << il2cpp_class_get_name(return_class) << " " << il2cpp_method_get_name(method) 123 | << "("; 124 | auto param_count = il2cpp_method_get_param_count(method); 125 | for (int i = 0; i < param_count; ++i) { 126 | auto param = il2cpp_method_get_param(method, i); 127 | auto attrs = param->attrs; 128 | if (_il2cpp_type_is_byref(param)) { 129 | if (attrs & PARAM_ATTRIBUTE_OUT && !(attrs & PARAM_ATTRIBUTE_IN)) { 130 | outPut << "out "; 131 | } else if (attrs & PARAM_ATTRIBUTE_IN && !(attrs & PARAM_ATTRIBUTE_OUT)) { 132 | outPut << "in "; 133 | } else { 134 | outPut << "ref "; 135 | } 136 | } else { 137 | if (attrs & PARAM_ATTRIBUTE_IN) { 138 | outPut << "[In] "; 139 | } 140 | if (attrs & PARAM_ATTRIBUTE_OUT) { 141 | outPut << "[Out] "; 142 | } 143 | } 144 | auto parameter_class = il2cpp_class_from_type(param); 145 | outPut << il2cpp_class_get_name(parameter_class) << " " 146 | << il2cpp_method_get_param_name(method, i); 147 | outPut << ", "; 148 | } 149 | if (param_count > 0) { 150 | outPut.seekp(-2, outPut.cur); 151 | } 152 | outPut << ") { }\n"; 153 | //TODO GenericInstMethod 154 | } 155 | return outPut.str(); 156 | } 157 | 158 | std::string dump_property(Il2CppClass *klass) { 159 | std::stringstream outPut; 160 | outPut << "\n\t// Properties\n"; 161 | void *iter = nullptr; 162 | while (auto prop_const = il2cpp_class_get_properties(klass, &iter)) { 163 | //TODO attribute 164 | auto prop = const_cast(prop_const); 165 | auto get = il2cpp_property_get_get_method(prop); 166 | auto set = il2cpp_property_get_set_method(prop); 167 | auto prop_name = il2cpp_property_get_name(prop); 168 | outPut << "\t"; 169 | Il2CppClass *prop_class = nullptr; 170 | uint32_t iflags = 0; 171 | if (get) { 172 | outPut << get_method_modifier(il2cpp_method_get_flags(get, &iflags)); 173 | prop_class = il2cpp_class_from_type(il2cpp_method_get_return_type(get)); 174 | } else if (set) { 175 | outPut << get_method_modifier(il2cpp_method_get_flags(set, &iflags)); 176 | auto param = il2cpp_method_get_param(set, 0); 177 | prop_class = il2cpp_class_from_type(param); 178 | } 179 | if (prop_class) { 180 | outPut << il2cpp_class_get_name(prop_class) << " " << prop_name << " { "; 181 | if (get) { 182 | outPut << "get; "; 183 | } 184 | if (set) { 185 | outPut << "set; "; 186 | } 187 | outPut << "}\n"; 188 | } else { 189 | if (prop_name) { 190 | outPut << " // unknown property " << prop_name; 191 | } 192 | } 193 | } 194 | return outPut.str(); 195 | } 196 | 197 | std::string dump_field(Il2CppClass *klass) { 198 | std::stringstream outPut; 199 | outPut << "\n\t// Fields\n"; 200 | auto is_enum = il2cpp_class_is_enum(klass); 201 | void *iter = nullptr; 202 | while (auto field = il2cpp_class_get_fields(klass, &iter)) { 203 | //TODO attribute 204 | outPut << "\t"; 205 | auto attrs = il2cpp_field_get_flags(field); 206 | auto access = attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK; 207 | switch (access) { 208 | case FIELD_ATTRIBUTE_PRIVATE: 209 | outPut << "private "; 210 | break; 211 | case FIELD_ATTRIBUTE_PUBLIC: 212 | outPut << "public "; 213 | break; 214 | case FIELD_ATTRIBUTE_FAMILY: 215 | outPut << "protected "; 216 | break; 217 | case FIELD_ATTRIBUTE_ASSEMBLY: 218 | case FIELD_ATTRIBUTE_FAM_AND_ASSEM: 219 | outPut << "internal "; 220 | break; 221 | case FIELD_ATTRIBUTE_FAM_OR_ASSEM: 222 | outPut << "protected internal "; 223 | break; 224 | } 225 | if (attrs & FIELD_ATTRIBUTE_LITERAL) { 226 | outPut << "const "; 227 | } else { 228 | if (attrs & FIELD_ATTRIBUTE_STATIC) { 229 | outPut << "static "; 230 | } 231 | if (attrs & FIELD_ATTRIBUTE_INIT_ONLY) { 232 | outPut << "readonly "; 233 | } 234 | } 235 | auto field_type = il2cpp_field_get_type(field); 236 | auto field_class = il2cpp_class_from_type(field_type); 237 | outPut << il2cpp_class_get_name(field_class) << " " << il2cpp_field_get_name(field); 238 | //TODO 获取构造函数初始化后的字段值 239 | if (attrs & FIELD_ATTRIBUTE_LITERAL && is_enum) { 240 | uint64_t val = 0; 241 | il2cpp_field_static_get_value(field, &val); 242 | outPut << " = " << std::dec << val; 243 | } 244 | outPut << "; // 0x" << std::hex << il2cpp_field_get_offset(field) << "\n"; 245 | } 246 | return outPut.str(); 247 | } 248 | 249 | std::string dump_type(const Il2CppType *type) { 250 | std::stringstream outPut; 251 | auto *klass = il2cpp_class_from_type(type); 252 | outPut << "\n// Namespace: " << il2cpp_class_get_namespace(klass) << "\n"; 253 | auto flags = il2cpp_class_get_flags(klass); 254 | if (flags & TYPE_ATTRIBUTE_SERIALIZABLE) { 255 | outPut << "[Serializable]\n"; 256 | } 257 | //TODO attribute 258 | auto is_valuetype = il2cpp_class_is_valuetype(klass); 259 | auto is_enum = il2cpp_class_is_enum(klass); 260 | auto visibility = flags & TYPE_ATTRIBUTE_VISIBILITY_MASK; 261 | switch (visibility) { 262 | case TYPE_ATTRIBUTE_PUBLIC: 263 | case TYPE_ATTRIBUTE_NESTED_PUBLIC: 264 | outPut << "public "; 265 | break; 266 | case TYPE_ATTRIBUTE_NOT_PUBLIC: 267 | case TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM: 268 | case TYPE_ATTRIBUTE_NESTED_ASSEMBLY: 269 | outPut << "internal "; 270 | break; 271 | case TYPE_ATTRIBUTE_NESTED_PRIVATE: 272 | outPut << "private "; 273 | break; 274 | case TYPE_ATTRIBUTE_NESTED_FAMILY: 275 | outPut << "protected "; 276 | break; 277 | case TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM: 278 | outPut << "protected internal "; 279 | break; 280 | } 281 | if (flags & TYPE_ATTRIBUTE_ABSTRACT && flags & TYPE_ATTRIBUTE_SEALED) { 282 | outPut << "static "; 283 | } else if (!(flags & TYPE_ATTRIBUTE_INTERFACE) && flags & TYPE_ATTRIBUTE_ABSTRACT) { 284 | outPut << "abstract "; 285 | } else if (!is_valuetype && !is_enum && flags & TYPE_ATTRIBUTE_SEALED) { 286 | outPut << "sealed "; 287 | } 288 | if (flags & TYPE_ATTRIBUTE_INTERFACE) { 289 | outPut << "interface "; 290 | } else if (is_enum) { 291 | outPut << "enum "; 292 | } else if (is_valuetype) { 293 | outPut << "struct "; 294 | } else { 295 | outPut << "class "; 296 | } 297 | outPut << il2cpp_class_get_name(klass); //TODO genericContainerIndex 298 | std::vector extends; 299 | auto parent = il2cpp_class_get_parent(klass); 300 | if (!is_valuetype && !is_enum && parent) { 301 | auto parent_type = il2cpp_class_get_type(parent); 302 | if (parent_type->type != IL2CPP_TYPE_OBJECT) { 303 | extends.emplace_back(il2cpp_class_get_name(parent)); 304 | } 305 | } 306 | void *iter = nullptr; 307 | while (auto itf = il2cpp_class_get_interfaces(klass, &iter)) { 308 | extends.emplace_back(il2cpp_class_get_name(itf)); 309 | } 310 | if (!extends.empty()) { 311 | outPut << " : " << extends[0]; 312 | for (int i = 1; i < extends.size(); ++i) { 313 | outPut << ", " << extends[i]; 314 | } 315 | } 316 | outPut << "\n{"; 317 | outPut << dump_field(klass); 318 | outPut << dump_property(klass); 319 | outPut << dump_method(klass); 320 | //TODO EventInfo 321 | outPut << "}\n"; 322 | return outPut.str(); 323 | } 324 | 325 | void il2cpp_api_init(void *handle) { 326 | LOGI("il2cpp_handle: %p", handle); 327 | init_il2cpp_api(handle); 328 | if (il2cpp_domain_get_assemblies) { 329 | Dl_info dlInfo; 330 | if (dladdr((void *) il2cpp_domain_get_assemblies, &dlInfo)) { 331 | il2cpp_base = reinterpret_cast(dlInfo.dli_fbase); 332 | } 333 | LOGI("il2cpp_base: %" PRIx64"", il2cpp_base); 334 | } else { 335 | LOGE("Failed to initialize il2cpp api."); 336 | return; 337 | } 338 | while (!il2cpp_is_vm_thread(nullptr)) { 339 | LOGI("Waiting for il2cpp_init..."); 340 | sleep(1); 341 | } 342 | auto domain = il2cpp_domain_get(); 343 | il2cpp_thread_attach(domain); 344 | } 345 | 346 | void il2cpp_dump(const char *outDir) { 347 | LOGI("dumping..."); 348 | size_t size; 349 | auto domain = il2cpp_domain_get(); 350 | auto assemblies = il2cpp_domain_get_assemblies(domain, &size); 351 | std::stringstream imageOutput; 352 | for (int i = 0; i < size; ++i) { 353 | auto image = il2cpp_assembly_get_image(assemblies[i]); 354 | imageOutput << "// Image " << i << ": " << il2cpp_image_get_name(image) << "\n"; 355 | } 356 | std::vector outPuts; 357 | if (il2cpp_image_get_class) { 358 | LOGI("Version greater than 2018.3"); 359 | //使用il2cpp_image_get_class 360 | for (int i = 0; i < size; ++i) { 361 | auto image = il2cpp_assembly_get_image(assemblies[i]); 362 | std::stringstream imageStr; 363 | imageStr << "\n// Dll : " << il2cpp_image_get_name(image); 364 | auto classCount = il2cpp_image_get_class_count(image); 365 | for (int j = 0; j < classCount; ++j) { 366 | auto klass = il2cpp_image_get_class(image, j); 367 | auto type = il2cpp_class_get_type(const_cast(klass)); 368 | //LOGD("type name : %s", il2cpp_type_get_name(type)); 369 | auto outPut = imageStr.str() + dump_type(type); 370 | outPuts.push_back(outPut); 371 | } 372 | } 373 | } else { 374 | LOGI("Version less than 2018.3"); 375 | //使用反射 376 | auto corlib = il2cpp_get_corlib(); 377 | auto assemblyClass = il2cpp_class_from_name(corlib, "System.Reflection", "Assembly"); 378 | auto assemblyLoad = il2cpp_class_get_method_from_name(assemblyClass, "Load", 1); 379 | auto assemblyGetTypes = il2cpp_class_get_method_from_name(assemblyClass, "GetTypes", 0); 380 | if (assemblyLoad && assemblyLoad->methodPointer) { 381 | LOGI("Assembly::Load: %p", assemblyLoad->methodPointer); 382 | } else { 383 | LOGI("miss Assembly::Load"); 384 | return; 385 | } 386 | if (assemblyGetTypes && assemblyGetTypes->methodPointer) { 387 | LOGI("Assembly::GetTypes: %p", assemblyGetTypes->methodPointer); 388 | } else { 389 | LOGI("miss Assembly::GetTypes"); 390 | return; 391 | } 392 | typedef void *(*Assembly_Load_ftn)(void *, Il2CppString *, void *); 393 | typedef Il2CppArray *(*Assembly_GetTypes_ftn)(void *, void *); 394 | for (int i = 0; i < size; ++i) { 395 | auto image = il2cpp_assembly_get_image(assemblies[i]); 396 | std::stringstream imageStr; 397 | auto image_name = il2cpp_image_get_name(image); 398 | imageStr << "\n// Dll : " << image_name; 399 | //LOGD("image name : %s", image->name); 400 | auto imageName = std::string(image_name); 401 | auto pos = imageName.rfind('.'); 402 | auto imageNameNoExt = imageName.substr(0, pos); 403 | auto assemblyFileName = il2cpp_string_new(imageNameNoExt.data()); 404 | auto reflectionAssembly = ((Assembly_Load_ftn) assemblyLoad->methodPointer)(nullptr, 405 | assemblyFileName, 406 | nullptr); 407 | auto reflectionTypes = ((Assembly_GetTypes_ftn) assemblyGetTypes->methodPointer)( 408 | reflectionAssembly, nullptr); 409 | auto items = reflectionTypes->vector; 410 | for (int j = 0; j < reflectionTypes->max_length; ++j) { 411 | auto klass = il2cpp_class_from_system_type((Il2CppReflectionType *) items[j]); 412 | auto type = il2cpp_class_get_type(klass); 413 | //LOGD("type name : %s", il2cpp_type_get_name(type)); 414 | auto outPut = imageStr.str() + dump_type(type); 415 | outPuts.push_back(outPut); 416 | } 417 | } 418 | } 419 | LOGI("write dump file"); 420 | auto outPath = std::string(outDir).append("/files/dump.cs"); 421 | std::ofstream outStream(outPath); 422 | outStream << imageOutput.str(); 423 | auto count = outPuts.size(); 424 | for (int i = 0; i < count; ++i) { 425 | outStream << outPuts[i]; 426 | } 427 | outStream.close(); 428 | LOGI("dump done!"); 429 | } -------------------------------------------------------------------------------- /module/src/main/cpp/il2cpp-api-functions.h: -------------------------------------------------------------------------------- 1 | #ifndef DO_API_NO_RETURN 2 | #define DO_API_NO_RETURN(r, n, p) DO_API(r,n,p) 3 | #endif 4 | 5 | DO_API(int, il2cpp_init, (const char* domain_name)); 6 | DO_API(int, il2cpp_init_utf16, (const Il2CppChar * domain_name)); 7 | DO_API(void, il2cpp_shutdown, ()); 8 | DO_API(void, il2cpp_set_config_dir, (const char *config_path)); 9 | DO_API(void, il2cpp_set_data_dir, (const char *data_path)); 10 | DO_API(void, il2cpp_set_temp_dir, (const char *temp_path)); 11 | DO_API(void, il2cpp_set_commandline_arguments, (int argc, const char* const argv[], const char* basedir)); 12 | DO_API(void, il2cpp_set_commandline_arguments_utf16, (int argc, const Il2CppChar * const argv[], const char* basedir)); 13 | DO_API(void, il2cpp_set_config_utf16, (const Il2CppChar * executablePath)); 14 | DO_API(void, il2cpp_set_config, (const char* executablePath)); 15 | 16 | DO_API(void, il2cpp_set_memory_callbacks, (Il2CppMemoryCallbacks * callbacks)); 17 | DO_API(const Il2CppImage*, il2cpp_get_corlib, ()); 18 | DO_API(void, il2cpp_add_internal_call, (const char* name, Il2CppMethodPointer method)); 19 | DO_API(Il2CppMethodPointer, il2cpp_resolve_icall, (const char* name)); 20 | 21 | DO_API(void*, il2cpp_alloc, (size_t size)); 22 | DO_API(void, il2cpp_free, (void* ptr)); 23 | 24 | // array 25 | DO_API(Il2CppClass*, il2cpp_array_class_get, (Il2CppClass * element_class, uint32_t rank)); 26 | DO_API(uint32_t, il2cpp_array_length, (Il2CppArray * array)); 27 | DO_API(uint32_t, il2cpp_array_get_byte_length, (Il2CppArray * array)); 28 | DO_API(Il2CppArray*, il2cpp_array_new, (Il2CppClass * elementTypeInfo, il2cpp_array_size_t length)); 29 | DO_API(Il2CppArray*, il2cpp_array_new_specific, (Il2CppClass * arrayTypeInfo, il2cpp_array_size_t length)); 30 | DO_API(Il2CppArray*, il2cpp_array_new_full, (Il2CppClass * array_class, il2cpp_array_size_t * lengths, il2cpp_array_size_t * lower_bounds)); 31 | DO_API(Il2CppClass*, il2cpp_bounded_array_class_get, (Il2CppClass * element_class, uint32_t rank, bool bounded)); 32 | DO_API(int, il2cpp_array_element_size, (const Il2CppClass * array_class)); 33 | 34 | // assembly 35 | DO_API(const Il2CppImage*, il2cpp_assembly_get_image, (const Il2CppAssembly * assembly)); 36 | 37 | // class 38 | DO_API(void, il2cpp_class_for_each, (void(*klassReportFunc)(Il2CppClass* klass, void* userData), void* userData)); 39 | DO_API(const Il2CppType*, il2cpp_class_enum_basetype, (Il2CppClass * klass)); 40 | DO_API(bool, il2cpp_class_is_generic, (const Il2CppClass * klass)); 41 | DO_API(bool, il2cpp_class_is_inflated, (const Il2CppClass * klass)); 42 | DO_API(bool, il2cpp_class_is_assignable_from, (Il2CppClass * klass, Il2CppClass * oklass)); 43 | DO_API(bool, il2cpp_class_is_subclass_of, (Il2CppClass * klass, Il2CppClass * klassc, bool check_interfaces)); 44 | DO_API(bool, il2cpp_class_has_parent, (Il2CppClass * klass, Il2CppClass * klassc)); 45 | DO_API(Il2CppClass*, il2cpp_class_from_il2cpp_type, (const Il2CppType * type)); 46 | DO_API(Il2CppClass*, il2cpp_class_from_name, (const Il2CppImage * image, const char* namespaze, const char *name)); 47 | DO_API(Il2CppClass*, il2cpp_class_from_system_type, (Il2CppReflectionType * type)); 48 | DO_API(Il2CppClass*, il2cpp_class_get_element_class, (Il2CppClass * klass)); 49 | DO_API(const EventInfo*, il2cpp_class_get_events, (Il2CppClass * klass, void* *iter)); 50 | DO_API(FieldInfo*, il2cpp_class_get_fields, (Il2CppClass * klass, void* *iter)); 51 | DO_API(Il2CppClass*, il2cpp_class_get_nested_types, (Il2CppClass * klass, void* *iter)); 52 | DO_API(Il2CppClass*, il2cpp_class_get_interfaces, (Il2CppClass * klass, void* *iter)); 53 | DO_API(const PropertyInfo*, il2cpp_class_get_properties, (Il2CppClass * klass, void* *iter)); 54 | DO_API(const PropertyInfo*, il2cpp_class_get_property_from_name, (Il2CppClass * klass, const char *name)); 55 | DO_API(FieldInfo*, il2cpp_class_get_field_from_name, (Il2CppClass * klass, const char *name)); 56 | DO_API(const MethodInfo*, il2cpp_class_get_methods, (Il2CppClass * klass, void* *iter)); 57 | DO_API(const MethodInfo*, il2cpp_class_get_method_from_name, (Il2CppClass * klass, const char* name, int argsCount)); 58 | DO_API(const char*, il2cpp_class_get_name, (Il2CppClass * klass)); 59 | DO_API(void, il2cpp_type_get_name_chunked, (const Il2CppType * type, void(*chunkReportFunc)(void* data, void* userData), void* userData)); 60 | DO_API(const char*, il2cpp_class_get_namespace, (Il2CppClass * klass)); 61 | DO_API(Il2CppClass*, il2cpp_class_get_parent, (Il2CppClass * klass)); 62 | DO_API(Il2CppClass*, il2cpp_class_get_declaring_type, (Il2CppClass * klass)); 63 | DO_API(int32_t, il2cpp_class_instance_size, (Il2CppClass * klass)); 64 | DO_API(size_t, il2cpp_class_num_fields, (const Il2CppClass * enumKlass)); 65 | DO_API(bool, il2cpp_class_is_valuetype, (const Il2CppClass * klass)); 66 | DO_API(int32_t, il2cpp_class_value_size, (Il2CppClass * klass, uint32_t * align)); 67 | DO_API(bool, il2cpp_class_is_blittable, (const Il2CppClass * klass)); 68 | DO_API(int, il2cpp_class_get_flags, (const Il2CppClass * klass)); 69 | DO_API(bool, il2cpp_class_is_abstract, (const Il2CppClass * klass)); 70 | DO_API(bool, il2cpp_class_is_interface, (const Il2CppClass * klass)); 71 | DO_API(int, il2cpp_class_array_element_size, (const Il2CppClass * klass)); 72 | DO_API(Il2CppClass*, il2cpp_class_from_type, (const Il2CppType * type)); 73 | DO_API(const Il2CppType*, il2cpp_class_get_type, (Il2CppClass * klass)); 74 | DO_API(uint32_t, il2cpp_class_get_type_token, (Il2CppClass * klass)); 75 | DO_API(bool, il2cpp_class_has_attribute, (Il2CppClass * klass, Il2CppClass * attr_class)); 76 | DO_API(bool, il2cpp_class_has_references, (Il2CppClass * klass)); 77 | DO_API(bool, il2cpp_class_is_enum, (const Il2CppClass * klass)); 78 | DO_API(const Il2CppImage*, il2cpp_class_get_image, (Il2CppClass * klass)); 79 | DO_API(const char*, il2cpp_class_get_assemblyname, (const Il2CppClass * klass)); 80 | DO_API(int, il2cpp_class_get_rank, (const Il2CppClass * klass)); 81 | DO_API(uint32_t, il2cpp_class_get_data_size, (const Il2CppClass * klass)); 82 | DO_API(void*, il2cpp_class_get_static_field_data, (const Il2CppClass * klass)); 83 | 84 | // testing only 85 | DO_API(size_t, il2cpp_class_get_bitmap_size, (const Il2CppClass * klass)); 86 | DO_API(void, il2cpp_class_get_bitmap, (Il2CppClass * klass, size_t * bitmap)); 87 | 88 | // stats 89 | DO_API(bool, il2cpp_stats_dump_to_file, (const char *path)); 90 | DO_API(uint64_t, il2cpp_stats_get_value, (Il2CppStat stat)); 91 | 92 | // domain 93 | DO_API(Il2CppDomain*, il2cpp_domain_get, ()); 94 | DO_API(const Il2CppAssembly*, il2cpp_domain_assembly_open, (Il2CppDomain * domain, const char* name)); 95 | DO_API(const Il2CppAssembly**, il2cpp_domain_get_assemblies, (const Il2CppDomain * domain, size_t * size)); 96 | 97 | // exception 98 | DO_API_NO_RETURN(void, il2cpp_raise_exception, (Il2CppException*)); 99 | DO_API(Il2CppException*, il2cpp_exception_from_name_msg, (const Il2CppImage * image, const char *name_space, const char *name, const char *msg)); 100 | DO_API(Il2CppException*, il2cpp_get_exception_argument_null, (const char *arg)); 101 | DO_API(void, il2cpp_format_exception, (const Il2CppException * ex, char* message, int message_size)); 102 | DO_API(void, il2cpp_format_stack_trace, (const Il2CppException * ex, char* output, int output_size)); 103 | DO_API(void, il2cpp_unhandled_exception, (Il2CppException*)); 104 | DO_API(void, il2cpp_native_stack_trace, (const Il2CppException * ex, uintptr_t** addresses, int* numFrames, char** imageUUID, char** imageName)); 105 | 106 | // field 107 | DO_API(int, il2cpp_field_get_flags, (FieldInfo * field)); 108 | DO_API(const char*, il2cpp_field_get_name, (FieldInfo * field)); 109 | DO_API(Il2CppClass*, il2cpp_field_get_parent, (FieldInfo * field)); 110 | DO_API(size_t, il2cpp_field_get_offset, (FieldInfo * field)); 111 | DO_API(const Il2CppType*, il2cpp_field_get_type, (FieldInfo * field)); 112 | DO_API(void, il2cpp_field_get_value, (Il2CppObject * obj, FieldInfo * field, void *value)); 113 | DO_API(Il2CppObject*, il2cpp_field_get_value_object, (FieldInfo * field, Il2CppObject * obj)); 114 | DO_API(bool, il2cpp_field_has_attribute, (FieldInfo * field, Il2CppClass * attr_class)); 115 | DO_API(void, il2cpp_field_set_value, (Il2CppObject * obj, FieldInfo * field, void *value)); 116 | DO_API(void, il2cpp_field_static_get_value, (FieldInfo * field, void *value)); 117 | DO_API(void, il2cpp_field_static_set_value, (FieldInfo * field, void *value)); 118 | DO_API(void, il2cpp_field_set_value_object, (Il2CppObject * instance, FieldInfo * field, Il2CppObject * value)); 119 | DO_API(bool, il2cpp_field_is_literal, (FieldInfo * field)); 120 | // gc 121 | DO_API(void, il2cpp_gc_collect, (int maxGenerations)); 122 | DO_API(int32_t, il2cpp_gc_collect_a_little, ()); 123 | DO_API(void, il2cpp_gc_start_incremental_collection , ()); 124 | DO_API(void, il2cpp_gc_disable, ()); 125 | DO_API(void, il2cpp_gc_enable, ()); 126 | DO_API(bool, il2cpp_gc_is_disabled, ()); 127 | DO_API(void, il2cpp_gc_set_mode, (Il2CppGCMode mode)); 128 | DO_API(int64_t, il2cpp_gc_get_max_time_slice_ns, ()); 129 | DO_API(void, il2cpp_gc_set_max_time_slice_ns, (int64_t maxTimeSlice)); 130 | DO_API(bool, il2cpp_gc_is_incremental, ()); 131 | DO_API(int64_t, il2cpp_gc_get_used_size, ()); 132 | DO_API(int64_t, il2cpp_gc_get_heap_size, ()); 133 | DO_API(void, il2cpp_gc_wbarrier_set_field, (Il2CppObject * obj, void **targetAddress, void *object)); 134 | DO_API(bool, il2cpp_gc_has_strict_wbarriers, ()); 135 | DO_API(void, il2cpp_gc_set_external_allocation_tracker, (void(*func)(void*, size_t, int))); 136 | DO_API(void, il2cpp_gc_set_external_wbarrier_tracker, (void(*func)(void**))); 137 | DO_API(void, il2cpp_gc_foreach_heap, (void(*func)(void* data, void* userData), void* userData)); 138 | DO_API(void, il2cpp_stop_gc_world, ()); 139 | DO_API(void, il2cpp_start_gc_world, ()); 140 | DO_API(void*, il2cpp_gc_alloc_fixed, (size_t size)); 141 | DO_API(void, il2cpp_gc_free_fixed, (void* address)); 142 | // gchandle 143 | DO_API(uint32_t, il2cpp_gchandle_new, (Il2CppObject * obj, bool pinned)); 144 | DO_API(uint32_t, il2cpp_gchandle_new_weakref, (Il2CppObject * obj, bool track_resurrection)); 145 | DO_API(Il2CppObject*, il2cpp_gchandle_get_target , (uint32_t gchandle)); 146 | DO_API(void, il2cpp_gchandle_free, (uint32_t gchandle)); 147 | DO_API(void , il2cpp_gchandle_foreach_get_target, (void(*func)(void* data, void* userData), void* userData)); 148 | 149 | // vm runtime info 150 | DO_API(uint32_t, il2cpp_object_header_size, ()); 151 | DO_API(uint32_t, il2cpp_array_object_header_size, ()); 152 | DO_API(uint32_t, il2cpp_offset_of_array_length_in_array_object_header, ()); 153 | DO_API(uint32_t, il2cpp_offset_of_array_bounds_in_array_object_header, ()); 154 | DO_API(uint32_t, il2cpp_allocation_granularity, ()); 155 | 156 | // liveness 157 | DO_API(void*, il2cpp_unity_liveness_allocate_struct, (Il2CppClass * filter, int max_object_count, il2cpp_register_object_callback callback, void* userdata, il2cpp_liveness_reallocate_callback reallocate)); 158 | DO_API(void, il2cpp_unity_liveness_calculation_from_root, (Il2CppObject * root, void* state)); 159 | DO_API(void, il2cpp_unity_liveness_calculation_from_statics, (void* state)); 160 | DO_API(void, il2cpp_unity_liveness_finalize, (void* state)); 161 | DO_API(void, il2cpp_unity_liveness_free_struct, (void* state)); 162 | 163 | // method 164 | DO_API(const Il2CppType*, il2cpp_method_get_return_type, (const MethodInfo * method)); 165 | DO_API(Il2CppClass*, il2cpp_method_get_declaring_type, (const MethodInfo * method)); 166 | DO_API(const char*, il2cpp_method_get_name, (const MethodInfo * method)); 167 | DO_API(const MethodInfo*, il2cpp_method_get_from_reflection, (const Il2CppReflectionMethod * method)); 168 | DO_API(Il2CppReflectionMethod*, il2cpp_method_get_object, (const MethodInfo * method, Il2CppClass * refclass)); 169 | DO_API(bool, il2cpp_method_is_generic, (const MethodInfo * method)); 170 | DO_API(bool, il2cpp_method_is_inflated, (const MethodInfo * method)); 171 | DO_API(bool, il2cpp_method_is_instance, (const MethodInfo * method)); 172 | DO_API(uint32_t, il2cpp_method_get_param_count, (const MethodInfo * method)); 173 | DO_API(const Il2CppType*, il2cpp_method_get_param, (const MethodInfo * method, uint32_t index)); 174 | DO_API(Il2CppClass*, il2cpp_method_get_class, (const MethodInfo * method)); 175 | DO_API(bool, il2cpp_method_has_attribute, (const MethodInfo * method, Il2CppClass * attr_class)); 176 | DO_API(uint32_t, il2cpp_method_get_flags, (const MethodInfo * method, uint32_t * iflags)); 177 | DO_API(uint32_t, il2cpp_method_get_token, (const MethodInfo * method)); 178 | DO_API(const char*, il2cpp_method_get_param_name, (const MethodInfo * method, uint32_t index)); 179 | 180 | // profiler 181 | #if IL2CPP_ENABLE_PROFILER 182 | 183 | DO_API(void, il2cpp_profiler_install, (Il2CppProfiler * prof, Il2CppProfileFunc shutdown_callback)); 184 | DO_API(void, il2cpp_profiler_set_events, (Il2CppProfileFlags events)); 185 | DO_API(void, il2cpp_profiler_install_enter_leave, (Il2CppProfileMethodFunc enter, Il2CppProfileMethodFunc fleave)); 186 | DO_API(void, il2cpp_profiler_install_allocation, (Il2CppProfileAllocFunc callback)); 187 | DO_API(void, il2cpp_profiler_install_gc, (Il2CppProfileGCFunc callback, Il2CppProfileGCResizeFunc heap_resize_callback)); 188 | DO_API(void, il2cpp_profiler_install_fileio, (Il2CppProfileFileIOFunc callback)); 189 | DO_API(void, il2cpp_profiler_install_thread, (Il2CppProfileThreadFunc start, Il2CppProfileThreadFunc end)); 190 | 191 | #endif 192 | 193 | // property 194 | DO_API(uint32_t, il2cpp_property_get_flags, (PropertyInfo * prop)); 195 | DO_API(const MethodInfo*, il2cpp_property_get_get_method, (PropertyInfo * prop)); 196 | DO_API(const MethodInfo*, il2cpp_property_get_set_method, (PropertyInfo * prop)); 197 | DO_API(const char*, il2cpp_property_get_name, (PropertyInfo * prop)); 198 | DO_API(Il2CppClass*, il2cpp_property_get_parent, (PropertyInfo * prop)); 199 | 200 | // object 201 | DO_API(Il2CppClass*, il2cpp_object_get_class, (Il2CppObject * obj)); 202 | DO_API(uint32_t, il2cpp_object_get_size, (Il2CppObject * obj)); 203 | DO_API(const MethodInfo*, il2cpp_object_get_virtual_method, (Il2CppObject * obj, const MethodInfo * method)); 204 | DO_API(Il2CppObject*, il2cpp_object_new, (const Il2CppClass * klass)); 205 | DO_API(void*, il2cpp_object_unbox, (Il2CppObject * obj)); 206 | 207 | DO_API(Il2CppObject*, il2cpp_value_box, (Il2CppClass * klass, void* data)); 208 | 209 | // monitor 210 | DO_API(void, il2cpp_monitor_enter, (Il2CppObject * obj)); 211 | DO_API(bool, il2cpp_monitor_try_enter, (Il2CppObject * obj, uint32_t timeout)); 212 | DO_API(void, il2cpp_monitor_exit, (Il2CppObject * obj)); 213 | DO_API(void, il2cpp_monitor_pulse, (Il2CppObject * obj)); 214 | DO_API(void, il2cpp_monitor_pulse_all, (Il2CppObject * obj)); 215 | DO_API(void, il2cpp_monitor_wait, (Il2CppObject * obj)); 216 | DO_API(bool, il2cpp_monitor_try_wait, (Il2CppObject * obj, uint32_t timeout)); 217 | 218 | // runtime 219 | DO_API(Il2CppObject*, il2cpp_runtime_invoke, (const MethodInfo * method, void *obj, void **params, Il2CppException **exc)); 220 | DO_API(Il2CppObject*, il2cpp_runtime_invoke_convert_args, (const MethodInfo * method, void *obj, Il2CppObject **params, int paramCount, Il2CppException **exc)); 221 | DO_API(void, il2cpp_runtime_class_init, (Il2CppClass * klass)); 222 | DO_API(void, il2cpp_runtime_object_init, (Il2CppObject * obj)); 223 | 224 | DO_API(void, il2cpp_runtime_object_init_exception, (Il2CppObject * obj, Il2CppException** exc)); 225 | 226 | DO_API(void, il2cpp_runtime_unhandled_exception_policy_set, (Il2CppRuntimeUnhandledExceptionPolicy value)); 227 | 228 | // string 229 | DO_API(int32_t, il2cpp_string_length, (Il2CppString * str)); 230 | DO_API(Il2CppChar*, il2cpp_string_chars, (Il2CppString * str)); 231 | DO_API(Il2CppString*, il2cpp_string_new, (const char* str)); 232 | DO_API(Il2CppString*, il2cpp_string_new_len, (const char* str, uint32_t length)); 233 | DO_API(Il2CppString*, il2cpp_string_new_utf16, (const Il2CppChar * text, int32_t len)); 234 | DO_API(Il2CppString*, il2cpp_string_new_wrapper, (const char* str)); 235 | DO_API(Il2CppString*, il2cpp_string_intern, (Il2CppString * str)); 236 | DO_API(Il2CppString*, il2cpp_string_is_interned, (Il2CppString * str)); 237 | 238 | // thread 239 | DO_API(Il2CppThread*, il2cpp_thread_current, ()); 240 | DO_API(Il2CppThread*, il2cpp_thread_attach, (Il2CppDomain * domain)); 241 | DO_API(void, il2cpp_thread_detach, (Il2CppThread * thread)); 242 | 243 | DO_API(Il2CppThread**, il2cpp_thread_get_all_attached_threads, (size_t * size)); 244 | DO_API(bool, il2cpp_is_vm_thread, (Il2CppThread * thread)); 245 | 246 | // stacktrace 247 | DO_API(void, il2cpp_current_thread_walk_frame_stack, (Il2CppFrameWalkFunc func, void* user_data)); 248 | DO_API(void, il2cpp_thread_walk_frame_stack, (Il2CppThread * thread, Il2CppFrameWalkFunc func, void* user_data)); 249 | DO_API(bool, il2cpp_current_thread_get_top_frame, (Il2CppStackFrameInfo * frame)); 250 | DO_API(bool, il2cpp_thread_get_top_frame, (Il2CppThread * thread, Il2CppStackFrameInfo * frame)); 251 | DO_API(bool, il2cpp_current_thread_get_frame_at, (int32_t offset, Il2CppStackFrameInfo * frame)); 252 | DO_API(bool, il2cpp_thread_get_frame_at, (Il2CppThread * thread, int32_t offset, Il2CppStackFrameInfo * frame)); 253 | DO_API(int32_t, il2cpp_current_thread_get_stack_depth, ()); 254 | DO_API(int32_t, il2cpp_thread_get_stack_depth, (Il2CppThread * thread)); 255 | DO_API(void, il2cpp_override_stack_backtrace, (Il2CppBacktraceFunc stackBacktraceFunc)); 256 | 257 | // type 258 | DO_API(Il2CppObject*, il2cpp_type_get_object, (const Il2CppType * type)); 259 | DO_API(int, il2cpp_type_get_type, (const Il2CppType * type)); 260 | DO_API(Il2CppClass*, il2cpp_type_get_class_or_element_class, (const Il2CppType * type)); 261 | DO_API(char*, il2cpp_type_get_name, (const Il2CppType * type)); 262 | DO_API(bool, il2cpp_type_is_byref, (const Il2CppType * type)); 263 | DO_API(uint32_t, il2cpp_type_get_attrs, (const Il2CppType * type)); 264 | DO_API(bool, il2cpp_type_equals, (const Il2CppType * type, const Il2CppType * otherType)); 265 | DO_API(char*, il2cpp_type_get_assembly_qualified_name, (const Il2CppType * type)); 266 | DO_API(bool, il2cpp_type_is_static, (const Il2CppType * type)); 267 | DO_API(bool, il2cpp_type_is_pointer_type, (const Il2CppType * type)); 268 | 269 | // image 270 | DO_API(const Il2CppAssembly*, il2cpp_image_get_assembly, (const Il2CppImage * image)); 271 | DO_API(const char*, il2cpp_image_get_name, (const Il2CppImage * image)); 272 | DO_API(const char*, il2cpp_image_get_filename, (const Il2CppImage * image)); 273 | DO_API(const MethodInfo*, il2cpp_image_get_entry_point, (const Il2CppImage * image)); 274 | 275 | DO_API(size_t, il2cpp_image_get_class_count, (const Il2CppImage * image)); 276 | DO_API(const Il2CppClass*, il2cpp_image_get_class, (const Il2CppImage * image, size_t index)); 277 | 278 | // Memory information 279 | DO_API(Il2CppManagedMemorySnapshot*, il2cpp_capture_memory_snapshot, ()); 280 | DO_API(void, il2cpp_free_captured_memory_snapshot, (Il2CppManagedMemorySnapshot * snapshot)); 281 | 282 | DO_API(void, il2cpp_set_find_plugin_callback, (Il2CppSetFindPlugInCallback method)); 283 | 284 | // Logging 285 | DO_API(void, il2cpp_register_log_callback, (Il2CppLogCallback method)); 286 | 287 | // Debugger 288 | DO_API(void, il2cpp_debugger_set_agent_options, (const char* options)); 289 | DO_API(bool, il2cpp_is_debugger_attached, ()); 290 | DO_API(void, il2cpp_register_debugger_agent_transport, (Il2CppDebuggerTransport * debuggerTransport)); 291 | 292 | // Debug metadata 293 | DO_API(bool, il2cpp_debug_get_method_info, (const MethodInfo*, Il2CppMethodDebugInfo * methodDebugInfo)); 294 | 295 | // TLS module 296 | DO_API(void, il2cpp_unity_install_unitytls_interface, (const void* unitytlsInterfaceStruct)); 297 | 298 | // custom attributes 299 | DO_API(Il2CppCustomAttrInfo*, il2cpp_custom_attrs_from_class, (Il2CppClass * klass)); 300 | DO_API(Il2CppCustomAttrInfo*, il2cpp_custom_attrs_from_method, (const MethodInfo * method)); 301 | 302 | DO_API(Il2CppObject*, il2cpp_custom_attrs_get_attr, (Il2CppCustomAttrInfo * ainfo, Il2CppClass * attr_klass)); 303 | DO_API(bool, il2cpp_custom_attrs_has_attr, (Il2CppCustomAttrInfo * ainfo, Il2CppClass * attr_klass)); 304 | DO_API(Il2CppArray*, il2cpp_custom_attrs_construct, (Il2CppCustomAttrInfo * cinfo)); 305 | 306 | DO_API(void, il2cpp_custom_attrs_free, (Il2CppCustomAttrInfo * ainfo)); 307 | 308 | // Il2CppClass user data for GetComponent optimization 309 | DO_API(void, il2cpp_class_set_userdata, (Il2CppClass * klass, void* userdata)); 310 | DO_API(int, il2cpp_class_get_userdata_offset, ()); 311 | 312 | DO_API(void, il2cpp_set_default_thread_affinity, (int64_t affinity_mask)); 313 | -------------------------------------------------------------------------------- /module/src/main/cpp/xdl/xdl.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-04. 23 | 24 | #include "xdl.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include "xdl_iterate.h" 45 | #include "xdl_linker.h" 46 | #include "xdl_lzma.h" 47 | #include "xdl_util.h" 48 | 49 | #ifndef __LP64__ 50 | #define XDL_LIB_PATH "/system/lib" 51 | #else 52 | #define XDL_LIB_PATH "/system/lib64" 53 | #endif 54 | 55 | #define XDL_DYNSYM_IS_EXPORT_SYM(shndx) (SHN_UNDEF != (shndx)) 56 | #define XDL_SYMTAB_IS_EXPORT_SYM(shndx) \ 57 | (SHN_UNDEF != (shndx) && !((shndx) >= SHN_LORESERVE && (shndx) <= SHN_HIRESERVE)) 58 | 59 | extern __attribute((weak)) unsigned long int getauxval(unsigned long int); 60 | 61 | #pragma clang diagnostic push 62 | #pragma clang diagnostic ignored "-Wpadded" 63 | 64 | typedef struct xdl { 65 | char *pathname; 66 | uintptr_t load_bias; 67 | const ElfW(Phdr) *dlpi_phdr; 68 | ElfW(Half) dlpi_phnum; 69 | 70 | struct xdl *next; // to next xdl obj for cache in xdl_addr() 71 | void *linker_handle; // hold handle returned by xdl_linker_load() 72 | 73 | // 74 | // (1) for searching symbols from .dynsym 75 | // 76 | 77 | bool dynsym_try_load; 78 | ElfW(Sym) *dynsym; // .dynsym 79 | const char *dynstr; // .dynstr 80 | 81 | // .hash (SYSV hash for .dynstr) 82 | struct { 83 | const uint32_t *buckets; 84 | uint32_t buckets_cnt; 85 | const uint32_t *chains; 86 | uint32_t chains_cnt; 87 | } sysv_hash; 88 | 89 | // .gnu.hash (GNU hash for .dynstr) 90 | struct { 91 | const uint32_t *buckets; 92 | uint32_t buckets_cnt; 93 | const uint32_t *chains; 94 | uint32_t symoffset; 95 | const ElfW(Addr) *bloom; 96 | uint32_t bloom_cnt; 97 | uint32_t bloom_shift; 98 | } gnu_hash; 99 | 100 | // 101 | // (2) for searching symbols from .symtab 102 | // 103 | 104 | bool symtab_try_load; 105 | uintptr_t base; 106 | 107 | ElfW(Sym) *symtab; // .symtab 108 | size_t symtab_cnt; 109 | char *strtab; // .strtab 110 | size_t strtab_sz; 111 | } xdl_t; 112 | 113 | #pragma clang diagnostic pop 114 | 115 | // load from memory 116 | static int xdl_dynsym_load(xdl_t *self) { 117 | // find the dynamic segment 118 | ElfW(Dyn) *dynamic = NULL; 119 | for (size_t i = 0; i < self->dlpi_phnum; i++) { 120 | const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]); 121 | if (PT_DYNAMIC == phdr->p_type) { 122 | dynamic = (ElfW(Dyn) *)(self->load_bias + phdr->p_vaddr); 123 | break; 124 | } 125 | } 126 | if (NULL == dynamic) return -1; 127 | 128 | // iterate the dynamic segment 129 | for (ElfW(Dyn) *entry = dynamic; entry && entry->d_tag != DT_NULL; entry++) { 130 | switch (entry->d_tag) { 131 | case DT_SYMTAB: //.dynsym 132 | self->dynsym = (ElfW(Sym) *)(self->load_bias + entry->d_un.d_ptr); 133 | break; 134 | case DT_STRTAB: //.dynstr 135 | self->dynstr = (const char *)(self->load_bias + entry->d_un.d_ptr); 136 | break; 137 | case DT_HASH: //.hash 138 | self->sysv_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0]; 139 | self->sysv_hash.chains_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1]; 140 | self->sysv_hash.buckets = &(((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2]); 141 | self->sysv_hash.chains = &(self->sysv_hash.buckets[self->sysv_hash.buckets_cnt]); 142 | break; 143 | case DT_GNU_HASH: //.gnu.hash 144 | self->gnu_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0]; 145 | self->gnu_hash.symoffset = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1]; 146 | self->gnu_hash.bloom_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2]; 147 | self->gnu_hash.bloom_shift = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[3]; 148 | self->gnu_hash.bloom = (const ElfW(Addr) *)(self->load_bias + entry->d_un.d_ptr + 16); 149 | self->gnu_hash.buckets = (const uint32_t *)(&(self->gnu_hash.bloom[self->gnu_hash.bloom_cnt])); 150 | self->gnu_hash.chains = (const uint32_t *)(&(self->gnu_hash.buckets[self->gnu_hash.buckets_cnt])); 151 | break; 152 | default: 153 | break; 154 | } 155 | } 156 | 157 | if (NULL == self->dynsym || NULL == self->dynstr || 158 | (0 == self->sysv_hash.buckets_cnt && 0 == self->gnu_hash.buckets_cnt)) { 159 | self->dynsym = NULL; 160 | self->dynstr = NULL; 161 | self->sysv_hash.buckets_cnt = 0; 162 | self->gnu_hash.buckets_cnt = 0; 163 | return -1; 164 | } 165 | 166 | return 0; 167 | } 168 | 169 | static void *xdl_read_file_to_heap(int file_fd, size_t file_sz, size_t data_offset, size_t data_len) { 170 | if (0 == data_len) return NULL; 171 | if (data_offset >= file_sz) return NULL; 172 | if (data_offset + data_len > file_sz) return NULL; 173 | 174 | if (data_offset != (size_t)lseek(file_fd, (off_t)data_offset, SEEK_SET)) return NULL; 175 | 176 | void *data = malloc(data_len); 177 | if (NULL == data) return NULL; 178 | 179 | #pragma clang diagnostic push 180 | #pragma clang diagnostic ignored "-Wgnu-statement-expression" 181 | if ((ssize_t)data_len != XDL_UTIL_TEMP_FAILURE_RETRY(read(file_fd, data, data_len))) 182 | #pragma clang diagnostic pop 183 | { 184 | free(data); 185 | return NULL; 186 | } 187 | 188 | return data; 189 | } 190 | 191 | static void *xdl_read_file_to_heap_by_section(int file_fd, size_t file_sz, ElfW(Shdr) *shdr) { 192 | return xdl_read_file_to_heap(file_fd, file_sz, (size_t)shdr->sh_offset, shdr->sh_size); 193 | } 194 | 195 | static void *xdl_read_memory_to_heap(void *mem, size_t mem_sz, size_t data_offset, size_t data_len) { 196 | if (0 == data_len) return NULL; 197 | if (data_offset >= mem_sz) return NULL; 198 | if (data_offset + data_len > mem_sz) return NULL; 199 | 200 | void *data = malloc(data_len); 201 | if (NULL == data) return NULL; 202 | 203 | memcpy(data, (void *)((uintptr_t)mem + data_offset), data_len); 204 | return data; 205 | } 206 | 207 | static void *xdl_read_memory_to_heap_by_section(void *mem, size_t mem_sz, ElfW(Shdr) *shdr) { 208 | return xdl_read_memory_to_heap(mem, mem_sz, (size_t)shdr->sh_offset, shdr->sh_size); 209 | } 210 | 211 | static void *xdl_get_memory(void *mem, size_t mem_sz, size_t data_offset, size_t data_len) { 212 | if (0 == data_len) return NULL; 213 | if (data_offset >= mem_sz) return NULL; 214 | if (data_offset + data_len > mem_sz) return NULL; 215 | 216 | return (void *)((uintptr_t)mem + data_offset); 217 | } 218 | 219 | static void *xdl_get_memory_by_section(void *mem, size_t mem_sz, ElfW(Shdr) *shdr) { 220 | return xdl_get_memory(mem, mem_sz, (size_t)shdr->sh_offset, shdr->sh_size); 221 | } 222 | 223 | // load from disk and memory 224 | static int xdl_symtab_load_from_debugdata(xdl_t *self, int file_fd, size_t file_sz, 225 | ElfW(Shdr) *shdr_debugdata) { 226 | void *debugdata = NULL; 227 | ElfW(Shdr) *shdrs = NULL; 228 | int r = -1; 229 | 230 | // get zipped .gnu_debugdata 231 | uint8_t *debugdata_zip = (uint8_t *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr_debugdata); 232 | if (NULL == debugdata_zip) return -1; 233 | 234 | // get unzipped .gnu_debugdata 235 | size_t debugdata_sz; 236 | if (0 != xdl_lzma_decompress(debugdata_zip, shdr_debugdata->sh_size, (uint8_t **)&debugdata, &debugdata_sz)) 237 | goto end; 238 | 239 | // get ELF header 240 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)debugdata; 241 | if (0 == ehdr->e_shnum || ehdr->e_shentsize != sizeof(ElfW(Shdr))) goto end; 242 | 243 | // get section headers 244 | shdrs = (ElfW(Shdr) *)xdl_read_memory_to_heap(debugdata, debugdata_sz, (size_t)ehdr->e_shoff, 245 | ehdr->e_shentsize * ehdr->e_shnum); 246 | if (NULL == shdrs) goto end; 247 | 248 | // get .shstrtab 249 | if (SHN_UNDEF == ehdr->e_shstrndx || ehdr->e_shstrndx >= ehdr->e_shnum) goto end; 250 | char *shstrtab = (char *)xdl_get_memory_by_section(debugdata, debugdata_sz, shdrs + ehdr->e_shstrndx); 251 | if (NULL == shstrtab) goto end; 252 | 253 | // find .symtab & .strtab 254 | for (ElfW(Shdr) *shdr = shdrs; shdr < shdrs + ehdr->e_shnum; shdr++) { 255 | char *shdr_name = shstrtab + shdr->sh_name; 256 | 257 | if (SHT_SYMTAB == shdr->sh_type && 0 == strcmp(".symtab", shdr_name)) { 258 | // get & check associated .strtab section 259 | if (shdr->sh_link >= ehdr->e_shnum) continue; 260 | ElfW(Shdr) *shdr_strtab = shdrs + shdr->sh_link; 261 | if (SHT_STRTAB != shdr_strtab->sh_type) continue; 262 | 263 | // get .symtab & .strtab 264 | ElfW(Sym) *symtab = (ElfW(Sym) *)xdl_read_memory_to_heap_by_section(debugdata, debugdata_sz, shdr); 265 | if (NULL == symtab) continue; 266 | char *strtab = (char *)xdl_read_memory_to_heap_by_section(debugdata, debugdata_sz, shdr_strtab); 267 | if (NULL == strtab) { 268 | free(symtab); 269 | continue; 270 | } 271 | 272 | // OK 273 | self->symtab = symtab; 274 | self->symtab_cnt = shdr->sh_size / shdr->sh_entsize; 275 | self->strtab = strtab; 276 | self->strtab_sz = shdr_strtab->sh_size; 277 | r = 0; 278 | break; 279 | } 280 | } 281 | 282 | end: 283 | free(debugdata_zip); 284 | if (NULL != debugdata) free(debugdata); 285 | if (NULL != shdrs) free(shdrs); 286 | return r; 287 | } 288 | 289 | // load from disk and memory 290 | static int xdl_symtab_load(xdl_t *self) { 291 | if ('[' == self->pathname[0]) return -1; 292 | 293 | int r = -1; 294 | ElfW(Shdr) *shdrs = NULL; 295 | char *shstrtab = NULL; 296 | 297 | // get base address 298 | uintptr_t vaddr_min = UINTPTR_MAX; 299 | for (size_t i = 0; i < self->dlpi_phnum; i++) { 300 | const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]); 301 | if (PT_LOAD == phdr->p_type) { 302 | if (vaddr_min > phdr->p_vaddr) vaddr_min = phdr->p_vaddr; 303 | } 304 | } 305 | if (UINTPTR_MAX == vaddr_min) return -1; 306 | self->base = self->load_bias + vaddr_min; 307 | 308 | // open file 309 | int flags = O_RDONLY | O_CLOEXEC; 310 | int file_fd; 311 | if ('/' == self->pathname[0]) { 312 | file_fd = open(self->pathname, flags); 313 | } else { 314 | char full_pathname[1024]; 315 | // try the fast method 316 | snprintf(full_pathname, sizeof(full_pathname), "%s/%s", XDL_LIB_PATH, self->pathname); 317 | file_fd = open(full_pathname, flags); 318 | if (file_fd < 0) { 319 | // try the slow method 320 | if (0 != xdl_iterate_get_full_pathname(self->base, full_pathname, sizeof(full_pathname))) return -1; 321 | file_fd = open(full_pathname, flags); 322 | } 323 | } 324 | if (file_fd < 0) return -1; 325 | struct stat st; 326 | if (0 != fstat(file_fd, &st)) goto end; 327 | size_t file_sz = (size_t)st.st_size; 328 | 329 | // get ELF header 330 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)self->base; 331 | if (0 == ehdr->e_shnum || ehdr->e_shentsize != sizeof(ElfW(Shdr))) goto end; 332 | 333 | // get section headers 334 | shdrs = (ElfW(Shdr) *)xdl_read_file_to_heap(file_fd, file_sz, (size_t)ehdr->e_shoff, 335 | ehdr->e_shentsize * ehdr->e_shnum); 336 | if (NULL == shdrs) goto end; 337 | 338 | // get .shstrtab 339 | if (SHN_UNDEF == ehdr->e_shstrndx || ehdr->e_shstrndx >= ehdr->e_shnum) goto end; 340 | shstrtab = (char *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdrs + ehdr->e_shstrndx); 341 | if (NULL == shstrtab) goto end; 342 | 343 | // find .symtab & .strtab 344 | for (ElfW(Shdr) *shdr = shdrs; shdr < shdrs + ehdr->e_shnum; shdr++) { 345 | char *shdr_name = shstrtab + shdr->sh_name; 346 | 347 | if (SHT_SYMTAB == shdr->sh_type && 0 == strcmp(".symtab", shdr_name)) { 348 | // get & check associated .strtab section 349 | if (shdr->sh_link >= ehdr->e_shnum) continue; 350 | ElfW(Shdr) *shdr_strtab = shdrs + shdr->sh_link; 351 | if (SHT_STRTAB != shdr_strtab->sh_type) continue; 352 | 353 | // get .symtab & .strtab 354 | ElfW(Sym) *symtab = (ElfW(Sym) *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr); 355 | if (NULL == symtab) continue; 356 | char *strtab = (char *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr_strtab); 357 | if (NULL == strtab) { 358 | free(symtab); 359 | continue; 360 | } 361 | 362 | // OK 363 | self->symtab = symtab; 364 | self->symtab_cnt = shdr->sh_size / shdr->sh_entsize; 365 | self->strtab = strtab; 366 | self->strtab_sz = shdr_strtab->sh_size; 367 | r = 0; 368 | break; 369 | } else if (SHT_PROGBITS == shdr->sh_type && 0 == strcmp(".gnu_debugdata", shdr_name)) { 370 | if (0 == xdl_symtab_load_from_debugdata(self, file_fd, file_sz, shdr)) { 371 | // OK 372 | r = 0; 373 | break; 374 | } 375 | } 376 | } 377 | 378 | end: 379 | close(file_fd); 380 | if (NULL != shdrs) free(shdrs); 381 | if (NULL != shstrtab) free(shstrtab); 382 | return r; 383 | } 384 | 385 | static xdl_t *xdl_find_from_auxv(unsigned long type, const char *pathname) { 386 | if (NULL == getauxval) return NULL; 387 | 388 | uintptr_t val = (uintptr_t)getauxval(type); 389 | if (0 == val) return NULL; 390 | 391 | // get base 392 | uintptr_t base = (AT_PHDR == type ? (val & (~0xffful)) : val); 393 | if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) return NULL; 394 | 395 | // ELF info 396 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base; 397 | const ElfW(Phdr) *dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff); 398 | ElfW(Half) dlpi_phnum = ehdr->e_phnum; 399 | 400 | // get bias 401 | uintptr_t min_vaddr = UINTPTR_MAX; 402 | for (size_t i = 0; i < dlpi_phnum; i++) { 403 | const ElfW(Phdr) *phdr = &(dlpi_phdr[i]); 404 | if (PT_LOAD == phdr->p_type) { 405 | if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr; 406 | } 407 | } 408 | if (UINTPTR_MAX == min_vaddr || base < min_vaddr) return NULL; 409 | uintptr_t load_bias = base - min_vaddr; 410 | 411 | // create xDL object 412 | xdl_t *self; 413 | if (NULL == (self = calloc(1, sizeof(xdl_t)))) return NULL; 414 | if (NULL == (self->pathname = strdup(pathname))) { 415 | free(self); 416 | return NULL; 417 | } 418 | self->load_bias = load_bias; 419 | self->dlpi_phdr = dlpi_phdr; 420 | self->dlpi_phnum = dlpi_phnum; 421 | self->dynsym_try_load = false; 422 | self->symtab_try_load = false; 423 | return self; 424 | } 425 | 426 | static int xdl_find_iterate_cb(struct dl_phdr_info *info, size_t size, void *arg) { 427 | (void)size; 428 | 429 | uintptr_t *pkg = (uintptr_t *)arg; 430 | xdl_t **self = (xdl_t **)*pkg++; 431 | const char *filename = (const char *)*pkg; 432 | 433 | // check load_bias 434 | if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0; 435 | 436 | // check pathname 437 | if ('[' == filename[0]) { 438 | if (0 != strcmp(info->dlpi_name, filename)) return 0; 439 | } else if ('/' == filename[0]) { 440 | if ('/' == info->dlpi_name[0]) { 441 | if (0 != strcmp(info->dlpi_name, filename)) return 0; 442 | } else { 443 | if (!xdl_util_ends_with(filename, info->dlpi_name)) return 0; 444 | } 445 | } else { 446 | if ('/' == info->dlpi_name[0]) { 447 | if (!xdl_util_ends_with(info->dlpi_name, filename)) return 0; 448 | } else { 449 | if (0 != strcmp(info->dlpi_name, filename)) return 0; 450 | } 451 | } 452 | 453 | // found the target ELF 454 | if (NULL == ((*self) = calloc(1, sizeof(xdl_t)))) return 1; // return failed 455 | if (NULL == ((*self)->pathname = strdup(info->dlpi_name))) { 456 | free(*self); 457 | *self = NULL; 458 | return 1; // return failed 459 | } 460 | (*self)->load_bias = info->dlpi_addr; 461 | (*self)->dlpi_phdr = info->dlpi_phdr; 462 | (*self)->dlpi_phnum = info->dlpi_phnum; 463 | (*self)->dynsym_try_load = false; 464 | (*self)->symtab_try_load = false; 465 | return 1; // return OK 466 | } 467 | 468 | static xdl_t *xdl_find(const char *filename) { 469 | // from auxv (linker, vDSO) 470 | xdl_t *self = NULL; 471 | if (xdl_util_ends_with(filename, XDL_UTIL_LINKER_BASENAME)) 472 | self = xdl_find_from_auxv(AT_BASE, XDL_UTIL_LINKER_PATHNAME); 473 | else if (xdl_util_ends_with(filename, XDL_UTIL_VDSO_BASENAME)) 474 | self = xdl_find_from_auxv(AT_SYSINFO_EHDR, XDL_UTIL_VDSO_BASENAME); 475 | 476 | // from auxv (app_process) 477 | const char *basename, *pathname; 478 | #if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__ 479 | if (xdl_util_get_api_level() < __ANDROID_API_L__) { 480 | basename = XDL_UTIL_APP_PROCESS_BASENAME_K; 481 | pathname = XDL_UTIL_APP_PROCESS_PATHNAME_K; 482 | } else 483 | #endif 484 | { 485 | basename = XDL_UTIL_APP_PROCESS_BASENAME; 486 | pathname = XDL_UTIL_APP_PROCESS_PATHNAME; 487 | } 488 | if (xdl_util_ends_with(filename, basename)) self = xdl_find_from_auxv(AT_PHDR, pathname); 489 | 490 | if (NULL != self) return self; 491 | 492 | // from dl_iterate_phdr 493 | uintptr_t pkg[2] = {(uintptr_t)&self, (uintptr_t)filename}; 494 | xdl_iterate_phdr(xdl_find_iterate_cb, pkg, XDL_DEFAULT); 495 | return self; 496 | } 497 | 498 | static void *xdl_open_always_force(const char *filename) { 499 | // always force dlopen() 500 | void *linker_handle = xdl_linker_load(filename); 501 | if (NULL == linker_handle) return NULL; 502 | 503 | // find 504 | xdl_t *self = xdl_find(filename); 505 | if (NULL == self) 506 | dlclose(linker_handle); 507 | else 508 | self->linker_handle = linker_handle; 509 | 510 | return (void *)self; 511 | } 512 | 513 | static void *xdl_open_try_force(const char *filename) { 514 | // find 515 | xdl_t *self = xdl_find(filename); 516 | if (NULL != self) return (void *)self; 517 | 518 | // try force dlopen() 519 | void *linker_handle = xdl_linker_load(filename); 520 | if (NULL == linker_handle) return NULL; 521 | 522 | // find again 523 | self = xdl_find(filename); 524 | if (NULL == self) 525 | dlclose(linker_handle); 526 | else 527 | self->linker_handle = linker_handle; 528 | 529 | return (void *)self; 530 | } 531 | 532 | void *xdl_open(const char *filename, int flags) { 533 | if (NULL == filename) return NULL; 534 | 535 | if (flags & XDL_ALWAYS_FORCE_LOAD) 536 | return xdl_open_always_force(filename); 537 | else if (flags & XDL_TRY_FORCE_LOAD) 538 | return xdl_open_try_force(filename); 539 | else 540 | return xdl_find(filename); 541 | } 542 | 543 | void *xdl_close(void *handle) { 544 | if (NULL == handle) return NULL; 545 | 546 | xdl_t *self = (xdl_t *)handle; 547 | if (NULL != self->pathname) free(self->pathname); 548 | if (NULL != self->symtab) free(self->symtab); 549 | if (NULL != self->strtab) free(self->strtab); 550 | 551 | void *linker_handle = self->linker_handle; 552 | free(self); 553 | return linker_handle; 554 | } 555 | 556 | static uint32_t xdl_sysv_hash(const uint8_t *name) { 557 | uint32_t h = 0, g; 558 | 559 | while (*name) { 560 | h = (h << 4) + *name++; 561 | g = h & 0xf0000000; 562 | h ^= g; 563 | h ^= g >> 24; 564 | } 565 | return h; 566 | } 567 | 568 | static uint32_t xdl_gnu_hash(const uint8_t *name) { 569 | uint32_t h = 5381; 570 | 571 | while (*name) { 572 | h += (h << 5) + *name++; 573 | } 574 | return h; 575 | } 576 | 577 | static ElfW(Sym) *xdl_dynsym_find_symbol_use_sysv_hash(xdl_t *self, const char *sym_name) { 578 | uint32_t hash = xdl_sysv_hash((const uint8_t *)sym_name); 579 | 580 | for (uint32_t i = self->sysv_hash.buckets[hash % self->sysv_hash.buckets_cnt]; 0 != i; 581 | i = self->sysv_hash.chains[i]) { 582 | ElfW(Sym) *sym = self->dynsym + i; 583 | if (0 != strcmp(self->dynstr + sym->st_name, sym_name)) continue; 584 | return sym; 585 | } 586 | 587 | return NULL; 588 | } 589 | 590 | static ElfW(Sym) *xdl_dynsym_find_symbol_use_gnu_hash(xdl_t *self, const char *sym_name) { 591 | uint32_t hash = xdl_gnu_hash((const uint8_t *)sym_name); 592 | 593 | static uint32_t elfclass_bits = sizeof(ElfW(Addr)) * 8; 594 | size_t word = self->gnu_hash.bloom[(hash / elfclass_bits) % self->gnu_hash.bloom_cnt]; 595 | size_t mask = 0 | (size_t)1 << (hash % elfclass_bits) | 596 | (size_t)1 << ((hash >> self->gnu_hash.bloom_shift) % elfclass_bits); 597 | 598 | // if at least one bit is not set, this symbol is surely missing 599 | if ((word & mask) != mask) return NULL; 600 | 601 | // ignore STN_UNDEF 602 | uint32_t i = self->gnu_hash.buckets[hash % self->gnu_hash.buckets_cnt]; 603 | if (i < self->gnu_hash.symoffset) return NULL; 604 | 605 | // loop through the chain 606 | while (1) { 607 | ElfW(Sym) *sym = self->dynsym + i; 608 | uint32_t sym_hash = self->gnu_hash.chains[i - self->gnu_hash.symoffset]; 609 | 610 | if ((hash | (uint32_t)1) == (sym_hash | (uint32_t)1)) { 611 | if (0 == strcmp(self->dynstr + sym->st_name, sym_name)) { 612 | return sym; 613 | } 614 | } 615 | 616 | // chain ends with an element with the lowest bit set to 1 617 | if (sym_hash & (uint32_t)1) break; 618 | 619 | i++; 620 | } 621 | 622 | return NULL; 623 | } 624 | 625 | void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size) { 626 | if (NULL == handle || NULL == symbol) return NULL; 627 | if (NULL != symbol_size) *symbol_size = 0; 628 | 629 | xdl_t *self = (xdl_t *)handle; 630 | 631 | // load .dynsym only once 632 | if (!self->dynsym_try_load) { 633 | self->dynsym_try_load = true; 634 | if (0 != xdl_dynsym_load(self)) return NULL; 635 | } 636 | 637 | // find symbol 638 | if (NULL == self->dynsym) return NULL; 639 | ElfW(Sym) *sym = NULL; 640 | if (self->gnu_hash.buckets_cnt > 0) { 641 | // use GNU hash (.gnu.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1) 642 | sym = xdl_dynsym_find_symbol_use_gnu_hash(self, symbol); 643 | } 644 | if (NULL == sym && self->sysv_hash.buckets_cnt > 0) { 645 | // use SYSV hash (.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1) 646 | sym = xdl_dynsym_find_symbol_use_sysv_hash(self, symbol); 647 | } 648 | if (NULL == sym || !XDL_DYNSYM_IS_EXPORT_SYM(sym->st_shndx)) return NULL; 649 | 650 | if (NULL != symbol_size) *symbol_size = sym->st_size; 651 | return (void *)(self->load_bias + sym->st_value); 652 | } 653 | 654 | void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size) { 655 | if (NULL == handle || NULL == symbol) return NULL; 656 | if (NULL != symbol_size) *symbol_size = 0; 657 | 658 | xdl_t *self = (xdl_t *)handle; 659 | 660 | // load .symtab only once 661 | if (!self->symtab_try_load) { 662 | self->symtab_try_load = true; 663 | if (0 != xdl_symtab_load(self)) return NULL; 664 | } 665 | 666 | // find symbol 667 | if (NULL == self->symtab) return NULL; 668 | for (size_t i = 0; i < self->symtab_cnt; i++) { 669 | ElfW(Sym) *sym = self->symtab + i; 670 | 671 | if (!XDL_SYMTAB_IS_EXPORT_SYM(sym->st_shndx)) continue; 672 | if (0 != strncmp(self->strtab + sym->st_name, symbol, self->strtab_sz - sym->st_name)) continue; 673 | 674 | if (NULL != symbol_size) *symbol_size = sym->st_size; 675 | return (void *)(self->load_bias + sym->st_value); 676 | } 677 | 678 | return NULL; 679 | } 680 | 681 | static bool xdl_elf_is_match(uintptr_t load_bias, const ElfW(Phdr) *dlpi_phdr, ElfW(Half) dlpi_phnum, 682 | uintptr_t addr) { 683 | if (addr < load_bias) return false; 684 | 685 | uintptr_t vaddr = addr - load_bias; 686 | for (size_t i = 0; i < dlpi_phnum; i++) { 687 | const ElfW(Phdr) *phdr = &(dlpi_phdr[i]); 688 | if (PT_LOAD != phdr->p_type) continue; 689 | 690 | if (phdr->p_vaddr <= vaddr && vaddr < phdr->p_vaddr + phdr->p_memsz) return true; 691 | } 692 | 693 | return false; 694 | } 695 | 696 | static int xdl_open_by_addr_iterate_cb(struct dl_phdr_info *info, size_t size, void *arg) { 697 | (void)size; 698 | 699 | uintptr_t *pkg = (uintptr_t *)arg; 700 | xdl_t **self = (xdl_t **)*pkg++; 701 | uintptr_t addr = *pkg; 702 | 703 | if (xdl_elf_is_match(info->dlpi_addr, info->dlpi_phdr, info->dlpi_phnum, addr)) { 704 | // found the target ELF 705 | if (NULL == ((*self) = calloc(1, sizeof(xdl_t)))) return 1; // failed 706 | if (NULL == ((*self)->pathname = strdup(info->dlpi_name))) { 707 | free(*self); 708 | *self = NULL; 709 | return 1; // failed 710 | } 711 | (*self)->load_bias = info->dlpi_addr; 712 | (*self)->dlpi_phdr = info->dlpi_phdr; 713 | (*self)->dlpi_phnum = info->dlpi_phnum; 714 | (*self)->dynsym_try_load = false; 715 | (*self)->symtab_try_load = false; 716 | return 1; // OK 717 | } 718 | 719 | return 0; // mismatch 720 | } 721 | 722 | static void *xdl_open_by_addr(void *addr) { 723 | if (NULL == addr) return NULL; 724 | 725 | xdl_t *self = NULL; 726 | uintptr_t pkg[2] = {(uintptr_t)&self, (uintptr_t)addr}; 727 | xdl_iterate_phdr(xdl_open_by_addr_iterate_cb, pkg, XDL_DEFAULT); 728 | 729 | return (void *)self; 730 | } 731 | 732 | static bool xdl_sym_is_match(ElfW(Sym) *sym, uintptr_t offset, bool is_symtab) { 733 | if (is_symtab) { 734 | if (!XDL_SYMTAB_IS_EXPORT_SYM(sym->st_shndx)) false; 735 | } else { 736 | if (!XDL_DYNSYM_IS_EXPORT_SYM(sym->st_shndx)) false; 737 | } 738 | 739 | return ELF_ST_TYPE(sym->st_info) != STT_TLS && offset >= sym->st_value && 740 | offset < sym->st_value + sym->st_size; 741 | } 742 | 743 | static ElfW(Sym) *xdl_sym_by_addr(void *handle, void *addr) { 744 | xdl_t *self = (xdl_t *)handle; 745 | 746 | // load .dynsym only once 747 | if (!self->dynsym_try_load) { 748 | self->dynsym_try_load = true; 749 | if (0 != xdl_dynsym_load(self)) return NULL; 750 | } 751 | 752 | // find symbol 753 | if (NULL == self->dynsym) return NULL; 754 | uintptr_t offset = (uintptr_t)addr - self->load_bias; 755 | if (self->gnu_hash.buckets_cnt > 0) { 756 | const uint32_t *chains_all = self->gnu_hash.chains - self->gnu_hash.symoffset; 757 | for (size_t i = 0; i < self->gnu_hash.buckets_cnt; i++) { 758 | uint32_t n = self->gnu_hash.buckets[i]; 759 | if (n < self->gnu_hash.symoffset) continue; 760 | do { 761 | ElfW(Sym) *sym = self->dynsym + n; 762 | if (xdl_sym_is_match(sym, offset, false)) return sym; 763 | } while ((chains_all[n++] & 1) == 0); 764 | } 765 | } else if (self->sysv_hash.chains_cnt > 0) { 766 | for (size_t i = 0; i < self->sysv_hash.chains_cnt; i++) { 767 | ElfW(Sym) *sym = self->dynsym + i; 768 | if (xdl_sym_is_match(sym, offset, false)) return sym; 769 | } 770 | } 771 | 772 | return NULL; 773 | } 774 | 775 | static ElfW(Sym) *xdl_dsym_by_addr(void *handle, void *addr) { 776 | xdl_t *self = (xdl_t *)handle; 777 | 778 | // load .symtab only once 779 | if (!self->symtab_try_load) { 780 | self->symtab_try_load = true; 781 | if (0 != xdl_symtab_load(self)) return NULL; 782 | } 783 | 784 | // find symbol 785 | if (NULL == self->symtab) return NULL; 786 | uintptr_t offset = (uintptr_t)addr - self->load_bias; 787 | for (size_t i = 0; i < self->symtab_cnt; i++) { 788 | ElfW(Sym) *sym = self->symtab + i; 789 | if (xdl_sym_is_match(sym, offset, true)) return sym; 790 | } 791 | 792 | return NULL; 793 | } 794 | 795 | int xdl_addr(void *addr, xdl_info_t *info, void **cache) { 796 | if (NULL == addr || NULL == info || NULL == cache) return 0; 797 | 798 | memset(info, 0, sizeof(Dl_info)); 799 | 800 | // find handle from cache 801 | xdl_t *handle = NULL; 802 | for (handle = *((xdl_t **)cache); NULL != handle; handle = handle->next) 803 | if (xdl_elf_is_match(handle->load_bias, handle->dlpi_phdr, handle->dlpi_phnum, (uintptr_t)addr)) break; 804 | 805 | // create new handle, save handle to cache 806 | if (NULL == handle) { 807 | handle = (xdl_t *)xdl_open_by_addr(addr); 808 | if (NULL == handle) return 0; 809 | handle->next = *(xdl_t **)cache; 810 | *(xdl_t **)cache = handle; 811 | } 812 | 813 | // we have at least: load_bias, pathname, dlpi_phdr, dlpi_phnum 814 | info->dli_fbase = (void *)handle->load_bias; 815 | info->dli_fname = handle->pathname; 816 | info->dli_sname = NULL; 817 | info->dli_saddr = 0; 818 | info->dli_ssize = 0; 819 | info->dlpi_phdr = handle->dlpi_phdr; 820 | info->dlpi_phnum = (size_t)handle->dlpi_phnum; 821 | 822 | // keep looking for: symbol name, symbol offset, symbol size 823 | ElfW(Sym) *sym; 824 | if (NULL != (sym = xdl_sym_by_addr((void *)handle, addr))) { 825 | info->dli_sname = handle->dynstr + sym->st_name; 826 | info->dli_saddr = (void *)(handle->load_bias + sym->st_value); 827 | info->dli_ssize = sym->st_size; 828 | } else if (NULL != (sym = xdl_dsym_by_addr((void *)handle, addr))) { 829 | info->dli_sname = handle->strtab + sym->st_name; 830 | info->dli_saddr = (void *)(handle->load_bias + sym->st_value); 831 | info->dli_ssize = sym->st_size; 832 | } 833 | 834 | return 1; 835 | } 836 | 837 | void xdl_addr_clean(void **cache) { 838 | if (NULL == cache) return; 839 | 840 | xdl_t *handle = *((xdl_t **)cache); 841 | while (NULL != handle) { 842 | xdl_t *tmp = handle; 843 | handle = handle->next; 844 | xdl_close(tmp); 845 | } 846 | *cache = NULL; 847 | } 848 | 849 | int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags) { 850 | if (NULL == callback) return 0; 851 | 852 | return xdl_iterate_phdr_impl(callback, data, flags); 853 | } 854 | 855 | int xdl_info(void *handle, int request, void *info) { 856 | if (NULL == handle || XDL_DI_DLINFO != request || NULL == info) return -1; 857 | 858 | xdl_t *self = (xdl_t *)handle; 859 | xdl_info_t *dlinfo = (xdl_info_t *)info; 860 | 861 | dlinfo->dli_fbase = (void *)self->load_bias; 862 | dlinfo->dli_fname = self->pathname; 863 | dlinfo->dli_sname = NULL; 864 | dlinfo->dli_saddr = 0; 865 | dlinfo->dli_ssize = 0; 866 | dlinfo->dlpi_phdr = self->dlpi_phdr; 867 | dlinfo->dlpi_phnum = (size_t)self->dlpi_phnum; 868 | return 0; 869 | } 870 | --------------------------------------------------------------------------------