├── 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 |
--------------------------------------------------------------------------------