├── .clang-format ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature-request.yml └── pull_request_template.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── README.zh-CN.md ├── SECURITY.md ├── build.gradle ├── clang-format.py ├── gradle.properties ├── gradle ├── publish.gradle ├── sanitizer.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── xdl ├── .gitignore ├── build.gradle ├── local_dependency.cmake └── src │ └── main │ ├── AndroidManifest.xml │ └── cpp │ ├── CMakeLists.txt │ ├── include │ └── xdl.h │ ├── xdl.c │ ├── xdl.map.txt │ ├── xdl_iterate.c │ ├── xdl_iterate.h │ ├── xdl_linker.c │ ├── xdl_linker.h │ ├── xdl_lzma.c │ ├── xdl_lzma.h │ ├── xdl_util.c │ └── xdl_util.h └── xdl_sample ├── .gitignore ├── build.gradle └── src └── main ├── AndroidManifest.xml ├── cpp ├── Android.mk ├── CMakeLists.txt └── sample.c ├── java └── io │ └── github │ └── hexhacking │ └── xdl │ └── sample │ ├── MainActivity.java │ ├── MyCustomApplication.java │ └── NativeSample.java └── res ├── drawable-v24 └── ic_launcher_foreground.xml ├── drawable └── ic_launcher_background.xml ├── layout └── activity_main.xml ├── mipmap-anydpi-v26 ├── ic_launcher.xml └── ic_launcher_round.xml ├── mipmap-hdpi ├── ic_launcher.png └── ic_launcher_round.png ├── mipmap-mdpi ├── ic_launcher.png └── ic_launcher_round.png ├── mipmap-xhdpi ├── ic_launcher.png └── ic_launcher_round.png ├── mipmap-xxhdpi ├── ic_launcher.png └── ic_launcher_round.png ├── mipmap-xxxhdpi ├── ic_launcher.png └── ic_launcher_round.png └── values ├── colors.xml ├── strings.xml └── themes.xml /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | --- 4 | 5 | Language: Cpp 6 | 7 | AlignConsecutiveMacros: true 8 | AllowShortFunctionsOnASingleLine: Empty 9 | ColumnLimit: 110 10 | IndentExternBlock: NoIndent 11 | TypenameMacros: ['ElfW'] 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us improve. 3 | labels: "bug" 4 | 5 | body: 6 | 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ## Notice 11 | 12 | 1. Please follow the [Code of Conduct](https://github.com/hexhacking/xDL/blob/master/CODE_OF_CONDUCT.md) that this project adheres to. 13 | 2. Please test using [the latest release](https://github.com/hexhacking/xDL/releases) to make sure your issue has not already been fixed. 14 | 3. Before submitting a bug, please make sure the issue hasn't been already addressed by searching through [the existing and past issues](https://github.com/hexhacking/xDL/issues?q=is%3Aissue+sort%3Acreated-desc+). 15 | 16 | - type: input 17 | attributes: 18 | label: xDL Version 19 | placeholder: "1.0.0" 20 | validations: 21 | required: true 22 | 23 | - type: input 24 | attributes: 25 | label: Android OS Version 26 | placeholder: "10.0" 27 | validations: 28 | required: true 29 | 30 | - type: dropdown 31 | attributes: 32 | label: Android ABIs 33 | multiple: true 34 | options: 35 | - armeabi-v7a 36 | - armeabi-v7a on Houdini 37 | - arm64-v8a 38 | - arm64-v8a on Houdini 39 | - x86 40 | - x86_64 41 | - Other (specify below) 42 | validations: 43 | required: true 44 | 45 | - type: input 46 | attributes: 47 | label: Device Manufacturers and Models 48 | placeholder: "Google Pixel 6 Pro, Samsung Galaxy S22 Ultra, ..." 49 | validations: 50 | required: true 51 | 52 | - type: textarea 53 | attributes: 54 | label: Describe the Bug 55 | description: | 56 | Please provide a clear and concise description of what the bug is. 57 | 58 | If relevant, add a minimal example so that we can reproduce the error by running the code. It is very important for the code snippet to be as succinct (minimal) as possible, so please take time to trim down any irrelevant code to help us debug efficiently. We are going to copy-paste your code and we expect to get the same result as you did. 59 | 60 | If you observe an error, please paste the **full** error message you got. In case of a native crash, please paste the **full** tombstone in the `/data/tombstones` directory. For non-rooted devices, refer to [bugreport](https://developer.android.com/studio/debug/bug-report). 61 | validations: 62 | required: true 63 | 64 | - type: markdown 65 | attributes: 66 | value: > 67 | Thanks for contributing! :tada: 68 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Questions and Discussions 4 | url: https://github.com/hexhacking/xDL/discussions 5 | about: Looking for help. Discuss general issues. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Submit a proposal/request for a new feature. 3 | labels: "enhancement" 4 | 5 | body: 6 | 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ## Notice 11 | 12 | 1. Please follow the [Code of Conduct](https://github.com/hexhacking/xDL/blob/master/CODE_OF_CONDUCT.md) that this project adheres to. 13 | 2. Please check [the latest release](https://github.com/hexhacking/xDL/releases) to make sure your proposal has not already exists. 14 | 3. Before submitting a feature request, please make sure the proposal hasn't been already addressed by searching through [the existing and past issues](https://github.com/hexhacking/xDL/issues?q=is%3Aissue+sort%3Acreated-desc+). 15 | 16 | - type: textarea 17 | attributes: 18 | label: the Feature, Motivation and Pitch 19 | description: > 20 | A clear and concise description of the feature proposal. Please outline the motivation for the proposal. Is your feature request related to a specific problem? If this is related to another GitHub issue, please link here too. 21 | validations: 22 | required: true 23 | 24 | - type: textarea 25 | attributes: 26 | label: Alternatives 27 | description: > 28 | A description of any alternative solutions or features you've considered, if any. 29 | 30 | - type: textarea 31 | attributes: 32 | label: Additional context 33 | description: > 34 | Add any other context about the feature request. 35 | 36 | - type: markdown 37 | attributes: 38 | value: > 39 | Thanks for contributing! :tada: 40 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | This PR fixes # 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | build/ 4 | .cxx/ 5 | .gradle/ 6 | .idea/ 7 | *.iml 8 | *.log 9 | *.so 10 | wrap.sh 11 | local.properties -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available 126 | at [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | [translations]: https://www.contributor-covenant.org/translations 133 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to xDL 2 | 3 | Welcome to the xDL project. Read on to learn more about our development process and how to propose bug fixes and improvements. 4 | 5 | ## Issues 6 | 7 | We use GitHub issues to track public bugs and feature requests. Before creating an issue, please note the following: 8 | 9 | 1. Please search existing issues before creating a new one. 10 | 2. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue. The more information the better. 11 | 12 | 13 | ## Branch Management 14 | 15 | There are 2 main branches: 16 | 17 | 1. `master` branch 18 | 19 | * It's the latest (pre-)release branch. We use `master` for tags. 20 | * **Please do NOT submit any PR on `master` branch.** 21 | 22 | 2. `dev` branch 23 | 24 | * It's our stable developing branch. 25 | * Once `dev` has passed our internal tests, it will be merged to `master` branch for the next release. 26 | * **Please always submit PR on `dev` branch.** 27 | 28 | 29 | ## Pull Requests 30 | 31 | **If possible, always create an issue first where you can describe your problem or feature request. We can also communicate in this issue. Then you can link to that issue in the pull request.** 32 | 33 | Please make sure the following is done when submitting a pull request: 34 | 35 | 1. Fork the repo and create your branch from `master`. 36 | 2. Add the copyright notice to the top of any new files you've added. 37 | 3. Try your best to test your code. 38 | 4. Squash all of your commits into one meaningful commit. 39 | 40 | 41 | ## Code Style Guide 42 | 43 | Follow the [.clang-format](.clang-format) for C code. 44 | 45 | 46 | ## License 47 | 48 | By contributing to xDL, you agree that your contributions will be licensed under its [MIT LICENSE](LICENSE). 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2024 HexHacking Team 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xDL 2 | 3 | ![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat) 4 | ![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat) 5 | ![](https://img.shields.io/badge/release-2.2.0-red.svg?style=flat) 6 | ![](https://img.shields.io/badge/Android-4.1%20--%2015-blue.svg?style=flat) 7 | ![](https://img.shields.io/badge/arch-armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86__64-blue.svg?style=flat) 8 | 9 | xDL is an enhanced implementation of the Android DL series functions. 10 | 11 | [**简体中文**](README.zh-CN.md) 12 | 13 | 14 | ## Features 15 | 16 | * Enhanced `dlopen()` + `dlsym()` + `dladdr()`. 17 | * Bypass the restrictions of Android 7.0+ linker namespace. 18 | * Lookup dynamic link symbols in `.dynsym`. 19 | * Lookup debuging symbols in `.symtab` and "`.symtab` in `.gnu_debugdata`". 20 | * Enhanced `dl_iterate_phdr()`. 21 | * Compatible with Android 4.x on ARM32. 22 | * Including linker / linker64 (for Android <= 8.x). 23 | * Return full pathname instead of basename (for Android 5.x). 24 | * Return app\_process32 / app\_process64 instead of package name. 25 | * Support Android 4.1 - 15 (API level 16 - 35). 26 | * Support armeabi-v7a, arm64-v8a, x86 and x86_64. 27 | * MIT licensed. 28 | 29 | 30 | ## Artifacts Size 31 | 32 | If xDL is compiled into an independent dynamic library: 33 | 34 | | ABI | Compressed (KB) | Uncompressed (KB) | 35 | | :---------- |----------------:|------------------:| 36 | | armeabi-v7a | 7.9 | 15 | 37 | | arm64-v8a | 9.1 | 20 | 38 | | x86 | 9.0 | 18 | 39 | | x86_64 | 9.1 | 20 | 40 | 41 | 42 | ## Usage 43 | 44 | ### 1. Add dependency in build.gradle 45 | 46 | xDL is published on [Maven Central](https://search.maven.org/), and uses [Prefab](https://google.github.io/prefab/) package format for [native dependencies](https://developer.android.com/studio/build/native-dependencies), which is supported by [Android Gradle Plugin 4.0+](https://developer.android.com/studio/releases/gradle-plugin?buildsystem=cmake#native-dependencies). 47 | 48 | ```Gradle 49 | android { 50 | buildFeatures { 51 | prefab true 52 | } 53 | } 54 | 55 | dependencies { 56 | implementation 'io.github.hexhacking:xdl:2.2.0' 57 | } 58 | ``` 59 | 60 | **NOTE**: 61 | 62 | 1. Starting from version `2.0.0` of xDL, group ID changed from `io.hexhacking` to `io.github.hexhacking`. 63 | 64 | | version range | group ID | artifact ID | Repository URL | 65 | |:---------------|:-------------------------|:------------| :--------------| 66 | | [1.0.3, 1.2.1] | io.hexhacking | xdl | [repo](https://repo1.maven.org/maven2/io/hexhacking/xdl/) | 67 | | [2.0.0, ) | **io.github.hexhacking** | xdl | [repo](https://repo1.maven.org/maven2/io/github/hexhacking/xdl/) | 68 | 69 | 2. xDL uses the [prefab package schema v2](https://github.com/google/prefab/releases/tag/v2.0.0), which is configured by default since [Android Gradle Plugin 7.1.0](https://developer.android.com/studio/releases/gradle-plugin?buildsystem=cmake#7-1-0). If you are using Android Gradle Plugin earlier than 7.1.0, please add the following configuration to `gradle.properties`: 70 | 71 | ``` 72 | android.prefabVersion=2.0.0 73 | ``` 74 | 75 | ### 2. Add dependency in CMakeLists.txt or Android.mk 76 | 77 | > CMakeLists.txt 78 | 79 | ```CMake 80 | find_package(xdl REQUIRED CONFIG) 81 | 82 | add_library(mylib SHARED mylib.c) 83 | target_link_libraries(mylib xdl::xdl) 84 | ``` 85 | 86 | > Android.mk 87 | 88 | ``` 89 | include $(CLEAR_VARS) 90 | LOCAL_MODULE := mylib 91 | LOCAL_SRC_FILES := mylib.c 92 | LOCAL_SHARED_LIBRARIES += xdl 93 | include $(BUILD_SHARED_LIBRARY) 94 | 95 | $(call import-module,prefab/xdl) 96 | ``` 97 | 98 | ### 3. Specify one or more ABI(s) you need 99 | 100 | ```Gradle 101 | android { 102 | defaultConfig { 103 | ndk { 104 | abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' 105 | } 106 | } 107 | } 108 | ``` 109 | 110 | ### 4. Add packaging options 111 | 112 | If you are using xDL in an SDK project, you may need to avoid packaging libxdl.so into your AAR, so as not to encounter duplicate libxdl.so file when packaging the app project. 113 | 114 | ```Gradle 115 | android { 116 | packagingOptions { 117 | exclude '**/libxdl.so' 118 | } 119 | } 120 | ``` 121 | 122 | On the other hand, if you are using xDL in an APP project, you may need to add some options to deal with conflicts caused by duplicate libxdl.so file. 123 | 124 | ```Gradle 125 | android { 126 | packagingOptions { 127 | pickFirst '**/libxdl.so' 128 | } 129 | } 130 | ``` 131 | 132 | There is a sample app in the [xdl-sample](xdl_sample) folder you can refer to. 133 | 134 | 135 | ## API 136 | 137 | ```C 138 | #include "xdl.h" 139 | ``` 140 | 141 | ### 1. `xdl_open()` and `xdl_close()` 142 | 143 | ```C 144 | #define XDL_DEFAULT 0x00 145 | #define XDL_TRY_FORCE_LOAD 0x01 146 | #define XDL_ALWAYS_FORCE_LOAD 0x02 147 | 148 | void *xdl_open(const char *filename, int flags); 149 | void *xdl_open2(struct dl_phdr_info *info); 150 | void *xdl_close(void *handle); 151 | ``` 152 | 153 | They are very similar to [`dlopen()`](https://man7.org/linux/man-pages/man3/dlopen.3.html) and [`dlclose()`](https://man7.org/linux/man-pages/man3/dlclose.3.html). But `xdl_open()` can bypass the restrictions of Android 7.0+ linker namespace. 154 | 155 | Depending on the value of the `flags` parameter, the behavior of `xdl_open()` will have some differences: 156 | 157 | * `XDL_DEFAULT`: If the library has been loaded into memory, `xdl_open()` will not `dlopen()` it again. (But it will still return a valid `handle`) 158 | * `XDL_TRY_FORCE_LOAD`: If the library has not been loaded into memory, `xdl_open()` will try to `dlopen()` it. 159 | * `XDL_ALWAYS_FORCE_LOAD`: `xdl_open()` will always `dlopen()` the library. 160 | 161 | If `xdl_open()` really uses `dlopen()` to load the library, `xdl_close()` will return the handle from linker (the return value of `dlopen()`), and then you can decide whether and when to close it with standard `dlclose()`. Otherwise, `NULL` will be returned. 162 | 163 | `filename` can be basename or full pathname. However, Android linker has used the namespace mechanism since 7.0. If you pass basename, you need to make sure that no duplicate ELF is loaded into the current process. `xdl_open()` will only return the first matching ELF. Please consider this fragment of `/proc/self/maps` on Android 10: 164 | 165 | ``` 166 | 756fc2c000-756fc7c000 r--p 00000000 fd:03 2985 /system/lib64/vndk-sp-29/libc++.so 167 | 756fc7c000-756fcee000 --xp 00050000 fd:03 2985 /system/lib64/vndk-sp-29/libc++.so 168 | 756fcee000-756fcef000 rw-p 000c2000 fd:03 2985 /system/lib64/vndk-sp-29/libc++.so 169 | 756fcef000-756fcf7000 r--p 000c3000 fd:03 2985 /system/lib64/vndk-sp-29/libc++.so 170 | 7571fdd000-757202d000 r--p 00000000 07:38 20 /apex/com.android.conscrypt/lib64/libc++.so 171 | 757202d000-757209f000 --xp 00050000 07:38 20 /apex/com.android.conscrypt/lib64/libc++.so 172 | 757209f000-75720a0000 rw-p 000c2000 07:38 20 /apex/com.android.conscrypt/lib64/libc++.so 173 | 75720a0000-75720a8000 r--p 000c3000 07:38 20 /apex/com.android.conscrypt/lib64/libc++.so 174 | 760b9df000-760ba2f000 r--p 00000000 fd:03 2441 /system/lib64/libc++.so 175 | 760ba2f000-760baa1000 --xp 00050000 fd:03 2441 /system/lib64/libc++.so 176 | 760baa1000-760baa2000 rw-p 000c2000 fd:03 2441 /system/lib64/libc++.so 177 | 760baa2000-760baaa000 r--p 000c3000 fd:03 2441 /system/lib64/libc++.so 178 | ``` 179 | 180 | `xdl_open2()` creates a `handle` from `struct dl_phdr_info`. `xdl_open2()` is always `XDL_DEFAULT` semantics, i.e. it will not try to load ELF with `dlopen()`. 181 | 182 | ### 2. `xdl_sym()` and `xdl_dsym()` 183 | 184 | ```C 185 | void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size); 186 | void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size); 187 | ``` 188 | 189 | They are very similar to [`dlsym()`](https://man7.org/linux/man-pages/man3/dlsym.3.html). They all takes a "handle" of an ELF returned by `xdl_open()` and the null-terminated symbol name, returning the address where that symbol is loaded into memory. 190 | 191 | If the `symbol_size` parameter is not `NULL`, it will be assigned as "the bytes occupied by the content corresponding to the symbol in the ELF". If you don't need this information, just pass `NULL`. 192 | 193 | `xdl_sym()` lookup "dynamic link symbols" in `.dynsym` as `dlsym()` does. 194 | 195 | `xdl_dsym()` lookup "debuging symbols" in `.symtab` and "`.symtab` in `.gnu_debugdata`". 196 | 197 | Notice: 198 | 199 | * The symbol sets in `.dynsym` and `.symtab` do not contain each other. Some symbols only exist in `.dynsym`, and some only exist in `.symtab`. You may need to use tools such as readelf to determine which ELF section the symbol you are looking for is in. 200 | * `xdl_dsym()` needs to load debuging symbols from disk file, and `xdl_sym()` only lookup dynamic link symbols from memory. So `xdl_dsym()` runs slower than `xdl_sym()`. 201 | * The dynamic linker only uses symbols in `.dynsym`. The debugger actually uses the symbols in both `.dynsym` and `.symtab`. 202 | 203 | ### 3. `xdl_addr()` 204 | 205 | ```C 206 | typedef struct 207 | { 208 | const char *dli_fname; 209 | void *dli_fbase; 210 | const char *dli_sname; 211 | void *dli_saddr; 212 | size_t dli_ssize; 213 | const ElfW(Phdr) *dlpi_phdr; 214 | size_t dlpi_phnum; 215 | } xdl_info_t; 216 | 217 | #define XDL_DEFAULT 0x00 218 | #define XDL_NON_SYM 0x01 219 | 220 | int xdl_addr(void *addr, xdl_info_t *info, void **cache); 221 | int xdl_addr4(void *addr, xdl_info_t *info, void **cache, int flags); 222 | void xdl_addr_clean(void **cache); 223 | ``` 224 | 225 | `xdl_addr()` is similar to [`dladdr()`](https://man7.org/linux/man-pages/man3/dladdr.3.html). But there are a few differences: 226 | 227 | * `xdl_addr()` can lookup not only dynamic link symbols, but also debugging symbols. 228 | * `xdl_addr()` uses the `xdl_info_t` structure instead of the `Dl_info` structure, which contains more extended information: `dli_ssize` is the number of bytes occupied by the current symbol; `dlpi_phdr` points to the program headers array of the ELF where the current symbol is located; `dlpi_phnum` is the number of elements in the `dlpi_phdr` array. 229 | * `xdl_addr()` needs to pass an additional parameter (`cache`), which will cache the ELF handle opened during the execution of `xdl_addr()`. The purpose of caching is to make subsequent executions of `xdl_addr()` of the same ELF faster. When you do not need to execute `xdl_addr()`, please use `xdl_addr_clean()` to clear the cache. For example: 230 | 231 | ```C 232 | void *cache = NULL; 233 | xdl_info_t info; 234 | xdl_addr(addr_1, &info, &cache); 235 | xdl_addr(addr_2, &info, &cache); 236 | xdl_addr(addr_3, &info, &cache); 237 | xdl_addr_clean(&cache); 238 | ``` 239 | 240 | * `xdl_addr4()` is similar to `xdl_addr()`, except that it adds the `flags` parameter. When the `flags` value is `XDL_DEFAULT`, the behavior of `xdl_addr4()` is the same as `xdl_addr()`. When the `flags` value is `XDL_NON_SYM`, `xdl_addr4()` will not obtain symbol-related information (the values of `dli_sname`, `dli_saddr`, and `dli_ssize` in `xdl_info_t` are all `0`). 241 | 242 | ### 4. `xdl_iterate_phdr()` 243 | 244 | ```C 245 | #define XDL_DEFAULT 0x00 246 | #define XDL_FULL_PATHNAME 0x01 247 | 248 | int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags); 249 | ``` 250 | 251 | `xdl_iterate_phdr()` is similar to [`dl_iterate_phdr()`](https://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html). But `xdl_iterate_phdr()` is compatible with android 4.x on ARM32, and always including linker / linker64. 252 | 253 | `xdl_iterate_phdr()` has an additional "flags" parameter, one or more flags can be bitwise-or'd in it: 254 | 255 | * `XDL_DEFAULT`: Default behavior. 256 | * `XDL_FULL_PATHNAME`: Always return full pathname instead of basename. 257 | 258 | These flags are needed because these capabilities require additional execution time, and you don't always need them. 259 | 260 | ### 5. `xdl_info()` 261 | 262 | ```C 263 | #define XDL_DI_DLINFO 1 // type of info: xdl_info_t 264 | 265 | int xdl_info(void *handle, int request, void *info); 266 | ``` 267 | 268 | `xdl_info()` is similar to [`dlinfo()`](https://man7.org/linux/man-pages/man3/dlinfo.3.html). `xdl_info()` obtains information about the dynamically loaded object referred to by `handle` (obtained by an earlier call to `xdl_open`). 269 | 270 | The only `request` parameter currently supported is `XDL_DI_DLINFO`, which means to return data of type `xdl_info_t` through the `info` parameter (note that the values of `dli_sname`, `dli_saddr`, `dli_ssize` in the returned `xdl_info_t` at this time both are `0`). 271 | 272 | On success, `xdl_info()` returns `0`. On failure, it returns `-1`. 273 | 274 | ## Support 275 | 276 | * [GitHub Issues](https://github.com/hexhacking/xDL/issues) 277 | * [GitHub Discussions](https://github.com/hexhacking/xDL/discussions) 278 | * [Telegram Public Group](https://t.me/android_native_geeks) 279 | 280 | 281 | ## Contributing 282 | 283 | * [Code of Conduct](CODE_OF_CONDUCT.md) 284 | * [Contributing Guide](CONTRIBUTING.md) 285 | * [Reporting Security vulnerabilities](SECURITY.md) 286 | 287 | 288 | ## License 289 | 290 | xDL is MIT licensed, as found in the [LICENSE](LICENSE) file. 291 | 292 | 293 | ## History 294 | 295 | [xCrash 2.x](https://github.com/hexhacking/xCrash/tree/4748d183c1395c54bfb760ec6c454966d52ab73f) contains a very rudimentary module [xc_dl](https://github.com/hexhacking/xCrash/blob/4748d183c1395c54bfb760ec6c454966d52ab73f/src/native/libxcrash/jni/xc_dl.c) for searching system library symbols, which has many problems in performance and compatibility. xCrash 2.x uses it to search a few symbols from libart, libc and libc++. 296 | 297 | Later, some other projects began to use the xc_dl module alone, including in some performance-sensitive usage scenarios. At this time, we began to realize that we need to rewrite this module, and we need a better implementation. 298 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # xDL 2 | 3 | ![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat) 4 | ![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat) 5 | ![](https://img.shields.io/badge/release-2.2.0-red.svg?style=flat) 6 | ![](https://img.shields.io/badge/Android-4.1%20--%2015-blue.svg?style=flat) 7 | ![](https://img.shields.io/badge/arch-armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86__64-blue.svg?style=flat) 8 | 9 | xDL 是 Android DL 系列函数的增强实现。 10 | 11 | [**English**](README.md) 12 | 13 | 14 | ## 特征 15 | 16 | * 增强的 `dlopen()` + `dlsym()` + `dladdr()`。 17 | * 绕过 Android 7.0+ linker namespace 的限制。 18 | * 查询 `.dynsym` 中的动态链接符号。 19 | * 查询 `.symtab` 和 “`.gnu_debugdata` 里的 `.symtab`” 中的调试符号。 20 | * 增强的 `dl_iterate_phdr()`。 21 | * 兼容 ARM32 平台的 Android 4.x。 22 | * 在 Android <= 8.x 时,包含 linker / linker64。 23 | * 在 Android 5.x 中,返回完整的路径名(full pathname),而不是文件名(basename)。 24 | * 返回 app\_process32 / app\_process64,而不是包名。 25 | * 支持 Android 4.1 - 15 (API level 16 - 35)。 26 | * 支持 armeabi-v7a, arm64-v8a, x86 和 x86_64。 27 | * 使用 MIT 许可证授权。 28 | 29 | 30 | ## 产出物体积 31 | 32 | 如果将 xDL 编译成独立的动态库: 33 | 34 | | ABI | 压缩后 (KB) | 未压缩 (KB) | 35 | | :---------- |---------:|---------:| 36 | | armeabi-v7a | 7.9 | 15 | 37 | | arm64-v8a | 9.1 | 20 | 38 | | x86 | 9.0 | 18 | 39 | | x86_64 | 9.1 | 20 | 40 | 41 | 42 | ## 使用 43 | 44 | ### 1. 在 build.gradle 中增加依赖 45 | 46 | xDL 发布在 [Maven Central](https://search.maven.org/) 上。为了使用 [native 依赖项](https://developer.android.com/studio/build/native-dependencies),xDL 使用了从 [Android Gradle Plugin 4.0+](https://developer.android.com/studio/releases/gradle-plugin?buildsystem=cmake#native-dependencies) 开始支持的 [Prefab](https://google.github.io/prefab/) 包格式。 47 | 48 | ```Gradle 49 | android { 50 | buildFeatures { 51 | prefab true 52 | } 53 | } 54 | 55 | dependencies { 56 | implementation 'io.github.hexhacking:xdl:2.2.0' 57 | } 58 | ``` 59 | 60 | **注意**: 61 | 62 | 1. xDL 从版本 `2.0.0` 开始,group ID 从 `io.hexhacking` 改为 `io.github.hexhacking`。 63 | 64 | | 版本号范围 | group ID | artifact ID | 仓库 URL | 65 | |:---------------|:-------------------------|:------------|:----------------------------------------------------------| 66 | | [1.0.3, 1.2.1] | io.hexhacking | xdl | [repo](https://repo1.maven.org/maven2/io/hexhacking/xdl/) | 67 | | [2.0.0, ) | **io.github.hexhacking** | xdl | [repo](https://repo1.maven.org/maven2/io/github/hexhacking/xdl/) | 68 | 69 | 2. xDL 使用 [prefab package schema v2](https://github.com/google/prefab/releases/tag/v2.0.0),它是从 [Android Gradle Plugin 7.1.0](https://developer.android.com/studio/releases/gradle-plugin?buildsystem=cmake#7-1-0) 开始作为默认配置的。如果你使用的是 Android Gradle Plugin 7.1.0 之前的版本,请在 `gradle.properties` 中加入以下配置: 70 | 71 | ``` 72 | android.prefabVersion=2.0.0 73 | ``` 74 | 75 | ### 2. 在 CMakeLists.txt 或 Android.mk 中增加依赖 76 | 77 | > CMakeLists.txt 78 | 79 | ```CMake 80 | find_package(xdl REQUIRED CONFIG) 81 | 82 | add_library(mylib SHARED mylib.c) 83 | target_link_libraries(mylib xdl::xdl) 84 | ``` 85 | 86 | > Android.mk 87 | 88 | ``` 89 | include $(CLEAR_VARS) 90 | LOCAL_MODULE := mylib 91 | LOCAL_SRC_FILES := mylib.c 92 | LOCAL_SHARED_LIBRARIES += xdl 93 | include $(BUILD_SHARED_LIBRARY) 94 | 95 | $(call import-module,prefab/xdl) 96 | ``` 97 | 98 | ### 3. 指定一个或多个你需要的 ABI 99 | 100 | ```Gradle 101 | android { 102 | defaultConfig { 103 | ndk { 104 | abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' 105 | } 106 | } 107 | } 108 | ``` 109 | 110 | ### 4. 增加打包选项 111 | 112 | 如果你是在一个 SDK 工程里使用 xDL,你可能需要避免把 libxdl.so 打包到你的 AAR 里,以免 app 工程打包时遇到重复的 libxdl.so 文件。 113 | 114 | ```Gradle 115 | android { 116 | packagingOptions { 117 | exclude '**/libxdl.so' 118 | } 119 | } 120 | ``` 121 | 122 | 另一方面, 如果你是在一个 APP 工程里使用 xDL,你可以需要增加一些选项,用来处理重复的 libxdl.so 文件引起的冲突。 123 | 124 | ```Gradle 125 | android { 126 | packagingOptions { 127 | pickFirst '**/libxdl.so' 128 | } 129 | } 130 | ``` 131 | 132 | 你可以参考 [xdl-sample](xdl_sample) 文件夹中的示例 app。 133 | 134 | 135 | ## API 136 | 137 | ```C 138 | #include "xdl.h" 139 | ``` 140 | 141 | ### 1. `xdl_open()` 和 `xdl_close()` 142 | 143 | ```C 144 | #define XDL_DEFAULT 0x00 145 | #define XDL_TRY_FORCE_LOAD 0x01 146 | #define XDL_ALWAYS_FORCE_LOAD 0x02 147 | 148 | void *xdl_open(const char *filename, int flags); 149 | void *xdl_open2(struct dl_phdr_info *info); 150 | void *xdl_close(void *handle); 151 | ``` 152 | 153 | 它们和 [`dlopen()`](https://man7.org/linux/man-pages/man3/dlopen.3.html) / [`dlclose()`](https://man7.org/linux/man-pages/man3/dlclose.3.html) 类似。但是 `xdl_open()` 可以绕过 Android 7.0+ linker namespace 的限制。 154 | 155 | 根据 `flags` 参数值的不同,`xdl_open()` 的行为会有一些差异: 156 | 157 | * `XDL_DEFAULT`: 如果动态库已经被加载到内存中了,`xdl_open()` 不会再使用 `dlopen()` 加载它。(但依然会返回一个有效的 `handle`) 158 | * `XDL_TRY_FORCE_LOAD`: 如果动态库还没有被加载到内存中,`xdl_open()` 将尝试使用 `dlopen()` 加载它。 159 | * `XDL_ALWAYS_FORCE_LOAD`: `xdl_open()` 将总是使用 `dlopen()` 加载动态库。 160 | 161 | 如果 `xdl_open()` 真的使用 `dlopen()` 加载了动态库,`xdl_close()` 将返回从 linker 那里取得的 handle(`dlopen()` 的返回值),然后你可以决定是否以及什么时候使用标准的 `dlclose()` 来关闭它。否则,将返回 `NULL`。 162 | 163 | `filename` 可以是文件名(basename)也可以是完整的路径名(full pathname)。然而,Android linker 从 7.0 开始启用了 namespace 机制。如果你传递文件名,你需要确认当前进程中没有重名的 ELF 文件。`xdl_open()` 只会返回第一个匹配到的 ELF文件。请考虑以下 Android 10 中的 `/proc/self/maps` 片段: 164 | 165 | ``` 166 | 756fc2c000-756fc7c000 r--p 00000000 fd:03 2985 /system/lib64/vndk-sp-29/libc++.so 167 | 756fc7c000-756fcee000 --xp 00050000 fd:03 2985 /system/lib64/vndk-sp-29/libc++.so 168 | 756fcee000-756fcef000 rw-p 000c2000 fd:03 2985 /system/lib64/vndk-sp-29/libc++.so 169 | 756fcef000-756fcf7000 r--p 000c3000 fd:03 2985 /system/lib64/vndk-sp-29/libc++.so 170 | 7571fdd000-757202d000 r--p 00000000 07:38 20 /apex/com.android.conscrypt/lib64/libc++.so 171 | 757202d000-757209f000 --xp 00050000 07:38 20 /apex/com.android.conscrypt/lib64/libc++.so 172 | 757209f000-75720a0000 rw-p 000c2000 07:38 20 /apex/com.android.conscrypt/lib64/libc++.so 173 | 75720a0000-75720a8000 r--p 000c3000 07:38 20 /apex/com.android.conscrypt/lib64/libc++.so 174 | 760b9df000-760ba2f000 r--p 00000000 fd:03 2441 /system/lib64/libc++.so 175 | 760ba2f000-760baa1000 --xp 00050000 fd:03 2441 /system/lib64/libc++.so 176 | 760baa1000-760baa2000 rw-p 000c2000 fd:03 2441 /system/lib64/libc++.so 177 | 760baa2000-760baaa000 r--p 000c3000 fd:03 2441 /system/lib64/libc++.so 178 | ``` 179 | 180 | `xdl_open2()` 通过 `struct dl_phdr_info` 创建 `handle`。`xdl_open2()` 始终是 `XDL_DEFAULT` 的语意,即它不会尝试用 `dlopen()` 去加载 ELF。 181 | 182 | ### 2. `xdl_sym()` 和 `xdl_dsym()` 183 | 184 | ```C 185 | void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size); 186 | void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size); 187 | ``` 188 | 189 | 它们和 [`dlsym()`](https://man7.org/linux/man-pages/man3/dlsym.3.html) 类似。 它们都需要传递一个 `xdl_open()` 返回的 “handle”,和一个 null 结尾的符号名字,返回该符号在内存中的加载地址。 190 | 191 | 如果 `symbol_size` 参数不为 `NULL`,它将被赋值为“符号对应的内容在 ELF 中占用的字节数”,如果你不需要这个信息,传递 `NULL` 就可以了。 192 | 193 | `xdl_sym()` 从 `.dynsym` 中查询 “动态链接符号”,就像 `dlsym()` 做的那样。 194 | 195 | `xdl_dsym()` 从 `.symtab` 和 “`.gnu_debugdata` 里的 `.symtab`” 中查询 “调试符号”。 196 | 197 | 注意: 198 | 199 | * `.dynsym` 和 `.symtab` 中的符号集合并没有相互包含的关系。有些符号只存在于 `.dynsym` 中,有些则只存在于 `.symtab` 中。你可能需要通过 readelf 之类的工具确定你要查找的符号在哪个 ELF section 中。 200 | * `xdl_dsym()` 需要从磁盘文件中加载调试符号,而 `xdl_sym()` 只从内存中查询动态链接符号。所以 `xdl_dsym()` 比 `xdl_sym()` 执行的更慢。 201 | * 动态链接器只使用 `.dynsym` 中的符号。调试器其实同时使用了 `.dynsym` 和 `.symtab` 中的符号。 202 | 203 | ### 3. `xdl_addr()` 204 | 205 | ```C 206 | typedef struct 207 | { 208 | const char *dli_fname; 209 | void *dli_fbase; 210 | const char *dli_sname; 211 | void *dli_saddr; 212 | size_t dli_ssize; 213 | const ElfW(Phdr) *dlpi_phdr; 214 | size_t dlpi_phnum; 215 | } xdl_info_t; 216 | 217 | #define XDL_DEFAULT 0x00 218 | #define XDL_NON_SYM 0x01 219 | 220 | int xdl_addr(void *addr, xdl_info_t *info, void **cache); 221 | int xdl_addr4(void *addr, xdl_info_t *info, void **cache, int flags); 222 | void xdl_addr_clean(void **cache); 223 | ``` 224 | 225 | `xdl_addr()` 和 [`dladdr()`](https://man7.org/linux/man-pages/man3/dladdr.3.html) 类似。但有以下几点不同: 226 | 227 | * `xdl_addr()` 不仅能查询动态链接符号,还能查询调试符号。 228 | * `xdl_addr()` 使用 `xdl_info_t` 结构体代替了 `Dl_info` 结构体,它包含了更多的扩展信息:`dli_ssize` 是当前符号所占用的字节数;`dlpi_phdr` 指向当前符号所在 ELF 的 program headers 数组;`dlpi_phnum` 是 `dlpi_phdr` 数组的元素个数。 229 | * `xdl_addr()` 需要传递一个附加的参数(cache),其中会缓存 `xdl_addr()` 执行过程中打开的 ELF handle,缓存的目的是使后续对同一个 ELF 的 `xdl_addr()` 执行的更快。当不需要再执行 `xdl_addr()` 时,请使用 `xdl_addr_clean()` 清除缓存。举例: 230 | 231 | ```C 232 | void *cache = NULL; 233 | xdl_info_t info; 234 | xdl_addr(addr_1, &info, &cache); 235 | xdl_addr(addr_2, &info, &cache); 236 | xdl_addr(addr_3, &info, &cache); 237 | xdl_addr_clean(&cache); 238 | ``` 239 | 240 | * `xdl_addr4()` 和 `xdl_addr()` 类似,区别是增加了 `flags` 参数。`flags` 值为 `XDL_DEFAULT` 时,`xdl_addr4()` 的行为与 `xdl_addr()` 相同。`flags` 值为 `XDL_NON_SYM` 时,`xdl_addr4()` 不会去获取符号相关的信息(`xdl_info_t` 中 `dli_sname`、`dli_saddr` 和 `dli_ssize` 的值均为 `0`)。 241 | 242 | ### 4. `xdl_iterate_phdr()` 243 | 244 | ```C 245 | #define XDL_DEFAULT 0x00 246 | #define XDL_FULL_PATHNAME 0x01 247 | 248 | int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags); 249 | ``` 250 | 251 | `xdl_iterate_phdr()` 和 [`dl_iterate_phdr()`](https://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html) 类似。但是 `xdl_iterate_phdr()` 兼容 ARM32 平台的 Android 4.x 系统,并且总是包含 linker / linker64。 252 | 253 | `xdl_iterate_phdr()` 有一个额外的“flags”参数,一个或多个“flag”可以按位“或”后传递给它: 254 | 255 | * `XDL_DEFAULT`: 默认行为。 256 | * `XDL_FULL_PATHNAME`: 总是返回完整的路径名(full pathname),而不是文件名(basename)。 257 | 258 | 需要这些 flags 的原因是,这些额外的能力也需要花费额外的执行时间,而你并不总是需要这些能力。 259 | 260 | ### 5. `xdl_info()` 261 | 262 | ```C 263 | #define XDL_DI_DLINFO 1 // type of info: xdl_info_t 264 | 265 | int xdl_info(void *handle, int request, void *info); 266 | ``` 267 | 268 | `xdl_info()` 和 [`dlinfo()`](https://man7.org/linux/man-pages/man3/dlinfo.3.html) 类似。`xdl_info()` 通过 `handle`(`xdl_open` 的返回值)来获取动态加载对象的信息。 269 | 270 | 目前唯一支持的 `request` 参数是 `XDL_DI_DLINFO`,表示通过 `info` 参数返回 `xdl_info_t` 类型的数据(注意,此时返回的 `xdl_info_t` 中 `dli_sname`、`dli_saddr`、`dli_ssize` 的值均为 `0`)。 271 | 272 | 成功时 `xdl_info()` 返回 `0`,失败时返回 `-1`。 273 | 274 | ## 技术支持 275 | 276 | * [GitHub Issues](https://github.com/hexhacking/xDL/issues) 277 | * [GitHub Discussions](https://github.com/hexhacking/xDL/discussions) 278 | * [Telegram Public Group](https://t.me/android_native_geeks) 279 | 280 | 281 | ## 贡献 282 | 283 | * [Code of Conduct](CODE_OF_CONDUCT.md) 284 | * [Contributing Guide](CONTRIBUTING.md) 285 | * [Reporting Security vulnerabilities](SECURITY.md) 286 | 287 | 288 | ## 许可证 289 | 290 | xDL 使用 [MIT 许可证](LICENSE)。 291 | 292 | 293 | ## 历史 294 | 295 | [xCrash 2.x](https://github.com/hexhacking/xCrash/tree/4748d183c1395c54bfb760ec6c454966d52ab73f) 包含一个非常原始的 [xc_dl](https://github.com/hexhacking/xCrash/blob/4748d183c1395c54bfb760ec6c454966d52ab73f/src/native/libxcrash/jni/xc_dl.c) 模块,用它来查询系统库的符号,这个模块在性能和兼容性上都有一些问题。xCrash 2.x 使用它在 libart,libc 和 libc++ 中查询很少量的几个符号。 296 | 297 | 后来,一些其他的项目开始单独使用 xc_dl 模块,其中也包含一些性能敏感的场景。这时候,我们开始意识到我们需要重写这个模块,并且我们需要一个更好的实现。 298 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting Security vulnerabilities 2 | 3 | The xDL team and community take security bugs in xDL seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. 4 | 5 | **Please do not report security vulnerabilities through GitHub issues, discussions or pull requests. This makes the problem immediately visible to everyone, including malicious actors.** 6 | 7 | Instead, please send email to . If possible, encrypt your message with [this PGP key](https://raw.githubusercontent.com/caikelun/caikelun.github.io/master/site/pgp-public-key.txt). 8 | 9 | The xDL team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. 10 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' version '8.5.1' apply false 3 | id 'com.android.library' version '8.5.1' apply false 4 | } 5 | 6 | tasks.register('clean', Delete) { 7 | delete rootProject.getLayout.getBuildDirectory 8 | } 9 | 10 | ext { 11 | minSdkVersion = 16 12 | compileSdkVersion = 35 13 | targetSdkVersion = 35 14 | buildToolsVersion = "35.0.0" 15 | javaVersion = JavaVersion.VERSION_1_8 16 | ndkVersion = "23.2.8568313" 17 | cmakeVersion = "3.22.1" 18 | abiFilters = "armeabi-v7a,arm64-v8a,x86,x86_64" 19 | useASAN = false 20 | dependencyOnLocalLibrary = true 21 | xdlVersion = "2.2.0" 22 | 23 | POM_GROUP_ID = "io.github.hexhacking" 24 | POM_ARTIFACT_ID = "xdl" 25 | POM_VERSION_NAME = "2.2.0" 26 | 27 | POM_NAME = "xDL Android Lib" 28 | POM_DESCRIPTION = "xDL is an enhanced implementation of the Android DL series functions." 29 | POM_URL = "https://github.com/hexhacking/xDL" 30 | POM_INCEPTION_YEAR = "2020" 31 | POM_PACKAGING = "aar" 32 | 33 | POM_SCM_CONNECTION = "https://github.com/hexhacking/xDL.git" 34 | 35 | POM_ISSUE_SYSTEM = "github" 36 | POM_ISSUE_URL = "https://github.com/hexhacking/xDL/issues" 37 | 38 | POM_LICENCE_NAME = "The MIT License" 39 | POM_LICENCE_URL = "https://opensource.org/licenses/MIT" 40 | POM_LICENCE_DIST = "repo" 41 | 42 | POM_DEVELOPER_ID = "HexHacking" 43 | POM_DEVELOPER_NAME = "HexHacking Team" 44 | } 45 | -------------------------------------------------------------------------------- /clang-format.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import sys 4 | import argparse 5 | import subprocess 6 | 7 | path_list = ['xdl/src/main/cpp/include', 8 | 'xdl/src/main/cpp', 9 | 'xdl_sample/src/main/cpp'] 10 | suffix_tuple = ('.h', '.c') 11 | 12 | def main(): 13 | parser = argparse.ArgumentParser(description='clang-format runner') 14 | parser.add_argument('-n', '--dry-run', action='store_true', help='If set, do not actually make the formatting changes') 15 | options = parser.parse_args() 16 | dry_run = '-n ' if options.dry_run else '' 17 | 18 | for path in path_list: 19 | for filename in sorted(os.listdir(path)): 20 | f = os.path.join(path, filename) 21 | if os.path.isfile(f) and f.endswith(suffix_tuple): 22 | cmd = 'clang-format ' + dry_run + '--Werror -i -style=file ' + f 23 | ret = subprocess.run(cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 24 | retcode = ('' if ret.returncode == 0 else (' >>> return code: ' + str(ret.returncode))) 25 | print('>>> ' + cmd + retcode) 26 | print(ret.stdout, end='') 27 | 28 | if __name__ == '__main__': 29 | sys.exit(main()) 30 | -------------------------------------------------------------------------------- /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 | android.nonTransitiveRClass=false 21 | android.nonFinalResIds=false 22 | -------------------------------------------------------------------------------- /gradle/publish.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven-publish' 2 | apply plugin: 'signing' 3 | 4 | ext["signing.keyId"] = '' 5 | ext["signing.password"] = '' 6 | ext["signing.secretKeyRingFile"] = '' 7 | ext["ossrhUsername"] = '' 8 | ext["ossrhPassword"] = '' 9 | 10 | File secretPropsFile = project.rootProject.file('local.properties') 11 | if (secretPropsFile.exists()) { 12 | Properties p = new Properties() 13 | p.load(new FileInputStream(secretPropsFile)) 14 | p.each { name, value -> 15 | ext[name] = value 16 | } 17 | } else { 18 | ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') 19 | ext["signing.password"] = System.getenv('SIGNING_PASSWORD') 20 | ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_SECRET_KEY_RING_FILE') 21 | ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME') 22 | ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD') 23 | } 24 | 25 | project.afterEvaluate { 26 | publishing { 27 | publications { 28 | release(MavenPublication) { 29 | from components.release 30 | 31 | groupId POM_GROUP_ID 32 | artifactId POM_ARTIFACT_ID 33 | version POM_VERSION_NAME 34 | 35 | pom { 36 | name = POM_NAME 37 | description = POM_DESCRIPTION 38 | url = POM_URL 39 | inceptionYear = POM_INCEPTION_YEAR 40 | packaging = POM_PACKAGING 41 | scm { 42 | connection = POM_SCM_CONNECTION 43 | url = POM_URL 44 | } 45 | issueManagement { 46 | system = POM_ISSUE_SYSTEM 47 | url = POM_ISSUE_URL 48 | } 49 | licenses { 50 | license { 51 | name = POM_LICENCE_NAME 52 | url = POM_LICENCE_URL 53 | distribution = POM_LICENCE_DIST 54 | } 55 | } 56 | developers { 57 | developer { 58 | id = POM_DEVELOPER_ID 59 | name = POM_DEVELOPER_NAME 60 | } 61 | } 62 | } 63 | } 64 | } 65 | repositories { 66 | maven { 67 | name = "sonatype" 68 | 69 | def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" 70 | def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" 71 | url = POM_VERSION_NAME.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl 72 | 73 | credentials { 74 | username ossrhUsername 75 | password ossrhPassword 76 | } 77 | } 78 | } 79 | } 80 | } 81 | 82 | signing { 83 | sign publishing.publications 84 | } 85 | -------------------------------------------------------------------------------- /gradle/sanitizer.gradle: -------------------------------------------------------------------------------- 1 | project.afterEvaluate { 2 | 3 | preBuild.doFirst { 4 | def projectDir = project.projectDir.toString() 5 | 6 | if (rootProject.ext.useASAN) { 7 | def ndkLibDir = android.ndkDirectory.absolutePath + "/toolchains/llvm/prebuilt/" 8 | def ABIs = rootProject.ext.abiFilters.split(",") 9 | 10 | for (String abi : ABIs) { 11 | def arch = abi 12 | if (abi == 'armeabi-v7a') { 13 | arch = "arm" 14 | } else if (abi == "arm64-v8a") { 15 | arch = "aarch64" 16 | } else if (abi == "x86") { 17 | arch = "i686" 18 | } else if (abi == "x86_64") { 19 | arch = "x86_64" 20 | } 21 | 22 | // create ASAN wrap.sh 23 | def resDir = new File(projectDir + "/src/main/resources/lib/" + abi) 24 | resDir.mkdirs() 25 | def wrapFile = new File(resDir, "wrap.sh") 26 | wrapFile.withWriter { writer -> 27 | writer.write('#!/system/bin/sh\n') 28 | writer.write('HERE="$(cd "$(dirname "$0")" && pwd)"\n') 29 | writer.write('export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1,detect_stack_use_after_return=1,check_initialization_order=true,quarantine_size_mb=64,color=never,new_delete_type_mismatch=0,sleep_before_dying=5,use_sigaltstack=0\n') 30 | writer.write("ASAN_LIB=\$(ls \$HERE/libclang_rt.asan-${arch}-android.so)\n") 31 | writer.write('if [ -f "$HERE/libc++_shared.so" ]; then\n') 32 | writer.write(' export LD_PRELOAD="$ASAN_LIB $HERE/libc++_shared.so"\n') 33 | writer.write('else\n') 34 | writer.write(' export LD_PRELOAD="$ASAN_LIB"\n') 35 | writer.write('fi\n') 36 | writer.write('"\$@"\n') 37 | } 38 | println "sanitizer: [${abi}] create wrap.sh: " + wrapFile.absolutePath 39 | 40 | // copy ASAN libs 41 | def libDir = new File(projectDir + "/src/main/jniLibs/" + abi) 42 | libDir.mkdirs() 43 | FileTree tree = fileTree(dir: ndkLibDir).include("**/libclang_rt.asan-${arch}-android.so") 44 | tree.each { File file -> 45 | copy { 46 | from file 47 | into libDir.absolutePath 48 | } 49 | println "sanitizer: [${abi}] copy lib: ${file.absolutePath} -> ${libDir.absolutePath}" 50 | } 51 | } 52 | } else { 53 | delete projectDir + '/src/main/resources/' 54 | delete projectDir + '/src/main/jniLibs/' 55 | } 56 | } 57 | } 58 | 59 | clean.doFirst { 60 | def projectDir = project.projectDir.toString() 61 | delete projectDir + '/src/main/resources/' 62 | delete projectDir + '/src/main/jniLibs/' 63 | } 64 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hexhacking/xDL/891149efd57f7bdfc5844180be9087462a4d1777/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Nov 08 14:07:21 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots/" } 14 | } 15 | } 16 | rootProject.name = "xDL" 17 | include ':xdl' 18 | include ':xdl_sample' 19 | -------------------------------------------------------------------------------- /xdl/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /xdl/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | android { 6 | namespace 'io.github.hexhacking.xdl' 7 | compileSdk rootProject.ext.compileSdkVersion 8 | buildToolsVersion = rootProject.ext.buildToolsVersion 9 | ndkVersion rootProject.ext.ndkVersion 10 | defaultConfig { 11 | minSdkVersion rootProject.ext.minSdkVersion 12 | targetSdkVersion rootProject.ext.targetSdkVersion 13 | externalNativeBuild { 14 | cmake { 15 | abiFilters rootProject.ext.abiFilters.split(",") 16 | arguments "-DANDROID_STL=none" 17 | if(rootProject.ext.useASAN) { 18 | arguments "-DANDROID_ARM_MODE=arm" 19 | arguments "-DUSEASAN=ON" 20 | } 21 | } 22 | } 23 | } 24 | externalNativeBuild { 25 | cmake { 26 | path "src/main/cpp/CMakeLists.txt" 27 | version rootProject.ext.cmakeVersion 28 | } 29 | } 30 | compileOptions { 31 | sourceCompatibility rootProject.ext.javaVersion 32 | targetCompatibility rootProject.ext.javaVersion 33 | } 34 | buildTypes { 35 | debug { 36 | minifyEnabled false 37 | } 38 | release { 39 | minifyEnabled false 40 | } 41 | } 42 | buildFeatures { 43 | prefabPublishing true 44 | buildConfig false 45 | } 46 | prefab { 47 | xdl { 48 | headers "src/main/cpp/include" 49 | } 50 | } 51 | publishing { 52 | singleVariant('release') { 53 | withSourcesJar() 54 | withJavadocJar() 55 | } 56 | } 57 | } 58 | 59 | apply from: rootProject.file('gradle/publish.gradle') 60 | -------------------------------------------------------------------------------- /xdl/local_dependency.cmake: -------------------------------------------------------------------------------- 1 | # We found that AGP could not generate the correct cmake configuration 2 | # when relying on a prefab package generated by another local project. 3 | # We had to write one ourselves. 4 | # 5 | # Use it like this: 6 | # 7 | # if(DEPENDENCY_ON_LOCAL_LIBRARY) 8 | # include(${CMAKE_CURRENT_SOURCE_DIR}/../../../../xdl/local_dependency.cmake) 9 | # else() 10 | # find_package(xdl REQUIRED CONFIG) 11 | # endif() 12 | # 13 | # target_link_libraries(myapp xdl::xdl) 14 | # 15 | 16 | string(TOLOWER ${CMAKE_BUILD_TYPE} XDL_BUILD_TYPE_DIRNAME) 17 | if(${XDL_BUILD_TYPE_DIRNAME} STREQUAL "relwithdebinfo") 18 | set(XDL_BUILD_TYPE_DIRNAME "release") 19 | endif() 20 | 21 | set(XDL_BASE ${CMAKE_CURRENT_SOURCE_DIR}/../../../../xdl) 22 | 23 | add_library(xdl::xdl SHARED IMPORTED) 24 | set_target_properties(xdl::xdl PROPERTIES 25 | IMPORTED_LOCATION "${XDL_BASE}/build/intermediates/cmake/${XDL_BUILD_TYPE_DIRNAME}/obj/${ANDROID_ABI}/libxdl.so" 26 | INTERFACE_INCLUDE_DIRECTORIES "${XDL_BASE}/src/main/cpp/include" 27 | INTERFACE_LINK_LIBRARIES "" 28 | ) 29 | -------------------------------------------------------------------------------- /xdl/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /xdl/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22.1) 2 | project(xdl) 3 | 4 | file(GLOB XDL_SRC *.c) 5 | add_library(xdl SHARED ${XDL_SRC}) 6 | target_compile_features(xdl PUBLIC c_std_17) 7 | target_compile_options(xdl PUBLIC -std=c17 -Weverything -Werror) 8 | target_include_directories(xdl PUBLIC include .) 9 | #target_link_libraries(xdl log) 10 | 11 | if(USEASAN) 12 | target_compile_options(xdl PUBLIC -fsanitize=address -fno-omit-frame-pointer) 13 | target_link_options(xdl PUBLIC -fsanitize=address) 14 | else() 15 | target_compile_options(xdl PUBLIC -Oz -ffunction-sections -fdata-sections) 16 | target_link_options(xdl PUBLIC -Oz -Wl,--exclude-libs,ALL -Wl,--gc-sections -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/xdl.map.txt) 17 | endif() 18 | 19 | if((${ANDROID_ABI} STREQUAL "arm64-v8a") OR (${ANDROID_ABI} STREQUAL "x86_64")) 20 | target_link_options(xdl PUBLIC "-Wl,-z,max-page-size=16384") 21 | endif() 22 | -------------------------------------------------------------------------------- /xdl/src/main/cpp/include/xdl.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2024 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: 2.2.0 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_GITHUB_HEXHACKING_XDL 33 | #define IO_GITHUB_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 xdl_open(), xdl_addr4(), 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_open2(struct dl_phdr_info *info); 67 | void *xdl_close(void *handle); 68 | void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size); 69 | void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size); 70 | 71 | // 72 | // Enhanced dladdr(). 73 | // 74 | #define XDL_NON_SYM 0x01 75 | int xdl_addr(void *addr, xdl_info_t *info, void **cache); 76 | int xdl_addr4(void *addr, xdl_info_t *info, void **cache, int flags); 77 | void xdl_addr_clean(void **cache); 78 | 79 | // 80 | // Enhanced dl_iterate_phdr(). 81 | // 82 | #define XDL_FULL_PATHNAME 0x01 83 | int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags); 84 | 85 | // 86 | // Custom dlinfo(). 87 | // 88 | #define XDL_DI_DLINFO 1 // type of info: xdl_info_t 89 | int xdl_info(void *handle, int request, void *info); 90 | 91 | #ifdef __cplusplus 92 | } 93 | #endif 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /xdl/src/main/cpp/xdl.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2024 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 STT_GNU_IFUNC 50 | #define STT_GNU_IFUNC 10 51 | #endif 52 | 53 | #ifndef __LP64__ 54 | #define XDL_LIB_PATH "/system/lib" 55 | #else 56 | #define XDL_LIB_PATH "/system/lib64" 57 | #endif 58 | 59 | #define XDL_DYNSYM_IS_EXPORT_SYM(shndx) (SHN_UNDEF != (shndx)) 60 | #define XDL_SYMTAB_IS_EXPORT_SYM(shndx) \ 61 | (SHN_UNDEF != (shndx) && !((shndx) >= SHN_LORESERVE && (shndx) <= SHN_HIRESERVE)) 62 | 63 | extern __attribute((weak)) unsigned long int getauxval(unsigned long int); 64 | 65 | #pragma clang diagnostic push 66 | #pragma clang diagnostic ignored "-Wpadded" 67 | 68 | typedef struct xdl { 69 | char *pathname; 70 | uintptr_t load_bias; 71 | const ElfW(Phdr) *dlpi_phdr; 72 | ElfW(Half) dlpi_phnum; 73 | 74 | struct xdl *next; // to next xdl obj for cache in xdl_addr() 75 | void *linker_handle; // hold handle returned by xdl_linker_force_dlopen() 76 | 77 | // 78 | // (1) for searching symbols from .dynsym 79 | // 80 | 81 | bool dynsym_try_load; 82 | ElfW(Sym) *dynsym; // .dynsym 83 | const char *dynstr; // .dynstr 84 | 85 | // .hash (SYSV hash for .dynstr) 86 | struct { 87 | const uint32_t *buckets; 88 | uint32_t buckets_cnt; 89 | const uint32_t *chains; 90 | uint32_t chains_cnt; 91 | } sysv_hash; 92 | 93 | // .gnu.hash (GNU hash for .dynstr) 94 | struct { 95 | const uint32_t *buckets; 96 | uint32_t buckets_cnt; 97 | const uint32_t *chains; 98 | uint32_t symoffset; 99 | const ElfW(Addr) *bloom; 100 | uint32_t bloom_cnt; 101 | uint32_t bloom_shift; 102 | } gnu_hash; 103 | 104 | // 105 | // (2) for searching symbols from .symtab 106 | // 107 | 108 | bool symtab_try_load; 109 | uintptr_t base; 110 | 111 | ElfW(Sym) *symtab; // .symtab 112 | size_t symtab_cnt; 113 | char *strtab; // .strtab 114 | size_t strtab_sz; 115 | } xdl_t; 116 | 117 | #pragma clang diagnostic pop 118 | 119 | static ElfW(Sym) *xdl_sym_by_addr(void *handle, void *addr); 120 | static ElfW(Sym) *xdl_dsym_by_addr(void *handle, void *addr); 121 | 122 | // load from memory 123 | static int xdl_dynsym_load(xdl_t *self) { 124 | // find the dynamic segment 125 | ElfW(Dyn) *dynamic = NULL; 126 | for (size_t i = 0; i < self->dlpi_phnum; i++) { 127 | const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]); 128 | if (PT_DYNAMIC == phdr->p_type) { 129 | dynamic = (ElfW(Dyn) *)(self->load_bias + phdr->p_vaddr); 130 | break; 131 | } 132 | } 133 | if (NULL == dynamic) return -1; 134 | 135 | // iterate the dynamic segment 136 | for (ElfW(Dyn) *entry = dynamic; entry && entry->d_tag != DT_NULL; entry++) { 137 | switch (entry->d_tag) { 138 | case DT_SYMTAB: //.dynsym 139 | self->dynsym = (ElfW(Sym) *)(self->load_bias + entry->d_un.d_ptr); 140 | break; 141 | case DT_STRTAB: //.dynstr 142 | self->dynstr = (const char *)(self->load_bias + entry->d_un.d_ptr); 143 | break; 144 | case DT_HASH: //.hash 145 | self->sysv_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0]; 146 | self->sysv_hash.chains_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1]; 147 | self->sysv_hash.buckets = &(((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2]); 148 | self->sysv_hash.chains = &(self->sysv_hash.buckets[self->sysv_hash.buckets_cnt]); 149 | break; 150 | case DT_GNU_HASH: //.gnu.hash 151 | self->gnu_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0]; 152 | self->gnu_hash.symoffset = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1]; 153 | self->gnu_hash.bloom_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2]; 154 | self->gnu_hash.bloom_shift = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[3]; 155 | self->gnu_hash.bloom = (const ElfW(Addr) *)(self->load_bias + entry->d_un.d_ptr + 16); 156 | self->gnu_hash.buckets = (const uint32_t *)(&(self->gnu_hash.bloom[self->gnu_hash.bloom_cnt])); 157 | self->gnu_hash.chains = (const uint32_t *)(&(self->gnu_hash.buckets[self->gnu_hash.buckets_cnt])); 158 | break; 159 | default: 160 | break; 161 | } 162 | } 163 | 164 | if (NULL == self->dynsym || NULL == self->dynstr || 165 | (0 == self->sysv_hash.buckets_cnt && 0 == self->gnu_hash.buckets_cnt)) { 166 | self->dynsym = NULL; 167 | self->dynstr = NULL; 168 | self->sysv_hash.buckets_cnt = 0; 169 | self->gnu_hash.buckets_cnt = 0; 170 | return -1; 171 | } 172 | 173 | return 0; 174 | } 175 | 176 | static void *xdl_read_file_to_heap(int file_fd, size_t file_sz, size_t data_offset, size_t data_len) { 177 | if (0 == data_len) return NULL; 178 | if (data_offset >= file_sz) return NULL; 179 | if (data_offset + data_len > file_sz) return NULL; 180 | 181 | if (data_offset != (size_t)lseek(file_fd, (off_t)data_offset, SEEK_SET)) return NULL; 182 | 183 | void *data = malloc(data_len); 184 | if (NULL == data) return NULL; 185 | 186 | #pragma clang diagnostic push 187 | #pragma clang diagnostic ignored "-Wgnu-statement-expression" 188 | if ((ssize_t)data_len != XDL_UTIL_TEMP_FAILURE_RETRY(read(file_fd, data, data_len))) 189 | #pragma clang diagnostic pop 190 | { 191 | free(data); 192 | return NULL; 193 | } 194 | 195 | return data; 196 | } 197 | 198 | static void *xdl_read_file_to_heap_by_section(int file_fd, size_t file_sz, ElfW(Shdr) *shdr) { 199 | return xdl_read_file_to_heap(file_fd, file_sz, (size_t)shdr->sh_offset, shdr->sh_size); 200 | } 201 | 202 | static void *xdl_read_memory_to_heap(void *mem, size_t mem_sz, size_t data_offset, size_t data_len) { 203 | if (0 == data_len) return NULL; 204 | if (data_offset >= mem_sz) return NULL; 205 | if (data_offset + data_len > mem_sz) return NULL; 206 | 207 | void *data = malloc(data_len); 208 | if (NULL == data) return NULL; 209 | 210 | memcpy(data, (void *)((uintptr_t)mem + data_offset), data_len); 211 | return data; 212 | } 213 | 214 | static void *xdl_read_memory_to_heap_by_section(void *mem, size_t mem_sz, ElfW(Shdr) *shdr) { 215 | return xdl_read_memory_to_heap(mem, mem_sz, (size_t)shdr->sh_offset, shdr->sh_size); 216 | } 217 | 218 | static void *xdl_get_memory(void *mem, size_t mem_sz, size_t data_offset, size_t data_len) { 219 | if (0 == data_len) return NULL; 220 | if (data_offset >= mem_sz) return NULL; 221 | if (data_offset + data_len > mem_sz) return NULL; 222 | 223 | return (void *)((uintptr_t)mem + data_offset); 224 | } 225 | 226 | static void *xdl_get_memory_by_section(void *mem, size_t mem_sz, ElfW(Shdr) *shdr) { 227 | return xdl_get_memory(mem, mem_sz, (size_t)shdr->sh_offset, shdr->sh_size); 228 | } 229 | 230 | // load from disk and memory 231 | static int xdl_symtab_load_from_debugdata(xdl_t *self, int file_fd, size_t file_sz, 232 | ElfW(Shdr) *shdr_debugdata) { 233 | void *debugdata = NULL; 234 | ElfW(Shdr) *shdrs = NULL; 235 | int r = -1; 236 | 237 | // get zipped .gnu_debugdata 238 | uint8_t *debugdata_zip = (uint8_t *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr_debugdata); 239 | if (NULL == debugdata_zip) return -1; 240 | 241 | // get unzipped .gnu_debugdata 242 | size_t debugdata_sz; 243 | if (0 != xdl_lzma_decompress(debugdata_zip, shdr_debugdata->sh_size, (uint8_t **)&debugdata, &debugdata_sz)) 244 | goto end; 245 | 246 | // get ELF header 247 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)debugdata; 248 | if (0 == ehdr->e_shnum || ehdr->e_shentsize != sizeof(ElfW(Shdr))) goto end; 249 | 250 | // get section headers 251 | shdrs = (ElfW(Shdr) *)xdl_read_memory_to_heap(debugdata, debugdata_sz, (size_t)ehdr->e_shoff, 252 | ehdr->e_shentsize * ehdr->e_shnum); 253 | if (NULL == shdrs) goto end; 254 | 255 | // get .shstrtab 256 | if (SHN_UNDEF == ehdr->e_shstrndx || ehdr->e_shstrndx >= ehdr->e_shnum) goto end; 257 | char *shstrtab = (char *)xdl_get_memory_by_section(debugdata, debugdata_sz, shdrs + ehdr->e_shstrndx); 258 | if (NULL == shstrtab) goto end; 259 | 260 | // find .symtab & .strtab 261 | for (ElfW(Shdr) *shdr = shdrs; shdr < shdrs + ehdr->e_shnum; shdr++) { 262 | char *shdr_name = shstrtab + shdr->sh_name; 263 | 264 | if (SHT_SYMTAB == shdr->sh_type && 0 == strcmp(".symtab", shdr_name)) { 265 | // get & check associated .strtab section 266 | if (shdr->sh_link >= ehdr->e_shnum) continue; 267 | ElfW(Shdr) *shdr_strtab = shdrs + shdr->sh_link; 268 | if (SHT_STRTAB != shdr_strtab->sh_type) continue; 269 | 270 | // get .symtab & .strtab 271 | ElfW(Sym) *symtab = (ElfW(Sym) *)xdl_read_memory_to_heap_by_section(debugdata, debugdata_sz, shdr); 272 | if (NULL == symtab) continue; 273 | char *strtab = (char *)xdl_read_memory_to_heap_by_section(debugdata, debugdata_sz, shdr_strtab); 274 | if (NULL == strtab) { 275 | free(symtab); 276 | continue; 277 | } 278 | 279 | // OK 280 | self->symtab = symtab; 281 | self->symtab_cnt = shdr->sh_size / shdr->sh_entsize; 282 | self->strtab = strtab; 283 | self->strtab_sz = shdr_strtab->sh_size; 284 | r = 0; 285 | break; 286 | } 287 | } 288 | 289 | end: 290 | free(debugdata_zip); 291 | if (NULL != debugdata) free(debugdata); 292 | if (NULL != shdrs) free(shdrs); 293 | return r; 294 | } 295 | 296 | // load from disk and memory 297 | static int xdl_symtab_load(xdl_t *self) { 298 | if ('[' == self->pathname[0]) return -1; 299 | 300 | int r = -1; 301 | ElfW(Shdr) *shdrs = NULL; 302 | char *shstrtab = NULL; 303 | 304 | // get base address 305 | uintptr_t vaddr_min = UINTPTR_MAX; 306 | for (size_t i = 0; i < self->dlpi_phnum; i++) { 307 | const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]); 308 | if (PT_LOAD == phdr->p_type) { 309 | if (vaddr_min > phdr->p_vaddr) vaddr_min = phdr->p_vaddr; 310 | } 311 | } 312 | if (UINTPTR_MAX == vaddr_min) return -1; 313 | self->base = self->load_bias + vaddr_min; 314 | 315 | // open file 316 | int flags = O_RDONLY | O_CLOEXEC; 317 | int file_fd; 318 | if ('/' == self->pathname[0]) { 319 | file_fd = open(self->pathname, flags); 320 | } else { 321 | char full_pathname[1024]; 322 | // try the fast method 323 | snprintf(full_pathname, sizeof(full_pathname), "%s/%s", XDL_LIB_PATH, self->pathname); 324 | file_fd = open(full_pathname, flags); 325 | if (file_fd < 0) { 326 | // try the slow method 327 | if (0 != xdl_iterate_get_full_pathname(self->base, full_pathname, sizeof(full_pathname))) return -1; 328 | file_fd = open(full_pathname, flags); 329 | } 330 | } 331 | if (file_fd < 0) return -1; 332 | struct stat st; 333 | if (0 != fstat(file_fd, &st)) goto end; 334 | size_t file_sz = (size_t)st.st_size; 335 | 336 | // get ELF header 337 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)self->base; 338 | if (0 == ehdr->e_shnum || ehdr->e_shentsize != sizeof(ElfW(Shdr))) goto end; 339 | 340 | // get section headers 341 | shdrs = (ElfW(Shdr) *)xdl_read_file_to_heap(file_fd, file_sz, (size_t)ehdr->e_shoff, 342 | ehdr->e_shentsize * ehdr->e_shnum); 343 | if (NULL == shdrs) goto end; 344 | 345 | // get .shstrtab 346 | if (SHN_UNDEF == ehdr->e_shstrndx || ehdr->e_shstrndx >= ehdr->e_shnum) goto end; 347 | shstrtab = (char *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdrs + ehdr->e_shstrndx); 348 | if (NULL == shstrtab) goto end; 349 | 350 | // find .symtab & .strtab 351 | for (ElfW(Shdr) *shdr = shdrs; shdr < shdrs + ehdr->e_shnum; shdr++) { 352 | char *shdr_name = shstrtab + shdr->sh_name; 353 | 354 | if (SHT_SYMTAB == shdr->sh_type && 0 == strcmp(".symtab", shdr_name)) { 355 | // get & check associated .strtab section 356 | if (shdr->sh_link >= ehdr->e_shnum) continue; 357 | ElfW(Shdr) *shdr_strtab = shdrs + shdr->sh_link; 358 | if (SHT_STRTAB != shdr_strtab->sh_type) continue; 359 | 360 | // get .symtab & .strtab 361 | ElfW(Sym) *symtab = (ElfW(Sym) *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr); 362 | if (NULL == symtab) continue; 363 | char *strtab = (char *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr_strtab); 364 | if (NULL == strtab) { 365 | free(symtab); 366 | continue; 367 | } 368 | 369 | // OK 370 | self->symtab = symtab; 371 | self->symtab_cnt = shdr->sh_size / shdr->sh_entsize; 372 | self->strtab = strtab; 373 | self->strtab_sz = shdr_strtab->sh_size; 374 | r = 0; 375 | break; 376 | } else if (SHT_PROGBITS == shdr->sh_type && 0 == strcmp(".gnu_debugdata", shdr_name)) { 377 | if (0 == xdl_symtab_load_from_debugdata(self, file_fd, file_sz, shdr)) { 378 | // OK 379 | r = 0; 380 | break; 381 | } 382 | } 383 | } 384 | 385 | end: 386 | close(file_fd); 387 | if (NULL != shdrs) free(shdrs); 388 | if (NULL != shstrtab) free(shstrtab); 389 | return r; 390 | } 391 | 392 | static xdl_t *xdl_find_from_auxv(unsigned long type, const char *pathname) { 393 | if (NULL == getauxval) return NULL; // API level < 18 394 | 395 | uintptr_t val = (uintptr_t)getauxval(type); 396 | if (0 == val) return NULL; 397 | 398 | // get base 399 | uintptr_t base = (AT_PHDR == type ? (val & (~0xffful)) : val); 400 | if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) return NULL; 401 | 402 | // ELF info 403 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base; 404 | const ElfW(Phdr) *dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff); 405 | ElfW(Half) dlpi_phnum = ehdr->e_phnum; 406 | 407 | // get bias 408 | uintptr_t min_vaddr = UINTPTR_MAX; 409 | for (size_t i = 0; i < dlpi_phnum; i++) { 410 | const ElfW(Phdr) *phdr = &(dlpi_phdr[i]); 411 | if (PT_LOAD == phdr->p_type) { 412 | if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr; 413 | } 414 | } 415 | if (UINTPTR_MAX == min_vaddr || base < min_vaddr) return NULL; 416 | uintptr_t load_bias = base - min_vaddr; 417 | 418 | // create xDL object 419 | xdl_t *self; 420 | if (NULL == (self = calloc(1, sizeof(xdl_t)))) return NULL; 421 | if (NULL == (self->pathname = strdup(pathname))) { 422 | free(self); 423 | return NULL; 424 | } 425 | self->load_bias = load_bias; 426 | self->dlpi_phdr = dlpi_phdr; 427 | self->dlpi_phnum = dlpi_phnum; 428 | self->dynsym_try_load = false; 429 | self->symtab_try_load = false; 430 | return self; 431 | } 432 | 433 | static int xdl_find_iterate_cb(struct dl_phdr_info *info, size_t size, void *arg) { 434 | (void)size; 435 | 436 | uintptr_t *pkg = (uintptr_t *)arg; 437 | xdl_t **self = (xdl_t **)*pkg++; 438 | const char *filename = (const char *)*pkg; 439 | 440 | // check load_bias 441 | if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0; 442 | 443 | // check pathname 444 | if ('[' == filename[0]) { 445 | if (0 != strcmp(info->dlpi_name, filename)) return 0; 446 | } else if ('/' == filename[0]) { 447 | if ('/' == info->dlpi_name[0]) { 448 | if (0 != strcmp(info->dlpi_name, filename)) return 0; 449 | } else { 450 | if (!xdl_util_ends_with(filename, info->dlpi_name)) return 0; 451 | } 452 | } else { 453 | if ('/' == info->dlpi_name[0]) { 454 | if (!xdl_util_ends_with(info->dlpi_name, filename)) return 0; 455 | } else { 456 | if (0 != strcmp(info->dlpi_name, filename)) return 0; 457 | } 458 | } 459 | 460 | // found the target ELF 461 | if (NULL == ((*self) = calloc(1, sizeof(xdl_t)))) return 1; // return failed 462 | if (NULL == ((*self)->pathname = strdup((const char *)info->dlpi_name))) { 463 | free(*self); 464 | *self = NULL; 465 | return 1; // return failed 466 | } 467 | (*self)->load_bias = info->dlpi_addr; 468 | (*self)->dlpi_phdr = info->dlpi_phdr; 469 | (*self)->dlpi_phnum = info->dlpi_phnum; 470 | (*self)->dynsym_try_load = false; 471 | (*self)->symtab_try_load = false; 472 | return 1; // return OK 473 | } 474 | 475 | static xdl_t *xdl_find(const char *filename) { 476 | // from auxv (linker, vDSO) 477 | xdl_t *self = NULL; 478 | if (xdl_util_ends_with(filename, XDL_UTIL_LINKER_BASENAME)) 479 | self = xdl_find_from_auxv(AT_BASE, XDL_UTIL_LINKER_PATHNAME); 480 | else if (xdl_util_ends_with(filename, XDL_UTIL_VDSO_BASENAME)) 481 | self = xdl_find_from_auxv(AT_SYSINFO_EHDR, XDL_UTIL_VDSO_BASENAME); 482 | 483 | // from auxv (app_process) 484 | const char *basename, *pathname; 485 | #if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__ 486 | if (xdl_util_get_api_level() < __ANDROID_API_L__) { 487 | basename = XDL_UTIL_APP_PROCESS_BASENAME_K; 488 | pathname = XDL_UTIL_APP_PROCESS_PATHNAME_K; 489 | } else 490 | #endif 491 | { 492 | basename = XDL_UTIL_APP_PROCESS_BASENAME; 493 | pathname = XDL_UTIL_APP_PROCESS_PATHNAME; 494 | } 495 | if (xdl_util_ends_with(filename, basename)) self = xdl_find_from_auxv(AT_PHDR, pathname); 496 | 497 | if (NULL != self) return self; 498 | 499 | // from dl_iterate_phdr 500 | uintptr_t pkg[2] = {(uintptr_t)&self, (uintptr_t)filename}; 501 | xdl_iterate_phdr(xdl_find_iterate_cb, pkg, XDL_DEFAULT); 502 | return self; 503 | } 504 | 505 | static void *xdl_open_always_force(const char *filename) { 506 | // always force dlopen() 507 | void *linker_handle = xdl_linker_force_dlopen(filename); 508 | if (NULL == linker_handle) return NULL; 509 | 510 | // find 511 | xdl_t *self = xdl_find(filename); 512 | if (NULL == self) 513 | dlclose(linker_handle); 514 | else 515 | self->linker_handle = linker_handle; 516 | 517 | return (void *)self; 518 | } 519 | 520 | static void *xdl_open_try_force(const char *filename) { 521 | // find 522 | xdl_t *self = xdl_find(filename); 523 | if (NULL != self) return (void *)self; 524 | 525 | // try force dlopen() 526 | void *linker_handle = xdl_linker_force_dlopen(filename); 527 | if (NULL == linker_handle) return NULL; 528 | 529 | // find again 530 | self = xdl_find(filename); 531 | if (NULL == self) 532 | dlclose(linker_handle); 533 | else 534 | self->linker_handle = linker_handle; 535 | 536 | return (void *)self; 537 | } 538 | 539 | void *xdl_open(const char *filename, int flags) { 540 | if (NULL == filename) return NULL; 541 | 542 | if (flags & XDL_ALWAYS_FORCE_LOAD) 543 | return xdl_open_always_force(filename); 544 | else if (flags & XDL_TRY_FORCE_LOAD) 545 | return xdl_open_try_force(filename); 546 | else 547 | return xdl_find(filename); 548 | } 549 | 550 | void *xdl_open2(struct dl_phdr_info *info) { 551 | xdl_t *self = calloc(1, sizeof(xdl_t)); 552 | if (NULL == self) return NULL; 553 | if (NULL == (self->pathname = strdup((const char *)info->dlpi_name))) { 554 | free(self); 555 | return NULL; 556 | } 557 | self->load_bias = info->dlpi_addr; 558 | self->dlpi_phdr = info->dlpi_phdr; 559 | self->dlpi_phnum = info->dlpi_phnum; 560 | self->dynsym_try_load = false; 561 | self->symtab_try_load = false; 562 | return self; 563 | } 564 | 565 | void *xdl_close(void *handle) { 566 | if (NULL == handle) return NULL; 567 | 568 | xdl_t *self = (xdl_t *)handle; 569 | if (NULL != self->pathname) free(self->pathname); 570 | if (NULL != self->symtab) free(self->symtab); 571 | if (NULL != self->strtab) free(self->strtab); 572 | 573 | void *linker_handle = self->linker_handle; 574 | free(self); 575 | return linker_handle; 576 | } 577 | 578 | static uint32_t xdl_sysv_hash(const uint8_t *name) { 579 | uint32_t h = 0, g; 580 | 581 | while (*name) { 582 | h = (h << 4) + *name++; 583 | g = h & 0xf0000000; 584 | h ^= g; 585 | h ^= g >> 24; 586 | } 587 | return h; 588 | } 589 | 590 | static uint32_t xdl_gnu_hash(const uint8_t *name) { 591 | uint32_t h = 5381; 592 | 593 | while (*name) { 594 | h += (h << 5) + *name++; 595 | } 596 | return h; 597 | } 598 | 599 | static ElfW(Sym) *xdl_dynsym_find_symbol_use_sysv_hash(xdl_t *self, const char *sym_name) { 600 | uint32_t hash = xdl_sysv_hash((const uint8_t *)sym_name); 601 | 602 | for (uint32_t i = self->sysv_hash.buckets[hash % self->sysv_hash.buckets_cnt]; 0 != i; 603 | i = self->sysv_hash.chains[i]) { 604 | ElfW(Sym) *sym = self->dynsym + i; 605 | if (0 != strcmp(self->dynstr + sym->st_name, sym_name)) continue; 606 | return sym; 607 | } 608 | 609 | return NULL; 610 | } 611 | 612 | static ElfW(Sym) *xdl_dynsym_find_symbol_use_gnu_hash(xdl_t *self, const char *sym_name) { 613 | uint32_t hash = xdl_gnu_hash((const uint8_t *)sym_name); 614 | 615 | static uint32_t elfclass_bits = sizeof(ElfW(Addr)) * 8; 616 | size_t word = self->gnu_hash.bloom[(hash / elfclass_bits) % self->gnu_hash.bloom_cnt]; 617 | size_t mask = 0 | (size_t)1 << (hash % elfclass_bits) | 618 | (size_t)1 << ((hash >> self->gnu_hash.bloom_shift) % elfclass_bits); 619 | 620 | // if at least one bit is not set, this symbol is surely missing 621 | if ((word & mask) != mask) return NULL; 622 | 623 | // ignore STN_UNDEF 624 | uint32_t i = self->gnu_hash.buckets[hash % self->gnu_hash.buckets_cnt]; 625 | if (i < self->gnu_hash.symoffset) return NULL; 626 | 627 | // loop through the chain 628 | while (1) { 629 | ElfW(Sym) *sym = self->dynsym + i; 630 | uint32_t sym_hash = self->gnu_hash.chains[i - self->gnu_hash.symoffset]; 631 | 632 | if ((hash | (uint32_t)1) == (sym_hash | (uint32_t)1)) { 633 | if (0 == strcmp(self->dynstr + sym->st_name, sym_name)) { 634 | return sym; 635 | } 636 | } 637 | 638 | // chain ends with an element with the lowest bit set to 1 639 | if (sym_hash & (uint32_t)1) break; 640 | 641 | i++; 642 | } 643 | 644 | return NULL; 645 | } 646 | 647 | typedef struct { 648 | unsigned long size; /** Set to sizeof(__ifunc_arg_t). */ 649 | unsigned long hwcap; /** Set to getauxval(AT_HWCAP). */ 650 | unsigned long hwcap2; /** Set to getauxval(AT_HWCAP2). */ 651 | } xdl_ifunc_arg_t; 652 | 653 | #if defined(__aarch64__) 654 | #define XDL_IFUNC_ARG_HWCAP (1ULL << 62) 655 | #endif 656 | 657 | static void *xdl_resolve_symbol_address(xdl_t *self, ElfW(Sym) *sym, size_t *symbol_size) { 658 | if (ELF_ST_TYPE(sym->st_info) == STT_TLS) { 659 | return NULL; 660 | } else if (ELF_ST_TYPE(sym->st_info) == STT_GNU_IFUNC) { 661 | void *sym_addr = (void *)(self->load_bias + sym->st_value); 662 | void *real_sym_addr = NULL; 663 | if (xdl_util_get_api_level() < __ANDROID_API_R__) { 664 | // Android [4.x, 10] 665 | typedef void *(*ifunc_resolver_t)(void); 666 | real_sym_addr = ((ifunc_resolver_t)sym_addr)(); 667 | } else { 668 | // Android [11, ...) 669 | #if defined(__aarch64__) 670 | if (NULL == getauxval) return NULL; 671 | typedef void *(*ifunc_resolver_t)(uint64_t, xdl_ifunc_arg_t *); 672 | static xdl_ifunc_arg_t arg; 673 | static bool initialized = false; 674 | if (!initialized) { 675 | arg.size = sizeof(xdl_ifunc_arg_t); 676 | arg.hwcap = getauxval(AT_HWCAP); 677 | arg.hwcap2 = getauxval(AT_HWCAP2); 678 | initialized = true; 679 | } 680 | real_sym_addr = ((ifunc_resolver_t)sym_addr)(arg.hwcap | XDL_IFUNC_ARG_HWCAP, &arg); 681 | #elif defined(__arm__) 682 | if (NULL == getauxval) return NULL; 683 | typedef void *(*ifunc_resolver_t)(unsigned long); 684 | static unsigned long hwcap; 685 | static bool initialized = false; 686 | if (!initialized) { 687 | hwcap = getauxval(AT_HWCAP); 688 | initialized = true; 689 | } 690 | real_sym_addr = ((ifunc_resolver_t)sym_addr)(hwcap); 691 | #else 692 | typedef void *(*ifunc_resolver_t)(void); 693 | real_sym_addr = ((ifunc_resolver_t)sym_addr)(); 694 | #endif 695 | } 696 | if (NULL != symbol_size && NULL != real_sym_addr) { 697 | ElfW(Sym) *real_sym = xdl_sym_by_addr(self, real_sym_addr); 698 | if (NULL == real_sym) real_sym = xdl_dsym_by_addr(self, real_sym_addr); 699 | if (NULL != real_sym) *symbol_size = real_sym->st_size; 700 | } 701 | return real_sym_addr; 702 | } else { 703 | if (NULL != symbol_size) *symbol_size = sym->st_size; 704 | return (void *)(self->load_bias + sym->st_value); 705 | } 706 | } 707 | 708 | void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size) { 709 | if (NULL == handle || NULL == symbol) return NULL; 710 | if (NULL != symbol_size) *symbol_size = 0; 711 | 712 | xdl_t *self = (xdl_t *)handle; 713 | 714 | // load .dynsym only once 715 | if (!self->dynsym_try_load) { 716 | self->dynsym_try_load = true; 717 | if (0 != xdl_dynsym_load(self)) return NULL; 718 | } 719 | 720 | // find symbol 721 | if (NULL == self->dynsym) return NULL; 722 | ElfW(Sym) *sym = NULL; 723 | if (self->gnu_hash.buckets_cnt > 0) { 724 | // use GNU hash (.gnu.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1) 725 | sym = xdl_dynsym_find_symbol_use_gnu_hash(self, symbol); 726 | } 727 | if (NULL == sym && self->sysv_hash.buckets_cnt > 0) { 728 | // use SYSV hash (.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1) 729 | sym = xdl_dynsym_find_symbol_use_sysv_hash(self, symbol); 730 | } 731 | if (NULL == sym || !XDL_DYNSYM_IS_EXPORT_SYM(sym->st_shndx)) return NULL; 732 | 733 | return xdl_resolve_symbol_address(self, sym, symbol_size); 734 | } 735 | 736 | // clang-format off 737 | /* 738 | * For internal symbols in .symtab, LLVM may add some suffixes (for example for thinLTO). 739 | * The format of the suffix is: ".xxxx.[hash]". LLVM may add multiple suffixes at once. 740 | * The symbol name after removing these all suffixes is called canonical name. 741 | * 742 | * Because the hash part in the suffix may change when recompiled, so here we only match 743 | * the canonical name. 744 | * 745 | * IN ADDITION: According to C/C++ syntax, it is illegal for a function name to contain 746 | * dot character('.'), either in the middle or at the end. 747 | * 748 | * samples: 749 | * 750 | * symbol name in .symtab lookup is match 751 | * ---------------------- ---------------- -------- 752 | * abcd abc N 753 | * abcd abcde N 754 | * abcd abcd Y 755 | * abcd.llvm.10190306339727611508 abc N 756 | * abcd.llvm.10190306339727611508 abcd Y 757 | * abcd.llvm.10190306339727611508 abcd. N 758 | * abcd.llvm.10190306339727611508 abcd.llvm Y 759 | * abcd.llvm.10190306339727611508 abcd.llvm. N 760 | * abcd.__uniq.513291356003753 abcd.__uniq.51329 N 761 | * abcd.__uniq.513291356003753 abcd.__uniq.513291356003753 Y 762 | */ 763 | // clang-format on 764 | static inline bool xdl_dsym_is_match(const char *str, const char *sym, size_t sym_len) { 765 | size_t str_len = strlen(str); 766 | if (0 == str_len) return false; 767 | 768 | if (str_len < sym_len) { 769 | return false; 770 | } else { 771 | bool sym_len_match = (0 == memcmp(str, sym, sym_len)); 772 | if (str_len == sym_len) 773 | return sym_len_match; 774 | else // str_len > sym_len 775 | return sym_len_match && (str[sym_len] == '.' || str[sym_len] == '$'); 776 | } 777 | } 778 | 779 | void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size) { 780 | if (NULL == handle || NULL == symbol) return NULL; 781 | if (NULL != symbol_size) *symbol_size = 0; 782 | 783 | xdl_t *self = (xdl_t *)handle; 784 | 785 | // load .symtab only once 786 | if (!self->symtab_try_load) { 787 | self->symtab_try_load = true; 788 | if (0 != xdl_symtab_load(self)) return NULL; 789 | } 790 | 791 | // find symbol 792 | if (NULL == self->symtab) return NULL; 793 | size_t symbol_len = strlen(symbol); 794 | for (size_t i = 0; i < self->symtab_cnt; i++) { 795 | ElfW(Sym) *sym = self->symtab + i; 796 | 797 | if (!XDL_SYMTAB_IS_EXPORT_SYM(sym->st_shndx)) continue; 798 | // if (0 != strncmp(self->strtab + sym->st_name, symbol, self->strtab_sz - sym->st_name)) continue; 799 | if (!xdl_dsym_is_match(self->strtab + sym->st_name, symbol, symbol_len)) continue; 800 | 801 | if (NULL != symbol_size) *symbol_size = sym->st_size; 802 | return (void *)(self->load_bias + sym->st_value); 803 | } 804 | 805 | return NULL; 806 | } 807 | 808 | static bool xdl_elf_is_match(uintptr_t load_bias, const ElfW(Phdr) *dlpi_phdr, ElfW(Half) dlpi_phnum, 809 | uintptr_t addr) { 810 | if (addr < load_bias) return false; 811 | 812 | uintptr_t vaddr = addr - load_bias; 813 | for (size_t i = 0; i < dlpi_phnum; i++) { 814 | const ElfW(Phdr) *phdr = &(dlpi_phdr[i]); 815 | if (PT_LOAD != phdr->p_type) continue; 816 | 817 | if (phdr->p_vaddr <= vaddr && vaddr < phdr->p_vaddr + phdr->p_memsz) return true; 818 | } 819 | 820 | return false; 821 | } 822 | 823 | static int xdl_open_by_addr_iterate_cb(struct dl_phdr_info *info, size_t size, void *arg) { 824 | (void)size; 825 | 826 | uintptr_t *pkg = (uintptr_t *)arg; 827 | xdl_t **self = (xdl_t **)*pkg++; 828 | uintptr_t addr = *pkg; 829 | 830 | if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0; // continue 831 | 832 | if (xdl_elf_is_match(info->dlpi_addr, info->dlpi_phdr, info->dlpi_phnum, addr)) { 833 | // found the target ELF 834 | if (NULL == ((*self) = calloc(1, sizeof(xdl_t)))) return 1; // failed 835 | if (NULL == ((*self)->pathname = strdup((const char *)info->dlpi_name))) { 836 | free(*self); 837 | *self = NULL; 838 | return 1; // failed 839 | } 840 | (*self)->load_bias = info->dlpi_addr; 841 | (*self)->dlpi_phdr = info->dlpi_phdr; 842 | (*self)->dlpi_phnum = info->dlpi_phnum; 843 | (*self)->dynsym_try_load = false; 844 | (*self)->symtab_try_load = false; 845 | return 1; // OK 846 | } 847 | 848 | return 0; // continue 849 | } 850 | 851 | static void *xdl_open_by_addr(void *addr) { 852 | if (NULL == addr) return NULL; 853 | 854 | xdl_t *self = NULL; 855 | uintptr_t pkg[2] = {(uintptr_t)&self, (uintptr_t)addr}; 856 | xdl_iterate_phdr(xdl_open_by_addr_iterate_cb, pkg, XDL_DEFAULT); 857 | 858 | return (void *)self; 859 | } 860 | 861 | static bool xdl_sym_is_match(ElfW(Sym) *sym, uintptr_t offset, bool is_symtab) { 862 | if (is_symtab) { 863 | if (!XDL_SYMTAB_IS_EXPORT_SYM(sym->st_shndx)) return false; 864 | } else { 865 | if (!XDL_DYNSYM_IS_EXPORT_SYM(sym->st_shndx)) return false; 866 | } 867 | if (ELF_ST_TYPE(sym->st_info) == STT_TLS) return false; 868 | 869 | // For thumb instructions, "st_value" is an odd number, and the instructions are stored 870 | // in the range: [st_value - 1, st_value - 1 + st_size). 871 | // NOTE: The dladdr() implementation in the Android bionic linker does NOT fix this for 872 | // thumb and is therefore incorrect. 873 | uintptr_t sym_st_value_fixed = sym->st_value; 874 | #if defined(__arm__) && defined(__thumb__) 875 | #define CLEAR_BIT0(addr) ((addr)&0xFFFFFFFE) 876 | sym_st_value_fixed = CLEAR_BIT0(sym->st_value); 877 | #endif 878 | 879 | return offset >= sym_st_value_fixed && offset < sym_st_value_fixed + sym->st_size; 880 | } 881 | 882 | static ElfW(Sym) *xdl_sym_by_addr(void *handle, void *addr) { 883 | xdl_t *self = (xdl_t *)handle; 884 | 885 | // load .dynsym only once 886 | if (!self->dynsym_try_load) { 887 | self->dynsym_try_load = true; 888 | if (0 != xdl_dynsym_load(self)) return NULL; 889 | } 890 | 891 | // find symbol 892 | if (NULL == self->dynsym) return NULL; 893 | uintptr_t offset = (uintptr_t)addr - self->load_bias; 894 | if (self->gnu_hash.buckets_cnt > 0) { 895 | const uint32_t *chains_all = self->gnu_hash.chains - self->gnu_hash.symoffset; 896 | for (size_t i = 0; i < self->gnu_hash.buckets_cnt; i++) { 897 | uint32_t n = self->gnu_hash.buckets[i]; 898 | if (n < self->gnu_hash.symoffset) continue; 899 | do { 900 | ElfW(Sym) *sym = self->dynsym + n; 901 | if (xdl_sym_is_match(sym, offset, false)) return sym; 902 | } while ((chains_all[n++] & 1) == 0); 903 | } 904 | } else if (self->sysv_hash.chains_cnt > 0) { 905 | for (size_t i = 0; i < self->sysv_hash.chains_cnt; i++) { 906 | ElfW(Sym) *sym = self->dynsym + i; 907 | if (xdl_sym_is_match(sym, offset, false)) return sym; 908 | } 909 | } 910 | 911 | return NULL; 912 | } 913 | 914 | static ElfW(Sym) *xdl_dsym_by_addr(void *handle, void *addr) { 915 | xdl_t *self = (xdl_t *)handle; 916 | 917 | // load .symtab only once 918 | if (!self->symtab_try_load) { 919 | self->symtab_try_load = true; 920 | if (0 != xdl_symtab_load(self)) return NULL; 921 | } 922 | 923 | // find symbol 924 | if (NULL == self->symtab) return NULL; 925 | uintptr_t offset = (uintptr_t)addr - self->load_bias; 926 | for (size_t i = 0; i < self->symtab_cnt; i++) { 927 | ElfW(Sym) *sym = self->symtab + i; 928 | if (xdl_sym_is_match(sym, offset, true)) return sym; 929 | } 930 | 931 | return NULL; 932 | } 933 | 934 | int xdl_addr(void *addr, xdl_info_t *info, void **cache) { 935 | return xdl_addr4(addr, info, cache, XDL_DEFAULT); 936 | } 937 | 938 | int xdl_addr4(void *addr, xdl_info_t *info, void **cache, int flags) { 939 | if (NULL == addr || NULL == info || NULL == cache) return 0; 940 | 941 | memset(info, 0, sizeof(Dl_info)); 942 | 943 | // find handle from cache 944 | xdl_t *handle = NULL; 945 | for (handle = *((xdl_t **)cache); NULL != handle; handle = handle->next) 946 | if (xdl_elf_is_match(handle->load_bias, handle->dlpi_phdr, handle->dlpi_phnum, (uintptr_t)addr)) break; 947 | 948 | // create new handle, save handle to cache 949 | if (NULL == handle) { 950 | handle = (xdl_t *)xdl_open_by_addr(addr); 951 | if (NULL == handle) return 0; 952 | handle->next = *(xdl_t **)cache; 953 | *(xdl_t **)cache = handle; 954 | } 955 | 956 | // we have at least: load_bias, pathname, dlpi_phdr, dlpi_phnum 957 | info->dli_fbase = (void *)handle->load_bias; 958 | info->dli_fname = handle->pathname; 959 | info->dli_sname = NULL; 960 | info->dli_saddr = 0; 961 | info->dli_ssize = 0; 962 | info->dlpi_phdr = handle->dlpi_phdr; 963 | info->dlpi_phnum = (size_t)handle->dlpi_phnum; 964 | 965 | // keep looking for: symbol name, symbol offset, symbol size 966 | if (!(flags & XDL_NON_SYM)) { 967 | ElfW(Sym) *sym; 968 | if (NULL != (sym = xdl_sym_by_addr((void *)handle, addr))) { 969 | info->dli_sname = handle->dynstr + sym->st_name; 970 | info->dli_saddr = (void *)(handle->load_bias + sym->st_value); 971 | info->dli_ssize = sym->st_size; 972 | } else if (NULL != (sym = xdl_dsym_by_addr((void *)handle, addr))) { 973 | info->dli_sname = handle->strtab + sym->st_name; 974 | info->dli_saddr = (void *)(handle->load_bias + sym->st_value); 975 | info->dli_ssize = sym->st_size; 976 | } 977 | } 978 | 979 | return 1; 980 | } 981 | 982 | void xdl_addr_clean(void **cache) { 983 | if (NULL == cache) return; 984 | 985 | xdl_t *handle = *((xdl_t **)cache); 986 | while (NULL != handle) { 987 | xdl_t *tmp = handle; 988 | handle = handle->next; 989 | xdl_close(tmp); 990 | } 991 | *cache = NULL; 992 | } 993 | 994 | int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags) { 995 | if (NULL == callback) return 0; 996 | 997 | return xdl_iterate_phdr_impl(callback, data, flags); 998 | } 999 | 1000 | int xdl_info(void *handle, int request, void *info) { 1001 | if (NULL == handle || XDL_DI_DLINFO != request || NULL == info) return -1; 1002 | 1003 | xdl_t *self = (xdl_t *)handle; 1004 | xdl_info_t *dlinfo = (xdl_info_t *)info; 1005 | 1006 | dlinfo->dli_fbase = (void *)self->load_bias; 1007 | dlinfo->dli_fname = self->pathname; 1008 | dlinfo->dli_sname = NULL; 1009 | dlinfo->dli_saddr = 0; 1010 | dlinfo->dli_ssize = 0; 1011 | dlinfo->dlpi_phdr = self->dlpi_phdr; 1012 | dlinfo->dlpi_phnum = (size_t)self->dlpi_phnum; 1013 | return 0; 1014 | } 1015 | -------------------------------------------------------------------------------- /xdl/src/main/cpp/xdl.map.txt: -------------------------------------------------------------------------------- 1 | { 2 | global: 3 | xdl_open; 4 | xdl_open2; 5 | xdl_close; 6 | xdl_sym; 7 | xdl_dsym; 8 | xdl_addr; 9 | xdl_addr4; 10 | xdl_addr_clean; 11 | xdl_iterate_phdr; 12 | xdl_info; 13 | 14 | local: 15 | *; 16 | }; 17 | -------------------------------------------------------------------------------- /xdl/src/main/cpp/xdl_iterate.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2024 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)) 225 | goto clean; 226 | 227 | if ('-' == exec && 0 == offset) { 228 | // r--p 229 | prev_base = base; 230 | line = (line == buf1 ? buf2 : buf1); 231 | try_next_line = true; 232 | continue; 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 | -------------------------------------------------------------------------------- /xdl/src/main/cpp/xdl_iterate.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2024 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_GITHUB_HEXHACKING_XDL_ITERATE 25 | #define IO_GITHUB_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 | -------------------------------------------------------------------------------- /xdl/src/main/cpp/xdl_linker.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2024 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 | #ifndef __LP64__ 42 | #define LIB "lib" 43 | #else 44 | #define LIB "lib64" 45 | #endif 46 | 47 | typedef void *(*xdl_linker_dlopen_n_t)(const char *, int, const void *, void *); 48 | typedef void *(*xdl_linker_dlopen_o_t)(const char *, int, const void *); 49 | 50 | static pthread_mutex_t *xdl_linker_mutex = NULL; 51 | static void *xdl_linker_dlopen = NULL; 52 | 53 | typedef enum { MATCH_PREFIX, MATCH_SUFFIX } xdl_linker_match_type_t; 54 | 55 | #pragma clang diagnostic push 56 | #pragma clang diagnostic ignored "-Wpadded" 57 | typedef struct { 58 | xdl_linker_match_type_t type; 59 | const char *value; 60 | } xdl_linker_match_t; 61 | #pragma clang diagnostic pop 62 | 63 | typedef struct { 64 | void *addr; 65 | xdl_linker_match_t *matches; 66 | size_t matches_cursor; 67 | } xdl_linker_caller_t; 68 | 69 | // https://source.android.com/docs/core/architecture/vndk/linker-namespace 70 | // The following rules are loose and incomplete, you can add more according to your needs. 71 | static xdl_linker_match_t xdl_linker_match_default[] = {{MATCH_SUFFIX, "/libc.so"}}; 72 | static xdl_linker_match_t xdl_linker_match_art[] = {{MATCH_SUFFIX, "/libart.so"}}; 73 | static xdl_linker_match_t xdl_linker_match_sphal[] = {{MATCH_PREFIX, "/vendor/" LIB "/egl/"}, 74 | {MATCH_PREFIX, "/vendor/" LIB "/hw/"}, 75 | {MATCH_PREFIX, "/vendor/" LIB "/"}, 76 | {MATCH_PREFIX, "/odm/" LIB "/"}}; 77 | static xdl_linker_match_t xdl_linker_match_vndk[] = {{MATCH_PREFIX, "/apex/com.android.vndk.v"}, 78 | {MATCH_PREFIX, "/vendor/" LIB "/vndk-sp/"}, 79 | {MATCH_PREFIX, "/odm/" LIB "/vndk-sp/"}}; 80 | static xdl_linker_caller_t xdl_linker_callers[] = { 81 | {NULL, xdl_linker_match_default, sizeof(xdl_linker_match_default) / sizeof(xdl_linker_match_t)}, 82 | {NULL, xdl_linker_match_art, sizeof(xdl_linker_match_art) / sizeof(xdl_linker_match_t)}, 83 | {NULL, xdl_linker_match_sphal, sizeof(xdl_linker_match_sphal) / sizeof(xdl_linker_match_t)}, 84 | {NULL, xdl_linker_match_vndk, sizeof(xdl_linker_match_vndk) / sizeof(xdl_linker_match_t)}}; 85 | 86 | static void xdl_linker_init_symbols_impl(void) { 87 | // find linker from: /proc/self/maps (API level < 18) or getauxval (API level >= 18) 88 | void *handle = xdl_open(XDL_UTIL_LINKER_BASENAME, XDL_DEFAULT); 89 | if (NULL == handle) return; 90 | 91 | int api_level = xdl_util_get_api_level(); 92 | if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) { 93 | // == Android 5.x 94 | xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL); 95 | } else if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) { 96 | // == Android 7.x 97 | xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_EXT_N, NULL); 98 | if (NULL == xdl_linker_dlopen) { 99 | xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DO_DLOPEN_N, NULL); 100 | xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL); 101 | } 102 | } else if (__ANDROID_API_O__ == api_level || __ANDROID_API_O_MR1__ == api_level) { 103 | // == Android 8.x 104 | xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_O, NULL); 105 | } else if (api_level >= __ANDROID_API_P__) { 106 | // >= Android 9.0 107 | xdl_linker_dlopen = xdl_sym(handle, XDL_LINKER_SYM_LOADER_DLOPEN_P, NULL); 108 | } 109 | 110 | xdl_close(handle); 111 | } 112 | 113 | static void xdl_linker_init_symbols(void) { 114 | static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 115 | static bool inited = false; 116 | if (!inited) { 117 | pthread_mutex_lock(&lock); 118 | if (!inited) { 119 | xdl_linker_init_symbols_impl(); 120 | inited = true; 121 | } 122 | pthread_mutex_unlock(&lock); 123 | } 124 | } 125 | 126 | void xdl_linker_lock(void) { 127 | xdl_linker_init_symbols(); 128 | 129 | if (NULL != xdl_linker_mutex) pthread_mutex_lock(xdl_linker_mutex); 130 | } 131 | 132 | void xdl_linker_unlock(void) { 133 | if (NULL != xdl_linker_mutex) pthread_mutex_unlock(xdl_linker_mutex); 134 | } 135 | 136 | static void *xdl_linker_get_caller_addr(struct dl_phdr_info *info) { 137 | for (size_t i = 0; i < info->dlpi_phnum; i++) { 138 | const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]); 139 | if (PT_LOAD == phdr->p_type) { 140 | return (void *)(info->dlpi_addr + phdr->p_vaddr); 141 | } 142 | } 143 | return NULL; 144 | } 145 | 146 | static void xdl_linker_save_caller_addr(struct dl_phdr_info *info, xdl_linker_caller_t *caller, 147 | size_t cursor) { 148 | void *addr = xdl_linker_get_caller_addr(info); 149 | if (NULL != addr) { 150 | caller->addr = addr; 151 | caller->matches_cursor = cursor; 152 | } 153 | } 154 | 155 | static int xdl_linker_get_caller_addr_cb(struct dl_phdr_info *info, size_t size, void *arg) { 156 | (void)size, (void)arg; 157 | if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0; // continue 158 | 159 | int ret = 1; // OK 160 | for (size_t i = 0; i < sizeof(xdl_linker_callers) / sizeof(xdl_linker_callers[0]); i++) { 161 | xdl_linker_caller_t *caller = &xdl_linker_callers[i]; 162 | for (size_t j = 0; j < caller->matches_cursor; j++) { 163 | xdl_linker_match_t *match = &caller->matches[j]; 164 | if (MATCH_PREFIX == match->type) { 165 | if (xdl_util_starts_with(info->dlpi_name, match->value)) { 166 | xdl_linker_save_caller_addr(info, caller, j); 167 | } 168 | } else if (MATCH_SUFFIX == match->type) { 169 | if (xdl_util_ends_with(info->dlpi_name, match->value)) { 170 | xdl_linker_save_caller_addr(info, caller, j); 171 | } 172 | } 173 | } 174 | if (NULL == caller->addr || 0 != caller->matches_cursor) ret = 0; // continue 175 | } 176 | return ret; 177 | } 178 | 179 | static void xdl_linker_init_caller_addr_impl(void) { 180 | xdl_iterate_phdr_impl(xdl_linker_get_caller_addr_cb, NULL, XDL_DEFAULT); 181 | } 182 | 183 | static void xdl_linker_init_caller_addr(void) { 184 | static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 185 | static bool inited = false; 186 | if (!inited) { 187 | pthread_mutex_lock(&lock); 188 | if (!inited) { 189 | xdl_linker_init_caller_addr_impl(); 190 | inited = true; 191 | } 192 | pthread_mutex_unlock(&lock); 193 | } 194 | } 195 | 196 | void *xdl_linker_force_dlopen(const char *filename) { 197 | int api_level = xdl_util_get_api_level(); 198 | 199 | if (api_level <= __ANDROID_API_M__) { 200 | // <= Android 6.0 201 | return dlopen(filename, RTLD_NOW); 202 | } else { 203 | xdl_linker_init_symbols(); 204 | if (NULL == xdl_linker_dlopen) return NULL; 205 | xdl_linker_init_caller_addr(); 206 | 207 | void *handle = NULL; 208 | if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) { 209 | // == Android 7.x 210 | xdl_linker_lock(); 211 | for (size_t i = 0; i < sizeof(xdl_linker_callers) / sizeof(xdl_linker_callers[0]); i++) { 212 | xdl_linker_caller_t *caller = &xdl_linker_callers[i]; 213 | if (NULL != caller->addr) { 214 | handle = ((xdl_linker_dlopen_n_t)xdl_linker_dlopen)(filename, RTLD_NOW, NULL, caller->addr); 215 | if (NULL != handle) break; 216 | } 217 | } 218 | xdl_linker_unlock(); 219 | } else { 220 | // >= Android 8.0 221 | for (size_t i = 0; i < sizeof(xdl_linker_callers) / sizeof(xdl_linker_callers[0]); i++) { 222 | xdl_linker_caller_t *caller = &xdl_linker_callers[i]; 223 | if (NULL != caller->addr) { 224 | handle = ((xdl_linker_dlopen_o_t)xdl_linker_dlopen)(filename, RTLD_NOW, caller->addr); 225 | if (NULL != handle) break; 226 | } 227 | } 228 | } 229 | return handle; 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /xdl/src/main/cpp/xdl_linker.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2024 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_GITHUB_HEXHACKING_XDL_LINKER 25 | #define IO_GITHUB_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_force_dlopen(const char *filename); 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /xdl/src/main/cpp/xdl_lzma.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2024 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 | #include 35 | 36 | #include "xdl.h" 37 | #include "xdl_util.h" 38 | 39 | // LZMA library pathname & symbol names 40 | #ifndef __LP64__ 41 | #define XDL_LZMA_PATHNAME "/system/lib/liblzma.so" 42 | #else 43 | #define XDL_LZMA_PATHNAME "/system/lib64/liblzma.so" 44 | #endif 45 | #define XDL_LZMA_SYM_CRCGEN "CrcGenerateTable" 46 | #define XDL_LZMA_SYM_CRC64GEN "Crc64GenerateTable" 47 | #define XDL_LZMA_SYM_CONSTRUCT "XzUnpacker_Construct" 48 | #define XDL_LZMA_SYM_ISFINISHED "XzUnpacker_IsStreamWasFinished" 49 | #define XDL_LZMA_SYM_FREE "XzUnpacker_Free" 50 | #define XDL_LZMA_SYM_CODE "XzUnpacker_Code" 51 | 52 | // LZMA data type definition 53 | #define SZ_OK 0 54 | typedef struct ISzAlloc ISzAlloc; 55 | typedef const ISzAlloc *ISzAllocPtr; 56 | struct ISzAlloc { 57 | void *(*Alloc)(ISzAllocPtr p, size_t size); 58 | void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */ 59 | }; 60 | typedef enum { 61 | CODER_STATUS_NOT_SPECIFIED, /* use main error code instead */ 62 | CODER_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ 63 | CODER_STATUS_NOT_FINISHED, /* stream was not finished */ 64 | CODER_STATUS_NEEDS_MORE_INPUT /* you must provide more input bytes */ 65 | } ECoderStatus; 66 | typedef enum { 67 | CODER_FINISH_ANY, /* finish at any point */ 68 | CODER_FINISH_END /* block must be finished at the end */ 69 | } ECoderFinishMode; 70 | 71 | // LZMA function type definition 72 | typedef void (*xdl_lzma_crcgen_t)(void); 73 | typedef void (*xdl_lzma_crc64gen_t)(void); 74 | typedef void (*xdl_lzma_construct_t)(void *, ISzAllocPtr); 75 | typedef int (*xdl_lzma_isfinished_t)(const void *); 76 | typedef void (*xdl_lzma_free_t)(void *); 77 | typedef int (*xdl_lzma_code_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, ECoderFinishMode, 78 | ECoderStatus *); 79 | typedef int (*xdl_lzma_code_q_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, int, 80 | ECoderFinishMode, ECoderStatus *); 81 | 82 | // LZMA function pointor 83 | static xdl_lzma_construct_t xdl_lzma_construct = NULL; 84 | static xdl_lzma_isfinished_t xdl_lzma_isfinished = NULL; 85 | static xdl_lzma_free_t xdl_lzma_free = NULL; 86 | static void *xdl_lzma_code = NULL; 87 | 88 | // LZMA init 89 | static void xdl_lzma_init(void) { 90 | void *lzma = xdl_open(XDL_LZMA_PATHNAME, XDL_TRY_FORCE_LOAD); 91 | if (NULL == lzma) return; 92 | 93 | xdl_lzma_crcgen_t crcgen = NULL; 94 | xdl_lzma_crc64gen_t crc64gen = NULL; 95 | if (NULL == (crcgen = (xdl_lzma_crcgen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRCGEN, NULL))) goto end; 96 | if (NULL == (crc64gen = (xdl_lzma_crc64gen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRC64GEN, NULL))) goto end; 97 | if (NULL == (xdl_lzma_construct = (xdl_lzma_construct_t)xdl_sym(lzma, XDL_LZMA_SYM_CONSTRUCT, NULL))) 98 | goto end; 99 | if (NULL == (xdl_lzma_isfinished = (xdl_lzma_isfinished_t)xdl_sym(lzma, XDL_LZMA_SYM_ISFINISHED, NULL))) 100 | goto end; 101 | if (NULL == (xdl_lzma_free = (xdl_lzma_free_t)xdl_sym(lzma, XDL_LZMA_SYM_FREE, NULL))) goto end; 102 | if (NULL == (xdl_lzma_code = xdl_sym(lzma, XDL_LZMA_SYM_CODE, NULL))) goto end; 103 | crcgen(); 104 | crc64gen(); 105 | 106 | end: 107 | xdl_close(lzma); 108 | } 109 | 110 | // LZMA internal alloc / free 111 | static void *xdl_lzma_internal_alloc(ISzAllocPtr p, size_t size) { 112 | (void)p; 113 | return malloc(size); 114 | } 115 | static void xdl_lzma_internal_free(ISzAllocPtr p, void *address) { 116 | (void)p; 117 | free(address); 118 | } 119 | 120 | int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size) { 121 | size_t src_offset = 0; 122 | size_t dst_offset = 0; 123 | size_t src_remaining; 124 | size_t dst_remaining; 125 | ISzAlloc alloc = {.Alloc = xdl_lzma_internal_alloc, .Free = xdl_lzma_internal_free}; 126 | long long state[4096 / sizeof(long long)]; // must be enough, 8-bit aligned 127 | ECoderStatus status; 128 | int api_level = xdl_util_get_api_level(); 129 | 130 | // init and check 131 | static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 132 | static bool inited = false; 133 | if (!inited) { 134 | pthread_mutex_lock(&lock); 135 | if (!inited) { 136 | xdl_lzma_init(); 137 | inited = true; 138 | } 139 | pthread_mutex_unlock(&lock); 140 | } 141 | if (NULL == xdl_lzma_code) return -1; 142 | 143 | xdl_lzma_construct(&state, &alloc); 144 | 145 | *dst_size = 2 * src_size; 146 | *dst = NULL; 147 | do { 148 | *dst_size *= 2; 149 | if (NULL == (*dst = realloc(*dst, *dst_size))) { 150 | xdl_lzma_free(&state); 151 | return -1; 152 | } 153 | 154 | src_remaining = src_size - src_offset; 155 | dst_remaining = *dst_size - dst_offset; 156 | 157 | int result; 158 | if (api_level >= __ANDROID_API_Q__) { 159 | xdl_lzma_code_q_t lzma_code_q = (xdl_lzma_code_q_t)xdl_lzma_code; 160 | result = lzma_code_q(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining, 1, 161 | CODER_FINISH_ANY, &status); 162 | } else { 163 | xdl_lzma_code_t lzma_code = (xdl_lzma_code_t)xdl_lzma_code; 164 | result = lzma_code(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining, 165 | CODER_FINISH_ANY, &status); 166 | } 167 | if (SZ_OK != result) { 168 | free(*dst); 169 | xdl_lzma_free(&state); 170 | return -1; 171 | } 172 | 173 | src_offset += src_remaining; 174 | dst_offset += dst_remaining; 175 | } while (status == CODER_STATUS_NOT_FINISHED); 176 | 177 | xdl_lzma_free(&state); 178 | 179 | if (!xdl_lzma_isfinished(&state)) { 180 | free(*dst); 181 | return -1; 182 | } 183 | 184 | *dst_size = dst_offset; 185 | *dst = realloc(*dst, *dst_size); 186 | return 0; 187 | } 188 | -------------------------------------------------------------------------------- /xdl/src/main/cpp/xdl_lzma.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2024 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_GITHUB_HEXHACKING_XDL_LZMA 25 | #define IO_GITHUB_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 | -------------------------------------------------------------------------------- /xdl/src/main/cpp/xdl_util.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2024 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 | -------------------------------------------------------------------------------- /xdl/src/main/cpp/xdl_util.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2024 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_GITHUB_HEXHACKING_XDL_UTIL 25 | #define IO_GITHUB_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 | -------------------------------------------------------------------------------- /xdl_sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /xdl_sample/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | namespace 'io.github.hexhacking.xdl.sample' 7 | compileSdk rootProject.ext.compileSdkVersion 8 | buildToolsVersion = rootProject.ext.buildToolsVersion 9 | ndkVersion rootProject.ext.ndkVersion 10 | defaultConfig { 11 | applicationId "io.github.hexhacking.xdl.sample" 12 | minSdkVersion rootProject.ext.minSdkVersion 13 | targetSdkVersion rootProject.ext.targetSdkVersion 14 | versionCode 1 15 | versionName "1.0" 16 | ndk { 17 | abiFilters rootProject.ext.abiFilters.split(",") 18 | } 19 | externalNativeBuild { 20 | cmake { 21 | abiFilters rootProject.ext.abiFilters.split(",") 22 | if(rootProject.ext.dependencyOnLocalLibrary) { 23 | arguments "-DDEPENDENCY_ON_LOCAL_LIBRARY=ON" 24 | } 25 | } 26 | } 27 | } 28 | externalNativeBuild { 29 | cmake { 30 | path "src/main/cpp/CMakeLists.txt" 31 | version rootProject.ext.cmakeVersion 32 | } 33 | // ndkBuild { 34 | // path "src/main/cpp/Android.mk" 35 | // } 36 | } 37 | compileOptions { 38 | sourceCompatibility rootProject.ext.javaVersion 39 | targetCompatibility rootProject.ext.javaVersion 40 | } 41 | buildTypes { 42 | debug { 43 | minifyEnabled false 44 | } 45 | release { 46 | minifyEnabled false 47 | } 48 | } 49 | packagingOptions { 50 | jniLibs { 51 | pickFirsts += ['**/libxdl.so'] 52 | } 53 | if (rootProject.ext.useASAN) { 54 | doNotStrip "**/*.so" 55 | } 56 | } 57 | buildFeatures { 58 | prefab true 59 | } 60 | } 61 | 62 | dependencies { 63 | implementation 'androidx.appcompat:appcompat:1.6.1' 64 | implementation 'com.google.android.material:material:1.11.0' 65 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 66 | 67 | if (rootProject.ext.dependencyOnLocalLibrary) { 68 | implementation project(':xdl') 69 | } else { 70 | implementation 'io.github.hexhacking:xdl:' + rootProject.ext.xdlVersion 71 | } 72 | } 73 | 74 | apply from: rootProject.file('gradle/sanitizer.gradle') 75 | -------------------------------------------------------------------------------- /xdl_sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /xdl_sample/src/main/cpp/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := sample 5 | LOCAL_SRC_FILES := sample.c 6 | LOCAL_CFLAGS := -Weverything -Werror 7 | LOCAL_CONLYFLAGS := -std=c17 8 | LOCAL_LDLIBS := -llog 9 | LOCAL_SHARED_LIBRARIES += xdl 10 | include $(BUILD_SHARED_LIBRARY) 11 | 12 | #import xdl by maven 13 | $(call import-module,prefab/xdl) 14 | -------------------------------------------------------------------------------- /xdl_sample/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22.1) 2 | project(sample) 3 | 4 | if(DEPENDENCY_ON_LOCAL_LIBRARY) 5 | include(${CMAKE_CURRENT_SOURCE_DIR}/../../../../xdl/local_dependency.cmake) 6 | else() 7 | find_package(xdl REQUIRED CONFIG) 8 | endif() 9 | 10 | add_library(sample SHARED sample.c) 11 | target_compile_features(sample PUBLIC c_std_17) 12 | target_compile_options(sample PUBLIC -std=c17 -Weverything -Werror) 13 | target_link_libraries(sample xdl::xdl log) 14 | 15 | if((${ANDROID_ABI} STREQUAL "arm64-v8a") OR (${ANDROID_ABI} STREQUAL "x86_64")) 16 | target_link_options(sample PUBLIC "-Wl,-z,max-page-size=16384") 17 | endif() 18 | -------------------------------------------------------------------------------- /xdl_sample/src/main/cpp/sample.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "xdl.h" 14 | 15 | #define SAMPLE_JNI_VERSION JNI_VERSION_1_6 16 | #define SAMPLE_JNI_CLASS_NAME "io/github/hexhacking/xdl/sample/NativeSample" 17 | 18 | // log 19 | #pragma clang diagnostic push 20 | #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" 21 | #define LOG(fmt, ...) __android_log_print(ANDROID_LOG_INFO, "xdl_tag", fmt, ##__VA_ARGS__) 22 | #pragma clang diagnostic pop 23 | #define LOG_END "*** --------------------------------------------------------------" 24 | 25 | #if defined(__LP64__) 26 | #define BASENAME_LINKER "linker64" 27 | #define BASENAME_APP_PROCESS "app_process64" 28 | #define PATHNAME_LIBC "/system/lib64/libc.so" 29 | #define PATHNAME_LIBC_Q "/apex/com.android.runtime/lib64/bionic/libc.so" 30 | #define PATHNAME_LIBCPP "/system/lib64/libc++.so" 31 | #else 32 | #define BASENAME_LINKER "linker" 33 | #define BASENAME_APP_PROCESS "app_process32" 34 | #define PATHNAME_LIBC "/system/lib/libc.so" 35 | #define PATHNAME_LIBC_Q "/apex/com.android.runtime/lib/bionic/libc.so" 36 | #define PATHNAME_LIBCPP "/system/lib/libc++.so" 37 | #endif 38 | #define BASENAME_VDSO "[vdso]" 39 | #define BASENAME_LIBNETUTILS "libnetutils.so" 40 | #define BASENAME_LIBCAP "libcap.so" 41 | #define BASENAME_LIBOPENCL "libOpenCL.so" 42 | #define BASENAME_LIBART "libart.so" 43 | 44 | #define PATHNAME_LIBC_FIXED \ 45 | (android_get_device_api_level() < __ANDROID_API_Q__ ? PATHNAME_LIBC : PATHNAME_LIBC_Q) 46 | 47 | static int callback(struct dl_phdr_info *info, size_t size, void *arg) { 48 | (void)size, (void)arg; 49 | 50 | LOG(">>> %" PRIxPTR " %s (phdr: %" PRIxPTR ", phnum: %zu)", (uintptr_t)info->dlpi_addr, info->dlpi_name, 51 | (uintptr_t)info->dlpi_phdr, (size_t)info->dlpi_phnum); 52 | return 0; 53 | } 54 | 55 | static void sample_test_iterate(void) { 56 | LOG("+++ xdl_iterate_phdr(XDL_DEFAULT)"); 57 | xdl_iterate_phdr(callback, NULL, XDL_DEFAULT); 58 | LOG(LOG_END); 59 | usleep(100 * 1000); 60 | 61 | LOG("+++ xdl_iterate_phdr(XDL_FULL_PATHNAME)"); 62 | xdl_iterate_phdr(callback, NULL, XDL_FULL_PATHNAME); 63 | LOG(LOG_END); 64 | usleep(100 * 1000); 65 | } 66 | 67 | static void *sample_test_dlsym(const char *filename, const char *symbol, bool debug_symbol, void **cache, 68 | bool try_force_dlopen) { 69 | xdl_info_t info; 70 | 71 | if (try_force_dlopen) { 72 | void *linker_handle = dlopen(filename, RTLD_NOW); 73 | LOG("--- dlopen(%s) : handle %" PRIxPTR, filename, (uintptr_t)linker_handle); 74 | if (NULL != linker_handle) dlclose(linker_handle); 75 | } 76 | 77 | LOG("+++ xdl_open + xdl_info + %s + xdl_addr", debug_symbol ? "xdl_dsym" : "xdl_sym"); 78 | 79 | // xdl_open 80 | void *handle = xdl_open(filename, try_force_dlopen ? XDL_TRY_FORCE_LOAD : XDL_DEFAULT); 81 | LOG(">>> xdl_open(%s) : handle %" PRIxPTR, filename, (uintptr_t)handle); 82 | 83 | // xdl_info(XDL_DI_DLINFO) 84 | memset(&info, 0, sizeof(xdl_info_t)); 85 | if (0 > xdl_info(handle, XDL_DI_DLINFO, &info)) 86 | LOG(">>> xdl_info(XDL_DI_DLINFO, %" PRIxPTR ") : FAILED", (uintptr_t)handle); 87 | else 88 | LOG(">>> xdl_info(XDL_DI_DLINFO, %" PRIxPTR ") : %" PRIxPTR " %s (phdr %" PRIxPTR ", phnum %zu)", 89 | (uintptr_t)handle, (uintptr_t)info.dli_fbase, (NULL == info.dli_fname ? "(NULL)" : info.dli_fname), 90 | (uintptr_t)info.dlpi_phdr, info.dlpi_phnum); 91 | 92 | // xdl_dsym / xdl_sym 93 | size_t symbol_size = 0; 94 | void *symbol_addr = (debug_symbol ? xdl_dsym : xdl_sym)(handle, symbol, &symbol_size); 95 | LOG(">>> %s(%s) : addr %" PRIxPTR ", sz %zu", debug_symbol ? "xdl_dsym" : "xdl_sym", symbol, 96 | (uintptr_t)symbol_addr, symbol_size); 97 | 98 | // xdl_close 99 | void *linker_handle = xdl_close(handle); 100 | 101 | // xdl_addr 102 | memset(&info, 0, sizeof(xdl_info_t)); 103 | if (0 == xdl_addr(symbol_addr, &info, cache)) 104 | LOG(">>> xdl_addr(%" PRIxPTR ") : FAILED", (uintptr_t)symbol_addr); 105 | else 106 | LOG(">>> xdl_addr(%" PRIxPTR ") : %" PRIxPTR " %s (phdr %" PRIxPTR ", phnum %zu), %" PRIxPTR 107 | " %s (sz %zu)", 108 | (uintptr_t)symbol_addr, (uintptr_t)info.dli_fbase, 109 | (NULL == info.dli_fname ? "(NULL)" : info.dli_fname), (uintptr_t)info.dlpi_phdr, info.dlpi_phnum, 110 | (uintptr_t)info.dli_saddr, (NULL == info.dli_sname ? "(NULL)" : info.dli_sname), info.dli_ssize); 111 | 112 | // xdl_addr4(XDL_NON_SYM) 113 | memset(&info, 0, sizeof(xdl_info_t)); 114 | if (0 == xdl_addr4(symbol_addr, &info, cache, XDL_NON_SYM)) 115 | LOG(">>> xdl_addr4(XDL_NON_SYM, %" PRIxPTR ") : FAILED", (uintptr_t)symbol_addr); 116 | else 117 | LOG(">>> xdl_addr4(XDL_NON_SYM, %" PRIxPTR ") : %" PRIxPTR " %s (phdr %" PRIxPTR ", phnum %zu), %" PRIxPTR 118 | " %s (sz %zu)", 119 | (uintptr_t)symbol_addr, (uintptr_t)info.dli_fbase, 120 | (NULL == info.dli_fname ? "(NULL)" : info.dli_fname), (uintptr_t)info.dlpi_phdr, info.dlpi_phnum, 121 | (uintptr_t)info.dli_saddr, (NULL == info.dli_sname ? "(NULL)" : info.dli_sname), info.dli_ssize); 122 | 123 | LOG(LOG_END); 124 | 125 | return linker_handle; 126 | } 127 | 128 | static void sample_test(JNIEnv *env, jobject thiz) { 129 | (void)env; 130 | (void)thiz; 131 | 132 | // cache for xdl_addr() 133 | void *cache = NULL; 134 | 135 | // iterate test 136 | sample_test_iterate(); 137 | 138 | // linker 139 | sample_test_dlsym(BASENAME_LINKER, "__dl__ZL10g_dl_mutex", true, &cache, false); 140 | 141 | // app_process 142 | sample_test_dlsym(BASENAME_APP_PROCESS, "sigaction", false, &cache, false); 143 | 144 | // vDSO 145 | sample_test_dlsym(BASENAME_VDSO, "__kernel_rt_sigreturn", false, &cache, false); 146 | 147 | // libc.so 148 | sample_test_dlsym(PATHNAME_LIBC_FIXED, "android_set_abort_message", false, &cache, false); 149 | sample_test_dlsym(PATHNAME_LIBC_FIXED, "je_mallctl", true, &cache, false); 150 | sample_test_dlsym("libc.so", "memmove", false, &cache, false); 151 | 152 | // libc++.so 153 | sample_test_dlsym(PATHNAME_LIBCPP, "_ZNSt3__14cerrE", false, &cache, false); 154 | sample_test_dlsym(PATHNAME_LIBCPP, "abort_message", true, &cache, false); 155 | 156 | // libart.so 157 | sample_test_dlsym(BASENAME_LIBART, "_ZN3artL16FindOatMethodForEPNS_9ArtMethodENS_11PointerSizeEPb", true, 158 | &cache, false); 159 | 160 | // libnetutils.so (may need to be loaded from disk into memory) 161 | void *linker_handle_libnetutils = 162 | sample_test_dlsym(BASENAME_LIBNETUTILS, "ifc_get_hwaddr", false, &cache, true); 163 | 164 | // libcap.so (may need to be loaded from disk into memory) 165 | void *linker_handle_libcap = sample_test_dlsym(BASENAME_LIBCAP, "cap_dup", false, &cache, true); 166 | 167 | // libOpenCL.so (may need to be loaded from disk into memory) 168 | void *linker_handle_libOpenCL = 169 | sample_test_dlsym(BASENAME_LIBOPENCL, "clCreateContext", false, &cache, true); 170 | 171 | // clean cache for xdl_addr() 172 | xdl_addr_clean(&cache); 173 | 174 | // dlclose (may need to be unloaded from memory) 175 | if (NULL != linker_handle_libnetutils) { 176 | LOG("--- dlclose(%s) : linker_handle %" PRIxPTR, BASENAME_LIBNETUTILS, 177 | (uintptr_t)linker_handle_libnetutils); 178 | dlclose(linker_handle_libnetutils); 179 | } 180 | if (NULL != linker_handle_libcap) { 181 | LOG("--- dlclose(%s) : linker_handle %" PRIxPTR, BASENAME_LIBCAP, (uintptr_t)linker_handle_libcap); 182 | dlclose(linker_handle_libcap); 183 | } 184 | if (NULL != linker_handle_libOpenCL) { 185 | LOG("--- dlclose(%s) : linker_handle %" PRIxPTR, BASENAME_LIBOPENCL, (uintptr_t)linker_handle_libOpenCL); 186 | dlclose(linker_handle_libOpenCL); 187 | } 188 | } 189 | 190 | static JNINativeMethod sample_jni_methods[] = {{"nativeTest", "()V", (void *)sample_test}}; 191 | 192 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { 193 | JNIEnv *env; 194 | jclass cls; 195 | 196 | (void)reserved; 197 | 198 | if (NULL == vm) return JNI_ERR; 199 | if (JNI_OK != (*vm)->GetEnv(vm, (void **)&env, SAMPLE_JNI_VERSION)) return JNI_ERR; 200 | if (NULL == env || NULL == *env) return JNI_ERR; 201 | if (NULL == (cls = (*env)->FindClass(env, SAMPLE_JNI_CLASS_NAME))) return JNI_ERR; 202 | if (0 != (*env)->RegisterNatives(env, cls, sample_jni_methods, 203 | sizeof(sample_jni_methods) / sizeof(sample_jni_methods[0]))) 204 | return JNI_ERR; 205 | 206 | return SAMPLE_JNI_VERSION; 207 | } 208 | -------------------------------------------------------------------------------- /xdl_sample/src/main/java/io/github/hexhacking/xdl/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package io.github.hexhacking.xdl.sample; 2 | 3 | import androidx.activity.EdgeToEdge; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | 6 | import android.os.Bundle; 7 | import android.util.Log; 8 | import android.view.View; 9 | 10 | public class MainActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | EdgeToEdge.enable(this); 16 | setContentView(R.layout.activity_main); 17 | 18 | findViewById(R.id.testButton).setOnClickListener(new View.OnClickListener() { 19 | public void onClick(View v) { 20 | NativeSample.test(); 21 | } 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /xdl_sample/src/main/java/io/github/hexhacking/xdl/sample/MyCustomApplication.java: -------------------------------------------------------------------------------- 1 | package io.github.hexhacking.xdl.sample; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.util.Log; 6 | 7 | public class MyCustomApplication extends Application { 8 | 9 | @Override 10 | protected void attachBaseContext(Context base) { 11 | super.attachBaseContext(base); 12 | 13 | System.loadLibrary("xdl"); 14 | System.loadLibrary("sample"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /xdl_sample/src/main/java/io/github/hexhacking/xdl/sample/NativeSample.java: -------------------------------------------------------------------------------- 1 | package io.github.hexhacking.xdl.sample; 2 | 3 | public class NativeSample { 4 | public static void test() { 5 | nativeTest(); 6 | } 7 | 8 | private static native void nativeTest(); 9 | } 10 | -------------------------------------------------------------------------------- /xdl_sample/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /xdl_sample/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /xdl_sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 |