Repository: tiann/epic
Branch: master
Commit: c67d0d3e3769
Files: 137
Total size: 349.2 KB
Directory structure:
gitextract_6vz_tr23/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ └── android.yml
├── .gitignore
├── LICENSE
├── README.md
├── README_cn.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── me/
│ │ └── weishu/
│ │ └── epic/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── me/
│ │ │ └── weishu/
│ │ │ └── epic/
│ │ │ └── samples/
│ │ │ ├── MainActivity.java
│ │ │ ├── MainApplication.java
│ │ │ └── tests/
│ │ │ ├── CallingConventationTest.java
│ │ │ ├── LogMethodHook.java
│ │ │ ├── TestCase.java
│ │ │ ├── TestManager.java
│ │ │ ├── TestSuite.java
│ │ │ ├── arguments/
│ │ │ │ ├── AbsArgStaticCase.java
│ │ │ │ ├── ArgStatic0.java
│ │ │ │ ├── ArgStatic4.java
│ │ │ │ ├── ArgStatic44.java
│ │ │ │ ├── ArgStatic444.java
│ │ │ │ ├── ArgStatic4444.java
│ │ │ │ ├── ArgStatic4448.java
│ │ │ │ ├── ArgStatic448.java
│ │ │ │ ├── ArgStatic4484.java
│ │ │ │ ├── ArgStatic4488.java
│ │ │ │ ├── ArgStatic48.java
│ │ │ │ ├── ArgStatic484.java
│ │ │ │ ├── ArgStatic4844.java
│ │ │ │ ├── ArgStatic4848.java
│ │ │ │ ├── ArgStatic488.java
│ │ │ │ ├── ArgStatic4884.java
│ │ │ │ ├── ArgStatic4888.java
│ │ │ │ ├── ArgStatic8.java
│ │ │ │ ├── ArgStatic84.java
│ │ │ │ ├── ArgStatic844.java
│ │ │ │ ├── ArgStatic8444.java
│ │ │ │ ├── ArgStatic8448.java
│ │ │ │ ├── ArgStatic848.java
│ │ │ │ ├── ArgStatic8484.java
│ │ │ │ ├── ArgStatic8488.java
│ │ │ │ ├── ArgStatic88.java
│ │ │ │ ├── ArgStatic884.java
│ │ │ │ ├── ArgStatic8844.java
│ │ │ │ ├── ArgStatic8848.java
│ │ │ │ ├── ArgStatic888.java
│ │ │ │ ├── ArgStatic8884.java
│ │ │ │ ├── ArgStatic8888.java
│ │ │ │ └── ArgumentTarget.java
│ │ │ ├── custom/
│ │ │ │ ├── Case.java
│ │ │ │ ├── Case1.java
│ │ │ │ ├── Case10_Default_Constructor.java
│ │ │ │ ├── Case11_SuspendAll.java
│ │ │ │ ├── Case12_MultiCallback.java
│ │ │ │ ├── Case13_FastNative.java
│ │ │ │ ├── Case14_GC.java
│ │ │ │ ├── Case15_StopJit.java
│ │ │ │ ├── Case16_SameEntry.java
│ │ │ │ ├── Case17_SameMethod.java
│ │ │ │ ├── Case18_returnConst.java
│ │ │ │ ├── Case2.java
│ │ │ │ ├── Case3.java
│ │ │ │ ├── Case4.java
│ │ │ │ ├── Case5.java
│ │ │ │ ├── Case6.java
│ │ │ │ ├── Case7.java
│ │ │ │ ├── Case8_Activity_onCreate.java
│ │ │ │ ├── Case9_ThreadMonitor.java
│ │ │ │ ├── CaseManager.java
│ │ │ │ └── Target.java
│ │ │ ├── invoketype/
│ │ │ │ ├── InvokeConstructor.java
│ │ │ │ └── InvokeTypeTarget.java
│ │ │ └── returntype/
│ │ │ ├── BooleanType.java
│ │ │ ├── ByteType.java
│ │ │ ├── CharType.java
│ │ │ ├── CustomType.java
│ │ │ ├── DoubleType.java
│ │ │ ├── FloatType.java
│ │ │ ├── IntType.java
│ │ │ ├── LongType.java
│ │ │ ├── ReturnTypeTarget.java
│ │ │ ├── ShortType.java
│ │ │ ├── StringArrayType.java
│ │ │ ├── StringType.java
│ │ │ └── VoidType.java
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── child_layout.xml
│ │ │ └── parent_layout.xml
│ │ └── values/
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test/
│ └── java/
│ └── me/
│ └── weishu/
│ └── epic/
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── library/
│ ├── .gitignore
│ ├── CMakeCache.txt
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── cpp/
│ │ ├── .gitignore
│ │ ├── CMakeLists.txt
│ │ ├── art.cpp
│ │ ├── art.h
│ │ ├── build_with_cmake
│ │ ├── epic.cpp
│ │ ├── fake_dlfcn.cpp
│ │ └── fake_dlfcn.h
│ ├── java/
│ │ ├── com/
│ │ │ └── taobao/
│ │ │ └── android/
│ │ │ └── dexposed/
│ │ │ ├── ClassUtils.java
│ │ │ ├── DeviceCheck.java
│ │ │ └── utility/
│ │ │ ├── Debug.java
│ │ │ ├── Logger.java
│ │ │ ├── NeverCalled.java
│ │ │ ├── NougatPolicy.java
│ │ │ ├── Platform.java
│ │ │ ├── Runtime.java
│ │ │ └── Unsafe.java
│ │ ├── de/
│ │ │ └── robv/
│ │ │ └── android/
│ │ │ └── xposed/
│ │ │ └── DexposedBridge.java
│ │ └── me/
│ │ └── weishu/
│ │ └── epic/
│ │ └── art/
│ │ ├── Epic.java
│ │ ├── EpicNative.java
│ │ ├── Trampoline.java
│ │ ├── arch/
│ │ │ ├── Arm64.java
│ │ │ ├── Arm64_2.java
│ │ │ ├── ShellCode.java
│ │ │ └── Thumb2.java
│ │ ├── entry/
│ │ │ ├── Entry.java
│ │ │ ├── Entry64.java
│ │ │ └── Entry64_2.java
│ │ └── method/
│ │ ├── ArtMethod.java
│ │ └── Offset.java
│ └── res/
│ └── values/
│ └── strings.xml
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: # replace
patreon: weishu
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: http://paypal.me/virtualxposed
================================================
FILE: .github/workflows/android.yml
================================================
name: Android CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Gradle
run: ./gradlew build
================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild
.idea
================================================
FILE: LICENSE
================================================
Original work Copyright (c) 2005-2008, The Android Open Source Project
Modified work Copyright (c) 2013, rovo89 and Tungstwenty
Modified work Copyright (c) 2015, Alibaba Mobile Infrastructure (Android) Team
Modified work Copyright (c) 2017, weishu
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
================================================
FILE: README.md
================================================
[ ](https://bintray.com/twsxtd/maven/epic/_latestVersion)
[](https://gitter.im/android-hacker/epic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[中文文档入口](README_cn.md "中文")
What is it?
-----------
Epic is the continuation of [Dexposed](https://github.com/alibaba/dexposed) on ART (Supports 5.0 ~ 11).
> Dexposed is a powerful yet non-invasive runtime [AOP (Aspect-oriented Programming)](http://en.wikipedia.org/wiki/Aspect-oriented_programming) framework
for Android app development, based on the work of open-source [Xposed](https://github.com/rovo89/Xposed) [framework](https://github.com/rovo89/XposedBridge) project.
>
> The AOP of Dexposed is implemented purely non-invasive, without any annotation processor,
weaver or bytecode rewriter. The integration is as simple as loading a small JNI library
in just one line of code at the initialization phase of your app.
>
> Not only the code of your app, but also the code of Android framework that running in your
app process can be hooked.
Epic keeps the same API and all capability of Dexposed, you can do anything which is supported by Dexposed.
Typical use-cases
-----------------
* Classic AOP programming
* Instrumentation (for testing, performance monitoring and etc.)
* Security audit (sensitive api check,Smash shell)
* Just for fun :)
Integration
-----------
Directly add epic aar to your project as compile libraries, Gradle dependency like following(jitpack):
```groovy
dependencies {
compile 'com.github.tiann:epic:0.11.2'
}
```
Everything is ready.
Basic usage
-----------
There are three injection points for a given method: *before*, *after*, *origin*.
Example 1: monitor the creation and destroy of java thread
```java
class ThreadMethodHook extends XC_MethodHook{
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Thread t = (Thread) param.thisObject;
Log.i(TAG, "thread:" + t + ", started..");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Thread t = (Thread) param.thisObject;
Log.i(TAG, "thread:" + t + ", exit..");
}
}
DexposedBridge.hookAllConstructors(Thread.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Thread thread = (Thread) param.thisObject;
Class> clazz = thread.getClass();
if (clazz != Thread.class) {
Log.d(TAG, "found class extend Thread:" + clazz);
DexposedBridge.findAndHookMethod(clazz, "run", new ThreadMethodHook());
}
Log.d(TAG, "Thread: " + thread.getName() + " class:" + thread.getClass() + " is created.");
}
});
DexposedBridge.findAndHookMethod(Thread.class, "run", new ThreadMethodHook());
```
Example 2: Intercept the dex loading behavior
```java
DexposedBridge.findAndHookMethod(DexFile.class, "loadDex", String.class, String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
String dex = (String) param.args[0];
String odex = (String) param.args[1];
Log.i(TAG, "load dex, input:" + dex + ", output:" + odex);
}
});
```
Checkout the `sample` project to find out more.
Support
----------
Epic supports ART thumb2 and arm64 architecture from Android 5.0 ~ 11. arm32, x86, x86_64 and mips are not supported now (Thus it cannot work on android emulator).
Known Issues
-------------
1. Short method (instruction less 8 bytes on thumb2 or less 16bytes in ARM64) are not supported.
2. Fully inline methods are not supported.
Contribute
----------
We are open to constructive contributions from the community, especially pull request
and quality bug report. **Currently, the implementation for ART is not proved in large scale, we value your help to test or improve the implementation.**
You can clone this project, build and install the sample app, just make some click in your device, if some bugs/crash occurs, please file an issue or a pull request, I would appreciate it :)
Thanks
-------
1. [Dexposed](https://github.com/alibaba/dexposed)
2. [Xposed](http://repo.xposed.info/module/de.robv.android.xposed.installer)
3. [mar-v-in/ArtHook](https://github.com/mar-v-in/ArtHook)
4. [Nougat_dlfunctions](https://github.com/avs333/Nougat_dlfunctions.git)
Contact me
----------
twsxtd@gmail.com
[Join discussion](https://gitter.im/android-hacker/epic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
================================================
FILE: README_cn.md
================================================
## 简介
Epic 是一个在虚拟机层面、以 Java Method 为粒度的 **运行时** AOP Hook 框架。简单来说,Epic 就是 ART 上的 [Dexposed](https://github.com/alibaba/dexposed)(支持 Android 5.0 ~ 11)。它可以拦截本进程内部几乎任意的 Java 方法调用,可用于实现 AOP 编程、运行时插桩、性能分析、安全审计等。
Epic 被 [VirtualXposed](https://github.com/android-hacker/VirtualXposed) 以及 [太极](https://www.coolapk.com/apk/me.weishu.exp) 使用,用来实现非 Root 场景下的 Xposed 功能,已经经过了相当广泛的验证。
关于 Epic 的实现原理,可以参考 [本文](http://weishu.me/2017/11/23/dexposed-on-art/)。
## 使用
### 添加依赖
在你项目的 build.gradle 中添加如下依赖(jitpack 仓库):
```groovy
dependencies {
compile 'com.github.tiann:epic:0.11.2'
}
```
然后就可以使用了。
### 几个例子
1. 监控 Java 线程的创建和销毁:
```java
class ThreadMethodHook extends XC_MethodHook{
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Thread t = (Thread) param.thisObject;
Log.i(TAG, "thread:" + t + ", started..");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Thread t = (Thread) param.thisObject;
Log.i(TAG, "thread:" + t + ", exit..");
}
}
DexposedBridge.hookAllConstructors(Thread.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Thread thread = (Thread) param.thisObject;
Class> clazz = thread.getClass();
if (clazz != Thread.class) {
Log.d(TAG, "found class extend Thread:" + clazz);
DexposedBridge.findAndHookMethod(clazz, "run", new ThreadMethodHook());
}
Log.d(TAG, "Thread: " + thread.getName() + " class:" + thread.getClass() + " is created.");
}
});
DexposedBridge.findAndHookMethod(Thread.class, "run", new ThreadMethodHook());
```
以上代码拦截了 `Thread` 类以及 `Thread` 类所有子类的 `run`方法,在 `run` 方法开始执行和退出的时候进行拦截,就可以知道进程内部所有 Java 线程创建和销毁的时机;更进一步,你可以结合 Systrace 等工具,来生成整个过程的执行流程图,比如:
2. 监控 dex 文件的加载:
```java
DexposedBridge.findAndHookMethod(DexFile.class, "loadDex", String.class, String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
String dex = (String) param.args[0];
String odex = (String) param.args[1];
Log.i(TAG, "load dex, input:" + dex + ", output:" + odex);
}
});
```
## 支持情况
目前 Epic 支持 Android 5.0 ~ 11 的 Thumb-2/ARM64 指令集,arm32/x86/x86_64/mips/mips64 不支持。本项目被 [VirtualXposed](https://github.com/android-hacker/VirtualXposed) 和 [太极](http://taichi.cool) 以及大量企业级用户使用,经过了数千万用户的验证,已经被证明非常稳定。目前,手机 QQ 已经在产品中使用 Epic。
## 已知问题
1. 受限于 inline hook 本身,短方法 (Thumb-2 下指令小于 8 个字节,ARM64 小于 16 字节) 无法支持。
2. 被完全内联的方法不支持。
## 致谢
1. [Dexposed](https://github.com/alibaba/dexposed)
2. [Xposed](http://repo.xposed.info/module/de.robv.android.xposed.installer)
3. [mar-v-in/ArtHook](https://github.com/mar-v-in/ArtHook)
4. [Nougat_dlfunctions](https://github.com/avs333/Nougat_dlfunctions.git)
## 联系我
twsxtd@gmail.com
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 30
defaultConfig {
applicationId "me.weishu.epic.samples"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
ndkBuild {
abiFilters "armeabi-v7a", "x86"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
lintOptions {
abortOnError false
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(":library")
implementation 'com.qmuiteam:qmui:1.0.4'
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/weishu/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: app/src/androidTest/java/me/weishu/epic/ExampleInstrumentedTest.java
================================================
//package me.weishu.epic;
//
//import android.content.Context;
//import android.support.test.InstrumentationRegistry;
//import android.support.test.runner.AndroidJUnit4;
//
//import org.junit.Test;
//import org.junit.runner.RunWith;
//
//import static junit.framework.Assert.assertEquals;
//import static org.junit.Assert.*;
//
///**
// * Instrumentation test, which will execute on an Android device.
// *
// * @see Testing documentation
// */
//@RunWith(AndroidJUnit4.class)
//public class ExampleInstrumentedTest {
// @Test
// public void useAppContext() throws Exception {
// // Context of the app under test.
// Context appContext = InstrumentationRegistry.getTargetContext();
//
// assertEquals("me.weishu.epic", appContext.getPackageName());
// }
//}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/me/weishu/epic/samples/MainActivity.java
================================================
package me.weishu.epic.samples;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.Button;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import me.weishu.epic.samples.tests.TestCase;
import me.weishu.epic.samples.tests.TestManager;
import me.weishu.epic.samples.tests.TestSuite;
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
ExpandableListView listView;
List allSuites;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "savedInstance:" + savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("Epic Test");
listView = findViewById(R.id.list);
allSuites = TestManager.getInstance().getAllSuites();
ExpandableListAdapter adapter = new MyAdapter();
listView.setAdapter(adapter);
}
private class MyAdapter extends BaseExpandableListAdapter {
@Override
public int getGroupCount() {
return allSuites.size();
}
@Override
public int getChildrenCount(int groupPosition) {
return allSuites.get(groupPosition).getAllCases().size();
}
@Override
public TestSuite getGroup(int groupPosition) {
return allSuites.get(groupPosition);
}
@Override
public TestCase getChild(int groupPosition, int childPosition) {
return allSuites.get(groupPosition).getAllCases().get(childPosition);
}
@Override
public long getGroupId(int groupPosition) {
return 0;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return 0;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
View parentView = View.inflate(parent.getContext(), R.layout.parent_layout, null);
TextView t = parentView.findViewById(R.id.text);
t.setText(getGroup(groupPosition).getName());
ImageView indicator = parentView.findViewById(R.id.indicator);
if (isExpanded) {
indicator.setImageResource(R.drawable.arrow_down);
} else {
indicator.setImageResource(R.drawable.arrow_up);
}
return parentView;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
final TestCase child = getChild(groupPosition, childPosition);
final View childView = View.inflate(parent.getContext(), R.layout.child_layout, null);
final TextView title = childView.findViewById(R.id.label);
title.setText(child.getName());
Button test = childView.findViewById(R.id.test);
test.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
child.test();
}
});
Button validate = childView.findViewById(R.id.validate);
validate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean validate = child.validate();
final int color = v.getContext().getResources().getColor(validate ?
android.R.color.holo_green_light : android.R.color.holo_red_light);
childView.setBackgroundColor(color);
}
});
return childView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/MainApplication.java
================================================
package me.weishu.epic.samples;
import android.app.Application;
import android.content.Context;
/**
* Created by weishu on 17/10/31.
*/
public class MainApplication extends Application {
private static Context sContext;
public static Context getAppContext() {
return sContext;
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
sContext = base;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/CallingConventationTest.java
================================================
package me.weishu.epic.samples.tests;
import me.weishu.epic.samples.tests.arguments.ArgumentTarget;
/**
* Created by weishu on 17/11/15.
*/
public class CallingConventationTest {
public static void longParams1() {
// r0 = ArtMethod.this
// r1 = 4
// r2 = 8
// r3 = parital 12
// sp + 16 = partial 12
ArgumentTarget.arg3(4, 8, 12L);
}
public static void longParams2() {
// r0 = ArtMethod
// r1, r2 = 1024L
// r3 = bbcc1122
// sp + 16 = 0xffaa
ArgumentTarget.arg2(1024L, 0xffaabbcc1122L);
}
public static void longParams3() {
ArgumentTarget.arg2(123, 0xffaabbcc1122L);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/LogMethodHook.java
================================================
package me.weishu.epic.samples.tests;
import android.util.Log;
import java.util.Arrays;
import de.robv.android.xposed.XC_MethodHook;
/**
* Created by weishu on 17/11/13.
*/
public class LogMethodHook extends XC_MethodHook {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i(getClass().getSimpleName(), "beforeHookedMethod() called with: param = [" + paramToString(param) + "]");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i(getClass().getSimpleName(), "afterHookedMethod() called with: param = [" + paramToString(param) + "]");
}
private static String paramToString(MethodHookParam param) {
StringBuilder sb = new StringBuilder(param.getClass().getSimpleName()).append("{");
sb.append("method = ").append(param.method.getName()).append(", ");
sb.append("this = ").append(param.thisObject).append(", ");
sb.append("args = ").append(Arrays.toString(param.args)).append(",");
sb.append("result = ").append(param.getResult()).append("}");
return sb.toString();
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/TestCase.java
================================================
package me.weishu.epic.samples.tests;
import android.util.Log;
import android.widget.Toast;
import me.weishu.epic.samples.MainApplication;
/**
* Created by weishu on 17/11/13.
*/
public abstract class TestCase {
private static final String TAG = "TestCase";
protected String name;
public TestCase(String name) {
this.name = name;
}
public abstract void test();
public abstract boolean predicate();
public boolean validate() {
boolean validate;
try {
validate = predicate();
} catch (Throwable e) {
validate = false;
Log.e(TAG, "error happened:", e);
}
if (!validate) {
Toast.makeText(MainApplication.getAppContext(), "测试不通过。", Toast.LENGTH_SHORT).show();
}
return validate;
}
public String getName() {
return name;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/TestManager.java
================================================
package me.weishu.epic.samples.tests;
import android.os.Build;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import me.weishu.epic.samples.tests.arguments.ArgStatic0;
import me.weishu.epic.samples.tests.arguments.ArgStatic4;
import me.weishu.epic.samples.tests.arguments.ArgStatic44;
import me.weishu.epic.samples.tests.arguments.ArgStatic444;
import me.weishu.epic.samples.tests.arguments.ArgStatic4444;
import me.weishu.epic.samples.tests.arguments.ArgStatic4448;
import me.weishu.epic.samples.tests.arguments.ArgStatic448;
import me.weishu.epic.samples.tests.arguments.ArgStatic4484;
import me.weishu.epic.samples.tests.arguments.ArgStatic48;
import me.weishu.epic.samples.tests.arguments.ArgStatic484;
import me.weishu.epic.samples.tests.arguments.ArgStatic4844;
import me.weishu.epic.samples.tests.arguments.ArgStatic488;
import me.weishu.epic.samples.tests.arguments.ArgStatic4888;
import me.weishu.epic.samples.tests.arguments.ArgStatic8;
import me.weishu.epic.samples.tests.arguments.ArgStatic84;
import me.weishu.epic.samples.tests.arguments.ArgStatic844;
import me.weishu.epic.samples.tests.arguments.ArgStatic8444;
import me.weishu.epic.samples.tests.arguments.ArgStatic8448;
import me.weishu.epic.samples.tests.arguments.ArgStatic848;
import me.weishu.epic.samples.tests.arguments.ArgStatic8484;
import me.weishu.epic.samples.tests.arguments.ArgStatic8488;
import me.weishu.epic.samples.tests.arguments.ArgStatic88;
import me.weishu.epic.samples.tests.arguments.ArgStatic884;
import me.weishu.epic.samples.tests.arguments.ArgStatic8844;
import me.weishu.epic.samples.tests.arguments.ArgStatic8848;
import me.weishu.epic.samples.tests.arguments.ArgStatic888;
import me.weishu.epic.samples.tests.arguments.ArgStatic8884;
import me.weishu.epic.samples.tests.arguments.ArgStatic8888;
import me.weishu.epic.samples.tests.custom.Case1;
import me.weishu.epic.samples.tests.custom.Case10_Default_Constructor;
import me.weishu.epic.samples.tests.custom.Case11_SuspendAll;
import me.weishu.epic.samples.tests.custom.Case12_MultiCallback;
import me.weishu.epic.samples.tests.custom.Case13_FastNative;
import me.weishu.epic.samples.tests.custom.Case14_GC;
import me.weishu.epic.samples.tests.custom.Case17_SameMethod;
import me.weishu.epic.samples.tests.custom.Case18_returnConst;
import me.weishu.epic.samples.tests.custom.Case2;
import me.weishu.epic.samples.tests.custom.Case3;
import me.weishu.epic.samples.tests.custom.Case4;
import me.weishu.epic.samples.tests.custom.Case5;
import me.weishu.epic.samples.tests.custom.Case6;
import me.weishu.epic.samples.tests.custom.Case7;
import me.weishu.epic.samples.tests.custom.Case8_Activity_onCreate;
import me.weishu.epic.samples.tests.custom.Case9_ThreadMonitor;
import me.weishu.epic.samples.tests.custom.CaseManager;
import me.weishu.epic.samples.tests.invoketype.InvokeConstructor;
import me.weishu.epic.samples.tests.returntype.BooleanType;
import me.weishu.epic.samples.tests.returntype.ByteType;
import me.weishu.epic.samples.tests.returntype.CharType;
import me.weishu.epic.samples.tests.returntype.CustomType;
import me.weishu.epic.samples.tests.returntype.DoubleType;
import me.weishu.epic.samples.tests.returntype.FloatType;
import me.weishu.epic.samples.tests.returntype.IntType;
import me.weishu.epic.samples.tests.returntype.LongType;
import me.weishu.epic.samples.tests.returntype.ShortType;
import me.weishu.epic.samples.tests.returntype.StringArrayType;
import me.weishu.epic.samples.tests.returntype.StringType;
import me.weishu.epic.samples.tests.returntype.VoidType;
/**
* Created by weishu on 17/11/13.
*/
public class TestManager {
private static TestManager sManager = new TestManager();
private List suites = new ArrayList<>();
private TestManager() {
initAllSuites();
}
public static TestManager getInstance() {
return sManager;
}
public void addSuite(TestSuite suite) {
suites.add(suite);
}
public List getAllSuites() {
return suites;
}
private void initAllSuites() {
TestSuite returnType = new TestSuite("返回值测试");
returnType.addCase(new VoidType());
returnType.addCase(new ByteType());
returnType.addCase(new ShortType());
returnType.addCase(new CharType());
returnType.addCase(new IntType());
returnType.addCase(new FloatType());
returnType.addCase(new LongType());
returnType.addCase(new DoubleType());
returnType.addCase(new BooleanType());
returnType.addCase(new StringType());
returnType.addCase(new StringArrayType());
returnType.addCase(new CustomType());
addSuite(returnType);
TestSuite arguments = new TestSuite("参数测试");
arguments.addCase(new ArgStatic0());
arguments.addCase(new ArgStatic4());
arguments.addCase(new ArgStatic8());
arguments.addCase(new ArgStatic44());
arguments.addCase(new ArgStatic48());
arguments.addCase(new ArgStatic84());
arguments.addCase(new ArgStatic88());
arguments.addCase(new ArgStatic444());
arguments.addCase(new ArgStatic448());
arguments.addCase(new ArgStatic484());
arguments.addCase(new ArgStatic844());
arguments.addCase(new ArgStatic488());
arguments.addCase(new ArgStatic848());
arguments.addCase(new ArgStatic884());
arguments.addCase(new ArgStatic888());
arguments.addCase(new ArgStatic4444());
arguments.addCase(new ArgStatic4448());
arguments.addCase(new ArgStatic4484());
arguments.addCase(new ArgStatic4844());
arguments.addCase(new ArgStatic8444());
arguments.addCase(new ArgStatic8844());
arguments.addCase(new ArgStatic8484());
arguments.addCase(new ArgStatic8448());
arguments.addCase(new ArgStatic8884());
arguments.addCase(new ArgStatic8848());
arguments.addCase(new ArgStatic8488());
arguments.addCase(new ArgStatic4888());
arguments.addCase(new ArgStatic8888());
addSuite(arguments);
TestSuite invokeType = new TestSuite("调用类型");
invokeType.addCase(new InvokeConstructor());
addSuite(invokeType);
TestSuite custom = new TestSuite("自定义");
CaseManager.getInstance().getCase(Case1.class);
CaseManager.getInstance().getCase(Case2.class);
CaseManager.getInstance().getCase(Case3.class);
CaseManager.getInstance().getCase(Case4.class);
CaseManager.getInstance().getCase(Case4.class);
CaseManager.getInstance().getCase(Case5.class);
CaseManager.getInstance().getCase(Case6.class);
CaseManager.getInstance().getCase(Case7.class);
CaseManager.getInstance().getCase(Case8_Activity_onCreate.class);
CaseManager.getInstance().getCase(Case9_ThreadMonitor.class);
CaseManager.getInstance().getCase(Case10_Default_Constructor.class);
CaseManager.getInstance().getCase(Case12_MultiCallback.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
CaseManager.getInstance().getCase(Case11_SuspendAll.class);
CaseManager.getInstance().getCase(Case14_GC.class);
// CaseManager.getInstance().getCase(Case15_StopJit.class);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CaseManager.getInstance().getCase(Case13_FastNative.class);
}
// CaseManager.getInstance().getCase(Case16_SameEntry.class);
CaseManager.getInstance().getCase(Case17_SameMethod.class);
CaseManager.getInstance().getCase(Case18_returnConst.class);
final Set> cases = CaseManager.getInstance().getCases();
for (final Class> aCase : cases) {
custom.addCase(new TestCase(aCase.getSimpleName()) {
@Override
public void test() {
CaseManager.getInstance().getCase(aCase).hook();
}
@Override
public boolean predicate() {
return CaseManager.getInstance().getCase(aCase).validate();
}
});
}
addSuite(custom);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/TestSuite.java
================================================
package me.weishu.epic.samples.tests;
import java.util.ArrayList;
import java.util.List;
/**
* Created by weishu on 17/11/13.
*/
public class TestSuite {
private String name;
private List cases = new ArrayList<>();
public TestSuite(String name) {
this.name = name;
}
public void addCase(TestCase caze) {
cases.add(caze);
}
public String getName() {
return name;
}
public List getAllCases() {
return cases;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/AbsArgStaticCase.java
================================================
package me.weishu.epic.samples.tests.arguments;
import android.util.Log;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Random;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XposedHelpers;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* @author weishu
* @date 17/11/14.
*/
public abstract class AbsArgStaticCase extends TestCase {
private final String TAG = getClass().getSimpleName();
final long[] args;
private Random r = new Random();
public AbsArgStaticCase() {
super(null);
args = new long[8];
for (int i = 0; i < args.length; i++) {
args[i] = 0L;
}
name = getClass().getSimpleName();
}
@Override
public void test() {
DexposedBridge.hookMethod(getTargetMethod(), new LogMethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
int length = param.args.length;
if (length > 0) {
for (int i = 0; i < length; i++) {
long tmp = ((Number) param.args[i]).longValue();
args[i] = tmp;
}
}
}
});
}
@Override
public boolean predicate() {
long[] arguments = getArguments();
Log.i(TAG, "call arguments: " + Arrays.toString(toHex(arguments)));
makeCall(arguments);
boolean ret = true;
int length = getArgumentNumber();
for (int i = 0; i < length; i++) {
if (arguments[i] != args[i]) {
ret = false;
Log.i(TAG, "hooked arguments: " + Arrays.toString(toHex(args)));
}
}
return ret;
}
private long[] getArguments() {
final int argumentNumber = getArgumentNumber();
long[] arguments = new long[argumentNumber];
for (int i = 0; i < arguments.length; i++) {
arguments[i] = nextLong();
}
return arguments;
}
protected Method getTargetMethod() {
final int argumentNumber = getArgumentNumber();
String methodName = "arg" + argumentNumber;
Method method = XposedHelpers.findMethodExact(ArgumentTarget.class, methodName, getParamsSignature());
Log.i(TAG, "find target Method:" + method);
return method;
}
protected int getArgumentNumber() {
return getParamsSignature().length;
}
protected void makeCall(long... args) {
Log.i(TAG, getName() + " make call with arguments:" + Arrays.toString(args));
}
private long nextLong() {
int ret = r.nextInt();
return ret;
}
private Class>[] getParamsSignature() {
String className = getClass().getSimpleName();
String signature = className.substring("ArgStatic".length());
final int length = signature.length();
Class>[] clazz = new Class>[length];
for (int i = 0; i < length; i++) {
final char c = signature.charAt(i);
if (c == '4') {
clazz[i] = int.class;
} else if (c == '8') {
clazz[i] = long.class;
} else {
throw new RuntimeException("Unknown signature!!");
}
}
return clazz;
}
private String[] toHex(long[] value) {
String[] ret = new String[value.length];
for (int i = 0; i < value.length; i++) {
ret[i] = Long.toHexString(value[i]);
}
return ret;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic0.java
================================================
package me.weishu.epic.samples.tests.arguments;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* @author weishu
* @date 17/11/14.
*/
public class ArgStatic0 extends TestCase {
boolean beforeCalled = false;
boolean afterCalled = false;
public ArgStatic0() {
super("ArgStatic0");
}
@Override
public void test() {
DexposedBridge.findAndHookMethod(ArgumentTarget.class, "arg0", new LogMethodHook() {
@Override
protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
beforeCalled = true;
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
afterCalled = true;
}
});
}
@Override
public boolean predicate() {
ArgumentTarget.arg0();
return beforeCalled && afterCalled;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* @author weishu
* @date 17/11/14.
*/
public class ArgStatic4 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg1((int)args[0]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic44.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* @author weishu
* @date 17/11/14.
*/
public class ArgStatic44 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg2((int) args[0], (int) args[1]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic444.java
================================================
package me.weishu.epic.samples.tests.arguments;
import android.util.Log;
import java.util.Arrays;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic444 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
Log.i("mylog", "make call args:" + Arrays.toString(args));
ArgumentTarget.arg3((int) args[0], (int) args[1], (int) args[2]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4444.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic4444 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4((int)args[0], (int)args[1], (int)args[2], (int)args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4448.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic4448 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4((int) args[0], (int)args[1], (int)args[2], args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic448.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic448 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg3((int) args[0], (int) args[1], args[2]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4484.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic4484 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4((int)args[0], (int)args[1], args[2], (int) args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4488.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic4488 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4((int)args[0], (int)args[1], args[2], args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic48.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* @author weishu
* @date 17/11/14.
*/
public class ArgStatic48 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg2((int) args[0], args[1]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic484.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic484 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg3((int) args[0], args[1], (int) args[2]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4844.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic4844 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4((int) args[0], args[1], (int)args[2], (int)args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4848.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic4848 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4((int)args[0], args[1], (int)args[2], args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic488.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic488 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg3((int) args[0], args[1], args[2]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4884.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic4884 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4((int)args[0], args[1], args[2], (int)args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4888.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic4888 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4((int)args[0], args[1], args[2], args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* @author weishu
* @date 17/11/14.
*/
public class ArgStatic8 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg1(args[0]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic84.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* @author weishu
* @date 17/11/14.
*/
public class ArgStatic84 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg2(args[0], (int)args[1]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic844.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic844 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg3(args[0], (int) args[1], (int) args[2]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8444.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic8444 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4(args[0], (int)args[1], (int)args[2], (int)args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8448.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic8448 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4(args[0], (int)args[1], (int)args[2], args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic848.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic848 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg3(args[0], (int) args[1], args[2]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8484.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic8484 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4(args[0], (int)args[1], args[2], (int)args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8488.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic8488 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4(args[0], (int)args[1], args[2], args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic88.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* @author weishu
* @date 17/11/14.
*/
public class ArgStatic88 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
super.makeCall(args);
ArgumentTarget.arg2(args[0], args[1]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic884.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic884 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg3(args[0], args[1], (int) args[2]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8844.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic8844 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4(args[0], args[1], (int)args[2], (int)args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8848.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic8848 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4(args[0], args[1], (int)args[2], args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic888.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic888 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg3(args[0], args[1], args[2]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8884.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic8884 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4(args[0], args[1], args[2], (int)args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8888.java
================================================
package me.weishu.epic.samples.tests.arguments;
/**
* Created by weishu on 17/11/14.
*/
public class ArgStatic8888 extends AbsArgStaticCase {
@Override
protected void makeCall(long... args) {
ArgumentTarget.arg4(args[0], args[1], args[2], args[3]);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgumentTarget.java
================================================
package me.weishu.epic.samples.tests.arguments;
import android.util.Log;
/**
* @author weishu
* @date 17/11/13.
*/
public class ArgumentTarget {
private static final String TAG = "ArgumentTarget";
//region ---------------static---------------
public static void arg0() {
Log.i(TAG, "arg0() called");
}
public static void arg1(int v1) {
Log.i(TAG, "arg1() called with: v1 = [" + v1 + "]");
}
public static void arg1(long v1) {
Log.i(TAG, "arg1() called with: v1 = [" + v1 + "]");
}
public static void arg2(int v1, int v2) {
Log.i(TAG, "arg2() called with: v1 = [" + v1 + "], v2 = [" + v2 + "]");
}
public static void arg2(int v1, long v2) {
Log.i(TAG, "arg2() called with: v1 = [" + v1 + "], v2 = [" + v2 + "]");
}
public static void arg2(long v1, int v2) {
Log.i(TAG, "arg2() called with: v1 = [" + v1 + "], v2 = [" + v2 + "]");
}
public static void arg2(long v1, long v2) {
Log.i(TAG, "arg2() called with: v1 = [" + v1 + "], v2 = [" + v2 + "]");
}
public static void arg3(int v1, int v2, int v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public static void arg3(long v1, int v2, int v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public static void arg3(int v1, long v2, int v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public static void arg3(int v1, int v2, long v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public static void arg3(long v1, long v2, int v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public static void arg3(long v1, int v2, long v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public static void arg3(int v1, long v2, long v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public static void arg3(long v1, long v2, long v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public static void arg4(int v1, int v2, int v3, int v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(long v1, int v2, int v3, int v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(int v1, long v2, int v3, int v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(int v1, int v2, long v3, int v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(int v1, int v2, int v3, long v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(long v1, long v2, int v3, int v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(long v1, int v2, long v3, int v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(long v1, int v2, int v3, long v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(int v1, long v2, long v3, int v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(int v1, long v2, int v3, long v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(int v1, int v2, long v3, long v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(int v1, long v2, long v3, long v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(long v1, int v2, long v3, long v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(long v1, long v2, int v3, long v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(long v1, long v2, long v3, int v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg4(long v1, long v2, long v3, long v4) {
Log.i(TAG, "arg4() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "]");
}
public static void arg5(int v1, int v2, int v3, int v4, int v5) {
Log.i(TAG, "arg5() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "], v5 = [" + v5 + "]");
}
public static void arg5(long v1, long v2, long v3, long v4, long v5) {
Log.i(TAG, "arg5() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "], v5 = [" + v5 + "]");
}
public static void arg6(int v1, int v2, int v3, int v4, int v5, int v6) {
Log.i(TAG, "arg6() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "], v5 = [" + v5 + "], v6 = [" + v6 + "]");
}
public static void arg6(long v1, long v2, long v3, long v4, long v5, long v6) {
Log.i(TAG, "arg6() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "], v5 = [" + v5 + "], v6 = [" + v6 + "]");
}
public static void arg7(int v1, int v2, int v3, int v4, int v5, int v6, int v7) {
Log.i(TAG, "arg7() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "], v5 = [" + v5 + "], v6 = [" + v6 + "], v7 = [" + v7 + "]");
}
public static void arg7(long v1, long v2, long v3, long v4, long v5, long v6, long v7) {
Log.i(TAG, "arg7() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "], v4 = [" + v4 + "], v5 = [" + v5 + "], v6 = [" + v6 + "], v7 = [" + v7 + "]");
}
//endregion
//region ---------------non static---------------
public void iarg0() {
Log.i(TAG, "arg0() called");
}
public void iarg1(int v1) {
Log.i(TAG, "arg1() called with: v1 = [" + v1 + "]");
}
public void iarg1(long v1) {
Log.i(TAG, "arg1() called with: v1 = [" + v1 + "]");
}
public void iarg2(int v1, int v2) {
Log.i(TAG, "arg2() called with: v1 = [" + v1 + "], v2 = [" + v2 + "]");
}
public void iarg2(int v1, long v2) {
Log.i(TAG, "arg2() called with: v1 = [" + v1 + "], v2 = [" + v2 + "]");
}
public void iarg2(long v1, long v2) {
Log.i(TAG, "arg2() called with: v1 = [" + v1 + "], v2 = [" + v2 + "]");
}
public void iarg3(int v1, int v2, int v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public void iarg3(long v1, int v2, int v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public void iarg3(int v1, long v2, int v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public void iarg3(int v1, int v2, long v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public void iarg3(long v1, long v2, int v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public void iarg3(long v1, int v2, long v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public void iarg3(int v1, long v2, long v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
public void iarg3(long v1, long v2, long v3) {
Log.i(TAG, "arg3() called with: v1 = [" + v1 + "], v2 = [" + v2 + "], v3 = [" + v3 + "]");
}
//endregion
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case.java
================================================
package me.weishu.epic.samples.tests.custom;
/**
* Created by weishu on 17/11/6.
*/
public interface Case {
void hook();
boolean validate(Object... args);
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case1.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.os.SystemClock;
import android.util.Log;
import java.lang.reflect.Field;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
/**
* Created by weishu on 17/11/6.
*/
public class Case1 implements Case {
static Field sThread$target;
static {
try {
// Thread#target
sThread$target = Thread.class.getDeclaredField("target");
sThread$target.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
private static final String TAG = "Case1";
@Override
public void hook() {
DexposedBridge.findAndHookMethod(Thread.class, "run", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i(TAG, "afterHookedMethod: " + param.thisObject);
Thread thread = (Thread) param.thisObject;
Runnable target = (Runnable) sThread$target.get(thread);
// start|threadName|priority|class|startTime|stacktrace
Log.i(TAG, "runnable target:" + target);
}
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i(TAG, "beforeHookedMethod: " + param.thisObject);
}
});
}
@Override
public boolean validate(Object...args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "before sleep");
SystemClock.sleep(3000);
Log.i(TAG, "after sleep");
}
});
t1.start();
class MyThread extends Thread {
@Override
public void run() {
super.run();
Log.i(TAG, "before sleep");
SystemClock.sleep(3000);
Log.i(TAG, "after sleep");
}
}
Thread t2 = new MyThread();
t2.start();
return true;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case10_Default_Constructor.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.util.Log;
import java.lang.reflect.Constructor;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
/**
* Created by weishu on 17/11/13.
*/
public class Case10_Default_Constructor implements Case {
private static final String TAG = "Case10_Default_Constructor";
@Override
public void hook() {
Constructor> constructor = XposedHelpers.findConstructorExact(Target.class);
DexposedBridge.hookMethod(constructor, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i(TAG, "Target constructor called");
}
});
}
@Override
public boolean validate(Object... args) {
new Target();
return true;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case11_SuspendAll.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import me.weishu.epic.art.EpicNative;
/**
* Created by weishu on 17/11/18.
*/
public class Case11_SuspendAll implements Case {
private static final String TAG = "Case11_SuspendAll";
@Override
public void hook() {
Executor executor = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
executor.execute(new Runnable() {
@Override
public void run() {
int j = 0;
while (true) {
Log.i(TAG, "I am:" + Thread.currentThread().getName() + ", count:" + (j++));
SystemClock.sleep(1000);
if (j > 3) {
break;
}
}
}
});
}
}
@Override
public boolean validate(Object... args) {
if (Build.VERSION.SDK_INT < 24) {
Log.i(TAG, "resume/suspend only support Android N+ now.");
return false;
}
long cookie = EpicNative.suspendAll();
for (Thread thread : Thread.getAllStackTraces().keySet()) {
Log.i(TAG, thread.getName() + " status:" + thread.getState());
}
EpicNative.resumeAll(cookie);
return true;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case12_MultiCallback.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.util.Log;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
/**
* Created by weishu on 17/11/21.
*/
public class Case12_MultiCallback implements Case {
private static final String TAG = "Case12_MultiCallback";
int beforeCount = 0;
int afterCount = 0;
XC_MethodHook callback1 = new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i(TAG, "beforeHookMethod 1");
beforeCount++;
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i(TAG, "afterHookMethod 1");
afterCount++;
}
};
XC_MethodHook callback2 = new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i(TAG, "beforeHookMethod 2 lalala ");
beforeCount++;
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i(TAG, "afterHookMethod 2 lalala");
afterCount++;
}
};
XC_MethodHook callback3 = new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i(TAG, "beforeHookMethod 3 zezeze");
beforeCount++;
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i(TAG, "afterHookMethod 3 zezeze");
afterCount++;
}
};
@Override
public void hook() {
DexposedBridge.findAndHookMethod(Target.class, "test1", Object.class, int.class, callback1);
DexposedBridge.findAndHookMethod(Target.class, "test1", Object.class, int.class, callback2);
DexposedBridge.findAndHookMethod(Target.class, "test1", Object.class, int.class, callback3);
}
@Override
public boolean validate(Object... args) {
new Target().test1("123", 1);
boolean ret = beforeCount == 3 && afterCount == 3;
// reset.
beforeCount = 0;
afterCount = 0;
return ret;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case13_FastNative.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.util.Log;
import java.lang.reflect.Method;
import de.robv.android.xposed.XposedHelpers;
/**
* Created by weishu on 17/12/13.
*/
public class Case13_FastNative implements Case {
@Override
public void hook() {
// DexposedBridge.findAndHookMethod(Target.class, "longRunMethod", new LogMethodHook());
final Method invoke = XposedHelpers.findMethodExact(Method.class, "invoke", Object.class, Object[].class);
Log.i("mylog", "invole: " + invoke);
}
@Override
public boolean validate(Object... args) {
new Target().longRunMethod();
return true;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case14_GC.java
================================================
package me.weishu.epic.samples.tests.custom;
import de.robv.android.xposed.DexposedBridge;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.returntype.ReturnTypeTarget;
/**
* Created by weishu on 17/12/13.
*/
public class Case14_GC implements Case {
@Override
public void hook() {
DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnString", String.class, new LogMethodHook());
System.gc();
}
@Override
public boolean validate(Object... args) {
return "123".equals(ReturnTypeTarget.returnString(new String("123")));
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case15_StopJit.java
================================================
package me.weishu.epic.samples.tests.custom;
import me.weishu.epic.art.EpicNative;
/**
* Created by weishu on 17/12/13.
*/
public class Case15_StopJit implements Case {
long cookie;
@Override
public void hook() {
cookie = EpicNative.stopJit();
}
@Override
public boolean validate(Object... args) {
EpicNative.startJit(cookie);
return true;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case16_SameEntry.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.util.Log;
import com.taobao.android.dexposed.utility.Debug;
import java.lang.reflect.Method;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import me.weishu.epic.art.method.ArtMethod;
/**
* Created by weishu on 18/1/8.
*/
public class Case16_SameEntry implements Case {
private static final String TAG = "Case16_SameEntry";
@Override
public void hook() {
final Method add = XposedHelpers.findMethodExact(Target.class, "add", int.class, int.class);
final Method plus = XposedHelpers.findMethodExact(Target.class, "plus", int.class, int.class);
ArtMethod artMethod3 = ArtMethod.of(add);
ArtMethod artMethod4 = ArtMethod.of(plus);
Log.i(TAG, "plus: addr: " + Debug.addrHex(artMethod3.getAddress()) + ", entry:"
+ Debug.addrHex(artMethod3.getEntryPointFromQuickCompiledCode()));
Log.i(TAG, "milus: addr: " + Debug.addrHex(artMethod4.getAddress()) + ", entry:"
+ Debug.addrHex(artMethod4.getEntryPointFromQuickCompiledCode()));
DexposedBridge.hookMethod(add, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.d(TAG, "beforeHookedMethod() called with: param = [" + param + "]");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.d(TAG, "afterHookedMethod() called with: param = [" + param + "]");
}
});
DexposedBridge.hookMethod(plus, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.d(TAG, "beforeHookedMethod() called with: param = [" + param + "]");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.d(TAG, "afterHookedMethod() called with: param = [" + param + "]");
}
});
}
@Override
public boolean validate(Object... args) {
Target target = new Target();
int add = target.add(1, 2);
Log.i(TAG, "1 + 2 = " + add);
int plus = target.plus(3, 4);
Log.i(TAG, "3 + 4 = " + plus);
return true;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case17_SameMethod.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.util.Log;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
/**
* Created by weishu on 18/1/11.
*/
public class Case17_SameMethod implements Case {
private static final String TAG = "Case17_SameMethod";
@Override
public void hook() {
DexposedBridge.findAndHookMethod(Target.class, "add", int.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.d(TAG, "beforeHookedMethod() called with: param = [" + param + "]");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.d(TAG, "afterHookedMethod() called with: param = [" + param + "]");
}
});
DexposedBridge.findAndHookMethod(Target.class, "add", int.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.d(TAG, "beforeHookedMethod2() called with: param = [" + param + "]");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.d(TAG, "afterHookedMethod2() called with: param = [" + param + "]");
}
});
}
@Override
public boolean validate(Object... args) {
new Target().add(1, 2);
new Target().add(3, 4);
return false;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case18_returnConst.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.util.Log;
import java.util.Arrays;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
/**
* Created by weishu on 18/1/11.
*/
public class Case18_returnConst implements Case {
private static final String TAG = "Case18_returnConst";
@Override
public void hook() {
DexposedBridge.findAndHookMethod(Target.class, "returnConst", int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
param.setResult(124);
Log.d(TAG, "beforeHookedMethod() called with: param = [" + Arrays.toString(param.args) + "]");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.d(TAG, "afterHookedMethod() called with: param = [" + Arrays.toString(param.args) + "]");
}
});
}
@Override
public boolean validate(Object... args) {
int i = new Target().returnConst(123);
Log.i(TAG, "return : " + i);
return (i == 124);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case2.java
================================================
package me.weishu.epic.samples.tests.custom;
/**
* Created by weishu on 17/11/6.
*/
public class Case2 implements Case{
private static final String TAG = "Case2";
@Override
public void hook() {
// DexposedBridge.findAndHookMethod(Target.class, "add", int.class, int.class, new XC_MethodHook() {
// @Override
// protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// Log.i(TAG, "before add hooked:" + Arrays.toString(param.args));
// param.setResult(4);
// super.beforeHookedMethod(param);
// }
// });
//
// DexposedBridge.findAndHookMethod(Target.class, "test4", int.class, new XC_MethodHook() {
// @Override
// protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// super.beforeHookedMethod(param);
// Log.i("mylog", "before", new RuntimeException("stack"));
// Log.i("mylog", "this:" + param.thisObject);
// Log.i("mylog", "method:" + param.method);
// Log.i("mylog", "args:" + Arrays.toString(param.args));
//
// }
//
// @Override
// protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// super.afterHookedMethod(param);
// Log.i("mylog", "after");
// }
// });
}
@Override
public boolean validate(Object... args) {
// Log.i(TAG, "1 + 2: " + Target.add(1, 2));
// return Target.add(1, 2) == 4;
Target.validate();
return true;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case3.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.util.Log;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
/**
* Created by weishu on 17/11/6.
*/
public class Case3 implements Case {
private static final String TAG = "Case3";
@Override
public void hook() {
DexposedBridge.findAndHookMethod(System.class, "currentTimeMillis", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i("mylog", "before currentTimeMillis");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i("mylog", "after currentTimeMillis");
}
});
}
@Override
public boolean validate(Object... args) {
Log.i(TAG, "currentTimeMillis: " + System.currentTimeMillis());
return true;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case4.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.text.TextUtils;
import android.util.Log;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
/**
* Created by weishu on 17/11/6.
*/
public class Case4 implements Case {
private static final String TAG = "Case4";
@Override
public void hook() {
DexposedBridge.findAndHookMethod(TextUtils.class, "equals", CharSequence.class, CharSequence.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Log.i(TAG, "beforeHookedMethod: this:" + param.thisObject, new RuntimeException("stack"));
Log.i(TAG, "beforeHookedMethod: String1:" + param.args[0]);
Log.i(TAG, "beforeHookedMethod: String2:" + param.args[1]);
param.setResult(false);
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});
}
@Override
public boolean validate(Object... args) {
String a1 = new String("123");
String a2 = new String("123");
Log.i(TAG, " '123' equals '123' ? " + TextUtils.equals(a1, a2));
return true;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case5.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.util.Log;
import android.widget.TextView;
import com.taobao.android.dexposed.utility.Unsafe;
import java.lang.reflect.Method;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
import me.weishu.epic.art.EpicNative;
/**
* Created by weishu on 17/11/6.
*/
public class Case5 implements Case {
@Override
public void hook() {
DexposedBridge.findAndHookMethod(TextView.class, "setPadding", int.class, int.class, int.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
if (param.thisObject != null) {
Log.i("mylog", "this:" + Long.toHexString(Unsafe.getObjectAddress(param.thisObject)));
}
if (param.method != null) {
Log.i("mylog", "mehod:" + Long.toHexString(EpicNative.getMethodAddress((Method) param.method)));
}
if (param.args != null) {
for (Object arg : param.args) {
Log.i("mylog", "param:" + arg);
if (arg != null) {
Log.i("mylog", "<" + arg.getClass() + "> : 0x" +
Long.toHexString(Unsafe.getObjectAddress(arg)) + ", value: " + arg);
} else {
Log.i("mylog", "param: null");
}
}
}
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i("mylog", "after");
}
});
}
@Override
public boolean validate(Object... args) {
return true;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case6.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.os.SystemClock;
import android.util.Log;
import com.taobao.android.dexposed.utility.Debug;
import com.taobao.android.dexposed.utility.Unsafe;
import java.lang.reflect.Method;
import de.robv.android.xposed.XposedHelpers;
import me.weishu.epic.art.EpicNative;
import me.weishu.epic.art.method.ArtMethod;
/**
* Created by weishu on 17/11/6.
*/
public class Case6 implements Case {
private static final String TAG = "Case6";
@Override
public void hook() {
Object test = new Object();
Log.i(TAG, "test object:" + test);
long testAddr = Unsafe.getObjectAddress(test);
Log.i(TAG, "test object address :" + testAddr);
Log.i(TAG, "test object :" + EpicNative.getObject(XposedHelpers.getLongField(Thread.currentThread(), "nativePeer"), testAddr));
// Log.i(TAG, "object:" + EpicNative.getObject())
final Method nanoTime = XposedHelpers.findMethodExact(System.class, "nanoTime");
final Method uptimeMillis = XposedHelpers.findMethodExact(SystemClock.class, "uptimeMillis");
final Method map = XposedHelpers.findMethodExact(Target.class, "test1", Object.class, int.class);
final Method malloc = XposedHelpers.findMethodExact(Target.class, "test3", Object.class, int.class);
ArtMethod artMethod1 = ArtMethod.of(nanoTime);
ArtMethod artMethod2 = ArtMethod.of(uptimeMillis);
ArtMethod artMethod3 = ArtMethod.of(map);
ArtMethod artMethod4 = ArtMethod.of(malloc);
Log.i(TAG, "nanoTime: addr: 0x" + artMethod1.getAddress() + ", entry:" + Debug.addrHex(artMethod1.getEntryPointFromQuickCompiledCode()));
Log.i(TAG, "uptimeMills: addr: 0x" + artMethod2.getAddress() + ", entry:" + Debug.addrHex(artMethod2.getEntryPointFromQuickCompiledCode()));
Log.i(TAG, "map : addr: 0x" + artMethod3.getAddress() + ", entry:" + Debug.addrHex(artMethod3.getEntryPointFromQuickCompiledCode()));
Log.i(TAG, "malloc: addr: 0x" + artMethod4.getAddress() + ", entry:" + Debug.addrHex(artMethod4.getEntryPointFromQuickCompiledCode()));
}
@Override
public boolean validate(Object... args) {
return true;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case7.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.util.Log;
import java.util.Arrays;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
/**
* Created by weishu on 17/11/8.
*/
public class Case7 implements Case {
private static final String TAG = "Case7";
@Override
public void hook() {
Log.i(TAG, "hook test1");
DexposedBridge.findAndHookMethod(Target.class, "test1", Object.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Log.i(TAG, "before add hooked:" + Arrays.toString(param.args));
param.setResult(4);
super.beforeHookedMethod(param);
}
});
}
@Override
public boolean validate(Object... args) {
Target t = new Target();
t.test1(t, 123);
return true;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case8_Activity_onCreate.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.os.Bundle;
import android.util.Log;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
import me.weishu.epic.samples.MainActivity;
/**
* Created by weishu on 17/11/9.
*/
public class Case8_Activity_onCreate implements Case {
private static final String TAG = "Case8_Activity_onCreate";
@Override
public void hook() {
DexposedBridge.findAndHookMethod(MainActivity.class, "onCreate", Bundle.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i(TAG, "before hooked");
if (param.args[0] == null) {
param.args[0] = new Bundle();
}
Bundle b = (Bundle) param.args[0];
Log.i(TAG, "bundle: " + param.args[0]);
b.putString("hehe", "hacked");
}
});
}
@Override
public boolean validate(Object... args) {
return true;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Case9_ThreadMonitor.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.util.Log;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
/**
* Created by weishu on 17/11/10.
*/
public class Case9_ThreadMonitor implements Case {
private static final String TAG = "Case9_ThreadMonitor";
@Override
public void hook() {
try {
class ThreadMethodHook extends XC_MethodHook {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Thread t = (Thread) param.thisObject;
Log.i(TAG, "thread:" + t + ", started..");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Thread t = (Thread) param.thisObject;
Log.i(TAG, "thread:" + t + ", exit..");
}
}
DexposedBridge.hookAllConstructors(Thread.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Thread thread = (Thread) param.thisObject;
Class> clazz = thread.getClass();
if (clazz != Thread.class) {
Log.d(TAG, "found class extend Thread:" + clazz);
DexposedBridge.findAndHookMethod(clazz, "run", new ThreadMethodHook());
}
Log.d(TAG, "Thread: " + thread.getName() + " class:" + thread.getClass() + " is created.");
}
});
DexposedBridge.findAndHookMethod(Thread.class, "run", new ThreadMethodHook());
} catch (Throwable e) {
Log.e(TAG, "hook failed", e);
}
}
@Override
public boolean validate(Object... args) {
new Thread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "I am started..");
}
}).start();
new MyThread().start();
final ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 4; i++) {
// final int num = i;
executorService.execute(new Runnable() {
@Override
public void run() {
Log.i(TAG, " lalala");
}
});
}
return true;
}
static class MyThread extends Thread {
@Override
public void run() {
Log.i(TAG, "dang dang dang..");
}
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/CaseManager.java
================================================
package me.weishu.epic.samples.tests.custom;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created by weishu on 17/11/6.
*/
public class CaseManager {
private static volatile CaseManager INSTANCE = new CaseManager();
private Map, Case> caseMap = new ConcurrentHashMap<>();
public static synchronized CaseManager getInstance() {
return INSTANCE;
}
public synchronized Case getCase(Class> clazz) {
Case caze = caseMap.get(clazz);
if (caze != null) {
return caze;
} else {
try {
caze = (Case) clazz.newInstance();
caseMap.put(clazz, caze);
} catch (Throwable e) {
throw new RuntimeException("can not get case !!", e);
}
}
return caze;
}
public synchronized Set> getCases() {
return caseMap.keySet();
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/custom/Target.java
================================================
package me.weishu.epic.samples.tests.custom;
import android.os.SystemClock;
import android.util.Log;
import java.lang.reflect.Method;
import de.robv.android.xposed.XposedHelpers;
import me.weishu.epic.art.method.ArtMethod;
/**
* Created by weishu on 17/11/3.
*/
public class Target {
public Target() {
}
public int test1(Object a, int b) {
Log.i("mylog", "test1, arg1: " + a + " , arg2:" + b);
new Runnable() {
@Override
public void run() {
final Method enclosingMethod = getClass().getEnclosingMethod();
long entry = ArtMethod.of(enclosingMethod).getEntryPointFromQuickCompiledCode();
if (entry != ArtMethod.getQuickToInterpreterBridge()) {
Log.w("mylog", "method compiled....");
}
Log.i("mylog", enclosingMethod + "entry: point: 0x" + Long.toHexString(entry));
}
}.run();
return a.hashCode() + b;
}
public int returnConst(int a) {
return a;
}
public int add(int a, int b) {
return a + b;
}
public int plus(int a, int b) {
return a + b;
}
public int test3(Object a, int b) {
Log.i("mylog", "test1, arg1: " + a + " , arg2:" + b);
return a.hashCode() + b;
}
public static int test4(int a) {
return Integer.valueOf(a).hashCode();
}
public static float add(int a, float b) {
return a + b;
}
public static int test2(Object a, int b) {
Log.i("mylog", "test1, arg1: " + a + " , arg2:" + b);
return a.hashCode() + b;
}
public long longRunMethod() {
SystemClock.sleep(4000);
return SystemClock.elapsedRealtime();
}
public static void validate() {
final Method validate = XposedHelpers.findMethodExact(Target.class, "validate");
long entry = ArtMethod.of(validate).getEntryPointFromQuickCompiledCode();
if (entry != ArtMethod.getQuickToInterpreterBridge()) {
Log.w("mylog", "method compiled....");
new Target().test1("123", 1);
}
Log.i("mylog", validate + "entry: point: 0x" + Long.toHexString(entry));
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/invoketype/InvokeConstructor.java
================================================
package me.weishu.epic.samples.tests.invoketype;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XposedHelpers;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* Created by weishu on 17/11/14.
*/
public class InvokeConstructor extends TestCase {
boolean callBefore = false;
boolean callAfter = false;
public InvokeConstructor() {
super("Constructor");
}
@Override
public void test() {
DexposedBridge.hookMethod(XposedHelpers.findConstructorExact(InvokeTypeTarget.class), new LogMethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
callBefore = true;
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
callAfter = true;
}
});
}
@Override
public boolean predicate() {
new InvokeTypeTarget();
return callBefore && callAfter;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/invoketype/InvokeTypeTarget.java
================================================
package me.weishu.epic.samples.tests.invoketype;
/**
* Created by weishu on 17/11/14.
*/
public class InvokeTypeTarget {
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/returntype/BooleanType.java
================================================
package me.weishu.epic.samples.tests.returntype;
import de.robv.android.xposed.DexposedBridge;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* Created by weishu on 17/11/13.
*/
public class BooleanType extends TestCase {
final boolean returnType = Boolean.FALSE;
final boolean returnTypeModified = !returnType;
public BooleanType() {
super("Boolean");
}
@Override
public void test() {
DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnBoolean", boolean.class, new LogMethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(returnTypeModified);
super.beforeHookedMethod(param);
}
});
}
@Override
public boolean predicate() {
return ReturnTypeTarget.returnBoolean(returnType) == returnTypeModified;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/returntype/ByteType.java
================================================
package me.weishu.epic.samples.tests.returntype;
import de.robv.android.xposed.DexposedBridge;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* Created by weishu on 17/11/13.
*/
public class ByteType extends TestCase {
final byte returnType = Byte.MAX_VALUE;
final byte returnTypeModified = Byte.MAX_VALUE - 1;
public ByteType() {
super("Byte");
}
@Override
public void test() {
DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnByte", byte.class, new LogMethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(returnTypeModified);
super.beforeHookedMethod(param);
}
});
}
@Override
public boolean predicate() {
final byte raw = ReturnTypeTarget.returnByte(returnType);
return raw == returnTypeModified;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/returntype/CharType.java
================================================
package me.weishu.epic.samples.tests.returntype;
import de.robv.android.xposed.DexposedBridge;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* Created by weishu on 17/11/13.
*/
public class CharType extends TestCase {
final char returnType = Character.MAX_VALUE;
final char returnTypeModified = returnType - 1;
public CharType() {
super("Char");
}
@Override
public void test() {
DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnChar", char.class, new LogMethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(returnTypeModified);
super.beforeHookedMethod(param);
}
});
}
@Override
public boolean predicate() {
return ReturnTypeTarget.returnChar(returnType) == returnTypeModified;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/returntype/CustomType.java
================================================
package me.weishu.epic.samples.tests.returntype;
import de.robv.android.xposed.DexposedBridge;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* Created by weishu on 17/11/13.
*/
public class CustomType extends TestCase {
final ReturnTypeTarget returnType = new ReturnTypeTarget();
final ReturnTypeTarget returnTypeModified = new ReturnTypeTarget();
public CustomType() {
super("Custom");
}
@Override
public void test() {
DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnCustom", ReturnTypeTarget.class, new LogMethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(returnTypeModified);
super.beforeHookedMethod(param);
}
});
}
@Override
public boolean predicate() {
return ReturnTypeTarget.returnCustom(returnType) == returnTypeModified;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/returntype/DoubleType.java
================================================
package me.weishu.epic.samples.tests.returntype;
import de.robv.android.xposed.DexposedBridge;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* Created by weishu on 17/11/13.
*/
public class DoubleType extends TestCase {
final double returnType = 12343748.12435;
final double returnTypeModified = returnType - 1;
public DoubleType() {
super("Double");
}
@Override
public void test() {
DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnDouble", double.class, new LogMethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(returnTypeModified);
super.beforeHookedMethod(param);
}
});
}
@Override
public boolean predicate() {
return ReturnTypeTarget.returnDouble(returnType) == returnTypeModified;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/returntype/FloatType.java
================================================
package me.weishu.epic.samples.tests.returntype;
import de.robv.android.xposed.DexposedBridge;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* Created by weishu on 17/11/13.
*/
public class FloatType extends TestCase {
final float returnType = 12545.212f;
final float returnTypeModified = returnType - 1;
public FloatType() {
super("Float");
}
@Override
public void test() {
DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnFloat", float.class, new LogMethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(returnTypeModified);
super.beforeHookedMethod(param);
}
});
}
@Override
public boolean predicate() {
return ReturnTypeTarget.returnFloat(returnType) == returnTypeModified;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/returntype/IntType.java
================================================
package me.weishu.epic.samples.tests.returntype;
import de.robv.android.xposed.DexposedBridge;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* Created by weishu on 17/11/13.
*/
public class IntType extends TestCase {
final int returnType = Integer.MAX_VALUE;
final int returnTypeModified = returnType - 1;
public IntType() {
super("Int");
}
@Override
public void test() {
DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnInt", int.class, new LogMethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(returnTypeModified);
super.beforeHookedMethod(param);
}
});
}
@Override
public boolean predicate() {
final int i = ReturnTypeTarget.returnInt(returnType);
return i == returnTypeModified;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/returntype/LongType.java
================================================
package me.weishu.epic.samples.tests.returntype;
import de.robv.android.xposed.DexposedBridge;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* Created by weishu on 17/11/13.
*/
public class LongType extends TestCase {
final long returnType = Long.MAX_VALUE / 2;
final long returnTypeModified = returnType - 1;
public LongType() {
super("Long");
}
@Override
public void test() {
DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnLong", long.class, new LogMethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(returnTypeModified);
super.beforeHookedMethod(param);
}
});
}
@Override
public boolean predicate() {
return ReturnTypeTarget.returnLong(returnType) == returnTypeModified;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/returntype/ReturnTypeTarget.java
================================================
package me.weishu.epic.samples.tests.returntype;
import android.util.Log;
/**
* Created by weishu on 17/11/13.
*/
public class ReturnTypeTarget {
private static final String TAG = "ReturnTypeTarget";
public static void returnVoid() {
Log.d(TAG, "returnVoid() called");
}
public static byte returnByte(byte b) {
Log.i(TAG, "returnByte() called");
return b;
}
public static char returnChar(char c) {
Log.i(TAG, "returnChar() called");
return c;
}
public static short returnShort(short s) {
Log.i(TAG, "returnShort() called");
return s;
}
public static int returnInt(int i) {
Log.i(TAG, "returnInt() called");
return i;
}
public static long returnLong(long l) {
Log.i(TAG, "returnLong() called");
return l;
}
public static float returnFloat(float f) {
Log.i(TAG, "returnFloat() called");
return f;
}
public static double returnDouble(double d) {
Log.i(TAG, "returnDouble() called");
return d;
}
public static boolean returnBoolean(boolean b) {
Log.i(TAG, "returnBoolean() called");
return b;
}
public static String returnString(String s) {
Log.i(TAG, "returnString() called with: s = [" + s + "]");
return s;
}
public static String[] returnStringArray(String[] a) {
Log.i(TAG, "returnStringArray() called with: a = [" + a + "]");
return a;
}
public static ReturnTypeTarget returnCustom(ReturnTypeTarget r) {
Log.i(TAG, "returnCustom() called with: r = [" + r + "]");
return r;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/returntype/ShortType.java
================================================
package me.weishu.epic.samples.tests.returntype;
import de.robv.android.xposed.DexposedBridge;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* Created by weishu on 17/11/13.
*/
public class ShortType extends TestCase {
final short returnType = Short.MAX_VALUE / 2;
final short returnTypeModified = returnType - 1;
public ShortType() {
super("Short");
}
@Override
public void test() {
DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnShort", short.class, new LogMethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(returnTypeModified);
super.beforeHookedMethod(param);
}
});
}
@Override
public boolean predicate() {
return ReturnTypeTarget.returnShort(returnType) == returnTypeModified;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/returntype/StringArrayType.java
================================================
package me.weishu.epic.samples.tests.returntype;
import de.robv.android.xposed.DexposedBridge;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* Created by weishu on 17/11/13.
*/
public class StringArrayType extends TestCase {
final String[] returnType = new String[]{"123", "456"};
final String[] returnTypeModified = new String[]{"123", "456", "678"};
public StringArrayType() {
super("StringArray");
}
@Override
public void test() {
DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnStringArray", String[].class, new LogMethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(returnTypeModified);
super.beforeHookedMethod(param);
}
});
}
@Override
public boolean predicate() {
return ReturnTypeTarget.returnStringArray(returnType) == returnTypeModified;
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/returntype/StringType.java
================================================
package me.weishu.epic.samples.tests.returntype;
import de.robv.android.xposed.DexposedBridge;
import me.weishu.epic.samples.tests.LogMethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* Created by weishu on 17/11/13.
*/
public class StringType extends TestCase {
final String returnType = "123455";
final String returnTypeModified = "784fsgiulfodsg";
public StringType() {
super("String");
}
@Override
public void test() {
DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnString", String.class, new LogMethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(returnTypeModified);
super.beforeHookedMethod(param);
}
});
}
@Override
public boolean predicate() {
return ReturnTypeTarget.returnString(returnType).equals(returnTypeModified);
}
}
================================================
FILE: app/src/main/java/me/weishu/epic/samples/tests/returntype/VoidType.java
================================================
package me.weishu.epic.samples.tests.returntype;
import android.util.Log;
import de.robv.android.xposed.DexposedBridge;
import de.robv.android.xposed.XC_MethodHook;
import me.weishu.epic.samples.tests.TestCase;
/**
* Created by weishu on 17/11/13.
*/
public class VoidType extends TestCase {
private static final String TAG = "VoidType";
boolean callBefore = false;
boolean callAfter = false;
public VoidType() {
super("无返回值");
}
@Override
public void test() {
DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnVoid", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
callBefore = true;
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
callAfter = true;
}
});
}
@Override
public boolean predicate() {
ReturnTypeTarget.returnVoid();
Log.i(TAG, "callBefore:" + callBefore + ", callAfter:" + callAfter);
return callBefore && callAfter;
}
}
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/layout/child_layout.xml
================================================
================================================
FILE: app/src/main/res/layout/parent_layout.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#3F51B5#303F9F#FF4081
================================================
FILE: app/src/main/res/values/strings.xml
================================================
epic
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/test/java/me/weishu/epic/ExampleUnitTest.java
================================================
//package me.weishu.epic;
//
//import org.junit.Test;
//
//import static org.junit.Assert.*;
//
///**
// * Example local unit test, which will execute on the development machine (host).
// *
// * @see Testing documentation
// */
//public class ExampleUnitTest {
// @Test
// public void addition_isCorrect() throws Exception {
// assertEquals(4, 2 + 2);
// }
//}
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
maven {
url 'https://maven.google.com/'
name 'Google'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.1'
classpath 'com.github.panpf.bintray-publish:bintray-publish:1.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
jcenter()
maven {
url 'https://maven.google.com/'
name 'Google'
}
maven { url 'https://jitpack.io' }
}
tasks.withType(Javadoc) {
enabled = false
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Tue Nov 07 13:44:26 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
================================================
FILE: gradle.properties
================================================
## Project-wide Gradle settings.
#
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
#
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx1024m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: library/.gitignore
================================================
/build
================================================
FILE: library/CMakeCache.txt
================================================
# This is the CMakeCache file.
# For build in directory: /Users/weishu/dev/space/arthook/epic/library
# It was generated by CMake: /usr/local/Cellar/cmake/3.9.1/bin/cmake
# You can edit this file to change values found and used by cmake.
# If you do not want to change any of the values, simply exit the editor.
# If you do want to change a value, simply edit, save, and exit the editor.
# The syntax for the file is as follows:
# KEY:TYPE=VALUE
# KEY is the name of a variable in the cache.
# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
# VALUE is the current value for the KEY.
########################
# EXTERNAL cache entries
########################
//No help, variable specified on the command line.
ANDROID_TOOLCHAIN:UNINITIALIZED=gcc
//Path to a program.
CMAKE_AR:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar
//Choose the type of build, options are: None(CMAKE_CXX_FLAGS or
// CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.
CMAKE_BUILD_TYPE:STRING=Debug
//Enable/Disable color output during build.
CMAKE_COLOR_MAKEFILE:BOOL=ON
//A wrapper around 'ar' adding the appropriate '--plugin' option
// for the GCC compiler
CMAKE_CXX_COMPILER_AR:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc-ar
//A wrapper around 'ranlib' adding the appropriate '--plugin' option
// for the GCC compiler
CMAKE_CXX_COMPILER_RANLIB:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc-ranlib
//Flags used by the compiler during all build types.
CMAKE_CXX_FLAGS:STRING=
//Flags used by the compiler during debug builds.
CMAKE_CXX_FLAGS_DEBUG:STRING=
//Flags used by the compiler during release builds for minimum
// size.
CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
//Flags used by the compiler during release builds.
CMAKE_CXX_FLAGS_RELEASE:STRING=
//Flags used by the compiler during release builds with debug info.
CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
//Libraries linked by default with all C++ applications.
CMAKE_CXX_STANDARD_LIBRARIES:STRING=-lm "/Users/weishu/Library/Android/sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/libgnustl_static.a"
//A wrapper around 'ar' adding the appropriate '--plugin' option
// for the GCC compiler
CMAKE_C_COMPILER_AR:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc-ar
//A wrapper around 'ranlib' adding the appropriate '--plugin' option
// for the GCC compiler
CMAKE_C_COMPILER_RANLIB:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc-ranlib
//Flags used by the compiler during all build types.
CMAKE_C_FLAGS:STRING=
//Flags used by the compiler during debug builds.
CMAKE_C_FLAGS_DEBUG:STRING=
//Flags used by the compiler during release builds for minimum
// size.
CMAKE_C_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
//Flags used by the compiler during release builds.
CMAKE_C_FLAGS_RELEASE:STRING=
//Flags used by the compiler during release builds with debug info.
CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
//Libraries linked by default with all C applications.
CMAKE_C_STANDARD_LIBRARIES:STRING=-lm
//Flags used by the linker.
CMAKE_EXE_LINKER_FLAGS:STRING=
//Flags used by the linker during debug builds.
CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during release minsize builds.
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during release builds.
CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during Release with Debug Info builds.
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//Enable/Disable output of compile commands during generation.
CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=OFF
//Install path prefix, prepended onto install directories.
CMAKE_INSTALL_PREFIX:PATH=/usr/local
//Path to a program.
CMAKE_LINKER:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ld
//Path to a program.
CMAKE_MAKE_PROGRAM:FILEPATH=/opt/local/bin/gmake
//Flags used by the linker during the creation of modules.
CMAKE_MODULE_LINKER_FLAGS:STRING=
//Flags used by the linker during debug builds.
CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during release minsize builds.
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during release builds.
CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during Release with Debug Info builds.
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//Path to a program.
CMAKE_NM:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-nm
//Path to a program.
CMAKE_OBJCOPY:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-objcopy
//Path to a program.
CMAKE_OBJDUMP:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-objdump
//Value Computed by CMake
CMAKE_PROJECT_NAME:STATIC=Project
//Path to a program.
CMAKE_RANLIB:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ranlib
//Flags used by the linker during the creation of dll's.
CMAKE_SHARED_LINKER_FLAGS:STRING=
//Flags used by the linker during debug builds.
CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during release minsize builds.
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during release builds.
CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during Release with Debug Info builds.
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//If set, runtime paths are not added when installing shared libraries,
// but are added when building.
CMAKE_SKIP_INSTALL_RPATH:BOOL=NO
//If set, runtime paths are not added when using shared libraries.
CMAKE_SKIP_RPATH:BOOL=NO
//Flags used by the linker during the creation of static libraries.
CMAKE_STATIC_LINKER_FLAGS:STRING=
//Flags used by the linker during debug builds.
CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during release minsize builds.
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during release builds.
CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during Release with Debug Info builds.
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//Path to a program.
CMAKE_STRIP:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-strip
//The CMake toolchain file
CMAKE_TOOLCHAIN_FILE:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/build/cmake/android.toolchain.cmake
//If this value is on, makefiles will be generated without the
// .SILENT directive, and all commands will be echoed to the console
// during the make. This is useful for debugging only. With Visual
// Studio IDE projects all commands are done without /nologo.
CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE
//Value Computed by CMake
Project_BINARY_DIR:STATIC=/Users/weishu/dev/space/arthook/epic/library
//Value Computed by CMake
Project_SOURCE_DIR:STATIC=/Users/weishu/dev/space/arthook/epic/library
//Dependencies for the target
epic_LIB_DEPENDS:STATIC=general;/Users/weishu/Library/Android/sdk/ndk-bundle/platforms/android-9/arch-arm/usr/lib/liblog.so;
//Path to a library.
log-lib:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/platforms/android-9/arch-arm/usr/lib/liblog.so
########################
# INTERNAL cache entries
########################
//ADVANCED property for variable: CMAKE_AR
CMAKE_AR-ADVANCED:INTERNAL=1
//This is the directory where this CMakeCache.txt was created
CMAKE_CACHEFILE_DIR:INTERNAL=/Users/weishu/dev/space/arthook/epic/library
//Major version of cmake used to create the current loaded cache
CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3
//Minor version of cmake used to create the current loaded cache
CMAKE_CACHE_MINOR_VERSION:INTERNAL=9
//Patch version of cmake used to create the current loaded cache
CMAKE_CACHE_PATCH_VERSION:INTERNAL=1
//ADVANCED property for variable: CMAKE_COLOR_MAKEFILE
CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1
//Path to CMake executable.
CMAKE_COMMAND:INTERNAL=/usr/local/Cellar/cmake/3.9.1/bin/cmake
//Path to cpack program executable.
CMAKE_CPACK_COMMAND:INTERNAL=/usr/local/Cellar/cmake/3.9.1/bin/cpack
//Path to ctest program executable.
CMAKE_CTEST_COMMAND:INTERNAL=/usr/local/Cellar/cmake/3.9.1/bin/ctest
//ADVANCED property for variable: CMAKE_CXX_COMPILER_AR
CMAKE_CXX_COMPILER_AR-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_COMPILER_RANLIB
CMAKE_CXX_COMPILER_RANLIB-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_FLAGS
CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG
CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL
CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_STANDARD_LIBRARIES
CMAKE_CXX_STANDARD_LIBRARIES-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_COMPILER_AR
CMAKE_C_COMPILER_AR-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_COMPILER_RANLIB
CMAKE_C_COMPILER_RANLIB-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS
CMAKE_C_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_MINSIZEREL
CMAKE_C_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_C_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_STANDARD_LIBRARIES
CMAKE_C_STANDARD_LIBRARIES-ADVANCED:INTERNAL=1
//Path to cache edit program executable.
CMAKE_EDIT_COMMAND:INTERNAL=/usr/local/Cellar/cmake/3.9.1/bin/ccmake
//Executable file format
CMAKE_EXECUTABLE_FORMAT:INTERNAL=ELF
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS
CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG
CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE
CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS
CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1
//Name of external makefile project generator.
CMAKE_EXTRA_GENERATOR:INTERNAL=
//Name of generator.
CMAKE_GENERATOR:INTERNAL=Unix Makefiles
//Name of generator platform.
CMAKE_GENERATOR_PLATFORM:INTERNAL=
//Name of generator toolset.
CMAKE_GENERATOR_TOOLSET:INTERNAL=
//Source directory with the top level CMakeLists.txt file for this
// project
CMAKE_HOME_DIRECTORY:INTERNAL=/Users/weishu/dev/space/arthook/epic/library
//Install .so files without execute permission.
CMAKE_INSTALL_SO_NO_EXE:INTERNAL=0
//ADVANCED property for variable: CMAKE_LINKER
CMAKE_LINKER-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MAKE_PROGRAM
CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS
CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG
CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE
CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_NM
CMAKE_NM-ADVANCED:INTERNAL=1
//number of local generators
CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1
//ADVANCED property for variable: CMAKE_OBJCOPY
CMAKE_OBJCOPY-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_OBJDUMP
CMAKE_OBJDUMP-ADVANCED:INTERNAL=1
//Platform information initialized
CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1
//ADVANCED property for variable: CMAKE_RANLIB
CMAKE_RANLIB-ADVANCED:INTERNAL=1
//Path to CMake installation.
CMAKE_ROOT:INTERNAL=/usr/local/Cellar/cmake/3.9.1/share/cmake
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS
CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG
CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE
CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH
CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SKIP_RPATH
CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS
CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG
CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE
CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STRIP
CMAKE_STRIP-ADVANCED:INTERNAL=1
//uname command
CMAKE_UNAME:INTERNAL=/usr/bin/uname
//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE
CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1
================================================
FILE: library/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 31
defaultConfig {
minSdkVersion 21
targetSdkVersion 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
ndk {
// Specifies the ABI configurations of your native
// libraries Gradle should build and package with your APK.
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
externalNativeBuild {
cmake {
cppFlags "-std=c++11"
// arguments "-DANDROID_TOOLCHAIN=gcc"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path 'src/main/cpp/CMakeLists.txt'
}
}
lintOptions {
abortOnError false
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "com.github.tiann:FreeReflection:3.1.0"
api 'me.weishu.exposed:exposed-xposedapi:0.4.5'
}
apply plugin: 'com.github.panpf.bintray-publish'
publish {
userOrg = 'twsxtd'//
groupId = 'me.weishu'
artifactId = 'epic'
publishVersion = '0.11.1'
desc = 'Android Java AOP Method Hook (Dexposed on ART)'
website = 'https://github.com/tiann/epic'
}
================================================
FILE: library/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/weishu/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keepattributes Signature,SourceFile,LineNumberTable,Exceptions
-keep class com.taobao.android.dexposed.** {*;}
-keep class me.weishu.epic.art.** {*;}
# delete log in release mode.
-assumenosideeffects class com.taobao.android.dexposed.utility.Logger {
public static void i(...);
public static void w(...);
public static void d(...);
public static void e(...);
}
-assumenosideeffects class com.taobao.android.dexposed.utility.Debug {
public static *** hexdump(...);
}
================================================
FILE: library/src/main/AndroidManifest.xml
================================================
================================================
FILE: library/src/main/cpp/.gitignore
================================================
build/
================================================
FILE: library/src/main/cpp/CMakeLists.txt
================================================
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
set (SRC_LIST epic.cpp fake_dlfcn.cpp art.cpp)
add_library( # Sets the name of the library.
epic
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
${SRC_LIST})
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
epic
# Links the target library to the log library
# included in the NDK.
${log-lib} )
================================================
FILE: library/src/main/cpp/art.cpp
================================================
/*
* Copyright (c) 2017, weishu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "art.h"
#include
#include
#define LOGV(...) ((void)__android_log_print(ANDROID_LOG_INFO, "epic.Native", __VA_ARGS__))
#define ANDROID_R_API 30
#define MAX_SEARCH_LEN 2000
void* ArtHelper::runtime_instance_ = nullptr;
int ArtHelper::api = 0;
template
inline int find_offset(void* hay, int size, T needle)
{
for (int i = 0; i < size; i += sizeof(T)) {
auto current = reinterpret_cast(reinterpret_cast(hay) + i);
if (*current == needle) {
return i;
}
}
return -1;
}
void ArtHelper::init(JNIEnv* env, int api)
{
ArtHelper::api = api;
JavaVM* javaVM;
env->GetJavaVM(&javaVM);
JavaVMExt* javaVMExt = (JavaVMExt*)javaVM;
void* runtime = javaVMExt->runtime;
if (runtime == nullptr) {
return;
}
if (api < ANDROID_R_API) {
runtime_instance_ = runtime;
} else {
int vm_offset = find_offset(runtime, MAX_SEARCH_LEN, javaVM);
runtime_instance_ = reinterpret_cast(reinterpret_cast(runtime) + vm_offset - offsetof(PartialRuntimeR, java_vm_));
}
}
void* ArtHelper::getClassLinker()
{
if (runtime_instance_ == nullptr || api < ANDROID_R_API) {
return nullptr;
}
PartialRuntimeR* runtimeR = (PartialRuntimeR*)runtime_instance_;
return runtimeR->class_linker_;
}
void* ArtHelper::getJniIdManager()
{
if (runtime_instance_ == nullptr || api < ANDROID_R_API) {
return nullptr;
}
PartialRuntimeR* runtimeR = (PartialRuntimeR*)runtime_instance_;
return runtimeR->jni_id_manager_;
}
void* ArtHelper::getJitCodeCache()
{
if (runtime_instance_ == nullptr || api < ANDROID_R_API) {
return nullptr;
}
PartialRuntimeR* runtimeR = (PartialRuntimeR*)runtime_instance_;
return runtimeR->jit_code_cache_;
}
void* ArtHelper::getHeap()
{
if (runtime_instance_ == nullptr) {
return nullptr;
}
if (api < 26) {
Runtime_7X* runtime7X = (Runtime_7X*)runtime_instance_;
return runtime7X->heap_;
} else if (api < ANDROID_R_API) {
Runtime_8X* runtime8X = (Runtime_8X*)runtime_instance_;
LOGV("bootclasspath : %s", runtime8X->boot_class_path_string_.c_str());
return runtime8X->heap_;
} else {
PartialRuntimeR* runtimeR = (PartialRuntimeR*)runtime_instance_;
return runtimeR->heap_;
}
}
================================================
FILE: library/src/main/cpp/art.h
================================================
/*
* Copyright (c) 2017, weishu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef EPIC_ART_H
#define EPIC_ART_H
#include
#include
#include
#include
#include
// Android 6.0: http://androidxref.com/6.0.0_r5/xref/art/runtime/runtime.h
// Android 7.0: http://androidxref.com/7.0.0_r1/xref/art/runtime/runtime.h
// Android 7.1.1: http://androidxref.com/7.1.1_r6/xref/art/runtime/runtime.h
// Android 8.0: http://androidxref.com/8.0.0_r4/xref/art/runtime/runtime.h
struct Runtime_7X {
uint64_t callee_save_methods_[3];
void* pre_allocated_OutOfMemoryError_;
void* pre_allocated_NoClassDefFoundError_;
void* resolution_method_;
void* imt_conflict_method_;
// Unresolved method has the same behavior as the conflict method, it is used by the class linker
// for differentiating between unfilled imt slots vs conflict slots in superclasses.
void* imt_unimplemented_method_;
void* sentinel_;
int instruction_set_;
uint32_t callee_save_method_frame_infos_[9]; // QuickMethodFrameInfo = uint32_t * 3
void* compiler_callbacks_;
bool is_zygote_;
bool must_relocate_;
bool is_concurrent_gc_enabled_;
bool is_explicit_gc_disabled_;
bool dex2oat_enabled_;
bool image_dex2oat_enabled_;
std::string compiler_executable_;
std::string patchoat_executable_;
std::vector compiler_options_;
std::vector image_compiler_options_;
std::string image_location_;
std::string boot_class_path_string_;
std::string class_path_string_;
std::vector properties_;
// The default stack size for managed threads created by the runtime.
size_t default_stack_size_;
void* heap_;
};
struct Runtime_8X {
uint64_t callee_save_methods_[3];
void* pre_allocated_OutOfMemoryError_;
void* pre_allocated_NoClassDefFoundError_;
void* resolution_method_;
void* imt_conflict_method_;
// Unresolved method has the same behavior as the conflict method, it is used by the class linker
// for differentiating between unfilled imt slots vs conflict slots in superclasses.
void* imt_unimplemented_method_;
void* sentinel_;
int instruction_set_;
uint32_t callee_save_method_frame_infos_[9]; // QuickMethodFrameInfo = uint32_t * 3
void* compiler_callbacks_;
bool is_zygote_;
bool must_relocate_;
bool is_concurrent_gc_enabled_;
bool is_explicit_gc_disabled_;
bool dex2oat_enabled_;
bool image_dex2oat_enabled_;
std::string compiler_executable_;
std::string patchoat_executable_;
std::vector compiler_options_;
std::vector image_compiler_options_;
std::string image_location_;
std::string boot_class_path_string_;
std::string class_path_string_;
std::vector properties_;
std::list agents_;
std::vector plugins_;
// The default stack size for managed threads created by the runtime.
size_t default_stack_size_;
void* heap_;
};
struct PartialRuntimeR {
void* heap_;
void* jit_arena_pool_;
void* arena_pool_;
// Special low 4gb pool for compiler linear alloc. We need ArtFields to be in low 4gb if we are
// compiling using a 32 bit image on a 64 bit compiler in case we resolve things in the image
// since the field arrays are int arrays in this case.
void* low_4gb_arena_pool_;
// Shared linear alloc for now.
void* linear_alloc_;
// The number of spins that are done before thread suspension is used to forcibly inflate.
size_t max_spins_before_thin_lock_inflation_;
void* monitor_list_;
void* monitor_pool_;
void* thread_list_;
void* intern_table_;
void* class_linker_;
void* signal_catcher_;
void* jni_id_manager_;
void* java_vm_;
void* jit_;
void* jit_code_cache_;
};
struct JavaVMExt {
void* functions;
void* runtime;
};
class ArtHelper {
public:
static void init(JNIEnv*, int);
static void* getRuntimeInstance() { return runtime_instance_; }
static void* getClassLinker();
static void* getJniIdManager();
static void* getJitCodeCache();
static void* getHeap();
private:
static void* runtime_instance_;
static int api;
};
#endif //EPIC_ART_H
================================================
FILE: library/src/main/cpp/build_with_cmake
================================================
#! /bin/sh
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_SDK/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_TOOLCHAIN=gcc -H. -Bbuild && cd build && make
================================================
FILE: library/src/main/cpp/epic.cpp
================================================
/*
* Original Copyright 2014-2015 Marvin Wißfeld
* Modified work Copyright (c) 2017, weishu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "fake_dlfcn.h"
#include "art.h"
#undef NDEBUG
#ifdef NDEBUG
#define LOGV(...) ((void)__android_log_print(ANDROID_LOG_INFO, "epic.Native", __VA_ARGS__))
#else
#define LOGV(...)
#endif
#define JNIHOOK_CLASS "me/weishu/epic/art/EpicNative"
jobject (*addWeakGloablReference)(JavaVM *, void *, void *) = nullptr;
void* (*jit_load_)(bool*) = nullptr;
void* jit_compiler_handle_ = nullptr;
bool (*jit_compile_method_)(void*, void*, void*, bool) = nullptr;
void* (*JitCodeCache_GetCurrentRegion)(void*) = nullptr;
typedef bool (*JIT_COMPILE_METHOD1)(void *, void *, void *, bool);
typedef bool (*JIT_COMPILE_METHOD2)(void *, void *, void *, bool, bool); // Android Q
typedef bool (*JIT_COMPILE_METHOD3)(void *, void *, void *, void *, bool, bool); // Android R
typedef bool (*JIT_COMPILE_METHOD4)(void *, void *, void *, void *, int); // Android S
void (*jit_unload_)(void*) = nullptr;
class ScopedSuspendAll {};
void (*suspendAll)(ScopedSuspendAll*, char*) = nullptr;
void (*resumeAll)(ScopedSuspendAll*) = nullptr;
class ScopedJitSuspend {};
void (*startJit)(ScopedJitSuspend*) = nullptr;
void (*stopJit)(ScopedJitSuspend*) = nullptr;
void (*DisableMovingGc)(void*) = nullptr;
void* (*JniIdManager_DecodeMethodId_)(void*, jlong) = nullptr;
void* (*ClassLinker_MakeInitializedClassesVisiblyInitialized_)(void*, void*, bool) = nullptr;
void* __self() {
#ifdef __arm__
register uint32_t r9 asm("r9");
return (void*) r9;
#elif defined(__aarch64__)
register uint64_t x19 asm("x19");
return (void*) x19;
#else
#endif
};
static int api_level;
void init_entries(JNIEnv *env) {
char api_level_str[5];
__system_property_get("ro.build.version.sdk", api_level_str);
api_level = atoi(api_level_str);
LOGV("api level: %d", api_level);
ArtHelper::init(env, api_level);
if (api_level < 23) {
// Android L, art::JavaVMExt::AddWeakGlobalReference(art::Thread*, art::mirror::Object*)
void *handle = dlopen("libart.so", RTLD_LAZY | RTLD_GLOBAL);
addWeakGloablReference = (jobject (*)(JavaVM *, void *, void *)) dlsym(handle,
"_ZN3art9JavaVMExt22AddWeakGlobalReferenceEPNS_6ThreadEPNS_6mirror6ObjectE");
} else if (api_level < 24) {
// Android M, art::JavaVMExt::AddWeakGlobalRef(art::Thread*, art::mirror::Object*)
void *handle = dlopen("libart.so", RTLD_LAZY | RTLD_GLOBAL);
addWeakGloablReference = (jobject (*)(JavaVM *, void *, void *)) dlsym(handle,
"_ZN3art9JavaVMExt16AddWeakGlobalRefEPNS_6ThreadEPNS_6mirror6ObjectE");
} else {
// Android N and O, Google disallow us use dlsym;
void *handle = dlopen_ex("libart.so", RTLD_NOW);
void *jit_lib = dlopen_ex("libart-compiler.so", RTLD_NOW);
LOGV("fake dlopen install: %p", handle);
const char *addWeakGloablReferenceSymbol = api_level <= 25
? "_ZN3art9JavaVMExt16AddWeakGlobalRefEPNS_6ThreadEPNS_6mirror6ObjectE"
: "_ZN3art9JavaVMExt16AddWeakGlobalRefEPNS_6ThreadENS_6ObjPtrINS_6mirror6ObjectEEE";
addWeakGloablReference = (jobject (*)(JavaVM *, void *, void *)) dlsym_ex(handle, addWeakGloablReferenceSymbol);
jit_compile_method_ = (bool (*)(void *, void *, void *, bool)) dlsym_ex(jit_lib, "jit_compile_method");
jit_load_ = reinterpret_cast(dlsym_ex(jit_lib, "jit_load"));
bool generate_debug_info = false;
jit_compiler_handle_ = (jit_load_)(&generate_debug_info);
LOGV("jit compile_method: %p", jit_compile_method_);
suspendAll = reinterpret_cast(dlsym_ex(handle, "_ZN3art16ScopedSuspendAllC1EPKcb"));
resumeAll = reinterpret_cast(dlsym_ex(handle, "_ZN3art16ScopedSuspendAllD1Ev"));
if (api_level >= 30) {
// Android R would not directly return ArtMethod address but an internal id
ClassLinker_MakeInitializedClassesVisiblyInitialized_ = reinterpret_cast(dlsym_ex(handle, "_ZN3art11ClassLinker40MakeInitializedClassesVisiblyInitializedEPNS_6ThreadEb"));
JniIdManager_DecodeMethodId_ = reinterpret_cast(dlsym_ex(handle, "_ZN3art3jni12JniIdManager14DecodeMethodIdEP10_jmethodID"));
if (api_level >= 31) {
// Android S CompileMethod accepts a CompilationKind enum instead of two booleans
// source: https://android.googlesource.com/platform/art/+/refs/heads/android12-release/compiler/jit/jit_compiler.cc
jit_compile_method_ = (bool (*)(void *, void *, void *, bool)) dlsym_ex(jit_lib, "_ZN3art3jit11JitCompiler13CompileMethodEPNS_6ThreadEPNS0_15JitMemoryRegionEPNS_9ArtMethodENS_15CompilationKindE");
} else {
jit_compile_method_ = (bool (*)(void *, void *, void *, bool)) dlsym_ex(jit_lib, "_ZN3art3jit11JitCompiler13CompileMethodEPNS_6ThreadEPNS0_15JitMemoryRegionEPNS_9ArtMethodEbb");
}
JitCodeCache_GetCurrentRegion = (void* (*)(void*)) dlsym_ex(handle, "_ZN3art3jit12JitCodeCache16GetCurrentRegionEv");
}
// Disable this now.
// startJit = reinterpret_cast(dlsym_ex(handle, "_ZN3art3jit16ScopedJitSuspendD1Ev"));
// stopJit = reinterpret_cast(dlsym_ex(handle, "_ZN3art3jit16ScopedJitSuspendC1Ev"));
// DisableMovingGc = reinterpret_cast(dlsym_ex(handle, "_ZN3art2gc4Heap15DisableMovingGcEv"));
}
LOGV("addWeakGloablReference: %p", addWeakGloablReference);
}
jboolean epic_compile(JNIEnv *env, jclass, jobject method, jlong self) {
LOGV("self from native peer: %p, from register: %p", reinterpret_cast(self), __self());
jlong art_method = (jlong) env->FromReflectedMethod(method);
if (art_method % 2 == 1) {
art_method = reinterpret_cast(JniIdManager_DecodeMethodId_(ArtHelper::getJniIdManager(), art_method));
}
bool ret;
if (api_level >= 30) {
void* current_region = JitCodeCache_GetCurrentRegion(ArtHelper::getJitCodeCache());
if (api_level >= 31) {
ret = ((JIT_COMPILE_METHOD4)jit_compile_method_)(jit_compiler_handle_, reinterpret_cast(self),
reinterpret_cast(current_region),
reinterpret_cast(art_method), 1);
} else {
ret = ((JIT_COMPILE_METHOD3)jit_compile_method_)(jit_compiler_handle_, reinterpret_cast(self),
reinterpret_cast(current_region),
reinterpret_cast(art_method), false, false);
}
} else if (api_level >= 29) {
ret = ((JIT_COMPILE_METHOD2) jit_compile_method_)(jit_compiler_handle_,
reinterpret_cast(art_method),
reinterpret_cast(self), false, false);
} else {
ret = ((JIT_COMPILE_METHOD1) jit_compile_method_)(jit_compiler_handle_,
reinterpret_cast(art_method),
reinterpret_cast(self), false);
}
return (jboolean)ret;
}
jlong epic_suspendAll(JNIEnv *, jclass) {
ScopedSuspendAll *scopedSuspendAll = (ScopedSuspendAll *) malloc(sizeof(ScopedSuspendAll));
suspendAll(scopedSuspendAll, "stop_jit");
return reinterpret_cast(scopedSuspendAll);
}
void epic_resumeAll(JNIEnv* env, jclass, jlong obj) {
ScopedSuspendAll* scopedSuspendAll = reinterpret_cast(obj);
resumeAll(scopedSuspendAll);
}
jlong epic_stopJit(JNIEnv*, jclass) {
ScopedJitSuspend *scopedJitSuspend = (ScopedJitSuspend *) malloc(sizeof(ScopedJitSuspend));
stopJit(scopedJitSuspend);
return reinterpret_cast(scopedJitSuspend);
}
void epic_startJit(JNIEnv*, jclass, jlong obj) {
ScopedJitSuspend *scopedJitSuspend = reinterpret_cast(obj);
startJit(scopedJitSuspend);
}
void epic_disableMovingGc(JNIEnv* env, jclass ,jint api) {
void *heap = ArtHelper::getHeap();
DisableMovingGc(heap);
}
jboolean epic_munprotect(JNIEnv *env, jclass, jlong addr, jlong len) {
long pagesize = sysconf(_SC_PAGESIZE);
unsigned alignment = (unsigned)((unsigned long long)addr % pagesize);
LOGV("munprotect page size: %d, alignment: %d", pagesize, alignment);
int i = mprotect((void *) (addr - alignment), (size_t) (alignment + len),
PROT_READ | PROT_WRITE | PROT_EXEC);
if (i == -1) {
LOGV("mprotect failed: %s (%d)", strerror(errno), errno);
return JNI_FALSE;
}
return JNI_TRUE;
}
jboolean epic_cacheflush(JNIEnv *env, jclass, jlong addr, jlong len) {
#if defined(__arm__)
int i = cacheflush(addr, addr + len, 0);
LOGV("arm cacheflush for, %ul", addr);
if (i == -1) {
LOGV("cache flush failed: %s (%d)", strerror(errno), errno);
return JNI_FALSE;
}
#elif defined(__aarch64__)
char* begin = reinterpret_cast(addr);
__builtin___clear_cache(begin, begin + len);
LOGV("aarch64 __builtin___clear_cache, %p", (void*)begin);
#endif
return JNI_TRUE;
}
void epic_MakeInitializedClassVisibilyInitialized(JNIEnv *env, jclass, jlong self) {
if (api_level >= 30 && ClassLinker_MakeInitializedClassesVisiblyInitialized_ && ArtHelper::getClassLinker()) {
ClassLinker_MakeInitializedClassesVisiblyInitialized_(ArtHelper::getClassLinker(), reinterpret_cast(self), true);
}
}
void epic_memcpy(JNIEnv *env, jclass, jlong src, jlong dest, jint length) {
char *srcPnt = (char *) src;
char *destPnt = (char *) dest;
for (int i = 0; i < length; ++i) {
destPnt[i] = srcPnt[i];
}
}
void epic_memput(JNIEnv *env, jclass, jbyteArray src, jlong dest) {
jbyte *srcPnt = env->GetByteArrayElements(src, 0);
jsize length = env->GetArrayLength(src);
unsigned char *destPnt = (unsigned char *) dest;
for (int i = 0; i < length; ++i) {
// LOGV("put %d with %d", i, *(srcPnt + i));
destPnt[i] = (unsigned char) srcPnt[i];
}
env->ReleaseByteArrayElements(src, srcPnt, 0);
}
jbyteArray epic_memget(JNIEnv *env, jclass, jlong src, jint length) {
jbyteArray dest = env->NewByteArray(length);
if (dest == NULL) {
return NULL;
}
unsigned char *destPnt = (unsigned char *) env->GetByteArrayElements(dest, 0);
unsigned char *srcPnt = (unsigned char *) src;
for (int i = 0; i < length; ++i) {
destPnt[i] = srcPnt[i];
}
env->ReleaseByteArrayElements(dest, (jbyte *) destPnt, 0);
return dest;
}
jlong epic_mmap(JNIEnv *env, jclass, jint length) {
void *space = mmap(0, (size_t) length, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (space == MAP_FAILED) {
LOGV("mmap failed: %d", errno);
return 0;
}
return (jlong) space;
}
void epic_munmap(JNIEnv *env, jclass, jlong addr, jint length) {
int r = munmap((void *) addr, (size_t) length);
if (r == -1) {
LOGV("munmap failed: %d", errno);
}
}
jlong epic_malloc(JNIEnv *env, jclass, jint size) {
size_t length = sizeof(void *) * size;
void *ptr = malloc(length);
LOGV("malloc :%d of memory at: %p", (int) length, ptr);
return (jlong) ptr;
}
jobject epic_getobject(JNIEnv *env, jclass clazz, jlong self, jlong address) {
JavaVM *vm;
env->GetJavaVM(&vm);
LOGV("java vm: %p, self: %p, address: %p", vm, (void*) self, (void*) address);
jobject object = addWeakGloablReference(vm, (void *) self, (void *) address);
jobject new_local_object = env->NewLocalRef(object);
env->DeleteWeakGlobalRef(object);
return new_local_object;
}
jlong epic_getMethodAddress(JNIEnv *env, jclass clazz, jobject method) {
jlong art_method = (jlong) env->FromReflectedMethod(method);
if (art_method % 2 == 1) {
art_method = reinterpret_cast(JniIdManager_DecodeMethodId_(ArtHelper::getJniIdManager(), art_method));
}
return art_method;
}
jboolean epic_isGetObjectAvaliable(JNIEnv *, jclass) {
return (jboolean) (addWeakGloablReference != nullptr);
}
jboolean epic_activate(JNIEnv* env, jclass jclazz, jlong jumpToAddress, jlong pc, jlong sizeOfDirectJump,
jlong sizeOfBridgeJump, jbyteArray code) {
// fetch the array, we can not call this when thread suspend(may lead deadlock)
jbyte *srcPnt = env->GetByteArrayElements(code, 0);
jsize length = env->GetArrayLength(code);
jlong cookie = 0;
bool isNougat = api_level >= 24;
if (isNougat) {
// We do thus things:
// 1. modify the code mprotect
// 2. modify the code
// Ideal, this two operation must be atomic. Below N, this is safe, because no one
// modify the code except ourselves;
// But in Android N, When the jit is working, between our step 1 and step 2,
// if we modity the mprotect of the code, and planning to write the code,
// the jit thread may modify the mprotect of the code meanwhile
// we must suspend all thread to ensure the atomic operation.
LOGV("suspend all thread.");
cookie = epic_suspendAll(env, jclazz);
}
jboolean result = epic_munprotect(env, jclazz, jumpToAddress, sizeOfDirectJump);
if (result) {
unsigned char *destPnt = (unsigned char *) jumpToAddress;
for (int i = 0; i < length; ++i) {
destPnt[i] = (unsigned char) srcPnt[i];
}
jboolean ret = epic_cacheflush(env, jclazz, pc, sizeOfBridgeJump);
if (!ret) {
LOGV("cache flush failed!!");
}
} else {
LOGV("Writing hook failed: Unable to unprotect memory at %d", jumpToAddress);
}
if (cookie != 0) {
LOGV("resume all thread.");
epic_resumeAll(env, jclazz, cookie);
}
env->ReleaseByteArrayElements(code, srcPnt, 0);
return result;
}
static JNINativeMethod dexposedMethods[] = {
{"mmap", "(I)J", (void *) epic_mmap},
{"munmap", "(JI)Z", (void *) epic_munmap},
{"memcpy", "(JJI)V", (void *) epic_memcpy},
{"memput", "([BJ)V", (void *) epic_memput},
{"memget", "(JI)[B", (void *) epic_memget},
{"munprotect", "(JJ)Z", (void *) epic_munprotect},
{"getMethodAddress", "(Ljava/lang/reflect/Member;)J", (void *) epic_getMethodAddress},
{"cacheflush", "(JJ)Z", (void *) epic_cacheflush},
{"MakeInitializedClassVisibilyInitialized", "(J)V", (void *) epic_MakeInitializedClassVisibilyInitialized},
{"malloc", "(I)J", (void *) epic_malloc},
{"getObjectNative", "(JJ)Ljava/lang/Object;", (void *) epic_getobject},
{"compileMethod", "(Ljava/lang/reflect/Member;J)Z",(void *) epic_compile},
{"suspendAll", "()J", (void *) epic_suspendAll},
{"resumeAll", "(J)V", (void *) epic_resumeAll},
{"stopJit", "()J", (void *) epic_stopJit},
{"startJit", "(J)V", (void *) epic_startJit},
{"disableMovingGc", "(I)V", (void *) epic_disableMovingGc},
{"activateNative", "(JJJJ[B)Z", (void *) epic_activate},
{"isGetObjectAvailable", "()Z", (void *) epic_isGetObjectAvaliable}
};
static int registerNativeMethods(JNIEnv *env, const char *className,
JNINativeMethod *gMethods, int numMethods) {
jclass clazz = env->FindClass(className);
if (clazz == NULL) {
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
LOGV("JNI_OnLoad");
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
if (!registerNativeMethods(env, JNIHOOK_CLASS, dexposedMethods,
sizeof(dexposedMethods) / sizeof(dexposedMethods[0]))) {
return -1;
}
init_entries(env);
return JNI_VERSION_1_6;
}
================================================
FILE: library/src/main/cpp/fake_dlfcn.cpp
================================================
// Copyright (c) 2016 avs333
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//fork from https://github.com/avs333/Nougat_dlfunctions
//do some modify
//support all cpu abi such as x86, x86_64
//support filename search if filename is not start with '/'
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define TAG_NAME "dlfcn_ex"
#ifdef LOG_DBG
#define log_info(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG_NAME, (const char *) fmt, ##args)
#define log_err(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG_NAME, (const char *) fmt, ##args)
#define log_dbg log_info
#else
#define log_dbg(...)
#define log_info(fmt, args...)
#define log_err(fmt, args...)
#endif
#ifdef __LP64__
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#else
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#endif
struct ctx {
void *load_addr;
void *dynstr;
void *dynsym;
int nsyms;
off_t bias;
};
extern "C" {
static int fake_dlclose(void *handle) {
if (handle) {
struct ctx *ctx = (struct ctx *) handle;
if (ctx->dynsym) free(ctx->dynsym); /* we're saving dynsym and dynstr */
if (ctx->dynstr) free(ctx->dynstr); /* from library file just in case */
free(ctx);
}
return 0;
}
/* flags are ignored */
static void *fake_dlopen_with_path(const char *libpath, int flags) {
FILE *maps;
char buff[256];
struct ctx *ctx = 0;
off_t load_addr, size;
int k, fd = -1, found = 0;
char *shoff;
Elf_Ehdr *elf = (Elf_Ehdr *) MAP_FAILED;
#define fatal(fmt, args...) do { log_err(fmt,##args); goto err_exit; } while(0)
maps = fopen("/proc/self/maps", "r");
if (!maps) fatal("failed to open maps");
while (!found && fgets(buff, sizeof(buff), maps)) {
if (strstr(buff, libpath) && (strstr(buff, "r-xp") || strstr(buff, "r--p"))) found = 1;
}
fclose(maps);
if (!found) fatal("%s not found in my userspace", libpath);
if (sscanf(buff, "%lx", &load_addr) != 1)
fatal("failed to read load address for %s", libpath);
log_info("%s loaded in Android at 0x%08lx", libpath, load_addr);
/* Now, mmap the same library once again */
fd = open(libpath, O_RDONLY);
if (fd < 0) fatal("failed to open %s", libpath);
size = lseek(fd, 0, SEEK_END);
if (size <= 0) fatal("lseek() failed for %s", libpath);
elf = (Elf_Ehdr *) mmap(0, size, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
fd = -1;
if (elf == MAP_FAILED) fatal("mmap() failed for %s", libpath);
ctx = (struct ctx *) calloc(1, sizeof(struct ctx));
if (!ctx) fatal("no memory for %s", libpath);
ctx->load_addr = (void *) load_addr;
shoff = ((char *) elf) + elf->e_shoff;
for (k = 0; k < elf->e_shnum; k++, shoff += elf->e_shentsize) {
Elf_Shdr *sh = (Elf_Shdr *) shoff;
log_dbg("%s: k=%d shdr=%p type=%x", __func__, k, sh, sh->sh_type);
switch (sh->sh_type) {
case SHT_DYNSYM:
if (ctx->dynsym) fatal("%s: duplicate DYNSYM sections", libpath); /* .dynsym */
ctx->dynsym = malloc(sh->sh_size);
if (!ctx->dynsym) fatal("%s: no memory for .dynsym", libpath);
memcpy(ctx->dynsym, ((char *) elf) + sh->sh_offset, sh->sh_size);
ctx->nsyms = (sh->sh_size / sizeof(Elf_Sym));
break;
case SHT_STRTAB:
if (ctx->dynstr) break; /* .dynstr is guaranteed to be the first STRTAB */
ctx->dynstr = malloc(sh->sh_size);
if (!ctx->dynstr) fatal("%s: no memory for .dynstr", libpath);
memcpy(ctx->dynstr, ((char *) elf) + sh->sh_offset, sh->sh_size);
break;
case SHT_PROGBITS:
if (!ctx->dynstr || !ctx->dynsym) break;
/* won't even bother checking against the section name */
ctx->bias = (off_t) sh->sh_addr - (off_t) sh->sh_offset;
k = elf->e_shnum; /* exit for */
break;
}
}
munmap(elf, size);
elf = 0;
if (!ctx->dynstr || !ctx->dynsym) fatal("dynamic sections not found in %s", libpath);
#undef fatal
log_dbg("%s: ok, dynsym = %p, dynstr = %p", libpath, ctx->dynsym, ctx->dynstr);
return ctx;
err_exit:
if (fd >= 0) close(fd);
if (elf != MAP_FAILED) munmap(elf, size);
fake_dlclose(ctx);
return 0;
}
#if defined(__LP64__)
static const char *const kSystemLibDir = "/system/lib64/";
static const char *const kOdmLibDir = "/odm/lib64/";
static const char *const kVendorLibDir = "/vendor/lib64/";
static const char *const kApexLibDir = "/apex/com.android.runtime/lib64/";
static const char *const kApexArtNsLibDir = "/apex/com.android.art/lib64/";
#else
static const char *const kSystemLibDir = "/system/lib/";
static const char *const kOdmLibDir = "/odm/lib/";
static const char *const kVendorLibDir = "/vendor/lib/";
static const char *const kApexLibDir = "/apex/com.android.runtime/lib/";
static const char *const kApexArtNsLibDir = "/apex/com.android.art/lib/";
#endif
static void *fake_dlopen(const char *filename, int flags) {
if (strlen(filename) > 0 && filename[0] == '/') {
return fake_dlopen_with_path(filename, flags);
} else {
char buf[512] = {0};
void *handle = NULL;
//sysmtem
strcpy(buf, kSystemLibDir);
strcat(buf, filename);
handle = fake_dlopen_with_path(buf, flags);
if (handle) {
return handle;
}
// apex in ns com.android.runtime
memset(buf, 0, sizeof(buf));
strcpy(buf, kApexLibDir);
strcat(buf, filename);
handle = fake_dlopen_with_path(buf, flags);
if (handle) {
return handle;
}
// apex in ns com.android.art
memset(buf, 0, sizeof(buf));
strcpy(buf, kApexArtNsLibDir);
strcat(buf, filename);
handle = fake_dlopen_with_path(buf, flags);
if (handle) {
return handle;
}
//odm
memset(buf, 0, sizeof(buf));
strcpy(buf, kOdmLibDir);
strcat(buf, filename);
handle = fake_dlopen_with_path(buf, flags);
if (handle) {
return handle;
}
//vendor
memset(buf, 0, sizeof(buf));
strcpy(buf, kVendorLibDir);
strcat(buf, filename);
handle = fake_dlopen_with_path(buf, flags);
if (handle) {
return handle;
}
return fake_dlopen_with_path(filename, flags);
}
}
static void *fake_dlsym(void *handle, const char *name) {
int k;
struct ctx *ctx = (struct ctx *) handle;
Elf_Sym *sym = (Elf_Sym *) ctx->dynsym;
char *strings = (char *) ctx->dynstr;
for (k = 0; k < ctx->nsyms; k++, sym++)
if (strcmp(strings + sym->st_name, name) == 0) {
/* NB: sym->st_value is an offset into the section for relocatables,
but a VMA for shared libs or exe files, so we have to subtract the bias */
void *ret = (char *) ctx->load_addr + sym->st_value - ctx->bias;
log_info("%s found at %p", name, ret);
return ret;
}
return 0;
}
static const char *fake_dlerror() {
return NULL;
}
// =============== implementation for compat ==========
static int SDK_INT = -1;
static int get_sdk_level() {
if (SDK_INT > 0) {
return SDK_INT;
}
char sdk[PROP_VALUE_MAX] = {0};;
__system_property_get("ro.build.version.sdk", sdk);
SDK_INT = atoi(sdk);
return SDK_INT;
}
int dlclose_ex(void *handle) {
if (get_sdk_level() >= 24) {
return fake_dlclose(handle);
} else {
return dlclose(handle);
}
}
void *dlopen_ex(const char *filename, int flags) {
log_info("dlopen: %s", filename);
if (get_sdk_level() >= 24) {
return fake_dlopen(filename, flags);
} else {
return dlopen(filename, flags);
}
}
void *dlsym_ex(void *handle, const char *symbol) {
if (get_sdk_level() >= 24) {
return fake_dlsym(handle, symbol);
} else {
return dlsym(handle, symbol);
}
}
const char *dlerror_ex() {
if (get_sdk_level() >= 24) {
return fake_dlerror();
} else {
return dlerror();
}
}
}
================================================
FILE: library/src/main/cpp/fake_dlfcn.h
================================================
// Copyright (c) 2016 avs333
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef DEXPOSED_DLFCN_H
#define DEXPOSED_DLFCN_H
#include
#include
#include
extern "C" {
void *dlopen_ex(const char *filename, int flags);
void *dlsym_ex(void *handle, const char *symbol);
int dlclose_ex(void *handle);
const char *dlerror_ex();
};
#endif //DEXPOSED_DLFCN_H
================================================
FILE: library/src/main/java/com/taobao/android/dexposed/ClassUtils.java
================================================
/*
* Original work Copyright (c) 2005-2008, The Android Open Source Project
* Modified work Copyright (c) 2013, rovo89 and Tungstwenty
* Modified work Copyright (c) 2015, Alibaba Mobile Infrastructure (Android) Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taobao.android.dexposed;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
/**
*
Operates on classes without using reflection.
*
*
This class handles invalid {@code null} inputs as best it can.
* Each method documents its behaviour in more detail.
*
*
The notion of a {@code canonical name} includes the human
* readable name for the type, for example {@code int[]}. The
* non-canonical method variants work with the JVM names, such as
* {@code [I}.
*
* @since 2.0
* @version $Id: ClassUtils.java 1199894 2011-11-09 17:53:59Z ggregory $
*/
public class ClassUtils {
/**
*
The package separator character: '.' == {@value}.
*/
public static final char PACKAGE_SEPARATOR_CHAR = '.';
/**
*
The package separator String: {@code "."}.
*/
public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR);
/**
*
The inner class separator character: '$' == {@value}.
*/
public static final char INNER_CLASS_SEPARATOR_CHAR = '$';
/**
*
The inner class separator String: {@code "$"}.
*/
public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR);
/**
*
Empty string.
*/
public static final String STRING_EMPTY = "";
/**
* Maps primitive {@code Class}es to their corresponding wrapper {@code Class}.
*/
private static final Map, Class>> primitiveWrapperMap = new HashMap, Class>>();
static {
primitiveWrapperMap.put(Boolean.TYPE, Boolean.class);
primitiveWrapperMap.put(Byte.TYPE, Byte.class);
primitiveWrapperMap.put(Character.TYPE, Character.class);
primitiveWrapperMap.put(Short.TYPE, Short.class);
primitiveWrapperMap.put(Integer.TYPE, Integer.class);
primitiveWrapperMap.put(Long.TYPE, Long.class);
primitiveWrapperMap.put(Double.TYPE, Double.class);
primitiveWrapperMap.put(Float.TYPE, Float.class);
primitiveWrapperMap.put(Void.TYPE, Void.TYPE);
}
/**
* Maps wrapper {@code Class}es to their corresponding primitive types.
*/
private static final Map, Class>> wrapperPrimitiveMap = new HashMap, Class>>();
static {
for (Class> primitiveClass : primitiveWrapperMap.keySet()) {
Class> wrapperClass = primitiveWrapperMap.get(primitiveClass);
if (!primitiveClass.equals(wrapperClass)) {
wrapperPrimitiveMap.put(wrapperClass, primitiveClass);
}
}
}
/**
* Maps a primitive class name to its corresponding abbreviation used in array class names.
*/
private static final Map abbreviationMap = new HashMap();
/**
* Maps an abbreviation used in array class names to corresponding primitive class name.
*/
private static final Map reverseAbbreviationMap = new HashMap();
/**
* Add primitive type abbreviation to maps of abbreviations.
*
* @param primitive Canonical name of primitive type
* @param abbreviation Corresponding abbreviation of primitive type
*/
private static void addAbbreviation(String primitive, String abbreviation) {
abbreviationMap.put(primitive, abbreviation);
reverseAbbreviationMap.put(abbreviation, primitive);
}
/**
* Feed abbreviation maps
*/
static {
addAbbreviation("int", "I");
addAbbreviation("boolean", "Z");
addAbbreviation("float", "F");
addAbbreviation("long", "J");
addAbbreviation("short", "S");
addAbbreviation("byte", "B");
addAbbreviation("double", "D");
addAbbreviation("char", "C");
}
/**
*
ClassUtils instances should NOT be constructed in standard programming.
* Instead, the class should be used as
* {@code ClassUtils.getShortClassName(cls)}.
*
*
This constructor is public to permit tools that require a JavaBean
* instance to operate.
*/
public ClassUtils() {
super();
}
// Short class name
// ----------------------------------------------------------------------
/**
*
Gets the class name minus the package name for an {@code Object}.
*
* @param object the class to get the short name for, may be null
* @param valueIfNull the value to return if null
* @return the class name of the object without the package name, or the null value
*/
public static String getShortClassName(Object object, String valueIfNull) {
if (object == null) {
return valueIfNull;
}
return getShortClassName(object.getClass());
}
/**
*
Gets the class name minus the package name from a {@code Class}.
*
*
Consider using the Java 5 API {@link Class#getSimpleName()} instead.
* The one known difference is that this code will return {@code "Map.Entry"} while
* the {@code java.lang.Class} variant will simply return {@code "Entry"}.
*
* @param cls the class to get the short name for.
* @return the class name without the package name or an empty string
*/
public static String getShortClassName(Class> cls) {
if (cls == null) {
return "";
}
return getShortClassName(cls.getName());
}
/**
*
Gets the class name minus the package name from a String.
*
*
The string passed in is assumed to be a class name - it is not checked.
*
Note that this method differs from Class.getSimpleName() in that this will
* return {@code "Map.Entry"} whilst the {@code java.lang.Class} variant will simply
* return {@code "Entry"}.
*
* @param className the className to get the short name for
* @return the class name of the class without the package name or an empty string
*/
public static String getShortClassName(String className) {
if (className == null) {
return STRING_EMPTY;
}
if (className.length() == 0) {
return STRING_EMPTY;
}
StringBuilder arrayPrefix = new StringBuilder();
// Handle array encoding
if (className.startsWith("[")) {
while (className.charAt(0) == '[') {
className = className.substring(1);
arrayPrefix.append("[]");
}
// Strip Object type encoding
if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') {
className = className.substring(1, className.length() - 1);
}
}
if (reverseAbbreviationMap.containsKey(className)) {
className = reverseAbbreviationMap.get(className);
}
int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
int innerIdx = className.indexOf(
INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : lastDotIdx + 1);
String out = className.substring(lastDotIdx + 1);
if (innerIdx != -1) {
out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR);
}
return out + arrayPrefix;
}
/**
*
Null-safe version of aClass.getSimpleName()
*
* @param cls the class for which to get the simple name.
* @return the simple class name.
* @since 3.0
* @see Class#getSimpleName()
*/
public static String getSimpleName(Class> cls) {
if (cls == null) {
return STRING_EMPTY;
}
return cls.getSimpleName();
}
/**
*
Null-safe version of aClass.getSimpleName()
*
* @param object the object for which to get the simple class name.
* @param valueIfNull the value to return if object is null
* @return the simple class name.
* @since 3.0
* @see Class#getSimpleName()
*/
public static String getSimpleName(Object object, String valueIfNull) {
if (object == null) {
return valueIfNull;
}
return getSimpleName(object.getClass());
}
// Package name
// ----------------------------------------------------------------------
/**
*
Gets the package name of an {@code Object}.
*
* @param object the class to get the package name for, may be null
* @param valueIfNull the value to return if null
* @return the package name of the object, or the null value
*/
public static String getPackageName(Object object, String valueIfNull) {
if (object == null) {
return valueIfNull;
}
return getPackageName(object.getClass());
}
/**
*
Gets the package name of a {@code Class}.
*
* @param cls the class to get the package name for, may be {@code null}.
* @return the package name or an empty string
*/
public static String getPackageName(Class> cls) {
if (cls == null) {
return STRING_EMPTY;
}
return getPackageName(cls.getName());
}
/**
*
Gets the package name from a {@code String}.
*
*
The string passed in is assumed to be a class name - it is not checked.
*
If the class is unpackaged, return an empty string.
*
* @param className the className to get the package name for, may be {@code null}
* @return the package name or an empty string
*/
public static String getPackageName(String className) {
if (className == null || className.length() == 0) {
return STRING_EMPTY;
}
// Strip array encoding
while (className.charAt(0) == '[') {
className = className.substring(1);
}
// Strip Object type encoding
if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') {
className = className.substring(1);
}
int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
if (i == -1) {
return STRING_EMPTY;
}
return className.substring(0, i);
}
// Superclasses/Superinterfaces
// ----------------------------------------------------------------------
/**
*
Gets a {@code List} of superclasses for the given class.
*
* @param cls the class to look up, may be {@code null}
* @return the {@code List} of superclasses in order going up from this one
* {@code null} if null input
*/
public static List> getAllSuperclasses(Class> cls) {
if (cls == null) {
return null;
}
List> classes = new ArrayList>();
Class> superclass = cls.getSuperclass();
while (superclass != null) {
classes.add(superclass);
superclass = superclass.getSuperclass();
}
return classes;
}
/**
*
Gets a {@code List} of all interfaces implemented by the given
* class and its superclasses.
*
*
The order is determined by looking through each interface in turn as
* declared in the source file and following its hierarchy up. Then each
* superclass is considered in the same way. Later duplicates are ignored,
* so the order is maintained.
*
* @param cls the class to look up, may be {@code null}
* @return the {@code List} of interfaces in order,
* {@code null} if null input
*/
public static List> getAllInterfaces(Class> cls) {
if (cls == null) {
return null;
}
LinkedHashSet> interfacesFound = new LinkedHashSet>();
getAllInterfaces(cls, interfacesFound);
return new ArrayList>(interfacesFound);
}
/**
* Get the interfaces for the specified class.
*
* @param cls the class to look up, may be {@code null}
* @param interfacesFound the {@code Set} of interfaces for the class
*/
private static void getAllInterfaces(Class> cls, HashSet> interfacesFound) {
while (cls != null) {
Class>[] interfaces = cls.getInterfaces();
for (Class> i : interfaces) {
if (interfacesFound.add(i)) {
getAllInterfaces(i, interfacesFound);
}
}
cls = cls.getSuperclass();
}
}
// Convert list
// ----------------------------------------------------------------------
/**
*
Given a {@code List} of class names, this method converts them into classes.
*
*
A new {@code List} is returned. If the class name cannot be found, {@code null}
* is stored in the {@code List}. If the class name in the {@code List} is
* {@code null}, {@code null} is stored in the output {@code List}.
*
* @param classNames the classNames to change
* @return a {@code List} of Class objects corresponding to the class names,
* {@code null} if null input
* @throws ClassCastException if classNames contains a non String entry
*/
public static List> convertClassNamesToClasses(List classNames) {
if (classNames == null) {
return null;
}
List> classes = new ArrayList>(classNames.size());
for (String className : classNames) {
try {
classes.add(Class.forName(className));
} catch (Exception ex) {
classes.add(null);
}
}
return classes;
}
/**
*
Given a {@code List} of {@code Class} objects, this method converts
* them into class names.
*
*
A new {@code List} is returned. {@code null} objects will be copied into
* the returned list as {@code null}.
*
* @param classes the classes to change
* @return a {@code List} of class names corresponding to the Class objects,
* {@code null} if null input
* @throws ClassCastException if {@code classes} contains a non-{@code Class} entry
*/
public static List convertClassesToClassNames(List> classes) {
if (classes == null) {
return null;
}
List classNames = new ArrayList(classes.size());
for (Class> cls : classes) {
if (cls == null) {
classNames.add(null);
} else {
classNames.add(cls.getName());
}
}
return classNames;
}
// Class loading
// ----------------------------------------------------------------------
/**
* Returns the class represented by {@code className} using the
* {@code classLoader}. This implementation supports the syntaxes
* "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}",
* "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}".
*
* @param classLoader the class loader to use to load the class
* @param className the class name
* @param initialize whether the class must be initialized
* @return the class represented by {@code className} using the {@code classLoader}
* @throws ClassNotFoundException if the class is not found
*/
public static Class> getClass(
ClassLoader classLoader, String className, boolean initialize) throws ClassNotFoundException {
try {
Class> clazz;
if (abbreviationMap.containsKey(className)) {
String clsName = "[" + abbreviationMap.get(className);
clazz = Class.forName(clsName, initialize, classLoader).getComponentType();
} else {
clazz = Class.forName(toCanonicalName(className), initialize, classLoader);
}
return clazz;
} catch (ClassNotFoundException ex) {
// allow path separators (.) as inner class name separators
int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
if (lastDotIndex != -1) {
try {
return getClass(classLoader, className.substring(0, lastDotIndex) +
INNER_CLASS_SEPARATOR_CHAR + className.substring(lastDotIndex + 1),
initialize);
} catch (ClassNotFoundException ex2) { // NOPMD
// ignore exception
}
}
throw ex;
}
}
/**
* Returns the (initialized) class represented by {@code className}
* using the {@code classLoader}. This implementation supports
* the syntaxes "{@code java.util.Map.Entry[]}",
* "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}",
* and "{@code [Ljava.util.Map$Entry;}".
*
* @param classLoader the class loader to use to load the class
* @param className the class name
* @return the class represented by {@code className} using the {@code classLoader}
* @throws ClassNotFoundException if the class is not found
*/
public static Class> getClass(ClassLoader classLoader, String className) throws ClassNotFoundException {
return getClass(classLoader, className, true);
}
/**
* Returns the (initialized) class represented by {@code className}
* using the current thread's context class loader. This implementation
* supports the syntaxes "{@code java.util.Map.Entry[]}",
* "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}",
* and "{@code [Ljava.util.Map$Entry;}".
*
* @param className the class name
* @return the class represented by {@code className} using the current thread's context class loader
* @throws ClassNotFoundException if the class is not found
*/
public static Class> getClass(String className) throws ClassNotFoundException {
return getClass(className, true);
}
/**
* Returns the class represented by {@code className} using the
* current thread's context class loader. This implementation supports the
* syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}",
* "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}".
*
* @param className the class name
* @param initialize whether the class must be initialized
* @return the class represented by {@code className} using the current thread's context class loader
* @throws ClassNotFoundException if the class is not found
*/
public static Class> getClass(String className, boolean initialize) throws ClassNotFoundException {
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL;
return getClass(loader, className, initialize);
}
// ----------------------------------------------------------------------
/**
* Converts a class name to a JLS style class name.
*
* @param className the class name
* @return the converted name
*/
private static String toCanonicalName(String className) {
className = deleteWhitespace(className);
if (className == null) {
throw new NullPointerException("className must not be null.");
} else if (className.endsWith("[]")) {
StringBuilder classNameBuffer = new StringBuilder();
while (className.endsWith("[]")) {
className = className.substring(0, className.length() - 2);
classNameBuffer.append("[");
}
String abbreviation = abbreviationMap.get(className);
if (abbreviation != null) {
classNameBuffer.append(abbreviation);
} else {
classNameBuffer.append("L").append(className).append(";");
}
className = classNameBuffer.toString();
}
return className;
}
/**
*
Converts an array of {@code Object} in to an array of {@code Class} objects.
* If any of these objects is null, a null element will be inserted into the array.
*
*
This method returns {@code null} for a {@code null} input array.
*
* @param array an {@code Object} array
* @return a {@code Class} array, {@code null} if null array input
* @since 2.4
*/
public static Class>[] toClass(Object... array) {
if (array == null) {
return null;
} else if (array.length == 0) {
return new Class[0];
}
Class>[] classes = new Class[array.length];
for (int i = 0; i < array.length; i++) {
classes[i] = array[i] == null ? null : array[i].getClass();
}
return classes;
}
// Delete
//-----------------------------------------------------------------------
/**
*
Deletes all whitespaces from a String as defined by
* {@link Character#isWhitespace(char)}.
*
*
* StringUtils.deleteWhitespace(null) = null
* StringUtils.deleteWhitespace("") = ""
* StringUtils.deleteWhitespace("abc") = "abc"
* StringUtils.deleteWhitespace(" ab c ") = "abc"
*
*
* @param str the String to delete whitespace from, may be null
* @return the String without whitespaces, {@code null} if null String input
*/
public static String deleteWhitespace(String str) {
if (isEmpty(str)) {
return str;
}
int sz = str.length();
char[] chs = new char[sz];
int count = 0;
for (int i = 0; i < sz; i++) {
if (!Character.isWhitespace(str.charAt(i))) {
chs[count++] = str.charAt(i);
}
}
if (count == sz) {
return str;
}
return new String(chs, 0, count);
}
// Empty checks
//-----------------------------------------------------------------------
/**
*